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