Savegame Versioning - Grisgram/gml-raptor GitHub Wiki

Please note: This feature is only available in gml-raptor v2.5 and later!

There is one big thing, game developers often struggle with:

"What if I update the game and it now requires data that is not available in old savegames?"

Best practise here is to add kind of a version to your savegame. raptor offers this functionality out-of-the-box.

In the _GAME_SETUP_ folder of the project template you find a file called Savegame_Configuration. In this file, there's a macro defined:

#macro SAVEGAME_FILE_VERSION            1

When you start developing your game, this version is normally set to 1. However, after release, if you update your game with a patch or even with new features, it might be the case, that older savegames now are incompatible.

If this is the case in your planned update, you should increase the version number of this macro. Next step is then, to prepare your objects for the patch.

When the savegame system loads a file with a version lower than the current one, the upgrade mechanism becomes active.
It will then look into each object loaded from the file, whether they provide methods, that fit into the SAVEGAME_UPGRADE_METHOD_PATTERN. This is also a macro defined in Savegame_Configuration:

#macro SAVEGAME_UPGRADE_METHOD_PATTERN        "savegame_upgrade_v{0}"

The {0} placeholder will be replaced by a version number.

Imagine, you load a savegame version 1 and the current version is 4, then savegame will invoke
savegame_upgrade_v2,
savegame_upgrade_v3,
savegame_upgrade_v4
in order, so you always need only to code the "delta to the previous version" in your method and supply defaults/new data there.


Structs vs. Objects

Version upgrade works in a fixed timeline, as it is expected, that objects depend on data structs and not vice versa.

Version Upgrade Timeline

  • GLOBALDATA is restored (entire tree) --> then, version check for each struct in GLOBALDATA of the savegame
  • STRUCTS is restored (entire tree) --> then, version check for each struct in STRUCTS of the savegame
  • All restored object instances do their version check

Note

The upgrade methods on object instances are invoked just BEFORE onGameLoading, UserEvent15 and onGameLoaded are invoked.

Please note also:

  • For structs, to have them be part of the version check chain, make sure, you meet all the requirements for a versioning:

    • The savegame_upgrade_v* method must be static in your data struct
    • Your struct is a child class of VersionedDataStruct. This parent class performs the version check for you, with the same naming rules for the upgrade methods, that apply for objects. The VersionedDataStruct class adds a member file_version to your struct and adds itself as a receiver for the raptor internal broadcast __RAPTOR_BROADCAST_SAVEGAME_VERSION_CHECK, which occurs at the end of the game load process, right before the version check of object instances. When the broadcast arrives, it performs the version check on the struct and removes itself as a receiver.
    • You used the construct method (see Example 2 below) in your data class. Only then, savegame can restore your class through the original constructor (which will call the parent constructor, which will do the registration for the broadcast.
  • For objects, make sure to place these methods in the CREATE event of your object, so the method is known when the object is created!


Example 1: An Object Instance

This all sounds way more complicated than it is. Let's continue with the imaginary situation of loading a version 1 file in a version 4 game.
Let's assume, version 2 brought new skills to the game, version 3 had no additional data for the loaded object, but we need to do something in version 4, because the default value of the damage of the skill is now set to 10 instead of 15 as it has been before.

The sample code below could be part of the Create event of the Attack object, which will receive the new skill from version 2 onwards and then will receive a reduction of the default damage since the patch to version 4.

/// Imaginary create event
event_inherited();

savegame_upgrade_v2 = function() {
    // add a new imaginary skill to an imaginary skills struct in your data
    variable_struct_set(data.skills, "FantasticBlow", new AttackSkill(15)); // 15 is the default damage
}

savegame_upgrade_v4 = function() {
    var blow = variable_struct_get(data.skills, "FantasticBlow");
    blow.default_damage = 10;  // Reduce it to 10 as these are the rules for this patch
}

Example 2: A Data Struct

This is a short example for a data struct for some game_statistics class.

function GameStatistics() : VersionedDataStruct() constructor {
    construct(GameStatistics);

    hours_played = 0;
    wins = 0;
    losses = 0;
    // .... tons of statistics fields ....

    static savegame_upgrade_v2 = function() {
        // add a new statistics value to the class
        // The next time, this gets saved, the "head_shots" will be part of the class
        head_shots = 0;
    }
}

An example implementation can be found in the ProfileData object of the gml-raptor-demo project.


Continue reading in Workflow and User Events.

⚠️ **GitHub.com Fallback** ⚠️