ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
@@ -19,55 +19,250 @@
#---------------------------------------------------------------------
import GameWorld
import GameWorship
import GameXiangong
import PlayerControl
import NetPackCommon
import GameWorldArena
import ChPyNetSendPack
import PlayerFBHelpBattle
import GameWorldSkyTower
import CrossChampionship
import CrossBattlefield
import CrossRealmPlayer
import PyGameDataStruct
import PlayerPackData
import IpyGameDataPY
import PyDataManager
import CrossRealmMsg
import ShareDefine
import PyGameData
import CommFunc
import ChPlayer
import ChConfig
import json
import time
import random
TempCache = PyGameDataStruct.tagPlayerViewCachePy()
#玩家缓存管理,该类只做数据缓存存取,不写功能逻辑,防止重读脚本时功能逻辑脚本不生效
class PlayerViewCachePyManager(object):
    def __init__(self):
        self.__viewCacheList = [] # [tagPlayerViewCachePy, ...]
        self.__idIndexDict = {} # {playerID:index, ...}
        self.__needSort = False
        self.__serverIDRangePlayerIDDict = {} # {serverIDRangeTuple:[playerID, ...], ....}
        return
    def GetPlayerViewCache(self, playerID):
        self.__refreshIDIndex()
        viewCache = None
        if playerID in self.__idIndexDict:
            index = self.__idIndexDict[playerID]
            if index < len(self.__viewCacheList):
                viewCache = self.__viewCacheList[index]
        return viewCache
    def AddPlayerViewCache(self, playerID, viewCache):
        self.__refreshIDIndex()
        if playerID in self.__idIndexDict:
            return
        viewCache.PlayerID = playerID
        self.__viewCacheList.append(viewCache)
        self.__idIndexDict[playerID] = len(self.__viewCacheList) - 1
        self.__needSort = True
        return
    def GetPlayerIDListByServerIDInfo(self, serverIDList):
        ## 根据服务器ID列表信息获取对应服务器ID范围的玩家ID战力排序列表
        if serverIDList == None:
            return []
        self.Sort()
        key = tuple(serverIDList)
        if key not in self.__serverIDRangePlayerIDDict:
            playerIDList = []
            for viewCache in self.__viewCacheList:
                playerID = viewCache.PlayerID
                cacheDict = GetCachePropDataDict(viewCache)
                if not cacheDict:
                    continue
                serverID = GameWorld.GetAccIDServerID(cacheDict["AccID"])
                for idInfo in serverIDList:
                    if (isinstance(idInfo, int) and serverID == idInfo) \
                        or ((isinstance(idInfo, tuple) or isinstance(idInfo, list)) \
                            and len(idInfo) == 2 and idInfo[0] <= serverID <= idInfo[1]):
                        playerIDList.append(playerID)
            GameWorld.DebugLog("重新加载区服玩家查看缓存ID列表: %s, %s, %s" % (key, len(playerIDList), playerIDList))
            self.__serverIDRangePlayerIDDict[key] = playerIDList
        return self.__serverIDRangePlayerIDDict[key]
    def IsPlayerIn(self, playerID):
        self.__refreshIDIndex()
        return playerID in self.__idIndexDict
    def __refreshIDIndex(self):
        if not self.__idIndexDict:
            self.__idIndexDict = {}
            for index, viewCache in enumerate(self.__viewCacheList):
                self.__idIndexDict[viewCache.PlayerID] = index
        return self.__idIndexDict
    def DelPlayerViewCache(self, playerID):
        self.__refreshIDIndex()
        index = self.__idIndexDict.pop(playerID, -1)
        if index >= 0 and index < len(self.__viewCacheList):
            self.__viewCacheList.pop(index)
        for playerIDList in self.__serverIDRangePlayerIDDict.values():
            if playerID in playerIDList:
                playerIDList.remove(playerID)
        self.__idIndexDict = {}
        self.__serverIDRangePlayerIDDict = {}
        return
    def GetCount(self): return len(self.__viewCacheList)
    def At(self, index):
        viewCache = self.__viewCacheList[index]
        if not viewCache and False:
            viewCache = PyGameDataStruct.tagPlayerViewCachePy() # 不会执行到,只为了.出代码提示
        return viewCache
    def Sort(self):
        ## 默认按战力倒序排
        if not self.__needSort:
            return
        self.__needSort = False
        self.__viewCacheList.sort(cmp=self.__cmp)
        self.__idIndexDict = {}
        self.__serverIDRangePlayerIDDict = {}
        self.__refreshIDIndex()
        return
    def __cmp(self, a, b):
        ## 按战力倒序, cmp模式排序效率较低,如果需要再改为key模式
        aFightPower = 0
        cacheDict = GetCachePropDataDict(a)
        if cacheDict:
            aFightPower = cacheDict.get("FightPower", 0)
        bFightPower = 0
        cacheDict = GetCachePropDataDict(b)
        if cacheDict:
            bFightPower = cacheDict.get("FightPower", 0)
        return cmp(bFightPower, aFightPower)
    # 保存数据 存数据库和realtimebackup
    def GetSaveData(self):
        savaData = ""
        cntData = ""
        cnt = 0
        for dbData in self.__viewCacheList:
            #if dbData.PlayerID < 10000:
                # 假人玩家不存储
            #    continue
            cnt += 1
            savaData += dbData.getBuffer()
        GameWorld.Log("Save PlayerViewCachePy count :%s len=%s" % (cnt, len(savaData)))
        return CommFunc.WriteDWORD(cntData, cnt) + savaData
    # 从数据库载入数据
    def LoadPyGameData(self, datas, pos, dataslen):
        cnt, pos = CommFunc.ReadDWORD(datas, pos)
        GameWorld.Log("Load PlayerViewCachePy count :%s" % cnt)
        for _ in xrange(cnt):
            dbData = PyGameDataStruct.tagPlayerViewCachePy()
            dbData.clear()
            pos += dbData.readData(datas, pos, dataslen)
            self.AddPlayerViewCache(dbData.PlayerID, dbData)
        self.Sort()
        return pos
def DoOnDayEx():
    DelOutofTimeViewCacheData()
    return
def IsSaveDBViewCache(playerID, playerLV):
    ## 是否保存基本的缓存数据
def IsSaveDBViewCache(viewCache):
    ## 缓存数据是否入库
    if not viewCache:
        return False
    playerID = viewCache.PlayerID
    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(playerID):
        return True
    
    if GameWorldArena.IsArenaBattlePlayer(playerID):
        return True
    if CrossBattlefield.IsBattlefieldCallPlayer(playerID):
        return True
    if CrossChampionship.IsChampionshipPlayer(playerID):
        return True
    if GameWorship.IsWorshipPlayer(playerID):
        return True
    if PyDataManager.GetDBPyFuncTeamManager().IsTeamPlayer(playerID):
        return True
    if GameXiangong.IsXiangongPlayer(playerID):
        return True
    if GameWorldSkyTower.IsSkyTowerPassPlayer(playerID):
        return True
    if PlayerPackData.IsPackDataPlayer(playerID):
        return True
    #跨服榜单上的默认保留
    if GameWorld.IsCrossServer():
        billboardMgr = PyDataManager.GetCrossBillboardManager()
        for billboardType in ShareDefine.CrossBillboardTypeList:
            groupList = billboardMgr.GetBillboardGroupList(billboardType)
            for billboardType, groupValue1, groupValue2 in groupList:
                billboardObj = billboardMgr.GetCrossBillboard(billboardType, groupValue1, groupValue2)
                if billboardObj.FindByID(playerID):
                    return True
    else:
        NeedCheckBillBoardType = IpyGameDataPY.GetFuncEvalCfg("PlayerViewCache", 2)
        #校验玩家是否上排行榜
        billboardMgr = GameWorld.GetBillboard()
        for BillBoardType in NeedCheckBillBoardType:
            curBillboard = billboardMgr.FindBillboard(BillBoardType)
            if not curBillboard:
                continue
            if curBillboard.FindByID(playerID):
                return True
    # 以上是相关功能需要用到的数据,必定不能删除的
    # 以下是保留近期活跃玩家,等级限制
    playerLV = viewCache.LV
    offTime = viewCache.OffTime
    if not playerLV and not offTime:
        # 跨服服务器之前某些情况没有存储LV及OffTime,防止误删,做旧数据兼容用
        return True
    
    SaveDBLimitLV = IpyGameDataPY.GetFuncCfg("PlayerViewCache", 1)
    #校验玩家等级
    if playerLV < SaveDBLimitLV:
        return False
    return True
def IsSaveAllViewCache(playerID):
    ## 是否保存所有缓存数据
    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(playerID):
        return True
    if GameWorldArena.IsArenaBattlePlayer(playerID):
        return True
    NeedCheckBillBoardType = IpyGameDataPY.GetFuncEvalCfg("PlayerViewCache", 2)
    #校验玩家是否上排行榜
    billboardMgr = GameWorld.GetBillboard()
    for BillBoardType in NeedCheckBillBoardType:
        curBillboard = billboardMgr.FindBillboard(BillBoardType)
        if not curBillboard:
            continue
        if curBillboard.FindByID(playerID):
    if playerLV >= SaveDBLimitLV:
        maxDays = IpyGameDataPY.GetFuncCfg("PlayerViewCache", 3)
        if not maxDays:
            maxDays = 7 # 默认7天
        MaxTime = maxDays * 3600 * 24
        curTime = int(time.time())
        passTime = curTime - viewCache.OffTime
        if passTime < MaxTime:
            return True
        
    return False
@@ -75,51 +270,163 @@
def DelOutofTimeViewCacheData():
    ## 删除过期的查看缓存数据
    
    curTime = int(time.time())
    MaxTime = 30 * 3600 * 24 # 30天
    onlineMgr = ChPlayer.GetOnlinePlayerMgr()
    pyViewCacheMgr = PyDataManager.GetPlayerViewCachePyManager()
    playerViewCachePyDict = pyViewCacheMgr.playerViewCachePyDict
    for playerID, viewCache in playerViewCachePyDict.items():
        passTime = curTime - viewCache.OffTime
        if passTime < MaxTime:
    for index in range(pyViewCacheMgr.GetCount())[::-1]: # 有删除需倒序遍历
        viewCache = pyViewCacheMgr.At(index)
        if IsSaveDBViewCache(viewCache):
            continue
        if IsSaveAllViewCache(playerID):
        playerID = viewCache.PlayerID
        if onlineMgr.IsOnline(playerID):
            #在线的先不删除
            continue
        playerViewCachePyDict.pop(playerID)
        pyViewCacheMgr.DelPlayerViewCache(playerID)
        
    PyGameData.g_crossPlayerViewCache = {} # 每日直接清空跨服玩家查看缓存
    return
def DeleteViewCache(playerID):
    ## 删除玩家缓存
    pyViewCacheMgr = PyDataManager.GetPlayerViewCachePyManager()
    playerViewCachePyDict = pyViewCacheMgr.playerViewCachePyDict
    playerViewCachePyDict.pop(playerID, None)
    pyViewCacheMgr.DelPlayerViewCache(playerID)
    GameWorld.DebugLog("删除查看缓存!", playerID)
    return
def FindViewCache(playerID, isAddNew=False):
def FindViewCache(playerID, isAddNew=False, newPropData=None):
    ## 查找玩家缓存
    curCache = None
    # @param newPropData: 新数据初始PropData {}, key: LV,RealmLV,Job,VIPLV,Name,FamilyID,FamilyName,FightPower
    pyViewCacheMgr = PyDataManager.GetPlayerViewCachePyManager()
    playerViewCachePyDict = pyViewCacheMgr.playerViewCachePyDict
    if playerID in playerViewCachePyDict:
        curCache = playerViewCachePyDict[playerID]
    elif isAddNew:
    curCache = pyViewCacheMgr.GetPlayerViewCache(playerID)
    if curCache:
        pass
    elif isAddNew or playerID < 10000:
        # 内网测试假玩家
        if playerID < 10000:
            if not newPropData:
                newPropData = {}
            fakeName = newPropData.get("Name", "")
            if not fakeName:
                fakeName = "神秘道友".decode(ShareDefine.Def_Game_Character_Encoding).encode(GameWorld.GetCharacterEncoding())
                fakeName = "%s%s" % (fakeName, playerID)
            accID = newPropData.get("AccID", "")
            if not accID:
                serverID = playerID % 100 + 1 # 1 ~ 100 服
                accID = "fake%s@test@s%s" % (playerID, serverID)
            else:
                serverID = GameWorld.GetAccIDServerID(accID)
            serverGroupID = serverID
            if serverID < 50:
                serverGroupID = serverGroupID / 10 + 1 # 前50服每10服1主服
            isOnline = True if playerID % 2 == 0 else False
            olMgr = ChPlayer.GetOnlinePlayerMgr()
            olMgr.SetOnlineState(playerID, isOnline, serverGroupID)
            job = newPropData.get("Job", 0)
            if not job:
                openJobList = IpyGameDataPY.GetFuncEvalCfg("OpenJob", 1)
                job = random.choice(openJobList) if openJobList else 1
            lv = newPropData.get("LV", random.randint(100, 200))
            realmLV = newPropData.get("RealmLV", random.randint(5, 15))
            fightPower = newPropData.get("FightPower", random.randint(1000000, 100000000))
            newPropData.update({"AccID":accID, "PlayerID":playerID, "Name":fakeName, "Job":job, "LV":lv,
                                "RealmLV":realmLV, "FightPower":fightPower, "ServerGroupID":serverGroupID,})
        curCache = PyGameDataStruct.tagPlayerViewCachePy()
        curCache.PlayerID = playerID
        playerViewCachePyDict[playerID] = curCache
        curCache.OffTime = int(time.time())
        if newPropData:
            curCache.PropData = json.dumps(newPropData, ensure_ascii=False).replace(" ", "")
            curCache.PropDataSize = len(curCache.PropData)
        pyViewCacheMgr.AddPlayerViewCache(playerID, curCache)
    return curCache
def GetCachePropDataDict(curCache):
    ## 获取缓存基础属性字典信息
    if not curCache:
        return {}
    if not hasattr(curCache, "PropDataDict"):
        curCache.PropDataDict = {}
    if not curCache.PropDataDict:
    if not curCache.PropDataDict and curCache.PropData:
        curCache.PropDataDict = eval(curCache.PropData)
    return curCache.PropDataDict
def GetShotCacheDict(playerID, *exAttrs):
    ## 获取玩家简短的缓存信息字典
    viewCache = FindViewCache(playerID)
    cacheDict = GetCachePropDataDict(viewCache)
    if not cacheDict:
        return {}
    shotCacheDict = {
                     "Name":cacheDict["Name"],
                     "Job":cacheDict["Job"],
                     "LV":cacheDict["LV"],
                     "RealmLV":cacheDict["RealmLV"],
                     }
    for attrName in exAttrs:
        if attrName == "PlayerID":
            shotCacheDict["PlayerID"] = playerID
        elif attrName == "ServerID":
            shotCacheDict["ServerID"] = GameWorld.GetAccIDServerID(cacheDict["AccID"])
        elif attrName == "OfflineValue":
            olMgr = ChPlayer.GetOnlinePlayerMgr()
            shotCacheDict["OfflineValue"] = olMgr.GetOfflineValue(playerID, viewCache)
        # 附带外观模型展示相关
        elif attrName == "Model":
            shotCacheDict.update({
                                  "TitleID":cacheDict.get("TitleID", 0),
                                  "EquipShowSwitch":cacheDict.get("EquipShowSwitch", 0),
                                  "EquipShowID":cacheDict.get("EquipShowID", []),
                                  })
        elif attrName in cacheDict:
            shotCacheDict[attrName] = cacheDict[attrName]
    return shotCacheDict
def GetSyncCrossCacheBase(curPlayer):
    ## 获取同步跨服基础查看缓存,主要用于个别功能需要提前先同步玩家基础缓存到跨服,因为跨服不一定有玩家缓存,需要提前同步
    if isinstance(curPlayer, int):
        playerID = curPlayer
        curPlayer = None
    else:
        playerID = curPlayer.GetPlayerID()
    cacheDict = GetCachePropDataDict(FindViewCache(playerID))
    cacheBase = {
                 "AccID":curPlayer.GetAccID() if curPlayer else cacheDict.get("AccID", ""),
                 "LV":curPlayer.GetLV() if curPlayer else cacheDict.get("LV", 1),
                 "RealmLV":curPlayer.GetOfficialRank() if curPlayer else cacheDict.get("RealmLV", 1),
                 "Job":curPlayer.GetJob() if curPlayer else cacheDict.get("Job", 1),
                 "VIPLV":curPlayer.GetVIPLv() if curPlayer else cacheDict.get("VIPLV", 0),
                 "Name":curPlayer.GetName() if curPlayer else cacheDict.get("Name", ""), # 此处不用跨服名称,如前端需要展示跨服名称,可通过ServerID或AccID取得ServerID展示
                 "Face":curPlayer.GetFace() if curPlayer else cacheDict.get("Face", 0),
                 "FacePic":curPlayer.GetFacePic() if curPlayer else cacheDict.get("FacePic", 0),
                 "FamilyID":curPlayer.GetFamilyID() if curPlayer else cacheDict.get("FacmilyID", 0),
                 "FamilyName":cacheDict.get("FamilyName", ""),
                 "TitleID":cacheDict.get("TitleID", 0),
                 "FightPower":PlayerControl.GetFightPower(curPlayer) if curPlayer else cacheDict.get("FightPower", 0),
                 "EquipShowSwitch":cacheDict.get("EquipShowSwitch", 0),
                 "EquipShowID":cacheDict.get("EquipShowID", []),
                 "ServerGroupID":PlayerControl.GetPlayerServerGroupID(curPlayer) if curPlayer else cacheDict.get("ServerGroupID", GameWorld.GetServerGroupID()),
                 }
    return cacheBase
def UpdCrossCacheBase(playerID, cacheBase, isLogout=False):
    ## 更新同步跨服基础查看缓存
    curCache = FindViewCache(playerID, True, cacheBase)
    if not curCache:
        return {}
    curCache.LV = cacheBase.get("LV", 0)
    curCache.OffTime = int(time.time()) # 每次都更新,最后一次可视为跨服玩家的最近一次离线时间
    cacheDict = GetCachePropDataDict(curCache)
    if not cacheBase:
        return cacheDict
    for k, v in cacheBase.items():
        cacheDict[k] = v
    if isLogout:
        if not IsSaveDBViewCache(curCache):
            DeleteViewCache(playerID)
            return {}
    return cacheDict
ItemDataClassMax = 20 # 最大装备阶数
#//04 01 地图同步玩家缓存数据到GameServer#tagMGUpdatePlayerCache
#
#struct    tagMGUpdatePlayerCache
@@ -136,34 +443,28 @@
#    WORD        ItemDataSize1;
#    char        ItemData1[ItemDataSize1];    //1阶装备数据
#    ...         ...
#    WORD        ItemDataSize15;
#    char        ItemData15[ItemDataSize15];
#    WORD        ItemDataSize20;
#    char        ItemData20[ItemDataSize20];
#    BYTE        PackDataSyncState;    // 打包数据同步状态: 0-不同步;个位-是否同步本服;十位-是否同步跨服
#    DWORD       PackDataLen;
#    char        PackData[PackDataLen];
#    WORD        PackMsgLen;
#    char        PackMsg[PackMsgLen];
#};
def OnMGUpdatePlayerCache(routeIndex, mapID, curPackData, tick):
    playerID = curPackData.PlayerID
    playerLV = curPackData.PlayerLV
    #playerLV = curPackData.PlayerLV
    isLogout = curPackData.IsLogouting
    GameWorld.DebugLog('ViewCache### OnMGUpdatePlayerCache isLogout=%s' % isLogout, playerID)
    isSaveAll = True # 是否保存所有数据
    if isLogout:
        #不需要保存离线数据的,直接删除缓存数据
        if not IsSaveDBViewCache(playerID, playerLV):
            DeleteViewCache(playerID)
            return
        isSaveAll = IsSaveAllViewCache(playerID)
        GameWorld.DebugLog("    isSaveAll=%s" % isSaveAll, playerID)
    packDataSyncState = curPackData.PackDataSyncState
    packDataLen = curPackData.PackDataLen
    GameWorld.DebugLog('地图同步玩家缓存: isLogout=%s,packDataSyncState=%s,packDataLen=%s'
                       % (isLogout, packDataSyncState, packDataLen), playerID)
    
    curCache = FindViewCache(playerID, True)
    if not curCache:
        return
    curCache.LV = curPackData.PlayerLV
    curCache.OffTime = curPackData.OffTime
    if isLogout:
        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        if curPlayer:
            curCache.GeTuiID = curPlayer.GetGeTuiClientID()
            curCache.GeTuiIDSize = len(curCache.GeTuiID)
    curCache.PropDataDict = {} # 每次更新数据时,重置字典缓存,下次获取时重新eval缓存
    curCache.PropData = curPackData.PropData
    curCache.PropDataSize = curPackData.PropDataSize
@@ -173,20 +474,36 @@
    #GameWorld.DebugLog("    更新Prop数据: size=%s, %s" % (curCache.PropDataSize, curCache.PropData), playerID)
    #GameWorld.DebugLog("    更新Plus数据: size=%s, %s" % (curCache.PlusDataSize, curCache.PlusData), playerID)
    
    # 装备数据存储,不保存装备数据的话则清空
    for classLV in xrange(1, 15 + 1):
        if not isSaveAll:
            itemDataSize = 0
            itemData = ""
        else:
            itemDataSize = getattr(curPackData, "ItemDataSize%s" % classLV)
            if not itemDataSize:
                continue
            itemData = getattr(curPackData, "ItemData%s" % classLV)
    # 装备数据存储,只更新有同步的阶,只要该阶有同步,则至少是 {}
    for classLV in xrange(1, ItemDataClassMax + 1):
        itemDataSize = getattr(curPackData, "ItemDataSize%s" % classLV)
        if not itemDataSize:
            continue
        itemData = getattr(curPackData, "ItemData%s" % classLV)
        setattr(curCache, "ItemData%s" % classLV, itemData)
        setattr(curCache, "ItemDataSize%s" % classLV, itemDataSize)
        #GameWorld.DebugLog("    更新Item数据: classLV=%s,size=%s, %s" % (classLV, itemDataSize, itemData), playerID)
        
    msgInfo = eval(curPackData.PackMsg) if curPackData.PackMsg else {} # 打包数据附带的信息
    # 需要同步跨服
    if packDataSyncState&pow(2, 2):
        dataMsg = {"playerID":playerID, "cacheBuffer":curCache.getBuffer(), "msgInfo":msgInfo}
        CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PushPlayerCache, dataMsg)
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    # 在可能删除之前执行打包数据相关逻辑
    PlayerPackData.OnMGUpdatePlayerPackData(curPlayer, curPackData, msgInfo)
    if isLogout:
        #不需要保存离线数据的,直接删除缓存数据
        if not IsSaveDBViewCache(curCache):
            DeleteViewCache(playerID)
            return
        if curPlayer:
            curCache.GeTuiID = curPlayer.GetGeTuiClientID()
            curCache.GeTuiIDSize = len(curCache.GeTuiID)
    #GameWorld.DebugLog("    %s" % curCache.outputString())
    # 同步更新助战信息
    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(playerID):
@@ -210,15 +527,121 @@
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(curPackData.PlayerID)
    findPlayerID = curPackData.FindPlayerID
    equipClassLV = curPackData.EquipClassLV
    curCache = FindViewCache(findPlayerID)
    OnQueryPlayerCache(curPlayer, findPlayerID, equipClassLV)
    return
#// C0 02 查看跨服玩家信息 #tagCGViewCrossPlayerInfo
#
#struct    tagCGViewCrossPlayerInfo
#{
#    tagHead        Head;
#    DWORD        PlayerID;    // 跨服玩家ID
#    BYTE        EquipClassLV;    //大于0为查看指定境界阶装备信息,  0为查看默认信息
#};
def OnViewCrossPlayerInfo(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    tagPlayerID = clientData.PlayerID
    equipClassLV = clientData.EquipClassLV
    OnQueryPlayerCache(curPlayer, tagPlayerID, equipClassLV)
    return
#===============================================================================
# //B3 06 查询玩家的简短信息 #tagCGViewPlayerShortInfo
# struct    tagCGViewPlayerShortInfo
# {
#    tagHead        Head;
#    DWORD        PlayerID;
# };
#===============================================================================
def OnViewPlayerShortInfo(index, clientPack, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    tagPlayerID = clientPack.PlayerID
    tagPlayer = GameWorld.GetPlayerManager().FindPlayerByID(tagPlayerID)
    if tagPlayer:
        answerPack = ChPyNetSendPack.tagGCAnswerPlayerShortInfo()
        answerPack.Clear()
        answerPack.PlayerID = clientPack.PlayerID
        answerPack.PlayerName = tagPlayer.GetName()
        answerPack.Job = tagPlayer.GetJob()
        answerPack.LV = tagPlayer.GetLV()
        answerPack.RealmLV = tagPlayer.GetOfficialRank()
        answerPack.OnlineType = ChConfig.Def_Online
        answerPack.IsInTeam = tagPlayer.GetTeamID() > 0
        answerPack.ServerGroupID = PlayerControl.GetPlayerServerGroupID(tagPlayer)
        answerPack.Face = tagPlayer.GetFace()
        answerPack.FacePic = tagPlayer.GetFacePic()
        NetPackCommon.SendFakePack(curPlayer, answerPack)
    else:
        OnQueryPlayerCache(curPlayer, tagPlayerID, isShort=1)
    return
def SetNeedViewCache(playerIDList):
    ## 设置需要缓存数据,跨服专用
    if not GameWorld.IsCrossServer():
        return
    for playerID in playerIDList:
        curCache = FindViewCache(playerID)
        if curCache:
            continue
        dataMsg = {"tagPlayerID":playerID}
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PullPlayerViewCache, dataMsg)
    return
def OnQueryPlayerCache(curPlayer, tagPlayerID, equipClassLV=0, isShort=0):
    '''查询玩家缓存,支持直接在本服或跨服查询任意服务器玩家
    @param tagPlayerID: 目标玩家ID
    @param equipClassLV: 指定查看某一阶装备信息
    @param isShort: 是否查看简短信息
    '''
    if not curPlayer:
        return
    playerID = curPlayer.GetPlayerID()
    GameWorld.DebugLog("查看玩家: tagPlayerID=%s,equipClassLV=%s,isShort=%s" % (tagPlayerID, equipClassLV, isShort), playerID)
    # 在跨服服务器查询
    if GameWorld.IsCrossServer():
        curCache = FindViewCache(tagPlayerID)
        if curCache:
            GameWorld.DebugLog("    在跨服查看玩家跨服有数据,直接回包! tagPlayerID=%s" % tagPlayerID, playerID)
            Sync_PlayerCache(curPlayer, curCache, equipClassLV, isShort)
        else:
            GameWorld.DebugLog("    在跨服查看玩家跨服无数据,从子服拉取! tagPlayerID=%s" % tagPlayerID, playerID)
            viewFrom = 0
            msgInfo = {"playerID":playerID, "tagPlayerID":tagPlayerID, "equipClassLV":equipClassLV, "isShort":isShort, "viewFrom":viewFrom}
            dataMsg = {"tagPlayerID":tagPlayerID, "msgInfo":msgInfo}
            CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PullPlayerViewCache, dataMsg)
    # 在子服服务器查询
    else:
        # 同db玩家
        if PlayerControl.GetDBPlayerAccIDByID(tagPlayerID):
            GameWorld.DebugLog("    查看玩家本服有数据,直接回包! tagPlayerID=%s" % tagPlayerID, playerID)
            curCache = FindViewCache(tagPlayerID)
            Sync_PlayerCache(curPlayer, curCache, equipClassLV, isShort)
        else:
            if tagPlayerID in PyGameData.g_crossPlayerViewCache:
                tick = GameWorld.GetGameWorld().GetTick()
                curCache, cacheTick = PyGameData.g_crossPlayerViewCache[tagPlayerID]
                if tick - cacheTick <= 1 * 60 * 1000:
                    GameWorld.DebugLog("    1分钟内重复查看跨服玩家且本服有数据,直接回包! tagPlayerID=%s" % tagPlayerID, playerID)
                    Sync_PlayerCache(curPlayer, curCache, equipClassLV, isShort)
                    return
            GameWorld.DebugLog("    查看玩家本服没有数据,发跨服查! tagPlayerID=%s" % tagPlayerID, playerID)
            viewFrom = GameWorld.GetServerGroupID()
            dataMsg = {"playerID":playerID, "tagPlayerID":tagPlayerID, "equipClassLV":equipClassLV, "isShort":isShort, "viewFrom":viewFrom}
            CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_ViewPlayerCache, dataMsg)
    return
def Sync_PlayerCache(curPlayer, curCache, equipClassLV=0, isShort=0):
    ## 同步玩家缓存
    if isShort:
        Sync_PlayerShortInfo(curPlayer, curCache)
        return
    if not curCache:
        PlayerControl.NotifyCode(curPlayer, "ViewPlayer_OffLine")
        return
    Sync_PlayerCache(curPlayer, curCache, equipClassLV)
    return
def Sync_PlayerCache(curPlayer, curCache, equipClassLV=0):
    ## 同步玩家缓存
    if equipClassLV:
        itemData = ""
        if hasattr(curCache, "ItemDataSize%s" % equipClassLV):
@@ -241,54 +664,26 @@
    NetPackCommon.SendFakePack(curPlayer, sendPack)
    return
#===============================================================================
# //B3 06 查询玩家的简短信息 #tagCGViewPlayerShortInfo
# struct    tagCGViewPlayerShortInfo
# {
#    tagHead        Head;
#    DWORD        PlayerID;
# };
#===============================================================================
def OnViewPlayerShortInfo(index, clientPack, tick):
    ## 封包通知
    tagPlayer = GameWorld.GetPlayerManager().FindPlayerByID(clientPack.PlayerID)
def Sync_PlayerShortInfo(curPlayer, curCache):
    if not curCache:
        PlayerControl.NotifyCode(curPlayer, "ViewPlayer_OffLine")
        return
    answerPack = ChPyNetSendPack.tagGCAnswerPlayerShortInfo()
    answerPack.Clear()
    if not tagPlayer:
        curCache = FindViewCache(clientPack.PlayerID)
        if not curCache:
            # 实在找不到设置为初始化数据
            answerPack.PlayerID = clientPack.PlayerID
            answerPack.PlayerName = ""
            answerPack.Job = 1
            answerPack.LV = 1
            answerPack.RealmLV = 1
            answerPack.OnlineType = ChConfig.Def_Offline
            answerPack.ServerGroupID = 0
        else:
            cacheDict = GetCachePropDataDict(curCache)
            answerPack.PlayerID = clientPack.PlayerID
            answerPack.PlayerName = cacheDict["Name"]
            answerPack.Job = cacheDict["Job"]
            answerPack.LV = cacheDict["LV"]
            answerPack.RealmLV = cacheDict["RealmLV"]
            answerPack.OnlineType = ChConfig.Def_Offline
            if GameWorld.IsCrossServer():
                answerPack.ServerGroupID = cacheDict.get("ServerGroupID", 0)
            else:
                answerPack.ServerGroupID = GameWorld.GetServerGroupID()
    cacheDict = GetCachePropDataDict(curCache)
    answerPack.PlayerID = curCache.PlayerID
    answerPack.PlayerName = cacheDict["Name"]
    answerPack.Job = cacheDict["Job"]
    answerPack.LV = cacheDict["LV"]
    answerPack.RealmLV = cacheDict["RealmLV"]
    answerPack.OnlineType = ChConfig.Def_Offline
    answerPack.Face = cacheDict.get("Face", 0)
    answerPack.FacePic = cacheDict.get("FacePic", 0)
    if GameWorld.IsCrossServer():
        answerPack.ServerGroupID = cacheDict.get("ServerGroupID", 0)
    else:
        answerPack.PlayerID = clientPack.PlayerID
        answerPack.PlayerName = tagPlayer.GetName()
        answerPack.Job = tagPlayer.GetJob()
        answerPack.LV = tagPlayer.GetLV()
        answerPack.RealmLV = tagPlayer.GetOfficialRank()
        answerPack.OnlineType = ChConfig.Def_Online
        answerPack.IsInTeam = tagPlayer.GetTeamID() > 0
        answerPack.ServerGroupID = PlayerControl.GetPlayerServerGroupID(tagPlayer)
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
        answerPack.ServerGroupID = GameWorld.GetServerGroupID()
    NetPackCommon.SendFakePack(curPlayer, answerPack)
    return
@@ -305,3 +700,146 @@
    curCache.PropDataSize = len(curCache.PropData)
    return
def ClientServerMsg_ViewPlayerCache(serverGroupID, msgData):
    playerID = msgData["playerID"]
    tagPlayerID = msgData["tagPlayerID"]
    GameWorld.DebugLog("收到子服查看跨服玩家信息: serverGroupID=%s,playerID=%s,tagPlayerID=%s" % (serverGroupID, playerID, tagPlayerID))
    curCache = FindViewCache(tagPlayerID)
    if curCache:
        Send_CrossServerMsg_ViewPlayerCacheRet(curCache, msgData, serverGroupID)
    else:
        dataMsg = {"tagPlayerID":tagPlayerID, "msgInfo":msgData}
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PullPlayerViewCache, dataMsg)
    return
def Send_CrossServerMsg_ViewPlayerCacheRet(curCache, msgData, serverGroupID):
    ## 发送查看玩家缓存结果给目标子服
    msgData["cacheBuffer"] = curCache.getBuffer() if curCache else ""
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_ViewPlayerCacheRet, msgData, [serverGroupID])
    return
def ClientServerMsg_PushPlayerCache(serverGroupID, msgData):
    ## 收到子服推送玩家缓存信息
    playerID = msgData["playerID"]
    cacheBuffer = msgData["cacheBuffer"]
    msgInfo = msgData.get("msgInfo", {})
    curCache = None
    if not cacheBuffer:
        GameWorld.DebugLog("子服推送的玩家缓存数据为空! playerID=%s" % playerID, serverGroupID)
    else:
        curCache = FindViewCache(playerID, True)
        ReadCacheBufferToCacheObj(playerID, cacheBuffer, curCache)
    if not msgInfo:
        return
    viewFrom = msgInfo.get("viewFrom", 0)
    if viewFrom != 0:
        # 其他子服查询的,推送给目标服务器
        Send_CrossServerMsg_ViewPlayerCacheRet(curCache, msgInfo, viewFrom)
        return
    # 在跨服查询的,直接回包
    viewPlayerID = msgInfo.get("playerID", 0)
    if not viewPlayerID:
        return
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(viewPlayerID)
    if not curPlayer:
        return
    equipClassLV = msgInfo.get("equipClassLV", 0)
    isShort = msgInfo.get("isShort", 0)
    Sync_PlayerCache(curPlayer, curCache, equipClassLV, isShort)
    return
def ReadCacheBufferToCacheObj(playerID, cacheBuffer, cacheObj=None):
    try:
        TempCache.clear()
        setattr(TempCache, "PropDataDict", {})
        if TempCache.readData(cacheBuffer) == -1:
            GameWorld.ErrLog("玩家缓存数据readData失败! playerID=%s" % playerID)
            return
    except:
        GameWorld.ErrLog("玩家缓存数据readData异常! playerID=%s" % playerID)
        return
    if TempCache.PlayerID != playerID:
        GameWorld.ErrLog("玩家缓存数据readData后玩家ID不一致! playerID=%s != cachePlayerID=%s" % (playerID, TempCache.PlayerID))
        return
    if not cacheObj:
        return TempCache
    cacheObj.PropDataDict = {} # 每次更新数据时,重置字典缓存,下次获取时重新eval缓存
    cacheObj.PlayerID = TempCache.PlayerID
    cacheObj.GeTuiIDSize = TempCache.GeTuiIDSize
    cacheObj.GeTuiID = TempCache.GeTuiID
    cacheObj.LV = TempCache.LV
    cacheObj.OffTime = TempCache.OffTime
    cacheObj.PropData = TempCache.PropData
    cacheObj.PropDataSize = TempCache.PropDataSize
    cacheObj.PlusData = TempCache.PlusData
    cacheObj.PlusDataSize = TempCache.PlusDataSize
    for classLV in xrange(1, ItemDataClassMax + 1):
        itemDataSize = getattr(TempCache, "ItemDataSize%s" % classLV)
        if not itemDataSize:
            continue
        itemData = getattr(TempCache, "ItemData%s" % classLV)
        setattr(cacheObj, "ItemData%s" % classLV, itemData)
        setattr(cacheObj, "ItemDataSize%s" % classLV, itemDataSize)
    return cacheObj
def CrossServerMsg_ViewPlayerCacheRet(msgData, tick):
    ## 收到跨服服务器回复的查看玩家信息
    playerID = msgData["playerID"]
    tagPlayerID = msgData["tagPlayerID"]
    cacheBuffer = msgData["cacheBuffer"]
    equipClassLV = msgData["equipClassLV"]
    isShort = msgData["isShort"]
    curCache = None
    if cacheBuffer:
        if tagPlayerID in PyGameData.g_crossPlayerViewCache:
            curCache = PyGameData.g_crossPlayerViewCache[tagPlayerID][0]
        else:
            curCache = PyGameDataStruct.tagPlayerViewCachePy()
        if ReadCacheBufferToCacheObj(tagPlayerID, cacheBuffer, curCache):
            PyGameData.g_crossPlayerViewCache[tagPlayerID] = [curCache, tick] # 更新信息
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if curPlayer:
        Sync_PlayerCache(curPlayer, curCache, equipClassLV, isShort)
    return
def CrossServerMsg_PullPlayerViewCache(msgData):
    ## 收到跨服服务器拉取玩家玩家缓存数据
    msgInfo = msgData.get("msgInfo", {})
    tagPlayerID = msgData["tagPlayerID"]
    DoPullPlayerViewCache(tagPlayerID, msgInfo)
    return
def DoPullPlayerViewCache(playerID, msgInfo):
    if not PlayerControl.GetDBPlayerAccIDByID(playerID):
        # 不是本服db玩家不处理
        return
    curCache = FindViewCache(playerID)
    if curCache:
        GameWorld.DebugLog("本服有缓存玩家数据,直接推给跨服!", playerID)
        dataMsg = {"playerID":playerID, "cacheBuffer":curCache.getBuffer(), "msgInfo":msgInfo}
        CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PushPlayerCache, dataMsg)
        return
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        GameWorld.DebugLog("本服无缓存玩家数据且不在线,直接回空数据给跨服!", playerID)
        dataMsg = {"playerID":playerID, "cacheBuffer":"", "msgInfo":msgInfo}
        CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PushPlayerCache, dataMsg)
        return
    GameWorld.DebugLog("玩家在线的发给地图同步缓存数据!", playerID)
    # 在线的转发给地图
    PlayerPackData.QueryPlayerResult_PlayerMirror(curPlayer, "PullPlayerViewCache", msgInfo)
    return