From 5878f2872607b9b9186ad5ce3623aff88bbcef6b Mon Sep 17 00:00:00 2001 From: hxp <ale99527@vip.qq.com> Date: 星期二, 04 三月 2025 16:34:07 +0800 Subject: [PATCH] 5563 【英文】【BT】跨服服务器维护优化(打包数据改为db自己管理存取) --- ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py | 232 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 197 insertions(+), 35 deletions(-) diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py index ee23ee1..7620a80 100644 --- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py +++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py @@ -37,6 +37,7 @@ import CrossRealmMsg import ShareDefine import PyGameData +import CommFunc import ChPlayer import ChConfig @@ -46,6 +47,146 @@ 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 @@ -129,14 +270,12 @@ def DelOutofTimeViewCacheData(): ## 删除过期的查看缓存数据 - PlayerPackData.DelOutofTimePackData() - pyViewCacheMgr = PyDataManager.GetPlayerViewCachePyManager() - playerViewCachePyDict = pyViewCacheMgr.playerViewCachePyDict - for playerID, viewCache in playerViewCachePyDict.items(): + for index in range(pyViewCacheMgr.GetCount())[::-1]: # 有删除需倒序遍历 + viewCache = pyViewCacheMgr.At(index) if IsSaveDBViewCache(viewCache): continue - playerViewCachePyDict.pop(playerID) + pyViewCacheMgr.DelPlayerViewCache(viewCache.PlayerID) PyGameData.g_crossPlayerViewCache = {} # 每日直接清空跨服玩家查看缓存 return @@ -144,50 +283,56 @@ 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 = playerID % 200 + 1 # 1 ~ 200 服 + 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) - 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":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): @@ -241,7 +386,7 @@ "RealmLV":curPlayer.GetOfficialRank(), "Job":curPlayer.GetJob(), "VIPLV":curPlayer.GetVIPLv(), - "Name":CrossRealmPlayer.GetCrossPlayerName(curPlayer), + "Name":curPlayer.GetName(), # 此处不用跨服名称,如前端需要展示跨服名称,可通过ServerID或AccID取得ServerID展示 "Face":curPlayer.GetFace(), "FacePic":curPlayer.GetFacePic(), "FamilyID":curPlayer.GetFamilyID(), @@ -421,6 +566,19 @@ 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 @@ -564,7 +722,7 @@ GameWorld.DebugLog("子服推送的玩家缓存数据为空! playerID=%s" % playerID, serverGroupID) else: curCache = FindViewCache(playerID, True) - __updCacheBufferToCacheObj(playerID, cacheBuffer, curCache) + ReadCacheBufferToCacheObj(playerID, cacheBuffer, curCache) if not msgInfo: return @@ -587,9 +745,10 @@ Sync_PlayerCache(curPlayer, curCache, equipClassLV, isShort) return -def __updCacheBufferToCacheObj(playerID, cacheBuffer, cacheObj): +def ReadCacheBufferToCacheObj(playerID, cacheBuffer, cacheObj=None): try: TempCache.clear() + setattr(TempCache, "PropDataDict", {}) if TempCache.readData(cacheBuffer) == -1: GameWorld.ErrLog("玩家缓存数据readData失败! playerID=%s" % playerID) return @@ -599,6 +758,9 @@ 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 @@ -618,7 +780,7 @@ setattr(cacheObj, "ItemData%s" % classLV, itemData) setattr(cacheObj, "ItemDataSize%s" % classLV, itemDataSize) - return True + return cacheObj def CrossServerMsg_ViewPlayerCacheRet(msgData, tick): ## 收到跨服服务器回复的查看玩家信息 @@ -634,7 +796,7 @@ curCache = PyGameData.g_crossPlayerViewCache[tagPlayerID][0] else: curCache = PyGameDataStruct.tagPlayerViewCachePy() - if __updCacheBufferToCacheObj(tagPlayerID, cacheBuffer, curCache): + if ReadCacheBufferToCacheObj(tagPlayerID, cacheBuffer, curCache): PyGameData.g_crossPlayerViewCache[tagPlayerID] = [curCache, tick] # 更新信息 curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID) @@ -644,7 +806,7 @@ def CrossServerMsg_PullPlayerViewCache(msgData): ## 收到跨服服务器拉取玩家玩家缓存数据 - msgInfo = msgData["msgInfo"] + msgInfo = msgData.get("msgInfo", {}) tagPlayerID = msgData["tagPlayerID"] DoPullPlayerViewCache(tagPlayerID, msgInfo) return -- Gitblit v1.8.0