Multiplayer implementation is somewhat similar to the work of a surgeon. I have 2 computers running the game. I need to synchronize each event that occurs on any of them - form a package for it and send it to the other computer, so that this event is displayed there as well. As a result, it's like I'm stitching 2 games together, painstakingly connecting each event.
Save and load functions play an important role while syncing. I can save the game on the server, and save it not to a file, but to an array of bytes. Then I can send this array to another computer, and load the game from it, as if it was a save file. In such a simple way, I quickly achieved that after starting the game on two computers we see the same world. Even if player on the server managed to cut down a couple of trees before the client connected to it, the client will see that trees have already been cut down, because when connected, the server will send to it a save of the world's current state. However, after both players have loaded the same world, nothing connects them anymore - everyone plays his own game. That's why it's necessary to synchronize all game events further.
Synchronization of heroes
The first thing I synchronized was main heroes of all players themselves. By this I mean not only their position and movements, but also their customization, inventory, weapons in their hands, and so on.
When saving the game, the server began to save all characters' settings, sending them to the rest of the players along with the world files. Also, the server assigns a unique ID to each connected player, by which the players then distinguish each other. Hurray! As a result, all connected players can be seen after the game start. But for now they just stand still and do nothing.
2 Methods of client-server communication
First you need to decide which way to send packages. There are 2 ways: 1) each client sends data packets to everyone else directly and 2) each client sends it's state to server, and then server forwards it to other clients. When there are only two players, there is no difference - in both variants each client simply sends a package with it's condition to the second one. However, when there are lots of players, the situation begins to change. Suppose we have 10 players. In the first method everyone has to send a packet to the others, i. e. 9 packets. Totally, to synchronize the state of all characters on all computers, we need to send 90 packets. In the second method, each of 9 clients will send 1 packet to the server, and the server will send 1 packet with the state of all players to each client, i. e. a total of 18 packets (5 times less than in the first method). But also the delivery time of a package from one client to another is doubled. For the first part of the game I chose the second method as more versatile. However, for the second part I reevaluated both of these methods and came to the conclusion that the first one is better. The maximum number of players is 4, and in this case 12 packages will be sent against 6. However, we'll need to send many other packages - with the states of monsters, buildings, environments, special effects, and so on. So 6 extra packages won't make much difference. In addition, these extra packages completely fall on the clients, not on the server, which in this case is the bottleneck of the system. But the delay in the game will be halved.
By the way, I managed to significantly optimize characters' state packages themselves compared to the first part of the game. If in the first part the full state of the character sent over the network took 166 bytes, now it takes only 26 bytes, i. e. 6 times less. Which is very useful, because in the future I still have to synchronize monsters, which in the second part we have several times more.
The method of communication of each with each is implemented only to synchronize characters' position and animation, where the minimum delay is most important. For most other packages, a centralized scheme was chosen, when all packages pass through the server. This allows to ensure that the order of packages processing is the same on all clients (in which order packets arrive at the server, in the same order it redirects them to clients, and in the same order clients process them). If clients send packets to each other, then their order may be violated. And this can create problems.
Motion smoothing
Just sending a character's position regularly will not be enough. Packets arrive from one computer to another with different delays, even if both are in the same room. As a result, movements of the remote characters are intermittent, discontinuous, not smooth. To get rid of this, I need to smooth out the received data. In the second part I significantly improved the algorithm from the first part - it not only smooths characters' position, but also foresees their movement in advance, based on previous data. As a result, I can get smooth and natural movements even when sending only 10 packets per second (in the first part 30 packets were sent per second).
Synchronization of settings
In addition to the characters' position I had to tinker a little more with the synchronization of their animations. The task is not very difficult - you just need to track the change in your animation and send a new animation to the rest of the players. However, the difficulty was to figure out how to get such control over the animations in Unity.
After synchronizing heroes I implemented the change of weapons, equipment, inventory, hot slots and all client settings (such as the position of interface windows on the screen, the selected preferred ingredients in recipes where there is a choice, the skill tree, the state of hot slots and much more). There were quite a lot of tasks and they took a decent amount of time, but it doesn't make sense to describe them in detail - they were all implemented according to the same scheme:
- we monitor changes in something, save these changes to the stream, send it to the server
- the server stores this state (so that in further game sessions with the same player to send him all his settings)
- the server redirects to other clients only the part of data that directly affect character's appearance
Ugh! A lot of text. I'll try to describe other tasks in less detail. In the meantime, let's check our ToDo list - since the previous post, the "Synchronization of crafting and building" item has moved to ready tasks:
Write a comment
Robert (Thursday, 16 December 2021 17:47)
Loving the details of everything you're working on (and the length is great!). Thanks for all the blog updates!