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.
Version upgrade works in a fixed timeline, as it is expected, that objects depend on data structs and not vice versa.
-
GLOBALDATA
is restored (entire tree) --> then, version check for each struct inGLOBALDATA
of the savegame -
STRUCTS
is restored (entire tree) --> then, version check for each struct inSTRUCTS
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 bestatic
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. TheVersionedDataStruct
class adds a memberfile_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 (seeExample 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.
- The
-
For
objects
, make sure to place these methods in theCREATE
event of your object, so the method is known when the object is created!
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
}
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.