Difference between revisions of "Save Files Notes (Papyrus)"
imported>Taleden (added FormLists content) |
imported>Taleden (added "Missing Plugin" under "FormLists") |
||
Line 262: | Line 262: | ||
Debug.Trace( fl.GetAt(3) ) ; Dragonstone | Debug.Trace( fl.GetAt(3) ) ; Dragonstone | ||
</source> | </source> | ||
===Missing Plugin=== | |||
If a form which is provided by some plugin is added to a FormList by a script, and that plugin is removed or disabled between saving and reloading the game, then the affected entries in the FormList will not be removed, but will instead be relocated to the beginning of the list and changed to None. If the game is saved again at this point and the missing plugin is restored, the FormList entries which became None will nonetheless remain None and will not be removed or regain their original values and positions in the list. A FormList's Revert() method will still remove these missing entries, however, just as it removes all script-added entries. | |||
<source lang="papyrus"> | |||
; FormList Property fl Auto | |||
; Form Property Dragonstone Auto | |||
; CK defines fl = [Torch, Lockpick] | |||
Debug.Trace( fl.GetSize() ) ; 2 | |||
fl.AddForm(Game.GetFormFromFile(0x123, "Plugin.esp")) | |||
fl.AddForm(Dragonstone) | |||
Debug.Trace( fl.GetSize() ) ; 4 | |||
Debug.Trace( fl.GetAt(0) ) ; Torch | |||
Debug.Trace( fl.GetAt(1) ) ; Lockpick | |||
Debug.Trace( fl.GetAt(2) ) ; PluginItem0x123 | |||
Debug.Trace( fl.GetAt(3) ) ; Dragonstone | |||
Game.SaveGame("save1") | |||
; Plugin.esp is disabled or removed, save1 is loaded, script execution resumes here | |||
Debug.Trace( fl.GetSize() ) ; 4 | |||
Debug.Trace( fl.GetAt(0) ) ; None | |||
Debug.Trace( fl.GetAt(1) ) ; Torch | |||
Debug.Trace( fl.GetAt(2) ) ; Lockpick | |||
Debug.Trace( fl.GetAt(3) ) ; Dragonstone | |||
Game.SaveGame("save2") | |||
; Plugin.esp is restored, save2 is loaded, script execution resumes here | |||
Debug.Trace( fl.GetAt(0) ) ; None | |||
Debug.Trace( fl.GetAt(1) ) ; Torch | |||
Debug.Trace( fl.GetAt(2) ) ; Lockpick | |||
Debug.Trace( fl.GetAt(3) ) ; Dragonstone | |||
</source> | |||
There is no way to add None to a FormList without actually removing a plugin; the CK does not offer None as a drag-and-drop option, and FormList.AddForm(None) has no effect. Similarly, FormList.HasForm(None) and FormList.Find(None) will always return False and -1, respectively, even if there are None-entries at the beginning of the list as a result of a missing plugin. | |||
{{Languages}} | {{Languages}} | ||
[[Category: Papyrus]] | [[Category: Papyrus]] |
Revision as of 15:52, 10 October 2013
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!
FormLists
If entries are added to a FormList by a script, and the game is saved and reloaded, then the additional entries will be restored from the save file. If the FormList is modified in the Creation Kit between saving and loading, then those modifications will also take effect: after reloading the save, the FormList will contain all elements specified in the CK as of loading (not as of saving), followed by all elements which had previously been added by scripts (as of saving). If the FormList's length is changed in the CK, this implies that script-added entries will have different indecies after reloading than they did when saving. A FormList's Revert() method discards all script-added entries from the list, including entries added during a previous play session which were then restored from a save file.
; FormList Property fl Auto
; Form Property Dragonstone Auto
; CK defines fl = [Torch, Lockpick]
Debug.Trace( fl.GetSize() ) ; 2
Debug.Trace( fl.GetAt(0) ) ; Torch
Debug.Trace( fl.GetAt(1) ) ; Lockpick
Debug.Trace( fl.GetAt(2) ) ; None
fl.AddForm(Dragonstone)
Debug.Trace( fl.GetSize() ) ; 3
Debug.Trace( fl.GetAt(2) ) ; Dragonstone
Game.SaveGame("save1")
; CK redefines fl = [Lockpick, IronOre, IronIngot] ; save1 is loaded, script execution resumes here
Debug.Trace( fl.GetSize() ) ; 4
Debug.Trace( fl.GetAt(0) ) ; Lockpick
Debug.Trace( fl.GetAt(1) ) ; IronOre
Debug.Trace( fl.GetAt(2) ) ; IronIngot
Debug.Trace( fl.GetAt(3) ) ; Dragonstone
Missing Plugin
If a form which is provided by some plugin is added to a FormList by a script, and that plugin is removed or disabled between saving and reloading the game, then the affected entries in the FormList will not be removed, but will instead be relocated to the beginning of the list and changed to None. If the game is saved again at this point and the missing plugin is restored, the FormList entries which became None will nonetheless remain None and will not be removed or regain their original values and positions in the list. A FormList's Revert() method will still remove these missing entries, however, just as it removes all script-added entries.
; FormList Property fl Auto
; Form Property Dragonstone Auto
; CK defines fl = [Torch, Lockpick]
Debug.Trace( fl.GetSize() ) ; 2
fl.AddForm(Game.GetFormFromFile(0x123, "Plugin.esp"))
fl.AddForm(Dragonstone)
Debug.Trace( fl.GetSize() ) ; 4
Debug.Trace( fl.GetAt(0) ) ; Torch
Debug.Trace( fl.GetAt(1) ) ; Lockpick
Debug.Trace( fl.GetAt(2) ) ; PluginItem0x123
Debug.Trace( fl.GetAt(3) ) ; Dragonstone
Game.SaveGame("save1")
; Plugin.esp is disabled or removed, save1 is loaded, script execution resumes here
Debug.Trace( fl.GetSize() ) ; 4
Debug.Trace( fl.GetAt(0) ) ; None
Debug.Trace( fl.GetAt(1) ) ; Torch
Debug.Trace( fl.GetAt(2) ) ; Lockpick
Debug.Trace( fl.GetAt(3) ) ; Dragonstone
Game.SaveGame("save2")
; Plugin.esp is restored, save2 is loaded, script execution resumes here
Debug.Trace( fl.GetAt(0) ) ; None
Debug.Trace( fl.GetAt(1) ) ; Torch
Debug.Trace( fl.GetAt(2) ) ; Lockpick
Debug.Trace( fl.GetAt(3) ) ; Dragonstone
There is no way to add None to a FormList without actually removing a plugin; the CK does not offer None as a drag-and-drop option, and FormList.AddForm(None) has no effect. Similarly, FormList.HasForm(None) and FormList.Find(None) will always return False and -1, respectively, even if there are None-entries at the beginning of the list as a result of a missing plugin.
Language: | English |
---|