Author’s note: I originally posted this earlier this year to the roguelikedev subreddit. Then I deleted it in my periodic social media purge, but thought this should be preserved somewhere.

DCSS webtiles does all the “engine” stuff on the server side. Its architecture, and most of the client-side and middleware code, was developed by our devteam member edlothiol (Florian Diebold) starting in 2011. There are three components (and a fourth non-webtiles component for online console play over ssh):

  • The Crawl binary (written in C++) does everything the console version does, including rendering ASCII to the console. It *also* opens a Unix-domain socket which it uses to send information about the visible game state to the web server, as a JSON stream. One copy of Crawl runs for each player.
  • The webtiles server is a webserver written in Python (using the Tornado framework). On one end, it communicates with the Javascript client through the web; on the other, it launches the Crawl binary and talks to it through the socket and through Crawl’s stdin. The server is responsible for keeping track of who is playing or watching which games, and based on that forwarding JSON messages from Crawl to the appropriate clients, and converting client messages into game input. The server also handles in-game chat, and serves static files such as tilesheets and javascript over HTTP.
  • The client is written in Javascript. It establishes a websockets connection to the server, and speaks JSON over this connection. Playing a game versus watching it uses the same client code. The client takes the JSON messages coming from the server and uses them (along with the tilesheets) to determine what to render in its canvas. Input from the user is translated into a JSON message and sent to the server. Many things you would think can be handled on the client side, like scrolling a multi-page menu or displaying the inventory, actually go through the server, so that watchers can see what the player sees even before the full command is issued. One exception is the “line reader” (used whenever we ask the user to type something): there, the input editing uses an ordinary HTML text field; only when the user presses <Enter> are its contents sent to the server as input.
  • Console games replace the server with (an old version of) dgamelaunch, the software developed for nethack.alt.org by paxed (who is now a member of the nethack devteam, congrats). This is a really thin wrapper around the game: it runs Crawl, sends the game’s stdout to both a ttyrec and to dgamelaunch’s stdout, and sends its own stdin to Crawl’s stdin (this is why console players can play robotfindskitten, or boggle or atc, on some of the servers: the wrapper is so thin it works with pretty much any game). It is the responsibility of sshd or telnetd to actually do the network communications: as far as dgamelaunch is concerned, it’s just talking to a tty. Console watchers are fed a stream of the player’s ttyrec. On most servers dgamelaunch runs the webtiles version of the Crawl binary, and tells it to make a (Unix-domain socket) connection to the webtiles server as well. That way webtiles watchers can connect to and watch games being controlled from the console; the other direction is handled by having the webtiles server also make ttyrecs. dgamelaunch has its own messaging system, entirely separate from webtiles chat.

Hidden information, such as the contents of out-of-sight map cells or the identity of unidentified objects, never leaves the Crawl binary. Even before Webtiles existed, we kept most public and private information separate: there are classes for map_knowledge and monster_info entirely separate from the actual map and monster data. Items do use the same type, but have a function to strip off unknown info.

Lame ASCII-art diagram. As in Crawl, § represents a cloudthe Internet.

                 SERVER SIDE   §   PLAYER SIDE
                               §
          unix socket     websockets
            (JSON)           (JSON)
      CRAWL <----> WEBTILES <--§--> CLIENT
        ^   <-----          ---§-->
        |    stdin           HTTP
        |   (UTF-8)      (images, JS, etc)
        |                      §
        |                      §
        `--> DGL <---> SSHD <--§--> SSH
           stdin/stdout        §
         (UTF-8 + curses)      §