Source code for pokerthproto.protocol

# -*- coding: utf-8 -*-
"""
The PokerTH protocol consisting of messages and replies with respect to the
current state in the communication.
"""
from __future__ import print_function, absolute_import, division

from twisted.internet import reactor
from twisted.python import log
from twisted.internet.protocol import Protocol, ClientFactory

from . import pokerth_pb2
from . import transport
from . import lobby
from . import game
from . import poker

__author__ = 'Florian Wilhelm'
__copyright__ = 'Florian Wilhelm'


[docs]class PokerTHProtocol(Protocol): _buffer = '' _msgSize = None def _getBufferedData(self, data): self._buffer += data bufData = [] while True: if self._msgSize is None: if len(self._buffer) >= 4: self._msgSize = transport.readSizeBytes(self._buffer[:4]) self._buffer = self._buffer[4:] else: break if len(self._buffer) >= self._msgSize: bufData.append(self._buffer[:self._msgSize]) self._buffer = self._buffer[self._msgSize:] self._msgSize = None else: break return bufData @staticmethod def _getHook(msg_name): hook = msg_name.replace('Message', 'Received') return hook[0].lower() + hook[1:]
[docs] def unhandledMessageReceived(self, msg): log.msg('Received unhandled message {}:\n{}'.format( msg.__class__.__name__, msg))
[docs] def connectionMade(self): log.msg('Connection established.')
[docs] def dataReceived(self, data): for buffer in self._getBufferedData(data): msg = transport.develop(transport.unpack(buffer)) msg_name = msg.__class__.__name__ hook = self._getHook(msg_name) #log.msg("Data: {}".format(buffer.encode('hex'))) log.msg("{} received".format(msg_name)) #log.msg(msg, logLevel=logging.DEBUG) getattr(self, hook)(msg)
def _sendMessage(self, msg): envelope = transport.envelop(msg) self.transport.write(transport.pack(envelope))
[docs] def connectionLost(self, reason): log.msg('Connection lost due to: {}'.format(reason)) # Set default method for all possible message types
for msg_type in pokerth_pb2.PokerTHMessage.PokerTHMessageType.keys(): msg_name = msg_type.split("_", 1)[1] hook = PokerTHProtocol._getHook(msg_name) setattr(PokerTHProtocol, hook, PokerTHProtocol.unhandledMessageReceived)
[docs]class States(object): """ Enum of all client states """ INIT = 0 LOBBY = 1 GAME_JOINED = 2 GAME_STARTED = 3
[docs]class ClientProtocol(PokerTHProtocol): state = States.INIT
[docs] def announceReceived(self, msg): reply = pokerth_pb2.InitMessage() replyVersion = reply.requestedVersion msgVersion = msg.protocolVersion replyVersion.majorVersion = msgVersion.majorVersion replyVersion.minorVersion = msgVersion.minorVersion reply.buildId = 0 # 0 for Linux build reply.nickName = self.factory.nickName if msg.serverType == msg.serverTypeInternetNoAuth: reply.login = reply.unauthenticatedLogin else: raise NotImplementedError("Handle authentication!") assert reply.IsInitialized() self._sendMessage(reply) log.msg("InitMessage sent")
[docs] def initAckReceived(self, msg): self.factory.playerId = msg.yourPlayerId self.factory.sessionId = msg.yourSessionId self.state = States.LOBBY reactor.callLater(1, self.handleInsideLobby, self.factory.lobby)
[docs] def handleInsideLobby(self, lobbyInfo): """ Handle the behavior of our client in the lobby. Overwrite this method. :param lobbyInfo: information about the lobby (:obj:`~.Lobby`) """ raise NotImplementedError("We are in the lobby, implement an action!")
[docs] def sendJoinExistingGame(self, gameId, autoLeave=False): msg = pokerth_pb2.JoinExistingGameMessage() msg.gameId = gameId msg.autoLeave = autoLeave self._sendMessage(msg) log.msg("JoinExistingGameMessage sent")
[docs] def sendJoinNewGame(self, gameInfo, password=None, autoLeave=False): msg = pokerth_pb2.JoinNewGameMessage() msg.gameInfo.MergeFrom(gameInfo.getMsg()) if password is not None: msg.password = password msg.autoLeave = autoLeave self._sendMessage(msg) log.msg("JoinNewGameMessage sent")
[docs] def playerListReceived(self, msg): if msg.playerListNotification == msg.playerListNew: self.factory.lobby.addPlayer(msg.playerId) reply = pokerth_pb2.PlayerInfoRequestMessage() reply.playerId.append(msg.playerId) self._sendMessage(reply) log.msg("InfoRequestMessage sent") else: # msg.playerListLeft self.factory.lobby.delPlayer(msg.playerId)
[docs] def playerInfoReplyReceived(self, msg): self.factory.lobby.setPlayerInfo(msg.playerId, msg.playerInfoData)
[docs] def gameListNewReceived(self, msg): gameInfo = lobby.GameInfo() gameInfo.setInfo(msg.gameInfo) gameInfo.gameId = msg.gameId gameInfo.gameMode = msg.gameMode gameInfo.isPrivae = msg.isPrivate gameInfo.adminPlayerId = msg.adminPlayerId self.factory.lobby.addGameInfo(gameInfo)
[docs] def gameListPlayerJoinedReceived(self, msg): self.factory.lobby.addPlayerToGame(msg.playerId, msg.gameId)
[docs] def gameListPlayerLeftReceived(self, msg): self.factory.lobby.delPlayerFromGame(msg.playerId, msg.gameId)
[docs] def gamePlayerJoinedReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId player = self.factory.lobby.getPlayer(msg.playerId) game.addPlayer(player)
[docs] def gamePlayerLeftReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId player = self.factory.lobby.getPlayer(msg.playerId) # TODO: Handle GamePlayerLeftReason game.delPlayer(player)
[docs] def joinGameAckReceived(self, msg): self.factory.game = game.Game(msg.gameId, self.factory.playerId) if msg.areYouGameAdmin: myGameInfo = self.factory.lobby.getGameInfo(msg.gameId) assert myGameInfo.adminPlayerId == self.factory.playerId myself = self.factory.lobby.getPlayer(self.factory.playerId) self.factory.game.addPlayer(myself) self.state = States.GAME_JOINED
[docs] def gameListUpdateReceived(self, msg): gameInfo = self.factory.lobby.getGameInfo(msg.gameId) gameInfo.gameMode = msg.gameMode
[docs] def sendStartEvent(self, gameId, startEventType=None, fillWithBots=False): msg = pokerth_pb2.StartEventMessage() msg.gameId = gameId if startEventType is not None: msg.startEventType = startEventType msg.fillWithComputerPlayers = fillWithBots self._sendMessage(msg) log.msg("StartEventMessage sent")
[docs] def startEventReceived(self, msg): assert self.factory.game.gameId == msg.gameId gameInfo = self.factory.lobby.getGameInfo(msg.gameId) gameInfo.fillWithComputerPlayers = msg.fillWithComputerPlayers reply = pokerth_pb2.StartEventAckMessage() reply.gameId = msg.gameId self._sendMessage(reply) log.msg("StartEventAckMessage sent") self.state = States.GAME_STARTED
[docs] def chatReceived(self, msg): chatType = enum2str(pokerth_pb2.ChatMessage.ChatType, msg.chatType) chatType = chatType.replace('chatType', '') lobby = self.factory.lobby if msg.gameId != 0: game = self.factory.game assert game.gameId == msg.gameId else: game = None if msg.playerId != 0: player = lobby.getPlayer(msg.playerId) else: player = None self.handleChat(chatType, msg.chatText, lobby, game, player)
[docs] def sendChatRequest(self, text, gameId=None, playerId=None): """ Send a chat message. :param text: your message :param gameId: optional game id :param playerId: optional player id """ msg = pokerth_pb2.ChatRequestMessage() msg.chatText = text if gameId is not None: msg.targetGameId = gameId if playerId is not None: msg.targetPlayerId = playerId self._sendMessage(msg) log.msg("ChatRequestMessage sent")
[docs] def handleChat(self, chatType, text, lobbyInfo, gameInfo=None, playerInfo=None): """ Handle the behavior of our client when a chat message was received. Overwrite this method. :param chatType: "Lobby", "Game", "Bot", "Broadcast" or "Private" :param text: text of the message :param lobbyInfo: lobby information (:obj:`~.Lobby`) :param gameInfo: optional game information (:obj:`~.Game`) :param playerInfo: optional player information (:obj:`~.Player`) """ log_str = '' if gameInfo is not None: log_str += '<{game}> ' gameInfo = lobbyInfo.getGameInfo(gameInfo.gameId).gameName if playerInfo is not None: log_str += '{player} ' playerInfo = playerInfo.name log_str += '[{type}]: {text}' log.msg(log_str.format(game=gameInfo, player=playerInfo, type=chatType, text=text))
[docs] def gameStartInitialReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId game.dealer = game.getPlayer(msg.startDealerPlayerId) for seat, playerId in enumerate(msg.playerSeats): player = game.getPlayer(playerId) player.seat = seat
[docs] def handStartReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId game.startNewHand() game.smallBlind = msg.smallBlind cards = (msg.plainCards.plainCard1, msg.plainCards.plainCard2) game.pocketCards = [poker.intToCard(card) for card in cards] # TODO: Handle Seatstates log.msg("Got cards {}".format(game.pocketCards))
[docs] def playersActionDoneReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId round = msg.gameState if round != game.currRound and round in poker.poker_rounds[:2]: game.addRound(round) game.addAction(msg.playerId, msg.playerAction, msg.totalPlayerBet) game.highestSet = msg.highestSet game.minimumRaise = msg.minimumRaise player = game.getPlayer(msg.playerId) player.money = msg.playerMoney
[docs] def playersTurnReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId # First real turn of a player triggers Preflop if game.currRound in poker.poker_rounds[:2]: game.addRound(poker.Round.PREFLOP) if msg.playerId == self.factory.playerId: self.handleMyTurn(game) else: player = self.factory.lobby.getPlayer(msg.playerId) self.handleOthersTurn(player, game)
[docs] def sendMyAction(self, action, bet, relative=True): """ Send my action during a poker game. :param action: action of :obj:`~.poker.Action` :param bet: bet with respect to the action :param relative: boolean if the bet is relative to the highest set bet """ game = self.factory.game msg = pokerth_pb2.MyActionRequestMessage() msg.myAction = action if not relative: bet -= game.highestSet msg.myRelativeBet = bet msg.gameId = game.gameId msg.handNum = game.handNum msg.gameState = game.currRound self._sendMessage(msg)
[docs] def yourActionRejected(self, msg): raise RuntimeError("Wrong action taken:\n{}".format(msg))
[docs] def handleOthersTurn(self, playerInfo, gameInfo): """ Handle the start of another player's turn. :param playerInfo: player information (:obj:`~.Player`) :param gameInfo: game information (:obj:`~.Game`) """ log.msg("Turn of player {}".format(playerInfo.name))
[docs] def handleMyTurn(self, gameInfo): """ Decide what action to take when it is our turn. :param gameInfo: game information (:obj:`~.Game`) """ raise NotImplementedError("What action should I take?")
[docs] def dealFlopCardsReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId assert game.currRound == poker.Round.PREFLOP card1 = poker.intToCard(msg.flopCard1) card2 = poker.intToCard(msg.flopCard2) card3 = poker.intToCard(msg.flopCard3) game.addRound(poker.Round.FLOP, cards=[card1, card2, card3]) game.highestSet = 0
[docs] def dealTurnCardReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId assert game.currRound == poker.Round.FLOP card = poker.intToCard(msg.turnCard) game.addRound(poker.Round.TURN, cards=[card]) game.highestSet = 0
[docs] def dealRiverCardReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId assert game.currRound == poker.Round.TURN card = poker.intToCard(msg.riverCard) game.addRound(poker.Round.RIVER, cards=[card]) game.highestSet = 0
[docs] def allInShowCardsReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId for playerAllin in msg.playersAllIn: card1 = poker.intToCard(playerAllin.allInCard1) card2 = poker.intToCard(playerAllin.allInCard2) game.addOthersCards(playerAllin.playerId, [card1, card2])
def _recordResult(self, result): game = self.factory.game game.addWin(result.playerId, result.moneyWon) card1 = poker.intToCard(result.resultCard1) card2 = poker.intToCard(result.resultCard2) game.addOthersCards(result.playerId, [card1, card2]) player = game.getPlayer(result.playerId) player.money = result.playerMoney
[docs] def endOfHandShowCardsReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId for result in msg.playerResults: self._recordResult(result) self.handleEndOfHand(game)
[docs] def endOfHandHideCardsReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId game.addWin(msg.playerId, msg.moneyWon) player = game.getPlayer(msg.playerId) player.money = msg.playerMoney self.handleEndOfHand(game)
[docs] def showMyCardsRequestReceived(self, msg): pass # empty message
[docs] def afterHandShowCardsReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId self._recordResult(msg.playerResult) self.handleEndOfHand(game)
[docs] def handleEndOfHand(self, gameInfo): """ Handle the end of a hand :param gameInfo: game information (:obj:`~.Game`) """ log.msg("End of hand {}".format(gameInfo.handNum))
[docs] def endOfGameReceived(self, msg): game = self.factory.game assert game.gameId == msg.gameId winner = game.getPlayer(msg.winnerPlayerId) self.state = States.GAME_JOINED reactor.callLater(1, self.handleEndOfGame, game, winner)
[docs] def handleEndOfGame(self, gameInfo, winner): """ Handle the end of a game The end of a game brings you back to the lobby :param gameInfo: game information (:obj:`~.Game`) :param winner: winner of the game (:obj:`~.Player`) """ log.msg("End of game {}\n" "Winner: {}".format(gameInfo.handNum, winner.name))
[docs]class ClientProtocolFactory(ClientFactory): protocol = ClientProtocol def __init__(self, nickName): self.nickName = nickName self.playerId = None self.SessionID = None self.game = None self.lobby = lobby.Lobby()
[docs] def clientConnectionLost(self, connector, reason): pass #connector.connect()
[docs] def clientConnectionFailed(self, connector, reason): reactor.stop()
[docs]def enum2str(enumType, enum): """ Translates a pokerth_pb2 enum type to a string. :param enumType: enum type class :param enum: the enum element of the type :return: identifier string of enum """ return [k for k, v in enumType.items() if v == enum][0]