LSL Protocol/RestrainedLifeAPI

From Second Life Wiki
Jump to navigation Jump to search

Restrained Life viewer v1.14 Specification

By Marine Kelley


Audience

This document is aimed at people who wish to modify or create their own LSL scripts to use the functionalities of the RestrainedLife viewer. It does not, however, explains LSL concepts such as messages and events, nor universal concepts such as UUIDs.

Introduction

The RestrainedLife viewer is meant to execute certain behaviours when receiving special messages from scripts in-world. These messages are mostly calls to the llOwnerSay() LSL function.

Architecture

The RestrainedLife viewer parses every llOwnerSay call and executes every command beginning with '@'. For instance, a call to llOwnerSay ("@detach=n") will tell the viewer that the object sending the message cannot be detached until further notice.

Version 1.10 and above are able to parse multiple commands in one message, in order to avoid spamming the user who is not using this viewer. The format of a message is therefore :

@<behaviour1>[:option1]=<param1>,<behaviour2>[:option2]=<param2>,...,<behaviourN>[:optionN]=<paramN>

Note that there is only one '@' sign, placed at the beginning of the message. The viewer understands this as "this whole llOwnerSay() is actually a command to execute", so no need to put a '@' before every command, it wouldn't even work. If at least one command fails (typo), the viewer says "... fails command : ... " and mentions it all. However correct commands are parsed and executed, only incorrect ones are discarded.

Warning : These behaviours are not persistent between sessions, and an object changes UUID everytime it rezzes. This means the object must resend its "status" (undetachable, preventing IMs...) in the on_rez () event as well as when it changes.

List of commands

Note : These commands are not case-sensitive but are spacing-sensitive. In other words, "@detach = n" will not work.

  • Automated version checking : "@version=<channel_number>"

Implemented in v1.0b Makes the viewer automatically say its version immediately on the chat channel number <channel_number> that the script can listen to. Always use a positive integer. Remember that regular viewers do not answer anything at all so remove the listener after a timeout.

Warning : when logging in, the on_rez event of all the attachments occurs way before the avatar can actually send chat messages (about half the way through the login progress bar). This means the timeout should be long enough, like 30 seconds to one minute in order to receive the automatic reply from the viewer.

  • Manual version checking : "@version"

Implemented in v1.0a This command must be sent in IM from an avatar to the user (will not work from objects). The viewer automatically answers its version to the sender in IM, but neither the message nor the answer appears in the user's IM window, so it's totally stealthy.

  • Render an object detachable/nondetachable : "@detach=<y/n>"

Implemented in v1.0a When called with the "n" option, the object sending this message (which must be an attachment) will be made nondetachable. It can be detached again when the "y" option is called.

  • Allow/prevent sending chat messages : "@sendchat=<y/n>"

Implemented in v1.0b When prevented, everything typed on channel 0 will be discarded. However, emotes and messages beginning with a slash ('/') will go through, truncated to strings of 30 and 15 characters long respectively (likely to change later). Messages with special signs like ()"-*=_^ are prohibited, and will be discarded. When a period ('.') is present, the rest of the message is discarded.

  • Remove/add an exception to the emote truncation above : "@emote=<rem/add>"

Implemented in v1.01 When adding this exception, the emotes are not truncated anymore (however, special signs will still discard the message).

  • Allow/prevent sending instant messages : "@sendim=<y/n>"

Implemented in v1.0b When prevented, everything typed in IM will be discarded and a bogus message will be sent to the receiver instead.

  • Remove/add exceptions to the instant message sending prevention : "@sendim:<UUID>=<rem/add>"

Implemented in v1.01 When adding an exception, the user can send IMs to the receiver whose UUID is specified in the command. This overrides the prevention for this avatar only (there is no limit to the number of exceptions), don't forget to remove it when it becomes obsolete.

  • Allow/prevent receiving chat messages : "@recvchat=<y/n>"

Implemented in v1.0b When prevented, everything heard in public chat will be discarded.

  • Remove/add exceptions to the chat message receiving prevention : "@recvchat:<UUID>=<rem/add>"

Implemented in v1.01 When adding an exception, the user can hear chat messages from the sender whose UUID is specified in the command. This overrides the prevention for this avatar only (there is no limit to the number of exceptions), don't forget to remove it when it becomes obsolete.

  • Allow/prevent receiving instant messages : "@recvim=<y/n>"

Implemented in v1.0b When prevented, every incoming IM will be discarded and the sender will be notified that the user cannot read them.

  • Remove/add exceptions to the chat message receiving prevention : "@recvim:<UUID>=<rem/add>"

Implemented in v1.01 When adding an exception, the user can read instant messages from the sender whose UUID is specified in the command. This overrides the prevention for this avatar only (there is no limit to the number of exceptions), don't forget to remove it when it becomes obsolete.

  • Allow/prevent teleporting to a landmark : "@tplm=<y/n>"

Implemented in v1.0 When prevented, the user cannot use a landmark, pick or any other preset location to teleport there.

  • Allow/prevent teleporting to a location : "@tploc=<y/n>"

Implemented in v1.0 When prevented, the user cannot use teleport to a coordinate by using the map and such.

  • Allow/prevent teleporting by a friend : "@tplure=<y/n>"

Implemented in v1.0 When prevented, the user automatically discards any teleport offer, and the avatar who initiated the offer is notified.

  • Remove/add exceptions to the friend teleport prevention : "@tplure:<UUID>=<rem/add>"

Implemented in v1.0 When adding an exception, the user can be teleported by the avatar whose UUID is specified in the command. This overrides the prevention for this avatar only (there is no limit to the number of exceptions), don't forget to remove it when it becomes obsolete.

  • Unlimit/limit sit-tp : "@sittp=<y/n>"

Implemented in v1.0 When limited, the avatar cannot sit on a prim unless it is closer than 1.5 m. This allows cages to be secure, preventing the avatar from warping its position through the walls (unless the prim is too close).

  • Clear all the rules tied to an object : "@clear"

Implemented in v1.0a, but working only since v1.04a This command clears all the restrictions and exceptions tied to a particular UUID.

  • Clear a subset of the rules tied to an object : "@clear=<string>"

Implemented in v1.0a, but working only since v1.04a This command clears all the restrictions and exceptions tied to a particular UUID which name contains <string>. A good example would be "@clear=tp" which clears all the teleport restrictions and exceptions tied to that object, whereas "@clear=tplure:" would only clear the exceptions to the "teleport-by-friend" restriction

  • Allow/prevent editing objects : "@edit=<y/n>"

Implemented in v1.03 When prevented from editing and opening objects, the Build & Edit window will refuse to open.

  • Allow/prevent rezzing inventory : "@rez=<y/n>"

Implemented in v1.03 When prevented from rezzing stuff, creating and deleting objects, drag-dropping from inventory and dropping attachments will fail.

  • Allow/prevent wearing clothes : @addoutfit[:<part>]=<y/n>

Implemented in v1.10, added skin hair and eyes in v1.10.1 Where part is :

gloves|jacket|pants|shirt|shoes|skirt|socks|underpants|undershirt|skin|eyes|hair|shape

If part is not specified, prevents from wearing anything beyond what the avatar is already wearing.

  • Allow/prevent removing clothes : @remoutfit[:<part>]=<y/n> (underpants and underwear are kept for teens)

Implemented in v1.10, added skin hair and eyes in v1.10.1 Where part is :

gloves|jacket|pants|shirt|shoes|skirt|socks|underpants|undershirt|skin|eyes|hair|shape

If part is not specified, prevents from removing anything in what the avatar is wearing.

  • Force removing clothes : @remoutfit[:<part>]=force (*) (teens can't be forced to remove underpants and underwear)

Implemented in v1.10 Where part is :

gloves|jacket|pants|shirt|shoes|skirt|socks|underpants|undershirt|skin|eyes|hair|shape

If part is not specified, removes everything.

  • Force removing attachments : @detach[:attachpt]=force (*)

Implemented in v1.10 Where part is :

chest|skull|left shoulder|right shoulder|left hand|right hand|left foot|right foot|spine|
pelvis|mouth|chin|left ear|right ear|left eyeball|right eyeball|nose|r upper arm|r forearm|
l upper arm|l forearm|right hip|r upper leg|r lower leg|left hip|l upper leg|l lower leg|stomach|left pec|
right pec|center 2|top right|top|top left|center|bottom left|bottom|bottom right

If part is not specified, removes everything.

  • Get the list of worn clothes : @getoutfit[:part]=<channel_number>

Implemented in v1.10, added skin hair and eyes in v1.10.1 Makes the viewer automatically answer the current occupation of clothes layers as a list of 0s (empty) and 1s (occupied) immediately on the chat channel number <channel_number> that the script can listen to. Always use a positive integer. Remember that regular viewers do not answer anything at all so remove the listener after a timeout.

The list of 0s and 1s corresponds to :

gloves,jacket,pants,shirt,shoes,skirt,socks,underpants,undershirt,skin,eyes,hair,shape

in that order.

If a part is specified, answers a single 0 (empty) or 1 (occupied) corresponding to the part.

Ex 1 : @getoutfit=2222 => "0011000111" => avatar is wearing pants, shirt, underpants and undershirt, and of course a skin.
Ex 2 : @getoutfit:socks=2222 => "0" => the avatar is not wearing socks.
  • Get the list of worn attachments : @getattach[:attachpt]=<channel_number>

Implemented in v1.10 Makes the viewer automatically answer the current occupation of attachment points as a list of 0s (empty) and 1s (occupied) immediately on the chat channel number <channel_number> that the script can listen to. Always use a positive integer. Remember that regular viewers do not answer anything at all so remove the listener after a timeout.

The list of 0s and 1s corresponds to :

none,chest,skull,left shoulder,right shoulder,left hand,right hand,left foot,right foot,spine,
pelvis,mouth,chin,left ear,right ear,left eyeball,right eyeball,nose,r upper arm,r forearm,
l upper arm,l forearm,right hip,r upper leg,r lower leg,left hip,l upper leg,l lower leg,stomach,left pec,
right pec,center 2,top right,top,top left,center,bottom left,bottom,bottom right

in that order.

If an attachment point is specified, answers a single 0 (empty) or 1 (occupied) corresponding to the point.

Ex 1 : @getattach=2222 => "011000011010000000000000100100000000101" => avatar is wearing attachments on 
chest, skull, left and right foot, pelvis, l and r lower leg, HUD bottom left and HUD bottom right.
Ex 2 : @getattach:chest=2222 => "1" => avatar is wearing something on the chest.

Note : The first character ("none") is always '0', so the index of each attach point in the string is exactly equal to the corresponding ATTACH_* macro in LSL. For instance, the index 9 in the string is ATTACH_BACK (which means "spine"). Remember the indices start at zero.

  • Allow/prevent using inventory : @showinv=<y/n>

Implemented in v1.10 Forces the inventory windows to close and stay closed.

  • Allow/prevent reading notecards : @viewnote=<y/n>

Implemented in v1.10 Prevents from opening notecards but does not close the ones already open.

  • Allow/prevent standing up : @unsit=<y/n>

Implemented in v1.10 Hides the Stand up button.

  • Force sit on an object : @sit:<UUID>=force (*)

Implemented in v1.10 Does not work if the user is prevented from sit-tping and further than 1.5 meters away, or when prevented from unsitting.

  • Force unsit : @unsit=force (*)

Implemented in v1.10 Self-explanatory but for some reason it randomly fails, so don't rely on it for now. Further testing is needed.

  • Allow/prevent using any chat channel but certain channels : @sendchannel[:<channel>]=<y/n>

Implemented in v1.10 Complimentary of @sendchat, this command prevents the user from sending messages on non-public channels. If channel is specified, it becomes an exception to the aforementioned restriction. It does not prevent the viewer automatic replies like @version=nnnn, @getstatus=nnnn etc.

  • Get the list of restrictions the avatar is currently submitted to : @getstatus[:<part_of_rule>]=<channel>

Implemented in v1.10 Makes the viewer automatically answer the list of rules the avatar is currently under, only for the object containing the script issuing that command, immediately on the chat channel number <channel_number> that the script can listen to. Always use a positive integer. Remember that regular viewers do not answer anything at all so remove the listener after a timeout. The answer is a list of rules, separated by slashes ('/').

This command is useful for people who write scripts that may conflict with other scripts in the same object (for instance : third-party plugins). Conflict do not occur in different objects, that's why this command only applies to the object calling it.

<part_of_rule> is the name of a rule, or a part of it, useful if the script only needs to know about a certain restriction.

Example : If the avatar is under tploc, tplure, tplm and sittp, here is what the script would get :
@getstatus=2222  =>  tploc/tplure/tplm/sittp
@getstatus:sittp=2222  =>  sittp
@getstatus:tpl=2222  =>  tploc/tplure/tplm  (because "tpl" is part of "tploc", "tplure" and "tplm" but not "sittp")
  • Get the list of shared folders in the avatar's inventory : @getinv[:folder1/.../folderN]=<channel_number>

Implemented in v1.11, added sub-folders in v1.13 Makes the viewer automatically answer the list of folders contained into the folder named "#RLV" (if it exists), immediately on the chat channel number <channel_number> that the script can listen to. If folders are specified, it will give the list of sub-folders contained into the folder located at that path instead of the shared root (example : "@getinv:Restraints/Leather cuffs/Arms=2222"). Always use a positive integer. Remember that regular viewers do not answer anything at all so remove the listener after a timeout. The answer is a list of names, separated by commas (',').

  • Get the path to a shared folder by giving a search criterion : @findfolder:part1[&&...&&partN]=<channel_number>

Implemented in v1.13.1 Makes the viewer automatically answer the path to the first shared folder which name contains <part1> and <part2> and ... and <partN>, immediately on the chat channel number <channel_number> that the script can listen to. The search is in depth first, notice the separator which is "&&" like "and". Always use a positive integer. Remember that regular viewers do not answer anything at all so remove the listener after a timeout. It does not take disabled folders into account (folders which name begins with a dot '.'). The answer is a list of folders, separated by slashes ('/').

  • Force attach items contained inside a shared folder : @attach:<folder_name>=force (*)

Implemented in v1.11 Forces the viewer to attach every object and wear every piece of clothing contained inside <folder_name> (which must be directly under "#RLV"). Objects names must contain the name of their target attachment point or they won't be attached.

Attachment point names are the same as the ones contained into the Attach To submenu : "skull", "chest", "l forearm"...

  • Force detach items contained inside a shared folder : @detach:<folder_name>=force (*)

Implemented in v1.11 Forces the viewer to detach every object and unwear every piece of clothing contained inside <folder_name>(which must be directly under "#RLV"). If "@detach" is used with an attachment point name (skull, pelvis... see above), it takes priority over this way of detaching since it is the same command.

  • Allow/prevent touching objects located further than 1.5 meters away from the avatar : @fartouch=<y/n>

Implemented in v1.11 When prevented, the avatar is unable to touch/grab objects from more than 1.5 m away, this command makes restraints more realistic since the avatar litterally has to press against the object in order to click on it.

  • Allow/prevent viewing the world map : @showworldmap=<y/n>

Implemented in v1.11 When prevented, the avatar is unable to view the world map, and it closes if it is open when the restriction becomes active.

  • Allow/prevent viewing the mini map : @showminimap=<y/n>

Implemented in v1.11 When prevented, the avatar is unable to view the mini map, and it closes if it is open when the restriction becomes active.

  • Allow/prevent knowing the current location : @showloc=<y/n>

Implemented in v1.12 When prevented, the user is unable to know where they are : the world map is hidden, the parcel and region name on the top menubar are hidden, they can't create landmarks, nor buy the land, nor see what land they have just left after a teleport, nor see the location in the About box, and even system and object messages are obfuscated if they contain the name of the region and/or the name of the parcel. However, llOwnerSay calls are not obfuscated so radars will still work (and RL commands as well).

  • Force-Teleport the user : @tpto:X/Y/Z=force (*)

Implemented in v1.12 This command forces the avatar to teleport to the indicated coordinates. Note that these coordinates are always global, hence the script that calls this command will not be trivial. Moreso, if the destination contains a telehub or a landing point, the user will land there instead of the desired point. This is a SL limitation. Also keep in mind that @tpto is inhibited by @tploc=n.

Here is a sample code to call that command properly :

<lsl>

// FORCE TELEPORT EXAMPLE // Listens on channel 4 for local coordinates and a sim name // and tells your viewer to teleport you there. // // By Marine Kelley 2008-08-26 // RLV version required : 1.12 and above // // HOW TO USE : // * Create a script inside a box // * Overwrite the contents of the script with this one // * Wear the box // * Say the destination coords Region/X/Y/Z on channel 4 : // Example : /4 Help Island Public/128/128/50

key kRequestHandle; // UUID of the dataserver request vector vLocalPos; // local position extracted from the

Init () {

 kRequestHandle = NULL_KEY;
 llListen (4, "", llGetOwner (), "");

}


default {

 state_entry () {
   Init ();
 }
 
 on_rez(integer start_param) {
   Init ();
 }
 
 listen(integer channel, string name, key id, string message) {
   list tokens = llParseString2List (message, ["/"], []);
   integer L = llGetListLength (tokens);
   if (L==4) {
     // Extract local X, Y and Z
     vLocalPos.x = llList2Float (tokens, 1);
     vLocalPos.y = llList2Float (tokens, 2);
     vLocalPos.z = llList2Float (tokens, 3);
     // Request info about the sim
     kRequestHandle=llRequestSimulatorData (llList2String (tokens, 0), DATA_SIM_POS);
   }
 }
 
 dataserver(key queryid, string data) {
   if (queryid == kRequestHandle) {
     // Parse the dataserver response (it is a vector cast to a string)
     list tokens = llParseString2List (data, ["<", ",", ">"], []);
     string pos_str = "";
     vector global_pos;
     // The coordinates given by the dataserver are the ones of the
     // South-West corner of this sim
     // => offset with the specified local coordinates
     global_pos.x = llList2Float (tokens, 0);
     global_pos.y = llList2Float (tokens, 1);
     global_pos.z = llList2Float (tokens, 2);
     global_pos += vLocalPos;
     // Build the command
     pos_str =      (string)((integer)global_pos.x)
               +"/"+(string)((integer)global_pos.y)
               +"/"+(string)((integer)global_pos.z);
     llOwnerSay ("Global position : "+(string)pos_str); // Debug purposes
     // Fire !
     llOwnerSay ("@tpto:"+pos_str+"=force");
   }
 }
 

}

</lsl>


  • Allow/prevent seeing the names of the people around : @shownames=<y/n>

Implemented in v1.12.2 When prevented, the user is unable to know who is around. The names don't show on the screen, the names on the chat are replaced by "dummy" names such as "Someone", "A resident", the tooltips are hidden, the pie menu is almost useless so the user can't get the profile directly etc.

  • Allow/prevent flying : @fly=<y/n>

Implemented in v1.12.2 When prevented, the user is unable to fly.

  • Get the UUID of the object the avatar is sitting on : @getsitid=<channel_number>

Implemented in v1.12.4 Makes the viewer automatically answer the UUID of the object the avatar is currently sitting on, or NULL_KEY if they are not sitting.

  • Allow/prevent changing the environment settings : @setenv=<y/n>

Implemented in v1.14 When prevented, the user is unable to change the viewer environment settings (World > Environment Settings > Sunrise/Midday/Sunset/Midnight/Revert to region default/Environment editor are all locked out).

test

  • Force change an environment setting : @setenv_<setting>:<value>=force (*)

Implemented in v1.14 Forces the viewer to change a particular environment setting (time of day or Windlight) and set it to <value>. This command is actually a package of many sub-commands, that are regrouped under "@setenv_...", for instance "@setenv_daytime:0.5=force", "@setenv_bluehorizonr:0.21=force" etc. This command (like any other "force" command) is silently discarded if the corresponding restriction has been set, here "@setenv", but in this case the restriction is ignored if the change is issued from the object that has created it. In other words, a collar can restrict environment changes, yet force a change by itself, while another object could not do it until the collar lifts the restriction.

Each particular sub-command works as follows :

Command Value range Description
@setenv_daytime:<X>=force 0.0-1.0 and <0 Sets time of day (sunrise:0.25, midday:0.567, sunset:0.75, midnight:0.0, set back to region default:<0)
@setenv_bluehorizonr:<>=force 0.0-1.0 Sets Blue Horizon Red
@setenv_bluehorizong:<>=force 0.0-1.0 Sets Blue Horizon Green
@setenv_bluehorizonb:<>=force 0.0-1.0 Sets Blue Horizon Blue
@setenv_bluehorizoni:<>=force 0.0-1.0 Sets Blue Horizon Intensity



(*) Silently discarded if the user is prevented from doing so by the corresponding restriction. This is on purpose.

   Ex : Force detach won't work if the object is undetachable. Force undress won't work if the user is prevented from undressing.

Important note about the global behaviours such as sendchat

Such behaviours are global, which means they don't depend on a particular object. However, they are triggered by objects with a set UUID which can change, and several objects can add the same behaviour, which will be stored several times as the UUIDs are different.

This has a nice side effect : when wearing 2 locked devices that prevent chat, it is necessary to unlock them both to be able to chat again. But it has a nasty side effect, too : if the item changes UUID (for instance it was derezzed and rezzed again), and it doesn't allow chat beforehand, then the user will have to wait a short moment because the rule stays "orphaned" (its UUID is defunct) until the garbage collector kicks in.

Therefore : ALWAYS DEACTIVATE A BEHAVIOUR WHEN THE OBJECT THAT HAS ACTIVATED IT DEREZZES FOR ANY REASON BESIDES LOGGING OUT ! "@CLEAR" IS YOUR FRIEND !

Shared Folders

Since v1.11, the viewer can "share" some of your items with scripts in world in order to let them force you to attach, detach and list what you have shared.

"Share" does NOT mean they will be taken by other people if they want to (some of the items may be no-transfer anyway), but only that they can force YOU to wear/unwear them at will through the use of a script YOUR restraints contain. They will remain in your inventory.

To do this :

  • Create a folder named "#RLV" (without the quotes) directly under "My Inventory" (right-click on "My Inventory", select "New Folder"). We'll call this folder the "shared root".
  • Move a folder containing restraints or other attachments directly into this new folder.
  • Wear the contents of that folder, that's it !

So it would look like this :

My Inventory
|- #RLV
|  |- cuffs
|  |  |- left cuff (l forearm)   (no copy)
|  |  \- right cuff (r forearm)   (no copy)
|  \- gag
|     \- gag (mouth)   (no copy)
|- Animations
|- Body Parts
.
.
.

For example : If you're owning a set of RR Straps and want to share them, just move the folder "Straps BOXED" under the shared root.

Either wear all the items of the folders you have just moved (one folder at a time !) or rename your items yourself, so that each item name contains the name of the target attachment point. For example : "left cuff (l forearm)", "right ankle cuff (r lower leg)". Please note that no-modify items are a bit more complex to share, because they cannot be renamed either by you or by the viewer. More on that below.

The attachment point name is the same as the one you find in the "Attach To" menu of your inventory, and is case insensitive (for example : "chest", "skull", "stomach", "left ear", "r upper arm"...). If you wear the item without renaming it first it will be renamed automatically, but only if it is in a shared folder, and does not contain any attachment point name already, and is mod. If you want to wear it on another attachment point, you'll need to rename it by hand first.

Pieces of clothing are treated exactly the same way (in fact they can even be put in the folder of a set of restraints and be worn with the same command). Shoes, for instance, are a good example of mixed outfits : some attachments and the Shoes layer. Clothes are NOT renamed automatically when worn, since their very type decides where they are to be worn (skirt, jacket, undershirt...).

HOW TO SHARE NO-MODIFY ITEMS : As you already know, no-mod items cannot be renamed so the technique is a bit more complex. Create a sub-folder inside the outfit folder (such as "cuffs" in the example above), put ONE no-modify item in it. When wearing the object, you'll see the folder itself be renamed (that's why you must not put more than one object inside it). So if your outfit contains several no-mod objects, you'll need to create as many folders and put the no-mod objects in them, one in each folder.

Example with no-modify shoes :

My Inventory
|- #RLV
|  \- shoes
|     |- left shoe (left foot)
|     |  \- left shoe   (no modify) (no transfer)  <-- no-mod object
|     |- right shoe (right foot)
|     |  \- right shoe   (no modify) (no transfer) <-- no-mod object
|     \- shoe base   (no modify) (no transfer)     <-- this is not an object
|- Animations
|- Body Parts
.
.
.

GOTCHAS :

  • Do NOT put several levels of folders inside the shared root, it wouldn't work. For instance, do not create a folder named "Restraints" in "#RLV" and put the "Straps BOXED" folder inside it. No-modify objects are an exception since they must be put in their own sub-folder.
  • Do NOT put a comma (',') in the name of the folders under the shared root or it would screw the list up.
  • Don't forget to rename the items in the shared folders (or to wear these items at least once to have them be renamed automatically) or the force attach command will appear to do nothing at all.
  • Avoid cluttering the shared root with many folders, since some scripts may rely on the list they got with the @getinv command and chat messages are limited to 1023 characters. Choose wisely, and use short names. But with 9 characters per folder name average, you can expect to have about 100 folders available.
  • Remember to put no-modify items in sub-folders, one each, so their names can be used by the viewer do find out where to attach them. They can't be shared like modify items since they can't be renamed, and the outfit folder itself will not be renamed (since it contains several items).

For your information

Here is how it works internally, for a better understanding of the gotchas you may encounter :

  • Each command is parsed into a Behaviour (ex: "remoutfit"), an Option (ex: "shirt") and a Param (ex: "force") and comes from an UUID (the unique identifier of the emitting object).
  • There are two types of commands : one-shot commands (those which Param is "force" and those which Param is a number such as the channel number of a "version" call) and rules (those which Param is "y", "n", "add" or "rem"). "clear" is special but can be seen as a one-shot command.
  • Parameters "n" and "add" are exactly equal and are treated exactly the same way, they are just synonyms. Same goes for "y" and "rem". The only purpose is to distinguish rules ("sendchannel=n") from exceptions ("sendchannel:8=add") in a script for clarity.
  • Rules are stored inside a table linking the UUID of the emitter to the rule itself. They are added when receiving a "n"/"add" Param, and removed when receiving a "y"/"rem" Param.

If UUID1 is a collar and UUID2 is a gag :

UUID1 => detach, tploc, tplm, tplure, sittp

UUID2 => detach, sendim, sendim:(keyholder)

Those two rules mean that the user cannot send IMs except to their keyholder, and cannot TP at all. Those two items are not detachable. Now if the collar sends "@sendim=n", the table becomes :

UUID1 => detach, tploc, tplm, tplure, sittp, sendim

UUID2 => detach, sendim, sendim:(keyholder)

If it sends "@sendim=n" a second time nothing changes, as there is a check for its existence prior to adding it. If the gag is unlocked and detached, either it sends a "@clear" or the garbage collector kicks in so the rules linked to UUID2 disappear. However, the avatar is still unable to send IMs even to their keyholder, as the exception has disappeared as well. This is because rules linked to one object don't conflict with rules linked to another one.

  • One-shot commands, on the other hand, are executed on-the-fly and are not stored.
  • When logging on, the avatar stays non-operational (cannot chat, cannot move) for some time, while the user sees the progress bar. However, worn scripted objects rez in the meantime and start sending rules and commands before the viewer can execute them. Therefore it stores them in a buffer and executes them only when the user is given controls (when the progress bar disappears).
  • The viewer periodically (every N seconds) checks all its rules and removes the ones linked to an UUID that does not exist around anymore ("garbage collector"). This means that rules issued by an unworn owned object will be discarded as soon as the avatar teleports elsewhere.