ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py
@@ -24,11 +24,12 @@
#            如切磋一下,玩家可以在任意场景对任意本服或跨服玩家发起切磋,与其镜像进行一场友谊PK,纯娱乐
#            这种为被动式,即目标玩家可能不存在打包数据表中,需要取拉取
#
# 由于打包数据较大,影响开关服及备档的速度,还会导致内存不足,故改为db直接管理打包数据获取及入库
# GameServer仅保留单次启动后有同步/获取的玩家数据,一般比db少,只保留打包数据信息,玩家基本信息改为从ViewCahce中获取
#-------------------------------------------------------------------------------
#"""Version = 2024-10-17 15:00"""
#-------------------------------------------------------------------------------
import CommFunc
import GameWorld
import PyDataManager
import PlayerViewCache
@@ -39,113 +40,69 @@
import PyGameData
import ChConfig
import time
import base64
Def_CahceCountMax = 100 # 最大缓存个数,注:GameServer缓不缓存这个数据都无所谓(改为在db缓存),保留原取数据逻辑不变,暂时缓存个x条,方便本服的直接取
TempDBPlayer = PyGameDataStruct.tagDBPlayer()
def GetDBPlayerByPackData(packData):
    ## 根据 curPlayer.GetPackData() 打包返回的数据获取DBPlayer数据
    TempDBPlayer.clear()
    if packData:
        TempDBPlayer.readData(base64.b64decode(packData))
    return TempDBPlayer
class DBPlayerPackDataManager():
    ## 玩家打包数据管理
    ## 玩家打包数据管理 - 这里仅管理本次启动后的热数据缓存,不入库
    
    def __init__(self):
        self.Clear()
        return
    
    def Clear(self):
        self.playerPackDataDict = {} # {playerID:tagDBPlayerPackData, ...}
        self.__packDataDcit = {} # {playerID:packData, ...}
        self.__packDataPlayerIDList = [] # [playerID, ...] # 限制缓存数,先进先出
        return
    def IsPlayerIn(self, playerID):
        return playerID in self.__packDataDcit
    
    def GetPlayerPackObj(self, playerID, isAddNew=False):
        packDataObj = None
        if playerID in self.playerPackDataDict:
            packDataObj = self.playerPackDataDict[playerID]
        elif isAddNew:
            packDataObj = PyGameDataStruct.tagDBPlayerPackData()
            packDataObj.PlayerID = playerID
            self.playerPackDataDict[playerID] = packDataObj
        return packDataObj
    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.UpdTime = int(time.time())
        packObj.PackData = packData
        packObj.PackDataSize = len(packObj.PackData)
        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
    # 保存数据 存数据库和realtimebackup
    def GetSaveData(self):
        savaData = ""
        cntData = ""
        cnt = 0
        for dbData in self.playerPackDataDict.values():
            cnt += 1
            savaData += dbData.getBuffer()
        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)
            self.playerPackDataDict[dbData.PlayerID] = dbData
        return pos
def IsSaveDB(packDataObj):
    ## 是否入库
    if not packDataObj:
        return False
    # 功能固定需要的
    # ...
    maxDays = 7 # 默认7天
    MaxTime = maxDays * 3600 * 24
    curTime = int(time.time())
    passTime = curTime - packDataObj.UpdTime
    if passTime < MaxTime:
        return True
    return False
def DelOutofTimePackData():
    ## 删除过期
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    playerPackDataDict = packDataMgr.playerPackDataDict
    for playerID, packDataObj in playerPackDataDict.items():
        if IsSaveDB(packDataObj):
            continue
        playerPackDataDict.pop(playerID)
    return
def IsPackDataPlayer(playerID):
    return playerID in PyDataManager.GetDBPlayerPackDataManager().playerPackDataDict
    return PyDataManager.GetDBPlayerPackDataManager().IsPlayerIn(playerID)
def OnPlayerLogin(curPlayer):
    ## 本服登录逻辑
    playerID = curPlayer.GetPlayerID()
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    if playerID in packDataMgr.playerPackDataDict:
        isCross, isNeed = 0, 1
        QueryPlayerResult_PlayerMirror(curPlayer, "PackDataSyncState", [isCross, isNeed])
    return
def OnPlayerLogin_CrossLogic(serverGroupID, serverID, playerID):
    ## 跨服登录逻辑
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    if playerID in packDataMgr.playerPackDataDict:
        dataMsg = {"playerID":playerID}
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PlayerPackDataState, dataMsg, [serverGroupID])
    if packDataMgr.IsPlayerIn(playerID):
        QueryPlayerResult_PlayerMirror(curPlayer, "PackDataSyncState", {"PackData":1})
    return
def SetNeedPackData(playerIDList):
@@ -157,7 +114,7 @@
    pullPlayerIDList = []
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    for playerID in playerIDList:
        if playerID in packDataMgr.playerPackDataDict:
        if packDataMgr.IsPlayerIn(playerID):
            continue
        pullPlayerIDList.append(playerID)
        
@@ -183,6 +140,7 @@
    
    # pullFrom 0-跨服拉子服; >0-子服通过跨服拉子服
    if GameWorld.IsCrossServer():
        PlayerViewCache.SetNeedViewCache(pullPlayerIDList) # 拉打包数据的时候默认需要缓存数据
        # 广播给子服拉数据
        msgInfo["pullFrom"] = 0
        dataMsg = {"pullPlayerIDList":pullPlayerIDList, "msgInfo":msgInfo}
@@ -213,7 +171,7 @@
    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)
    # 在线的转发给地图
@@ -256,22 +214,23 @@
    msgType = msgInfo.get("msgType")
    # 镜像战斗
    if msgType == "MirrorBattle":
        sceneMapID = msgInfo.get("sceneMapID", 0)
        playerID = msgInfo.get("playerID", 0)
        # 玩家发起的
        if playerID:
            playerID = msgInfo["playerID"]
            curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
            if not curPlayer:
                return
            tagMapID = GameWorld.GetQueryPlayerMapID(curPlayer)
            routeIndex = curPlayer.GetRouteServerIndex()
        else:
            tagMapID = msgInfo.get("requestMapID", 0)
            routeIndex = -1
        routeIndex = -1
#        # 玩家发起的
#        if playerID:
#            playerID = msgInfo["playerID"]
#            curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
#            if not curPlayer:
#                return
#            sceneMapID = GameWorld.GetQueryPlayerMapID(curPlayer)
#            routeIndex = curPlayer.GetRouteServerIndex()
#        else:
#            routeIndex = -1
            
        sendMsg = str([msgInfo, packDataDict])
        GameWorld.DebugLog("MapServer_QueryPlayer tagMapID=%s,len=%s" % (tagMapID, len(sendMsg)), playerID)
        GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, 0, playerID, tagMapID, "PlayerMirror", sendMsg, len(sendMsg), routeIndex)
        GameWorld.DebugLog("MapServer_QueryPlayer sceneMapID=%s,len=%s" % (sceneMapID, len(sendMsg)), playerID)
        GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, 0, playerID, sceneMapID, "PlayerMirror", sendMsg, len(sendMsg), routeIndex)
        
    # 其他功能可再扩展
    else:
@@ -283,9 +242,6 @@
    ## 收到子服同步的玩家打包数据
    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", {})
@@ -310,22 +266,23 @@
    
    msgInfo = msgData["msgInfo"]
    pullPlayerIDList = msgData["pullPlayerIDList"]
    dbPackDataIDList = msgData.get("dbPackDataIDList", []) # db标记的有打包数据的玩家ID
    
    otherServerPlayerIDList = []
    packDataDict = {}
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    for playerID in pullPlayerIDList:
        packObj = packDataMgr.GetPlayerPackObj(playerID)
        packData = packDataMgr.GetPlayerPackdata(playerID)
        # 已经有的数据先推送回去
        if packObj:
            packDataDict[playerID] = packObj.PackData
            dataMsg = {"playerID":playerID, "packData":packObj.PackData, "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)
        
@@ -339,8 +296,7 @@
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        return
    isCross, isNeed = 1, 1
    QueryPlayerResult_PlayerMirror(curPlayer, "PackDataSyncState", [isCross, isNeed])
    QueryPlayerResult_PlayerMirror(curPlayer, "PackDataSyncState", msgData)
    return
def CrossServerMsg_PullPlayerPackData(msgData):
@@ -349,18 +305,16 @@
    msgInfo = msgData["msgInfo"]
    pullPlayerIDList = msgData["pullPlayerIDList"]
    
    needPullPlayerIDList = []
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    for playerID in pullPlayerIDList:
        packObj = packDataMgr.GetPlayerPackObj(playerID)
        if packObj:
            # 本服有数据,直接推给跨服
            dataMsg = {"playerID":playerID, "packData":packObj.PackData, "msgInfo":msgInfo}
        packData = packDataMgr.GetPlayerPackdata(playerID)
        if packData:
            GameWorld.DebugLog("本服有缓存玩家打包数据,直接推给跨服! playerID=%s" % playerID)
            dataMsg = {"playerID":playerID, "packData":packData, "msgInfo":msgInfo}
            CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PlayerPackData, dataMsg)
        else:
            needPullPlayerIDList.append(playerID)
            DoPullPlayerPackData(playerID, msgInfo)
            
    DoPullPlayerPackData(needPullPlayerIDList, msgInfo)
    return
def CrossServerMsg_PushPlayerPackData(msgData):
@@ -376,7 +330,7 @@
    curPlayer.MapServer_QueryPlayerResult(0, 0, "PlayerMirror", sysMsg, len(sysMsg))
    return
def OnMGUpdatePlayerPackData(curPlayer, curPackData):
def OnMGUpdatePlayerPackData(curPlayer, curPackData, msgInfo):
    ## 地图同步更新的玩家打包数据
    if GameWorld.IsCrossServer():
        return
@@ -385,16 +339,15 @@
    packData = curPackData.PackData
    if not packDataSyncState or not packData:
        return
    msgInfo = eval(curPackData.PackMsg) if curPackData.PackMsg else {} # 打包数据附带的信息
    
    # 本服需要,先更新数据
    if packDataSyncState % 10:
    # 本服需要,先更新数据;跨服需要则也默认本服需要
    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 / 10:
        cacheBase = PlayerViewCache.GetSyncCrossCacheBase(curPlayer) if curPlayer else {}
        dataMsg = {"playerID":playerID, "packData":packData, "cacheBase":cacheBase, "msgInfo":msgInfo}
    if packDataSyncState&pow(2, 1):
        dataMsg = {"playerID":playerID, "packData":packData, "msgInfo":msgInfo}
        CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PlayerPackData, dataMsg)
        
    # 本服需要的功能
@@ -415,6 +368,7 @@
        CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PlayerPackData, dataMsg)
        return
    
    PyDataManager.GetDBPlayerPackDataManager().UpdPlayerPackData(playerID, packData)
    # 本服需要,汇总结果
    ReuestPlayerPackDataRet(msgInfo, playerID, packData)
    return
@@ -430,9 +384,9 @@
    requestID = msgInfo["requestID"]
    mirrorIDList = msgInfo["mirrorIDList"]
    for mirrorID in mirrorIDList:
        packObj = packDataMgr.GetPlayerPackObj(mirrorID)
        if packObj:
            packDataDict[mirrorID] = packObj.PackData
        packData = packDataMgr.GetPlayerPackdata(mirrorID)
        if packData:
            packDataDict[mirrorID] = packData
            continue
        pullPlayerIDList.append(mirrorID)