GameLift container port mapping issue

0

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.

1 Answer
0

The issue you're experiencing appears to be related to the port configuration in your GameLift Managed Containers setup. Here's what's happening and what you might be missing:

  1. Container Port Configuration: In your game server code, you're setting the port to 9000. This is the internal port that your game server is listening on inside the container.

  2. Fleet Configuration:

    • In your first fleet, you set the connection port range to 9000-9000, which matches the internal port of your game server. This works because the external port matches the internal port.
    • In your second fleet, you set the connection port range to 2000-2000, which doesn't match the internal port of your game server (9000).
  3. Port Mapping: GameLift should handle the port mapping between the external connection port and the internal container port. However, it seems that in your second configuration, this mapping isn't occurring as expected.

To resolve this issue, try the following:

  1. In your GameServerContainerDefinition, ensure that the PortConfiguration is set correctly. It should include the internal port your game server is using (9000 in this case).

  2. In your fleet configuration, set the ConnectionPortRange to the external ports you want to use (e.g., 2000-2000 or a wider range if needed).

  3. Make sure your instance inbound permissions cover both the connection port range and the internal container port (e.g., TCP 2000-3000 and 9000).

  4. In your container group definition, verify that the ContainerPortConfiguration is set up correctly to map the external ports to the internal port.

If you've done all of these and are still experiencing issues, it might be worth double-checking your GameLift SDK integration to ensure it's correctly reporting the port that the game server is listening on.

Remember, GameLift should handle the port mapping automatically, translating the external connection port to the internal container port. If this isn't happening, there might be a misconfiguration in your container group definition or fleet settings.

If the problem persists after verifying these settings, it would be advisable to reach out to AWS support again with the specific details of your configuration and the steps you've taken to troubleshoot.

Sources
ContainerPortConfiguration - Amazon GameLift
SupportContainerDefinitionInput - Amazon GameLift
UpdateContainerFleet - Amazon GameLift
GameServerContainerDefinitionInput - Amazon GameLift

profile picture
answered 10 days ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions