20080415

Hard Loading Made for Easy Saving

I complained for a while about the difficulty I had in coming up with a satisfactory solution to loading various types of objects from XML area files. The problems of having recursive, semi-cyclic structure to the files posed quite a challenge. I eventually solved it by describing an object's persisted state in terms of a list of "properties" that are tied to actual members on instances of classes.

It was nice (really nice) to see my design validated at some level today, when I was able to write only the obvious code I had floating around in my brain and get saving working. Simply put, the saving process uses the same properties that were used in loading to extract data from the corresponding members of classes and put them automatically into named elements in the XML file. Again: the same mechanism I used to establish a relationship between XML elements in the area file and data members could be used both for reading and writing. Yes, I'm patting myself on the back for getting this right (at least right enough).

Gotchas for Bags

There are never no gotchas, but luckily the gotchas were easy to solve this time. The structure of a Bag (i.e. container of things--dealies, mobs, etc.) in XML is a single element containing multiple elements, one for each thing in the bag, each with the name item, but differentiated by an attribute called order. Therefore, the "name" of each property of a bag in XML is actually an xpath like item[@order='1']. The naive method of creating a new element with the "name" of the property and filling it with the contents of the item won't work, because it's not really a name.

To get around this, we make two passes. In the first pass, the bag creates empty elements with the correct element name and order attribute. In the second pass, we look for each property's (i.e. item's) element in the XML, which is identified by a unique xpath query, and--rather than creating a new element for the item--reuse the existing one and fill it with the item's serialized representation.

Probably worth illustrating. Here's the first pass. The bag has one item inside, a food dealie. It creates an empty element that has the order set correctly. You can imagine if it had two items within, the next one would have a different order. The bag has a single property, that can be described roughly as: the element identified by item[@order='0'] is tied to the element with index 0 in this container1.

<inventory kind='dealie_bag'>
  <b><item kind='food_dealie' order='0'/></b>
</inventory>

In the second pass, we examine each property of the bag. As I mentioned above, there's just one property, identified by a specific XPath. We search within the inventory element for that sub-element, find it (bolded above), and proceed to fill it with the item's own data:

<inventory kind='dealie_bag'>
  <item kind='food_dealie' order='0'>
    <short_description>the cover for a barn</short_description>
    <long_description>The spare roof of a barn is here.</long_description>
    <proto_lid area_name='main' local_id='10'/>
    <nourishment>10</nourishment>
    <keywords>
      <keyword order='0'>roof</keyword>
      <keyword order='1'>cover</keyword>
      <keyword order='2'>barn</keyword>
    </keywords>
  </item>
</inventory>

Gotchas for Container Dealies

Container Dealies, which are items that can contain other items, have the special characteristic of having their "short description" and "long description" methods overridden to indicate that the item is a container.

<type commands for a list> look
Room: The second room

The spare roof of a barn is here.
<b><C></b> How many hands have been carried in this bag?

a man is here.

<type commands for a list> 

As you can see above, the container has a marker in front telling that it's a container. Usually, the property for the short_description in XML is tied to the ContainerDealie.shortDescription() method. This yields an XML representation that includes the container marker, which means when it's loaded, the marker is loaded as being part of the unadorned description, and you end up with a duplicate container tag. The fix is to tie the short description XML property at write-time to the object's unadorned short/long description attribute instead, which is saved off when the object is loaded originally.

As it turns out, both of these special cases fit in elegantly to the current design, so I don't consider them all that special. I'm fairly confident that this same method is flexible enough to be used to save anything to XML. I'm kicking around the idea of having a totally persistent world, where things don't respawn on reboot, necessarily. There's a lot of issues to work out there, though. Thoughts?


1 Actually, I use procs, not indices (indices are so passe), to identify each item within the container. In a loop through the bag's items, each iteration has a variable that references the current item. At that point, I can create a proc that forever references that item and pass it into the property to save off. I like to think it's a pretty cool trick.

20080412

Multiple Inheritance is Evil, Even in Ruby

So yeah, I took a break for a while. According to Fallenearth's blog page, about 10 months. Wow. What happened in there? School, work, moving, lots of good video games, etc.. But the most influential thing in there that stopped me from coding was multiple inheritance. I'll get to that shortly.

Recently, Leia started playing a Korean MMO, and when I'm around someone who's doing something roughly like mudding, it makes me want to mud, too. And then I slap myself and remind myself that I'm creating one now, not playing one (I just don't have time for the latter). So that ultimately got me motivated to defeat multiple inheritance.

(as an aside, the blog editing page is unusable in IE8 Beta1. I'll have to file some bugs on that...)

When we last left our hero (me, 10 months ago), he (I) was struggling with an inheritance problem. As you may have gathered from previous posts, my still-unnamed mud is heavily object oriented, like everything else written with Ruby, I suppose. The concrete classes in the mud (Dealie, Mob, and company) mixin modules whenever possible, in a weak attempt at formalizing their interface to their callers.

As a concrete example, every dealie on the mud has a set of keywords that identify it in the mud world. These are typically specified in the XML area file, like so:

<dealie_prototype order="1" kind="food_proto">
    <lid area_name='main' local_id='10'/>
    <b><keywords>
        <keyword order="0">barn</keyword>
        <keyword order="1">cover</keyword>
        <keyword order="2">roof</keyword>
    </keywords></b>
    <short_description>the cover for a barn</short_description>
    <long_description>The spare roof of a barn is here.</long_description>
    <nourishment>10</nourishment>
</dealie_prototype>

Therafter, a player can refer to that item in their inventory, on the ground, and so on by using any substring of "barn", "cover", or "roof". By the way, we could theoretically extrapolate the right set of keywords by eliminating articles and such from the short description, but this gives more control to area authors, and that's generally a good thing.

Many very different things have the property of being identified by keywords in the mud world (Mob is another good example), so it makes sense to extract the common code for enabling this keyword identification into a separate module that all interested classes can mixin to get the functionality for free. In my mud, this class is called--stupidly, because I couldn't think of a better name--Keyworded.

The Keyworded module implements several things to enable this. It adds a data structure filled with the actual keywords (stupidly called ThingKeywords) as a member of the object. It provides some methods for matching an instance of the object against a keyword. Since this module has state (the list of keywords, basically), there needs to be a way to initialize the state. One way to do this is to define the initialize method in the Keyworded mixin. Callers who call super(keywords) at the top of their initialize method will pass on the keywords to the Keyworded initialize method. Great!

Not so fast! Callers often inherit from other modules and classes, and doing this means that all modules and classes in the method chain for initialize will get this spurious keywords parameter. Clearly, this won't work. I thought, "how would C++ handle this?" because I recently read Scott Meyers' Effective C++, for which I believe there cannot exist enough praise. Given that C++ is a language that fully supports multiple inheritance, surely it's solved this problem, right?

Indeed, it has. In C++, you don't call a single super method that does everything (pardon the pun), but instead the inheriting class calls the base class/interface constructors in a caller-specified way and order. Oh, if only Ruby supported some kind of syntax for targeting a specific class or module's method when calling it. I'm actually lameting; I don't think it has such a thing. Does it? Anybody?

Needless to say, the solution I ended up implementing is essentially trying to mimic the above. Each module that requires initialization, instead of defining initialize, defines the instance method Modulename_initialize (and Modulename_initialize_copy). Callers, like in C++, explicitly call the module "constructors" in whatever order they want, and with whatever parameters they want. Everybody goes home more or less happy, since as far as workarounds/hacks go, this one isn't so bad in that it follows another tried-and-tested language's pattern.

Having solved this, I went on to fix much more pressing things, like a load of bugs I found along the way, ranging from the minor syntax errors on code that just hadn't been covered yet, to corner cases in loading containers that contain other containers that contain the first one.