diff --git a/Source/FPSTemplate/Private/Game/ShooterGameMode.cpp b/Source/FPSTemplate/Private/Game/ShooterGameMode.cpp index ba451349..7bb66d76 100644 --- a/Source/FPSTemplate/Private/Game/ShooterGameMode.cpp +++ b/Source/FPSTemplate/Private/Game/ShooterGameMode.cpp @@ -3,18 +3,11 @@ #include "Game/ShooterGameMode.h" #include "Logging/LogMacros.h" -#include "DSP/BufferDiagnostics.h" -#include "UObject/ConstructorHelpers.h" -#include "Kismet/GameplayStatics.h" #if WITH_GAMELIFT -#include "GameLiftServerSDKModels.h" +#include "GameLiftServerSDK.h" #endif -#include "Chaos/ImplicitQRSVD.h" -#include "GenericPlatform/GenericPlatformOutputDevices.h" -#include "Misc/TypeContainer.h" - DEFINE_LOG_CATEGORY(LogShooterGameMode) AShooterGameMode::AShooterGameMode() @@ -25,334 +18,135 @@ AShooterGameMode::AShooterGameMode() void AShooterGameMode::BeginPlay() { Super::BeginPlay(); + #if WITH_GAMELIFT - InitGameLift(); +// InitGameLift(); #endif } -#if WITH_GAMELIFT - -// ReSharper disable once CppMemberFunctionMayBeConst - cannot be const due to FCommandLine::Get() and UE_LOG with non-const log category -bool AShooterGameMode::SetParametersFromCommandLine(FServerParameters& OutServerParameters, - FProcessParameters& OutProcessParameters) +void AShooterGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) { - bool bCriticalFault = false; - - FString CommandLine = FCommandLine::Get(); - UE_LOG(LogShooterGameMode, Log, TEXT(">>> Command Line: %s"), *CommandLine); + Super::InitGame(MapName, Options, ErrorMessage); + CachedCommandLine = FCommandLine::Get(); + bool bIsCriticalError = false; - // Checks if param exists (returns bool) + // Parsing of Command Line + bDebugMode = FParse::Param(*CachedCommandLine, TEXT("Debug")); - // AuthToken returned from the "aws gamelift get-compute-auth-token" API. Note this will expire and require a new call to the API after 15 minutes. - bCriticalFault |= ValidateServerParameters(CommandLine, TEXT("authtoken"),OutServerParameters.m_authToken); - - // The Host/compute-name of the GameLift Anywhere instance - bCriticalFault |= ValidateServerParameters(CommandLine, TEXT("hostid"),OutServerParameters.m_hostId); - - // The AnywhereFleet ID. - bCriticalFault |= ValidateServerParameters(CommandLine, TEXT("fleetid"),OutServerParameters.m_fleetId); - - // The WebSocket URL (GameLiftServiceSdkEndpoint) - bCriticalFault |= ValidateServerParameters(CommandLine, TEXT("websocketurl"),OutServerParameters.m_webSocketUrl); - - // The port being used, uses UE system default if not entered as a switch - OutProcessParameters.port = FURL::UrlConfig.DefaultPort; - FParse::Value(*CommandLine, TEXT("port="), OutProcessParameters.port); - -// LogServerSummary(OutServerParameters,OutProcessParameters); - - return bCriticalFault; -} - -void AShooterGameMode::LogServerParameters(const FServerParameters& InServerParameters) -{ - // Log Server Parameters - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_YELLOW); - UE_LOG(LogShooterGameMode, Log, TEXT(">>>> WebSocket URL: %s"), *InServerParameters.m_webSocketUrl); - UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Fleet ID: %s"), *InServerParameters.m_fleetId); - UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Process ID: %s"), *InServerParameters.m_processId); - UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Host ID (Compute Name): %s"), *InServerParameters.m_hostId); - UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Auth Token: %s"), *InServerParameters.m_authToken); - // UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Aws Region: %s"), *ServerParameters.m_awsRegion); - // UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Access Key: %s"), *ServerParameters.m_accessKey); - // UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Secret Key: %s"), *ServerParameters.m_secretKey); - // UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Session Token: %s"), *ServerParameters.m_sessionToken); - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE); -} - -void AShooterGameMode::SetProcessIdOnServerParameters(FServerParameters& OutServerParameters) -{ - // The PID of the running process -#if PLATFORM_WINDOWS - OutServerParameters.m_processId = FString::Printf(TEXT("%d"), GetCurrentProcessId()); -#else - OutServerParameters.m_processId = FString::Printf(TEXT("%d"), FGenericPlatformProcess::GetCurrentProcessId()); -#endif -} - -bool AShooterGameMode::ValidateServerParameters(const FString& CommandLine, const FString& ParameterName, - FString& OutValue, const bool bCritical) -{ - if (!FParse::Value(*CommandLine, *FString::Printf(TEXT("%s="), *ParameterName), OutValue)) + if (bDebugMode) { - UE_LOG(LogShooterGameMode, Warning, TEXT("-%s not found"), *ParameterName); - return bCritical; - } - return false; -} - -void AShooterGameMode::LogServerSummary(const FServerParameters& InServerParameters, - const FProcessParameters& InProcessParameters) -{ - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_YELLOW); - UE_LOG(LogShooterGameMode, Log, TEXT("=== GAMELIFT SERVER STARTUP SUMMARY ===")); - UE_LOG(LogShooterGameMode, Log, TEXT("Port: %d"), InProcessParameters.port); - - // Inline validation of ServerParams - bool bAllValid = !InServerParameters.m_authToken.IsEmpty() && - !InServerParameters.m_hostId.IsEmpty() && - !InServerParameters.m_fleetId.IsEmpty() && - !InServerParameters.m_webSocketUrl.IsEmpty(); - - UE_LOG(LogShooterGameMode, Log, TEXT("All Anywhere params: %s"), - bAllValid ? TEXT("VALID ✓") : TEXT("MISSING ❌")); - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE); -} - -void AShooterGameMode::LogGameLiftServerSummary(bool bIsAnywhere, const FServerParameters& InServerParameters, - const FProcessParameters& InProcessParameters) -{ - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_YELLOW); - UE_LOG(LogShooterGameMode, Log, TEXT("=== GAMELIFT SERVER STARTUP SUMMARY ===")); - - if (bIsAnywhere) - { - // Anywhere only Logging - if (!InServerParameters.m_fleetId.IsEmpty()) - { - UE_LOG(LogShooterGameMode, Log, TEXT("FleetId: %s"), *InServerParameters.m_fleetId); - } - - if (!InServerParameters.m_webSocketUrl.IsEmpty()) - { - UE_LOG(LogShooterGameMode, Log, TEXT("WebSocketUrl: %s"), *InServerParameters.m_webSocketUrl); - } - - if (!InServerParameters.m_hostId.IsEmpty()) - { - UE_LOG(LogShooterGameMode, Log, TEXT("HostId: %s"), *InServerParameters.m_hostId); - } - - - #ifdef UE_BUILD_DEBUG - const FString TokenStatus = FString::Printf(TEXT("FULL: %s"), *InServerParameters.m_authToken); - #else - const FString TokenStatus = InServerParameters.m_authToken.IsEmpty() ? TEXT("MISSING ❌") : FString::Printf(TEXT("LEN: %d"),InServerParameters.m_authToken.Len()); - #endif - - UE_LOG(LogShooterGameMode, Log, TEXT("AuthToken: %s"), *TokenStatus); - - - // Inline validation of ServerParams - const bool bAllValid = !InServerParameters.m_authToken.IsEmpty() && - !InServerParameters.m_hostId.IsEmpty() && - !InServerParameters.m_fleetId.IsEmpty() && - !InServerParameters.m_webSocketUrl.IsEmpty(); - - UE_LOG(LogShooterGameMode, Log, TEXT("All Anywhere params: %s"), bAllValid ? TEXT("VALID ✓") : TEXT("MISSING ❌")); - + UE_LOG(LogShooterGameMode, Log, TEXT("Debug mode: ENABLED")); + UE_LOG(LogShooterGameMode, Log, TEXT("Command Line Arguments: %s"), *CachedCommandLine); } - UE_LOG(LogShooterGameMode, Log, TEXT("Port: %d"), InProcessParameters.port); - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE); -} - -void AShooterGameMode::BindGameLiftCallbackFunctions(FProcessParameters& ProcessParameters, FGameLiftServerSDKModule& GameLiftSdkModule) -{ - // Implement callback function onStartGameSession - // GameLift sends a game session activation request to the game server - // and passes a game session object with game properties and other settings. - // Here is where a game server takes action based on the game session object. - // When the game server is ready to receive incoming player connections, - // it invokes the server SDK call ActivateGameSession(). - - - ProcessParameters.OnStartGameSession.BindLambda([this, &GameLiftSdkModule](Aws::GameLift::Server::Model::GameSession InGameSession) + ServerPort = GetConfiguredOrDefaultPort(); + + bIsAnywhereFleet = FParse::Param(*CachedCommandLine, TEXT("glAnywhere")); + if (bIsAnywhereFleet) { - FString GameSessionId = FString(InGameSession.GetGameSessionId()); - UE_LOG(LogShooterGameMode, Log, TEXT("GameSession Initializing: %s"), *GameSessionId); - FGameLiftGenericOutcome ActivateGameSessionOutcome = GameLiftSdkModule.ActivateGameSession(); - if (ActivateGameSessionOutcome.IsSuccess()) + UE_LOG(LogShooterGameMode, Log, TEXT("GameLift Anywhere Fleet Command Line Parsing")); + UE_LOG(LogShooterGameMode, Log, TEXT("======Command Line Parameters======")); + + if (!FParse::Value(*CachedCommandLine, TEXT("fleetID="), FleetId)) { - UE_LOG(LogShooterGameMode, Log, TEXT("Activate GameSession successful")); + UE_LOG(LogShooterGameMode, Error, TEXT(">>>FleetId Missing in Command Line")); + bIsCriticalError = true; } else { - const FGameLiftError& Error = ActivateGameSessionOutcome.GetError(); - UE_LOG(LogShooterGameMode, Error, TEXT("ActivateGameSession() failed. Error: %s"), Error.m_errorMessage.IsEmpty() ? TEXT("Unknown Error") : *Error.m_errorMessage); - FPlatformMisc::RequestExit(true); + UE_LOG(LogShooterGameMode, Log, TEXT(">>>Fleet ID: %s"), *FleetId); } - }); - - ProcessParameters.OnTerminate.BindLambda([this, &GameLiftSdkModule]() - { - FGameLiftGenericOutcome ProcessEndingOutcome = GameLiftSdkModule.ProcessEnding(); - FGameLiftGenericOutcome DestroyOutcome = GameLiftSdkModule.Destroy(); - if (ProcessEndingOutcome.IsSuccess() && DestroyOutcome.IsSuccess()) + + if (!FParse::Value(*CachedCommandLine, TEXT("authtoken="), AuthToken)) { - UE_LOG(LogShooterGameMode, Log, TEXT("Server process ending successfully")); - FPlatformMisc::RequestExit(false); + UE_LOG(LogShooterGameMode, Error, TEXT(">>>AuthToken Missing in Command Line")); + bIsCriticalError = true; } else { - if (!ProcessEndingOutcome.IsSuccess()) + if (bDebugMode) { - const FGameLiftError& Error = ProcessEndingOutcome.GetError(); - UE_LOG(LogShooterGameMode, Error, TEXT("ProcessEnding() failed. Error: %s"), Error.m_errorMessage.IsEmpty() ? TEXT("Unknown Error") : *Error.m_errorMessage); + UE_LOG(LogShooterGameMode, Log, TEXT(">>>AuthToken: %s"), *AuthToken); } - if (!DestroyOutcome.IsSuccess()) + else { - const FGameLiftError& Error = DestroyOutcome.GetError(); - UE_LOG(LogShooterGameMode, Error, TEXT("Destroy() failed. Error: %s"), Error.m_errorMessage.IsEmpty() ? TEXT("Unknown error") : *Error.m_errorMessage); + UE_LOG(LogShooterGameMode, Log, TEXT(">>>AuthToken Length: %d"), AuthToken.Len()); } - FPlatformMisc::RequestExit(true); } - }); - - ProcessParameters.OnHealthCheck.BindLambda([=]() - { - return true; - }); - - // Here, the game server tells GameLift where to find game session Log files. - // At the end of a game session, GameLift uploads everything in the specified - // location and stores it in the cloud for access later. - - TArray LogFiles; - FString LogDirectory =FPaths::ProjectSavedDir() / TEXT("Logs"); - FString LogFile = LogDirectory / TEXT("GameLiftServer.log"); - LogFiles.Add(LogFile); - - UE_LOG(LogShooterGameMode, Log, TEXT("Before Adding Log Files")); - ProcessParameters.logParameters = LogFiles; -} - -void AShooterGameMode::InitiateProcessReady(FProcessParameters& ProcessParameters, FGameLiftServerSDKModule& GameLiftSdkModule) -{ - // The game server calls ProcessReady() to tell Amazon GameLift Servers it's ready to host game sessions. - UE_LOG(LogShooterGameMode, Log, TEXT("Calling Process Ready...")); - FGameLiftGenericOutcome ProcessReadyOutcome = GameLiftSdkModule.ProcessReady(ProcessParameters); - - if (ProcessReadyOutcome.IsSuccess()) - { - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_GREEN); - UE_LOG(LogShooterGameMode, Log, TEXT("Process Ready!")); - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE); + if (!FParse::Value(*CachedCommandLine, TEXT("hostId="), HostId)) + { + UE_LOG(LogShooterGameMode, Error, TEXT(">>>HostId Missing in Command Line")); + bIsCriticalError = true; + } + { + UE_LOG(LogShooterGameMode, Log, TEXT(">>>Host ID: %s"), *HostId); + } + if (!FParse::Value(*CachedCommandLine, TEXT("websocketUrl="), WebSocketUrl)) + { + UE_LOG(LogShooterGameMode, Error, TEXT(">>>WebSocketUrl Missing in Command Line")); + bIsCriticalError = true; + } + else + { + UE_LOG(LogShooterGameMode, Log, TEXT(">>>Websocket URL: %s"), *WebSocketUrl); + } + if (ServerPort == 0) + { + UE_LOG(LogShooterGameMode, Error, TEXT("Invalid or Missing Server Port Number.")); + bIsCriticalError = true; + } + if (!FParse::Value(*CachedCommandLine, TEXT("serverRegion="), ServerRegion)) + { + UE_LOG(LogShooterGameMode, Error, TEXT(">>>ServerRegion Missing in Command Line")); + } + else + { + UE_LOG(LogShooterGameMode, Log, TEXT(">>>Server Region: %s"), *ServerRegion); + } + + UE_LOG(LogShooterGameMode, Log, TEXT("===================================")); } else { - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_RED); - UE_LOG(LogShooterGameMode, Log, TEXT("ERROR: Process Ready Failed!")); - FGameLiftError ProcessReadyError = ProcessReadyOutcome.GetError(); - UE_LOG(LogShooterGameMode, Log, TEXT("ERROR: %s"), *ProcessReadyError.m_errorMessage); - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE); + UE_LOG(LogShooterGameMode, Log, TEXT("GameLift EC2 Fleet")); + if (ServerPort == 0) + { + UE_LOG(LogShooterGameMode, Error, TEXT("Invalid or Missing Server Port Number. Shutting Down.")); + bIsCriticalError = true; + } } - - UE_LOG(LogShooterGameMode, Log, TEXT("InitGameLift completed!")); -} - -bool AShooterGameMode::InitiateConnectionWithGameLiftAgent(FServerParameters& ServerParameters, FGameLiftServerSDKModule& GameLiftSdkModule) -{ - // InitSDK will establish a local connection with GameLift's agent to enable further communication - FGameLiftGenericOutcome InitSdkOutcome = GameLiftSdkModule.InitSDK(ServerParameters); - if (InitSdkOutcome.IsSuccess()) + if (bIsCriticalError) { - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_GREEN); - UE_LOG(LogShooterGameMode, Log, TEXT("GameLift Init SDK Succeeded")); - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE); - } - else - { - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_RED); - UE_LOG(LogShooterGameMode, Log, TEXT("ERROR: InitSDK failed : (")); - FGameLiftError GameLiftError = InitSdkOutcome.GetError(); - UE_LOG(LogShooterGameMode, Log, TEXT("ERROR: %s"), *GameLiftError.m_errorMessage); - UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE); - return true; - } - return false; -} - -#endif - -void AShooterGameMode::InitGameLift() -{ -#if WITH_GAMELIFT - UE_LOG(LogShooterGameMode, Log, TEXT("Calling InitGameLift")); - - FServerParameters ServerParameters; - FProcessParameters ProcessParameters; - - // Getting the module first. - FGameLiftServerSDKModule& GameLiftSdkModule = FModuleManager::LoadModuleChecked(FName("GameLiftServerSDK")); - - // The port being used, uses UE system default if not entered as a switch - ProcessParameters.port = FURL::UrlConfig.DefaultPort; - FParse::Value(FCommandLine::Get(), TEXT("port="), ProcessParameters.port); - - if (ProcessParameters.port <= 0 || ProcessParameters.port > 65535) - { - UE_LOG(LogShooterGameMode, Error, TEXT("Port Value Fault: %d (Value must be between 1 and 65535"), ProcessParameters.port); + UE_LOG(LogShooterGameMode, Error, TEXT("Critical Missing or Invalid Arguments in Command Line. Shutting Down.")); FPlatformMisc::RequestExit(true); } - - // Define the server parameters for a GameLift Anywhere fleet. These are not needed for a GameLift managed EC2 fleet. - bool bIsAnywhereActive = false; - if (FParse::Param(FCommandLine::Get(), TEXT("-glAnywhere"))) - { - bIsAnywhereActive = true; - } - - if (bIsAnywhereActive) - { - UE_LOG(LogShooterGameMode, Log, TEXT("Configuring server parameters for Anywhere...")); - if (SetParametersFromCommandLine(ServerParameters, ProcessParameters)) - { - UE_LOG(LogShooterGameMode, Error, TEXT("Critical Error, shutting server down...")); - FPlatformMisc::RequestExit(true); - return; - } - - SetProcessIdOnServerParameters(ServerParameters); - if (ServerParameters.m_processId.IsEmpty()) - { - UE_LOG(LogShooterGameMode, Warning, TEXT("ProcessId is empty")); - FPlatformMisc::RequestExit(true); - return; - } -// LogServerParameters(ServerParameters); - - UE_LOG(LogShooterGameMode, Log, TEXT("Initializing the GameLift Server...")); - - BindGameLiftCallbackFunctions(ProcessParameters, GameLiftSdkModule); - } else { - // Using EC2 - UE_LOG(LogShooterGameMode, Log, TEXT("Using Gamelift EC2 Managed Fleet")); - - // EC2: Bind Callbacks (populates logParameters automatically) - BindGameLiftCallbackFunctions(ProcessParameters, GameLiftSdkModule); - - // EC2: Empty ServerParameters = GameLift agent provies all config - UE_LOG(LogShooterGameMode, Log, TEXT("Initializing EC2 GameLift Server...")); + UE_LOG(LogShooterGameMode, Log, TEXT("Command Line Parsed Successfully.")); } - if (InitiateConnectionWithGameLiftAgent(ServerParameters, GameLiftSdkModule)) return; - LogGameLiftServerSummary(bIsAnywhereActive, ServerParameters, ProcessParameters); - InitiateProcessReady(ProcessParameters, GameLiftSdkModule); - -#endif } + +int32 AShooterGameMode::GetConfiguredOrDefaultPort() const +{ + // Default Unreal Engine listen/dedicated server port + int32 Port = 7777; + + // Check if a port was passed via command line: -port=xxxx + int32 CmdPort = 0; + if (FParse::Value(*CachedCommandLine, TEXT("port="), CmdPort)) + { + if (CmdPort > 0 && CmdPort <= 65535) + { + Port = CmdPort; + } + else + { + Port = 0; + } + } + return Port; +} + diff --git a/Source/FPSTemplate/Public/Game/ShooterGameMode.h b/Source/FPSTemplate/Public/Game/ShooterGameMode.h index 1a8ee4fb..f4d39c0d 100644 --- a/Source/FPSTemplate/Public/Game/ShooterGameMode.h +++ b/Source/FPSTemplate/Public/Game/ShooterGameMode.h @@ -5,14 +5,13 @@ #include "CoreMinimal.h" #include "ShooterGameModeBase.h" -#if WITH_GAMELIFT -#include "GameLiftServerSDK.h" -#endif - #include "ShooterGameMode.generated.h" DECLARE_LOG_CATEGORY_EXTERN(LogShooterGameMode, Log, All); +struct FProcessParameters; +struct FServerParameters; + /** * */ @@ -23,25 +22,44 @@ class FPSTEMPLATE_API AShooterGameMode : public AShooterGameModeBase public: AShooterGameMode(); - + protected: + + UPROPERTY(Config, BlueprintReadOnly) + bool bIsAnywhereFleet = false; + + UPROPERTY(Config, BlueprintReadOnly) + FString FleetId; + + UPROPERTY(Config, BlueprintReadOnly) + FString AuthToken; + + UPROPERTY(Config, BlueprintReadOnly) + FString HostId; + + UPROPERTY(Config, BlueprintReadOnly) + FString WebSocketUrl; + + UPROPERTY(Config) + FString ServerRegion; + + UPROPERTY(Config, BlueprintReadOnly) + int32 ServerPort; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool bDebugMode = false; + virtual void BeginPlay() override; - + virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) override; + private: - - void InitGameLift(); + FString CachedCommandLine; + TSharedPtr ProcessParameters; + TSharedPtr ServerParameters; + + int32 GetConfiguredOrDefaultPort() const; + #if WITH_GAMELIFT - bool SetParametersFromCommandLine(FServerParameters& OutServerParameters, FProcessParameters& OutProcessParameters); - static void LogServerParameters(const FServerParameters& InServerParameters); - void SetProcessIdOnServerParameters(FServerParameters& OutServerParameters); - bool ValidateServerParameters(const FString& CommandLine, const FString& ParameterName, FString& OutValue, const bool bCritical = true); - void LogServerSummary(const FServerParameters& InServerParameters, const FProcessParameters& InProcessParameters); - void LogGameLiftServerSummary(bool bIsAnywhere, const FServerParameters& InServerParameters, const FProcessParameters& InProcessParameters); - void BindGameLiftCallbackFunctions(FProcessParameters& ProcessParameters, - FGameLiftServerSDKModule& GameLiftSdkModule); - void InitiateProcessReady(FProcessParameters& ProcessParameters, FGameLiftServerSDKModule& GameLiftSdkModule); - bool InitiateConnectionWithGameLiftAgent(FServerParameters& ServerParameters, - FGameLiftServerSDKModule& GameLiftSdkModule); + void InitGameLift(); #endif - };