From e41e926bbbd0255acde8b4b62d5558a025b83a02 Mon Sep 17 00:00:00 2001 From: hxp <ale99527@vip.qq.com> Date: 星期三, 11 六月 2025 17:18:27 +0800 Subject: [PATCH] 10263 【越南】【BT】增加后台执行命令清除本服榜单某个玩家ID数据: ClearBillboardData --- ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py | 759 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 592 insertions(+), 167 deletions(-) diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py index 3e85bec..1003e0a 100644 --- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py +++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py @@ -20,6 +20,7 @@ import GameWorld import GameWorship +import GameXiangong import PlayerControl import NetPackCommon import GameWorldArena @@ -30,21 +31,172 @@ 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 @@ -63,41 +215,15 @@ if PyDataManager.GetDBPyFuncTeamManager().IsTeamPlayer(playerID): return True + if GameXiangong.IsXiangongPlayer(playerID): + return True + if GameWorldSkyTower.IsSkyTowerPassPlayer(playerID): return True - SaveDBLimitLV = IpyGameDataPY.GetFuncCfg("PlayerViewCache", 1) - #校验玩家等级 - if playerLV < SaveDBLimitLV: - return False - - return True - -def IsSaveAllViewCache(playerID): - ## 是否保存所有缓存数据 - - if PlayerFBHelpBattle.IsInHelpBattleCheckInList(playerID): + if PlayerPackData.IsPackDataPlayer(playerID): return True - if GameWorldArena.IsArenaBattlePlayer(playerID): - return True - - if CrossBattlefield.IsBattlefieldCallPlayer(playerID): - return True - - if CrossChampionship.IsChampionshipPlayer(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): - return True - #跨服榜单上的默认保留 if GameWorld.IsCrossServer(): billboardMgr = PyDataManager.GetCrossBillboardManager() @@ -108,67 +234,110 @@ 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: + 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 def DelOutofTimeViewCacheData(): ## 删除过期的查看缓存数据 - curTime = int(time.time()) - MaxTime = IpyGameDataPY.GetFuncCfg("PlayerViewCache", 3) * 3600 * 24 - + 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, newPropData={}): +def FindViewCache(playerID, isAddNew=False, newPropData=None): ## 查找玩家缓存 # @param newPropData: 新数据初始PropData {}, key: LV,RealmLV,Job,VIPLV,Name,FamilyID,FamilyName,FightPower - curCache = None pyViewCacheMgr = PyDataManager.GetPlayerViewCachePyManager() - playerViewCachePyDict = pyViewCacheMgr.playerViewCachePyDict - if playerID in playerViewCachePyDict: - curCache = playerViewCachePyDict[playerID] + curCache = pyViewCacheMgr.GetPlayerViewCache(playerID) + if curCache: + pass elif isAddNew or playerID < 10000: # 内网测试假玩家 - if playerID < 10000 and not newPropData: - openJobList = IpyGameDataPY.GetFuncEvalCfg("OpenJob", 1) - fakeName = "匿名玩家".decode(ShareDefine.Def_Game_Character_Encoding).encode(GameWorld.GetCharacterEncoding()) - fakeName = "%s%s" % (fakeName, playerID) - serverID = random.randint(9900, 9950) - accID = "fake%s@test@s%s" % (playerID, serverID) - newPropData = { - "AccID":accID, - "PlayerID":playerID, - "Name":fakeName, - "Job":random.choice(openJobList) if openJobList else 1, - "LV":random.randint(100, 200), - "RealmLV":random.randint(5, 15), - "FightPower":random.randint(1000000, 100000000), - "ServerGroupID":serverID, - } + 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 + curCache.OffTime = int(time.time()) if newPropData: curCache.PropData = json.dumps(newPropData, ensure_ascii=False).replace(" ", "") curCache.PropDataSize = len(curCache.PropData) - playerViewCachePyDict[playerID] = curCache + pyViewCacheMgr.AddPlayerViewCache(playerID, curCache) return curCache def GetCachePropDataDict(curCache): @@ -181,58 +350,83 @@ curCache.PropDataDict = eval(curCache.PropData) return curCache.PropDataDict -def GetShotCahceDict(playerID, withEquip=False): +def GetShotCacheDict(playerID, *exAttrs): ## 获取玩家简短的缓存信息字典 - cacheDict = GetCachePropDataDict(FindViewCache(playerID)) + viewCache = FindViewCache(playerID) + cacheDict = GetCachePropDataDict(viewCache) if not cacheDict: return {} shotCacheDict = { - "PlayerID":playerID, "Name":cacheDict["Name"], "Job":cacheDict["Job"], "LV":cacheDict["LV"], "RealmLV":cacheDict["RealmLV"], - "FightPower":cacheDict["FightPower"], - "ServerID":GameWorld.GetAccIDServerID(cacheDict["AccID"]), } - if withEquip: - shotCacheDict.update({ - "TitleID":cacheDict.get("TitleID", 0), - "EquipShowSwitch":cacheDict.get("EquipShowSwitch", 0), - "EquipShowID":cacheDict.get("EquipShowID", 0), - }) + 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): ## 获取同步跨服基础查看缓存,主要用于个别功能需要提前先同步玩家基础缓存到跨服,因为跨服不一定有玩家缓存,需要提前同步 - playerID = curPlayer.GetPlayerID() + if isinstance(curPlayer, int): + playerID = curPlayer + curPlayer = None + else: + playerID = curPlayer.GetPlayerID() cacheDict = GetCachePropDataDict(FindViewCache(playerID)) cacheBase = { - "AccID":curPlayer.GetAccID(), - "LV":curPlayer.GetLV(), - "RealmLV":curPlayer.GetOfficialRank(), - "Job":curPlayer.GetJob(), - "VIPLV":curPlayer.GetVIPLv(), - "Name":CrossRealmPlayer.GetCrossPlayerName(curPlayer), - "FamilyID":curPlayer.GetFamilyID(), + "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), + "FightPower":PlayerControl.GetFightPower(curPlayer) if curPlayer else cacheDict.get("FightPower", 0), "EquipShowSwitch":cacheDict.get("EquipShowSwitch", 0), - "EquipShowID":cacheDict.get("EquipShowID", 0), - "ServerGroupID":PlayerControl.GetPlayerServerGroupID(curPlayer), + "EquipShowID":cacheDict.get("EquipShowID", []), + "ServerGroupID":PlayerControl.GetPlayerServerGroupID(curPlayer) if curPlayer else cacheDict.get("ServerGroupID", GameWorld.GetServerGroupID()), } return cacheBase -def UpdCrossCacheBase(playerID, cacheBase): +def UpdCrossCacheBase(playerID, cacheBase, isLogout=False): ## 更新同步跨服基础查看缓存 - cacheDict = GetCachePropDataDict(FindViewCache(playerID, True)) + 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 @@ -251,32 +445,26 @@ # ... ... # 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 @@ -286,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, 20 + 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): @@ -323,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): @@ -354,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 @@ -418,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 -- Gitblit v1.8.0