Save File Structure UML Diagram - C7-Game/Prototype GitHub Wiki
The save file structure is currently being formalized, as it was realized in 2022 that simply trusting .NET to do its thing would not work, in no small part because of loops in the save file structure. We need to think about how we want to serialize our data for saving, whether we want to or not.
History
pcen opened the first save PR on April 14, 2022 (https://github.com/C7-Game/Prototype/pull/235). In its comments, he called out the problem of serialization, and noted that there were some issues with AI data. maxpetul noted some issues with null pointers, and Quintillus noted some issues with duplicated nested data.
Unfortunately, activity on the challenge was slow at the time.
On November 4, 2022, Quintillus proposed a set of save format principles (https://github.com/C7-Game/Prototype/discussions/377). The three keys were:
- Items are not duplicated
- The format is easy for humans to read
- Polymorphism is supported
The first one was particularly important from a functionality standpoint, especially as some loops had been introduced in the format by that point (class A references class B, which references class A, or perhaps class B references class C which in turn references class A). But it also would improve human editability.
Polymorphism would allow object-oriented development to continue, but is not natively supported in .NET 6.0. It will be in .NET 7.0, but we don't know how long we will be waiting for Godot to support it. Newtonsoft JSON, the de facto standard in the C# world before Microsoft added their own (Newtonsoft-inspired) implementation, does support polymorphism. Thus it is proposed to move forward with Newtonsoft. Save support is too important of a feature to wait indefinitely for .NET 7.0.
UML Diagram
On February 5, 2023, Quintillus decided to create a UML diagram showing the relationships between the classes in C7DataFormat, to help reason through the question of how the format should be serialized. The UML diagram only shows object references, not primitive types (int, string, double, etc.), thus focusing on the more complex parts of the problem.
The UML diagram lives https://github.com/C7-Game/Prototype/blob/Newtonsoft-Serialization/C7GameData/UML%20Diagram.graphml (you may need to switch to the Development branch in the future), and the PNG equivalent is reproduced below. Blue represents classes storing AI-related data. Right-click and open in a new tab for the full-resolution version.
As you can see, there are a few loops in the diagram. The good news is that by storing references using item IDs, we can load the data despite the loops, and then use the IDs to link to the actual objects as a secondary step.
Still, care has to be taken with the loops and bidirectional references. Consider the case of Tile's personWorkingTile, which is a CityResident, and CityResident's tileWorked, which is a Tile. If one of these were modified in a text editor, but the other was not, we would have an inconsistent state. To achieve Quintillus's principle of e pluribus unum, and avoid this inconsistency, we must only store this once in the save file. That is why some of the properties are listed as generated.
Plans
The next step is likely formalizing the identifiers for the various classes. A goal is to have these be human-understandable, and thus GUIDs are not preferred, at least for anything that someone may want to understand at a glance from reading a reference elsewhere in the save file. Keep an eye on this page for updates!