diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index d58bdfda..2e96c4eb 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -179,3 +179,6 @@ DefaultBroadphaseSettings=(bUseMBPOnClient=False,bUseMBPOnServer=False,bUseMBPOu MinDeltaVelocityForHitEvents=0.000000 ChaosSettings=(DefaultThreadingModel=TaskGraph,DedicatedThreadTickMode=VariableCappedWithTarget,DedicatedThreadBufferMode=Double) + +[CoreRedirects] ++PropertyRedirects=(OldName="/Script/FPSTemplate.CommandLineArgs.Arguments",NewName="/Script/FPSTemplate.CommandLineArgs.CmdArguments") \ No newline at end of file diff --git a/Source/FPSTemplate/Private/Game/ShooterGameMode.cpp b/Source/FPSTemplate/Private/Game/ShooterGameMode.cpp index 70a3cfa6..a56d03dd 100644 --- a/Source/FPSTemplate/Private/Game/ShooterGameMode.cpp +++ b/Source/FPSTemplate/Private/Game/ShooterGameMode.cpp @@ -30,210 +30,169 @@ void AShooterGameMode::BeginPlay() } #if WITH_GAMELIFT -void AShooterGameMode::SetServerParameters(TSharedPtr OutServerParameters) + +// 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) { + bool bCriticalFault = false; + + FString CommandLine = FCommandLine::Get(); + UE_LOG(LogShooterGameMode, Log, TEXT(">>> Command Line: %s"), *CommandLine); + + // Checks if param exists (returns bool) + // 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. - FString GameLiftAnywhereAuthToken = ""; - if (FParse::Value(FCommandLine::Get(), TEXT("-authtoken="), GameLiftAnywhereAuthToken)) - { - OutServerParameters->m_authToken = GameLiftAnywhereAuthToken; - } - else - { - UE_LOG(LogShooterGameMode, Warning, TEXT("-authtoken not found in command line.")); - } + bCriticalFault |= ValidateServerParameters(CommandLine, TEXT("authtoken"),OutServerParameters.m_authToken); - // The Host/compute-name of the GameLift Anywhere instance - FString GameLiftAnywhereHostId = ""; - if (FParse::Value(FCommandLine::Get(), TEXT("-hostid="), GameLiftAnywhereHostId)) - { - OutServerParameters->m_hostId = GameLiftAnywhereHostId; - } - else - { - UE_LOG(LogShooterGameMode, Warning, TEXT("-hostid not found in command line.")); - } - + bCriticalFault |= ValidateServerParameters(CommandLine, TEXT("hostid"),OutServerParameters.m_hostId); + // The AnywhereFleet ID. - FString GameLiftAnywhereFleetId = ""; - if (FParse::Value(FCommandLine::Get(), TEXT("-fleetid="), GameLiftAnywhereFleetId)) - { - OutServerParameters->m_fleetId = GameLiftAnywhereFleetId; - } - else - { - UE_LOG(LogShooterGameMode, Warning, TEXT("-fleetid not found in command line.")); - } - + bCriticalFault |= ValidateServerParameters(CommandLine, TEXT("fleetid"),OutServerParameters.m_fleetId); + // The WebSocket URL (GameLiftServiceSdkEndpoint) - FString GameLiftAnywhereWebSocketUrl = ""; - if (FParse::Value(FCommandLine::Get(), TEXT("-websocketurl="), GameLiftAnywhereWebSocketUrl)) - { - OutServerParameters->m_webSocketUrl = GameLiftAnywhereWebSocketUrl; - } - else - { - UE_LOG(LogShooterGameMode, Warning, TEXT("-websocketurl not found in command line.")); - } + bCriticalFault |= ValidateServerParameters(CommandLine, TEXT("websocketurl"),OutServerParameters.m_webSocketUrl); - // 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 + // 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(TSharedPtr ServerParameters) +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"), *ServerParameters->m_webSocketUrl); - UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Fleet ID: %s"), *ServerParameters->m_fleetId); - UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Process ID: %s"), *ServerParameters->m_processId); - UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Host ID (Compute Name): %s"), *ServerParameters->m_hostId); - UE_LOG(LogShooterGameMode, Log, TEXT(">>>> Auth Token: %s"), *ServerParameters->m_authToken); + 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); } -#endif -void AShooterGameMode::ParseCommandLinePort(int32& OutPort) +void AShooterGameMode::SetProcessIdOnServerParameters(FServerParameters& OutServerParameters) { - TArray CommandLineTokens; - TArray CommandLineSwitches; - - FCommandLine::Parse(FCommandLine::Get(), CommandLineTokens, CommandLineSwitches); - - for (FString SwitchStr : CommandLineSwitches) - { - FString Key; - FString Value; - - if (SwitchStr.Split("=", &Key, &Value)) - { - if (Key.Equals(TEXT("port"), ESearchCase::IgnoreCase)) - { - OutPort = FCString::Atoi(*Value); - return; - } - } - } + // 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 } -void AShooterGameMode::InitGameLift() +bool AShooterGameMode::ValidateServerParameters(const FString& CommandLine, const FString& ParameterName, + FString& OutValue, const bool bCritical) { + if (!FParse::Value(*CommandLine, *FString::Printf(TEXT("%s="), *ParameterName), OutValue)) + { + UE_LOG(LogShooterGameMode, Warning, TEXT("-%s not found"), *ParameterName); + return bCritical; + } + return false; +} -#if WITH_GAMELIFT - UE_LOG(LogShooterGameMode, Log, TEXT("Calling InitGameLift")); - FProcessParameters LocalProcessParameters; +void AShooterGameMode::LogServerSummary(const FServerParameters& InServerParameters, + const FProcessParameters& InProcessParameters) +{ + UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_YELLOW); + UE_LOG(LogShooterGameMode, Log, TEXT("=== SERVER STARTUP SUMMARY ===")); + UE_LOG(LogShooterGameMode, Log, TEXT("Port: %d"), InProcessParameters.port); - // Getting the module first. - FGameLiftServerSDKModule* GameLiftSdkModule = &FModuleManager::LoadModuleChecked(FName("GameLiftServerSDK")); + // Inline validation of ServerParams + bool bAllValid = !InServerParameters.m_authToken.IsEmpty() && + !InServerParameters.m_hostId.IsEmpty() && + !InServerParameters.m_fleetId.IsEmpty() && + !InServerParameters.m_webSocketUrl.IsEmpty(); - //Define the server parameters for a GameLift Anywhere fleet. These are not needed for a GameLift managed EC2 fleet. - FServerParameters ServerParameters; - - 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 GameLift Anywhere is enabled, parse command line arguments and pass them in the ServerParameters object. - - TSharedPtr ServerParams = MakeShared(ServerParameters); - - SetServerParameters(ServerParams); - LogServerParameters(ServerParams); - ServerParameters = *ServerParams; - } + UE_LOG(LogShooterGameMode, Log, TEXT("All Anywhere params: %s"), + bAllValid ? TEXT("VALID ✓") : TEXT("MISSING ❌")); + UE_LOG(LogShooterGameMode, SetColor, TEXT("%s"), COLOR_NONE); +} - UE_LOG(LogShooterGameMode, Log, TEXT("Initializing the GameLift Server...")); - - // InitSDK will establish a local connection with GameLift's agent to enable futher communication - - FGameLiftGenericOutcome InitSdkOutcome = GameLiftSdkModule->InitSDK(ServerParameters); - if (InitSdkOutcome.IsSuccess()) - { - 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; - } - - +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(). - - auto OnGameSession = [=](const Aws::GameLift::Server::Model::GameSession& InGameSession) + + + ProcessParameters.OnStartGameSession.BindLambda([this, &GameLiftSdkModule](Aws::GameLift::Server::Model::GameSession InGameSession) { FString GameSessionId = FString(InGameSession.GetGameSessionId()); UE_LOG(LogShooterGameMode, Log, TEXT("GameSession Initializing: %s"), *GameSessionId); - GameLiftSdkModule->ActivateGameSession(); - }; - - LocalProcessParameters.OnStartGameSession.BindLambda(OnGameSession); + FGameLiftGenericOutcome ActivateGameSessionOutcome = GameLiftSdkModule.ActivateGameSession(); + if (ActivateGameSessionOutcome.IsSuccess()) + { + UE_LOG(LogShooterGameMode, Log, TEXT("Activate GameSession successful")); + } + 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); + } + }); - auto OnTerminate = [=]() + ProcessParameters.OnTerminate.BindLambda([this, &GameLiftSdkModule]() { - UE_LOG(LogShooterGameMode, Log, TEXT("Game Server Process is terminating")); - GameLiftSdkModule->ProcessEnding(); - }; + FGameLiftGenericOutcome ProcessEndingOutcome = GameLiftSdkModule.ProcessEnding(); + FGameLiftGenericOutcome DestroyOutcome = GameLiftSdkModule.Destroy(); + if (ProcessEndingOutcome.IsSuccess() && DestroyOutcome.IsSuccess()) + { + UE_LOG(LogShooterGameMode, Log, TEXT("Server process ending successfully")); + FPlatformMisc::RequestExit(false); + } + else + { + if (!ProcessEndingOutcome.IsSuccess()) + { + const FGameLiftError& Error = ProcessEndingOutcome.GetError(); + UE_LOG(LogShooterGameMode, Error, TEXT("ProcessEnding() failed. Error: %s"), Error.m_errorMessage.IsEmpty() ? TEXT("Unknown Error") : *Error.m_errorMessage); + } + if (!DestroyOutcome.IsSuccess()) + { + const FGameLiftError& Error = DestroyOutcome.GetError(); + UE_LOG(LogShooterGameMode, Error, TEXT("Destroy() failed. Error: %s"), Error.m_errorMessage.IsEmpty() ? TEXT("Unknown error") : *Error.m_errorMessage); + } + FPlatformMisc::RequestExit(true); + } + }); - LocalProcessParameters.OnTerminate.BindLambda(OnTerminate); - - auto OnHealthCheck = [=]() + ProcessParameters.OnHealthCheck.BindLambda([=]() { - UE_LOG(LogShooterGameMode, Log, TEXT("Performing Health Check")); return true; - }; - - - LocalProcessParameters.OnHealthCheck.BindLambda(OnHealthCheck); - - int32 Port = FURL::UrlConfig.DefaultPort; - ParseCommandLinePort(Port); - - LocalProcessParameters.port = Port; + }); // 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; - LogFiles.Add(TEXT("FPSTemplate/Saved/Logs/FPSTemplate.log")); + FString LogDirectory =FPaths::ProjectSavedDir() / TEXT("Logs"); + FString LogFile = LogDirectory / TEXT("GameLiftServer.log"); + LogFiles.Add(LogFile); UE_LOG(LogShooterGameMode, Log, TEXT("Before Adding Log Files")); - LocalProcessParameters.logParameters = LogFiles; - - + 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(LocalProcessParameters); + FGameLiftGenericOutcome ProcessReadyOutcome = GameLiftSdkModule.ProcessReady(ProcessParameters); if (ProcessReadyOutcome.IsSuccess()) { @@ -251,8 +210,82 @@ void AShooterGameMode::InitGameLift() } 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()) + { + 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")); + + // 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...")); + + if (InitiateConnectionWithGameLiftAgent(ServerParameters, GameLiftSdkModule)) return; + BindGameLiftCallbackFunctions(ProcessParameters, GameLiftSdkModule); + InitiateProcessReady(ProcessParameters, GameLiftSdkModule); + } + else + { + // Using EC2 + UE_LOG(LogShooterGameMode, Log, TEXT("Using Gamelift EC2 Managed Fleet")); + //TODO: Implement EC2 later. + UE_LOG(LogShooterGameMode, Log, TEXT("Not Implemented")); + } +#endif +} diff --git a/Source/FPSTemplate/Public/Game/ShooterGameMode.h b/Source/FPSTemplate/Public/Game/ShooterGameMode.h index eec5befd..499b3950 100644 --- a/Source/FPSTemplate/Public/Game/ShooterGameMode.h +++ b/Source/FPSTemplate/Public/Game/ShooterGameMode.h @@ -12,9 +12,205 @@ #include "ShooterGameMode.generated.h" +/* +struct FCommandLineArgs +{ + TMap Options; + TArray CmdArguments; + + void ParseCommandLine() + { + const TCHAR* CommandLine = FCommandLine::Get(); -struct FProcessParameters; + // Reserves estimated space to avoid reallocations + Options.Reserve(10); + CmdArguments.Reserve(10); + + FString Key, Value, Token; + FString FlagName = FString(TEXT("")); + while (FParse::Token(CommandLine, Token, true)) + { + // Check if token starts with '-' + if (Token.StartsWith(TEXT("-"))) + { + // Remove the leading '-' and store the flag name + FlagName = Token.Mid(1); + + // If it's in the form Key=Value, split it + if (FlagName.Split(TEXT("="), &Key, &Value)) + { + Key=Key.ToLower(); + Value=Value.ToLower(); + // Normalize boolean values + if (Value.Equals(TEXT("true"), ESearchCase::IgnoreCase) || Value.Equals(TEXT("1"))) + { + Options.Add(Key, TEXT("true")); + } + else if (Value.Equals(TEXT("false"), ESearchCase::IgnoreCase) || Value.Equals(TEXT("0"))) + { + Options.Add(Key, TEXT("false")); + } + else + { + Options.Add(Key, Value); + } + } + else + { + // Bare flag (for + // example -Debug or -NoSound) + + // The convention is flags starting with "No" are false + if (FlagName.StartsWith(TEXT("No")) && FlagName.Len() > 2) + { + Key = FlagName.Mid(2).ToLower(); + Options.Add(Key, TEXT("false")); + } + else + { + Key = FlagName.ToLower(); + Options.Add(Key, TEXT("true")); + } + + // Also add to Arguments array for backward compatibility + CmdArguments.Add(FlagName); + } + } + else + { + CmdArguments.Add(Token); + } + } + } + + FString GetValueFromKey(const FString& Key, const FString& DefaultValue = TEXT("")) const + { + FString NormalizedKey = Key.ToLower(); + const FString* ValuePtr = Options.Find(NormalizedKey); + return ValuePtr ? *ValuePtr : DefaultValue; + } + + bool TryGetValue(const FString& Key, FString& OutValue) const + { + FString NormalizedKey = Key.ToLower(); + if (const FString* ValuePtr = Options.Find(NormalizedKey)) + { + OutValue = *ValuePtr; + return true; + } + return false; + } + + FString ToString() const + { + FString Result; + for (const auto& Pair : Options) + { + Result += FString::Printf(TEXT("-%s=%s "), *Pair.Key, *Pair.Value); + } + for (const FString& Arg : CmdArguments) + { + Result += Arg + TEXT(" "); + } + return Result.TrimEnd(); + } + + // Get a boolean value, returns Default if not found or invalid + bool GetBool(const FString& Key, bool DefaultValue = false) const + { + FString Value; + if (TryGetValue(Key, Value)) + { + if (Value.Equals(TEXT("true"), ESearchCase::IgnoreCase) || + Value.Equals(TEXT("1")) || + Value.Equals(TEXT("yes"), ESearchCase::IgnoreCase)) + { + return true; + } + if (Value.Equals(TEXT("false"), ESearchCase::IgnoreCase) || + Value.Equals(TEXT("0")) || + Value.Equals(TEXT("no"), ESearchCase::IgnoreCase)) + { + return false; + } + } + return DefaultValue; + } + + // Get an integer value, returns Default if not found or not numeric + int32 GetInt(const FString& Key, int32 DefaultValue = 0) const + { + FString Value; + if (TryGetValue(Key, Value)) + { + return FCString::Atoi(*Value); + } + return DefaultValue; + } + + // Get a float value + float GetFloat(const FString& Key, float DefaultValue = 0.0f) const + { + FString Value; + if (TryGetValue(Key, Value)) + { + return FCString::Atof(*Value); + } + return DefaultValue; + } + + // Get as FName (useful for modes, map names, etc.) + FName GetName(const FString& Key, const FName& DefaultValue = NAME_None) const + { + FString Value; + if (TryGetValue(Key, Value)) + { + return FName(*Value); + } + return DefaultValue; + } + + // Set a string value (core setter used by others) + void SetValue(const FString& Key, const FString& Value) + { + Options.Add(Key.ToLower(), Value.ToLower()); + } + + // Set a boolean option + void SetBool(const FString& Key, bool bValue) + { + Options.Add(Key.ToLower(), bValue ? TEXT("true") : TEXT("false")); + } + + // Set an integer option + void SetInt(const FString& Key, int32 Value) + { + Options.Add(Key.ToLower(), FString::FromInt(Value)); + } + + // Set a float option (with minimal formatting) + void SetFloat(const FString& Key, float Value, int32 Precision = 2) + { + FString FloatStr = FString::SanitizeFloat(Value, Precision); + Options.Add(Key.ToLower(), FloatStr); + } + + // Set an FName option + void SetName(const FString& Key, const FName& NameValue) + { + Options.Add(Key.ToLower(), NameValue.ToString().ToLower()); + } + + // Add a bare flag (true/false toggle stored for ToString output) + void SetFlag(const FString& Flag, bool bEnabled = true) + { + FString Key = Flag.ToLower(); + Options.Add(Key, bEnabled ? TEXT("true") : TEXT("false")); + CmdArguments.Add(Flag); // maintain compatibility with older flag parsing + } +}; +*/ DECLARE_LOG_CATEGORY_EXTERN(LogShooterGameMode, Log, All); /** @@ -35,10 +231,16 @@ private: void InitGameLift(); #if WITH_GAMELIFT - void SetServerParameters(TSharedPtr OutServerParameters); - void LogServerParameters(TSharedPtr ServerParameters); + 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 BindGameLiftCallbackFunctions(FProcessParameters& ProcessParameters, + FGameLiftServerSDKModule& GameLiftSdkModule); + void InitiateProcessReady(FProcessParameters& ProcessParameters, FGameLiftServerSDKModule& GameLiftSdkModule); + bool InitiateConnectionWithGameLiftAgent(FServerParameters& ServerParameters, + FGameLiftServerSDKModule& GameLiftSdkModule); #endif - void ParseCommandLinePort(int32& OutPort); - TSharedPtr ProcessParameters; };