Jump to content

Best Scripter Tips and Shortcuts


Lexie Linden
 Share

You are about to reply to a thread that has been inactive for 353 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

2 hours ago, Wulfie Reanimator said:

A string that begins with "0x" will be interpreted as a hexadecimal number, it will keep reading the number until a character other than 0-9 and A-F is encountered.

Ah ha, so it's the "-" in the key that causes conversion to cease without there being any overflow. Neat. Qie was right then about it being no coincidence the first 32 bits are a distinct chunk?

Edited by Profaitchikenz Haiku
Link to comment
Share on other sites

3 hours ago, Profaitchikenz Haiku said:

Ah ha, so it's the "-" in the key that causes conversion to cease without there being any overflow. Neat. Qie was right then about it being no coincidence the first 32 bits are a distinct chunk?

Yes and no. It's not a coincidence.. in the sense that UUIDs are a general standard used in a lot of places, not just Second Life, so the Lindens did not come up with UUIDs with this in mind. But at the same time.. the fact that they chose this system is a happy coincidence/convenience that just happens to allow this kind of neat conversion (and maybe that's a contributing reason they chose it, but I doubt it). Even the way strings are interpreted as numbers is something seen in C/C++, so that's not specific to SL either.

Edited by Wulfie Reanimator
  • Like 2
Link to comment
Share on other sites

  • 4 weeks later...
On 2/25/2020 at 12:27 PM, CoffeeDujour said:

Really have to be careful when using tools designed to make LSL mimic a bigger more connected language, very easy to end up with a large slow script that if hand optimized would be a fraction of the size and much faster.

Agreed, unfortunately as scripts grow in size and complexity, it becomes difficult to keep one's sanity without some QoL features.

  • Like 1
Link to comment
Share on other sites

  • 1 month later...
  • 1 year later...

If you want to add more prims to an object, it's not too hard to rez them and then llCreateLink. But what if you want to delete a prim from your linkset? It was a nice 'ah-hah' moment when I realized that rather than having a die script in every prim that might need to be deleted, or trying to llRemoteLoadScriptPin() you can just set them to temporary and move them out of the way:

integer addremove;
delete_link(integer n)
{   llSetLinkPrimitiveParamsFast(n,
    [   PRIM_POS_LOCAL,<0,0,-1>, // out of the way.
        PRIM_TEMP_ON_REZ, TRUE, // set to delete // setting for one prim seems to set for the whole linkset.
        PRIM_COLOR,ALL_SIDES,<0,0,0>,0.0 // invisible.
    ]);
    llBreakLink(n);
    llSetLinkPrimitiveParamsFast(llGetNumberOfPrims()!=1, // when there is only 1 prim, the root has link# 0, Ugh.
    [	PRIM_TEMP_ON_REZ, FALSE]);
}
default
{   state_entry()
    {   llRequestPermissions(llGetOwner(),PERMISSION_CHANGE_LINKS);
    }
    touch_start(integer total_number)
    {   addremove=!addremove;
        if(addremove)
        {   llRezAtRoot(llGetInventoryName(INVENTORY_OBJECT,0),llGetPos()+<0,0,1>,<0,0,0>,<0,0,0,1>,0);
        }else
        {   delete_link(2); // should do a permissions check.
        }
    }
    object_rez(key ID)
    {   llCreateLink(ID,TRUE); // should do a permissions check.
    }
}

 

  • Like 3
  • Thanks 1
Link to comment
Share on other sites

  • 4 weeks later...

i will pop this in here as it comes up enough times to warrant doing so

 

often we need to keep a value within some lower (min) and upper (max) bounds. The textbook code to do this is typically

if (n < min)
   n = min;
else if (n > max)
   n = max;
// else (n >= min && n <= max)
//    n = n;

when we need to do this more than one time in our script then we often wrap this in a function, similar too

integer bounds(integer n, integer min, integer max)
{
   if (n < min) return min;
   if (n > max) return max;
   return n;
}

// usage
   n = bounds(n, min, max);

 

a alternative to a user-defined function, is a list lookup where the expression evaluates to a index in the set [0, 1, -1]

n = llList2Integer([min, n, max], (n > min) * (-(n > max) | 1));

 

which saves us about 500 memory bytes that a user-defined function incurs

Edited by Mollymews
typo
  • Like 5
Link to comment
Share on other sites

  • 3 months later...
On 3/22/2022 at 8:43 PM, Mollymews said:

often we need to keep a value within some lower (min) and upper (max) bounds.

I had a think about this again, and ended up with a slightly more efficient answer:

If our lower bound is 0, we can be really efficient:

x *= (x>0); // if (x < 0) x=0;

this can be generalized by adding and subtracting a constant from x:

x = c+((x-c)*(x>c)); // if(x<c) x=c;

for upper bounds, we get a very similar looking answer by subtracting x from a constant: (N.B. (c-(c-x))==x)

x = c-((c-x)*(x<c)); // if(x>c) x=c;
//ETA: this is actually equivalent to:
x = c+((x-c)*(x<c));
//e.g. upper and lower bounds are identical save for the conditional.

a bit of a brain teaser to work through.

Note that the conditions represent when x=x, which is the opposite of the "textbook" conditional of when to set x to the boundary.

ETA: Threading those together and re-arranging a bit leads to:

x = (x>l)*((x<u)*(x-u)+(u-l))+l; // if(x<l) x=l; if(x>u) x=u;
// example:
x = (x>3)*((x<5)*(x-5)+2)+3;
Edited by Quistess Alpha
  • Like 1
  • Thanks 2
Link to comment
Share on other sites

Also, as I think @Mollymews tested at one point, inline list construction is quite inefficient. Instead of

Value = llList2Float([a,b,c,d],i);

consider using

Value = (a*(i==0))+(b*(i==1))+(c*(i==2))+(d*(i==3));

if a,b,c,d are integers, floats or vectors, and aren't already in a list, but ~are calculated right before you select one. (If it makes sense to have the list as a global variable that is never or rarely modified, then llList2* might be as or more efficient)

Applying that to the previous post, we get:

integer x /*= ...*/; 
// clamp x to be max if x>max, and min if x<min
{ // brackets can be used to isolate the scope of a 'working variable'. In this case, we only need i for this calculation.
  integer i = (x>max)-(x<min);
  x = (min*(i==-1)) + (x*(i==0)) + (max*(i==1));
}

 

Edited by Quistess Alpha
'i' can be an index rather than a bitmask, saving a few cycles.
  • Like 2
  • Thanks 1
Link to comment
Share on other sites

14 hours ago, Quistess Alpha said:

Also, as I think @Mollymews tested at one point, inline list construction is quite inefficient

about inline lists, was Qie prompted by Wulfie who showed that a var list is significantly more performant than a const list

they showed that is best to go with a var list as an parameter for llList2... rather than a const list
 

// do this

list args = ["No", "Yes"];

string result = llList2String(args, condition); // where condition is TRUE or FALSE

// and not

string result = llList2String(["No", "Yes"], condition); // where condition is TRUE or FALSE.

 

14 hours ago, Quistess Alpha said:
integer x /*= ...*/; 
// clamp x to be max if x>max, and min if x<min
{ // brackets can be used to isolate the scope of a 'working variable'. In this case, we only need i for this calculation.
  integer i = (x>max)-(x<min);
  x = (min*(i==-1)) + (x*(i==0)) + (max*(i==1));
}

 

 

i really really like this

about the only thing I could add to it is that when we compare a variable to a const in LSL Mono then is a bit more performant to place the const on the left. Whyam not exactly sure, something something stack handling

x = (min * (-1 == i)) + ( x * (0 == i)) + (max * (1 == i));

 

 

  • Thanks 1
Link to comment
Share on other sites

13 minutes ago, Mollymews said:

is a bit more performant to place the const on the left.

I've also heard it's better for catching = vs == mistakes: -1=i; is not a valid statement.

As for above code and miniscule improvements, I've been mulling over

x = (x>l)*((x<u)*(x-u)+(u-l))+l; // if(x<l) x=l; if(x>u) x=u;

and although a bit confusing, the general pattern allows for checks (if) and default (else{ }), and is about as efficient as possible:

// normally would write:
if(!cA)      return rA;
else if(!cB) return rB;
else if(!cC) return rC;
else if(!cD) return rD;
else         return rE;

// can instead try:

(cA)*
  ((cB)*
    ((cC)*
      ((cD)*
        (   (rE-rD)
        )+(rD-rC)
      )+(rC-rB)
    )+(rB-rA)
  )+rA;

If the return values rA, rB etc. are constants, the difference can be done in-code.

(cA)*((cB)*((cC)*(W)+X)+Y)+Z;
// if(!cA) return Z; if(!cB) return Y+Z; if(!cC) return X+Y+Z; else return W+X+Y+Z;

Compare

(x>3)*((x<5)*(x-5)+2)+3;

2 comparisons, 2 multiplications, 1 difference, 2 sums. with

{ integer i = (x>5)-(x<3);
  x = (-1==i)*3 + (!i)*x + (1==i)*5;
}

5 comparisons, 3 multiplications, 1 difference, 2 sums.

. . . but maybe this discussion would be best for an efficiency/ optimization thread . . .

  • Thanks 2
Link to comment
Share on other sites

8 hours ago, Quistess Alpha said:

maybe this discussion would be best for an efficiency/ optimization thread . . .
 

is probably better to start a new thread yes

this thread is more for quick tips/statements rather than a general discussion on inlining code

inline code is a large topic and quite varied, and the question to do or not do is often circumstantial. I think is worth having these more indepth discussion in its own thread, about the various methods and techniques that can optimise when we do inline code

and much of what we are currently discussing also touches on logic programming. So the discussion we can also have in its own thread can be about about how and where we can use logic programming methods to achieve some measurable gain for our scripts, be that in execution speed or memory savings

 

ps. Repost your last in  a new thread as is quite interesting. Is also a thing about using equality vs logical operators which is also worth talking about 

Edited by Mollymews
typs
Link to comment
Share on other sites

  • 5 months later...
On 7/3/2022 at 7:51 AM, Quistess Alpha said:

I've also heard it's better for catching = vs == mistakes: -1=i; is not a valid statement.

As for above code and miniscule improvements, I've been mulling over

x = (x>l)*((x<u)*(x-u)+(u-l))+l; // if(x<l) x=l; if(x>u) x=u;

and although a bit confusing, the general pattern allows for checks (if) and default (else{ }), and is about as efficient as possible:

// normally would write:
if(!cA)      return rA;
else if(!cB) return rB;
else if(!cC) return rC;
else if(!cD) return rD;
else         return rE;

// can instead try:

(cA)*
  ((cB)*
    ((cC)*
      ((cD)*
        (   (rE-rD)
        )+(rD-rC)
      )+(rC-rB)
    )+(rB-rA)
  )+rA;

If the return values rA, rB etc. are constants, the difference can be done in-code.

(cA)*((cB)*((cC)*(W)+X)+Y)+Z;
// if(!cA) return Z; if(!cB) return Y+Z; if(!cC) return X+Y+Z; else return W+X+Y+Z;

Compare

(x>3)*((x<5)*(x-5)+2)+3;

2 comparisons, 2 multiplications, 1 difference, 2 sums. with

{ integer i = (x>5)-(x<3);
  x = (-1==i)*3 + (!i)*x + (1==i)*5;
}

5 comparisons, 3 multiplications, 1 difference, 2 sums.

. . . but maybe this discussion would be best for an efficiency/ optimization thread . . .

I am curious if the condensed form is actually any "faster", or just takes less bytecode.

  • Like 1
Link to comment
Share on other sites

  • 1 month later...
  • 4 weeks later...

I found that my knowledge of how .Net CLR works, especially of the Intermediate Language (IL) used by the CLR, is very helpful in understanding the many 'tricks' of size optimization.

For instance, why is ++i smaller than i++ ?

In a nutshell, ++i compiles to the following IL:

Quote
LOAD (i)   // Load variable into stack
INCR       // Increment value on the top of the stack
STORE (i)  // Store top of the stack into a variable

While its counterpart, i++ , compiles to the following:

Quote
LOAD (i)   // Load variable into stack
DUP        // Duplicate top of stack
INCR       // Increment value on the top of the stack
STORE (i)  // Store top of the stack into a variable
POP        // Remove top of the stack, exposing original value in step 1

This also means that if you try to keep everything in-stack, you'll get more optimized results.

For instance, the following code:

Quote
string name = llGetObjectName();
llOwnerSay("I am " + name);

Is likely to be compiled into:

Quote
// Assume stack is empty
CALL (llGetObjectName)  // Call function with empty stack, result will be in top of stack
STORE (name)            // Store top of stack into variable
POP_ALL                 // Empty the stack
LOADSTR ("I am ")       // Push a literal string into the stack
LOAD (name)             // Load variable onto stack
CALL (_stringConcat)    // Concatenate the strings in the stack, returning 1 string
CALL (llOwnerSay)       // Call function with stack containing 1 string

But if we keep everything on the "stack side":

Quote
string name;
llOwnerSay("I am " + (name = llGetObjectName()));

It will be compiled into:

Quote
LOADSTR ("I am ")
CALL (llGetObjectName)  // This function has no param, so it will not consume the stack
STORE (name)
CALL (_stringConcat)
CALL (llOwnerSay)

A saving of 2 instructions + some computing cycles. Not much, but repeat this many times and you're on to something...

Now, I'm not privy into the actual compilation results, but based on my experience writing C# programs -- and often doing reverse compiling / decompiling of them (using dotPeek) -- this is the more likely outcome. Of course, YMMV CMIIW and all that 😄

Edited by primerib1
2 not 3
  • Like 2
Link to comment
Share on other sites

  • 2 weeks later...

Okay, another tip because this just bit me:

Stop TimerEvent before switching states

I have a state "Ready" with llSetTimerEvent(1)  -- this is because I need to multiplex several timer events.

I switched to a different state "Utils" with llSetTimerEvent(60). The new state shows a menu. But if I click on the menu, the timer() handler gets triggered!

Apparently there's a leftover timer() event in the queue that managed to enter the queue before switching states, resulting in the timer() handler in "Utils" to get triggered.

After I added llSetTimerEvent(0) in "Ready"'s state_exit(), I no longer experience the spurious timer() event again.

Edit: Ah, apparently it is mentioned in the wiki page, I just didn't understand it before I got bitten:

Quote
  • The timer persists across state changes, but gets removed when the script is reset. So if you change to a state that has a timer() event, with the timer still running, it will fire in the new state.

 

Edited by primerib1
  • Like 2
Link to comment
Share on other sites

33 minutes ago, primerib1 said:

Okay, another tip because this just bit me:

Stop TimerEvent before switching states

I have a state "Ready" with llSetTimerEvent(1)  -- this is because I need to multiplex several timer events.

I switched to a different state "Utils" with llSetTimerEvent(60). The new state shows a menu. But if I click on the menu, the timer() handler gets triggered!

Apparently there's a leftover timer() event in the queue that managed to enter the queue before switching states, resulting in the timer() handler in "Utils" to get triggered.

After I added llSetTimerEvent(0) in "Ready"'s state_exit(), I no longer experience the spurious timer() event again.

Edit: Ah, apparently it is mentioned in the wiki page, I just didn't understand it before I got bitten:

 

Or..there are also use-cases where you would WANT the timer to fire in the new state! Just so long as you're aware it will.

  • Like 1
Link to comment
Share on other sites

On 2/19/2023 at 3:26 AM, primerib1 said:

For instance, why is ++i smaller than i++ ?

In a nutshell, ++i compiles to the following IL:

Quote
LOAD (i)   // Load variable into stack
INCR       // Increment value on the top of the stack
STORE (i)  // Store top of the stack into a variable

While its counterpart, i++ , compiles to the following:

Quote
LOAD (i)   // Load variable into stack
DUP        // Duplicate top of stack
INCR       // Increment value on the top of the stack
STORE (i)  // Store top of the stack into a variable
POP        // Remove top of the stack, exposing original value in step 1

Interesting that they made the extra copy for "i++".

I think this is because the original copy is used in the "comparison" or whatever else the "i++" is a part of, and THEN the variable is incremented.  I don't see that as an "extra" copy! 

Link to comment
Share on other sites

On 3/4/2023 at 12:19 AM, Love Zhaoying said:

I think this is because the original copy is used in the "comparison" or whatever else the "i++" is a part of, and THEN the variable is incremented.  I don't see that as an "extra" copy! 

Remember that the semantics is different.

Given variable i, an integer:

i = 5;
(++i == 6 ) is TRUE  // Increment var first, then use result of increment for comparison

but

i = 5;
(i++ == 5) is TRUE  // Use original value for comparison, then increment the var

The extra "copy" is only in the stack, but still requires two additional ops: "DUP" and "POP".

It's a carry over from how C works.

Link to comment
Share on other sites

3 hours ago, primerib1 said:

Remember that the semantics is different.

Given variable i, an integer:

i = 5;
(++i == 6 ) is TRUE  // Increment var first, then use result of increment for comparison

but

i = 5;
(i++ == 5) is TRUE  // Use original value for comparison, then increment the var

The extra "copy" is only in the stack, but still requires two additional ops: "DUP" and "POP".

It's a carry over from how C works.

Prior to the current generation of my Parser, I was going to have an option of "peek at a Parm (on the "stack") - but leave it on the Stack". 

Had I kept that option, I wouldn't need the "extra copy" for "i++", without "Peek":

- PUSH/POP (for comparision / action prior to the ++), then PUSH/INC/SAVE

But I would still need (with Peek):

- PUSH/PEEK (for comparision / action prior to the ++), then INC/SAVE.   So, I'd only be saving one operation.

With my current generation parser, I am only supporting "++i" and no "embedded comparison" so, if I wanted to also compare BEFORE increment, I would need to:

- PUSH/POP (for compare), then - PUSH/POP/PUSH (for increment- Push current value, pop it as part of the increment process, push the result..)

It's a long story. lulz. Maybe the current WIP high-level parser will support "i++" with comparison (no point in i++ vs. ++i without a comparison IMHO)..

ETA: I don't have a "DUP", just "get" and "set":

- Get: gets a value either "constant" or an "identifier" onto the "stack".  This is like "PUSH".

- Set: Set an "identifer" or parameter to a value from the "stack". This is like "POP".

 

Edited by Love Zhaoying
Link to comment
Share on other sites

1 hour ago, Love Zhaoying said:

my Parser,

(not really sure this is a good place to be having this discussion, but I'll play along)

If you've ever done any semi-involved work with a stack language like postscript, stack manipulation operations like dup, roll and exch (swap top 2 on the stack) are invaluable when making user-defined functions take arguments in an order that makes sense, but use them in whatever order for their internal operations, although you can probably get around it by binding arguments to function-specific variables.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

1 minute ago, Quistess Alpha said:

(not really sure this is a good place to be having this discussion, but I'll play along)

Yes, sorry - hard to discuss "tips and tricks" when some of us are so..."advanced".

Tip:

- When parsing "messages", your code will use less memory, be easier to read/write, if you use JSON! (Fight me!)

Trick:

- Use JSON for passing messages between scripts (whether llMessageLinked() or llSay(), etc.), and it will take many times less effort to parse the message. (Fight me!)

Before:

A. Use variable "strides"

Example Message Format:   Message = "Cat,3,a,b,c,Elephant,1,a,Zebra,2,a,b";

- This is not really a multi-stride list because the amount of data varies.

- First entry: Normal value of some sort

- Second entry: a Count for "how many to read next" (Entry 

- Next entries: are determined by Count

General algorithm:

1) Convert entire sequence to List

2) Read first entry and/or "search for a specific entry"

3) Read next entry => Count

4) Read next Count entries

 

List list1;

list1 = llParseString2List(message, [","], []);

string sAnimal = "Zebra";

integer iPos = llListFindList(list1, [sAnimal]);

if (iPos!=-1) {  // Found it!

    integer iCount = (integer)llList2String(list1, iPos+1);

//or llList2Integer.()..

  iPos += 2; // Starting point

   while (iCount>0) {

     string sEntry = llList2String(list1, iPos);

// Process the entry..

     ++iPos;

     --iCount;

   }



}

 

B. Use Multiple Separators

Example Message Format = "Cat,a;b;c,Elephant,a,Zebra;a;b";

Pro: No "count" is needed in the data for the sub-entries.

Con: Separators can't be contained in the data. Lists.

General algorithm:

1) Could use llParseString2List() but..see 2)

2) Lists cannot contain Lists.. so, have to keep the "sub-lists" within individual entries of the list until needed.

This becomes a "2-stride" list..

List1 = llParseString2List(Message, [","], []);

string sAnimal = "Zebra";

integer iIndex = llListFindList(List1, [sAnimal])

if (iIndex!=0) {  // Found it!

  List2 = llParseString2List(llList2String(List1, iIndex), [";"], []);

// Parse each value in List2..

  integer iLength = llGetListLength(List2);

  integer iPos = 0;

 while (iPos < iLength) {

     string sEntry;

     sEntry = llList2String(List2, iPos);

// Process the Entry..

    ++iPos;

 }

}

After:

Using JSON:

Example Message Format = 

{

"Cat": ["a","b","c"],

"Elephant": ["a"],

"Zebra": ["a","b"]

}

Pro: No lists, no Count, lists-within-lists is fine, etc. You can access entries directly by "name"/"value", depending on your JSON.

Con: Can't use special JSON characters in data.

//1) Find first-level entry

string sAnimal = "Zebra";

string sDetails = llJsonGetValue(sJson, [sAnimal]); // Note! Accessing the Json entry directly, by "name"/"value"!

//2) Process second-level entries if found

if (sDetails!=JSON_INVALID) { // Found it!

  integer iDetail=0;

  string sDetail;

  sDetail = llJsonGetValue(sDetails, [iDetail]);

  while (sDetail!=JSON_INVALID) {

// Process each entry in sDetail

     ++iDetail;

      sDetail = llJsonGetValue(sDetails, [iDetail]);

   } 

}

..ain't no lists.

 

Link to comment
Share on other sites

38 minutes ago, Love Zhaoying said:

Use JSON for passing messages between scripts

I won't disagree that JSON message passing is very useful when the message complexity warrants it, but communication protocol design could probably take up its own thread. For most of my actual use-cases, I've only needed a single 'command' and a list of arguments, which fits nicely into a 'comma separated value' string.

string message = llList2CSV(["CMD",arg,arg]); // fetched from a listen event, probably.
// ... later, in the receiving script:
{  list message = llCSV2List(message); // TIP: variable name overloading.
   string CMD = llList2String(message,0);
   if("Turn"==CMD)
   {   rotation r = (rotation)llList2String(message,1); // CSV correctly handles rotations and vectors!
       llSetRot(r*llGetRot());
   }else if("Say"==CMD)
   {   integer channel = (integer)llList2String(message,1);
       string msg = llList2String(message,2);
       llSay(channel,msg);
   }// etc.
}

doing that with JSON seems like added complexity for little benefit, but to each their own, and in a different context I could really see a benefit:

string message;
message = llJsonSetValue(message,["CMD"],"Turn");
message = llJsonSetValue(message,["rotation"],"<0,0,0.71,0.71>");
// ...
{  string CMD = llJsonGetValue(message,["CMD"]);
   if("Turn"==CMD)
   {   rotation r = (rotation)llJsonGetValue(message,["rotation"]);
       llSetRot(r*llGetRot());
   }else if("Say"==CMD)
   {   integer channel = (integer)llJsonGetValue(message,["channel"]);
       string msg = llJsonGetValue(message,["msg"]);
       llSay(channel,msg);
   }// etc.
}

(JSON uses a lot of static lists for arguments; I don't think you'll see a performance upgrade using it this way for this example.)

Edited by Quistess Alpha
Link to comment
Share on other sites

5 minutes ago, Quistess Alpha said:

I won't disagree that JSON message passing is very useful when the message complexity warrants it, but communication protocol design could probably take up its own thread. For most of my actual use-cases, I've only needed a single 'command' and a list of arguments, which fits nicely into a 'comma separated value' string.

string message = llList2CSV(["CMD",arg,arg]); // fetched from a listen event, probably.
{  list message = llCSV2List(message); // TIP: variable name overloading.
   string CMD = llList2String(message,0);
   if("Turn"==CMD)
   {   rotation r = (rotation)llList2String(message,1); // CSV correctly handles rotations and vectors!
       llSetRot(r*llGetRot());
   }else if("Say"==CMD)
   {   integer channel = (integer)llList2String(message,1);
       string msg = llList2String(message,2);
       llSay(channel,msg);
   }// etc.
}

doing that with JSON seems like added complexity for little benefit, but to each their own, and in a different context I could really see a benefit:

string message;
message = llJsonSetValue(message,["CMD"],"Turn");
message = llJsonSetValue(message,["rotation"],"<0,0,0.71,0.71>");
// ...
{  string CMD = llJsonGetValue(message,["CMD"]);
   if("Turn"==CMD)
   {   rotation r = (rotation)llJsonGetValue(message,["rotation"]);
       llSetRot(r*llGetRot());
   }else if("Say"==CMD)
   {   integer channel = (integer)llJsonGetValue(message,["channel"]);
       string msg = llJsonGetValue(message,["msg"]);
       llSay(channel,msg);
   }// etc.
}

(JSON uses a lot of static lists for arguments; I don't think you'll see a performance upgrade using it this way for this example.)

Yes, simple commands with few parameters / simple structure have little benefit from JSON.

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 353 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...