Vellum saves game state as text that stays exact, readable, and loadable after the code that wrote it has changed. A save is a tree of SaveNodes: text, null, or a table of named nodes. SavedValue turns the built-in types (strings, bools, chars, enums, every number type, TimeSpan, DateTime, DateTimeOffset, Guid, and Nullables of them) into nodes and back, independent of culture. Doubles are saved as their exact bits, with the decimal value beside them as a comment. A bool is written in lowercase and read whatever its case or padding, so a hand-edited "True" loads. To save a type of your own, mark it [Saveable]: Vellum.Generator writes its converter, saving every instance field and auto-property not marked [NoSave], readonly ones included. Loading goes through a private constructor taking a SaveReader, which the type can write itself to adjust what's loaded. To save a type Vellum doesn't know and you can't mark, write a public SaveConverter. SaveConverters.For(assembly) gathers the ones an assembly names with [assembly: SavedWith(...)], which Vellum.Generator writes from every converter in the assembly and the assemblies it references. To use it, a project needs: build.py refuses to build a project that references Vellum without it. A project that only reads saves says so with true. SaveConverters.For(assembly) also throws for an assembly that defines a public converter but names none, which covers a build that doesn't go through build.py. SaveConverters only finds the converter for each type, and it is safe to share between threads. Saving and loading happen in a SaveSession from SaveConverters.CreateSession(), made fresh for each save or load. A session remembers every object it has handled, so an object met twice stays one object. For the same reason, a second save through the same session returns the first save's nodes for any object it has seen, changed or not. A session is for one thread at a time. A load never fails over a bad part of a save. The part is left out, or left as its default or initial value, and the session's Sandbox (a LoadSandbox) records an issue naming its path, eg: "Scouts: [1]: Rank". Only a value the caller asked for directly still throws. A converter reads its table through a SaveReader, whose TryRead does this for a field, and a collection's converter does it for each item. Typed calls, session.ToNode(value) and session.FromNode(node), take the type from the code around them. A value held in a base class slot needs it spelled, ToNode(dog), or it is saved without its subtype's name. Vellum.Generator checks the type argument of each typed call at build time. A generic method that hands its own type parameters on to one of those calls, or to another generic method that does, is checked the same way wherever it is called from. Arrays, lists, sets, queues, stacks, linked lists, sorted collections and dictionaries save as long as what they hold does, and a generic SaveConverter can handle generic types in the same way. A type parameter marked [Saveable] on a generic converter requires the argument to be saveable by anything (a built-in, a converter or a [Saveable] type), not to carry the attribute. An unmarked one takes any argument. A field declared as a base class or a [Saveable] interface can hold a subtype. A converter family is a generic converter of its own type parameter, constrained to one base class or interface: class ArcConverter : SaveConverter where ArcType : Arc It saves every concrete type derived from that class or implementing that interface, and its other type parameters take the type arguments the base was given. Nothing needs listing per type, and a field declared as the base can hold any of them, saved under its full name. A type with an exact converter uses that. Otherwise the nearest base class with a family wins, and then the most derived interface. Two unrelated interface families for one type is an error. LuaSaveFormat writes a tree as a Lua chunk that returns it, sorted so an unchanged save writes identical text, and reads it back in a MoonSharp sandbox with no way to reach outside the save.