SDG Blog

It's pronounced "Boo-Ack"!

Unturned II Devlog #033 update focused on saving and loading game state.

Persistence Overview

While there are definitely bugs still lurking, the foundations for saving and loading are relatively functional in the beta now:

Save Game Menu

Much more level state is persisted than in the original game. For example items manually placed in the level editor are only spawned once, and dropped items are (finally) safe between loads.

There are three types of local saves:

  • Each level has a quicksave slot. Press F5 to quicksave and F9 to quickload.
  • On top of that each level has an autosave slot. This is usually the default when loading a map, and saved to periodically and when exiting.
  • Manual save slots can be created and deleted to preserve a point in time.

Dedicated servers configure savedata through an ini file, and have key features like timed autosaves.

Build Machine

My poor laptop has been chugging away for years compiling and building updates to Unturned and Unturned II. During community member HoldBaker's New Year Horde Mode event I could not patch a bug they ran into because it would take too long to build. Thankfully a proper dedicated build machine has been assembled, and the laptop can rest. This new machine blazes through updates 13x faster!

Persistence Implementation

Lots of planning and refactoring went into this save system the past few weeks. While there are a few aspects I am not completely happy with yet, here are the key interesting decisions:

  • Stored data is kept in JSON format for dedicated servers by default, and a binary/json hybrid for singleplayer. It would be straightforward to write a plugin implementation of the data formatter for XML or another format. My plan is that the JSON format will be useful for the web API backends, and easy to integrate with 3rd-party tools.
  • From a game code perspective the reading and writing is forwards-only, supports key-value maps, and supports arrays. The underlying format does not matter.
  • Blueprints have full control over their own save/load handling. This was important and significantly impacted the approach. They receive a "SaveContext" or "LoadContext" with wrappers to handle the native c++ code.
  • While automatic property serialization is nice, and might be automated for savegame-tagged properties at some point, there is often significantly more game logic required on the loading side than on the saving side. In particular for blueprints to support versioning because they do not have a simple number like the UE4Version or LicenseeVersion. For this reason all of the read functions can fail e.g. TryReadInteger.
  • Game logic controls what is saved and loaded, but how the data is stored is configurable. In the future I expect there will be a [Persistence] Implementation key in the config file to point the dedicated server storage at the local file system, a remote API, or something else.