def collectVoiceData(self, requests): """collect voices of other players""" if not self.running: return block = DeferredBlock(self) voiceDataRequests = [] for request in requests: if request.answer == Message.ClientWantsVoiceData: # another human player requests sounds for voiceId voiceId = request.args[0] voiceFor = [ x for x in self.game.players if isinstance(self.remotes[x], User) and self.remotes[x].voiceId == voiceId ][0] voiceFor.voice = Voice(voiceId) if Debug.sound: logDebug('client %s wants voice data %s for %s' % (request.user.name, request.args[0], voiceFor)) voiceDataRequests.append((request.user, voiceFor)) if not voiceFor.voice.oggFiles(): # the server does not have it, ask the client with that # voice block.tell(voiceFor, voiceFor, Message.ServerWantsVoiceData) block.callback(self.sendVoiceData, voiceDataRequests)
def _clientDiscarded4(self, dummyResults, msg, dangerousText, mustPlayDangerous): """client told us he discarded a tile. Continue, check for dangerous game""" block = DeferredBlock(self) player = msg.player if dangerousText: if mustPlayDangerous and not player.lastSource.isDiscarded: if Debug.dangerousGame: tile = Tile(msg.args[0]) logDebug( '%s claims no choice. Discarded %s, keeping %s. %s' % (player, tile, ''.join( player.concealedTiles), ' / '.join(dangerousText))) player.claimedNoChoice = True block.tellAll(player, Message.NoChoice, tiles=TileList(player.concealedTiles)) else: player.playedDangerous = True if Debug.dangerousGame: tile = Tile(msg.args[0]) logDebug( '%s played dangerous. Discarded %s, keeping %s. %s' % (player, tile, ''.join( player.concealedTiles), ' / '.join(dangerousText))) block.tellAll(player, Message.DangerousGame, tiles=TileList(player.concealedTiles)) if msg.answer == Message.OriginalCall: player.isCalling = True block.callback(self.clientMadeOriginalCall, msg) else: block.callback(self._askForClaims, msg)
def sendVoiceIds(self): """tell each player what voice ids the others have. By now the client has a Game instance!""" humanPlayers = [ x for x in self.game.players if isinstance(self.remotes[x], User) ] if len(humanPlayers) < 2 or not any(self.remotes[x].voiceId for x in humanPlayers): # no need to pass around voice data self.assignVoices() return block = DeferredBlock(self) for player in humanPlayers: remote = self.remotes[player] if remote.voiceId: # send it to other human players: others = [x for x in humanPlayers if x != player] if Debug.sound: logDebug( 'telling other human players that %s has voiceId %s' % (player.name, remote.voiceId)) block.tell(player, others, Message.VoiceId, source=remote.voiceId) block.callback(self.collectVoiceData)
def assignVoices(self, dummyResults=None): """now all human players have all voice data needed""" humanPlayers = [ x for x in self.game.players if isinstance(self.remotes[x], User) ] block = DeferredBlock(self) block.tell(None, humanPlayers, Message.AssignVoices) block.callback(self.startHand)
def clientMadeOriginalCall(self, dummyResults, msg): """first tell everybody about original call and then treat the implicit discard""" msg.player.originalCall = True if Debug.originalCall: logDebug('server.clientMadeOriginalCall: %s' % msg.player) block = DeferredBlock(self) block.tellAll(msg.player, Message.OriginalCall) block.callback(self._askForClaims, msg)
def _clientDiscarded3(self, dummyResults, msg, dangerousText, mustPlayDangerous): """client told us he discarded a tile. Continue, check for calling""" block = DeferredBlock(self) player = msg.player if self.game.ruleset.mustDeclareCallingHand and not player.isCalling: if player.hand.callingHands: player.isCalling = True block.tellAll(player, Message.Calling) block.callback(self._clientDiscarded4, msg, dangerousText, mustPlayDangerous)
def _clientDiscarded2(self, dummyResults, msg, dangerousText, mustPlayDangerous, violates): """client told us he discarded a tile. Continue, check for violating original call""" block = DeferredBlock(self) player = msg.player if violates: if Debug.originalCall: tile = Tile(msg.args[0]) logDebug('%s just violated OC with %s' % (player, tile)) player.mayWin = False block.tellAll(player, Message.ViolatesOriginalCall) block.callback(self._clientDiscarded3, msg, dangerousText, mustPlayDangerous)
def claimMahJongg(self, msg): """a player claims mah jongg. Check this and if correct, tell all. Otherwise abort game, kajongg client is faulty""" if not self.running: return player = msg.player concealedMelds = MeldList(msg.args[0]) withDiscard = Tile(msg.args[1]) if msg.args[1] else None lastMeld = Meld(msg.args[2]) if self.game.ruleset.mustDeclareCallingHand: assert player.isCalling, '%s %s %s says MJ but never claimed: concmelds:%s withdiscard:%s lastmeld:%s' % ( self.game.handId, player.hand, player, concealedMelds, withDiscard, lastMeld) discardingPlayer = self.game.activePlayer lastMove = next(self.game.lastMoves(withoutNotifications=True)) robbedTheKong = lastMove.message == Message.DeclaredKong if robbedTheKong: player.robsTile() withDiscard = lastMove.meld[0].concealed lastMove.player.robTileFrom(withDiscard) msgArgs = player.showConcealedMelds(concealedMelds, withDiscard) if msgArgs: self.abort(*msgArgs) return player.declaredMahJongg(concealedMelds, withDiscard, player.lastTile, lastMeld) if not player.hand.won: msg = i18nE('%1 claiming MahJongg: This is not a winning hand: %2') self.abort(msg, player.name, player.hand.string) return block = DeferredBlock(self) if robbedTheKong: block.tellAll(player, Message.RobbedTheKong, tile=withDiscard) if (player.lastSource is TileSource.LivingWallDiscard and self.game.dangerousFor(discardingPlayer, player.lastTile) and discardingPlayer.playedDangerous): player.usedDangerousFrom = discardingPlayer if Debug.dangerousGame: logDebug('%s wins with dangerous tile %s from %s' % (player, self.game.lastDiscard, discardingPlayer)) block.tellAll(player, Message.UsedDangerousFrom, source=discardingPlayer.name) block.tellAll(player, Message.MahJongg, melds=concealedMelds, lastTile=player.lastTile, lastMeld=lastMeld, withDiscardTile=withDiscard) block.callback(self.endHand)
def initGame(self): """ask clients if they are ready to start""" game = self.game game.saveStartTime() block = DeferredBlock(self) for player in game.players: block.tellPlayer(player, Message.ReadyForGameStart, tableid=self.tableid, gameid=game.gameid, shouldSave=player.shouldSave, wantedGame=game.wantedGame, playerNames=list( (x.wind, x.name) for x in game.players)) block.callback(self.startGame)
def claimTile(self, player, claim, meldTiles, nextMessage): """a player claims a tile for pung, kong or chow. meldTiles contains the claimed tile, concealed""" if not self.running: return lastDiscard = self.game.lastDiscard # if we rob a tile, self.game.lastDiscard has already been set to the # robbed tile hasTiles = Meld(meldTiles[:]) discardingPlayer = self.game.activePlayer hasTiles = hasTiles.without(lastDiscard) meld = Meld(meldTiles) if len(meld) != 4 and not (meld.isPair or meld.isPungKong or meld.isChow): msg = i18nE('%1 wrongly said %2 for meld %3') self.abort(msg, player.name, claim.name, str(meld)) return if not player.hasConcealedTiles(hasTiles): msg = i18nE( '%1 wrongly said %2: claims to have concealed tiles %3 but only has %4' ) self.abort(msg, player.name, claim.name, ' '.join(hasTiles), ''.join(player.concealedTiles)) return # update our internal state before we listen to the clients again self.game.discardedTiles[lastDiscard.exposed] -= 1 self.game.activePlayer = player if lastDiscard: player.lastTile = lastDiscard.exposed player.lastSource = TileSource.LivingWallDiscard player.exposeMeld(hasTiles, lastDiscard) self.game.lastDiscard = None block = DeferredBlock(self) if (nextMessage != Message.Kong and self.game.dangerousFor(discardingPlayer, lastDiscard) and discardingPlayer.playedDangerous): player.usedDangerousFrom = discardingPlayer if Debug.dangerousGame: logDebug('%s claims dangerous tile %s discarded by %s' % (player, lastDiscard, discardingPlayer)) block.tellAll(player, Message.UsedDangerousFrom, source=discardingPlayer.name) block.tellAll(player, nextMessage, meld=meld) if claim == Message.Kong: block.callback(self.pickKongReplacement) else: block.callback(self.moved)
def divided(self, dummyResults=None): """the wall is now divided for all clients""" if not self.running: return block = DeferredBlock(self) for clientPlayer in self.game.players: for player in self.game.players: if player == clientPlayer or self.game.playOpen: tiles = player.concealedTiles else: tiles = TileList(Tile.unknown * 13) block.tell(player, clientPlayer, Message.SetConcealedTiles, tiles=TileList(chain(tiles, player.bonusTiles))) block.callback(self.dealt)
def proposeGameId(self, gameid): """server proposes an id to the clients ands waits for answers""" while True: query = Query('insert into game(id,seed) values(?,?)', (gameid, 'proposed'), mayFail=True, failSilent=True) if not query.failure: break gameid += random.randrange(1, 100) block = DeferredBlock(self) for player in self.game.players: if player.shouldSave and isinstance(self.remotes[player], User): # do not ask robot players, they use the server data base block.tellPlayer(player, Message.ProposeGameId, gameid=gameid) block.callback(self.collectGameIdAnswers, gameid)
def endHand(self, dummyResults=None): """hand is over, show all concealed tiles to all players""" if not self.running: return if self.game.playOpen: self.saveHand() else: block = DeferredBlock(self) for player in self.game.players: # there might be no winner, winner.others() would be wrong if player != self.game.winner: # the winner tiles are already shown in claimMahJongg block.tellOthers(player, Message.ShowConcealedTiles, show=True, tiles=TileList(player.concealedTiles)) block.callback(self.saveHand)
def leaveTable(self, user, tableid, message=None, *args): """user leaves table. If no human user is left on a new table, remove it""" if tableid in self.tables: table = self.tables[tableid] if user in table.users: if len(table.users) == 1 and not table.suspendedAt: # silent: do not tell the user who left the table that he # did self.removeTable(table, 'silent', message, *args) else: table.delUser(user) if self.srvUsers: block = DeferredBlock(table) block.tell(None, self.srvUsers, Message.TableChanged, source=table.asSimpleList()) block.callback(False) return True
def clientDiscarded(self, msg): """client told us he discarded a tile. Check for consistency and tell others.""" if not self.running: return player = msg.player assert player == self.game.activePlayer tile = Tile(msg.args[0]) if tile not in player.concealedTiles: self.abort('player %s discarded %s but does not have it' % (player, tile)) return dangerousText = self.game.dangerousFor(player, tile) mustPlayDangerous = player.mustPlayDangerous() violates = player.violatesOriginalCall(tile) self.game.hasDiscarded(player, tile) block = DeferredBlock(self) block.tellAll(player, Message.Discard, tile=tile) block.callback(self._clientDiscarded2, msg, dangerousText, mustPlayDangerous, violates)
def sendVoiceData(self, requests, voiceDataRequests): """sends voice sounds to other human players""" self.processAnswers(requests) block = DeferredBlock(self) for voiceDataRequester, voiceFor in voiceDataRequests: # this player requested sounds for voiceFor voice = voiceFor.voice content = voice.archiveContent if content: if Debug.sound: logDebug('server got voice data %s for %s from client' % (voiceFor.voice, voiceFor.name)) block.tell(voiceFor, voiceDataRequester, Message.VoiceData, md5sum=voice.md5sum, source=content) elif Debug.sound: logDebug('server got empty voice data %s for %s from client' % (voice, voiceFor.name)) block.callback(self.assignVoices)
def joinTable(self, user, tableid): """user joins table""" table = self._lookupTable(tableid) table.addUser(user) block = DeferredBlock(table) block.tell(None, self.srvUsers, Message.TableChanged, source=table.asSimpleList()) if len(table.users) == table.maxSeats(): if Debug.table: logDebug('Table %s: All seats taken, starting' % table) def startTable(dummy): """now all players know about our join""" table.readyForGameStart(table.owner) block.callback(startTable) else: block.callback(False) return True
def pickTile(self, dummyResults=None, deadEnd=False): """the active player gets a tile from wall. Tell all clients.""" if not self.running: return player = self.game.activePlayer try: tile = player.pickedTile(deadEnd) except WallEmpty: self.endHand() else: self.game.lastDiscard = None block = DeferredBlock(self) block.tellPlayer(player, Message.PickedTile, tile=tile, deadEnd=deadEnd) showTile = tile if tile.isBonus or self.game.playOpen else Tile.unknown block.tellOthers(player, Message.PickedTile, tile=showTile, deadEnd=deadEnd) block.callback(self.moved)
def tellOthers(self, player, command, callback=None, **kwargs): """tell something about player to all other players""" block = DeferredBlock(self) block.tellOthers(player, command, **kwargs) block.callback(callback) return block