wasCSVToListfor which you can find an implementation on the comma separated values page. This is due to the fact that the built-in Linden Lab functions
llCSV2Listdo not adhere to any standard (for example,
llList2CSVyields identical strings to
llDumpList2String(string, ", ")which makes the Linden Lab function useless).
tellcommand sent to Corrade can make Corrade talk in local chat, in a group and to avatars depending on the
entityparameter and, in case of local chat, the
lastnamewhen you need to refer to an avatar. If
lastnameis not known to the script, you can just supply the
agentparameter and feed it the key of the designated agent.
targetparameter with a group name or group UUID as a value in order to act upon a different group.
http_requestof an LSL script. If that is the case, then the alternative is to run Corrade's internal HTTP server and query the data outside of the grid (for example, with a PHP script). An alternative solution if you have any previous knowledge about the data that is to be returned by Corrade and you really want the result returned in-world is to use sifting.
It is important to realize that the grid is not data-driven and that visual elements such as avatars and objects are passed to a viewer on a least-effort basis. There are few to no guarantees that specific objects or avatars will be passed to the viewer.
All commands that are radar-bound are subject to the draw distance, either defined in the configuration file as the
Range parameter or passed to the individual command as the
range parameter. The range represents the cutoff point after which objects and avatars will not be perceived by the viewer.
Other commands that rely on different subsystems such as directory services, or the map API will often offer better guarantees for finding avatars and objects. For instance, compare the two commands:
The former command, "getprimitivesdata" is radar-bound and will not guarantee that any primitive in particular will be detected by the viewer whilst the latter command "getprimitiveowners" goes through the land and parcel subsystem and the command is guaranteed to return complete results.
The same applies to avatars, for instance, compare the two commands:
The former command, "getavatarsdata" is radar-bound and will not guarantee that any avatar in particular will be detected by the viewer whilst the "getavatarpositions" command goes through the map API such that the command is guaranteed to return complete results.
Note that commands such as "getavatarsdata" and "getavatarpositions" are different in semantics where "getavatarsdata" is supposed to query avatars for various parameters and that "getavatarpositions" will return, as per the command definition, a CSV list of avatars by positions. Nevertheless, if the purpose is to find all avatars on a region reliably then "getavatarpositions" should be used instead of "getavatarsdata" and then the information that is not needed (such as the map position) can be discarded. Otherwise, "getavatarsdata" can be used in a context where incomplete data is acceptable.
For all API commands, when an
item parameter is required by a command, either the primitive (or object) UUID or primitive name can be passed to the
item parameter. In case a name is passed to the
item parameter, then Corrade will scan all objects in the vicinity and attempt to find the primitive or object by name. In case an UUID is supplied, Corrade will attempt to find the primitive or object by UUID.
In most cases, it is preferable to combine Corrade command with
llSensorRepeat in order to provide Corrade with the UUID of the item instead of the name. This is due to the fact that objects in Linden-based virtual worlds carry only a limited amount of information and the name of the object is not part of that information such that the command may take a long time to complete. Furthermore, object or primitive names are not distinct such that issuing a command with the
item parameter set to an object name might return ambiguous results where a different primitive is returned rather than the one desired.
In many cases when interacting with primitives and avatars, you will notice that Corrade takes as parameter a
range is the search radius in meters from the bot where Corrade will attempt to find a named primitive. Note that Corrade's search functions also include avatar attachments such that
range should be kept as low as possible, otherwise very many primitives will be scanned and the command will take longer to complete.
range parameter corresponds to the
Range configuration setting in the configuration file - usually, for all commands, if
range is specified, then the parameter will take precedence over the
Range configuration setting. Otherwise, if
range is not supplied, the search range will be extended to the
Range parameter in the configuration file.
sit, there is no guarantee that
standwill execute before
sitbut it may happen that
sitwill execute before
stand! In order to have any sort of guarantee of linearity, you must use a callback and confirm that a command has executed before issuing a follow-up command. In other words, following the previous example: send the
standcommand and confirm via the callback that
standhas executed; after that, send the
sitcommand (and confirm via the callback that the
sitcommand has executed).
llRegionSayTo(in order to avoid the throttling penalty of instant messages) which makes Corrade move incrementally and since it is irrelevant whether a single one of the infinitesimal nudges got executed then the callback is not checked whilst spamming nudge commands.
You can read more about key-value pairs and comma separated values on the tutorial page concerning data returned by Corrade.
llList2CSVcannot be used because they are not
RFC 4180compliant. To illustrate why
llList2CSVwill not work, lets take the
invitecommand which takes one or more roles in the
roleparameters and invites an avatar into a group and places them in the supplied role. Suppose that the role we want to invite an avatar to carries the name:
The "Good", Role!. As you can see, there are several problems here. First, the comma
,would interfere with CSV and the double-quotes around
Goodwould interfere with an attempt to escape. In such cases, we would send the command as:
llInstantMessage(CORRADE, wasKeyValueEncode( [ "command", "invite", "group", GROUP, "password", PASSWORD, "firstname", "John", "lastname", "Steinbeck", "role", llEscapeURL("\"The \"\"Good\"\", Role!\""), "callback", llEscapeURL(URL) ] ) );
Notice the role name
\"The \"\"Good\"\", Role!\":
") must be escaped in order to not interfere with LSL's start and end-of-string delimiters and will be thus written as (
\"). This will vary if you are using Corrade's HTTP interface and are using a different language that supports other delimiters. For example, in PHP, you could use single quotes to avoid clashes and the role would be thus escaped in PHP as
'"The ""Good"", Role!"'.
\"(beginning and end) escape the entire role name - we need to do this because the role name contains a comma
"". So, that is why in order to successfully pass
"Good", we escape the double quotes using another double quote:
When setting or getting the permissions for an item - either in-world or as an inventory item, Corrade serializes and deserializes permissions from a specially-formatted string which resembles loosely a superset of the UNIX literal permissions. This is due to the fact that all permissions can be expressed unambiguously using Corrade's formatted string as well as making it much easier to deal with.
An example of what a permission string looks like, is the following permission extracted from a newly created object with "copy" and "transfer" set:
To dissect these permissions, you can use the following guide:
-) instead, that means that those permissions are missing.
Annotating the example, we have:
everyone next owner | | +-+--+ +--+-+ | | | | cdemvt------------cde-vtcdemvt | | | | | | +-+--+ +--+-+ +-+--+ | | | base group owner
And interpreting the results, we have:
cdemvtindicate that all permissions are set for the base mask are set.
------indicate that no permissions are granted to the everyone mask.
------indicate that no permissions are granted to the group mask.
cde-vtindicate that the next owner has the ability to copy (
c), damage (
d), export (
e), move (
v) and transfer (
t) but cannot modify the object (since the
mis missing and a dash is there instead).
cdemvtindicate that all permissions are set for the owner.
If you take a closer look at the
everyone mask, and the
group mask you will notice that there are no permissions there. These permissions are used in cases where, for example, you would grant everyone or a group the permission to move the object. All the fields are relevant, but it is highly likely that the next owner mask is the one you would want to change before giving an object away.
Note that in the instances where you have to set permissions, the
Damage permissions are special and pertain mostly to OpenSim. As such, setting an item to full-permission will require the string:
The following table summaries all the permissions that are usually set for the next owner on an object:
|Next Owner Permissions||Required String|
|Modify, Copy, Transfer||
Note that the permissions summarised in the table above only change the hexa corresponding to the next owner and that the same variations can be applied to the other hexas.
The restriction for each hexa is that only
Modify objects (or objects with no permissions) cannot exist in the Linden permission system. Thus, the following permission strings are illegal:
|Next Owner Permission||Illegal String|
Corrade will still attempt to set the permissions but most likely the grid will reject the setting of the permissions and Corrade will thus return a script error to the callback. Keep in mind that Corrade can do anything (and more) that a viewer can do such that a large subset of the Linden restrictions apply to Corrade as well.
In order to blend elegantly with LSL semantics, Corrade uses vectors to reference parcels instead of parcel local IDs since local IDs are not recognized by the LSL API. By consequence, most commands that deal with parcels take a
position argument that is supposed to represent a point vector that will intersect a single parcel when projected onto the plane.
For instance in the following illustration, when the vector is projected onto a simulator map, will intersect a given parcel.
y ^ / 255 -------------------------------------- /region / / / / | ------------- / / v(x, y)| / / / |/ / / / v parcel / / /-----------/ / / / / / - - - - - - - - - - - - - - - - - -/- - - - -> x 255
Vector can then be fed to the
position parameter for all commands that interact with parcels.
The getregionparcellocations command can be used to retrieve a CSV list of parcel names by parcel local ID by descriptive vector.
The getparceldata command can also be used to retrieve the
AABMax parameters for a parcel representing the south-western extremity point respectively the north-eastern extremity point of a parcel. Intuitively, one could then take the arithmetic mean value to determine the midpoint and compute a point vector that would fall within the parcel. However, in Second Life parcels are allowed to be disjoined such that using the
AABMax parameters from the
getparceldata command will fail in certain situations.
As an example where calculating the midpoint using
AABMax via the
getparceldata command would yield erroneous results, let us assume the following land topology:
AABMax +----+--+----+ |(A)-|//|----| |----|//|----| +----+//+----+ |//(B)///////| +----+//+----+ |----|//|----| |----|//|----| +----+--+----+ AABMin
representing a simulator that has been divided into two parcels denoted with
B. Taking the
AABMax points for parcel
A and calculating the midpoint as the arithmetic mean would produce a point vector (on the diagonal from
AABMax) that falls within parcel
The getregionparcellocations command is designed to always produced valid point vectors to describe parcels and should always be used instead of computing the midpoint via the
Corrade distinguishes between two types of plural forms:
The distinction is made due to the way SecondLife offers various properties that have different kind of contexts leading to different ways of handling the command entirely.
In any case, plural forms for commands that return data (all commands with the
data suffix) will return the data requested by the user and only the data requested by the user. For example, suppose that the following command is used to query the IsTrial property of two avatars using the plural form command
llInstantMessage(CORRADE, wasKeyValueEncode( [ "command", "batchgetavatarsappearancedata", "group", wasURLEscape(GROUP), "password", wasURLEscape(PASSWORD), "avatars", wasListToCSV([ "Juno Resident", "Cat Resident" ]), "data", "IsTrial", "callback", wasURLEscape(URL) ] ) );
The result passed to the callback URL (or other external services if querying via HTTP, MQTT, etc.) will be a CSV list containing two booleans and it will be impossible to determine to which avatar (either
Juno Resident or
Cat Resident) a particular boolean in the list corresponds to:
In order to solve the issue, the AvatarAppearanceEventArgs structure contains an
AgentID property which can be queried along with the
llInstantMessage(CORRADE, wasKeyValueEncode( [ "command", "batchgetavatarappearancedata", "group", wasURLEscape(GROUP), "password", wasURLEscape(PASSWORD), "avatars", wasListToCSV([ "Juno Resident", "Cat Resident" ]), "data", wasListToCSV([ "AgentID", "IsTrial" ]), "callback", wasURLEscape(URL) ] ) );
The result will now be a CSV list of avatar UUIDs by booleans:
Furthermore, plural forms shall never mangle the order of the properties passed to the
data parameter within a subgroup yet subgroup order may be mangled. In other words, the following two results are possible if the previous command were to be issued twice:
is equivalent to:
As you can observe, the avatar IDs and trial flags have been switched for both avatars however, the correct trial flag still corresponds to the correct avatar. In other words, the subgroups:
If you are curious why, imagine a table where the columns / column headers are the properties that users supply to the
data parameter and the rows of that table are the results. Since there is no way to transfer a 2-dimensional table, Corrade places the column name before each cell, followed by the cell and each row after the other in the same long string. Following the analogy, the order of the rows is not relevant but the contents of each cell in a row is since it corresponds to a column header name.
The following BNF grammar describes the syntax of a Corrade command:
command ::= [ prefix ] <body> [ suffix ] prefix ::= "batch" | "" body ::= ( <term> [ plural ] )+ suffix ::= "data" | "" plural ::= "s" | ""
Additionally the following notes apply universally to all commands:
batchdenote a command that is a named plural of a command. Such commands take a list of specific items and perform an action or retrieve some information on or for the specified items.
dataare commands that query various properties of itmes. Such commands are expected to return a
datakey to the callback whose value represents the queried data or the command does not return a
datakey at all in which case no data was available for the queried property (or the property does not exist).
A formation that contains two or more plurals but one and only one named plural is possible. For example, a fictive command "batchgetavatarsnamedata" could possibly be a command that would query the name of multiple avatars (plural avatars) within multiple parcels (named plural grouping).
Although pertaining more to Linden grid design rather than Corrade, the meaning of UUIDs change depending on the executed commands. Items on the grid have different UUIDs, some of which can be accessed using a standard viewer and others that cannot.
For example, all objects in inventory have an inventory UUID that can be used to reference the objects when using inventory Corrade commands or even when sending the object to someone via the give command. However, the same object does not even have an asset UUID because objects do not have asset UUIDs (compared to, say, textures or sounds that can be downloaded via the download command where an asset UUID is required). If the object were to be rezzed in-world, then the grid will generate a random UUID for the in-world object (and the grid does so every time the same object is rezzed such that the UUID changes) and commands like touch require the in-world UUID to be passed to its parameters.
So far, using the example above, we have collected three different contexts where UUID may appear:
This list can now be shunted with avatar UUIDs, group UUIDs and others. Whenever a Corrade command is issued that takes (or can take) as parameter an UUID, it is important to ensure that the passed UUID matches the proper context. Doing otherwise, will not find the object that you are looking for, for example, attempting to de-rez an in-world object via its inventory UUID will fail because an in-world object is referred to by its in-world UUID. Commands should mention the type of UUID that is required on their corresponding API pages.
All commands that must reference an inventory item, typically via the
item parameter, can be referred to by either by inventory UUID or by inventory path.
One of the problems with Linden protocols is that inventory items may carry the same name, which is something that filesystems usually do not support, which makes referring to an item by name ambiguous. To overcome this problem, Corrade allows paths to inventory items to be built by name or by UUID or a combination thereof.
As an example, consider the following tree:
+ My Inventory (UUID: 7c38c488-5440-46c2-9f14-d5e3fcd0cc16) | +- Textures (UUID: 56a54ebe-7d4d-4839-a618-42ee3b307717) + | +- Floors (UUID: b13b0890-fac6-4085-975c-c2f760b185e4) | + | | | +- Deck (UUID: 3e51dd70-9cd8-4dcf-9465-58c7036e673d) | +- Floors (UUID: c590792b-a724-4bb6-9f0c-81b6e733fad5) + | +- Table (UUID: 6eaffc93-df2a-479b-836b-4778bb917619)
If we wanted to list
Deck via the inventory command and supposing that we are currently in
My Inventory which is the root, then supplying
/My Inventory/Textures/Floors/Deck to the
path parameter would be ambiguous since it is uncertain which
Floors folder should be browsed. To get rid of the Linden ambiguity, Corrade allows specifying UUIDs in the paths such that we are able to correctly reference
Deck by supplying:
/My Inventory/Textures/b13b0890-fac6-4085-975c-c2f760b185e4/Deck to the
path parameter. Note that it is also possible to specify any of the other path components as UUIDs rather than by names - yet, in this case, that is not necessary: when you supply a name in the path, you are making a weak reference, when you are supplying an UUID you are making a strong reference.
Items that contain a forward-slash that would clash with the forward-slash (
/) representing a path component can be escaped using a backslash (
\). For instance, suppose that you have a texture named:
// Awesome Texture //
and that you want to reference it - you would then write:
"path" , wasURLEscape("/My Inventory/Textures/\/\/ Awesome Texture \/\/");
Alternatively, you can just use the UUID instead of the name.
Corrade accepts some parameters that are considered to have a global scoping in that they apply to every command. The following is a list of global parameters that can be passed as part of certain sets of commands and with various effects on the command.
| ||an integer (default 60000)||all commands||this parameter allows overriding the default Corrade timeout of for the called command|
| ||a string or UUID||commands acting on groups||this parameter allows referring to a different group whilst authenticating with a configured group|