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.

4 comments:

  1. You're quite right about the namespace problem, but it's easily avoided with a prefix. As for the table idea... is it more efficient to have a property inside a table, or with a longer property name? It's hard for me to tell whether foobar.propname is faster or easier to use than foobar_propname, and whether there is really any benefit for most worlds with the option to group properties or iterate through them. I guess I'd have to try it out and see :)

    ReplyDelete
  2. I'd say there's no difference in ease - same length to type; and I doubt the performance is anything anyone will notice.

    For me, the best feature of using a table property is being able to remove the property at any time by assigning

    self.proptable.propertyname=nil

    which we can't do for "normal" properties.

    ReplyDelete
  3. There are some major drawbacks to using tables as property buckets.

    Take the case where you are making a module that has a new template in it, which has a script from somebody elses module attached to it. The default values for the properties of that script might not best suit your new template, so you want to set different ones. You can't edit the script so the way it *should* be done is to redefine those properties on the template so that they are saved to the stylesheet.

    If all those property values are in one table then some issues are: 1) managing the string version of a table is not very user-friendly, particularly in the tools template GUI, and 2) there is a size limit to that table string (256 characters, which is understandable is you are trying to avoid stylesheet bloat), so you are severely restricted in it's use.

    I have a similar but slight different case where I want to use a table as a property bucket, not necessarily for namespace reasons but for dynamic use. If someone imports my module they can configure an object of a template from that module via the BT, with custom UI, and then save those settings back to the template so that new objects of that type will have those default settings. But this fails as the size of my table, even at its most basic, if way bigger than 256 characters.

    Obo

    ReplyDelete
  4. Very true; whether or not you use table properties really does depend on whether or not the properties are considered configurable values (which would then need to be exported) or internal variables (which do not).

    I always forget about the template-level property editing, always thinking that that better falls under the realm of attributes (which of course cannot be added via script).

    The customizability of properties did come to light just last night, with a module I published: I had a property that the Behavior Tool needed to see and edit, so while everything else went into a table property, that one value used a big namespace_style name instead.

    ReplyDelete