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.