示例#1
0
 def __init__(self):
     UGAMEClientProtocol.__init__(self)
     self.callbacks = {'current': {}, 'not_current': {}, 'outbound': {}}
     self.setCurrentGameId(None)
     self.pending_auth_request = False
     self.publish_packets = []
     self.input_packets = []
     self.publish_timer = None
     self.publish_time = 0
     self.publishPackets()
     self.lag = DEFAULT_LAGMAX
     self.lagmax_callbacks = []
     self.explain = PokerExplain()
示例#2
0
 def __init__(self):
     UGAMEClientProtocol.__init__(self)
     self.log = PokerClientProtocol.log.get_instance(
         self,
         refs=[('User', self, lambda client: client.user.serial
                if client.user else None)])
     self.callbacks = {'current': {}, 'not_current': {}, 'outbound': {}}
     self.setCurrentGameId(None)
     self.pending_auth_request = False
     self.publish_packets = []
     self.input_packets = []
     self.publish_timer = None
     self.publish_time = 0
     self.publishPackets()
     self.lag = DEFAULT_LAGMAX
     self.lagmax_callbacks = []
     self.explain = PokerExplain()
示例#3
0
 def __init__(self):
     UGAMEClientProtocol.__init__(self)
     self.callbacks = {
         'current': {},
         'not_current': {},
         'outbound': {}
         }
     self.setCurrentGameId(None)
     self.pending_auth_request = False
     self.publish_packets = []
     self.input_packets = []
     self.publish_timer = None
     self.publish_time = 0
     self.publishPackets()
     self.lag = DEFAULT_LAGMAX
     self.lagmax_callbacks = []
     self.explain = PokerExplain()
示例#4
0
 def __init__(self):
     UGAMEClientProtocol.__init__(self)
     self.log = PokerClientProtocol.log.get_instance(self, refs=[
         ('User', self, lambda client: client.user.serial if client.user else None)
     ])
     self.callbacks = {
         'current': {},
         'not_current': {},
         'outbound': {}
         }
     self.setCurrentGameId(None)
     self.pending_auth_request = False
     self.publish_packets = []
     self.input_packets = []
     self.publish_timer = None
     self.publish_time = 0
     self.publishPackets()
     self.explain = PokerExplain()
     
     self._poll = False
示例#5
0
class PokerClientProtocol(UGAMEClientProtocol):
    """Poker client"""

    def __init__(self):
        UGAMEClientProtocol.__init__(self)
        self.callbacks = {
            'current': {},
            'not_current': {},
            'outbound': {}
            }
        self.setCurrentGameId(None)
        self.pending_auth_request = False
        self.publish_packets = []
        self.input_packets = []
        self.publish_timer = None
        self.publish_time = 0
        self.publishPackets()
        self.lag = DEFAULT_LAGMAX
        self.lagmax_callbacks = []
        self.explain = PokerExplain()

    def setPrefix(self, prefix):
        self._prefix = prefix
        self.explain.setPrefix(prefix)
        
    def setCurrentGameId(self, game_id):
        if hasattr(self.factory, 'verbose') and self.factory.verbose > 2: self.message("setCurrentGameId(%s)" % game_id)
        self.hold(0)
        self.currentGameId = game_id

    def getCurrentGameId(self):
        return self.currentGameId
    
    def connectionMade(self):
        "connectionMade"
        if self.factory.delays_enable:
            self._lagmax = ABSOLUTE_LAGMAX
            self.lag = self.factory.delays.get("lag", DEFAULT_LAGMAX)
        self.no_display_packets = self.factory.no_display_packets
        UGAMEClientProtocol.connectionMade(self)

    def packetDeferred(self, what, name):
        d = defer.Deferred()
        def fire(client, packet):
            d.callback((client, packet))
        self.registerHandler(what, name, fire)
        def unregister(arg):
            self.unregisterHandler(what, name, fire)
            return  arg
        d.addCallback(unregister)
        return d
        
    def registerHandler(self, what, name, meth):
        if name:
            names = [ name ]
        else:
            names = PacketNames.keys()
        if what != True:
            whats = [ what ]
        else:
            whats = [ 'current', 'not_current', 'outbound' ]
        for what in whats:
            callbacks = self.callbacks[what]
            for name in names:
                callbacks.setdefault(name, []).append(meth)
        
    def unregisterHandler(self, what, name, meth):
        if name:
            names = [ name ]
        else:
            names = PacketNames.keys()
        if what != True:
            whats = [ what ]
        else:
            whats = [ 'current', 'not_current', 'outbound' ]
        for what in whats:
            callbacks = self.callbacks[what]
            for name in names:
                callbacks[name].remove(meth)
        
    def normalizeChips(self, game, chips):
        if game.unit in self.factory.chips_values:
            values = self.factory.chips_values[self.factory.chips_values.index(game.unit):]
        else:
            values = []
        list = PokerChips(values, chips).tolist()
        if self.factory.verbose > 4:
            self.message("normalizeChips: " + str(list) + " " + str(values))
        return list
            
    def updatePlayerChips(self, game, player):
        packet = PacketPokerPlayerChips(game_id = game.id,
                                        serial = player.serial,
                                        bet = player.bet,
                                        money = player.money)
        return packet

    def updatePotsChips(self, game, side_pots):
        packets = []
        
        if not side_pots:
            packet = PacketPokerChipsPotReset(game_id = game.id)
            return [ packet ]
        
        index = 0
        for (amount, total) in side_pots['pots']:
            chips = amount
            bet = self.normalizeChips(game, chips)
            pot = PacketPokerPotChips(game_id = game.id,
                                      index = index,
                                      bet = bet)
            packets.append(pot)
            index += 1
        return packets

    def chipsPlayer2Bet(self, game, player, chips):
        packets = []
        packet = PacketPokerChipsPlayer2Bet(game_id = game.id,
                                            serial = player.serial,
                                            chips = self.normalizeChips(game, chips))
        packets.append(packet)
        packets.append(self.updatePlayerChips(game, player))
        return packets

    def chipsBet2Pot(self, game, player, bet, pot_index):
        packets = []
        if ( pot_index == 0 and
             player.dead > 0 and
             game.isSecondRound() ):
            #
            # The ante or the dead are already in the pot
            #
            bet -= player.dead
        packet = PacketPokerChipsBet2Pot(game_id = game.id,
                                         serial = player.serial,
                                         chips = self.normalizeChips(game, bet),
                                         pot = pot_index)
        packets.append(packet)
        packets.append(self.updatePlayerChips(game, player))
        return packets
        
    def chipsPot2Player(self, game, player, bet, pot_index, reason):
        packet = PacketPokerChipsPot2Player(game_id = game.id,
                                            serial = player.serial,
                                            chips = self.normalizeChips(game, bet),
                                            pot = pot_index,
                                            reason = reason)
        return packet
        
    def handleUserInfo(self, packet):
        if self.factory.verbose > 2:
            self.message("handleUserInfo: " + str(packet))
        self.user_info = packet

    def handlePersonalInfo(self, packet):
        self.handleUserInfo(packet)
        self.personal_info = packet

    def handleSerial(self, packet):
        self.user.serial = packet.serial
        self.sendPacket(PacketPokerGetUserInfo(serial = packet.serial))

    def handlePlayerInfo(self, packet):
        skin = self.factory.getSkin()
        #
        # Check that the implementation of the outfit is still valid. If it
        # needs upgrade, send it back to the server.
        #
        ( url, outfit ) = skin.interpret(packet.url, packet.outfit)
        if url != packet.url or outfit != packet.outfit:
            ( url_check, outfit_check ) = self.factory.getSkin().interpret(url, outfit)
            #
            # Make sure that we wont loop indefinitely because of an instability of the interpret
            # function. In normal operation the url and outfit returned by interpret must be
            # returned as is when fed to interpret again. If the implementation of interpret
            # fails to implement this stability, don't enter a loop because sending PokerPlayerInfo
            # will return us a PokerPlayerInfo for confirmation of the success.
            #
            if url_check != url or outfit_check != outfit:
                self.error("*CRITICAL*: PACKET_POKER_PLAYER_INFO: may enter loop packet.url = %s\n url = %s\n url_check = %s\npacket.outfit = %s\n outfit = %s\n outfit_check = %s" % ( packet.url, url, url_check, packet.outfit, outfit, outfit_check ))
            else:
                packet.url = url
                packet.outfit = outfit
                self.sendPacket(packet)
        skin.setUrl(url)
        skin.setOutfit(outfit)

    def logout(self):
        self.sendPacket(PacketLogout())
        self.user.logout()

    def setPlayerDelay(self, game, serial, value):
        player = game.getPlayer(serial)
        if player == None:
            self.message("WARNING setPlayerDelay for a non-existing player %d" % serial)
        else:
            player.getUserData()['delay'] = seconds() + value

    def getPlayerDelay(self, game, serial):
        if not game: return 0
        player = game.getPlayer(serial)
        if not player: return 0
        user_data = player.getUserData()
        if not user_data or not user_data.has_key('delay'): return 0
        return user_data['delay']

    def canHandlePacket(self, packet):
        if not self.factory.isAlwaysHandled(packet) and hasattr(packet, "game_id") and hasattr(packet, "serial"):
            delay = self.getPlayerDelay(self.factory.packet2game(packet), packet.serial)
            if delay <= seconds():
                return ( True, 0 )
            else:
                return ( False, delay )
        else:
            return ( True, 0 )

    def resendPlayerTimeoutWarning(self, game):
        if game.isRunning() and game.getSerialInPosition() == self.getSerial():
            player = game.getPlayer(self.getSerial())
            if player.user_data['timeout']:
                ( when, timeout ) = player.user_data['timeout']
                now = seconds()
                timeout = timeout - ( now - when )
                if timeout > 0:
                    return ( PacketPokerTimeoutWarning(game_id = game.id,
                                                       serial = self.getSerial(),
                                                       timeout = int(timeout),
                                                       when = int(now) ), )
        return ()
        
    def setPlayerTimeout(self, game, packet):
        packet.timeout -= int(self.getLag())
        if packet.timeout > 0:
            packet.when = int(seconds())
            player = game.getPlayer(packet.serial)
            player.getUserData()['timeout'] = ( packet.when, packet.timeout )
            return True
        else:
            return False
        
    def unsetPlayerTimeout(self, game, serial):
        player = game.getPlayer(serial)
        player.getUserData()['timeout'] = None
    
    def postMuck(self, game, want_to_muck):
        if game:            
            packet_type = want_to_muck and PacketPokerMuckAccept or PacketPokerMuckDeny
            self.sendPacket(packet_type(game_id = game.id, 
                                        serial  = self.getSerial()) )
    
    def _handleConnection(self, packet):
        if self.factory.verbose > 3: self.message("PokerClientProtocol:handleConnection: %s" % packet )

        if packet.type == PACKET_POKER_TIMEOUT_WARNING:
            packet.timeout -= int(self.getLag())

        elif packet.type == PACKET_POKER_USER_INFO:
            self.handleUserInfo(packet)

        elif packet.type == PACKET_POKER_PERSONAL_INFO:
            self.handlePersonalInfo(packet)

        elif packet.type == PACKET_POKER_TABLE:
            self.setCurrentGameId(packet.id)

        elif packet.type == PACKET_SERIAL:
            self.handleSerial(packet)
            self.sendPacket(PacketPokerGetPlayerInfo())

        elif packet.type == PACKET_POKER_PLAYER_INFO:
            self.handlePlayerInfo(packet)

        game = self.factory.packet2game(packet)

        if game and packet.type == PACKET_POKER_TABLE_DESTROY:
            self.scheduleTableQuit(game)
            game = None

        #
        # It is possible to receive packets related to a game that we know nothing
        # about after quitting a table. When quitting a table the client deletes
        # all information related to the game without waiting confirmation from
        # the server. Therefore the server may keep sending packets related to
        # the game before noticing TABLE_QUIT packet.
        #
        if game:
            if packet.type == PACKET_POKER_SEAT:
                if packet.seat == -1:
                    self.error("This seat is busy")
                else:
                    if game.isTournament():
                        self.sendPacket(PacketPokerSit(serial = self.getSerial(),
                                                       game_id = game.id))

            elif packet.type == PACKET_POKER_MUCK_REQUEST:                
                if packet.game_id != self.getCurrentGameId():
                   self.postMuck(game, True)

        self.explain.explain(packet)

        if game:
            if packet.type == PACKET_POKER_PLAYER_ARRIVE:
                player = game.getPlayer(packet.serial)
                player.setUserData(DEFAULT_PLAYER_USER_DATA.copy())
        
        for forward_packet in self.explain.forward_packets:
            self.schedulePacket(forward_packet)
        self.explain.forward_packets = None

    def currentGames(self, exclude = None):
        games = self.factory.getGameIds()
        if exclude:
            games.remove(exclude)
        return PacketPokerCurrentGames(game_ids = games,
                                       count = len(games))
    
    def connectionLost(self, reason):
        if self.factory.crashing:
            self.message("connectionLost: crashing, just return.")
            return
        if self.factory.verbose:
            self.message("connectionLost: noticed, aborting all tables.")
        self.abortAllTables()
        UGAMEClientProtocol.connectionLost(self, reason)
        
    def abortAllTables(self):
        for game in self.factory.games.getAll():
            self.scheduleTableAbort(game)

    def scheduleTableAbort(self, game):
        game_id = game.id
        def thisgame(packet):
            return hasattr(packet, "game_id") and packet.game_id == game_id
        self.unschedulePackets(thisgame)
        self.discardPackets(game_id)
        self.scheduleTableQuit(game)

    def scheduleTableQuit(self, game):
        self.schedulePacket(PacketPokerBatchMode(game_id = game.id))
        for player in game.playersAll():
            packet = PacketPokerPlayerLeave(game_id = game.id,
                                            serial = player.serial,
                                            seat = player.seat)
            self.schedulePacket(packet)
        self.schedulePacket(PacketPokerStreamMode(game_id = game.id))
        self.schedulePacket(PacketPokerTableQuit(game_id = game.id,
                                                serial = self.getSerial()))
        self.schedulePacket(self.currentGames(game.id))
        self.publishAllPackets()

    def resendPackets(self, game_id):
        self.publishAllPackets()
        game = self.getGame(game_id)
        self.setCurrentGameId(game.id)
        packets = []
        packet = PacketPokerTable(id = game.id,
                                  name = game.name,
                                  variant = game.variant,
                                  seats = game.max_players,
                                  betting_structure = game.betting_structure,
                                  players = game.allCount(),
                                  # observers ?
                                  # waiting ?
                                  # player_timeout ?
                                  # muck_timeout ?
                                  hands_per_hour = game.stats["hands_per_hour"],                                  
                                  average_pot = game.stats["average_pot"],
                                  percent_flop = game.stats["percent_flop"],
                                  skin = game.level_skin
                                  )
        packets.append(PacketPokerBatchMode(game_id = game.id))
        packet.seats_all = game.seats_all
        packets.append(packet)
        packets.append(PacketPokerBuyInLimits(game_id = game.id,
                                              min = game.buyIn(self.getSerial()),
                                              max = game.maxBuyIn(self.getSerial()),
                                              best = game.bestBuyIn(self.getSerial()),
                                              rebuy_min = game.minMoney()))
        packets.append(PacketPokerDealer(game_id = game.id, dealer = game.dealer_seat))
        for player in game.playersAll():
            packets.append(PacketPokerPlayerArrive(game_id = game.id,
                                                   serial = player.serial,
                                                   name = player.name,
                                                   url = player.url,
                                                   outfit = player.outfit,
                                                   blind = player.blind,
                                                   remove_next_turn = player.remove_next_turn,
                                                   sit_out = player.sit_out,
                                                   sit_out_next_turn = player.sit_out_next_turn,
                                                   auto = player.auto,
                                                   auto_blind_ante = player.auto_blind_ante,
                                                   wait_for = player.wait_for,
                                                   seat = player.seat))
            # FIXME: Should a PokerPlayerStats() packet be sent here?
            if player.isSit():
                packets.append(PacketPokerSit(game_id = game.id,
                                              serial = player.serial))
            else:
                packets.append(PacketPokerSitOut(game_id = game.id,
                                                 serial = player.serial))
            packets.append(self.updatePlayerChips(game, player))
        packets.append(PacketPokerSeats(game_id = game.id,
                                        seats = game.seats()))
        packets.append(PacketPokerStart(game_id = game.id,
                                        hand_serial = game.hand_serial))
        if game.isRunning():
            players_with_cards = game.playersNotFold()
        elif  game.isGameEndInformationValid():
            players_with_cards = game.playersWinner()
        else:
            players_with_cards = []

        if players_with_cards:
            for player in players_with_cards:
                packet = PacketPokerPlayerCards(game_id = game.id,
                                                serial = player.serial,
                                                cards = player.hand.toRawList())
                packets.append(packet)
            packets.append(PacketPokerBoardCards(game_id = game.id,
                                                 cards = game.board.tolist(False)))
        if game.isRunning():
            if not self.no_display_packets:
                packets.extend(self.updatePotsChips(game, game.getPots()))
            packets.append(PacketPokerPosition(game_id = game.id,
                                               serial = game.getSerialInPosition()))
            if not self.no_display_packets:
                packets.extend(self.updateBetLimit(game))
        else:
            if not self.no_display_packets and game.isGameEndInformationValid():
                packets.extend(self.packetsShowdown(game))
                packets.append(PacketPokerShowdown(game_id = game.id, showdown_stack = game.showdown_stack))
        packets.append(PacketPokerStreamMode(game_id = game.id))
        packets.extend(self.resendPlayerTimeoutWarning(game))
        
        for packet in packets:
            self.schedulePacket(packet)

    def deleteGames(self):
        self.setCurrentGameId(None)
        for game_id in self.factory.getGameIds():
            self.deleteGame(game_id)
        
    def deleteGame(self, game_id):
        if self.factory.verbose > 2: self.message("deleteGame: %d" % game_id)
        self.factory.deleteGame(game_id)
        def thisgame(packet):
            return hasattr(packet, "game_id") and packet.game_id == game_id
        self.unschedulePackets(thisgame)
        self.discardPackets(game_id)

    def getGame(self, game_id):
        return self.factory.getGame(game_id)

    def sendPacket(self, packet):
        if packet.type == PACKET_POKER_TABLE_QUIT:
            self.scheduleTableAbort(self.getGame(packet.game_id))
        elif packet.type == PACKET_POKER_SIT_OUT:
            game = self.getGame(packet.game_id)
            if game:
                game.sitOutNextTurn(packet.serial)
            self.schedulePacket(PacketPokerSitOutNextTurn(game_id = packet.game_id,
                                                          serial = packet.serial))
        elif packet.type == PACKET_POKER_SIT:
            game = self.getGame(packet.game_id)
            if game:
                game.sitRequested(packet.serial)
            self.schedulePacket(PacketPokerSitRequest(game_id = packet.game_id,
                                                      serial = packet.serial))
        elif packet.type == PACKET_QUIT:
            self.ignoreIncomingData()
            self.abortAllTables()

        UGAMEClientProtocol.sendPacket(self, packet)

    def protocolEstablished(self):
        self.setPingDelay(self.factory.ping_delay)
        poll_frequency = self.factory.settings.headerGet("/settings/@poll_frequency")
        if poll_frequency:
            self._poll_frequency = float(poll_frequency)
        self.user.name = self.factory.name
        self.user.password = self.factory.password
        self._packet2id = self.packet2id
        self._packet2front = self.packet2front
        self.schedulePacket(PacketBootstrap())
        UGAMEClientProtocol.protocolEstablished(self)

    def packet2front(self, packet):
        if ( hasattr(packet, "game_id") and
             self.getGame(packet.game_id) ):
            if ( packet.type == PACKET_POKER_CHAT ):
                return True

            elif packet.type == PACKET_POKER_MESSAGE:
                return True

            elif ( packet.type == PACKET_POKER_PLAYER_ARRIVE and
                   packet.serial == self.getSerial() ):
                return True

        return False

    def registerLagmax(self, method):
        self.lagmax_callbacks.append(method)

    def unregisterLagmax(self, method):
        self.lagmax_callbacks.remove(method)
        
    def triggerLagmax(self, packet):
        for method in self.lagmax_callbacks:
            method(packet)
    
    def packet2id(self, packet):
        self.triggerLagmax(packet)
        if not self.factory.isOutbound(packet) and hasattr(packet, "game_id"):
            return packet.game_id
        elif packet.type == PACKET_POKER_TABLE:
            return packet.id
        else:
            return 0
        
    def protocolInvalid(self, server, client):
        self.schedulePacket(PacketProtocolError(message = "Upgrade the client from\nhttp://mekensleep.org/\nServer version is %s\nClient version is %s" % ( server, client ) ))
        self.publishAllPackets()
        UGAMEClientProtocol.protocolInvalid(self, server, client)

    def publishDelay(self, delay):
        if self.factory.verbose > 2: self.message("publishDelay: %f delay" % delay)
        publish_time = seconds() + delay
        if publish_time > self.publish_time:
            self.publish_time = publish_time
            
    def schedulePacket(self, packet):
        if not self.factory.isOutbound(packet) and hasattr(packet, "game_id") and not self.factory.gameExists(packet.game_id):
            return
        self.publish_packets.append(packet)
        if not self._poll:
            self.publishPacket()
        else:
            self.publishPacketTriggerTimer()
            
    def unschedulePackets(self, predicate):
        self.publish_packets = filter(lambda packet: not predicate(packet), self.publish_packets)
        if self._poll:
            self.publishPacketTriggerTimer()
        
    def publishPackets(self):
        if not self._poll:
            return

        delay = 0.01
        if len(self.publish_packets) > 0:
            #
            # If time has not come, make sure we are called at a later time
            # to reconsider the situation
            #
            wait_for = self.publish_time - seconds()
            if wait_for > 0:
                if self.factory.verbose > 2:
                    self.message("publishPackets: %f before next packet is sent" % wait_for)
                delay = wait_for
                self.block()
            else:
                self.publishPacket()
                if len(self.publish_packets) > 0:
                    self.block()
                else:
                    self.unblock()
        else:
            self.unblock()
            
        self.publishPacketTriggerTimer(delay)

    def publishPacketTriggerTimer(self, delay = 0.01):
        if not self.publish_timer or not self.publish_timer.active():
            if len(self.publish_packets) > 0:
                self.publish_timer = reactor.callLater(delay, self.publishPackets)

    def publishPacket(self):
        packet = self.publish_packets[0]
        if not self.established and not self.factory.isConnectionLess(packet):
            if self.factory.verbose > 5:
                self.message("publishPacket: skip because connection not established")
            return
        self.publish_packets.pop(0)
        what = 'outbound'
        if hasattr(packet, "game_id"):
            if self.factory.isOutbound(packet):
                what = 'outbound'
            else:
                if packet.game_id == self.getCurrentGameId():
                    what = 'current'
                else:
                    what = 'not_current'
        elif ( packet.type == PACKET_POKER_TABLE or
               packet.type == PACKET_POKER_TABLE_QUIT ):
            what = 'current'
        else:
            what = 'outbound'

        if self.factory.verbose > 2: self.message("publishPacket(%d): %s: %s" % ( self.getSerial(), what, packet ) )
        if self.callbacks[what].has_key(packet.type):
            callbacks = self.callbacks[what][packet.type]
            for callback in callbacks:
                callback(self, packet)
        
    def publishAllPackets(self):
        while len(self.publish_packets) > 0:
            self.publishPacket()
示例#6
0
class PokerClientProtocol(UGAMEClientProtocol):
    """Poker client"""

    log = log.get_child('PokerClientProtocol')

    def __init__(self):
        UGAMEClientProtocol.__init__(self)
        self.log = PokerClientProtocol.log.get_instance(
            self,
            refs=[('User', self, lambda client: client.user.serial
                   if client.user else None)])
        self.callbacks = {'current': {}, 'not_current': {}, 'outbound': {}}
        self.setCurrentGameId(None)
        self.pending_auth_request = False
        self.publish_packets = []
        self.input_packets = []
        self.publish_timer = None
        self.publish_time = 0
        self.publishPackets()
        self.lag = DEFAULT_LAGMAX
        self.lagmax_callbacks = []
        self.explain = PokerExplain()

    def setPrefix(self, prefix):
        self._prefix = prefix
        self.explain.setPrefix(prefix)

    def setCurrentGameId(self, game_id):
        self.log.debug("setCurrentGameId(%s)", game_id)
        self.hold(0)
        self.currentGameId = game_id

    def getCurrentGameId(self):
        return self.currentGameId

    def connectionMade(self):
        "connectionMade"
        if self.factory.delays_enable:
            self._lagmax = ABSOLUTE_LAGMAX
            self.lag = self.factory.delays.get("lag", DEFAULT_LAGMAX)
        self.no_display_packets = self.factory.no_display_packets
        UGAMEClientProtocol.connectionMade(self)

    def packetDeferred(self, what, name):
        d = defer.Deferred()

        def fire(client, packet):
            d.callback((client, packet))

        self.registerHandler(what, name, fire)

        def unregister(arg):
            self.unregisterHandler(what, name, fire)
            return arg

        d.addCallback(unregister)
        return d

    def registerHandler(self, what, name, meth):
        if name:
            names = [name]
        else:
            names = PacketNames.keys()
        if what != True:
            whats = [what]
        else:
            whats = ['current', 'not_current', 'outbound']
        for what in whats:
            callbacks = self.callbacks[what]
            for name in names:
                callbacks.setdefault(name, []).append(meth)

    def unregisterHandler(self, what, name, meth):
        if name:
            names = [name]
        else:
            names = PacketNames.keys()
        if what != True:
            whats = [what]
        else:
            whats = ['current', 'not_current', 'outbound']
        for what in whats:
            callbacks = self.callbacks[what]
            for name in names:
                callbacks[name].remove(meth)

    def normalizeChips(self, game, chips):
        if game.unit in self.factory.chips_values:
            values = self.factory.chips_values[self.factory.chips_values.
                                               index(game.unit):]
        else:
            values = []
        chips_list = PokerChips(values, chips).tochips_list()
        self.log.debug("normalizeChips: %s %s", chips_list, values)
        return chips_list

    def updatePlayerChips(self, game, player):
        packet = PacketPokerPlayerChips(game_id=game.id,
                                        serial=player.serial,
                                        bet=player.bet,
                                        money=player.money)
        return packet

    def updatePotsChips(self, game, side_pots):
        packets = []

        if not side_pots:
            packet = PacketPokerChipsPotReset(game_id=game.id)
            return [packet]

        index = 0
        for (amount, total) in side_pots['pots']:
            chips = amount
            bet = self.normalizeChips(game, chips)
            pot = PacketPokerPotChips(game_id=game.id, index=index, bet=bet)
            packets.append(pot)
            index += 1
        return packets

    def chipsPlayer2Bet(self, game, player, chips):
        packets = []
        packet = PacketPokerChipsPlayer2Bet(game_id=game.id,
                                            serial=player.serial,
                                            chips=self.normalizeChips(
                                                game, chips))
        packets.append(packet)
        packets.append(self.updatePlayerChips(game, player))
        return packets

    def chipsBet2Pot(self, game, player, bet, pot_index):
        packets = []
        if (pot_index == 0 and player.dead > 0 and game.isSecondRound()):
            #
            # The ante or the dead are already in the pot
            #
            bet -= player.dead
        packet = PacketPokerChipsBet2Pot(game_id=game.id,
                                         serial=player.serial,
                                         chips=self.normalizeChips(game, bet),
                                         pot=pot_index)
        packets.append(packet)
        packets.append(self.updatePlayerChips(game, player))
        return packets

    def chipsPot2Player(self, game, player, bet, pot_index, reason):
        packet = PacketPokerChipsPot2Player(game_id=game.id,
                                            serial=player.serial,
                                            chips=self.normalizeChips(
                                                game, bet),
                                            pot=pot_index,
                                            reason=reason)
        return packet

    def handleUserInfo(self, packet):
        self.log.debug("handleUserInfo: %s", packet)
        self.user_info = packet

    def handlePersonalInfo(self, packet):
        self.handleUserInfo(packet)
        self.personal_info = packet

    def handleSerial(self, packet):
        self.user.serial = packet.serial
        self.sendPacket(PacketPokerGetUserInfo(serial=packet.serial))

    def handlePlayerInfo(self, packet):
        skin = self.factory.getSkin()
        #
        # Check that the implementation of the outfit is still valid. If it
        # needs upgrade, send it back to the server.
        #
        (url, outfit) = skin.interpret(packet.url, packet.outfit)
        if url != packet.url or outfit != packet.outfit:
            (url_check,
             outfit_check) = self.factory.getSkin().interpret(url, outfit)
            #
            # Make sure that we wont loop indefinitely because of an instability of the interpret
            # function. In normal operation the url and outfit returned by interpret must be
            # returned as is when fed to interpret again. If the implementation of interpret
            # fails to implement this stability, don't enter a loop because sending PokerPlayerInfo
            # will return us a PokerPlayerInfo for confirmation of the success.
            #
            if url_check != url or outfit_check != outfit:
                self.crit(
                    "PACKET_POKER_PLAYER_INFO: may enter loop packet.url = %s\n"
                    " url = %s\n"
                    " url_check = %s\n"
                    "packet.outfit = %s\n"
                    " outfit = %s\n"
                    " outfit_check = %s", packet.url, url, url_check,
                    packet.outfit, outfit, outfit_check)
            else:
                packet.url = url
                packet.outfit = outfit
                self.sendPacket(packet)
        skin.setUrl(url)
        skin.setOutfit(outfit)

    def logout(self):
        self.sendPacket(PacketLogout())
        self.user.logout()

    def setPlayerDelay(self, game, serial, value):
        player = game.getPlayer(serial)
        if player == None:
            self.log.warn("setPlayerDelay for a non-existing player %d",
                          serial)
        else:
            player.getUserData()['delay'] = seconds() + value

    def getPlayerDelay(self, game, serial):
        if not game: return 0
        player = game.getPlayer(serial)
        if not player: return 0
        user_data = player.getUserData()
        if not user_data or 'delay' not in user_data: return 0
        return user_data['delay']

    def canHandlePacket(self, packet):
        if not self.factory.isAlwaysHandled(packet) and hasattr(
                packet, "game_id") and hasattr(packet, "serial"):
            delay = self.getPlayerDelay(self.factory.packet2game(packet),
                                        packet.serial)
            if delay <= seconds():
                return (True, 0)
            else:
                return (False, delay)
        else:
            return (True, 0)

    def resendPlayerTimeoutWarning(self, game):
        if game.isRunning() and game.getSerialInPosition() == self.getSerial():
            player = game.getPlayer(self.getSerial())
            if player.user_data['timeout']:
                (when, timeout) = player.user_data['timeout']
                now = seconds()
                timeout = timeout - (now - when)
                if timeout > 0:
                    return (PacketPokerTimeoutWarning(game_id=game.id,
                                                      serial=self.getSerial(),
                                                      timeout=int(timeout),
                                                      when=int(now)), )
        return ()

    def setPlayerTimeout(self, game, packet):
        packet.timeout -= int(self.getLag())
        if packet.timeout > 0:
            packet.when = int(seconds())
            player = game.getPlayer(packet.serial)
            player.getUserData()['timeout'] = (packet.when, packet.timeout)
            return True
        else:
            return False

    def unsetPlayerTimeout(self, game, serial):
        player = game.getPlayer(serial)
        player.getUserData()['timeout'] = None

    def postMuck(self, game, want_to_muck):
        if game:
            packet_type = want_to_muck and PacketPokerMuckAccept or PacketPokerMuckDeny
            self.sendPacket(
                packet_type(game_id=game.id, serial=self.getSerial()))

    def _handleConnection(self, packet):
        self.log.debug("_handleConnection: %s", packet)

        if packet.type == PACKET_POKER_TIMEOUT_WARNING:
            packet.timeout -= int(self.getLag())

        elif packet.type == PACKET_POKER_USER_INFO:
            self.handleUserInfo(packet)

        elif packet.type == PACKET_POKER_PERSONAL_INFO:
            self.handlePersonalInfo(packet)

        elif packet.type == PACKET_POKER_TABLE:
            self.setCurrentGameId(packet.id)

        elif packet.type == PACKET_SERIAL:
            self.handleSerial(packet)
            self.sendPacket(PacketPokerGetPlayerInfo())

        elif packet.type == PACKET_POKER_PLAYER_INFO:
            self.handlePlayerInfo(packet)

        game = self.factory.packet2game(packet)

        if game and packet.type == PACKET_POKER_TABLE_DESTROY:
            self.scheduleTableQuit(game)
            game = None

        #
        # It is possible to receive packets related to a game that we know nothing
        # about after quitting a table. When quitting a table the client deletes
        # all information related to the game without waiting confirmation from
        # the server. Therefore the server may keep sending packets related to
        # the game before noticing TABLE_QUIT packet.
        #
        if game:
            if packet.type == PACKET_POKER_SEAT:
                if packet.seat == -1:
                    self.log.inform("This seat is busy")
                else:
                    if game.isTournament():
                        self.sendPacket(
                            PacketPokerSit(serial=self.getSerial(),
                                           game_id=game.id))

            elif packet.type == PACKET_POKER_MUCK_REQUEST:
                if packet.game_id != self.getCurrentGameId():
                    self.postMuck(game, True)

        self.explain.explain(packet)

        if game:
            if packet.type == PACKET_POKER_PLAYER_ARRIVE:
                player = game.getPlayer(packet.serial)
                player.setUserData(DEFAULT_PLAYER_USER_DATA.copy())

        for forward_packet in self.explain.forward_packets:
            self.schedulePacket(forward_packet)
        self.explain.forward_packets = None

    def currentGames(self, exclude=None):
        games = self.factory.getGameIds()
        if exclude:
            games.remove(exclude)
        return PacketPokerCurrentGames(game_ids=games, count=len(games))

    def connectionLost(self, reason):
        if self.factory.crashing:
            self.log.warn("connectionLost: crashing, just return.")
            return
        self.log.warn("connectionLost: noticed, aborting all tables.")
        self.abortAllTables()
        UGAMEClientProtocol.connectionLost(self, reason)

    def abortAllTables(self):
        for game in self.factory.games.getAll():
            self.scheduleTableAbort(game)

    def scheduleTableAbort(self, game):
        game_id = game.id

        def thisgame(packet):
            return hasattr(packet, "game_id") and packet.game_id == game_id

        self.unschedulePackets(thisgame)
        self.discardPackets(game_id)
        self.scheduleTableQuit(game)

    def scheduleTableQuit(self, game):
        self.schedulePacket(PacketPokerBatchMode(game_id=game.id))
        for player in game.playersAll():
            packet = PacketPokerPlayerLeave(game_id=game.id,
                                            serial=player.serial,
                                            seat=player.seat)
            self.schedulePacket(packet)
        self.schedulePacket(PacketPokerStreamMode(game_id=game.id))
        self.schedulePacket(
            PacketPokerTableQuit(game_id=game.id, serial=self.getSerial()))
        self.schedulePacket(self.currentGames(game.id))
        self.publishAllPackets()

    def resendPackets(self, game_id):
        self.publishAllPackets()
        game = self.getGame(game_id)
        self.setCurrentGameId(game.id)
        packets = []
        packet = PacketPokerTable(
            id=game.id,
            name=game.name,
            variant=game.variant,
            seats=game.max_players,
            betting_structure=game.betting_structure,
            players=game.allCount(),
            # observers ?
            # waiting ?
            # player_timeout ?
            # muck_timeout ?
            hands_per_hour=game.stats["hands_per_hour"],
            average_pot=game.stats["average_pot"],
            percent_flop=game.stats["percent_flop"],
            skin=game.level_skin)
        packets.append(PacketPokerBatchMode(game_id=game.id))
        packet.seats_all = game.seats_all
        packets.append(packet)
        packets.append(
            PacketPokerBuyInLimits(game_id=game.id,
                                   min=game.buyIn(),
                                   max=game.maxBuyIn(),
                                   best=game.bestBuyIn(),
                                   rebuy_min=game.minMoney()))
        packets.append(
            PacketPokerDealer(game_id=game.id, dealer=game.dealer_seat))
        for player in game.playersAll():
            packets.append(
                PacketPokerPlayerArrive(
                    game_id=game.id,
                    serial=player.serial,
                    name=player.name,
                    url=player.url,
                    outfit=player.outfit,
                    blind=player.blind,
                    remove_next_turn=player.remove_next_turn,
                    sit_out=player.sit_out,
                    sit_out_next_turn=player.sit_out_next_turn,
                    auto=player.auto,
                    auto_blind_ante=player.auto_blind_ante,
                    wait_for=player.wait_for,
                    seat=player.seat))
            # FIXME: Should a PokerPlayerStats() packet be sent here?
            if player.isSit():
                packets.append(
                    PacketPokerSit(game_id=game.id, serial=player.serial))
            else:
                packets.append(
                    PacketPokerSitOut(game_id=game.id, serial=player.serial))
            packets.append(self.updatePlayerChips(game, player))
        packets.append(PacketPokerSeats(game_id=game.id, seats=game.seats()))
        packets.append(
            PacketPokerStart(game_id=game.id, hand_serial=game.hand_serial))
        if game.isRunning():
            players_with_cards = game.playersNotFold()
        elif game.isGameEndInformationValid():
            players_with_cards = game.playersWinner()
        else:
            players_with_cards = []

        if players_with_cards:
            for player in players_with_cards:
                packet = PacketPokerPlayerCards(game_id=game.id,
                                                serial=player.serial,
                                                cards=player.hand.toRawList())
                packets.append(packet)
            packets.append(
                PacketPokerBoardCards(game_id=game.id,
                                      cards=game.board.tolist(False)))
        if game.isRunning():
            if not self.no_display_packets:
                packets.extend(self.updatePotsChips(game, game.getPots()))
            packets.append(
                PacketPokerPosition(game_id=game.id,
                                    serial=game.getSerialInPosition()))
            if not self.no_display_packets:
                packets.extend(self.updateBetLimit(game))
        else:
            if not self.no_display_packets and game.isGameEndInformationValid(
            ):
                packets.extend(self.packetsShowdown(game))
                packets.append(
                    PacketPokerShowdown(game_id=game.id,
                                        showdown_stack=game.showdown_stack))
        packets.append(PacketPokerStreamMode(game_id=game.id))
        packets.extend(self.resendPlayerTimeoutWarning(game))

        for packet in packets:
            self.schedulePacket(packet)

    def deleteGames(self):
        self.setCurrentGameId(None)
        for game_id in self.factory.getGameIds():
            self.deleteGame(game_id)

    def deleteGame(self, game_id):
        self.log.debug("deletedGame: %d", game_id)
        self.factory.deleteGame(game_id)

        def thisgame(packet):
            return hasattr(packet, "game_id") and packet.game_id == game_id

        self.unschedulePackets(thisgame)
        self.discardPackets(game_id)

    def getGame(self, game_id):
        return self.factory.getGame(game_id)

    def sendPacket(self, packet):
        if packet.type == PACKET_POKER_TABLE_QUIT:
            self.scheduleTableAbort(self.getGame(packet.game_id))
        elif packet.type == PACKET_POKER_SIT_OUT:
            game = self.getGame(packet.game_id)
            if game:
                game.sitOutNextTurn(packet.serial)
            self.schedulePacket(
                PacketPokerSitOutNextTurn(game_id=packet.game_id,
                                          serial=packet.serial))
        elif packet.type == PACKET_POKER_SIT:
            game = self.getGame(packet.game_id)
            if game:
                game.sitRequested(packet.serial)
            self.schedulePacket(
                PacketPokerSitRequest(game_id=packet.game_id,
                                      serial=packet.serial))
        elif packet.type == PACKET_QUIT:
            self.ignoreIncomingData()
            self.abortAllTables()

        UGAMEClientProtocol.sendPacket(self, packet)

    def protocolEstablished(self):
        self.setPingDelay(self.factory.ping_delay)
        poll_frequency = self.factory.settings.headerGet(
            "/settings/@poll_frequency")
        if poll_frequency:
            self._poll_frequency = float(poll_frequency)
        self.user.name = self.factory.name
        self.user.password = self.factory.password
        self._packet2id = self.packet2id
        self._packet2front = self.packet2front
        self.schedulePacket(PacketBootstrap())
        UGAMEClientProtocol.protocolEstablished(self)

    def packet2front(self, packet):
        if (hasattr(packet, "game_id") and self.getGame(packet.game_id)):
            if (packet.type == PACKET_POKER_CHAT):
                return True

            elif packet.type == PACKET_POKER_MESSAGE:
                return True

            elif (packet.type == PACKET_POKER_PLAYER_ARRIVE
                  and packet.serial == self.getSerial()):
                return True

        return False

    def registerLagmax(self, method):
        self.lagmax_callbacks.append(method)

    def unregisterLagmax(self, method):
        self.lagmax_callbacks.remove(method)

    def triggerLagmax(self, packet):
        for method in self.lagmax_callbacks:
            method(packet)

    def packet2id(self, packet):
        self.triggerLagmax(packet)
        if not self.factory.isOutbound(packet) and hasattr(packet, "game_id"):
            return packet.game_id
        elif packet.type == PACKET_POKER_TABLE:
            return packet.id
        else:
            return 0

    def protocolInvalid(self, server, client):
        self.schedulePacket(
            PacketProtocolError(
                message=
                "Upgrade the client from\nhttp://mekensleep.org/\nServer version is %s\nClient version is %s"
                % (server, client)))
        self.publishAllPackets()
        UGAMEClientProtocol.protocolInvalid(self, server, client)

    def publishDelay(self, delay):
        self.log.debug("publishDelay: %f delay", delay)
        publish_time = seconds() + delay
        if publish_time > self.publish_time:
            self.publish_time = publish_time

    def schedulePacket(self, packet):
        if not self.factory.isOutbound(packet) and hasattr(
                packet,
                "game_id") and not self.factory.gameExists(packet.game_id):
            return
        self.publish_packets.append(packet)
        if not self._poll:
            self.publishPacket()
        else:
            self.publishPacketTriggerTimer()

    def unschedulePackets(self, predicate):
        self.publish_packets = filter(lambda packet: not predicate(packet),
                                      self.publish_packets)
        if self._poll:
            self.publishPacketTriggerTimer()

    def publishPackets(self):
        if not self._poll:
            return

        delay = 0.01
        if len(self.publish_packets) > 0:
            #
            # If time has not come, make sure we are called at a later time
            # to reconsider the situation
            #
            wait_for = self.publish_time - seconds()
            if wait_for > 0:
                self.log.debug("publishPacket: %f before next packet is sent",
                               wait_for)
                delay = wait_for
                self.block()
            else:
                self.publishPacket()
                if len(self.publish_packets) > 0:
                    self.block()
                else:
                    self.unblock()
        else:
            self.unblock()

        self.publishPacketTriggerTimer(delay)

    def publishPacketTriggerTimer(self, delay=0.01):
        if not self.publish_timer or not self.publish_timer.active():
            if len(self.publish_packets) > 0:
                self.publish_timer = reactor.callLater(delay,
                                                       self.publishPackets)

    def publishPacket(self):
        packet = self.publish_packets[0]
        if not self.established and not self.factory.isConnectionLess(packet):
            self.log.debug(
                "publishPacket: skip because connection not established")
            return
        self.publish_packets.pop(0)
        what = 'outbound'
        if hasattr(packet, "game_id"):
            if self.factory.isOutbound(packet):
                what = 'outbound'
            else:
                if packet.game_id == self.getCurrentGameId():
                    what = 'current'
                else:
                    what = 'not_current'
        elif (packet.type == PACKET_POKER_TABLE
              or packet.type == PACKET_POKER_TABLE_QUIT):
            what = 'current'
        else:
            what = 'outbound'

        self.log.debug("publishPacket(%d): %s: %s", self.getSerial(), what,
                       packet)
        if packet.type in self.callbacks[what]:
            callbacks = self.callbacks[what][packet.type]
            for callback in callbacks:
                callback(self, packet)

    def publishAllPackets(self):
        while len(self.publish_packets) > 0:
            self.publishPacket()