hch
昨天 3bc2e9aae7e595d5be896a9db4c909b76fa6f5be
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py
@@ -16,11 +16,16 @@
#-------------------------------------------------------------------------------
import CommFunc
import DBStruct
import ChConfig
import GameWorld
import PyGameData
import DBEventTrig
import PyMongoMain
import DBPlayerViewCache
import DBEventTrig
import DBBillboard
import DBFuncTeam
import DBGameRec
import DBFamily
import DBMail
@@ -28,43 +33,56 @@
import os
BakRoot = "C:\ServerRealTimeBackup"
BakFileType = ".bak"
BackupCopyMax = 3
BakFileType = ".rtb"
BackupCopyMax = 5 # 保留历史备档文件数
BackupInterval = 30 # 备档频率,分钟,暂定每30分钟
g_loadErr = False
def OnServerStart():
    ##启动服务器数据处理
    GameWorld.DebugLog("地图服务器启动")
    LoadServerDataBackup()
    if not LoadGameWorldDataFromBackup():
        LoadGameWorldDataFromDB()
    return
def OnServerClose():
    ##关服服务器数据处理
    SaveGameWorldDataToDB()
    return
def OnMinute(curTime):
    #curMinute = curTime.minute
    ServerDataBackup()
    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
def LoadGameWorldDataFromBackup():
    ## 备档加载服务器公共数据
    # @return: 是否存在备档成功加载
    serverID = GameWorld.GetGameWorld().GetServerID()
    serverName = "S%s" % serverID
    BakDir = os.path.join(BakRoot, serverName)
@@ -73,26 +91,42 @@
    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()
    f.close()
    
    bakData = CommFunc.Decompress(compressed_data)
    if not bakData:
        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
    if not PyGameData.g_loadDataOK:
        GameWorld.Log("加载数据未完成,不存储备档")
        return
    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)
@@ -101,11 +135,12 @@
    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))
    compressed_data = CommFunc.Compress(bakData)
    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
@@ -118,15 +153,42 @@
    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():
    
@@ -146,33 +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.MailMgr = DBMail.MailMgr()
        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.MailMgr.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.MailMgr.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():
@@ -212,3 +283,12 @@
    dbDataMgr = GetDBDataMgr()
    return dbDataMgr.BillboardMgr
def GetGameRecMgr():
    ## 通用记录数据管理器
    dbDataMgr = GetDBDataMgr()
    return dbDataMgr.GameRecMgr
def GetFuncTeamMgr():
    ## 功能队伍数据管理器
    dbDataMgr = GetDBDataMgr()
    return dbDataMgr.FuncTeamMgr