Exemplo n.º 1
0
def _lockResource(lockName, lockval, relockKey):
    # TODO 处理重入锁定
    lockkey = 'lock:' + lockName + ':' + str(lockval)
    ftlock = _FTLOCKS.get(lockkey, None)
    if ftlock == None:
        ftlock = FTLock(lockkey, relockKey)
        _FTLOCKS[lockkey] = ftlock
    ftlog.debug('lock resource of', lockkey, 'wait !!', relockKey)
    ftlock.lock(relockKey)
    ftlog.debug('lock resource of', lockkey, 'locked !!', relockKey)
    return ftlock
Exemplo n.º 2
0
def _lockResource(lockName, lockval, relockKey):
    # TODO 处理重入锁定
    lockkey = 'lock:' + lockName + ':' + str(lockval)
    ftlock = _FTLOCKS.get(lockkey, None)
    if ftlock == None:
        ftlock = FTLock(lockkey, relockKey)
        _FTLOCKS[lockkey] = ftlock
    ftlog.debug('lock resource of', lockkey, 'wait !!', relockKey)
    ftlock.lock(relockKey)
    ftlog.debug('lock resource of', lockkey, 'locked !!', relockKey)
    return ftlock
Exemplo n.º 3
0
    def __init__(self, room):
        self.room = room
        self.locker = FTLock(self.__class__.__name__ + "_%d" % id(self))

        self._state = self.STATE_IDLE  # 玩家从队列分配到牌桌过程中,不允许玩家leave room,以免造成开局缺人问题。

        # 初始化玩家和牌桌对列
        self.users = OrderedDict()
        self.sittingUsers = set()  # 正在坐下的玩家,不允许离开
        self.activeTableIds = set()
        self._initIdleTableIds()
Exemplo n.º 4
0
 def _findOrCreateLocker(self, key):
     """
     发现或者创建锁
     :param key: 
     :return: 
     """
     locker = self._lockMap.get(key)
     if not locker:
         locker = FTLock('%s_%s' % (id(self), key))
         self._lockMap[key] = locker
     return locker
Exemplo n.º 5
0
def getTcTemplates(moduleKey, funDecode):
    '''
    取得配置系统的一个游戏相关的键值的json对象值(list或dict类型)
    '''
    # 若有缓存,直接返回缓存内容, 其缓存内容为funAdjust的返回值,而非原始数据
    datas = _templatesCache.get(moduleKey, None)
    if datas:
        return datas
    else:
        locker = _templatesLockers.get(moduleKey, None)
        if not locker:
            locker = FTLock(moduleKey)
            _templatesLockers[moduleKey] = locker
        with lock(locker):
            return _getTcTemplates(moduleKey, funDecode)
Exemplo n.º 6
0
 def __init__(self, room, tableId, dealer):
     super(TableBase, self).__init__()
     # 房间
     self._room = room
     # 当前状态
     self._state = dealer.sm.findStateByName('idle')
     # 桌子ID
     self._tableId = tableId
     # 玩法
     self._dealer = dealer
     # 桌子定时器
     self._timer = None
     # 所有座位
     self._seats = []
     # 锁
     self.locker = FTLock(self.__class__.__name__ + "_%d" % id(self))
Exemplo n.º 7
0
def handlerCommand(msg):
    """
    TCP消息命令处理总入口
    """
    orgcmd = msg.getCmd()
    if orgcmd == _runenv._CMD_RPC_:
        return rpccore._handlerRpcCommand(msg)

    orgaction = msg.getParam('action')
    gameId = msg.getParam('gameId')
    cmd, action = None, None
    try:
        if orgaction == None:
            orgaction = ''
        # ftlog.debug('handlerCommand->cmd, action->', cmd, action)
        cmdpath, cmd, action = oldcmd.convertCmdPath(orgcmd, orgaction, msg)
        #         ftlog.debug('handlerCommand->cmd, action, cmdpath, ver, gameId ->', cmd, action, cmdpath, ver, gameId)
        # 先以cmd#action#gameId去查找命令处理器
        vercalls = None
        if gameId:
            vercalls = _runenv._cmd_path_methods.get(cmdpath + '#' + str(gameId), None)
        # 再以cmd#action去查找命令处理器
        if not vercalls:
            vercalls = _runenv._cmd_path_methods.get(cmdpath, None)
        # 再以cmd#*去查找名利处理器
        if not vercalls:
            vercalls = _runenv._cmd_path_methods.get(str(cmd) + '#*#' + str(gameId), None)
        # 再以cmd#*去查找名利处理器
        if not vercalls:
            vercalls = _runenv._cmd_path_methods.get(str(cmd) + '#*', None)
        # ftlog.debug('handlerCommand->vercalls->', vercalls)
        # 再经过clietnId的版本号过滤
        markParams = None
        if vercalls:
            if len(vercalls) > 1:
                ver = getClientIdVer(msg)
                for vercall in vercalls:
                    if ver >= vercall[0]:
                        markParams = vercall[1]
                        break
            else:
                markParams = vercalls[0][1]
        else:
            vercalls = []
        # 若未找到对应的mark定义, 错误返回
        if not markParams:
            raise Exception('the cmd path method not found ! cmdpath=' + str(cmdpath) +
                            ' clientIdVer=' + str(getClientIdVer(msg)) + ' vercalls len=' + str(len(vercalls)) +
                            ' gameId=' + str(gameId) + ' msg=' + str(msg))
        # 锁定当前mark定义的资源
        lockParamName = markParams.get('lockParamName', None)
        if lockParamName:
            lockval = msg.getParam(lockParamName)
            if lockParamName == 'userId' or lockParamName == 'tableId':
                assert (isinstance(lockval, int))
            lockkey = 'lock:' + lockParamName + ':' + str(lockval)
            ftlock = _runenv._FTLOCKS.get(lockkey, None)
            if ftlock == None:
                ftlock = FTLock(lockkey)
                _runenv._FTLOCKS[lockkey] = ftlock
            ftlog.debug('lock resource of', lockkey, 'wait !!')
            ftlock.lock()
            ftlog.debug('lock resource of', lockkey, 'locked !!')

        try:
            if markParams['isRpc']:
                # RPC CALL
                _handlerRpcCommand(markParams, msg)
            else:
                # CLIENT MSG CALL
                _handlerMsgCommand(markParams, msg)
        finally:
            # 释放当前mark定义的资源
            if lockParamName:
                ftlog.debug('lock resource of', lockkey, 'released !!')
                if ftlock.unlock() == 0:
                    del _runenv._FTLOCKS[lockkey]
                    pass
    except Exception, e:
        ftlog.error('cmd=' + str(cmd) + ' action=' + str(action) + ' gameId=' + str(gameId),
                    'orgcmdpath=' + str(orgcmd) + '#' + str(orgaction))
        if router.isQuery():
            targs = ftsvr.getTaskRunArg()
            if not targs.get('responsed') and targs.get('userheader1') == 'CQ':
                response = newErrorMsgPack(1, str(e))
                router.responseQurery(response)
Exemplo n.º 8
0
def handlerCommand(msg):
    """
    TCP消息命令处理总入口
    """
    orgcmd = msg.getCmd()
    if orgcmd == _runenv._CMD_RPC_:
        return rpccore._handlerRpcCommand(msg)

    orgaction = msg.getParam('action')
    gameId = msg.getParam('gameId')
    cmd, action = None, None
    try:
        if orgaction == None:
            orgaction = ''
        # ftlog.debug('handlerCommand->cmd, action->', cmd, action)
        cmdpath, cmd, action = oldcmd.convertCmdPath(orgcmd, orgaction, msg)
        #         ftlog.debug('handlerCommand->cmd, action, cmdpath, ver, gameId ->', cmd, action, cmdpath, ver, gameId)
        # 先以cmd#action#gameId去查找命令处理器
        vercalls = None
        if gameId:
            vercalls = _runenv._cmd_path_methods.get(
                cmdpath + '#' + str(gameId), None)
        # 再以cmd#action去查找命令处理器
        if not vercalls:
            vercalls = _runenv._cmd_path_methods.get(cmdpath, None)
        # 再以cmd#*去查找名利处理器
        if not vercalls:
            vercalls = _runenv._cmd_path_methods.get(
                str(cmd) + '#*#' + str(gameId), None)
        # 再以cmd#*去查找名利处理器
        if not vercalls:
            vercalls = _runenv._cmd_path_methods.get(str(cmd) + '#*', None)
        # ftlog.debug('handlerCommand->vercalls->', vercalls)
        # 再经过clietnId的版本号过滤
        markParams = None
        if vercalls:
            if len(vercalls) > 1:
                ver = getClientIdVer(msg)
                for vercall in vercalls:
                    if ver >= vercall[0]:
                        markParams = vercall[1]
                        break
            else:
                markParams = vercalls[0][1]
        else:
            vercalls = []
        # 若未找到对应的mark定义, 错误返回
        if not markParams:
            raise Exception('the cmd path method not found ! cmdpath=' +
                            str(cmdpath) + ' clientIdVer=' +
                            str(getClientIdVer(msg)) + ' vercalls len=' +
                            str(len(vercalls)) + ' gameId=' + str(gameId) +
                            ' msg=' + str(msg))
        # 锁定当前mark定义的资源
        lockParamName = markParams.get('lockParamName', None)
        if lockParamName:
            lockval = msg.getParam(lockParamName)
            if lockParamName == 'userId' or lockParamName == 'tableId':
                assert (isinstance(lockval, int))
            lockkey = 'lock:' + lockParamName + ':' + str(lockval)
            ftlock = _runenv._FTLOCKS.get(lockkey, None)
            if ftlock == None:
                ftlock = FTLock(lockkey)
                _runenv._FTLOCKS[lockkey] = ftlock
            ftlog.debug('lock resource of', lockkey, 'wait !!')
            ftlock.lock()
            ftlog.debug('lock resource of', lockkey, 'locked !!')

        try:
            if markParams['isRpc']:
                # RPC CALL
                _handlerRpcCommand(markParams, msg)
            else:
                # CLIENT MSG CALL
                _handlerMsgCommand(markParams, msg)
        finally:
            # 释放当前mark定义的资源
            if lockParamName:
                ftlog.debug('lock resource of', lockkey, 'released !!')
                if ftlock.unlock() == 0:
                    del _runenv._FTLOCKS[lockkey]
                    pass
    except Exception, e:
        ftlog.error(
            'cmd=' + str(cmd) + ' action=' + str(action) + ' gameId=' +
            str(gameId), 'orgcmdpath=' + str(orgcmd) + '#' + str(orgaction))
        if router.isQuery():
            targs = ftsvr.getTaskRunArg()
            if not targs.get('responsed') and targs.get('userheader1') == 'CQ':
                response = newErrorMsgPack(1, str(e))
                router.responseQurery(response)
Exemplo n.º 9
0
 def __init__(self):
     self.locker = FTLock(self.__class__.__name__ + '_%d' % id(self))
Exemplo n.º 10
0
class LedUtils2(object):

    locker = FTLock("LedUtils")

    @classmethod
    def _mkRichTextBody(cls, content):
        return [{'color': color, 'text': text} for color, text in content]

    @classmethod
    def _mkRichTextLedBody(cls, content, _type='led', roomId=0, tableId=0):
        """ _type:
        led      纯LED消息,带颜色
        watch    LED消息,带颜色,观战按钮
        vip      LED消息,带颜色,进入(坐下)按钮
        mtt
        """

        richText = {
            'text': cls._mkRichTextBody(content),
            'type': _type,
            'roomId': roomId,
            'tableId': tableId
        }

        return RICH_TEXT_LED_MSG_HEADER + json.dumps({'richText': richText})

    @classmethod
    def sendToAllCoServer(cls, mo):
        ''' 发送消息到所有TCP管理服务器'''
        msgpack = mo.pack()
        coSrvIds = gdata.serverTypeMap()[gdata.SRV_TYPE_CONN]
        for coSrvId in coSrvIds:
            wrapper.send(coSrvId, msgpack, "S1", "")

    @classmethod
    def sendRealTimeLed(cls, gameId, plain_text, receivers=[]):
        """ 发送一条实时LED消息
        receivers: 接收者USERID列表。如果为[],发给所有在线玩家 """

        content = [['FFFFFF', plain_text]]
        cls.sendRealTimeColorfulLed(gameId, content, receivers)

    @classmethod
    def sendRealTimeColorfulLed(cls, gameId, content, receivers=[]):
        """ 发送一条实时LED消息
        receivers: 接收者USERID列表。如果为[],发给所有在线玩家 """

        mo = MsgPack()
        mo.setCmd('game_public_led')
        mo.setParam('gameId', gameId)
        mo.setParam('receivers', receivers)
        mo.setParam('msg', cls._mkRichTextLedBody(content))
        mo.setParam('ledWithTodoTask', {'text': cls._mkRichTextBody(content)})

        cls.sendToAllCoServer(mo)

    @classmethod
    def sendled(cls, gameId, plain_text):
        """ 发送一条实时GDSS那样的LED消息。玩家会在心跳协议中陆续收到该消息"""
        mo = MsgPack()
        mo.setCmd('send_led')
        mo.setParam('msg', plain_text)
        mo.setParam('gameId', gameId)
        mo.setParam('ismgr', 1)
        router.sendUtilServer(mo)

    @classmethod
    @locked
    def _doGamePublicLed(cls, mi):
        '''
        这个方法在CO进程中由game_public_led的消息handler进行调用,
        不允许在其它进程中调用
        '''
        assert (gdata.serverType() == gdata.SRV_TYPE_CONN)

        ftlog.debug("<< |msgPackIn:", mi, caller=cls)

        # 如果从 result 中获取不到数据,从 param 中获取,还取不到,用默认值
        gameId = mi.getResult('gameId', 0) or mi.getParam('gameId') or 0
        receivers = mi.getResult('receivers') or mi.getParam('receivers') or []
        excludeUsers = mi.getResult('excludeUsers') or mi.getParam(
            'excludeUsers') or set()
        msg = mi.getResult('msg') or mi.getParam('msg')
        timelimit = mi.getResult('timelimit') or mi.getResult(
            'timelimit') or {}
        force = mi.getResult('force') or mi.getParam('force') or [
        ]  # 这里指定的用户不能过滤,必须收

        intervals = timelimit.get('timeLimitIntervals')  # 这个时间内收过led的不再收
        limitName = timelimit.get('timeLimitName')  # 时间间隔的种类,反射机制设置到 user 对象中去

        msg = cls._tryParseRichTextLed(msg)
        leds = [[0, gameId, msg]]
        ftlog.debug("|leds:", leds, caller=cls)

        popWinInfo = mi.getResult('popWin')
        ledWithTodoTask = mi.getResult('ledWithTodoTask') or mi.getParam(
            'ledWithTodoTask')
        #         if not ledWithTodoTask:
        #             ledWithTodoTask = Message._richText2Todotask(mi, msg) #TODO:

        if gameId <= 0:
            ftlog.error(
                "doGamePublicLed: error: game not found or gameid is 0",
                gameId)
            return

        mo_cache = {}  # 缓存不同版本的 led消息,避免每次生成,浪费CPU

        def mo_cache_key(gameId, clientVer, clientId):
            if gameId == 8 and clientVer == 3.37 and 'hall8' in clientId:
                return gameId, clientVer, 'hall8'
            return gameId, clientVer

        def make_led_mo(newleds, clientVer, clientId):
            mo = MsgPack()
            mo.setCmd('led')

            if clientVer >= 3.6:
                if ledWithTodoTask:
                    mo.setKey('result', ledWithTodoTask)
                    return mo.pack()
                return None

            if newleds['origLeds']:
                mo.setKey('result', newleds['origLeds'])
            if newleds['richLeds']:
                mo.setKey('result', newleds['richLeds'])
                mo.setKey('richText', newleds['richLeds'])
                mo.setResult('gameId', gameId)

                if gameId == 8:
                    if clientVer < 3.0:
                        # 德州老单包有问题,所有格式都转为没有按钮的LED (也就是type='led')
                        newleds['richLeds']['type'] = 'led'
                    elif newleds['richLeds'][
                            'type'] == 'vip' and clientVer == 3.37 and 'hall8' in clientId:
                        # 德州大厅3.37版本BUG: 如果消息中有 richText,则收不到消息
                        mo.rmKey('richText')
            if popWinInfo and clientId.startswith('Winpc'):
                mo.setResult('popWin', popWinInfo)
            return mo.pack()

        def sendled():
            newleds = cls._getLedMsgLists(leds, gameId, userId)
            clientVer = sessiondata.getClientIdVer(userId)
            clientId = sessiondata.getClientId(userId)
            if newleds['richLeds'] or newleds['origLeds']:
                key = mo_cache_key(gameId, clientVer, clientId)
                if key not in mo_cache:
                    mo_cache[key] = make_led_mo(newleds, clientVer, clientId)
                mo = mo_cache[key]
                if mo:
                    protocols.sendCarryMessage(mo, [userId])

        if receivers:
            receivers = receivers + force
        else:
            receivers = protocols._ONLINE_USERS.keys()

        #需求:两次同类型led之间需要相隔intervals
        now = time.time()
        sc = 0
        for userId in receivers:
            sc += 1
            if sc % 20 == 0:
                FTTasklet.getCurrentFTTasklet().sleepNb(0.1)
            if userId in force:
                if limitName:
                    #                     setattr(user, limitName, now)
                    userdata.setAttr(userId, limitName, now)
                sendled()
                continue

            if userId not in excludeUsers:
                if limitName and intervals:  # 时间限制
                    #                     lastSendTime = getattr(user, limitName, 0)
                    lastSendTime = userdata.getAttr(userId, limitName)
                    if not lastSendTime:
                        lastSendTime = 0
                    ftlog.debug('|userId, limitName, intervals, lastSendTime:',
                                userId,
                                limitName,
                                intervals,
                                lastSendTime,
                                caller=cls)
                    if lastSendTime + intervals <= now:
                        userdata.setAttr(userId, limitName, now)
                        sendled()
                else:
                    sendled()

    @classmethod
    def _getLedMsgLists(cls, leds, gameId, userId):
        if not leds:
            return

        newleds = {
            'richLeds': [],  # 带格式的 led 消息
            'origLeds': [],  # 纯文本的 led 消息
        }

        clientVer = sessiondata.getClientIdVer(userId)

        # 过滤 led 消息。过滤器把通过过滤器的消息加入到 richLeds 或者 origLeds 里
        for led in leds:
            if cls._ledMsgFilterRich(gameId, userId, clientVer, led, newleds):
                continue
            if cls._ledMsgFilterOrig(gameId, userId, clientVer, led, newleds):
                continue

        return newleds

    @classmethod
    def _ledMsgFilterOrig(cls, gameId, userId, clientVer, led, newleds):
        """ 这是以前的过滤方法 """

        _id, _gid, ledmsg = led
        if isinstance(ledmsg, str) and ledmsg[0] == '#':
            #             cutVer = TyContext.Configure.get_game_item_float(gameId, 'version.2.2', 2.15)
            cutVer = 2.15  # TODO:
            if clientVer and clientVer < cutVer:
                ledmsg = ledmsg[7:]
        newleds['origLeds'].append([_id, _gid, ledmsg])

        return True

    @classmethod
    def _ledMsgFilterRich(cls, gameId, userId, clientVer, led, newleds):
        """ 过滤富文本 led 消息 """

        _id, _gid, ledmsg = led
        isRichTextLed = isinstance(ledmsg, dict) and ledmsg.has_key('richText')
        if not isRichTextLed:
            # 德州1.67版本LED有bug,这是bug处理,3.37解决bug,不对3.37以上的版本做处理,但是3.37的插件又存在这个bug
            if gameId == 8:
                # 可以接收富文本格式的LED消息的客户端最低版本
                versionCanReceiveRichTextLed = 1.67
                versionHaveLedBug = 1.67
                versionLedBugFixed = 3.37
                clientId = sessiondata.getClientId(userId)
                if versionLedBugFixed > clientVer >= versionHaveLedBug or 'hall8' not in clientId:
                    ledmsg = {
                        "richText": {
                            "text": [{
                                "color": "FFFFFF",
                                "text": ledmsg
                            }],
                            "type": "led",
                            "gameId": 8
                        },
                        "excludeUsers": set()
                    }
                    ftlog.debug("convert orig  led to richtext led", ledmsg)
                else:
                    return False
            else:
                return False

        # 加入 exclude 机制:业务需求要求一部分LED实时发送给用户,其余用户
        # 保留现有心跳时接收 LED 方式。为了让收过LED的用户不再重复接收,
        # 凡是在 excludeUsers 中的用户就不再接收 LED 了。
        if userId in ledmsg['excludeUsers']:
            return False

        if gameId == 8:
            # 可以接收富文本格式的LED消息的客户端最低版本
            versionCanReceiveRichTextLed = 1.67
            versionHaveLedBug = 1.67
            versionLedBugFixed = 3.37
            # 德州 1.67 版本加入富文本消息
            if clientVer < versionCanReceiveRichTextLed:
                newleds['origLeds'].append([_id, _gid, ledmsg['plainText']])

            # 1.67版本LED有bug,这是bug处理
            elif clientVer >= versionHaveLedBug:
                newleds['richLeds'] = ledmsg['richText']
                newleds['origLeds'] = ledmsg['richText']
            else:
                newleds['richLeds'].append(ledmsg['richText'])
            return True
        elif gameId == 1:
            newleds['richLeds'] = ledmsg['richText']
            newleds['origLeds'] = ledmsg['richText']
            return True

        return False

    @classmethod
    def _tryParseRichTextLed(cls, msg):
        """检查led消息是否富文本格式。
        如果是,解析格式并返回;如否,原样返回;如果出错,返回None

        msg json 格式示例:
        {
            'richText': {
                'text': [{
                    "color": "RRGGBB",
                    "text": "aaa"
                },{
                    "color": "RRGGBB",
                    "text": "bbbccc"
                }],
            },
            'excludeUsers': [123456, 32134534]
        }
        """

        header = 'richTextLedMsg'
        if not msg.startswith(header):
            return msg

        try:
            ledmsg = json.loads(msg[len(header):])
            ledmsg['excludeUsers'] = set(ledmsg.get('excludeUsers', []))
            ledmsg['plainText'] = u''.join(
                (unicode(seg['text']) for seg in ledmsg['richText']['text']))
            return ledmsg
        except:
            ftlog.error("load led json error", msg)

    @classmethod
    @catchedmethod
    def _richText2Todotask(cls, gamePublicLedMsg, ledMsg):
        '''richTextLed 消息转换为 game_enter todotask 格式消息
        因为 decodeMsgV2 用的 todotask 不是 enter_game,且不知道有没有游戏已经用了那个,所以才写了这个
        在实时LED里用(conn.py: doGamePublicLed)
        '''
        text = ledMsg.get('richText', {}).get('text', [])
        gameId = gamePublicLedMsg.getResult('gameId')
        roomId = ledMsg.get('roomId')
        tableId = ledMsg.get('tableId')
        ledType = ledMsg.get('type')
        gameMark = {
            1: 't3card',
            3: 'chess',
            6: 'ddz',
            7: 'majiang',
            8: 'texas',
            10: 'douniu'
        }.get(gameId)
        lbl = {'vip': u'进入', 'watch': u'观战'}.get(ledType)
        if not (text and gameId and roomId and tableId and ledType and gameMark
                and lbl):
            return

        gameType = ledMsg.get('gameType', 1)
        todotaskParams = {
            'gameId': gameId,
            'roomId': roomId,
            'tableId': tableId,
            'type': ledType
        }
        tasks = [
            TodoTaskEnterGame(gameMark, gameType, **todotaskParams).toDict()
        ]
        ledresult = {'text': text, 'lbl': lbl, 'tasks': tasks}

        return ledresult