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