The Web UI

As in adv3, so in adv3Lite, you can choose between the traditional TADS user interface or the new Web UI. The method for configuring this is almost identical to that used for the standard adv3 library, apart from one or two file-names. For the full details, see the section on "Playing on the Web" in the TADS 3 System Manual. In the present section we reproduce part of that material, duly adapted for use with adv3Lite.

In the traditional UI, the user installs a TADS interpreter on his or her machine, and the interpreter displays the user interface using the native features of the operating system — opening a window on the Mac, for example, or writing to the terminal window on Unix. With the Web UI, the user interface isn't part of the TADS interpreter at all, but instead runs in a standard Web browser. The game still runs in the TADS interpreter, but the interpreter doesn't show any UI windows; instead, the game sends everything to the Web browser for display, via a network connection.

The Web UI configuration offers several big new capabilities beyond what's possible with the traditional TADS user interface:

Choosing between the regular UI and the Web UI

The Web UI is a whole separate output layer from the traditional user interface, with its own set of functions. This is good news and bad news.

The good news is that the Web UI gives you vastly more control and power than the traditional console UI offers. For one thing, all of the networking features are directly available - they're not hidden inside the interpreter, but are readily accessible and controllable from your program. For another, you have direct access to all of the browser-side coding: you can write your own HTML, CSS, Javascript, and AJAX code to run in the browser, so you can customize virtually every aspect of the user interface. There's absolutely nothing about the Web UI that's built into the interpreter; everything that's predefined is simply part of the library, so it's readily accessible and customizable. Your game's UI can do anything that a Web site can do - drag-and-drop, animation, dynamic pop-up lists, layered objects, menus, and on and on.

The bad news is that you must choose in advance whether your game will use the Web UI or the traditional UI. If you stick to the basic command-line user interface, the only difference is in compiler options, so you can actually compile the same game both ways — so you'd have a single set of source files, but you'd compile it twice, once into a .t3 file for conventional play, and again into a separate .t3 for Web play. On the other hand, if you decide to take advantage of the more advanced features of the conventional UI (such as sound playback or the Banner API), or if you want to customize the Javascript front end of the Web UI, your game will be tied to the UI that you choose.

If you have an existing game that you created with a previous TADS 3 version, you can port it to the Web UI. If you were careful to use only Adv3Lite functions for input and output, rather than calling built-in interpreter functions directly, porting your game to the Web UI should be a simple matter of recompiling with the Web UI compiler settings. If you made use of more advanced UI features, though, porting requires somewhat more work:

Creating a new Web UI game from scratch

The first step in creating a Web UI game is to set up your build instructions (your project's .t3m file) to include the networking functions and the Web UI version of the Adv3Lite library.

If you're using Workbench on Windows: Select the Create New Project command. The "wizard" dialog will ask you which type of user interface to use; select the "Adv3Lite - Web UI" option from the list of project types.

If you're creating your project file manually: Follow the standard procedure for creating a .t3m file for a regular Adv3Lite game. Once you've set up the basic .t3m file, follow the instructions below for changing a .t3m file from a conventional game to the Web UI.

Porting an existing game

To port an existing game based on the traditional console user interface (including HTML TADS games), the first step is to switch to the Web UI libraries. This requires a few changes to your project's .t3m file.

Attention Workbench users on Windows: It's possible to make the necessary changes using Workbench, but the procedure is actually a bit easier if you edit the .t3m file manually, so the explanation below assumes you're going that route. Close Workbench before proceeding, and use a different editor, such as Notepad. Don't attempt to edit the .t3m file using Workbench or while it's open in Workbench, since Workbench will overwrite any changes you make as long as Workbench has it open.

Here's the procedure to update your .t3m file to use the Web UI:

The first item, -D TADS_INCLUDE_NET, tells the compiler to include the network-related built-in functions. These interfaces aren't included by default because they're not universally supported on all interpreters, and a game that includes them won't run on an interpreter that doesn't support them. This makes it better to omit them unless you actually make use of them, which of course you must for a Web UI game.

The second item, -lib adv3LiteWeb, selects the Web version of the Adv3Lite library. Adv3LiteWeb emulates all of the programming interfaces from Adv3Lite related to basic command-line interactions, such as displaying text or HTML output, reading command lines, reading keystrokes, and so on. This lets you write a Web UI game as though it were a standard console-based game.

The third item, -lib webui -source tadsnet, adds the TADS Web UI library to the build, along with some helper classes for the networking interfaces. The Web UI library is a separate component from Adv3LiteWeb. Adv3LiteWeb doesn't actually implement the Web interface, but rather implements an emulation of the traditional Adv3Lite input and output functions on top of the Web UI library. The Web UI library is a whole separate subsystem that consists of a Javascript library that runs in the user's Web browser, and a TADS library that runs on the server machine hosting the game. The TADS and Javascript portions communicate with each other using the TADS networking system.

The Web UI library is analogous to the traditional input/output functions built into the interpreter. With traditional console-based games, you always had the option to bypass the Adv3Lite library and work directly with the interpreter built-ins. For Web-based games, you similarly have the option to bypass the Adv3LiteWeb library and work directly with the Web UI library. If you do so, of course, your game will be inextricably tied to the Web UI - you won't be able to easily port it back to the console UI later. But the trade-off is that you can exploit the full power of Javascript in the browser.

Depending on how much custom UI work you use in your existing game, the library additions above might be all you have to do. The Adv3LiteWeb library emulates the input and output functions in Adv3Lite, so you won't have to change anything related to displaying text or reading input as long as you used Adv3Lite functions for I/O rather than calling the interpreter built-ins directly.

If you use any HTML image or audio files, such as JPEG images, there's an extra step. See resource files below.

To test your game in Web UI mode, recompile with the .t3m changes above. If it compiles successfully, try running it. If you're using Windows, HTML TADS will run a Web UI game locally as though it were a standard game - so you don't have to set up a Web server to test Web UI mode.

If you encounter any compiler errors building your game after making the .t3m changes, you must have used a part of Adv3Lite that isn't supported in the Web version. The most likely missing piece is the banner API - Adv3LiteWeb doesn't support it. The Javascript front end has analogous capabilities, but the specifics are different enough that there's no Adv3LiteWeb emulation for the console-mode banners.

If the game compiles successfully, but doesn't work correctly when you run it, your game probably calls built-in functions in the interpreter rather than using the Adv3Lite equivalents. You'll have to examine your code and eliminate calls to built-ins.

Building the same game both ways

If you stick to the basic command-line user interface, and you're careful to use Adv3Lite library methods for all of your input/output operations, porting from the console UI to the Web UI is usually just a matter of changing your .t3m file as described above.

An interesting implication is that you can easily create both UI versions of your game from the same set of source files. If you create two copies of your .t3m file - one for the console UI, and another for the Web UI - creating the two versions is just a matter of compiling the game with each .t3m file.

Why would you want to do this? The main reason is to accommodate different users' preferences. Playing on the Web has a number of advantages (greater portability, no need to install anything, the ability to resume a saved game on different machines, multi-user collaboration), but the traditional console UI has some advantages of its own (faster response time, no need to be connected to the Internet while playing). Offering both versions of your game lets users pick the mode that works better for them.

Here are some tips for creating a dual-mode game:

Resource files

If you use HTML resources such as images or audio files (JPEGs, PNGs, MP3s, etc), there's an extra detail to tend to: you have to tell the library that it's okay for client browsers to download those files. The library is cautious about network security, so by default it doesn't let clients download anything without your say-so.

It's fairly easy to specify resource download permissions. First, you should group your resources into one or more folders - that is, subdirectories of your main project folder for the game. Second, for each folder, add an object definition like this to your game:

WebResourceResFile
   vpath = static new RexPattern('/foldername/')
;

That tells the library to treat anything in the "foldername" subdirectory of the game folder as an HTML resource, allowing the client browser to download those files. It's just an ordinary object definition, so you can put it anywhere in your source code that object definitions are allowed. If you divide your resources into multiple folders, just add a definition like the one above for each folder. (You don't have to do this for subfolders, though - the '/foldername/' definition will allow files in subfolders of 'foldername'.)

QUIT, disconnect, and program termination

In the traditional console UI, the way the game program terminates is fairly straightforward. When the player types QUIT, the Adv3Lite command loop exits, which unwinds the call stack until the main() function returns. This terminates the game program, at which point the interpreter program exits to the operating system. Alternatively, on GUI versions (such as QTads or HTML TADS), the player can end the program by closing the main window. This is processed in much the same way as QUIT internally: the interpreter sends an "end of file" indication to the command loop, to let it know that no more input is forthcoming (as there's no way for the player to enter more input without the main window), causing the command loop to exit.

With the Web UI, things aren't quite as simple. The complication is that the player and the game are on separate computers. At first glance, it might seem as though we could take the same approach: if the player types QUIT or closes their browser, we should simply let the TADS program terminate as usual. But what happens if the player's network connection momentarily lapses? We obviously don't want to treat that the same way as an intentional disconnection, but it's not always possible to tell the difference with the network protocols involved. And what happens if the player presses the Refresh button in their browser? It turns out that "Refresh" and "Close Page" are essentially indistinguishable from the perspective of a Web page. Further, what happens if the player types QUIT, but still wants to see what's on the page for a while after? If they try to refresh the page after typing QUIT, the server - the game program - still has to be running to service the request.

To handle these various situations, the Web UI has to use a different approach to managing its session lifetime. For the Web UI, the program's job is larger than merely running the game; it's also acting as a Web server. The Web server's lifetime isn't defined by the duration of the game session, but rather by the existence of connected clients: as long as there's a client connected, the Web server needs to be available to service any new requests from the client.

As we mentioned above, the network protocols we use don't tell us with certainty whether or not a client is connected. More precisely, they don't tell us with certainty that a client is not connected. We can tell for sure that a client is connected, because if we receive a request message, we know that it had to come from a connected client. But the absence of any requests doesn't necessarily mean that there are no clients; it could simply mean that the client doesn't have any work for us at the moment, or that the client computer is tied up doing something else before it can issue its next request, or that a network glitch has temporarily delayed messages from the client.

The Web UI has a two-pronged approach to dealing with all these uncertainties. First, the Javascript portion of the UI uses a publish/subscribe model to maintain a continuous communication channel with the server. The server uses this channel to send periodic "keepalive" messages to the client when no other messaging activity is taking place. This ensures that the client and server exchange messages with a minimum frequency (about every 90 seconds — frequently enough to allow reasonably timely detection of a disconnected client, but rarely enough that the extra messages won't affect network performance). Second, the server monitors client sessions for long periods of inactivity. When a client doesn't responds to our "keepalive" messages for longer than a certain interval (about 60 seconds), we assume that the client has disconnected. When we haven't heard from any clients for a certain interval (about 120 seconds), we assume that the player must be finished with the game. This the point when the program exits and the interpreter terminates.

The timeout intervals mentioned above are nothing magical; they're just parameters in the Web UI server code, chosen based on practical experience. Detecting a disconnection is inherently probabilistic: we can never be certain a client is never coming back, no matter how long it's been since we last heard from them, but the longer we go the more likely it becomes that we have a disconnection rather than something temporary like a network glitch or client-side CPU bottleneck. The practical goal is to choose timeouts that let us shut down the server-side program as soon as possible when it's no longer needed (so that the server machine resources aren't tied up longer than necessary), but no sooner.

Note that when running in local stand-alone mode, some interpreters maintain a direct connection to the browser window that allows them to detect when the player closes the window. This bypasses the timeouts and allows the game program and interpreter to terminate immediately when the player closes the browser window. This is possible in standalone mode because everything's running on one machine, which eliminates the uncertainties of the network configuration and allows the interpreter to know exactly what's going on with the browser. The Windows interpreter provides this feature.

Local single-machine (stand-alone) testing

One of the main reasons to create a Web UI game is that it allows users to play on the Web, without having to download your game or install software. However, Web UI games can also be played without a Web server, a browser, or a network connection. Most TADS interpreters let you play a Web UI game in "local" mode, also known as standalone mode - this means that you run the game on a single computer without a Web server involved. Note, however, that the local mode WebUI interpreter built into Workbench can be unstable, causing frequent crashes. If you encounter this problem the best workaround is probably to develop and test your game in the default (non WebUI) mode and then convert it to a WebUI game following the instructions above.

You'll probably want to use standalone mode while you're developing and testing your game, since it would be inconvenient to have to upload your game to a server every time you recompile.

Debugging with Workbench on Windows

If you're using Workbench on Windows to develop your game, standalone mode is the default way of running. When you use the Go command, Workbench automatically runs the game in standalone mode. Everything looks just like it does with a traditional console-mode game. The only difference is that the game window is actually an embedded Web browser - but this is all handled invisibly, so you don't have to worry about launching a browser separately or typing a URL or anything like that.

As we described earlier, a Web UI game continues running even after the player types QUIT, so that it can continue to provide HTTP services as long as the client is connected (to allow page refreshes, for example). This can be a little strange when you're using the debugger. There are two simple ways to truly terminate the game program: you can close the game window, or you can use the Terminate Game command in the debugger. Closing the game window triggers a special message to the game program that tells it to terminate immediately, bypassing the usual timeout intervals that would otherwise keep the game running for a while longer. When you use the Terminate Game command, this directly ends the game program.

Adv3LiteWeb usage tips

When using the Adv3LiteWeb library, there are some additional things you should do to make sure your game works correctly.

Use aHref() for command links

In HTML TADS games, you can use <A HREF=xxx> to create clickable command links. With the Web UI, all HTML is displayed by a standard Web browser, so the <A HREF> tag has its standard meaning, which is to create a hyperlink to a separate Web page.

To create a command link, use the Adv3LiteWeb function aHref():

"To save the game, type <<aHref('SAVE')>>. "

You should continue to use <A HREF> for actual hyperlinks, where the link navigates to another Web page rather than entering a command, as well as for special links such as mailto: links.

Don't use <TAB>

The <TAB> tag is an HTML TADS extension that isn't in standard HTML. Standard Web browsers don't support it. Unfortunately, standard HTML doesn't have a simple equivalent of <TAB>, but you can usually achieve similar effects using <SPAN> or <DIV> tags with CSS styling to control spacing.

Use inputManager methods for input

As we've already mentioned a couple of times, you can't use the interpreter's built-in functions for input and output. The built-ins all operate directly on the local console, which obviously isn't available to a remote user playing on the Web.

Instead of the interpreter built-ins, always use the inputManager equivalents:

The promptFunc parameter can be supplied either as an anonymous function that displays a prompt or simply as a single-quoted string. For getInputLine() and getKey() (or their macro equivalents) this parameter can be omitted altogether; you can simply display a prompt string before calling the method.

For the most common cases you can use the following macros:

Use cls() instead of clearScreen()

You shouldn't use the built-in function clearScreen(), just as you shouldn't use any other built-in I/O functions. Use cls() instead - this will work correctly in both Adv3 and Adv3web.

Don't use the Banner API

The Banner API isn't supported in Adv3LiteWeb. This includes both the low-level banner functions built into the interpreter and the Adv3Lite BannerWindow class.

The Web UI provides similar capabilities, but it uses a new programming interface. The new system is much more flexible than the banner API because it uses Javascript and CSS in the client. This combination isn't limited to the "tiled" model of the banner API - you can create overlapping windows, create transparent windows, move windows dynamically, set sizes using font metrics, etc. The new system is so different that it makes more sense to work directly with the new interfaces, so that you can take better advantage of their new capabilities.

Note that Adv3LiteWeb does provide the higher-level components that were built on the banner API in Adv3Lite: the status line, the menu system, and the hint system. The Adv3LiteWeb versions of these components are built directly on the new Javascript front end instead of the banner API.

Sound playback

The <SOUND> tag is an HTML TADS extension that's not in standard HTML, so you can't use it for sound playback in Web UI games.

There's currently no support for sound playback in the Web UI library. Mike Roberts plans to add this in the future. For the moment, if you need sound playback, you'll have to resort to direct Javascript programming.

<ABOUTBOX>

The <ABOUTBOX>> tag is an HTML TADS extension, not part of standard HTML. You shouldn't use this tag. There's currently no equivalent in the Web UI. However, note that the Adv3Lite library only calls gameMain.setAboutBox() when running in console mode, so it's fine to write your <ABOUTBOX> code as usual in that method.