Plugin for Unreal: Set up local testing with Amazon GameLift Anywhere - Amazon GameLift

Plugin for Unreal: Set up local testing with Amazon GameLift Anywhere

In this workflow, you add client and server game code for Amazon GameLift functionality, and use the plugin to designate your local workstation as a test game server host. When you've completed integration tasks, use the plugin to build your game client and server components.

To start the Amazon GameLift Anywhere workflow:
  • In the Unreal editor main toolbar, choose the Amazon GameLift menu, and select Host with Anywhere. This action opens the plugin page Deploy Anywhere, which presents a six-step process to integrate, build, and launch your game components.

Step 1: Set your profile.

Choose the profile you want to use when following this workflow. The profile you select impacts all steps in the workflow. All resources you create are associated with the profile's AWS account and are placed in the profile's default AWS Region. The profile user's permissions determine your access to AWS resources and actions.

To set a user profile
  1. Select a profile from the dropdown list of available profiles. If you don't have a profile yet or want to create a new one, go to the Amazon GameLift menu and choose Set AWS User Profiles.

  2. If bootstrap status is not "Active", choose Bootstrap profile and wait for the status to change to "Active".

Step 2: Set up your game code

In this step, you make a series of updates to your client and server code to add hosting functionality. If you haven't already set up a source-built version of the Unreal editor, the plugin provides links to instructions and source code.

With the plugin, can take advantage of some conveniences when integrating your game code. You can do a minimal integration to set up basic hosting functionality. You can also do a more extensive custom integration. The information in this section describes the minimal integration option. Use the test maps included with the plugin to add client Amazon GameLift functionality to your game project. For server integration, use the provided code sample to update your project's game mode.

Integrate your server game mode

Add server code to your game that enables communication between your game server and the Amazon GameLift service. Your game server must be able to respond to requests from Amazon GameLift, such as to start a new game session, and also report status on game server health and player connections.

To add server code for Amazon GameLift
  1. In your code editor, open the solution (.sln) file for your game project, usually found in the project root folder. For example: GameLiftUnrealApp.sln.

  2. With the solution open, locate the project game mode header file: [project-name]GameMode.h file. For example: GameLiftUnrealAppGameMode.h.

  3. Change the header file to align with the following example code. Be sure to replace "GameLiftServer" with your own project name. These updates are specific to the game server; we recommend that you make a backup copy of the original game mode files for use with your client.

    // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 #pragma once #include "CoreMinimal.h" #include "GameFramework/GameModeBase.h" #include "GameLiftServerGameMode.generated.h" struct FProcessParameters; DECLARE_LOG_CATEGORY_EXTERN(GameServerLog, Log, All); UCLASS(minimalapi) class AGameLiftServerGameMode : public AGameModeBase { GENERATED_BODY() public: AGameLiftServerGameMode(); protected: virtual void BeginPlay() override; private: void InitGameLift(); private: TSharedPtr<FProcessParameters> ProcessParameters; };
  4. Open the related source file [project-name]GameMode.cpp file (for example GameLiftUnrealAppGameMode.cpp). Change the code to align with the following example code. Be sure to replace "GameLiftUnrealApp" with your own project name. These updates are specific to the game server; we recommend that you make a backup copy of the original file for use with your client.

    The following example code shows how to add the minimum required elements for server integration with Amazon GameLift:

    • Initialize an Amazon GameLift API client. The InitSDK() call with server parameters is required for an Amazon GameLift Anywhere fleet. When you connect to an Anywhere fleet, the plugin stores the server parameters as console arguments The sample code can access the values at runtime.

    • Implement required callback functions to respond to requests from the Amazon GameLift service, including OnStartGameSession, OnProcessTerminate, and onHealthCheck.

    • Call ProcessReady() with a designated port to notify the Amazon GameLift service when ready to host game sessions.

    // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 #include "GameLiftServerGameMode.h" #include "UObject/ConstructorHelpers.h" #include "Kismet/GameplayStatics.h" #if WITH_GAMELIFT #include "GameLiftServerSDK.h" #include "GameLiftServerSDKModels.h" #endif #include "GenericPlatform/GenericPlatformOutputDevices.h" DEFINE_LOG_CATEGORY(GameServerLog); AGameLiftServerGameMode::AGameLiftServerGameMode() : ProcessParameters(nullptr) { // Set default pawn class to our Blueprinted character static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter")); if (PlayerPawnBPClass.Class != NULL) { DefaultPawnClass = PlayerPawnBPClass.Class; } UE_LOG(GameServerLog, Log, TEXT("Initializing AGameLiftServerGameMode...")); } void AGameLiftServerGameMode::BeginPlay() { Super::BeginPlay(); #if WITH_GAMELIFT InitGameLift(); #endif } void AGameLiftServerGameMode::InitGameLift() { #if WITH_GAMELIFT UE_LOG(GameServerLog, Log, TEXT("Calling InitGameLift...")); // Getting the module first. FGameLiftServerSDKModule* GameLiftSdkModule = &FModuleManager::LoadModuleChecked<FGameLiftServerSDKModule>(FName("GameLiftServerSDK")); //Define the server parameters for a GameLift Anywhere fleet. These are not needed for a GameLift managed EC2 fleet. FServerParameters ServerParametersForAnywhere; bool bIsAnywhereActive = false; if (FParse::Param(FCommandLine::Get(), TEXT("glAnywhere"))) { bIsAnywhereActive = true; } if (bIsAnywhereActive) { UE_LOG(GameServerLog, Log, TEXT("Configuring server parameters for Anywhere...")); // If GameLift Anywhere is enabled, parse command line arguments and pass them in the ServerParameters object. FString glAnywhereWebSocketUrl = ""; if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereWebSocketUrl="), glAnywhereWebSocketUrl)) { ServerParametersForAnywhere.m_webSocketUrl = TCHAR_TO_UTF8(*glAnywhereWebSocketUrl); } FString glAnywhereFleetId = ""; if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereFleetId="), glAnywhereFleetId)) { ServerParametersForAnywhere.m_fleetId = TCHAR_TO_UTF8(*glAnywhereFleetId); } FString glAnywhereProcessId = ""; if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereProcessId="), glAnywhereProcessId)) { ServerParametersForAnywhere.m_processId = TCHAR_TO_UTF8(*glAnywhereProcessId); } else { // If no ProcessId is passed as a command line argument, generate a randomized unique string. ServerParametersForAnywhere.m_processId = TCHAR_TO_UTF8( *FText::Format( FText::FromString("ProcessId_{0}"), FText::AsNumber(std::time(nullptr)) ).ToString() ); } FString glAnywhereHostId = ""; if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereHostId="), glAnywhereHostId)) { ServerParametersForAnywhere.m_hostId = TCHAR_TO_UTF8(*glAnywhereHostId); } FString glAnywhereAuthToken = ""; if (FParse::Value(FCommandLine::Get(), TEXT("glAnywhereAuthToken="), glAnywhereAuthToken)) { ServerParametersForAnywhere.m_authToken = TCHAR_TO_UTF8(*glAnywhereAuthToken); } UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_YELLOW); UE_LOG(GameServerLog, Log, TEXT(">>>> WebSocket URL: %s"), *ServerParametersForAnywhere.m_webSocketUrl); UE_LOG(GameServerLog, Log, TEXT(">>>> Fleet ID: %s"), *ServerParametersForAnywhere.m_fleetId); UE_LOG(GameServerLog, Log, TEXT(">>>> Process ID: %s"), *ServerParametersForAnywhere.m_processId); UE_LOG(GameServerLog, Log, TEXT(">>>> Host ID (Compute Name): %s"), *ServerParametersForAnywhere.m_hostId); UE_LOG(GameServerLog, Log, TEXT(">>>> Auth Token: %s"), *ServerParametersForAnywhere.m_authToken); UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_NONE); } UE_LOG(GameServerLog, Log, TEXT("Initializing the GameLift Server...")); //InitSDK will establish a local connection with GameLift's agent to enable further communication. FGameLiftGenericOutcome InitSdkOutcome = GameLiftSdkModule->InitSDK(ServerParametersForAnywhere); if (InitSdkOutcome.IsSuccess()) { UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_GREEN); UE_LOG(GameServerLog, Log, TEXT("GameLift InitSDK succeeded!")); UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_NONE); } else { UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_RED); UE_LOG(GameServerLog, Log, TEXT("ERROR: InitSDK failed : (")); FGameLiftError GameLiftError = InitSdkOutcome.GetError(); UE_LOG(GameServerLog, Log, TEXT("ERROR: %s"), *GameLiftError.m_errorMessage); UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_NONE); return; } ProcessParameters = MakeShared<FProcessParameters>(); //When a game session is created, GameLift sends an activation request to the game server and passes along the game session object containing game properties and other settings. //Here is where a game server should take action based on the game session object. //Once the game server is ready to receive incoming player connections, it should invoke GameLiftServerAPI.ActivateGameSession() ProcessParameters->OnStartGameSession.BindLambda([=](Aws::GameLift::Server::Model::GameSession InGameSession) { FString GameSessionId = FString(InGameSession.GetGameSessionId()); UE_LOG(GameServerLog, Log, TEXT("GameSession Initializing: %s"), *GameSessionId); GameLiftSdkModule->ActivateGameSession(); }); //OnProcessTerminate callback. GameLift will invoke this callback before shutting down an instance hosting this game server. //It gives this game server a chance to save its state, communicate with services, etc., before being shut down. //In this case, we simply tell GameLift we are indeed going to shutdown. ProcessParameters->OnTerminate.BindLambda([=]() { UE_LOG(GameServerLog, Log, TEXT("Game Server Process is terminating")); GameLiftSdkModule->ProcessEnding(); }); //This is the HealthCheck callback. //GameLift will invoke this callback every 60 seconds or so. //Here, a game server might want to check the health of dependencies and such. //Simply return true if healthy, false otherwise. //The game server has 60 seconds to respond with its health status. GameLift will default to 'false' if the game server doesn't respond in time. //In this case, we're always healthy! ProcessParameters->OnHealthCheck.BindLambda([]() { UE_LOG(GameServerLog, Log, TEXT("Performing Health Check")); return true; }); //GameServer.exe -port=7777 LOG=server.mylog ProcessParameters->port = FURL::UrlConfig.DefaultPort; TArray<FString> CommandLineTokens; TArray<FString> CommandLineSwitches; FCommandLine::Parse(FCommandLine::Get(), CommandLineTokens, CommandLineSwitches); for (FString SwitchStr : CommandLineSwitches) { FString Key; FString Value; if (SwitchStr.Split("=", &Key, &Value)) { if (Key.Equals("port")) { ProcessParameters->port = FCString::Atoi(*Value); } } } //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<FString> Logfiles; Logfiles.Add(TEXT("GameServerLog/Saved/Logs/GameServerLog.log")); ProcessParameters->logParameters = Logfiles; //The game server calls ProcessReady() to tell GameLift it's ready to host game sessions. UE_LOG(GameServerLog, Log, TEXT("Calling Process Ready...")); FGameLiftGenericOutcome ProcessReadyOutcome = GameLiftSdkModule->ProcessReady(*ProcessParameters); if (ProcessReadyOutcome.IsSuccess()) { UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_GREEN); UE_LOG(GameServerLog, Log, TEXT("Process Ready!")); UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_NONE); } else { UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_RED); UE_LOG(GameServerLog, Log, TEXT("ERROR: Process Ready Failed!")); FGameLiftError ProcessReadyError = ProcessReadyOutcome.GetError(); UE_LOG(GameServerLog, Log, TEXT("ERROR: %s"), *ProcessReadyError.m_errorMessage); UE_LOG(GameServerLog, SetColor, TEXT("%s"), COLOR_NONE); } UE_LOG(GameServerLog, Log, TEXT("InitGameLift completed!")); #endif }

Integrate your client game map

The startup game map contains blueprint logic and UI elements that already include basic code to request game sessions and use connection information to connect to a game session. You can use the map as is or modify these as needed. Use the startup game map with other game assets, such as the Third Person template project provided by Unreal Engine. These assets are available in Content Browser. You can use them to test the plugin's deployment workflows, or as a guide to create a custom backend service for your game.

The startup map has the following characteristics:

  • It includes logic for both an Anywhere fleet and a managed EC2 fleet. When you run your client, you can choose to connect to either fleet.

  • Client functionality includes find a game session (SearchGameSessions()), create a new game session (CreateGameSession()), and join a game session directly.

  • It gets a unique player ID from your project's Amazon Cognito user pool (this is part of a deployed Anywhere solution).

To use the startup game map
  1. In the UE editor, open the Project Settings, Maps & Modes page, and expand the Default Maps section.

  2. For Editor Startup Map, select "StartupMap" from the dropdown list. You might need to search for the file, which is located in ... > Unreal Projects/[project-name]/Plugins/Amazon GameLift Plugin Content/Maps.

  3. For Game Default Map, select the same "StartupMap" from the dropdown list.

  4. For Server Default Map, select "ThirdPersonMap". This is a default map included in your game project. This map is designed for two players in the game.

  5. Open the details panel for the server default map. Set GameMode Override to "None".

  6. Expand the Default Modes section, and set Global Default Server Game Mode to the game mode you updated for your server integration.

After you've made these changes to your project, you're ready to build your game components.

Package your game components

To package your game server and game client builds
  1. Create new server and client target files

    1. In your game project folder, go to the Source folder and find the Target.cs files.

    2. Copy the file [project-name]Editor.Target.cs to two new files named [project-name]Client.Target.cs and [project-name]Server.Target.cs.

    3. Edit each of the new files to update the class name and target type values, as shown:

    UnrealProjects > MyGame > Source > MyGameClient.Target.cs // Copyright Epic Games, Inc. All Rights Reserved. using UnrealBuildTool; using System.Collections.Generic; public class MyGameClientTarget : TargetRules { public MyGameClientTarget(TargetInfo Target) : base(Target) { Type = TargetType.Client; DefaultBuildSettings = BuildSettingsVersion.V2; IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_1; ExtraModuleNames.Add("MyGame"); } }
    UnrealProjects > MyGame > Source > MyGameServer.Target.cs // Copyright Epic Games, Inc. All Rights Reserved. using UnrealBuildTool; using System.Collections.Generic; public class MyGameServerTarget : TargetRules { public MyGameServerTarget(TargetInfo Target) : base(Target) { Type = TargetType.Server; DefaultBuildSettings = BuildSettingsVersion.V2; IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_1; ExtraModuleNames.Add("MyGame"); } }
  2. Update the .Build.cs file.

    1. Open the .Build.cs file for your project. This file is located in UnrealProjects/[project name]/Source/[project name]/[project name].Build.cs.

    2. Update the ModuleRules class as shown in the following code sample.

      public class MyGame : ModuleRules { public GameLiftUnrealApp(TargetInfo Target) { PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); bEnableExceptions = true; if (Target.Type == TargetRules.TargetType.Server) { PublicDependencyModuleNames.AddRange(new string[] { "GameLiftServerSDK" }); PublicDefinitions.Add("WITH_GAMELIFT=1"); } else { PublicDefinitions.Add("WITH_GAMELIFT=0"); } } }
  3. Rebuild your game project solution.

  4. Open your game project in a source-built version of the Unreal Engine editor.

  5. Do the following for both your client and server:

    1. Choose a target. Go to Platforms, Windows and select one of the following:

      • Server: [your-application-name]Server

      • Client: [your-application-name]Client

    2. Start the build. Go to Platform, Windows, Package Project.

Each packaging process generates an executable: [your-application-name]Client.exe or [your-application-name]Server.exe.

In the plugin, set the paths to the client and server build executables on your local workstation.

Step 3: Connect to an Anywhere fleet

In this step, you designate an Anywhere fleet to use. An Anywhere fleet defines a collection of compute resources, which can be located anywhere, for game server hosting.

  • If the AWS account you're currently using has existing Anywhere fleets, open the Fleet name dropdown field and choose a fleet. This dropdown only shows the Anywhere fleets in the AWS Region for the currently active user profile.

  • If there are no existing fleets—or you want to create a new one, choose Create new Anywhere fleet and provide a fleet name.

After you've chosen an Anywhere fleet for your project, Amazon GameLift verifies that fleet status is active ad displays the fleet ID. You can track progress of this request in the Unreal editor's output log.

Step 4: Register your workstation

In this step, you register your local workstation as a compute resource in the new Anywhere fleet.

To register your workstation as an Anywhere compute
  1. Enter a compute name for your local machine. If you add more than one compute in the fleet, the names must be unique.

  2. Provide an IP address for your local machine. This field defaults to your machine's public IP address. You can also use localhost (127.0.0.1) as long as you're running your game client and server on the same machine.

  3. Choose Register compute. You can track progress of this request in the Unreal editor's output log.

In response to this action, Amazon GameLift verifies that it can connect to the compute and returns information about the newly registered compute. It also creates the console arguments that your game executables need when initializing communication with the Amazon GameLift service.

Step 5: Generate auth token

Game server processes that are running on your Anywhere compute need an authentication token to make calls to the GameLift service. The plugin automatically generates and stores an auth token for the Anywhere fleet whenever you launch the game server from the plugin. The auth token value is stored as a command line argument, which your server code can retrieve at runtime.

You do not have to take any action in this step.

Step 6: Launch game

At this point, you've completed all of the tasks needed to launch and play your multiplayer game on a local workstation using Amazon GameLift.

To play your hosted game
  1. Launch your game server. The game server will notify Amazon GameLift when it is ready to host game sessions.

  2. Launch your game client and use the new functionality to start a new game session. This request is sent to Amazon GameLift via the new backend service. In response, Amazon GameLift, calls the game server, running on your local machine, to start a new game session. When the game session is ready to accept players, Amazon GameLift provides connection information for the game client to join the game session.