示例#1
0
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
示例#2
0
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'),
        )
示例#3
0
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
示例#4
0
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'),
        )
示例#5
0
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
示例#6
0
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'),
        )
示例#7
0
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'),
        )