Hello! Most recently, I began to talk about how we are working on Stormfall: Rise of Balur and writing the client part of the project on Unity. Today we will talk about the approach to skinning, multithreading, networking with a poor connection and query caching.

Skins and work with them
The genre of RTS adapts well to different settings based on the same engine. We have several such games. When writing a project, a lot of things change: UI, UX, gameplay logic. Sometimes there may be specific requirements for the integration of libraries and social services. When designing the game, we had to lay the requirements in the architecture, while maintaining the maximum possible sharing of the code.
In order to customize the behavior of the UI, we decided to make the operation of UI objects begin with the dynamic loading of prefabs from resources. After that, a specific View script is run, which is tied to special classes from the ViewModel. The model itself is written to support all features, because it is a reflection of the server and is configured when logging in from the server.
')

In Unity there is no file with a description of the project: all the code that is in the Assets folder gets into the build. Under such conditions, it is difficult to create several projects with a common code base. We decided that we would use a common repository, in which the project is not in the form of a Unity project, but in the form we need.
Using the script that creates the symlinks on the folder with the code, we expand the Unity project. If necessary, deploy the dev-build. It has all the code, including from other projects. This is needed, for example, to refactor, but the gameplay will not affect in any way.
Multithreading in Stormfall: Rise of Balur
When the mobile market only gained momentum, the developers discussed whether multithreading is needed for their applications. Now it is no longer a matter for discussion. Multithreading we solve several problems:
- Perform callback requests to the server in threads from ThreadPool. The commands themselves, in addition to performing WebRequest, serialize the data sent to the JSON format and deserialize the response. They also carry out the process of updating the model with new data that comes in response.
- Execution of the mechanism for updating the model in a separate thread. The mechanism is triggered once a second and can run for quite a long time.
The data access mechanism of the model works through the monitor and supports various data blocking policies. It gives read access to a number of objects at the same time, but does not allow the possibility of combining this with writing data to the Model.
Unity does not allow to work with the engine from a minor stream. Keep this in mind when designing your application, and try to unload the main thread while taking non-UI data processing to other threads.

Work with the network with a bad connection
In Stormfall: Rise of Balur, interaction with the server occurs via HTTP requests. In the event of a request error, we cannot always determine at what stage the error occurred and whether the request was executed on the server. It must be remembered that mobile games work through the mobile Internet, which is not always stable. To provide users with a comfortable gameplay, we have implemented several approaches:
- Optimistic query execution.
- Overrun user request. This method protects against double execution of commands if the request was for changing data and terminated on return to the client.
Now in more detail about each approach.
Optimistic query execution
We implemented a mechanism in the query commands that allows you to avoid UI locks when they are executed. After the request begins to execute, we change the model so that the server returns a successful response. If the answer was really successful, we do not change or supplement the model with a response from the server. If the server returns an error, call the rollback method of the changes that were executed at the initial stage and notify the user of the error.
What exactly have we done for this:
- They implemented a separate type of command that describes the ability to pre-execute the request, without waiting for the server to respond.
- We made it so that requests are executed strictly sequentially using a queue. It can contain 2 requests: active and waiting. There are so few of them, because we don’t want the user to be able to plan many actions. If the first command returns an error, then all other commands must be canceled, otherwise their execution will lead to inconsistency of data. We do not exclude that the user can quit the game, and the command queue will not be sent to the server for execution. If the queue is full, the user is shown a wait window.
- We have ensured that in the event of an error in executing the request for it, all actions taken are rolled back, and the next request in the queue, if present, is canceled. Rollback of actions is programmed manually.
Caching server-side editing queries
Let us turn to the implementation of the second approach:
- If the client receives a network error when requesting a server, it tries to resend the request.
- Each request has its own identifier.
- If the request is exceeded, the attempt number is also recorded in the header.
- For each subsequent request, the timeout increases from 10 seconds to 20. We did this for cases when the user has a poor Internet connection and does not have enough speed in the allotted time to download a large response from the server. It may seem that this makes no sense, you can immediately put the maximum value. In practice, it turns out that the request, which was dropped for network reasons, will repeat at the minimum interval. This is better than waiting for a maximum timeout and retry request.
- If all requests fail, we show the user information about the error, and when the maximum number of network errors is reached, we consider that the session cannot be continued, and we suggest the user to restart the game.
- On the server side, several recent edit requests for each user are briefly cached. Upon receiving a request with an identifier that has already been executed, the cached result is returned - of course, if it exists. If not, the server returns an error.
- Requests for reading are not cached and are always processed by the server for a new one.
According to statistics, 0.76% of requests get out of the cache, and this is every 130th user request.
See you in the third part! If you missed the beginning of the cycle on creating MMO RTS on Unity, look for it
here .
Other articles from the series: