-
Notifications
You must be signed in to change notification settings - Fork 7
Tutorial Old Mod Updating
*Note that this page is currently a work-in-progress*
One of the biggest issues when managing a complex mod is dealing with existing saves.
A common problem is when you have a database for specific settings, and you decide to change values in that database, or change the database itself after people have already been using your mod.
Databases are stored in the save itself, so knowing when to clear things out and re-initialize your new databases requires some form of version control. LeaderLib provides a way to do so with the LeaderUpdater.
The recommended way to handle versioning is by making a script dedicated to doing so. Ideally this script is loaded after all your other scripts, which can be done by naming it in a way that makes it sorted last, alphabetically.
For example, let's say I have a mod called "Mimicry", and I prefix my scripts with the acronym LLMIME_
(LaughingLeader Mimicry), naming my updater script "LLMIME_ZZZ_Updater" ensures it is loaded after my other scripts by Osiris.
To start things off, we'll need to register our mod and our mod's current version to the LeaderUpdater. Create a new updater story script, and add this to the INIT section:
LeaderUpdater_Register_Mod("MyMod", "Author", 1,0,0,0);
LeaderUpdater_Register_ActiveGoal("MyMod", "Author", "MyMod_ZZZ_Updater");
Replace MyMod
with the ID you want for your mod, Author
with the name you want (such as a username), and 1,0,0,0 with your mod's version numbers.
For the second procedure, LeaderUpdater_Register_ActiveGoal
, you'll also need to replace MyMod_ZZZ_Updater
with the name of a script that will always be active while your mod is enabled.
Let's break down what these procedures do:
LeaderUpdater_Register_Mod((STRING)_ModID, (STRING)_Author, (INTEGER)_Major, (INTEGER)_Minor, (INTEGER)_Revision, (INTEGER)_Build)
This procedure simply registers your mod with a specific version. Pre-existing registered versions that specific combination of _ModID
and _Author
are cleared, and an update procedure is called.
LeaderUpdater_Register_ActiveGoal((STRING)_ModID, (STRING)_Author, (STRING)_GoalTitle)
In order for LeaderLib to know when your mod is active (or inactive), we need a story goal to check. _GoalTitle
should be the name of a specific story script for your mod (minus the file extension), that will always be active.
Now that we have an initial registration set up, what do we do for existing saves that are already running our mod? We need a rule that updates existing saves to the current version. Place this in the KB section of your updater script:
IF
GameStarted(_,_)
AND
LeaderUpdater_QRY_ModUpdateNeeded("MyMod", "Author", 1,0,0,0)
THEN
LeaderUpdater_Register_Mod("MyMod", "Author", 1,0,0,0);
As before, replace the values used in the query and procedure with ones for your mod. We use a query here, LeaderUpdater_QRY_ModUpdateNeeded
, to check if the version passed in is registered with LeaderLib. IF it isn't, we simple register the new version, which will clear out the previous.
Your active goal may also be registered in this bit, if LeaderLib support was added after you already released your mod.
Now that we have a basic script in place to register our mod, and update our mod's registered version, we can utilize a procedure that's called when our mod is updated.
Let's say we have a goal, called "MyMod_Skills", that calls the following procedure:
INIT:
MyMod_Skills_InitSettings();
KB:
PROC
MyMod_Skills_InitSettings()
THEN
DB_MyMod_Skills_SkillToStatus("MYMOD_WARRIOR_BUFF", "Target_MyMod_PiercingGaze");
DB_MyMod_Skills_SkillToStatus("MYMOD_WARRIOR_BUFF", "Shout_MyMod_DeathCry");
DB_MyMod_Skills_SkillToStatus("MYMOD_ROGUE_BUFF", "Target_MyMod_PickpocketAgain");
DB_MyMod_Skills_SkillToStatus("MYMOD_ROGUE_BUFF", "Shout_MyMod_DisguiseSelf");
When this goal is first initialized, this procedure is called, and our database is registered.
Now, here's the problem: What if, later on in development, we decide to add more entries to DB_MyMod_Skills_SkillToStatus
? Existing saves that used our mod won't get these new entries, since the goal has already been initialized.
To get around this problem, we can use a procedure LeaderLib calls when a register mod version changes:
LeaderUpdater_ModUpdated((STRING)_ModID, (STRING)_Author, (STRING)_PastVersion, (STRING)_NewVersion)
Updating your mod's databases when the version changes is as simple as adding some new rules for this procedure. In our case, let's say we update "MyMod" to version "1.1.0.0", and we need to update our DB_MyMod_Skills_SkillToStatus
database. We can do so by adding a new rule to MyMod_Skills
:
PROC
LeaderUpdater_ModUpdated("MyMod", "Author", (STRING)_PastVersion, (STRING)_NewVersion)
AND
LeaderLib_StringExt_QRY_VersionIsLessThan(_PastVersion, 1,1,0,0)
THEN
SysClear("DB_MyMod_Skills_SkillToStatus", 2); // Start fresh
MyMod_Skills_InitSettings();
Now when our mod updates from "1.0.0.0" to "1.1.0.0", DB_MyMod_Skills_SkillToStatus
is cleared with SysClear, and re-initialized via MyMod_Skills_InitSettings
.
Note that since DB_MyMod_Skills_SkillToStatus
is only for settings (meaning, it's a static database, set once by our mod), clearing it with SysClear is a viable way to make sure the database remains consistent, without us having to check if each individual new entry is missing.
Databases added to dynamically, by other mods, or by our mod as the game happens, would need a little bit more finesse to update, rather than calling SysClear
, lest you lose important data, or break existing events.