diff --git a/Content/BP_BugPlacerPawn.uasset b/Content/BP_BugPlacerPawn.uasset index 1c3bcdb..23b90d1 100644 Binary files a/Content/BP_BugPlacerPawn.uasset and b/Content/BP_BugPlacerPawn.uasset differ diff --git a/Source/Unrealzilla/Private/BugMarkerLoader.cpp b/Source/Unrealzilla/Private/BugMarkerLoader.cpp new file mode 100644 index 0000000..be85c36 --- /dev/null +++ b/Source/Unrealzilla/Private/BugMarkerLoader.cpp @@ -0,0 +1,164 @@ +// ©2022 Batty Bovine Productions, LLC. All Rights Reserved. + +#include "BugMarkerLoader.h" + +#include "BugMarkerActor.h" +#include "HttpModule.h" +#include "JsonObjectConverter.h" +#include "UnrealzillaGlobalSettings.h" +#include "Kismet/GameplayStatics.h" + + +void ABugMarkerLoader::BeginPlay() +{ + Super::BeginPlay(); + + const FString FullURL = GetDefault()->SubmissionServer + "/rest.cgi"; + + TArray StatusQueries; + if (GetDefault()->bShowUnresolvedBugs) + { + for (const FString Unresolved : GetDefault()->UnresolvedStatuses) + { + StatusQueries.Add("status=" + Unresolved); + } + } + if (GetDefault()->bShowInProgressBugs) + { + for (const FString InProgress : GetDefault()->InProgressStatuses) + { + StatusQueries.Add("status=" + InProgress); + } + } + if (GetDefault()->bShowResolvedBugs) + { + for (const FString Resolved : GetDefault()->ResolvedStatuses) + { + StatusQueries.Add("status=" + Resolved); + } + } + StatusQueries.Add("cf_mapname=" + this->GetWorld()->GetMapName().RightChop(this->GetWorld()->StreamingLevelsPrefix.Len())); + StatusQueries.Add("api_key=" + GetDefault()->APIKey); + const FString QueryString = FString::Join(StatusQueries, TEXT("&")); + + FHttpModule &HttpModule = FHttpModule::Get(); + TSharedRef SeverityRequest = HttpModule.CreateRequest(); + SeverityRequest->SetVerb(TEXT("GET")); + SeverityRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + SeverityRequest->SetURL(FullURL + "/bug" + "?" + QueryString); + SeverityRequest->OnProcessRequestComplete().BindUObject(this, &ABugMarkerLoader::ServerBugResponse); + SeverityRequest->ProcessRequest(); +} + +void ABugMarkerLoader::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + this->GetWorldTimerManager().ClearTimer(this->NewBatchTimerHandle); + this->GetWorldTimerManager().ClearTimer(this->UnloadAllTimerHandle); + + Super::EndPlay(EndPlayReason); +} + +void ABugMarkerLoader::ServerBugResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success) +{ + if (Success) + { + FJSONBugResponse ResponseData; + FString JSONResponse = Response->GetContentAsString(); + FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData); + + if (ResponseData.error) + { + this->Destroy(); + return; + } + else + { + this->BugBatch = ResponseData.bugs; + this->GetWorldTimerManager().SetTimer(this->NewBatchTimerHandle, this, &ABugMarkerLoader::LoadNewBatch, 1.0f/*this->GetWorld()->GetDeltaSeconds()*/); + } + } + else + { + this->Destroy(); + return; + } +} +void ABugMarkerLoader::LoadNewBatch() +{ + while (!this->BugBatch.IsEmpty()) + { + const FJSONBugData BugData = this->BugBatch[0]; + + FString LocationString, UpVectorString; + BugData.cf_location.Split(":", &LocationString, &UpVectorString); + + FString LocationX, LocationY, LocationZ; + LocationString.Split(",", &LocationX, &LocationY); + LocationY.Split(",", &LocationY, &LocationZ); + + FString UpVectorX, UpVectorY, UpVectorZ; + UpVectorString.Split(",", &UpVectorX, &UpVectorY); + UpVectorY.Split(",", &UpVectorY, &UpVectorZ); + + const FVector Location(FCString::Atof(*LocationX),FCString::Atof(*LocationY),FCString::Atof(*LocationZ)); + const FVector UpVector(FCString::Atof(*UpVectorX),FCString::Atof(*UpVectorY),FCString::Atof(*UpVectorZ)); + + TSubclassOf Class = StaticLoadClass(ABugMarkerActor::StaticClass(), this, BUG_MARKER_ACTOR_BP); + FActorSpawnParameters SpawnParams; + SpawnParams.Owner = this; + const FTransform Transform = FTransform(FRotationMatrix::MakeFromZ(UpVector).Rotator(), Location, FVector::OneVector); + ABugMarkerActor *Marker = this->GetWorld()->SpawnActorDeferred(Class, Transform, this); + Marker->SetBugID(BugData.id); + Marker->SetBugSummary(BugData.summary); + if (GetDefault()->UnresolvedStatuses.Contains(BugData.status)) + { + Marker->SetBugStatus(EBugStatus::Unresolved); + } + else if (GetDefault()->InProgressStatuses.Contains(BugData.status)) + { + Marker->SetBugStatus(EBugStatus::InProgress); + } + else if (GetDefault()->ResolvedStatuses.Contains(BugData.status)) + { + Marker->SetBugStatus(EBugStatus::Resolved); + } + Marker->FinishSpawning(Transform); + this->Markers.Add(Marker); + + this->BugBatch.RemoveAt(0); + if (!(this->BugBatch.Num() % GetDefault()->LoadMarkersBatch)) + { + break; + } + } + + if (!this->BugBatch.IsEmpty()) + { + this->GetWorldTimerManager().SetTimer(this->NewBatchTimerHandle, this, &ABugMarkerLoader::LoadNewBatch, this->GetWorld()->GetDeltaSeconds()); + } +} + +void ABugMarkerLoader::UnloadAll() +{ + this->GetWorldTimerManager().ClearTimer(this->NewBatchTimerHandle); + + while (!this->Markers.IsEmpty()) + { + AActor *Marker = this->Markers[0]; + this->Markers.RemoveAt(0); + Marker->Destroy(); + if (!(this->Markers.Num() % GetDefault()->UnloadMarkersBatch)) + { + break; + } + } + + if (!this->Markers.IsEmpty()) + { + this->GetWorldTimerManager().SetTimer(this->UnloadAllTimerHandle, this, &ABugMarkerLoader::UnloadAll, this->GetWorld()->GetDeltaSeconds()); + } + else + { + this->Destroy(); + } +} diff --git a/Source/Unrealzilla/Private/BugPlacerPawn.cpp b/Source/Unrealzilla/Private/BugPlacerPawn.cpp index e73db0a..f10da6c 100644 --- a/Source/Unrealzilla/Private/BugPlacerPawn.cpp +++ b/Source/Unrealzilla/Private/BugPlacerPawn.cpp @@ -3,11 +3,13 @@ #include "BugPlacerPawn.h" #include "BugMarkerActor.h" +#include "BugMarkerLoader.h" #include "ExitingBugPlacementScreen.h" #include "UnrealzillaGlobalSettings.h" #include "Components/MaterialBillboardComponent.h" #include "Components/SphereComponent.h" +#include "EngineUtils.h" #include "GameFramework/Character.h" #include "GameFramework/FloatingPawnMovement.h" #include "HAL/IConsoleManager.h" @@ -118,8 +120,9 @@ void ABugPlacerPawn::PlaceBugMarker() #if WITH_EDITOR SpawnParams.bHideFromSceneOutliner = true; #endif - - ABugMarkerActor *Marker = this->GetWorld()->SpawnActor(this->BugMarkerBlueprintClass.Get(), this->PlacementMarkerRoot->GetComponentTransform(), SpawnParams); + + TSubclassOf Class = StaticLoadClass(ABugMarkerActor::StaticClass(), nullptr, BUG_MARKER_ACTOR_BP); + ABugMarkerActor *Marker = this->GetWorld()->SpawnActor(Class.Get(), this->PlacementMarkerRoot->GetComponentTransform(), SpawnParams); Marker->LoadBugSubmissionForm(); } } @@ -168,10 +171,42 @@ void ABugPlacerPawn::ExitCanceled() } -void ABugPlacerPawn::SpawnBugPlacerPawn(const UObject *WorldContextObject, TSubclassOf Class) +void ABugPlacerPawn::SpawnBugPlacerPawn(const UObject *WorldContextObject) { + TSubclassOf Class = StaticLoadClass(ABugPlacerPawn::StaticClass(), nullptr, BUG_PLACER_PAWN_BP); const FTransform &Transform = UGameplayStatics::GetPlayerCameraManager(WorldContextObject, 0)->GetActorTransform(); FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; - WorldContextObject->GetWorld()->SpawnActor(Class.Get(), Transform, SpawnParams); + WorldContextObject->GetWorld()->SpawnActor(Class, Transform, SpawnParams); +} + +void ABugPlacerPawn::ShowBugMarkersInLevel(const UObject *WorldContextObject) +{ + for (TActorIterator Iterator(WorldContextObject->GetWorld(), ABugMarkerLoader::StaticClass()); Iterator; ++Iterator) + { + return; + } + WorldContextObject->GetWorld()->SpawnActor(ABugMarkerLoader::StaticClass(), FVector::ZeroVector, FRotator::ZeroRotator); +} + +void ABugPlacerPawn::HideBugMarkersInLevel(const UObject *WorldContextObject) +{ + for (TActorIterator Iterator(WorldContextObject->GetWorld(), ABugMarkerLoader::StaticClass()); Iterator; ++Iterator) + { + ABugMarkerLoader *BugMarkerLoader = Cast(*Iterator); + BugMarkerLoader->UnloadAll(); + } +} + +const FString ABugPlacerPawn::FormatQueryString(const TMap &QueryData) +{ + TArray AssembledKeyValuePairs; + TArray QueryKeys; + QueryData.GenerateKeyArray(QueryKeys); + for (const FString &QueryKey : QueryKeys) + { + AssembledKeyValuePairs.Add(QueryKey + "=" + QueryData[QueryKey]); + } + + return FString::Join(AssembledKeyValuePairs, TEXT("&")); } diff --git a/Source/Unrealzilla/Private/BugSubmissionForm.cpp b/Source/Unrealzilla/Private/BugSubmissionForm.cpp index 83d1c75..c0a69c9 100644 --- a/Source/Unrealzilla/Private/BugSubmissionForm.cpp +++ b/Source/Unrealzilla/Private/BugSubmissionForm.cpp @@ -3,6 +3,7 @@ #include "BugSubmissionForm.h" #include "BugMarkerActor.h" +#include "BugPlacerPawn.h" #include "CommonButtonBase.h" #include "CommonTextBlock.h" #include "HttpModule.h" @@ -31,19 +32,6 @@ const FText FormatFloatToText(float Float, int32 IntegralDigits, int32 Fractiona return FText::AsNumber(Float, &NumberFormat); } -const FString FormatQueryString(const TMap &QueryData) -{ - TArray AssembledKeyValuePairs; - TArray QueryKeys; - QueryData.GenerateKeyArray(QueryKeys); - for (const FString &QueryKey : QueryKeys) - { - AssembledKeyValuePairs.Add(QueryKey + "=" + QueryData[QueryKey]); - } - - return FString::Join(AssembledKeyValuePairs, TEXT("&")); -} - const FString GetGameVersion() { FString GameVersion; @@ -67,7 +55,7 @@ void UBugSubmissionForm::NativeOnInitialized() // Assemble query data into key:value pairs TMap QueryData; QueryData.Add("api_key", GetDefault()->APIKey); - const FString QueryString = FormatQueryString(QueryData); + const FString QueryString = ABugPlacerPawn::FormatQueryString(QueryData); // Query the server for information about the current product FHttpModule &HttpModule = FHttpModule::Get(); @@ -113,11 +101,11 @@ void UBugSubmissionForm::SetMarkerData(ABugMarkerActor *BugMarker) this->MapName = FText::AsCultureInvariant(this->GetWorld()->GetMapName().RightChop(this->GetWorld()->StreamingLevelsPrefix.Len())); - const FVector MarkerLocation = this->BugMarkerActor->GetActorLocation(); + const FVector MarkerLocationVector = this->BugMarkerActor->GetActorLocation(); FFormatNamedArguments MarkerLocationArgs; - MarkerLocationArgs.Add("X", FormatFloatToText(MarkerLocation.X, 1, 4)); - MarkerLocationArgs.Add("Y", FormatFloatToText(MarkerLocation.Y, 1, 4)); - MarkerLocationArgs.Add("Z", FormatFloatToText(MarkerLocation.Z, 1, 4)); + MarkerLocationArgs.Add("X", FormatFloatToText(MarkerLocationVector.X, 1, 4)); + MarkerLocationArgs.Add("Y", FormatFloatToText(MarkerLocationVector.Y, 1, 4)); + MarkerLocationArgs.Add("Z", FormatFloatToText(MarkerLocationVector.Z, 1, 4)); const FText MarkerLocationText = FText::Format(NSLOCTEXT("BugSubmissionForm", "MarkerLocation", "{X},{Y},{Z}"), MarkerLocationArgs); const FVector MarkerUpVector = this->BugMarkerActor->GetActorUpVector(); @@ -224,7 +212,7 @@ void UBugSubmissionForm::SubmitForm() TSharedRef Request = HttpModule.CreateRequest(); Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); - Request->SetURL(FullURL + RequestURL + "?" + FormatQueryString(QueryData)); + Request->SetURL(FullURL + RequestURL + "?" + ABugPlacerPawn::FormatQueryString(QueryData)); Request->SetContentAsString(PostJsonString); Request->OnProcessRequestComplete().BindUObject(this, &UBugSubmissionForm::ServerPOSTResponse); Request->ProcessRequest(); diff --git a/Source/Unrealzilla/Public/BugMarkerActor.h b/Source/Unrealzilla/Public/BugMarkerActor.h index 687ffc8..0461467 100644 --- a/Source/Unrealzilla/Public/BugMarkerActor.h +++ b/Source/Unrealzilla/Public/BugMarkerActor.h @@ -6,6 +6,17 @@ #include "BugMarkerActor.generated.h" +#define BUG_MARKER_ACTOR_BP TEXT("Blueprint'/Unrealzilla/BP_BugMarkerActor.BP_BugMarkerActor_C'") + + +UENUM() +enum class EBugStatus : uint8 +{ + Unresolved UMETA(DisplayName="Unresolved"), + InProgress UMETA(DisplayName="In Progress"), + Resolved UMETA(DisplayName="Resolved"), + MAX UMETA(Hidden) +}; UCLASS() class UNREALZILLA_API ABugMarkerActor : public AActor @@ -15,4 +26,13 @@ class UNREALZILLA_API ABugMarkerActor : public AActor public: UFUNCTION(BlueprintImplementableEvent) void LoadBugSubmissionForm(); + + void SetBugID(const uint32 &ID) { this->BugID = ID; } + void SetBugSummary(const FString &Summary) { this->BugSummary = Summary; } + void SetBugStatus(const EBugStatus &Status) { this->BugStatus = Status; } + +private: + uint32 BugID = 0; + FString BugSummary; + EBugStatus BugStatus; }; diff --git a/Source/Unrealzilla/Public/BugMarkerLoader.h b/Source/Unrealzilla/Public/BugMarkerLoader.h new file mode 100644 index 0000000..5e8c288 --- /dev/null +++ b/Source/Unrealzilla/Public/BugMarkerLoader.h @@ -0,0 +1,88 @@ +// ©2022 Batty Bovine Productions, LLC. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + +#include "Interfaces/IHttpRequest.h" +#include "Interfaces/IHttpResponse.h" + +#include "BugMarkerLoader.generated.h" + + +/** +* JSON structs for bug lists +*/ +USTRUCT(Blueprintable) +struct FJSONBugData +{ + GENERATED_BODY() +public: + UPROPERTY() + int32 id = -1; + UPROPERTY() + FString summary; + UPROPERTY() + FString component; + UPROPERTY() + FString cf_mapname; + UPROPERTY() + FString cf_location; + UPROPERTY() + FString platform; + UPROPERTY() + FString op_sys; + UPROPERTY() + bool is_open = true; + UPROPERTY() + FString severity; + UPROPERTY() + FString status; + UPROPERTY() + FString resolution; + UPROPERTY() + int32 dupe_of = -1; +}; + +USTRUCT(Blueprintable) +struct FJSONBugResponse +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadOnly) + TArray bugs; + UPROPERTY(BlueprintReadOnly) + bool error = false; + UPROPERTY(BlueprintReadOnly) + int32 code = -1; + UPROPERTY(BlueprintReadOnly) + FString message; + UPROPERTY(BlueprintReadOnly) + FString documentation; +}; +/** +* END JSON structs for bug lists +*/ + + +UCLASS() +class UNREALZILLA_API ABugMarkerLoader : public AActor +{ + GENERATED_BODY() + +public: + virtual void BeginPlay() override; + virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; + + void UnloadAll(); + +private: + void ServerBugResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success); + void LoadNewBatch(); + + TArray Markers; + + TArray BugBatch; + FTimerHandle NewBatchTimerHandle; + FTimerHandle UnloadAllTimerHandle; +}; diff --git a/Source/Unrealzilla/Public/BugPlacerPawn.h b/Source/Unrealzilla/Public/BugPlacerPawn.h index 014361d..d7e6080 100644 --- a/Source/Unrealzilla/Public/BugPlacerPawn.h +++ b/Source/Unrealzilla/Public/BugPlacerPawn.h @@ -7,6 +7,8 @@ #include "BugPlacerPawn.generated.h" +#define BUG_PLACER_PAWN_BP TEXT("Blueprint'/Unrealzilla/BP_BugPlacerPawn.BP_BugPlacerPawn_C'") + UCLASS() class UNREALZILLA_API ABugPlacerPawn : public APawn @@ -25,7 +27,16 @@ public: void Deactivate(); UFUNCTION(BlueprintCallable, meta=(WorldContext="WorldContextObject")) - static void SpawnBugPlacerPawn(const UObject *WorldContextObject, TSubclassOf Class); + static void SpawnBugPlacerPawn(const UObject *WorldContextObject); + + UFUNCTION(BlueprintCallable, meta=(WorldContext="WorldContextObject")) + static void ShowBugMarkersInLevel(const UObject *WorldContextObject); + + UFUNCTION(BlueprintCallable, meta=(WorldContext="WorldContextObject")) + static void HideBugMarkersInLevel(const UObject *WorldContextObject); + + UFUNCTION(BlueprintPure) + static const FString FormatQueryString(const TMap &QueryData); protected: UFUNCTION(BlueprintCallable) @@ -60,8 +71,6 @@ protected: UPROPERTY(BlueprintReadOnly, VisibleAnywhere) TObjectPtr PawnMovement; - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) - TSubclassOf BugMarkerBlueprintClass; UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) TSubclassOf ExitingBugPlacementScreenClass; @@ -75,11 +84,10 @@ private: uint8 bCurrentTraceHit : 1; TObjectPtr OriginalPlayer; + TObjectPtr BugMarker; TObjectPtr ExitingBugPlacementScreen; - //UPROPERTY(EditDefaultsOnly, meta=(UIMin="0.0", UIMax="0.25")) - // float TraceTimerDelay = 0.05f; FTimerHandle TraceTimerHandle; }; diff --git a/Source/Unrealzilla/Public/BugSubmissionForm.h b/Source/Unrealzilla/Public/BugSubmissionForm.h index 27f27f7..d294f05 100644 --- a/Source/Unrealzilla/Public/BugSubmissionForm.h +++ b/Source/Unrealzilla/Public/BugSubmissionForm.h @@ -65,41 +65,41 @@ public: */ -/** - * JSON structs for bug lists - */ -USTRUCT(Blueprintable) -struct FJSONBugData -{ - GENERATED_BODY() -public: - UPROPERTY() - FString component; - UPROPERTY() - FString cf_mapname; - UPROPERTY() - FString cf_location; -}; - -USTRUCT(Blueprintable) -struct FJSONBugResponse -{ - GENERATED_BODY() -public: - UPROPERTY(BlueprintReadOnly) - TArray bugs; - UPROPERTY(BlueprintReadOnly) - bool error = false; - UPROPERTY(BlueprintReadOnly) - int32 code = -1; - UPROPERTY(BlueprintReadOnly) - FString message; - UPROPERTY(BlueprintReadOnly) - FString documentation; -}; -/** - * END JSON structs for bug lists - */ +///** +// * JSON structs for bug lists +// */ +//USTRUCT(Blueprintable) +//struct FJSONBugData +//{ +// GENERATED_BODY() +//public: +// UPROPERTY() +// FString component; +// UPROPERTY() +// FString cf_mapname; +// UPROPERTY() +// FString cf_location; +//}; +// +//USTRUCT(Blueprintable) +//struct FJSONBugResponse +//{ +// GENERATED_BODY() +//public: +// UPROPERTY(BlueprintReadOnly) +// TArray bugs; +// UPROPERTY(BlueprintReadOnly) +// bool error = false; +// UPROPERTY(BlueprintReadOnly) +// int32 code = -1; +// UPROPERTY(BlueprintReadOnly) +// FString message; +// UPROPERTY(BlueprintReadOnly) +// FString documentation; +//}; +///** +// * END JSON structs for bug lists +// */ /** diff --git a/Source/Unrealzilla/Public/UnrealzillaGlobalSettings.h b/Source/Unrealzilla/Public/UnrealzillaGlobalSettings.h index c07ee71..15f91dc 100644 --- a/Source/Unrealzilla/Public/UnrealzillaGlobalSettings.h +++ b/Source/Unrealzilla/Public/UnrealzillaGlobalSettings.h @@ -23,6 +23,10 @@ public: UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly, Category="Bug Placement", meta=(DisplayName="Arbitrary Placement Distance")) float ArbitraryBugPlacementDistance = 250.0f; + // The status to use when filing a new bug. A status such as "UNCONFIRMED" is suggested. + UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting") + FString DefaultStatus; + // The Bugzilla server where bugs will be posted. UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting") FString SubmissionServer; @@ -36,32 +40,45 @@ public: UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting") int32 BugReportWidgetDepth = 0; - // The status to use when filing a new bug. A status such as "UNCONFIRMED" is suggested. - UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") - FString DefaultStatus; - // Whether to show unresolved bugs when displaying bug report markers. UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") - bool bShowUnresolvedBugs = false; - // Whether to show in-progress bugs when displaying bug report markers. + bool bShowUnresolvedBugs = true; + // Colour tint to use for unresolved bugs. UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") - bool bShowInProgressBugs = false; - // Whether to show resolved bugs when displaying bug report markers. - UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") - bool bShowResolvedBugs = false; - + FLinearColor UnresolvedTint; // A list of bug statuses that represent unresolved bugs in your Bugzilla install. // Generally this would be something like "UNCONFIRMED" and "CONFIRMED". UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") TArray UnresolvedStatuses; + + // Whether to show in-progress bugs when displaying bug report markers. + UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") + bool bShowInProgressBugs = true; + // Colour tint to use for in-progress bugs. + UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") + FLinearColor InProgressTint; // A list of bug statuses that represent in progress bugs in your Bugzilla install. // Generally this would include "IN_PROGRESS". UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") TArray InProgressStatuses; + + // Whether to show resolved bugs when displaying bug report markers. + UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") + bool bShowResolvedBugs = false; + // Colour tint to use for resolved bugs. + UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") + FLinearColor ResolvedTint; // A list of bug statuses that represent resolved bugs in your Bugzilla install. // Generally this would include "RESOLVED" and "VERIFIED". UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") TArray ResolvedStatuses; + + // How many bug markers to show in one batch. Each batch loads on a new frame. + UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") + int32 LoadMarkersBatch = 10; + // How many bug markers to hide in one batch. Each batch loads on a new frame. + UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking") + int32 UnloadMarkersBatch = 25; public: virtual void PostInitProperties() override;