DarkRift2 Plugin scheduler

This short article will explain you how to schedule plugins initialization easely, even with the free version of Darkrift2. This article assumes that you have basics of networking with DarkRift2.

Prepare your plugins.

The most important thing is :

« Don’t put code in the plugin constructor, but use an Init() function instead »

By doing this, we will let DarkRift instanciate our plugins, and after that, we will initialize them with a plugin dedicated to schedule other plugins

Here is an example of a simple plugin for the purpose (FirstPlugin.cs) :

using System;
using DarkRift.Server;

namespace MateriaGame.DarkRift.Plugins
{
    public class FirstPlugin : Plugin
    {
        #region Plugin implementation
        public override bool ThreadSafe => false;

        public override Version Version => new Version(1, 0, 0);

        public FirstPlugin(PluginLoadData pluginLoadData) : base(pluginLoadData)
        {
            //Do nothing here
            //Eventually, store pluginLoadData data if needed
        }
        #endregion

        #region Implementation

        public void Init()
        {
            Console.WriteLine("FirstPlugin plugin initialization");
        }

        #endregion
    }
}

I have made 5 plugins with the same code to test our scheduler. (Of course, i changed their names :

Create the Scheduler

The scheduler task is very simpe :

  • Wait for DarkRift to load all plugins
  • Initialize manually all plugins with the desired sorting

For that, we’ll need to call an async task wich enable the constructor to finish it’s own execution. We need to do this because DarkRift2 Plugin Manager doesn’t allow us to find plugins during Plugin instantiation step (Internal to DarkRift)

Here is the Scheduler.cs code :

using DarkRift.Server;
using System;
using System.Threading.Tasks;

namespace MateriaGame.DarkRift.Plugins
{
    /// <summary>
    /// Plugin wich schedule initialization of darkrift plugins
    /// </summary>
    class Scheduler : Plugin
    {
        #region Plugin implementation

        public override bool ThreadSafe => false;
        public override Version Version => new Version(1, 0, 0);

        public Scheduler(PluginLoadData pluginLoadData) : base(pluginLoadData)
        {
            //Initialize some data
            //...
            Console.WriteLine("Initialize some stuff here that is needed in plugins");

            //Initialize plugins async - enable continue execution
            InitPluginsAsync(PluginManager);

            Console.WriteLine("Scheduler instanciated successfully");
        }

        /// <summary>
        /// Initialize plugins
        /// </summary>
        /// <param name="pPluginManager"></param>
        /// <returns></returns>
        private static async Task InitPluginsAsync(IPluginManager pPluginManager)
        {
            //Free execution
            await Task.Yield();

            bool arePluginsLoaded = false;
            while (!arePluginsLoaded)
            {
                try
                {
                    pPluginManager.GetPluginByType<Scheduler>();
                    arePluginsLoaded = true;
                }
                catch (Exception)
                {
                    //Console.WriteLine("Darkrift hasn't finished to load plugins");
                }
            }

            //Initialize plugins
            Console.WriteLine("Scheduler initialization start");
            ((FirstPlugin)pPluginManager.GetPluginByType<FirstPlugin>()).Init();
            ((SecondPlugin)pPluginManager.GetPluginByType<SecondPlugin>()).Init();
            ((ThirdPlugin)pPluginManager.GetPluginByType<ThirdPlugin>()).Init();
            ((FourthPlugin)pPluginManager.GetPluginByType<FourthPlugin>()).Init();
            ((FifthPlugin)pPluginManager.GetPluginByType<FifthPlugin>()).Init();
            Console.WriteLine("Scheduler initialization finished");
        }

        #endregion
    }
}

You can now build your plugins and add them into the Plugins directory of DarkRift. Let’s start the server and here is the result you should have if you’ve followed the tutorial correctly :

You can find the solution in my official github right here.

Don’t forget to follow me on twitter , i’ll really appreciate the help ! I just want to provide a lots of articles like that, but need your support.

Thanks again,
David.

DarkRift 2 Tutorial for Unity 3D – Part 11 – Synchronize physics over network (2/2)

Reminder : You can find all the DarkRift2 related articles here 
You can find the entire project on my official GitHub

Prepare the bouncy ball to be synchronized

On the script NetworkBouncyBall, we want to receive the ball position from the server. So we need to listen to the event “MessageReceived” from the client manager. As said in the previous article, messages order is not guaranteed, thus we need to store the last received message from the server wich contains the serverTick (it’s incremented each FixedUpdate()).

We also need the clientTick wich is the counter that is incremented by the client each FixedUpdate in the NetworkBouncyBall Script. This counter will help us to do reconciliation.

The reconciliation is the process wich will correct the information of the client compared to those of the server. As the client send the data each 10 ticks, we’ll need a list of all client position each fixed update.

Here are the new properties we need in the NetworkBouncyBall script :

/// <summary>
/// Last received message from the server
/// </summary>
public BouncyBallSyncMessageModel lastReceivedMessage;

/// <summary>
/// Tick counted by the client
/// </summary>
public int clientTick = -1;

/// <summary>
/// Contains data wich will be generated by the client
/// </summary>
public List<BouncyBallSyncMessageModel> reconciliationInfoList;

Listen for the message receive events

As said, the client need to receive messages from the server, As you already did it in the past articles, i just will give you the code :

public override void Start()
{
	base.Start();

	////////////////////////////////////
	// Get references
	rigidbodyReference = GetComponent<Rigidbody>();

	//If we are on client side
	if (!Equals(ClientManager.instance, null))
	{
		//////////////////
		/// Suscribe to events
		ClientManager.instance.clientReference.MessageReceived += UpdateFromServerState;
		reconciliationInfoList = new List<BouncyBallSyncMessageModel>();
	}
}

private void OnDestroy()
{
	//If we are on client side
	if (!Equals(ClientManager.instance, null))
	{
		ClientManager.instance.clientReference.MessageReceived -= UpdateFromServerState;
	}
}

You can notice here that there is 2 interesting things :

  • Initialization of the reconciliation data : reconciliationInfoList. (Be sure to use the namespace using System.Collections.Generic)
  • Unsuscribe to the event on the OnDestroy function. It’s a good practice to always to that.

Now, we finally can create our function UpdateFromServerState :

/// <summary>
/// update from the server state
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void UpdateFromServerState(object sender, DarkRift.Client.MessageReceivedEventArgs e)
{
	if (e.Tag == NetworkTags.InGame.BOUNCY_BALL_SYNC_POS)
	{
		//Get message data
		BouncyBallSyncMessageModel syncMessage = e.GetMessage().Deserialize<BouncyBallSyncMessageModel>();

		//If this is the first time we receive the message
		if (Object.Equals(null, lastReceivedMessage))
		{
			//Update data
			rigidbodyReference.velocity = syncMessage.velocity;
			rigidbodyReference.transform.position = syncMessage.position;
			clientTick = syncMessage.serverTick;
			lastReceivedMessage = syncMessage;
		}

		//If the message regards this object and is older than the previous one
		if (id == syncMessage.networkID && syncMessage.serverTick > lastReceivedMessage.serverTick)
		{
			lastReceivedMessage = syncMessage;
		}
	}
}

This needs some explanations.

First time we receive the message

When it’s the first time we receive this message, we need to initialize some data as :

  • Client tick : this is the counter of the ball in the client side. When we receive the first message from the server, we set the same value than the server. It will be used to make reconciliation.
NOTE : using the fixed update function to increment the counter is not the safest way. I heard that sometimes, Unity can skip some fixed update. That why if you plan to do a multiplayer game, you need a strongest way to handle counters.
  • Synchronize immediately the velocity and the position.
  • Store the message in the last received message

Receive following messages

For the following messages, we only need to store the message received in the property lastMessagereceived.

Reconciliation

At this point, we only synchronize the position and the velocity of the ball from the first received message .

As it’s a game object affected by physics, we need to do correction, if needed, in the FixedUpdate() function.

In this function we’ll perform these following actions :

  • Increment the client tick counter
  • Add data to the reconcialiation info list.
  • Performs reconciliation

Here is the code :

private void FixedUpdate()
{
	//If we are on server side
	if (!Equals(GameServerManager.instance, null))
	{
		if (GameServerManager.instance.currentTick % 1 == 0)
			SendBallPositionToClients();
	}
	else if (!Equals(ClientManager.instance, null) && clientTick != -1)
	{
		clientTick++;
		reconciliationInfoList.Add(new BouncyBallSyncMessageModel
		{
			position = transform.position,
			serverTick = clientTick,
			velocity = rigidbodyReference.velocity
		});

		Reconciliate();
	}
}

How it will works ?

As the physic engine will works both on client and server, we need to check frequently if the divergence between client and server is not to important.

When we receive the message from the server, we need to check the position of the ball at this specific tick on the client. For that, we’ll use the reconciliationInfoList to take the snaphot of the ball at this tick (wich is the server tick received).

If the difference is above a certain threshold, we’ll correct basically the position and the velocity of the client immediately.

/// <summary>
/// Reconciliate the client with the server data
/// </summary>
private void Reconciliate()
{
	if (reconciliationInfoList.Count() > 0)
	{
		//Get the position of the client at this specific frame
		BouncyBallSyncMessageModel clientInfo = reconciliationInfoList.Where(i => i.serverTick == lastReceivedMessage.serverTick).FirstOrDefault();

		//If there is more than 50 tick that the ball has not been updated depending to the server position
		if (reconciliationInfoList.Count() > 50)
		{
			rigidbodyReference.velocity = lastReceivedMessage.velocity;
			rigidbodyReference.transform.position = lastReceivedMessage.position;
			clientTick = lastReceivedMessage.serverTick;
			clientInfo = lastReceivedMessage;
		}                

		if (!Equals(clientInfo, null))
		{
			//Check for position divergence
			if (Vector3.Distance(clientInfo.position, lastReceivedMessage.position) >= 0.05f)
			{
				//Update data
				rigidbodyReference.velocity = lastReceivedMessage.velocity;
				rigidbodyReference.transform.position = lastReceivedMessage.position;
			}

			//Empty the list
			reconciliationInfoList.Clear();
		}            
	}
}

If a reconcialiation is made, we empty the list to free memory.

NOTE : In this tutorial, i clear the list only when a reconcialition occurs. In your game, i suggest you to limit the length of the list to not consumme  
a large amount of memory, especially if there is no often reconciliation

Let’s try the result

At this point of the tutorial, we receive the ball position each 10 tick. (as defined in the previous article). The ball only bounce on the Y axis. Let’s try it. I will
will launch the server scene on Unity then build my client an launch it.

To build the client executable, you need to select both MainClientScene and MainGameScene like that (The order is important). Unselect Server Build if already select :
NOTE : I added two cubes to make the ball bounce randomly each start

Here is the result :

Seems to be good ! we finally synchronized our physic game object.

As the position of the ball is lead by the physic engine, it’s a little complicated to make interpolation. We definitively can do that to make the reconciliation smoother.

Improvement ideas

There is a lot to do to increase the playability, the smoothness, … Here is some tips that you can use :

  • Increase the rate of messages sending by the server
  • Use math to predict the ball position to make the ball move smoother on the client screen when reconciliate
  • Force reconcialition each X tick should be adapted to each client connection

Thanks for reading !

Thanks for reading this last article about how to synchronize a physic object bewteen a server and a client with Dark Rift 2. If you liked it, you can thanks me by :

  • Following me on twitter
  • Give me you feedback

See you soon for another tutorial or a Unity 3D tips
thanks you, David.

DarkRift 2 Tutorial for Unity 3D – Part 11 – Synchronize physics over network (1/2)

Reminder : You can find all the DarkRift2 related articles here 
You can find the entire project on my official GitHub

Define data to be synchronized

For the tutorial, we only want to synchronize the ball position. As it’s a game object based on physics, the player must synchronize two informations :

  • Position of the ball
  • Velocity of the ball

So, let’s define a message model (has done previously for the spawn message). Create a new script called BouncyBallSyncMessageModel in the folder Scripts/Network/Messages

Here is the code of the class :

using DarkRift;
using UnityEngine;

public class BouncyBallSyncMessageModel : IDarkRiftSerializable
{
    #region Properties
    public int networkID;
    public int serverTick;
    public Vector3 position;
    public Vector3 velocity;
    #endregion

    #region IDarkRiftSerializable implementation
    public void Deserialize(DeserializeEvent e)
    {
        networkID = e.Reader.ReadInt32();
        serverTick = e.Reader.ReadInt32();
        position = new Vector3(e.Reader.ReadSingle(), e.Reader.ReadSingle(), e.Reader.ReadSingle());
        velocity = new Vector3(e.Reader.ReadSingle(), e.Reader.ReadSingle(), e.Reader.ReadSingle());
    }

    public void Serialize(SerializeEvent e)
    {
        //Write id of the network object
        e.Writer.Write(networkID);

        //Write id of the network object
        e.Writer.Write(serverTick);

        //Write position
        e.Writer.Write(position.x);
        e.Writer.Write(position.y);
        e.Writer.Write(position.z);

        //Write velocity
        e.Writer.Write(velocity.x);
        e.Writer.Write(velocity.y);
        e.Writer.Write(velocity.z);
    }
    #endregion
}

You can see a property called serverTick, it will be discussed later on this tutorial.

Create the associated tag

As you know, we need to create an associated tag wich will be used to identify the message. Take the previsouly created script called NetworkTag and the tag like that :

/// <summary>
/// Classes that stores all tags
/// </summary>
public static class NetworkTags
{
    /// <summary>
    /// Tags used in game
    /// </summary>
    public struct InGame
    {
        ///////////////////////////
        // Common
        public const ushort SPAWN_OBJECT = 1001;

        ///////////////////////////
        // Bouncy ball
        public const ushort BOUNCY_BALL_SYNC_POS = 2001;
    }

}

How synchronize the bouncy ball ?

Now, we need a script that will be shared by both server and client to synchronize the position. From the server side, it will just send the position to the client each “X” frame where “X” will be defined within the script.
On the client side, it will be more difficult because we want that the movement of the ball to be fluid. If we update the position of the ball each received message, the result will probably be not very fluid. Here are the cons of doing that :

  • Messages order are not guarranted with DarkRift2, means that we can receive message N°27 before the message N°22
  • The visual effect of updating the ball position on each received message won’t be acceptable for the player

So, we’ll need interpolation between client and server to fix that. We’ll talk about it later on this tutorial.

As the server is the master for the ball position and there is no input for controlling the ball, the server doesn’t need any interpolation, only the client.

How to get the number position of a message ?

Dark Rift 2, at my knowledge, doesn’t provide a way to get the position order of messages. So we need to create it entirely. With unity 3D, we have 2 functions wich are defined below :

  • Update() : called each frame, the elapsed time between each update call is not fixed and will be different on each machine. Depends too on what the game is processing, means that if there is a lot of game object that performs actions, the time between each call will be longer. that is what we call Frame Rate in game developpement or Frame Per Second (FPS)
  • FixedUpdate() : this function is frame-rate independant. It’s the frequency of the physic system in Unity 3D. by default, it’s called every 0.02 seconds (equivalent to 50 times per second).

By knowing that, we know that to set up our network frame counter, we’ll use the FixedUpdate function on both server and client side !

Create the server tick counter

The server tick is a simple counter wich increments by 1 every FixedUpdate(). It start to 0 on the server. On the client, we just store the last received counter on each game object wich needs it

It’s very simple to do this on the server. For the client, we’ll see it on the next article

GameServerManager :

/// <summary>
/// Last tick received from the server
/// </summary>
public int currentTick = -1;

...

void FixedUpdate()
{
    currentTick++;
}

NetworkBouncyBall controller

Create a folder Objects in the Network folder and create the following script :

and attach it to the BouncyBall in the main game scene :

As you can see, it’s not very elegant to have a NetworkObject and a NetworkBouncyBall. The best practice is to make the script NetworkBouncyBall inherit from the NetworkObject :

public class NetworkBouncyBall : NetworkObject

Now, remove the NetworkObject component from the BouncyBall and re-set the resource id to 1 (as it was before) :

That’s much better ! We need to do one thing in order the make it works fine. Declare the Start() function as virtual on the parent class (NetworkObject) and override it on the child class :

NetworkObject :

public virtual void Start()

NetworkBouncyBall :

#region Properties
public Rigidbody rigidbodyReference;
#endregion

// Start is called before the first frame update
public override void Start()
{
	base.Start();

	////////////////////////////////////
	// Get references
	rigidbodyReference = GetComponent<Rigidbody>();

}

I took the opportunity to add the reference right now to the rigidbody of the ball. We’ll need it to get the velocity and the position too when we’ll send the message.

Send the ball position to all clients

The message sending will occurs only on the server side. so for that, we’ll create a function to send the ball position to all clients within the script :

/// <summary>
/// Send ball server position to all clients
/// </summary>
private void SendBallPositionToClients()
{
	//Create the message
	BouncyBallSyncMessageModel bouncyBallPositionMessageData = new BouncyBallSyncMessageModel(
		base.id,
		GameServerManager.instance.currentTick,
		rigidbodyReference.transform.position,
		rigidbodyReference.velocity);

	//create the message 
	using (Message m = Message.Create(
		NetworkTags.InGame.BOUNCY_BALL_SYNC_POS,        //Tag
		bouncyBallPositionMessageData)                  //Data
	)
	{
		foreach (IClient client in GameServerManager.instance.serverReference.Server.ClientManager.GetAllClients())
		{
			client.SendMessage(m, SendMode.Reliable);
		}
	}
}

And call this function into the FixedUpdated() function. We need to test in wich side we are :

private void FixedUpdate()
{
	//If we are on server side
	if (!Equals(GameServerManager.instance, null))
	{
		if (GameServerManager.instance.currentTick % 10 == 0)
			SendBallPositionToClients();
	}
}

I’ve decided to send the ball position every 10 tick to not broadcast this message every frame and then reduce the necessary network bandwith. But by doing that, we’ll see that if we update the position on message received, the behavior of our synchronization will be weird.

What’s next

As you have noticed, we don’t have implemented the client synchronization. we’ll do it on the next and before last article ! Stay tuned.

Thanks for reading.

DarkRift 2 Tutorial for Unity 3D – Part 10 – Get spawn messages from server

Reminder : You can find all the DarkRift2 related articles here 
You can find the entire project on my official GitHub

Tell the client to listen messages

First thing to perform, is to update our client to tell him to listen when a message is arrived. We will tell him to connect in background instead of auto-connect (on the inspector). Why ? Just to show you that we can connect easely with code. There is two way of connecting :

  • Client.Connect() : Connect to the server, but keep “freezed” until the connection is made. It must be used when a connection to the server is mandatory or when you need to be sure that the connection is made.
  • Client.ConnectInBackground() : Performs the connection asynchronously. Means that the game won’t freeze, even if the connection is not made.

Disable the auto connect in the client :

So, let’s update the ClientManager script with this code :

void Start()
{        
    //////////////////
    /// Load the game scene
    SceneManager.LoadScene("MainGameScene", LoadSceneMode.Additive);

    //////////////////
    /// Suscribe to events
    clientReference.MessageReceived += SpawnGameObjects;

    //////////////////
    /// Connect to the server manually
    clientReference.ConnectInBackground(
        IPAddress.Parse("127.0.0.1"),
        4296,
        DarkRift.IPVersion.IPv4,
        null
        );

}

I’ve put the server adress without variables, but in your game, you should use another way. That’s only for the example.
As you can see, we’ve added a listener on message received called SpawnGameObjects. Let’s now create this function !

Create the spawn function

ok, what wee need to do ? i will sum up this function feature :

  • Check if the received message is tagged as SPAWN_OBJECT (see Part 9)
  • Load the specified game object
  • Instantiate him at the specified position

And that’s all. Are you ready ? … but wait a minute. How can i know what is the desired ressource ? Yes, you’re right, at this point we can’t know. We need to create our NetworkDictionnary !

Network Game Object Dictionnary

The dictionnary will be useful to determines wich resource is focus for a specified ID. He will looks like this :

IDResourcePath
0“BouncyBall”
1“MyFolder/AnotherGameObjectToSpawn”
2“OnMoreGameObjectToSpawn”

As said before, for loading object, we’ll use the resource folder wich is a special folder within unity3D (see this for more information). I don’t encourage to use this in a production environnement ! But for prototypes, it’s ok.

Let’s create a new script called NetworkObjectDictionnary in the Scripts/Network folder :

Here is the code :

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

public static class NetworkObjectDictionnary
{
    /// <summary>
    /// Dictionnary that contains all gameobjects spawnable
    /// </summary>
    private static readonly Dictionary<int, string> dictionnary = new Dictionary<int, string>
    {
        {1, "BouncyBall" } 
    };

    /// <summary>
    /// Returns the specified object name 
    /// </summary>
    /// <param name="pID"></param>
    /// <returns></returns>
    public static string GetResourcePathFor(int pID)
    {
        string objectName;
        dictionnary.TryGetValue(pID, out objectName);
        return objectName;
    }
}

The dictionnary cannot be modified during run time, that’s why we specify the readonly keyword. We create a function to get the resource path for a specific if (GetResourcePathFor)

Doing it with a function enable us to modify the way how the path is retrieved whitout modifyng everywhere the dictionnary is used.

Finally, just need to create the Resources folder and add our BouncyBall as a prefab (you will need to open the MainGameScene) :

Perfect ! we propably may now writing our SpawnFunction ? not yet, because, if you take a look on the message receveid, we only have 2 informations :

  • NetworkID : wich is the network identifier
  • Position : Position where the object has to be spwaned

We are missing one information : the ressourceID wich is defined in our dictionnary. So, i think you guessed, we need to update our message model and the way we send the message.

Update the server message

Let’s firstly update our message spawn model wich contains the data structure of the message. The script to update is SpawnMessageModel :

We just will add a new property called RessourceID :

...

/// <summary>
/// Resource to spawn
/// </summary>
public int resourceID { get; set; }

#region DarkRift IDarkRiftSerializable implementation
public void Deserialize(DeserializeEvent e)
{
	networkID = e.Reader.ReadInt32();
	resourceID = e.Reader.ReadInt32();
	x = e.Reader.ReadSingle();
	y = e.Reader.ReadSingle();
}

public void Serialize(SerializeEvent e)
{
	e.Writer.Write(networkID);
	e.Writer.Write(resourceID);
	e.Writer.Write(x);
	e.Writer.Write(y);
} 
#endregion

Now, we need to add this property into the NetworkObject Script. This property will be manually filled within the inspector :

/// <summary>
/// Resource identifier for the spawner
/// </summary>
public int resourceId;

#region Unity Callbacks

private void Start()
{
	//If we are not on the server and id is not set, destroy the gameobject
	if (Equals(GameServerManager.instance, null) && id == 0)
	{
		Destroy(gameObject);
	}
	else if(!Equals(GameServerManager.instance, null))
	{
		// Get the instance id of the gameobject on the server scene
		id = GetInstanceID();
		//Register with the server
		GameServerManager.instance.RegisterNetworkObject(this);
		//Test for resource ID
		if (resourceId == 0)
			throw new System.Exception(string.Format("There is no resource id for {0} gameobject", name));
	}
}
#endregion

Just look on the Start() function, i added a check to inform us if we forget to set the resourceId for a NetworkObject. So let’s do this now. Open the prefab and set the resourceID to 1 :

And now, we’ll simply modifiy the SendObjectToSpawnTo function within the GameServerManager script :

/// <summary>
/// Send a message to the client to spawn an object into its scene
/// </summary>
/// <param name="pClient"></param>
public void SendObjectToSpawnTo(NetworkObject pNetworkObject, IClient pClient)
{
    //Spawn data to send
    SpawnMessageModel spawnMessageData = new SpawnMessageModel
    {
        networkID = pNetworkObject.id,
        resourceID = pNetworkObject.resourceId,
        x = pNetworkObject.gameObject.transform.position.x,
        y = pNetworkObject.gameObject.transform.position.y
    };

    //create the message 
    using (Message m = Message.Create(
        NetworkTags.InGame.SPAWN_OBJECT,                //Tag
        spawnMessageData)                               //Data
    ){
        //Send the message in TCP mode (Reliable)
        pClient.SendMessage(m, SendMode.Reliable);
    }
}

The spawn function (finally)

Here we are, we now can write our function within the ClientManager script. There is nothing complicated with this and the code speaks by itself :

/// <summary>
/// Spawn object if message received is tagged as SPAWN_OBJECT
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SpawnGameObjects(object sender, MessageReceivedEventArgs e)
{
	if (e.Tag == NetworkTags.InGame.SPAWN_OBJECT)
	{
		//Get message data
		SpawnMessageModel spawnMessage = e.GetMessage().Deserialize<SpawnMessageModel>();

		//Spawn the game object
		string resourcePath = NetworkObjectDictionnary.GetResourcePathFor(spawnMessage.resourceID);
		GameObject go = Resources.Load(resourcePath) as GameObject;
		go.GetComponent<NetworkObject>().id = spawnMessage.networkID;
		Instantiate(go, new Vector3(spawnMessage.x, spawnMessage.y, 0), Quaternion.identity);
	}
}

Build server and try the client

Let’s try our implementation. Here is how you need to process :

  1. Build the server by selecting these 2 scenes : MainServerScene and MainGameScene
  2. Launch the exe previously builded
  3. Open your client scene in unity : MainClientScene and start the game.

If you need more information about how to build, see the part

You should see the ball appears in the scene ! That’s cool but the position of the ball should be random because as the ball bounce in the server scene, the spawn message will send the current position of the ball. That’s why when you lauch the game several time, the ball is not spawned on the same position !

What’s next ?

On the next article, we’ll now synchronize the position of the ball with the server position. Exciting, isn’t it ?

Thanks for reading.

DarkRift 2 Tutorial for Unity 3D – Part 9 – Send spawn messages to clients

Reminder : You can find all the DarkRift2 related articles here 
You can find the entire project on my official GitHub

Create the serializable class

As seen in the last article, we’ll use serializable class to send message. We need two information.

  • The network ID
  • The object spawn position

In fact, we need a third information wich contains what object it is but it will be discussed in dedicated article.
Create a new folder into Scripts/Network and name it SpawnMessageModel :

Here is the code :

using DarkRift;

public class SpawnMessageModel : IDarkRiftSerializable
{

    #region Properties
    /// <summary>
    /// Id of the object in the server
    /// </summary>
    public int networkID { get; set; }

    /// <summary>
    /// X position
    /// </summary>
    public float x { get; set; }

    /// <summary>
    /// Y position
    /// </summary>
    public float y { get; set; }
    #endregion


    #region DarkRift IDarkRiftSerializable implementation
    public void Deserialize(DeserializeEvent e)
    {
        networkID = e.Reader.ReadInt32();
        x = e.Reader.ReadSingle();
        y = e.Reader.ReadSingle();
    }

    public void Serialize(SerializeEvent e)
    {
        e.Writer.Write(networkID);
        e.Writer.Write(x);
        e.Writer.Write(y);
    } 
    #endregion
}

You don’t need any reference to unity namespace. This class is elementary. It’s the representation of a spawn message.

Create a tag dictionnary

As you know each message is tagged to give a general meaning of what it concerns. Thus, for that, i decided to create a specific class that will contains all code tags.
Create a new Script into the folder previously created and name it NetworkTags :

For now, we just need one tag, but it’s good to be organized in order to add some tags easely. I use a classe and structs to build this “dictionnary”. Here is the implementation :

/// <summary>
/// Classes that stores all tags
/// </summary>
public static class NetworkTags
{
    /// <summary>
    /// Tags used in game
    /// </summary>
    public struct InGame
    {
        public const ushort SPAWN_OBJECT = 1001;
    }

}

Update the GameServerManager

OK, all it’s ready to create our messages and send them trough the network, but, what we exactly need to send ? There is two cases :

  1. When a client connects, send a spawn message for all network objects
  2. When a new object appears, send a spawn message for all clients.

In the first case, in a production environnement, this step is made during the loading phase. The second case is when the game runs.

First step is to create a property in the GameServerManager that will keep in memory all network objects :

/// <summary>
/// List of objects handled by the server
/// </summary>
public List<NetworkObject> networkObjects;

Don’t forget to initialize it into the Start() unity callback :

private void Start()
{
	...
    networkObjects = new List<NetworkObject>();

Then, let’s create the two functions to send our messages plus on function that will be called by all network objects in the scene : RegisterNetworkObject

#region Implementation

    /// <summary>
    /// Use this function to add a network object that must be handle by the server
    /// </summary>
    /// <param name="pNetworkObject"></param>
    public void RegisterNetworkObject(NetworkObject pNetworkObject)
    {
        //Add the object to the list
        networkObjects.Add(pNetworkObject);
    }

    /// <summary>
    /// Send a message to the client to spawn an object into its scene
    /// </summary>
    /// <param name="pClient"></param>
    public void SendObjectToSpawnTo(NetworkObject pNetworkObject, IClient pClient)
    {
        //Spawn data to send
        SpawnMessageModel spawnMessageData = new SpawnMessageModel
        {
            networkID = pNetworkObject.id,
            x = pNetworkObject.gameObject.transform.position.x,
            y = pNetworkObject.gameObject.transform.position.y
        };

        //create the message 
        using (Message m = Message.Create(
            NetworkTags.InGame.SPAWN_OBJECT,                //Tag
            spawnMessageData)                               //Data
        ){
            //Send the message in TCP mode (Reliable)
            pClient.SendMessage(m, SendMode.Reliable);
        }
    }

    /// <summary>
    /// Send a message with all objects to spawn
    /// </summary>
    /// <param name="pClient"></param>
    public void SendAllObjectsToSpawnTo(IClient pClient)
    {
        foreach (NetworkObject networkObject in networkObjects)
            SendObjectToSpawnTo(networkObject, pClient);
    }

    #endregion

Finally, we just need to call the function SendAllObjectsToSpawnTo() when a client connects. For that, we already have a listener, so just modify the callback as below :

/// <summary>
    /// When a client connects to the DarkRift server
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ClientConnected(object sender, ClientConnectedEventArgs e)
    {
        clientsId.Add(e.Client.ID);

        //Send all objects to spawn
        SendAllObjectsToSpawnTo(e.Client);
    }

Here we are ! the server will send all necessary spawn message when a client connects ! but there is something missing to make it working ! we need to register each network object to the server.

In the NetworkObject script, just modify the Start() function to have this :

private void Start()
{
    //If we are not on the server, destroy the gameobject
    if (Equals(GameServerManager.instance, null))
    {
        Destroy(gameObject);
    }
    else
    {
        // Get the instance id of the gameobject on the server scene
        id = GetInstanceID();
        //Register with the server
        GameServerManager.instance.RegisterNetworkObject(this);
    }
}
#endregion

In fact we already have written this, you just need to uncomment the line where we call the registration method.

Now, you can try it. Run the MainServerScene and take a look to the GameServerManager into the inspector. You should see in the networkObjects list that there is one entry. It’s our ball !

What’s next ?

In the next article, we will receive the message within the client. We’ll need to create a new component name SpawnManager that will be in charge to instantiate the right game object depending to the message received !

Thanks for reading, don’t hesitate to give me your feedbacks !

DarkRift 2 Tutorial for Unity 3D – Part 8 – DarkRift 2 Messages – Concepts

Reminder : You can find all the DarkRift2 related articles here 
You can find the entire project on my official GitHub

What is a DarkRift2 message ?

Each communication between a client and a server is message-based. That’s the only way to send and receive information from a client or the server. Messages are used for example to :

  • Spawn objects into the client scene, when another players connects for exemple
  • Send network objects positions
  • Send profil information (player rank, connected friends, …)

All message are send by TCP or UDP. Depending of the type, messages is built like that :

  • A : Header Message : This part is uncompressible. The size taken by this one cannot be reduced. In DarkRift2, the size has been incredibly optimized
    • DarkRift1 : 13 bytes
    • DarkRift2 : 3 bytes with UDP, 7 bytes with TCP composed by :
      • TCP additional header : 4 bytes wich are necessary for TCP communication only.
      • Flag : 1 byte : for additional information like bit numbering or future features like server-server communication or ping I think (Thx @Allmaron)
      • Tag : ushort (2 bytes) :
        What the message is about (Spawn, Position, login info, …)
  • B : Message content data : Here is the information you want to send :
    size depends on sended data

Send DarkRift2 messages

There is 2 classes (in DarkRift namespace) that we need to create and send the message :

  • DarkRift.Writer : This class will build the content message.
  • DarkRift.Message : This is the complete message with the 3 parts described above.

Here is its usage :

using (DarkRiftWriter messageDataWriter = DarkRiftWriter.Create())
{
	//Build the message data
	messageDataWriter.Write("MyDataToSend");
	messageDataWriter.Write("AnotherDataToSend");
	messageDataWriter.Write(154);

	using (Message myMessage = Message.Create(MESSAGE_TAG, playerWriter))
	{
		//Send a message to a client
		ClientToSendMessage.SendMessage(myMessage, SendMode.Reliable);
	}
		
}

If you need more information about Using(), you can find what you need here. To sum up, using this will destroy automatically the object instantiated into the Using(), wich free the memory of this object

You can see the MESSAGE_TAG parameter. It describes what the message is about. It will be useful when you need to read the message, in order to know what this message concerns

Receive DarkRift2 messages

When a client receive a message, it throws an event MessageReceived that must be used in order to read it. It’s the only way to read the message.

For this, you will need 2 classes (in DarkRift namespace) wich are :

  • Reader : This class will get data inside the message. You can read data one by one or can use serializable classes (wich are described below)
  • Message : This is the same classe used for the sending.

So, when you instantiate your client, you need to listen to this message :

client.MessageReceived += MyFunctionToCall;

Once it’s done, you need to create the function MyFunctionToCall with these parameters wich are mandatory to be compliant with the suscribtion of the event

void MyFunctionToCall(object sender, MessageReceivedEventArgs e)
{
    //Get the received message
    using (Message message = e.GetMessage())
    //Use the DarkRift2 reader
    using (DarkRiftReader reader = message.GetReader())
    {
        //Get the tag
        int tag = message.Tag;

        //read message content
        string firstLine = reader.ReadString();
        string secondLine = reader.ReadString();
        int firstInt = reader.ReadInt32();

        if(tag == 1)
            //Do something with your data
        else if (tag == 2)
            //Do another thing with your data
    }
}

The code is pretty simple to understand but there is something that perturbs me. the way of getting back data from the message. As you can see, you have to know exactly how the data are organized in the message. Of course, you know that because it’s probably you that have written the message. but it’s very inappropriate in a big network game with a lot of messages.

There is another way to send and read data from DarkRift2 messages. that’s serializable classes. DarkRift2 provides an interface that allows you to build message structure independantly from the message. Like that you can modify easly a message content whitout rewritting everywhere it’s used.

How to use IDarkRiftSerializable 

In order to use classe to send message, you need to create a classe that implements this interface : IDarkRiftSerializable .
Here is an example :

public class MyMessageContent : IDarkRiftSerializable
{
    //Data to send within the message
    public string myName;
    public int myAge;

    //Constructor (can be empty)
    public MyMessageContent()
    {    }

    #region Interface implementation IDarkRiftSerializable
    //Used by the DarkRift reader
    public void Deserialize(DeserializeEvent e)
    {
        myName = e.Reader.ReadString();
        myAge = e.Reader.ReadInt32();
    }

    //Used by the DarkRift writer
    public void Serialize(SerializeEvent e)
    {
        e.Writer.Write(myName);
        e.Writer.Write(myAge);
    }
    #endregion
}

Sending message with serializable

using (DarkRiftWriter messageDataWriter = DarkRiftWriter.Create())
{
    //Build the message data
    MyMessageContent content = new MyMessageContent
    {
        myName = "TheUncas",
        myAge = 30
    };

    using (Message myMessage = Message.Create(MESSAGE_TAG, content))
    {
        //Send a message to a client
        ClientToSendMessage.SendMessage(myMessage, SendMode.Reliable);
    }
}

We just need to create the object, fill properties with some data and then pass the object to the message.

Receiving message with serializable

using (DarkRiftReader messageReader = message.GetReader())
{
    //...
    MyMessageContent content = reader.ReadSerializable<MyMessageContent>();
    //...
}

No way, reading message like that is more elegant and maintenable. You can fin more information about DarkRift2 messages here

What’s next ?

In the next article, we will write the spawn message to all connected clients and read it in the client.

Stay informed by all my article by following me on twitter here

DarkRift 2 Tutorial for Unity 3D – Part 7 – How about synchronized objects

Reminder : You can find all the DarkRift2 related articles here 
You can find the entire project on my official GitHub

Concept

When you’re working in a Server/Client environment, there is some problems that doesn’t exists in a solo game. For exemple, the synchronisation of gameobjects.

One diffuculty is to keep a track of each network gameobjects and synchronize them. You get it ? It means that all synchronized game object on the network must have an Unique ID. This unique ID must be the same both on server and on all connected clients

Here is the way we’ll proceed :

As you can see, we need to identify on the scene wich game object need to be synchronized. After that, when the scene is loaded, we have to check on wich side we are executing the code (Server or Client side) in order to generate an unique network ID or to destroy the gameobject itself.

We’ll first focus on identifying where the game is executed.

Where game is executed ?

There is many ways of doing that. I choosed to use the Singleton pattern to accomplish this task. How it will works ?

Both ServerManager and ClientManager will use the singleton pattern. It means that we can access at any time (except on Awake()) to the ServerManager instance by writing :

GameServerManager.instance; //Return the unique instance

Download my utilities on GitHub

I provide you some utilities on my official github repository that you can download right now here : https://github.com/TheUncas/unity3D-tools

Download it as .ZIP and copy/paste the content on the scripts folder on the unity project. You should have this : (The content may change over time…)

We’ll use 2 classes wich are :

  • MonoBehaviourSingletonNonPersistent : Singleton Pattern that doesn’t persists after scene is destroyed
  • MonoBehaviourSingletonPersistent : Singleton Pattern that persits after scene is destroyed. It means that the game object will be not destroyed.

In our case, we want to keep an instance always alive, so we’ll use the Persistent Singleton. Let’s implement it on both GameServerManager. Modify the class declaration like that :

using Utilities;

public class GameServerManager : MonoBehaviourSingletonPersistent<GameServerManager>

Do the same for the ClientManager class.

Generate a unique network ID

For each object that needs to e synchronized over network, we want a unique network ID, so… let’s create a specific script that will perform it. Let’s call him NetworkObject in the Network folder :

We want from this component that it :

  • Generates an unique ID if we are on the server and register the object into a the ServerManager gameobject (To be created)
  • Destroys immediately the gameobject if we are on a client.

So simple as it is. Here is the code :

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

public class NetworkObject : MonoBehaviour
{
    #region Properties

    /// <summary>
    /// Id of the network object
    /// </summary>
    [HideInInspector]
    public int id;

    #endregion

    #region Unity Callbacks

    private void Start()
    {
        //If we are not on the server and id is not set, destroy the gameobject
        if (Equals(GameServerManager.instance, null) && id == 0)
        {
            Destroy(gameObject);
        }
        else if(!Equals(GameServerManager.instance, null))
        {
            // Get the instance id of the gameobject on the server scene
            id = GetInstanceID();
            //Register with the server
            //GameServerManager.instance.RegisterNetworkObject(this);
        }
    }

    #endregion
}

As you can see, i commented the line with the RegisterNetworkObject() because this method doesn’t exists yet on the ServerManager script. We’ll create it on the next article.

So now, let’s assign this script to the ball on the MainGameScene and launch both the client and both the server in unity :

That’s perfect. We are now ready to tell the server to send a message to the client to tell him to spawn a ball into its own scene. Of course, we’ll need to instantiate gameobject on the fly. There is 2 way to do that :

  • Using the Resource folder, wich is not an option if we decide to put in production our produce
  • Using Asset Bundles, wich is the most efficient and elegant way of instanciate objects

For our purpose, we’ll use the resources folder because it’s totally appropriate to fastly prototype a game. If you decide to make a production version of your game, you should consider to move to AssetBundles.

What about object position synchronization ?

Regarding the position of synchronized objects, we will treat this subject later, we first need to instantiate these objects on each connected clients.

What’s next ?

On the next article of this tutorial, we’ll keep a track of each network gameobject in the server scene and create our message to send to the client when he connects.

DarkRift 2 Tutorial for Unity 3D – Part 6 – Create the client

Reminder : You can find all the DarkRift2 related articles here 
You can find the entire project on my official GitHub

This article deals with the client implementation within Unity 3D. Here are the steps we’ll accomplish :

  • Create the client scene
  • Create the DarkRift2 client game object
  • Load the main game scene
  • Try to connect to the server (built on the last article)

Create the client scene

Open the project and create a new scene called “MainClientScene“. As for the server, we can delete the camera because the camera is contained on the MainGameScene.

Create a new GameObject “ClientManager” in the hierarchy :

Add a new component Client wich is the official DarkRift2 client :

The DarkRift2 Client

By adding the component on the GameObject, you saw that there is a lot of properties. Let’s talk about them :

  • Adress : IP adress of the DarkRift2 Server (127.0.0.1 is the lookup adress wich reference the current machine)
  • Port : Port number of the server
  • IPVersion : You can choose between IPV4 or IPV6
  • InvokeFromDispatcher : As you know, there is a dispatcher. You can invoke from it
  • SniffData : Print all messages into the console
  • Cache : Some settings to adjust for the cache system

For the tutorial, we can let it like that because the server we ran in the last article had this adress : 127.0.0.1:4296

Create a custom ClientManager

As for the server again, we’ll create a new script in the Network folder called ClientManager. It will handle our logic for our game regarding the client (What to do on connection, disconnect, …)

Create the ClientManager and add it to the GameObject ClientManager. For now, we just need a reference to the Client (DarkRift2).

First of all, you need to download my Utilies in order to use the script MonoBehaviourSingletonPersistent wich when is inherited, it implements the Singleton pattern. Persistent means that it doesn’t destroy on load.

Here is the link : https://github.com/TheUncas/unity3D-tools
Then, you can write the following code for the ClientManager :

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

public class ClientManager : MonoBehaviourSingletonPersistent<ClientManager>
{

    #region Properties

    /// <summary>
    /// Reference to the DarkRift2 client
    /// </summary>
    public UnityClient clientReference;

    #endregion

    #region Unity Callbacks
    private void Awake()
    {
        base.Awake();

        //////////////////
        /// Properties initialization
        clientReference = GetComponent<UnityClient>();
    }

    // Start is called before the first frame update
    void Start()
    {
        //////////////////
        /// Load the game scene
        SceneManager.LoadScene("MainGameScene", LoadSceneMode.Additive);
    }
    #endregion
}

Don’t forget to use the DarkRift.Client.Unity namespace !

Try the client connection

All is ready to be tested, do you think that we can lauch the client right now ? Maybe.. let’s try :

Seems to work. The MainGameScene is correctly loaded but if you look at the console window, you will notice that there is an error. The client cannot connect to the server.

This error occurs only if the server is unreachable. In my case (and maybe yours) i don’t have launched the server before lauching the client. Of course, it’s basic. If the client need to connects to the server, the server must be turned on before the client try to connect.

Start the server with the build we made on the last article and try to restart the client.

We did it right. The clients succeded to connect to the server. And if you go back to the server windows process, you will notice that the server displayed a message :

Yes, it informs you that a new client has connected ! Perfect, isn’t it ? …. but, wait… is the ball synchronized ?

Of course not ! we’ve just handle the client connection. We havn’t send any message to client about the ball position… And the client doesn’t know read message from the server for now.

What’s next ?

On the next article, we’ll explain how we can synchronize object of the MainGameScene and how we will do it… a lot of work is waiting for us but we are on the good way !

Thanks for reading.

DarkRift 2 Tutorial for Unity 3D – Part 5 – Build the server and load game scene

Reminder : You can find all the DarkRift2 related articles here 
You can find the entire project on my official GitHub

Load the game scene

In the last article, we’ve built our server scene. As you surely know, this scene needs to load the shared game scene between the server and the client. So, we just need to include the following namespace in the GameServerManager script :

using UnityEngine.SceneManagement;

and load the scene on the Start() function :

//////////////////
/// Load the game scene
SceneManager.LoadScene("MainGameScene", LoadSceneMode.Additive);

The LoasScene method of the SceneManager takes 2 parameters which are :

  • The name of the scene.
  • The loading mode. If we don’t specify the Additive mode, Unity will remove from the game all opened scenes and load the scene “MainGameScene” alone

It’s not finished, in order to use the correctly the SceneManager, all scenes loaded by this classe need to be added to the build settings. So, follow these steps :


Close the Build Settings window. Let’s run the game. You can see that the MainGameScene is now loaded correctly in the hierarchy.

Build the server

For now, if you have already built games on windows (or another OS), you know that what you see on unity will be displayed on a window on your computer. That’s fine if you need to test your game or build a client version, but for a server, it’s inappropriate.

Yes, the server process doesn’t need to display graphics, play sounds, …, it just needs to handle the game in a background process. Desactivating graphics and sounds for example will reduce the need of resource. By doing that, you will be able to run more servers on the same machine.

Unity provide a “Headless mode” for that. It’s specialy recommended when you build servers.

First step is to add the MainServerScene into the Build Settings. After that, you need to put the server scene at the first place of the build. Like that, when the build will be executed, it will launch the first scene in the Build Settings, to know : MainServerScene :

Select a destination folder for the build, and launch the .exe

Oh damn ! the build seems to execute but throw an exception ! By reading the message, it seems that there is an issue with the DR2 library… the error is :

NotSupportedException: Specified method is not supported.
  at System.Threading.Mutex..ctor (System.Boolean initiallyOwned, System.String name) [0x00006] in <0000d56de0ae43ca875d7babfd990580>:0
  at (wrapper remoting-invoke-with-check) System.Threading.Mutex..ctor(bool,string)
  at DarkRift.Server.DataManager..ctor (DarkRift.Server.DarkRiftServer server, DarkRift.Server.ServerSpawnData+DataSettings settings) [0x0006f] in <56e64ae805e047608792040aeb2db28d>:0
  at DarkRift.Server.DarkRiftServer..ctor (DarkRift.Server.ServerSpawnData spawnData) [0x00024] in <56e64ae805e047608792040aeb2db28d>:0
  at DarkRift.Server.Unity.XmlUnityServer.Create (System.Collections.Specialized.NameValueCollection variables) [0x0007f] in <88a1dbe1fedd44a6993e063fea92a13f>:0
  at DarkRift.Server.Unity.XmlUnityServer.Create () [0x00006] in <88a1dbe1fedd44a6993e063fea92a13f>:0
  at DarkRift.Server.Unity.XmlUnityServer.OnEnable () [0x00008] in <88a1dbe1fedd44a6993e063fea92a13f>:0

Ok, don’t worry, it’s only because the “mutex” class used in DarkRift2 is not compatible with the current “Api Compatibility Level*” parameter. So, open the Build Settings window, and open Player Settings :

Scroll down and change the value to “.NET 4.x” and rebuild the server. Launch it :

Authorize the application to communicate accross the network. And now, you cannot see any error ! Even better, if you read well, you can see a message informing you that the server is listening :

[Info]    DefaultNetworkListener Server mounted, listening on port 4296.

What next ?

On the next article, we will create the client and load the game scene too. It will be relatively simple because it will be extremely closed to what we’ve done in this article. So, if you think that you can do it alone wihtout my help, you can skip the part 6 and go directly to the part 7 !

Thanks you for reading.

DarkRift 2 Tutorial for Unity 3D – Part 4 – Create Unity embedded server

Reminder : You can find all the DarkRift2 related articles here 
You can find the entire project on my official GitHub

Welcome back ! At this point of the tutorial, we are ready to create the unity embedded server. As explained on the last article, we need to create a specific scene for the server. Let’s create the scene named “MainServerScene” :

Create the Unity server

Open the scene

As the scene is created only to host the server process, we don’t need any camera. So delete it. Create a new GameObject in the hierarchy, and name it “GameServerManager“.

Once created, add the “Server” component provided by DarkRift. See the screenshot below :

Be careful of using the right server component, don’t use the Server (Legacy) wich is an old version of the DarkRift server component

Oh, you can see that there is few properties. I will explain them right now !

XML Unity Server properties

  • Configuration : It’s a text asset that the server needs and wich contains the configuration of the server. We’ll take a look on it later.
  • Create on enable : Says to the component that the server creates automatically on startup.
  • Events from dispatcher : DarkRift2 as an internal dispatcher (see more here). Enabling this option allow the server to receive events from the dispacther.
Good to know : The server component is named XML Unity Server only because it is excpecting a configuration file on the XML format !

The configuration file

The configuration file is XML formatted and has the following structure :

<configuration>
  <server/>
  <pluginSearch></pluginSearch>
  <logging></logging>
  <plugins ></plugins>
  <data/>
  <databases></databases>
  <listeners></listeners>
  <cache></cache>
</configuration>

As you can see, there is a lot of elements. All are mandatory, except one : <cache>. – See this for reference documentation.

Are you ready to create the configuration file ? No ? Uhhh, ok, by chance, DarkRift provides an example configuration file within the unity package.

You can find it in the following location : DarkRift/DarkRift/Plugins/Server. Drag the file to the component on the GameServerManager :

Now, try to launch the scene, and take a look to the console :

That’s all, the server is mounted and it’s ready to handle client connections and broadcast messages. That’s good but it’s not enough. we need a server manager that will handle clients connections, message broadcasting, ..

So, create folder Scripts/Network and add a new C# script named “GameServerManager“. Add it as component to the GameServerManager game object

The GameServerManager component

For now, we just want that the GameServerManager keep a list of all connected clients. for that we will use a C# List and listen to an event from the XML Unity Server called ClientConnected.

Open the script on Visual Studio or your favorite IDE… wich i hope is Visual Studio 🙂 and copy this code :

/// <summary>
/// List of connected clients
/// </summary>
public List<int> clientsId;

This list need to be initialized before to be used, so in the Start() function, write this :

    private void Start()
    {
        //////////////////
        /// Properties initialization
        clientsId = new List<int>();
    }

Now the list is ready to be used, we want to fill it with all the new client who will connect. First thing is to create a property and then get the reference of the XML Unity Server.

using DarkRift.Server.Unity;

...

    /// <summary>
    /// Reference to the DarkRift server
    /// </summary>
    public XmlUnityServer serverReference;

Don’t forget to reference the DarkRift.Server.Unity namespace ! Then, in the Start() method, use the GetComponent() function :

serverReference = GetComponent<XmlUnityServer>();

Now we have the reference to the DarkRift server, we’ll just suscribe to the event in order to add the client ID to the list. So in the Start() method :

//////////////////
/// Events subscription
serverReference.Server.ClientManager.ClientConnected += ClientConnected;

Finally, create now the ClientConnected callback function :

#region Server events
/// <summary>
/// When a client connects to the DarkRift server
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClientConnected(object sender, ClientConnectedEventArgs e)
{
    clientsId.Add(e.Client.ID);
}
#endregion

Ok, that’s all. You now know when a client is connected and what is its ID. Good job !

Client disconnection

You can reproduce the same behavior when a client disconnect. Here is the step you need to follow :

  • Suscribe to the ClientDisconnect event
  • Create the ClientDisconnect callback function
  • Remove the ID from the clientsId list

Here is the code

private void Start()
{
    //////////////////
    /// Properties initialization
    clientsId = new List<int>();
    serverReference = GetComponent<XmlUnityServer>();

    //////////////////
    /// Events subscription
    serverReference.Server.ClientManager.ClientConnected += ClientConnected;
    serverReference.Server.ClientManager.ClientDisconnected += ClientDisconnected;
}

...

/// <summary>
/// When a client disconnects to the DarkRift server
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClientDisconnected(object sender, ClientDisconnectedEventArgs e)
{
    throw new NotImplementedException();
}
#endregion

I have something to say you…

In fact, you can have at any time the list of all connected clients by using the following code :

//Returns all clients
serverReference.Server.ClientManager.GetAllClients();
//Return the first client connected
serverReference.Server.ClientManager.GetClient(0);

So, yes, our code is a litte useless now you are aware (JCVD) that you can access these information natively. But later in the tutorial, we’ll use this events to perform additional actions.

In the next article, we’ll load dynamically the MainGameScene and we’ll make a server build of our “game”. Bye !