vorple.js

I could write that much Javascript. Not a problem. But so what? It wouldn’t have anything to do with T3.

Sending input back is not trivial at all, unless there’s a document that explains exactly how to do it, step by step, with all of the relevant options shown with code examples. It may be trivial to you, but you’re not actually explaining anything. And that’s the problem I have with this whole concept. I’d be happy to read the manual, but where’s the manual?

If you want a manual, you’re probably in for a bit of a wait. If you want an explanation of how the TADS 3 client / server model works, I can give you a high level summary: the TADS 3 virtual machine plays the role of a traditional web server, and responds to client requests with formatted output. It never sends or pushes data to the client; it waits for the client to ask.

The default WebUI client basically just calls serverRequest("/webui/getEvent") repeatedly. serverRequest is a wrapper around AJAX - here, meaning asynchronous javascript. It’s pretty important that this is asynchronous and you’ll see why in a minute. It’s also pretty important to wrap around the AJAX details, because they are different for each browser and that’s not much fun to worry about.

On the server side, the question is what does “/webui/getEvent” mean? In lieu of documentation we have the source code, which tells us that this is a vpath (virtual path) provided by eventPage. When a request comes in, it winds up in the processRequest method for that object.

Now, eventPage is special because it knows that it won’t always have an event ready to go when the client asks for one. So it basically just sits on the connection - it remembers that the client asked for something, but it doesn’t acknowledge the request in any way. As far as it’s concerned, the game might not ever reply and that would be just fine.

If this request had been synchronous, then the browser would simply block (freeze) until the server replied. But since it was an asynchronous request, the browser decides that it doesn’t actually need the answer now, and in the meantime it can let the user do silly things like typing, clicking and scrolling.

The basic TADS / WebUI command cycle looks like this:

  • Browser accesses /webui/getEvent, TADS sends the contents of the status line.
  • Browser accesses /webui/getEvent, TADS sends any output for the command window (room name, etc).
  • Browser accesses /webui/getEvent, TADS sends a special tag that means “ask for a line of input”.
  • Browser accesses /webui/getEvent, TADS has nothing left to say so it just sits tight.

If the player starts typing and clicking at this point, the client code kicks in.

  • All keystrokes are caught and added to the input line until the player presses enter.
  • The browser accesses /webui/inputEvent, passing the player’s input as part of the URL.
  • The browser has satisfied the game’s latest request for a line of input, and stops capturing keys.

“/webui/inputEvent” is provided by inputEventPage. From there the input gets routed through processRequest to the main window and on to the parser. The server / game spends a few cycles thinking it over. When it has something to say, it “prints” it into the commandWin’s buffer. (commandWin is an instance of WebCommandWin which gets initialized in browser.t.)

Sooner or later, the game will stop “printing” text and want to ask the player for input again. It does this by calling the getInputLine method in the commandWin, which causes two things to happen:

  • All “printed” text is flushed out to the client, in the form of an event destined for the command win, aka cmdwin.htm.
  • An input line is requested from the client.

Now that we have events to send, the server digs up the browser’s long-suffering request for /webui/getEvent and replies with the command window output (its first queued event). Lo and behold, the client comes right back and asks for another event, and this time it gets the “ask for a line of input” response. Undeterred, it comes back a third time and winds up cooling its heels in /webui/getEvent limbo again.

(You may ask, and it’s a good question, why the client asks for this last event, given that it’s already been told to go to its room, do some homework, and come back when it has a line of input to share. There are a couple reasons, actually: the game may be running a real-time Daemon or Fuse; or if another player has joined the session, he may beat this client to the punch and type in the requested input line first. In both cases the game needs a way to say, “Hey, forget about that line input request, and here’s some more text to print, and oh by the way could you send me another line of input?”)

Anyhow, the essential takeaway here is that you can declare new client windows, send events to those windows from the server, and listen for those events in your client code. You can even send a reply back to the server and process the data from the reply.

This is pretty much the foundation for the entire system so it’s “trivial” in the sense that the problem is already solved. Admittedly this makes light of a lot of hard work on MJR’s part and I don’t mean to trivialize that effort. But it doesn’t have to be your effort because like I said, the client / server communication part is just plumbing and you seem more interested in remodeling the bathroom.

If I can say this in a respectful tone, without it being read as snarky … “Hunh?”

The difficulty I’m having is this: What you just wrote makes, I’m sure, perfect sense. But it doesn’t tell me how to DO anything. Also, I understood only the tiniest bits and pieces of it. I know what a client is. I know what a server is. I understand why the communication is asynchronous. I’ve heard of AJAX. But that’s about it. The rest of it might as well be written in Farsi, for all the good it does me.

Lest those who just drifted into the room conclude that I’m indulging in pointless whining, I should probably point out that I’ve written and released three complete TADS 3 games, plus half of another. If I can’t make heads or tails of it, how likely is it that other TADS authors or would-be authors will be able to use these cool new features?

When John Cage was a young man, he made a profound decision about his career as a composer. It wasn’t enough, he realized, just to write the music. His job wouldn’t be complete, he saw, until he got the music performed. Since he became, in the end, one of the most influential composers of the 20th century, it’s at least arguable that that was a smart move on his part.

I’m tempted to apply this observation to the T3 WebUI, or for that matter to any other great piece of software. It isn’t enough just to create the software. Your job as a programmer isn’t finished until a complete, accurate, and well-written owner’s manual is published.

Not taking potshots at MJR, who is an amazing, genius-level guy for whom I have immense respect. Just saying, “Hunh?”

I wish there were more and better manuals for every aspect of TADS 3. My willingness to discuss technical details or reference the source code is not meant to trivialize the need for documentation. Nor is my lack of willingness to write it. I agree that it’s a real problem; as it happens it’s not the problem I am most interested in solving.

Understood. I’m not trying to get on your case either, Ben! I’m just wishing I knew how to do what I want to do.