def get_input(): # should be [tag, <Data for Inputlet.parse>] # tag likes 'I?:ChooseOption:2345' # try for data in buffer for p in players: try: _, rst = p.client.gexpect(tag + str(synctags[p]), blocking=False) if rst is not GamedataMixin.NODATA: return p, rst except EndpointDied: return p, None # wait for new data ev = waitany([p.client.gdevent for p in players]) assert ev p = evmap[ev] try: _, rst = p.client.gexpect(tag + str(synctags[p]), blocking=False) except EndpointDied: return p, None return p, rst
def user_input(players, inputlet, timeout=25, type='single', trans=None): ''' Type can be 'single', 'all' or 'any' ''' assert type in ('single', 'all', 'any') assert not type == 'single' or len(players) == 1 timeout = max(0, timeout) g = Game.getgame() inputlet.timeout = timeout players = players[:] if not trans: with InputTransaction(inputlet.tag(), players) as trans: return user_input(players, inputlet, timeout, type, trans) t = {'single': '', 'all': '&', 'any': '|'}[type] tag = 'I{0}:{1}:'.format(t, inputlet.tag()) ilets = {p: copy(inputlet) for p in players} for p in players: ilets[p].actor = p results = {p: None for p in players} synctags = {p: g.get_synctag() for p in players} orig_players = players[:] input_group = GreenletGroup() g.gr_groups.add(input_group) _input_group = set() till = time.time() + timeout + 5 try: inputany_player = None def get_input_waiter(p, t): try: # should be [tag, <Data for Inputlet.parse>] # tag likes 'I?:ChooseOption:2345' tag, rst = p.client.gexpect(t) return rst except EndpointDied: return None for p in players: t = tag + str(synctags[p]) w = input_group.spawn(get_input_waiter, p, t) _input_group.add(w) w.player = p w.gr_name = 'get_input_waiter: p=%r, tag=%s' % (p, t) for p in players: g.emit_event('user_input_start', (trans, ilets[p])) while players: # NOTICE: This is a must. # TLE would be raised at other part (notably my.post_process) in the original solution # (wrapping large parts of code in 'with TimeLimitExceeded(): ...') with TimeLimitExceeded(max(till - time.time(), 0)): w = waitany(_input_group) _input_group.discard(w) try: rst = w.get() p, data = w.player, rst except: p, data = w.player, None g.players.client.gwrite('R{}{}'.format(tag, synctags[p]), data) my = ilets[p] try: rst = my.parse(data) except: log.error('user_input: exception in .process()', exc_info=1) # ----- FOR DEBUG ----- if g.IS_DEBUG: raise # ----- END FOR DEBUG ----- rst = None rst = my.post_process(p, rst) g.emit_event('user_input_finish', (trans, my, rst)) players.remove(p) results[p] = rst if type == 'any' and rst is not None: inputany_player = p break except TimeLimitExceeded: pass finally: input_group.kill() # timed-out players for p in players: my = ilets[p] rst = my.parse(None) rst = my.post_process(p, rst) results[p] = rst g.emit_event('user_input_finish', (trans, my, rst)) g.players.client.gwrite('R{}{}'.format(tag, synctags[p]), None) if type == 'single': return results[orig_players[0]] elif type == 'any': if not inputany_player: return None, None return inputany_player, results[inputany_player] elif type == 'all': return OrderedDict([(p, results[p]) for p in orig_players]) assert False, 'WTF?!'