In the past, I’ve described the process of launching the game in Co-op mode and improving the synchronization of players' positioning and animations. Read on to learn more.
Interaction with the environment
I decided to synchronize environmental objects (i.e. trees, bushes, items lying on the ground, plants that can be gathered, and so on). In general, the synchronization of these objects occurs according to the same scheme:
- Firstly, we define the entire set of events that can occur with the object. For example, for an item on the ground, this is picking it up and dropping out, for a tree - dealing damage to it and cutting it down, for a plant - gathering it.
- For each event, it's necessary to create a data packet which includes the indication of the object and the operation on it.
- Send this packet to all other players.
- When receiving a packet, resolve an object, an operation, and perform this operation with the object.
For example, since the map is initially synchronized for all players, players A and B both see an apple lying on the ground in the same place. Player A gets closer to that apple and wants to pick it up (clicks on its label or presses LCtrl).
What happens next? An apple is added to player A's inventory and information is sent to the server (this was already mentioned in the previous post). However, the apple must be removed from the ground so that no one else can pick it up. Therefore, player A forms a data packet, places the apple in it, and writes the operation "update quantity" (equal to zero). Player B, having received this packet, will understand that the apple just needs to be removed, and will do it. Everything is quite simple, but this scheme does have its difficulties.
Regarding simplicity, there are not many objects in the environment and include a tree (in the game a bush is also considered a tree), a stone, a plant, an item, and a decoration (which includes barrels, boxes, fences in the first location, and so on). There are not many actions that can be done with these simple objects.
The second item listed above has one difficulty - you need to be able to point other players to a certain object (a particular tree or a specific bush of wheat). The need to point to specific objects will be useful later in synchronizing other game elements like effects, attacks, and animals. This means that we require a universal mechanism and so, each object or other element in the game must have a unique ID.
During the initial map loading, the server assigns these identifiers to all objects and - when the client connects and requests a map save - the server sends these IDs along with other data. Clients should also be able to assign IDs to new objects and the events they generate themselves. At the same time, IDs must not be duplicated, otherwise there will be confusion. To generate unique, unrepeatable identifiers, there is a ready-made, simple, and secure solution - GUID (Globally Unique Identifier)
With just one line of code I can generate such identifiers, but their size is 16 bytes. Since the links to the objects will have to be sent constantly, I decided to go a more complicated route by using unsigned integers of 8 bytes as identifiers. Session number is always stored in the first 4 bytes, and the ID itself is stored in the remaining 4 bytes. Every time someone connects to the game, the server gives them a session number which will increase by one each time. Even if the same client connects to the game, exits, and connects again, they will receive a different session number. 4 bytes for identifiers within one session is enough even if you generate events and items 100 times every second continuously for a year.
In order to send a data packet to other clients (point three of the list), each such packet is sent to the server where it is copied and sent to all other clients. I wrote about the ways of sending packets in my previous post. Afterwards, one of the Factorio developers wrote to me. If you’re reading this, hey! And hello to all other developers who have been brought to my blog!
So, he told me that the network communication schemes I wrote about are called Star (when all messages pass through the servers) and Mesh (when each client sends messages directly to other clients). Based on experience, he also advised me to use the Star model instead of Mesh. Additionally, he told me about some of the subtleties of the Internet and the specifics of sending messages via Steam API. Thank you very much, kind man! Now, what was I talking about? Oh yes! The Star scheme is used to send the package to all clients for environment packets.
There are no difficulties in receiving the packet, resolving the object by ID, determining the operation, and performing this operation. However, there is another significant point, which (I think) is a big problem for almost all online games: What happens if two players simultaneously click on an apple to pick it up?
If you leave everything as it is, the apple will slide into the inventory of both players, leading to a simultaneous message sent to the server that the apple should be hidden. The apple will be removed, but a total of 2 apples will be added to the players' collective inventory. To resolve such conflicts, we have to make an object locking system. Before picking up an apple, each player sends a request to the server to allow the operation with the object. The server sends a positive response, while noting that the apple is currently in use. If someone else asks the server for permission to do something with this apple, the server will deny them. After the player (the one who had received initial permission to work with the apple) picks it up, they immediately send a message to the server that the work with the apple is finished.
After all this was implemented, players could already see the interactions of other players with the environment. At this stage, I posted the first video demonstrating these capabilities. I will post it again since it corresponds well to what I’ve described. Keep in mind that it is an old video which you may have already seen.
Now let's check the task list:
- Lobby - Done!
- Starting the game and setting up the connection - Done!
- Auxiliary tools - Done!
- Synchronization of heroes - Done!
- Interaction with the environment - Done!
- Synchronization of audio and video effects - Done!
- Jumping between worlds - Done!
- Synchronization of constructions - Done!
- Synchronization of crafting and building - Done!
- Synchronization of monsters - Half done
- Synchronization of quests and scenario events - Not ready
- Testing and balancing - Not ready
Since I first wrote this list, I realized that there are 2 more tasks that should be implemented - synchronization of the minimap and the weather. However, these are not very large tasks and, in total, will take no more than a week. All together, it should take about a month of work. However, for greater reliability, I’m estimating it will take around 2 months. In total, the expected release date of the multiplayer at the moment is mid-March.