コード例 #1
0
ファイル: servertable.py プロジェクト: zero804/kajongg
 def addUser(self, user):
     """add user to this table"""
     if user.name in list(x.name for x in self.users):
         raise srvError(pb.Error, i18nE('You already joined this table'))
     if len(self.users) == self.maxSeats():
         raise srvError(pb.Error, i18nE('All seats are already taken'))
     self.users.append(user)
     if Debug.table:
         logDebug('%s seated on table %s' % (user.name, self))
     self.sendChatMessage(
         ChatMessage(self.tableid,
                     user.name,
                     i18nE('takes a seat'),
                     isStatusMessage=True))
コード例 #2
0
ファイル: servertable.py プロジェクト: zero804/kajongg
 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)
コード例 #3
0
ファイル: servertable.py プロジェクト: zero804/kajongg
 def nextHand(self, dummyResults):
     """next hand: maybe rotate"""
     if not self.running:
         return
     DeferredBlock.garbageCollection()
     for block in DeferredBlock.blocks:
         if block.table == self:
             logError('request left from previous hand: %s' %
                      block.outstandingStr())
     token = self.game.handId.prompt(
         withAI=False)  # we need to send the old token until the
     # clients started the new hand
     rotateWinds = self.game.maybeRotateWinds()
     if self.game.finished():
         self.server.removeTable(self, 'gameOver',
                                 i18nE('Game <numid>%1</numid> is over!'),
                                 self.game.seed)
         if Debug.process and os.name != 'nt':
             logDebug('MEM:%s' %
                      resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)
         return
     self.game.sortPlayers()
     playerNames = list((x.wind, x.name) for x in self.game.players)
     self.tellAll(None,
                  Message.ReadyForHandStart,
                  self.startHand,
                  playerNames=playerNames,
                  rotateWinds=rotateWinds,
                  token=token)
コード例 #4
0
 def _lookupTable(self, tableid):
     """return table by id or raise exception"""
     if tableid not in self.tables:
         raise srvError(pb.Error,
                        i18nE('table with id <numid>%1</numid> not found'),
                        tableid)
     return self.tables[tableid]
コード例 #5
0
 def _checkedPassword(matched, userid):
     """after the password has been checked"""
     if not matched:
         return fail(
             credError.UnauthorizedLogin(srvMessage(
                 i18nE('Wrong password'))))
     return userid
コード例 #6
0
ファイル: servertable.py プロジェクト: zero804/kajongg
 def delUser(self, user):
     """remove user from this table"""
     if user in self.users:
         self.running = False
         self.users.remove(user)
         self.sendChatMessage(
             ChatMessage(self.tableid,
                         user.name,
                         i18nE('leaves the table'),
                         isStatusMessage=True))
         if user is self.owner:
             # silently pass ownership
             if self.users:
                 self.owner = self.users[0]
                 if Debug.table:
                     logDebug('%s leaves table %d, %s is the new owner' %
                              (user.name, self.tableid, self.owner))
             else:
                 if Debug.table:
                     logDebug('%s leaves table %d, table is now empty' %
                              (user.name, self.tableid))
         else:
             if Debug.table:
                 logDebug('%s leaves table %d, %s stays owner' %
                          (user.name, self.tableid, self.owner))
コード例 #7
0
ファイル: deferredutil.py プロジェクト: zero804/kajongg
 def __failed(self, result, request):
     """a user did not or not correctly answer"""
     if request in self.requests:
         self.removeRequest(request)
     if result.type in [pb.PBConnectionLost]:
         msg = i18nE('The game server lost connection to player %1')
         self.table.abort(msg, request.user.name)
     else:
         msg = i18nE('Error for player %1: %2\n%3')
         if hasattr(result, 'traceback'):
             traceBack = result.traceback
         else:
             try:
                 traceBack = result.getBriefTraceback()
             except BaseException as exc:
                 traceBack = 'twisted cannot give us a traceback:{}'.format(
                     exc)
         self.table.abort(msg, request.user.name, result.getErrorMessage(),
                          traceBack)
コード例 #8
0
 def logout(self, user):
     """remove user from all tables"""
     if user not in self.srvUsers:
         return
     self.srvUsers.remove(user)
     for tableid in self.tablesWith(user):
         self.leaveTable(user, tableid, i18nE('Player %1 has logged out'),
                         user.name)
     # wait a moment. We want the leaveTable message to arrive everywhere before
     # we say serverDisconnects. Sometimes the order was reversed.
     reactor.callLater(1, self.__logout2, user)
コード例 #9
0
 def perspective_setClientProperties(self,
                                     dbIdent,
                                     voiceId,
                                     maxGameId,
                                     clientVersion=None):
     """perspective_* methods are to be called remotely"""
     self.pinged()
     self.dbIdent = dbIdent
     self.voiceId = voiceId
     self.maxGameId = maxGameId
     serverVersion = Internal.defaultPort
     if clientVersion != serverVersion:
         # we assume that versions x.y.* are compatible
         if clientVersion is None:
             # client passed no version info
             return fail(
                 srvError(
                     pb.Error,
                     i18nE(
                         'Your client has a version older than 4.9.0 but you need %1 for this server'
                     ), serverVersion))
         else:
             commonDigits = len([
                 x for x in zip(clientVersion.split(b'.'),
                                serverVersion.split(b'.')) if x[0] == x[1]
             ])
             if commonDigits < 2:
                 return fail(
                     srvError(
                         pb.Error,
                         i18nE(
                             'Your client has version %1 but you need %2 for this server'
                         ), clientVersion or '<4.9.0',
                         '.'.join(serverVersion.split('.')[:2]) + '.*'))
     if Debug.table:
         logDebug(
             'client has dbIdent={} voiceId={} maxGameId={} clientVersion {}'
             .format(self.dbIdent, self.voiceId, self.maxGameId,
                     clientVersion))
     self.server.sendTables(self)
コード例 #10
0
 def showConcealedMelds(self, concealedMelds, ignoreDiscard=None):
     """the server tells how the winner shows and melds his
     concealed tiles. In case of error, return message and arguments"""
     for meld in concealedMelds:
         for tile in meld:
             if tile == ignoreDiscard:
                 ignoreDiscard = None
             else:
                 if tile not in self._concealedTiles:
                     msg = i18nE(
                         '%1 claiming MahJongg: She does not really have tile %2')
                     return msg, self.name, tile
                 self._concealedTiles.remove(tile)
         if meld.isConcealed and not meld.isKong:
             self._concealedMelds.append(meld)
         else:
             self._exposedMelds.append(meld)
     if self._concealedTiles:
         msg = i18nE(
             '%1 claiming MahJongg: She did not pass all concealed tiles to the server')
         return msg, self.name
     self._hand = None
コード例 #11
0
ファイル: deferredutil.py プロジェクト: zero804/kajongg
    def tell(self, about, receivers, command, **kwargs):
        """send info about player 'about' to users 'receivers'"""
        def encodeKwargs():
            """those values are classes like Meld, Tile etc.
               Convert to bytes"""
            for keyword in kwargs:
                if any(keyword.lower().endswith(x)
                       for x in ('tile', 'tiles', 'meld', 'melds')):
                    if kwargs[keyword] is not None:
                        kwargs[keyword] = str(kwargs[keyword])

        encodeKwargs()
        if about.__class__.__name__ == 'User':
            about = self.playerForUser(about)
        if not isinstance(receivers, list):
            receivers = list([receivers])
        assert receivers, 'DeferredBlock.tell(%s) has no receiver' % command
        self.__enrichMessage(self.table.game, about, command, kwargs)
        aboutName = about.name if about else None
        if self.table.running and len(receivers) in [1, 4]:
            # messages are either identical for all 4 players
            # or identical for 3 players and different for 1 player. And
            # we want to capture each message exactly once.
            self.table.game.appendMove(about, command, kwargs)
        localDeferreds = []
        for rec in self.__convertReceivers(receivers):
            isClient = rec.__class__.__name__.endswith('Client')
            if Debug.traffic and not isClient:
                message = '-> {receiver:<15} about {about} {command}{kwargs}'.format(
                    receiver=rec.name[:15],
                    about=about,
                    command=command,
                    kwargs=Move.prettyKwargs(kwargs))
                logDebug(message)
            if isClient:
                defer = Deferred()
                defer.addCallback(rec.remote_move, command, **kwargs)
            else:
                defer = self.table.server.callRemote(rec, 'move', aboutName,
                                                     command.name, **kwargs)
            if defer:
                defer.command = command.name
                defer.notifying = 'notifying' in kwargs
                self.__addRequest(defer, rec, about)
            else:
                msg = i18nE('The game server lost connection to player %1')
                self.table.abort(msg, rec.name)
            if isClient:
                localDeferreds.append(defer)
        for defer in localDeferreds:
            defer.callback(aboutName)  # callback needs an argument !
コード例 #12
0
ファイル: servertable.py プロジェクト: zero804/kajongg
 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)
コード例 #13
0
ファイル: servertable.py プロジェクト: zero804/kajongg
 def readyForGameStart(self, user):
     """the table initiator told us he wants to start the game"""
     if len(self.users) < self.maxSeats() and self.owner != user:
         raise srvError(
             pb.Error,
             i18nE('Only the initiator %1 can start this game, you are %2'),
             self.owner.name, user.name)
     if self.suspendedAt:
         self.__connectPlayers()
         self.__checkDbIdents()
         self.initGame()
     else:
         self.game = self.__prepareNewGame()
         self.__connectPlayers()
         self.__checkDbIdents()
         self.proposeGameId(self.calcGameId())
コード例 #14
0
ファイル: servertable.py プロジェクト: zero804/kajongg
 def declareKong(self, player, meldTiles):
     """player declares a Kong, meldTiles is a list"""
     kongMeld = Meld(meldTiles)
     if not player.hasConcealedTiles(kongMeld) and kongMeld[
             0].exposed.pung not in player.exposedMelds:
         msg = i18nE('declareKong:%1 wrongly said Kong for meld %2')
         args = (player.name, str(kongMeld))
         logDebug(i18n(msg, *args))
         logDebug('declareKong:concealedTiles:%s' %
                  ''.join(player.concealedTiles))
         logDebug('declareKong:concealedMelds:%s' %
                  ' '.join(str(x) for x in player.concealedMelds))
         logDebug('declareKong:exposedMelds:%s' %
                  ' '.join(str(x) for x in player.exposedMelds))
         self.abort(msg, *args)
         return
     player.exposeMeld(kongMeld)
     self.tellAll(player,
                  Message.DeclaredKong,
                  self.pickKongReplacement,
                  meld=kongMeld)
コード例 #15
0
    def chat(self):
        """chat. Only generate ChatWindow after the
        message has successfully been sent to the server.
        Because the server might have gone away."""
        def initChat(_):
            """now that we were able to send the message to the server
            instantiate the chat window"""
            table.chatWindow = ChatWindow(table=table)
            table.chatWindow.receiveLine(msg)

        table = self.selectedTable()
        if not table.chatWindow:
            line = i18nE('opens a chat window')
            msg = ChatMessage(table.tableid,
                              table.client.name,
                              line,
                              isStatusMessage=True)
            table.client.sendChat(msg).addCallback(initChat).addErrback(
                self.client.tableError)
        elif table.chatWindow.isVisible():
            table.chatWindow.hide()
        else:
            table.chatWindow.show()
コード例 #16
0
ファイル: predefined.py プロジェクト: zero804/kajongg
 def __init__(self, name=None):
     PredefinedRuleset.__init__(self, name
                                or i18nE('Classical Chinese standard'))
コード例 #17
0
class Score(StrMixin):
    """holds all parts contributing to a score. It has two use cases:
    1. for defining what a rules does: either points or doubles or limits, holding never more than one unit
    2. for summing up the scores of all rules: Now more than one of the units can be in use. If a rule
    should want to set more than one unit, split it into two rules.
    For the first use case only we have the attributes value and unit"""

    __hash__ = None

    def __init__(self, points=0, doubles=0, limits=0, ruleset=None):
        self.points = 0  # define the types for those values
        self.doubles = 0
        self.limits = 0.0
        self.ruleset = ruleset
        self.points = type(self.points)(points)
        self.doubles = type(self.doubles)(doubles)
        self.limits = type(self.limits)(limits)

    unitNames = {
        i18nE('points'): 0,
        i18ncE('kajongg', 'doubles'): 50,
        i18ncE('kajongg', 'limits'): 9999
    }

    def clear(self):
        """set all to 0"""
        self.points = self.doubles = self.limits = 0

    def change(self, unitName, value):
        """sets value for unitName. If changed, return True"""
        oldValue = self.__getattribute__(unitName)
        newValue = type(oldValue)(value)
        if newValue == oldValue:
            return False, None
        if newValue:
            if unitName == 'points':
                if self.doubles:
                    return False, 'Cannot have points and doubles'
            if unitName == 'doubles':
                if self.points:
                    return False, 'Cannot have points and doubles'
        self.__setattr__(unitName, newValue)
        return True, None

    def __str__(self):
        """make score printable"""
        parts = []
        if self.points:
            parts.append('points=%d' % self.points)
        if self.doubles:
            parts.append('doubles=%d' % self.doubles)
        if self.limits:
            parts.append('limits=%f' % self.limits)
        return ' '.join(parts)

    def i18nStr(self):
        """make score readable for humans, i18n"""
        parts = []
        if self.points:
            parts.append(i18nc('Kajongg', '%1 points', self.points))
        if self.doubles:
            parts.append(i18nc('Kajongg', '%1 doubles', self.doubles))
        if self.limits:
            limits = str(self.limits)
            if limits.endswith('.0'):
                limits = limits[-2:]
            parts.append(i18nc('Kajongg', '%1 limits', limits))
        return ' '.join(parts)

    def __eq__(self, other):
        """ == comparison """
        assert isinstance(other, Score)
        return self.points == other.points and self.doubles == other.doubles and self.limits == other.limits

    def __ne__(self, other):
        """ != comparison """
        return self.points != other.points or self.doubles != other.doubles or self.limits != other.limits

    def __lt__(self, other):
        return self.total() < other.total()

    def __le__(self, other):
        return self.total() <= other.total()

    def __gt__(self, other):
        return self.total() > other.total()

    def __ge__(self, other):
        return self.total() >= other.total()

    def __add__(self, other):
        """implement adding Score"""
        return Score(self.points + other.points, self.doubles + other.doubles,
                     max(self.limits, other.limits), self.ruleset
                     or other.ruleset)

    def total(self):
        """the total score"""
        score = int(self.points * (2**self.doubles))
        if self.limits:
            if self.limits >= 1:
                self.points = self.doubles = 0
            elif self.limits * self.ruleset.limit >= score:
                self.points = self.doubles = 0
            else:
                self.limits = 0
        if self.limits:
            return int(round(self.limits * self.ruleset.limit))
        if score and not self.ruleset.roofOff:
            score = min(score, self.ruleset.limit)
        return score

    def __int__(self):
        """the total score"""
        return self.total()

    def __nonzero__(self):
        """for bool() conversion"""
        return self.points != 0 or self.doubles != 0 or self.limits != 0
コード例 #18
0
ファイル: predefined.py プロジェクト: zero804/kajongg
 def __init__(self, name=None):
     ClassicalChinese.__init__(self, name
                               or i18nE('Classical Chinese BMJA'))