Showing posts with label commands. Show all posts
Showing posts with label commands. Show all posts

Monday, August 31, 2009

Patterns in programming

Just managed to sneak in ONE post for August...

It's not that I haven't been playing with Metaplace lately (though not nearly as often as I'd like), but a lot of the stuff I've been doing hasn't been groundbreaking or, really, that interesting. Not in its current stage, anyway.

Ever since I added sounds to my Ultima Online world, I've been drawn back to getting that large project going again, happily ignoring the fact that custom avatars are the bane of my Metaplace existence.

While the footstep sounds were interesting, the most memorable sounds in UO were the spells, so I decided to focus on that subsystem next. As is typical of my programming history, I'm much better off at the behind-the-scenes coding than the user interface portion, and since UO spells (more specifically, the spellbooks) can require a bit of UI that I shudder at creating (and I won't even bring up UiXml()... well, except there), I went to work on the back-end portion of spellcasting.


One of the most common things I do in Metaplace, whether it's designing a new module, a new system, or just testing out new functionality, is to write a command-line interface to the code I'm developing. This is useful for testing things that would otherwise require buttons and sliders and textboxes, but don't have them yet, and for quickly trying different values in different situations. This is something I do so often, in fact, that I've got a routine when I first create a script, that sets me up for development. For instance, when I decided I was going to work on spellcasting in UO (specifically, Magery, which is actually a skill...) I created a script called "magery", and right off the bat, wrote the following:


Define Properties()
magery={}
end

Define Commands()
MakeCommand("magery", "magery interface", "cmd:sentence")
end

Command magery(cmd)
local params=string.gmatch(cmd,"%S+")
local subcommand=params()

end


This sets me up for a few things: it gives me some local storage for anything I create during testing, such as window IDs, sound IDs, state variables, etc. all within the self.magery table. It also gives me a quick way to pop up the command-line and start sending commands to my code, taking advantage of the handy "sentence" type in Metaplace. In case you've not seen it, it lets you type, say


magery cast gate travel


And it'll take everything after the command name and pass it as a single string, spaces and all. This allows for parameters with spaces (such as "gate travel" above), and for sub-commands that have varying or variable numbers of parameters.
The little bit of code at the start of the magery Command helps me tokenize the subcommand and the parameters.


After throwing together a little bit of magic in my UO world, I decided that I should really add in skill support (because, as I mentioned, Magery is technically a skill that you use), so I made a script called "skills" and started it with


Define Properties()
skill={}
end

Define Commands()
MakeCommand("skill", "skill interface", "cmd:sentence")
end

Command skill(cmd)
local params=string.gmatch(cmd,"%S+")
local subcommand=params()
local skillname=params()

end


Almost the same as before. Then I could quickly add


if subcommand=="use" then
SendTo(self,"use_"..skillname,0,params(),params(),params(),params(),params())
elseif subcommand=="set" then
self.skill[skillname]=params()
else
AlertToUser(self,2000,"Unknown skill command: "..subcommand)
end


Now, admittedly, I kind of lose some of the nicety of the sentence type by coding my skillnames without spaces (AnimalTaming instead of Animal Taming). And the ugly bit with the repeated calls to params(), to pull off each of the extra parameters (if they exist), works because the function returned by string.gmatch() will just continue to return nils once it's done, and sending extra nils through to the Trigger is harmless (provided the Trigger didn't expect anything there, of course). I could spend the time and write code that says, "if the skillname is Animal Taming, then there's just one extra parameter, the target, so I'll only pass one extra params(); but if it's Provocation, then there are two...", but this lets me change the rule in their own individual handler functions, making for very rapid prototyping.


The other day I got distracted from UO development by a conversation with LunarRaid, which made me want to try out implementing drag-and-drop functionality using the UiEvent() system. Replace the "magery" with "dndui" and you have my starting block of code, ready to change settings, pop up windows, or whatever else I might want to change while testing. The nice thing about this setup is that many UI elements call Commands when pressed or used, and thus the same single interface can be used by them. Also, I tend to have all of the conditional code in the Command just fire Triggers to self so other code I write can easily duplicate, with a SendTo(), the functionality that I've been testing from the command-line.

For just testing concepts, or to avoid fiddling with buttons, this is a great way to just get coding. If I didn't have a nice way of quickly prototyping the code I come up with, I'd probably still be fiddling with a spellbook interface and have nothing but a few sprites to show for it.

Friday, June 12, 2009

Metascript

The scripting language for Metaplace, Metascript, is strongly based off of Lua, so much so that one might think that it WAS Lua. If you don't know Lua before diving into Metaplace, then that's fine, but if you've got some Lua under your belt, you might hit some hurdles.

This post isn't about learning Metascript, though, or about the differences between it and other programming languages, or how Lua/Metascript sucks compared to your favorite language. Just because you come from a background where arrays are indexed from zero doesn't mean that indexing from one is wrong. And if you want to argue the point, then I'll point out that Lua doesn't have arrays anyway.

Syntactic sugar

One of the things that will strike Lua programmers is the extra set of definitions available in Metascript:

  • Define Properties()

  • Define Commands()

  • Trigger foo()

  • Command bar()

  • WebTrigger baz()


These all start "special" functions in Metascript, and aren't standard Lua. What post-alpha users might not know, though, is that these are just colourful candy coatings for some mundane-looking counterparts:

  • function def_properties()

  • function def_commands()

  • function trg_foo()

  • function cmd_bar()

  • function trg_http_baz()


In fact, way back when we also included the "trg_" prefix in the SendTo() calls. As far as I know, these old methods still work; I assume this because

  1. I still have old worlds that use it (thought I haven't visited them in a while)
  2. We were told that they wouldn't stop working


Kinda takes some of the mystery away, doesn't it? I can see why the change was made: it helps to emphasize the special nature of these functions from others defined with "function XXX()"; and it hides the unfortunate fact that variable prefixes are used to denote semantic meaning. Don't consider them variables? In Lua,

function foo_bar()
...
end

is equivalent to

foo_bar=function()
...
end

Hrm. I'm now curious whether I could write

def_properties=function() foo_bar=0 end

in Metascript and have it work. I'm going to guess not, which I'll talk about shortly.

So, should you use function trg_foo() instead of Trigger foo() ? One reason you might is to do some offline programming, so you have code that compiles under a standard Lua interpreter, but can still be tested (by implementing your own backend library that knows how to handle Triggers and Commands). I've used this in the past to do some rapid development and to avoid some editor idiosyncrasies.

Define Properties() and Define Commands() (or function def_properties()...)

I've made no secret my dislike for these "functions". Define Properties() is where you define your script properties (where they might collide with others on the object), include scripts (similar to Lua's dofile()), expose properties and export functions.

Define Commands() is where you define your MakeInput()s and your MakeCommand()s. (There used to be a separate function def_inputs() -- I wonder if that still works.)


These functions are handled "specially"; they're actually executed at compile time, which can reveal bugs earlier than runtime, such as attempting to access functions that aren't available (which is almost all of them). That means no Debug(), no AlertToPlace() - no pairs() or ipairs().

They're actually code, though; you can have for loops in here, if there's anything worth looping over, and conditionals, if there's anything to compare. But generally, these functions act as definition blocks, and might better be implemented as such, extending the Metascript further from standard Lua. Then we could have some alternate notation for defining properties, such as

float value;

instead of relying on

float=0.0

to work. Which it won't. This is because behind-the-scenes, any local variables defined in this block are being specially processed into member variables on a C++ object on the server (all supposition -- I'm not actually privy to the source code). Being C++, types need to be known and once defined, permanent. This means that, unlike Lua, Metascript won't let you redefine the type of a property once it has been set (and this is a reason why I advocate table properties). Not only that, but it means that certain Lua types - ones that aren't available in C++ - aren't allowed as properties, such as booleans and functions. I would expect that supporting table properties had to require a bit of work, having the Lua form of the table being converted into something that C++ understands, and can hold, so why not a Lua function? Why not the lowly boolean? Also, this desire to define property type using Lua notation, instead of just creating a custom definition block, leads to ugliness such as

child="_object_"

to define a property as an object. Ouch.

As for Define Commands(), I'll just re-iterate from my previous post that I don't understand why MakeInput() and MakeCommand() cannot be used outside of Define Commands(). Sure, some compile-time checking is nice, but you're not saving me from all sorts of other scripting bugs, so why this? Also, these definitions check whether a Command specified in MakeCommand() is actually defined. Why? What's so bad about a Command being sent that doesn't have a handler? Triggers allow this (and allow multiple handlers), yet Commands must have specifically 1.

And because I just can't let it go: why are these two scripts run in such a tight sandbox? Fine, it might make no sense to call SendTo() while defining properties, or perhaps not even Debug() while defining an input, but no pairs()/ipairs()? Am I really the first person to write "code" in these blocks, instead of just a handful of definitions and pre-structured function calls?

Scope

In Lua, everything is a global variable unless you define it as "local". In Metascript, too, your definitions have a global scope within a script -- this probably bites me in the ass once a week, since I tend to write a lot of recursive functions. The "within a script" is important, though. When first looking at Metascript, and Metaplace, and the idea that multiple scripts are attached to an object, you might want to think that these are all loaded into a shared environment as far as the object is concerned. However, this isn't true, and rightfully so.

Remember how

Trigger foo()

is the same as

function trg_foo()

is the same as

trg_foo=function() ...

? Well this would be problematic if all scripts shared the same scope, because it would mean that an object couldn't have definitions for a Trigger function more than once, as the latter ones would overwrite the former, and having multiple definitions for the same Trigger is a key, powerful part of Metaplace. This scoping is unfortunate, however, because this means if we use IncludeScript() to import a set of functions, we have to do it for every script that needs them, instead of just having it imported once for the object. This makes it awkward to have a library that's used throughout a set of scripts.

Also, the scope affects the idea of "self". Commands and Triggers, usually the largest portion of a script, have an inherent sense of self. But functions do not. Why? Well, they actually used to, I believe before we had IncludeScript(), so there wasn't a question of the context in which a function was being run. Not being privy to the way the Lua sandbox is being run, I'm not exactly sure why "self" can't still be defined in the environment of a function call, but it is no longer supported - you must now pass it in explicitly.

Userdata

Stock Lua also has a userdata type -- it's basically a C++ object -- so this doesn't make Metascript different in that regard. However, because every object in Metaplace is represented by a userdata object, how they interact in script is important.

Knowing the structure of the userdata is usually required, because there's no reflection or introspection by default; you can access self.foo if you know it's there, but if you don't, you have no way to ask (not exactly true, see below). Why is this important? If I don't know that self.foo is there, should I really be using it?

Well, yes, sometimes there are cases where iterating over everything is a good idea. One case is the set of stylesheet API functions that Metaplace provides. These let us look at the stylesheet (basically, the static portion of a world), to peruse the templates, places, sprites, scripts and modules. To do this without any prior knowledge, we need to be able to iterate over them all, much the same way that pairs() and ipairs() allow us to iterate through a table. In some cases, we have a special ._all_ property on the userdata, which returns a table which can indeed be iterated over with ipairs(). But why not all the time?

For instance, if I want to browse the Places in my world, I can access a userdata object with stylesheet.places, and if I know the name of one, I can index into this userdata, such as stylesheet.places["0:1"]. Alternately, I can use stylesheet.places._all_ and loop through them all, finding the one I want. And once I have a specific Place, I can get another userdata from it with someplace.tiles. Not knowing anything about how many tiles there might be, I don't know what to ask for specifically, so I try someplace.tiles._all_, and get back ... nothing. It turns out that instead of a nice table to iterate through, I have to basically guess the indexes of the tiles, with something like

t=0
while someplace.tiles[t] do
...
t=t+1
end

And worse, there are other objects, such as the Place itself, that neither have a way to iterate (with ._all_) nor to enumerate (with a 0->n loop), but that you just have to guess/know the properties of, hoping that the wiki documentation is up-to-date. Again, this isn't something specific to Metascript -- Lua userdata can have this problem too -- but it would be nice if we had the ._all_ table available on all accessible userdata objects. Alternately, all userdata objects could act as tables for purposes of using pairs() or ipairs(), if they really wanted to.

Metatables

Metatables (the "meta" being unrelated to Metaplace) of Lua are a very powerful feature, one that, in my opinion, turns Lua from a simple toy language into a powerful full-featured one. Metatables allow us to redefine our environment, creating a sandbox where a limited set of functions might be available. Metatables allow tables to take on new functionality, such as addition of two tables, or different handling of accessing values that don't exist. And metatables ... aren't available in Metascript.

My only guess is that the current sandboxing provided to the Metaplace scripts makes further access to metatables ... hard? Unwise? Dangerous? Confusing? I'm not sure, but it's a real shame that we don't have them. Granted, they're an advanced feature, and it's quite likely that very few Metaplace users will notice their absence, but heavier coders like myself certainly do, and working around them can be difficult, awkward, or near impossible.

Case in point: I'm writing a vector/matrix library for Metaplace, to help implement a new physics engine that I'm writing. Such a library must exist for Lua somewhere, right? Sure, there are some out there, but most (all?) wisely take advantage of metatables to overwrite the built-in operators, since it makes for much nicer and cleaner usage of such a library if I can say

newvector=v1+v2

instead of

newvector=v1.sum(v2)

which I have to do now. Even something as simple as making the vectors printable:

Debug(newvector)

is a lot nicer than

Debug(newvector.tostring())

Small things? Yes, but these only touch on what metatables can allow. Frankly, I'd like to stop there because I don't want to realize what other powerful things I cannot do in Metascript because of their absence.

Verdict?

All that being said, Metascript isn't a bad language. It's Lua at its heart, with a few surgeries to make it tick a little differently, some of which might have been required, or some only elective. This customized version of Lua hasn't itself prevented me from doing anything in Metaplace, apart from quickly porting in pure-Lua code from the internet; any walls I've hit have been with the Metaplace platform itself, and not the scripting language.

Monday, June 8, 2009

Namespaces

Metaplace's biggest strength has to be the Marketplace, where the community can contribute their own modules to everyone else, either free or for a cost of Metacoins, the current virtual currency being tested during Open Beta. This biggest strength, however, is going to become a problem if something isn't done to deal with the ever-crowding realm of namespaces.

In case you're not aware, a "namespace" is all of the available possibilities for naming something in a certain context. The names of the Metaplace worlds make up a namespace -- no two people can have the same "cleanname" world name (so, for instance, you cannot make a world called "MPCentralLive", or "UO", or any of the tens of thousands of other worlds that are made.) On the other hand, the display names are probably not strictly a namespace; I've not tried, but I do wonder if I can have the same display name on my world as someone else's.

The world names aren't TOO big of a deal. Sure, there might eventually be issues with copyright, or "prior art" in the case of names: if Metaplace is going to take over the world, then I'm sure worlds named "McDonald's", "Microsoft" and the like are going to be fought over, in the same way that domain names (mcdonalds.com, microsoft.com) were when the internet started growing. But the more pressing namespaces are from the scripting point-of-view, as more and more content becomes available, and the likelihood of problems increase.

Properties

Metaplace objects are all about their properties. Objects have a set of default ones, and different ones can be available if an object is a Physical object, or a Place, or a World. All of these built-in properties tend to be set upon creation of the object (such as "id"), or as they operate inside the Metaplace environment ("x", "y", "z"). These properties are accessed by having a reference to the object and then using dot- or bracket-notation to access it:


self.id
self["spriteId"]
GetObjectById(10003).vx
etc.


Attributes, which can be thought of as properties that are configurable for a template (from which objects are created), are also accessed with this same notation. This means, then, that you cannot add an attribute to your templates called "id", or "name", or "speed". But you could have your RPG game where all of the objects need to have a damage value, so after checking the Properties pages on the wiki, you can have that:


self.damage
monster[4].damage


Also, scripts attached to an object can also add properties. A module might define these in one of its scripts to allow passing of values between all of its component scripts. It might also use them to persist values, such as an RPG module that provides the ability scores such as "strength" and "dexterity". These are added in with the Define Properties() block, typically found at the top of a script.


Define Properties()
strength=10
intelligence=10
dexterity=10
constitution=10
wisdom=10
charisma=10
PersistProperty("strength") -- etc.
end



self.strength=self.strength+1


We're okay unless our RPG game has ability scores for "speed" or "lifetime" or a "type" feature.


As you can see we've already got concerns, so if you don't read the wiki property pages daily, you might, say, want to write your own physics library, and try to make script properties called "physics" or "speed" -- like I did. Or, you might want to write your own containment library, and feel that objects should each keep track of where their "container" and what they "contained" -- like I did.

Now I believe they've helped the issue a bit by having the compiler generate errors if you try to define script properties that already exist as template properties, and perhaps even attributes? I can't remember, because you'd think that once you'd made this mistake twice, and have wasted countless minutes, perhaps hours, debugging it. But while the script compiler can save you from attempting to use built-in property names, it can do nothing to prevent you from using properties that someone else is also using.


And this fact is actually taken advantage of, in a way I don't much care for. The Behavior Tool, in its latest form, recommends that any behaviour should have, in its script (and that's really all behaviours are -- scripts), a handful of properties defining that they are indeed a behaviour, name, description, and a few other future properties. But... wouldn't this be a problem once you have more than one behaviour?

It would, if these properties were used for anything other than identification. It's possible to pull out the values of these from the original script, even if these values get lost on the actual object due to being overwritten by other properties of the same name. In fact, these values are retrievable without the script actually being attached to an object, quite different from accessing properties via the dot- or bracket-notation.

I don't care for this "trick" to get values about the module. I think other methods should be used, such as labels, instead of this series of faux properties. I suppose it works, but I think it only fosters misuse of properties. Especially for those that are meant to take on per-object values.

Commands and Triggers

Properties aren't the only namespace to worry about - as was emphasized today, in fact. Newcomer tester Karkacabra was hitting a strange error, which quickly revealed itself to be a namespace collision -- with one of my own modules, no less. He had defined a Command called "close", which collided with the one that I had used in my languagewindows module. Admittedly, (especially since I'm writing this blogpost,) I should have used a better name, such as "languagewindow_close". And in my defense, Karkacabra should have also. *:^) In fact, we should ALL be doing so; the content from the Metaplace team is actually pretty good at this, prefixing Commands and Triggers with a something meaningful and hopefully less likely to collide.

Triggers are actually more of a problem for collision, because technically they DON'T collide -- it is perfectly valid for multiple scripts to define the same Trigger name, and they will each get called, with the order based on their attachment order. This is intended, and a very useful function -- but ONLY if it's intended. If I write a "use" Trigger on one of my objects, I had better hope that I had intended it for use with the Smart Object system in Metaplace, or else I'm going to see some odd behaviour -- not the wrong behaviour, as you're likely to see with colliding Command names, but additional behaviour, as all of the like-named Triggers get fired.

Workarounds

So, we know that if we start using verbose prefixes on our Command and Trigger names, we're going to help reduce the collision in their namespaces; the namespace is rather large, what with 50-60 or so characters per position in the name, and more length from a prefix just means more possibilities. But what about the properties?

Of course, the prefix system works just fine there, too, and I believe the Content Team uses it there. But I prefer a different method, which works, in my opinion, better.

Instead of defining each of the various or numerous properties individually, I define a single table as a property, with a suitably namespace-safe name, and then insert all of my properties within. Here are some of the benefits:


  • automatically grouped together by name, using foobar.propname instead of foobar_propname
  • requires only one PersistRuntimeProperty() call to keep all of the properties persistent (which also means none are accidentally forgotten
  • the property types do not need to be specified in the Define Properties() block
  • the table method allows easy deletion of properties, instead of setting to a "nonce" value
  • all properties for a module are easily iterated through
  • properties can be booleans, or functions
  • two modules, if they both try to define the same table property, won't fatally collide unless those tables predefine the properties (which is only necesary for default values)


The only drawbacks that come to mind are:


  • reading the script's Define Properties() doesn't tell you the properties used
  • reliance on the PersistRuntimeProperty() of a table, which has had a history with a few problems



I've been lightly pushing people in Metaplace to use the table property approach, only because I think it's nicer. What I will start pushing harder for, however, is the namespace consideration -- and I'll be the first to admit that I'm quite the culprit. In my defense, a lot of my code started off as "for me", so I would be aware of my own namespace usage, but of course as soon as I decide to publish it, or I start using others' modules, I need to be responsible.

Friday, May 29, 2009

Commands

A "Command" in Metaplace is like an instruction or action from the user. Clicking on the ground in a Metaplace world sends a Command ("walk_to"), pressing a key sends a Command ("inventory open") and clicking a button in a pop-up window sends a Command ("purchase confirm"). These are all examples of "user-defined" Commands, where each of these, "walk_to", "inventory", "purchase" would have to be defined in a script attached to the user's "object" in the world.

There are also "system" Commands, which for the most part, can be thought of as "meta" commands -- "meta" means "above" or "beyond", and is typically used in the sense of something thinking or acting "outside of the box" or interacting with its own reality (in Dungeons & Dragons, the term "meta-knowledge" is often used to refer to knowledge the player's character has that it shouldn't, but does because the player has it, such as the fact that that dragon is immune to fire, even though the character should know nothing about dragons). The system Commands, then, usually don't deal with the world content itself - walking around, opening inventory or confirming events - but modify things external to the gameplay, such as add new graphics to the world or restarting the server. These commands are built-in, not written by users, and are distinguished by their leading slash, such as "/create_sprite" or "/restart_server", and are generally not (knowingly) sent by the user, but rather are sent, for the most part, by the world-building tools.


The idea that Commands are instructions from the user, then, means that, in theory, they're not something that non-users should need to send. This is emphasized by the fact that Commands are defined in scripts attached to the user template, and can't exist in the script attached to this monster or that rock. Monsters and rocks don't give Commands, only users!

Since the Metaplace client is the user's interface into a Metaplace world, it seems to make sense that the client is the only thing that would need to send a Command (on behalf of the user's actions of clicking and typing. However, during alpha and beta testing, some scripters found that there were some cases where calling these same Commands from withing a script seemed reasonable: if the player walks up to an NPC shopkeeper and says that he or she would like to buy or sell some goods, it was nice for the "shopkeeper" script to automatically pop up the user's inventory for him or her. Since we already had a Command set up for this (for when the user pressed "i" on their client), we could use a built-in function called DoCommand(), which would tell the system to execute the Command just as if the user had asked to.

The DoCommand() was also able to issue system commands. This meant that we could have code that could automatically create sprites, or remove them; that could re-configure object templates; or that could enhance the build environment with extra tools.

Unfortunately, we lost access to DoCommand(). The reasoning makes sense: with more modules available on the Marketplace and more users arriving all the time, it's just a matter of time before malicious code comes along.


Malicious modules are going to happen. I think it must fall under some sort of law of averages or something, but eventually there will be someone that comes along and writes a Metaplace script that does something different (or in addition) to what it claims. I, myself, think along these lines, perhaps because my line of work involves security, or perhaps because I have a "hacker" mindset at times, and admittedly like to "cheat" at times. But the majority of malicious code that can exist can, at most, just affect the world's operation, removing monsters, clearing highscores, or providing superuser rights. I say "just", even though these can be devastating to a world, because I don't think these compare to the damage that could be caused if someone had access to system Commands in a world. Even with peer review, a rating system, and distrust of closed-source modules, there are still ways to sneak in some nasty code (a future post).

While script access to the slash commands through DoCommand() provided the ability to make all sorts of interesting modules, I can definitely understand why it had to be removed. It's one thing to delete all of the trees you painstaking placed in your forest realm, but it's another if I remove the template completely, or export your code without your permission, or include even more modules... it doesn't take long to come up with some evil.

And access to the user Commands from script has also been removed. I believe the motivation behind this is because there now exist modules that interact with the user in a "meta" fashion -- things such as in-game purchases using the metacoins, or accepting friend requests, or meeping another Metaplace user -- are now possible. These aren't actions that are world-specific, such as popping up the inventory or buying a sword, but affect your global Metaplace self. And if a malicious script could automatically buy 1000 balloons for someone, or friend every user, or flood-meep everyone, without the user's permission... well, again, it'd be one thing if it was something just in-world (it made you automatically sell all of your equipment to the shopkeeper), but it's something completely different to affect the user at that extra-world level.


So I understand why DoCommand() was removed. But are there legitimate uses for it?

Luckily, there's an easy solution for the user Commands: turn them all into Triggers (so scripts can easily call them), and have the Command that is called by the user (via the client) also call the Trigger. The numerous DoCommand()s I had in my UO scripts were all changed over to this method in less than five minutes.

And what about system Commands? Well, there's now a DoSlashCommand() function, but it's restricted to "privileged" scripts, which means specially-marked Metaplace-written scripts. What about the rest of us? I have three main reasons to have scriptable access to system Commands -- slash-only Commands, batch Commands, and in-world tools.


Slash-only Commands means Commands that don't have a world-builder equivalent. While many/most of the system commands can be activated by some part of the build tools, there are some that cannot (and worse, some that used to be under the old, beloved, "JS tools" from the alpha days). Things such as parallax and data templates (future blog posts) can only be done using slash commands (not precisely true -- some of the parallax and data templates stuff can be done from scripting as well, but not from the build tools). The command-line interface to Metaplace isn't friendly, so being able to script these operations instead of typing them by hand would be handy, especially if you need to do large batches...

Entering batches of Commands isn't really possible with the command-line, because it only takes one line at a time, and means that you'd have to cut-and-paste each line in. Slow, error-prone... not fun. The creation of my UO test worlds is done through a lot of automation, and involves a lot of system commands that upload sprites, configure them, set up tiles, place objects... this is hundreds of commands that can no longer be done from a script. Another example is the avatar system...

The character customization system (future blog post) is not very user-friendly at all. Not only does it require a lot of slash-commands to be entered manually through the command-line, but even the scripting portion, and the data template portion, can be error-prone and difficult. Having an in-world tool, an avatar wizard, could help people create their avatar module by letting them provide the easy stuff (images and animations), and having the wizard automate all of the error-prone work.


So how do we trade off script security and scripting flexibility? Is there a solution? I think there is, and I think it's relatively easy: allow local scripts to use DoSlashCommand(). This means that imported modules don't have access to it, so the only malicious code is something the world owner adds. It allows users to write a script to do slash-only and batch Commands. The only thing that it wouldn't fix is buying an in-world wizard for things like the avatar builder. Something like that would have to be cut-and-pasted into a local script and run from there, which wouldn't be a good practice to get into, but at least it's a workaround.

I don't know if this is being considered as a solution, or if another one is coming. We're told that DoCommand() was "deprecated", but that usually implies that one method is being phased out because a new method is being brought in. I'm still waiting for the new method!