Mod:Creation Kit/Save File Notes (Papyrus)

This page details information about how the save game system saves Papyrus scripts. Information on this page will be updated as things change! Known issues and targets for being fixed/changed will be marked.

Important
Removing mods and continuing to use the same save game is not supported by the game. (For anything, not just scripts)

If you remove a mod you must go back to a save made before the mod was installed.

When can the game save?
The game can save at any time. This can be between lines in your script, or even in the middle of running a single line. For the most part, you won't have to worry about it, as the game will successfully return your script to running when the game is loaded, assuming you don't change anything. But what if you do change something? Well, that's what this page will help you with.

Adding to an Object
If you add a script to an object in the save game after a save was made, that script will exist on the object when the save is loaded, but will be completely untouched (all of its properties will be set to their masterfile values, and its OnInit block will be run as soon as the save finishes loading).

Removing From an Object
If you remove a script from an object in the save game after a save was made, that script will still exist on the object when the save is loaded. If the script is deleted entirely, oddities may result.

Adding
Note: For obvious reasons, if the object's OnInit event has already run when the save was made, it will not be run again. Therefore, if you set up a new variable in the OnInit event, you cannot be guaranteed that that variable will have the proper value unless you are sure that object never existed in the save game before.
 * Variable with a default value: The variable will have its default value when the save is loaded. (This is the "int myVar = 5" syntax)
 * Variable with no default value: The variable will have no value when the save is loaded (0, empty string, None, or false)
 * Auto Property: The property will receive the value it was given in the master file, if any. (These are properties that end with the "auto" keyword, like so: "ObjectReference Property MyObject Auto"
 * Non-auto Property: The property will not receive the value it was given in the master file, if any. It will be blank.

Removing
If you remove a property or variable after a save was made, that property or variable's value in the save game will be discarded, and a warning will be printed to the script log detailing what happened.

Changing
If you change the name of a property or variable, it will be treated as if it was deleted and a new one was added. If you change the type (but not the name), then any value it had in the save game will be discarded, and a warning will be printed to the script log detailing what happened.

If you change the value of a const object variable in script, the variable will gain the new value when the save game is loaded.

Changing Masterfile Value
If you have an existing property and change its value in the masterfile, the script will only receive the new value if it doesn't exist in the save. In other words - changing the value will not overwrite the value it has in the save game.

Const properties will ignore values saved in save games, so if the masterfile value changes, the property will gain the new masterfile value when the save is loaded.

Note that if you change the masterfile value of a const property only the property's value will change. Any variables you assigned the value to during the normal course of scripting (via passing as a function parameter or simply assignment) will retain the old value. This is true of object, struct, and array values which store by reference because the property will be pointed at a new value, leaving the other variables pointing at the old one.

Changing const-ness
Const properties and variables still save their last value in the save game (they just ignore it on load). Therefore if you make a const property or variable non-const, the value recorded in the save game will then override any masterfile or script-initial value. If you make a non-const property or variable into a const property or value, any value in the save game will then be ignored on load and the masterfile or script-initial value will re-assert itself.

Property Examples
Scriptname MyScript Extends ObjectReference
 * Version 1

int Property MyAutoValue Auto

int internalValue int Property MyNonAutoValue Function Set(int value) internalValue = value EndFunction int Function Get return internalValue EndFunction EndProperty

int MyVariableWithInit = 1 int MyVariableWithoutInit

Event OnInit MyVariableWithoutInit = 1 EndEvent

Event OnActivate(ObjectReference akActivator) Debug.Trace("MyAutoValue = " + MyAutoValue) Debug.Trace("MyNonAutoValue = " + MyNonAutoValue) Debug.Trace("MyVariableWithInit = " + MyVariableWithInit) Debug.Trace("MyVariableWithoutInit = " + MyVariableWithoutInit) EndEvent

In the masterfile:
 * MyAutoValue = 1
 * MyNonAutoValue = 1

In the log, after activation: MyAutoValue = 1 MyNonAutoValue = 1 MyVariableWithInit = 1 MyVariableWithoutInit = 1

Scriptname MyScript Extends ObjectReference
 * Version 2

int Property MyAutoValue Auto int Property MyAutoValue2 Auto

int internalValue int Property MyNonAutoValue Function Set(int value) internalValue = value EndFunction int Function Get return internalValue EndFunction EndProperty

int internalValue2 int Property MyNonAutoValue2 Function Set(int value) internalValue2 = value EndFunction int Function Get return internalValue2 EndFunction EndProperty

int MyVariableWithInit = 2 int MyVariableWithoutInit

int MyVariableWithInit2 = 2 int MyVariableWithoutInit2

Event OnInit MyVariableWithoutInit = 2 MyVariableWithoutInit2 = 2 EndEvent

Event OnActivate(ObjectReference akActivator) Debug.Trace("MyAutoValue = " + MyAutoValue) Debug.Trace("MyNonAutoValue = " + MyNonAutoValue) Debug.Trace("MyVariableWithInit = " + MyVariableWithInit) Debug.Trace("MyVariableWithoutInit = " + MyVariableWithoutInit) Debug.Trace("MyAutoValue2 = " + MyAutoValue2) Debug.Trace("MyNonAutoValue2 = " + MyNonAutoValue2) Debug.Trace("MyVariableWithInit2 = " + MyVariableWithInit2) Debug.Trace("MyVariableWithoutInit2 = " + MyVariableWithoutInit2) EndEvent

In the masterfile:
 * MyAutoValue = 2
 * MyNonAutoValue = 2
 * MyAutoValue2 = 2
 * MyNonAutoValue2 = 2

In the log, after activation and loading the previous save: MyAutoValue = 1 MyNonAutoValue = 1 MyVariableWithInit = 1 MyVariableWithoutInit = 1 MyAutoValue2 = 2 MyNonAutoValue2 = 0 MyVariableWithInit2 = 2 MyVariableWithoutInit2 = 0

Adding
There are no issues with adding new functions.

Removing
If you remove a function that was in the middle of running when a save was made, the old function will be loaded from the save and allowed to finish. A warning will be printed to the script log, and further calls to the removed function (usually because some other changed or removed function is calling it) will fail.

Changing Parameters or Return Type
The function will be noted as different and the old version of the function will be loaded from the save game and allowed to finish running. Further calls to the function will use the version in the archives/loose files, possibly issuing errors if there are parameter or return type mismatches (usually because some other changed or removed function is calling it).

Adding/Removing/Changing Function Variables
The function will be noted as different and the old version of the function will be loaded from the save game and allowed to finish running. Further calls to the function will use the version in the archives/loose files.

Changing Code
The function will be noted as different and the old version of the function will be loaded from the save game and allowed to finish running. Further calls to the function will use the version in the archives/loose files. Exception: If a function was native and is now scripted, the stack will be thrown out instead of resumed.

Function Examples
Scriptname MyScript extends ObjectReference
 * Version 1

Event OnActivate(ObjectReference akActivator) MyFunction(1) MyFunction(2) EndEvent

Function MyFunction(int aiMyValue) Debug.Trace("Entered my function version 1: aiMyValue = " + aiMyValue) ; Save is made *here* Debug.Trace("Left my function version 1: aiMyValue = " + aiMyValue) EndFunction

Scriptname MyScript extends ObjectReference
 * Version 2 - identical to version 1, except for the trace statements

Event OnActivate(ObjectReference akActivator) MyFunction(1) MyFunction(2) EndEvent

Function MyFunction(int aiMyValue) Debug.Trace("Entered my function version 2: aiMyValue = " + aiMyValue) ; Save is made *here* Debug.Trace("Left my function version 2: aiMyValue = " + aiMyValue) EndFunction

Scriptname MyScript extends ObjectReference
 * Version 3

Event OnActivate(ObjectReference akActivator) Debug.Trace("OnActivate received!") EndEvent

Save is made during MyFunction(1) called from OnActivate in version 1 of the script.

Script log before save: Entered my function version 1: aiMyValue = 1

Script log after load with version 2: warning: Function MyScript..MyFunction in stack frame 2 in stack 457 differs from the in-game resource files - using version from save ... Left my function version 1: aiMyValue = 1 Entered my function version 2: aiMyValue = 2 Left my function version 2: aiMyValue = 2

Script log after load with version 3: warning: Function Myscript..MyFunction in stack frame 2 in stack 457 doesn't exist in the in-game resource files - using version from save warning: Function MyScript..OnActivate in stack frame 1 in stack 457 differs from the in-game resource files - using version from save ... Left my function version 1: aiMyValue = 1 error: Method MyFunction not found on MyScript. Aborting call and returning None ...  OnActivate received!

Gotchas
If a reference is put into a container at some point and isn't persistent at the time it went into the container, reference-level changes in plugins will be not be applied because the in-world object is a new reference and the game has no way of connecting the two together to pull the changes through.