December 31st 2020 by SDGNelson
Looking ahead to the new year.
For 2020 along with the regular updates I completed three big refactors that had been on the to-do list for a long time: foliage streaming, network transport layers, and support for uGUI. This has me feeling optimistic (aside from Unity frustrations...) for refactoring in 2021 with one particular goal in mind: eliminating multiplayer "lag lines".
Currently in multiplayer the game has awful lag spikes / hitches / stutters when loading large bases on the client. A lot of players refer to this as "lag lines" because there is no hysteresis - walking back and forth across a grid cell boundary repeatedly destroys and reloads bases.
The core problem is that all barricades and structures in a region are synced in a single frame. The server has to send a lot of data all at once, and the client stalls the game thread to process it and instantiate all of the objects. This is currently necessary because an RPC for one of the barricades might arrive on the next frame and get out of sync if the barricade does not exist on the client yet.
There are a lot of contributors to this problem which will need resolving:
- Queueing up RPCs for not-yet-created objects will allow the client to pool and defer the creation of objects rather than doing all the work on a single frame.
- Currently each system has its own IDs for routing RPCs, for example vehicles have 32-bit IDs whereas structures have essentially a spatial ID. In order to make the RPC improvements reusable for other systems these will be replaced with unique 32-bit instance IDs. Long-term this will allow some other cool features like standing on moving trains.
- Recycling (pooling) eligible barricades and structures will save a significant amount of time instantiating and destroying them. This will probably have to be opt-in on a per-item basis to avoid breaking mods.
- Time-slicing the instantiation (and maybe destruction) across multiple frames will reduce the impact on any individual frame.
- Once instantiation can be deferred the loading can also be deferred and run async. This will improve game startup times.
- Perhaps a better network culling system. Since 2014 the server has been partitioned into 128 x 128 meter grid cells ("regions") which has proven useful, but is hard capped at 8 x 8 kilometer maps, and might benefit from more fine-grained culling.
- Smarter state syncing system. In particular reducing allocations and preventing plugins from corrupting the buffers. This is not critical for the main goal, but will certainly make it easier. There are still components of the server that act like a request/response web API which would be more resilient against request spam if state was sent at a fixed rate (currently rate limited).
I am feeling excited to work on these, as well as other general improvements for Unturned. In the meantime I hope your 2020 has ended well, and wish you a happy new year!