question

Martin K avatar image
Martin K asked

Connecting to UDP port?

I am trying to connect to a LiteNetLib server (https://github.com/RevenantX/LiteNetLib) for Multiplayer, with no success.

It works locally.

I can also connect to the multiplayer server and receive the http response as desribed in the guides here:

https://github.com/PlayFab/gsdkSamples/tree/master/WindowsRunnerCSharp

I use the IP address from that to attempt to connect to UDP.


However, it doesn't work. Do I need to do anything else on the server side to enable UDP connections?
Would be great if someone had a working example. Preferably with LiteNetLib, but I guess the same principle would apply to any kind of UDP setup.

I also added the UDP port in the build settings as:

CloudScriptsupport
udp-port.png (16.2 KiB)
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Sarah Zhang avatar image
Sarah Zhang answered

>> Do I need to do anything else on the server side to enable UDP connections?

To interact with PlayFab GSDK, your game server no need to do anything extra to “enable” UDP connections. For your case, you just need to modify the sample from the LiteNetLib official GitHub page to start the server.

Have you tried to follow this doc -- Locally debugging game servers and integration with PlayFab to debug the game server locally? If so, you can navigate to the path of OutputFolder in the MultiplayerSettings to check the PF_ConsoleLogs for error messages. Then you can do troubleshooting according to the logs.

We tried to do a simple interaction with PlayFab GSDK using the modified sample from the LiteNetLib official GitHub page. It can work fine in our testing environment. We use the latest MockVmAgent to test it within the container. You can refer to the following code and follow the Locally debugging game servers and integration with PlayFab to test it.

Besides, if you meet this error, "A fatal error occurred. The required library hostfxr.dll could not be found.", you can follow this thread to select the Self Contained as the Deployment Mode of your sever. We meet this issue about .net core run time in the testing environment. Please ignore it if your sample works fine within the container.

After your server works fine within the container with the MockVmAgent, you can try to deploy it on the PlayFab.

Server sample.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using LiteNetLib;
using LiteNetLib.Utils;
using Microsoft.Playfab.Gaming.GSDK.CSharp;
using Newtonsoft.Json;


namespace LitNewLibTestServer
{
    class Program
    {


        private static EventBasedNetListener listener = new EventBasedNetListener();
        private static NetManager server = new NetManager(listener);


        const string ListeningPortKey = "game_port";


        const string AssetFilePath = @"C:\Assets\testassetfile.txt";
        private const string GameCertAlias = "winRunnerTestCert";


        private static List<ConnectedPlayer> players = new List<ConnectedPlayer>();
        private static int requestCount = 0;


        private static bool _isActivated = false;
        private static string _assetFileText = String.Empty;
        private static string _installedCertThumbprint = String.Empty;
        private static DateTimeOffset _nextMaintenance = DateTimeOffset.MinValue;


        static void OnShutdown()
        {
            LogMessage("Shutting down...");


            server.Stop();
            Environment.Exit(0);
        }


        static bool IsHealthy()
        {
            // Should return whether this game server is healthy
            return true;
        }


        static void OnMaintenanceScheduled(DateTimeOffset time)
        {
            LogMessage($"Maintenance Scheduled at: {time}");
            _nextMaintenance = time;
        }


        static void Main(string[] args)
        {


            // GSDK Setup
            try
            {
                GameserverSDK.Start();
            }
            catch (Microsoft.Playfab.Gaming.GSDK.CSharp.GSDKInitializationException initEx)
            {
                LogMessage("Cannot start GSDK. Please make sure the MockAgent is running. ", false);
                LogMessage($"Got Exception: {initEx.ToString()}", false);
                return;
            }
            catch (Exception ex)
            {
                LogMessage($"Got Exception: {ex.ToString()}", false);
            }


            GameserverSDK.RegisterShutdownCallback(OnShutdown);
            GameserverSDK.RegisterHealthCallback(IsHealthy);
            GameserverSDK.RegisterMaintenanceCallback(OnMaintenanceScheduled);


            // Read our asset file
            if (File.Exists(AssetFilePath))
            {
                _assetFileText = File.ReadAllText(AssetFilePath);
            }


            IDictionary<string, string> initialConfig = GameserverSDK.getConfigSettings();


            // Start the http server
            if (initialConfig?.ContainsKey(ListeningPortKey) == true)
            {
                int listeningPort = int.Parse(initialConfig[ListeningPortKey]);


                server.Start(listeningPort /* port */);
            }
            else
            {
                LogMessage($"Cannot find {ListeningPortKey} in GSDK Config Settings. Please make sure the MockAgent is running " +
                           $"and that the MultiplayerSettings.json file includes {ListeningPortKey} as a GamePort Name.");
                return;
            }


            // Load our game certificate if it was installed
            if (initialConfig?.ContainsKey(GameCertAlias) == true)
            {
                string expectedThumbprint = initialConfig[GameCertAlias];
                X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
                store.Open(OpenFlags.ReadOnly);
                X509Certificate2Collection certificateCollection = store.Certificates.Find(X509FindType.FindByThumbprint, expectedThumbprint, false);


                if (certificateCollection.Count > 0)
                {
                    _installedCertThumbprint = certificateCollection[0].Thumbprint;
                }
                else
                {
                    LogMessage("Could not find installed game cert in LocalMachine\\My. Expected thumbprint is: " + expectedThumbprint);
                }
            }
            else
            {
                LogMessage("Config did not contain cert! Config is: " + string.Join(";", initialConfig.Select(x => x.Key + "=" + x.Value)));
            }


            Thread t = new Thread(ProcessConnection);
            t.Start();


            if (GameserverSDK.ReadyForPlayers())
            {
                _isActivated = true;


                // After allocation, we can grab the session cookie from the config
                IDictionary<string, string> activeConfig = GameserverSDK.getConfigSettings();


                if (activeConfig.TryGetValue(GameserverSDK.SessionCookieKey, out string sessionCookie))
                {
                    LogMessage($"The session cookie from the allocation call is: {sessionCookie}");
                }
            }
            else
            {
                // No allocation happened, the server is getting terminated (likely because there are too many already in standing by)
                LogMessage("Server is getting terminated.");
            }


        }




        private static void ProcessConnection()
        {
            listener.ConnectionRequestEvent += request =>
            {
                if (server.ConnectedPeersCount < 10 /* max connections */)
                {
                    request.AcceptIfKey("SomeConnectionKey");
                    players.Add(new ConnectedPlayer("gamer" + requestCount));
                    requestCount++;
                    GameserverSDK.UpdateConnectedPlayers(players);
                }


                else
                    request.Reject();
            };


            listener.PeerConnectedEvent += peer =>
            {
                LogMessage(string.Format("We got connection: {0}", peer.EndPoint)); // Show peer ip
                NetDataWriter writer = new NetDataWriter();                 // Create writer class
                writer.Put("Hello client!");                                // Put some string
                peer.Send(writer, DeliveryMethod.ReliableOrdered);          // Send with reliability
            };


            while (true)
            {
                server.PollEvents();
                Thread.Sleep(15);
            }
        }


        private static void LogMessage(string message, bool enableGSDKLogging = true)
        {
            Console.WriteLine(message);
            if (enableGSDKLogging)
            {
                GameserverSDK.LogMessage(message);
            }
        }
    }
}


Client sample.

using LiteNetLib;
using System;
using System.Threading;


namespace LiteNetLibProject
{
    class Program
    {
        static void Main(string[] args)
        {
            EventBasedNetListener listener = new EventBasedNetListener();
            NetManager client = new NetManager(listener);
            client.Start();


//port is the NodePort set in your MultiplayerSettings.json (default 56100)
            client.Connect("localhost" /* host ip or name */, 56100 /* port */, "SomeConnectionKey" /* text key or NetDataWriter */);
            listener.NetworkReceiveEvent += (fromPeer, dataReader, deliveryMethod) =>
            {
                Console.WriteLine("We got: {0}", dataReader.GetString(100 /* max length of string */));
                dataReader.Recycle();
            };


            while (!Console.KeyAvailable)
            {
                client.PollEvents();
                Thread.Sleep(15);
            }


            client.Stop();
        }
    }
}

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Martin K avatar image
Martin K answered

Thanks, that pointed me into the right direction!

However, those who may be having the same issue, my problem was actually listening to the correct port on the client, when connecting to a remote server. Locally, if you launch LiteNetLib and start the server on port 8080, then connect to 8080 from the client, it will work as expected.


So when configuring the Multiplayer servers, I set UDP port to 8080, and expected that if I connect to 8080 from the client it will also work when connecting to the remote host.

It doesn't.

Instead, when you receive the connection info via RequestMultiplayerServerAsync, you get a result that looks like:

"connectionInfo", ...{\"Name\":\"UPDGamePort\",\"ServerListeningPort\":8080,\"ClientConnectionPort\":30100}]}"

it is the port under 'ClientConnectionPort' that you actually need to connect to on the client, not what you set in the Network settings when you created your build. You can grab this from under:


PlayFabResult<RequestMultiplayerServerResponse> server.Result.Ports

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.