Save Files Notes (Papyrus)

Revision as of 15:58, 10 October 2013 by imported>Taleden (added "Missing Plugin" under "Properties and Script Variables", summarizing experiments into behavior when loading a save after removing one of its plugins)

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.

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.

Scripts

Adding to an Object

If you add a script to an object in the masterfile 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 masterfile 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.

Properties and Script Variables

Adding

  • 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.

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.

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.

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.

Property Examples

; Version 1
Scriptname MyScript Extends ObjectReference

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
<save is made here>
; Version 2
Scriptname MyScript Extends ObjectReference

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:

<save is loaded here>
MyAutoValue = 1
MyNonAutoValue = 1
MyVariableWithInit = 1
MyVariableWithoutInit = 1
MyAutoValue2 = 2
MyNonAutoValue2 = 0
MyVariableWithInit2 = 2
MyVariableWithoutInit2 = 0

Missing Plugin

If a variable, property or array element points to a Form (or any subclass) which is provided by some plugin, and that plugin is removed or disabled between saving and reloading the game, then the variable will not become None but will instead point to a "missing" placeholder form which has ID #0. If the game is saved again at this point and the missing plugin is restored, the variables which became "missing" will nonetheless remain "missing" and will not regain their original values.

Form f = Game.GetFormFromFile(0x123, "Plugin.esp")
Debug.Trace( f == None ) ; false
Debug.Trace( f.GetFormID() ) ; 0x--000123
Game.SaveGame("save1")
; Plugin.esp is disabled or removed, save1 is loaded, script execution resumes here
Debug.Trace( f == None ) ; false
Debug.Trace( f.GetFormID() ) ; 0
Debug.Trace( f.GetType() ) ; 0
Debug.Trace( f.GetName() ) ; ""
Game.SaveGame("save2")
; Plugin.esp is restored, save2 is loaded, script execution resumes here
Debug.Trace( f == None ) ; false
Debug.Trace( f.GetFormID() ) ; 0

If two Form variables both become "missing" in this way, they will then evaluate as equal, even if they originally pointed to different forms. Nonetheless, there is no way to set a Form variable to point to the "missing" form without actually removing a plugin; for example, Game.GetForm(0) returns None, not the "missing" placeholder form.

Functions

Notes

Function changes only matter if the save was made while a function was running. However, since a save can be made at any time, these following points need to be taken into account. Also, the following information assumes a build AND save made after 11/29/2010, earlier builds and saves (even if loaded with later builds) will throw out any running stacks if any of the functions in the thread changes.

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

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

; Version 1
Scriptname MyScript extends ObjectReference

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
; Version 2 - identical to version 1, except for the trace statements
Scriptname MyScript extends ObjectReference

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
; Version 3
Scriptname MyScript extends ObjectReference

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
... <later, after another activation>
OnActivate received!


Language: [[::Save Files Notes (Papyrus)|English]]