def worker(): if msg.startswith('!!') and (options.freeplay or user.account.userid in (2, )): # admin commands cmd = msg[2:] if cmd == 'stacktrace': admin_stacktrace(user.current_game) return elif cmd == 'clearzombies': admin_clearzombies() return elif cmd == 'ping': admin_ping(user) return packed = (user.account.username, msg) if user.state == 'hang': # hall chat for u in users.values(): if u.state == 'hang': u.write(['chat_msg', packed]) elif user.state in ('inroomwait', 'ready', 'ingame', 'observing'): # room chat ul = user.current_game.players.client obl = BatchList() map(obl.__iadd__, ul.observers) _type = 'ob_msg' if user.state == 'observing' else 'chat_msg' ul.write([_type, packed]) # should be here? obl.write([_type, packed])
def worker(): if msg.startswith('!!') and (options.freeplay or acc.userid in self.admins): self.handle_admin_cmd(user, msg[2:]) return manager = GameManager.get_by_user(user) if manager.muted: user.write(['system_msg', [None, u'当前房间被管理员禁言']]) return packed = (acc.username, msg) if user.state == 'hang': # lobby chat log.info(u'(Lobby): %s' % msg) for u in self.users.values(): if u.state == 'hang': u.write(['chat_msg', packed]) elif user.state in ('inroomwait', 'ready', 'ingame', 'observing'): # room chat log.info(u'(%s): %s' % (user.current_game.gameid, msg)) ul = manager.users obl = BatchList() map(obl.__iadd__, ul.observers) _type = 'ob_msg' if user.state == 'observing' else 'chat_msg' ul.write([_type, packed]) # should be here? obl.write([_type, packed])
def __init__(self, gid, gamecls, name, invite_only): g = gamecls() self.game = g self.users = BatchList([ClientPlaceHolder] * g.n_persons) self.game_started = False self.game_name = name self.banlist = defaultdict(set) self.ob_banlist = defaultdict(set) self.gameid = gid self.gamecls = gamecls self.game_items = defaultdict(set) # userid -> {'item:meh', ...} self.game_params = {k: v[0] for k, v in gamecls.params_def.items()} self.is_match = False self.match_users = [] self.invite_only = invite_only self.invite_list = set() self.muted = False g.gameid = gid g._manager = self g.rndseed = random.getrandbits(63) g.random = random.Random(g.rndseed) g.players = BatchList() g.gr_groups = WeakSet()
def worker(): if msg.startswith('!!') and (options.freeplay or user.account.userid in (2,)): # admin commands cmd = msg[2:] if cmd == 'stacktrace': admin_stacktrace(user.current_game) return elif cmd == 'clearzombies': admin_clearzombies() return elif cmd == 'ping': admin_ping(user) return packed = (user.account.username, msg) if user.state == 'hang': # hall chat for u in users.values(): if u.state == 'hang': u.write(['chat_msg', packed]) elif user.state in ('inroomwait', 'ready', 'ingame', 'observing'): # room chat ul = user.current_game.players.client obl = BatchList() map(obl.__iadd__, ul.observers) _type = 'ob_msg' if user.state == 'observing' else 'chat_msg' ul.write([_type, packed]) # should be here? obl.write([_type, packed])
def __init__(self, sock, addr): Endpoint.__init__(self, sock, addr) Greenlet.__init__(self) self.observers = BatchList() self.gamedata = Gamedata() self.cmd_listeners = defaultdict(WeakSet) self.current_game = None
def __init__(self, sock, addr): Endpoint.__init__(self, sock, addr) Greenlet.__init__(self) self.observers = BatchList() self.init_gamedata_mixin() self.gdhistory = [] self.usergdhistory = []
def testChooseOptionInputlet(self): from game import autoenv from game.autoenv import user_input from client.core import TheChosenOne, PeerPlayer from thb.thb3v3 import THBattle from thb.inputlets import ChooseOptionInputlet from utils import BatchList autoenv.init('Server') g = THBattle() g.IS_DEBUG = True pl = [create_mock_player([]) for i in xrange(6)] p = pl[0] g.me = p p.client.gdlist.extend([ ['I:ChooseOption:1', True], ['I&:ChooseOption:2', False], ['I|:ChooseOption:3', True], ]) p.client.gdevent.set() g.players = BatchList(pl) hook_game(g) g.gr_groups = WeakSet() ilet = ChooseOptionInputlet(self, (False, True)) eq_(user_input([p], ilet), True) eq_(user_input([p], ilet, type='all'), {p: False}) eq_(user_input([p], ilet, type='any'), (p, True)) for p in pl: eq_(p.client.gdhistory, [ ['RI:ChooseOption:1', True], ['RI&:ChooseOption:2', False], ['RI|:ChooseOption:3', True], ]) autoenv.init('Client') g = THBattle() pl = [PeerPlayer() for i in xrange(6)] svr = MockConnection([ ['RI:ChooseOption:1', True], ['RI&:ChooseOption:2', False], ['RI|:ChooseOption:3', True], ]) p = TheChosenOne(svr) pl[0] = p g.me = p svr.gdevent.set() g.players = BatchList(pl) hook_game(g) assert autoenv.Game.getgame() is g ilet = ChooseOptionInputlet(self, (False, True)) eq_(user_input([p], ilet), True) eq_(user_input([p], ilet, type='all'), {p: False}) eq_(user_input([p], ilet, type='any'), (p, True))
def worker(): packed = (user.account.username, msg) if user.state == 'hang': # hall chat for u in users.values(): if u.state == 'hang': u.write(['chat_msg', packed]) elif user.state in ('inroomwait', 'ready', 'ingame', 'observing'): # room chat ul = user.current_game.players.client obl = BatchList() map(obl.__iadd__, ul.observers) _type = 'ob_msg' if user.state == 'observing' else 'chat_msg' ul.write([_type, packed]) # should be here? obl.write([_type, packed])
def effect_string(act): # for LaunchCard.ui_meta.effect_string source = act.source card = act.card tl = BatchList(act.target_list) cl = BatchList(card.associated_cards) s = u'|G【%s】|r发动了|G变化|r技能,将|G%s|r当作|G%s|r对|G【%s】|r使用。' % ( source.ui_meta.name, u'|r、|G'.join(cl.ui_meta.name), card.treat_as.ui_meta.name, u'】|r、|G【'.join(tl.ui_meta.name), ) return s
def __init__(self, sock, addr): Endpoint.__init__(self, sock, addr) Greenlet.__init__(self) self.gdqueue = deque(maxlen=100) self.gdevent = Event() self.observers = BatchList() import socket try: sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True) sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 30) sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 6) sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, 3) except: pass
def apply_action(self): src = self.source tgt = self.target g = Game.getgame() pl = BatchList([tgt, src]) pindian_card = {src: None, tgt: None} with InputTransaction('Pindian', pl) as trans: for p in pl: cards = user_choose_cards(self, p, ('cards', 'showncards'), trans=trans) if cards: card = cards[0] else: card = random_choose_card([p.cards, p.showncards]) pindian_card[p] = card detach_cards([card]) g.emit_event('pindian_card_chosen', (p, card)) g.players.reveal([pindian_card[src], pindian_card[tgt]]) g.emit_event('pindian_card_revealed', self) # for ui. migrate_cards([pindian_card[src], pindian_card[tgt]], g.deck.droppedcards, unwrap=True, is_bh=True) return pindian_card[src].number > pindian_card[tgt].number
def build_initial_players(self): from server.core.game_server import Player, NPCPlayer pl = BatchList([Player(u) for u in self.users]) pl[:0] = [NPCPlayer(NPCClient(i.name), i.input_handler) for i in self.game.npc_players] return pl
def observe_started(self, data): Executive.server.gclear() if self.last_game: self.last_game.kill(ForcedKill) self.last_game.get() self.last_game = None params, tgtid, pldata = data from client.core import PeerPlayer, TheLittleBrother pl = [PeerPlayer.parse(i) for i in pldata] pid = [i.account.userid for i in pl] i = pid.index(tgtid) self.last_game_info = params, i, pldata g = self.game g.players = BatchList(pl) me = g.players[i] me.__class__ = TheLittleBrother me.server = Executive.server g.me = me g.game_params = params # g.start() Starts by UI log.info('=======OBSERVE STARTED=======') log.info(g) @g.link_exception def crash(*a): self.event_cb('game_crashed', g) @g.link_value def finish(*a): v = g.get() if not isinstance(v, ForcedKill): self.event_cb('client_game_finished', g) self.event_cb('game_started', (g, params, pldata, g.players[:]))
def game_started(self, pldata): Executive.server.gclear() if self.last_game: self.last_game.kill(ForcedKill) self.last_game.get() self.last_game = None from client.core import PeerPlayer, TheChosenOne pl = [PeerPlayer.parse(i) for i in pldata] pid = [i.account.userid for i in pl] me = TheChosenOne(Executive.server) me.account = self.account i = pid.index(me.account.userid) pl[i] = me g = self.game g.me = me g.players = BatchList(pl) #g.start() log.info('=======GAME STARTED: %d=======' % g.gameid) log.info(g) @g.link_exception def crash(*a): self.event_cb('game_crashed', g) @g.link_value def finish(*a): v = g.get() if not isinstance(v, ForcedKill): self.event_cb('client_game_finished', g) self.event_cb('game_started', g)
def __init__(self): Greenlet.__init__(self) game.base.Game.__init__(self) self.players = BatchList() self.game_params = {} self.game_items = {} self._my_user_input = (None, None)
class ClientPlaceHolder(object): state = 'left' account = None observers = BatchList() raw_write = write = lambda *a: False def __data__(self): return (None, None, 'left')
def user_input_all(self, tag, process, attachment=None, timeout=15): g = Game.getgame() st = g.get_synctag() workers = BatchList() try: def worker(p, i): retry = 0 while True: input = p.user_input( tag, attachment=attachment, timeout=timeout, g=g, st=100000 + st*1000 + i*10 + retry, ) try: input = process(p, input) except ValueError: retry += 1 if retry >= 3: input = None break continue break for i, p in enumerate(self): w = gevent.spawn(worker, p, i) w.game = g workers.append(w) workers.join() finally: workers.kill()
def effect_string(act): # for LaunchCard.ui_meta.effect_string source = act.source tl = BatchList(act.target_list) return u'全人类的绯想天,当然不能只打一个!于是|G【%s】|r选了|G【%s】|r一共%d个目标!' % ( source.ui_meta.name, u'】|r、|G【'.join(tl.ui_meta.name), len(tl), )
def effect_string_before(act): s, tl = act.source, BatchList(act.target_list) c = act.card from gamepack.thb.cards import Skill if isinstance(c, Skill): return c.ui_meta.effect_string(act) elif c: return u'|G【%s】|r对|G【%s】|r使用了|G%s|r。' % ( s.ui_meta.char_name, u'】|r、|G【'.join( tl.ui_meta.char_name), act.card.ui_meta.name)
def __init__(self, target, dmgact): self.source = self.target = target self.dmgact = dmgact g = Game.getgame() if target.dead: log.error('TryRevive buggy condition, __init__') return self.asklist = BatchList( p for p in g.players if not p.dead ).rotate_to(target)
def end_game(self): if self.is_match and not self.game.suicide: gevent.spawn(lambda: interconnect.publish( 'speaker', [u'文文', u'“%s”结束了!获胜玩家:%s' % ( self.game_name, u','.join(BatchList(self.game.winners).account.username) )] )) for u in self.users: u.write(['end_game', None]) u.observers and u.observers.write(['end_game', None]) u.state = 'hang'
def effect_string_before(act): s, tl = act.source, BatchList(act.target_list) c = act.card if not c: return meta = getattr(c, 'ui_meta', None) effect_string = getattr(meta, 'effect_string', None) if effect_string: return effect_string(act) return u'|G【%s】|r对|G【%s】|r使用了|G%s|r。' % ( s.ui_meta.name, u'】|r、|G【'.join( tl.ui_meta.name), act.card.ui_meta.name)
def effect_string(act): # for LaunchCard.ui_meta.effect_string source = act.source card = act.card.associated_cards[0] tl = BatchList(act.target_list) if card.is_card(cards.AttackCard): s = u'弹幕掺了金坷垃,攻击范围一千八!' else: s = u'符卡掺了金坷垃,一张能顶两张用!' return u'|G【%s】|r:“%s|G【%s】|r接招吧!”' % ( source.ui_meta.name, s, u'】|r、|G【'.join(tl.ui_meta.name), )
def handle(self, evt_type, act): if evt_type == 'action_before' and isinstance(act, SpellCardAction): if act.cancelled: return act # some other thing have done the job if act.non_responsive: return act g = Game.getgame() has_reject = False while g.SERVER_SIDE: from ..characters.baseclasses import Character from ..characters.reimu import SpiritualAttack for p in g.players: if isinstance(p, Character) and p.has_skill(SpiritualAttack): has_reject = True break if has_reject: break from .definition import RejectCard for c in flatten([[p.cards, p.showncards] for p in g.players]): if isinstance(c, RejectCard): has_reject = True break break has_reject = sync_primitive(has_reject, g.players) if not has_reject: return act self.target_act = act pl = BatchList(p for p in g.players if not p.dead) with InputTransaction('AskForRejectAction', pl) as trans: p, rst = ask_for_action(self, pl, ('cards', 'showncards'), [], trans) if not p: return act cards, _ = rst assert cards and self.cond(cards) g.process_action( LaunchCard(p, [act.target], cards[0], Reject(p, act))) return act
def user_input_all(self, tag, process, attachment=None, timeout=15): g = Game.getgame() g.emit_event('user_input_all_begin', (self, tag, attachment)) st = g.get_synctag() workers = BatchList() try: def worker(p, i): retry = 0 while True: input = p.user_input( tag, attachment=attachment, timeout=timeout, g=g, st=100000 + st * 1000 + i * 10 + retry, ) try: input = process(p, input) except ValueError: retry += 1 if retry >= 3: input = None break continue g.emit_event('user_input_all_data', (tag, p, input)) break for i, p in enumerate(self): w = gevent.spawn(worker, p, i) w.game = g workers.append(w) workers.join() finally: workers.kill() g.emit_event('user_input_all_end', tag)
def makeGame(self): from game import autoenv from thb.thb3v3 import THBattle from thb.cards import Deck, CardList from thb.characters.eirin import FirstAid, Medic from utils import BatchList autoenv.init('Server') g = THBattle() g.IS_DEBUG = True g.random = random hook_game(g) deck = Deck() g.deck = deck g.action_stack = [autoenv.Action(None, None)] g.gr_groups = WeakSet() pl = [create_mock_player([]) for i in xrange(6)] for p in pl: p.skills = [FirstAid, Medic] p.cards = CardList(p, 'cards') # Cards in hand p.showncards = CardList( p, 'showncard' ) # Cards which are shown to the others, treated as 'Cards in hand' p.equips = CardList(p, 'equips') # Equipments p.fatetell = CardList(p, 'fatetell') # Cards in the Fatetell Zone p.faiths = CardList(p, 'faiths') # Cards in the Fatetell Zone p.special = CardList(p, 'special') # used on special purpose p.showncardlists = [p.showncards, p.fatetell] p.tags = defaultdict(int) p.dead = False p = pl[0] p.client.gdevent.set() g.players = BatchList(pl) return g, p
def create_game(user, gametype, gamename): from gamepack import gamemodes if not gametype in gamemodes: user.write(['gamehall_error', 'gametype_not_exist']) return g = gamemodes[gametype]() g.game_started = False g.game_name = gamename g.players = BatchList([PlayerPlaceHolder] * g.n_persons) g.players_original = None g.rndseed = random.randint(1, 10**20) g.random = random.Random(g.rndseed) g.banlist = defaultdict(set) g.gr_groups = WeakSet() gid = new_gameid() g.gameid = gid games[gid] = g log.info("create game") evt_datachange.set() return g
def handle(self, evt_type, act): if evt_type == 'action_before' and isinstance(act, SpellCardAction): if act.cancelled: return act # some other thing have done the job if isinstance(act, NonResponsiveInstantSpellCardAction): return act g = Game.getgame() has_reject = False while g.SERVER_SIDE: from ..characters.reimu import Reimu for p in g.players: if isinstance(p, Reimu): has_reject = True break if has_reject: break from .definition import RejectCard for c in flatten([[p.cards, p.showncards] for p in g.players]): if isinstance(c, RejectCard): has_reject = True break break has_reject = sync_primitive(has_reject, g.players) if not has_reject: return act self.target_act = act # for ui pl = BatchList(p for p in g.players if not p.dead) p, rst = ask_for_action(self, pl, ['cards', 'showncards'], []) if not p: return act cards, _ = rst assert cards and self.cond(cards) g.process_action(LaunchReject(p, act, cards[0])) return act
def start_replay(self, rep, event_cb): assert self.state == 'initial' self.state = 'replay' from client.core import PeerPlayer, TheLittleBrother from thb import modes g = modes[rep.game_mode]() self.server = ReplayEndpoint(rep, g) pl = [PeerPlayer.parse(i) for i in rep.users] g.players = BatchList(pl) me = g.players[rep.me_index] me.__class__ = TheLittleBrother me.server = self.server g.me = me g.game_params = rep.game_params g.game_items = rep.game_items log.info('=======REPLAY STARTED=======') # g.start() Starts by UI @g.link_exception def crash(*a): self.state = 'initial' event_cb('game_crashed', g) @g.link_value def finish(*a): self.state = 'initial' v = g.get() if not isinstance(v, ForcedKill): event_cb('client_game_finished', g) event_cb('end_game', g) return g
def start_game(g): log.info("game started") g.game_started = True g.players_original = BatchList(g.players) g.usergdhistory = ugh = [] g.gdhistory = [list() for p in g.players] for i, (u, l) in enumerate(zip(g.players.client, g.gdhistory)): u.player_index = i u.usergdhistory = ugh u.gdhistory = l g.start_time = time.time() for u in g.players.client: u.write(["game_started", g.players]) u.gclear() if u.observers: u.observers.gclear() u.observers.write( ['observe_started', [u.account.userid, g.players]]) u.state = 'ingame' evt_datachange.set()
class Client(Endpoint, Greenlet): def __init__(self, sock, addr): Endpoint.__init__(self, sock, addr) Greenlet.__init__(self) self.gdqueue = deque(maxlen=100) self.gdevent = Event() self.observers = BatchList() import socket try: sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True) sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 30) sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 6) sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, 3) except: pass def _run(self): cmds = {} self.account = None def handler(*state): def register(f): for s in state: d = cmds.setdefault(s, {}) d[f.__name__] = f return register # --------- Handlers --------- @handler('connected') def register(self,data): self.write(['not_impl', None]) @handler('connected') def auth(self, cred): login, password = cred acc = Account.authenticate(login, password) if acc: self.account = acc if not acc.available(): self.write(['auth_result', 'not_available']) self.close() else: self.write(['auth_result', 'success']) self.account = acc hall.new_user(self) else: self.write(['auth_result', 'invalid_credential']) @handler('hang') def create_game(self, arg): _type, name = arg g = hall.create_game(self, _type, name) hall.join_game(self, g.gameid) @handler('hang') def join_game(self, gameid): hall.join_game(self, gameid) @handler('hang') def get_hallinfo(self, _): hall.send_hallinfo(self) @handler('hang') def quick_start_game(self, _): hall.quick_start_game(self) @handler('hang') def observe_user(self, uid): hall.observe_user(self, uid) @handler('inroomwait', 'ready', 'ingame') def observe_grant(self, rst): hall.observe_grant(self, rst) @handler('hang') def query_gameinfo(self, gid): hall.query_gameinfo(self, gid) @handler('inroomwait') def get_ready(self, _): hall.get_ready(self) @handler('inroomwait', 'ready', 'ingame', 'observing') def exit_game(self, _): hall.exit_game(self) @handler('inroomwait', 'ready') def kick_user(self, uid): hall.kick_user(self, uid) @handler('inroomwait') def change_location(self, loc): hall.change_location(self, loc) @handler('ready') def cancel_ready(self, _): hall.cancel_ready(self) @handler('ingame') def gamedata(self, data): p = Packet(data) p.scan_count = 0 self.gdqueue.append(p) self.gdevent.set() @handler('__any__') def _disconnect(self, _): self.write(['bye', None]) self.close() @handler('hang', 'inroomwait', 'ready', 'ingame', 'observing') def chat(self, data): hall.chat(self, data) @handler('hang', 'inroomwait', 'ready', 'ingame', 'observing') def speaker(self, data): hall.speaker(self, data) # --------- End --------- # ----- Banner ----- from settings import VERSION self.write(['thbattle_greeting', VERSION]) # ------------------ self.state = 'connected' while True: try: cmd, data = self.read() f = cmds[self.state].get(cmd) if not f: f = cmds['__any__'].get(cmd) if (self.account or cmd == 'auth') and f: f(self, data) else: self.write(['invalid_command', [cmd, data]]) except EndpointDied as e: self.gdqueue.append(e) self.gdevent.set() break # client died, do clean ups if self.state not in ('connected', 'hang'): hall.exit_game(self, drops=True) if self.state != 'connected': hall.user_exit(self) def gexpect(self, tag): log.debug('GAME_EXPECT: %s', repr(tag)) l = self.gdqueue e = self.gdevent while True: for i in xrange(len(l)): d = l.popleft() if isinstance(d, EndpointDied): raise d elif d[0] == tag: log.debug('GAME_READ: %s', repr(d)) self.usergdhistory.append((self.player_index, d[0], d[1])) return d[1] else: d.scan_count += 1 if d.scan_count >= 15: log.debug('Dropped gamedata: %s' % d) else: log.debug('GAME_DATA_MISS: %s', repr(d)) log.debug('EXPECTS: %s' % tag) l.append(d) e.clear() e.wait() def gwrite(self, tag, data): log.debug('GAME_WRITE: %s', repr([tag, data])) encoded = self.encode(['gamedata', [tag, data]]) self.raw_write(encoded) self.gdhistory.append([tag, json.loads(self.encode(data))]) if self.observers: self.observers.raw_write(encoded) def replay(self, ob): for data in self.gdhistory: ob.raw_write(json.dumps(['gamedata', data]) + '\n') def __data__(self): return [self.account.userid, self.account.username, self.state] def gbreak(self): # is it a hack? # XXX: definitly, and why it's here?! can't remember # Explanation: # Well, when sb. exit game in input state, # the others must wait until his timeout exceeded. # called by gamehall.exit_game to break such condition. self.gdqueue.append(EndpointDied()) self.gdevent.set() def gclear(self): ''' Clear the gamedata queue, used when a game starts, to eliminate data from last game, which confuses the new game. ''' self.gdqueue.clear() def close(self): Endpoint.close(self) self.kill(EndpointDied) def __repr__(self): acc = self.account if not acc: return Endpoint.__repr__(self) return '%s:%s:%s' % ( self.__class__.__name__, self.address[0], acc.username.encode('utf-8'), )
def apply_action(self): g = Game.getgame() params = self.params from thb.cards import Deck g.picks = [] g.deck = Deck() g.ehclasses = list(action_eventhandlers) + g.game_ehs.values() H, M = Identity.TYPE.HAKUREI, Identity.TYPE.MORIYA if params['random_seat']: # reseat seed = get_seed_for(g.players) random.Random(seed).shuffle(g.players) g.emit_event('reseat', None) L = [[H, H, M, M, H, M], [H, M, H, M, H, M]] rnd = random.Random(get_seed_for(g.players)) L = rnd.choice(L) * 2 s = rnd.randrange(0, 6) idlist = L[s:s+6] del L, s, rnd else: idlist = [H, M, H, M, H, M] del H, M for p, identity in zip(g.players, idlist): p.identity = Identity() p.identity.type = identity g.process_action(RevealIdentity(p, g.players)) force_hakurei = BatchList() force_moriya = BatchList() force_hakurei.pool = [] force_moriya.pool = [] for p in g.players: if p.identity.type == Identity.TYPE.HAKUREI: force_hakurei.append(p) p.force = force_hakurei elif p.identity.type == Identity.TYPE.MORIYA: force_moriya.append(p) p.force = force_moriya g.forces = BatchList([force_hakurei, force_moriya]) roll_rst = roll(g, self.items) first = roll_rst[0] # choose girls --> from . import characters chars = characters.get_characters('common', 'faith') choices, _ = build_choices( g, self.items, candidates=chars, players=g.players, num=[4] * 6, akaris=[1] * 6, shared=False, ) rst = user_input(g.players, SortCharacterInputlet(g, choices, 2), timeout=30, type='all') for p in g.players: a, b = [choices[p][i] for i in rst[p][:2]] b.chosen = None p.force.reveal(b) g.switch_character(p, a) p.force.pool.append(b) for p in g.players: if p.player is first: first = p break pl = g.players first_index = pl.index(first) order = BatchList(range(len(pl))).rotate_to(first_index) for p in pl: g.process_action(RevealIdentity(p, pl)) g.emit_event('game_begin', g) for p in pl: g.process_action(DistributeCards(p, amount=4)) pl = g.players.rotate_to(first) rst = user_input(pl[1:], ChooseOptionInputlet(DeathHandler(), (False, True)), type='all') for p in pl[1:]: rst.get(p) and g.process_action(RedrawCards(p, p)) pl = g.players for i, idx in enumerate(cycle(order)): if i >= 6000: break p = pl[idx] if p.dead: continue g.emit_event('player_turn', p) try: g.process_action(PlayerTurn(p)) except InterruptActionFlow: pass return True
def testBuildChoices(self): import logging from thb.common import build_choices import random from thb.characters.baseclasses import get_characters from thb import characters from game import autoenv # def build_choices(g, items, candidates, players, num, akaris, shared): log = logging.getLogger('test') g = ObjectDict({ 'players': BatchList([ ObjectDict({ 'account': ObjectDict({'userid': i}), 'reveal': lambda o, i=i: log.info('Reveal to %s: %s', i, o), }) for i in xrange(8) ]), 'random': random.Random(12341234), 'SERVER_SIDE': True, 'CLIENT_SIDE': False, }) autoenv.Game.getgame = staticmethod(lambda: g) chars = get_characters('common', '3v3') assert chars choices, imperial = build_choices(g, {}, chars, g.players, 10, 3, True) eq_(len(choices.items()), len(g.players)) eq_(len(set([id(i) for i in choices.values()])), 1) eq_(set(choices.keys()), set(g.players)) eq_(imperial, []) choices, imperial = build_choices( g, {0: ['imperial-choice:SpAya', 'foo']}, chars, g.players, 10, 3, True) eq_(len(choices.items()), len(g.players)) eq_(len(set([id(i) for i in choices.values()])), 1) eq_(set(choices.keys()), set(g.players)) p, c = imperial[0] eq_((p, c.char_cls), (g.players[0], characters.sp_aya.SpAya)) assert c in choices[p] del c eq_(sum([c.akari for c in choices[p]]), 3) choices, imperial = build_choices( g, {0: ['imperial-choice:SpAya', 'foo']}, chars, g.players, [4] * 8, [1] * 8, False) eq_(len(choices.items()), len(g.players)) eq_(len(set([id(i) for i in choices.values()])), 8) eq_(set(choices.keys()), set(g.players)) eq_([len(i) for i in choices.values()], [4] * 8) eq_([len([j for j in i if j.akari]) for i in choices.values()], [1] * 8) p, c = imperial[0] eq_((p, c.char_cls), (g.players[0], characters.sp_aya.SpAya)) assert c in choices[p]
from client.core import game_client game_client.Executive = Executive # Hack GameMode = modes[mode] npcs = len(GameMode.npc_players) players = [PeerPlayer() for i in xrange(GameMode.n_persons + npcs)] players[loc + npcs].__class__ = TheLittleBrother players[loc + npcs].server = server for p in players: p.account = Account.authenticate('Proton', '123') g = GameMode() g.players = BatchList(players) g.game_params = params g.game_items = items g.me = players[loc + npcs] g.replay_file = options.replay_file @hook(g) def pause(*a): pass @hook(g) def get_synctag(ori): tag = ori() if options.print_synctag:
class GameManager(object): def __init__(self, gid, gamecls, name, invite_only): g = gamecls() self.game = g self.users = BatchList([ClientPlaceHolder] * g.n_persons) self.game_started = False self.game_name = name self.banlist = defaultdict(set) self.ob_banlist = defaultdict(set) self.gameid = gid self.gamecls = gamecls self.game_params = {k: v[0] for k, v in gamecls.params_def.items()} self.is_match = False self.match_users = [] self.invite_only = invite_only self.invite_list = set() g.gameid = gid g.manager = self g.rndseed = random.getrandbits(63) g.random = random.Random(g.rndseed) g.players = BatchList() g.gr_groups = WeakSet() def __data__(self): return { 'id': self.gameid, 'type': self.gamecls.__name__, 'started': self.game_started, 'name': self.game_name, 'nplayers': len(self.get_online_users()), 'params': self.game_params, } @classmethod def get_by_user(cls, user): ''' Get GameManager object for user. :rtype: GameManager ''' return user.current_game def send_gameinfo(self, user): g = self.game user.write(['gameinfo', [g.gameid, g.players]]) def set_match(self, match_users): self.is_match = True self.match_users = match_users gevent.spawn(lambda: [gevent.sleep(1), interconnect.publish( 'speaker', [u'文文', u'“%s”房间已经建立,请相关玩家就位!' % self.game_name] )]) @throttle(0.1) def notify_playerchange(self): @gevent.spawn def notify(): from server.core.game_server import Player pl = self.game.players if self.game_started else map(Player, self.users) s = Client.encode(['player_change', pl]) for cl in self.users: cl.raw_write(s) cl.observers and cl.observers.raw_write(s) def next_free_slot(self): try: return self.users.index(ClientPlaceHolder) except ValueError: return None def archive(self): g = self.game if not options.archive_path: return data = [] data.append('# ' + ', '.join([ p.account.username.encode('utf-8') for p in self.users ])) data.append('# Ver: ' + VERSION) data.append('# GameId: ' + str(self.gameid)) s, e = int(self.start_time), int(time.time()) data.append('# Time: start = %d, end = %d, elapsed = %d' % (s, e, e - s)) data.append(self.gamecls.__name__) data.append(json.dumps(self.game_params)) data.append(str(g.rndseed)) data.append(json.dumps(self.usergdhistory)) data.append(json.dumps(self.gdhistory)) f = gzip.open(os.path.join(options.archive_path, '%s-%s.gz' % (options.node, str(self.gameid))), 'wb') f.write('\n'.join(data)) f.close() def get_ready(self, user): if user.state not in ('inroomwait',): return g = self.game if user not in self.users: log.error('User not in player list') return user.state = 'ready' self.notify_playerchange() if all(u.state == 'ready' for u in self.users): if not g.started: # race condition here. # wrap in 'if g.started' to prevent double starting. log.info("game starting") g.start() def cancel_ready(self, user): if user.state not in ('ready',): return if user not in self.users: log.error('User not in player list') return user.state = 'inroomwait' self.notify_playerchange() def set_game_param(self, user, key, value): if user.state != 'inroomwait': return if user not in self.users: log.error('User not in this game!') return cls = self.gamecls if key not in cls.params_def: log.error('Invalid option "%s"', key) return if value not in cls.params_def[key]: log.error('Invalid value "%s" for key "%s"', value, key) return if self.game_params[key] == value: return self.game_params[key] = value for u in self.users: if u.state == 'ready': self.cancel_ready(u) u.write(['set_game_param', [user, key, value]]) u.write(['game_params', self.game_params]) self.notify_playerchange() def update_game_param(self, params): self.game_params.update(params) self.users.write(['game_params', self.game_params]) self.notify_playerchange() def change_location(self, user, loc): if user.state not in ('inroomwait', ): return pl = self.users if (not 0 <= loc < len(pl)) or (pl[loc] is not ClientPlaceHolder): user.write(['change_loc_failed', None]) return # observing: should send join game i = pl.index(user) pl[loc], pl[i] = pl[i], pl[loc] self.notify_playerchange() def kick_user(self, user, other): if user.state not in ('inroomwait', 'ready'): return cl = self.users bl = self.banlist[other] bl.add(user) s = Client.encode(['kick_request', [user, other, len(bl)]]) for cl in self.users: cl.raw_write(s) cl.observers and cl.observers.raw_write(s) return len(bl) >= len(self.users) // 2 def kick_observer(self, user, other): if user not in self.users: return False if GameManager.get_by_user(other) is not self: return False if other.state != 'observing': return False bl = self.ob_banlist[other] bl.add(user) s = Client.encode(['ob_kick_request', [user, other, len(bl)]]) for cl in self.users: cl.raw_write(s) cl.observers and cl.observers.raw_write(s) return len(bl) >= len(self.users) // 2 def observe_leave(self, user, no_move=False): assert user.state == 'observing' tgt = user.observing tgt.observers.remove(user) try: del self.ob_banlist[user] except KeyError: pass user.state = 'hang' user.observing = None user.current_game = None user.gclear() no_move or user.write(['game_left', None]) @gevent.spawn def notify_observer_leave(): ul = self.users info = [user.account.userid, user.account.username, tgt.account.username] ul.write(['observer_leave', info]) for obl in ul.observers: obl and obl.write(['observer_leave', info]) def observe_user(self, user, observee): g = self.game assert observee in self.users assert user.state == 'hang' log.info("observe game") observee.observers.append(user) user.state = 'observing' user.current_game = self user.observing = observee user.write(['game_joined', self]) user.gclear() # clear stale gamedata self.notify_playerchange() @gevent.spawn def notify_observer(): ul = self.users info = [user.account.userid, user.account.username, observee.account.username] ul.write(['observer_enter', info]) for obl in ul.observers: obl and obl.write(['observer_enter', info]) if g.started: user.write(['observe_started', [self.game_params, observee.account.userid, self.build_initial_players()]]) self.replay(user, observee) else: self.notify_playerchange() def is_banned(self, user): if self.is_match: return not (user.account.userid in self.match_users or user.account.username in self.match_users) return len(self.banlist[user]) >= max(self.game.n_persons // 2, 1) def is_invited(self, user): return not self.invite_only or user.account.userid in self.invite_list def add_invited(self, user): self.invite_list.add(user.account.userid) def copy_invited(self, mgr): self.invite_list = set(mgr.invite_list) def join_game(self, user, slot, observing=False): assert user not in self.users assert user.state == ('observing' if observing else 'hang') if slot is None: slot = self.next_free_slot() elif self.users[slot] is not ClientPlaceHolder: slot = None if slot is None: return self.users[slot] = user if observing: origin = self.get_by_user(user) origin.observe_leave(user, no_move=origin is self) user.current_game = self user.state = 'inroomwait' user.write(['game_joined', self]) if user.observers: for ob in user.observers: ob.write(['game_joined', self]) ob.current_game = self ob.state = 'observing' self.notify_playerchange() def start_game(self): g = self.game assert ClientPlaceHolder not in self.users assert all([u.state == 'ready' for u in self.users]) if self.is_match: gevent.spawn(lambda: interconnect.publish( 'speaker', [u'文文', u'“%s”开始了!参与玩家:%s' % ( self.game_name, u','.join(self.users.account.username) )] )) self.game_started = True g.players = self.build_initial_players() self.usergdhistory = [] self.gdhistory = [list() for p in self.users] self.start_time = time.time() for u in self.users: u.write(["game_started", [self.game_params, g.players]]) u.gclear() if u.observers: u.observers.gclear() u.observers.write(['observe_started', [self.game_params, u.account.userid, g.players]]) u.state = 'ingame' def build_initial_players(self): from server.core.game_server import Player, NPCPlayer pl = BatchList([Player(u) for u in self.users]) pl[:0] = [NPCPlayer(NPCClient(i.name), i.input_handler) for i in self.game.npc_players] return pl def record_gamedata(self, user, tag, data): idx = self.users.index(user) self.gdhistory[idx].append((tag, Client.decode(Client.encode(data)))) def record_user_gamedata(self, user, tag, data): idx = self.users.index(user) self.usergdhistory.append((idx, tag, Client.decode(Client.encode(data)))) def replay(self, observer, observee): idx = self.users.index(observee) for data in self.gdhistory[idx]: observer.write(['gamedata', data]) def squeeze_out(self, old, new): old.write(['others_logged_in', None]) if old.state == 'ingame': self.reconnect(new) def reconnect(self, new): g = self.game new.state = 'ingame' new.current_game = self for p in g.players: if p.client.account.userid == new.account.userid: p.reconnect(new) break else: assert False, 'Oops' for i, u in enumerate(self.users): if u.account.userid == new.account.userid: self.users[i] = new break else: assert False, 'Oops' new.write(['game_joined', self]) self.notify_playerchange() players = self.build_initial_players() new.write(['game_started', [self.game_params, players]]) self.replay(new, new) def exit_game(self, user, is_drop): rst = None assert user in self.users is_drop or user.gclear() g = self.game if self.game_started: i = g.players.client.index(user) p = g.players[i] log.info('player dropped') can_leave = g.can_leave(p) if can_leave: user.write(['game_left', None]) p.set_fleed(False) else: p.set_dropped() if not is_drop: user.write(['fleed', None]) p.set_fleed(True) else: p.set_fleed(False) p.client.gbreak() # XXX: f**k I forgot why it's here. Exp: see comment on Client.gbreak dummy = DroppedClient(g.players[i].client) p.set_client(dummy) self.users.replace(user, dummy) rst = dummy else: log.info('player leave') self.users.replace(user, ClientPlaceHolder) user.write(['game_left', None]) self.notify_playerchange() for bl in self.banlist.values(): bl.discard(user) return rst def end_game(self): if self.is_match and not self.game.suicide: gevent.spawn(lambda: interconnect.publish( 'speaker', [u'文文', u'“%s”结束了!获胜玩家:%s' % ( self.game_name, u','.join(BatchList(self.game.winners).account.username) )] )) for u in self.users: u.write(['end_game', None]) u.observers and u.observers.write(['end_game', None]) u.state = 'hang' def get_online_users(self): return [p for p in self.users if (p is not ClientPlaceHolder and not isinstance(p, DroppedClient))] def kill_game(self): if self.is_match and self.game.started: gevent.spawn(lambda: interconnect.publish( 'speaker', [u'文文', u'“%s”意外终止了!' % self.game_name] )) self.game.suicide = True # game will kill itself in get_synctag() def get_bonus(self): assert self.get_online_users() t = time.time() g = self.game percent = min(1.0, (t - self.start_time) / 1200) import math rate = math.sin(math.pi / 2 * percent) winners = g.winners bonus = g.n_persons * 5 / len(winners) if winners else 0 rst = [] for p in g.players: u = p.client if isinstance(u, NPCClient): continue rst.append((u, 'games', 1)) if p.dropped or p.fleed: if not options.no_counting_flee: rst.append((u, 'drops', 1)) else: s = 5 + bonus if p in winners else 5 rst.append((u, 'credits', int(s * rate * options.credit_multiplier))) return rst def record_stacktrace(self): g = self.game log.info('>>>>> GAME STACKTRACE <<<<<') def logtraceback(gr): import traceback log.info('----- %r -----\n%s', gr, ''.join(traceback.format_stack(gr.gr_frame))) logtraceback(g) for i in g.gr_groups: for j in i: logtraceback(j) for cl in g.players.client: if isinstance(cl, Client): logtraceback(cl) log.info('===========================')
class Client(Endpoint): def __init__(self, sock, addr, greenlet): Endpoint.__init__(self, sock, addr) self.observers = BatchList() self.gamedata = Gamedata() self.cmd_listeners = defaultdict(WeakSet) self.current_game = None self.greenlet = greenlet self.account = None @classmethod def serve(cls, sock, addr): c = getcurrent() cli = cls(sock, addr, c) c.gr_name = repr(cli) cli._serve() @log_failure(log) def _serve(self): # ----- Banner ----- from settings import VERSION self.write(['thbattle_greeting', (options.node, VERSION)]) # ------------------ self.state = 'connected' while True: try: hasdata = False with Timeout(90, False): cmd, data = self.read() hasdata = True if not hasdata: self.close() # client should send heartbeat periodically raise EndpointDied self.handle_command(cmd, data) except EndpointDied: self.gbreak() break except Exception: log.exception("Error occurred when handling client command") # client died, do clean ups self.handle_drop() def close(self): Endpoint.close(self) gr = self.greenlet self.greenlet = None gr and gr.kill(EndpointDied) def __repr__(self): acc = self.account if not acc: return Endpoint.__repr__(self) return '%s:%s:%s' % ( self.__class__.__name__, self.address[0], acc.username.encode('utf-8'), ) def __data__(self): return dict( account=self.account, state=self.state, ) def __eq__(self, other): return self.account is other.account def listen_command(self, *cmds): listeners_set = self.cmd_listeners q = Queue(100) for cmd in cmds: listeners_set[cmd].add(q) return q def handle_command(self, cmd, data): if cmd == 'gamedata': self.gamedata.feed(data) return f = getattr(self, 'command_' + str(cmd), None) if not f: listeners = self.cmd_listeners[cmd] if listeners: [l.put(data) for l in listeners] return log.debug('No command %s', cmd) self.write(['invalid_command', [cmd, data]]) return if not isinstance(data, (list, tuple)): log.error('Malformed command: %s %s', cmd, data) return n = f.__code__.co_argcount - 1 if n != len(data): log.error( 'Command "%s" argcount mismatch, expect %s, got %s', cmd, n, len(data) ) else: f(*data) def handle_drop(self): if self.state not in ('connected', 'hang'): Subsystem.lobby.exit_game(self, is_drop=True) if self.state != 'connected': Subsystem.lobby.user_leave(self) def gexpect(self, tag, blocking=True): tag, data = self.gamedata.gexpect(tag, blocking) tag and _record_user_gamedata(self, tag, data) return tag, data def gwrite(self, tag, data): log.debug('GAME_WRITE: %s -> %s', self.account.username, repr([tag, data])) _record_gamedata(self, tag, data) encoded = self.encode(['gamedata', [tag, data]]) self.raw_write(encoded) self.observers and self.observers.raw_write(encoded) def gbreak(self): return self.gamedata.gbreak() def gclear(self): self.gamedata = Gamedata() # --------- Handlers --------- def command_auth(self, login, password): if self.state != 'connected' or self.account: self.write(['invalid_command', ['auth', '']]) return acc = Account.authenticate(login, password) if acc: self.account = acc if not acc.available(): self.write(['auth_result', 'not_available']) self.close() else: self.write(['auth_result', 'success']) self.account = acc Subsystem.lobby.user_join(self) else: self.write(['auth_result', 'invalid_credential']) def command_lobby(self, cmd, args): Subsystem.lobby.process_command(self, cmd, args) def command_item(self, cmd, args): Subsystem.item.process_command(self, cmd, args) def command_heartbeat(self): pass
def apply_action(self): g = Game.getgame() params = self.params from cards import Deck g.picks = [] g.deck = Deck() g.ehclasses = list(action_eventhandlers) + g.game_ehs.values() H, M = Identity.TYPE.HAKUREI, Identity.TYPE.MORIYA if params['random_seat']: # reseat seed = get_seed_for(g.players) random.Random(seed).shuffle(g.players) g.emit_event('reseat', None) L = [[H, H, M, M, H, M], [H, M, H, M, H, M]] rnd = random.Random(get_seed_for(g.players)) L = rnd.choice(L) * 2 s = rnd.randrange(0, 6) idlist = L[s:s+6] del L, s, rnd else: idlist = [H, M, H, M, H, M] del H, M for p, identity in zip(g.players, idlist): p.identity = Identity() p.identity.type = identity g.process_action(RevealIdentity(p, g.players)) force_hakurei = BatchList() force_moriya = BatchList() force_hakurei.pool = [] force_moriya.pool = [] for p in g.players: if p.identity.type == Identity.TYPE.HAKUREI: force_hakurei.append(p) p.force = force_hakurei elif p.identity.type == Identity.TYPE.MORIYA: force_moriya.append(p) p.force = force_moriya g.forces = BatchList([force_hakurei, force_moriya]) # ----- roll ------ roll = range(len(g.players)) g.random.shuffle(roll) pl = g.players roll = sync_primitive(roll, pl) roll = [pl[i] for i in roll] g.emit_event('game_roll', roll) first = roll[0] g.emit_event('game_roll_result', first) # ---- # choose girls --> from . import characters chars = characters.get_characters('faith') g.random.shuffle(chars) # ANCHOR(test) testing = list(settings.TESTING_CHARACTERS) testing = filter_out(chars, lambda c: c.__name__ in testing) chars = g.random.sample(chars, 30 - len(testing)) chars.extend(testing) if Game.SERVER_SIDE: choices = [CharChoice(cls) for cls in chars[-24:]] else: choices = [CharChoice(None) for _ in xrange(24)] del chars[-24:] for p in g.players: c = choices[-3:] del choices[-3:] akari = CharChoice(characters.akari.Akari) akari.real_cls = chars.pop() c.append(akari) p.choices = c p.choices_chosen = [] p.reveal(c) mapping = {p: p.choices for p in g.players} rst = user_input(g.players, SortCharacterInputlet(g, mapping, 2), timeout=30, type='all') for p in g.players: p.choices_chosen = [mapping[p][i] for i in rst[p][:2]] for p in g.players: a, b = p.choices_chosen b.chosen = None p.force.reveal(b) g.switch_character(p, a) p.force.pool.append(b) del p.choices_chosen for p in g.players: if p.player is first: first = p break pl = g.players first_index = pl.index(first) order = BatchList(range(len(pl))).rotate_to(first_index) for p in pl: g.process_action(RevealIdentity(p, pl)) g.emit_event('game_begin', g) for p in pl: g.process_action(DistributeCards(p, amount=4)) pl = g.players.rotate_to(first) rst = user_input(pl[1:], ChooseOptionInputlet(DeathHandler(), (False, True)), type='all') for p in pl[1:]: rst.get(p) and g.process_action(RedrawCards(p, p)) pl = g.players for i, idx in enumerate(cycle(order)): if i >= 6000: break p = pl[idx] if p.dead: continue g.emit_event('player_turn', p) try: g.process_action(PlayerTurn(p)) except InterruptActionFlow: pass return True
def sysmsg(): while True: users = BatchList(self.users.values()) users.write(['system_msg', [None, u'游戏已经更新,当前的游戏结束后将会被自动踢出,请更新后重新游戏']]) gevent.sleep(30)
def game_start(g): # game started, init state from cards import Deck g.deck = Deck() g.ehclasses = list(action_eventhandlers) + g.game_ehs.values() # reseat seed = get_seed_for(g.players) random.Random(seed).shuffle(g.players) g.emit_event('reseat', None) H, M = Identity.TYPE.HAKUREI, Identity.TYPE.MORIYA L = [[H, H, M, M, H, M], [H, M, H, M, H, M]] rnd = random.Random(get_seed_for(g.players)) L = rnd.choice(L) * 2 s = rnd.randrange(0, 6) idlist = L[s:s+6] del H, M, L, s, rnd for p, identity in zip(g.players, idlist): p.identity = Identity() p.identity.type = identity g.process_action(RevealIdentity(p, g.players)) force_hakurei = BatchList() force_moriya = BatchList() force_hakurei.pool = [] force_moriya.pool = [] for p in g.players: if p.identity.type == Identity.TYPE.HAKUREI: force_hakurei.append(p) p.force = force_hakurei elif p.identity.type == Identity.TYPE.MORIYA: force_moriya.append(p) p.force = force_moriya g.forces = BatchList([force_hakurei, force_moriya]) # ----- roll ------ roll = range(len(g.players)) g.random.shuffle(roll) pl = g.players roll = sync_primitive(roll, pl) roll = [pl[i] for i in roll] g.emit_event('game_roll', roll) first = roll[0] g.emit_event('game_roll_result', first) # ---- # choose girls --> from . import characters chars = list(characters.characters) g.random.shuffle(chars) if Game.SERVER_SIDE: choices = [CharChoice(cls) for cls in chars[-24:]] else: choices = [CharChoice(None) for _ in xrange(24)] del chars[-24:] for p in g.players: c = choices[-3:] del choices[-3:] akari = CharChoice(characters.akari.Akari) akari.real_cls = chars.pop() c.append(akari) p.choices = c p.choices_chosen = [] p.reveal(c) mapping = {p: p.choices for p in g.players} remaining = {p: 2 for p in g.players} deadline = time.time() + 30 with InputTransaction('ChooseGirl', g.players, mapping=mapping) as trans: while True: pl = [p for p in g.players if remaining[p]] if not pl: break p, c = user_input( pl, ChooseGirlInputlet(g, mapping), timeout=deadline - time.time(), trans=trans, type='any' ) p = p or pl[0] c = c or [_c for _c in p.choices if not _c.chosen][0] c.chosen = p p.choices.remove(c) p.choices_chosen.append(c) remaining[p] -= 1 trans.notify('girl_chosen', c) for p in g.players: a, b = p.choices_chosen b.chosen = None p.force.reveal(b) g.switch_character(p, a) p.force.pool.append(b) del p.choices_chosen for p in g.players: if p.player is first: first = p break try: pl = g.players first_index = pl.index(first) order = BatchList(range(len(pl))).rotate_to(first_index) for p in pl: g.process_action(RevealIdentity(p, pl)) g.emit_event('game_begin', g) for p in pl: g.process_action(DrawCards(p, amount=4)) pl = g.players.rotate_to(first) rst = user_input(pl[1:], ChooseOptionInputlet(DeathHandler(), (False, True)), type='all') for p in pl[1:]: rst.get(p) and g.process_action(RedrawCards(p, p)) pl = g.players for i, idx in enumerate(cycle(order)): if i >= 6000: break p = pl[idx] if p.dead: continue g.emit_event('player_turn', p) try: g.process_action(PlayerTurn(p)) except InterruptActionFlow: pass except GameEnded: pass log.info(u'>> Winner: %s', Identity.TYPE.rlookup(g.winners[0].identity.type))
class Client(Endpoint, Greenlet): def __init__(self, sock, addr): Endpoint.__init__(self, sock, addr) Greenlet.__init__(self) self.observers = BatchList() self.gamedata = Gamedata() self.cmd_listeners = defaultdict(WeakSet) self.current_game = None @log_failure(log) def _run(self): self.account = None # ----- Banner ----- from settings import VERSION self.write(['thbattle_greeting', (options.node, VERSION)]) # ------------------ self.state = 'connected' while True: try: hasdata = False with Timeout(90, False): cmd, data = self.read() hasdata = True if not hasdata: self.close() # client should send heartbeat periodically raise EndpointDied if cmd == 'gamedata': self.gamedata.feed(data) else: self.handle_command(cmd, data) except EndpointDied: self.gbreak() break except Exception: log.exception("Error occurred when handling client command") # client died, do clean ups self.handle_drop() def close(self): Endpoint.close(self) self.kill(EndpointDied) def __repr__(self): acc = self.account if not acc: return Endpoint.__repr__(self) return '%s:%s:%s' % ( self.__class__.__name__, self.address[0], acc.username.encode('utf-8'), ) def __data__(self): return dict( account=self.account, state=self.state, ) def __eq__(self, other): return self.account is other.account def listen_command(self, *cmds): listeners_set = self.cmd_listeners q = Queue(100) for cmd in cmds: listeners_set[cmd].add(q) return q def handle_command(self, cmd, data): f = getattr(self, 'command_' + str(cmd), None) if not f: listeners = self.cmd_listeners[cmd] if listeners: [l.put(data) for l in listeners] return log.debug('No command %s', cmd) self.write(['invalid_command', [cmd, data]]) return for_state = getattr(f, 'for_state', None) if for_state and self.state not in for_state: log.debug('Command %s is for state %s, called in %s', cmd, for_state, self.state) self.write(['invalid_command', [cmd, data]]) return if not isinstance(data, (list, tuple)): log.error('Malformed command: %s %s', cmd, data) return n = f.__code__.co_argcount - 1 if n != len(data): log.error( 'Command "%s" argcount mismatch, expect %s, got %s', cmd, n, len(data) ) else: f(*data) def handle_drop(self): if self.state not in ('connected', 'hang'): lobby.exit_game(self, is_drop=True) if self.state != 'connected': lobby.user_leave(self) def gexpect(self, tag, blocking=True): tag, data = self.gamedata.gexpect(tag, blocking) if tag: manager = GameManager.get_by_user(self) manager.record_user_gamedata(self, tag, data) return tag, data def gwrite(self, tag, data): log.debug('GAME_WRITE: %s -> %s', self.account.username, repr([tag, data])) manager = GameManager.get_by_user(self) manager.record_gamedata(self, tag, data) encoded = self.encode(['gamedata', [tag, data]]) self.raw_write(encoded) self.observers and self.observers.raw_write(encoded) def gbreak(self): return self.gamedata.gbreak() def gclear(self): self.gamedata = Gamedata() def for_state(*state): def register(f): f.for_state = state return f return register # --------- Handlers --------- @for_state('connected') def command_auth(self, login, password): if self.account: self.write(['invalid_command', ['auth', '']]) return acc = Account.authenticate(login, password) if acc: self.account = acc if not acc.available(): self.write(['auth_result', 'not_available']) self.close() else: self.write(['auth_result', 'success']) self.account = acc lobby.user_join(self) else: self.write(['auth_result', 'invalid_credential']) @for_state('hang') def command_create_game(self, _type, name, invite_only): manager = lobby.create_game(self, _type, name, invite_only) manager.add_invited(self) manager and lobby.join_game(self, manager.gameid) @for_state('hang') def command_quick_start_game(self): lobby.quick_start_game(self) @for_state('hang') def command_join_game(self, gameid): lobby.join_game(self, gameid) def command_get_lobbyinfo(self): lobby.send_lobbyinfo([self]) @for_state('hang') def command_observe_user(self, uid): lobby.observe_user(self, uid) @for_state('hang') def command_query_gameinfo(self, gid): lobby.send_gameinfo(self, gid) @for_state('inroomwait') def command_get_ready(self): lobby.get_ready(self) @for_state('inroomwait') def command_set_game_param(self, key, value): lobby.set_game_param(self, key, value) @for_state('inroomwait', 'ready', 'ingame', 'observing') def command_exit_game(self): lobby.exit_game(self) @for_state('inroomwait', 'ready') def command_kick_user(self, uid): lobby.kick_user(self, uid) @for_state('inroomwait', 'ready') def command_invite_user(self, uid): lobby.invite_user(self, uid) @for_state('inroomwait', 'ready', 'ingame') def command_kick_observer(self, uid): lobby.kick_observer(self, uid) @for_state('inroomwait', 'observing') def command_change_location(self, loc): lobby.change_location(self, loc) @for_state('ready') def command_cancel_ready(self): lobby.cancel_ready(self) def command_heartbeat(self): pass @for_state('hang', 'inroomwait', 'ready', 'ingame', 'observing') def command_chat(self, text): lobby.chat(self, text) @for_state('hang', 'inroomwait', 'ready', 'ingame', 'observing') def command_speaker(self, text): lobby.speaker(self, text) del for_state
class Client(Endpoint, GamedataMixin, Greenlet): def __init__(self, sock, addr): Endpoint.__init__(self, sock, addr) Greenlet.__init__(self) self.observers = BatchList() self.init_gamedata_mixin() self.gdhistory = [] self.usergdhistory = [] @log_failure(log) def _run(self): self.account = None # ----- Banner ----- from settings import VERSION self.write(['thbattle_greeting', VERSION]) # ------------------ self.state = 'connected' while True: try: hasdata = False with Timeout(30, False): cmd, data = self.read() hasdata = True if not hasdata: self.close() # client should send heartbeat periodically raise EndpointDied if cmd == 'gamedata': self.gamedata(data) else: self.handle_command(cmd, data) except EndpointDied: self.gbreak() break # client died, do clean ups self.handle_drop() def gexpect(self, tag, blocking=True): tag, data = GamedataMixin.gexpect(self, tag, blocking) tag and self.usergdhistory.append((self.player_index, tag, data)) return tag, data def gwrite(self, tag, data): log.debug('GAME_WRITE: %s', repr([tag, data])) encoded = self.encode(['gamedata', [tag, data]]) self.raw_write(encoded) self.gdhistory.append([tag, json.loads(self.encode(data))]) self.observers and self.observers.raw_write(encoded) def replay(self, ob): for data in self.gdhistory: ob.raw_write(json.dumps(['gamedata', data]) + '\n') def __data__(self): return [self.account.userid, self.account.username, self.state] def close(self): Endpoint.close(self) self.kill(EndpointDied) def __repr__(self): acc = self.account if not acc: return Endpoint.__repr__(self) return '%s:%s:%s' % ( self.__class__.__name__, self.address[0], acc.username.encode('utf-8'), )