May 27th, 2010

Network services, part 3

This time I'm getting into some inner details of the networking layer, so my usual disclaimer: to write a game you won't have to know how any of this works.  This is mostly for those interested in how this all works on the inside.

As I've been explaining, the http server that's feeding HTML/XML to the player's browser is part of the game itself, not an external Apache server or anything like that.  This is important because it means that a game's process lifecycle is the same as what we're accustomed to with writing conventional TADS games: the game and all its objects stay in memory throughout the game session.  Meaning that as an author you don't have to overhaul your whole programming approach to fit the transactional programming model more typical of web servers, where nothing stays in memory between requests.

But if you're already familiar with that web model, this raises a bootstrapping question.  If you're thinking of regular web programming, and you're hearing me say the game is an http server, you're probably imagining that our server machine will need to have a bunch of "t3run mygame.t3" processes started up at boot time, idly waiting for people to connect.  That's basically the way a normal web server runs - you set up an Apache daemon (httpd)  to launch at boot time, bind to port 80, and start listening for connections.  If the game is a web server, does this mean that it has to start at boot time and bind to port 80?  And if so, what happens if two people want to play at the same time?  Do we need one game on port 80, another on port 81, etc?  Obviously the answer had better be no, and it is.

In fact, there is no "t3rund" that runs continuously or gets launched at boot time.  Instead, t3run processes are spun up on demand.  For a stand-alone configuration, where everything runs on the player's PC (just like a conventional TADS game today), the technical details are pretty simple:

- the player double-clicks mygame.t3 on the desktop
- the OS launches "t3run mygame.t3"
- t3run loads mygame.t3
- mygame.t3 does the 'srv = new HTTPServer' setup work
- mygame.t3 calls connectWebUI(srv)
- conectWebUI opens the local browser and navigates to the game server
- the game server returns the initial HTML page for the game

The magic function is connectWebUI().  This is another net built-in, whose function is to connect the newly launched game server with the user interface.  This function is the key abstraction in making stand-alone vs Web play seamless to the game (and the game programmer).  The interpreter knows how the user is connecting, based on how the interpreter was invoked (and based on its capabilities - HTML TADS will continue to be for local play only, for example).  When the interpreter is invoked in stand-alone mode, which is the case when it's invoked from the desktop shell by a .t3 file double-click, it knows that "connect to the UI" means to open a browser window locally and connect it to the game's http server.  And remember that when I say "browser window" here, I really mean a customized TADS application frame with an HTML widget inside.  Although on some systems it might really be just a simple browser invocation - either way works; it's just a matter of how seamless it looks.

So from the user's perspective, they double-click a .t3 file, and a game window appears on the desktop.  Exactly like running a conventional game with the current system.

In the Web play configuration, everything is exactly the same from the game's perspective, but of course the user's experience is a little different, as is the underlying implementation.  Even in the Web play configuration, t3run is spun up on demand, so the question is, how do we demand it?  Clearly we need some kind of web server running.  I keep saying there's no Apache involved, but that's not entirely true; it is true that we don't need Apache involved once t3run is launched, but we do need some kind of conventional web server to do the launch in the first place.  So on a machine that hosts t3run, we'll need to set up a regular web server, and install a couple of php scripts alongside the t3run executable.  These scripts will be part of the TADS release, and shouldn't need per-system customization.  Web server installation should thus be very straightforward; it'll probably amount to some file copies and a little .htaccess editing.  What these scripts do is handle an HTTP request that's directed to the "launch t3run" URL on the server, by launching a t3run process instance, communicating with the new process to get its TCP port, and replying to the HTTP request with a redirect to the newly established t3run server.  The sequence of events is like so:

- the game's author puts a hyperlink on her home page
- the player finds the game page and clicks on the hyperlink
- the browser navigates to the web page in the link
- the Apache runs the t3run launcher php script
- t3run starts up and loads the game .t3
- the .t3 program does the 'new HTTPServer()' stuff
- the .t3 calls connectWebUI(srv)
- connectWebUI communicates the port info back to the php script
- the php script sends a '301' redirect reply with the t3run server URL
- the browser navigates to the redirect URL
- the game server returns the initial HTML page for the game

Note that the first step, where you put a link on your home page, will probably be the only thing you need to "install" as an author.  That link will probably point to a generic, shared t3run server on some other machine (as we got into in the comment discussion for my previous post "Network services part 2").  So you probably won't need to host your own t3run server, although you'll certainly be able to if you have a hosting plan that's capable of it.

From the game's perspective, there's absolutely no difference between this and the local configuration - the game creates an HTTPServer object and connects it to the UI via connectWebUI().  From the player's perspective, it's also extremely simple: click on a link, the game comes up in the browser.   From the server's perspective, configuration is easy (just some packaged php scripts to install); t3run instances are created on demand, and bind to OS-assigned random ports, so the server machine can run as many simultaneous game sessions as its CPU and memory will permit.  TADS games tend to be pretty light on CPU averaged over time - the sense-path stuff can make them gorge on cycles in brief bursts, but overall they spend most of their time waiting for user input.  The limiting factor for scaling is probably memory; I'd guess TADS 3 games are mid-sized in web app terms.  I expect a minimal 128 MB VPS could easily handle 10-15 simultaneous sessions for a good-sized game.  I think I'm being conservative with that estimate; we'll see when I get a little further along with the implementation.