GameLift and FlexMatch
Deploy game servers, match players, and complete the server lifecycle.
GameLift hosts your dedicated server. FlexMatch finds players and can place their match on that server.
| Runs in | Responsibility |
|---|---|
| Game client | Queue UI, acceptance, connection, and the player’s reservation token |
| Trusted backend | Authorized placement and matchmaking requests; distribute each player’s own result |
| Dedicated server | SDK lifecycle, health, player-session validation, and shutdown |
Use scoped AWS access for service calls. Fleet administration and deployment credentials belong in your deployment environment.
Prepare the fleet
- Package a dedicated-server build containing
AWSIKGameLiftServersand its runtime libraries. Upload it to GameLift and create a fleet with the correct executable, launch arguments, and capacity. - Match the server’s listening port, fleet inbound permissions, and port reported to GameLift.
- Add the fleet or alias to a game-session queue when using placement across locations.
The plugin includes the server SDK integration. Follow AWS’s server lifecycle through the wrappers below.
In Betide → Crossplay → AWS GameLift, choose one placement target:
| Setting | Use |
|---|---|
| Default Game Session Queue Name | Queue placement; takes precedence over the fleet and alias defaults |
| Default Fleet Id / Default Alias Id | Direct game-session creation when no queue is selected |
| Default Location | Optional fleet location; otherwise use the fleet’s home region |
| Default Maximum Player Session Count | Capacity when the request does not set it |
Keep Register Hosting Provider enabled and select GameLift in the Crossplay Hosting policy. Restart after changing provider registration.
Start the server process
This example hosts one match on an already-loaded map. Call Start Managed Server once the dedicated server’s world is ready; it activates the session when GameLift assigns it. If your game loads a map per assignment, delay Activate Game Session until that load finishes.
Loading the interactive viewer.
Server module dependencies: AWSIKGameLiftServers, BetideCore, Engine. This module supports Editor and Server targets only; keep these calls out of client modules.
// GameLiftServerGameInstance.h
#pragma once
#include "Engine/GameInstance.h"
#include "Subsystems/AWSIKGameLiftServersSubsystem.h"
#include "GameLiftServerGameInstance.generated.h"
UCLASS()
class UGameLiftServerGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void StartManagedServer();
UFUNCTION(BlueprintCallable)
void EndManagedServer();
private:
UPROPERTY(Transient)
TObjectPtr<UAWSIKGameLiftServersSubsystem> Server;
bool bStartupRequested = false;
bool bStopping = false;
UFUNCTION()
void OnAssignedSession(const FAWSIKGameLiftServerGameSession &GameSession);
void ReleaseGameLiftSdkAndExit();
};
// GameLiftServerGameInstance.cpp
#include "GameLiftServerGameInstance.h"
#include "AWSIKGameLiftServers.h"
#include "Libraries/AWSIKGameLiftServersLibrary.h"
void UGameLiftServerGameInstance::StartManagedServer()
{
if (!IsRunningDedicatedServer() || bStartupRequested)
{
return;
}
bStartupRequested = true;
Server = GetSubsystem<UAWSIKGameLiftServersSubsystem>();
if (!Server)
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("GameLift server subsystem unavailable"));
UAWSIKGameLiftServersLibrary::RequestServerExit(false);
return;
}
FAWSIKGameLiftServerError Error;
if (!Server->InitSdk(Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
ReleaseGameLiftSdkAndExit();
return;
}
Server->OnStartGameSession.AddUniqueDynamic(this, &ThisClass::OnAssignedSession);
Server->OnProcessTerminate.AddUniqueDynamic(this, &ThisClass::EndManagedServer);
Server->SetHealthy(true);
FAWSIKGameLiftServerProcessParameters Parameters;
FString Address;
UAWSIKGameLiftServersLibrary::GetServerBindAddressAndPort(Address, Parameters.Port);
Parameters.LogPaths.Add(UAWSIKGameLiftServersLibrary::GetLogFilePath());
if (!Server->ProcessReady(Parameters, Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
EndManagedServer();
return;
}
UE_LOG(LogAWSIKGameLiftServers, Log, TEXT("GameLift process ready; waiting for a session"));
}
void UGameLiftServerGameInstance::OnAssignedSession(
const FAWSIKGameLiftServerGameSession &GameSession)
{
UE_LOG(LogAWSIKGameLiftServers, Log, TEXT("%s"), *GameSession.GameSessionId);
FAWSIKGameLiftServerError Error;
if (!Server->ActivateGameSession(Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
EndManagedServer();
return;
}
UE_LOG(LogAWSIKGameLiftServers, Log, TEXT("GameLift session active"));
}
void UGameLiftServerGameInstance::EndManagedServer()
{
if (bStopping || !Server)
{
return;
}
bStopping = true;
Server->SetHealthy(false);
FAWSIKGameLiftServerError Error;
if (!Server->ProcessEnding(Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
}
ReleaseGameLiftSdkAndExit();
}
void UGameLiftServerGameInstance::ReleaseGameLiftSdkAndExit()
{
FAWSIKGameLiftServerError Error;
if (!Server->Destroy(Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
}
UAWSIKGameLiftServersLibrary::RequestServerExit(false);
}Use a server-only Game Instance, excluded from client cooks, with GameLiftStartupRequested and GameLiftStopping (Boolean, false). The example reads -port= (or the GameLift Servers port setting) and the current log file path; that port must match the actual server listener. It exits immediately on termination. Finish any game-specific saving or player cleanup before End Managed Server.
Auto Initialize On Dedicated Server and Auto Process Ready are off by default. Enable them only if their timing suits your startup; do not also run the manual startup calls. They do not replace your session-start and termination handlers.
Place and reserve a session
Run this Game Instance flow in a trusted Unreal backend. It allocates an eight-slot server and reserves one player’s slot. Pass a valid player handle and a unique placement key; keep that key to reconcile a request whose result is lost.
Loading the interactive viewer.
| Variable | Type / default |
|---|---|
| PlacementBusy, HasAllocatedServer | Boolean / false |
| PlacementPolls | Integer / 0 |
| AllocatedServer | Crossplay Server Handle |
| ReservedPlayer | Crossplay Player Handle |
| PlayerConnection | Crossplay Connect Info |
Dependencies: UCIKCore, BetideCore, AWSIKGameLift, Engine.
// GameLiftPlacementGameInstance.h
#pragma once
#include "Engine/GameInstance.h"
#include "Hosting/UCIKHostingSubsystem.h"
#include "TimerManager.h"
#include "GameLiftPlacementGameInstance.generated.h"
UCLASS()
class UGameLiftPlacementGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void PlaceGameLiftForPlayer(const FUCIK_PlayerHandle &Player, const FString &PlacementKey);
UFUNCTION(BlueprintCallable)
void EndAllocatedMatch();
UPROPERTY(BlueprintReadOnly)
FUCIK_ServerHandle AllocatedServer;
UPROPERTY(BlueprintReadOnly)
FUCIK_PlayerHandle ReservedPlayer;
UPROPERTY(BlueprintReadOnly)
FUCIK_ConnectInfo PlayerConnection;
private:
UPROPERTY(Transient)
TObjectPtr<UUCIKHostingSubsystem> Hosting;
bool bPlacementBusy = false;
bool bHasAllocatedServer = false;
int32 PlacementPolls = 0;
FTimerHandle PollTimer;
void PollGameLiftPlacement();
void ReservePlayer();
void ReleaseAllocatedGameLiftServer();
};
// GameLiftPlacementGameInstance.cpp
#include "GameLiftPlacementGameInstance.h"
#include "Providers/AWSIKGameLiftProviderIds.h"
#include "UCIKCore.h"
void UGameLiftPlacementGameInstance::PlaceGameLiftForPlayer(const FUCIK_PlayerHandle &Player,
const FString &PlacementKey)
{
if (bPlacementBusy || bHasAllocatedServer)
{
UE_LOG(LogUCIK, Warning, TEXT("Finish or release the current allocation first"));
return;
}
Hosting = GetSubsystem<UUCIKHostingSubsystem>();
if (!Hosting || !Player.IsValid())
{
UE_LOG(LogUCIK, Error, TEXT("Hosting subsystem and a valid player handle are required"));
return;
}
bPlacementBusy = true;
ReservedPlayer = Player;
PlacementPolls = 0;
FUCIK_ServerRequest Request;
Request.Provider = BetideProviderIds::GameLift;
Request.MaxPlayers = 8;
Request.IdempotencyKey = PlacementKey;
Hosting->RequestServer(Request,
FBetideCallback<FUCIK_ServerHandle>::CreateWeakLambda(
this, [this](const TBetideResult<FUCIK_ServerHandle> &Result) {
if (!Result.bOk)
{
UE_LOG(LogUCIK, Error, TEXT("%s"), *Result.Error.Message);
bPlacementBusy = false;
return;
}
AllocatedServer = Result.GetValue();
bHasAllocatedServer = true;
PollGameLiftPlacement();
}));
}
void UGameLiftPlacementGameInstance::PollGameLiftPlacement()
{
if (PlacementPolls >= 60)
{
UE_LOG(LogUCIK, Warning, TEXT("Readiness poll limit reached; releasing the allocation"));
ReleaseAllocatedGameLiftServer();
return;
}
++PlacementPolls;
Hosting->GetServerStatus(
AllocatedServer, FBetideCallback<FUCIK_ServerStatus>::CreateWeakLambda(
this, [this](const TBetideResult<FUCIK_ServerStatus> &Result) {
if (!Result.bOk)
{
UE_LOG(LogUCIK, Error, TEXT("%s"), *Result.Error.Message);
ReleaseAllocatedGameLiftServer();
return;
}
const FUCIK_ServerStatus &Status = Result.GetValue();
switch (Status.Phase)
{
case EUCIK_ServerPhase::Ready:
ReservePlayer();
return;
case EUCIK_ServerPhase::Unknown:
case EUCIK_ServerPhase::Requested:
case EUCIK_ServerPhase::Provisioning:
GetTimerManager().SetTimer(PollTimer, this,
&ThisClass::PollGameLiftPlacement,
2.0f, false);
return;
default:
UE_LOG(LogUCIK, Error, TEXT("%s"), *Status.RawStatus);
ReleaseAllocatedGameLiftServer();
return;
}
}));
}
void UGameLiftPlacementGameInstance::ReservePlayer()
{
Hosting->RequestPlayerSlots(
AllocatedServer, {ReservedPlayer},
FBetideCallback<TArray<FUCIK_ConnectInfo>>::CreateWeakLambda(
this, [this](const TBetideResult<TArray<FUCIK_ConnectInfo>> &Result) {
if (!Result.bOk)
{
UE_LOG(LogUCIK, Error, TEXT("%s"), *Result.Error.Message);
ReleaseAllocatedGameLiftServer();
return;
}
if (Result.GetValue().Num() != 1)
{
UE_LOG(LogUCIK, Error, TEXT("Expected exactly one player reservation"));
ReleaseAllocatedGameLiftServer();
return;
}
PlayerConnection = Result.GetValue()[0];
bPlacementBusy = false;
UE_LOG(LogUCIK, Log,
TEXT("Player reservation ready; deliver PlayerConnection to this player"));
}));
}
void UGameLiftPlacementGameInstance::EndAllocatedMatch()
{
if (bPlacementBusy || !bHasAllocatedServer)
{
UE_LOG(LogUCIK, Warning,
TEXT("Wait for the operation to finish; an allocation is required"));
return;
}
ReleaseAllocatedGameLiftServer();
}
void UGameLiftPlacementGameInstance::ReleaseAllocatedGameLiftServer()
{
bPlacementBusy = true;
GetTimerManager().ClearTimer(PollTimer);
Hosting->TerminateServer(
AllocatedServer,
FBetideCallbackVoid::CreateWeakLambda(this, [this](const TBetideResult<void> &Result) {
bPlacementBusy = false;
if (!Result.bOk)
{
UE_LOG(LogUCIK, Error, TEXT("%s; allocation retained, retry EndAllocatedMatch"),
*Result.Error.Message);
return;
}
bHasAllocatedServer = false;
AllocatedServer = {};
PlayerConnection = {};
UE_LOG(LogUCIK, Log, TEXT("Allocation released"));
}));
}The flow checks readiness up to 60 times, waiting two seconds between responses. Network time is additional. Leave the request’s Player Ids empty here: reservations happen after readiness.
Deliver Player Connection, including its token, only to Reserved Player through your authenticated backend. For more players, reserve one per call on the same allocation; the unified result does not include player IDs for matching a batch of tokens.
Call End Allocated Match when the match ends. Failed cleanup retains the handle for retry. Persist allocation ownership in your backend so a process restart cannot lose it.
Validate player reservations
Each player needs their own Player Session Id. Send PlayerConnection.Token through your authenticated admission handshake: ToUrl() and Travel To Crossplay Connect Info do not transmit it.
Keep one reservation component per connection on a persistent server actor. After Validate Player Reservation, admit the player only when Last Action Succeeded is true; otherwise reject the connection with Last Error. These calls are synchronous. Your game supplies the handshake and connection rejection.
Loading the interactive viewer.
| Variable | Type / default |
|---|---|
| HasReservation, LastActionSucceeded | Boolean / false |
| PlayerSessionId, LastError | String / empty |
Dependencies: AWSIKGameLiftServers, BetideCore, Engine; Editor and Server targets only.
// GameLiftReservationComponent.h
#pragma once
#include "Components/ActorComponent.h"
#include "GameLiftReservationComponent.generated.h"
UCLASS(ClassGroup = (Game), meta = (BlueprintSpawnableComponent))
class UGameLiftReservationComponent : public UActorComponent
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void ValidatePlayerReservation(const FString &ReservationToken);
UFUNCTION(BlueprintCallable)
void ReleasePlayerReservation();
UPROPERTY(BlueprintReadOnly)
bool HasReservation = false;
UPROPERTY(BlueprintReadOnly)
bool LastActionSucceeded = false;
UPROPERTY(BlueprintReadOnly)
FString LastError;
private:
FString PlayerSessionId;
bool BeginAction();
void Fail(const FString &Message);
};
// GameLiftReservationComponent.cpp
#include "GameLiftReservationComponent.h"
#include "AWSIKGameLiftServers.h"
#include "Engine/GameInstance.h"
#include "Engine/World.h"
#include "Subsystems/AWSIKGameLiftServersSubsystem.h"
void UGameLiftReservationComponent::Fail(const FString &Message)
{
LastError = Message;
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *LastError);
}
bool UGameLiftReservationComponent::BeginAction()
{
LastActionSucceeded = false;
LastError.Reset();
if (!GetWorld() || GetWorld()->GetNetMode() != NM_DedicatedServer)
{
Fail(TEXT("Reservation calls must run on the dedicated server"));
return false;
}
return true;
}
void UGameLiftReservationComponent::ValidatePlayerReservation(const FString &ReservationToken)
{
if (!BeginAction())
return;
if (HasReservation)
{
Fail(TEXT("This connection already owns a reservation"));
return;
}
const UGameInstance *GameInstance = GetWorld()->GetGameInstance();
auto *Server =
GameInstance ? GameInstance->GetSubsystem<UAWSIKGameLiftServersSubsystem>() : nullptr;
if (!Server)
{
Fail(TEXT("GameLift server subsystem unavailable"));
return;
}
FAWSIKGameLiftServerError Error;
if (!Server->AcceptPlayerSession(ReservationToken, Error))
{
Fail(Error.Common.Message);
return;
}
PlayerSessionId = ReservationToken;
HasReservation = true;
LastActionSucceeded = true;
}
void UGameLiftReservationComponent::ReleasePlayerReservation()
{
if (!BeginAction())
return;
if (!HasReservation)
{
Fail(TEXT("This connection has no accepted reservation"));
return;
}
const UGameInstance *GameInstance = GetWorld()->GetGameInstance();
auto *Server =
GameInstance ? GameInstance->GetSubsystem<UAWSIKGameLiftServersSubsystem>() : nullptr;
if (!Server)
{
Fail(TEXT("GameLift server subsystem unavailable"));
return;
}
FAWSIKGameLiftServerError Error;
if (!Server->RemovePlayerSession(PlayerSessionId, Error))
{
Fail(Error.Common.Message);
return;
}
HasReservation = false;
PlayerSessionId.Reset();
LastActionSucceeded = true;
}Call Release Player Reservation when the player leaves, or if admission fails after acceptance. A failed release keeps the token for retry; keep the component alive until cleanup succeeds. Initialize and activate the GameLift session before accepting players. Keep this component server-only and do not replicate reservation tokens.
FlexMatch
Create a rule set and matchmaking configuration in AWS. Connect the configuration to a game-session queue if FlexMatch should also allocate servers. Set Default Matchmaking Configuration Name and enable Register Flex Match Provider. Matchmaking Region Override selects the region containing that configuration.
Unified FlexMatch example blocked: the current MIK result drops the per-player reservation map and formats the server address incorrectly. Do not use Find Match And Travel for GameLift until these are fixed.
Blueprint example
For a backend using the lower-level GameLift API, keep the ticket ID, handle acceptance, and read Describe Matchmaking → Game Session Connection Info → Matched Player Sessions. Match each returned player ID to its reservation token and deliver it only to that player. AWS returns reservations for the players in the original ticket. AWS connection result
The unified Accept Match call answers for all players on the ticket; collect party approval first. Cancel Matchmaking ends the local queue without reporting whether AWS successfully stopped the remote ticket.
Teams, attributes, and latency
Players with a nonnegative TeamIndex are sent as team-<index>. Others use Default Team Name, initially players. These names must match the rule set.
The unified adapter applies the ticket’s shared attributes and latency map to every player. Use the lower-level GameLift player models when your request needs different values for each party member. Local queue size and skill settings do not rewrite the remote rule set.
Backfill an existing match
Use this flow after the dedicated server has activated a FlexMatch-created session. Pass its matchmaking configuration ARN and the current roster, including each player’s ID, assigned team, attributes, and latency values. These values must match the rule set. The server wrapper accepts 1–40 players.
Only one backfill request can run per server process. Stop the previous request before replacing its roster; use this flow for manual backfill. AWS backfill lifecycle
Loading the interactive viewer.
| Variable | Type / default |
|---|---|
| BackfillActive | Boolean / false |
| BackfillTicketId, BackfillConfigArn, BackfillSessionArn | String / empty |
| LastBackfillUpdate | AWSIK Game Lift Server Update Game Session |
Add this flow to your server Game Instance. Dependencies: AWSIKGameLiftServers, BetideCore, Engine; Editor and Server targets only.
// GameLiftBackfillGameInstance.h
#pragma once
#include "Engine/GameInstance.h"
#include "Subsystems/AWSIKGameLiftServersSubsystem.h"
#include "GameLiftBackfillGameInstance.generated.h"
UCLASS()
class UGameLiftBackfillGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void BeginServerBackfill(const FString &ConfigurationArn,
const TArray<FAWSIKGameLiftServerPlayer> &CurrentPlayers);
UFUNCTION(BlueprintCallable)
void StopServerBackfill();
UPROPERTY(BlueprintReadOnly)
bool BackfillActive = false;
UPROPERTY(BlueprintReadOnly)
FAWSIKGameLiftServerUpdateGameSession LastBackfillUpdate;
private:
UPROPERTY(Transient)
TObjectPtr<UAWSIKGameLiftServersSubsystem> Server;
FString BackfillTicketId;
FString BackfillConfigArn;
FString BackfillSessionArn;
UFUNCTION()
void OnUpdatedSession(const FAWSIKGameLiftServerUpdateGameSession &Update);
void FinishServerBackfill();
};
// GameLiftBackfillGameInstance.cpp
#include "GameLiftBackfillGameInstance.h"
#include "AWSIKGameLiftServers.h"
#include "Engine/World.h"
void UGameLiftBackfillGameInstance::BeginServerBackfill(
const FString &ConfigurationArn, const TArray<FAWSIKGameLiftServerPlayer> &CurrentPlayers)
{
if (!GetWorld() || GetWorld()->GetNetMode() != NM_DedicatedServer)
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("Backfill must run on the dedicated server"));
return;
}
if (BackfillActive)
{
UE_LOG(LogAWSIKGameLiftServers, Warning,
TEXT("Stop the current backfill request before replacing it"));
return;
}
Server = GetSubsystem<UAWSIKGameLiftServersSubsystem>();
if (!Server)
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("GameLift server subsystem unavailable"));
return;
}
FAWSIKGameLiftServerError Error;
if (!Server->GetGameSessionId(BackfillSessionArn, Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
return;
}
BackfillConfigArn = ConfigurationArn;
BackfillTicketId = FGuid::NewGuid().ToString();
LastBackfillUpdate = {};
Server->OnUpdateGameSession.AddUniqueDynamic(this, &ThisClass::OnUpdatedSession);
BackfillActive = true;
FAWSIKStartMatchBackfillRequest Request;
Request.TicketId = BackfillTicketId;
Request.MatchmakingConfigurationArn = BackfillConfigArn;
Request.GameSessionArn = BackfillSessionArn;
Request.Players = CurrentPlayers;
FAWSIKStartMatchBackfillResult Result;
if (!Server->StartMatchBackfill(Request, Result, Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
FinishServerBackfill();
return;
}
UE_LOG(LogAWSIKGameLiftServers, Log,
TEXT("Backfill submitted; waiting for the matching session update"));
}
void UGameLiftBackfillGameInstance::OnUpdatedSession(
const FAWSIKGameLiftServerUpdateGameSession &Update)
{
if (!BackfillActive || Update.BackfillTicketId != BackfillTicketId)
return;
LastBackfillUpdate = Update;
switch (Update.UpdateReason)
{
case EAWSIKGameLiftServerUpdateReason::MatchmakingDataUpdated:
UE_LOG(LogAWSIKGameLiftServers, Log,
TEXT("Backfill complete; apply LastBackfillUpdate game-session data"));
break;
case EAWSIKGameLiftServerUpdateReason::BackfillFailed:
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("Backfill failed"));
break;
case EAWSIKGameLiftServerUpdateReason::BackfillTimedOut:
UE_LOG(LogAWSIKGameLiftServers, Warning, TEXT("Backfill timed out"));
break;
case EAWSIKGameLiftServerUpdateReason::BackfillCancelled:
UE_LOG(LogAWSIKGameLiftServers, Log, TEXT("Backfill cancelled"));
break;
default:
UE_LOG(LogAWSIKGameLiftServers, Warning,
TEXT("Unknown update reason; retain the ticket until stopped or completed"));
return;
}
FinishServerBackfill();
}
void UGameLiftBackfillGameInstance::StopServerBackfill()
{
if (!BackfillActive)
{
UE_LOG(LogAWSIKGameLiftServers, Warning, TEXT("No active backfill request"));
return;
}
FAWSIKStopMatchBackfillRequest Request;
Request.TicketId = BackfillTicketId;
Request.MatchmakingConfigurationArn = BackfillConfigArn;
Request.GameSessionArn = BackfillSessionArn;
FAWSIKGameLiftServerError Error;
if (!Server->StopMatchBackfill(Request, Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error,
TEXT("%s; ticket retained, retry StopServerBackfill"), *Error.Common.Message);
return;
}
UE_LOG(LogAWSIKGameLiftServers, Log, TEXT("Backfill stopped"));
FinishServerBackfill();
}
void UGameLiftBackfillGameInstance::FinishServerBackfill()
{
BackfillActive = false;
BackfillTicketId.Reset();
Server->OnUpdateGameSession.RemoveDynamic(this, &ThisClass::OnUpdatedSession);
}Start Match Backfill success means the request was submitted. On Matchmaking Data Updated, use LastBackfillUpdate.GameSession.MatchmakerData to apply new team assignments and player attributes; new connections still need player-session validation. Failure, timeout, and cancellation end that request. AWS session updates
Call Stop Server Backfill when the roster changes, the match fills, or the server is shutting down. Failed stops retain the ticket for retry. Complete backfill cleanup before ending the server process.
Anywhere fleets
This example initializes an Anywhere server without the GameLift Agent. Create an Anywhere fleet and register the compute first. Your launcher supplies these values at runtime:
| Input | Source |
|---|---|
| Web Socket Url | GameLiftServerSdkEndpoint returned by Register Compute |
| Auth Token | An unexpired token from Get Compute Auth Token for this compute |
| Fleet Id | The fleet containing the registered compute |
| Process Id | A unique ID for this running server process |
| Host Id | The ComputeName used during registration |
Keep the token in the server’s runtime environment; do not save it in Blueprint defaults or client builds. AWS Anywhere setup
Loading the interactive viewer.
Game Instance variables: GameLiftStartupRequested and GameLiftStopping (Boolean, false).
Dependencies: AWSIKGameLiftServers, BetideCore, Engine; Editor and Server targets only.
// GameLiftAnywhereGameInstance.h
#pragma once
#include "Engine/GameInstance.h"
#include "Subsystems/AWSIKGameLiftServersSubsystem.h"
#include "GameLiftAnywhereGameInstance.generated.h"
UCLASS()
class UGameLiftAnywhereGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void StartAnywhereServer(const FString &WebSocketUrl, const FString &AuthToken,
const FString &FleetId, const FString &ProcessId,
const FString &HostId);
UFUNCTION(BlueprintCallable)
void EndAnywhereServer();
private:
UPROPERTY(Transient)
TObjectPtr<UAWSIKGameLiftServersSubsystem> Server;
bool bStartupRequested = false;
bool bStopping = false;
UFUNCTION()
void OnAssignedSession(const FAWSIKGameLiftServerGameSession &GameSession);
void ReleaseGameLiftSdkAndExit();
};
// GameLiftAnywhereGameInstance.cpp
#include "GameLiftAnywhereGameInstance.h"
#include "AWSIKGameLiftServers.h"
#include "Libraries/AWSIKGameLiftServersLibrary.h"
void UGameLiftAnywhereGameInstance::StartAnywhereServer(const FString &WebSocketUrl,
const FString &AuthToken,
const FString &FleetId,
const FString &ProcessId,
const FString &HostId)
{
if (!IsRunningDedicatedServer() || bStartupRequested)
{
return;
}
bStartupRequested = true;
Server = GetSubsystem<UAWSIKGameLiftServersSubsystem>();
if (!Server)
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("GameLift server subsystem unavailable"));
UAWSIKGameLiftServersLibrary::RequestServerExit(false);
return;
}
FAWSIKGameLiftServerError Error;
FAWSIKGameLiftServerParameters Parameters;
Parameters.WebSocketUrl = WebSocketUrl;
Parameters.AuthToken = AuthToken;
Parameters.FleetId = FleetId;
Parameters.ProcessId = ProcessId;
Parameters.HostId = HostId;
if (!Server->InitSdkForAnywhere(Parameters, Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
ReleaseGameLiftSdkAndExit();
return;
}
Server->OnStartGameSession.AddUniqueDynamic(this, &ThisClass::OnAssignedSession);
Server->OnProcessTerminate.AddUniqueDynamic(this, &ThisClass::EndAnywhereServer);
Server->SetHealthy(true);
FAWSIKGameLiftServerProcessParameters ReadyParameters;
FString Address;
UAWSIKGameLiftServersLibrary::GetServerBindAddressAndPort(Address, ReadyParameters.Port);
ReadyParameters.LogPaths.Add(UAWSIKGameLiftServersLibrary::GetLogFilePath());
if (!Server->ProcessReady(ReadyParameters, Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
EndAnywhereServer();
return;
}
UE_LOG(LogAWSIKGameLiftServers, Log, TEXT("GameLift process ready; waiting for a session"));
}
void UGameLiftAnywhereGameInstance::OnAssignedSession(
const FAWSIKGameLiftServerGameSession &GameSession)
{
UE_LOG(LogAWSIKGameLiftServers, Log, TEXT("%s"), *GameSession.GameSessionId);
FAWSIKGameLiftServerError Error;
if (!Server->ActivateGameSession(Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
EndAnywhereServer();
return;
}
UE_LOG(LogAWSIKGameLiftServers, Log, TEXT("GameLift session active"));
}
void UGameLiftAnywhereGameInstance::EndAnywhereServer()
{
if (bStopping || !Server)
{
return;
}
bStopping = true;
Server->SetHealthy(false);
FAWSIKGameLiftServerError Error;
if (!Server->ProcessEnding(Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
}
ReleaseGameLiftSdkAndExit();
}
void UGameLiftAnywhereGameInstance::ReleaseGameLiftSdkAndExit()
{
FAWSIKGameLiftServerError Error;
if (!Server->Destroy(Error))
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
}
UAWSIKGameLiftServersLibrary::RequestServerExit(false);
}Call Start Anywhere Server once, after the match map and server listener are ready. Keep automatic initialization/readiness off. This flow activates assigned sessions and exits on termination; finish game-specific cleanup before End Anywhere Server. As in managed startup, the reported port must match the listener.
Health, shutdown, and diagnostics
Health starts as true. Drive Set Healthy from checks that reflect whether this process can host a match.
This diagnostic shutdown handler reports the termination deadline, then exits immediately. Install it after SDK initialization and before Process Ready, replacing the earlier startup example’s On Process Terminate handler. Keep one shutdown path per process.
Loading the interactive viewer.
Game Instance variables: ShutdownHandlerInstalled and ServerShuttingDown (Boolean, false), and TerminationDeadline (Integer64, 0; Unix seconds).
Merge this handler into your server Game Instance. Dependencies: AWSIKGameLiftServers, BetideCore, Engine; Editor and Server targets only.
// GameLiftShutdownGameInstance.h
#pragma once
#include "Engine/GameInstance.h"
#include "Subsystems/AWSIKGameLiftServersSubsystem.h"
#include "GameLiftShutdownGameInstance.generated.h"
UCLASS()
class UGameLiftShutdownGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void InstallShutdownHandler();
UFUNCTION(BlueprintCallable)
void EndGameLiftProcess();
UPROPERTY(BlueprintReadOnly)
int64 TerminationDeadline = 0;
private:
UPROPERTY(Transient)
TObjectPtr<UAWSIKGameLiftServersSubsystem> Server;
bool ShutdownHandlerInstalled = false;
bool ServerShuttingDown = false;
UFUNCTION()
void OnTerminationRequested();
};
// GameLiftShutdownGameInstance.cpp
#include "GameLiftShutdownGameInstance.h"
#include "AWSIKGameLiftServers.h"
#include "Engine/World.h"
#include "Libraries/AWSIKGameLiftServersLibrary.h"
#include "Misc/DateTime.h"
void UGameLiftShutdownGameInstance::InstallShutdownHandler()
{
if (!GetWorld() || GetWorld()->GetNetMode() != NM_DedicatedServer || ShutdownHandlerInstalled)
return;
Server = GetSubsystem<UAWSIKGameLiftServersSubsystem>();
if (!Server)
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("GameLift server subsystem unavailable"));
return;
}
Server->OnProcessTerminate.AddUniqueDynamic(this, &ThisClass::OnTerminationRequested);
ShutdownHandlerInstalled = true;
}
void UGameLiftShutdownGameInstance::OnTerminationRequested()
{
if (ServerShuttingDown)
return;
FAWSIKGameLiftServerError Error;
if (Server->GetTerminationTime(TerminationDeadline, Error))
{
UE_LOG(LogAWSIKGameLiftServers, Log, TEXT("GameLift termination deadline: %s"),
*FDateTime::FromUnixTimestamp(TerminationDeadline).ToIso8601());
}
else
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
}
EndGameLiftProcess();
}
void UGameLiftShutdownGameInstance::EndGameLiftProcess()
{
if (!GetWorld() || GetWorld()->GetNetMode() != NM_DedicatedServer || ServerShuttingDown)
return;
ServerShuttingDown = true;
Server = GetSubsystem<UAWSIKGameLiftServersSubsystem>();
if (!Server)
{
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("GameLift server subsystem unavailable"));
UAWSIKGameLiftServersLibrary::RequestServerExit(false);
return;
}
Server->SetHealthy(false);
Server->OnProcessTerminate.RemoveDynamic(this, &ThisClass::OnTerminationRequested);
FAWSIKGameLiftServerError Error;
if (!Server->ProcessEnding(Error))
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
if (!Server->Destroy(Error))
UE_LOG(LogAWSIKGameLiftServers, Error, TEXT("%s"), *Error.Common.Message);
UAWSIKGameLiftServersLibrary::RequestServerExit(false);
}For a graceful drain, stop admitting players and finish your game’s saving, player-session release, and backfill cleanup before End Game Lift Process. Complete that work before the reported deadline; reading the deadline does not extend it. This example does not wait for cleanup tasks.
For an unready fleet, check executable arguments, staged SDK libraries, port permissions, and ProcessReady errors. For a stuck placement, check queue destinations and capacity. For rejected players, check token ownership, expiry, and duplicate use. Capture Error.Common and configured server logs; do not log reservation tokens.