Save File Notes (Papyrus)

From the Fallout4 CreationKit Wiki
Jump to navigation Jump to search

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[edit | edit source]

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?[edit | edit source]

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[edit | edit source]

Adding to an Object[edit | edit source]

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[edit | edit source]

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.

Properties and Script Variables[edit | edit source]

Adding[edit | edit source]

  • 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[edit | edit source]

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[edit | edit source]

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[edit | edit source]

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[edit | edit source]

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[edit | edit source]

; 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

Functions[edit | edit source]

Notes[edit | edit source]

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.

Adding[edit | edit source]

There are no issues with adding new functions.

Removing[edit | edit source]

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[edit | edit source]

Changing Parameters or Return Type[edit | edit source]

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[edit | edit source]

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[edit | edit source]

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[edit | edit source]

; 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!

Gotchas[edit | edit source]

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.