showme vs showobj

Can anybody point me to the code underlying the I7 debugging command “showme”? I’d like to be able to generate a list of all object in a game, along with their properties and attributes. I’m thinking about this in the context of wanting to be able to save state for a web-based game, where this (and perhaps knowing the current scene) would be enough to allow save/restore, rather than taking a snapshot of the entire VMs memory. I suppose I’d want to package all this up as JSON and squirrel it away somewhere.

I have a very concrete understanding of how this worked with I6 --it is laid out in the showobj routine in parserm.h. I wish I could just plunder that code, but according to the I7 documentation, objects, their properties, flags, etc., aren’t stored in the same way in I7.

In I7 I can repeat with item running through objects in the game and figure out containment (holder of the item), but I have no idea of the arcana needed to comprehensively list their associated attribute and properties.

[But what about globals? Well, I’d just tuck them into some convenient dummy object “The gameState is nowhere. The gameState has a number called ennui. The ennui of the gameState is 5., etc.” I suppose score, turn count, etc., could be tucked into that object as well at the end of every turn.]

Thoughts? Thanks - Jack

Most* Inform 7 globals are stored in a single huge I6 array, so that’s not a problem.

*well, any that have to be accessed by hand-written I6 code are given I6 registers instead, but they’re the minority: things like “noun” and “second”.

A bigger problem might be tables, which are just big blocks of memory, but you can avoid storing data in those if you try hard.

Unfortunately the SHOWME command’s code is created by the ni compiler, which is closed-source so we can’t access the internals. The I6 code in Tests.i6t (which defines all the debugging verbs) looks like this:

[ ShowMeSub t_0 na; t_0 = noun; if (noun == nothing) noun = real_location; if (ShowMeRecursively(noun, 0, (noun == real_location))) { if (noun == real_location) print "* denotes things which are not in scope^"; } if (t_0 ofclass K2_thing) { print "location:"; ShowRLocation(noun, true); print "^"; } {-call:PL::Showme::compile_SHOWME_details} ];

In other words, a nice readable Inform 6 subroutine…with {-call:PL::Showme::compile_SHOWME_details} replacing all the interesting bits. This instructs NI to call an internal routine called “compile_SHOWME_details” that makes all the magic happen depending on the exact details of the game.

On the other hand, you might not need the showme code at all! After all, the fanciest thing it does is figure out the name and type of each property—but a save/restore system doesn’t need that. Just iterate over all objects, iterate over all property numbers, if the object provides that property, save its value as an integer. Whether those bytes represent an int, a float, a pointer, etc is all irrelevant to saving and restoring it: you’ve just got to put those same bytes back in the same spot.

Thanks, that’s very helpful. I had grepped for “showme”, but missed this due to case. ShowMeRecursively gives a nice tree, but provides only superficial details, so it looks like I have to pin my hopes on ShowMeSub, specifically that last somewhat opaque line, “{-call:PL::Showme::compile_SHOWME_details}”. I’d have to capture the text that spews out and parse it as needed, which I think is doable. One item I don’t understand: how is a parameter (the thing in question) passed to that last low-level routine, does it just work with the noun?

As for tables (and lists), they would have to be static, no reordering or destructive management. I’d need to rely on how they are defined at run time. Pointers to the table, like current_row, could be saved. Too bad everything isn’t an object.

Thanks again! - Jack

You want to look at the generated auto.inf code to see what {-call:PL::Showme::compile_SHOWME_details} turns into. It looks like:

if (t_0) {if (na > 0) { na = 0; print "; "; }
    if ((AllowInShowme(pluralname)) && (t_0  has pluralname)) { if (na++ > 0) print ", "; print "plural-named"; }
    if ((AllowInShowme(pluralname)) && (t_0  hasnt pluralname)) { if (na++ > 0) print ", "; print "singular-named"; }
    if ((AllowInShowme(proper)) && (t_0  has proper)) { if (na++ > 0) print ", "; print "proper-named"; }
    if ((AllowInShowme(proper)) && (t_0  hasnt proper)) { if (na++ > 0) print ", "; print "improper-named"; }
    if ((AllowInShowme(ambigpluralname)) && (t_0  has ambigpluralname)) { if (na++ > 0) print ", "; print "ambiguously plural"; }
}

…and then similar sections for properties defined on rooms, on thing, etc, etc.

So it’s not doing anything complicated, just checking each property and printing text.

It looks like that compiler line is hard-wired to display “t_0”, which is defined as a local variable in ShowMeSub.

Just out of curiosity, why would you want to implement a custom way of saving? Is it so it works across mutiple versions of the game?

Because even on the web you could just use regular saves.

Plus, if you want to save across versions, you have to deal with pointers going rotten. Which is a serious issue.

Thanks everyone for comments. I’ve never looked under the covers at auto.inf – that’s cool!

Natrium – I’m thinking about two things. First, how to save a game written with zoom/vorple. Isn’t that a still unaddressed limitation (at least in version 3 preview)?

But I was also curious in a more general sense. If one wanted to save an object, or maybe an object and its descendants (in terms of containment) or even a collection of objects, I wondered if there was a programmatic way to comprehensibly capture all of the properties of each object in a way that could be exported. Again, I’m thinking in terms of vorple, values can move back and forth between javascript and inform.

I’m toying with the idea of games where objects could be imported/exported to a server and perhaps shared between games and/or players. I don’t have a specific application in mind, but I think there is a lot of potential here.

  • Jack

If you want to capture all the properties, it’s straightforward if hacky: iterate through the property numbers, see if the object provides that property, if so save the value.