Cross Compile Data Integrity in a Virtual Machine

As I said earlier, there are a lot of ways that a command-record can go wrong. Real-world possibilities, not theoretical flaws.

An action-record can also go wrong, but I think will be easier to validate each action as it goes by. (And if an action fails, you abort – the player is left in a consistent state, having lost some but maybe not all progress.)

In general, yes, but e.g. removing an object from early in the source code will invalidate all actions on other objects (because all the pointers are now off by sizeof(object)). Same for changing the order of action definitions.

Thus the necessity of naming things in a stable way.

Right. So my thoughts are that along with the Extension and VM change, there would be testing tools to validate a save file. The author is going to know what they changed or removed, so they can also create exceptions to the action-running routine. Before an old action is executed, it’s tested against a list of exceptions. If there’s a match, the exception handles the problem.

I want to mention the flip-side strategy. Instead of saving as much information as possible and trying to fix the places where it’s broken, you save the least information possible.

That is, you do the normal save procedure, and then append a minimal summary from which the game can reconstruct “the important parts”. Imagine a single summary line saying “The player has completed through chapter 3.” When the file is loaded cross-version, the game ignores the Quetzal data; it just looks at the last line and runs a “warp to the start of chapter 4” routine. The player loses some progress, but this only happens occasionally so it’s tolerable. (A player will restore many times during play, but only a couple of those will be cross-version.)

If there’s important state information in the earlier chapters which affect the later game, you’ll need to add additional data lines to the save file. As the game author, you can figure out how to do this optimally. You can also aim for a finer granularity (how much progress has been made in chapter 4?); that increases the workload. You can decide how far to take it, trading off developer effort against player convenience.

For some games this will be easier, others harder. Spider and Web is organized in tight chapters with only a bit of ongoing state data, so this scheme would be pretty easy. Zork is amorphous, with lots of incidental state and objects potentially scattered across the entire game, so it’d be much harder – it basically degenerates into my previous scheme of “save every bit of information”.

Maybe a hybrid approach would be useful. As you progress through a game, it’s not uncommon for authors to use a funnel approach to narrow the game play, then expand it again. These choke points could be used as common save points.

It occurs to me that this may be a simpler solution in the short term. In writing games, an author could track all of their transition points and create a phrase to set the game to that point.

To set the player in Chapter 3:
        now the player is in the Cathedral;
        now the player carries horloges keys;
        now the player carries the old gear;
        now the chapter number is 3.

But the hybrid version may do both things. Keep track of transitions points and only save the last one plus individual actions. This way, if the author changes anything prior to the last transition point in a save file, the save file will be less likely to fail and the author need only revise the transition phrases and manage any exceptions in-between transitions.

If you add a new hallway, then you’ll need to add a new GO EAST action into the player’s stream of past actions, at “the appropriate place”. But I think the function int Appropriate(History history) would be pretty non-trivial.

If you’re not using Inform or TADS but your own system, it could do as someone upthread mentioned and just output its memory map as nested Dictionaries in JSON or BSON. (Nested, because Scope.)