What is the IFComp automatic-transcript-saving protocol?

First, an apology: I have a distinct memory of asking this before, and Dannii answering with a link to a repo with everything written down. But none of my searches can find that topic anywhere!

Anyway…

As I continue messing with the Å-machine web interpreter, it would be nice to hook it up to IFComp’s automatic transcripting features. The web interpreter actually already has a setup for this sort of logging (it’s just disabled by default); but its protocol looks different from the Parchment one.

How the Å-machine one works

It keeps track of two numbers. pos is the total number of characters sent to the server; servpos is [???].

On every input, it sends an AJAX request:

data: {
    session: [game]-[date]-[time]-[random number]
    text: [raw text of the transcript from `pos` to the end]
    pos: `servpos`
}

Whatever the server sends back is saved as the new value of servpos. Then pos is set to the end of the current transcript.

It doesn’t seem like it should be too hard to adapt to the IFComp Parchment format; but, I don’t know what the IFComp Parchment format is. Is there documentation of this protocol somewhere?

(The Å-machine implementation will also be opt-out the same way as Parchment. Nothing will be recorded if the user doesn’t want it to be.)

3 Likes

I see this, which looks promising. It looks like it starts with:

session: [date][time][random]
start: {
    story: [string]
    version: [string]
    info: [???]
    interpreter: [string]
    browser: [string]
}

Which returns “OK”.

Then each message is supposed to be:

session: [date][time][random]
log: {
    inputcount: [total number of inputs]
    outputcount: [total number of outputs...how is that different?]
    input: [what the player typed]
    output: [what the game printed in response]
    window: [???]
    styles: [???]
    timestamp: [date][time]
}
1 Like

That’s the old defunct format. The new one is documented at

There’s also a basic server you can test with.

3 Likes

Fantastic, thank you! The only thing I don’t understand from the README is the timestamp and outtimestamp fields. These are the Unix times when the input was submitted, and when the game finished producing output afterward?

Well, I guess not the only thing—looking at the server, I’m also curious:

  • Should the output field also contain the player’s typed input, because input is discarded by the server?
  • It looks like I should expect the server to return a 200 code and no body if recording is successful?

If you want to match exactly what Parchment does then it’s milliseconds since the epoch (Date.now()). I don’t remember if they’re actually used.

Was it this thread?
(I found it while I was digging earlier.)

Yes! That would be it! Tyvm

And one more question, even: what exactly is it that the IFComp website tries to inject into the HTML, so that I can look for the right identiers?

Maybe this (in function _enable_recording) is the place?

1 Like

All right! I have a basic implementation up and running. It’ll need more testing before it goes live, but it should take the data injected by IFComp and use it to send data in the appropriate format.

Like with Parchment, if cookies (transcript_recording_opt_out=1) or query parameters (?nofeedback) indicate that the player does not want their transcripts sent, it will be disabled. Unlike with Parchment, there will also be a checkbox in the menu that can enable or disable it at any time.

The checkbox will only appear if remote feedback is set up (i.e. parameters are supplied either by the author or by the IFComp website), and will start out unchecked if transcript_recording_opt_out=1 or ?nofeedback is supplied, otherwise checked.

Feedback is only sent on player input, so flicking the checkbox on and off experimentally will not immediately send all your past logs. Flicking it on, typing a command, and then flicking it off will send all your past logs, unless that would be more than 50kB, in which case it assumes the server doesn’t want that much data at once and gives up.

Sessions should also be maintained across closing and reopening the tab, though that needs a bit of fire-testing.

Hopefully that’s a solution that works for everyone!

1 Like

Yes because echoing input is up to the storyfile. (And I guess for simplicity.) A server could use the input to identify what the command was, but it’s not needed.

Yep.

It injects ParchmentOptions, though in this case it’s just a few of the GlkOteOptions that are relevant.

Note though that the IFComp won’t inject anything if it doesn’t know about the format. So if you’re wanting to make auto transcripting work for Dialog the IFComp site will need to be taught the format and what to do to it.

1 Like

JTN pointed me to this block of code:

sub _enable_recording {
    my ($self) = @_;
    my $play_file = $self->content_directory->file('play.html');
    my $game_file = $self->inform_game_file->basename;
    unless ( ( -e $play_file ) && $game_file ) {
        # Look for index.html as well
        $play_file = $self->content_directory->file('index.html');
        unless ( ( -e $play_file ) && $game_file ) {
            # No play.html or index.html? OK, this isn't a standard I7 "with interpreter" arrangement,
            # so we won't do anything.
            return;
        }
    }
    my $play_html = $play_file->slurp;
    my $options_js_object;
    if ( $play_html =~ m{game_options.*</head>}s ) {
        # It's Quixe
        $options_js_object = 'game_options';
    }
    else {
        # It's Parchment
        $options_js_object = 'parchment_options';
    }
    # Activate transcription, aiming it at the local transcription action.
    # (Via injecting additional values into the game_options config object.)
    my $entry_id           = $self->id;
    my $transcription_code = <<EOF;
<script>
if ($options_js_object) {
    $options_js_object.recording_url = '/play/$entry_id/transcribe'
    $options_js_object.recording_format = 'simple'
}
</script>
EOF
    $play_html =~ s{</head>}{$transcription_code</head>};
    $play_file->spew($play_html);
}

Which looks like it should inject two values into either game_options (if the word game_options appears literally in the document head) or parchment_options (if it doesn’t).

So I’ve currently added this block to the document head:

	<script>
		game_options = {}; // The IFComp server injects some data into here
	</script>

And at setup time, if the script finds a recording_url key in that object, it copies it into options.aaLogServerPath and sets options.aaLogFormat to 'ifcomp'.

Is that the right way to go about it? Unfortunately I don’t know how exactly IFComp recognizes different formats, and I have no real way to test this until IFComp season starts (by which point the deadline will be very tight).

(In particular, it seems like this code also looks for inform_game_file to decide whether to inject the code, but I can’t figure out how it actually makes that determination…)

Talk to @markm – he’s the one who manages the ifcomp app. You definitely want to have it worked out before IFComp season begins.

1 Like