In my personal projects I’ve been using variations on the same simple code for saving/loadings objects for a decade or so, and have very few problems. The heart of the code is this interface - public interface IStashy { void Save (T t, K id); T Load (K id); IEnumerable LoadAll (); void Delete (K id); K GetNewId (); } And implementations of that are very stable over time. Objects get serialized as json and stored in a…
Serialization for C# Games
21–30 of 51 posts
Re: Serialization for C# Games
#22In my personal projects I’ve been using variations on the same simple code for saving/loadings objects for a decade or so, and have very few problems. The heart of the code is this interface - public interface IStashy { void Save (T t, K id); T Load (K id); IEnumerable LoadAll (); void Delete (K id); K GetNewId (); } And implementations of that are very stable over time. Objects get serialized as json and stored in a…
How do you deal with serializing properties "by reference"? E.g., if 3 objects reference object "Foo", then Foo is serialized once instead of being duplicated in the json 3 times?
It might be that we serialize foo and foo has a list of references to its 3 children. The “parent” reference from the child back to foo is marked as do not serialize; an “after rehydration” function on foo could then set the value each child’s parent reference.
But more often — say baz bar and bam reference foo — the speed at which baz changes is different to the speed at which foo changes. The reference to foo from Baz is marked do not serialize. Baz also has a property indicating the ID of Baz. (For IStashy - K is the type used for the keys, the IDs; it might be a string or an int or a guid, I tend to use string. All objects in the system have the same kind of ID, and it is unique per type.)
Generally if cyclic data structures are possible then some part of the cycle will be marked as no serializable and I’ll keep a key reference adjacent to it.
Situations that triggers huge cascading saves — they’re kind of an anti pattern for how I work. If one little change changes everything then perhaps it can be calculated on the fly from a pure function, not persisted at all— or perhaps there’s over-coupling etc.
Re: Serialization for C# Games
#23With that approach, a language-assisted serialization system also looses a lot of its appeal IMHO (although it can still be useful of course for describing the separate savegame data format).
Also: resist the architecture astronaut in you, especially savegame systems are a honey trap for overengineering ;)
Re: Serialization for C# Games
#24Naive question: is there a reason why SQLite wouldn’t work for something like this?
The initial world state was baked into tables in an SQLite database file, and savegames were just mutated SQLite files (we kept a record of created, mutated and deleted database rows, and periodically flushed those changes into SQLite).
It worked well, but was overkill because we didn't actually make use of any advanced SQL features (just simple search over an object-id column). It would have been easier to cut SQL out of the loop and just write a simple table-based persistency system.
Re: Serialization for C# Games
#25Sometimes, when battling these issues, I wish the Smalltalk-style approach[1][2] was more popular/feasible. Basically, saving the entire state of the VM is a fundamental operation supported by the language. Only truly transient things like network connections require special effort. There are some echoes of this with things like Lua's Pluto/Eris, or serializable continuations in other languages (eg: Perl's Continuity…
A full memory dump for a game will nowadays often be multiple gigabytes, that's a non-starter. Even back in the day, Game Maker had a function to dump the state to disk that was intended for game saves. It sucked - turns out there's a bunch of state that you don't want in your savefile - keybinds, settings, even most game state actually. Save state should be opt-in, not opt-out, and on top of that a VM/memory dump ma…
On the flipside, there are (hopefully obvious) big advantages for the development process, when you can snapshot full states.
Of course, none of it matters if you actually need max performance — no AAA shooters would use it. But there are lots of not-performance-critical games which might benefit more from the better development experience at the expense of some performance. Perhaps point-and-click adventures, sidescrollers, shootemups, and such.
Anyway, just spitballing (:
I'm working on a game now that has almost no state, and I wish for a way to have that same freedom I feel in a more stateful traditional game, without having to muddy up everything with serialization interfaces et. al.
Re: Serialization for C# Games
#26Naive question: is there a reason why SQLite wouldn’t work for something like this?
Well you still need to solve for what happens when a new version of your app (maybe with a new embedded version of SQLite) loads up an old data file saved by an old version of your app. The old version might not contain all the tables you need, and the ones it has may not have the columns you expect. So you need to run some data migrations on the database. Now you no longer have a serialization problem but instead yo…
That same versioning problem also exists with other approaches. Having a versioned schema of the savegame format around for version migrations is generally a good idea.
Re: Serialization for C# Games
#27If I learned one important lesson from writing savegame systems: don't directly serialize your entire game state on the "game object level" (e.g. don't create a savegame by running a serializer over your game object soup), instead decouple the saved data from your game logic internals, have a clear boundary between your game logic and the savegame-system, keep the saved state as minimal as possible, and reconstruct o…
Re: Serialization for C# Games
#28Re: Serialization for C# Games
#29Also means you can do stuff like if(version<x) { load old thing + migrate} else {load new thing} very easily. And it's just code, not magic.
Re: Serialization for C# Games
#30Naive question: is there a reason why SQLite wouldn’t work for something like this?
Well you still need to solve for what happens when a new version of your app (maybe with a new embedded version of SQLite) loads up an old data file saved by an old version of your app. The old version might not contain all the tables you need, and the ones it has may not have the columns you expect. So you need to run some data migrations on the database. Now you no longer have a serialization problem but instead yo…
I use SQLite for game state management. It's just like any other database scenario. I write migrators that check the user_version of the database. It's just a for loop from user_version to current version. The migrators themselves can be arbitrary methods that sometimes modify game state to bring it up to date. The most common scenario is adding a new property to something, and then figuring out the appropriate defaults to assign for existing rows (typically null/0). But you can go all the way into the ETL rabbit hole.
I think the relational model via SQLite is the best way to manage state for the more complicated games like in the 4X and deck builder genres.