Welcome to this complete guide to sending and receiving messages using Unity’s New Network Transport Layer.
In this short course I’ll show you everything you need to know to become quite comfortable sending and receiving messages across the internet.
With the new Transport Layer unity aims to solve or at least reduce the core problems network programmers face such as latency and packet loss. So this system is meant to be a more reliable and efficient solution for people looking to build modern connected games in Unity.
Now who is this course for? This is for you if you’re looking to learn how to implement the new networking layer in a game you’re working on, or looking for a more long-term solution for networking your game and migrating from the older Unity API’s to this new one.
What We Shall Be Building
To give you a real world understanding of the Transport Layer, we shall build a complete network message system that is of the kind of quality that is used in professional card games such as Hearthstone and Magic: The Gathering Arena. In these games, like any other game nowadays, security is of at-most importance, therefore the developers resort to a Server Authoritative workflow. By the end of this series you will know how to archive the same in you game.
Download The Starting Project
To get started with this tutorial you need to head to my GitHub and grab the master branch of the Authoritative Game Server Repo.
All you need to do is open unity and import the project. Be sure to use Unity version 2019.4 or above.
Ignore The Warning
Now I’m not entirely sure why this warning keeps on popping up during the resolving of packages for this game, but it shows up for you gracefully ignore it and hit Continue.
Once this is done you should be able to run the Game.scene and test the functionality I showed you in the video earlier. Next we shall create a new network message to understand the requirement for sending an object over the network.
Understanding Network Messages
Messages get sent via the network connection as an array of bytes. We can create classes and have variables inside them that we want to have delivered across the network but this means that these classes need to be converted in a certain way into an array of bytes that can actually be sent as packets over the network.
In this section I’ll walk you through how to create a compatible network object that can be shared across the network between the client and the server. This is crucial to understanding any other step that we take from here on.
Creating The Message Class
Open the NetworkProtocal.cs file and add the following class at the bottom.
Marking this class as [System.Serializable] means we can convert it to bytes using the built in .Net Binary Formatter, only after converting it to bytes can we send it over the network, so you will need to add this attribute to all classes you want to send over the network.
public ServerNotificationMessage() { Type = NetworkProtocal.SerevrNotification; }
Adding this line means we are assigning this message the type NetworkProtocal.ServerNotification, these types are used to differentiate messages sent over the network, since these messages all come in as byte arrays, the receiver needs a way to know what kind of data is present in the message and he uses this type variable to determine that.
Adding The New Message Type
As of now the code above causes an error, to fix this you need to scroll to the top of the network protocol file and modify the network protocol class to look as follows.
By adding this line public static byte SerevrNotification = 11; you are creating a new type of message, the message class we created earlier will then assign itself this type.
And just like that, we’re done creating a new network message. Now let’s make it go somewhere.
Woohoo!! You’re 30% done with the tutorial. We’ve still got a long way to go but let’s refresh our minds with a funny gif and keep learning
Sending The Network Message
Alright, I have built a level of obstruction into this networking system that allows us to send and receive Network messages quite easily, we will use this for now. Later on we shall actually look into how to build this kind of system from the ground and how messages are internally filtered to figure out what type they are.
The server has certain classes that help it know when something special has happened over the network. We shall use the class that’s in charge of handling movement of cards on the board to send the client a notification whenever the server realizes that he has made a move.
To do this head over to the Server_CardMessagesHandler class and modify its OnCardMoved() function to look as follows;
By adding “//ADDED DPORTION” code, you are telling the server to construct a new message and send it to the client that has just moved a card. As simple as that, we have now sent a message to the client. We now need to prepare the client to receive this message.
Handling Messages Sent Over The Internet
To handle messages that are sent to the client we need to create a new method, just like the OnCardMoved() method on the server this method will be called whenever something special happens across the network
This particular function is called when the sever acknowledges that we have made a move and is happy with the outcome of the move.
First, let’s make the method to be called. Open the GameClient class and add the following method.
This is a simple method, all it does is display the message that has been sent by the server. Note that the method takes in a MessageBase and NetworkConnection as parameters, this is the general format followed by Network Handler methods, it provides them the message they are to receive and information about the sender of the message. We shall look at why this is and how to change it later.
Creating the method alone will not call it though, we need to let the client know that this method needs to be called when a message is received. To do this modify the RegisteMessageHandlerClasses() to look as follows.
Calling the AddMessageHandler(NetworkProtocal.SerevrNotification, NotificationHandler); portion allows the client know that we want our method to be called when a message is received, furthermore it lets it know that we should only be disturbed if the message matches a give type, in this case, the ServerNotification type.
Now when you play the game, a message should popup whenever you make a valid move. We have now learnt how to send and receive messages, however our approach to receiving messages still needs some work. Next let’s further understand this concept of message handlers.
We’re now 60% done with the tutorial. woohoo!! We’ve learnt a lot, but this last portion is a professional extra. There is reason to celebrate!
Understanding Message Handlers
The Importance Of Encapsulation
One main component about object oriented programming is encapsulation, specifically the fact that it is advisable for every class to only contain functions and variables that are needed to perform a specific task.
The problem here is that we are overloading the GameClient, we are including methods that are not related to it’s task. It’s task is to start the connection and listen for messages that come from the connection. Right now we are also making it perform the task of handling notifications from the server. This is not good because it clatters the class with unrelated functionality. Thankfully there is a pretty easy way around this.
Enter Message Handler Classes 😉
Message handler classes allow us to hold all the information and functionality needed to handle a related list of Messages that our client can receive. We then let the client know that whenever a message of that sort comes in, it should be sent our way.
In the image above there are different handlers for numbers, letters and miscellaneous messages, as you can imagine the message of type A that is coming in would follow the path highlighted red. Client → LettersHandler → OnLetterAReceived Method
Now you know how handler classes work. I’m pumped to create one with you.
Creating A Server Notification Handler Class
Create a new script and call it Client_NotificationMessageHandlerClass open it and modify it with this code.
The class derives from MessagesHandlerClass because this is the base type for all classes that want to register to the client for notifications. Select the base and hit F12, let’s inspect it. This is an abstract class and provides two things that we are sure all message handlers will need. First is a reference to the NetworkBehaviour whose messages we are handling, an example of a network behaviour is the GameClient class. Second is a constructor that takes in the NetworkBehaviour and saves it. That’s pretty much it for the MessageBase.
The Variable client is our way of knowing that we are a handler class for the client and not any other type of NetworkBehaviour i.e Server. The way we get this variable is by simply casting the network behaviour stored in the base to a Client. If we get an error we shall know right away that an incompatible network behavior is trying to make use of our handler.
The Constructor is a rather interesting part of this script for anyone that might be new to C#, be sure to check out this reference if you’re unfamiliar with the way this class overrides it’s base’s constructor.
A more important part of this class is the fact that it calls _client.AddMessageHandler(NetworkProtocal.SerevrNotification, OnServerNotificationMessage); on line 10. Hmm… think about it, we’ve seen this code somewhere before. 🤔🤔
Did you guess? This was the exact same code we wrote earlier to let the client know what function to call when the server sent it a ServerNotification. Impressive, so internally these message handler classes just have a list of functions for different methods, then in their constructors they take in a NetworkBehaviour and let it know when to call each one of their methods as handlers.
The concept I’ve explained is really the core to proper handling of events across a game network, be sure to read it a second time if you haven't grasped it properly, be sure to go through the code as well.
Pro Tip; I have joined many projects where a previous programmer wrote single class that had all the different message handler methods Do Not Do This. This means that a programmer editing a single method would need to know how all the other methods work to be sure that he won’t mess anything up while editing this class. Besides that, it creates excessive cluster in the class and makes it hard to understand. Be sure to use the full power of C# Events, Lists and Interfaces to keep the Network Code in your game well distributed.
Phew!! That was a long explanation, I’m glad you finished it, but we’re not done just yet. Now to the easy part, we have created the NotificationHandlerClass and set it up properly. Now our client needs to subscribe to it to use it’s methods to handle messages.
Adding A Handler To A NetworkBehaviour
p.s I’m glad you really like my spelling of the word behavior
A NetworkBehaviour is any class that can create a connection to another computer. It is the head of our connection, and no network related action can happen except through it.
The GameClient class derives from NetworkBehaviour and is the head of the connection on a client machine. The server also has a NetworkBehaviour class called GameServer.
Now that we know what a network behaviour does, lets make an instance of the NotificationHandler class to help us handle such messages in our GameClient. Open the GameClient class and modify the RegisterMessageHandlerClasses() as follows.
As you can see we have removed the line that added the NotificationHandler Method from before, this is now done automatically by the Client_NotificationHandler class, be sure to remove the method in the GameClient as well, as we shall not need that anymore.
Now if you play the game you should see the same notification popup to let you know that the server has approved any move you make. Notice how the server only approves on move per turn? This part of the Server Authoritative functionality. We shall take a look at that in another tutorial.