summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/webserver/connection.py
diff options
context:
space:
mode:
authorFlorian Diebold <flodiebold@gmail.com>2011-10-06 23:05:31 +0200
committerFlorian Diebold <flodiebold@gmail.com>2011-10-15 23:18:00 +0200
commit50c127e8ad514a2817be501f8bd2acbeb1bce4e4 (patch)
tree073e5b5af7dbe680202740922a66fcdd537c1b08 /crawl-ref/source/webserver/connection.py
parentcde49cf01ee4d1acf2a4c36050fbeb8c228d1606 (diff)
downloadcrawl-ref-50c127e8ad514a2817be501f8bd2acbeb1bce4e4.tar.gz
crawl-ref-50c127e8ad514a2817be501f8bd2acbeb1bce4e4.zip
Make Webtiles output happen in parallel to the console display, and make the server record ttyrecs.
Crawl compiled with WEBTILES=y should now be playable normally (i.e. indistinguishable from one compiled without WEBTILES) when run from a terminal. (This is not yet completely the case.) The Webtiles data is written on a Unix-domain datagram socket; the Crawl parameter -webtiles-socket determines a path on which the Crawl process receives control messages. The Webtiles server then runs Crawl in a pseudo-terminal and records its console output into a ttyrec file. The goal of all this is of course to be able to watch Webtiles games from ssh, and later the reverse.
Diffstat (limited to 'crawl-ref/source/webserver/connection.py')
-rw-r--r--crawl-ref/source/webserver/connection.py81
1 files changed, 81 insertions, 0 deletions
diff --git a/crawl-ref/source/webserver/connection.py b/crawl-ref/source/webserver/connection.py
new file mode 100644
index 0000000000..e5675aa561
--- /dev/null
+++ b/crawl-ref/source/webserver/connection.py
@@ -0,0 +1,81 @@
+import socket
+import fcntl
+import os, os.path
+import time
+import warnings
+
+from config import server_socket_path
+
+class WebtilesSocketConnection(object):
+ def __init__(self, io_loop, socketpath):
+ self.io_loop = io_loop
+ self.crawl_socketpath = socketpath
+ self.message_callback = None
+ self.socket = None
+ self.socketpath = None
+
+ self.msg_buffer = None
+
+ def connect(self):
+ if not os.path.exists(self.crawl_socketpath):
+ # Wait until the socket exists
+ self.io_loop.add_timeout(time.time() + 1, self.connect)
+ return
+
+ self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+
+ # Set close-on-exec
+ flags = fcntl.fcntl(self.socket.fileno(), fcntl.F_GETFD)
+ fcntl.fcntl(self.socket.fileno(), flags | fcntl.FD_CLOEXEC)
+
+ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+
+ # Bind to a temp path
+ # Ignore the security warning about tempnam; in this case,
+ # there is no security risk (the most that can happen is that
+ # the bind call fails)
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ self.socketpath = os.tempnam(server_socket_path, "crawl")
+ self.socket.bind(self.socketpath)
+
+ # Install handler
+ self.io_loop.add_handler(self.socket.fileno(),
+ self._handle_read,
+ self.io_loop.ERROR | self.io_loop.READ)
+
+ self.socket.sendto('{"msg":"attach"}', self.crawl_socketpath)
+
+ def _handle_read(self, fd, events):
+ if events & self.io_loop.READ:
+ data = self.socket.recv(128 * 1024, socket.MSG_DONTWAIT)
+
+ self._handle_data(data)
+
+ if events & self.io_loop.ERROR:
+ pass
+
+ def _handle_data(self, data):
+ if self.msg_buffer is not None:
+ data = self.msg_buffer + data
+
+ if data[-1] != "\n":
+ # All messages from crawl end with \n.
+ # If this one doesn't, it's fragmented.
+ self.msg_buffer = data
+
+ else:
+ self.msg_buffer = None
+
+ if self.message_callback:
+ self.message_callback(data)
+
+ def send_message(self, data):
+ self.socket.sendto(data, self.crawl_socketpath)
+
+ def close(self):
+ if self.socket:
+ self.io_loop.remove_handler(self.socket.fileno())
+ self.socket.close()
+ os.remove(self.socketpath)
+ self.socket = None