Features of the development of mobile MMO RTS. Part 2

    Hello! Most recently, I began to talk about how we work on Stormfall: Rise of Balur and write the client part of the project in Unity. Today we’ll talk about the approach to skinning, multithreading, working with a network with poor connection and query caching.

    Skins and work with them

    The RTS genre adapts well to different settings based on one engine. We have several such games. When writing a project, a lot changes: UI, UX, gameplay logic. Sometimes, specific requirements for the integration of libraries and social services may appear. When designing the game, we had to lay the requirements in the architecture, preserving the maximum possible sharing of the code.

    In order to customize the behavior of UI, we decided to make the work of UI objects begin with the dynamic loading of prefabs from resources. After that, a specific View script is executed, 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.

    Unity does not have a file with a description of the project: all the code that is in the Assets folder ends up in the build. Under such conditions, creating several projects with a common code base is difficult. We decided that we would use a common repository in which the project does not lie in the form of a Unity project, but in the form that we need.

    Using a script that creates symlinks to the code folder, we deploy the Unity project. If necessary, deploy the Dev build. It has all the code, including from other projects. This is necessary, for example, for refactoring, but will not affect the gameplay.

    Multithreading in Stormfall: Rise of Balur

    When the mobile market was just gaining momentum, developers discussed whether multithreading was needed for their applications. Now this is not a question for discussion. By multithreading we solve several problems:

    1. Execution of callback requests to the server in threads from ThreadPool. The commands themselves, in addition to executing WebRequest, serialize the data sent to the JSON format and deserialize the response. They also perform the process of updating the model with new data that comes in the response.
    2. Execution of the logic of the mechanism of updating the model in a separate thread. The mechanism fires once a second and can take quite a while.

    The model data access mechanism works through the monitor and supports various data blocking policies. It gives read access to many objects at the same time, but does not allow the ability to combine this with writing data to the Model.

    Unity does not allow working with the engine from a non-mainstream. Keep this in mind when designing the application and try to offload the main thread, taking out processing of non-UI data to other threads.

    Bad network connectivity

    In Stormfall: Rise of Balur, interaction with the server occurs through 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:

    1. Optimistic query execution.
    2. User request overflow. This method protects against double execution of commands if the request was on changing data and broke off on return to the client.

    Now in more detail about each approach.

    Optimistic query execution

    We have implemented a mechanism in request commands, which allows us to avoid UI locks during their execution. After the request begins to be executed, we change the model so that the server returns a successful response. If the answer was really successful, we do not change anything or supplement the model with a response from the server. If the server returns an error, we call the rollback method of the changes that were made at the initial stage, and notify the user of the error.

    What exactly have we done for this:

    1. We implemented a separate type of command that describes the possibility of preliminary execution of a request without waiting for a server response.
    2. They made the requests run strictly sequentially using the queue. It can contain 2 requests: active and pending. There are so few of them, because we do not want the user to be able to schedule many activities. If the first command returns an error, then all other commands need to be canceled, otherwise their execution will lead to data inconsistency. We do not exclude that the user can exit the game, and the queue of commands will not be sent to the server for execution. If the queue is full, a wait window is displayed to the user.
    3. They achieved the fact that in the event of a request execution error, all actions performed are rolled back for him, and the next request in the queue, if present, is canceled. Rollback of actions is programmed manually.

    Server Side Editing Caching

    Let's move on to the implementation of the second approach:

    1. If the client receives a network error when requesting to the server, it tries to resend the request.
    2. Each request has its own identifier.
    3. In case of overfulfillment of the request, the number of the attempt is also recorded in the header.
    4. For each subsequent request, the timeout increases from 10 seconds to 20. We did this for cases when the user has poor Internet and there is not enough speed to download a large response from the server in the allotted time. It may seem that this makes no sense, but you can immediately set the maximum value. In practice, it turns out that a request that has been dropped for network reasons will be repeated with a minimum interval. This is better than waiting for the maximum timeout and retry of the request.
    5. If all requests have failed, we show the user information about the error, and when the maximum number of network errors is reached, we believe that the session cannot be continued, and we suggest that the user re-enter the game.
    6. On the server side, the last few editing requests for each user are cached briefly. Upon receipt of a request with an identifier that has already been executed, the cached result is returned - of course, if there is one. If not, the server returns an error.
    7. Read requests are not cached and are always processed by the server again.

    According to statistics, 0.76% of requests are taken from the cache, and this is every 130th user request.

    See you in the third part! If you missed the beginning of the cycle about creating MMO RTS on Unity, look for it here .

    Other articles from the series:

    Also popular now: