Hello everyone and welcome to our devblog!
In today’s post we are going to talk about the NetCode we’re implementing for Interlayer and especially the way we synchronize the client application with the server. Here is the video of how it works:
First of all let’s define what exactly is happening here.
Client and Server Worlds
As you probably know, we are going to have a cooperative mode in the Interlayer, which means you can play with your friends online on the same server. In this case one of you will host the game and others will join. When creating a new game (a server), the new world will be procedurally generated, so that every time you start a new game, the set of available resources, creatures, landscape, etc., will be completely different. The world that was just created on the Host machine is called the Server world.
The next step is when somebody connects to the server to join the game. When this happens, the client has no idea how the world looks like and it has to get this data from the server. As you can imagine, the generated world can be quite big, so it doesn’t make any sense to send the whole Server world to all clients and keeping it in sync. The only thing we need to synchronize is the information that the client player can currently see.
Let’s see how it can look like. I am personally a fan of the World of Warcraft, so I’ll show you the example using one of the famous locations of WoW: Northrend.
Let’s imagine the client player has just logged in and is currently spawned somewhere in the Icecrown. Then, the only part of the world this player should know, is a small area around the player:
That’s exactly what we do in the Interlayer. We send the part of the Server world to the client, but only the part that the player can currently see. We call it the Client world (which is different for each connected client). Let’s see what exactly we synchronize between the client and server worlds.
What to synchronize?
There are many parts that have to be synchronized between the server and client, but they all can be combined into a few abstract entity groups:
Today we’re talking about the Objects’ synchronization. What is the object? Here are a few examples:
- Building that the player built
- Tree that can be cut
- Stone that can be mined
- Coal ore that can be mined
- Any other object in the world that the player can interact with
Each of those objects have different logic & parameters. For example, if you mine a Coal ore from the Coal deposit, the amount left in this deposit will decrease and another player should see that change immediately. That’s why all the players’ actions should be sent to the server and then to all other players so that they see the change.
How to decide what objects to synchronize and what not? That’s why we need the red & green circles that are shown in this post’s video. Now, let’s dive in and see how we implemented this logic.
Minor & Major Radiuses
As you can see from the video, there are two different circles around the player:
The small green circle is called the Minor radius or the Synchronization radius.
Few times a second the server checks which objects are currently inside the Minor radius of each player. After obtaining a list of those objects, the server checks if the object was already inside the radius or just got inside it during the last check.
If the object was outside the minor radius before and entered the minor radius, server sends the object’s information to the client and the object appears in the Client’s world.
If the object was inside the minor radius already, server skips it.
What happens when the object leaves the minor radius? Nothing. This will be handled by the Major radius.
The big red circle is called the Major radius or the Caching radius.
Apart of the Minor radius, Major radius is not considered on the server side, but is used on the client side instead. That’s what happens:
Since there is no way for client to know if the object entered the Major radius (server didn’t send information about the object as it didn’t enter the Minor radius yet), nothing happens in this case.
However, when the object leaves the Major radius, the client application deletes the object from the Client world.
One may ask a question: why don’t you delete the object from the Client world once it leaves the minor radius? Well, there is a reason:
Every time the client app has to create a new object in the Client world, it actually spends quite some time on instantiating the 3d model, materials, initialize scripts, etc., which means there is some boilerplate for creating an object. Taking into consideration that we want Interlayer to be a game with quite a dynamic gameplay, we think that our players will move through the map a lot, which will make the objects appear and disappear all the time.
To lower the amount of instantiations, we created some kind of the “buffer area” in between of the Minor & Major radiuses. The objects in this area don’t get updates from the server (don’t get synchronized), neither they are destroyed. The main idea is that this area is some kind of a caching area where we keep the objects that the client may require soon when the player moves along the map.
Once the object from inside the Major radius gets into the Minor radius, the server sends the update to the client and instead of creating a new instance of the object, the client will just update the data of the object, which will save us some time.
Let’s take an example.
- The player 1 is moving along the map and approaching an object that gets into the Minor Radius:
Server sees that the object is in the Minor radius of the player and sends the information of this object to player 1. The object appears in the player’s world.
2. Player 1 decides to go north and leaves the object behind. The object leaves the Minor radius
Nothing happens. Literally. We don’t take this event into consideration.
3. Player 2 approaching the object and (lets imagine the object is a Storage container) takes some resources from the container.
Since the object is changed, the server looks for the players who have the object inside the Minor radius. Since our player 1 has moved to the north, he/she won’t get the update of the change that the Player 2 just did.
4. Player 1 comes back to the object to get some resources just like the Player 2 just did
The object enters the Minor radius of the Player 1 again. Server sends the command to the player 1 to create the object in the client’s world. However, since the object was never destroyed on the client side (it was always inside the Major radius), there is no need to create a new instance of the object but just to update the data. Since the player 1 gets the update of the object, it will also know that Player 2 has just took some items from it.
5. Player 1 leaves the base and go looking for adventures.
Now, since the object leaves the Major radius, the object in the first player’s world will be completely destroyed.
Thank you very much for reading, we hope you liked it and it was interesting to have a look at the way Interlayer works under the hood!
If you like the posts from the devblog, please let us know by liking or commenting it in Twitter so that we know you’re interested and continue sharing more info about the development process of the Interlayer!