def callback(): if self.isDestroyed: avt_mb.enterRoomFailed(const.ENTER_FAILED_ROOM_DESTROYED) return for i, p in enumerate(self.players_list): if p and p.mb and p.mb.userId == avt_mb.userId: p.mb = avt_mb avt_mb.enterRoomSucceed(self, i) return DEBUG_MSG("{} userId:{} reqEnterRoom".format(self.prefixLogStr, avt_mb.userId)) idx = self.getSit() # AA支付的情况下, 可能多个玩家同时走到这里 if idx is None: avt_mb.enterRoomFailed(const.ENTER_FAILED_ROOM_FULL) return n_player = PlayerProxy(avt_mb, self, idx) self.players_dict[avt_mb.userId] = n_player self.players_list[idx] = n_player # 茶楼座位信息变更 if self.club_table: self.club_table.seatInfoChanged() # 确认准备,不需要手动准备 if self.hand_prepare == const.AUTO_PREPARE: self.prepare(avt_mb) if not first: self.broadcastEnterRoom(idx) else: avt_mb.createRoomSucceed(self) self.ready_after_prepare()
def reqEnterRoom(self, avt_mb, first=False): """ defined. 客户端调用该接口请求进入房间/桌子 """ if self.isFull: avt_mb.enterRoomFailed(const.ENTER_FAILED_ROOM_FULL) return # 代开房 if first and self.is_agent == 1: self.agent = PlayerProxy(avt_mb, self, -1) self.players_dict[avt_mb.userId] = self.agent avt_mb.enterRoomSucceed(self, -1) return for i, p in enumerate(self.players_list): if p and p.mb and p.mb.userId == avt_mb.userId: p.mb = avt_mb avt_mb.enterRoomSucceed(self, i) return DEBUG_MSG("Room.reqEnterRoom: %s" % (self.roomID)) idx = self.getSit() n_player = PlayerProxy(avt_mb, self, idx) self.players_dict[avt_mb.userId] = n_player self.players_list[idx] = n_player # 确认准备 if idx not in self.confirm_next_idx: self.confirm_next_idx.append(idx) if not first: self.broadcastEnterRoom(idx) self.check_same_ip() if self.isFull: if self.is_agent == 1 and self.agent and self.agent.mb: try: self.agent.mb.quitRoomSucceed() leave_tips = "您代开的房间已经开始游戏, 您已被请离.\n房间号【{}】".format( self.roomID) self.agent.mb.showTip(leave_tips) except: pass self.startGame()
def callback(content): content = content.decode() try: data = json.loads(content) card_cost, diamond_cost = switch.calc_cost( self.game_round, self.player_num, self.pay_mode) if diamond_cost > data["diamond"]: avt_mb.enterRoomFailed( const.ENTER_FAILED_ROOM_DIAMOND_NOT_ENOUGH) return # 代开房 if first and self.is_agent == 1: self.agent = PlayerProxy(avt_mb, self, -1) self.players_dict[avt_mb.userId] = self.agent avt_mb.enterRoomSucceed(self, -1) return for i, p in enumerate(self.players_list): if p and p.mb and p.mb.userId == avt_mb.userId: p.mb = avt_mb avt_mb.enterRoomSucceed(self, i) return DEBUG_MSG("Room.reqEnterRoom: %s" % (self.roomID)) idx = self.getSit() n_player = PlayerProxy(avt_mb, self, idx) self.players_dict[avt_mb.userId] = n_player self.players_list[idx] = n_player # 确认准备 # if idx not in self.confirm_next_idx: # self.confirm_next_idx.append(idx) if not first: self.broadcastEnterRoom(idx) self.check_same_ip() if self.isFull: self.origin_players_list = self.players_list[:] except: DEBUG_MSG("enterRoomFailed callback error:{}".format(content))
def reqEnterRoom(self, avt_mb, first=False): """ defined. 客户端调用该接口请求进入房间/桌子 """ if self.isFull: avt_mb.enterRoomFailed(const.ENTER_FAILED_ROOM_FULL) return # 代开房 if first and self.is_agent == 1: self.agent = PlayerProxy(avt_mb, self, -1) self.players_dict[avt_mb.userId] = self.agent avt_mb.enterRoomSucceed(self, -1) return for i, p in enumerate(self.players_list): if p and p.mb and p.mb.userId == avt_mb.userId: p.mb = avt_mb avt_mb.enterRoomSucceed(self, i) return DEBUG_MSG("Room.reqEnterRoom: %s" % (self.roomID)) idx = self.getSit() n_player = PlayerProxy(avt_mb, self, idx) self.players_dict[avt_mb.userId] = n_player self.players_list[idx] = n_player # 确认准备 # if idx not in self.confirm_next_idx: # self.confirm_next_idx.append(idx) if not first: self.broadcastEnterRoom(idx) self.check_same_ip() if self.isFull: self.origin_players_list = self.players_list[:]
class GameRoom(KBEngine.Base, GameObject, iRoomRules): """ 这是一个游戏房间/桌子类 该类处理维护一个房间中的实际游戏, 例如:斗地主、麻将等 该房间中记录了房间里所有玩家的mailbox,通过mailbox我们可以将信息推送到他们的客户端。 """ def __init__(self): GameObject.__init__(self) iRoomRules.__init__(self) self.owner_uid = 0 self.agent = None self.roomID = None # 状态0:未开始游戏, 1:某一局游戏中 self.state = 0 # 存放该房间内的玩家mailbox self.players_dict = {} self.players_list = [None] * const.ROOM_PLAYER_NUMBER self.origin_players_list = [None] * const.ROOM_PLAYER_NUMBER # 控牌玩家 self.controller_idx = -1 # 控牌玩家出的牌 self.controller_discard_list = [] # 等待出牌玩家 self.wait_idx = -1 # 桌牌 self.deskPokerList = [[] for i in range(const.ROOM_PLAYER_NUMBER)] #玩家出完牌的顺序 self.show_empty_list = [] # 当前局数 self.current_round = 0 # 翻牌 self.key_card = 0 # 交换顺序 self.swap_list = [i for i in range(const.ROOM_PLAYER_NUMBER)] # 房间开局所有操作的记录(idx, op, cards, insteadCards) self.op_record = [] # 房间基础轮询timer self._poll_timer = None # 玩家操作限时timer self._op_timer = None # 一局游戏结束后, 玩家准备界面等待玩家确认timer self._next_game_timer = None # 确认继续的玩家 self.confirm_next_idx = [] # 解散房间操作的发起者 self.dismiss_room_from = -1 # 解散房间操作开始的时间戳 self.dismiss_room_ts = 0 # 解散房间操作投票状态 self.dismiss_room_state_list = [0, 0, 0, 0] self.dismiss_timer = None self.roomOpenTime = time.time() @property def isFull(self): count = sum([1 for i in self.players_list if i is not None]) return count == const.ROOM_PLAYER_NUMBER @property def isEmpty(self): count = sum([1 for i in self.players_list if i is not None]) return count == 0 and self.agent is None def getNextWaitIdx(self, cur_wait_idx): next_wait_idx = cur_wait_idx for i in range(1, const.ROOM_PLAYER_NUMBER + 1): next_wait_idx = (next_wait_idx + 1) % const.ROOM_PLAYER_NUMBER if self.controller_idx == next_wait_idx and len( self.players_list[self.controller_idx].cards) <= 0: return (next_wait_idx + 2) % 4, True if len(self.players_list[next_wait_idx].cards) > 0: return next_wait_idx, False return next_wait_idx, False def getSit(self): for i, j in enumerate(self.players_list): if j is None: return i def _reset(self): self.state = 0 self.agent = None self.players_list = [None] * const.ROOM_PLAYER_NUMBER self.controller_idx = -1 self.wait_idx = -1 self.swap_list = [i for i in range(const.ROOM_PLAYER_NUMBER)] self.controller_discard_list = [] self.deskPokerList = [[] for i in range(const.ROOM_PLAYER_NUMBER)] self._poll_timer = None self._op_timer = None self._next_game_timer = None self.current_round = 0 self.confirm_next_idx = [] self.dismiss_timer = None self.dismiss_room_ts = 0 self.dismiss_room_state_list = [0, 0, 0, 0] self.key_card = 0 KBEngine.globalData["GameWorld"].delRoom(self) def onTimer(self, id, userArg): DEBUG_MSG("Room.onTimer called: id %i, userArg: %i" % (id, userArg)) if userArg == const.TIMER_TYPE_DISMISS_WAIT: self.delTimer(id) self.dropRoom() # if userArg == const.TIMER_TYPE_ROOM_EXIST: # self.game_round = self.current_round # self.delTimer(id) def reqEnterRoom(self, avt_mb, first=False): """ defined. 客户端调用该接口请求进入房间/桌子 """ if self.isFull: avt_mb.enterRoomFailed(const.ENTER_FAILED_ROOM_FULL) return # 代开房 if first and self.is_agent == 1: self.agent = PlayerProxy(avt_mb, self, -1) self.players_dict[avt_mb.userId] = self.agent avt_mb.enterRoomSucceed(self, -1) return for i, p in enumerate(self.players_list): if p and p.mb and p.mb.userId == avt_mb.userId: p.mb = avt_mb avt_mb.enterRoomSucceed(self, i) return DEBUG_MSG("Room.reqEnterRoom: %s" % (self.roomID)) idx = self.getSit() n_player = PlayerProxy(avt_mb, self, idx) self.players_dict[avt_mb.userId] = n_player self.players_list[idx] = n_player # 确认准备 # if idx not in self.confirm_next_idx: # self.confirm_next_idx.append(idx) if not first: self.broadcastEnterRoom(idx) self.check_same_ip() if self.isFull: self.origin_players_list = self.players_list[:] # if self.isFull: # if self.is_agent == 1 and self.agent and self.agent.mb: # try: # self.agent.mb.quitRoomSucceed() # leave_tips = "您代开的房间已经开始游戏, 您已被请离.\n房间号【{}】".format(self.roomID) # self.agent.mb.showTip(leave_tips) # except: # pass # self.startGame() # self.addTimer(const.START_GAME_WAIT_TIME, 0, const.TIMER_TYPE_START_GAME) def reqReconnect(self, avt_mb): DEBUG_MSG("GameRoom reqReconnect userid = {}".format(avt_mb.userId)) if avt_mb.userId not in self.players_dict.keys(): return DEBUG_MSG("GameRoom reqReconnect player is in room".format( avt_mb.userId)) # 如果进来房间后牌局已经开始, 就要传所有信息 # 如果还没开始, 跟加入房间没有区别 player = self.players_dict[avt_mb.userId] if self.agent and player.userId == self.agent.userId: self.agent.mb = avt_mb self.agent.online = 1 avt_mb.enterRoomSucceed(self, -1) return player.mb = avt_mb player.online = 1 if self.state == 1 or 0 < self.current_round <= self.game_round: if self.state == 0: # 重连回来直接准备 self.roundEndCallback(avt_mb) rec_room_info = self.get_reconnect_room_dict(player.mb.userId) player.mb.handle_reconnect(rec_room_info) else: sit = 0 for idx, p in enumerate(self.players_list): if p and p.mb: if p.mb.userId == avt_mb.userId: sit = idx break avt_mb.enterRoomSucceed(self, sit) # self.check_same_ip() def reqLeaveRoom(self, player): """ defined. 客户端调用该接口请求离开房间/桌子 """ DEBUG_MSG("Room.reqLeaveRoom:{0}, is_agent={1}".format( self.roomID, self.is_agent)) if player.userId in self.players_dict.keys(): n_player = self.players_dict[player.userId] idx = n_player.idx if idx == -1 and self.is_agent == 1: self.dropRoom() elif idx == 0 and self.is_agent == 0: # 房主离开房间, 则解散房间 self.dropRoom() else: n_player.mb.quitRoomSucceed([ p.get_final_client_dict() for p in self.players_list if p is not None ]) self.players_list[idx] = None del self.players_dict[player.userId] if idx in self.confirm_next_idx: self.confirm_next_idx.remove(idx) # 通知其它玩家该玩家退出房间 for i, p in enumerate(self.players_list): if i != idx and p and p.mb: p.mb.othersQuitRoom(idx) if self.agent and self.agent.mb: self.agent.mb.othersQuitRoom(idx) if self.isEmpty: self._reset() def dropRoom(self): for p in self.players_list: if p and p.mb: try: p.mb.quitRoomSucceed([ p.get_final_client_dict() for p in self.players_list if p is not None ]) except: pass if self.agent and self.agent.mb: try: self.agent.mb.quitRoomSucceed([ p.get_final_client_dict() for p in self.players_list if p is not None ]) except: pass self._reset() def startGame(self): """ 开始游戏 """ self.op_record = [] self.controller_discard_list = [] self.deskPokerList = [[] for i in range(const.ROOM_PLAYER_NUMBER)] self.state = 1 self.current_round += 1 self.key_card = 0 #扣费后开始游戏的回调 def callback(content): content = content.decode() # try: if content[0] != '{': DEBUG_MSG(content) self.dropRoom() return for p in self.players_list: p.reset() self.initCards() self.deal() if self.changeSeat: if True or self.current_round == 1 or len( self.show_empty_list) == 2: # 根据需求每次重新换座位 self.random_key_card() first_key_idx, second_key_idx = self.getKeyCardSeat() self.swap_list = self.swapSeat(first_key_idx, second_key_idx) self.controller_idx = self.swap_list.index(first_key_idx) self.wait_idx = self.controller_idx else: self.key_card = 0 self.controller_idx = self.show_empty_list[0] self.wait_idx = self.controller_idx else: if len(self.show_empty_list) > 0: self.controller_idx = self.show_empty_list[0] self.wait_idx = self.controller_idx else: self.controller_idx = 0 self.wait_idx = self.controller_idx self.show_empty_list = [] #DEBUG_MSG for i, p in enumerate(self.players_list): DEBUG_MSG("player [{0}] cards: {1}".format(p.idx, p.cards)) for p in self.players_list: if p and p.mb: p.start() p.mb.startGame(self.controller_idx, self.key_card, p.cards, self.swap_list) # except: # DEBUG_MSG("consume failed!") if self.current_round == 1 and self.is_agent == 0: # 仅仅在第1局扣房卡, 不然每局都会扣房卡 card_cost, diamond_cost = switch.calc_cost(self.game_round, self.room_mode) if switch.DEBUG_BASE: callback('{"card":99, "diamond":999}'.encode()) else: utility.update_card_diamond( self.players_list[0].mb.accountName, -card_cost, -diamond_cost, callback, "BaiBianShuangKou RoomID:{}".format(self.roomID)) return DEBUG_MSG("start Game: Room{0} - Round{1}".format( self.roomID, str(self.current_round) + '/' + str(self.game_round))) callback('{"card":99, "diamond":999}'.encode()) def swapSeat(self, first_key_idx, second_key_idx): swap_seat_list = [i for i in range(len(self.players_list))] if first_key_idx == second_key_idx or abs(first_key_idx - second_key_idx) == 2: return swap_seat_list tmpList = self.players_list self.players_list = [None] * const.ROOM_PLAYER_NUMBER if first_key_idx == 0 or second_key_idx == 0: others = [] for i in range(len(swap_seat_list)): if swap_seat_list[i] != first_key_idx and swap_seat_list[ i] != second_key_idx: others.append(swap_seat_list[i]) else: others = [first_key_idx, second_key_idx] others = sorted(others) if others[0] - 1 > 0: # 1 2换 self.players_list[0] = tmpList[0] self.players_list[1] = tmpList[2] self.players_list[2] = tmpList[1] self.players_list[3] = tmpList[3] swap_seat_list[1], swap_seat_list[2] = swap_seat_list[ 2], swap_seat_list[1] else: # 2 3换 self.players_list[0] = tmpList[0] self.players_list[1] = tmpList[1] self.players_list[2] = tmpList[3] self.players_list[3] = tmpList[2] swap_seat_list[2], swap_seat_list[3] = swap_seat_list[ 3], swap_seat_list[2] for i in range(len(self.players_list)): self.players_list[i].idx = i return swap_seat_list def getKeyCardSeat(self): DEBUG_MSG("key card:{}".format(self.key_card)) keyIdxList = [] for i in range(len(self.players_list)): for card in self.players_list[i].cards: if card == self.key_card: DEBUG_MSG(i) keyIdxList.append(i) DEBUG_MSG(str(keyIdxList)) return keyIdxList[0], keyIdxList[1] def swapTileToTop(self, tile): pass # if tile in self.tiles: # tileIdx = self.tiles.index(tile) # self.tiles[0], self.tiles[tileIdx] = self.tiles[tileIdx], self.tiles[0] def reqStopGame(self, player): """ 客户端调用该接口请求停止游戏 """ DEBUG_MSG("Room.reqLeaveRoom: %s" % (self.roomID)) self.state = 0 # self.delTimer(self._poll_timer) # self._poll_timer = None def doOperation(self, avt_mb, aid, cards): if self.dismiss_room_ts != 0 and int(time.time() - self.dismiss_room_ts ) < const.DISMISS_ROOM_WAIT_TIME: # 说明在准备解散投票中,不能进行其他操作 return idx = -1 for i, p in enumerate(self.players_list): if p and p.mb == avt_mb: idx = i if idx != self.wait_idx: avt_mb.doOperationFailed(const.OP_ERROR_NOT_CURRENT) return def confirmOperation(self, avt_mb, aid, cards): DEBUG_MSG("confirmOperatin,aid:{0},cards:{1}.".format(aid, cards)) """ 被轮询的玩家确认了某个操作 """ if self.dismiss_room_ts != 0 and int(time.time() - self.dismiss_room_ts ) < const.DISMISS_ROOM_WAIT_TIME: # 说明在准备解散投票中,不能进行其他操作 return idx = -1 for i, p in enumerate(self.players_list): if p and p.mb == avt_mb: idx = i if idx != self.wait_idx: avt_mb.doOperationFailed(const.OP_ERROR_NOT_CURRENT) return p = self.players_list[idx] if aid == const.OP_PASS and self.controller_idx != self.wait_idx: p.turnPass() self.wait_idx, isChangeController = self.getNextWaitIdx( self.wait_idx) DEBUG_MSG( "confirmOperation,wait_idx:{0},isChangeController:{1}".format( self.wait_idx, isChangeController)) if isChangeController: self.deskPokerList[self.controller_idx] = [] self.controller_idx = self.wait_idx for i, p in enumerate(self.players_list): if p.mb is not None: p.mb.notifyChangeController(self.wait_idx) self.deskPokerList[self.wait_idx] = [] self.waitForOperation(self.wait_idx) elif aid == const.OP_DISCARD: isCanPlay, insteadCard = self.can_play_cards(p.cards, cards, idx) if isCanPlay: p.discard(cards, insteadCard) self.deskPokerList[idx] = insteadCard if self.checkWin(p): self.winGame() else: #玩家出完牌 游戏没有结束 显示对家手上牌 if len(p.cards) <= 0: cooperation_idx = (idx + 2) % 4 p.mb.notifyCooperationCards( cooperation_idx, self.players_list[cooperation_idx].cards) self.wait_idx, isChangeController = self.getNextWaitIdx( self.wait_idx) self.deskPokerList[(self.controller_idx + 1) % 4] = [] self.deskPokerList[self.wait_idx] = [] self.waitForOperation(self.wait_idx) else: avt_mb.doOperationFailed(const.OP_ERROR_ILLEGAL) else: avt_mb.doOperationFailed(const.OP_ERROR_ILLEGAL) def waitForOperation(self, wait_idx): for i, p in enumerate(self.players_list): if p and p.mb is not None: p.mb.waitForOperation(wait_idx, const.OP_DISCARD, []) def checkWin(self, p): if len(p.cards) > 0 or p.idx in self.show_empty_list: return False self.show_empty_list.append(p.idx) if len(self.show_empty_list) == 3 or (len(self.show_empty_list) == 2 and self.show_empty_list[0] % 2 == self.show_empty_list[1] % 2): return True return False def showHand(self, avt_mb): idx = -1 for i, p in enumerate(self.players_list): if p and p.mb == avt_mb: idx = i single = True if self.score_mode == 1 else False pokerReplace = True if self.room_mode == 1 else False if len(self.players_list[idx].op) <= 0 and utility.calcCutMaxContribute( self.players_list[idx].cards, single, pokerReplace, 11) >= 0: info = dict() info['win_idx_list'] = self.show_empty_list calInfoList, winType = self.calShowHandScore(idx) info['cal_score_list'] = calInfoList info['winType'] = winType info['leftCards'] = [ p.cards for i, p in enumerate(self.players_list) ] if self.current_round < self.game_round: self.broadcastRoundEnd(info) else: self.endAll(info) def calShowHandScore(self, pushIdx): single = True if self.score_mode == 1 else False pokerReplace = True if self.room_mode == 1 else False info_list = [] for i, p in enumerate(self.players_list): if i == pushIdx: maxContribute = utility.calcCutMaxContribute( p.cards, single, pokerReplace, 11) info_list.append([maxContribute]) else: maxContribute, maxLine = utility.calcMaxContribute( p.cards, single, pokerReplace, 11, p.bombNumDict) info_list.append([maxContribute]) DEBUG_MSG( "calShowHandScore calcMaxContribute{0},{1},{2},{3},{4},{5}". format(p.cards, single, pokerReplace, self.best_phase, p.bombNumDict, maxContribute)) def getContribution(info_list, idx): contribution = 0 for i in range(len(info_list)): if i == idx: contribution += 3 * info_list[i][0] else: contribution -= info_list[i][0] return int(contribution) def getGapPrice(info_list, curIdx, cop_idx): val = round(((self.players_list[curIdx].original_line_score - info_list[curIdx][0]) - (self.players_list[cop_idx].original_line_score - info_list[cop_idx][0])) * 2) return int(val) calList = [None] * len(self.players_list) for i, p in enumerate(self.players_list): p.addscore(0 + getContribution(info_list, i) + getGapPrice(info_list, i, (i + 2) % 4)) calList[i] = [ 0, 0, int(getContribution(info_list, i)), getGapPrice(info_list, i, (i + 2) % 4) ] return calList, const.WIN_SHOW_HAND def calWinScore(self, show_empty_list): #平扣 1分 单扣 2分 双扣 3分 single = True if self.score_mode == 1 else False pokerReplace = True if self.room_mode == 1 else False info_list = [] for p in self.players_list: maxContribute, maxLine = utility.calcMaxContribute( p.cards, single, pokerReplace, 11, p.bombNumDict) DEBUG_MSG( "calWinScore calcMaxContribute{0},{1},{2},{3},{4},{5},{6}". format(p.cards, single, pokerReplace, self.best_phase, p.bombNumDict, maxContribute, maxLine)) info_list.append([maxContribute, maxLine]) def getContribution(info_list, idx): contribution = 0 for i in range(len(info_list)): if i == idx: contribution += 3 * info_list[i][0] DEBUG_MSG("+3*{}".format(info_list[i][0])) else: contribution -= info_list[i][0] DEBUG_MSG("-{}".format(info_list[i][0])) DEBUG_MSG("getContribution:{0}-{1}".format(idx, contribution)) return int(contribution) def getGapPrice(info_list, curIdx, cop_idx): val = round(((self.players_list[curIdx].original_line_score - info_list[curIdx][0]) - (self.players_list[cop_idx].original_line_score - info_list[cop_idx][0])) * 2) return int(val) if len(show_empty_list) == 2: #双扣 baseScore = 3 allMaxLine = info_list[show_empty_list[0]][1] if info_list[ show_empty_list[0]][1] > info_list[ show_empty_list[1]][1] else info_list[ show_empty_list[1]][1] allMaxLine = self.best_phase if allMaxLine > self.best_phase else allMaxLine allMaxLine_mul = 2**(allMaxLine - 4 if allMaxLine >= 4 else 0) DEBUG_MSG("doubleWin,allMaxLine:{0},allMaxLine_mul:{1}".format( allMaxLine, allMaxLine_mul)) calList = [None] * len(self.players_list) for i, p in enumerate(self.players_list): if i in show_empty_list: cop_idx = show_empty_list[0] if show_empty_list.index( i) == 1 else show_empty_list[1] p.addscore(baseScore * allMaxLine_mul + getContribution(info_list, i) + getGapPrice(info_list, i, cop_idx)) calList[i] = [ int(baseScore), int(allMaxLine), int(getContribution(info_list, i)), getGapPrice(info_list, i, cop_idx) ] else: cop_idx = [ idx for idx in range(len(self.players_list)) if idx not in show_empty_list and idx != i ][0] p.addscore(-baseScore * allMaxLine_mul + getContribution(info_list, i) + getGapPrice(info_list, i, cop_idx)) calList[i] = [ int(-baseScore), int(allMaxLine), int(getContribution(info_list, i)), getGapPrice(info_list, i, cop_idx) ] return calList, const.WIN_DOUBLE_BUCKLE elif len(show_empty_list) == 3 and (show_empty_list[0] + 2) % 4 == show_empty_list[2]: #单扣 baseScore = 2 allMaxLine = info_list[show_empty_list[0]][1] if info_list[ show_empty_list[0]][1] > info_list[ show_empty_list[2]][1] else info_list[ show_empty_list[2]][1] allMaxLine = self.best_phase if allMaxLine > self.best_phase else allMaxLine allMaxLine_mul = 2**(allMaxLine - 4 if allMaxLine >= 4 else 0) DEBUG_MSG("singleWin,allMaxLine:{0},allMaxLine_mul:{1}".format( allMaxLine, allMaxLine_mul)) calList = [None] * len(self.players_list) for i, p in enumerate(self.players_list): if i == show_empty_list[0]: cop_idx = show_empty_list[2] p.addscore(baseScore * allMaxLine_mul + getContribution(info_list, i) + getGapPrice(info_list, i, cop_idx)) calList[i] = [ int(baseScore), int(allMaxLine), int(getContribution(info_list, i)), getGapPrice(info_list, i, cop_idx) ] elif i == show_empty_list[1]: cop_idx = [ idx for idx in range(len(self.players_list)) if idx not in show_empty_list ][0] p.addscore(-baseScore * allMaxLine_mul + getContribution(info_list, i) + getGapPrice(info_list, i, cop_idx)) calList[i] = [ int(-baseScore), int(allMaxLine), int(getContribution(info_list, i)), getGapPrice(info_list, i, cop_idx) ] elif i == show_empty_list[2]: cop_idx = show_empty_list[0] p.addscore(baseScore * allMaxLine_mul + getContribution(info_list, i) + getGapPrice(info_list, i, cop_idx)) calList[i] = [ int(baseScore), int(allMaxLine), int(getContribution(info_list, i)), getGapPrice(info_list, i, cop_idx) ] else: cop_idx = show_empty_list[1] p.addscore(-baseScore * allMaxLine_mul + getContribution(info_list, i) + getGapPrice(info_list, i, cop_idx)) calList[i] = [ int(-baseScore), int(allMaxLine), int(getContribution(info_list, i)), getGapPrice(info_list, i, cop_idx) ] return calList, const.WIN_SINGLE_BUCKLE elif len(show_empty_list) == 3 and (show_empty_list[1] + 2) % 4 == show_empty_list[2]: #平扣 baseScore = 1 first_cop = [ idx for idx in range(len(self.players_list)) if idx not in show_empty_list ][0] allMaxLine = info_list[ show_empty_list[0]][1] if info_list[show_empty_list[0]][ 1] > info_list[first_cop][1] else info_list[first_cop][1] allMaxLine = self.best_phase if allMaxLine > self.best_phase else allMaxLine allMaxLine_mul = 2**(allMaxLine - 4 if allMaxLine >= 4 else 0) DEBUG_MSG("peaceWin,allMaxLine:{0},allMaxLine_mul:{1}".format( allMaxLine, allMaxLine_mul)) calList = [None] * len(self.players_list) for i, p in enumerate(self.players_list): if i == show_empty_list[0]: cop_idx = first_cop p.addscore(baseScore * allMaxLine_mul + getContribution(info_list, i) + getGapPrice(info_list, i, cop_idx)) calList[i] = [ int(baseScore), int(allMaxLine), int(getContribution(info_list, i)), getGapPrice(info_list, i, cop_idx) ] elif i == show_empty_list[1]: cop_idx = show_empty_list[2] p.addscore(-baseScore * allMaxLine_mul + getContribution(info_list, i) + getGapPrice(info_list, i, cop_idx)) calList[i] = [ int(-baseScore), int(allMaxLine), int(getContribution(info_list, i)), getGapPrice(info_list, i, cop_idx) ] elif i == show_empty_list[2]: cop_idx = show_empty_list[1] p.addscore(-baseScore * allMaxLine_mul + getContribution(info_list, i) + getGapPrice(info_list, i, cop_idx)) calList[i] = [ int(-baseScore), int(allMaxLine), int(getContribution(info_list, i)), getGapPrice(info_list, i, cop_idx) ] else: cop_idx = show_empty_list[0] p.addscore(baseScore * allMaxLine_mul + getContribution(info_list, i) + getGapPrice(info_list, i, cop_idx)) calList[i] = [ int(baseScore), int(allMaxLine), int(getContribution(info_list, i)), getGapPrice(info_list, i, cop_idx) ] return calList, const.WIN_DRAW_BUCKLE def winGame(self): DEBUG_MSG("winGame") info = dict() info['win_idx_list'] = self.show_empty_list calInfoList, winType = self.calWinScore(self.show_empty_list) info['cal_score_list'] = calInfoList info['winType'] = winType info['leftCards'] = [p.cards for i, p in enumerate(self.players_list)] if self.current_round < self.game_round: self.broadcastRoundEnd(info) else: self.endAll(info) def endAll(self, info): DEBUG_MSG("endAll") self.record_round_result() info['player_info_list'] = [ p.get_round_client_dict() for p in self.players_list if p is not None ] player_info_list = [ p.get_final_client_dict() for p in self.players_list if p is not None ] for p in self.players_list: if p and p.mb: p.mb.finalResult(player_info_list, info) self._reset() def sendEmotion(self, avt_mb, eid): """ 发表情 """ # DEBUG_MSG("Room.Player[%s] sendEmotion: %s" % (self.roomID, eid)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb == p.mb: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvEmotion(idx, eid) for i, p in enumerate(self.players_list): if p and i != idx: p.mb.recvEmotion(idx, eid) def sendMsg(self, avt_mb, mid): """ 发消息 """ # DEBUG_MSG("Room.Player[%s] sendMsg: %s" % (self.roomID, mid)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb == p.mb: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvMsg(idx, mid) for i, p in enumerate(self.players_list): if p and i != idx: p.mb.recvMsg(idx, mid) def sendVoice(self, avt_mb, url): # DEBUG_MSG("Room.Player[%s] sendVoice" % (self.roomID)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb.userId == p.userId: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvVoice(idx, url) for i, p in enumerate(self.players_list): if p and p.mb: p.mb.recvVoice(idx, url) def sendAppVoice(self, avt_mb, url, time): # DEBUG_MSG("Room.Player[%s] sendVoice" % (self.roomID)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb.userId == p.userId: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvAppVoice(idx, url, time) for i, p in enumerate(self.players_list): if p and p.mb: p.mb.recvAppVoice(idx, url, time) def broadcastOperation(self, idx, aid, real_card_list=[], instead_card_list=[]): """ 将操作广播给所有人, 包括当前操作的玩家 :param idx: 当前操作玩家的座位号 :param aid: 操作id :param cards: 出牌的cards """ for i, p in enumerate(self.players_list): if p is not None: p.mb.postOperation(idx, aid, real_card_list, instead_card_list) def broadcastOperation2(self, idx, aid, real_card_list=[], instead_card_list=[]): """ 将操作广播除了自己之外的其他人 """ for i, p in enumerate(self.players_list): if p and i != idx: p.mb.postOperation(idx, aid, real_card_list, instead_card_list) def broadcastMultiOperation(self, idx_list, aid_list, card_list=None): for i, p in enumerate(self.players_list): if p is not None: p.mb.postMultiOperation(idx_list, aid_list, card_list) def broadcastRoundEnd(self, info): # 广播胡牌或者流局导致的每轮结束信息, 包括算的扎码和当前轮的统计数据 # 先记录玩家当局战绩, 会累计总得分 self.record_round_result() self.state = 0 info['player_info_list'] = [ p.get_round_client_dict() for p in self.players_list if p is not None ] DEBUG_MSG("&" * 30) DEBUG_MSG("RoundEnd info = {}".format(info)) self.confirm_next_idx = [] for p in self.players_list: if p: p.mb.roundResult(info) # self._next_game_timer = self.addTimer(const.NEXT_GAME_WAIT_TIME, 0, const.TIMER_TYPE_NEXT_GAME) def get_init_client_dict(self): agent_d = { 'nickname': "", 'userId': 0, 'head_icon': "", 'ip': '0.0.0.0', 'sex': 1, 'idx': -1, 'uuid': 0, 'online': 1, } if self.is_agent and self.agent: d = self.agent.get_init_client_dict() agent_d.update(d) return { 'roomID': self.roomID, 'ownerId': self.owner_uid, 'isAgent': self.is_agent, 'agentInfo': agent_d, 'curRound': self.current_round, 'maxRound': self.game_round, 'room_mode': self.room_mode, 'insert_card': self.insert_card, 'deal_mode': self.deal_mode, 'score_mode': self.score_mode, 'changeSeat': self.changeSeat, 'best_phase': self.best_phase, 'player_base_info_list': [ p.get_init_client_dict() for p in self.players_list if p is not None ], 'player_state_list': [ 1 if i in self.confirm_next_idx else 0 for i in range(const.ROOM_PLAYER_NUMBER) ], } def get_reconnect_room_dict(self, userId): dismiss_left_time = int(time.time() - self.dismiss_room_ts) if self.dismiss_room_ts == 0 or dismiss_left_time >= const.DISMISS_ROOM_WAIT_TIME: dismiss_left_time = 0 idx = 0 for p in self.players_list: if p and p.userId == userId: idx = p.idx return { 'init_info': self.get_init_client_dict(), 'controllerIdx': self.controller_idx, 'controller_discard_list': self.controller_discard_list, 'deskPokerList': self.deskPokerList, 'isPlayingGame': self.state, 'player_state_list': [ 1 if i in self.confirm_next_idx else 0 for i in range(const.ROOM_PLAYER_NUMBER) ], 'waitIdx': self.wait_idx, 'keyCard': self.key_card, 'applyCloseFrom': self.dismiss_room_from, 'applyCloseLeftTime': dismiss_left_time, 'applyCloseStateList': self.dismiss_room_state_list, 'player_advance_info_list': [ p.get_reconnect_client_dict(userId) for p in self.players_list if p is not None ], 'cooperation_cards': self.players_list[(idx + 2) % 4].cards if len(self.players_list[idx].cards) <= 0 else [0] * len(self.players_list[(idx + 2) % 4].cards), # 若自己的牌已经打完 则需要知道 队友的牌 'cooperation_idx': (idx + 2) % 4, 'showHandFlag': 1 if len(self.players_list[idx].op) <= 0 else 0, } def broadcastEnterRoom(self, idx): new_p = self.players_list[idx] if self.is_agent == 1: if self.agent and self.agent.mb: self.agent.mb.othersEnterRoom(new_p.get_init_client_dict()) for i, p in enumerate(self.players_list): if p is None: continue if i == idx: p.mb.enterRoomSucceed(self, idx) else: p.mb.othersEnterRoom(new_p.get_init_client_dict()) def cal_score(self, idx, aid, lucky_tile=0, multiple=0): pass def roundEndCallback(self, avt_mb): """ 一局完了之后玩家同意继续游戏 """ if self.state == 1: return idx = -1 for i, p in enumerate(self.players_list): if p and p.userId == avt_mb.userId: idx = i break if idx not in self.confirm_next_idx: self.confirm_next_idx.append(idx) for p in self.players_list: if p and p.idx != idx: p.mb.readyForNextRound(idx) if len(self.confirm_next_idx ) == const.ROOM_PLAYER_NUMBER and self.isFull: if self.current_round == 0 and self.is_agent == 1 and self.agent and self.agent.mb: try: self.agent.mb.quitRoomSucceed() leave_tips = "您代开的房间已经开始游戏, 您已被请离.\n房间号【{}】".format( self.roomID) self.agent.mb.showTip(leave_tips) except: pass self.startGame() def record_round_result(self): # 玩家记录当局战绩 d = datetime.fromtimestamp(time.time()) round_result_d = { 'date': '-'.join([str(d.year), str(d.month), str(d.day)]), 'time': ':'.join([str(d.hour), str(d.minute)]), 'round_record': [p.get_round_result_info() for p in self.origin_players_list if p], } # 第一局结束时push整个房间所有局的结构, 以后就增量push if self.current_round == 1: game_result_l = [[round_result_d]] for p in self.players_list: if p: p.record_all_result(game_result_l) else: for p in self.players_list: if p: p.record_round_game_result(round_result_d) def check_same_ip(self): ip_list = [] for p in self.players_list: if p and p.mb and p.ip != '0.0.0.0': ip_list.append(p.ip) else: ip_list.append(None) tips = [] checked = [] for i in range(const.ROOM_PLAYER_NUMBER): if ip_list[i] is None or i in checked: continue checked.append(i) repeat = [] repeat.append(i) for j in range(i + 1, const.ROOM_PLAYER_NUMBER): if ip_list[j] is None or j in checked: continue if ip_list[i] == ip_list[j]: repeat.append(j) if len(repeat) > 1: name = [] for k in repeat: checked.append(k) name.append(self.players_list[k].nickname) tip = '和'.join(name) + '有相同的ip地址' tips.append(tip) if tips: tips = '\n'.join(tips) # DEBUG_MSG(tips) for p in self.players_list: if p and p.mb: p.mb.showTip(tips) def apply_dismiss_room(self, avt_mb): """ 游戏开始后玩家申请解散房间 """ self.dismiss_room_ts = time.time() src = None for i, p in enumerate(self.players_list): if p.userId == avt_mb.userId: src = p break # 申请解散房间的人默认同意 self.dismiss_room_from = src.idx self.dismiss_room_state_list[src.idx] = 1 self.dismiss_timer = self.addTimer(const.DISMISS_ROOM_WAIT_TIME, 0, const.TIMER_TYPE_DISMISS_WAIT) for p in self.players_list: if p and p.mb and p.userId != avt_mb.userId: p.mb.req_dismiss_room(src.idx) def vote_dismiss_room(self, avt_mb, vote): """ 某位玩家对申请解散房间的投票 """ src = None for p in self.players_list: if p and p.userId == avt_mb.userId: src = p break self.dismiss_room_state_list[src.idx] = vote for p in self.players_list: if p and p.mb: p.mb.vote_dismiss_result(src.idx, vote) yes = self.dismiss_room_state_list.count(1) no = self.dismiss_room_state_list.count(2) if yes >= 3: self.delTimer(self.dismiss_timer) self.dismiss_timer = None self.dropRoom() if no >= 2: self.delTimer(self.dismiss_timer) self.dismiss_timer = None self.dismiss_room_from = -1 self.dismiss_room_ts = 0 self.dismiss_room_state_list = [0, 0, 0, 0] def notify_player_online_status(self, userId, status): src = -1 for idx, p in enumerate(self.players_list): if p and p.userId == userId: p.online = status src = idx break if src == -1: return for idx, p in enumerate(self.players_list): if p and p.mb and p.userId != userId: p.mb.notifyPlayerOnlineStatus(src, status)
class GameRoom(KBEngine.Base, GameObject, iRoomRules): """ 这是一个游戏房间/桌子类 该类处理维护一个房间中的实际游戏, 例如:斗地主、麻将等 该房间中记录了房间里所有玩家的mailbox,通过mailbox我们可以将信息推送到他们的客户端。 """ def __init__(self): GameObject.__init__(self) iRoomRules.__init__(self) self.owner_uid = 0 self.agent = None self.roomID = None # 状态0:未开始游戏, 1:某一局游戏中 self.state = 0 # 存放该房间内的玩家mailbox self.players_dict = {} self.players_list = [None] * self.player_num self.origin_players_list = [None] * const.ROOM_PLAYER_NUMBER # 庄家index self.dealer_idx = 0 # 当前控牌的玩家index self.current_idx = 0 # 对当前打出的牌可以进行操作的玩家的index, 服务端会限时等待他的操作 self.wait_idx_list = [] self.wait_op = None # 房间基础轮询timer self._poll_timer = None # 玩家操作限时timer self._op_timer = None # 一局游戏结束后, 玩家准备界面等待玩家确认timer self._next_game_timer = None #财神(多个) self.kingTiles = [] #圈风 self.prevailing_wind = const.WIND_EAST #玩家坐庄状态,所有玩家做过庄换圈风 0-没做过庄 1-做过庄 self.player_dealer_state = [0] * self.player_num self.current_round = 0 self.all_discard_tiles = [] # 最后一位出牌的玩家 self.last_player_idx = -1 # 房间开局所有操作的记录(aid, src, des, tile) self.op_record = [] # 确认继续的玩家 self.confirm_next_idx = [] # 解散房间操作的发起者 self.dismiss_room_from = -1 # 解散房间操作开始的时间戳 self.dismiss_room_ts = 0 # 解散房间操作投票状态 self.dismiss_room_state_list = [0] * self.player_num self.dismiss_timer = None # 等待玩家确认胡的dict self.wait_for_win_dict = { } # waitIdx:{"state": 0, "formIdx": idx, "win_op":aid, "quantity":4, "result":[]} # 等待确认放弃玩家列表 self.wait_give_up_list = [] # 放弃玩家列表 self.give_up_list = [] # 本局轮询放弃的操作 self.polling_give_up_op = -1 # self.addTimer(const.ROOM_EXIST_TIME, 0, const.TIMER_TYPE_ROOM_EXIST) self.roomOpenTime = time.time() @property def isFull(self): count = sum([1 for i in self.players_list if i is not None]) return count == self.player_num @property def isEmpty(self): count = sum([1 for i in self.players_list if i is not None]) return count == 0 and self.agent is None @property def nextIdx(self): tryNext = (self.current_idx + 1) % self.player_num for j in range(2): for i in range(self.player_num): if self.player_num > tryNext and self.players_list[ tryNext] not in self.give_up_list: return tryNext tryNext = (tryNext + 1) % self.player_num return (self.current_idx + 1) % self.player_num def getSit(self): for i, j in enumerate(self.players_list): if j is None: return i def _reset(self): self.state = 0 self.agent = None self.players_list = [None] * self.player_num self.dealer_idx = 0 self.current_idx = 0 self._poll_timer = None self._op_timer = None self._next_game_timer = None self.all_discard_tiles = [] self.kingTiles = [] self.current_round = 0 self.confirm_next_idx = [] self.prevailing_wind = const.WIND_EAST self.dismiss_timer = None self.dismiss_room_ts = 0 self.dismiss_room_state_list = [0, 0, 0, 0] self.wait_give_up_list = [] self.give_up_list = [] self.polling_give_up_op = -1 KBEngine.globalData["GameWorld"].delRoom(self) def throwTheDice(self, idxList): if self.player_num == 3: diceList = [[0, 0], [0, 0], [0, 0]] else: diceList = [[0, 0], [0, 0], [0, 0], [0, 0]] for idx in idxList: for i in range(0, 2): diceNum = random.randint(1, 6) diceList[idx][i] = diceNum return diceList def getMaxDiceIdx(self, diceList): numList = [] for i in range(len(diceList)): numList.append(diceList[i][0] + diceList[i][1]) idx = 0 num = 0 for i in range(len(numList)): if numList[i] > num: idx = i num = numList[i] return idx, num def onTimer(self, id, userArg): DEBUG_MSG("Room.onTimer called: id %i, userArg: %i" % (id, userArg)) if userArg == const.TIMER_TYPE_DISMISS_WAIT: self.delTimer(id) self.dropRoom() # if userArg == const.TIMER_TYPE_ROOM_EXIST: # self.game_round = self.current_round # self.delTimer(id) def reqEnterRoom(self, avt_mb, first=False): """ defined. 客户端调用该接口请求进入房间/桌子 """ if self.isFull: avt_mb.enterRoomFailed(const.ENTER_FAILED_ROOM_FULL) return def callback(content): content = content.decode() try: data = json.loads(content) card_cost, diamond_cost = switch.calc_cost( self.game_round, self.player_num, self.pay_mode) if diamond_cost > data["diamond"]: avt_mb.enterRoomFailed( const.ENTER_FAILED_ROOM_DIAMOND_NOT_ENOUGH) return # 代开房 if first and self.is_agent == 1: self.agent = PlayerProxy(avt_mb, self, -1) self.players_dict[avt_mb.userId] = self.agent avt_mb.enterRoomSucceed(self, -1) return for i, p in enumerate(self.players_list): if p and p.mb and p.mb.userId == avt_mb.userId: p.mb = avt_mb avt_mb.enterRoomSucceed(self, i) return DEBUG_MSG("Room.reqEnterRoom: %s" % (self.roomID)) idx = self.getSit() n_player = PlayerProxy(avt_mb, self, idx) self.players_dict[avt_mb.userId] = n_player self.players_list[idx] = n_player # 确认准备 # if idx not in self.confirm_next_idx: # self.confirm_next_idx.append(idx) if not first: self.broadcastEnterRoom(idx) self.check_same_ip() if self.isFull: self.origin_players_list = self.players_list[:] except: DEBUG_MSG("enterRoomFailed callback error:{}".format(content)) if switch.DEBUG_BASE: callback('{"card":99, "diamond":999}'.encode()) else: if first or self.pay_mode != 0: callback('{"card":99, "diamond":999}'.encode()) else: utility.get_user_info(avt_mb.accountName, callback) # # 代开房 # if first and self.is_agent == 1: # self.agent = PlayerProxy(avt_mb, self, -1) # self.players_dict[avt_mb.userId] = self.agent # avt_mb.enterRoomSucceed(self, -1) # return # for i, p in enumerate(self.players_list): # if p and p.mb and p.mb.userId == avt_mb.userId: # p.mb = avt_mb # avt_mb.enterRoomSucceed(self, i) # return # DEBUG_MSG("Room.reqEnterRoom: %s" % (self.roomID)) # idx = self.getSit() # n_player = PlayerProxy(avt_mb, self, idx) # self.players_dict[avt_mb.userId] = n_player # self.players_list[idx] = n_player # # 确认准备 # # if idx not in self.confirm_next_idx: # # self.confirm_next_idx.append(idx) # if not first: # self.broadcastEnterRoom(idx) # self.check_same_ip() # if self.isFull: # self.origin_players_list = self.players_list[:] # if self.isFull: # if self.is_agent == 1 and self.agent and self.agent.mb: # try: # self.agent.mb.quitRoomSucceed() # leave_tips = "您代开的房间已经开始游戏, 您已被请离.\n房间号【{}】".format(self.roomID) # self.agent.mb.showTip(leave_tips) # except: # pass # self.startGame() # self.addTimer(const.START_GAME_WAIT_TIME, 0, const.TIMER_TYPE_START_GAME) def reqReconnect(self, avt_mb): DEBUG_MSG("GameRoom reqReconnect userid = {}".format(avt_mb.userId)) if avt_mb.userId not in self.players_dict.keys(): return DEBUG_MSG("GameRoom reqReconnect player is in room".format( avt_mb.userId)) # 如果进来房间后牌局已经开始, 就要传所有信息 # 如果还没开始, 跟加入房间没有区别 player = self.players_dict[avt_mb.userId] if self.agent and player.userId == self.agent.userId: self.agent.mb = avt_mb self.agent.online = 1 avt_mb.enterRoomSucceed(self, -1) return player.mb = avt_mb player.online = 1 if self.state == 1 or 0 < self.current_round <= self.game_round: if self.state == 0: # 重连回来直接准备 self.roundEndCallback(avt_mb) rec_room_info = self.get_reconnect_room_dict(player.mb.userId) player.mb.handle_reconnect(rec_room_info) else: sit = 0 for idx, p in enumerate(self.players_list): if p and p.mb: if p.mb.userId == avt_mb.userId: sit = idx break avt_mb.enterRoomSucceed(self, sit) # self.check_same_ip() def reqLeaveRoom(self, player): """ defined. 客户端调用该接口请求离开房间/桌子 """ DEBUG_MSG("Room.reqLeaveRoom:{0}, is_agent={1}".format( self.roomID, self.is_agent)) if player.userId in self.players_dict.keys(): n_player = self.players_dict[player.userId] idx = n_player.idx if idx == -1 and self.is_agent == 1: self.dropRoom() elif idx == 0 and self.is_agent == 0: # 房主离开房间, 则解散房间 self.dropRoom() else: n_player.mb.quitRoomSucceed() self.players_list[idx] = None del self.players_dict[player.userId] if idx in self.confirm_next_idx: self.confirm_next_idx.remove(idx) # 通知其它玩家该玩家退出房间 for i, p in enumerate(self.players_list): if i != idx and p and p.mb: p.mb.othersQuitRoom(idx) if self.agent and self.agent.mb: self.agent.mb.othersQuitRoom(idx) if self.isEmpty: self._reset() def dropRoom(self): for p in self.players_list: if p and p.mb: try: p.mb.quitRoomSucceed() except: pass if self.agent and self.agent.mb: try: # # 如果是代开房, 没打完一局返还房卡 # if self.is_agent == 1 and self.current_round < 2: # # cost = 2 if self.game_round == 16 else 1 # cost = 1 # self.agent.mb.addCards(cost, "dropRoom") self.agent.mb.quitRoomSucceed() except: pass self._reset() def startGame(self): """ 开始游戏 """ DEBUG_MSG("startGame") self.op_record = [] self.state = 1 self.current_round += 1 self.wait_give_up_list = [] self.give_up_list = [] self.polling_give_up_op = -1 for i, p in enumerate(self.players_list): if p is not None: p.wreaths = [] diceList = self.throwTheDice([self.dealer_idx]) idx, num = self.getMaxDiceIdx(diceList) # 仅仅在第1局扣房卡, 不然每局都会扣房卡 def callback(content): content = content.decode() try: if content[0] != '{': DEBUG_MSG(content) self.dropRoom() return # 第一局时房间默认房主庄家, 之后谁上盘赢了谁是, 如果臭庄, 最后摸牌的人是庄 for p in self.players_list: p.reset() self.current_idx = self.dealer_idx #圈风 if sum([1 for state in self.player_dealer_state if state == 1]) == self.player_num: windIdx = (self.prevailing_wind + 1 - const.WIND_EAST) % len(const.WINDS) self.prevailing_wind = const.WINDS[windIdx] self.player_dealer_state = [0, 0, 0, 0] self.player_dealer_state[self.dealer_idx] = 1 #自风 for i, p in enumerate(self.players_list): if p is not None: p.wind = (self.player_num + i - self.dealer_idx ) % self.player_num + const.WIND_EAST self.initTiles() self.deal(self.king_num) wreathsList = [ p.wreaths for i, p in enumerate(self.players_list) ] playerWindList = [ p.wind for i, p in enumerate(self.players_list) ] for p in self.players_list: if p and p.mb: DEBUG_MSG( "start game,dealer_idx:{0},tiles:{1}, wreathsList:{2}, kingTiles:{3}, diceList:{4},leftTileNum:{5}" .format(self.dealer_idx, p.tiles, wreathsList, self.kingTiles, diceList, len(self.tiles))) p.mb.startGame(self.dealer_idx, p.tiles, wreathsList, self.kingTiles, self.prevailing_wind, playerWindList, diceList) self.beginRound() except: DEBUG_MSG("consume failed!") if self.current_round == 1 and self.is_agent == 0: card_cost, diamond_cost = switch.calc_cost(self.game_round, self.player_num, self.pay_mode) if switch.DEBUG_BASE: callback('{"card":99, "diamond":999}'.encode()) elif self.pay_mode == 1: utility.update_card_diamond( self.players_list[0].mb.accountName, -card_cost, -diamond_cost, callback, "FengHua RoomID:{}".format(self.roomID)) else: signal = 0 def payCallback(content): nonlocal signal try: signal += 1 if signal == len(self.players_list): callback(content) except: DEBUG_MSG("AA payCallback Failed") if self.player_num == 3: utility.update_card_diamond( self.players_list[0].mb.accountName, -card_cost, -diamond_cost, payCallback, "NingBoMJ RoomID:{}".format(self.roomID)) utility.update_card_diamond( self.players_list[1].mb.accountName, -card_cost, -diamond_cost, payCallback, "NingBoMJ RoomID:{}".format(self.roomID)) utility.update_card_diamond( self.players_list[2].mb.accountName, -card_cost, -diamond_cost, payCallback, "NingBoMJ RoomID:{}".format(self.roomID)) else: utility.update_card_diamond( self.players_list[0].mb.accountName, -card_cost, -diamond_cost, payCallback, "NingBoMJ RoomID:{}".format(self.roomID)) utility.update_card_diamond( self.players_list[1].mb.accountName, -card_cost, -diamond_cost, payCallback, "NingBoMJ RoomID:{}".format(self.roomID)) utility.update_card_diamond( self.players_list[2].mb.accountName, -card_cost, -diamond_cost, payCallback, "NingBoMJ RoomID:{}".format(self.roomID)) utility.update_card_diamond( self.players_list[3].mb.accountName, -card_cost, -diamond_cost, payCallback, "NingBoMJ RoomID:{}".format(self.roomID)) return DEBUG_MSG("start Game: Room{0} - Round{1}".format( self.roomID, str(self.current_round) + '/' + str(self.game_round))) callback('{"card":99, "diamond":999}'.encode()) def cutAfterKong(self): if not self.can_cut_after_kong(): return if len(self.tiles) <= 0: self.drawEnd() return player = self.players_list[self.current_idx] ti = self.tiles[0] self.tiles = self.tiles[1:] player.cutTile(ti) def beginRound(self): if len(self.tiles) <= 0: self.drawEnd() return player = self.players_list[self.current_idx] ti = self.tiles[0] self.tiles = self.tiles[1:] DEBUG_MSG("beginRound len:{0}".format(len(self.tiles))) player.drawTile(ti) def reqStopGame(self, player): """ 客户端调用该接口请求停止游戏 """ DEBUG_MSG("Room.reqLeaveRoom: %s" % (self.roomID)) self.state = 0 # self.delTimer(self._poll_timer) # self._poll_timer = None def drawEnd(self): """ 臭庄 """ info = dict() info['win_type'] = -1 info['win_idx_list'] = [] info['quantity_list'] = [0] info['lucky_tiles'] = [] info['result_list'] = [] if self.current_round < self.game_round: self.broadcastRoundEnd(info) else: self.endAll(info) def winGame(self, idx, op, quantity, result): """ 座位号为idx的玩家胡牌 """ self.cal_score(idx, op, quantity) if self.dealer_idx != idx: self.dealer_idx = (self.dealer_idx + 1) % self.player_num info = dict() info['win_type'] = op info['win_idx_list'] = [idx] info['quantity_list'] = [quantity] info['lucky_tiles'] = [] info['result_list'] = [result] if self.current_round < self.game_round: self.broadcastRoundEnd(info) else: self.endAll(info) def endAll(self, info): """ 游戏局数结束, 给所有玩家显示最终分数记录 """ # 先记录玩家当局战绩, 会累计总得分 self.record_round_result() info['left_tiles'] = info['left_tiles'] = self.tiles info['player_info_list'] = [ p.get_round_client_dict() for p in self.players_list if p is not None ] player_info_list = [ p.get_final_client_dict() for p in self.players_list if p is not None ] # DEBUG_MSG("%" * 30) # DEBUG_MSG("FinalEnd info = {}".format(player_info_list)) for p in self.players_list: if p and p.mb: p.mb.finalResult(player_info_list, info) self._reset() def sendEmotion(self, avt_mb, eid): """ 发表情 """ # DEBUG_MSG("Room.Player[%s] sendEmotion: %s" % (self.roomID, eid)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb == p.mb: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvEmotion(idx, eid) for i, p in enumerate(self.players_list): if p and i != idx: p.mb.recvEmotion(idx, eid) def sendMsg(self, avt_mb, mid): """ 发消息 """ # DEBUG_MSG("Room.Player[%s] sendMsg: %s" % (self.roomID, mid)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb == p.mb: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvMsg(idx, mid) for i, p in enumerate(self.players_list): if p and i != idx: p.mb.recvMsg(idx, mid) def sendVoice(self, avt_mb, url): # DEBUG_MSG("Room.Player[%s] sendVoice" % (self.roomID)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb.userId == p.userId: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvVoice(idx, url) for i, p in enumerate(self.players_list): if p and p.mb: p.mb.recvVoice(idx, url) def sendAppVoice(self, avt_mb, url, time): # DEBUG_MSG("Room.Player[%s] sendVoice" % (self.roomID)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb.userId == p.userId: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvAppVoice(idx, url, time) for i, p in enumerate(self.players_list): if p and p.mb: p.mb.recvAppVoice(idx, url, time) def doOperation(self, avt_mb, aid, tile_list): """ 当前控牌玩家摸牌后向服务端确认的操作 :param idx: 当前打牌的人的座位 :param aid: 操作id :param tile: 针对的牌 :return: 确认成功或者失败 """ if self.dismiss_room_ts != 0 and int(time.time() - self.dismiss_room_ts ) < const.DISMISS_ROOM_WAIT_TIME: # 说明在准备解散投票中,不能进行其他操作 return tile = tile_list[0] idx = -1 for i, p in enumerate(self.players_list): if p and p.mb == avt_mb: idx = i if idx != self.current_idx or len(self.wait_idx_list) > 0: avt_mb.doOperationFailed(const.OP_ERROR_NOT_CURRENT) return # self.delTimer(self._op_timer) p = self.players_list[idx] if aid == const.OP_DISCARD and self.can_discard( p.tiles, tile) and p not in self.give_up_list: self.all_discard_tiles.append(tile) p.discardTile(tile) elif aid == const.OP_CONCEALED_KONG and self.can_concealed_kong( p.tiles, tile) and p not in self.give_up_list: p.concealedKong(tile) elif aid == const.OP_EXPOSED_KONG and self.can_self_exposed_kong( p, tile) and p not in self.give_up_list: p.self_exposedKong(tile) elif aid == const.OP_KONG_WREATH and self.can_kong_wreath( p.tiles, tile) and p not in self.give_up_list: p.kongWreath(tile) elif aid == const.OP_PASS: # 自己摸牌的时候可以杠或者胡时选择过, 则什么都不做. 继续轮到该玩家打牌. pass elif aid == const.OP_DRAW_WIN and p not in self.give_up_list: #普通自摸胡 is_win, quantity, result = self.can_win(list(p.tiles), p.last_draw, const.OP_DRAW_WIN, idx) if is_win: p.win(aid, quantity, result) else: avt_mb.doOperationFailed(const.OP_ERROR_ILLEGAL) self.current_idx = self.nextIdx self.beginRound() elif aid == const.OP_WREATH_WIN and p not in self.give_up_list: #自摸8张花胡 is_win, quantity, result = self.can_win(list(p.tiles), p.last_draw, const.OP_WREATH_WIN, idx) if is_win: p.win(aid, quantity, result) else: avt_mb.doOperationFailed(const.OP_ERROR_ILLEGAL) self.current_idx = self.nextIdx self.beginRound() else: avt_mb.doOperationFailed(const.OP_ERROR_ILLEGAL) self.current_idx = self.nextIdx self.beginRound() def confirmOperation(self, avt_mb, aid, tile_list): """ 被轮询的玩家确认了某个操作 """ if self.dismiss_room_ts != 0 and int(time.time() - self.dismiss_room_ts ) < const.DISMISS_ROOM_WAIT_TIME: # 说明在准备解散投票中,不能进行其他操作 return tile = tile_list[0] idx = -1 for i, p in enumerate(self.players_list): if p and p.mb == avt_mb: idx = i if idx not in self.wait_idx_list and len(self.wait_idx_list) > 0: avt_mb.doOperationFailed(const.OP_ERROR_NOT_CURRENT) return # 确认要操作的牌是否是最后一张被打的牌 if aid != const.OP_PASS and tile != self.all_discard_tiles[-1] and len( self.wait_for_win_dict) <= 0: avt_mb.doOperationFailed(const.OP_ERROR_ILLEGAL) return # self.delTimer(self._poll_timer) temp_op = self.wait_op self.wait_idx_list = [] self.wait_op = None p = self.players_list[idx] if aid == const.OP_PONG and self.can_pong( p.tiles, tile) and p not in self.give_up_list: self.current_idx = idx p.pong(tile) elif aid == const.OP_EXPOSED_KONG and self.can_exposed_kong( p.tiles, tile) and p not in self.give_up_list: self.current_idx = idx p.exposedKong(tile) elif aid == const.OP_CHOW and self.can_chow_one( p.tiles, tile_list ) and ( idx + self.player_num - 1 ) % self.player_num == self.last_player_idx and p not in self.give_up_list: self.current_idx = idx p.chow(tile_list) elif aid == const.OP_KONG_WIN: if len(self.wait_for_win_dict) > 0: if idx in self.wait_for_win_dict: if self.wait_for_win_dict[idx]["state"] != 0: DEBUG_MSG("player is already commit win information.") return self.wait_for_win_dict[idx]["state"] = 1 self.handleMutiWinAfterCommit(tile) else: avt_mb.doOperationFailed(const.OP_ERROR_ILLEGAL) #无任何操作 过牌 self.confirmPass(idx) else: #无任何操作 过牌 #设置玩家 确认胡状态 if idx in self.wait_for_win_dict: self.wait_for_win_dict[idx]["state"] = 2 #state 0 未确认 1确认胡 2不胡 isAllCommit, isWin = self.handleMutiWinAfterCommit(tile) DEBUG_MSG("isAllCommit:{0},isWin:{1}".format(isAllCommit, isWin)) if isAllCommit and not isWin: # 所有玩家提交完毕,并且所有玩家都不胡 DEBUG_MSG( "check pong kong or pass,wait_op:{0}".format(temp_op)) if temp_op == const.OP_KONG_WIN: for i, p in enumerate(self.players_list): if p and i != self.last_player_idx and p not in self.give_up_list: DEBUG_MSG( "check==>idx:{0},tiles:{1},tile:{2}".format( i, str(p.tiles), str(tile))) if self.can_pong(p.tiles, tile) or self.can_exposed_kong( p.tiles, tile): self.wait_idx_list = [i] self.wait_op = const.OP_DISCARD DEBUG_MSG("can pong,idx:{0}".format(i)) p.mb.waitForOperation(const.OP_DISCARD, [ tile, ]) break else: self.confirmPass(idx) else: self.confirmPass(idx) def handleMutiWinAfterCommit(self, tile): # return 是否提交完毕, 是否可以胡 """任一可以胡的玩家提交结果完毕""" DEBUG_MSG("handleMutiWinAfterCommit:{0}".format( str(self.wait_for_win_dict))) win_list = [] quantity_list = [] formIdxList = [] result_list = [] win_op = const.OP_KONG_WIN for waitIdx in self.wait_for_win_dict: if self.wait_for_win_dict[waitIdx]["state"] == 0: return False, False elif self.wait_for_win_dict[waitIdx]["state"] == 1: win_list.append(self.players_list[waitIdx]) quantity_list.append( self.wait_for_win_dict[waitIdx]['quantity']) win_op = self.wait_for_win_dict[waitIdx]["win_op"] formIdxList.append(self.wait_for_win_dict[waitIdx]["formIdx"]) result_list.append(self.wait_for_win_dict[waitIdx]["result"]) self.wait_for_win_dict = {} # 所有玩家都选择不胡 if not win_list: DEBUG_MSG("all player select not win") return True, False # 有玩家选择胡 self.processMutiWin(win_list, quantity_list, result_list, win_op, formIdxList, tile) return True, True def confirmPass(self, idx): nextIdx = self.nextIdx nextMb = self.players_list[nextIdx] if idx != nextIdx and self.can_chow( nextMb.tiles, self.all_discard_tiles[-1] ) and nextMb not in self.give_up_list: self.wait_idx_list = [nextIdx] self.wait_op = const.OP_DISCARD nextMb.mb.waitForOperation(const.OP_DISCARD, [ self.all_discard_tiles[-1], ]) else: self.current_idx = nextIdx self.beginRound() #抢杠 胡处理 def processMutiWin(self, win_list, quantity_list, result_list, win_op, formIdx_list, tile): """ 处理抢杠胡, 可能有多人胡的情况 """ win_idx_list = [mem.idx for mem in win_list] op_list = [const.OP_KONG_WIN] * len(win_idx_list) self.broadcastMultiOperation(win_idx_list, op_list, win_op, [ tile, ]) #结算 for i in range(len(win_list)): fromIdx = formIdx_list[i] self.players_list[fromIdx].addScore(-quantity_list[i] * 5) win_list[i].addScore(quantity_list[i] * 5) if self.dealer_idx not in win_idx_list: self.dealer_idx = (self.dealer_idx + 1) % self.player_num for mem in win_list: mem.kong_win(tile) info = dict() info['win_type'] = const.OP_KONG_WIN info['win_idx_list'] = win_idx_list info['quantity_list'] = quantity_list info['lucky_tiles'] = [] info['result_list'] = result_list if self.current_round < self.game_round: self.broadcastRoundEnd(info) else: self.endAll(info) def broadcastOperation(self, idx, aid, tile_list=None): """ 将操作广播给所有人, 包括当前操作的玩家 :param idx: 当前操作玩家的座位号 :param aid: 操作id :param tile_list: 出牌的list """ for i, p in enumerate(self.players_list): if p is not None: p.mb.postOperation(idx, aid, tile_list) def broadcastOperation2(self, idx, aid, tile_list=None): """ 将操作广播除了自己之外的其他人 """ for i, p in enumerate(self.players_list): if p and i != idx: p.mb.postOperation(idx, aid, tile_list) def broadcastMultiOperation(self, idx_list, aid_list, win_op, tile_list=None): for i, p in enumerate(self.players_list): if p is not None: p.mb.postMultiOperation(idx_list, aid_list, tile_list) def broadcastRoundEnd(self, info): # 广播胡牌或者流局导致的每轮结束信息, 包括算的扎码和当前轮的统计数据 # 先记录玩家当局战绩, 会累计总得分 self.record_round_result() self.state = 0 info['left_tiles'] = self.tiles info['player_info_list'] = [ p.get_round_client_dict() for p in self.players_list if p is not None ] DEBUG_MSG("&" * 30) DEBUG_MSG("RoundEnd info = {}".format(info)) self.confirm_next_idx = [] for p in self.players_list: if p: p.mb.roundResult(info) def pollingGiveUp(self, operation, op_idx, oped_idx): self.polling_give_up_op = operation self.wait_give_up_list = [] for i, p in enumerate(self.players_list): if p and i != op_idx and i != oped_idx: self.wait_give_up_list.append(p) p.mb.notifyPlayerGiveUp() def confirmGiveUp(self, avt_mb, isGiveUp): if len(self.wait_give_up_list) <= 0: return for i, p in enumerate(self.wait_give_up_list): if p and avt_mb == p.mb: if isGiveUp and p not in self.give_up_list: self.give_up_list.append(p) self.wait_give_up_list.remove(p) break if len(self.wait_give_up_list) <= 0: '''所有玩家提交完毕''' DEBUG_MSG('all player committed') if self.polling_give_up_op == const.OP_PONG: '''碰的玩家可以打牌,解锁操作面板''' self.unlockPongPlayerOperation(self.current_idx) else: '''明杠玩家开始摸牌''' self.beginRound() def unlockPongPlayerOperation(self, idx): for i, p in enumerate(self.players_list): if p and i == idx: p.mb.unlockPongPlayerOperation() def waitForOperation(self, idx, aid, tile): isHaveWaitOp = False if aid == const.OP_KONG_WREATH: #玩家杠花 是否可以8张花胡 for i, p in enumerate(self.players_list): if p and i == idx and p not in self.give_up_list and self.can_wreath_win( p.wreaths): isHaveWaitOp = True p.mb.waitForOperation(const.OP_WREATH_WIN, [ tile, ]) break else: self.beginRound() return elif aid == const.OP_EXPOSED_KONG or aid == const.OP_CONCEALED_KONG: #玩家普通杠 是否可以抢杠胡 self.wait_idx_list = [] self.wait_for_win_dict = {} player_list, quantity_list, result_list = self.checkKongWin( idx, tile) for i, p in enumerate(player_list): if p is not None: wait_idx = self.players_list.index(p) self.wait_idx_list.append(wait_idx) self.wait_op = const.OP_KONG_WIN self.wait_for_win_dict[wait_idx] = { "state": 0, "formIdx": idx, "win_op": aid, "quantity": quantity_list[i], "result": result_list[i] } isHaveWaitOp = True p.mb.waitForOperation(const.OP_KONG_WIN, [ tile, ]) DEBUG_MSG("wait for operation win:{0}".format(wait_idx)) elif aid == const.OP_DISCARD: #玩家打牌 是否有其他玩家碰 杠 for i, p in enumerate(self.players_list): DEBUG_MSG("op_discard : {0},{1},{2}".format( i, idx, self.give_up_list)) if p and i != idx and p not in self.give_up_list: DEBUG_MSG("discard can_pong or can_exposed_kong") if self.can_pong(p.tiles, tile) or self.can_exposed_kong( p.tiles, tile): self.wait_idx_list = [i] self.wait_op = aid isHaveWaitOp = True p.mb.waitForOperation(aid, [ tile, ]) break if not isHaveWaitOp: #下家能不能吃 nextIdx = self.nextIdx p = self.players_list[nextIdx] if aid == const.OP_DISCARD and self.can_chow( p.tiles, tile) and p not in self.give_up_list: #能吃 self.wait_idx_list = [nextIdx] self.wait_op = aid p.mb.waitForOperation(aid, [ tile, ]) else: self.current_idx = nextIdx self.beginRound() #可以抢杠胡 玩家列表 def checkKongWin(self, idx, tile): wait_player_list = [] quantity_list = [] result_list = [] for i, p in enumerate(self.players_list): if i == idx or p in self.give_up_list: continue tmp = list(p.tiles) tmp.append(tile) is_win, quantity, result = self.can_win(tmp, tile, const.OP_KONG_WIN, i) if is_win: wait_player_list.append(p) quantity_list.append(quantity) result_list.append(result) return wait_player_list, quantity_list, result_list def get_init_client_dict(self): agent_d = { 'nickname': "", 'userId': 0, 'head_icon': "", 'ip': '0.0.0.0', 'sex': 1, 'idx': -1, 'uuid': 0, 'online': 1, } if self.is_agent and self.agent: d = self.agent.get_init_client_dict() agent_d.update(d) return { 'roomID': self.roomID, 'ownerId': self.owner_uid, 'isAgent': self.is_agent, 'agentInfo': agent_d, 'dealerIdx': self.dealer_idx, 'curRound': self.current_round, 'maxRound': self.game_round, 'player_num': self.player_num, 'pay_mode': self.pay_mode, 'win_quantity': self.win_quantity, 'king_num': self.king_num, 'player_base_info_list': [ p.get_init_client_dict() for p in self.players_list if p is not None ], 'player_state_list': [ 1 if i in self.confirm_next_idx else 0 for i in range(const.ROOM_PLAYER_NUMBER) ], } def get_reconnect_room_dict(self, userId): dismiss_left_time = int(time.time() - self.dismiss_room_ts) if self.dismiss_room_ts == 0 or dismiss_left_time >= const.DISMISS_ROOM_WAIT_TIME: dismiss_left_time = 0 idx = 0 for p in self.players_list: if p and p.userId == userId: idx = p.idx waitAid = -1 if len(self.wait_idx_list ) >= 0 and self.wait_op and idx in self.wait_idx_list: waitAid = self.wait_op giveUpState = const.NOT_GIVE_UP if self.players_list[idx] in self.give_up_list: giveUpState = const.GIVE_UP elif self.players_list[idx] in self.wait_give_up_list: giveUpState = const.WAIT_GIVE_UP return { 'init_info': self.get_init_client_dict(), 'curPlayerSitNum': self.current_idx, 'isPlayingGame': self.state, 'player_state_list': [ 1 if i in self.confirm_next_idx else 0 for i in range(self.player_num) ], 'lastDiscardTile': 0 if not self.all_discard_tiles else self.all_discard_tiles[-1], 'lastDrawTile': self.players_list[idx].last_draw, 'lastDiscardTileFrom': self.last_player_idx, 'kingTiles': self.kingTiles, 'waitAid': waitAid, 'giveUpState': giveUpState, 'leftTileNum': len(self.tiles), 'applyCloseFrom': self.dismiss_room_from, 'applyCloseLeftTime': dismiss_left_time, 'applyCloseStateList': self.dismiss_room_state_list, 'player_advance_info_list': [ p.get_reconnect_client_dict(userId) for p in self.players_list if p is not None ], 'prevailing_wind': self.prevailing_wind, } def broadcastEnterRoom(self, idx): new_p = self.players_list[idx] if self.is_agent == 1: if self.agent and self.agent.mb: self.agent.mb.othersEnterRoom(new_p.get_init_client_dict()) for i, p in enumerate(self.players_list): if p is None: continue if i == idx: p.mb.enterRoomSucceed(self, idx) else: p.mb.othersEnterRoom(new_p.get_init_client_dict()) def cal_score(self, idx, aid, quantity=0): if aid == const.OP_EXPOSED_KONG: pass elif aid == const.OP_CONCEALED_KONG: pass elif aid == const.OP_POST_KONG: pass elif aid == const.OP_GET_KONG: pass elif aid == const.OP_DRAW_WIN: for i, p in enumerate(self.players_list): if p is None or p in self.give_up_list: continue if idx == i: DEBUG_MSG("score:{0},{1},{2},{3},{4}".format( i, quantity, self.player_num, len(self.give_up_list), self.polling_give_up_op)) p.addScore(quantity * (self.player_num - 1 - len(self.give_up_list)) * (5 if self.polling_give_up_op > 0 else 1)) else: DEBUG_MSG("score:{0}, {1}, {2}".format( i, quantity, self.polling_give_up_op)) p.addScore(-quantity * (5 if self.polling_give_up_op > 0 else 1)) elif aid == const.OP_KONG_WIN: pass def roundEndCallback(self, avt_mb): """ 一局完了之后玩家同意继续游戏 """ if self.state == 1: return idx = -1 for i, p in enumerate(self.players_list): if p and p.userId == avt_mb.userId: idx = i break if idx not in self.confirm_next_idx: self.confirm_next_idx.append(idx) for p in self.players_list: if p and p.idx != idx: p.mb.readyForNextRound(idx) if len(self.confirm_next_idx) == self.player_num and self.isFull: if self.current_round == 0 and self.is_agent == 1 and self.agent: try: self.agent.mb.quitRoomSucceed() leave_tips = "您代开的房间已经开始游戏, 您已被请离.\n房间号【{}】".format( self.roomID) self.agent.mb.showTip(leave_tips) except: pass self.startGame() def record_round_result(self): # 玩家记录当局战绩 d = datetime.fromtimestamp(time.time()) round_result_d = { 'date': '-'.join([str(d.year), str(d.month), str(d.day)]), 'time': ':'.join([str(d.hour), str(d.minute)]), 'round_record': [p.get_round_result_info() for p in self.origin_players_list if p], } # 第一局结束时push整个房间所有局的结构, 以后就增量push if self.current_round == 1: game_result_l = [[round_result_d]] for p in self.players_list: if p: p.record_all_result(game_result_l) else: for p in self.players_list: if p: p.record_round_game_result(round_result_d) def check_same_ip(self): ip_list = [] for p in self.players_list: if p and p.mb and p.ip != '0.0.0.0': ip_list.append(p.ip) else: ip_list.append(None) tips = [] checked = [] for i in range(self.player_num): if ip_list[i] is None or i in checked: continue checked.append(i) repeat = [] repeat.append(i) for j in range(i + 1, self.player_num): if ip_list[j] is None or j in checked: continue if ip_list[i] == ip_list[j]: repeat.append(j) if len(repeat) > 1: name = [] for k in repeat: checked.append(k) name.append(self.players_list[k].nickname) tip = '和'.join(name) + '有相同的ip地址' tips.append(tip) if tips: tips = '\n'.join(tips) # DEBUG_MSG(tips) for p in self.players_list: if p and p.mb: p.mb.showTip(tips) def apply_dismiss_room(self, avt_mb): """ 游戏开始后玩家申请解散房间 """ self.dismiss_room_ts = time.time() src = None for i, p in enumerate(self.players_list): if p.userId == avt_mb.userId: src = p break # 申请解散房间的人默认同意 self.dismiss_room_from = src.idx self.dismiss_room_state_list[src.idx] = 1 self.dismiss_timer = self.addTimer(const.DISMISS_ROOM_WAIT_TIME, 0, const.TIMER_TYPE_DISMISS_WAIT) for p in self.players_list: if p and p.mb and p.userId != avt_mb.userId: p.mb.req_dismiss_room(src.idx) def vote_dismiss_room(self, avt_mb, vote): """ 某位玩家对申请解散房间的投票 """ src = None for p in self.players_list: if p and p.userId == avt_mb.userId: src = p break self.dismiss_room_state_list[src.idx] = vote for p in self.players_list: if p and p.mb: p.mb.vote_dismiss_result(src.idx, vote) yes = self.dismiss_room_state_list.count(1) no = self.dismiss_room_state_list.count(2) if yes >= 3: self.delTimer(self.dismiss_timer) self.dismiss_timer = None self.dropRoom() if no >= 2: self.delTimer(self.dismiss_timer) self.dismiss_timer = None self.dismiss_room_from = -1 self.dismiss_room_ts = 0 self.dismiss_room_state_list = [0, 0, 0, 0] def notify_player_online_status(self, userId, status): src = -1 for idx, p in enumerate(self.players_list): if p and p.userId == userId: p.online = status src = idx break if src == -1: return for idx, p in enumerate(self.players_list): if p and p.mb and p.userId != userId: p.mb.notifyPlayerOnlineStatus(src, status)
class GameRoom(KBEngine.Base, GameObject, iRoomRules): """ 这是一个游戏房间/桌子类 该类处理维护一个房间中的实际游戏, 例如:斗地主、麻将等 该房间中记录了房间里所有玩家的mailbox,通过mailbox我们可以将信息推送到他们的客户端。 """ def __init__(self): GameObject.__init__(self) iRoomRules.__init__(self) self.owner_uid = 0 self.agent = None self.roomID = None # 状态0:未开始游戏, 1:某一局游戏中 self.state = 0 # 存放该房间内的玩家mailbox self.players_dict = {} self.players_list = [None] * const.ROOM_PLAYER_NUMBER # 上局庄家 self.last_dealer_idx = 0 # 庄家index self.dealer_idx = 0 # 当前控牌的玩家index self.current_idx = 0 # 对当前打出的牌可以进行操作的玩家的index, 服务端会限时等待他的操作 self.wait_idx_list = [] self.wait_op = None # 房间基础轮询timer self._poll_timer = None # 玩家操作限时timer self._op_timer = None # 一局游戏结束后, 玩家准备界面等待玩家确认timer self._next_game_timer = None #财神 self.kingTile = 0 #打出财神的玩家idx self.discardKingTileIdx = -2 self.current_round = 0 self.all_discard_tiles = [] # 最后一位出牌的玩家 self.last_player_idx = -1 # 房间开局所有操作的记录(aid, src, des, tile) self.op_record = [] # 确认继续的玩家 self.confirm_next_idx = [] # 解散房间操作的发起者 self.dismiss_room_from = -1 # 解散房间操作开始的时间戳 self.dismiss_room_ts = 0 # 解散房间操作投票状态 self.dismiss_room_state_list = [0, 0, 0, 0] self.dismiss_timer = None #目前老庄数 self.curOldDealNum = self.startOldDealNum #等待玩家确认胡的dict self.wait_for_win_dict = {} # self.addTimer(const.ROOM_EXIST_TIME, 0, const.TIMER_TYPE_ROOM_EXIST) self.roomOpenTime = time.time() @property def isFull(self): count = sum([1 for i in self.players_list if i is not None]) return count == const.ROOM_PLAYER_NUMBER @property def isEmpty(self): count = sum([1 for i in self.players_list if i is not None]) return count == 0 and self.agent is None @property def timeLeft(self): return 0.0 roomTimeLeft = const.ROOM_EXIST_TIME - (time.time() - self.roomOpenTime) if roomTimeLeft <= 0: return 0.0 return roomTimeLeft def getSit(self): for i, j in enumerate(self.players_list): if j is None: return i def _reset(self): self.state = 0 self.agent = None self.players_list = [None] * const.ROOM_PLAYER_NUMBER self.last_dealer_idx = 0 self.dealer_idx = 0 self.current_idx = 0 self._poll_timer = None self._op_timer = None self._next_game_timer = None self.all_discard_tiles = [] self.kingTile = 0 self.discardKingTileIdx = -2 self.current_round = 0 self.confirm_next_idx = [] self.dismiss_timer = None self.dismiss_room_ts = 0 self.dismiss_room_state_list = [0, 0, 0, 0] self.curOldDealNum = self.startOldDealNum KBEngine.globalData["GameWorld"].delRoom(self) def throwTheDice(self, idxList): diceList = [[0, 0], [0, 0], [0, 0], [0, 0]] for idx in idxList: for i in range(0, 2): diceNum = random.randint(1, 6) diceList[idx][i] = diceNum return diceList def getMaxDiceIdx(self, diceList): numList = [] for i in range(len(diceList)): numList.append(diceList[i][0] + diceList[i][1]) idx = 0 num = 0 for i in range(len(numList)): if numList[i] > num: idx = i num = numList[i] return idx, num def setCurOldDealNum(self, addDealNum=0): if self.current_round > 1 and self.dealer_idx == self.last_dealer_idx: if self.maxOldDealNum <= 0: self.curOldDealNum += 1 elif self.curOldDealNum < self.maxOldDealNum: self.curOldDealNum += 1 else: if self.maxOldDealNum <= 0: self.curOldDealNum = self.startOldDealNum + addDealNum elif self.startOldDealNum + addDealNum > self.maxOldDealNum: self.curOldDealNum = self.maxOldDealNum else: self.curOldDealNum = self.startOldDealNum + addDealNum def deal(self): """ 发牌 """ for i in range(const.INIT_TILE_NUMBER): for j in range(const.ROOM_PLAYER_NUMBER): self.players_list[j].tiles.append(self.tiles[j]) # t1, t2, t3, t4 = self.tiles[:4] self.tiles = self.tiles[const.ROOM_PLAYER_NUMBER:] # self.players_list[0].tiles.append(t1) # self.players_list[1].tiles.append(t2) # self.players_list[2].tiles.append(t3) # self.players_list[3].tiles.append(t4) #财神 # self.kingTile = self.tiles[0] # self.tiles = self.tiles[1:] DEBUG_MSG("deal:room kingTile is {}.".format(self.kingTile)) for i in range(const.ROOM_PLAYER_NUMBER): self.players_list[i].tidy(self.kingTile) def onTimer(self, id, userArg): DEBUG_MSG("Room.onTimer called: id %i, userArg: %i" % (id, userArg)) if userArg == const.TIMER_TYPE_DISMISS_WAIT: self.delTimer(id) self.dropRoom() # if userArg == const.TIMER_TYPE_ROOM_EXIST: # self.game_round = self.current_round # self.delTimer(id) def reqEnterRoom(self, avt_mb, first=False): """ defined. 客户端调用该接口请求进入房间/桌子 """ if self.isFull: avt_mb.enterRoomFailed(const.ENTER_FAILED_ROOM_FULL) return # 代开房 if first and self.is_agent == 1: self.agent = PlayerProxy(avt_mb, self, -1) self.players_dict[avt_mb.userId] = self.agent avt_mb.enterRoomSucceed(self, -1) return for i, p in enumerate(self.players_list): if p and p.mb and p.mb.userId == avt_mb.userId: p.mb = avt_mb avt_mb.enterRoomSucceed(self, i) return DEBUG_MSG("Room.reqEnterRoom: %s" % (self.roomID)) idx = self.getSit() n_player = PlayerProxy(avt_mb, self, idx) self.players_dict[avt_mb.userId] = n_player self.players_list[idx] = n_player # 确认准备 if idx not in self.confirm_next_idx: self.confirm_next_idx.append(idx) if not first: self.broadcastEnterRoom(idx) self.check_same_ip() if self.isFull: if self.is_agent == 1 and self.agent and self.agent.mb: try: self.agent.mb.quitRoomSucceed() leave_tips = "您代开的房间已经开始游戏, 您已被请离.\n房间号【{}】".format( self.roomID) self.agent.mb.showTip(leave_tips) except: pass self.startGame() # self.addTimer(const.START_GAME_WAIT_TIME, 0, const.TIMER_TYPE_START_GAME) def reqReconnect(self, avt_mb): DEBUG_MSG("GameRoom reqReconnect userid = {}".format(avt_mb.userId)) if avt_mb.userId not in self.players_dict.keys(): return DEBUG_MSG("GameRoom reqReconnect player is in room".format( avt_mb.userId)) # 如果进来房间后牌局已经开始, 就要传所有信息 # 如果还没开始, 跟加入房间没有区别 player = self.players_dict[avt_mb.userId] if self.agent and player.userId == self.agent.userId: self.agent.mb = avt_mb self.agent.online = 1 avt_mb.enterRoomSucceed(self, -1) return player.mb = avt_mb player.online = 1 if self.state == 1 or 0 < self.current_round <= self.game_round: if self.state == 0: # 重连回来直接准备 self.roundEndCallback(avt_mb) rec_room_info = self.get_reconnect_room_dict(player.mb.userId) player.mb.handle_reconnect(rec_room_info) else: sit = 0 for idx, p in enumerate(self.players_list): if p and p.mb: if p.mb.userId == avt_mb.userId: sit = idx break avt_mb.enterRoomSucceed(self, sit) # self.check_same_ip() def reqLeaveRoom(self, player): """ defined. 客户端调用该接口请求离开房间/桌子 """ DEBUG_MSG("Room.reqLeaveRoom:{0}, is_agent={1}".format( self.roomID, self.is_agent)) if player.userId in self.players_dict.keys(): n_player = self.players_dict[player.userId] idx = n_player.idx if idx == -1 and self.is_agent == 1: self.dropRoom() elif idx == 0 and self.is_agent == 0: # 房主离开房间, 则解散房间 self.dropRoom() else: n_player.mb.quitRoomSucceed() self.players_list[idx] = None del self.players_dict[player.userId] # 通知其它玩家该玩家退出房间 for i, p in enumerate(self.players_list): if i != idx and p and p.mb: p.mb.othersQuitRoom(idx) if self.agent and self.agent.mb: self.agent.mb.othersQuitRoom(idx) if self.isEmpty: self._reset() def dropRoom(self): for p in self.players_list: if p and p.mb: try: p.mb.quitRoomSucceed() except: pass if self.agent and self.agent.mb: try: # 如果是代开房, 没打完一局返还房卡 # if self.is_agent == 1 and self.current_round < 2: # # cost = 2 if self.game_round == 16 else 1 # cost = 1 # self.agent.mb.addCards(cost, "dropRoom") self.agent.mb.quitRoomSucceed() except: pass self._reset() def startGame(self): """ 开始游戏 """ self.op_record = [] self.state = 1 self.current_round += 1 self.discardKingTileIdx = -2 diceList = [[0, 0], [0, 0], [0, 0], [0, 0]] addDealNum = 0 if self.current_round <= 1: #首次选庄 diceList = self.throwTheDice([0, 1, 2, 3]) idx, num = self.getMaxDiceIdx(diceList) self.dealer_idx = idx if self.diceAddNum > 0 and num >= self.diceAddNum: addDealNum += 1 elif self.isSameAdd > 0 and diceList[idx][0] == diceList[idx][1]: addDealNum += 1 else: diceList = self.throwTheDice([self.dealer_idx]) idx, num = self.getMaxDiceIdx(diceList) if self.dealer_idx != self.last_dealer_idx: if self.diceAddNum > 0 and num >= self.diceAddNum: addDealNum += 1 elif self.isSameAdd > 0 and diceList[idx][0] == diceList[idx][ 1]: addDealNum += 1 #老庄 self.setCurOldDealNum(addDealNum) DEBUG_MSG( "start game,curOldDealNum{0},dealer_idx{1},diceList{2}".format( self.curOldDealNum, self.dealer_idx, str(diceList))) # 仅仅在第1局扣房卡, 不然每局都会扣房卡 def callback(content): content = content.decode() try: if content[0] != '{': DEBUG_MSG(content) self.dropRoom() return for p in self.players_list: p.reset() self.initTiles() self.deal() self.current_idx = self.dealer_idx for p in self.players_list: if p and p.mb: p.mb.startGame(self.dealer_idx, p.tiles, self.kingTile, diceList, self.curOldDealNum, self.game_round, self.roomMode) self.beginRound() except: DEBUG_MSG("consume failed!") if self.current_round == 1 and self.is_agent == 0: card_cost, diamond_cost = switch.calc_cost(self.roomMode, self.game_round) if switch.DEBUG_BASE: callback('{"card":99, "diamond":999}'.encode()) else: utility.update_card_diamond( self.players_list[0].mb.accountName, -card_cost, -diamond_cost, callback, "XiaoShanMJ RoomID:{}".format(self.roomID)) return DEBUG_MSG("start Game: Room{0} - Round{1}".format( self.roomID, str(self.current_round) + '/' + str(self.game_round))) callback('{"card":99, "diamond":999}'.encode()) def cutAfterKong(self): if not self.can_cut_after_kong(): return if len(self.tiles) <= self.lucky_tile: self.drawEnd() return player = self.players_list[self.current_idx] ti = self.tiles[0] self.tiles = self.tiles[1:] player.cutTile(ti) def beginRound(self): if len(self.tiles) <= self.lucky_tile: self.drawEnd() return player = self.players_list[self.current_idx] ti = self.tiles[0] self.tiles = self.tiles[1:] player.drawTile(ti) def reqStopGame(self, player): """ 客户端调用该接口请求停止游戏 """ DEBUG_MSG("Room.reqLeaveRoom: %s" % (self.roomID)) self.state = 0 # self.delTimer(self._poll_timer) # self._poll_timer = None def drawEnd(self): """ 臭庄 """ self.last_dealer_idx = self.dealer_idx self.dealer_idx = (self.current_idx + 4 - 1) % 4 self.settlementScoreRoundEnd(True) info = dict() info['lucky_tiles'] = [] info['op_list'] = [-1] info['win_idx_list'] = [-1] info["win_op"] = const.OP_DRAW_END # if self.timeLeft <= 0: # self.game_round = self.current_round if self.current_round < self.game_round: self.broadcastRoundEnd(info) else: self.endAll(info) def winGame(self, idx, op): """ 座位号为idx的玩家胡牌 """ p = self.players_list[idx] hit = 0 lucky_tiles = [] if self.roomMode == 2: lucky_tiles = self.tiles[0:self.lucky_tile] hit = self.cal_lucky_tile_hit(lucky_tiles) self.cal_score(idx, op, hit) self.last_dealer_idx = self.dealer_idx self.dealer_idx = idx self.settlementScoreRoundEnd(False) info = dict() info['lucky_tiles'] = lucky_tiles info['op_list'] = [op] info['win_idx_list'] = [idx] info["win_op"] = const.OP_DRAW_WIN # if self.timeLeft <= 0: # self.game_round = self.current_round if self.current_round < self.game_round: self.broadcastRoundEnd(info) else: self.endAll(info) def settlementScoreRoundEnd(self, isDrawEnd): for i in range(len(self.players_list)): self.players_list[i].roundEndSettlement(isDrawEnd) # if self.players_list[i].roundEndSettlement(isDrawEnd): # self.game_round = self.current_round def endAll(self, info): """ 游戏局数结束, 给所有玩家显示最终分数记录 """ # 先记录玩家当局战绩, 会累计总得分 self.record_round_result() info['left_tiles'] = info['left_tiles'] = self.tiles info['player_info_list'] = [ p.get_round_client_dict() for p in self.players_list if p is not None ] player_info_list = [ p.get_final_client_dict() for p in self.players_list if p is not None ] # DEBUG_MSG("%" * 30) # DEBUG_MSG("FinalEnd info = {}".format(player_info_list)) for p in self.players_list: if p and p.mb: p.mb.finalResult(player_info_list, info) self._reset() def sendEmotion(self, avt_mb, eid): """ 发表情 """ # DEBUG_MSG("Room.Player[%s] sendEmotion: %s" % (self.roomID, eid)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb == p.mb: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvEmotion(idx, eid) for i, p in enumerate(self.players_list): if p and i != idx: p.mb.recvEmotion(idx, eid) def sendMsg(self, avt_mb, mid): """ 发消息 """ # DEBUG_MSG("Room.Player[%s] sendMsg: %s" % (self.roomID, mid)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb == p.mb: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvMsg(idx, mid) for i, p in enumerate(self.players_list): if p and i != idx: p.mb.recvMsg(idx, mid) def sendVoice(self, avt_mb, url): # DEBUG_MSG("Room.Player[%s] sendVoice" % (self.roomID)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb.userId == p.userId: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvVoice(idx, url) for i, p in enumerate(self.players_list): if p and p.mb: p.mb.recvVoice(idx, url) def sendAppVoice(self, avt_mb, url, time): # DEBUG_MSG("Room.Player[%s] sendVoice" % (self.roomID)) idx = None for i, p in enumerate(self.players_list): if p and avt_mb.userId == p.userId: idx = i break else: if self.agent and self.agent.userId == avt_mb.userId: idx = -1 if idx is None: return if self.agent and idx != -1 and self.agent.mb: self.agent.mb.recvAppVoice(idx, url, time) for i, p in enumerate(self.players_list): if p and p.mb: p.mb.recvAppVoice(idx, url, time) def doOperation(self, avt_mb, aid, tile_list): """ 当前控牌玩家摸牌后向服务端确认的操作 :param idx: 当前打牌的人的座位 :param aid: 操作id :param tile: 针对的牌 :return: 确认成功或者失败 """ if self.dismiss_room_ts != 0 and int(time.time() - self.dismiss_room_ts ) < const.DISMISS_ROOM_WAIT_TIME: # 说明在准备解散投票中,不能进行其他操作 return tile = tile_list[0] idx = -1 for i, p in enumerate(self.players_list): if p and p.mb == avt_mb: idx = i if idx != self.current_idx or len(self.wait_idx_list) > 0: avt_mb.doOperationFailed(const.OP_ERROR_NOT_CURRENT) return # self.delTimer(self._op_timer) p = self.players_list[idx] if aid == const.OP_DISCARD and self.can_discard(p.tiles, tile): self.all_discard_tiles.append(tile) p.discardTile(self.kingTile, tile) elif aid == const.OP_CONCEALED_KONG and self.can_concealed_kong( p.tiles, tile): p.concealedKong(tile) elif aid == const.OP_EXPOSED_KONG and self.can_self_exposed_kong( p, tile): p.self_exposedKong(tile) elif aid == const.OP_PASS: # 自己摸牌的时候可以杠或者胡时选择过, 则什么都不做. 继续轮到该玩家打牌. pass elif aid == const.OP_WIN: if self.can_win(list(p.tiles)): #可以自摸胡 p.win() else: avt_mb.doOperationFailed(const.OP_ERROR_ILLEGAL) self.current_idx = (self.current_idx + 1) % const.ROOM_PLAYER_NUMBER self.beginRound() else: avt_mb.doOperationFailed(const.OP_ERROR_ILLEGAL) self.current_idx = (self.current_idx + 1) % const.ROOM_PLAYER_NUMBER self.beginRound() def confirmOperation(self, avt_mb, aid, tile_list): """ 被轮询的玩家确认了某个操作 """ if self.dismiss_room_ts != 0 and int(time.time() - self.dismiss_room_ts ) < const.DISMISS_ROOM_WAIT_TIME: # 说明在准备解散投票中,不能进行其他操作 return tile = tile_list[0] idx = -1 for i, p in enumerate(self.players_list): if p and p.mb == avt_mb: idx = i if idx not in self.wait_idx_list and len(self.wait_idx_list) > 0: DEBUG_MSG("confirmOperation {0},{1}".format( idx, str(self.wait_idx_list))) avt_mb.doOperationFailed(const.OP_ERROR_NOT_CURRENT) return # 确认要操作的牌是否是最后一张被打的牌 if aid != const.OP_PASS and tile != self.all_discard_tiles[-1]: avt_mb.doOperationFailed(const.OP_ERROR_ILLEGAL) return # self.delTimer(self._poll_timer) temp_op = self.wait_op self.wait_idx_list = [] self.wait_op = None p = self.players_list[idx] if aid == const.OP_PONG and self.can_pong(p.tiles, tile, idx): self.current_idx = idx p.pong(tile) elif aid == const.OP_EXPOSED_KONG and self.can_exposed_kong( p.tiles, tile, idx): self.current_idx = idx p.exposedKong(tile) elif aid == const.OP_CHOW and self.can_chow_one( p.tiles, tile_list, idx): self.current_idx = idx p.chow(tile_list) elif aid == const.OP_WIN: if len(self.wait_for_win_dict) > 0: if idx in self.wait_for_win_dict: if self.wait_for_win_dict[idx]["state"] != 0: DEBUG_MSG("player is already commit win information.") return self.wait_for_win_dict[idx]["state"] = 1 self.handleMutiWinAfterCommit(tile) else: avt_mb.doOperationFailed(const.OP_ERROR_ILLEGAL) #无任何操作 过牌 self.confirmPass(idx) else: #无任何操作 过牌 #设置玩家 确认胡状态 if idx in self.wait_for_win_dict: self.wait_for_win_dict[idx]["state"] = 2 #state 0 未确认 1确认胡 2不胡 isAllCommit, isWin = self.handleMutiWinAfterCommit(tile) DEBUG_MSG("isAllCommit:{0},isWin:{1}".format(isAllCommit, isWin)) if isAllCommit and not isWin: # 所有玩家提交完毕,并且所有玩家都不胡 DEBUG_MSG( "check pong kong or pass,wait_op:{0}".format(temp_op)) if temp_op == const.OP_GIVE_WIN: for i, p in enumerate(self.players_list): if p and i != self.last_player_idx: DEBUG_MSG( "check==>idx:{0},tiles:{1},tile:{2}".format( i, str(p.tiles), str(tile))) if self.can_pong(p.tiles, tile, i) or self.can_exposed_kong( p.tiles, tile, i): self.wait_idx = [i] self.wait_op = const.OP_DISCARD DEBUG_MSG("can pong,idx:{0}".format(i)) p.mb.waitForOperation(const.OP_DISCARD, [ tile, ]) break else: self.confirmPass(idx) else: self.confirmPass(idx) def handleMutiWinAfterCommit(self, tile): # return 是否提交完毕, 是否可以胡 """任一可以胡的玩家提交结果完毕""" DEBUG_MSG("handleMutiWinAfterCommit:{0}".format( str(self.wait_for_win_dict))) win_list = [] win_op = const.OP_BLAST_WIN formIdxList = [] for waitIdx in self.wait_for_win_dict: if self.wait_for_win_dict[waitIdx]["state"] == 0: return False, False elif self.wait_for_win_dict[waitIdx]["state"] == 1: win_list.append(self.players_list[waitIdx]) win_op = self.wait_for_win_dict[waitIdx]["win_op"] formIdxList.append(self.wait_for_win_dict[waitIdx]["formIdx"]) self.wait_for_win_dict = {} # 所有玩家都选择不胡 if not win_list: DEBUG_MSG("all player select not win") formIdx = -1 for waitIdx in self.wait_for_win_dict: formIdx = self.wait_for_win_dict[waitIdx]["formIdx"] break if win_op == const.OP_EXPOSED_KONG_WIN and fromIdx >= 0: self.cal_score(formIdx, const.OP_EXPOSED_KONG) elif win_op == const.OP_CONCEALED_KONG_WIN and fromIdx >= 0: self.cal_score(formIdx, const.OP_CONCEALED_KONG) elif win_op == const.OP_CONTINUE_KONG_WIN and fromIdx >= 0: self.cal_score(self.last_player_idx, const.OP_POST_KONG) self.cal_score(formIdx, const.OP_GET_KONG) return True, False # 有玩家选择胡 self.processMutiWin(win_list, win_op, formIdxList, tile) return True, True def confirmPass(self, idx): nextIdx = (self.current_idx + 1) % const.ROOM_PLAYER_NUMBER nextMb = self.players_list[nextIdx] if idx != nextIdx and self.can_chow(nextMb.tiles, self.all_discard_tiles[-1], idx): self.wait_idx = [nextIdx] self.wait_op = const.OP_DISCARD nextMb.mb.waitForOperation(const.OP_DISCARD, [ self.all_discard_tiles[-1], ]) else: self.current_idx = (self.current_idx + 1) % const.ROOM_PLAYER_NUMBER self.beginRound() def broadcastOperation(self, idx, aid, tile_list=None): """ 将操作广播给所有人, 包括当前操作的玩家 :param idx: 当前操作玩家的座位号 :param aid: 操作id :param tile_list: 出牌的list """ for i, p in enumerate(self.players_list): if p is not None: p.mb.postOperation(idx, aid, tile_list) def broadcastOperation2(self, idx, aid, tile_list=None): """ 将操作广播除了自己之外的其他人 """ for i, p in enumerate(self.players_list): if p and i != idx: p.mb.postOperation(idx, aid, tile_list) def broadcastMultiOperation(self, idx_list, aid_list, winType, tile_list=None): for i, p in enumerate(self.players_list): if p is not None: p.mb.postMultiOperation(idx_list, aid_list, winType, tile_list) def broadcastRoundEnd(self, info): # 广播胡牌或者流局导致的每轮结束信息, 包括算的扎码和当前轮的统计数据 # 先记录玩家当局战绩, 会累计总得分 self.record_round_result() self.state = 0 info['left_tiles'] = self.tiles info['player_info_list'] = [ p.get_round_client_dict() for p in self.players_list if p is not None ] DEBUG_MSG("&" * 30) DEBUG_MSG("RoundEnd info = {}".format(info)) self.confirm_next_idx = [] for p in self.players_list: if p: p.mb.roundResult(info) # self._next_game_timer = self.addTimer(const.NEXT_GAME_WAIT_TIME, 0, const.TIMER_TYPE_NEXT_GAME) def checkDicsardKingTile(self, idx, aid, tile): #玩家打出财神 if aid != const.OP_DISCARD: return if tile == self.kingTile and self.discardKingTileIdx < 0: #打出的是财神 (同一圈只有第一张财神有效) self.discardKingTileIdx = idx elif tile != self.kingTile and self.discardKingTileIdx == idx: #又轮到出财神玩家出牌(继续出财神则等到下一圈才恢复) self.discardKingTileIdx = -2 def waitForOperation( self, idx, aid, tile, win_op=const.OP_BLAST_WIN): # 16 放炮胡 17明杠抢杠胡 18暗杠抢杠胡 19接杠抢杠胡 #能不能胡(放炮) giveWin = False self.wait_idx_list = [] self.wait_for_win_dict = {} player_list = self.checkOthersWin(idx, tile) for i in range(len(player_list)): p = player_list[i] wait_idx = self.players_list.index(p) if aid == const.OP_DISCARD or aid == const.OP_EXPOSED_KONG or aid == const.OP_CONCEALED_KONG: giveWin = True DEBUG_MSG("wait for operation win:{0}".format(wait_idx)) self.wait_idx_list.append(wait_idx) self.wait_op = const.OP_GIVE_WIN self.wait_for_win_dict[wait_idx] = { "state": 0, "formIdx": idx, "win_op": win_op } p.mb.waitForOperation(const.OP_GIVE_WIN, [ tile, ]) #不能放炮胡 if not giveWin: #能不能碰和杠 for i, p in enumerate(self.players_list): if p and i != idx: if aid == const.OP_DISCARD: if self.can_pong(p.tiles, tile, i) or self.can_exposed_kong( p.tiles, tile, i): self.wait_idx_list = [i] self.wait_op = aid p.mb.waitForOperation(aid, [ tile, ]) break else: #下家能不能吃 nextIdx = (self.current_idx + 1) % const.ROOM_PLAYER_NUMBER p = self.players_list[nextIdx] if self.can_chow(p.tiles, tile, nextIdx) and aid == const.OP_DISCARD: #能吃 self.wait_idx_list = [nextIdx] self.wait_op = aid p.mb.waitForOperation(aid, [ tile, ]) else: self.current_idx = (self.current_idx + 1) % const.ROOM_PLAYER_NUMBER self.beginRound() #判断是否可以 放炮/抢杠胡 def checkOthersWin(self, idx, tile): wait_player_list = [] if self.roomMode == 1 or self.roomMode == 2: return wait_player_list for i, p in enumerate(self.players_list): if i == idx: continue tmp = list(p.tiles) tmp.append(tile) if self.can_win(tmp): wait_player_list.append(p) return wait_player_list #放炮/抢杠 胡处理 def processMutiWin(self, win_list, win_op, formIdx_list, tile): """ 处理抢杠胡, 可能有多人胡的情况 """ win_idx_list = [mem.idx for mem in win_list] op_list = [const.OP_GIVE_WIN] * len(win_idx_list) self.broadcastMultiOperation(win_idx_list, op_list, win_op, [ tile, ]) #结算 for i in range(len(win_list)): fromIdx = formIdx_list[i] if win_op == const.OP_BLAST_WIN: score = 1 if fromIdx == self.dealer_idx or win_idx_list[ i] == self.dealer_idx: #有一方是庄家 win_list[i].addScore(score * 2) self.players_list[fromIdx].addScore(-score * 2) else: win_list[i].addScore(score) self.players_list[fromIdx].addScore(-score) elif win_op == const.OP_EXPOSED_KONG_WIN: score = 4 if fromIdx == self.dealer_idx or win_idx_list[ i] == self.dealer_idx: #有一方是庄家 win_list[i].addScore(score * 2) self.players_list[fromIdx].addScore(-score * 2) else: win_list[i].addScore(score) self.players_list[fromIdx].addScore(-score) elif win_op == const.OP_CONCEALED_KONG_WIN: score = 7 if fromIdx == self.dealer_idx or win_idx_list[ i] == self.dealer_idx: #有一方是庄家 win_list[i].addScore(score * 2) self.players_list[fromIdx].addScore(-score * 2) else: win_list[i].addScore(score) self.players_list[fromIdx].addScore(-score) elif win_op == const.OP_CONTINUE_KONG_WIN: score = 4 if fromIdx == self.dealer_idx or win_idx_list[ i] == self.dealer_idx: #有一方是庄家 win_list[i].addScore(score * 2) self.players_list[fromIdx].addScore(-score * 2) else: win_list[i].addScore(score) self.players_list[fromIdx].addScore(-score) self.dealer_idx = win_idx_list[random.randint(0, len(win_idx_list) - 1)] for mem in win_list: mem.give_win(tile) self.settlementScoreRoundEnd(False) info = dict() info['lucky_tiles'] = [] info['op_list'] = op_list info['win_idx_list'] = win_idx_list info["win_op"] = win_op if self.current_round < self.game_round: self.broadcastRoundEnd(info) else: self.endAll(info) def get_init_client_dict(self): agent_d = { 'nickname': "", 'userId': 0, 'head_icon': "", 'ip': '0.0.0.0', 'sex': 1, 'idx': -1, 'uuid': 0, 'online': 1, } if self.is_agent and self.agent: d = self.agent.get_init_client_dict() agent_d.update(d) return { 'roomID': self.roomID, 'ownerId': self.owner_uid, 'isAgent': self.is_agent, 'agentInfo': agent_d, 'dealerIdx': self.dealer_idx, 'curRound': self.current_round + 1, 'maxRound': self.game_round, 'luckyTileNum': self.lucky_tile, 'player_info_list': [ p.get_init_client_dict() for p in self.players_list if p is not None ], 'roomMode': self.roomMode, 'roomTimeLeft': self.timeLeft, } def get_reconnect_room_dict(self, userId): dismiss_left_time = int(time.time() - self.dismiss_room_ts) if self.dismiss_room_ts == 0 or dismiss_left_time >= const.DISMISS_ROOM_WAIT_TIME: dismiss_left_time = 0 idx = 0 for p in self.players_list: if p and p.userId == userId: idx = p.idx waitAid = -1 if len(self.wait_idx_list ) > 0 and self.wait_op and idx in self.wait_idx_list: waitAid = self.wait_op agent_d = { 'nickname': "", 'userId': 0, 'head_icon': "", 'ip': '0.0.0.0', 'sex': 1, 'idx': -1, 'uuid': 0, 'online': 1, } if self.is_agent and self.agent: d = self.agent.get_init_client_dict() agent_d.update(d) return { 'roomID': self.roomID, 'isAgent': self.is_agent, 'agentInfo': agent_d, 'curRound': self.current_round, 'maxRound': self.game_round, 'luckyTileNum': self.lucky_tile, 'ownerId': self.owner_uid, 'dealerIdx': self.dealer_idx, 'curPlayerSitNum': self.current_idx, 'isPlayingGame': self.state, 'player_state_list': [ 1 if i in self.confirm_next_idx else 0 for i in range(const.ROOM_PLAYER_NUMBER) ], 'lastDiscardTile': 0 if not self.all_discard_tiles else self.all_discard_tiles[-1], "lastDrawTile": self.players_list[idx].last_draw, 'lastDiscardTileFrom': self.last_player_idx, "lastDiscardTileNum": self.getSerialSameTileNum(), "curOldDealNum": self.curOldDealNum, "kingTile": self.kingTile, "discardKingTileIdx": self.discardKingTileIdx, 'waitAid': waitAid, 'leftTileNum': len(self.tiles), 'applyCloseFrom': self.dismiss_room_from, 'applyCloseLeftTime': dismiss_left_time, 'applyCloseStateList': self.dismiss_room_state_list, 'player_info_list': [ p.get_reconnect_client_dict(userId) for p in self.players_list if p is not None ], 'roomMode': self.roomMode, 'roomTimeLeft': self.timeLeft, } def broadcastEnterRoom(self, idx): new_p = self.players_list[idx] if self.is_agent == 1: if self.agent and self.agent.mb: self.agent.mb.othersEnterRoom(new_p.get_init_client_dict()) for i, p in enumerate(self.players_list): if p is None: continue if i == idx: p.mb.enterRoomSucceed(self, idx) else: p.mb.othersEnterRoom(new_p.get_init_client_dict()) def cal_score(self, idx, aid, lucky_tile=0): if aid == const.OP_EXPOSED_KONG: # 2 1 1 DEBUG_MSG("cal_score:OP_EXPOSED_KONG") for i, p in enumerate(self.players_list): if i != idx: p.addScore(-1) self.players_list[idx].addScore(3) elif aid == const.OP_CONCEALED_KONG: # 4 DEBUG_MSG("cal_score:OP_CONCEALED_KONG") for i, p in enumerate(self.players_list): if i != idx: p.addScore(-2) self.players_list[idx].addScore(6) elif aid == const.OP_POST_KONG: # 3 1 被杠 扣1分 DEBUG_MSG("cal_score:OP_POST_KONG") self.players_list[idx].addScore(-1) elif aid == const.OP_GET_KONG: # 3 1 杠(杠别人打出的牌) 得1分 DEBUG_MSG("cal_score:OP_GET_KONG") self.players_list[idx].addScore(1) elif aid == const.OP_WIN: for i, p in enumerate(self.players_list): if i != idx: if idx == self.dealer_idx: p.addScore(-4) p.addScore(-4 * lucky_tile) else: p.addScore(-2) p.addScore(-2 * lucky_tile) if idx == self.dealer_idx: self.players_list[idx].addScore(12) self.players_list[idx].addScore(12 * lucky_tile) else: self.players_list[idx].addScore(6) self.players_list[idx].addScore(6 * lucky_tile) elif aid == const.OP_KONG_WIN: pass def roundEndCallback(self, avt_mb): """ 一局完了之后玩家同意继续游戏 """ if self.state == 1: return idx = -1 for i, p in enumerate(self.players_list): if p and p.userId == avt_mb.userId: idx = i break if idx not in self.confirm_next_idx: self.confirm_next_idx.append(idx) for p in self.players_list: if p and p.idx != idx: p.mb.readyForNextRound(idx) if len(self.confirm_next_idx) == const.ROOM_PLAYER_NUMBER: # if self._next_game_timer: # self.delTimer(self._next_game_timer) # self._next_game_timer = None self.startGame() def record_round_result(self): # 玩家记录当局战绩 d = datetime.fromtimestamp(time.time()) round_result_d = { 'date': '-'.join([str(d.year), str(d.month), str(d.day)]), 'time': ':'.join([str(d.hour), str(d.minute)]), 'round_record': [p.get_round_result_info() for p in self.players_list if p], } # 第一局结束时push整个房间所有局的结构, 以后就增量push if self.current_round == 1: game_result_l = [[round_result_d]] for p in self.players_list: if p: p.record_all_result(game_result_l) else: for p in self.players_list: if p: p.record_round_game_result(round_result_d) def check_same_ip(self): ip_list = [] for p in self.players_list: if p and p.mb and p.ip != '0.0.0.0': ip_list.append(p.ip) else: ip_list.append(None) tips = [] checked = [] for i in range(const.ROOM_PLAYER_NUMBER): if ip_list[i] is None or i in checked: continue checked.append(i) repeat = [] repeat.append(i) for j in range(i + 1, const.ROOM_PLAYER_NUMBER): if ip_list[j] is None or j in checked: continue if ip_list[i] == ip_list[j]: repeat.append(j) if len(repeat) > 1: name = [] for k in repeat: checked.append(k) name.append(self.players_list[k].nickname) tip = '和'.join(name) + '有相同的ip地址' tips.append(tip) if tips: tips = '\n'.join(tips) # DEBUG_MSG(tips) for p in self.players_list: if p and p.mb: p.mb.showTip(tips) def apply_dismiss_room(self, avt_mb): """ 游戏开始后玩家申请解散房间 """ self.dismiss_room_ts = time.time() src = None for i, p in enumerate(self.players_list): if p.userId == avt_mb.userId: src = p break # 申请解散房间的人默认同意 self.dismiss_room_from = src.idx self.dismiss_room_state_list[src.idx] = 1 self.dismiss_timer = self.addTimer(const.DISMISS_ROOM_WAIT_TIME, 0, const.TIMER_TYPE_DISMISS_WAIT) for p in self.players_list: if p and p.mb and p.userId != avt_mb.userId: p.mb.req_dismiss_room(src.idx) def vote_dismiss_room(self, avt_mb, vote): """ 某位玩家对申请解散房间的投票 """ src = None for p in self.players_list: if p and p.userId == avt_mb.userId: src = p break self.dismiss_room_state_list[src.idx] = vote for p in self.players_list: if p and p.mb: p.mb.vote_dismiss_result(src.idx, vote) yes = self.dismiss_room_state_list.count(1) no = self.dismiss_room_state_list.count(2) if yes >= 3: self.delTimer(self.dismiss_timer) self.dismiss_timer = None self.dropRoom() if no >= 2: self.delTimer(self.dismiss_timer) self.dismiss_timer = None self.dismiss_room_from = -1 self.dismiss_room_ts = 0 self.dismiss_room_state_list = [0, 0, 0, 0] def notify_player_online_status(self, userId, status): src = -1 for idx, p in enumerate(self.players_list): if p and p.userId == userId: p.online = status src = idx break if src == -1: return for idx, p in enumerate(self.players_list): if p and p.mb and p.userId != userId: p.mb.notifyPlayerOnlineStatus(src, status)