Recommendation: GSI PUMA (v1)
Structured use of prim parameters (description and hovertext) and parcel descriptions to store small key-value pairs ("markers") passively readable from other scripts
The Global Scripting Institute (GSI) is an informal organization of Second Life® users that design and test standards for efficient, flexible, and readable scripts in Second Life. "Second Life®" and "Second Life Grid™" are trademarks of Linden Research, Inc., d/b/a Linden Lab. The Global Scripting Institute and its catalog are not affiliated with or sponsored by Linden Research.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
A Passive Universal Marker Array (PUMA) is a standardized method of storing very small key-value pairs in prim descriptions and hovertext. These values can be read passively by any other Lua or Linden Scripting Language (LSL) script in the region very quickly.
Lua-style syntax is used throughout this document, but all specifications also apply to the equivalent LSL functions and event callbacks.
PUMA stores a list of key-values in a PUMA string, similar to a strided list. A PUMA string is defined as a string of tilde-separated values:
key1~*value1~key2~*value2~...
Keys and values MUST contain only printable ASCII characters excluding tilde (~) and backtick (`) due to the format's design, and pipe (|) due to Second Life limitations.
Keys MAY be defined however the scripter desires, but SHOULD still be unique that potential conflicts will not occur. Between 3 and 8 characters is suggested. A key MUST NOT exceed 16 characters. Each key in the string MUST be unique.
Values MAY be defined however the scripter desires. The first character (* in the above example) MUST be used to signal the value's encoding. Possible encodings are:
a: raw printable ASCII (note that this may still be some other ASCII-safe type that was typecast to a string)b: base64-encoded stringz: "base92"-encoded string (all possible characters)*: undefined (may be any value, any type)When a PUMA string is stored in a prim, it is stored serially in the prim's description and hovertext, in that order. Each field used MUST be preceded with !~; if it is not, it is ignored when read. For example, a PUMA string (up to 377 characters) could be written as:
Description: !~key1~*value1~key2~*value2~...~key13~*value13~ke
Hovertext: !~y14~*value14~key15~*value15
Or, a shorter string (up to 252 characters) could be written only to the hovertext:
Description: Anything you want
Hovertext: !~key1~*value1~key2~*value2~key3~*value3~key4~*value4~key5~*value5
Or, an even shorter string (up to 125 characters) could be written only to the description:
Description: !~key1~*value1~key2~*value2~key3~*value3
Hovertext: Anything you want
Scripts wishing to read the prim's PUMA data MUST read both fields, stripping the !~ header from each. If a field does not have a !~ header, it MUST be skipped.
Scripts wishing to write the prim's PUMA data MUST write to each of these three fields in order, adding the !~ header to each, and adding the ` terminator to the final used field. If the PUMA string is not long enough to require all three fields, the script MAY erase the remaining fields or leave them unmodified (they will be ignored when read). The script MAY skip any fields as directed by the scripter or end-user; for example, PUMA data generally SHOULD NOT be written to an object's name unless absolutely necessary.
PUMA strings may also be stored in parcel descriptions. Unlike such strings stored in prims, the !~ header may be anywhere in the parcel description; all text after it will be treated as a PUMA string. For example, a description could be:
Paul's cool house!
Ignore this: !~key1~*value1~key2~*value2
Note that parcel descriptions cannot be set via script, so this option should be used sparingly, since it requires that the end-user modify the parcel description manually.
This recommendation does not define any standardized method of parsing and modifying PUMA data. Scripts MAY parse and modify values as they see fit, but MUST load and append to existing data unless directed otherwise by the end-user. Data is not guaranteed to be safe from modification, and can be overwritten or deleted at any time by any script in the linkset, including unexpected ones that overwrite fields used by PUMA.
Scripts intending to modify the PUMA string SHOULD minimize the time between reading and writing the values to minimize race conditions.
How much data can be stored?
Each key-value pair uses 3 bytes + the byte count of the key and encoded value.
The maximum capacity depends on which of the possible fields are used:
!~ headerCan multiple scripts use the same prim for PUMA data?
Yes, with the knowledge that scripts may encounter race conditions when updating the data at the same time, and there is no enforcement on the amount of bytes used per script or pair. Generally, PUMA data should be refreshed frequently and never relied on locally - consider LNX instead for local data storage.
Why not use CSV?
The built-in LSL CSV functions perform no escaping, and Lua does not have any built-in CSV functions. The separator and terminator were defined as ~ and `, respectively, because it is more likely that end-users will use commas than tildes and backticks in user-supplied data that may be interpreted as a PUMA value. This avoids needing to store values in base64, which reduces the bytes of text needed to be stored.
Why not use newline-separated values (\n) or pipe-separated values (|)?
The newline character is not part of printable ASCII, so cannot be used in prim names and descriptions, and the pipe character is specifically prohibited in prim names and descriptions.
CLEP was authored by Nelson Jenkins on behalf of GSI.