Prismatic Maze is a game that manages a lot of information that needs to be persisted to be retrieved later.
This post is a quick dive into how this state is managed, versioned and updated in the game.
Each save game is persisted as a database file that has some game data along side a hierarchy of structures that represent maps and tiles and creatures and items.
When the game is playing, some of this data is loaded and updated and more data is added dynamically.
This works based on a game engine we built called Ecology Engine, which consists of a collection of libraries that are referenced from the game project.
The Ecology Engine is designed to facilitate building games based on a grid-based representation of the game world where everything is mutable and many things are alive.
One of the main components of the engine is state management, designed to be improved and extended during development of multiple games.
Long term game state is managed using relational data and stored in a file-based database.
As development on the game continues, new features are introduced that depend on or work with new data in long term state.
We're adding features incrementally and consistently and the game is out there with saved games from versions ranging from initial release to current.
Managing the data in a file on the player's machine introduces a problem with how to update things over time.
Updates to the game bits are handled by the platform.
In this case Steam will update the game files automatically so when we publish an update players will download it when the game is launched next.
Updating the data in the save game can be more tricky.
If we don't update the save game format, we risk making it impossible to load an old save game after an update.
To solve this problem, we use a number of different versioning and update systems that have been built into the engine over time.
The basic pattern we use is:
- Add Version information to data you want to be updatable
- When you load that data, check its version and systematically update it until it's up to date
- Save the updates automatically with the rest of data
The database itself has a Metadata table that stores data related to its schema - the structure of tables and the shape of the data is stored in them.
When the engine loads the database, it checks the schema version and runs it through a sequence of updates to bring it up to the current version that the code requires.
The database updates are made by running update scripts that can effect both the structure of tables as well as the data in them.
Whenever a save game is loaded, it is automatically updated to the current format.
Database Metadata Table Contents
A lot of data stored in the database is also versioned.
For instance, both Players and Pigment Creatures are stored as Creatures.
All Creatures share some properties like their position on the map and whether they're alive as well as DNA.
DNA is stored as a string, but really it's a serialized version of an instance of a creature of a specific type - all the properties that make up that creature as well as all the properties of those properties, all the way down until the entire thing can be re-loaded in the same state from that description.
When the engine loads a map, it unthaws the creatures associated with that map from their DNA, which can then be updated and saved along with the map data when the game is saved.
When the creature is unthawed, it's version is compared and systematically updated using logic similar to the way the database is updated, bringing it up to the current format.
When that creature gets saved next, its updated version persists along with its current state.
Versioned data currently in use:
- PigmentCreatureDNA - 0.0.0.2
- PlayerCreatureDNA - 0.0.0.4
- MazeGameDataModel - 0.0.0.5
- MazeMapBlueprint - 0.0.0.16
The game state and configuration options work in a similar way to Creature DNA.
This game state describes things like what maps have been completed and what items are in storage.
The configuration options like current save game and language and volume settings are stored in a registry key.
These too are versioned and updated dynamically when loaded.
The image above is from one of the tools we use,
showing how game and creature data is stored as raw strings.
With patterns like these - semantic versioning, auto-updating, polymorphism, open-closed principle, etc. - we are able to add to and extend the game in significant ways that wouldn't be possible otherwise.