⬆️ ⬇️

Implementation of the backend in multiplayer online games

During my short life, I have never met a programmer who would not like games. And even more so, a programmer who never wrote them.

Someone starts with Tetris, someone with a snake. Someone passes this hobby, and someone “gets sick” with this and turns his illness into a favorite job or an entertaining hobby.



In the era of the Internet and social networks, it is not interesting to play alone, I want to communicate and play with friends.

And not just to communicate, but to walk in a group in a dungeon or show who is the boss in the arena.

In this article, I would like to talk about my approach to the server implementation of such interaction.





')

Task



Consider an example of a simple game application in which a player controls a character who can buy / sell items, fight in the arena, go to the dungeons alone or with friends.



Concept



For example, a player creates a group to go to a dungeon. The player sends a request to our server, which adds a new group to the list of available groups for hikes, after which the other players have the opportunity to receive this list and join. The group is filled, and, by pressing a button, the creator sends it to the dungeon to meet dangerous monsters.



It would seem that everything is simple. Create a table in the database, write down those who wish there and send it to the dungeon.

So many novice programmers think, but they forget about the most important problem of multitasking systems - race condition .



It is logical to assume that the correct implementation of such interaction would be a server daemon who was responsible for the state of the game world, received requests from players (in turn), processed them and gave the result.

In our case, the server must keep the current state of the players in order to be able to correctly process the results.



Schematically it looks like this:



The basic scheme of our demon



Perhaps the most interesting is the request handler.



It contains the main logic of our server:

- storing the status of players (free, located in a dungeon, in a group, etc.)

- storing and processing information about the game world (battles in the arena, trips to dungeons, groups for trekking)



We need to keep the player’s status so that, for example, while the character is engaged in the genocide of monsters in the dungeon, we couldn’t go to the arena or buy a new armor.



In addition, the server should handle the game world.

Let's say we went to the dungeon for 5 minutes. In 5 minutes, the server must process the battle with the monsters, count the experience, drop, and also change the player’s state.



It should be remembered that the number of players can be very large and our server will have a hard time.



Implementation





To implement such a demon, PHP 5.3 was chosen with the libevent extension as the most familiar to the author.

There are many articles about libevent, the well-known phpDaemon has been created, so it doesn't make sense to delve into its work.

It is worth noting the possibility of creating deferred events (the EV_TIMEOUT flag), which in our case solves a lot of problems.



However, our server should, among other things, actively work with the database to record the results, buy things, etc.

As is known, a database is a bottleneck of any serious application, and our server can “lie down” from a large number of requests.



Therefore, to handle "heavy" requests, you can provide an additional server daemon with the number of threads we need (thread / workers), which will be happy to do all the hard work.



Extra demon



It is worth noting that requests to work daemon also queued and are not processed until we receive a response.

The principle of work daemon is the same as the main daemon, except for the presence of several threads (workers / workers) for processing requests.



findings





The advantages of this approach are as follows:

- all the hard work we drop on the work daemon, and we leave the processing of player states on our main daemon, which makes it very lightweight and fast

- work daemon can be taken out on a separate server, if necessary, and the number of workers varies depending on the iron

- possibility of scaling



And now about the shortcomings.

The main lack of implementation: PHP flows. Even the new garbage collector in 5.3 does not solve all the problems. And we have:



Memory status for each worker



Solution: periodically overload the worker when the amount of memory used reaches a certain limit.



The most correct solution I see is the use of the Erlang \ OTP programming language.

The task set fits perfectly into his concept of FSM / gen_server, which the author plans to do in the near future.



I hope that the article will help beginner game developers not to step on the rake and properly design their application.



PS If this kind of topic is interesting to the residents of Habr, then he is ready to tell in more detail about each of the components of such a system.

Source: https://habr.com/ru/post/122840/



All Articles