Arranging llDialog buttons

Okay, back in Second Life! And/or OpenSim.

A purely Scripting-geekery post today, on a really niche topic.

When you get one of those blue dropdowns (or pop-ups, or drop-ins, depending on your viewer) with a number (up to twelve!) of buttons in them (when you touch a danceball for instance and it asks you what dance you want to do), the piece of LSL (Liden Scripting Language) that shows you that thing with those buttons is a built-in function called “llDialog“.

(All of the built-in functions start with “ll” which presumably stands for “Linden Lab”, but which has made many beginning LSL scripters, including Yers Truly, wonder how an identifier can start with eleven at all.)

One of the arguments to llDialog is, naturally enough, a list of the strings to put on the buttons. And the order in which the strings are copied from the list onto the buttons is… perhaps counterintuitive. For instance, if the list looks like:

[ "one", "two", "three", "four" ]

then the dialog as displayed will look like:

which might not be the first thing one would have guessed.

It basically starts at the bottom, filling rows upward as it goes, until it runs out of strings.

I have sometimes fiddled around to make things come out how I want them to manually, and more often have ignored the problem entirely and just not cared that the buttons were in a stupid order.

But for some reason today I got tired of it and decided to fix it; so here:

list arrange(list l) {
list outl = [];
integer n = llGetListLength(l);
do {
if (n<3) return outl + l;
n = n - 3;
outl = outl + llList2List(l, -3, -1);
if (n==0) return outl;
l = llList2List(l, 0, -4);
} while (TRUE);
return []; // UNREACHABLE
}

This will take a list in an order, and return a list in a different order such that if you pass the new list to llDialog, the labels on the buttons will be in the order that one might expect, left to right and up to down. As in for instance:

llDialog(avatar_id, prompt, arrange(button_list), channel);

It basically just divides the input list into pieces of size three, and then reverses them, with complications in case the input list isn’t equally divisible by three. And because if you ask LSL to copy the first through fourth-to-last (inclusive) elements of a three-element list, it silently copies the entire list rather than (as one might have expected) copying nothing. And because it doesn’t realize that a “do while(TRUE)” will never exit and without the unreachable line you get an error saying that not all code paths return a value. And because llGetListLength is said to be computationally expensive. And because LSL does let you use negative indices (roughly) on lists!

This works in both Second Life and OpenSim, because the scripting languages are extremely compatible for obvious reasons.

Many, many, implementations of this same algorithm no doubt exist all over the place :) but this is mine, for the moment and for what it’s worth!

Randomly generating terrains

… aka “When the Creator misplaces a parenthesis”. :)

So my first attempt to write a program to generate random but pleasing landforms for my Sim on a Stick did not go quite as expected.

When the Creator God misplaces a paren...

Bit spikey, for instance.

But then I found a misplaced parenthesis! And things got better.

Ah, much better

I think I am liking the algorithm…

Landforms from the RNG

For the curious, here is the basic (recursive, self-similar) routine, in everyone’s favorite language: Python. (Squished down to fit in the weblog here, not guaranteed to compile as-is.)

def fill_in_terrain(t,lowx,lowy,maxx,maxy):
   ''' Takes the input terrain, where the low-by-max '''
   ''' corners are already filled in, and fills in '''
   ''' everything inside those corners, by the '''
   ''' Magic of Recursion. '''
   if maxx-lowx<2: return t
   if maxy-lowy<2: return t
   xmid = int((lowx+maxx)/2)    
   ymid = int((lowy+maxy)/2)
   hscale = (maxx - lowx) * HSCALE
   # first the corners
   if t[lowx][ymid] is None:
     t[lowx][ymid] = (t[lowx][lowy] + t[lowx][maxy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   if t[maxx][ymid] is None:
     t[maxx][ymid] = (t[maxx][lowy] + t[maxx][maxy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   if t[xmid][lowy] is None:
     t[xmid][lowy] = (t[lowx][lowy] + t[maxx][lowy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   if t[xmid][maxy] is None:
     t[xmid][maxy] = (t[lowx][maxy] + t[maxx][maxy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   # then the center
   if t[xmid][ymid] is None:
     t[xmid][ymid] = (t[lowx][ymid]+t[maxx][ymid]
                     + t[xmid][lowy]+[xmid][maxy])/4 
                     + hscale - math.floor(2*hscale*random.random())
   # and recurse on the four quadrants
   t = fill_in_terrain(t,lowx,lowy,xmid,ymid)
   t = fill_in_terrain(t,lowx,ymid,xmid,maxy)
   t = fill_in_terrain(t,xmid,lowy,maxx,ymid)
   t = fill_in_terrain(t,xmid,ymid,maxx,maxy)
   return t

def get_terrain(x,y):
   ''' returns an [x][y] array of floats representing a terrain; '''
   ''' range unpredictable '''
   ''' x and y ought to be powers of two, for best results.  '''
   ''' oh, and equal I suppose '''
   answer = [ [None for a in range(y+1)] for b in range(x+1) ]
   answer[0][0] = 0
   answer[x][0] = 0
   answer[0][y] = 0
   answer[x][y] = 0
   answer = fill_in_terrain(answer,0,0,x,y)
   return answer

Fun that all these pretty hills and things come out of that little bit of (also pretty) code…

Breeding like prims

So yet another thing about having one’s own private virtual world is that one can do all sorts of fun but dangerous stuff that one wouldn’t do in anyone else’s.

The main fun-but-dangerous thing, naturally, being the creation of uncontrolled replicators.

(Carefully controlled replicators are safe even for, say, Second Life; and of course SL has the Grey Goo Fence, which is good for the Grid but can be annoying for Residents.)

The other day in my local OpenSim-on-a-stick I made a little physical sphere that randomly moves around. It promptly took off into the open water ‘way off the side of the sim (into space that, strictly speaking, doesn’t exist).

So then I made a tall green wall, and put the randomly-moving ball inside the wall, and that was better.

Then I taught the ball to make copies of itself, and also to randomly delete itself. I set the probability of replicating just barely above the probability of self-deletion, knowing from my days as an Anti-Virus Guru that this should lead to an (initially) slow population increase.

Then I got bored, watching the number of balls inside the wall go from one to two to three, back to two, back to three, to four, back to one…

So I cranked up the probability of replication a bit. And then of course the phone rang and I had to open a different window to do something, and when I went back to the viewer:

Runaway 1

Oh, dear.

Well, no problem, I thought; the sim was running kind of slow, but I could just cam up, select everything nearby, deselect the wall, and hit Delete.

And that worked great, except that in the few seconds between my selecting everything and hitting Delete, another few dozen balls had spawned, and they weren’t selected or deleted, and then a couple seconds after that the sim was running so slowly that deleting stuff wasn’t really working anymore.

And then I noticed…

Runaway 2

a potential crisis! If the balls were to start spawning outside the wall, it seemed not unlikely I would have to restore the sim from the last OAR file I took or something.

On closer investigation there were at least three balls that had apparently escaped the wall:

Runaway 3

but when I went to examine (and hopefully quickly Delete them), it turned out none of them actually existed as far as the sim was concerned, the viewer was just confused as a result of all the physics and lag going on. So that was good.

I thought I would log out and then log back in as the AV with Estate powers (Simona Stick, rather than Test User). I noticed that over on the Opensim console, things were not entirely happy:

Runaway 4

It look a long time to settle down again when I tried to log out, and at this point I started to panic a bit. In the time it would take me to log in as the estate owner and try to turn off scripts or whatever, would the sim have become utterly unusable? So instead I did a shutdown on the Opensim console. This resulted in lots more red messages for quite awhile…

Runaway 5

but eventually it did shut down.

Okay, smartypants, so now what? Well, it turns out that Opensim in Sim on a Stick happens to use a MySQL database to store everything in, and that due to the Day Job I have some knowledge of MySQL, and the database tables that Opensim uses are at least somewhat documented.

So once I’d figured out the MySQL username and password that SOAS had installed, I logged into the MySQL console, and viola:

mysql> select count(*) from prims;
+----------+
| count(*) |
+----------+
| 1482 |
+----------+
1 row in set (0.00 sec)

So we have fourteen hundred and eighty-two prims. Fortunately all of the self-reproducing ones have the same name, and each rezzed prim (as far as I can tell) corresponds to one or more records in just three different tables.

First we delete the shape-records for the prims (Opensim could save considerable space in the database by compressing out duplicates here, one would think):

mysql> delete from primshapes where UUID in (select UUID from prims where name="moving and reproducing ball");
Query OK, 1294 rows affected (0.05 sec)

So it looks like we have about 1294 replicators

Then, we destroy the contents (prim inventories) of all of them:

mysql> delete from primitems where primID in (select UUID from prims where name="moving and reproducing ball");
Query OK, 2385 rows affected (0.14 sec)

And that more or less makes sense; each replicator typically contains a script and a copy of itself, so we’d expect to have roughly twice as many contents as we have replicating prims. 2385 is somewhat less then twice 1294, but that’s because of that “roughly” in the last sentence, which I will not go into in detail.

And finally, we remove the little beggars themselves:

mysql> delete from prims where name="moving and reproducing ball";
Query OK, 1294 rows affected (0.05 sec)

Another reassuring 1294.

Now we would expect to have 1482-1294 prims left, and:

mysql> select count(*) from prims;
+----------+
| count(*) |
+----------+
| 188 |
+----------+

(counting on fingers) that adds up!

Crossing our fingers and venturing back into the world as Test User, we find:

Runaway 6

The wall (and everything else) undamaged, but the nasty replicators gone! And sim performance back to normal. So huzzah!

Disclaimer: I have no idea whether the steps above are actually the right way to remove a bunch of nasty things from an OpenSim instance, and for all I know they have left the database in some incoherent state that will cause it to die horribly tomorrow. If your Opensim world is valuable to you, take frequent backups and don’t go messing with the database directly in MySQL because of something you saw in a weblog once. Also, in retrospect, it probably would have been cleverer to use MySQL just to turn off scripting for the estate, and then gone in and cleaned up normally inworld. But hindsight is a penny earned!

Sim-on-a-Stick and Kitely: they both work!

After I described my uber-l33t bat file hax0ring the other day, Ener Hax emself commented that the latest Sim on a Stick would detect 32 vs 64 bit and do the right thing All By Itself, and also had a more recent version of OpenSim, and a thing to let you choose three different styles of world layout, and so on.

So I tried it! In particular, I exported what I had built so far (i.e. the increasingly awesome house, pictured here being built still in the older OpenSim I think:

Test User builds a house!

) to an OAR file on my laptop’s hard drive, using the actual Opensim documentation, imported it to the sim next door on my USB key just to make sure it worked (matter duplication!), saved Test User’s (tiny) inventory to an IAR, shut down that little OpenSim, installed the latest one from Sim On A Stick (they both fit onto the USB key with room to spare), developed even more l33t Opensim skills by finding out Test User’s UUID on the old one, creating a Test User with the same UUID on the new one, imported the OAR and IAR, and poof!

Test User was ready to start building again in the new moderner and unhacked-by-me virtual universe:

Interior Design in a USB key

And that was extremely cool.

After building a little more there I thought the next ossum thing to do would be to put this little house-in-a-world onto the public Web somewhere were other people could (potentially) also get to it.

Kitely was the obvious choice, so I looked at various other things first. :) If you are a company wanting to pay a small (by company standards) monthly fee for a virtual server in the cloud, there are lots of people eager to take your money. But if you are an individual wanting really not very much at all for hopefully more or less nothing, the choices are more limited as it turns out.

So Kitely it was! First I went back into the local USB-key world and put a crate in the yard and put all of Test User’s inventory items into it and made a new OAR (since Kitely I think will let you upload an OAR but not an IAR). Then I joined Kitely on the Free plan (by logging into Twitter and telling it to tell them that I was me, which is cool and/or scary), created my one free world, uploaded the OAR, and logged in…

kitely_001

Is that Test User’s house we see in the distance?

kitely_002

Hurrah, it is! (Including the inventory-crate in the yard, and the various textures harvested from the Innertubes, and their accompanying baggage of moral and legal copyright questions!)

Let’s go inside and check out the details.

kitely_003

Everything seems to have come over just fine, including the genuine Dale Innis art on the wall there, with the subtle band of color on the black horizontal piece, that moves slowly from one side to the other, changing hue as it goes. (And the amusing 70’s-style orgy picture in the background.)

Back out to the yard, and the crate of inventory items, and here is Kitely Dale with my favorite custom OpenSim eyes and hair, and Eloh Elliot skin, and home-made “n00b” tee shirt, ready to take on the world! (At least as long as the free two hours lasts, after which I will have to consider buying some Minutes a la carte.)

kitely_005

Fifteen minutes later, there is a nice wall around the yard:

kitely_006

and the universe is threatening to fork. I tried to export it, with the modifications, from Kitely as a new OAR, but apparently I don’t have enough Kitely Credits for that. And my patience for figuring out virtual currencies and stuff had apparently run out, because I went off to write in my weblog here rather than finding out about Kitely Credits and continuing on that path tonight.

Pretty neat, eh? Maybe sometime I will throw a party there! If I can figure out how the Minutes and Kitely Credits required would work an’ all…

World in my Pocket (USB Windows 64-bit version)

So I stumbled across the ‘Sim on a Stick’ idea for the Nth time (that one is Tateru Nino’s; see also SimOnAStick dot com from the adorably pink iliveisl entity, and NWN’s recent coverage), and this time I thought I’d try it.

Tateru’s ZIP file doesn’t actually work under 64-bit Windows, which is what the fancy laptop here runs, but it almost does. The symptom is that after the opensim console says “APPLICATION” you get a popup about opensim.exe being evil, like so:

and then a big Java-or-something traceback about a bad image and an invalid format and similar geeky stuff shows up in the console and it acts as though you’d typed “shutdown”:

I poked around a little, and found the fix here. Basically you just have to run opensim.32bitlaunch.exe instead of opensim.exe (this runs the 32-bit opensim; actually running a 64-bit opensim would be more work, especially since at least one important component, ODE, doesn’t seem to have a 64-bit build that I can find).

So what I did, concretely:

  • Open the usb-opensim folder on the USB key in Explorer,
  • Copy and paste the “opensim” thing, so there are two now,
  • Rename to new one to “opensim64”
  • Rightclick on it and Edit
  • Find where it says “opensim.exe”,
  • Change that to “opensim.32bitlaunch.exe”,
  • Save and exit,
  • Now run your world just as Tateru says, except:
  • Where she says to run opensim.bat, run opensim64.bat.

And that’s all! Mere moments later (and after not being able to figure out how or if it’s possible to tell Firestorm to point at anything besides SL, and being glad I still had an Imprudence around), I was duckwalking around, uploading some great old Eloh Elliot skins and my “noob” tee shirt, and making some brand-new prims on my very own laptop!

That is the comely and talented Test User, wearing capris from the small clothing collection that comes in the ZIP file, the abovementioned skin and tee shirt, and the ever-popular New Hair and New Eyes, admiring the First Three Prims Ever Created In This Particular Universe.

Which is really quite cool, in its own way…

Combat System Scripting, interlude: OpenSim

So on our last post in this thread someone asked if this stuff would work in OpenSim.

I did a little playing around, and the answer seems to be “it depends”.

Unlike Second Life, which is both (arguably) a bunch of software, and a particular virtual world that uses that software, OpenSim is just a bunch of software (and an Open Source one at that, which means that there are all various forks and versions and levels and patches around out there). So what you get when you log into one virtual world that’s running (some level of) OpenSim may be different from what you get in another.

What I found when I tried out the Scripts So Far in the first OpenSim-based grid that came to hand (running perhaps “OpenSim 0.7.2 ReactionGrid”, although it wasn’t ReactionGrid) was:

There’s one simple thing in the self-healing target script that this OpenSim didn’t like; this line gets a syntax error:

integer health = MAX_HEALTH;

because apparently this OpenSim is even more silly than Second Life is, about global initializers not having any hard stuff like math or variables in them. So I changed that to just:

integer health;

and added a

    health = MAX_HEALTH;

down in the state_entry() handler, and that got rid of the compilation error.

The autopopgun script worked fine except that (as one of the commentors anticipated) I had to create my own bullet for it because there was no popgun in the library to cheat with. :) I won’t post the bullet script and other bullet details here right now ’cause it would be a distraction, but it worked.

Then I pointed the autoshooter at the target, and the target said “Clunk”.

Repeatedly.

As you may recall, this means that it thinks it’s being hit by something that’s not moving fast enough to cause damage.

A little poking around revealed that in this particular OpenSim, llDetectedVel() was always returning 0.000, making it not very useful for our purposes!

As a temporary test, I changed the

    if (llVecMag(llDetectedVel(index))>15) {

in the target script to the obvious

    if (llVecMag(llDetectedVel(index))>=0) {

so that instead of “is the thing that hit us moving at least fifteen meters per second?”, it was instead asking “is the thing that hit us moving at all, or for that matter not moving at all?”. To which the answer must always be Yes! :)

With that change the target worked fine; its health count went down while the shooter was shooting at it, went back up again when I moved the shooter away, and then went down and turned yellow and then red and then the target Ceased to Be when I left the shooter pointed at it long enough.

There may be OpenSim-based grids where llDetectedVel() works (this list of LSL functions that work in OpenSim seems to claim that it is implemented), but it didn’t seem to in mine. But other than that, and the one tiny syntax change mentioned above, everything we’ve done so far seems to work in OpenSim!

In case anyone else was wondering. :)

How to keep a geek happy :)

Homestead Island Sandbox

I am out of frame to the right of this picture, in the Homestead Island Sandbox on OSgrid, one of the OpenSim-based grids on the net. It’s a bit lonely in terms of socializing, and things crash and otherwise don’t work more often than in Second Life say, but still…

That little semi-transparent flying saucer that you can see, passing in front of the sign just under the word “Island”, is fliting around the sandbox, randomly laying down those colored squares that you see scattered about. All over the whole sim! (They delete themselves after five minutes.)

Sometimes it’s nice having a whole region to oneself. :)

Among the Legends

A recent drama storm around the OpenSim-based Legend City Online (LCO) reminded me that I haven’t tried one of the public OpenSim virtual worlds for awhile, so when SL was being weird the other night I headed over (using the Second Life viewer, rather than their recommended Hippo, because I didn’t want to download a Whole Nother Thing).

Turns out I already had an account over there; here I am as Dale Mullins (in a pretty radically unattractive shot, now that I look at it), helping one of the Legends (roughly the equivalent of Lindens in SL, I gather) adjust a sit-ball on a park bench at the default landing space:

Lounging among the Legends

(That’s me on the right, the Legend on the left: a very nice woman, with an adorable shape and skin, and a million-watt facelight.)

There are some very nice builds in the parts of LCO that I wandered through, and there are enough bits of free clothing and shapes and stuff that I was able to make myself more or less my familiar self without much trouble (although those glasses are Not Realy Me; I will have to make some myself).

The underlying server software, though, is pretty clearly still pre-beta. Things I noticed:

  • Inter-sim teleports and sim boundary crossings were quite risky, relatively often resulting in disconnection, infinite uncontrollable flight into nowhere, and/or appearing at one’s destination but being unable to move,
  • Even just standing around is somewhat risky; I got logged out a few times when I was doing nothing notable at all,
  • Once one has crashed or been logged out, it typically takes two or three or more tries to get in again; lots of message about already being logged in, or having been disconnected from the region, or whatever,
  • Scripted attachments are odd: I made myself a little invisible attachment with a hovertext title and some decorative particles just for fun, and found that if I went to a different sim it would be sort of half-off (the title would be in black, and I couldn’t see any particles although sometimes others could) until I detached it and re-attached it.

It’s possible of course that some of these problems were due to using an Unsupported Viewer; I ought to try Hippo next time if I’m not too lazy.

There weren’t a whole lot of people in the world, but the dozen or so that I saw seemed to be your typical early-adopter type and therefore above average conversationally; so that was nice. LaLa Legend herself, who I gather runs the place, was also hanging around at the entrypoint; something you don’t see in SL. I don’t know how positively she would have responded to challenging questions about the current drama of course. :) And the Legend in the picture up there (I apologize for not recalling her name; for some reason the viewer wasn’t logging chat while I was in LCO; I probably forgot to check the box) gave me 250 of the local currency so I could (for instance) upload the texture for my famous Noob Tee Shirt, which I then handed out randomly.

It’s fun to play around in such a young world, and to see how things are being worked out in them compared to mama Second Life. On the other hand the infrastructure is still wonky enough that I doubt I’ll be there regularly. It’s worse than my first week in SL (and that was pretty bad!). But it’s coming along…