Friday, June 19, 2015

Using Media as a Stand-Alone HUD

    Any SL Resident worth their salt has used a prim HUD at some point...while they get the job done, they are clunky and sometimes difficult to interact with. Chat commands, dialog menus, and the like are a poor attempt at an interface for these devices; each has its limits and downsides that have been largely ignored because they are still the most feasible option available. Enter: Shared Media (or Media-On-A-Prim....MOAP). Most people know it as that thing that lets you look at porn together on the grid.  There are a lot of obvious possibilities for a web-based interface for many things - however, you'd need hosting and all that junk. That's enough to turn a lot of people away from this sort of project, but what if I told you you can host websites inside of prims themselves? It's true - you can serve HTML (and JavaScript, and technically CSS - more on that later) to MOAP from within a single LSL script. Completely standalone, with all the flexibility and power of web design at your disposal. Buttons, text fields, images, video, sound, hyperlinks, and whatnot! So much whatnot! And no hosting required! Are you excited? I know I am.
    First, I'm going to mention a couple of slight drawbacks, and the way to work around them. The first is that there doesn't seem to be a way for LSL to serve content in the CSS format; the solution I came up with was to load the CSS dynamically, stored in JSON format instead of traditional CSS. A simple bit of JavaScript takes the JSON data and applies it to the document's stylesheet.
    Another thing that needs mentioning is that LSL will not serve HTML-formatted content to a person who is not the script owner. This is an attempt to keep people from hosting websites inside prims, or some such. Well, I worked around it...All you have to do is serve the content as plaintext and then apply it to the innerHTML attribute of an iframe element. In the context of this article, this means that your HUD can access HTML content that is stored in external objects, dynamically, instead of being limited to the ones you have stored locally in the device. There are all sorts of ways you can apply this to produce a much better interface for objects than the typical dialog menus and whatnot.
    I'm not going to make this into a full-blown tutorial - I am merely going to describe the processes, link a few resources, and (eventually) include a working example. There are a few working examples already available on the wiki, but I find my approach is slightly different, more developed, and handles a few important shortcomings.
    I'll start with describing the general way Shared Media works...it is a little strange and not entirely obvious; I had to do a good bit of experimenting before I really understood what was happening. When you change the Media URL, it will connect all users in the sim with MOAP enabled to that URL. Everyone who can see it will see directed to that page. So if you were to look at the requests being made to that URL, you might see a few or even several different requests coming from loading up just one page. If the server isn't anticipating this kind of behavior, it might act a little weird. However, once the URL has been reached, the 'shared' part of Shared Media is over. You can think of it like being brought to the same starting point, then setting off on your own individual path. If you interact with that page in any way, you are the only one who sees the effects. You will not 'sync up' again with the other people viewing the content until the URL is changed again. The reason I bring this up is because it not only overcomplicates things but increases the work involved by several multitudes by having so many HTTP requests through your server. The way I avoid it is by loading up a 'Start' screen that is lightweight and doesn't have anything on it but a button to proceed with. Whoever clicks the button will then be (individually) redirected to the main content of the HUD. The kicker is, the HUD can only be seen and clicked by you! If you happen to be making a non-HUD device, you can set PRIM_MEDIA_CONTROL to PRIM_MEDIA_OWNER so that only you can click that button to proceed to the main content. This keeps everyone else on the splash page, leaving you free to browse in private. Keep in mind you have to control the page content through the page itself, and not the URL bar, or else it 'shares' that content with everyone all over again.
    Perhaps I'm getting ahead of myself, though. The MOAP connects to a url which the LSL script inside the prim has already created with llRequestURL(). If you didn't know about this, prims use this function to generate an actual, working URL that can be accessed with an HTTP request. Like, through the Internets! Whoa! Imagine entire websites living in prims...but HUDs only, of course. An important fact to take notice of is that the LSL script and the MOAP are two entirely different entities, working together in one device. They cannot communicate with each other except through HTTP requests. This is why we use llRequestURL() - to create a way for them to communicate. The MOAP connects to the URL of the script, which fires the http_request() event. This event contains all the data of the request: the path info, the query string, any metadata, all that stuff. You can pick this info apart to find out exactly what your MOAP is trying to do, and respond to it accordingly. Use llHTTPResponse() to send info back to it, like a load of HTML for instance! Here's the general way I handle my requests to such a device:

myscripturl/contacts/email_addresses.html?name=john

The first part of this URL is simply the script you are connecting to. The next bit, '/contacts/john.html' will be available as path info by the LSL script. The script can then infer that it needs to find a script named 'contacts', which contains 'email_addresses.html'. The part after the question mark is the query string, which can then be handled accordingly to produce John's email address specifically. Ultimately, you can handle path and query info however you want. This is the method I use as it resembles more conventional dispatching methods. Also, since all of the strings are stored in slave scripts (to be processed by the master script), you mostly avoid one of the issues regarding space and available script memory. You might be, at first, inclined to try and stick many pages into one script; if you are building any sort of complex device, take my word for it that this is not enough space for the strings you are handling and you need to spread them out a bit more. I like to keep my scripts in the Not Running state until they are needed, since they are using a lot memory.
    Keep in mind that you don't have to just serve HTML here. If you reference an external JavaScript or text file from within your HTML, for example

<script src='myscripturl/resources/my_script.js'>

the MOAP will then make another request to grab a string of text called 'my_script.js' from the LSL script 'resources', and use it as JavaScript for the page. While it does take a bit longer to load things from external sources, this is only necessary because of the memory and string limits within SL. And it's not that bad, as long as you don't overdo it. I mentioned earlier that I use JavaScript to apply CSS - instead of saving files in the CSS format, I save them as JSON Objects that can be understood by the JavaScript easily. This is something that's not really covered on the wiki - I had to come up with this method myself. This is what such a function would look like:

function loadCSS(file)
{
var css = httpGet(file);//this is a seperate user-defined function for sending an xml http request
var obj = JSON.parse(css);
var params = [];
for (var key in obj) {
  if (obj.hasOwnProperty(key)) {
    var rules = obj[key];
    for (var kv in rules)
        params += [kv+':'+rules[kv]];
    var style = document.createElement('style');
    style.type = 'text/css';
    document.getElementsByTagName('head')[0].appendChild(style);
    style.sheet.insertRule(key+' {'+params+'}',0);
  }
}
}

The script basically acquires the JSON-formatted CSS parameters, iterates through it, and inserts each item into a new CSS style object of the proper format. You can hardline your CSS directly into your HTML, but again, the purpose behind this is to allow for bigger and bigger files to be used for your device. If you have a decent dispatching system, and you can serve up HTML, Javascript, and CSS as individual files, you have a damn fine start for a LSL-based webserver. The only thing you might be wanting (other than an updated webkit that isn't five years old and broken) would be some access to PHP to do some pre-processing. No need! You can use LSL to pre-process your markup before serving it! Here's what I do: I have a list of tokens such as ${NAME} and ${AVATARS} and ${URL} that I can just include anywhere in the markup. When it runs through the pre-processor, all instances of those tokens are replaced with the appropriate info. So, a page with '${NAME}' in it would come out, in my case, as a page with 'Arkane Flux' instead. You want to go easy on the number of evaluations you do like this though, as searching through strings can get a bit heavy on resource consumption. You can use whatever method you like, in the end; this is just what I prefer. I am intentionally staying away from direct examples because this is less of a proof-of-concept and more of a summarization of the things I've had to consider and work around in my experiences creating such devices. CherryPy users might recognize the style of token I use. CherryPy and other webserver frameworks are definitely worth looking into as well, but are out of the scope of this article (we are focusing on locally hosting files). Coming back to the topic, LSL can be substituted for PHP in many cases - I would stay away from heavy lifting, though. PHP is FAST, LSL is SLOW (by comparison). Again, it's out of the scope of this article but if you need to do some big preprocessing work, there is nothing wrong with outsourcing the job to an external PHP script. I have not encountered such a need with the devices I have made, however. It is just good to be aware of your options. (While I'm writing this, I'm considering trying to serve a PHP file from LSL. I'm not sure it can be done, but if it can I will update my info accordingly. If the pre-processing can be done through the MOAP somehow, instead of the LSL backend, I believe it would be a lot faster. I haven't been held up at all by my current method, so there has been no reason to explore this option). This pre-processing is how you can send your query string parameters and other info directly into the markup, without having to use additional HTTP requests to acquire it.
    So that's basically it, as far as the actual requesting and serving of files goes. There are a lot of other points and concepts I could talk about, and so I will offer a few that I think might be interesting to you as a creator of a MOAP HUD:

~There is no way to serve images, video, or audio from LSL, but you can still embed them from external sources. Keep in mind the MOAP webkit is quite outdated, and you will likely have a lot of issues making content involving audio and video. Nothing that can really be done about it (by us). You can forget about HTML5, as well.

~Know what a Data URI is? MOAP supports them. Read about it here: http://wiki.secondlife.com/wiki/Shared_Media_and_data_URIs. This is pretty cool, because you don't need a HUD to display HTML content this way, as it is not using llHTTPResponse() to serve it. The input length is limited to 1000 bytes, which can be worked around with TinyURL. It has its uses, however it is not ideal for the technique discussed in this article because of the limited content length, and also the fact that since it is entered through the Media Control bar, it is shared with everyone (instead of keeping them on the splash screen as discussed before). Now that I'm considering it, I will probably use a data:URI for the splash screen and then proceed with the regular method after that. It always helps me to talk about these things.

~Focus on making your content responsive. Remember, these ain't your daddy's SL HUDs. You've got access to font control, hover effects, GIF files, and more. Really jazz it up! The main reason to make a HUD like this is for a superior interface. Consider animated backgrounds, transition effects, and the like.

~The webkit DOES use a cache. You will notice that images load slower the first time around, and faster after that. Use responsible caching methods to increase the overall speed and performance of you device. If you don't know what a CSS sprite is, look into it.

~Eventually I will make an example HUD that combines all of these principles in a working demo. Eventually. I'm still finding new ways to improve my system (I came up with a few new ideas while writing this), so, once I feel I've really covered all the bases, I will release it. Until then, check out this one by Kelly Linden: http://wiki.secondlife.com/wiki/HTML_HUD_Demo

    This about wraps up my presentation. Although these concepts are not new, people are always surprised to hear that you can do such things without external hosting. I always end up talking about it for days...now, I can simply link them to this! I hope this inspires more people to involve themselves in making the HUDs of the future!

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