Glulx crash on undo after replay input

So I’ve got a bit of an odd one I’m having trouble chasing down.

I’ve got a Glulx Inform game (Inform v6.33 Library v6.12.1 S) that has a mainwin, mapwin and compasswin as well as a sound channel.

Everything seems to work fine except after I replay a command script, even one of only one command. After doing that, if I issue an “undo” command, Git and Glulxe report:
Reference to non-existent Glk object

Lectrote is, as expected more helpful, showing:
Quixe run: glk_get_line_stream: invalid stream

If I take the same command, I can type it in, hit and then type ‘undo’ and nothing untoward happens.

Now, I’ve protected, in my code, all references to my windows to make certain the pointers are non-zero before I access them, but I can’t chase this down.

Has anyone ever see anything like or care to take a guess as to the cause?

I’ve got a minimal example (attached) that demonstrates this. Sorry the extension is .txt, but it’s really an INF file. Getting around some forum foolishness that blocks INF files.

If it is compiled with -~D in any way, then it crashes when you type undo after inputting a replay file.

If you compile with -D and -S on, undo works fine, but of course I can’t release this way as things like “purloin” are still available.

So again, compile this way:
inform6.exe -G -~D -~S +include_path=./,E:/devl/inform/lib,E:/devl/inform/lib/contrib testgame.inf

then “undo” after a replay input is broken.

But compile this way:
inform6.exe -G -D -S +include_path=./,E:/devl/inform/lib,E:/devl/inform/lib/contrib testgame.inf

then “undo” works after replay input.
testgame.txt (1.25 KB)

I haven’t looked through your code, but you have to take extra care if you undo/restore/restart a move that opened or closed a window. Or even closed and reopened a window. The undo state does not include window identities, so you can be left holding an invalid reference.

(Window references are not memory pointers, although they share the convention that 0 means “none”.)

There’s a library function which iterates through the currently-open windows and makes sure every global variable refers a valid reference. See on the IdentifyGlkObject() function.

Thanks, zarf.

I did look through all that because before I created the minimal game showing the problem, I did have lots of windows (well, 3) as well as a sound channel.

I had looked over the guide and Adam’s gull/glk pages and my IdentifyGlkObject until my eyes gave out, assuming it was something I was doing.

For the minimal game up above, there’s almost nothing left in the code where it could be occurring… no windows are created explicitly by the code, it’s some objects and an Initialise routine. I believe, from reading the guide that I’m okay without an IdentifyGlkObject in that case, but this game is still doing it.

It’s also possible that it’s an issue with the compiler I’m using, as I understand it’s not in wide use anymore, versus the Inform7 i6 compiler.

Okay, in parserm.h in GGRecoverObjects, there’s the following:

#Ifdef DEBUG;
gg_commandstr = 0;
gg_command_reading = false;
#Endif; ! DEBUG

So we only wipe gg_commandstr and clear gg_command_reading when it’s a debug build (which, unsurprisingly, is when it works properly for me, not crashing). If it’s a release build, gg_commandstr is not cleared.

If I remove the ifdef, then I can do a release build, replay my single-command recording and then issue ‘undo’ properly.

Removing these ifdefs does of course break command recording through a “restart”.

I don’t propose it as a solution, just trying to understand the issue at the moment. I’ll dig a bit into what happens when a replay ends.

Meant to add…

At a guess, when we type ‘undo’, all the variables of the previous state are restored, including the values of gg_commandstr and gg_command_reading… so gg_commandstr because a non-zero pointer, but the file it points to is no longer open.

I think I have a workaround. If I have a react_before that watches for CommandOn and CommandOff, I can have my own global that tracks if command recording is on.

When the library turns off undo, GGRecoverObjects is called, which in turn calls IdentifyGlkObject at phase 0.

So in my IdentifyGlkObject, I can wipe gg_commandstr and set gg_command_command to false (which allows ‘undo’ to work after a command replay), but only do that if we’re not currently recording commands.

End result is I fix ‘undo’ after a command replay without breaking command recording. And the fix is in-game rather than fiddling with the library in case I’m breaking something else.

I don’t have time to test a complete solution here. However, catching up on the thread:

I don’t know why the gg_commandstr lines in GGRecoverObjects() are conditionalized with “#ifdef DEBUG”. There’s no good reason for that, except maybe the expectation that the RECORD command will only be used on debug builds, which is not a good assumption. You can just remove the “#ifdef” lines there.

(There are two pairs of “#ifdef” lines, one inside the switch statement.)

It shouldn’t. The convention is that open streams, such as the transcript stream (gg_scriptstr), can remain open through a restart.

Okay, cool. I’ll try commenting those out again and report back. Thanks for the input once again.

Okay, commenting out both sets of #Ifdef lines works fine. I had only done the one set before. Thanks!

I don’t know how I missed this thread, but I just now fixed the problem and pushed the result to the Gitlab repo. See