wolfCallAsync
Wolf Territoriesstring wolfCallAsync(string name, list args)
Like wolfCall, but the answer comes later as dataserver(query, data), where query is the key this returns. For plugin functions that take time (out-of-process services are always asynchronous). Returns NULL_KEY with wolfGetLastError saying why when refused (no dataserver event then). Defined on Wolf_Api.wolfCallAsync / IWolfPluginCalls.
Like wolfCall, but for plugin functions that take time: it returns a key straight away and the answer arrives later in your script's dataserver event, with that key as the query. The answer is one string; a function that answers at once sends its values joined with "|" (split them with llParseString2List(data, ["|"], [])). Functions provided by a separate service on the grid (rather than inside the region) always answer this way. If the plugin is not there, refuses, or its service is down, you get NULL_KEY at once and wolfGetLastError says why - then no dataserver event comes. If a service goes away while it is working on your call, the answer is a string starting with "error: ", so your script never waits for ever.
Parameters
| Type | Name | What it does |
|---|---|---|
| string | name | The plugin function to call, e.g. "relay.upper". |
| list | args | What to give it: any list. |
Example
key asked;
default
{
touch_start(integer n)
{
asked = wolfCallAsync("relay.upper", [llDetectedName(0)]);
if (asked == NULL_KEY)
llOwnerSay("Not asked: " + wolfGetLastError());
}
dataserver(key query, string data)
{
if (query == asked)
llSay(0, "The service says: " + data); // "error: ..." if it went away meanwhile
}
}Wolf plugin commands on this grid
Every Wolf Territories region answers these names (the wolf-commands plugin). With them a script anywhere on the region - rezzed on your land or worn as a HUD - runs your own WolfNPCs. npc is the NPC's key (from npc.list, or its object's key).
| Name | Give it | You get back | Who may | What it does |
|---|---|---|---|---|
| wolf.version | [] | ["wolf-commands", version, 1] | anyone | Which version of these commands this region runs. |
| wolf.commands | [] | the command names | anyone | Every command in this list that this region answers. |
| npc.list | [] or ["all"] | [count, key, name, rights, ...] | anyone | Your NPCs on this region, or with "all" every NPC here. Rights is "edit" (yours to run), "control" (you may stop it, send it home, animate it, make a dragon breathe) or "none". At most 80 are listed; count is all of them. |
| npc.info | [npc] or [npc, "STATE" | "INTENT" | "EVENTS" | "CFG"] | [text] | control; CFG: owner | STATE is "intention|awake or asleep|alive or dead|health/max". INTENT is what it is doing, EVENTS its running events, CFG all its settings as JSON. |
| npc.say | [npc, text] | ["ok"] | owner | It says the words (up to 1000 characters) in local chat. An NPC speaks at most 5 lines in ten seconds. |
| npc.goto | [npc, <x, y, z>] or [npc, place] | ["ok"] | owner | It walks to that spot on this region, or to one of its own places (or tools) by name. |
| npc.follow | [npc, key or name] or [npc, ""] | ["ok"] | owner | It follows an avatar or NPC on this region; "" stops following. |
| npc.stop | [npc] | ["ok"] | control | It stops and stands. Its looped animations stop too. |
| npc.home | [npc] or [npc, "here"] | ["ok"] | control; "here": owner | It goes home. With "here", where it stands now becomes its home. |
| npc.anim | [npc, animation] or [npc, animation, "once" | "loop" | "stop"] | ["ok"] | control | Plays one of the animations in its contents once (the default), keeps it looping, or stops it. [npc, "", "stop"] stops them all. |
| npc.breath | [npc, "fire" | "ice" | "stop"] or [npc, "fire" | "ice", seconds] | ["ok"] | control | A dragon breathes fire or ice for 0.5 to 20 seconds (0 or none = 3), the same breath as in its window. |
Who may
- owner: the NPC's owner, while they may still build where it lives - they own the region or are one of its estate managers, or they own the parcel under its home (a travelling pet: anywhere). The same rule as editing it in the NPC window.
- control: the owner as above, or the region's owner or an estate manager - what the NPC window lets them do with anyone's NPC. They can never make someone else's NPC speak, walk, follow, move its home or read its settings.
- The script must belong to the owner of the object it is in: someone else's script dropped into your object never acts for you. Only NPCs on the same region.
Answers and limits
- Every command answers at least one value. An empty list (wolfCall) or NULL_KEY (wolfCallAsync) means it was refused, and wolfGetLastError() says why in plain words - for example "only its owner can do that".
- ["ok"] means the NPC has been told; it does it straight away, even while it is asleep with nobody near.
- With wolfCallAsync the answer comes in dataserver as one string, the values joined with "|".
- At most 20 calls in ten seconds from one object, 60 from all of one resident's objects, and 600 from the whole region; an NPC says at most 5 lines in ten seconds. Over that, the call is refused with a message and works again a few seconds later.
Example: make your NPC come to you and say hello
// Touch: your first NPC on this region walks to you and greets you.
touch_start(integer n)
{
if (llDetectedKey(0) != llGetOwner()) return;
list mine = wolfCall("npc.list", []); // [count, key, name, rights, ...]
if (llList2Integer(mine, 0) == 0)
{
llOwnerSay("No NPCs of yours here. " + wolfGetLastError());
return;
}
key npc = llList2Key(mine, 1);
if (wolfCall("npc.goto", [npc, llDetectedPos(0)]) == [])
llOwnerSay("It will not come: " + wolfGetLastError());
else
wolfCall("npc.say", [npc, "Hello, " + llDetectedName(0) + "!"]);
}A whole NPC remote to wear as a HUD (wolf_npc_remote.lsl, 113 lines: show or hide)
// Wolf NPC remote - wear it as a HUD (or rez it on your land) and control your own WolfNPCs on this region.
// Uses the wolf-commands plugin through wolfCall: no region restart is ever needed for these commands.
// Touch: pick one of your NPCs, then what it should do. Only NPCs you own (and may still run here) obey you.
// Wolf Territories, 2026. Copy, change and share freely.
integer CHANNEL;
integer listener;
list npcKeys;
list npcNames;
key chosen;
string chosenName;
// Every wolf-commands call answers with a list; an empty list means "refused", and wolfGetLastError() says why.
integer tell(string what, list answer)
{
if (llGetListLength(answer) == 0)
{
llOwnerSay(what + ": " + wolfGetLastError());
return FALSE;
}
return TRUE;
}
listenFor(float seconds)
{
llListenRemove(listener);
listener = llListen(CHANNEL, "", llGetOwner(), "");
llSetTimerEvent(seconds);
}
pickNpc()
{
// [count, key, name, rights, key, name, rights, ...] - rights is "edit", "control" or "none".
list r = wolfCall("npc.list", []);
if (!tell("Your NPCs", r)) return;
npcKeys = [];
npcNames = [];
integer i;
for (i = 1; i + 2 < llGetListLength(r) && llGetListLength(npcNames) < 12; i += 3)
{
if (llList2String(r, i + 2) != "none")
{
npcKeys += llList2Key(r, i);
npcNames += llGetSubString(llList2String(r, i + 1), 0, 23);
}
}
if (npcNames == [])
{
llOwnerSay("You have no NPCs you can control on this region.");
return;
}
listenFor(60.0);
llDialog(llGetOwner(), "Which NPC?", npcNames, CHANNEL);
}
pickAction()
{
listenFor(60.0);
llDialog(llGetOwner(), chosenName + " - what should it do?",
["Come here", "Follow me", "Stop", "Go home", "Say hello", "Status", "Fire", "Ice"], CHANNEL);
}
default
{
state_entry()
{
CHANNEL = -1 - (integer)("0x" + llGetSubString((string)llGetKey(), 0, 6));
chosen = NULL_KEY;
}
touch_start(integer n)
{
if (llDetectedKey(0) == llGetOwner()) pickNpc();
}
listen(integer ch, string name, key id, string msg)
{
integer i = llListFindList(npcNames, [msg]);
if (chosen == NULL_KEY || i != -1)
{
if (i == -1) return;
chosen = llList2Key(npcKeys, i);
chosenName = msg;
pickAction();
return;
}
list r;
if (msg == "Come here") r = wolfCall("npc.goto", [chosen, llList2Vector(llGetObjectDetails(llGetOwner(), [OBJECT_POS]), 0)]);
else if (msg == "Follow me") r = wolfCall("npc.follow", [chosen, llGetOwner()]);
else if (msg == "Stop") r = wolfCall("npc.stop", [chosen]);
else if (msg == "Go home") r = wolfCall("npc.home", [chosen]);
else if (msg == "Say hello") r = wolfCall("npc.say", [chosen, "Hello, " + llKey2Name(llGetOwner()) + "!"]);
else if (msg == "Fire" || msg == "Ice") r = wolfCall("npc.breath", [chosen, llToLower(msg), 3.0]); // dragons only
else if (msg == "Status")
{
// "intention|awake or asleep|alive or dead|health/max"
r = wolfCall("npc.info", [chosen]);
if (tell(chosenName, r)) llOwnerSay(chosenName + ": " + llList2String(r, 0));
}
if (msg != "Status") tell(chosenName, r);
chosen = NULL_KEY;
llListenRemove(listener);
llSetTimerEvent(0.0);
}
timer()
{
llListenRemove(listener);
llSetTimerEvent(0.0);
chosen = NULL_KEY;
}
}
Ours alone. Grid features you will not find on any other OpenSimulator grid.