Notice! Tutorial requires O3DE engine version greater than 24.09. If you're using 24.09 please add https://github.com/o3de/o3de/commit/aa100770eb744027ef221aeabfb7e9552781595d, https://github.com/o3de/o3de/commit/963f7cf3f90e0f929a4575abf20db805eecafcc8, and https://github.com/o3de/o3de/commit/27fe9c292dc8d556a4fb229cc6a53d5e88fb4fca
Notice! Tutorial requires Amazon Web Service (AWS) gems. See https://github.com/aws/o3de-repo for install instructions.
This tutorial is just the first step for running your multiplayer project on AWS GameLift. By the end, we will have an O3D game server running in the cloud, with a publicly available IP address that the GameLauncher (running locally) can connect to.
This will not be totally implemented for something a game developer would ship in the wild. The GameLauncher will not have any way of requesting to create a new game, or searching for existing servers to connect to. The game launcher will be at the mercy of the developer to feed it an IP address directly.
You could use this public IP for testing your game in-house for latency, or giving the IP address to testers or friends. It’s a great way (maybe the only way) for doing multiplayer in early development with friends since O3DE doesn’t have a NAT punchthrough, there’s no way of connecting directly from my network to yours since our true IP is hidden behind a router, where as GameLift gives us a public IP address, and some basic security protection.
Time to write new code: 10 minutes
Exporting a monolithic server: 1 hour
Testing Locally using GameLift Anywhere: 5 minutes
Deploying fleet to GameLift: 50 minutes
Total time: ~2 hours
- Notify GameLift the process is ready
- Listen for GameLift’s Create Session event in order to launch level
- Development branch won’t need this: Multiplayer::SessionNotificationBus::Handler::BusConnect(); OnCreateSessionEnd();
- Upload the server launcher
- Create a Fleet
- Create a Game Session
- Create a Player Session
- Connect to server given an ip address and player session id
Generally, game clients shouldn't ship with GameLift code for several important reasons:
- Security: Including GameLift SDK or direct API calls in the client could expose sensitive information or allow unauthorized access to your AWS resources.
- Flexibility: Using a backend service allows you to change your matchmaking or session management logic without updating the game client.
- Scalability: A backend service can better handle the load of multiple client requests and manage game sessions more efficiently.
Instead of including GameLift code in the client, consider this approach:
- Implement a backend service that interacts with GameLift using the AWS SDK.
- Have your game client communicate with this backend service via a secure API.
- The backend service can handle tasks like creating game sessions, managing matchmaking, and providing connection information to clients.
- Enable AWSGameLift gem
- Compile
- Wait for asset processor
- Demo the default level
- Update the Module.cpp AZ_DECLARE_MODULE_CLASS for the client-server split
- Dedicated Servers with sv_gameliftEnabled should NotifyGameLiftProcessReady
-bg_ConnectToAssetProcessor=0 Speed up launch time by not loading Asset Processor (we aren’t changing assets anyways)
--sv_gameLiftEnabled=true Only perform server logic if the gamelift cvar is set
GetRequiredServices()
#if AZ_TRAIT_SERVER && !AZ_TRAIT_CLIENT
required.push_back(AZ_CRC_CE("AWSGameLiftServerService"));
#endif
Add GameLift Server for the server build dependency, because the server needs to tell AWS that it’s ready to begin hosting
${gem_name}.Server.Static STATIC
...
BUILD_DEPENDENCIES
Gem::AWSGameLift.Server.Static
// Unified launchers and Editors can be either server and client.
// Make sure code only builds in dedicated servers.
#if AZ_TRAIT_SERVER && !AZ_TRAIT_CLIENT
#include <Request/AWSGameLiftServerRequestBus.h>
#endif
...
GameLiftDemoSystemComponent::Activate()
{
#if AZ_TRAIT_SERVER && !AZ_TRAIT_CLIENT
const auto console = AZ::Interface<AZ::IConsole>::Get();
console->GetCvarValue("sv_gameliftEnabled", gameLiftEnabled);
if (gameLiftEnabled)
{
AWSGameLift::AWSGameLiftServerRequestBus::Broadcast(
&AWSGameLift::IAWSGameLiftServerRequests::NotifyGameLiftProcessReady);
}
#endif
Open Project Manager
Export settings
Export > Windows
Notice! Linux server exports must include install.sh
in the root folder. GameLift runs install.sh in order to setup runtime dependencies before attempting to open the headless server.
Start up a headless server
Create a game session with game-properties
Check the server log to see that Multiplayer operating in DedicatedServer mode and default level is loaded
--game-properties "Key=level,Value=DefaultLevel"
aws gamelift create-game-session --region us-west-2 --location custom-location-1 --fleet-id fleet-1234 --name GameSession1 --maximum-player-session-count 3 --game-properties "Key=level,Value=DefaultLevel"
aws gamelift upload-build --server-sdk-version 5.1.2 --operating-system WINDOWS_2016 --build-root <path-to-export-folder>\GameLiftDemoHeadlessServerPackage --name GameLiftDemo --build-version v1.0 --region us-west-2
aws gamelift create-fleet --region us-west-2 --name GameLiftO3DTest --ec2-instance-type c5.xlarge --fleet-type ON_DEMAND --build-id <BuildId> --runtime-configuration "GameSessionActivationTimeoutSeconds=300, ServerProcesses=[{LaunchPath=C:\game\GameLiftDemo.HeadlessServerLauncher.exe, Parameters= --rhi=null -sys_PakPriority=2 -NullRenderer -sv_terminateOnPlayerExit=true -bg_ConnectToAssetProcessor=0 --sv_gameLiftEnabled=true --sv_dedicated_host_onstartup=false, ConcurrentExecutions=1}]" --ec2-inbound-permissions "FromPort=33450,ToPort=34449,IpRange=0.0.0.0/0,Protocol=UDP"
Wait 47 minutes and check GameLift console for updates
aws gamelift create-game-session --region us-west-2 --fleet-id fleet-123 --name foogamesession1 --maximum-player-session-count 10 --game-properties "Key=level,Value=DefaultLevel"
Notice! There are no GameLift specific API calls required in the game launcher
Refer to MultiplayerSample/MPSGameLift/Documentation
aws gamelift create-player-session --region us-west-2 --game-session-id <GameSessionId> --player-id Player1
Run GameLauncher and connect to GameLift server.
--bg_ConnectToAssetProcessor=0
--connect="35.85.44.17:33450:psess-abcd-ef12-3456"
In theory, scripting could work. C++ side knows what type of app you’re running. We’re open source, so this a call to action for anyone to add functionality which exposes the app type to scripting.
Demonstrating matchmaking using GameLift Flexmatch.
There’s an example of Matchmaking for Multiplayer Sample, but it requires more AWS backend:
- AWS Cognito (for login credentials)
- AWS Lambda for RESTful calls the client makes which calls GameLift API on the GameLaunchers behalf
- AWS DynamoDb, for storing which clients have requested a match
o3de-multiplayersample/MPSGameLift/Code/Source/MatchmakingSystemComponent.h