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, 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'), )
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
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'), )
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
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'), )
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(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(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'), )