Hi everyone,
We are currently creating multiple game sessions on GameLift Managed EC2 fleets. For instance, in only one fleet, we are running 20 game sesssions concurrently and with scale policy, GameLift creates new instances for new game sessions. Until this, everthing is fine. We want to do same things in GameLift Managed Containers service. I have followed all steps according to documentation, but we have a problem about port mapping. I am sharing two fleet with different configuration.
This is a code snippet from our game server. We assigned port as 9000 and pass it to WebSocketServer as parameter.
using System.Net.NetworkInformation;
using Aws.GameLift.Server;
using Aws.GameLift.Server.Model;
using Newtonsoft.Json;
using WebSocketSharp.Server;
namespace CasualA.Network.Server
{
public class ServerGameliftManager
{
private static WebSocketServer? _websocket;
public static MatchMakingResponse? MatchmakingResponse { get; private set; }
private ServerNetworkManager _serverNetworkManager;
public void Init(ServerNetworkManager serverNetworkManager)
{
LogSystemManager.Log("Server Gamelift manager init.", "ServerGameliftManager");
_serverNetworkManager = serverNetworkManager;
var initSdkOutcome = GameLiftServerAPI.InitSDK();
if (initSdkOutcome.Success)
{
LogSystemManager.Log("Gamelift Init Sdk Success", "ServerGameliftManager");
var port = 9000;
var processParameters = new ProcessParameters();
processParameters.OnStartGameSession += OnStartGameSession;
processParameters.OnProcessTerminate += OnProcessTerminate;
processParameters.OnHealthCheck += OnHealthCheck;
processParameters.OnUpdateGameSession += OnUpdateGameSession;
processParameters.Port = port;
processParameters.LogParameters = new LogParameters(new[]{LogSystemManager._logFilePath});
var processReadyOutcome = GameLiftServerAPI.ProcessReady(processParameters);
if (processReadyOutcome.Success)
{
_websocket = new WebSocketServer(port, false);
LogSystemManager.Log("Gamelift process ready success", "ServerGameliftManager");
return;
}
}
LogSystemManager.Log("Gamelift failed to initialize!", "ServerGameliftManager");
UnreadyServer();
}
private void OnStartGameSession(GameSession gameSession)
{
try
{
StartGame(gameSession);
return;
}
catch (Exception e)
{
LogSystemManager.LogError($"Start Game crashed unexpectedly: {e}","ServerGameliftManager");
}
UnreadyServer();
}
private bool OnHealthCheck()
{
return true;
}
private void OnUpdateGameSession(UpdateGameSession updateGameSession)
{
}
private void OnProcessTerminate()
{
LogSystemManager.Log("Terminate Game Session");
UnreadyServer();
}
private void StartGame(GameSession gameSession)
{
LogSystemManager.Log("ServerGameliftManager starts game", "ServerGameliftManager");
MatchmakingResponse = JsonConvert.DeserializeObject<MatchMakingResponse>(gameSession.GameSessionData);
LogSystemManager.Log($"MatchMaking Result: players count : {MatchmakingResponse.Players.Count}", "ServerGameliftManager");
LogSystemManager.Log($"Match ID : {MatchmakingResponse.MatchId}", "ServerGameliftManager");
LogSystemManager.Log($"Is Match Found : {MatchmakingResponse.MatchFound}", "ServerGameliftManager");
_serverNetworkManager.StartServer(_websocket, MatchmakingResponse.Players);
LogSystemManager.Log("ServerGameliftManager StartServer.", "ServerGameliftManager");
}
public static void ActivateGameSession()
{
var activateGameSessionOutcome = GameLiftServerAPI.ActivateGameSession();
if (activateGameSessionOutcome.Success)
{
LogSystemManager.Log("Gamelift activated game session!", "ServerGameliftManager");
}
}
public static void UnreadyServer()
{
_websocket?.Stop();
var processEndingOutcome = GameLiftServerAPI.ProcessEnding();
if (processEndingOutcome.Success)
{
LogSystemManager.Log($"Unready Server successful process ending!", "ServerGameliftManager");
var destroyOutcome = GameLiftServerAPI.Destroy();
if (destroyOutcome.Success)
{
LogSystemManager.Log($"Unready Server successfully destroyed!", "ServerGameliftManager");
Environment.Exit(0);
}
}
LogSystemManager.Log($"Unready Server failed to destroy!", "ServerGameliftManager");
Environment.Exit(1);
}
}
}
This is our Dockerfile of our game server.
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
ARG BUILD_NUMBER
ARG CONFIGURATION=Dev
COPY server/match-legends-gameplay-server .
COPY Assets/Scripts/Core ../../../Assets/Scripts/Core
RUN sed -i "/ApplicationVersion/s/\".*\"/\"$BUILD_NUMBER\"/" match-legends-gameplay-server/Server/Managers/ServerGameManager.cs
RUN dotnet publish "match-legends-gameplay-server/match-legends-gameplay-server.csproj" -c $CONFIGURATION -r linux-x64 --self-contained true -p:PublishSingleFile=true -o /app/publish
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["./match-legends-gameplay-server"]
This is our build pipeline and ecr commands that we used to push ECR.
docker buildx build --build-arg BUILD_NUMBER=3000 --platform=linux/amd64 -t gamelift-server-dev .
docker tag gamelift-server-dev:latest 12345.dkr.ecr.us-east-1.amazonaws.com/gamelift-server-dev-test:latest
docker push 12345.dkr.ecr.us-east-1.amazonaws.com/gamelift-server-dev-test:latest
I have one group definition.
Total Memory Limit: 512 Mib
Total vCPU limit: 0.5
There is only one definition inside it. It is a game server container and uses ECR uri. Version 5.2.0. Protocol TCP -> Port Range 9000-9000
I have two fleets and the first one like this:
- Connection port range 9000-9000
- On Demand c5a.large instance
- instance inbound permissions TCP 9000-10000
When I created a new game session, it assigned only one port as 9000 because there is no other option,t then when I tried to connect, it was succesfull.
wscat -c ws://ec2-100-26-198-93.compute-1.amazonaws.com:9000/pvp
Connected (press CTRL+C to quit)
second one like this:
- Connection port range 2000-2000
- On Demand c5a.large instance
- instance inbound permissions TCP 2000-3000
When I created a new game session, it assigned only one port as 2000 because there is no other option,t then when I tried to connect, it was succesfull.
wscat -c ws://ec2-3-91-87-58.compute-1.amazonaws.com:2000/pvp
error: Unexpected server response: 400
After we talked with someone from AWS support, he said that if your game server uses port 9000 and your connection port range is 2000-3000 and we can fit 4 game servers on an instance, you will end up with something like 2001 -> 9000, 2002 -> 9000, 2003 -> 9000. 2004 -> 9000. So from the game server perspective it’s always the same port for all 4 servers. But from the client it will be different. So internal container port 9000, connection port range 2000-3000 and instance inbound permissions 2000-3000, all TCP should work
It means that port forwarding can be handled by AWS, but in my scenarios, we can see that I can connect with configuration like this: 9000 -> 9000 -> 9000. but I could not connect with this configuration: 2000 -> 2000 -> 9000.
I just wonder that what I am missing. Thanks in advance.