diff options
author | Florian Diebold <flodiebold@gmail.com> | 2011-10-06 23:05:31 +0200 |
---|---|---|
committer | Florian Diebold <flodiebold@gmail.com> | 2011-10-15 23:18:00 +0200 |
commit | 50c127e8ad514a2817be501f8bd2acbeb1bce4e4 (patch) | |
tree | 073e5b5af7dbe680202740922a66fcdd537c1b08 /crawl-ref/source/webserver/connection.py | |
parent | cde49cf01ee4d1acf2a4c36050fbeb8c228d1606 (diff) | |
download | crawl-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.py | 81 |
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 |