1
\$\begingroup\$

I've learned that storing and loading game saves is commonly done by serializing runtime data into binary files and then loading in those binary files de-serialized to reconstruct the runtime state respectively

A Python library pickle allows for serializing and de-serializing entire objects. It makes intuitive sense and ease of use then to compose all relevant world state variables into a single object WorldState, which will be serialized on saves and reconstructed on loads.

class WorldState(metaclass=Singleton):
    relationships: dict[Entity, dict[Entity, Literal['friendly','neutral','hostile']]
    questlines: list[QuestLine]
    locations: list[Location]
    items: list[Item]
    entities: list[Entity]
    player: Entity
    time: float

This works, but I'm not sure how to handle the initial game state, before any saves have occurred. I'm left with a chicken/egg scenario, where in order to load game state there must already be serialized binary files to reconstruct, which happens on save, but in order to save game state into serialized binary files there must already be a WorldState instance constructed to save.

If I were using XML/JSON to store game state I would simply write the initial content manually which would be overwritten on saves to be loaded again later, but since I am serializing them into binary I obviously cannot do that.

The only solution seems to be adding an if to check whether any game saves already exist, and to manually construct all the initial objects if not, either in the source-code (using dependency injection containers) or loaded from xml/json files that would exist only to represent the initial game state since every other load after the first will be based on serialized binary files, not json.

Am I missing something? Is there a better approach? Should I just represent game state in human-readable formats like json? What techniques are common in industry?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ It depends if your game is generated automatically (there is no initial game state) or you have to design the level by hand/ editor. You can manually store this as your initial state since you as the game designer created. \$\endgroup\$
    – Zibelas
    Feb 21 at 12:18

2 Answers 2

2
\$\begingroup\$

The file system can fail beyond your control.

If you have a default file, it might be removed due to fortuitous reasons.

That might suggest to you that to check if the file exists is the way to go. But don't do that. That would be race condition… Because your game execution can be preempted by the operating system after you checked the file is there, but before you load it. That is a Time-of-check to time-of-use bug.

Thus, what you should do is to try to load the file (no check done before hand), and if that fails you instance your objects from code by more conventional means. Another option is to have your default file bundled in the game executable as a resource.

Note that I'm talking about saving game state, not the asset pipeline.

The fact that the file system can fail beyond your control also suggest that it is a good idea to make an automatic backup of the save file. Which means that when the load fails, you should try to load the backup before falling back to conventional instanciation.


If we are talking about a single player game, then tampering with the save files mainly affects the same player who did it. Let them. Perhaps worry about making the game entertaining enough that they don't want to go into the trouble. I'm saying that using a binary format as a deterrent against tampering with save files is not worth it.

However, sometimes players share save files, and if you are using a generic serialization solution that might be a security risk. You want a solution that either allows you to whitelist the classes it will handle, or allows setting a callback to resolve classes, which is allowed to fail.

If you opt for the human readable format, be mindful of loss of precision due to the representation of values in human readable format. You want to make sure that whatever you serialize can be deserialized losslessly, or it is only lossy where it does not matter.


If this is a multiplayer game, where each player has a save file (it is a multiplayer game with document-oriented database for the player data※)… You want a binary format because of performance.

※: Some games benefit from from having a document-oriented database for player data that is only acceded by the player (e.g. inventory), in combination with a more traditional relational database for other things that might be acceded by other players (e.g. name and public stats) or for which the game need complex queries. This also allows the game server to send a copy to the client who can browse the data without doing many requests to the server (operations on it still need to go through the server, and logged on the server, but usually the client can do the same operation locally - when the server approved it - instead of downloading and deserializing the document again).

Note that security would not be a concern of the serialization solution in this case, as only the copy of these files in the server is trusted, and it cannot be written directly from client machines.

Be aware that GMs might need to work with player data (or even run custom scripts over them) to solve problems (e.g. issue a mass refund). And there should be tooling for such operations.

Oh, and the file system can fail beyond your control, and if it is your server you want to know when that happens.

\$\endgroup\$
2
\$\begingroup\$

I made very good experiences with keeping savegames in JSON format. The reason is that you can easily view savegames for troubleshooting and manually create and edit savegames in a text editor to quickly test certain scenarios.

This also makes it possible to represent the initial state of a new game as yet another hand-written savegame.

When you are worried about JSON taking too much space, then just compress them using a stock compression algorithm like DEFLATE. That should greatly reduce the filesize. Usually not as much as a native binary format, though. But hard drive space is cheap, and it's not your space anyway.

If you insist on a binary file format, then you might want to consider to create your own savegame editor application for that purpose. Either that, or get comfortable with a hex editor.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .