Integrating Netcode with AWS Gamelift

Hi,
I could not really find a specific answer for my question in any forum which is why I list my issue here now. Basically, I want to use Unity Netcode and want to integrate my game on an AWS GameLift fleet. For testing purposes I created an ANYWHERE fleet and used the Bootstrap sample scene from unity netcode 2.1 to start with. Here, I added only one gameobject which has the following script attached:

using UnityEngine;
using System.Collections.Generic;

using Aws.GameLift.Server;

using Aws.GameLift.Server.Model;    
using System;
using Unity.Netcode;
using System.IO;
using System.Threading.Tasks;
using Amazon.Lambda;
using Amazon.Lambda.Model;
using Amazon;
using System.Net;
using System.Net.Sockets;
using Amazon.GameLift;
using Amazon.Runtime;
using Amazon.GameLift.Model;

namespace Assets.scripts.Multiplayer 
{
    /// <summary>
    /// THis is called by the server. The server must know whihc methods to call when the client wants to create aserver...
    /// </summary>
    public class GameLiftServerInitializer : MonoBehaviour
    {
        private string testToken = "MYTESTTOKENFORTHECORRESPONDINGFLEET";

        private string testFleet= "MYTESTFLEETID"; // hidden here so

        public bool isTest;

        public LaunchGameServer launchGameServer;

        private void Start()
        {
            Initiate();
        }

        public async Task Initiate()
        {
            //    ConfigureAppSettings();

            AWSConfigs.LoggingConfig.LogTo = LoggingOptions.None;
            AWSConfigs.LoggingConfig.LogResponses = ResponseLoggingOption.Never;
            AWSConfigs.LoggingConfig.LogMetrics = false;
            
            if (Application.platform == RuntimePlatform.WindowsEditor)
            {
            //    await GetAuthToken();
                UnityEngine.Debug.Log("platform is windows and you are running the test server");

                UnityEngine.Debug.Log("platform is windows Editor. This code should terminate here.");
                this.enabled = false;
                return;
            }
            else if (Application.platform == RuntimePlatform.WindowsPlayer)
            {

                string randomProcessId = CreateRandomProcessID();
        //        ConfigureLog4Net();
                UnityEngine.Debug.Log("platform is windows and you are running the test server");
                // Define the server parameters
                string webSocketUrl = "wss://eu-central-1.api.amazongamelift.com";
                string processId = randomProcessId;
                string fleetId = testFleet;
                string hostId = "NilsCompute"; // Replace with your registered compute name
                string authToken = testToken; // Retrieve this dynamically

                ServerParameters serverParameters = new ServerParameters(webSocketUrl, processId, hostId, fleetId, authToken);

                var localOutcome = GameLiftServerAPI.InitSDK(serverParameters);
                if (localOutcome.Success)
                {
                    Debug.Log("GameLift SDK initialized successfully.");
                }
                else
                {
                    Debug.LogError("Failed to initialize GameLift SDK.");
                    Application.Quit();
                }

            }
            else if (Application.platform == RuntimePlatform.LinuxServer || Application.platform == RuntimePlatform.LinuxPlayer  )
            {

                // Use extracted credentials
                
                string randomProcessId = CreateRandomProcessID();
                //        ConfigureLog4Net();
                UnityEngine.Debug.Log("platform is linux and you are running the test server");
                // Define the server parameters
                string webSocketUrl = "wss://eu-central-1.api.amazongamelift.com";
                string processId = randomProcessId;
                string fleetId = testFleet;
                string hostId = "NilsCompute"; // Replace with your registered compute name
                string authToken = testToken;

                ServerParameters serverParameters = new ServerParameters(webSocketUrl, processId, hostId, fleetId, authToken);

                var localOutcome = GameLiftServerAPI.InitSDK(serverParameters);
                if (localOutcome.Success)
                {
                    Debug.Log("GameLift SDK initialized successfully.");
                }
                else
                {
                    Debug.LogError("Failed to initialize GameLift SDK.");
                    Application.Quit();
                }


            }
            else if (Application.platform == RuntimePlatform.Android)
            {
                UnityEngine.Debug.Log("You are on android. This code is part of the server and thus it will not run here. Start disabling class");
                this.enabled = false;
                return;
            }
            else
            {
                Debug.LogError("Unsupported platform for GameLift server.");
                this.enabled = false;
                Application.Quit();
                return;
            }
            UnityEngine.Debug.Log("Initializing gamelift");
            
            InitializeGameLift();

        }

        string CreateRandomProcessID()
        {
            int length = 10;
            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
            System.Random random = new System.Random();
            char[] stringChars = new char[length];

            for (int i = 0; i < length; i++)
            {
                stringChars[i] = chars[random.Next(chars.Length)];
            }
            return new String(stringChars);
        }


        private void InitializeGameLift()
        {
            UnityEngine.Debug.Log("Initializing gamleift with execution methods");
            int availablePort = FindAvailablePort();
            if (!Directory.Exists("logs"))
            {
                Directory.CreateDirectory("logs");
            }

            ProcessParameters processParams = new ProcessParameters(
                onStartGameSession: OnStartGameSession, // gets the parsed attributes by the host and sets them on the server side. Calls setup server which launches netcode server
                onProcessTerminate: OnProcessTerminate,
                onHealthCheck: OnHealthCheck,
                onUpdateGameSession: UpdateGameSession,
                port: availablePort,
                logParameters: new LogParameters(new List<string>()
                {
                    "logs/server.log",
                    "logs/error.log"
                })
            );
            var processReadyOutcome = GameLiftServerAPI.ProcessReady(processParams);

            if (processReadyOutcome.Success)
            {
                Debug.Log("Gamelift setup successful.");
            }
            else
            {
                Debug.LogError("Failed to initialize GameLift SDK.");
                Application.Quit();
            }
        }

        private int FindAvailablePort()
        {
            const int maxAllowedPort = 60000; // GameLift's maximum allowed port
            const int minPort = 1027;         // Minimum port to avoid conflicts with reserved system ports

            int port = 0;
            TcpListener listener = null;

            try
            {
                for (int testPort = minPort; testPort <= maxAllowedPort; testPort++)
                {
                    listener = new TcpListener(IPAddress.Loopback, testPort);

                    // Attempt to bind to the test port
                    listener.Start();
                    port = ((IPEndPoint)listener.LocalEndpoint).Port;
                    listener.Stop();

                    Debug.Log($"Found available port: {port}");
                    return port;
                }
            }
            catch (SocketException ex)
            {
                Debug.LogError($"Error while searching for available port: {ex.Message}");
            }
            finally
            {
                listener?.Stop();
            }

            throw new Exception($"Failed to find an available port in the range {minPort}-{maxAllowedPort}");
        }



        // this is called initially...
        private async void OnStartGameSession(Aws.GameLift.Server.Model.GameSession gameSession)
        {
            Debug.Log($"Starting game session: {gameSession.GameSessionId}");

            // Retrieve GameProperties
            Dictionary<string, string> gameProperties = new Dictionary<string, string>();
            foreach (var prop in gameSession.GameProperties)
            {
                gameProperties[prop.Key] = prop.Value;
            }

            // Signal that the server is ready - after that: response.GameSession.Status == GameSessionStatus.ACTIVE
            GameLiftServerAPI.ActivateGameSession();
            UnityEngine.Debug.Log("session is activated");


            if (gameProperties.ContainsKey("Group"))
            {
                string group = gameProperties["Group"];
                string sessionId = gameSession.GameSessionId;
                await UpdateGameSessionInDynamoDB(gameSession.GameSessionId, group); // this tells the server aws that the corresponding gamesession for the corresponding group is active.
            }
            else
            {
                UnityEngine.Debug.Log("Could not find group in game properties. Automatic matching not possible");
            }


            if (NetworkManager.Singleton == null)
            {
                Debug.LogError("Network Manager is null");
                return;
            }
            // Start Unity Netcode server
            Debug.Log("Starting Unity Netcode server...");
            if (!NetworkManager.Singleton.IsServer)
            {
                NetworkManager.Singleton.StartServer();
                Debug.Log("Unity Netcode server started successfully.");
            }
            else
            {
                Debug.Log("Unity Netcode server is already running.");
            }

        }

        private void OnProcessTerminate()
        {

            //h here disconnect lambda function...
            if (NetworkManager.Singleton.IsServer)
            {
                NetworkManager.Singleton.Shutdown();
            }
            Debug.Log("Process termination requested by GameLift.");
            var ProcessEndingOutcome = GameLiftServerAPI.ProcessEnding();
            Application.Quit();
        }

        private bool OnHealthCheck()
        {
            Debug.Log("Health check received.");
         //   GameLiftServerAPI.ProcessEnding();
            return true;
        }

        void UpdateGameSession(UpdateGameSession updateGameSession)
        {
            UnityEngine.Debug.Log("OnUpdateGameSession called");
            return;
        }
        public async Task UpdateGameSessionInDynamoDB(string sessionId, string group, string status = "ACTIVE", string LambdaFunctionName = "headset_addSessionToTable")
        {
            // Initialize the Lambda client
            /// MAKE SURE TO ATTACH IAM ROLE TO GAMELIFT
            var lambdaClient = new AmazonLambdaClient(); // since this runs on aws the general environment variables should be set...

            // Create the payload
            var payload = new
            {
                sessionId = sessionId,
                group = group,
                status = status,
                creationTime = DateTime.UtcNow.ToString("o") // ISO 8601 format
            };

            // Invoke the Lambda function
            var request = new InvokeRequest
            {
                FunctionName = LambdaFunctionName,
                Payload = System.Text.Json.JsonSerializer.Serialize(payload)
            };

            try
            {
                var response = await lambdaClient.InvokeAsync(request);

                // Read the response
                using var reader = new System.IO.StreamReader(response.Payload);
                var responseBody = await reader.ReadToEndAsync();
                Console.WriteLine($"Lambda response: {responseBody}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error invoking Lambda function: {ex.Message}");
                throw;
            }
        }


    }

}


When I build the sccene as linux project I can see that the gamelift SDK is successfully initialized and that it also calls ProcessReady successfully. Thus, I have a lambda function on aws gamelift which just creates a gamesession on this fleet. As soon as I invoke it the method OnStartGameSession is called which Activates the session and there I integrated the logic for starting the Netcode server with : NetworkManager.Singleton.StartServer() . However, then I immediately get about 50 repeating error messages of this kind:
NullReferenceException: Object reference not set to an instance of an object
at Unity.Netcode.NetworkManager.NetworkUpdate (Unity.Netcode.NetworkUpdateStage updateStage) [0x000aa] in :0
at Unity.Netcode.NetworkUpdateLoop.RunNetworkUpdateStage (Unity.Netcode.NetworkUpdateStage updateStage) [0x00021] in :0
at Unity.Netcode.NetworkUpdateLoop+NetworkPostLateUpdate+<>c.b__0_0 () [0x00000] in :0

I am not exactly sure why because if i just ignore this class and call StartServer separately in a linux build it of course just starts it. So, I wanted to ask what is your take on it. I guess I just do something wrong, but I could also not find any kind of sample project on GitHub where someone tried to do that.

I am not familiar with GameLift, but it looks like it is creating a new process/thread which most likely means you are trying to invoke a static reference within the first application/process domain where you start the GameLift session’s process, but then within the new application/process domain you are trying to access the singleton that would no longer exist.

To verify this, you might try using something like AppDomain.CurrentDomain.FriendlyName and log that within InitializeGameLift and then log it again within OnStartGameSession… see if they are the same and if they are not then that would be the reason.

If they are not, then you might look through the AWS Unity plug-in?