Glulx Virtual Timers

I have been noting some demand for a fuller solution for real-time stuff in Inform 7 (the existing extensions Basic Real Time and Real-Time Delays are both stop-gaps, really). I created a pretty nice system of virtual timers into the old Glimmr animation extension, and thought that I’d port that over with some changes. I’m hoping to get a small community of folks interested in developing and maintaining it, since I probably won’t dedicate time to writing documentation, completing every corner of the implementation, or maintaining the extension in the future.

Before I release the first draft, I thought I’d check in to see whether there is a better way to do one of the more essential functions: callbacks, i.e. the function that gets called when the timer reaches 0.

Virtual timers are represented by a kind of value (“virtual timer”) and are intended to be essentially invisible to the author. Here’s how I want it to work: When you create a timer, the extension automatically selects a timer instance that isn’t running, sets up its properties, and ensures that it will fire at approximately the right time no matter how many other virtual timers might be running.

It’s essential that callbacks be both anonymous and flexible. What I have used in the past is a piece of text for the callback. The text could either be something that will simply be printed, or using the trick below, it could call essentially any phrase (though some punctuation is verboten).

To say @ (ph - phrase): (- if (0==0) {ph} -).

The idea is that for most authors, using the extension wouldn’t involve much more than this sort of thing:

every 250 seconds "[@ play sound of Theme B]" after 5000 milliseconds "[@ play sound of creaking door]";

Authors who needed to could of course define custom phrases, or e.g. call rulebooks from the callback text element.

So, my question is: Is there any other method that might seem more natural and Inform-7y that would be equally as simple and powerful as this trick with text? (I haven’t looked much as 6L38’s features, so if there’s something new that fits the bill–great!)

Thanks!

Maybe use rules. It’s more verbose, but I think more Inform-y:

Every 250 seconds follow the theme-b rule.
This is the theme-b rule: play sound of Theme B.

There’s a possible confusion in that authors might try to write “Every 250 seconds move the player to the Kitchen,” and the syntax wouldn’t allow that – it would be “Every (interval) follow (rule),” and maybe also “Follow (rule) every (interval).” Still, this is what I’d go with.

(6L38 doesn’t add anything new in this area.)

Hmm, the only improvement I can suggest is that the @ symbol isn’t necessary: To say (ph - phrase): (- if (0==0) {ph} -).

I think it would be good to support text phrases, actual named phrases and named rules - overloading phrases is easy after all. And when your text phrases are used, it would be good to use named texts rather than inline texts, so that then they can be removed later.

Well, it isn’t quite as easy as overloading phrases, since the phrases assign the parameter to a property for storage, e.g. (incomplete pseudocode):

To after (chron - a number) milliseconds (callback - text): let item be a random inactive virtual timer; now the timer-callback of the item is callback; now the item is active; request a glk timer event of chron.

The callback isn’t invoked until a timer event fires and the virtual timer decides that said event relates to it. So to allow multiple types of parameter, the code would at that point have to decide which type of event was associated with the virtual timer and cause it to fire. E.g. whether to follow a rule or named phrase, say a text, print the text of a response, etc. That’s not impossible at all, but it is cumbersome.

That’s why I like the text->phrase structure–it handles all of these with one mechanism:

[code]let chosen track be a random sound name;
after 250 seconds “[@ play the chosen track][chosen track]”; [both plays and prints the track; phrase invocations can be chained in the same way.]

every 5000 seconds “[one of]You hear water dripping[or]Something seems to groan ahead–or is it behind? You’re not sure[or][at random].”;

after 5000 milliseconds “[@ follow the atmospheric text rule]”;

after 0 milliseconds “[text of print empty inventory rule response (A)]”;[/code]

… The invocation for all of these is simply “say the callback of the virtual timer”. I think the @ symbol helps make it immediately clear to the user whether the text is to be printed or whether it is an instruction, but maybe it is better to get rid of it. I’ll have to think about it.

(I assume that by “named texts” you are referring to the new “responses”. I don’t know much about how they work, which is why I’m using that strange empty inventory rule response example from the manual.)

I think a lot is lost if you just go to invoking rules, as zarf suggests, but it might be worth it given how strange the all-purpose text may seem to folks not familiar with this old but obscure technique…

Maybe the best answer is to allow either a rule name or text to be supplied. That would be an intuitive distinction for all Inform users, and folks who grok the uses of all-purpose text can use it for that.

I was originally thinking of a table, but properties on objects work fine too: just define multiple properties and then test which one has been set. With texts, phrases and rules the code wouldn’t be very complex.

Sorry, named text isn’t a common term, I meant text constants. Supporting all of these would be good:

[code]To play theme B (this is playing theme B): play sound of Theme B.
Play Theme B is always “[play theme B]”.
This is the play theme B rule: play theme B.

every 250 seconds “[@ play theme B]”;
every 250 seconds “[play theme B]”;
every 250 seconds Play Theme B;
every 250 seconds playing theme B;
every 250 seconds play theme B rule;[/code]
Well these probably couldn’t all work at the same time because they’d have naming conflicts, but you get the idea.

Btw, I have long wished that I7 had lambda functions, but I guess you had already invented them with your @ phrases! Maybe the @ symbol should be kept, just to make it perfectly clear what’s going on. Five characters ("[@ ]") isn’t really too bad for a lambda function, all things considered. Still not as good as native supported anonymous functions, but it’s close.

I was going to say that true lambda functions should wrap local variables up in a closure. But in fact this syntax does that!

(I knew that I7 generates closures internally, but I didn’t realize that they could be pushed through to the I7 level like this.)

Yeah, it’s a beautifully compact and versatile syntax. I can’t take credit for anything other than understanding how great it is, though: John Clemens posted it to his site (now gone, I think) in 2008 or 2009–soon after I7 first came out–and I’ve kept it in my toolkit ever since.

I’ll think about the text constant thing–I wonder how many people are using them for anything other than writing extensions?

OK, I’ve got a working draft of this extension, at raw.githubusercontent.com/i7/ex … Timers.i7x

I’ll be adding an example gamelet to demonstrate the basic features/syntax in the next few days, but felt like getting the base extension up there. There are still some things to be cleaned up, and it hasn’t been thoroughly tested at all–but that’s why I’m posting, of course!

I do want to call attention to this snippet (lines 96ff), and ask whether there is a better way to do this:

[Inform 7 seems to have no way to test whether an enumerated value is valid for its kind; this does that for virtual timers.] To decide whether (chron - a virtual timer) is a valid timer: repeat with test running through virtual timers: if chron is test: decide yes; decide no.

My impression was that in past builds, you could do, e.g. “let chron be a random active virtual timer; if chron is a virtual timer…”, but that doesn’t work in 6L38. Maybe it never worked for enumerated values, though–I would have been more likely to use objects rather than values in the past for this sort of thing.

Have a look if you like, or wait for the demo later in the week…

You could write:

if T >= first value of virtual timer and T <= last value of virtual timer: …

Thanks, that’s at least a good bit less verbose!

[Posting here since the Inform Extensions board is closed to new posts]
There is now a demo for the Glulx Virtual Timers extension. It’s a silly little “game” with mechanics that I hope don’t make their way into real releases–but it nevertheless showcases some of the features of the extension. You can check it out and/or play it online here:

dl.dropboxusercontent.com/u/947 … index.html

Here’s what the extension does:

Glulx/Glk has only a single timer for real-time events. Glulx Virtual Timers creates a system of high-level “virtual timers” that allows authors to treat that single timer as if it were many, and to do so in a very easy way. For example, to create a one-time event:

After 20 seconds follow the things are underway rule.

No more code is needed to define the timer, other than to write the rule that should fire when those twenty seconds are up.

Glulx Virtual Timer features:

  • Multiple “virtual timers”* running concurrently (or consecutively, of course). The extension defines 7 timers, and users can create as many more as they like.
  • Timers can be anonymous, or authors can create named ones to allow for manipulation, test whether a given timer is running, etc.
  • Timers can trigger a one-time event, cycle indefinitely (until stopped by the author), or cycle a preset number of times.
  • Multiple ways to allow real-time events to safely interrupt line input are provided.
  • Timers can trigger events on the follow turn, allowing for a sort of compromise between real-time and strictly turn-based play (this is the functionality provided by Sarah Morayati’s old Basic Real Time extension).
  • Timers are automatically paused at problematic points, such as during alternate input (disambiguation, the final question, etc.)
  • Users can pause all timers, restarting them in unison exactly where they left off.
  • Timers gracefully handle undo, restart, and restore. For example, when the player types UNDO, the timer resets to the state it was in at the beginning of the previous turn.
  • Timers can be used to delay all input, i.e. to pause the game for a given period (this is the same functionality provided by the old Real-Time Delays extension). This is used in the demo for display effects on the title page.

If you’re into extension-authoring or code-prettifying or documentation-writing, there are a few things I’m hoping to get help with. I probably won’t do much more with the extension–including the stuff listed below–so my ideal would be for this to wind up as an extension authored by “Team I7 Glulx” (or some other collective that sounds less lame.) Things left to do include:

  • Testing. While the extension is functional, it has not been well tested.
  • The basic code for the virtual timing should probably be redone. Currently, when a timer is activated, it is compared to the rate at which the Glk timer is running and a new rate for the Glk timer is chosen as needed. That is, if the Glk timer is returning an event every 100ms, and the new timer requests a rate of 150ms, then we drop the global timer’s rate to one event every 50ms in order to ensure that all events will trigger when they should. Rather than compare to the global timer, however, we should probably be comparing all active timers to one another to choose the best rate. The current code is functional, but the latter seems more robust.
  • It might make sense to make the deferral of timer events during alternate input (such as disambiguation) optional, e.g. for authors who might be using the timer to handle music/sound effects.
  • Review the code interface for authors. Are there better ways that authors could interact with the extension? Should there be phrases that make interrupting line input to print something to the screen less verbose, for example?
  • Consider whether the extension should support more features.
  • Aside from the demo code, the documentation remains entirely to be written!

If you’d like to collaborate, the extension is hosted on Github:

github.com/i7/extensions/blob/m … Timers.i7x

I’ve added documentation, and also changed the name of the extension to Glulx Real Time.

Download: raw.githubusercontent.com/i7/ex … 20Time.i7x