SDG Blog

"Remember to put a witty tagline here!" – 2021 edition.

July 2021 Development Recap

Anecdotes from the past month of gamedev.


There was an Unturned update with some features for future maps, and Yarrrr retired from the community. Then I went on a quick roadtrip to visit my Grandma. Some of the unspecified work I did in the first half of the month did not pan out, but in the second half I made a lot of progress on my 2021 goal of resolving lag lines, detailed below.

Monthly Recaps

This is the seventh end-of-month style devlog. Originally the idea was to combine Unturned and Unturned II behind-the-scenes blogs into monthly updates that would be posted on a predictable day. The past few months however I have found them stressful to write when they do not nicely line up with the completed work, or when there is not much beyond technical details to discuss. Going forward I intend to return to doing occasional Friday posts, for features more engaging to look at.

Barricades & Structures

The motivations behind this rewrite are outlined in the Unturned in 2021+ blog post. In preparation there have already been several foundational netcode rewrites this year. There was a pause on this front since May in order to give plugins time to update, as the next changes are fundamentally incompatible with the older deprecated APIs. Now we can begin to use the improved netcode to fix the underlying problems!

Planning the barricade and structure revamp was overwhelming at first because of the number of obvious problems that would be great to fix. Perfect is the enemy of the good however. Ultimately the first goal is fixing "lag line" performance, so the others will have to wait.

Some of the difficulties and problems which had to be deprioritized include:

  • Hard (if not impossible) to rewrite without breaking some plugins. For example any code relying on the pre-RPC-rewrite invocations that has not updated to the new API will not be able to be routed to the correct object once they are time-sliced / async loaded / pooled.
  • Obvious similarities between "structure" and "barricade" placed item code that would be nice to abstract and reuse.
  • Owners of placed items are tracked separately in a few different ways which would be nice to consolidate.
  • Placed items are spatialized in a 2D grid of 128x128m cells. Quadtree or R-tree could be a better data structure considering how frequently code needs to query the items near a given point.
  • Modders have to create two versions of each placed item prefab: one for the client and one for the server. The server version could be automatically created in many cases.
  • Placed items have their own methods for damage/health/repair which would be nice to generalize.
  • Tags are used to identify objects as barricades and structures. Ideally anything which checks these would look for a component type with all the relevant methods instead. e.g., look for a Damageable component and damage it.
  • Lots of code uses the Transform as a handle which necessitates redundant barricade and structure lookups.
  • Rotation is represented as euler angles snapped to the nearest multiple of two rather than quaternions. Using the newer quaternion netcode would be higher precision.

Decided priorities:

  • Server maintained two separate lists of client-side and server-side placed item data which easily got out of sync if plugins modified them. The game relied on indices between those lists matching, so mismatches caused multiplayer problems like unable to damage placed items.
  • Instances of placed items were referenced over the network by their grid cell and index. This needed to be replaced with the newer "net ids" to allow the client list to not match the server (e.g. when async loading).
  • Add support for queueing network updates on the client until target instance exists. e.g., if a barricade moved update arrives while the barricade is async loading it will be deferred until the barricade gets instantiated.
  • Since placed items were referenced by grid cell and index their RPCs were all static methods. In order to facilitate deferred invocation they needed to be moved into instance methods. This simplified a lot of code, but was a bit of a slog.

These changes are now implemented on the preview branch, and placed item instantiation is now time-sliced to begin taking advantage of them! Future updates this year will bring time-sliced destruction, async loading, and pooling in order to fully take advantage of the now-possible performance benefits.