64kb ought to be enough for everyone

That’s what they must have thought, when they introduced Mono virtual machine to SL.

Well, I must say that LL’s LSL to Mono compiler sucks immensely. I noticed there were some things odd about it, recently, but today I have working and largely irrefutable proof on my hands of how much it sucks and I’m telling you it ain’t pretty.

As I have mentioned before all over, I have been working on a complex, highly automated, and hopefully, blonde-proof device to help with setting up treasure hunts of the more interesting and fun kinds. It’s almost ready for release and is currently in field testing, growing in features so much that it frustrates me to no end. This is a rather big project for LSL, and monstrously big for my LSL coding experience, totaling around 3000 lines of code in about 30 individual scripts scattered over 6 prims. Some of the scripts are particularly memory-intensive, because they manipulate long lists of keys, which is what makes the whole thing so versatile and, well, cool, if I say so myself. The core script is almost constantly operating at the limits of free memory right next to a stack-heap collision, it’s largely impossible to move many more components out of it into another script without slowing the whole thing into treacle, and I’ve been looking for ways to optimize it all the time I worked with it.

First thing that ran out of memory was another script, though, the script used to display the dialog menu interface, which brought me on to discover that simply declaring an empty state in Mono results in 3kb memory gone immediately. States weren’t critical for it, so I recoded it to work without them and went on my merry way. I was too busy to consider the possible implications.

And along that way I wanted to save memory by using a function to replace llListReplaceList(list,[data],pointer,same pointer) with something like l2r(list,data,pointer), hoping it would save me some bytecode.

Well, it doesn’t.

In fact, one function declaration and 100 (!) calls to that function, a short one, invariably results in more bytecode than 100 calls to the original function. I jotted down that result and went on my merry way again.

Today, I looked at the script again and suddenly had an eureka moment. I replaced all the long functions I only called once or twice, duplicating their code in the states. Then I replaced all the shorthand functions I called often, like signal(string) with llWhisper(COMM_CHANNEL,string). There were a lot of calls to those.

And the result was 5kb (!) more free memory and a significant gain in the amount of treasure items a single server box can handle.

My only theory to explain this is that declaring a function causes the compiler to generate code required to create a function context. Even more is probably required to define a script state. For a general purpose virtual machine that may well be less efficient than for the original LSL one. But instead of storing that code globally somewhere and calling it when required, like good compilers do, LL’s compiler just dumps a fixed size code block in, one that is at least 700 bytes long, and pretends that is nice and proper.

Well, it bloody hell isn’t nice, isn’t proper, and I bloody have no words.

What the fuck do they think they’re doing, pardon the term, doing something like that and then blaming people for ‘overusing openspace’?!

P.S., long after: Idea: an offworld preprocessor to unroll shorthand functions. And maybe allow list[start:end] instead of llList2List and friends. I can’t fix this braindead language, but hopefully I can make it more tolerable and prevent further blisters on my fingers.

Nastiest hack of my career

Imagine, you have a list of N variables, some of which are marked empty. You need to randomly select an empty one and return it’s number so that code elsewhere may use it as a slot to put in a non-empty one.

The obvious approach: Make a list of the numbers of the empty ones, randomly select one of these.

Well, it doesn’t work, cause there may be hundreds of them and we’re operating close to memory limits as it is, and a list of 100 integers will chew up 1521 bytes of memory. (…yes, it’s already in Mono, without Mono it wouldn’t be feasible at all.)

The less obvious approach: Keep selecting a random one until you bump into an empty one.

Doesn’t work either, because recursively selecting it takes even more memory, and if you do a while loop instead, with LSL random number generator being what it is, it might stall forever and you’ll never know.

The dirty hack approach: Behold and stare what these bloody people made me do.


integer findspot() {
    string empties = "";
    integer i;
    for (i=0;i<llGetListLength(items);++i) {
        if (llList2Key(items,i) == BLANK) {
            empties += llGetSubString("00"+(string)i,-3,-1);
        }
    }
    integer index = randInt(llStringLength(empties)/3);
    return (integer)llGetSubString(empties,index*3,(index+1)*3-1);
}

That only needs 338 bytes of memory to find an empty spot among 100 items, and actually works. 🙂

Suckers

Barnum was right, there really is a sucker born every minute. Calling them noble vampires doesn’t change that much.

When Vampire: the Masquerade came out, it was a revolutionary roleplaying game setting, that brought new blood into the subculture, and while the results have been mixed, it significantly advanced it’s sophistication. People who really are into roleplaying vampires probably know what I mean, people who aren’t, can imagine that for many this was a powerful vehicle of self-exploration, the rules were simple and encouraged smooth, minimally encumbered game of imagination.

Unfortunately, roleplaying has it’s history rooted in tabletop wargames and mathematical approach to strategy, so there are and will always be people who will try to exploit the mathematics of rules in order to win the game – if winning conditions are defined in any fashion.

And winning conditions are all that The Thist: Bloodlines is about. To put this long story very short, while the author(s) imply that it was originally meant as a support system for roleplaying — a noble cause, to be sure — as it is used now it is nothing more than a financial pyramid. An ingeniously evil one, because it’s legal:

  1. Everyone bitten by a vampire gets an invitation to become a vampire themselves.
  2. Everyone bitten only contains 5 liters of “vital blood”, period, and no more can be extracted. It does not regenerate.
  3. Every vampire consumes 0.25 liters of “vital blood” daily.
  4. Biting more victims confers higher status in the scoreboard on the vampire.

It goes without saying that if a victim is willing, they are likely to also purchase their own set of scripted fangs and join the game as vampires. Simple extrapolation shows that as soon as the pool of willing victims is exausted, unwilling victims will be introduced. Even though you cannot actually bite anyone without their permission, because the design of Second Life prevents it, far too many people will give the permission carelessly or without knowing what it actually entails.

This will slow the growth of the pool down, but only marginally, because the new entrants into the pool of vampires will mostly consist of careless or unknowing people who are, incidentally, more likely to enjoy treating the system not as a device for roleplaying and self-exploration, but as a button-pushing game. So they will seek out new victims and target people more like themselves, rather than roleplayers.

Eventually the number of non-roleplayers will exceed the finite number of roleplayers in the system, and cause untold amounts of annoyance to bystanders who don’t want to play it and consider the whole idea silly, not to mention shame the roleplayers.

Well, that’s exactly what happened.

Numerous calls to stake the impolite vampires so far resulted in nothing, since they’d have to give their permission just as the victims do, and more importantly, because they don’t realise that what they’re doing is annoying everybody. The makes of the game are starting to take countermeasures by rewarding roleplaying explicitly, but I think this is too little, too late — far too many people with no interest in roleplaying are already in the vampire pool.

There is only one hole in the system that I have been able to find so far: the victim scanner treats everyone who isn’t wearing the Bloodlines HUD as a potential victim. Since the HUD objects communicate by passing messages over open public channels, it should be possible to create an object that responds to this query with something plausible that excludes you from the potential victim list and saves everyone time and trouble, or at least, detects the scanner query and launches appropriate countermeasures, like a cloud of particle stakes.

Unfortunately, the number of possible channels is very high, and scanning for them is a rather complex endeavour, requiring controlled conditions, so I haven’t been able to figure out a way to do it without getting a set of fangs of my own, and even if I do break down and get me some fangs, it might take up to 66666 attempts to track the channel down.

Any ideas?