Adding an extra channel to glk, inform and quixe

There have been many posts over the years about how to communicate between inform and JavaScript. There has been talk of adding gestalt codes for certain interpreters, etc.

What would it take to do the following?
-Add a new string output channel for glk (similar to the main channel, the left hand status line, and so on)
-Instruct standard interpreters to ignore it (like a status line that just never gets seen)
-add an empty function to the JavaScript interpreter quixe that hackers can fill in
-add a new command in inform that prints to this channel.

From posts over several years, this would seem to be in demand, and would cut out the necessity of redoing this over and over again for each new interpreter. It would basically be a channel that the author could use to pass things through glk that aren’t intended to be seen, and would have many applications.

I don’t mind working on this, but I can’t change Inform itself, which is the biggest problem. I have done all of the above for the existing channel used for the right hand status line, but it shows up weird in non-hacked games.

It wouldn’t take too much. The difficult part will be modifying Quixe, not Inform.

First, you’d create a new type of stream. Write the functions for creating a new stream object, and then hook into glk_stream_close to handle closing the stream and sending the text to the JS engine for running. (Ideally going through the GlkOte interface so that it will work remotely, across frames etc.)

To get data back, you’d create a new type of event. Write functions for requesting the event, passing in a block of memory like for line input. Then your glk_select handler just has to handle the new event when it comes in.

The Inform side is easy because the compiler is future ready, you can call Glulxe and Glk functions which it doesn’t know exist.

Dannii,

This sounds very interesting! Could you give me a sample of Inform code that might call a Glk function it doesn’t know yet? I feel comfortable poking around with javascript, even if it takes a few weeks or months.

Here’s an I6 file showing how to call new Glk functions: github.com/curiousdannii/if/blo … e-text.inf

I haven’t written an I7 extension for that, but it’s just a matter of adding phrases which call I6 code.

I feel like I almost have the loop, but I’m not there. I need to intercept a new stream somewhere, but where?

There seems like a gap in my understanding with regard to glkote_update in glkote.js. That seems to be the master function that all sorts of streams come through.

It takes an input. However, it gets renamed to glk.update, which only ever gets called once, in quixe.js, with empty arguments. So I think I have a fundamental misunderstanding. When do args get passed to glkote_update by an outside function?

Edit: Oh, I see, glkote_update gets renamed to glkote.update which gets called by glk.update. So I passed that hurdle. On to the next!

Okay, here’s my next question: In Dannii’s example, and in this example: firthworks.com/roger/informfaq/ii.html

glk functions are called by something of the form glk($0023, 0, 0, 0, 3, 0);. Apparently the 23 means this corresponds to glk_window_open.

But glk_window_open is only defined by glkapi.js, and never called by Quixe at all. So what part of the code takes glk($0023, 0, 0, 3, 0) and translates it into javascript for Quixe?

Right, that’s the dispatch layer. github.com/erkyrath/quixe/blob/ … i_dispa.js

You’d need to add a new line to the big array in the middle of the function.

What are the definitions of the new functions you’re defining? I can help you with figuring out what lines to add.

Also do you have a reserved block of codes/selectors/etc yet? You could use some of mine if you want. Could even put up a public specification on my website if you don’t have anything better for that.

@Dannii What I want is to output a string into a new channel.

Then I want to find the place in the javascript side where it checks what channel is being inputted to. For instance, I know glkote_update is where the argument is checked to see what type it is (an update, a retry, an error, a pass) in a big switch statement.

So my goal is to find where the channel is checked in the javascript, and intercept that, with something like this (this is all made up):


function someupdate(arg){

...

if (arg.streamtype == whatever_stream) {
    perform_javascript_input(arg.input);
  }

...

}


function perform_javascript_input(input_text){
return;
}

I would leave the perform_javascript_input() function blank until I needed to do something with it.

So to answer your question, I just want to write a string to a new channel that isn’t being used for something else. It’s possibly I’ve completely misunderstood everything, but this is where I’m at right now.

Okay, my advice would be to handle all of the stream stuff before GlkOte gets involved, as GlkOte as it is now doesn’t know about streams, not really. So you’d open up a js stream, send text to it, and close it with glk_stream_close, which can then just append the code to the GlkOte update data.

Things to do:

  • Add a new stream type around line 2760 of glkapi.js
  • Make a new glk_stream_open_javascript function based on one of the existing stream functions, glk_stream_open_memory is probably simplest
  • Edit the gli_put_* functions to handle accepting text on the js stream
  • Edit glk_stream_close to handle closing the javascript stream

Doing this is quite involved sorry, but I think it’s the best way to do JS handling. Up to you if you think it’s worth it. Vorple does it a somewhat simpler way, through hacking the file system.

I want to do this myself one day, but I’m not there yet.

This really does sound involved! But it’s something I’ve been dreaming of, so I’ll keep trying. Thanks!

Hold my beer:

fyrevm-web

Another good option, and the functions could in principle be ported to other terps.

By the way, why can’t I just hack the opcodes directly? Like, at the end of the gi_dispa.js file, add a line like:

368 : new FuncSpec(368, "receive_js_command",new Prototype([new ArgUnicode()], null))

And then define glk_receive_js_command? Wouldn’t that directly give me a stream I could access in Inform by:

glk($0170, stuff); ?

Seriously…this is a solved problem if you use fyrevm-web. The entire system enables dynamic channels of output. You can make them up on the fly. All in Inform 7 with the fyrevm-web extensions.

The web side is rough and could use someone with web skills to “finish it up”, but it’s all there.

@DavidC It’s hard because I haven’t seen any completed fyrevm-web examples. Do you have one up on your website right now that I can see? I know you’ve run cloak of darkness on a local server before. If you had that uploaded to your page, I’d take a look at it.

Edit: Oh, I see you do have it. I’ll keep looking at it for a while. plover.net/~dave/cloakjs/ for anyone else interested.

@DavidC I’ve looked over the cloak of darkness example. The channels would work perfectly. This specific example doesn’t have ‘accretive text’ (or whatever it’s called); all the text gets replaced each time. Also, typing save or restore freezes the input bar and prevents further progress.

Are these the rough parts you were talking about? Because if that was cleared up, this would be great.

Edit: The question becomes, is it easier to hack Quixe or to add scroll back and save/restore to fyrevm? I’ll approach the problem from both angles until I find a solution.

Sorry for so many posts, but I’ll just say: I already have a solution for this that works fairly easily. By sending signals to the righthand status line, I can intercept them in Quixe and do anything I want (sound, graphics, styling changes, animations, videos). The only problem at all is that it looks a little bit funny if someone downloads the game file, which is why I provide a different version for download.

I guess I was just hoping for a solution that I could mainstream and help other players find easily.

Cloak is here: plover.net/~dave/cloakjs/

The extensions and terp are here: github.com/ChicagoDave/fyrevm-web

Cloak source looks like this:

[code]“Cloak of Darkness” by The IF Community

Include FyreVM Core by David Cornelson.
Include FyreVM Banner Output by David Cornelson.
Include FyreVM Prologue by David Cornelson.
Include FyreVM Text Styles by David Cornelson.
Include FyreVM Status Line by David Cornelson.

The story headline is “A basic IF demonstration using FyreVM”.
The story creation year is 2016.

p-style is a ui-style.

When play begins when outputting channels (this is the prologue channel rule):
say “[on prologue-channel][ui p-style]Hurrying through the rainswept November night, you’re glad to see the bright lights of the Opera House. It’s surprising that there aren’t more people about but, hey, what do you expect in a cheap demo game…?[/ui][paragraph break][end]”.

When play begins when not outputting channels (this is the normal prologue rule):
say “Hurrying through the rainswept November night, you’re glad to see the bright lights of the Opera House. It’s surprising that there aren’t more people about but, hey, what do you expect in a cheap demo game…?[paragraph break]”.

The prologue channel rule is listed last in the when play begins rules.

Use scoring.

The maximum score is 2.

[Whatever room we define first becomes the starting room of the game,
in the absence of other instructions:]

Foyer of the Opera House is a room. “You are standing in a spacious hall, splendidly decorated in [em]red and gold[/em], with glittering chandeliers overhead. The entrance from the street is to the north, and there are doorways [cmdlink go-south]south[/cmdlink] and [cmdlink go-west]west[/cmdlink].”

Instead of going north in the Foyer, say “You’ve only just arrived, and besides, the weather outside seems to be getting worse.”

[We can add more rooms by specifying their relation to the first room.
Unless we say otherwise, the connection will automatically be bidirectional,
so “The Cloakroom is west of the Foyer” will also mean “The Foyer is east of the Cloakroom”:]

The Cloakroom is west of the Foyer.
“The walls of this small room were clearly once lined with hooks, though now only one remains. The exit is a door to the [cmdlink go-east]east[/cmdlink].”

In the Cloakroom is a supporter called the small brass hook.
The hook is scenery. Understand “peg” as the hook.

[Inform will automatically understand any words in the object definition
(“small”, “brass”, and “hook”, in this case), but we can add extra synonyms
with this sort of Understand command.]

The description of the hook is “It’s just a small brass hook, [if something is on the hook]with [a list of things on the hook]
hanging on it[otherwise]screwed to the wall[end if].”

[This description is general enough that, if we were to add other hangable items
to the game, they would automatically be described correctly as well.]

The Bar is south of the Foyer. The printed name of the bar is “Foyer Bar”.
The Bar is dark. “The bar, much rougher than you’d have guessed after the opulence of the foyer to the north, is completely empty.
There seems to be some sort of message scrawled in the sawdust on the floor.”

The scrawled message is scenery in the Bar. Understand “floor” or “sawdust” as the message.

Neatness is a kind of value. The neatnesses are neat, scuffed, and trampled.
The message has a neatness. The message is neat.

[We could if we wished use a number to indicate how many times
the player has stepped on the message, but Inform also makes it easy
to add descriptive properties of this sort, so that the code remains readable
even when the reader does not know what “the number of the message” might mean.]

Instead of examining the message:
increase score by 1;
say “The message, neatly marked in the sawdust, reads…”;
end the story finally.

[This second rule takes precedence over the first one whenever the message is trampled.
Inform automatically applies whichever rule is most specific:]

Instead of examining the trampled message:
say “The message has been carelessly trampled, making it difficult to read.
You can just distinguish the words…”;
end the story saying “You have lost”.

[This command advances the state of the message from neat to scuffed
and from scuffed to trampled. We can define any kinds of value we like
and advance or decrease them in this way:]

Instead of doing something other than going in the bar when in darkness:
if the neatness of the message is not trampled:
if the neatness of the message is neat:
now the neatness of the message is scuffed;
if the neatness of the message is scuffed:
now the neatness of the message is trampled;
say “In the dark? You could easily disturb something.”

Instead of going nowhere from the bar when in darkness:
now the message is trampled;
say “Blundering around in the dark isn’t a good idea!”

[This defines an object which is worn at the start of play.
Because we have said the player is wearing the item, Inform infers that it is clothing
and can be taken off and put on again at will.]

The player wears a velvet cloak. The cloak can be hung or unhung.
Understand “dark” or “black” or “satin” as the cloak.
The description of the cloak is “A handsome cloak, of velvet trimmed with satin, and slightly splattered with raindrops.
Its blackness is so deep that it almost seems to suck light from the room.”

Carry out taking the cloak:
now the bar is dark.

Carry out putting the unhung cloak on something in the cloakroom:
now the cloak is hung;
increase score by 1.

Carry out putting the cloak on something in the cloakroom:
now the bar is lit.

Carry out dropping the cloak in the cloakroom:
now the bar is lit.

Instead of dropping or putting the cloak on when the player is not in the cloakroom:
say “This isn’t the best place to leave a smart cloak lying around.”

Understand “hang [something preferably held] on [something]” as putting it on.

Test me with “s / n / w / inventory / hang cloak on hook / e / s / read message”.
[/code]

Right, you could just send text code as one function call. The reason why my plan uses streams is to allow you to programmatically build up code in your story code.

All the turn data is saved ever time. If you look at the channel data in dev tools, you’ll see history. This allows you to reconstruct any turn at any time.

The only rough part is that I’m not a web designer and this really needs a skilled front-end dev/designer to make it “ready for prime-time”. Note I’ve asked the couple of people within the IF community that are good front-end devs and they’ve flatly refused to help. (Too busy or why bother are the common responses)