Daily Archives: 25 April, 2013

Savefile design

Most roguelike games need a savefile.  But building robust and maximally useful savefiles is surprisingly hard.  My first attempt at making a roguelike game failed on this one point; I had used a lot of dynamic structures and didn’t really have a good way to traverse them and pickle all the pointers for a savefile.  I considered options and went with fundamental redesign. Then I procrastinated for a couple of years and started over.Recently I’ve been reading a thread on r.g.r.nethack about a player recovering a game from a corrupt savefile, and it’s pointed out a bunch of other issues and problems.

Here’s what I think I need to do with savefiles.  Others may disagree, or select different lists of things they think they need to do, and it won’t bother me.  But discussion of it could be interesting, so I’d like to hear reactions.  For one thing I’m assuming disk space is cheap.  In a world where Terabyte drives have dropped under a hundred bucks, I don’t care if I have savefiles a few megabytes long. Other people may have different opinions.

  1. They should probably be plain UTF8 text.  Not necessarily human-readable (unless with a pretty detailed key), but they should have a plain text form that can catted, grepped, stream-edited, filtered, pasted into text documents, loaded into an editor, etc.  That form should be the same across all platforms, so that when I as a developer get a bug report with a savefile, I can load and inspect and play it without going to buy and configure a new computer for every different platform.
  2. Variable-length sections should have unique delimiters that appear between them, identifying both the start and end of each section uniquely.  Nethack does this another way, by writing the length of each section and then the section.  The problem with that is, one corrupted section and/or miscounted length means you’ve lost the rest of the file.
  3. When a data structure is read off the disk, the code has to check every value in it for “sanity” and take appropriate action if an insane or impossible value is read.  With an accompanying warning to the player and a chance to opt out of loading, insane or impossible values should be replaced with default values.
  4. Insofar as possible, it should be possible to add, change, and delete monster and item types without breaking savefile compatibility. With appropriate player warning, and possibly an explicit player action required, it should be possible to load a savefile from any version on any later version and hopefully on many earlier versions too.
    1. Particular item and monster types should have version-stable identifiers for use in savefiles.  Possibly made by concatenating the name with the version in which it was introduced or last changed.
    2. New monsters/items introduced after V1.00 should have a savefile form that identifies ‘preimage’ monsters/items from earlier versions including V1.00 to load instead if the new content isn’t in the version where the savefile is reloaded.  The game should load the preimage from the most recent version it knows about.
    3. Similarly, there should be a means for “splitting” existing monsters when moving to a new version that has a new monster whose preimage is an existing monster.  For example, if V1.00 has goblins, and V1.01 also has goblins but introduces goblin archers whose preimage are goblins, then when the v1.01 game loads a v1.00 savefile it should convert some known percentage of saved goblins into restored goblin archers, based on their respective rarities in V1.01.
    4. Similarly, the identifiers of items/monsters eliminated between versions after V1.00 should still be known to the game, and it should know what items/monsters (‘postimages’) they’ve been replaced with and load them instead.  And again, at least one postimage for each eliminated monster or item has to be available in the V1.00 set.
    5. The game should ignore sections of the savefile marked with delimiters it doesn’t use, as do XML versions. In some cases where fundamentals change, later versions should save sections in both earlier forms (which they and later versions will silently skip) and later forms (which earlier game versions will silently skip).
  5. It should be possible for people to edit savefiles and play edited savefiles.  It should not be easy to create an edited savefile that appears to be unedited.  Automated scripts in tournament servers, and automated inter-player services like Hearse, should be able to reject the vast majority of games resulting from edited savefiles.  The only way I can think of to do this (saving a record of every move and rerunning it on the tournament server to make sure it reaches the same result) will also mark version-converted savefiles as edited.
    I’ve decided that avoiding that is a big can o’worms, and besides free interconversion between versions is probably abusable, so I’m okay with rejecting converted games.
  6. Since I’ve decided I’ll be saving a record of each move anyway, it should be possible to use the savefile to observe/replay a saved game.  For starters, it’s a cool screensaver.  Second, careful observation of your past actions and their consequences allows another mode of learning from your mistakes.  Third, it should allow people to watch and possibly learn from each other’s playing styles if they exchange savefiles.
  7. It should be possible to make a savefile of a finished game. Players should be able to retrieve the savefile associated with any entry on the highscores page.  The highscores page itself, or “hall of fame”, is just a particular way for the game to display the contents of a directory in which savefiles are kept.