Sunday, December 26, 2010

Dynamic Objects update

I've submitted version 6 of Dynamic Objects, which adds compatibility with Inform 7 build 6G60. There were two compatibility issues, both involving the way Dynamic Objects uses undocumented I7 properties to add cloned objects to the per-kind linked lists.

First, the "IK_0" property was renamed to "KD_Count". This property contains the numeric index of each object's kind. For example, an object whose immediate kind is "person" will have a KD_Count of 8, since "person" is K8_person in I6.

Second, the logic for matching a kind name to the link property had to be changed to account for new properties. This deserves more explanation.

All things in an I7 game are connected via one or more linked lists, which helps to optimize kind-based loops. When we write "repeat with X running through people", the game doesn't have to loop through every object, checking each one to see if it's a person. Instead, it starts at the first person, which is referenced in the IK8_First constant, and proceeds to each subsequent person by following the IK8_Link properties.

Whenever we clone an object, Dynamic Objects has to add the new object to each of the appropriate linked lists. A cloned man has to be added to the list of men, the list of people, and the list of things.

The easy way to add something to a linked list is to add it at the beginning: change the head to point to the new item, then change the new item's "next" field to point to the old head. But we can't do that, because the head is stored in a compile-time constant like IK8_First. So we add it at the end, by starting from the old object (which we know is already in the list) and following the link property until we reach the end.

But finding out which link property to use is tricky in itself. The I7 compiler has an easy enough time knowing that it should write "IK8_Link" for loops involving "K8_person" -- even Ayn Rand knew that 8 IS 8 -- but the *values* of IK8_Link (a property number) and K8_person (an object address) are unrelated. They're similarly named, but those names are only for compile time, right?

As it turns out, no! Even in release builds, the I6 names of objects and properties are still available at runtime via the "print (object) x" and "print (property) x" syntaxes. So Dynamic Objects loops over every property, printing each one's name to a buffer and looking for the one whose name contains the same number as the name of the kind object. It's ugly but it works.

That's where the compatibility issue came in. Since the names don't match exactly ("K8_person" vs. "IK8_Link"), Dynamic Objects was only comparing the part between the K and the underscore. But 6G60 added new properties with names like "IK8_Count", which threw off that comparison. The extension now checks the character after the underscore to make sure it's an L.

Incidentally, if you use Dynamic Objects, please vote for this suggestion. A more stable way for the extension to get at this information would keep it from breaking with each new build, and adding more information for the extension to use at runtime would make it work more smoothly with indexed text properties.