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 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(): 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])
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('===========================')
def sysmsg(): while True: users = BatchList(self.users.values()) users.write(['system_msg', [None, u'游戏已经更新,当前的游戏结束后将会被自动踢出,请更新后重新游戏']]) gevent.sleep(30)