📜 ⬆️ ⬇️

Browser Strategy Development

Hello!

I want to share with the habrasoobshchestvom their experience in writing a multiplayer browser-based strategy from scratch to the working draft. From the point of view of programming, architecture and problems. This is my first experience of creating a game and maybe you will notice a lot of shortcomings or blunders, or you can advise what is useful. But it does not matter - the main thing I brought the matter to the working draft and surely many will be interested to learn the details.

image
')
What is a game? Apparently the shortest description would be “clone of Civilization” =). But this does not mean that I did not have enough imagination to come up with something of my own. Just making “Civilization” was my dream. I would hardly have received so much satisfaction from writing another game. Well, the fans of Civilization, on the contrary, consider that my game is not at all like Civilization, except perhaps only in appearance. Maybe it's for the best.

The game is called The Fate of Nation http://fatenation.com

To paint the architecture and logic of the application can be indefinitely, so you will probably have to divide the article into several parts, if you are interested in it. In addition, I do not see much point in citing a lot of code, since it is possible to implement what I have written in any language and platform.

To create the game I used php and MySQL on the server, html and javascript on the client. Flash is not used. From html5 there is only a video on the site and several areas with a canvas in the game itself - including the map surface and a mini-map. The amount of client-side code is several times larger than the server part, so I’ll mostly talk about client development, but let's start with the server.



General architecture

The overall architecture of the application looks like a fully asynchronous web application in JavaScript. Reloading pages is not provided. Exchanging data with the server exclusively via Ajax and JSON . In JSON, only data is transferred, without html code. Html markup is loaded separately at the beginning of the application download and is processed with the data through the client templating engine as data is loaded from the server.

image

I didn’t use any frameworks on the server - although I started writing using the Zend Framework, which I then mowed down as unnecessary. Instead, he created his simple architecture, vaguely reminiscent of controllers and action games from ZendFramework .

As can be seen from the figure, on the server one entry point is the index.php file. During the game on the server are requests like this: / Unit / Move. And JSON is sent with parameters, in this case it is the id of the unit and the coordinates of the movement. The server redirects this request to index.php, which sequentially connects to the database, checks the current user and parses the query string to determine the controller (Unit) and action (Move). If the controller is not specified, the server issues an index page with the code for building the client application, but more on that later. If the controller is set, then the file of this controller is searched, its code is connected and the processing of requests of this controller is started, where the necessary action is searched accordingly, and the input data is checked and the business logic is jerked.

image

To work with the database, a special class of database abstraction has been written through which all requests to the database from business logic and controllers, data shielding and other small conveniences pass. Actually on the server, everything is quite simple with the architecture, and the server’s area of ​​responsibility is only to verify the input data and output information from the database. The client does the rest.

Now a little about the game itself.

Map

The first thing that was done was a map on which almost all game actions take place: building cities, improvements (crops, roads), moving units and exploring the map. The map size was 1000 per 1000 cells for each individual entry in the database. I saw games where the map was made infinite and the cell entries were dynamically inserted when some actions were performed with the cell. But this approach scared me a little with its unpredictability. It is much easier to plan a game when you know for sure that you have a fixed card. You can plan the location of the players, their number, the number of cities and units, approximately estimate the load.

Total it turned out 1000 * 1000 = 1 000 000 records in a DB for a card. Before that, I did not work with so many entries and it alerted me. I thought that it would slow down.

image

I decided to outsmart MySql and place the card in 10 tables of 100,000 entries in each with the hope that it would work faster. As a result, I had to write stupid logic on a sample of cells from several tables at once, and the measurements showed that the performance only fell. Returned everything back to the 1st table.

image


Of course, this is not the entire list of fields, but hereinafter, for simplicity, I will cite only those fields that are described in the article.

Regions

The client is written in such a way that it does not request certain cells from the server, but requests them in batches of 100 pieces (10 per 10), which I called regions. That is, each cell belongs to a region and the client requests regions and not specific cells. As soon as the player moves the map so that the new region becomes visible, we send a request to the server behind this region and bordering it. The data of each loaded regionaccise for 30 seconds on the client. This allows you to easily scroll the map without brakes and unnecessary requests to the server and eliminates the delay when a new region appears on the map - since we load all the neighboring ones in advance.

When I did these "regions" I did not expect how much they would increase productivity. It turned out to allocate 100 cells by filtering across the field of the region is obtained many times faster than by filtering by coordinates. In spite of the fact that I combined the x and y coordinates of the cell into one field, location = 1000 * x + y. This was done primarily for convenience - to make it easier to get one cell.

image

Then, each entity (cities, units, resources), which are located on the map and have correspondingly specific coordinates, I also marked by region, which increased the performance of samples hundreds of times. It's one thing to look for values ​​in a table by key with a million unique values, and another thing by key with 10,000 values.

Thus, this system turned out to be: the client requests the regions - the server pulls a card from the database and all the entities on it, quickly filtering by region - the client draws it all in the browser on the canvas. Each entity has such fields as the time until the end of the battle or the time before moving to the next cell - in this case, after these timeouts expire, we update locally only what is required. For example, if we examine the map, we load the newly opened cells and no more. If the enemy unit has moved - reload the next point of its movement.

Study card

However, I was tormented by another question. I desperately wanted to do a study of the map - so that initially it was not explored and you had to walk on it to see something.

image

This I have not seen in browser games (in fact, as well as units moving around the map, and not through the air). I began to pay. The starting position of the player is located within the region. That is, the maximum number of players is 10,000 as well as regions. Each player can scout the entire map. A total of 10,000 * 1,000,000 = 10 billion records can be in the permision table per cell! The map table appeared in the background of this baby talk =). Of course, this figure is too high. It is unlikely that anyone will be able to scout the entire map - it is very big. But tens and hundreds of millions of entries in the permishen table can definitely be at the end of the game.

And on this table it was necessary to join all the entities including the map itself each time the regions were sampled. Here again, I was saved by a key across the field of the region, which allowed me to make these joins much faster.

image

It was not possible to conduct load testing to determine at what stage the server will begin to slow down. The maximum that I saw was just over 2 million entries in the permishen table.

Moving units

To make the movement of units, I also had to think and rewrite the logic several times.
The first thing we need is to accurately track the opening times of new cells so that cells, units and cities can be filtered out of this data. Immediately it is suggested to use the permishen table on the map, but with a special field - meaning the time when this record will become active. That was done. The client sends the id of the unit, and the new coordinate of the dislocation. The server calculates the current position of the unit, the coordinates of the cells along which it will move, and depending on the territory of these cells, the type of unit and other parameters, calculates the time when this unit will be in each cell. Then the neighboring cells are calculated additionally in the same way, depending on the radius of view of the unit.

All this is inserted into the perm table on the cells and the map works like a clock. A unit walks around the map, each time we move it, we request the cells around it using standard methods that will filter out entities based on new permishen data, taking into account the activation time of the perism, where there will be cherished open areas.

Next, the records of the permissions that talk about the movement of a unit we mark with 2 more fields: the id of the unit and the type of the record: ' survey cells ' or ' cells that the unit goes by '. The first field is necessary so that when a unit is stopped or a destination is changed, it is possible to delete them, the second is necessary so that when a unit is selected, record the times of change of location for it.

image

Then my colleagues suggested another rather obvious point: to enter the field indicating the time the unit exited the cell. I called it out_timestamp. This made it possible to easily select the current positions of all units and, accordingly, filter enemy units according to the visible cells.

I am sure that my example is not the most successful architecture for such a game, but it seems to work =) In the following articles I can talk about client architecture, caching, the framework used and how I managed to make a demo version of the game working without server requests, purely on the customer.

Yes, by the way, often after different posts about the game, people start praising the graphics, not the gameplay. So I will say right away - I did not draw it !!! This is all our artist-designer Maxim Kudritsky.

PS Thanks TheShock for the help and support in writing a topic! =)

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


All Articles