Sunday, November 28, 2010

The Not-So-Restrained Life

     Now onto one of my favorite topics, the Restrained Life Viewer (now known as Restrained Love, created by the fabulous Marine Kelley) and its implementation in many other third-party viewers. A lot of people don't know much about this lovely set of controls and functions, and I've been doing my best for some time now to change that. When they hear the word 'RLV', most people think of bondage, slavery, and sex toys. And you can't blame them, really...that's basically what it was created for. However, the possibilities available go much further than domination and other naughty things; in fact RLV provides an avatar with abilities and powers that transcend many limitations suppressing people everywhere. Basically, RLV uses llOwnerSay() messages to trigger many different functions that typically could only be done manually before. Among these functions are the ability to point-to-point teleport, sit on designated objects, and attach (and unattach) items directly from your inventory...all automatically. Any scripter reading this who is not yet familiar with RLV is probably drooling by now...I know I certainly did. This shit is GREAT. The only reason it isn't widely utilized in products is because viewers that support it are somewhat in the minority. Don't get me wrong, plenty of good viewers implement RLV controls, but not enough for the mass consumption of RLV-based products. Anyways, the most common usage of RLV is of course the slave collar. This allows another avatar to control your own in ways very conducive to your nasty little games, but say you made a 'collar' that only YOU could control? You would then be able to harness your avatar in ways unimaginable...but let's imagine some anyways:

~Your items can instantly teleport you to them (or anywhere else)
~You can make vehicles that automatically seat you on them (pose stands too)
~You can make RP chatters that automatically redirect your public chat to another channel, to be processed by script
~You can handle your Windlight settings (your perceived 'sky') by HUD or other device
~You can make outfits or 'transforms' that equip automatically (or instant nekkidness!)
~And more!

     As I said before, RLV doesn't reach most of consumers (especially with the unfortunate business with Emerald shunting loads of people back to Viewer 2). However, it makes a great supplement to your personal repertoire of items and effects. Did I mention it's great for bots? It's great for bots! If I've caught your interest in this amazing piece of technology, you can learn more about it here. And while Restrained Love Viewer might not be the greatest viewer around, as I mentioned before plenty of other third-party viewers use the coding in their own setups (coughcoughPhoenixcoughcough), and they aren't difficult to locate. Try it today!

~Arkane

Worth Your While...

     Let's talk about 'while' loops. First, let me say that they are pretty awesome. For serious. A while loop basically says 'while condition A is true, do statement B...as fast as freakin' possible.' However, due to the single minded nature of an LSL script (and several other types as well), while a while loop is being executed, there's not a whole lot that particular script can do...so they can be a bit hard to stop arbitrarily. Other events that would be triggered wait until after the while loop is done doing its thing. Meaning, you can't just stop them on a timer, or a listen event, or whatever...you likely have to involve child scripts at this point. But, due to what I just said, you can start a while loop off on a link message (the popular method for communicating between scripts in the same prim) but you can't stop it with them. So, what do you do if you want your while loop to run until receiving an outside signal rather than a preset condition evaluating as false on its own (that's basically what 'for' loops are for...lame). Well, its pretty simple, you just have to use a somewhat unconventional 'choke' parameter. For instance, say I have a pusher script. In case you didn't know, push is pretty nasty when set on a while loop, it happens so rapidly it will send targets flying. Well, I want to start and stop pushing on a chat command, for perhaps a force field effect (works great for this btw). My listen event can trigger it easily enough but it is utterly unable to stop once it has begun pushing. So, I take my 'pushing' part of the script, and move it to a child script, dedicated to the purpose. The main script listens for the 'go' command, and sends a link message to the pusher script when it hears it. The while loop starts up, and looks something like this:

while(llGetColor(0) != <1,0,0>)
llPushObject(gTarget,gDirection,ZERO_VECTOR,FALSE);

     As you can see, the condition for the while loop to keep executing depends on face 0 of the prim the script is in being not-red. Therefore, if my main script hears me give the cutoff command, all it has to do is change ol' face 0 to red and...voila, the pusher stops pushing. Probably immediately afterward in the pusher script I will have the face turned back to not-red. Using this method, we don't have to raise any events which wouldn't get triggered anyways, we simply rely on an arbitrary parameter that can be easily manipulated by the main script. You can use prim color, size, description, anything you want really...its terribly simple; it just takes a little thinking outside the box. Doing so puts the full power of the awesome while loop in your hands (timer-based events can't even come close to the speed of repetition here). Before we conclude, let's take a look at another while-based effect:

integer info = llGetAgentInfo(llGetOwner);
 while(info & AGENT_IN_AIR)
info = llGetAgentInfo(llGetOwner);//remember that only this line will execute if unless a scope is declared with { and }
llOwnerSay("landed");
  
     In this example we have a bit of code that presumably would be triggered while the owner is in the air (jumping, falling, etc). The loop will execute as long as the avatar is detected as being IN_AIR, and immediately stop when this ceases to be...continuing on with the code in a nice fashion that doesn't require timers or anything like that. I use logic like this in a double-jump effect where I like to restrict the times you can double-jump to once per in-air session. Another similar application I use this for is spouting off particles when the avatar is detected as typing, and idling until immediately stopping when finished (for chat poofers and the like). Timers can only check conditions so fast and splitting your code into more and unnecessary events might not always be desirable; this is a good way to keep things simple and accurate. So, if you aren't already using while loops to your advantage, I strongly suggest you do so. I hope this article has enlightened you to the power available to those who can harness it. Enjoy!

                                                                                                                                          ~Ark                                      

Saturday, November 27, 2010

The Next Level

     This article isn't as much of a technical one, I just want to take a moment to talk about two amazing supplements to scripters everywhere, PHP scripts and mySQL databases. Guys, seriously. This stuff is worth learning. If you can LSL, you can PHP...and PHP is a billion times more powerful than LSL ever was. mySQL is the simplest stuff in the world. The greatest advantage to using scripts outside of Second Life is that they can be accessed from any point in the grid, quicker than you can say 'hypertext preprocessor'. That means that communicating between sims has never been easier. When you throw a mySQL database into the mix, you can then hard-store data with your LSL scripts like you never could before. Seeing as those are basically the two biggest weaknesses of Second Life scripting, it's easy to tell why mastering these forces can boost your power as a scripter many times over. Believe me when I say it's a lot easier than it sounds. All you need is proper hosting that supports PHP and mySQL, and a few days tops of studying to get used to it. If you are fluent in LSL you will find that you already know many of the functions that PHP is capable of, you just need to find the name and format of each; this info is available at several points throughout the Web. Another thing to consider is that PHP is -so- much faster than LSL, and even if you can achieve the operation of a script entirely in LSL you can pass off much of the work to a similar PHP script to take care of the all of the heavy-lifting involved. So, definitely check this out. It is very much worth the meager time and effort. Perhaps in the future I will post some tutorial articles outlining the setup and implementations, but for now...it's all up to you. If you are serious about your scripting, take the next step!

Arkane's String-Transfer Method

     Alrighty then, here is my first post involving some original coding technique by yours truly. As some of you might have noticed, there is only one type of information that can be directly handed down from a parent prim to its rezzed 'children' (I use the term loosely in this article, as a child prim is typically part of the same linkset as the parent prim), and that is the integer designated as a 'starting parameter'. While start params have their uses, the fact that only integers can be used is quite limiting in application; ideally one would want the variable type to be a string or perhaps another variable type, depending on the situation (a string would be best though, seeing as it can be typecast to any other variable type). Anyone who has run into this problem has likely tried a number of workarounds, most involve bouncing chat messages back and forth, some use HTTP requests (if one has access to PHP and databases), some use more unconventional methods. While a reliable technique is not the most difficult thing to churn out, many people employ rather inefficient or slow ways of transferring this information; the goal of this article is to provide you with the (in my opinion, and I have yet to be proven wrong) fastest and simplest method of transferring a string of info from a rezzer script to a rezzed one.
     So, when I first started requiring a transfer such as the one I've been talking about, my first method worked to great effect, but it was -very- clunky. It would get the info across, sure, but doing things in rapid succession was out of the question. At the time it seemed the only way to do it, so I stuck with it. It involved the arduous process of opening a listen event in the rezzer script, rezzing the object, then having the rezzed object open up it's own listen event and send a ready message (you could theoretically just throw the message blindly after rezzing but, due to lag, the prim might not be listening yet...and adding a llSleep() delay just slows things up further when it might not always be necessary) to the original script...which would then reply with the desired information and listeners on both ends would close. This works well enough, and it seems to be the popular method among many scripters today...however, it is a lot of unnecessary work and can get extremely slow between chat messages in laggy sims. Next, I tried to slightly better effect a method that, after rezzing, would spout off the info on a very short timer event until it received a confirmation from the script that the content went through, similar but still too slow for some really heavy-duty work. Several months later I had the (at the time brilliant, I thought) idea to nix the listeners altogether and use HTTP requests, combined with some handy PHP and a mySQL database. The basic procedure involved posting the transferring info to said database, along with an integer that was designated as a 'reference ID' that could be used to retrieve the info at a later date. This reference ID was then passed along to the rezzed prim as the starting parameter, and then utilized accordingly to snatch the info up from the database. This actually worked surprisingly well, and as fast as the script could handle it; the only downside was that there was a lot of superfluous coding involved...there had to be a setup for converting the info to a plausible HTTP request, as well as an entire http_response event that was only used once and then laid dormant. I used this for about a year's time until one fateful day when my web hosting up and disappeared (long story)...leaving me without one of the most important functions (among many, many others) that my scripts relied upon to operate. Needless to say, I had to come up with another method to run said scripts on, because I sure wasn't looking forward to going back to the clunky original procedure. Thus, the following setup was born...and although I'm probably not the only one to utilize such methodology, I sure don't want you guys to have to go through the same long process of finding out how to pull it off that I did. It is both fast AND uses a minimal amount of coding, and doesn't rely on an outside source such as databases and the like. So here it is...Arkane's String-Transfer Extraordinaire. Let's take a look:

     First, we have the object_rez event in the rezzer script

object_rez(key id)
  {
       while(llList2String(llGetObjectDetails(id,[OBJECT_DESC]),0) != "listening") //check the description field of the rezzed prim
        ;//do nothing until the prim is ready
      integer myChan = -1 * (integer)("0x"+llGetSubString((string)id,-5,-1));//generate a random channel based on the rezzed object's UUID
        llRegionSay(myChan,myString);//spout it off 
   }

     Basically, what is going on here is this: once the object is rezzed, the rezzing script checks VERY rapidly (as fast as it can process, really) the description field of the rezzed prim, waiting for the moment it says 'listening' (to indicate the listen event is open, duh). Once this is accomplished, an channel integer is created that is based off of the UUID of the rezzed prim (the rezzed prim will have already arrived at this same integer and opened a listener on that channel) to regionsay the info on...and then subsequently do so. That's all the work that the rezzer script has on its plate, its job is now complete. No listeners, no timers, no nothin'. The above code can  be compressed to just two lines (I expanded it for purposes of the example), here's what it looks like:

object_rez(key id)
  {
       while(llList2String(llGetObjectDetails(id,[OBJECT_DESC]),0) != "listening");
        llRegionSay(( -1 * (integer)("0x"+llGetSubString((string)id,-5,-1)) ) + 135,myString);  
   }

     Pretty neat, huh? Quick, easy, and to the point. Now let's take a look at the code the rezzed prim uses:

on_rez(integer start_param)
    {
    if(start_param)
{
 myListen =  llListen( -1 * (integer)("0x"+llGetSubString((string)llGetKey(),-5,-1)),"","","");//open up the listener, based on the prim's UUID
   llSetObjectDesc("listening");//let the rezzer script know it's ready to receive the info
}
     }

     The above coding basically does the following: if it has a start parameter set (meaning it came out of an object and not inventory, make sure to accommodate for this in your original llRezObject() or llRezAtRoot() call), then open up a listener using the same logic as the rezzer script. After this has been accomplished, simply change the object's description field to 'listening' to let the rezzer know it has done so. Now let's go to the listen event used to handle the info:


 listen(integer chan, string name, key id, string msg)
   {
       llListenRemove(myListen);//always be responsible and take out unused listeners
          myInfo = msg;//store the info in a variable
}

     Isn't that easy? We remove the now useless listener and capture the info that came through it in a variable, to be used in whatever manner might be required. The whole process is almost instantaneous, and is pretty much infallible, assuming that the script is able to function (rezzing and scripts must be turned on for the parcel, of course)...and you don't use any code that might deliberately tamper with it. If you need the description field for other purposes, consider instead changing the color, size, or related parameter of the prim to something that would indicate that the listener is now ready to receive information. I'm positive you can find some way to do this without harming your other processes that you might be utilizing, there are loads. And that is that, hopefully this article helps you find a better way to run your data-transfers. And please, if you have and questions, comments, complaints, or even (especially) a better way of achieving this effect, feel free to leave a comment or message me in-world.
                                                                                                                       ~sugArkane Flux

Yes Outside Scripts!

Another important thing to take into account when dealing with scripted objects is the dreaded no outside scripts parcel. These are a huge downer and I personally try to stay away from them, but there are times when one must enter them anyways. The no-script rule exists for various reasons, and is entirely up to the parcel owner on whether or not to apply it, but even then...sometimes you just want your stuff to work! Fortunately there is a small workaround implemented by the Lindens to preserve the functionality of vehicles, AOs, and the like. Basically, a script that has taken controls from an avatar will NOT cease to function when entering a no-script parcel. That being said, here is a small example of a non-intrusive way to take controls from the owner without actually doing anything with them:

llTakeControls(CONTROL_LBUTTON,FALSE,TRUE);

This takes controls from the left mouse button but does not accept (process) them. You don't even need a control event to handle the input.

There are a few supposed limitations to this rule, however. For starters, the script must have already taken controls BEFORE entering the parcel, else the script cannot start up to take controls in the first place. Also, certain "blacklisted" functions will cause the script to stop working anyways. I have not tested this in detail so I can't tell you just which ones they are...the obvious answer is to experiment. This workaround is good for non-obtrusive scripts such as radars, AOs, dash huds, and the like. It's not so great for things like rezzers, for while the rezzing will still take place, if the rezzed object is scripted it will not be able to run properly. Anyways, always something to consider when making your various objects.

Side note: Some functions seem to work in no-script areas in a script that isn't even taking controls, for instance llOwnerSay(). That happens to be the only one I've tested, but it does prove my point. So, again, experiment a bit...your script might not even need to take controls to keep working.

Truly Warped

Edit: This method is obsolete with the release of llSetRegionPos(). I'm leaving the post as is, since there are still some interesting techniques demonstrated here.

Ever run into that extremely frustrating 10m cap on moving a prim to another position? Just another example of the Lindens trying to ruin our good time. Fortunately, thanks to Keknehv Psaltery and a few other talented scripters, we have this lovely little function-in-a-can commonly known as WarpPos. WarpPos uses a little oversight in the llSetPrimitiveParams implementation to possibly the greatest effect in the history of workarounds. In a nutshell, llSetPrimitiveParams executes a list of changes to the parameters of a prim in immediate succession. It is typically used for morphing the shape and size of prims, among other things, in a single function; without it we would have to call several functions in a row to achieve a similar effect. However, our boy Keknehv came up with the brilliant idea to stuff this list with many different calls to the same parameter...to change the position of a prim (imagine said prim puttering along 10m at a time in a straight line to the destination). While this could have been achieved with multiple calls to llSetPos, the script delay of 0.1 seconds makes this trip a great deal slower...with llSetPrimitiveParams, it happens in a single movement; so fast you can't even tell there are any movements beyond the first. Because of this fabulous bit of coding, many of the teleporters, weaponry, and all the things that need to move a great distance FAST are made extremely possible. So without any further delay, here is WarpPos!

warpPos( vector destpos,rotation destrot)
{
    //calculate the number of jumps
    integer jumps = (integer)(llVecDist(destpos, llGetPos()) / 10.0) + 1;
    //distance cap, can't go more than 4110 in a single warp
    if (jumps > 411)
        jumps = 411;
    list rules = [ PRIM_POSITION, destpos ];  //The start for the rules list
    integer count = 1;
    while ( ( count = count << 1 ) < jumps)
        rules = (rules=[]) + rules + rules;   //should tighten memory use.
   //putter along to the destination with the constructed rules list
    llSetLinkPrimitiveParamsFast(LINK_THIS, rules + llList2List( rules, (count - jumps) << 1, count) );
    if ( llVecDist( llGetPos(), destpos ) > .001 ) //Failsafe
        while ( --jumps )
        //when within less than 10m of the destination, do one final jump
            llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_POSITION,destpos,PRIM_ROTATION,destrot]);
}

I'm not going to go into extensive detail on how this works, but I will attempt a general outline. Basically the script first calculates the distance the prim's current position and its destination. Then, diving by 10 (the maximum distance in meters for a single jump) will give the number of jumps required to reach the destination. Next comes a bit of a safety cap (which CAN be removed should you require some REALLY long warping) that limits the maximum distance of the warp to 4110 meters (or 411 10m jumps, modeled after the 4096m ceiling on active scripts). Personally, I don't care for this cap, and remove it on my own scripts when necessary; it doesn't actually prevent the prim from going above the 4096m altitude anyways. Then we have a bit of complicated data-pushing that builds the step-by-step list of position changes necessary to reach the desired coordinate, followed by the execution of said list. Finally, once we are close enough to reach the final destination with a single jump, we do so...and voila! Faster than you can say 'ludicrous speed' we have closed the gap on a potentially enormous amount of distance with relative ease. Take that, Lindens (although, to their credit, they decided to support this 'bug' rather than 'fix' it)! One more thing worth pointing out is that, if implemented properly, scripts with WarpPos can easily traverse to coordinates in other sims, even hopping across several at a time. However, sitting on a prim that is doing this is -not- advised, you will probably crash (without avatars on them though, prims can change sims with ease). This amazing piece of code is available on the wiki, albeit with different commenting and some minor differences (I updated all parameter-changing functions to use llSetLinkPrimitiveParamsFast, as well as adding in a bit to allow for controlling the orientation the prim arrives at as well). I hope you enjoyed this article, and that you use this knowledge to do some heavy-duty warping in the future :)

                                                                                                                                      ~Arkane

Ghost Children!

Here's a nice little trick that admittedly is not my own work but is a pretty big deal nonetheless: making one or more child prims of a linked object phantom while the rest remain non-phantom. If I need to explain why this is advantageous to you, it probably isn't. Anyways, the actual implementation is very simple; all you have to do is select the desired child prim (already linked to the rest of the object), make it flexi, and then make it non-flexi again. It's that easy. However, while this is the simplest method, there are a couple of factors that limit its effectiveness. The first is that should the region restart, or the item be taken to inventory and re-rezzed, the affected prims will lose their phantom status. The second is a tad more important, and you might already have spotted it: not all prim types can be turned flexi. Suffice to say, this method has its uses but is only superior in its simplicity. However, the script below combats both of these issues, and makes phantom child prims a very reliable and easy thing to produce. Let's take a look:

default {
    state_entry() {
        //here's where we turn the prim flexi and back
        llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_TYPE, PRIM_TYPE_BOX,
            0, <0,1,0>, 0, <0,0,0>, <1,1,0>, <0,0,0>,
            PRIM_FLEXIBLE, TRUE, 0, 0, 0, 0, 0, <0,0,0>,
            PRIM_TYPE] + llGetPrimitiveParams([PRIM_TYPE]));
    }

    on_rez(integer s) {
        //do it again when re-rezzed
        llResetScript();
    }

    changed (integer vBitChanges){
        //and again whenever the sim restarts
        if (CHANGED_REGION_START & vBitChanges){
            llResetScript();
        }
    }
    collision_start(integer num_detected){
        //and hell, again if something hits it, since that only occurs when non-phantom
        llResetScript();

    }
}


So, as you can see, this script is placed in the child prim you want to affect. Upon starting up, it rapidly changes the prim type to a flexi Box, and then immediately back to whatever it was to begin with. It is coded to avoid losing any parameters in the switch, even, so you don't end up with a funky prim afterward. Then, whenever one of three different conditions are met (re-rez, region restart, or collision), it repeats the effect. Simplicity at its finest, allowing you to bypass one of the most frustrating obstacles during building. This script can be found on the wiki (although a tad different, without the comments and an unnoticeable nanosecond slower in operation; mine uses llSetLinkPrimitiveParamsFast instead of llSetPrimitiveParams). Hopefully it, along with this article, helps you in your quest to become a better builder. That's all for now then, til' next time.

Let us Begin!

Alright, so I've seen plenty of SL blogs around lately, and very few of them catch my interest at all. So, I figured I would make one of my own, dedicated to the topics I see fit; most will be technical in nature (and thus very boring) but for those actually interested, this will be the compendium of the tricks, maneuvers, and workarounds that I have come across during my life as a SL scripter/builder. So, check in from time to time...you might learn something.