| | |
| | | # 如切磋一下,玩家可以在任意场景对任意本服或跨服玩家发起切磋,与其镜像进行一场友谊PK,纯娱乐
|
| | | # 这种为被动式,即目标玩家可能不存在打包数据表中,需要取拉取
|
| | | #
|
| | | # 由于打包数据较大,影响开关服及备档的速度,还会导致内存不足,故改为db直接管理打包数据获取及入库
|
| | | # GameServer仅保留单次启动后有同步/获取的玩家数据,一般比db少,只保留打包数据信息,玩家基本信息改为从ViewCahce中获取
|
| | | #-------------------------------------------------------------------------------
|
| | | #"""Version = 2024-10-17 15:00"""
|
| | | #-------------------------------------------------------------------------------
|
| | |
|
| | | import CommFunc
|
| | | import GameWorld
|
| | | import PyDataManager
|
| | | import PlayerViewCache
|
| | | import PyGameDataStruct
|
| | | import CrossChampionship
|
| | | import CrossRealmMsg
|
| | | import PlayerControl
|
| | | import ShareDefine
|
| | | import PyGameData
|
| | | import ChConfig
|
| | |
|
| | | import time
|
| | | import base64
|
| | |
|
| | | Def_CahceCountMax = 100 # 最大缓存个数,注:GameServer缓不缓存这个数据都无所谓(改为在db缓存),保留原取数据逻辑不变,暂时缓存个x条,方便本服的直接取
|
| | | TempDBPlayer = PyGameDataStruct.tagDBPlayer()
|
| | |
|
| | | def GetDBPlayerByPackData(packData):
|
| | |
| | | if packData:
|
| | | TempDBPlayer.readData(base64.b64decode(packData))
|
| | | return TempDBPlayer
|
| | |
|
| | | class PlayerPackDataObj():
|
| | | |
| | | def __init__(self):
|
| | | self.dbPlayerPackData = None
|
| | | self.playerID = 0
|
| | | self.playerName = ""
|
| | | self.accID = ""
|
| | | self.lv = 0
|
| | | self.job = 0
|
| | | self.realmLV = 0
|
| | | self.face = 0
|
| | | self.facePic = 0
|
| | | self.fightPower = 0
|
| | | self.serverID = 0
|
| | | return
|
| | | |
| | | def GetBaseDict(self):
|
| | | return {"playerID":self.playerID, "playerName":self.playerName, "lv":self.lv, "job":self.job, |
| | | "realmLV":self.realmLV, "face":self.face, "facePic":self.facePic, "fightPower":self.fightPower}
|
| | | |
| | | def UpdPackData(self, packData):
|
| | | if not packData:
|
| | | return
|
| | | if not self.dbPlayerPackData:
|
| | | self.dbPlayerPackData = PyGameDataStruct.tagDBPlayerPackData()
|
| | | self.dbPlayerPackData.PlayerID = self.playerID
|
| | | self.dbPlayerPackData.PackData = packData
|
| | | self.dbPlayerPackData.PackDataSize = len(self.dbPlayerPackData.PackData)
|
| | | self.dbPlayerPackData.UpdTime = int(time.time())
|
| | | self.Unpack()
|
| | | return
|
| | | |
| | | def Unpack(self):
|
| | | if not self.dbPlayerPackData:
|
| | | return
|
| | | dbPlayer = GetDBPlayerByPackData(self.dbPlayerPackData.PackData)
|
| | | self.playerID = dbPlayer.PlayerID
|
| | | self.accID = dbPlayer.AccID
|
| | | self.playerName = dbPlayer.PlayerName
|
| | | self.lv = dbPlayer.LV
|
| | | self.job = dbPlayer.Job
|
| | | self.realmLV = dbPlayer.OfficialRank
|
| | | self.face = dbPlayer.Face
|
| | | self.facePic = dbPlayer.FacePic
|
| | | self.fightPower = dbPlayer.FightPowerEx * ChConfig.Def_PerPointValue + dbPlayer.FightPower
|
| | | self.serverID = GameWorld.GetAccIDServerID(self.accID)
|
| | | return
|
| | | |
| | | def GetPackData(self): return self.dbPlayerPackData.PackData if self.dbPlayerPackData else ""
|
| | | def GetUpdTime(self): return self.dbPlayerPackData.UpdTime if self.dbPlayerPackData else 0
|
| | |
|
| | | class DBPlayerPackDataManager():
|
| | | ## 玩家打包数据管理
|
| | | ## 玩家打包数据管理 - 这里仅管理本次启动后的热数据缓存,不入库
|
| | |
|
| | | def __init__(self):
|
| | | self.Clear()
|
| | | return
|
| | |
|
| | | def Clear(self):
|
| | | self.__packDataList = [] # [PlayerPackDataObj, ...]
|
| | | self.__idIndexDict = {} # {playerID:index, ...}
|
| | | self.__needSort = False
|
| | | self.__serverIDRangePlayerIDDict = {} # {serverIDRangeTuple:[playerID, ...], ....}
|
| | | self.__packDataDcit = {} # {playerID:packData, ...}
|
| | | self.__packDataPlayerIDList = [] # [playerID, ...] # 限制缓存数,先进先出
|
| | | return
|
| | | |
| | | def GetPlayerPackObj(self, playerID, isAddNew=False):
|
| | | packDataObj = None
|
| | | self.__refreshIDIndex()
|
| | | if playerID in self.__idIndexDict:
|
| | | index = self.__idIndexDict[playerID]
|
| | | if index < len(self.__packDataList):
|
| | | packDataObj = self.__packDataList[index]
|
| | | |
| | | if not packDataObj and isAddNew:
|
| | | packDataObj = PlayerPackDataObj()
|
| | | packDataObj.playerID = playerID
|
| | | self.__packDataList.append(packDataObj)
|
| | | self.__idIndexDict[playerID] = len(self.__packDataList) - 1
|
| | | self.__needSort = True
|
| | | |
| | | return packDataObj
|
| | | |
| | | def GetPlayerIDListByServerIDInfo(self, serverIDList):
|
| | | ## 根据服务器ID列表信息获取对应服务器ID范围的玩家ID战力排序列表
|
| | | self.Sort()
|
| | | key = tuple(serverIDList)
|
| | | if key not in self.__serverIDRangePlayerIDDict:
|
| | | playerIDList = []
|
| | | for dataObj in self.__packDataList:
|
| | | playerID = dataObj.playerID
|
| | | serverID = dataObj.serverID
|
| | | 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
|
| | | return playerID in self.__packDataDcit
|
| | |
|
| | | def __refreshIDIndex(self):
|
| | | if not self.__idIndexDict:
|
| | | self.__idIndexDict = {}
|
| | | for index, dataObj in enumerate(self.__packDataList):
|
| | | self.__idIndexDict[dataObj.playerID] = index
|
| | | return self.__idIndexDict
|
| | | def GetCount(self): return len(self.__packDataDcit)
|
| | | |
| | | def GetPlayerPackdata(self, playerID):
|
| | | if playerID not in self.__packDataDcit:
|
| | | return ""
|
| | | # 恢复数据热度
|
| | | if playerID in self.__packDataPlayerIDList:
|
| | | self.__packDataPlayerIDList.remove(playerID)
|
| | | self.__packDataPlayerIDList.append(playerID)
|
| | | GameWorld.DebugLog("获取打包数据缓存数更新: %s,%s" % (len(self.__packDataPlayerIDList), self.__packDataPlayerIDList))
|
| | | return self.__packDataDcit[playerID]
|
| | |
|
| | | def UpdPlayerPackData(self, playerID, packData):
|
| | | if not packData:
|
| | | return
|
| | | packObj = self.GetPlayerPackObj(playerID, True)
|
| | | packObj.UpdPackData(packData)
|
| | | self.__needSort = True
|
| | | return packObj
|
| | | |
| | | def DelPlayerPackData(self, playerID):
|
| | | self.__refreshIDIndex()
|
| | | index = self.__idIndexDict.pop(playerID, -1)
|
| | | if index >= 0 and index < len(self.__packDataList):
|
| | | self.__packDataList.pop(index)
|
| | | for playerIDList in self.__serverIDRangePlayerIDDict.values():
|
| | | if playerID in playerIDList:
|
| | | playerIDList.remove(playerID)
|
| | | |
| | | #只要有删除,需重置index映射
|
| | | self.__idIndexDict = {}
|
| | | self.__serverIDRangePlayerIDDict = {}
|
| | | self.__packDataDcit[playerID] = packData
|
| | | if playerID in self.__packDataPlayerIDList:
|
| | | # 添加热度,重复更新的不影响热度
|
| | | self.__packDataPlayerIDList.append(playerID)
|
| | | if len(self.__packDataPlayerIDList) > Def_CahceCountMax:
|
| | | delPlayerID = self.__packDataPlayerIDList.pop(0)
|
| | | if delPlayerID in self.__packDataDcit:
|
| | | del self.__packDataDcit[delPlayerID]
|
| | | GameWorld.DebugLog("删除打包数据缓存: delPlayerID=%s" % delPlayerID)
|
| | | GameWorld.DebugLog("添加打包数据缓存数更新: %s,%s" % (len(self.__packDataPlayerIDList), self.__packDataPlayerIDList))
|
| | | return
|
| | | |
| | | def GetCount(self): return len(self.__packDataList) |
| | | def At(self, index):
|
| | | dataObj = self.__packDataList[index]
|
| | | if not dataObj and False:
|
| | | dataObj = PlayerPackDataObj() # 不会执行到,只为了.出代码提示
|
| | | return dataObj
|
| | | |
| | | def Sort(self):
|
| | | ## 默认按战力倒序排
|
| | | if not self.__needSort:
|
| | | return
|
| | | self.__needSort = False
|
| | | self.__packDataList.sort(key=lambda d: (d.fightPower), reverse=True)
|
| | | self.__idIndexDict = {}
|
| | | self.__serverIDRangePlayerIDDict = {}
|
| | | self.__refreshIDIndex()
|
| | | return
|
| | | |
| | | # 保存数据 存数据库和realtimebackup
|
| | | def GetSaveData(self):
|
| | | savaData = ""
|
| | | cntData = ""
|
| | | cnt = 0
|
| | | |
| | | for index in xrange(self.GetCount()):
|
| | | dataObj = self.At(index)
|
| | | if not dataObj.dbPlayerPackData:
|
| | | continue
|
| | | cnt += 1
|
| | | savaData += dataObj.dbPlayerPackData.getBuffer()
|
| | | if cnt >= 10:
|
| | | break
|
| | | |
| | | GameWorld.Log("Save DBPlayerPackData 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 DBPlayerPackData count :%s" % cnt)
|
| | | |
| | | self.Clear()
|
| | | |
| | | for _ in xrange(cnt):
|
| | | dbData = PyGameDataStruct.tagDBPlayerPackData()
|
| | | pos += dbData.readData(datas, pos, dataslen)
|
| | | playerID = dbData.PlayerID
|
| | | |
| | | packObj = self.GetPlayerPackObj(playerID, True)
|
| | | packObj.dbPlayerPackData = dbData
|
| | | packObj.Unpack()
|
| | | |
| | | self.Sort()
|
| | | return pos
|
| | |
|
| | | def IsSaveDB(packDataObj):
|
| | | ## 是否入库
|
| | | if not packDataObj:
|
| | | return False
|
| | | |
| | | playerID = packDataObj.playerID
|
| | | |
| | | if CrossChampionship.IsChampionshipPlayer(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:
|
| | | pass
|
| | | # 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
|
| | | |
| | | # 以上是相关功能需要用到的数据,必定不能删除的
|
| | | |
| | | maxDays = 7 # 默认7天
|
| | | MaxTime = maxDays * 3600 * 24
|
| | | curTime = int(time.time())
|
| | | passTime = curTime - packDataObj.GetUpdTime()
|
| | | if passTime < MaxTime:
|
| | | return True
|
| | | |
| | | return False
|
| | |
|
| | | def DelOutofTimePackData():
|
| | | ## 删除过期
|
| | | |
| | | packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
|
| | | for index in range(packDataMgr.GetCount())[::-1]: # 可能删数据,倒序遍历
|
| | | packDataObj = packDataMgr.At(index)
|
| | | if IsSaveDB(packDataObj):
|
| | | continue
|
| | | packDataMgr.DelPlayerPackData(packDataObj.playerID)
|
| | | |
| | | return
|
| | |
|
| | | def IsPackDataPlayer(playerID):
|
| | | return PyDataManager.GetDBPlayerPackDataManager().IsPlayerIn(playerID)
|
| | |
| | |
|
| | | # pullFrom 0-跨服拉子服; >0-子服通过跨服拉子服
|
| | | if GameWorld.IsCrossServer():
|
| | | PlayerViewCache.SetNeedViewCache(pullPlayerIDList) # 拉打包数据的时候默认需要缓存数据
|
| | | # 广播给子服拉数据
|
| | | msgInfo["pullFrom"] = 0
|
| | | dataMsg = {"pullPlayerIDList":pullPlayerIDList, "msgInfo":msgInfo}
|
| | |
| | | if not curPlayer:
|
| | | GameWorld.DebugLog("玩家不在线的调用打包db数据! playerID=%s" % (playerID), playerID)
|
| | | data = str(msgInfo)
|
| | | GameWorld.GetGameWorld().SendDBLogic(ChConfig.gstDBLogic_PlayerPackData, playerID, data, len(data))
|
| | | GameWorld.GetGameWorld().SendDBLogic(ChConfig.gstDBLogic_PlayerPackDataReq, playerID, data, len(data))
|
| | | return
|
| | | GameWorld.DebugLog("玩家在线的发给地图打包数据! playerID=%s" % (playerID), playerID)
|
| | | # 在线的转发给地图
|
| | |
| | | ## 收到子服同步的玩家打包数据
|
| | | playerID = msgData["playerID"]
|
| | | packData = msgData["packData"]
|
| | | cacheBase = msgData.get("cacheBase", {})
|
| | | if cacheBase:
|
| | | PlayerViewCache.UpdCrossCacheBase(playerID, cacheBase)
|
| | | PyDataManager.GetDBPlayerPackDataManager().UpdPlayerPackData(playerID, packData)
|
| | |
|
| | | msgInfo = msgData.get("msgInfo", {})
|
| | |
| | |
|
| | | msgInfo = msgData["msgInfo"]
|
| | | pullPlayerIDList = msgData["pullPlayerIDList"]
|
| | | dbPackDataIDList = msgData.get("dbPackDataIDList", []) # db标记的有打包数据的玩家ID
|
| | |
|
| | | otherServerPlayerIDList = []
|
| | | packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
|
| | | for playerID in pullPlayerIDList:
|
| | | packObj = packDataMgr.GetPlayerPackObj(playerID)
|
| | | packData = packDataMgr.GetPlayerPackdata(playerID)
|
| | | # 已经有的数据先推送回去
|
| | | if packObj:
|
| | | GameWorld.DebugLog("跨服有缓存玩家打包数据,直接推给子服! playerID=%s" % playerID)
|
| | | dataMsg = {"playerID":playerID, "packData":packObj.GetPackData(), "msgInfo":msgInfo}
|
| | | if packData or playerID in dbPackDataIDList:
|
| | | GameWorld.DebugLog("跨服GameServer或db有缓存玩家打包数据,直接推给子服! playerID=%s" % playerID)
|
| | | dataMsg = {"playerID":playerID, "packData":packData, "msgInfo":msgInfo}
|
| | | CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PushPlayerPackData, dataMsg, [serverGroupID])
|
| | | else:
|
| | | otherServerPlayerIDList.append(playerID)
|
| | |
|
| | | # 还没有数据的,广播给其他子服拉数据
|
| | | if otherServerPlayerIDList:
|
| | | PlayerViewCache.SetNeedViewCache(otherServerPlayerIDList) # 拉打包数据的时候默认需要缓存数据
|
| | | dataMsg = {"pullPlayerIDList":otherServerPlayerIDList, "msgInfo":msgInfo}
|
| | | CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PullPlayerPackData, dataMsg)
|
| | |
|
| | |
| | |
|
| | | packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
|
| | | for playerID in pullPlayerIDList:
|
| | | packObj = packDataMgr.GetPlayerPackObj(playerID)
|
| | | if packObj:
|
| | | packData = packDataMgr.GetPlayerPackdata(playerID)
|
| | | if packData:
|
| | | GameWorld.DebugLog("本服有缓存玩家打包数据,直接推给跨服! playerID=%s" % playerID)
|
| | | dataMsg = {"playerID":playerID, "packData":packObj.GetPackData(), "msgInfo":msgInfo}
|
| | | dataMsg = {"playerID":playerID, "packData":packData, "msgInfo":msgInfo}
|
| | | CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PlayerPackData, dataMsg)
|
| | | else:
|
| | | DoPullPlayerPackData(playerID, msgInfo)
|
| | |
| | | if not packDataSyncState or not packData:
|
| | | return
|
| | |
|
| | | # 本服需要,先更新数据
|
| | | if packDataSyncState&pow(2, 0):
|
| | | # 本服需要,先更新数据;跨服需要则也默认本服需要
|
| | | if packDataSyncState&pow(2, 0) or packDataSyncState&pow(2, 1):
|
| | | PyDataManager.GetDBPlayerPackDataManager().UpdPlayerPackData(playerID, packData)
|
| | | GameWorld.GetGameWorld().SendDBLogic(ChConfig.gstDBLogic_PlayerPackDataUpd, playerID, packData, len(packData))
|
| | |
|
| | | # 跨服需要,同步给跨服,由跨服服务器再进一步处理
|
| | | if packDataSyncState&pow(2, 1):
|
| | | cacheBase = PlayerViewCache.GetSyncCrossCacheBase(curPlayer) if curPlayer else {}
|
| | | dataMsg = {"playerID":playerID, "packData":packData, "cacheBase":cacheBase, "msgInfo":msgInfo}
|
| | | dataMsg = {"playerID":playerID, "packData":packData, "msgInfo":msgInfo}
|
| | | CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PlayerPackData, dataMsg)
|
| | |
|
| | | # 本服需要的功能
|
| | |
| | | requestID = msgInfo["requestID"]
|
| | | mirrorIDList = msgInfo["mirrorIDList"]
|
| | | for mirrorID in mirrorIDList:
|
| | | packObj = packDataMgr.GetPlayerPackObj(mirrorID)
|
| | | if packObj:
|
| | | packDataDict[mirrorID] = packObj.GetPackData()
|
| | | packData = packDataMgr.GetPlayerPackdata(mirrorID)
|
| | | if packData:
|
| | | packDataDict[mirrorID] = packData
|
| | | continue
|
| | | pullPlayerIDList.append(mirrorID)
|
| | |
|