Jump to content

Recommended Posts

PapyrusUtil LE/SE/AE

View File

Which file to download:

For Skyrim LE (Oldrim): PapyrusUtil LE v33.zip

For Skyrim SE (Pre-AE, 1.5.x runtime): PapyrusUtil SE v39.zip

For Skyrim SE/AE (1.6.x+ runtime): PapyrusUtil AE v42.zip

 

 

 

1. Description

 

SKSE plugin that allows you to save any amount of int, float, form and string values on any form or globally from papyrus scripts. Also supports lists of those data types. These values can be accessed from any mod allowing easy dynamic compatibility.

 

Also adds the following papyrus commands:

  • Toggle fly cam and set fly cam speed - TFC.
  • Set menus on / off - TM.
  • Adds an additional package stack override on actors. See ActorUtil.psc
  • Replace any animations on selected objects. See ObjectUtil.psc
  • Print messages to console.
  • Get, set, save, or and load data to a custom external JSON file. See JsonUtil.psc

 


PapyrusUtil.psc - version check & variable initialized arrays.
StorageUtil.psc - store variables and lists of data on a form that can be pulled back out using the form and variable name as keys. See psc file for documentation.
JsonUtil.psc - Similar to StorageUtil.psc but saves data to custom external .json files instead of forms, letting them be customizable out of game and stored independent of a users save file.
ActorUtil.psc - actor package override.
ObjectUtil.psc - animation replacement.
MiscUtil.psc - some misc commands.

 

2. Examples

 

Example 1:
Setting and getting simple values

StorageUtil.SetIntValue(none, "myGlobalVariable", 5) ; // enter none as first argument to set global variableStorageUtil.SetIntValue(Game.GetPlayer(), "myVariable", 25) ; // set "myVariable" to 25 on playerStorageUtil.SetFloatValue(akTarget, "myVariable", 75.3) ; // set "myVariable" to 75.3 on akTargetStorageUtil.SetStringValue(none, "myGlobalVariable", "hello") ; // enter none as first argument to set global variableint ivalue1 = StorageUtil.GetIntValue(none, "myGlobalVariable") ; // get the previously saved global variableint ivalue2 = StorageUtil.GetIntValue(Game.GetPlayer(), "myVariable") ; // get value of myVariable from player; // myGlobalVariable can exist both as int and string at the same time.; // Different type values are separate from each other.float fvalue = StorageUtil.GetFloatValue(akTarget, "myVariable") ; // get float value from akTargetstring svalue1 = StorageUtil.GetStringValue(none, "myGlobalVariable") ; // get "hello"string svalue2 = StorageUtil.GetStringValue(none, "myMissingVariable", "goodbye") ; // get "goodbye"; // an optional 3rd variable can be passed in the Get function to be returned if the given key "myMissingVariable" doesn't exists. 
 


Example 2:
Saving object references

Actor akCasterActor akTargetStorageUtil.SetFormValue(akTarget, "Friend", akCaster)Actor friend = StorageUtil.GetFormValue(akTarget, "Friend") as Actor
 


Example 3:
Value lists

StorageUtil.IntListAdd(none, "myGlobalList", 5)StorageUtil.IntListAdd(none, "myGlobalList", 27)StorageUtil.IntListAdd(none, "myGlobalList", 183)StorageUtil.IntListAdd(none, "myGlobalList", 3)StorageUtil.IntListAdd(none, "myGlobalList", -12398); // iterate list from last added to first addedint valueCount = StorageUtil.IntListCount(none, "myGlobalList")while(valueCount > 0)	valueCount -= 1	Debug.Notification("List[" + valueCount + "] = " + StorageUtil.IntListGet(none, "myGlobalList", valueCount))endwhile; // iterate list from first added to last addedvalueCount = StorageUtil.IntListCount(none, "myGlobalList")int i = 0while(i < valueCount)	Debug.Notification("List[" + i + "] = " + StorageUtil.IntListGet(none, "myGlobalList", o))	i += 1endwhile; // Get the 2nd, 3rd, and 4th elements of the list into an arrayint[] myList = new int[3]StorageUtil.IntListSlice(none, "myGlobalList", myList, 1) ; // starts pulling elements from the list starting from from the 1 index; // skipping the 0 index value, "5" will fill the papyrus array until it runs out of either list or papyrus array elementsDebug.Notification("2nd: " + myList[0]) ; // prints "2nd: 27"Debug.Notification("3rd: " + myList[1]) ; // prints "3rd: 183"Debug.Notification("4th: " + myList[2]) ; // prints "4th: 3"; // remove 27 from the listStorageUtil.IntListRemove(none, "myGlobalList", 27); // remove last element of listStorageUtil.IntListRemoveAt(none, "myGlobalList", StorageUtil.IntListCount(none, "myGlobalList") - 1); // set first element to -7StorageUtil.IntListSet(none, "myGlobalList", 0, -7); // find first occurance of element in listint index = StorageUtil.IntListFind(none, "myGlobalList", 183)if(index < 0)	Debug.Notification("Not found!")else	Debug.Notification("Element 183 is at index " + index)endif; // clear listStorageUtil.IntListClear(none, "myGlobalList"); // create a new list from a papyrus arrayfloat[] newList = new float[3]newList[0] = 4.04newList[1] = 39.2newList[2] = -42.25StorageUtil.FloatListCopy(PlayerRef, "myCopiedList", newList)Debug.Notification("Copied value 0 = " +StorageUtil.FloatListGet(PlayerRef, "myCopiedList", 0)) ; // 4.04Debug.Notification("Copied value 1 = " +StorageUtil.FloatListGet(PlayerRef, "myCopiedList", 1)) ; // 39.2Debug.Notification("Copied value 2 = " +StorageUtil.FloatListGet(PlayerRef, "myCopiedList", 2)) ; // -42.25
 


Example 4:
Saving values that are shared among all savegames in an externally saved file.

JsonUtil.SetIntValue("MyModConfig.json", "AnswerToLifeUniverseEverything", 42); // (optional) Save any changes made to your file and creates it if it does not yet exists.; // This is done automatically without needing to be done manually whenever a player saves their game.; // Files are saved and loaded from Skyrim/data/SKSE/Plugins/StorageUtilDataJsonUtil.Save("MyModConfig.json")  ; // ... Start a new game ...int mySetting = JsonUtil.GetIntValue("MyModConfig.json", "AnswerToLifeUniverseEverything") ; // mySetting == 3; // Alternative version using the globally shared external file; // All mods using these commands share this file, saved/loaded from Skyrim/data/SKSE/Plugins/StorageUtil.jsonStorageUtil.SetIntValue("AnswerToLifeUniverseEverything", 42); // ... Start a new game ...int mySetting = StorageUtil.GetIntValue("AnswerToLifeUniverseEverything") ; mySetting == 3
 


3. Requirements

 


SKSE latest version: http://skse.silverlock.org/

Address Library for SKSE Plugins: https://www.nexusmods.com/skyrimspecialedition/mods/32444

 

 

4. Installing

 

Use mod manager or extract files manually.

 

 

 

5. Uninstalling

 

Remove the files you added in Installing step.

 

 

 

6. Updating

 

Just overwrite all files.

 

 

 

7. Compatibility & issues

 

Should be compatible with everything.

 

 

 

8. Credits

 

Ashal - continued maintenance & refactoring of original plugin's source code
h38fh2mf - original version, idea, address library conversion
SKSE team - for making this plugin possible
milzschnitte - for suggestions
eventHandler, Expired, aers, arha, ianpatt - SKSE64 conversion & update assistance

 


  • Submitter
  • Submitted
    12/07/2013
  • Category
  • Requires
    SKSE, Address Library
  • Special Edition Compatible
    Yes

 

Link to comment

Love such things

btw, there is anything that allows send another mod module (Quest) message (in json-like format ? :D) in dynamic manner?

i'd suggest to add a possibility for multiple storage instances as someone may not want to share the data with another mods and to avoid possible  key collisions

Link to comment

No you can't send message but for example if your mod does this:

 

event OnUpdate()

if(StorageUtil.IntListCount(none, "somethings") > 0)

; do stuff with the list

endif

endevent

 

And then another mod could do:

 

StorageUtil.IntListAdd(none, "somethings", 123)

 

Now these two mods can speak with each other.

 

As for the separate storage I chose not to do it that way, you can just put variable name something like "mymod_somethings" where mymod is your mod name and don't document this for other mod makers, you can be sure there won't be conflicts unless another mod maker opened source of your mod and made a conflict on purpose but that's not really your fault.

Link to comment

No not using SQLite there is no need, I use standard C maps and vectors and save it as stringstream, I can post the format here if there is interest  but I don't see how that would be useful, you can just use the papyrus functions and let them do the work. There are also debug functions to iterate through all data (to display in MCM for example).

 

It allows storing unique objects as long as you can get its form in papyrus. As far as I know object references aren't created until the item is dropped into world, but the form of item should still exist. If you can get the form you can store unique data.

Link to comment

What I had in mind was the ability to create hierarchical structures and associate data with each element / node in that structure. I have that structure in place for the slavery framework I'm working on but it requires getting an integer associated with the actor, using it to find the actor's associated alias index, casting that alias into it's script object, then getting the data stored in the script. Much faster to simply do "select masterid from actors where refid = 0x00000014".

 

Couldn't fault you for not wanting to use SQLite though. It's not what most people need or want. What would be really useful would be just the information on how to create a SKSE plugin.

Link to comment

You can create any structure you want with a bit of work, for example you can create a map using two lists. Element of first list is key and element of second list is value where list1 index == list2 index. Removing element you use

index = list1.find(key)

list1.erase(index)

list2.erase(index)

 

This should solve problem with getting value by key as you said? I know it's not natively so but there are hundres or thousands of ways to make data structures using just these simple options that's why I didn't touch it. :)

 

SKSE plugin example is included in the download if you download the archive instead of installer.

Link to comment

Updated to 1.2

 

Added write to file, read from file functions. These should not be used a lot during playing!

 

Added execute batch file in console command. This allows you to use any console commands from papyrus. And in batch so you can make a batch file before or write one from script with WriteToFile function.

 

Added ToggleMenu command functionality, so you can disable and enable menus from script for scinematic stuff.

 

Added experimental package stack on actors. It can be used like this:

Scriptname packageDebug extends activemagiceffect  

Package Property myPackage Auto

Event OnEffectStart(Actor akTarget, Actor akCaster)
	StorageUtil.IntListAdd(akTarget, "DebugPackageOverride", myPackage.GetFormID())
	akTarget.EvaluatePackage()
endEvent

Event OnEffectFinish(Actor akTarget, Actor akCaster)
	StorageUtil.IntListRemove(akTarget, "DebugPackageOverride", myPackage.GetFormID())
	akTarget.EvaluatePackage()
endEvent
Do not release any mods using this! It is only for testing and I will change this later. Right now it works like this: the last package form in the list will override NPC regular package. If you remove the last package from list then the package previous to last will override NPC regular package. If list is empty then no overrides are applied.

 

Things that need to be tested:

If conditions work for the package.

If package fragments are correctly run - on start script, on end script etc.

If there are any other problems or crashes using this.

 

Also if your package finishes then it is not removed from the list automatically! That means package will restart infinitely until you remove it. Good idea would be to have OnPackageEnd fragment that removes itself from actor on finish.

Link to comment

Its specifically this plugin. I'm testing the previous version now to see if it works properly.

 

Edit: yes the previous version works. To be clear for testing I have all mods disabled.  Now whenever the game autosaves it causes a crash when using version 1.2. version 1.1 however does not cause a crash.

Link to comment

There is a very big problem with this plugin right now, I needed to change almost everything and the save format also changed. Any mod authors that use this, hold on with releases for a bit, I will post a fixed version tomorrow. All data of this plugin in saves will be wiped with next version! This means data that is saved with StorageUtil.

Link to comment

I have the same crash on save. Here's log from hdtPhysicsExtention:

hdtPhysicsExtensions

[12/17/13 22:26:39]INFO: Queue OK
[12/17/13 22:26:39]INFO: System run with 8 threads
[12/17/13 22:26:39]INFO: Havok simulated world created.
[12/17/13 22:26:39]INFO: SKSEPlugin_Load
[12/17/13 22:27:57]INFO: Cell changed...
[12/17/13 22:28:00]ERROR: Fatal error occured
[12/17/13 22:28:00]ERROR: Code : 0xc0000005
[12/17/13 22:28:00]ERROR: Flag : 0x00000000
[12/17/13 22:28:00]ERROR: Module : D:\Steam\SteamApps\common\Skyrim\Data\SKSE\Plugins\StorageUtil.dll
[12/17/13 22:28:00]ERROR: Address : 0x7186e3bb
[12/17/13 22:28:00]ERROR: Module Address : 0x71860000
[12/17/13 22:28:00]ERROR: AccessViolation, try to read 0x00000004 failed
[12/17/13 22:28:00]ERROR: ExceptionContent saved in hdtPhysicsExtensions.dump
[12/17/13 22:28:00]ERROR: Plugin is trying to save game
 

 

By the way, when I load a save with female char everything is alright, but when I try new game with male char - crash on first save.

Link to comment

Ok new version. Few things first, all data in StorageUtil module will be erased with new version because I had to change save game saving format and also the old data was broken in many ways. That means if you use a mod that relies heavily on this module you may have to reinstall that mod.

Also to mod authors:

Form saving is now separate from int. Do not use int to save forms, it will not work properly. New usage is very similar but instead of

SetIntValue(akTarget, "abc", akCaster.GetFormID()) ; wrong

use

SetFormValue(akTarget, "abc", akCaster) ; correct

 

Another thing for mod authors, there is a new module ActorUtil which allows overriding packages on actor dynamically from papyrus without using reference aliases. It will override any package added from anywhere else than ActorUtil and as long as package ends or you remove it manually. If you add a never ending package to actor and never remove it then that actor will remain in that package for the rest of their life.

 

This is a stack, that means you can add as many packages as you like. There is also a priority option, value 0 to 100. This would allow you to do something like this:

Package Property myPackage Auto

Package Property myPackage2 Auto

Package Property myPackage3 Auto

Actor akTarget

ActorUtil.AddPackageOverride(akTarget, myPackage, 30)

ActorUtil.AddPackageOverride(akTarget, myPackage2, 30)

ActorUtil.AddPackageOverride(akTarget, myPackage3, 15)

 

now the packages will run in this order:

myPackage2

myPackage

myPackage3

 

Use akTarget.EvaluatePackage() to update package after adding. Package conditions set in CK will work. There is an option to disable package conditions if you enable  flags = 1 in AddPackageOverride.

Link to comment

Yes in the co-save, it will remain in the SKSE save when uninstalled I think. As of version 1.3 the mod will always save at least 4 KB of data, rest depends on how it is used. Every time I try to calculate how much space is used in the save game file to store all this data I reach value less than 50 KB per 1000 integers saved on all different objects which is pretty much the worst case scenario. I can write up the format for saving if you want to calculate yourself.

 

You can use papyrus function to clear all data and make a save game if you think it's too much :P

Link to comment

how did you managed  to hook API?

the only way i found is re-compile skse with _PPAPI macro defined  (absence of macro disables SKSEPapyrusInterface) .

although old plugins working fine, my custom functions return zero all time..

 

Link to comment

You can use SKSE functions to register papyrus implementations, see how they do it for example PapyrusMath.cpp, you only need the VMClassRegistry pointer, I hooked skyrim's function when it is registering papyrus implementations to add mine because SKSE for some reason does not allow us to register via their API.

Link to comment

Since you can alter the package stack on an actor would it be possible to set/remove Associations?
 

HasAssociation()

SpouseProperty.SetAssociation( kActorA, kActorB )
SpouseProperty.SetAssociation( kActorA, kActorC )

if (kActorA.HasAssociation(SpouseProperty))
  ; this is true
endIf

if (kActorA.HasAssociation(SpouseProperty, kActorB))
  ; this is true
endIf

if (kActorA.HasAssociation(SpouseProperty, kActorC))
  ; this is true
endIf

kActorA.RemoveAssociation(SpouseProperty, kActorB)

if (kActorA.HasAssociation(SpouseProperty))
  ; this is true
endIf

if (kActorA.HasAssociation(SpouseProperty, kActorB))
  ; this is false
endIf

if (kActorA.HasAssociation(SpouseProperty, kActorC))
  ; this is true
endIf

SpouseProperty.RemoveAssociation( kActorA )

if (kActorA.HasAssociation(SpouseProperty))
  ; this is false
endIf

if (kActorA.HasAssociation(SpouseProperty, kActorB))
  ; this is false
endIf

if (kActorA.HasAssociation(SpouseProperty, kActorC))
  ; this is false
endIf

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...