diff --git a/Content/BP_BugMarkerActor.uasset b/Content/BP_BugMarkerActor.uasset index 9bfcbdc..8c4e9aa 100644 Binary files a/Content/BP_BugMarkerActor.uasset and b/Content/BP_BugMarkerActor.uasset differ diff --git a/Content/BP_BugPlacerPawn.uasset b/Content/BP_BugPlacerPawn.uasset index 608f855..43d9668 100644 Binary files a/Content/BP_BugPlacerPawn.uasset and b/Content/BP_BugPlacerPawn.uasset differ diff --git a/Content/C_BuggieDistance.uasset b/Content/C_BuggieDistance.uasset new file mode 100644 index 0000000..5ecb537 Binary files /dev/null and b/Content/C_BuggieDistance.uasset differ diff --git a/Content/M_Arrow.uasset b/Content/M_Arrow.uasset new file mode 100644 index 0000000..f161610 Binary files /dev/null and b/Content/M_Arrow.uasset differ diff --git a/Content/M_BuggieIcon.uasset b/Content/M_BuggieIcon.uasset index a357e6f..20b4aca 100644 Binary files a/Content/M_BuggieIcon.uasset and b/Content/M_BuggieIcon.uasset differ diff --git a/Content/SM_Arrow.uasset b/Content/SM_Arrow.uasset new file mode 100644 index 0000000..37e37b0 Binary files /dev/null and b/Content/SM_Arrow.uasset differ diff --git a/Content/T_Buggie_Centred.uasset b/Content/T_Buggie_Centred.uasset index d0262b9..2557a65 100644 Binary files a/Content/T_Buggie_Centred.uasset and b/Content/T_Buggie_Centred.uasset differ diff --git a/Content/UI/CUI_BugFormTextEntryButton.uasset b/Content/UI/CUI_BugFormTextEntryButton.uasset index 09aab72..36d43dd 100644 Binary files a/Content/UI/CUI_BugFormTextEntryButton.uasset and b/Content/UI/CUI_BugFormTextEntryButton.uasset differ diff --git a/Content/UI/CUI_BugSubmissionForm.uasset b/Content/UI/CUI_BugSubmissionForm.uasset index a0ad011..a037da4 100644 Binary files a/Content/UI/CUI_BugSubmissionForm.uasset and b/Content/UI/CUI_BugSubmissionForm.uasset differ diff --git a/Source/Unrealzilla/Private/BugFormTextEntryButton.cpp b/Source/Unrealzilla/Private/BugFormTextEntryButton.cpp new file mode 100644 index 0000000..9d69538 --- /dev/null +++ b/Source/Unrealzilla/Private/BugFormTextEntryButton.cpp @@ -0,0 +1,6 @@ +// ©2022 Batty Bovine Productions, LLC. All Rights Reserved. + +#include "BugFormTextEntryButton.h" + + + diff --git a/Source/Unrealzilla/Private/BugPlacerPawn.cpp b/Source/Unrealzilla/Private/BugPlacerPawn.cpp index 31b23f9..cba0a63 100644 --- a/Source/Unrealzilla/Private/BugPlacerPawn.cpp +++ b/Source/Unrealzilla/Private/BugPlacerPawn.cpp @@ -75,7 +75,8 @@ void ABugPlacerPawn::Tick(float DeltaSeconds) this->LaserGlow->SetVisibility(false, true); // If the debugger requests arbitrary placement, put the marker a set distance away from the camera. - this->PlacementMarkerRoot->SetRelativeLocationAndRotation(FVector(GetDefault()->ArbitraryBugPlacementDistance, 0.0f, 0.0f), FRotator::ZeroRotator); + this->PlacementMarkerRoot->SetRelativeLocation(FVector(GetDefault()->ArbitraryBugPlacementDistance, 0.0f, 0.0f)); + this->PlacementMarkerRoot->SetWorldRotation(FRotator::ZeroRotator); } else { @@ -108,24 +109,6 @@ void ABugPlacerPawn::Tick(float DeltaSeconds) this->TraceOriginComponent->SetRelativeScale3D(FVector(1.0f, 1.0f, (TraceEnd - TraceStart).Length())); } - - //#if ENABLE_DRAW_DEBUG - // if (bTraceHit && TraceHit.bBlockingHit) - // { - // // Line trace and impact point - // DrawDebugLine(World, TraceStart, TraceHit.ImpactPoint, FColor::Red, false); - // DrawDebugLine(World, TraceHit.ImpactPoint, TraceEnd, FColor::Green, false); - // DrawDebugPoint(World, TraceHit.ImpactPoint, 10.0f, FColor::Red, false); - // - // // Impact normal - // DrawDebugLine(World, TraceHit.ImpactPoint, TraceHit.ImpactPoint + (TraceHit.ImpactNormal * 50.0f), FColor(0, 100, 255, 255), false); - // } - // else - // { - // // Line trace, no impact - // DrawDebugLine(World, TraceStart, TraceEnd, FColor::Red, false); - // } - //#endif } } @@ -169,51 +152,31 @@ void ABugPlacerPawn::Deactivate() void ABugPlacerPawn::ExitStarted() { - if (this->ExitingBugPlacementScreenClass.IsValid()) - { - this->ExitingBugPlacementScreen = CreateWidget(UGameplayStatics::GetPlayerController(this, 0), this->ExitingBugPlacementScreenClass.Get()); - this->ExitingBugPlacementScreen->AddToViewport(GetDefault()->BugReportWidgetDepth + 10); - } - else - { - this->ExitingBugPlacementScreen = nullptr; - } + this->ExitingBugPlacementScreen = CreateWidget(UGameplayStatics::GetPlayerController(this, 0), this->ExitingBugPlacementScreenClass.Get()); + this->ExitingBugPlacementScreen->AddToViewport(GetDefault()->BugReportWidgetDepth + 10); } void ABugPlacerPawn::ExitOngoing(const float ElapsedSeconds) { - if (this->ExitingBugPlacementScreen) - { - this->ExitingBugPlacementScreen->SetPercent(ElapsedSeconds); - } + this->ExitingBugPlacementScreen->SetPercent(ElapsedSeconds); } void ABugPlacerPawn::ExitTriggered() { - if (this->ExitingBugPlacementScreen) - { - this->ExitingBugPlacementScreen->RemoveFromParent(); - this->Deactivate(); - } + this->ExitingBugPlacementScreen->RemoveFromParent(); + this->Deactivate(); } void ABugPlacerPawn::ExitCanceled() { - if (this->ExitingBugPlacementScreen) - { - this->ExitingBugPlacementScreen->RemoveFromParent(); - } + this->ExitingBugPlacementScreen->RemoveFromParent(); } -void ABugPlacerPawn::SpawnBugPlacerPawn(const UObject *WorldContextObject, TSoftClassPtr Class) +void ABugPlacerPawn::SpawnBugPlacerPawn(const UObject *WorldContextObject, TSubclassOf Class) { - if (Class.IsValid()) - { - const FTransform &Transform = UGameplayStatics::GetPlayerCameraManager(WorldContextObject, 0)->GetActorTransform(); - FActorSpawnParameters SpawnParams; - //SpawnParams.Name = TEXT("BugPlacerPawn"); - SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; - WorldContextObject->GetWorld()->SpawnActor(Class.Get(), Transform, SpawnParams); - } + const FTransform &Transform = UGameplayStatics::GetPlayerCameraManager(WorldContextObject, 0)->GetActorTransform(); + FActorSpawnParameters SpawnParams; + SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; + WorldContextObject->GetWorld()->SpawnActor(Class.Get(), Transform, SpawnParams); } diff --git a/Source/Unrealzilla/Private/BugSubmissionForm.cpp b/Source/Unrealzilla/Private/BugSubmissionForm.cpp index 04511bc..0b34fc8 100644 --- a/Source/Unrealzilla/Private/BugSubmissionForm.cpp +++ b/Source/Unrealzilla/Private/BugSubmissionForm.cpp @@ -6,8 +6,12 @@ #include "CommonButtonBase.h" #include "CommonTextBlock.h" #include "HttpModule.h" +#include "JsonObjectConverter.h" #include "UnrealzillaGlobalSettings.h" + +#include "Components/CircularThrobber.h" #include "Components/Overlay.h" +#include "Components/VerticalBox.h" #include "Kismet/GameplayStatics.h" @@ -23,13 +27,70 @@ const FText FormatFloatToText(float Float, int32 IntegralDigits, int32 Fractiona NumberFormat.MaximumIntegralDigits = INT32_MAX; NumberFormat.MinimumFractionalDigits = FractionalDigits; NumberFormat.MaximumFractionalDigits = FractionalDigits; - + NumberFormat.UseGrouping = false; 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("&")); +} + void UBugSubmissionForm::NativeOnInitialized() { + this->ShowProcessingOverlayLoading(); + + this->ProductNameValue->SetText(FText::FromString(GetDefault()->ProductName)); + + const FString FullURL = GetDefault()->SubmissionServer + "/rest.cgi"; + + // Assemble query data into key:value pairs + TMap QueryData; + QueryData.Add("api_key", GetDefault()->APIKey); + const FString QueryString = FormatQueryString(QueryData); + + // Query the server for information about the current product + FHttpModule &HttpModule = FHttpModule::Get(); + TSharedRef ProductRequest = HttpModule.CreateRequest(); + ProductRequest->SetVerb(TEXT("GET")); + ProductRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + ProductRequest->SetURL(FullURL + "/product/" + GetDefault()->ProductName + "?" + QueryString); + ProductRequest->OnProcessRequestComplete().BindUObject(this, &UBugSubmissionForm::ServerProductInfoResponse); + ProductRequest->ProcessRequest(); + + // Send a second query to retrieve severity options + TSharedRef SeverityRequest = HttpModule.CreateRequest(); + SeverityRequest->SetVerb(TEXT("GET")); + SeverityRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + SeverityRequest->SetURL(FullURL + "/field/bug/bug_severity" + "?" + QueryString); + SeverityRequest->OnProcessRequestComplete().BindUObject(this, &UBugSubmissionForm::ServerSeverityInfoResponse); + SeverityRequest->ProcessRequest(); + + // Send a third query to retrieve platform options + TSharedRef PlatformsRequest = HttpModule.CreateRequest(); + PlatformsRequest->SetVerb(TEXT("GET")); + PlatformsRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + PlatformsRequest->SetURL(FullURL + "/field/bug/rep_platform" + "?" + QueryString); + PlatformsRequest->OnProcessRequestComplete().BindUObject(this, &UBugSubmissionForm::ServerPlatformInfoResponse); + PlatformsRequest->ProcessRequest(); + + // Send a final query to retrieve OS options + TSharedRef OSRequest = HttpModule.CreateRequest(); + OSRequest->SetVerb(TEXT("GET")); + OSRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + OSRequest->SetURL(FullURL + "/field/bug/op_sys" + "?" + QueryString); + OSRequest->OnProcessRequestComplete().BindUObject(this, &UBugSubmissionForm::ServerOSInfoResponse); + OSRequest->ProcessRequest(); + this->SubmitButton->OnClicked().AddUObject(this, &UBugSubmissionForm::SubmitForm); this->CancelButton->OnClicked().AddUObject(this, &UBugSubmissionForm::CancelForm); } @@ -58,27 +119,75 @@ void UBugSubmissionForm::SetMarkerData(ABugMarkerActor *BugMarker) FFormatNamedArguments MapDataArgs; MapDataArgs.Add("MarkerLocation", MarkerLocationText); MapDataArgs.Add("MarkerUpVector", MarkerUpVectorText); - const FText MapDataText = FText::Format(NSLOCTEXT("BugSubmissionForm", "MapDataFormat", "{MarkerLocation}\n{MarkerUpVector}"), MapDataArgs); + const FText MapDataText = FText::Format(NSLOCTEXT("BugSubmissionForm", "MapDataFormat", "{MarkerLocation}:{MarkerUpVector}"), MapDataArgs); this->LocationValue->SetText(MapDataText); } +void UBugSubmissionForm::ShowProcessingOverlayLoading() +{ + this->ProcessingRequestErrorText->SetText(FText::FromString("")); + this->ProcessingRequestOverlay->SetVisibility(ESlateVisibility::Visible); + this->ProcessingRequestThrobber->SetVisibility(ESlateVisibility::Visible); + this->ProcessingRequestErrorBox->SetVisibility(ESlateVisibility::Collapsed); +} + +void UBugSubmissionForm::ShowProcessingOverlayMessage(const FString Message) +{ + this->ProcessingRequestErrorText->SetText(FText::FromString(Message)); + this->ProcessingRequestOverlay->SetVisibility(ESlateVisibility::Visible); + this->ProcessingRequestThrobber->SetVisibility(ESlateVisibility::Collapsed); + this->ProcessingRequestErrorBox->SetVisibility(ESlateVisibility::Visible); +} +void UBugSubmissionForm::HideProcessingOverlay() +{ + this->ProcessingRequestOverlay->SetVisibility(ESlateVisibility::Collapsed); +} + + void UBugSubmissionForm::SubmitForm() { - this->ProcessingRequestOverlay->SetVisibility(ESlateVisibility::Visible); + this->ShowProcessingOverlayLoading(); - const FString RESTURL = "/rest.cgi"; + const FString FullURL = GetDefault()->SubmissionServer + "/rest.cgi"; const FString RequestURL = "/bug"; - const FString QueryString = "api_key=" + GetDefault()->APIKey; + + // Assemble query data into key:value pairs + TMap QueryData; + QueryData.Add("api_key", GetDefault()->APIKey); + + const FString SummaryText = this->SummaryEntryBox->GetText().ToString(); + const FString CommentText = this->CommentEntryBox->GetText().ToString(); + + if (SummaryText.IsEmpty()) + { + this->ShowProcessingOverlayMessage("You must provide a summary."); + } + + //if (CommentText.IsEmpty()) + //{ + // this->ShowProcessingOverlayMessage("You must provide a description."); + //} + + FString PostJsonString; + FJSONPostBug PostData; + PostData.product = GetDefault()->ProductName; + PostData.component = "Component"; + PostData.version = "unspecified"; + PostData.cf_mapname = this->MapNameValue->GetText().ToString(); + PostData.cf_location = this->LocationValue->GetText().ToString(); + PostData.summary = SummaryText; + PostData.description = CommentText; + FJsonObjectConverter::UStructToJsonObjectString(PostData, PostJsonString); FHttpModule &HttpModule = FHttpModule::Get(); TSharedRef Request = HttpModule.CreateRequest(); - Request->SetVerb(TEXT("GET")); + Request->SetVerb(TEXT("POST")); Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); - Request->SetURL(GetDefault()->SubmissionServer + RESTURL + RequestURL - + "?" + QueryString); - Request->OnProcessRequestComplete().BindUObject(this, &UBugSubmissionForm::ServerResponse); + Request->SetURL(FullURL + RequestURL + "?" + FormatQueryString(QueryData)); + Request->SetContentAsString(PostJsonString); + Request->OnProcessRequestComplete().BindUObject(this, &UBugSubmissionForm::ServerPOSTResponse); Request->ProcessRequest(); this->OnFormSubmit.ExecuteIfBound(); @@ -101,34 +210,226 @@ void UBugSubmissionForm::CloseForm() } -void UBugSubmissionForm::ServerResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success) +void UBugSubmissionForm::ServerPOSTResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success) { if (Success) { - UE_LOG(LogTemp, Log, TEXT("%s"), *Response->GetContentAsString()); + FJSONPostResponse ResponseData; + FString JSONResponse = Response->GetContentAsString(); + FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData); - this->CloseForm(); + if (ResponseData.error) + { + this->ShowProcessingOverlayMessage(ResponseData.message); + } + else + { + this->CloseForm(); + } } else { - switch (Request->GetStatus()) { - case EHttpRequestStatus::Failed_ConnectionError: - UE_LOG(LogTemp, Error, TEXT("Unable to connect")); - break; - case EHttpRequestStatus::NotStarted: - UE_LOG(LogTemp, Error, TEXT("Connection not started")); - break; - case EHttpRequestStatus::Processing: - UE_LOG(LogTemp, Error, TEXT("Connection processing")); - break; - case EHttpRequestStatus::Failed: - UE_LOG(LogTemp, Error, TEXT("Connection failed")); - break; - case EHttpRequestStatus::Succeeded: - UE_LOG(LogTemp, Log, TEXT("Connection succeeded")); - break; - default: - UE_LOG(LogTemp, Error, TEXT("Request failed")); - } + this->ServerConnectionError(Request->GetStatus()); + } +} + +void UBugSubmissionForm::ServerProductInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success) +{ + if (Success) + { + FJSONProductResponse ResponseData; + FString JSONResponse = Response->GetContentAsString(); + FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData); + + if (ResponseData.error) + { + this->ShowProcessingOverlayMessage(ResponseData.message); + } + else + { + for (const FJSONProductData &ProductData : ResponseData.products) + { + if (ProductData.name == GetDefault()->ProductName) + { + for (const FJSONComponentData &ComponentData : ProductData.components) + { + this->ComponentList.Add(ComponentData.name); + } + for (const FJSONVersionData &VersionData : ProductData.versions) + { + this->VersionsList.Add(VersionData.name); + } + break; + } + } + + this->CheckIfAllInitialResponsesAreIn(); + } + } + else + { + this->ServerConnectionError(Request->GetStatus()); + } +} + +void UBugSubmissionForm::ServerSeverityInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success) +{ + if (Success) + { + FJSONFieldResponse ResponseData; + FString JSONResponse = Response->GetContentAsString(); + FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData); + + if (ResponseData.error) + { + this->ShowProcessingOverlayMessage(ResponseData.message); + } + else + { + if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "bug_severity") + { + for (const FJSONFieldValueData &FieldValue : ResponseData.fields[0].values) + { + this->SeverityList.Add(FieldValue.name); + } + } + + this->CheckIfAllInitialResponsesAreIn(); + } + } + else + { + this->ServerConnectionError(Request->GetStatus()); + } +} + +void UBugSubmissionForm::ServerPlatformInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success) +{ + if (Success) + { + FJSONFieldResponse ResponseData; + FString JSONResponse = Response->GetContentAsString(); + FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData); + + if (ResponseData.error) + { + this->ShowProcessingOverlayMessage(ResponseData.message); + } + else + { + if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "rep_platform") + { + for (const FJSONFieldValueData &FieldValue : ResponseData.fields[0].values) + { + this->PlatformsList.Add(FieldValue.name); + } + } + + this->CheckIfAllInitialResponsesAreIn(); + } + } + else + { + this->ServerConnectionError(Request->GetStatus()); + } +} + +void UBugSubmissionForm::ServerOSInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success) +{ + if (Success) + { + FJSONFieldResponse ResponseData; + FString JSONResponse = Response->GetContentAsString(); + FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData); + + if (ResponseData.error) + { + this->ShowProcessingOverlayMessage(ResponseData.message); + } + else + { + if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "op_sys") + { + for (const FJSONFieldValueData &FieldValue : ResponseData.fields[0].values) + { + this->OSList.Add(FieldValue.name); + } + } + + this->CheckIfAllInitialResponsesAreIn(); + } + } + else + { + this->ServerConnectionError(Request->GetStatus()); + } +} + +void UBugSubmissionForm::CheckIfAllInitialResponsesAreIn() +{ + if (!this->ComponentList.IsEmpty() && !this->VersionsList.IsEmpty() && !this->SeverityList.IsEmpty() && + !this->PlatformsList.IsEmpty() && !this->OSList.IsEmpty()) + { + if (this->PlatformsList.Contains("PC")) + { + if (UGameplayStatics::GetPlatformName() == "Windows" && this->OSList.Contains("Windows")) + { + this->ShowProcessingOverlayMessage("Windows detected"); + } + else if (UGameplayStatics::GetPlatformName() == "Linux" && this->OSList.Contains("Linux")) + { + this->ShowProcessingOverlayMessage("Linux on PC detected"); + } + else if (UGameplayStatics::GetPlatformName() == "Mac" && this->OSList.Contains("Mac OS")) + { + this->ShowProcessingOverlayMessage("This should not be possible"); + } + else + { + this->ShowProcessingOverlayMessage("Could not auto-detect operating system"); + } + } + else if (this->PlatformsList.Contains("Macintosh")) + { + if (UGameplayStatics::GetPlatformName() == "Windows" && this->OSList.Contains("Windows")) + { + this->ShowProcessingOverlayMessage("Windows on a Macintosh detected"); + } + else if (UGameplayStatics::GetPlatformName() == "Linux" && this->OSList.Contains("Linux")) + { + this->ShowProcessingOverlayMessage("Linux on a Macintosh detected"); + } + else if (UGameplayStatics::GetPlatformName() == "Mac" && this->OSList.Contains("Mac OS")) + { + this->ShowProcessingOverlayMessage("macOS detected"); + } + else + { + this->ShowProcessingOverlayMessage("Could not auto-detect operating system"); + } + } + else // At this stage, don't bother trying to auto-detect; we'll just set it to All here. + { + this->ShowProcessingOverlayMessage("Can't auto-detect platform"); + } + + //this->HideProcessingOverlay(); + } +} + +void UBugSubmissionForm::ServerConnectionError(const EHttpRequestStatus::Type Status) +{ + switch (Status) { + case EHttpRequestStatus::Failed_ConnectionError: + this->ShowProcessingOverlayMessage("Unable to connect to the server"); + break; + case EHttpRequestStatus::NotStarted: + this->ShowProcessingOverlayMessage("Connection could not start"); + break; + case EHttpRequestStatus::Failed: + this->ShowProcessingOverlayMessage("Connection failed"); + break; + default: + this->ShowProcessingOverlayMessage("Request failed for unknown reasons"); } } diff --git a/Source/Unrealzilla/Public/BugFormTextEntryButton.h b/Source/Unrealzilla/Public/BugFormTextEntryButton.h new file mode 100644 index 0000000..741c7c5 --- /dev/null +++ b/Source/Unrealzilla/Public/BugFormTextEntryButton.h @@ -0,0 +1,26 @@ +// ©2022 Batty Bovine Productions, LLC. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "CommonButtonBase.h" +#include "CommonTextBlock.h" + +#include "BugFormTextEntryButton.generated.h" + + +UCLASS() +class UNREALZILLA_API UBugFormTextEntryButton : public UCommonButtonBase +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable) + void SetText(const FText Text) { this->TextLabel->SetText(Text); } + UFUNCTION(BlueprintPure) + FText GetText() const { return this->TextLabel->GetText(); } + +protected: + UPROPERTY(BlueprintReadOnly, meta=(BindWidget)) + TObjectPtr TextLabel; +}; diff --git a/Source/Unrealzilla/Public/BugPlacerPawn.h b/Source/Unrealzilla/Public/BugPlacerPawn.h index 7061b36..ed3db1d 100644 --- a/Source/Unrealzilla/Public/BugPlacerPawn.h +++ b/Source/Unrealzilla/Public/BugPlacerPawn.h @@ -28,7 +28,7 @@ public: void Deactivate(); UFUNCTION(BlueprintCallable, meta=(WorldContext="WorldContextObject")) - static void SpawnBugPlacerPawn(const UObject *WorldContextObject, TSoftClassPtr Class); + static void SpawnBugPlacerPawn(const UObject *WorldContextObject, TSubclassOf Class); protected: UFUNCTION(BlueprintCallable) @@ -64,9 +64,9 @@ protected: TObjectPtr PawnMovement; UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) - TSoftClassPtr BugMarkerBlueprintClass; + TSubclassOf BugMarkerBlueprintClass; UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) - TSoftClassPtr ExitingBugPlacementScreenClass; + TSubclassOf ExitingBugPlacementScreenClass; UPROPERTY(BlueprintReadWrite) float SavedMaxSpeed = 0.0f; diff --git a/Source/Unrealzilla/Public/BugSubmissionForm.h b/Source/Unrealzilla/Public/BugSubmissionForm.h index b7e767c..f84b623 100644 --- a/Source/Unrealzilla/Public/BugSubmissionForm.h +++ b/Source/Unrealzilla/Public/BugSubmissionForm.h @@ -3,6 +3,8 @@ #pragma once #include "CoreMinimal.h" + +#include "BugFormTextEntryButton.h" #include "CommonActivatableWidget.h" #include "Interfaces/IHttpRequest.h" #include "Interfaces/IHttpResponse.h" @@ -10,6 +12,221 @@ #include "BugSubmissionForm.generated.h" +/** +* JSON structs for POST +*/ +USTRUCT() +struct FJSONPostBug +{ + GENERATED_BODY() +public: + UPROPERTY() + FString product; + UPROPERTY() + FString component; + UPROPERTY() + FString version; + UPROPERTY() + FString cf_mapname; + UPROPERTY() + FString cf_location; + UPROPERTY() + FString platform; + UPROPERTY() + FString op_sys; + UPROPERTY() + FString summary; + UPROPERTY() + FString description; +}; + +USTRUCT(Blueprintable) +struct FJSONPostResponse +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadOnly) + int32 id = -1; + UPROPERTY(BlueprintReadOnly) + bool error = false; + UPROPERTY(BlueprintReadOnly) + int32 code = -1; + UPROPERTY(BlueprintReadOnly) + FString message; + UPROPERTY(BlueprintReadOnly) + FString documentation; +}; +/** +* END JSON structs for POST +*/ + + +/** + * 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 product data + */ +USTRUCT(Blueprintable) +struct FJSONComponentData +{ + GENERATED_BODY() +public: + UPROPERTY() + int32 id = -1; + UPROPERTY() + bool is_active = false; + UPROPERTY() + FString name; + UPROPERTY() + FString description; + UPROPERTY() + FString default_assigned_to; + UPROPERTY() + int32 sort_key = -1; +}; + +USTRUCT(Blueprintable) +struct FJSONVersionData +{ + GENERATED_BODY() +public: + UPROPERTY() + int32 id = -1; + UPROPERTY() + bool is_active = false; + UPROPERTY() + FString name; + UPROPERTY() + int32 sort_key = -1; +}; + +USTRUCT(Blueprintable) +struct FJSONProductData +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadOnly) + FString name; + UPROPERTY(BlueprintReadOnly) + FString classification; + UPROPERTY(BlueprintReadOnly) + FString description; + UPROPERTY(BlueprintReadOnly) + TArray components; + UPROPERTY(BlueprintReadOnly) + TArray versions; +}; + +USTRUCT(Blueprintable) +struct FJSONProductResponse +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadOnly) + TArray products; + + UPROPERTY(BlueprintReadOnly) + bool error = false; + UPROPERTY(BlueprintReadOnly) + int32 code = -1; + UPROPERTY(BlueprintReadOnly) + FString message; + UPROPERTY(BlueprintReadOnly) + FString documentation; +}; +/** +* END JSON structs for product data +*/ + + +/** + * JSON structs for product data + */ +USTRUCT(Blueprintable) +struct FJSONFieldValueData +{ + GENERATED_BODY() +public: + UPROPERTY() + FString name; + UPROPERTY() + int32 sort_key = -1; +}; + +USTRUCT(Blueprintable) +struct FJSONFieldData +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadOnly) + int32 id = -1; + UPROPERTY(BlueprintReadOnly) + FString name; + UPROPERTY(BlueprintReadOnly) + FString display_name; + UPROPERTY(BlueprintReadOnly) + bool is_mandatory = false; + UPROPERTY(BlueprintReadOnly) + TArray values; +}; + +USTRUCT(Blueprintable) +struct FJSONFieldResponse +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadOnly) + TArray fields; + + UPROPERTY(BlueprintReadOnly) + bool error = false; + UPROPERTY(BlueprintReadOnly) + int32 code = -1; + UPROPERTY(BlueprintReadOnly) + FString message; + UPROPERTY(BlueprintReadOnly) + FString documentation; +}; +/** +* END JSON structs for product data +*/ + + UCLASS() class UNREALZILLA_API UBugSubmissionForm : public UCommonActivatableWidget { @@ -26,12 +243,19 @@ public: protected: virtual void NativeOnInitialized() override; + UFUNCTION(BlueprintImplementableEvent, meta=(DisplayName="Display POST Response")) + void DisplayPOSTResponse(const FJSONPostResponse &Response); + UPROPERTY(BlueprintReadOnly, meta=(BindWidget)) TObjectPtr ProductNameValue; UPROPERTY(BlueprintReadOnly, meta=(BindWidget)) TObjectPtr MapNameValue; UPROPERTY(BlueprintReadOnly, meta=(BindWidget)) TObjectPtr LocationValue; + UPROPERTY(BlueprintReadOnly, meta=(BindWidget)) + TObjectPtr SummaryEntryBox; + UPROPERTY(BlueprintReadOnly, meta=(BindWidget)) + TObjectPtr CommentEntryBox; UPROPERTY(BlueprintReadOnly, meta=(BindWidget)) TObjectPtr CancelButton; @@ -40,9 +264,27 @@ protected: UPROPERTY(BlueprintReadOnly, meta=(BindWidget)) TObjectPtr ProcessingRequestOverlay; + UPROPERTY(BlueprintReadOnly, meta=(BindWidget)) + TObjectPtr ProcessingRequestThrobber; + UPROPERTY(BlueprintReadOnly, meta=(BindWidget)) + TObjectPtr ProcessingRequestErrorBox; + UPROPERTY(BlueprintReadOnly, meta=(BindWidget)) + TObjectPtr ProcessingRequestErrorText; private: - void ServerResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success); + void ServerPOSTResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success); + void ServerProductInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success); + void ServerSeverityInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success); + void ServerPlatformInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success); + void ServerOSInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success); + void CheckIfAllInitialResponsesAreIn(); + + UFUNCTION(BlueprintCallable) + void ShowProcessingOverlayLoading(); + UFUNCTION(BlueprintCallable) + void ShowProcessingOverlayMessage(const FString Message); + UFUNCTION(BlueprintCallable) + void HideProcessingOverlay(); UFUNCTION(BlueprintCallable) void SubmitForm(); @@ -51,5 +293,13 @@ private: void CloseForm(); + void ServerConnectionError(const EHttpRequestStatus::Type Status); + + TArray ComponentList; + TArray VersionsList; + TArray SeverityList; + TArray PlatformsList; + TArray OSList; + TObjectPtr BugMarkerActor; }; diff --git a/Source/Unrealzilla/Public/UnrealzillaGlobalSettings.h b/Source/Unrealzilla/Public/UnrealzillaGlobalSettings.h index d73201b..0655fbc 100644 --- a/Source/Unrealzilla/Public/UnrealzillaGlobalSettings.h +++ b/Source/Unrealzilla/Public/UnrealzillaGlobalSettings.h @@ -17,12 +17,14 @@ class UNREALZILLA_API UUnrealzillaGlobalSettings : public UDeveloperSettingsBack public: UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly, Category="Bug Placement", meta=(DisplayName="Precise Placement Distance")) - float BugPlacementTraceDistance = 1000.0f; + float BugPlacementTraceDistance = 1500.0f; UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly, Category="Bug Placement", meta=(DisplayName="Arbitrary Placement Distance")) float ArbitraryBugPlacementDistance = 250.0f; UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting") FString SubmissionServer; + UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting") + FString ProductName; UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting", meta=(DisplayName="API Key")) FString APIKey; UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting") diff --git a/Source/Unrealzilla/Unrealzilla.Build.cs b/Source/Unrealzilla/Unrealzilla.Build.cs index 5b5f764..997d8b5 100644 --- a/Source/Unrealzilla/Unrealzilla.Build.cs +++ b/Source/Unrealzilla/Unrealzilla.Build.cs @@ -39,6 +39,8 @@ namespace UnrealBuildTool.Rules new string[] { "HTTP", + "Json", + "JsonUtilities", "UMG", "CommonUI" }