9 def __init__(self, game, save_file='savefile'):
10 from plomrogue.parser import Parser
11 self.parser = Parser(game)
13 self.save_file = save_file
17 """Handle commands coming through queue q, run game, send results back.
19 As basic flood protection, only accepts ten commands per connection per
24 potential_flooders = {}
27 connection_id, command = q.get(timeout=0.001)
28 now = int(time.time() * 10)
29 if connection_id in potential_flooders and \
30 potential_flooders[connection_id][0] == now:
31 if potential_flooders[connection_id][1] > 10:
33 potential_flooders[connection_id][1] += 1
35 potential_flooders[connection_id] = [now, 1]
36 self.handle_input(command, connection_id)
41 """Start game loop, set up self.queue to communicate with it.
43 The game loop works sequentially through game commands received
44 via self.queue from connected servers' clients."""
46 self.queue = queue.Queue()
48 # optionally use this for main thread profiling:
50 # class ProfiledThread(threading.Thread):
52 # profiler = cProfile.Profile()
53 # profiler.runcall(threading.Thread.run, self)
54 # print('profiled thread finished')
55 # profiler.dump_stats('profile')
56 # c = ProfiledThread(target=self.loop, args=(self.queue,))
57 c = threading.Thread(target=self.loop, args=(self.queue,))
61 def start_server(self, port, server_class, certfile=None, keyfile=None):
62 """Start server of server_class in talk with game loop.
64 The server communicates with the game loop via self.queue.
66 if 'certfile' in list(inspect.signature(server_class.__init__).parameters):
67 server = server_class(self.queue, port, certfile=certfile, keyfile=keyfile)
69 server = server_class(self.queue, port)
70 self.servers += [server]
71 c = threading.Thread(target=server.serve_forever)
74 def handle_input(self, input_, connection_id=None, god_mode=False):
75 """Process input_ to command grammar, call command handler if found.
77 Command handlers that have no connectin_i argument in their
78 signature will only be called if god_mode is set.
81 from plomrogue.errors import GameError, ArgError, PlayError
82 from plomrogue.misc import quote
84 def answer(connection_id, msg):
86 self.send(msg, connection_id)
91 command, args = self.parser.parse(input_)
93 answer(connection_id, 'UNHANDLED_INPUT')
95 if 'connection_id' in list(inspect.signature(command).parameters):
96 command(*args, connection_id=connection_id)
99 # if store and not hasattr(command, 'dont_save'):
100 # with open(self.game_file_name, 'a') as f:
101 # f.write(input_ + '\n')
102 except ArgError as e:
103 answer(connection_id, 'ARGUMENT_ERROR ' + quote(str(e)))
104 except PlayError as e:
105 answer(connection_id, 'PLAY_ERROR ' + quote(str(e)))
106 except GameError as e:
107 answer(connection_id, 'GAME_ERROR ' + quote(str(e)))
109 def send(self, msg, connection_id=None):
110 """Send message msg to servers' client(s).
112 If a specific client is identified by connection_id, only
113 sends msg to that one. Else, sends it to all client sessions.
117 for server in self.servers:
118 if connection_id in server.clients:
119 client = server.clients[connection_id]
122 for c_id in self.game.sessions:
123 for server in self.servers:
124 if c_id in server.clients:
125 client = server.clients[c_id]