home · contact · privacy
Fix faulty main loop, comment out inactive cause buggy) flood protection.
[plomrogue2] / plomrogue / io.py
1 import queue
2 import threading
3 import inspect
4
5
6
7 class GameIO():
8
9     def __init__(self, game, save_file='savefile'):
10         from plomrogue.parser import Parser
11         self.parser = Parser(game)
12         self.game = game
13         self.save_file = save_file
14         self.servers = []
15
16     def loop(self, q):
17         """Handle commands coming through queue q, run game, send results back.
18
19         As basic flood protection, Only accepts one command per connection per
20         1/100 of a second (currently commented out).
21
22         """
23         import time
24         potential_flooders = {}
25         while True:
26             try:
27                 connection_id, command = q.get(timeout=0.001)
28
29                 # FIXME: this would catch the init command flood
30                 #if connection_id in potential_flooders:
31                 #    if int(time.time() * 100) == potential_flooders[connection_id]:
32                 #        continue
33                 #potential_flooders[connection_id] = int(time.time() * 100)
34
35                 self.handle_input(command, connection_id)
36             except queue.Empty:
37                 self.game.run_tick()
38
39     def start_loop(self):
40         """Start game loop, set up self.queue to communicate with it.
41
42         The game loop works sequentially through game commands received
43         via self.queue from connected servers' clients."""
44
45         self.queue = queue.Queue()
46         c = threading.Thread(target=self.loop, args=(self.queue,))
47         c.start()
48
49     def start_server(self, port, server_class, certfile=None, keyfile=None):
50         """Start server of server_class in talk with game loop.
51
52         The server communicates with the game loop via self.queue.
53         """
54         if 'certfile' in list(inspect.signature(server_class.__init__).parameters):
55             server = server_class(self.queue, port, certfile=certfile, keyfile=keyfile)
56         else:
57             server = server_class(self.queue, port)
58         self.servers += [server]
59         c = threading.Thread(target=server.serve_forever)
60         c.start()
61
62     def handle_input(self, input_, connection_id=None, god_mode=False):
63         """Process input_ to command grammar, call command handler if found.
64
65         Command handlers that have no connectin_i argument in their
66         signature will only be called if god_mode is set.
67
68         """
69         from plomrogue.errors import GameError, ArgError, PlayError
70         from plomrogue.misc import quote
71
72         def answer(connection_id, msg):
73             if connection_id:
74                 self.send(msg, connection_id)
75             else:
76                 print(msg)
77
78         try:
79             command, args = self.parser.parse(input_)
80             if command is None:
81                 answer(connection_id, 'UNHANDLED_INPUT')
82             else:
83                 if 'connection_id' in list(inspect.signature(command).parameters):
84                     command(*args, connection_id=connection_id)
85                 elif god_mode:
86                     command(*args)
87                     # if store and not hasattr(command, 'dont_save'):
88                     #     with open(self.game_file_name, 'a') as f:
89                     #         f.write(input_ + '\n')
90         except ArgError as e:
91             answer(connection_id, 'ARGUMENT_ERROR ' + quote(str(e)))
92         except PlayError as e:
93             answer(connection_id, 'PLAY_ERROR ' + quote(str(e)))
94         except GameError as e:
95             answer(connection_id, 'GAME_ERROR ' + quote(str(e)))
96
97     def send(self, msg, connection_id=None):
98         """Send message msg to servers' client(s).
99
100         If a specific client is identified by connection_id, only
101         sends msg to that one. Else, sends it to all client sessions.
102
103         """
104         if connection_id:
105             for server in self.servers:
106                 if connection_id in server.clients:
107                     client = server.clients[connection_id]
108                     client.put(msg)
109         else:
110             for c_id in self.game.sessions:
111                 for server in self.servers:
112                     if c_id in server.clients:
113                         client = server.clients[c_id]
114                         client.put(msg)
115                         break