home · contact · privacy
Re-do IO with websocket capabilities.
[plomrogue2-experiments] / new2 / plomrogue / io.py
1 import queue
2
3
4
5 class GameIO():
6
7     def __init__(self, game):
8         from plomrogue.parser import Parser
9         self.clients = {}
10         self.parser = Parser(game)
11         self.game = game
12
13     def loop(self, q):
14         """Handle commands coming through queue q, run game, send results back."""
15         while True:
16             try:
17                 command, connection_id = q.get(timeout=1)
18                 self.handle_input(connection_id, command)
19             except queue.Empty:
20                 self.game.run_tick()
21
22     def run_loop_with_server(self, port, server_class):
23         """Run connection of server talking to clients and game IO loop.
24
25         We have a server of self.server_class and we have the game IO loop,
26         a thread running self.loop. Both communicate with each other via a
27         queue.Queue. While the server may spawn parallel threads to many
28         clients, the IO loop works sequentially through game commands
29         received from the server's client connections.  A processed command
30         may trigger messages to the commanding client or to all clients,
31         delivered from the IO loop to the server via the queue.
32
33         """
34         import threading
35         q = queue.Queue()
36         c = threading.Thread(target=self.loop, daemon=True, args=(q,))
37         c.start()
38         self.server = server_class(q, port)
39         try:
40             self.server.serve_forever()
41         except KeyboardInterrupt:
42             pass
43         finally:
44             print('Killing server')
45             self.server.server_close()
46
47     def handle_input(self, input_, connection_id=None):
48         """Process input_ to command grammar, call command handler if found."""
49         from inspect import signature
50         from plomrogue.errors import GameError, ArgError
51         from plomrogue.misc import quote
52
53         def answer(connection_id, msg):
54             if connection_id:
55                 self.send(msg, connection_id)
56             else:
57                 print(msg)
58
59         try:
60             command, args = self.parser.parse(input_)
61             if command is None:
62                 answer(connection_id, 'UNHANDLED_INPUT')
63             else:
64                 if 'connection_id' in list(signature(command).parameters):
65                     command(*args, connection_id=connection_id)
66                 else:
67                     command(*args)
68                     #if store and not hasattr(command, 'dont_save'):
69                     #    with open(self.game_file_name, 'a') as f:
70                     #        f.write(input_ + '\n')
71         except ArgError as e:
72             answer(connection_id, 'ARGUMENT_ERROR ' + quote(str(e)))
73         except GameError as e:
74             answer(connection_id, 'GAME_ERROR ' + quote(str(e)))
75
76     def send(self, msg, connection_id=None):
77         """Send message msg to server's client(s).
78
79         If a specific client is identified by connection_id, only
80         sends msg to that one. Else, sends it to all clients.
81
82         """
83         if connection_id:
84             self.server.clients[connection_id].put(msg)
85         else:
86             for c in self.server.clients.values():
87                 c.put(msg)