Lets Make An “MMO” (DarkRift 2/Unity) Pt 7: Player Login And Registration

Over the last several posts, we have built up a simple client and server that supports multiple players moving around, spawning in, and de-spawning. In this post, we will start working on our player login and registration system.

You a recent post I wrote about creating a simple system for this using PlayFab – we are going to implement that here and build on it to eventually handle character creation, inventory, and more. We will use PlayFab on both the Client and Server. On the Client we will use it to either register a new account, or to login using an existing one. The PlayFab API takes care of the security involved in submitting the password for us. On the Server, we will use PlayFab to handle any request/action we don’t want the player to be able to call directly.

Once we login as a player Client side, we will get an ID back from PlayFab which we will pass on to the Server. The Server will be responsible for any requests that involve updating player data, and we will only let the Client make requests that are read-only or not important. We will store each players PlayFab ID inside our ConnectedClient class on the Server.

I highly suggest you take a read through my previous post on this topic, as I will be going forward under the assumption that you already have your PlayFab account setup, are logged in, have the required SDKs from PlayFab for Unity, and have at looked at the code in that post.

Setup PlayFab

Lets get the required packages installed. In both the Client and Server projects, import the two files we need (The Unity SDK and PlayFabEditorExtensions package). You will need to create a new title inside PlayFab, so inside your dashboard on the PlayFab website do this. I am calling mine “DarkRiftMMO” – You can leave all the tags blank, we don’t need them to actually be able to do anything.

In each project, go to Window->PlayFab->Editor Extensions and login. This is the same process in both projects except for one key difference – on the Server we want to enable the Admin and Server APIs for PlayFab. There may be a slight pause when you enable either while Unity does some behind the scenes work to enable that API.

The Admin API lets us manage our players, ban users, and well… a bunch of other admin calls.

The Server API is used for CloudScripts (server-less functions we can call to perform certain actions) which we would only ever want to run on our server.

You can check out the various levels of access each API gives you HERE. The *NAME* of the API is listed on the left (this might seem obvious but it confused the hell out of me for a minute). You can see both the Client and Admin API’s both have calls for Account Management, but you can’t nuke user accounts from Client API calls. We *never* want the client to be able to do that.

If you enable these APIs on the Client, you are putting your secret key at risk (since it would be stored on the client machine) and opening yourself up to a bunch of issues. Hopefully you can see how giving a malicious user the ability to call the PlayFab API (with something like postman) with your key could cause some serious damage to your game.

Setup The UI

Ok, enough with the doom and gloom. We are going to need to add some code to the client and build out our login scene to actually work as one. You are of course free to make the UI look however you want as long as you provide input fields and buttons for logging in and registering. I thought I would have some fun with this, so this is my login scene:

I am using THIS asset pack to create the forest background. The panels are made using Adobe XD but really any vector/UX software should be able to do the same. I will only use free assets throughout this project so the entire thing can be uploaded at the end . If you did do a different style of login/register UI just make sure you have the following elements:

  • Login Screen:
    • Email Input Field
    • Password Input Field
    • Login Button
    • Register Button
  • Register Screen:
    • Email Input Field
    • Display Name Input Field
    • Password Input Field
    • Register Button
    • Back Button

Wire Up The UI

At this point – the next step is going to be wire-up PlayFab so we can register and login. Here is how we are going to approach this:

  1. The player opens the game, and logins.
  2. PlayFab returns the user ID for that player, which we pass to our server
  3. We move from the login screen to a character selection screen and either choose or create a character (exploring PlayFab more)
  4. The player will select the character and join the game world, where everything will work as we had it – but we will add the PlayFab display name above each players head

We need to change some current code because right now if you hit play you will simply load right into the game. Remember the code up to this post is on Github so if something breaks you can always go back and start from that point.

The first thing we need to do is stop the game from loading right away. To do this, open up ConnectionManager.cs and comment out:

//Client.ConnectInBackground(IPAddress.Loopback, Client.Port, false, ConnectCallback);
//Client.MessageReceived += OnMessage;

If you hit play, you should no longer auto connect. Instead, we want to do stuff with PlayFab first. Right click in the heirchy and create a new empty game object called “PlayFabLoginManager” and create and attach “PlayFabLoginManager.cs” – open that up and paste in the login script we worked on in this post:

using UnityEngine;
using PlayFab;
using PlayFab.ClientModels;
using TMPro;
using UnityEngine.UI;

namespace DarkRiftRPG
{
    public class PlayFabLoginManager : MonoBehaviour
    {
        [Header("Screens")]
        public GameObject LoginPanel;
        public GameObject RegisterPanel;

        [Header("Login Screen")]
        public TMP_InputField LoginEmailField;
        public TMP_InputField LoginPasswordField;
        public Button LoginBtn;
        public Button RegisterBtn;

        [Header("Register Screen")]
        public TMP_InputField RegisterEmailField;
        public TMP_InputField RegisterDisplayNameField;
        public TMP_InputField RegisterPasswordwordField;
        public Button RegisterAccountBtn;
        public Button BackBtn;

        public void OpenLoginPanel()
        {
            LoginPanel.SetActive(true);
            RegisterPanel.SetActive(false);
        }

        public void OpenRegistrationPanel()
        {
            LoginPanel.SetActive(false);
            RegisterPanel.SetActive(true);
        }

        public void OnTryLogin()
        {
            string email = LoginEmailField.text;
            string password = LoginPasswordField.text;

            LoginBtn.interactable = false;
            RegisterBtn.interactable = false;

            LoginWithEmailAddressRequest loginWithEmailAddressRequest = new LoginWithEmailAddressRequest
            {
                Email = email,
                Password = password
            };
            LoginWithEmailAddressRequest req = loginWithEmailAddressRequest;

            PlayFabClientAPI.LoginWithEmailAddress(req,
            res =>
            {
                Debug.Log("Login Success - Can now connect to server");
                Debug.Log($"Player ID: {res.PlayFabId}");

            },
            err =>
            {
                Debug.Log("Error: " + err.ErrorMessage);
                LoginBtn.interactable = true;
                RegisterBtn.interactable = true;
            });
        }

        public void OnTryRegisterNewAccount()
        {
            BackBtn.interactable = false;
            RegisterAccountBtn.interactable = false;

            string email = RegisterEmailField.text;
            string displayName = RegisterDisplayNameField.text;
            string password = RegisterPasswordwordField.text;

            RegisterPlayFabUserRequest req = new RegisterPlayFabUserRequest
            {
                Email = email,
                DisplayName = displayName,
                Password = password,
                RequireBothUsernameAndEmail = false
            };

            PlayFabClientAPI.RegisterPlayFabUser(req,
            res =>
            {
                BackBtn.interactable = true;
                RegisterAccountBtn.interactable = true;
                OpenLoginPanel();
                Debug.Log(res.PlayFabId);
            },
            err =>
            {
                BackBtn.interactable = true;
                RegisterAccountBtn.interactable = true;
                Debug.Log("Error: " + err.ErrorMessage);
            });

        }
    }
}

I added a little bit of additional logging here but its basically the same file you should have read from the other post. Make sure you have the right namespace or we wont be able to call our other classes. We need to wire up the UI to this now, so back in the inspector assign your references:

Yours might look a little different, especially if you made your panels with less art. The important thing here is that you have all of the things that PlayFabLoginManager.cs is inspecting. How you name/set them up is up to you.

Next, we need to set the buttons onclick to call our code. Set the PlayFabLoginManager to be the script reference and wire up your various method calls.

On the Login Screen:

  1. The login button should call OnTryLogin
  2. The register button should call OpenRegistrationPanel

On the Registration Screen:

  1. The register button should call OnTryRegisterNewAccount
  2. The back button should call OpenLoginPanel

For example the login button should be wired as shown:

If you hit play, you should be able to move back and fourth between login and register, sign up for a new account, and login with an existing one. Once you hit Login with valid details, you should see:

Client Changes

Awesome – now what we want to do is start changing our code to load up a Character Select screen after we login and connect to our DR server in the background, passing it the Player ID for this title and assigning that ID inside the ConnectedClient.cs file.

Inside OnTryLogin() in our LoginWithEmailAddress result add:

ConnectionManager.Instance.ConnectToServer(res.PlayFabId);

Create this method inside ConnectionManager.cs – at the top of this file add:

public string LocalPlayerPlayFabID;

Inside ConnectToServer add:

public void ConnectToServer(string playFabId)
{
    LocalPlayerPlayFabID = playFabId;
    Client.ConnectInBackground(IPAddress.Loopback, Client.Port, false, ConnectCallback);
    Client.MessageReceived += OnMessage;
}

You can now delete the commented out lines from before since we are calling them here. We can actually keep out OnConnectedToServer call – but we need to update out tags/messages. Inside NetworkMessages.cs we are going to change around a few things:

Since we are no longer joining the game itself at this point, lets rename JoinGameRequest and JoinGameResponse to JoinServerRequest and JoinServerResponse respectively. Previously JoinGameRequest (now JoinServerRequest) was an empty message that just told the server “Hey I’m here and ready to play!”. We need to create a new struct so we can pass along the PlayFab ID of that player. Add a new message:

public struct JoinServerRequestData : IDarkRiftSerializable
    {
        public string PlayFabID;

        public JoinServerRequestData(string playfabID)
        {
            PlayFabID = playfabID;
        }

        public void Deserialize(DeserializeEvent e)
        {
            PlayFabID = e.Reader.ReadString();
        }

        public void Serialize(SerializeEvent e)
        {
            e.Writer.Write(PlayFabID);
        }
    }

ASIDE: I orignally forgot to assign e.Reader.ReadString() to PlayFabID which caused me several minutes of “WTF” since it was debugging correctly on the client and coming in null on the server. Again, if stuff like this happens triple check your messages.

Update OnConnectedToServer to be:

private void OnConnectedToServer()
{
    using (Message message = Message.Create((ushort)Tags.JoinServerRequest, new JoinServerRequestData(LocalPlayerPlayFabID)))
    {
        Client.SendMessage(message, SendMode.Reliable);
    }
}

ASIDE: A *lot* of our original code from parts 1-6 is going to be changed, re-written, or moved around. If something doesn’t work, make sure your code matches mine and if you are still stuck, find me on the DarkRift discord @Ace.

Server Changes

Great – lets move over to the Server project for a sec. We need to copy our network messages over, however if you just copy and paste between solutions VS won’t automatically update references. I suggest in this case you right-click and rename them in that project and then just paste over the new struct since it previously didn’t exist. Inside ServerManager.cs update OnPlayerJoinGameRequest to include the new messages:

case Tags.JoinServerRequest:
    OnPlayerJoinGameRequest(client, message.Deserialize<JoinServerRequestData>());
    break;

And the method itself accordingly:

private void OnPlayerJoinGameRequest(IClient client, JoinServerRequestData data)

You might notice we have a conflict since the name data already exists (we had used it to allow/deny the user previously). I am going to get around this here by renaming the version we had already been using from data to responseData. Before we can use our new PlayFab ID info, we need to update ConnectedClient to hold it so at the top of that file add a public string PlayFabID; and in the constructor we want to accept the string along with the client and set it:

public ConnectedClient(IClient client, string playfabID)
{
    Client = client;
    ClientID = client.ID;
    PlayFabID = playfabID;

    Client.MessageReceived += OnMessage;
}

Back in ServerManager, where we create a new ConnectedClient update that line to read:

ConnectedClient c = new ConnectedClient(client, data.PlayFabID);

Lastly for now update the response to the client to reflect the name change in our response data:

using (Message message = Message.Create((ushort)Tags.JoinServerResponse, responseData))
{
    client.SendMessage(message, SendMode.Reliable);
}

Awesome, we are getting there! Lets do a quick test – under that line add a Debug.Log(data.PlayFabID); Start the server and login on the client. If you have debugging in place for both you should see that the ID the client gets is successfully passed to the server – after which it loads the player into the game.

Now, instead of joining the game directly when we get back the “JoinServerResponse” we actually want to show a character select/creation screen that you would expect to see in an online rpg. However, that isn’t really about logging in or registering so I will continue with that in the next post of this series. See you there 🙂

PS: Here are what the updated files should look like (for files we updated):

ConnectionManager.cs

using System;
using System.Net;
using DarkRift;
using DarkRift.Client;
using DarkRift.Client.Unity;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace DarkRiftRPG
{
    public class ConnectionManager : MonoBehaviour
    {
        //We want a static reference to ConnectionManager so it can be called directly from other scripts
        public static ConnectionManager Instance;
        //A reference to the Client component on this game object. 
        public UnityClient Client { get; private set; }

        public string LocalPlayerPlayFabID;
        public ushort LocalClientID;

        void Awake()
        {
            if (Instance != null)
            {
                Destroy(gameObject);
                return;
            }

            Instance = this; 
            DontDestroyOnLoad(this); 
        }
        void Start()
        {
            Client = GetComponent<UnityClient>();
        }

        public void SendClickPosToServer(Vector3 point)
        {
            using (Message message = Message.Create((ushort)Tags.PlayerMovementRequest, new PlayerMovementRequestData(point)))
            {
                Client.SendMessage(message, SendMode.Reliable);
            }
        }

        private void ConnectCallback(Exception e)
        {
            if (Client.ConnectionState == ConnectionState.Connected) 
            {
                Debug.Log("Connected to server!");
                OnConnectedToServer();

            }
            else
            {
                Debug.LogError($"Unable to connect to server. Reason: {e.Message} "); 
            }
        }

        private void OnConnectedToServer()
        {
            using (Message message = Message.Create((ushort)Tags.JoinServerRequest, new JoinServerRequestData(LocalPlayerPlayFabID)))
            {
                Client.SendMessage(message, SendMode.Reliable);
            }
        }

        public void ConnectToServer(string playFabId)
        {
            LocalPlayerPlayFabID = playFabId;
            Debug.Log(LocalPlayerPlayFabID);
            Client.ConnectInBackground(IPAddress.Loopback, Client.Port, false, ConnectCallback);
            Client.MessageReceived += OnMessage;
        }

        private void OnMessage(object sender, MessageReceivedEventArgs e)
        {
            using (Message m = e.GetMessage())
            {
                switch ((Tags)m.Tag)
                {
                    case Tags.JoinServerResponse:
                        OnPlayerJoinGameResponse(m.Deserialize<JoinGameResponseData>());
                        break;
                    case Tags.PlayerMovementUpdate:
                        OnPlayerMovementUpdate(m.Deserialize<ProccessedPlayerMovementData>());
                        break;
                    case Tags.SpawnPlayer:
                        OnSpawnPlayer(m.Deserialize<PlayerSpawnData>());
                        break;
                    case Tags.DespawnPlayer:
                        OnDespawnPlayer(m.Deserialize<PlayerDespawnData>());
                        break;
                }
            }
        }

        private void OnDespawnPlayer(PlayerDespawnData data)
        {
            GameManager.Instance.RemovePlayerFromGame(data);
        }

        private void OnSpawnPlayer(PlayerSpawnData data)
        {
            GameManager.Instance.SpawnPlayer(data);
        }

        private void OnPlayerMovementUpdate(ProccessedPlayerMovementData data)
        {
            if (GameManager.Instance != null)
                GameManager.Instance.HandlePlayerMovementUpdate(data);
        }

        private void OnPlayerJoinGameResponse(JoinGameResponseData data)
        {
            if (!data.JoinGameRequestAccepted)
            {
                Debug.Log("houston we have a problem");
                return;
            }
            LocalClientID = Client.Client.ID;
            SceneManager.LoadScene("Game");
        }

        public void SpawnLocalPlayerRequest()
        {
            using (Message message = Message.CreateEmpty((ushort)Tags.SpawnLocalPlayerRequest))
            {
                Client.SendMessage(message, SendMode.Reliable);
            }
        }

        private void OnDestroy()
        {
            Client.MessageReceived -= OnMessage;
        }
    }

}

NetworkMessages.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DarkRift;

namespace DarkRiftRPG
{
    public enum Tags
    {
        JoinServerRequest,
        JoinServerResponse,

        SpawnLocalPlayerRequest,
        SpawnLocalPlayerResponse,

        PlayerMovementRequest,
        PlayerMovementUpdate,

        SpawnPlayer,
        DespawnPlayer

    }

    public struct JoinServerRequestData : IDarkRiftSerializable
    {
        public string PlayFabID;

        public JoinServerRequestData(string playfabID)
        {
            PlayFabID = playfabID;
        }

        public void Deserialize(DeserializeEvent e)
        {
            PlayFabID = e.Reader.ReadString();
        }

        public void Serialize(SerializeEvent e)
        {
            e.Writer.Write(PlayFabID);
        }
    }

    public struct JoinGameResponseData : IDarkRiftSerializable
    {
        public bool JoinGameRequestAccepted;

        public JoinGameResponseData(bool accepted)
        {
            JoinGameRequestAccepted = accepted;
        }
        public void Deserialize(DeserializeEvent e)
        {
            JoinGameRequestAccepted = e.Reader.ReadBoolean();
        }

        public void Serialize(SerializeEvent e)
        {
            e.Writer.Write(JoinGameRequestAccepted);
        }
    }

    public struct SpawnLocalPlayerResponseData : IDarkRiftSerializable
    {
        public ushort ID;

        public SpawnLocalPlayerResponseData(ushort id)
        {
            ID = id;
        }

        public void Deserialize(DeserializeEvent e)
        {
            ID = e.Reader.ReadUInt16();
        }

        public void Serialize(SerializeEvent e)
        {
            e.Writer.Write(ID);
        }
    }

    public struct PlayerMovementRequestData : IDarkRiftSerializable
    {
        public Vector3 PlayerClickLocation;

        public PlayerMovementRequestData(Vector3 clickPos)
        {
            PlayerClickLocation = clickPos;
        }
        public void Deserialize(DeserializeEvent e)
        {
            PlayerClickLocation = e.Reader.ReadVector3();
        }

        public void Serialize(SerializeEvent e)
        {
            e.Writer.WriteVector3(PlayerClickLocation);
        }
    }

    public struct PlayerPositionInputData : IDarkRiftSerializable
    {
        public ushort ID;
        public Vector3 Pos;

        public PlayerPositionInputData(ushort id, Vector3 pos)
        {
            ID = id;
            Pos = pos;
        }

        public void Deserialize(DeserializeEvent e)
        {
            ID = e.Reader.ReadUInt16();
            Pos = e.Reader.ReadVector3();
        }

        public void Serialize(SerializeEvent e)
        {
            e.Writer.Write(ID);
            e.Writer.WriteVector3(Pos);
        }
    }

    public struct ProccessedPlayerMovementData : IDarkRiftSerializable
    {
        public PlayerPositionInputData[] ProccessedMovementUpdate;

        public ProccessedPlayerMovementData(PlayerPositionInputData[] newPlayerPositions)
        {
            ProccessedMovementUpdate = newPlayerPositions;
        }
        public void Deserialize(DeserializeEvent e)
        {
            ProccessedMovementUpdate = e.Reader.ReadSerializables<PlayerPositionInputData>();
        }

        public void Serialize(SerializeEvent e)
        {
            e.Writer.Write(ProccessedMovementUpdate);
        }
    }

    public struct PlayerSpawnData : IDarkRiftSerializable
    {
        public ushort ID;
        public Vector3 Position;

        public PlayerSpawnData(ushort id, Vector3 position)
        {
            ID = id;
            Position = position;
        }

        public void Deserialize(DeserializeEvent e)
        {
            ID = e.Reader.ReadUInt16();
            Position = e.Reader.ReadVector3();
        }

        public void Serialize(SerializeEvent e)
        {
            e.Writer.Write(ID);
            e.Writer.WriteVector3(Position);
        }
    }

    public struct PlayerDespawnData : IDarkRiftSerializable
    {
        public ushort ID;

        public PlayerDespawnData(ushort id)
        {
            ID = id;
        }

        public void Deserialize(DeserializeEvent e)
        {
            ID = e.Reader.ReadUInt16();
        }

        public void Serialize(SerializeEvent e)
        {
            e.Writer.Write(ID);
        }
    }
}

PlayFabLoginManager.cs

using UnityEngine;
using PlayFab;
using PlayFab.ClientModels;
using TMPro;
using UnityEngine.UI;

namespace DarkRiftRPG
{
    public class PlayFabLoginManager : MonoBehaviour
    {
        [Header("Screens")]
        public GameObject LoginPanel;
        public GameObject RegisterPanel;

        [Header("Login Screen")]
        public TMP_InputField LoginEmailField;
        public TMP_InputField LoginPasswordField;
        public Button LoginBtn;
        public Button RegisterBtn;

        [Header("Register Screen")]
        public TMP_InputField RegisterEmailField;
        public TMP_InputField RegisterDisplayNameField;
        public TMP_InputField RegisterPasswordwordField;
        public Button RegisterAccountBtn;
        public Button BackBtn;

        public void OpenLoginPanel()
        {
            LoginPanel.SetActive(true);
            RegisterPanel.SetActive(false);
        }

        public void OpenRegistrationPanel()
        {
            LoginPanel.SetActive(false);
            RegisterPanel.SetActive(true);
        }

        public void OnTryLogin()
        {
            string email = LoginEmailField.text;
            string password = LoginPasswordField.text;

            LoginBtn.interactable = false;
            RegisterBtn.interactable = false;

            LoginWithEmailAddressRequest loginWithEmailAddressRequest = new LoginWithEmailAddressRequest
            {
                Email = email,
                Password = password
            };
            LoginWithEmailAddressRequest req = loginWithEmailAddressRequest;

            PlayFabClientAPI.LoginWithEmailAddress(req,
            res =>
            {
                Debug.Log("Login Success - Can now connect to server");
                Debug.Log($"Player ID: {res.PlayFabId}");
                ConnectionManager.Instance.ConnectToServer(res.PlayFabId);

            },
            err =>
            {
                Debug.Log("Error: " + err.ErrorMessage);
                LoginBtn.interactable = true;
                RegisterBtn.interactable = true;
            });
        }

        public void OnTryRegisterNewAccount()
        {
            BackBtn.interactable = false;
            RegisterAccountBtn.interactable = false;

            string email = RegisterEmailField.text;
            string displayName = RegisterDisplayNameField.text;
            string password = RegisterPasswordwordField.text;

            RegisterPlayFabUserRequest req = new RegisterPlayFabUserRequest
            {
                Email = email,
                DisplayName = displayName,
                Password = password,
                RequireBothUsernameAndEmail = false
            };

            PlayFabClientAPI.RegisterPlayFabUser(req,
            res =>
            {
                BackBtn.interactable = true;
                RegisterAccountBtn.interactable = true;
                OpenLoginPanel();
                Debug.Log(res.PlayFabId);
            },
            err =>
            {
                BackBtn.interactable = true;
                RegisterAccountBtn.interactable = true;
                Debug.Log("Error: " + err.ErrorMessage);
            });

        }
    }
}

ServerManager.cs

using System;
using System.Collections.Generic;
using DarkRift;
using DarkRift.Server;
using DarkRift.Server.Unity;
using UnityEngine;

namespace DarkRiftRPG
{
    public class ServerManager : MonoBehaviour
    {
        public static ServerManager Instance;

        public Dictionary<ushort, ConnectedClient> ConnectedClients = new Dictionary<ushort, ConnectedClient>();

        private XmlUnityServer xmlServer;
        private DarkRiftServer server;
        void Awake()
        {
            if (Instance != null)
            {
                Destroy(gameObject);
                return;
            }
            Instance = this;
            DontDestroyOnLoad(this);
        }
        void Start()
        {
            xmlServer = GetComponent<XmlUnityServer>();
            server = xmlServer.Server;
            server.ClientManager.ClientConnected += OnClientConnected;
            server.ClientManager.ClientDisconnected += OnClientDisconnected;
        }

        void OnDestroy()
        {
            server.ClientManager.ClientConnected -= OnClientConnected;
            server.ClientManager.ClientDisconnected -= OnClientDisconnected;
        }

        private void OnClientDisconnected(object sender, ClientDisconnectedEventArgs e)
        {
            ConnectedClients.Remove(e.Client.ID);
            Destroy(PlayerManager.Instance.CurrentPlayers[e.Client.ID]);
            PlayerManager.Instance.CurrentPlayers.Remove(e.Client.ID);
            SendToAllExcept(e.Client.ID, Tags.DespawnPlayer, new PlayerDespawnData(e.Client.ID));
        }

        public void SendNewPlayerToOthers(ushort clientID, Vector3 position)
        {
            PlayerSpawnData spawnData = new PlayerSpawnData(clientID, position);
            SendToAllExcept(clientID, Tags.SpawnPlayer, spawnData);
        }

        public void SendOthersToNewPlayer(ushort clientID, Dictionary<ushort, GameObject> players)
        {
            foreach (KeyValuePair<ushort, GameObject> player in players)
            {
                if (player.Key != clientID)
                {
                    PlayerSpawnData spawnData = new PlayerSpawnData(player.Key, player.Value.transform.position);
                    SendToClient(clientID, Tags.SpawnPlayer, spawnData);
                }
            }
        }

        private void OnClientConnected(object sender, ClientConnectedEventArgs e)
        {
            e.Client.MessageReceived += OnMessage;
        }

        private void OnMessage(object sender, MessageReceivedEventArgs e)
        {
            IClient client = (IClient)sender;
            using (Message message = e.GetMessage())
            {
                switch ((Tags)message.Tag)
                {
                    case Tags.JoinServerRequest:
                        OnPlayerJoinGameRequest(client, message.Deserialize<JoinServerRequestData>());
                        break;
                }
            }
        }

        private void OnPlayerJoinGameRequest(IClient client, JoinServerRequestData data)
        {
            JoinGameResponseData responseData = new JoinGameResponseData();
            
            if (ConnectedClients.ContainsKey(client.ID))
            {
                responseData.JoinGameRequestAccepted = false;

                using (Message message = Message.Create((ushort)Tags.JoinServerResponse, data))
                {
                    client.SendMessage(message, SendMode.Reliable);
                }
                return;
            } else
            {
                responseData.JoinGameRequestAccepted = true;

                client.MessageReceived -= OnMessage;

                ConnectedClient c = new ConnectedClient(client, data.PlayFabID);
                ConnectedClients.Add(c.ClientID, c);
                Debug.Log(c.PlayFabID);
                using (Message message = Message.Create((ushort)Tags.JoinServerResponse, responseData))
                {
                    client.SendMessage(message, SendMode.Reliable);
                }
            }
        }

        public void SendToClient(ushort id, Tags t, IDarkRiftSerializable obj, SendMode mode = SendMode.Reliable)
        {
            using (Message m = Message.Create<IDarkRiftSerializable>((ushort)t, obj))
            {
                ConnectedClients[id].Client.SendMessage(m, mode);
            }
        }

        public void SendToAll(Tags t, IDarkRiftSerializable obj, SendMode mode = SendMode.Reliable)
        {
            foreach (KeyValuePair<ushort, ConnectedClient> connectedClient in ConnectedClients)
            {
                using (Message m = Message.Create<IDarkRiftSerializable>((ushort)t, obj))
                {
                    connectedClient.Value.Client.SendMessage(m, mode);
                }
            }
        }

        public void SendToAllExcept(ushort id, Tags t, IDarkRiftSerializable obj, SendMode mode = SendMode.Reliable)
        {
            using (Message m = Message.Create<IDarkRiftSerializable>((ushort)t, obj))
            {
                foreach (KeyValuePair<ushort, ConnectedClient> connectedClient in ConnectedClients)
                {
                    if (connectedClient.Key != id)
                    {
                        connectedClient.Value.Client.SendMessage(m, mode);
                    }
                }
            }
        }
    }
}

ConnectedClient.cs

using System.Collections;
using System.Collections.Generic;
using System;
using DarkRift.Server;
using DarkRift;

namespace DarkRiftRPG
{
    public class ConnectedClient
    {
        public ushort ClientID;
        public IClient Client;
        public string PlayFabID;
        public ConnectedClient(IClient client, string playfabID)
        {
            Client = client;
            ClientID = client.ID;
            PlayFabID = playfabID;

            Client.MessageReceived += OnMessage;
        }

        private void OnMessage(object sender, MessageReceivedEventArgs e)
        {
            IClient client = (IClient)sender;
            using (Message m = e.GetMessage())
            {
                switch ((Tags)m.Tag)
                {
                    case Tags.SpawnLocalPlayerRequest:
                        OnSpawnLocalPlayerRequest();
                        break;
                    case Tags.PlayerMovementRequest:
                        OnPlayerMovementRequest(m.Deserialize<PlayerMovementRequestData>());
                        break;
                }
            }
        }

        private void OnPlayerMovementRequest(PlayerMovementRequestData data)
        {
            PlayerManager.Instance.HandlePlayerMovementRequest(ClientID, data.PlayerClickLocation);
        }

        private void OnSpawnLocalPlayerRequest()
        {
            PlayerManager.Instance.SpawnPlayerOnServer(ClientID);
        }

        public void SpawnLocalPlayerOnClient(PlayerSpawnData data)
        {
            ServerManager.Instance.SendToClient(data.ID, Tags.SpawnPlayer, data);
        }

        public void OnClientDisconnected(object sender, ClientDisconnectedEventArgs e)
        {
            e.Client.MessageReceived -= OnMessage;
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *