14 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
Norman Lansing
30c1e42379 Lesson 63 - Parsing the Payload 2026-03-16 06:46:34 -04:00
Norman Lansing
7f154aa6f9 Lesson 62 - Parsing the MetaData 2026-03-16 06:30:18 -04:00
Norman Lansing
8d6f3f68f1 Lesson 61 - Parsing an HTTP Response 2026-03-15 22:09:50 -04:00
Norman Lansing
3912fe52c6 Lesson 60 - Making an HTTP Request 2026-03-15 21:46:18 -04:00
Norman Lansing
d989960b41 Lesson 59 - Game Sessions API Data Asset 2026-03-15 17:31:56 -04:00
Norman Lansing
3407b94731 Lesson 58 - HTTP Request Manager 2026-03-15 17:02:22 -04:00
Norman Lansing
b26e9eb576 Lesson 57 - List Fleets Widget 2026-03-15 16:19:51 -04:00
Norman Lansing
898fba8153 Lession 56 - API Test HUD 2026-03-15 09:19:39 -04:00
Norman Lansing
f52a4225c3 Lesson 55 - #if #endif blocks added to DS_GameMode.cpp to prevent gamelift issues in editor mode 2026-03-15 08:58:52 -04:00
42 changed files with 677 additions and 4 deletions

View File

@@ -2,8 +2,8 @@
CommonUI.Debug.CheckGameViewportClientValid=0
[/Script/EngineSettings.GameMapsSettings]
GameDefaultMap=/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap
EditorStartupMap=/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap
GameDefaultMap=/Game/Maps/StartupMap.StartupMap
EditorStartupMap=/Game/Maps/StartupMap.StartupMap
GlobalDefaultGameMode=/Game/Blueprints/Game/BP_ShooterGameModeBase.BP_ShooterGameModeBase_C
ServerDefaultMap=/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap

Binary file not shown.

Binary file not shown.

View File

@@ -14,14 +14,19 @@ public class DedicatedServers : ModuleRules
{
"Core",
"CoreUObject",
"Engine"
"Engine",
"GameplayTags",
"HTTP"
});
PrivateDependencyModuleNames.AddRange(new string[]
{
"Slate",
"SlateCore",
"OpenSSL"
"OpenSSL",
"UMG",
"Json",
"JsonUtilities"
});
// Adds in the plugin for GameLiftServerSDK if it is the server build.

View File

@@ -4,3 +4,5 @@
#include "Modules/ModuleManager.h"
IMPLEMENT_MODULE( FDefaultModuleImpl, DedicateServers );
DEFINE_LOG_CATEGORY(LogDedicatedServers);

View File

@@ -3,3 +3,5 @@
#pragma once
#include "CoreMinimal.h"
DECLARE_LOG_CATEGORY_EXTERN(LogDedicatedServers, Log, All)

View File

@@ -0,0 +1,11 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Data/API/APIData.h"
FString UAPIData::GetAPIEndpoint(const FGameplayTag& APIEndpoint)
{
const FString ResourceName = Resources.FindChecked(APIEndpoint);
return InvokeUrl + "/" + Stage + "/" + ResourceName;
}

View File

@@ -18,6 +18,9 @@ void ADS_GameMode::BeginPlay()
void ADS_GameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
Super::InitGame(MapName, Options, ErrorMessage);
#if WITH_GAMELIFT
CachedCommandLine = FCommandLine::Get();
const FPortResult PortResult = cmdlineparser::details::GetConfiguredOrDefaultPort(CachedCommandLine, TEXT("port"));;
@@ -39,6 +42,8 @@ void ADS_GameMode::InitGame(const FString& MapName, const FString& Options, FStr
UE_LOGFMT(LogDS_GameMode, Log, "Fleet type: EC2");
// TODO: EC2 configuration
}
#endif
}
FString ADS_GameMode::GetSHA256Hash(const FString& InString)
@@ -320,6 +325,7 @@ void ADS_GameMode::LogAnywhereFleetParameters()
void ADS_GameMode::LogServerParameters(const FServerParameters& InServerParameters)
{
#if WITH_GAMELIFT
UE_LOGFMT(LogDS_GameMode, Log, ">>>> WebSocket URL: {WebSocketUrl}", InServerParameters.m_webSocketUrl);
UE_LOGFMT(LogDS_GameMode, Log, ">>>> Fleet ID: {FleetId}", InServerParameters.m_fleetId);
UE_LOGFMT(LogDS_GameMode, Log, ">>>> Process ID: {ProcessId}", InServerParameters.m_processId);
@@ -345,6 +351,7 @@ void ADS_GameMode::LogServerParameters(const FServerParameters& InServerParamete
{
UE_LOGFMT(LogDS_GameMode, Log, ">>>> Session Token: {SessionToken}", GetValueOrHash(InServerParameters.m_sessionToken));
}
#endif
}
FString ADS_GameMode::GetValueOrHash(const FString& Value)

View File

@@ -0,0 +1,9 @@
#include "GameplayTags/DedicatedServersTags.h"
namespace DedicatedServersTags
{
namespace GameSessionsAPI
{
UE_DEFINE_GAMEPLAY_TAG_COMMENT(ListFleets, "DedicatedServersTags.GameSessionsAPI.ListFleets", "List Fleets resource on the Game Session Resource")
}
}

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

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

View File

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

View File

@@ -0,0 +1,48 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "UI/API_Test/APITestManager.h"
#include "HttpModule.h"
#include "JsonObjectConverter.h"
#include "Data/API/APIData.h"
#include "DedicatedServers/DedicatedServers.h"
#include "GameplayTags/DedicatedServersTags.h"
#include "Interfaces/IHttpResponse.h"
#include "UI/HTTP/HTTPRequestTypes.h"
void UAPITestManager::ListFleets()
{
check(APIData);
TSharedRef<IHttpRequest> Request = FHttpModule::Get().CreateRequest();
Request->OnProcessRequestComplete().BindUObject(this, &UAPITestManager::ListFleets_Response);
const FString APIUrl = APIData->GetAPIEndpoint(DedicatedServersTags::GameSessionsAPI::ListFleets);
Request->SetURL(APIUrl);
Request->SetVerb(TEXT("GET"));
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
Request->ProcessRequest();
}
void UAPITestManager::ListFleets_Response(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, "List Fleets Response Received");
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
if (FJsonSerializer::Deserialize(JsonReader, JsonObject))
{
const TSharedPtr<FJsonObject>* ErrorObjPtr = nullptr;;
if (ContainsErrors(JsonObject, false))
{
OnListFleetsResponseReceived.Broadcast(FDS_ListFleetsResponse(), false);
return;
}
DumpMetaData(JsonObject);
FDS_ListFleetsResponse ListFleetsResponse;
FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), &ListFleetsResponse);
ListFleetsResponse.Dump();
OnListFleetsResponseReceived.Broadcast(ListFleetsResponse, true);
}
}

View File

@@ -0,0 +1,60 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "UI/API_Test/APITestOverlay.h"
#include "UI/API_Test/APITestManager.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/HTTP/HTTPRequestTypes.h"
void UAPITestOverlay::NativeConstruct()
{
Super::NativeConstruct();
check(APITestManagerClass);
APITestManager = NewObject<UAPITestManager>(this, APITestManagerClass);
check (ListFleetsBox)
check (ListFleetsBox->Button_ListFleets)
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

@@ -0,0 +1,17 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "UI/API_Test/API_TestHUD.h"
#include "UI/API_Test/APITestOverlay.h"
void AAPI_TestHUD::BeginPlay()
{
Super::BeginPlay();
APlayerController* PlayerController = GetOwningPlayerController();
if (IsValid(PlayerController) && APITestOverlayClass)
{
APITestOverlay = CreateWidget<UAPITestOverlay>(PlayerController, APITestOverlayClass);
APITestOverlay->AddToViewport();
}
}

View File

@@ -0,0 +1,45 @@
// Fill out your copyright notice in the Description page of Project Settings.
#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,24 @@
#include "UI/HTTP/HTTPRequestTypes.h"
#include "DedicatedServers/DedicatedServers.h"
void FDS_MetaData::Dump() const
{
UE_LOGFMT(LogDedicatedServers, Log, "MetaData:");
UE_LOGFMT(LogDedicatedServers, Log, "httpStatusCode: {httpStatusCode}", httpStatusCode);
UE_LOGFMT(LogDedicatedServers, Log, "requestId: {requestId}", requestId);
UE_LOGFMT(LogDedicatedServers, Log, "attemps: {attempts}", attempts);
UE_LOGFMT(LogDedicatedServers, Log, "totalRetryDelay: {totalRetryDelay}", totalRetryDelay);
}
void FDS_ListFleetsResponse::Dump() const
{
UE_LOGFMT(LogDedicatedServers, Log, "ListFleetsResponse:");
for (const FString& FleetId : FleetIds)
{
UE_LOGFMT(LogDedicatedServers, Log, "FleetId: {FleetId}", FleetId);
}
if (!NextToken.IsEmpty())
{
UE_LOGFMT(LogDedicatedServers, Log, "NextToken: {NextToken}", NextToken);
}
}

View File

@@ -0,0 +1,43 @@
#pragma once
// Because this is in the private section is is still usable by the DedicatedServer Module, but not outside of the module.
#include "HTTPRequestTypes.generated.h"
USTRUCT()
struct FDS_MetaData
{
GENERATED_BODY()
public:
UPROPERTY()
int32 httpStatusCode{};
UPROPERTY()
FString requestId{};
UPROPERTY()
int32 attempts{};
UPROPERTY()
double totalRetryDelay{};
void Dump() const;
};
USTRUCT()
struct FDS_ListFleetsResponse
{
GENERATED_BODY()
public:
UPROPERTY()
TArray<FString> FleetIds{};
UPROPERTY()
FString NextToken{};
void Dump() const;
};

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,38 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "GameplayTags/DedicatedServersTags.h"
#include "APIData.generated.h"
/**
*
*/
UCLASS()
class DEDICATEDSERVERS_API UAPIData : public UDataAsset
{
GENERATED_BODY()
public:
FString GetAPIEndpoint(const FGameplayTag& APIEndpoint);
protected:
// Name of the API - for labeling in the Data Asset; this is not used by any code.
UPROPERTY(EditAnywhere)
FString Name;
UPROPERTY(EditDefaultsOnly)
FString InvokeUrl;
UPROPERTY(EditDefaultsOnly)
FString Stage;
UPROPERTY(EditDefaultsOnly)
TMap<FGameplayTag, FString> Resources;
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include "CoreMinimal.h"
#include "NativeGameplayTags.h"
namespace DedicatedServersTags
{
namespace GameSessionsAPI
{
UE_DECLARE_GAMEPLAY_TAG_EXTERN(ListFleets)
}
}

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

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

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 "ListFleetsBox.generated.h"
class UButton;
class UScrollBox;
/**
*
*/
UCLASS()
class DEDICATEDSERVERS_API UListFleetsBox : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY(meta = (BindWidget))
TObjectPtr<UScrollBox> ScrollBox_ListFleets;
UPROPERTY(meta = (BindWidget))
TObjectPtr<UButton> Button_ListFleets;
};

View File

@@ -0,0 +1,29 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Interfaces/IHttpRequest.h"
#include "UI/HTTP/HTTPRequestManager.h"
#include "APITestManager.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListFleetsResponseReceived, const FDS_ListFleetsResponse&, ListFleetResponse, bool, bWasSuccessful);
/**
*
*/
UCLASS()
class DEDICATEDSERVERS_API UAPITestManager : public UHTTPRequestManager
{
GENERATED_BODY()
public:
UFUNCTION()
void ListFleets();
UPROPERTY()
FOnListFleetsResponseReceived OnListFleetsResponseReceived;
void ListFleets_Response(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
};

View File

@@ -0,0 +1,50 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "APITestOverlay.generated.h"
class UAPITestManager;
class UListFleetsBox;
struct FDS_ListFleetsResponse;
class UFleetId;
/**
*
*/
UCLASS()
class DEDICATEDSERVERS_API UAPITestOverlay : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAPITestManager> APITestManagerClass;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UFleetId> FleetIdWidgetClass;
protected:
virtual void NativeConstruct() override;
private:
UPROPERTY(meta = (BindWidget))
TObjectPtr<UListFleetsBox> ListFleetsBox;
UPROPERTY()
TObjectPtr<UAPITestManager> APITestManager;
UFUNCTION()
void ListFleetsButtonClicked();
UFUNCTION()
void OnListFleetsResponseReceived(const FDS_ListFleetsResponse& ListFleetResponse, bool bWasSuccessful);
};

View File

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

View File

@@ -0,0 +1,28 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "HTTPRequestManager.generated.h"
class UAPIData;
class FJsonObject;
/**
*
*/
UCLASS(Blueprintable)
class DEDICATEDSERVERS_API UHTTPRequestManager : public UObject
{
GENERATED_BODY()
protected:
UPROPERTY(EditDefaultsOnly)
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;
};