, 26 tweets, 5 min read
This is a big topic so this'll probably be a bit of a thread

Save games are hard primarily because modern programming doesn't just let you take chunks of raw bytes from RAM, write them to disk, then later put them back and have the same state you had before 1/
In fact modern languages/OSes mostly don't let you access memory in this way at all. This approach is how emulator savestates work: they just dump all the working memory to disk, and then reload it, and it basically restores the state of the entire machine. 2/
So instead you get what's called "serialization". Serialization roughly means taking an object that exists in memory and manually (or semi-automatically) converting that into a set of primitive values like numbers, true/false, and text. 3/
Primitive values are unique in that they're all stand-alone, i.e. they do not refer to other objects. A number is just a number, a string is just a string. They don't rely on some other object to have a complete and usable state. 4/
The problem is that in a game you always have references between objects. A monster might have an aggro target, that's a reference to another object. Sometimes these object references create complex interlocking graphs, and those become super difficult to save and load 5/
For example, MidBoss works around this by giving every entity in the game a unique numeric identifier. References between game entities only hold this number, and every time you want the actual object, it has to look it up in a table of number->object values 6/
That way when you save a reference to another object, you only have to save the number, and after load things will mostly just work automatically after the lookup table is repopulated 7/
The problem is that if you want to have control over your serialization (which is required for it to be secure!) the simplest way is to write code so that every value on every object that has to be saved is converted into a primitive type on save and converted back on load 8/
Needless to say this is a TON of work! And it can lead to human error, especially in systems with lots of complex interlocking references, which could mean savegames become corrupt and unloadable entirely 9/
There are serialization schemes built on top of the primitive values that I mentioned earlier, like the COM interop system Untitled Goose Game uses, which try to automate as much of this process as possible, but results can be unpredictable and not secure 10/
And a lot of these systems aren't foolproof either. Imagine objects A and B. If A only references B, everything is cool. But if B also references A, that becomes a problem. You can't naively store a copy of B's data in A, because that system would then put a copy of A into B, 11/
and then another copy of B into the copy of A, ad infinitum until this circular reference crashes everything. That's just one example of things that can go wrong.

In Untitled Goose Game the system they used will for the most part create ANY object in the .NET framework 12/
regardless of whether it has anything to do with the game or not. That's the downside of making serialization easier, it becomes more general and so has less constraints on what it can do. 13/
The attacker exploited this to create an ad-hoc function that pointed to multiple other ad-hoc functions and exploited a quirk of .NET where if you do this, these functions can return a different value than expected, which you can then use. 14/
The way around this is to confine what types can be created by the deserialization process, which was the fix, but this again means more work for the developer.

The only secure way to use untrusted user data (files, data from over the network) is to do it the hard way. 15/
The hard way means writing custom code for everything in your game to turn complex object graphs into collections of primitive values when you save, and then rebuilding the complex object graph when you load. And it's a pain. 16/16
There's loads of other issues with savegames too like making sure if you get a power cut or a crash mid-save you don't corrupt the user's previous saved state on disk which I could do a whole thread about (if you're interested google ACID)
This is correct. If all you have to save is the player's stats and inventory and what position on what map they're on things become super easy.

This is why you see games like Shadowrun Returns and Underworld Ascendant launch with only checkpoint saves
Adding save-anywhere to a game that wasn't built for it means retrofitting EVERYTHING with serialization and it's usually a giant pain in the ass, like multiplayer support it's really something you wanna plan for from the very start of the project
Interesting additional note: some old languages like QBasic are designed in a way that everything is a value type*, and objects can never have references to other objects. The lack of complex object graphs allowed them to implement automatic memory management on weak hardware.
(*QBasic has strings which are technically references but they are immutable, which effectively makes them a primitive, and arrays, but all arrays are arrays of primitives. Also custom value types are always made up of primitives, or fixed length arrays and strings)
(Fixed length arrays and strings are simply collections of N primitives and so are also not reference types)
PSA: do not do things like Brian

DO NOT
Every time I implement a new thing in MidBoss the amount of time I have to spend on it is about 1 part actually implementing it and 2 parts saving+loading logic
Realized how to TL;DR this thread and sum up serialization:

- Easy
- Secure
- Robust

Pick two
If you would like a thread-safe incremental numeric ID generator for C# I can recommend this one I wrote myself: gist.github.com/Enichan/9babeb…
Missing some Tweet in this thread? You can try to force a refresh.

Enjoying this thread?

Keep Current with Eniko

Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

Twitter may remove this content at anytime, convert it as a PDF, save and print for later use!

Try unrolling a thread yourself!

how to unroll video

1) Follow Thread Reader App on Twitter so you can easily mention us!

2) Go to a Twitter thread (series of Tweets by the same owner) and mention us with a keyword "unroll" @threadreaderapp unroll

You can practice here first or read more on our help page!

Follow Us on Twitter!

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just three indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3.00/month or $30.00/year) and get exclusive features!

Become Premium

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!