From 1cf37b4b51fc287ca3e443afb72604ec88f72cc4 Mon Sep 17 00:00:00 2001 From: hch <305670599@qq.com> Date: 星期三, 09 七月 2025 19:33:55 +0800 Subject: [PATCH] 0312 玩家物品支持DWORD数量 --- ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py | 201 +++++++++++++++++++++++++++++++++++-------------- 1 files changed, 142 insertions(+), 59 deletions(-) diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py index 47ff107..99dc267 100644 --- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py +++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py @@ -15,126 +15,180 @@ #"""Version = 2025-05-09 12:20""" #------------------------------------------------------------------------------- +import CommFunc +import DBStruct +import ChConfig import GameWorld import PyGameData +import PyMongoMain import DBPlayerViewCache +import DBEventTrig +import DBBillboard +import DBFuncTeam +import DBGameRec import DBFamily import DBMail -import binascii import time -import zlib import os BakRoot = "C:\ServerRealTimeBackup" -BakFileType = ".bak" +BakFileType = ".rtb" +BackupCopyMax = 5 # 保留历史备档文件数 +BackupInterval = 30 # 备档频率,分钟,暂定每30分钟 -#临时变量,之后优化 -g_loadErr = False -Map2ServerID = { - 10010:87, - 10090:89, - } - def OnServerStart(): + ##启动服务器数据处理 GameWorld.DebugLog("地图服务器启动") - LoadServerDataBackup() + if not LoadGameWorldDataFromBackup(): + LoadGameWorldDataFromDB() return def OnServerClose(): + ##关服服务器数据处理 + SaveGameWorldDataToDB() return def OnMinute(curTime): - curMinute = curTime.minute - ServerDataBackup() - DBFamily.OnMinute(curMinute) - return - -def OnDayEx(): + if PyGameData.g_serverClosing: + return + DBFamily.OnMinute() + DBBillboard.OnMinute() + + #备档、入库放最后,等数据都处理完后再保存,每天 [[时,分], ...],尽量岔开整点、5分 + if [curTime.hour, curTime.minute] in [[3, 6]]: + BackupGameWorldData(True) + else: + BackupGameWorldData(False) return #------------------------------------------- 备档 --------------------------------------------------- def GetBakFileSortList(bakPath): - ## 按备档时间倒序排序返回 [[bakTime, filePath], ...] + ## 按备档时间倒序排序返回 [[bakTime, 数据版本号, filePath], ...] bakFileList = [] for parent, _, filenames in os.walk(bakPath): for filename in filenames: if not filename.endswith(BakFileType): continue fullPath = os.path.join(parent, filename) - bakTime = GameWorld.ToIntDef(filename[:filename.index(".")].split("_")[1]) - bakFileList.append([bakTime, fullPath]) + bakInfo = filename[:filename.index(".")].split("_") + if len(bakInfo) != 3: + continue + dataVersionNO = GameWorld.ToIntDef(bakInfo[1]) # 数据版本号 + bakTime = GameWorld.ToIntDef(bakInfo[2]) # yyyyMMddhhmmss + bakFileList.append([bakTime, dataVersionNO, fullPath]) bakFileList.sort(reverse=True) return bakFileList -def LoadServerDataBackup(): - ## 服务器公共数据备档加载 - global g_loadErr - mapID = GameWorld.GetMap().GetMapID() - if mapID not in Map2ServerID: - return - serverName = "S%s" % Map2ServerID[mapID] +def LoadGameWorldDataFromBackup(): + ## 备档加载服务器公共数据 + # @return: 是否存在备档成功加载 + serverID = GameWorld.GetGameWorld().GetServerID() + serverName = "S%s" % serverID BakDir = os.path.join(BakRoot, serverName) GameWorld.Log("加载备档: %s" % BakDir) bakFileList = GetBakFileSortList(BakDir) if not bakFileList: GameWorld.Log("不存在备档!") return - bakFilePath = bakFileList[0][1] # 取第一个为最近的一次备档 + finalBakInfo = bakFileList[0] # 排序后的第一个为最新备档数据 + bakVersionNO = finalBakInfo[1] + bakFilePath = finalBakInfo[2] + if bakVersionNO != DBStruct.GAMEWORLD_DATA_VERSION_NO: + GameWorld.ErrLog("备档数据版本号校验失败,请使用正确版本服务器启动重新导入数据! bakVersionNO=%s, GAMEWORLD_DATA_VERSION_NO=%s" + % (bakVersionNO, DBStruct.GAMEWORLD_DATA_VERSION_NO)) + raise + GameWorld.Log("读取备档: %s" % bakFilePath) f = open(bakFilePath, 'rb') - compressed_data = f.read().strip() + compressed_data = f.read() f.close() - try: - decompressed_data = zlib.decompress(compressed_data) - bakData = binascii.a2b_hex(decompressed_data) - except: - g_loadErr = True - raise + saveData = CommFunc.Decompress(compressed_data) + if not saveData: + raise - LoadPyGameData(bakData, 0) - return + LoadPyGameData(saveData, 0) + SaveGameWorldDataToDB(saveData) # 如果是从备档读取的则直接入库 + return True -def ServerDataBackup(): +def BackupGameWorldData(saveToDB=False): ## 服务器公共数据定时备档,暂时直接存盘 - if g_loadErr: - GameWorld.ErrLog("加载备档已异常,暂时不在存储备档") + # @param saveToDB: 是否附带入库,为确保备档数据存在时一定优于db库数据,故常规入库时必须附带先备档一次 + if PyGameData.g_serverClosing: return - mapID = GameWorld.GetMap().GetMapID() - if mapID not in Map2ServerID: + if not PyGameData.g_loadDataOK: + GameWorld.Log("加载数据未完成,不存储备档") return - serverName = "S%s" % Map2ServerID[mapID] + curTime = int(time.time()) + if saveToDB: + if curTime - PyGameData.g_lastRTBTime < BackupInterval * 60: + GameWorld.Log("备档冷却中!") + return + + serverID = GameWorld.GetGameWorld().GetServerID() + serverName = "S%s" % serverID BakDir = os.path.join(BakRoot, serverName) - BackupCopyMax = 3 GameWorld.Log("服务器备档: %s" % serverName) if not os.path.exists(BakDir): os.makedirs(BakDir) - curTime = int(time.time()) - bakFilePath = os.path.join(BakDir, "%s_%s%s" % (serverName, curTime, BakFileType)) - bakData = GetSavePyData() - GameWorld.Log("Bak:%s, len=%s, %s" % (serverName, len(bakData), bakFilePath)) + dataVersionNO = DBStruct.GAMEWORLD_DATA_VERSION_NO + bakTiemStr = GameWorld.ChangeTimeNumToStr(curTime, ChConfig.TYPE_Time_Format_YmdHMS) + bakFilePath = os.path.join(BakDir, "%s_%s_%s%s" % (serverName, dataVersionNO, bakTiemStr, BakFileType)) + saveData = GetSavePyData() + GameWorld.Log("Bak:%s, len=%s, %s" % (serverName, len(saveData), bakFilePath)) + compressed_data = CommFunc.Compress(saveData) # 备档数据才进行压缩 + if not compressed_data: + GameWorld.SendGameError("ServerDataBackupError") + return + GameWorld.Log("compress len=%s" % len(compressed_data)) + try: - compressed_data = zlib.compress(bakData, 9) #最大压缩 - GameWorld.Log("compress len=%s" % len(compressed_data)) fp = open(bakFilePath, "wb") fp.write(compressed_data) fp.close() except: return + PyGameData.g_lastRTBTime = curTime bakFileList = GetBakFileSortList(BakDir) # 删除多余备档 - for _, filePath in bakFileList[BackupCopyMax:]: + for bakInfo in bakFileList[BackupCopyMax:]: + filePath = bakInfo[-1] os.remove(filePath) GameWorld.Log("删除多余备档文件: %s" % filePath) + if saveToDB: + SaveGameWorldDataToDB(saveData) + return + +def ClearBackupFile(): + ## 清空备档文件 + serverID = GameWorld.GetGameWorld().GetServerID() + serverName = "S%s" % serverID + BakDir = os.path.join(BakRoot, serverName) + GameWorld.Log("清空备档: %s" % BakDir) + CommFunc.DelFolder(BakDir, True) return #-------------------------------------------------------------------------------------------------- + +def LoadGameWorldDataFromDB(): + ## 直接从db读取公共数据 + GameWorld.Log("没有备档数据,直接从db加载数据!") + serverData = PyMongoMain.GetUserCtrlDB().readGameWorldData() + LoadPyGameData(serverData, 0) + return + +def SaveGameWorldDataToDB(saveData=""): + ## 公共数据入库 + if not saveData: + saveData = GetSavePyData() + PyMongoMain.GetUserCtrlDB().saveGameWorldData(saveData) + return def GetSavePyData(): @@ -144,12 +198,7 @@ pyGameDataMgr = GetDBDataMgr() result = pyGameDataMgr.GetSaveData() GameWorld.Log("GetSavePyData!! id = %s-%s"%(id(pyGameDataMgr), len(result))) - result = binascii.b2a_hex(result) - #GameWorld.DebugLog("GetSavePyData!! result = %s-%s"%(result, len(result))) - # 字节码在C++转化会发生错误must be string without null bytes, not str,但是可以正常保存,错误会在下次调用便宜接口才会触发 - # 暂时改成字符串返回,暂无解决方案 return result -# return str(len(result)) + "|" + result def LoadPyGameData(gameBuffer, pos): pyGameDataMgr = GetDBDataMgr() @@ -159,27 +208,42 @@ #加载数据后,一些功能转化为功能业务数据 #... + + #放最后 + PyGameData.g_loadDataOK = True return pos class PyGameDataManager(object): def __init__(self): + self.EventTrigMgr = DBEventTrig.EventTrigMgr() self.PlayerViewCacheMgr = DBPlayerViewCache.PlayerViewCacheMgr() - self.FamilyMgr = DBFamily.FamilyMgr() + self.BillboardMgr = DBBillboard.BillboardMgr() self.MailMgr = DBMail.MailMgr() + self.FamilyMgr = DBFamily.FamilyMgr() + self.GameRecMgr = DBGameRec.GameRecMgr() + self.FuncTeamMgr = DBFuncTeam.FuncTeamMgr() return def GetSaveData(self): buff = "" + buff += self.EventTrigMgr.GetSaveData() buff += self.PlayerViewCacheMgr.GetSaveData() - buff += self.FamilyMgr.GetSaveData() + buff += self.BillboardMgr.GetSaveData() buff += self.MailMgr.GetSaveData() + buff += self.FamilyMgr.GetSaveData() + buff += self.GameRecMgr.GetSaveData() + buff += self.FuncTeamMgr.GetSaveData() return buff def LoadGameData(self, gameBuffer, pos): dataslen = len(gameBuffer) + pos = self.EventTrigMgr.LoadPyGameData(gameBuffer, pos, dataslen) pos = self.PlayerViewCacheMgr.LoadPyGameData(gameBuffer, pos, dataslen) - pos = self.FamilyMgr.LoadPyGameData(gameBuffer, pos, dataslen) + pos = self.BillboardMgr.LoadPyGameData(gameBuffer, pos, dataslen) pos = self.MailMgr.LoadPyGameData(gameBuffer, pos, dataslen) + pos = self.FamilyMgr.LoadPyGameData(gameBuffer, pos, dataslen) + pos = self.GameRecMgr.LoadPyGameData(gameBuffer, pos, dataslen) + pos = self.FuncTeamMgr.LoadPyGameData(gameBuffer, pos, dataslen) return pos def GetDBDataMgr(): @@ -189,6 +253,11 @@ pyGameDataMgr = PyGameDataManager() PyGameData.g_pyGameDataManager = pyGameDataMgr return pyGameDataMgr + +def GetEventTrigMgr(): + ## 事件值管理器 + dbDataMgr = GetDBDataMgr() + return dbDataMgr.EventTrigMgr def GetPlayerViewCacheMgr(): ## 玩家查看缓存数据管理器 @@ -209,3 +278,17 @@ dbDataMgr = GetDBDataMgr() return dbDataMgr.MailMgr +def GetBillboardMgr(): + ## 排行榜数据管理器 + dbDataMgr = GetDBDataMgr() + return dbDataMgr.BillboardMgr + +def GetGameRecMgr(): + ## 通用记录数据管理器 + dbDataMgr = GetDBDataMgr() + return dbDataMgr.GameRecMgr + +def GetFuncTeamMgr(): + ## 功能队伍数据管理器 + dbDataMgr = GetDBDataMgr() + return dbDataMgr.FuncTeamMgr -- Gitblit v1.8.0