• Announcements

    • Ashal

      SITE MOVED - IN READ ONLY MODE   12/08/2015

      Please use http://www.loverslab.com moving forward. Site has been restored to a previous version, and this one placed into a read-only mode. This is available for a limited time so users may reference/copy content that has been lost in the transition. This will no longer be accessible by December 22nd, 2015.
481 posts in this topic

PapyrusUtil

View / Download File

1. Description
2. Examples
3. Requirements
4. Installing
5. Uninstalling
6. Updating
7. Compatibility & issues
8. Credits
9. Changelog

 

 

 

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 1.7.3 latest version: http://skse.silverlock.org/
 

 
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 - refactoring of original plugin's source code
h38fh2mf - original version and idea
SKSE team - for making this plugin possible
milzschnitte - for suggestions
 
9. Changelog
 
3.1 - 09/01/2015

  • Fixed a bug causing CTD during save load for some users.
  • Added Count<type>Prefix() to StorageUtil and JsonUtil - counts the number of keys that start with string
  • Added Clear<type>Prefix() to StorageUtil - removes any values with keys that start with string
  • Added Pluck<type>() to StorageUtil - gets a value and returns it, then removes it from storage.
  • Added Shift<type>List() to StorageUtil - gets the first value of a list and then removes it from that list.
  • Added Pop<type>List() to StorageUtil - gets the last value of a list and then removes it from that list.

 

 

3.0 - 08/21/2015

  • REQUIRES SKSE 1.7.3
  • StorageUtil & JsonUtil Int/Float/String/FormListToArray()
  • Various new utility and array functions in PapyrusUtil.psc
  • Various other new functions I can't remember, mostly related to dealing with or returning arrays
  • Fixed various crash related bugs
  • Improved performance for many functions
  • REMOVED MiscUtil.WriteToFile(),ReadFromFile(),ExecuteBat() - Functions were largely unused, a security risk, and better accomplished by other means.


2.8 - 10/03/2014

  • Fixed critical bug causing StringListRemove to do exactly the opposite of what you want it to do
  • Fixed crash to desktop issue some users have experienced when plugin loads an external json files for reading
  • Added papyrus array initializing functions to PapyrusUtil.psc


2.7 - 09/09/2014

  • Added back package override saving.
  • Added AdjustInt/FloatValue() and Int/FloatListAdjust() functions to StorageUtil and JsonUtil, shortcut function for adjusting existing values +/- a given amount
  • Added a ClearAll() function to JsonUtil for emptying out an external json files contents.
  • Cleaned up various native functions to better check for proper arguments being passed to prevent potential crashes.


2.6 - 08/11/2014

  • Fixed bug causing crash/freeze when attempting to load a nonexistent external file.


2.5 - 08/08/2014

  • REQUIRES SKSE 1.7.1
  • Rewrite of plugin source code
  • Added new JsonUtil script
  • ListSlice() function for copying list into a Papyrus array
  • ListCopy() function for copying a Papyrus array into a list
  • ListResize() function for changing the length of list
  • Various other bug fixes and minor new functions

 

 

 

Spoiler


 

2.3 - 04/06/2014

  • Used different method for thread safety, no more dependencies.


2.2 - 14/04/2014

  • Compiled plugin in VS 2008 environment.


2.1 - 22/03/2014

  • Attempted to fix problem with JSON export when selecting specific keys to export.
  • Added option to insert values in lists, didn't add this option to file lists yet.
  • Added option to sort values in lists, didn't add to file lists yet.
  • Added optional debug mode command (enables until you exit game or disable again). See at the end of StorageUtil.psc, currently in debug mode StorageUtil section size (bytes) in save game is reported to console when saving or loading game.


2.0 - ??/??/2014

  • I don't know what happened here I was sure there was version 2.0 somewhere.


1.9 - 11/01/2014

  • Fixed few bugs with string list comparison that broke find and add unique.
  • Added option to replace animations on objects.
  • Added option to import and export selected data in JSON format.
  • Please test these new things extensively before releasing a mod using them.


1.8 - 02/01/2014

  • Fixed problems caused by space in mod name.


1.7 - 01/01/2014

  • Fixed bug where starting new game broke everything. Important update.


1.6 - 31/12/2013

  • Fixed bug where there were two kinds of none when comparing - invalid form and none passed from papyrus.


1.5 - 28/12/2013

  • Fixed a bug that could cause all data to be lost. Important to update to this version.


1.4 - 23/12/2013

  • Renamed project.
  • Added command to get race editor ID.
  • Not really necessary to update to this version.


1.3 - 17/12/2013

  • Saving forms is now separate from integer saving! Do not use integers to save forms, it will not work properly.
  • Fixed crashes that some people were getting when saving game.
  • Added a new module ActorUtil, it allows overriding packages on actors. If you add a package that never ends you have to manually remove it. Packages that finish (e.g. traveling) should be removed automatically.
  • Save games made before version 1.3 will lose all data from StorageUtil module.


1.2 - 16/12/2013

  • Added write to file, read from file functions.
  • Added execute batch file in console.
  • Added toggle menus on / off command.
  • Added experimental package stack on actors. Do not release any mod using this yet!


1.1 - 14/12/2013

  • Fixed almost half the stuff not working because of unsigned mistake.
  • Added a new module: MiscUtil, contains TFC, set TFC speed, print console and get node rotation.


1.0 - 07/12/2013
Initial release

 


45

Share this post


Link to post

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


0

Share this post


Link to post

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.


1

Share this post


Link to post

Very interesting.  Does it allow for storing data on unique inventory objects?


0

Share this post


Link to post

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.


0

Share this post


Link to post

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.


0

Share this post


Link to post

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.


1

Share this post


Link to post

Updated to 1.1

There was a big mistake in last version that made half the stuff not work properly!

Added new module: MiscUtil, check the psc file for which commands are added.

Save file format was not changed, you can just overwrite all files.

0

Share this post


Link to post

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 AutoEvent OnEffectStart(Actor akTarget, Actor akCaster)	StorageUtil.IntListAdd(akTarget, "DebugPackageOverride", myPackage.GetFormID())	akTarget.EvaluatePackage()endEventEvent 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.

0

Share this post


Link to post

This seems to cause the game to crash whenever it saves.


0

Share this post


Link to post

I haven't changed saving method since 1.0 and many people are using it (including me), this is the first I'm hearing of it. I think your saving problem might be caused by something else.


0

Share this post


Link to post

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.


0

Share this post


Link to post

Alright, I'll go over code one more time and change few things, I can't release fix right now because I'm in the middle of adding new feature but I will post something tomorrow probably.


0

Share this post


Link to post

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.


1

Share this post


Link to post

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.


0

Share this post


Link to post

Ok thanks for the log. This will be fixed in next update. I need to test few more things then it will be ready.


0

Share this post


Link to post

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.

0

Share this post


Link to post

Im assuming that all of this is stored in skse co-save, right? what will happen to skse save when this mod is simply deleted?


skse save bloat?


0

Share this post


Link to post

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

0

Share this post


Link to post

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


 


0

Share this post


Link to post

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.


1

Share this post


Link to post

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 trueendIfif (kActorA.HasAssociation(SpouseProperty, kActorB))  ; this is trueendIfif (kActorA.HasAssociation(SpouseProperty, kActorC))  ; this is trueendIfkActorA.RemoveAssociation(SpouseProperty, kActorB)if (kActorA.HasAssociation(SpouseProperty))  ; this is trueendIfif (kActorA.HasAssociation(SpouseProperty, kActorB))  ; this is falseendIfif (kActorA.HasAssociation(SpouseProperty, kActorC))  ; this is trueendIfSpouseProperty.RemoveAssociation( kActorA )if (kActorA.HasAssociation(SpouseProperty))  ; this is falseendIfif (kActorA.HasAssociation(SpouseProperty, kActorB))  ; this is falseendIfif (kActorA.HasAssociation(SpouseProperty, kActorC))  ; this is falseendIf
0

Share this post


Link to post

I suppose a GetAssociations() would be useful as well



Actor[] Spouses = kActorA.GetAssociation(SpouseProperty)

0

Share this post


Link to post

What would be the difference of using this vs. using just forms to save same data?


0

Share this post


Link to post