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
16 def train_parser(self):
18 self.parser.string_options = {
19 'map_geometry': {'Hex', 'Square'},
21 string.digits + string.ascii_letters + string.punctuation
23 'direction': self.game.map_geometry.directions,
24 'direction+here': ['HERE'] + self.game.map_geometry.directions,
25 'thing_type': self.game.thing_types.keys()
29 """Handle commands coming through queue q, run game, send results back.
31 As basic flood protection, only accepts ten commands per connection per
36 potential_flooders = {}
39 connection_id, command = q.get(timeout=0.001)
40 now = int(time.time() * 10)
41 if connection_id in potential_flooders and \
42 potential_flooders[connection_id][0] == now:
43 if potential_flooders[connection_id][1] > 10:
45 potential_flooders[connection_id][1] += 1
47 potential_flooders[connection_id] = [now, 1]
48 self.handle_input(command, connection_id)
53 """Start game loop, set up self.queue to communicate with it.
55 The game loop works sequentially through game commands received
56 via self.queue from connected servers' clients."""
58 self.queue = queue.Queue()
60 # optionally use this for main thread profiling:
62 # class ProfiledThread(threading.Thread):
64 # profiler = cProfile.Profile()
65 # profiler.runcall(threading.Thread.run, self)
66 # print('profiled thread finished')
67 # profiler.dump_stats('profile')
68 # c = ProfiledThread(target=self.loop, args=(self.queue,))
69 c = threading.Thread(target=self.loop, args=(self.queue,))
73 def start_server(self, port, server_class, certfile=None, keyfile=None):
74 """Start server of server_class in talk with game loop.
76 The server communicates with the game loop via self.queue.
78 if 'certfile' in list(inspect.signature(server_class.__init__).parameters):
79 server = server_class(self.queue, port, certfile=certfile, keyfile=keyfile)
81 server = server_class(self.queue, port)
82 self.servers += [server]
83 c = threading.Thread(target=server.serve_forever)
86 def handle_input(self, input_, connection_id=None, god_mode=False):
87 """Process input_ to command grammar, call command handler if found.
89 Command handlers that have no connectin_i argument in their
90 signature will only be called if god_mode is set.
93 from plomrogue.errors import GameError, ArgError, PlayError
94 from plomrogue.misc import quote
96 def answer(connection_id, msg):
98 self.send(msg, connection_id)
103 command, args = self.parser.parse(input_)
105 answer(connection_id, 'UNHANDLED_INPUT')
107 if 'connection_id' in list(inspect.signature(command).parameters):
108 command(*args, connection_id=connection_id)
111 # if store and not hasattr(command, 'dont_save'):
112 # with open(self.game_file_name, 'a') as f:
113 # f.write(input_ + '\n')
114 except ArgError as e:
115 answer(connection_id, 'ARGUMENT_ERROR ' + quote(str(e)))
116 except PlayError as e:
117 answer(connection_id, 'PLAY_ERROR ' + quote(str(e)))
118 except GameError as e:
119 answer(connection_id, 'GAME_ERROR ' + quote(str(e)))
121 def send(self, msg, connection_id=None):
122 """Send message msg to servers' client(s).
124 If a specific client is identified by connection_id, only
125 sends msg to that one. Else, sends it to all client sessions.
129 for server in self.servers:
130 if connection_id in server.clients:
131 client = server.clients[connection_id]
134 for c_id in self.game.sessions:
135 for server in self.servers:
136 if c_id in server.clients:
137 client = server.clients[c_id]