5 Commits

Author SHA1 Message Date
Norman Lansing
9fb60888ba Lesson 67 - Portal Setup 2026-03-22 17:18:22 -04:00
Norman Lansing
acc105c84d WBP_ListFleetsBox to make it larger, and to allow sizing changes 2026-03-18 06:58:22 -04:00
Norman Lansing
d9eb52c047 Lesson 66 - Displaying Fleet Data 2026-03-17 07:16:52 -04:00
Norman Lansing
e95360489b Lesson 65 - ListFleets Callback 2026-03-16 22:30:26 -04:00
Norman Lansing
6f3e1d1f86 Lesson 65 - Parsing Errors 2026-03-16 18:12:29 -04:00
23 changed files with 270 additions and 17 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "UI/API/GameSessions/JoinGame.h"

View File

@@ -5,26 +5,21 @@
#include "HttpModule.h" #include "HttpModule.h"
#include "JsonObjectConverter.h" #include "JsonObjectConverter.h"
#include "Data/API/APIData.h" #include "Data/API/APIData.h"
#include "DedicatedServers/DedicatedServers.h"
#include "GameplayTags/DedicatedServersTags.h" #include "GameplayTags/DedicatedServersTags.h"
#include "Interfaces/IHttpResponse.h" #include "Interfaces/IHttpResponse.h"
#include "UI/HTTP/HTTPRequestTypes.h" #include "UI/HTTP/HTTPRequestTypes.h"
void UAPITestManager::ListFleetsButtonClicked() void UAPITestManager::ListFleets()
{ {
check(APIData); check(APIData);
TSharedRef<IHttpRequest> Request = FHttpModule::Get().CreateRequest(); TSharedRef<IHttpRequest> Request = FHttpModule::Get().CreateRequest();
Request->OnProcessRequestComplete().BindUObject(this, &UAPITestManager::ListFleets_Response); Request->OnProcessRequestComplete().BindUObject(this, &UAPITestManager::ListFleets_Response);
const FString APIUrl = APIData->GetAPIEndpoint(DedicatedServersTags::GameSessionsAPI::ListFleets); const FString APIUrl = APIData->GetAPIEndpoint(DedicatedServersTags::GameSessionsAPI::ListFleets);
Request->SetURL(APIUrl); Request->SetURL(APIUrl);
Request->SetVerb(TEXT("GET")); Request->SetVerb(TEXT("GET"));
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
Request->ProcessRequest(); Request->ProcessRequest();
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "List Fleets Request Made");
} }
void UAPITestManager::ListFleets_Response(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) void UAPITestManager::ListFleets_Response(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
@@ -33,18 +28,21 @@ void UAPITestManager::ListFleets_Response(FHttpRequestPtr Request, FHttpResponse
TSharedPtr<FJsonObject> JsonObject; TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(Response->GetContentAsString()); TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
if (FJsonSerializer::Deserialize(JsonReader, JsonObject)) if (FJsonSerializer::Deserialize(JsonReader, JsonObject))
{ {
if (JsonObject->HasField(TEXT("$metadata"))) const TSharedPtr<FJsonObject>* ErrorObjPtr = nullptr;;
if (ContainsErrors(JsonObject, false))
{ {
TSharedPtr<FJsonObject> MetaDataJsonObject = JsonObject->GetObjectField(TEXT("$metadata")); OnListFleetsResponseReceived.Broadcast(FDS_ListFleetsResponse(), false);
FDS_MetaData DSMetaData; return;
FJsonObjectConverter::JsonObjectToUStruct(MetaDataJsonObject.ToSharedRef(), &DSMetaData);
DSMetaData.Dump();
} }
DumpMetaData(JsonObject);
FDS_ListFleetsResponse ListFleetsResponse; FDS_ListFleetsResponse ListFleetsResponse;
FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), &ListFleetsResponse); FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), &ListFleetsResponse);
ListFleetsResponse.Dump(); ListFleetsResponse.Dump();
OnListFleetsResponseReceived.Broadcast(ListFleetsResponse, true);
} }
} }

View File

@@ -5,7 +5,11 @@
#include "Components/Button.h" #include "Components/Button.h"
#include "Components/ScrollBox.h"
#include "Components/TextBlock.h"
#include "UI/API/ListFleets/FleetId.h"
#include "UI/API/ListFleets/ListFleetsBox.h" #include "UI/API/ListFleets/ListFleetsBox.h"
#include "UI/HTTP/HTTPRequestTypes.h"
void UAPITestOverlay::NativeConstruct() void UAPITestOverlay::NativeConstruct()
@@ -17,5 +21,40 @@ void UAPITestOverlay::NativeConstruct()
check (ListFleetsBox) check (ListFleetsBox)
check (ListFleetsBox->Button_ListFleets) check (ListFleetsBox->Button_ListFleets)
ListFleetsBox->Button_ListFleets->OnClicked.AddDynamic(APITestManager, &UAPITestManager::ListFleetsButtonClicked); ListFleetsBox->Button_ListFleets->OnClicked.AddDynamic(this, &UAPITestOverlay::ListFleetsButtonClicked);
}
void UAPITestOverlay::ListFleetsButtonClicked()
{
check(APITestManager);
APITestManager->OnListFleetsResponseReceived.AddDynamic(this, &UAPITestOverlay::OnListFleetsResponseReceived);
APITestManager->ListFleets();
ListFleetsBox->Button_ListFleets->SetIsEnabled(false);
}
void UAPITestOverlay::OnListFleetsResponseReceived(const FDS_ListFleetsResponse& ListFleetResponse, bool bWasSuccessful)
{
if (APITestManager->OnListFleetsResponseReceived.IsAlreadyBound(this, &UAPITestOverlay::OnListFleetsResponseReceived))
{
APITestManager->OnListFleetsResponseReceived.RemoveDynamic(this, &UAPITestOverlay::OnListFleetsResponseReceived);
}
check(FleetIdWidgetClass);
ListFleetsBox->ScrollBox_ListFleets->ClearChildren();
if (bWasSuccessful)
{
for (const FString& FleetId : ListFleetResponse.FleetIds)
{
UFleetId* FleetIdWidget = CreateWidget<UFleetId>(this, FleetIdWidgetClass);
FleetIdWidget->TextBlock_FleetId->SetText(FText::FromString(FleetId));
ListFleetsBox->ScrollBox_ListFleets->AddChild(FleetIdWidget);
}
}
else
{
UFleetId* FleetIdWidget = CreateWidget<UFleetId>(this, FleetIdWidgetClass);
FleetIdWidget->TextBlock_FleetId->SetText(FText::FromString("Something went wrong!"));
ListFleetsBox->ScrollBox_ListFleets->AddChild(FleetIdWidget);
}
ListFleetsBox->Button_ListFleets->SetIsEnabled(true);
} }

View File

@@ -2,3 +2,44 @@
#include "UI/HTTP/HTTPRequestManager.h" #include "UI/HTTP/HTTPRequestManager.h"
#include "HTTPRequestTypes.h"
#include "JsonObjectConverter.h"
#include "DedicatedServers/DedicatedServers.h"
bool UHTTPRequestManager::ContainsErrors(TSharedPtr<FJsonObject> JsonObject, bool bSuppressLog)
{
if (JsonObject->HasField(TEXT("errorType")) || JsonObject->HasField(TEXT("errorMessage")))
{
if (!bSuppressLog)
{
FString ErrorType = JsonObject->HasField(TEXT("errorType")) ? JsonObject->GetStringField(TEXT("errorType")) : TEXT("Unknown Error");
FString ErrorMessage = JsonObject->HasField(TEXT("errorMessage")) ? JsonObject->GetStringField(TEXT("errorMessage")) : TEXT("Unknown Error Message");
UE_LOGFMT(LogDedicatedServers, Error, "Error Type: {ErrorType}", ErrorType);
UE_LOGFMT(LogDedicatedServers, Error, "Error Message: {ErrorMessage}", ErrorMessage);
}
return true;
}
if (JsonObject->HasField(TEXT("$fault")))
{
if (!bSuppressLog)
{
FString ErrorType = JsonObject->HasField(TEXT("name")) ? JsonObject->GetStringField(TEXT("name")) : TEXT("Unknown Error");
UE_LOGFMT(LogDedicatedServers, Error, "Error Type: {ErrorType}", ErrorType);
}
return true;
}
return false;
}
void UHTTPRequestManager::DumpMetaData(TSharedPtr<FJsonObject> JsonObject)
{
if (JsonObject->HasField(TEXT("$metadata")))
{
TSharedPtr<FJsonObject> MetaDataJsonObject = JsonObject->GetObjectField(TEXT("$metadata"));
FDS_MetaData DSMetaData;
FJsonObjectConverter::JsonObjectToUStruct(MetaDataJsonObject.ToSharedRef(), &DSMetaData);
DSMetaData.Dump();
}
}

View File

@@ -0,0 +1,23 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "UI/Portal/PortalHUD.h"
#include "UI/Portal/SignIn/SignInOverlay.h"
#include "Blueprint/UserWidget.h"
void APortalHUD::BeginPlay()
{
Super::BeginPlay();
APlayerController* OwningPlayerController = GetOwningPlayerController();
SignInOverlay = CreateWidget<USignInOverlay>(OwningPlayerController, SignInOverlayClass);
if (IsValid(SignInOverlay))
{
SignInOverlay->AddToViewport();
}
const FInputModeGameAndUI InputModeData;
OwningPlayerController->SetInputMode(InputModeData);
OwningPlayerController->SetShowMouseCursor(true);
}

View File

@@ -0,0 +1,4 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "UI/Portal/PortalManager.h"

View File

@@ -0,0 +1,14 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "UI/Portal/SignIn/SignInOverlay.h"
#include "UI/Portal/PortalManager.h"
void USignInOverlay::NativeConstruct()
{
Super::NativeConstruct();
check(PortalManagerClass);
PortalManager = NewObject<UPortalManager>(PortalManagerClass);
}

View File

@@ -0,0 +1,27 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "JoinGame.generated.h"
class UButton;
class UTextBlock;
/**
*
*/
UCLASS()
class DEDICATEDSERVERS_API UJoinGame : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY(meta = (BindWidget))
TObjectPtr<UButton> Button_JoinGame;
UPROPERTY(meta = (BindWidget))
TObjectPtr<UTextBlock> TextBlock_StatusMessage;
};

View File

@@ -18,6 +18,6 @@ class DEDICATEDSERVERS_API UFleetId : public UUserWidget
public: public:
UPROPERTY(meta = (BindWidget)) UPROPERTY(meta = (BindWidget), BlueprintReadWrite)
TObjectPtr<UTextBlock> TextBlock_FleetId; TObjectPtr<UTextBlock> TextBlock_FleetId;
}; };

View File

@@ -7,6 +7,8 @@
#include "UI/HTTP/HTTPRequestManager.h" #include "UI/HTTP/HTTPRequestManager.h"
#include "APITestManager.generated.h" #include "APITestManager.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListFleetsResponseReceived, const FDS_ListFleetsResponse&, ListFleetResponse, bool, bWasSuccessful);
/** /**
* *
*/ */
@@ -18,7 +20,10 @@ class DEDICATEDSERVERS_API UAPITestManager : public UHTTPRequestManager
public: public:
UFUNCTION() UFUNCTION()
void ListFleetsButtonClicked(); void ListFleets();
UPROPERTY()
FOnListFleetsResponseReceived OnListFleetsResponseReceived;
void ListFleets_Response(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); void ListFleets_Response(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
}; };

View File

@@ -8,6 +8,9 @@
class UAPITestManager; class UAPITestManager;
class UListFleetsBox; class UListFleetsBox;
struct FDS_ListFleetsResponse;
class UFleetId;
/** /**
* *
*/ */
@@ -21,6 +24,9 @@ public:
UPROPERTY(EditDefaultsOnly) UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAPITestManager> APITestManagerClass; TSubclassOf<UAPITestManager> APITestManagerClass;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UFleetId> FleetIdWidgetClass;
protected: protected:
virtual void NativeConstruct() override; virtual void NativeConstruct() override;
@@ -32,6 +38,11 @@ private:
UPROPERTY() UPROPERTY()
TObjectPtr<UAPITestManager> APITestManager; TObjectPtr<UAPITestManager> APITestManager;
UFUNCTION()
void ListFleetsButtonClicked();
UFUNCTION()
void OnListFleetsResponseReceived(const FDS_ListFleetsResponse& ListFleetResponse, bool bWasSuccessful);

View File

@@ -7,6 +7,7 @@
#include "HTTPRequestManager.generated.h" #include "HTTPRequestManager.generated.h"
class UAPIData; class UAPIData;
class FJsonObject;
/** /**
* *
@@ -21,5 +22,7 @@ protected:
UPROPERTY(EditDefaultsOnly) UPROPERTY(EditDefaultsOnly)
TObjectPtr<UAPIData> APIData; TObjectPtr<UAPIData> APIData;
bool ContainsErrors(TSharedPtr<FJsonObject> JsonObject, bool bSuppressLog = true);
void DumpMetaData(TSharedPtr<FJsonObject> JsonObject);
}; };

View File

@@ -0,0 +1,32 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "PortalHUD.generated.h"
class USignInOverlay;
/**
*
*/
UCLASS()
class DEDICATEDSERVERS_API APortalHUD : public AHUD
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly)
TSubclassOf<USignInOverlay> SignInOverlayClass;
protected:
virtual void BeginPlay() override;
private:
UPROPERTY()
TObjectPtr<USignInOverlay> SignInOverlay;
};

View File

@@ -0,0 +1,16 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UI/HTTP/HTTPRequestManager.h"
#include "PortalManager.generated.h"
/**
*
*/
UCLASS()
class DEDICATEDSERVERS_API UPortalManager : public UHTTPRequestManager
{
GENERATED_BODY()
};

View File

@@ -0,0 +1,36 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "SignInOverlay.generated.h"
class UJoinGame;
class UPortalManager;
/**
*
*/
UCLASS()
class DEDICATEDSERVERS_API USignInOverlay : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY(meta = (BindWidget))
TObjectPtr<UJoinGame> JoinGameWidget;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UPortalManager> PortalManagerClass;
protected:
virtual void NativeConstruct() override;
private:
UPROPERTY()
TObjectPtr<UPortalManager> PortalManager;
};