Quixe and timed events

It looks like zarf has pushed the fix, so you should be able to download the newest Quixe files and install them manually in your Inform templates folder (instructions for installing into template folder are here).

I am so happy I can’t even measure it!

Er, hrm, how do I… compile this back together? He made an adjustment to one of the js files that aren’t included… Hrm, sec…

Still getting the same error. Here’s the code I’m trying:

To get an input:
(-VM_ReadKeyboard(buffer, parse):wink:

To decide whether player consents:
say “[link]yes[end link] or [link]no[end link][line break]”;
While 1 is 1:
get an input;
if the player’s command matches “yes”:
decide on true;
if the player’s command matches “no”:
decide on false;
say “Please choose yes or no. >”;

Well, I’m baffled. Even with the fixed version of the glkote, Quixe is indeed still crashing on the attempt to print (i.e., “gli_put_char” [should be “glk_put_char”, no?]) during line input. Zarf’s code works, but any solution that uses Glulx Entry Points fails in the same way.

This is baffling because there is no attempt to print in any of the GEP code as far as I can tell. My best guess is that something other than a straight “say” is nevertheless resulting in a put_char request. Nothing intentionally printed is causing the problem; if it were, Gargoyle–which suppresses text printed illegally–would not produce the desired output. Instead, Gargoyle’s output is identical to the Mac IDE interpreter’s, and the latter doesn’t suppress output at all. (Well, they aren’t exactly identical–the IDE interpreter doesn’t print a line break when the line event is canceled as Gargoyle does, but that is a well-known divergence from spec on the part of the IDE terp.)

For reference, I’ve pulled out the relevant code from GEP and Inline Hyperlinks–the HandleGlkEvent code (excerpted from GEP) controls the flow, and the code beneath is the sequence of rules that are being called by HandleGlkEvent. This is followed by a complete example along the lines of Zarf’s but using Inline Hyperlinks. Finally, the same example using the Basic Hyperlinks extension (which also uses GEP under the hood).

[code]Include (-

[ HandleGlkEvent ev context abortres newcmd cmdlen ;
context = 0; ! suppress ignored warning
switch (ev–>0) {
evtype_Hyperlink:
FollowRulebook( (+glulx hyperlink rules+) );
if ( FollowRulebook( (+command-counting rules +) ) && RulebookSucceeded())
{
FollowRulebook( (+input-cancelling rules+) );
FollowRulebook( (+command-showing rules+) );
if ( FollowRulebook( (+command-pasting rules+) ) ) return 2;
}
}
];

-) before “Glulx.i6t”.

A glulx hyperlink rule (this is the default inline hyperlink handling rule):
now the current hyperlink ID is the link number of the selected hyperlink;
unless the current hyperlink ID is 0:
cancel glulx hyperlink request in main window;
cancel glulx hyperlink request in status window;
follow the hyperlink processing rules;
if the status window is the hyperlink source:
request glulx hyperlink event in status window;
otherwise:
request glulx hyperlink event in main window.

A hyperlink processing rule (this is the default command replacement by hyperlinks rule):
now the glulx replacement command is entry (current hyperlink ID) of the hyperlink list;
rule succeeds.

A command-counting rule (this is the ordinary checking for content rule):
if the number of characters in the glulx replacement command is 0, rule fails;
rule succeeds.

An input-cancelling rule (this is the cancelling input in the main window rule):
cancel line input in the main window;
cancel character input in the main window;

To cancel line input in the/-- main window:
(- glk_cancel_line_event(gg_mainwin, GLK_NULL); -)

To cancel character input in the/-- main window:
(- glk_cancel_char_event(gg_mainwin); -)

A command-showing rule (this is the print text to the input prompt rule):
say input-style-for-glulx;
say Glulx replacement command;
say roman type;

A command-pasting rule (this is the glue replacement command into parse buffer rule):
change the text of the player’s command to the Glulx replacement command;
rule succeeds.

[/code]

[code]Include Inline Hyperlinks by Erik Temple.

Release along with an interpreter.

Test is a room.

Instead of jumping:
say “Are you sure? [link]YES[end link] or [link]NO[end link]”;
if the player consents:
say “Whee!”;
otherwise:
say “You maintain your dignity.”[/code]

[code]Include Basic Hyperlinks by Emily Short.

Release along with an interpreter.

Test is a room.

Instead of jumping:
say “Are you sure? [set link 1]YES[end link] or [set link 2]NO[end link]”;
if the player consents:
say “Whee!”;
otherwise:
say “You maintain your dignity.”

Table of Hyperlink Glulx Replacement Commands (continued)
linknum replacement
1 “YES”
2 “NO”[/code]

Maybe someone else can make sense of this.

OK, I’ve just run the Inline Hyperlinks example from the previous post step-by-step through EMacsUser’s awesome debugger (in Gargoyle, since I don’t think it’s possible to use with Quixe), and found that the flow is indeed just as I described. There is absolutely NO PRINTING to any window while line input is pending after clicking on a hyperlink in the yes/no routine. So I’m happy to call this a bug in Quixe.

What type of bug remains to be seen. Could GlkOte itself be trying to print? Or failing to cancel the line event despite the call to glk_cancel_line_event?

I know that call works, because I used it in my solution.

I have not yet traced through your code in Quixe. I will try to get to that tomorrow.

Small idea. I do know when I click on things, it displays what the new command is.

Is this related?

Zarf, there are print statements in INDEXED_TEXT_TY_Cast(). Obviously, that routine is supposed to be printing to a memory stream rather than the screen, but is it possible that Quixe is overzealously considering those calls to be printing to the main window stream? (If so, I still don’t understand why this would happen only from within YesOrNo(), and not when we are in standard command-reading loop. But the most salient difference between your minimal example and the examples that use Glulx Entry Points is that the latter make use of indexed text…

Otherwise, there are no print statements at all before the call to glk_cancel_line_event in either of the examples I gave.

I can anecdotally add that my game Six uses tons of ‘start an X second timer’ timers, and prints more text before they’ve run their course, and this has never caused crashes in Gargoyle. (Edit - looks like you guys already proved that it wasn’t Gargoyle, using science?)

Quixe doesn’t crash, per se–it terminates on the error. In the same situation, Gargoyle merely suppresses the output; it doesn’t terminate. So it’s at least possible that something is being printed in both cases–maybe just an empty string–but that it is only visible to the user of Quixe, since Quixe stops operating on the illegal call. However, I stepped through the whole process line-by-line in a debugger last night, and the game didn’t make any attempts to print to the screen before the call to cancel line input. (The debugger was running in Gargoyle, FTR.)

Just to clarify the situation–the illegal printing error only happens when the game is running from within YesOrNo(), not when glk_select is called from the standard parser, nor from within disambiguation. This example includes all three situations:

dl.dropbox.com/u/947038/YesOrNo% … /play.html

–Erik

Further investigation:

The stack at the error point looks like:


0x107fd <InformFunc $107c1 ‘YesOrNo’>
0x9542 <InformFunc $9506 ‘KeyboardPrimitive’>
0x11b9 <InformFunc $117d ‘VM_ReadKeyboard’>
0x11f <InformFunc $e3 ‘HandleGlkEvent’>
0x250b6 <InformFunc $2507a ‘FollowRulebook’>
0x253bd <InformFunc $25381 ‘ProcessRulebook’>
0x253bd <InformFunc $25381 ‘ProcessRulebook’>
0x2058b <InformFunc $2054f ‘DivideParagraphPoint’>

DivideParagraphPoint(), of course, contains a new_line statement.

So, not a Quixe bug. It’s I7’s habit of printing a newline between rules, according to some set of criteria that I truly have never grokked.

(That came out of the Basic Hyperlinks version of the example.)

Thanks as always for the help, zarf! DivideParagraphPoint() has to be the source of the problem. For some reason, I had the impression on my line-by-line debugging step-through that the newline in that function was being skipped (it only fires if say__p is a positive value), so sorry not to have seen that. I was probably nodding off with boredom–stepping through line by line is incredibly dull!

Presumably, the solution will be to either put an explicit “run paragraph on”–or better yet a line break/period–in the last say phrase prior to the “if player consents”. (I won’t be able to test this properly until much later today.)

This raises a couple of questions for me about Quixe:

  1. Would it make more sense for Quixe to merely suppress illegal input rather than cease operating? A warning could be sent to the javascript console to indicate that something illegal had happened. I’m afraid that particularly where line breaks are concerned, the affordances of the different interpreters make things like this pretty hard for a non-savvy person to debug: Zoom goes ahead and prints the illegal line break, so we get the break that way; Gargoyle doesn’t print the illegal break, but does print a break (not printed by Zoom) when it receives the glk_cancel_line_event instruction. So figuring this out without learning about callstacks and debugging tools is just about impossible…
  2. How does one get at the callstack you posted in Quixe?

Thanks!
–Erik

Just tested, doing a [run paragraph on] before the VM_Readkeyboard avoided the crash, awesome!

Just to clarify, you did something like this, right? Just adding a run paragraph on right before “if player consents”?

[code]Include Inline Hyperlinks by Erik Temple.

Release along with an interpreter.

Test is a room.

Instead of jumping:
say “Are you sure? [link]YES[end link] or [link]NO[end link][run paragraph on]”;
if the player consents:
say “Whee!”;
otherwise:
say “You maintain your dignity.”[/code]

I did this:

[code]To get an input:
(-VM_ReadKeyboard(buffer, parse):wink:

To decide whether player consents:
say “[link]yes[end link] or [link]no[end link]>[line break][run paragraph on]”;
While 1 is 1:
get an input;
if the player’s command matches “yes”:
decide on true;
if the player’s command matches “no”:
decide on false;
if the player’s command matches “y”:
decide on true;
if the player’s command matches “n”:
decide on false;
say “[link]yes[end link] or [link]no[end link]>[line break][run paragraph on]”;
[/code]

I agree this is a bad situation, but my choices are to either get game authors to stop doing it, or allow them to keep doing it. Putting the error message in the javascript console is “allow them to keep doing it”, because nobody will see it there.

(We went through this fifteen years ago with Z-machine opcodes.)

The correct maneuver here is to fix the IDE interpreter to be strict. I can’t do that myself, unfortunately.

I added a few lines to dump a stack trace (addresses only) when a fatal error occurs. (Lemme push that out.)

Then I pasted the stack addresses into a half-assed script which parsed the gameinfo.dbg file. And I do mean half-assed; this was my profile-analyze.py script (from the glulxe distribution) with half of its ass chopped off with an axe.

I’ve used a little script (based on your profile-analyze.py) which generates files like these:

vm_functions = {3584: 'Hex', 11648: 'IncrementBranchTesttestfunc', 3208: 'Banner', ... 

Do we think it would be useful to have something like that produced by I7 itself automatically?

I was thinking that we could define a “symbolicated” Glulx format, where the gamedebug output is appended straight onto the end of the game file. (With the length and checksum fields updated, so that it’s a legal Glulx file and the gamedebug data is VM-readable data.)

That shouldn’t screw up any already-compiled games, should it? The standard library doesn’t use @setmemsize, and if a game does use it, the newly-allocated memory will just appear after the debug block.

Flag this in the header in some way (bump the ‘Info0100’ ID to ‘Info0101’). Then both VM-code debuggers and interpreter-level debuggers can detect it and parse the debug data.