Skip to content

Papyrus API Overview

SilverIce edited this page Oct 26, 2016 · 17 revisions

API overview

JContainers API declared by the following scripts:

  • Instanceable containers, an objects - JArray, JMap, JFormMap, JIntMap. You can create multiple instances of arrays, maps, form-maps (the same way like you can have multiple arrays, strings in Papyrus)
  • JValue - declares common functionality shared by JArray, JMap, JFormMap, JIntMap. For instance, all containers are JSON-serializable, thus JValue implements readFromFile and writeToFile functionp.
  • Global containers - JDB, JFormDB. Unlike instanceable containers, there is only one instance of JDB, JFormDB in game, and you don't have to create them manually
  • Utility functionality - JContainers

Persistence

Keep in mind that everything you operate on in JContainers is persistent, stored in SKSE co-save file. How JContainers deals with unused objects, garbage? See Lifetime Management

Instanceable containers

  • Any instanceable object in JContainers (even JDB) have unique number assigned, i.e. identifier
  • Identifier is a plain integer number, which ranges from -2^31 to 2^31
  • Zero identifier points to a non-existing object
  • Most of JContainers functions accept identifier as fist argument

To use any instanceable object, you must first create (instantiate) it with the object function or retrieve it from somewhere:

int arrayId = JArray.object()
int mapId = JMap.object()

Any function that returns an object actually returns its identifier. An identifier is a unique number that ranges from -2^31 to 2^31. Zero identifier points to a non-existing object. Each container is distinguished by this identifier.

Once created, you may put data in the array, the map:

JArray.addStr(arrayId, "it’s me")
JArray.addForm(arrayId, GetTargetActor())
JMap.setInt(mapId, "health", GetTargetActor().getAV("health"))

And read the data back:

string text = JArray.getStr(arrayId, 0)
form actor = JArray.getForm(arrayId, 1)
int health = JMap.getInt(mapId, "health")

JArray

Ordered collection (array) of values. It is dynamically resizeable, and can store any number of values of any combination of types.

JMap, JFormMap and JIntMap

All of them are associative containers (sets of unique keys and values where each key associated with one value). Each key must be unique within a given container. In a JMap, a key is a string, while a JFormMap key is a form (a form is any actor, item, quest, spell - almost everything in Skyrim). In a JIntMap key is, obviously, an integer value.

int map = JMap.object()
JMap.setForm(map, "me", GetTargetActor())
form actor = JMap.getForm(map, "me")

JValue

An interface that shows what functionality JArray, JMap and JFormMap share. So each of them can be emptied, serialized and deserialized to/from JSON and more.

int array = JArray.object()
int map = JMap.object()
-- equivalent ways to do same things.
-- all count functions return zero as new containers are empty
JValue.count(array) == JArray.count(array) == JValue.count(map)
-- write container content into file:
JValue.writeToFile(map, "map.txt")
JValue.writeToFile(array, "array.txt")

JDB

This is a global entry point, single, global instance of JMap container - you can put information in it under a string key "yourKey", and then you can access to it from any script in the game. There is only one JDB in game, so each time you access it you access that one, single JDB. It is an associative container like JMap (it is, in fact, JMap internally), but the script interface looks slightly different.

Typical JDB usage would involve:

  • Put a data into JDB using following JDB functions: setObj, solve*Setter functions
  • Read the data back using solve* functions
JDB.solveIntSetter(".vMYC.characterCount", 11, true)
JDB.solveObjSetter(".vMYC.characters", JArray.object(), true)
int count = JDB.solveInt(".vMYC.characterCount")
  • Un-link your root objects from JDB when uninstalling your mod.
JDB.setObj("vMYC", 0)

As you noticed, path-string always looks like .rootKey.restOfPath and may also contain [N] elements. See Feature Overview#path resolving for more info about string paths and solve*, solve*Setter functions.

JDB.solveIntSetter(".vMYC.characterCount", 11, true)
JDB.solveObjSetter(".vMYC.characters", JArray.object(), true)
int count = JDB.solveInt(".vMYC.characterCount")

int jcharacters = JDB.solveObj(".vMYC.characters")
JArray.addStr(jcharacters, "Jack")
-- firstName is "Jack"
string firstname = JDB.solveStr(".vMYC.characters[0]")

After the code above will be executed, JDB structure will look like this:

{
  "vMYC": {
    "characterCount": 11,
    "characters": ["Jack"]
  }
}

This is desired structure for any mod, it's best to keep your data inside single namespace (e.g. vMYC).

Important

Root key name should be unique, so it won't conflict with the rest of JDB root keys and JFormDB storage names, used by other modders.

JFormDB

Provides a convenient way to associate values with a form. You may find it looking like a mix of JMap and JDB - like JDB, there is only one JFormDB in game, and it's associative container, like JMap. Also it supports Feature Overview#path resolving. To store or retrieve value form fKey and string path must be passed:

-- store values
form me = GetTargetActor()
JFormDB.setFlt(me, ".yourModFormStorage.valueKey", 10)
JFormDB.setStr(me, ".yourModFormStorage.anotherValueKey", "name")
JFormDB.solveStrSetter(me, ".yourModFormStorage.parentKey.name", me.GetName(), True)
JFormDB.solveFormSetter(me, ".yourModFormStorage.parentKey.me", me, True)
-- and retrieve values
float value = JFormDB.getFlt(me, ".yourModFormStorage.valueKey")

String path must always consist of two parts: formStorageName plus valueKey or valuePath, e.g. .formStorageName.valueKey or .formStorageName.path.to.value. valueKey is a key used to retrieve value or create (valueKey, value) pair for a form. valuePath is a path used for path resolving.

Important

Form storage name should be unique, so it won't conflict with the rest of JDB root keys and JFormDB storage names, used by other modders.

Form storage is just an instance of JFormMap containing (formKey, Entry) pairs, linked to JDB (JDB contains (storage-name, Form-storage) pairs). And Entry is just an instance of JMap. The fact that form storage is a separate storage, not single container that is shared among many mods, makes it possible to delete it without any risk to delete another mod's form storage data:

-- @jstorage is JFormMap container
int jstorage = JDB.solveObj(".formStorageName")
-- Will unlink (I'd lied if I'd say just 'delete') "yourModFormStorage" from JDB,
-- so this container isn't accessible via JFormDB or JDB interface anymore
JDB.setObj("formStorageName", 0)

-- Something like this will create NEW form storage, since old one isn't accessible
JFormDB.setFlt(me, ".formStorageName.valueKey", 10)

Slightly more advanced usage:

Form me = akActor

-- Will destroy everything associated with 'me' form in "yourModFormStorage" storage
JFormDB.setEntry("yourModFormStorage", me, 0)
 
-- Custom entry type
JFormDB.setEntry("yourModFormStorage", me, JArray.object())

-- Dump your form-map storage
JValue.writeToFile(JDB.solveObj(".yourModFormStorage"), "file.json")
-- Read it back
JDB.solveObjSetter(".yourModFormStorage", JValue.readFromFile("file.json"), true)

-- Dump individual entry
JValue.writeToFile(JFormDB.findEntry("yourModFormStorage", me), "tototo.json")

JFormDB is actually agglomerate of multiple objects. See why:

JFormDB.setFlt(me, ".yourModFormStorage.valueKey", 10)
-- Line above equals to the following pseudocode:
JDB.yourModFormStorage[me].valueKey = 10

JFormDB.setEntry("yourModFormStorage", me, 0)
-- Equals to:
JDB.yourModFormStorage[me] = None

JFormDB.solveStrSetter(me, ".yourModFormStorage.parentKey.name", me.GetName(), True)
-- Equals to:
JDB.yourModFormStorage[me].parentKey.name = me.GetName()

Clone this wiki locally