Difference between revisions of "Modding: Versioning"

From Divinity Engine Wiki
Jump to: navigation, search
(Behaviour scripts)
m (Story/Osiris)
Line 121: Line 121:
 
# The story rules of the savegame story are replaced with the story rules of the new version of the mod(s)
 
# The story rules of the savegame story are replaced with the story rules of the new version of the mod(s)
 
# The previously dumped database fact are reimported (without triggering any rules that depend on them)
 
# The previously dumped database fact are reimported (without triggering any rules that depend on them)
# All goals that were [[Osiris_Overview#Goal_Initialisation_and_Completion|active/initialised]] in the savegame story, are again marked as active (based on goal name, regardless of their location in the hierarchy)
+
# All goals that were [[Osiris_Overview#Goal_Initialisation_and_Completion|active/initialised]] in the savegame story, are again marked as active (based on goal name, regardless of their location in the hierarchy). The same holds for completed goals.
# If new goals were added to the mod and these new goals are either top-level goals or subgoals of goals that were active/initialised in the savegame, then these new goals will be initialised at this point.
+
# If new goals were added to the mod and these new goals are either top-level goals or subgoals of goals that were completed in the savegame, then these new goals will be initialised at this point.
 
#* Other newly added goals will not be initialised at this point
 
#* Other newly added goals will not be initialised at this point
  

Revision as of 18:57, 14 November 2017

Overview

It is possible to release new versions of a mod that work both for existing savegames, that use an older version of the mod, and for new games. However, keeping savegame compatibility requires that you are careful regarding what and how you change things.

First golden rule

Never remove anything from your mod: root template, material, script, locals, lsb files, entries in lsb files, stats, etc. You can only add or modify certain things.

You can, however, use tricks to "remove" objects: set items/characters off-stage, or move them to inaccessible locations. Keep in mind that if the items can be be picked up, they may already be in a player's inventory and they most likely will not appreciate suddenly losing it.

Second golden rule

Only change things explicitly mentioned here as changeable.

Third golden rule

Test your changes with old savegames, to at least verify your change doesn't crash. Think about how your change can affect a play-through or savegame, and let someone think along with you.

Changes visible in existing savegames

  • Stat properties. Take into account that properties will change for existing objects as well.
  • Treasure tables
  • SkillData
  • Root template/item OnUse/OnDeath actions
  • You can change Tags of roots and objects. Be careful when removing existing Tags from them.
  • Materials
  • Textures
  • GR2s
  • Effects
  • Animations / Action templates
  • Physics
  • AI grid
  • Minimaps
  • Atmospheres (take into account that Osiris can change the atmosphere though)
  • Icons
  • Sound files
  • Voice types
  • Instances (grass etc...)
  • Terrain
  • Item combinations
  • Camera triggers
  • Walls
  • Decals
  • Lights
  • Lightprobes
  • Camera splines
  • Vision Grid
  • AllSpark effects
  • Objects which are tagged as “Scenery” can change all their properties
  • Create new levels
  • Crime reactions can be changed or modified, but not the properties of the crime itself.
  • You can add a new tag in the tag editor, but you cannot remove tags there

Changes only visible in new games

Everything that is stored in the savegames can of course not be changed for existing games (for characters / items / triggers). In short these are things like:

  • Position, rotation, scale
  • IsBoss
  • DefaultDialog
  • IsTrader
  • AnimationOverride
  • Root template
  • Alignment
  • Stats object
  • Inventory
  • Skillset
  • Character / item variables
  • IsKey
  • Key
  • LockLevel
  • Dynamic Tags of objects
  • Most crime properties (lifetime, continuous, audible, etc)
  • You can add/remove a skill to a creature and change the conditions on it.

Changes that are never safe

  • Anything that makes previously accessible AI-grid non-accessible (a player may have been standing in that place)
  • Changing a root template of an object from a dynamic item (orange cube icon) to a static item (blue cube item)
  • Do not turn off: CanBeMoved (player may have put this item in a way that now blocks a passage, or have it in their inventory)
  • Do not turn off: CanBePickedUp (idem)
  • Do not change: Local/Global property.
  • Do not remove: Scripts and script vars that have already been assigned

Issues related to specific resources

Stats

  • Never delete a stat.
  • Never delete a delta-modifier.
  • Never delete a skill.
  • You can change stats.
  • Runes, however, are already applied and have been saved to the item.

Objects/root template instances

The root template rules are also valid for LOCAL characters, items and triggers (except for atmosphere triggers), since these objects are stored in a special savegame (the levelcache) as soon as a player enters a certain region. For scenery all properties can still be changed, since these are not saved. However, never change an object from scenery to non-scenery or vice-versa.

Dialogs

You can add and change nodes and node connections in dialogs, but never remove nodes. The reason is that a game could have been saved while a player was in this node.

Behaviour scripts

Behaviour scripts are loaded from their textual form whenever the game starts. This means that any changes to behaviour scripts will automatically apply to savegames that were started with older versions of those scripts. There are, however, a number of things to take into account.

Variables

The following holds both for global and local variables:

  • New variables added to an existing behaviour script are added to existing characters that had this script assigned, and will get the default (null/0/empty value).
  • Removing a variable merely makes this variable no longer accessible in existing savegames; it does not remove it. It will not be present in new games though.
  • Do not change the type of a variable.

Reactions

Whenever a reaction is interrupted (such as when saving the game), the game will store the instruction pointer (~ line) in that reaction. This pointer is relative to the start of the reaction. The next time this reaction resumes, it will resume from this saved instruction pointer location.

If you add or remove lines to/from a reaction, the instruction pointer stored in a savegame may now point to a different instruction. If it now points past the last line of the reaction, it will be reset to point to the start of the reaction again.

Do not completely remove reactions. If a savegame was in the middle of a reaction that was removed from the game, the result is undefined behaviour (including possibly crashes, depending on the game version).

Events

Events are executed entirely in one gameplay tick. This means that a savegame can never be in the middle of an event, and changing, removing and adding events can be done at will.

Story/Osiris

It is impossible to save while in the middle of an Osiris rule/condition/action. In this sense, story code behaves like events in behaviour scripts: in principle, it is safe to change anything. However, unlike behaviour scripts, the compiled story is stored in a savegame. This means that upon loading the savegame again, potential changes to the story code in the used mods will not be picked up by default.

That said, Osiris has a feature called "story patching". This feature can be used to update the savegame's compiled story to a new version.

What is story patching

Story patching replaces the existing story rules with new rules while keeping the current story state:

  1. All database facts in the savegame are extracted/dumped
  2. The story rules of the savegame story are replaced with the story rules of the new version of the mod(s)
  3. The previously dumped database fact are reimported (without triggering any rules that depend on them)
  4. All goals that were active/initialised in the savegame story, are again marked as active (based on goal name, regardless of their location in the hierarchy). The same holds for completed goals.
  5. If new goals were added to the mod and these new goals are either top-level goals or subgoals of goals that were completed in the savegame, then these new goals will be initialised at this point.
    • Other newly added goals will not be initialised at this point

When does story patching activate

Every mod has a version. You can set this version in the Project menu -> Project Settings... -> General tab in the editor while your mod is loaded. When the version of the installed mod is higher than the version of the mod in the savegame, the game performs story patching.

Hooking into story patching

In most cases, you will also want to manually fix-up certain things in savegames apart from what gets done automatically by the story patching. The most common one is if you change the INIT section of a goal: since that INIT section will not be executed again if the goal was already active in a savegame, you need a way to perform those actions for existing savegames. Another common case is setting certain flags that should have been set under certain conditions, but were not for one reason or another.

In order to perform such changes, you need two things:

  • an event to catch at which point you perform your changes
  • a way to check the version of the savegame, to know which changes you have to perform at that point

The event to use is SavegameLoaded. It gets thrown whenever a savegame gets loaded. Unfortunately, the version number it gets passed is the version of the game binary and unrelated to your mod. As a result, you cannot use this to determine the version of your mod that was used in that savegame.

Therefore, to perform savegame versioning, you should define a unique version database fact that you check in the SavegameLoaded event to determine whether or not anything needs to be patched.

Example

First version of your mod

You have a skeleton with the Good NPC alignment. Additionally, you also have a story goal with the following contents:

INIT
{
  DB_Dialogs(CHARACTERGUID_MyPrefix_Skeleton1_UUID,"SkellyDialog");

  // Version that we will use to check what changes we need to apply to savegames
  DB_MyPrefix_ModVersion(1);
}
KB
{
}
EXIT
{
}

Second version of your mod

You realise that the skeleton is currently allied to all players, and you would rather have it neutral. So you change the alignment in the sidebar to Neutral NPC. Unfortunately, that won't change its alignment in existing savegame. Additionally, you forgot to add the dialog for a second skeleton that was also already in the game. Similarly as with the alignment, defining another DB_Dialogs in the init section of the goal will only define that dialog for new games.

Luckily, you can take advantage of story patching to ensure your changes also become available to existing savegames:

  • increase the version number of your mod so story patching gets activated for existing savegames
  • modify your story goal as follows:
INIT
{
  DB_Dialogs(CHARACTERGUID_MyPrefix_Skeleton1_UUID, "SkellyDialog");
  // Only gets added for new games
  DB_Dialogs(CHARACTERGUID_MyPrefix_Skeleton2_UUID, "SecondSkellyDialog");

  // Only new games will get this version
  DB_MyPrefix_ModVersion(2);
}
KB
{
  // We don't care about the game binary's version number
  IF
  SavegameLoaded(_,_,_,_)
  AND
  DB_MyPrefix_ModVersion(_Version)
  AND
  _Version < 2
  THEN
  // Update version number this patching does not happen every time
  NOT DB_MyPrefix_ModVersion(_Version);
  DB_MyPrefix_ModVersion(2);
  // Add the extra DB_Dialogs entry
  DB_Dialogs(CHARACTERGUID_MyPrefix_Skeleton2_UUID, "SecondSkellyDialog"); }
  // Fix the alignment of the skeleton
  SetFaction(CHARACTERGUID_MyPrefix_Skeleton1_UUID, "Neutral NPC");
EXIT
{
}