User:DavidJCobb/Miscellany

From the CreationKit Wiki
< User:DavidJCobb
Revision as of 18:17, 18 January 2015 by imported>DavidJCobb (→‎Unsorted facts: Important info for testing mods, and for making clean saves.)
Jump to navigation Jump to search

This page contains miscellaneous information about Skyrim modding that I've found during my glorious adventures through the Internet. Please lemme know if I link to any sources this wiki isn't cool with.

Scripting

Changing a player's Speed Multiplier (SpeedMult)

Source: gamesas: Dynamic Walk Speed Script

Changes to the player's Speed Multiplier actor value don't take effect unless you prompt the game engine to take notice. This issue applies to both scripted changes and changes triggered by a "Value Modifier" magic effect. There are two ways to force the game to actually use a changed SpeedMult:

  • Modify the player's inventory. Silently adding and then removing an item should do the trick.
  • Modify the player's carry weight actor value. Adding and then removing 0.1 works.

Detecting invalid references

Source: gamesas: Suspended stack count is over our warning threshold...

It's possible for your mod to reference (by variable or Alias) a Form from another mod. If that mod is uninstalled, your reference will break. Tests like kMyObject is ObjectReference will return true, but attempts to access any functions or data on the variable will throw errors. What you must do instead is:

  Int iFormID = kSomeReference.GetFormId()
  If iFormID && kSomeReference is ObjectReference
     ;
     ; we're cool bro
     ;
  Else
     ;
     ; CODE RED
     ;
  EndIf

Detecting references created at run-time

Source: AFKMods: How to distinguish run-time created actors from hand-placed ones ?

If the reference is an Actor, you can't! The base game can create actors to fill quest aliases, it seems.

As for other kinds of references? I'll look into that later.


Papyrus errors

Samples of Papyrus logs and errors that I haven't often seen posted on the web, along with meanings when known.

Baked script data is outdated

Source: AFKMods: The critter thread
warning: Function <name> in stack frame <number> in stack <number> differs from the in-game resource files - using version from save

Occurs when an old version of a script is baked into a savegame.

Baked script data malfunctioning badly enough to be dropped

Source: AFKMods: The critter thread
[02/09/2014 - 03:33:25PM] error: Failed to read basic script data for critterFish attached to  (FF002ABA)
[02/09/2014 - 03:33:25PM] error: Unable to load object 0x1305E1E0 from save game
[02/09/2014 - 03:33:25PM] Errors occurred while loading the Papyrus save game data

Occurs when old script data (baked into a savegame) has malfunctioned badly enough for the game engine to just throw it out.


Fun with multi-threading!

"Fun" may not be the right word to use here, actually.

Conditions are interruptible

Source: AFKMods: The critter thread

Conditions like the following are inherently unreliable due to how Papyrus handles threading.

  If kMyObjectReference && !kMyObjectReference.isDeleted() && !kMyObjectReference.isDisabled()
  EndIf

IsDeleted() and IsDisabled() are delayed functions (as opposed to non-delayed functions), so Papyrus may actually pause the current script in the middle of this If statement to run them. During that time, another thread can set the ObjectReference variable to None. Per the scripter's comments in the source, this can lead to two kinds of errors:

  • kMyObjectReference.IsDisabled() gets treated as None.IsDisabled(). This is invalid and returns None.
  • That returned None is then inverted with !, which is also invalid. This yields Papyrus log errors of the form:
    warning: Assigning None to a non-object variable named "::temp33"

The lesson to learn here is that even a single If statement can be interrupted and hosed by multithreading.

Makes me wonder if something like the following might work:

  If kMyObjectReference && !kMyObjectReference.isDeleted() && kMyObjectReference && !kMyObjectReference.isDisabled()
  EndIf


Delayed functions are interruptible

Just about every native function that isn't listed here is interruptible. Every moment you call these functions is a moment your script can be interrupted. This produces all sorts of "fun" oddities.

  • Delete() is a delayed function. Your attempt to delete an object can be interrupted by another attempt to delete that same object, resulting in your interrupted call failing (it's treated as None.Delete()).
  • The x, y, and z "properties" on ObjectReferences are actually interruptible functions. As such, code blocks like the following can fail:
    Int iCoordX = kMyObjectReference.x ; due to multi-threading, this code may be interrupted here...
    Int iCoordY = kMyObjectReference.y ; ...and by the time the code resumes, kMyObjectReference could've been screwed with


Other things to remember

  • Cross-script function calls are interruptible. Your precious Debug.Trace(...) statements actually can affect things.


Unsorted facts

Unsorted edge-cases

  • Be careful about handling things OnCellAttach and OnCellDetach in Riften. The city's walkways have you cross back and forth across a cell boundary.
    • The use case dealt with here involves deleting critters when you leave their containing cells. The scripter's attempted solution was to add a timed delay (using OnUpdateGameTime) to the deletion. Return to the cell before the timer has elapsed, and the critter is reused -- the deletion is canceled.