Inform6 command echo & invisible commands

Hello hello!

Does anyone know how to stop the echoing of each entered command in Inform6? Or, and what I’m really after, create verb that doesn’t get echoed back out?

I want to send commands to my game without the player seeing them and the game keeps echoing the command out.

Thank you!

How exactly are you sending commands to the game? From standard input, or within I6 code?

If you’re doing it from Javascript, you can call GlkOte.extevent() and then I think it shows up as a Glk event in the game. Then you can respond to it there without printing anything.

I think. I did something like this in the Hadean Lands app. It’s been a while.

Yeah, this was to support “click a room in the map, travel there”. It was a bit of a nuisance, but I had this JS code:

// arg is an integer
GlkOte.extevent('map-travel-'+arg);
game_options.extevent_hook = function(val) {
    var match = val.match(/^map-travel-([0-9]+)/);
    if (match && match[1]) {
        var addr = 1 * match[1];
        return {
            type: 0x80002200,
            val1: addr
        };
    }
};

Then in I6 code:

[ HandleGlkEvent ev ischar args   val;
	if (ischar == 0 && ev-->0 == $80002200) {
		val = ev-->2;
		glk_cancel_line_event(gg_mainwin, gg_event);
		VM_PrintToBuffer(buffer, INPUT_BUFFER_LEN-WORDSIZE, PrintVisitNum, val);
		return 2;
	}
	return 0;
];

[ PrintVisitNum val;
	print "-VISIT-NUM ", val;
];

This generates a command “-VISIT-NUM VAL” and processes it without printing it.

(Actually I do print “go to ROOM” while handling the action. That was a choice, though.)

The nuisance lies in the fact that you can only send integers into the game this way, not strings. Fortunately for me, the argument I needed to send was a room. So I represented each room as its Glulx object address, which is of course an integer.

Thank you for the detailed reply! I’m having trouble following the flow here. This feels like the starting point, how you would trigger the process in JS. No problem so far.

I’m not clear on what role the extevent_hook value plays. Is this documented somewhere? What calls it and what is it returning to? In glkote.js extevent just calls send_response(‘external’… and send_response doesn’t seem to call into the hook function.

I checked the release notes but couldn’t find it mentioned. Is this a builtin function in inform6? If so, where can I find documentation on it.

I couldn’t find any documentation on it, but it appears to be a Quixe thing:

Yes (or rather, it’s a stub that gets called if you provide it):


This is mentioned in the release notes.

I was clearly looking at the wrong release notes, thank you for that link, I’ve added it to my growing collection :smiley:

I’m still confused regarding HandleGlkEvent. The release notes describe it as an “entry point” similar to [Initialize;] but then it’s documented as a Glulxe-specific function that is called from within Inform6 code.

Does a call to GlkOte.extevent trigger an actual Inform6 entry-point? If so, where is that entry point documented?

It delivers a Glk event to the event-loop that the I6 library is running almost constantly when compiled for Glulx. That’s kinda the point of Glk, it’s the I/O interface between the interpreter and the story.

You can probably read more about it in the Glk specification.

Or you can see the event loop itself if you look at the context of that call to HandleGlkEvent that I linked above.

No. It causes the current glk_select() call to exit with specific values in the gg_event array. (This call is deep inside KeyboardPrimitive(), which is where the game is blocked waiting for line input.)

I don’t think it’s documented. It’s in glkapi.js in the Quixe code. It transforms a Javascript object (created by the GlkOte.extevent() call) into a set of integers (which is what winds up in the I6 gg_event array.)

Then how does the Inform6 code (i.e. my story code) know that the event has occurred? For example, what calls [ HandleGlkEvent; ] from your code original code example?

KeyboardPrimitive() calls it. Look in parserm.h.

Oh, so you’re redefining (or defining for the first time) the Stub HandleGlkEvent declared in grammar.h. Which is called in parser.h. So your code…

[ HandleGlkEvent ev ischar args   val;
	if (ischar == 0 && ev-->0 == $80002200) {
		val = ev-->2;
		glk_cancel_line_event(gg_mainwin, gg_event);
		VM_PrintToBuffer(buffer, INPUT_BUFFER_LEN-WORDSIZE, PrintVisitNum, val);
		return 2;
	}
	return 0;
];

redefines the behaviour to catch special events that your javascript code is introducing when it calls GlkOte.extevent().

Searching the Inform library, it doesn’t appear that HandleGlkEvent is defined anywhere else so I suspect a redefinition of HandleGlkEvent doesn’t harm anything.

From looking at the context that @mirality linked to, it seems that an implementation of HandleGlkEvent should return 2 if it handles the event or 0 to let Glulxe take over again.

Did I miss anything?

If not, I guess the last question is to how to pass in something more complex than a single number to GlkOte.extevent()? A string, for example.

Right. This is what “entry point” means, at least the way the DM4 uses it. It’s usually defined as a Stub; it does nothing by default; you can create a routine with that name in your own code to customize library behavior.

See the release notes.

This mechanism only supports integers, I’m afraid.

You can pass 0-2 integer values. Note that these can each also be addresses of an object/string/array, but AFAIK this has to be set up on the I6 side in advance; you can’t “fill in” data on the JS side. (Well, it might not be entirely impossible, since the VM state is accessible. But it’s highly dodgy.)

If you’re looking to pass arbitrary data from JS to I6 then the way to do it is via files (possibly only using an event to signal the I6 code to read in the file).

Ugh. You know the whole reason I started this inquiry was to trigger my I6 code to read from a file. With all the confusion over how to use extevent, I forgot why I was doing it in the first place :stuck_out_tongue:

Thank you!

Here’s what I’ve done:

  • Added Replace HandleGlkEvent; to my .inf before including “grammar”
  • Added this routine to my .inf:
    [ HandleGlkEvent ev context; 
        print "HandleGlkEvent^";
    
        return 0;
    ];
  • Add this code to my .js:
    let game = GlkOte.getinterface();
        
    game.accept({
        type: "external",
        gen: game.io.lastGenValue,
        window: 0,
        value: 42,
    });

    game.io.lastGenValue++;

However, the game never prints “HandleGlkEvent”. What did I miss?

Is there some reason you’re not calling GlkOte.extevent and implementing extevent_hook as zarf suggested?

I’ve replaced GlkOte completely in my implementation and, as far as I can tell, I’m doing everything that it would do when it comes to GlkOte.extevent.

As to extevent_hook, from reading the code it seemed like it was for processing the value passed to extevent and, since I wasn’t interested in the values right now, didn’t put it in. However, I added this code…

game_options.extevent_hook = function(val){
    console.log("Event hook: " + val);
    
    return { type: 42, val1: 68, val2: 33};
};

but it’s never called.

Update: my event hook function is now called and it seems that the external event call is being passed on to the VM but now I get this error in the console:

Quixe run: glk_put_jstring: window has pending line request

To double-check, I switched back to using the original GlkOte instead of my custom implementation and I get the same error.