hxp
2024-11-18 1029b172223ee7a30bda1b4dc64829b011fe2ff2
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldActionControl.py
@@ -33,10 +33,17 @@
import PlayerFamilyRedPacket
import PlayerFairyCeremony
import PlayerNewFairyCeremony
import PlayerActFamilyCTGAssist
import PlayerActGarbageSorting
import PlayerActBossTrial
import PlayerActXianXiaMJ
import PlayerActGubao
import PlayerActHorsePetTrain
import PlayerUniversalGameRec
import GameWorldAverageLv
import PlayerFamilyBoss
import PlayerHorsePetBoss
import CrossActionControl
import GameWorldProcess
import ChPyNetSendPack
import NetPackCommon
@@ -59,7 +66,14 @@
def OnPlayerLogin(curPlayer):
    
    isReload, OperationActionInfo = __GetOperationActionInfo()
    if GameWorld.IsCrossServer():
        # 跨服不处理运营活动
        return
    # 玩家登录的不触发重载活动,不然刚好在0点服务器处理OnDay之前登录的时候会有问题
    isReload, OperationActionInfo = __GetOperationActionInfo(needReload=False)
    if not OperationActionInfo:
        return
    operationActionDict = OperationActionInfo[OperationAction_TodayInfo]
    
    if isReload:
@@ -99,6 +113,12 @@
def SendMapServerOperationActionState():
    # 地图启动成功时通知本日运行活动相关状态
    
    CrossActionControl.SendMapServerCrossActionState()
    if GameWorld.IsCrossServer():
        # 跨服不处理运营活动
        return
    isReload, OperationActionInfo = __GetOperationActionInfo()
    mapServerInfoDict = OperationActionInfo[OperationAction_MapServerInfo]
    
@@ -106,18 +126,79 @@
        # 如果是重读的,则在内层已经同步了,此处不重复同步
        return
    
    gameWorld = GameWorld.GetGameWorld()
    for actName in ShareDefine.OperationActionNameList:
        dictName = ChConfig.Def_WorldKey_OperationActionState % actName
        state = gameWorld.GetDictByKey(dictName)
        sendMapServerMsgDict = mapServerInfoDict.get(actName, {})
        sendMapServerMsgDict[ShareDefine.ActKey_State] = state
        GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_OperationActionInfo % actName, sendMapServerMsgDict)
        if actName in ShareDefine.MultiActNumOperationActNameList:
            for actNumMapMsgDict in sendMapServerMsgDict.values():
                GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_OperationActionInfo % actName, actNumMapMsgDict)
        else:
            GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_OperationActionInfo % actName, sendMapServerMsgDict)
        
    return
def __GetOperationActionInfo(isRefreshState=True):
def GetOperationActNum(actName, ipyData=None):
    ''' 获取运营活动分组编号
        加入了分组编号概念,但由于有些活动配表还是旧规则配置,为了做规则兼容旧配表,减少配置表改动及代码改动这里做下自适应分配默认分组编号
        活动类型定义:
        1-默认(仅可配置开服天、日期、周循环W开头+数字,优先级 开服天 > 日期 > 周x)
                                        非开服配置的开服前X天不开,不受合服影响,功能配置表可配置前X天后交叉可开,每日重置的活动前X天后默认可开
        2-合服(仅可配置Mix开头+数字,如Mix1代表合服第1天,以此类推)
        3-节日(仅可配置日期,不受开服、合服影响)
        活动分组编号 = 活动类型 * 10 + 不同界面编号
    '''
    if ipyData and hasattr(ipyData, "GetActNum"):
        return ipyData.GetActNum()
    # 原节日活动的还是默认节日活动
    if actName in ShareDefine.FeastOperationActionNameList:
        return ShareDefine.ActType_Feast * 10
    # 其他默认常规,这里不考虑合服类型的了,新版本合服类型已独立出去,按 ipyData 配置
    return ShareDefine.ActType_OpenComm * 10
def GetOperationActType(actNum):
    ## 运营活动类型
    return actNum / 10
def __SaveActWorldLVLimitInfo(actWorldLVLimitInfo):
    GameWorld.GetUniversalRecMgr().Delete(ShareDefine.Def_UniversalGameRecType_ActWorldLVLimitInfo)
    recDataList = GameWorld.GetUniversalRecMgr().GetTypeList(ShareDefine.Def_UniversalGameRecType_ActWorldLVLimitInfo)
    GameWorld.Log("保存运营活动世界等级限制开启信息: %s" % len(actWorldLVLimitInfo))
    for actName, cfgLimitInfoDict in actWorldLVLimitInfo.items():
        for cfgID, limitInfo in cfgLimitInfoDict.items():
            recStartDateStr, recEndDateStr, recLimitWorldLV, recWorldLV = limitInfo
            recData = recDataList.AddRec()
            recData.SetStrValue1(recStartDateStr)
            recData.SetStrValue2(recEndDateStr)
            recData.SetStrValue3(actName)
            recData.SetValue1(cfgID)
            recData.SetValue2(recLimitWorldLV)
            recData.SetValue3(recWorldLV)
            GameWorld.Log("    actName=%s,cfgID=%s,recStartDateStr=%s,recEndDateStr=%s,recLimitWorldLV=%s,recWorldLV=%s"
                          % (actName, cfgID, recStartDateStr, recEndDateStr, recLimitWorldLV, recWorldLV))
    return
def __GetActWorldLVLimitInfo():
    actWorldLVLimitInfo = {}
    recDataList = GameWorld.GetUniversalRecMgr().GetTypeList(ShareDefine.Def_UniversalGameRecType_ActWorldLVLimitInfo)
    GameWorld.Log("加载运营活动世界等级限制开启信息: %s" % recDataList.Count())
    for index in xrange(recDataList.Count()):
        recData = recDataList.At(index)
        recStartDateStr = recData.GetStrValue1()
        recEndDateStr = recData.GetStrValue2()
        actName = recData.GetStrValue3()
        cfgID = recData.GetValue1()
        recLimitWorldLV = recData.GetValue2()
        recWorldLV = recData.GetValue3()
        if actName not in actWorldLVLimitInfo:
            actWorldLVLimitInfo[actName] = {}
        actWorldLVLimitInfo[actName][cfgID] = [recStartDateStr, recEndDateStr, recLimitWorldLV, recWorldLV]
        GameWorld.Log("    actName=%s,cfgID=%s,recStartDateStr=%s,recEndDateStr=%s,recLimitWorldLV=%s,recWorldLV=%s"
                      % (actName, cfgID, recStartDateStr, recEndDateStr, recLimitWorldLV, recWorldLV))
    return actWorldLVLimitInfo
def __GetOperationActionInfo(isRefreshState=True, needReload=True):
    # @return: isReload, OperationActionInfo
    
    key = "OperationActionInfo"
@@ -133,68 +214,167 @@
        #GameWorld.DebugLog("已经加载过本日运营活动处理信息!openServerDay=%s" % openServerDay)
        return False, OperationActionInfo
    
    if not needReload:
        return False, OperationActionInfo
    # 因为后面的时间判断都是精确到分的,而处理此逻辑的时候可能不是0秒,所以这里的datetime取当前时间精确到分的
    curDateTimeStr = "%d-%d-%d %02d:%02d:00" % (serverTime.year, serverTime.month, serverTime.day, serverTime.hour, serverTime.minute)
    curDateTime = datetime.datetime.strptime(curDateTimeStr, ChConfig.TYPE_Time_Format)
    
    startDateInCustomCanOpenList = IpyGameDataPY.GetFuncEvalCfg("OperationAction", 2) # 开始天在定制天内在定制天结束后可继续开启的活动列表
    customMaxServerDay = IpyGameDataPY.GetFuncCfg("OperationAction", 1) # 定制运营活动最大开服天
    maxCustomServerDayMix = IpyGameDataPY.GetFuncCfg("MixServer", 1) # 定制运营活动最大合服天
    operationActionDict = {}
    operationTodayActionDict = {}
    mapServerOperationActionDict = {}
    platform = GameWorld.GetPlatform()
    serverGroupID = GameWorld.GetServerGroupID()
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    curWeekday = curDateTime.weekday() + 1 # 今天星期几, 1代表星期1
    curWorldLV = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_WorldAverageLv)
    actWorldLVLimitInfoOld = __GetActWorldLVLimitInfo()
    actWorldLVLimitInfoNew = {}
    
    GameWorld.Log("===== 加载本日运营活动信息: %s, serverGroupID=%s,openServerDay=%s,customMaxServerDay=%s,isMixServer=%s,mixServerDay=%s,maxCustomServerDayMix=%s,reloadSign=%s =====" 
                  % (curDateTime, serverGroupID, openServerDay, customMaxServerDay, isMixServer, mixServerDay, maxCustomServerDayMix, reloadSign))
    GameWorld.Log("    今日周%s" % (curWeekday))
    GameWorld.Log("    今日周%s, curWorldLV=%s" % (curWeekday, curWorldLV))
    
    for actName in ShareDefine.OperationActionNameList:
        
        if actName not in mapServerOperationActionDict:
            mapServerOperationActionDict[actName] = {}
        # 取出本活动所属本服ID的所有配置
        GameWorld.Log("加载运营活动: actName=%s" % (actName))
        curServerActIpyDataList = __GetOperationActionServerIpyDataList(ipyDataMgr, serverGroupID, actName)
        GameWorld.Log("加载运营活动: actName=%s,platform=%s,serverGroupID=%s" % (actName, platform, serverGroupID))
        curServerActIpyDataList = __GetOperationActionServerIpyDataList(ipyDataMgr, platform, serverGroupID, actName)
        GameWorld.Log("    可处理条数=%s" % (len(curServerActIpyDataList)))
        disableWeekIpyDataInfo = __GetOperationActionDisableWeekIpyDataInfo(curDateTime, curServerActIpyDataList)
        needStartList = [] # [startDateTime, ...]
        needEndList = [] # [endDateTime, ...]
        needNotifyDict = {} # {notifyDateTime:[notifyKey, [参数]], ...}
        activityIpyData = None # 活动中的的配置,取需要处理的配置中开始时间最晚的,反之开关时间头尾相连的时候无法开启后面的活动
        # 注意:每个活动配置会有多个活动时间点,但是有且只能生效一条活动配置,换句话说就是每个活动配置时间不允许交叉
        coverDisableLoopIpyDataInfo, disableLoopCfgIDDict, otherLoopCfgIDDict, coverDisableWeekIpyDataInfo, disableWeekCfgIDDict = \
            __GetOperationActionDisableIpyDataInfo(actName, curDateTime, curServerActIpyDataList)
        for ipyData in curServerActIpyDataList:
            
            serverGroupIDList = [] if not hasattr(ipyData, "ServerGroupIDList") else ipyData.GetServerGroupIDList()
            serverGroupIDListExcept = [] if not hasattr(ipyData, "ServerGroupIDListExcept") else ipyData.GetServerGroupIDListExcept()
            platformList = [] if not hasattr(ipyData, "GetPlatformList") else ipyData.GetPlatformList()
            serverGroupIDList = [] if not hasattr(ipyData, "GetServerGroupIDList") else ipyData.GetServerGroupIDList()
            serverGroupIDListExcept = [] if not hasattr(ipyData, "GetServerGroupIDListExcept") else ipyData.GetServerGroupIDListExcept()
            cfgID = ipyData.GetCfgID()
            startDateStr = ipyData.GetStartDate()
            endDateStr = ipyData.GetEndDate()
            GameWorld.Log("    cfgID=%s,serverGroupIDList=%s,Except=%s,startDateStr=%s,endDateStr=%s,openServerDay=%s,isMixServer=%s,mixServerDay=%s,maxCustomServerDayMix=%s,curDateTime=%s"
                          % (cfgID, serverGroupIDList, serverGroupIDListExcept, startDateStr, endDateStr, openServerDay, isMixServer, mixServerDay, maxCustomServerDayMix, curDateTime))
            if cfgID in disableWeekIpyDataInfo:
                startWeekDate, endWeekDate, ymdCfgID, ymdStartDate, ymdEndDate = disableWeekIpyDataInfo[cfgID]
                GameWorld.Log("        按星期开启的在按日期开启的时间内,不处理! cfgID=%s,%s(%s) ~ %s(%s) in ymdCfgID=%s,%s ~ %s"
                              % (cfgID, startWeekDate, startDateStr, endWeekDate, endDateStr, ymdCfgID, ymdStartDate, ymdEndDate))
                continue
            actNum = GetOperationActNum(actName, ipyData)
            actType = GetOperationActType(actNum)
            GameWorld.Log("    cfgID=%s,actNum=%s,startDateStr=%s,endDateStr=%s,openServerDay=%s,isMixServer=%s,mixServerDay=%s,curDateTime=%s,platformList=%s,serverGroupIDList=%s,Except=%s"
                          % (cfgID, actNum, startDateStr, endDateStr, openServerDay, isMixServer, mixServerDay, curDateTime, platformList, serverGroupIDList, serverGroupIDListExcept))
            actIDDateTimeSpec = None # 特殊指定的活动ID日期
            startDateSync = None # 特殊同步前端显示用的开始日期,一般用于与开服前X天交叉的活动
            if actName in ShareDefine.MultiActNumOperationActNameList:
                # 多活动分组编号的需要把所有配置的 actNum 都登记进来,以确保地图能正确进行逻辑
                if actNum not in mapServerOperationActionDict[actName]:
                    mapServerOperationActionDict[actName][actNum] = {ShareDefine.ActKey_ActNum:actNum}
                curActTodayInfo = operationTodayActionDict.get(actName, {}).get(actNum)
            else:
                curActTodayInfo = operationTodayActionDict.get(actName)
            if not startDateStr:
                startDateStr = "%d-%d-%d" % (serverTime.year, serverTime.month, serverTime.day)
                GameWorld.Log("        开始日期为空,默认每天,今日为: startDateStr=%s" % startDateStr)
            if not endDateStr:
                endDateStr = "%d-%d-%d" % (serverTime.year, serverTime.month, serverTime.day)
                GameWorld.Log("        结束日期为空,默认每天,今日为: endDateStr=%s" % endDateStr)
            # 活动优先级为   开服天=合服天>运营日期>运营周天
            # 按开服天开的
            if startDateStr.isdigit() and endDateStr.isdigit():
                startServerDay, endServerDay = int(startDateStr), int(endDateStr)
                #结束日可能还需要处理广播之类,所以这里需要+1
                if openServerDay > endServerDay + 1:
                    GameWorld.Log("        当前开服天超过活动结束开服天,不处理! cfgID=%s,%s ~ %s < openServerDay(%s)" % (cfgID, startDateStr, endDateStr, openServerDay))
            actByLoopYmd = startDateStr.startswith("L") # 按日期循环
            # 根据日期循环的通用
            if actByLoopYmd:
                if cfgID in coverDisableLoopIpyDataInfo:
                    loopStartDate, loopEndDate, ymdCfgID, ymdStartDate, ymdEndDate = coverDisableLoopIpyDataInfo[cfgID]
                    GameWorld.Log("        按日期循环的在按日期开启的时间内,不处理! cfgID=%s,%s(%s) ~ %s(%s) in ymdCfgID=%s,%s ~ %s"
                                  % (cfgID, loopStartDate, startDateStr, loopEndDate, endDateStr, ymdCfgID, ymdStartDate, ymdEndDate))
                    continue
                openServerDateTime = curDateTime + datetime.timedelta(days=(startServerDay-openServerDay))
                endServerDateTime = curDateTime + datetime.timedelta(days=(endServerDay-openServerDay))
                startDateStr = "%d-%d-%d" % (openServerDateTime.year, openServerDateTime.month, openServerDateTime.day)
                endDateStr = "%d-%d-%d" % (endServerDateTime.year, endServerDateTime.month, endServerDateTime.day)
                GameWorld.Log("        开服天转化为日期: %s ~ %s" % (startDateStr, endDateStr))
            elif startDateStr.startswith("Mix") and endDateStr.startswith("Mix"):
                if cfgID in disableLoopCfgIDDict:
                    GameWorld.Log("        按日期循环的未到开启循环日期或已结束循环日期,不处理! cfgID=%s,startDateStr=%s,endDateStr=%s" % (cfgID, startDateStr, endDateStr))
                    continue
                if cfgID in otherLoopCfgIDDict:
                    loopCfgIDList, startDateStr, endDateStr, loopIndex, loopTimes = otherLoopCfgIDDict[cfgID]
                    GameWorld.Log("        按日期循环的还未循环到当前配置,不处理! cfgID=%s,startDateStr=%s,endDateStr=%s,loopCfgIDList=%s,loopIndex=%s,loopTimes=%s"
                                  % (cfgID, startDateStr, endDateStr, loopCfgIDList, loopIndex, loopTimes))
                    continue
                startDateStr, endDateStr = GameWorld.GetOperationActionDateStr(ipyData)
            # 开服常规:  开服天 > 日期 > 周x=日期循环   (不受合服天影响,合服活动新增一套独立的活动,还是走运营活动配置)
            if actType == ShareDefine.ActType_OpenComm:
                actByWeek = (startDateStr.startswith("W") and endDateStr.startswith("W")) # 按周x开
                actByDate = (not actByLoopYmd and not actByWeek and startDateStr.count("-") == 2 and endDateStr.count("-") == 2) # 按日期开
                # 开服天的
                if startDateStr.isdigit() and endDateStr.isdigit():
                    startServerDay, endServerDay = int(startDateStr), int(endDateStr)
                    #结束日可能还需要处理广播之类,所以这里需要+1
                    if openServerDay > endServerDay + 1:
                        GameWorld.Log("        当前开服天超过活动结束开服天,不处理! cfgID=%s,%s ~ %s < openServerDay(%s)" % (cfgID, startDateStr, endDateStr, openServerDay))
                        continue
                    openServerDateTime = curDateTime + datetime.timedelta(days=(startServerDay - openServerDay))
                    endServerDateTime = curDateTime + datetime.timedelta(days=(endServerDay - openServerDay))
                    startDateStr = "%d-%d-%d" % (openServerDateTime.year, openServerDateTime.month, openServerDateTime.day)
                    endDateStr = "%d-%d-%d" % (endServerDateTime.year, endServerDateTime.month, endServerDateTime.day)
                    GameWorld.Log("        开服天转化为日期: %s ~ %s" % (startDateStr, endDateStr))
                # 常规配置: 开服前X天不开,不受合服影响,功能配置表可配置 开服前X天后交叉可开,每日重置的活动默认可开
                elif actByWeek or actByDate or actByLoopYmd:
                    if openServerDay <= customMaxServerDay:
                        GameWorld.Log("        按日期/周开的在开服定制限制天内,不处理! cfgID=%s,%s ~ %s,openServerDay=%s" % (cfgID, startDateStr, endDateStr, openServerDay))
                        continue
                    if cfgID in coverDisableWeekIpyDataInfo:
                        startWeekDate, endWeekDate, ymdCfgID, ymdStartDate, ymdEndDate = coverDisableWeekIpyDataInfo[cfgID]
                        GameWorld.Log("        常规活动,按星期开启的在按日期开启的时间内,不处理! cfgID=%s,%s(%s) ~ %s(%s) in ymdCfgID=%s,%s ~ %s"
                                      % (cfgID, startWeekDate, startDateStr, endWeekDate, endDateStr, ymdCfgID, ymdStartDate, ymdEndDate))
                        continue
                    if cfgID in disableWeekCfgIDDict:
                        GameWorld.Log("        常规活动,按星期开启的未到开启循环日期或已结束循环日期,不处理! cfgID=%s,startDateStr=%s,endDateStr=%s, %s"
                                      % (cfgID, startDateStr, endDateStr, str(disableWeekCfgIDDict[cfgID])))
                        continue
                    if actByWeek:
                        startDateStr, endDateStr = GameWorld.GetOperationActionDateStr(ipyData)
                        GameWorld.Log("        星期X转化为日期: %s ~ %s" % (startDateStr, endDateStr))
                    curServerOpenDateTime = curDateTime + datetime.timedelta(days=(1 - openServerDay)) # 开服第一天的日期
                    customMaxServerDateTime = curDateTime + datetime.timedelta(days=(customMaxServerDay - openServerDay))
                    curStartDateTime = datetime.datetime.strptime("%s %02d:%02d:%02d" % (startDateStr, customMaxServerDateTime.hour, customMaxServerDateTime.minute,
                                                                                         customMaxServerDateTime.second), ChConfig.TYPE_Time_Format)
                    if curServerOpenDateTime <= curStartDateTime <= customMaxServerDateTime:
                        # 每日重置的默认交叉后可开启
                        isDayRest = 0 if not hasattr(ipyData, "GetIsDayReset") else ipyData.GetIsDayReset()
                        if not isDayRest and actName not in startDateInCustomCanOpenList:
                            GameWorld.Log("        按日期/周开的开始日期在开服定制限制天内,不处理! cfgID=%s,curServerOpenDateTime=%s<=curStartDateTime=%s<=customMaxServerDateTime=%s" % (cfgID, curServerOpenDateTime, curStartDateTime, customMaxServerDateTime))
                            continue
                        # 非每日重置的 且 开始天在定制天内在定制天结束后可继续开启的活动
                        # 注: 为防止开始日期与开服天内的活动开始天对应日期刚好同一天导致活动ID一样,所以这里默认将开始日期改为定制天后一天
                        if not isDayRest and actName in startDateInCustomCanOpenList:
                            actIDDateTimeSpec = datetime.datetime.strptime("%d-%d-%d 00:00:00"
                                                                           % (customMaxServerDateTime.year, customMaxServerDateTime.month, customMaxServerDateTime.day),
                                                                           ChConfig.TYPE_Time_Format) + datetime.timedelta(days=1)
                            GameWorld.Log("        开服天后可开启的非每日重置活动! 活动ID日期特殊设置为开服定制天结束后一天! cfgID=%s,actIDDateTimeSpec=%s" % (cfgID, actIDDateTimeSpec))
                        # 特殊同步的开始日期,无视是否每日重置
                        if actName in startDateInCustomCanOpenList:
                            startDateSync = datetime.datetime.strptime("%d-%d-%d 00:00:00"
                                                                       % (customMaxServerDateTime.year, customMaxServerDateTime.month, customMaxServerDateTime.day),
                                                                       ChConfig.TYPE_Time_Format) + datetime.timedelta(days=1)
                            startDateSync = "%d-%d-%d" % (startDateSync.year, startDateSync.month, startDateSync.day)
                else:
                    GameWorld.Log("        开服常规活动,配置时间格式不支持,不处理! cfgID=%s,startDateStr=%s,endDateStr=%s" % (cfgID, startDateStr, endDateStr))
                    continue
            # 合服活动: 只受合服天影响
            elif actType == ShareDefine.ActType_MixServer:
                if not startDateStr.startswith("Mix") or not endDateStr.startswith("Mix"):
                    GameWorld.Log("        合服活动,配置非合服天,不处理! cfgID=%s,startDateStr=%s,endDateStr=%s" % (cfgID, startDateStr, endDateStr))
                    continue
                if not isMixServer:
                    GameWorld.Log("        非合服服务器,不处理! cfgID=%s,%s ~ %s" % (cfgID, startDateStr, endDateStr))
                    continue
@@ -203,43 +383,29 @@
                if mixServerDay > endMixServerDay + 1:
                    GameWorld.Log("        当前合服天超过活动结束合服天,不处理! cfgID=%s,%s ~ %s < mixServerDay(%s)" % (cfgID, startDateStr, endDateStr, mixServerDay))
                    continue
                openServerDateTime = curDateTime + datetime.timedelta(days=(startMixServerDay-mixServerDay))
                endServerDateTime = curDateTime + datetime.timedelta(days=(endMixServerDay-mixServerDay))
                openServerDateTime = curDateTime + datetime.timedelta(days=(startMixServerDay - mixServerDay))
                endServerDateTime = curDateTime + datetime.timedelta(days=(endMixServerDay - mixServerDay))
                startDateStr = "%d-%d-%d" % (openServerDateTime.year, openServerDateTime.month, openServerDateTime.day)
                endDateStr = "%d-%d-%d" % (endServerDateTime.year, endServerDateTime.month, endServerDateTime.day)
                GameWorld.Log("        合服天转化为日期: %s ~ %s" % (startDateStr, endDateStr))
            elif startDateStr.startswith("W") and endDateStr.startswith("W"):
                startWeekday = int(startDateStr[1:])
                endWeekday = int(endDateStr[1:])
                startWeekDate = curDateTime + datetime.timedelta(days=(startWeekday-curWeekday))
                endWeekDate = curDateTime + datetime.timedelta(days=(endWeekday-curWeekday))
                startDateStr = "%d-%d-%d" % (startWeekDate.year, startWeekDate.month, startWeekDate.day)
                endDateStr = "%d-%d-%d" % (endWeekDate.year, endWeekDate.month, endWeekDate.day)
                GameWorld.Log("        星期X转化为日期: %s ~ %s" % (startDateStr, endDateStr))
            elif actName not in ShareDefine.FeastOperationActionNameList:
                if openServerDay <= customMaxServerDay:
                    GameWorld.Log("        按日期开的在开服定制限制天内,不处理! cfgID=%s,%s ~ %s,openServerDay=%s" % (cfgID, startDateStr, endDateStr, openServerDay))
                    continue
                curServerOpenDateTime = curDateTime + datetime.timedelta(days=(1-openServerDay)) # 开服第一天的日期
                customMaxServerDateTime = curDateTime + datetime.timedelta(days=(customMaxServerDay-openServerDay))
                curStartDateTime = datetime.datetime.strptime("%s %02d:%02d:%02d" % (startDateStr, customMaxServerDateTime.hour, customMaxServerDateTime.minute,
                                                                                     customMaxServerDateTime.second), ChConfig.TYPE_Time_Format)
                if curServerOpenDateTime <= curStartDateTime <= customMaxServerDateTime:
                    GameWorld.Log("        按日期开的开始日期在开服定制限制天内,不处理! cfgID=%s,curServerOpenDateTime=%s<=curStartDateTime=%s<=customMaxServerDateTime=%s" % (cfgID, curServerOpenDateTime, curStartDateTime, customMaxServerDateTime))
            # 节日活动:只受日期影响
            elif actType == ShareDefine.ActType_Feast:
                if startDateStr.count("-") != 2 or endDateStr.count("-") != 2:
                    GameWorld.Log("        节日活动,配置非日期,不处理! cfgID=%s,startDateStr=%s,endDateStr=%s" % (cfgID, startDateStr, endDateStr))
                    continue
                
                if isMixServer:
                    if mixServerDay <= maxCustomServerDayMix:
                        GameWorld.Log("        按日期开的在合服定制限制天内,不处理! cfgID=%s,%s ~ %s,mixServerDay=%s" % (cfgID, startDateStr, endDateStr, mixServerDay))
                        continue
                    mixStartServerDateTime = curDateTime + datetime.timedelta(days=(1-mixServerDay)) # 合服第一天的日期
                    customMaxServerDateTime = curDateTime + datetime.timedelta(days=(maxCustomServerDayMix-mixServerDay))
                    curStartDateTime = datetime.datetime.strptime("%s %02d:%02d:%02d" % (startDateStr, customMaxServerDateTime.hour, customMaxServerDateTime.minute,
                                                                                         customMaxServerDateTime.second), ChConfig.TYPE_Time_Format)
                    if mixStartServerDateTime <= curStartDateTime <= customMaxServerDateTime:
                        GameWorld.Log("        按日期开的开始日期在合服定制限制天内,不处理! cfgID=%s,mixStartServerDateTime=%s<=curStartDateTime=%s<=customMaxServerDateTime=%s" % (cfgID, mixStartServerDateTime, curStartDateTime, customMaxServerDateTime))
                        continue
            else:
                GameWorld.Log("        非法配置,未知活动类型,不处理! cfgID=%s,actNum=%s" % (cfgID, actNum))
                continue
            if hasattr(ipyData, "GetJoinStartTime") and hasattr(ipyData, "GetJoinEndTime"):
                joinStartTimeStr = ipyData.GetJoinStartTime()
                joinEndTimeStr = ipyData.GetJoinEndTime()
            else:
                joinStartTimeStr = ""
                joinEndTimeStr = ""
            if hasattr(ipyData, "GetStartTimeList") and hasattr(ipyData, "GetEndTimeList"):
                startHMStrList = ipyData.GetStartTimeList()
                endHMStrList = ipyData.GetEndTimeList()
@@ -256,6 +422,7 @@
                                 % (actName, cfgID, startHMStrList, endHMStrList))
                continue
            
            isDayRest = 0 if not hasattr(ipyData, "GetIsDayReset") else ipyData.GetIsDayReset()
            resetType = 0 if not hasattr(ipyData, "GetResetType") else ipyData.GetResetType() # 重置类型,0-0点重置;1-5点重置
            if resetType == 1:
                startDayDate = datetime.datetime.strptime("%s 05:00:00" % (startDateStr), ChConfig.TYPE_Time_Format)
@@ -270,58 +437,109 @@
            advanceMinutes = 0 if not hasattr(ipyData, "GetAdvanceMinutes") else ipyData.GetAdvanceMinutes() # 提前通知时间,分钟,暂只支持按天的
            GameWorld.Log("        resetType=%s,startDayDate=%s,endDayDate=%s,startHMStrList=%s,endHMStrList=%s,advanceMinutes=%s" 
                          % (resetType, startDayDate, endDayDate, startHMStrList, endHMStrList, advanceMinutes))
            advanceNoticeDateTime = None
            startDayDateJudge = startDayDate # 用于判断是否需要处理的起始时间,一般是活动开始时间,如果有提前广播或预告则时间会提前
            # 提前预告
            if advanceMinutes:
                advanceNoticeDateTime = startDayDate + datetime.timedelta(minutes= -advanceMinutes)
                startDayDateJudge = advanceNoticeDateTime
            # 提前广播
            minNotifyStartMinute = 0 if not notifyInfoDictStart else min(notifyInfoDictStart.keys())
            if minNotifyStartMinute < 0:
                minNotifyStartDateTime = startDayDate + datetime.timedelta(minutes=minNotifyStartMinute)
                if minNotifyStartDateTime < startDayDateJudge:
                    startDayDateJudge = minNotifyStartDateTime
            if curDateTime < startDayDateJudge or curDateTime > endDayDate: # 结束时间点的时候可能需要处理广播之类的
                GameWorld.Log("        非活动时间!不处理!")
                continue
            
            #注: 同个活动编号可配置多个时间不交叉的活动,如果多个时间配置同时满足条件,则只会以最后一个为准,提前预告、广播除外
            if curActTodayInfo and curDateTime < startDayDate:
                activityIpyData = curActTodayInfo[0]
                ## 防止未真正开始的活动需要提前预告,而导致覆盖掉了实际正在进行中的活动,一般是活动时间配置连续的时候可能出现该情况
                GameWorld.Log("        已经存在需要处理的配置ID(%s)! 当前需要提前通知或广播的活动未达到活动开始时间,不处理!cfgID=%s,advanceMinutes=%s,minNotifyStartMinute=%s,startDayDateJudge=%s"
                              % (activityIpyData.GetCfgID(), cfgID, advanceMinutes, minNotifyStartMinute, startDayDateJudge))
                continue
            #注:刚好是结束的时间点,防范已经有需要处理的配置被覆盖
            if curActTodayInfo and curDateTime == endDayDate:
                activityIpyData = curActTodayInfo[0]
                GameWorld.Log("        已经存在需要处理的配置ID(%s)! 当前刚好结束的时间点,不处理!cfgID=%s" % (activityIpyData.GetCfgID(), cfgID))
                continue
            # 在需要处理的时间内附加世界等级开启限制,未达到最低世界等级要求的不开,活动时间从未达到到达到也不开
            limitWorldLV = 0 if not hasattr(ipyData, "GetLimitWorldLV") else ipyData.GetLimitWorldLV() # 限制开启世界等级
            if limitWorldLV:
                GameWorld.Log("        limitWorldLV=%s,curWorldLV=%s" % (limitWorldLV, curWorldLV))
                recInfoType = ""
                worldLVLimitInfo = actWorldLVLimitInfoOld.get(actName, {})
                # 注: 该逻辑是在活动时间内的额外处理,所以需要判断此逻辑的前提理论上都在活动时间内,故只要有记录则以当时的记录为准
                if cfgID in worldLVLimitInfo:
                    recInfoType = "Old"
                    recStartDateStr, recEndDateStr, recLimitWorldLV, recWorldLV = worldLVLimitInfo[cfgID]
                else:
                    recInfoType = "New"
                    recStartDateStr, recEndDateStr, recLimitWorldLV, recWorldLV = startDateStr, endDateStr, limitWorldLV, curWorldLV
                if actName not in actWorldLVLimitInfoNew:
                    actWorldLVLimitInfoNew[actName] = {}
                actWorldLVLimitInfoNew[actName][cfgID] = [recStartDateStr, recEndDateStr, recLimitWorldLV, recWorldLV]
                if recLimitWorldLV > recWorldLV:
                    GameWorld.Log("        活动时间内,但服务器世界等级未达到开启活动世界等级,不处理!recLimitWorldLV=%s > recWorldLV=%s %s"
                                  % (recLimitWorldLV, recWorldLV, recInfoType))
                    continue
            startList = [] # [startDateTime, ...]
            endList = [] # [endDateTime, ...]
            startNotifyDict = {} # {notifyDateTime:notifyInfo, ...}
            endNotifyDict = {} # {notifyDateTime:notifyInfo, ...}
            loopNotifyDict = {} # {notifyDateTime:notifyInfo, ...}
            isActivity = False # 有需要处理开关时间的(开始、结束)
            notifyDict = {}
            isActTime = (startDayDate <= curDateTime < endDayDate)
            isEnd = (curDateTime == endDayDate)
            isNotify = False
            isAdvanceNotice = False
            isEnd = False
            # 没配置时分的代表全天, 只要开始或结束时分没配都算
            if not startHMStrList or not endHMStrList:
                startDateTime = startDayDate
                endDateTime = endDayDate
                startList.append(startDateTime)
                endList.append(endDateTime)
                # 同一个活动类型是不允许时间上配置有重叠的,所以一个活动类型满足活动中的时间的有且仅有一条配置
                # 策划可能同时配置多个时间,但是活动中的有且仅有一条配置
                if startDayDate <= curDateTime <= endDayDate:
                    needStartList.append(startDateTime)
                    needEndList.append(endDateTime)
                    isActivity = True
                    isEnd = (curDateTime == endDayDate)
            # 每天按时段开启的,支持多时段
            elif startDayDate <= curDateTime <= endDayDate:
                isEnd = (curDateTime == endDayDate)
                if not isEnd:
                    for hmIndex, startHMStr in enumerate(startHMStrList):
                        endHMStr = endHMStrList[hmIndex]
                        # 每天开的, 实际开关时间只取今天的日期; 这里有个问题,全服广播的时间不是今天的, 暂不做支持,之后真有这种需求再说
                        startTimeStr = "%d-%d-%d %s:00" % (curDateTime.year, curDateTime.month, curDateTime.day, startHMStr)
                        endTimeStr = "%d-%d-%d %s:00" % (curDateTime.year, curDateTime.month, curDateTime.day, endHMStr)
                        startDateTime = datetime.datetime.strptime(startTimeStr, ChConfig.TYPE_Time_Format)
                        endDateTime = datetime.datetime.strptime(endTimeStr, ChConfig.TYPE_Time_Format)
                        startList.append(startDateTime)
                        endList.append(endDateTime)
                        needStartList.append(startDateTime)
                        needEndList.append(endDateTime)
                        isActivity = True
            if advanceMinutes and startDayDate:
                advanceNoticeDateTime = startDayDate + datetime.timedelta(minutes=-advanceMinutes)
            else:
                for hmIndex, startHMStr in enumerate(startHMStrList):
                    endHMStr = endHMStrList[hmIndex]
                    # 每天开的, 实际开关时间只取今天的日期; 这里有个问题,全服广播的时间不是今天的, 暂不做支持,之后真有这种需求再说
                    startTimeStr = "%d-%d-%d %s:00" % (curDateTime.year, curDateTime.month, curDateTime.day, startHMStr)
                    endTimeStr = "%d-%d-%d %s:00" % (curDateTime.year, curDateTime.month, curDateTime.day, endHMStr)
                    startDateTime = datetime.datetime.strptime(startTimeStr, ChConfig.TYPE_Time_Format)
                    endDateTime = datetime.datetime.strptime(endTimeStr, ChConfig.TYPE_Time_Format)
                    startList.append(startDateTime)
                    endList.append(endDateTime)
            if advanceNoticeDateTime:
                if advanceNoticeDateTime.year == curDateTime.year and advanceNoticeDateTime.month == curDateTime.month and advanceNoticeDateTime.day == curDateTime.day:
                    isAdvanceNotice = True
                GameWorld.Log("        advanceNoticeDateTime=%s,isAdvanceNotice=%s" % (advanceNoticeDateTime, isAdvanceNotice))
            GameWorld.Log("        startList=%s" % (startList))
            GameWorld.Log("        end  List=%s" % (endList))
            GameWorld.Log("        needStartList=%s" % (needStartList))
            GameWorld.Log("        need  EndList=%s" % (needEndList))
            joinStartTimeList, joinEndTimeList = [], [] # 可指定活动可参与的时间点,不影响活动状态,只影响活动某些功能的参与时机,如上榜类
            if joinStartTimeStr:
                if isDayRest:
                    joinStartTimeList.append(datetime.datetime.strptime("%d-%d-%d %s:00" % (curDateTime.year, curDateTime.month, curDateTime.day, joinStartTimeStr), ChConfig.TYPE_Time_Format))
                    joinEndTimeList.append(datetime.datetime.strptime("%d-%d-%d %s:00" % (curDateTime.year, curDateTime.month, curDateTime.day, joinEndTimeStr), ChConfig.TYPE_Time_Format))
                else:
                    joinStartTimeList.append(datetime.datetime.strptime("%s %s:00" % (startDateStr, joinStartTimeStr), ChConfig.TYPE_Time_Format))
                    joinEndTimeList.append(datetime.datetime.strptime("%s %s:00" % (endDateStr, joinEndTimeStr), ChConfig.TYPE_Time_Format))
            GameWorld.Log("        joinStartTimeList=%s" % (joinStartTimeList))
            GameWorld.Log("        joinEndTime  List=%s" % (joinEndTimeList))
            
            for dtIndex, startDateTime in enumerate(startList):
                endDateTime = endList[dtIndex]
@@ -347,7 +565,7 @@
                    loopCount, loopMaxCount = 0, 100
                    while loopMinutes and loopNotifyKey and loopCount < loopMaxCount:
                        loopCount += 1
                        notifyDateTime = startDateTime + datetime.timedelta(minutes=loopMinutes*loopCount)
                        notifyDateTime = startDateTime + datetime.timedelta(minutes=loopMinutes * loopCount)
                        if notifyDateTime >= endDateTime:
                            break
                        if notifyDateTime.year == curDateTime.year and notifyDateTime.month == curDateTime.month and notifyDateTime.day == curDateTime.day:
@@ -358,50 +576,54 @@
                GameWorld.Log("        startNotifyDict: minutes=%s, %s" % (notifyInfoDictStart.keys(), startNotifyDict))
                GameWorld.Log("        end  NotifyDict: minutes=%s, %s" % (notifyInfoDictEnd.keys(), endNotifyDict))
                GameWorld.Log("        loop NotifyDict: lopInfo=%s, %s" % (notifyInfoLoopInfo, loopNotifyDict.keys()))
                needNotifyDict.update(startNotifyDict)
                needNotifyDict.update(endNotifyDict)
                needNotifyDict.update(loopNotifyDict)
                notifyDict.update(startNotifyDict)
                notifyDict.update(endNotifyDict)
                notifyDict.update(loopNotifyDict)
                
            if not isActivity and not isNotify and not isAdvanceNotice:
                continue
            GameWorld.Log("        需要处理的运营活动信息: cfgID=%s,isActTime=%s,isEnd=%s,isNotify=%s,isAdvanceNotice=%s" % (cfgID, isActTime, isEnd, isNotify, isAdvanceNotice))
            
            GameWorld.Log("        需要处理的运营活动信息: cfgID=%s,isAdvanceNotice=%s,isActivity=%s,isEnd=%s,isNotify=%s" % (cfgID, isAdvanceNotice, isActivity, isEnd, isNotify))
            # 当天有需要激活活动或者提前预告活动的取非结束的为准,如果有多个非结束的一般就是策划在活动时间上配置交叉,这种是不允许的
            if isActivity or isAdvanceNotice:
                if not activityIpyData or (isActivity and not isEnd):
                    activityIpyData = ipyData
                    activityInfoDict = {ShareDefine.ActKey_CfgID:cfgID}
                    if actName == ShareDefine.OperationActionName_LoginAward:
                        #活动每天的世界等级
                        activityInfoDict[ShareDefine.ActKey_WorldLVList] = GameWorldAverageLv.GetWorldLVListByTime(startDayDate, (endDayDate - startDayDate).days)
            # 兼容新旧运营活动逻辑处理数据
            if actName in ShareDefine.MultiActNumOperationActNameList:
                if actName not in operationTodayActionDict:
                    operationTodayActionDict[actName] = {} # 今日有需要处理的才初始化
                operationTodayActionDict[actName][actNum] = [ipyData, startList, endList, notifyDict, joinStartTimeList, joinEndTimeList]
            else:
                operationTodayActionDict[actName] = [ipyData, startList, endList, notifyDict, joinStartTimeList, joinEndTimeList]
            if isActTime:
                activityInfoDict = {ShareDefine.ActKey_CfgID:cfgID, ShareDefine.ActKey_ActNum:actNum}
                if actName == ShareDefine.OperationActionName_LoginAward:
                    #活动每天的世界等级
                    activityInfoDict[ShareDefine.ActKey_WorldLVList] = GameWorldAverageLv.GetWorldLVListByTime(startDayDate, (endDayDate - startDayDate).days)
                    
                    if startDayDate <= curDateTime < endDayDate:
                        dayIndex = 0
                        actIDDateTime = startDayDate
                        isDayRest = 0 if not hasattr(ipyData, "GetIsDayReset") else ipyData.GetIsDayReset()
                        # 按时段开的默认每天重置
                        if isDayRest or (startHMStrList and endHMStrList):
                            dayIndex = (curDateTime - startDayDate).days
                            actIDDateTime += datetime.timedelta(days=dayIndex)
                        actID = int(time.mktime(actIDDateTime.timetuple())) # 默认取开始时间点的time值作为活动ID
                        activityInfoDict[ShareDefine.ActKey_DayIndex] = dayIndex
                        activityInfoDict[ShareDefine.ActKey_ID] = actID
                        GameWorld.Log("        isDayRest=%s,actIDDateTime=%s,actID=%s" % (isDayRest, actIDDateTime, actID))
                    needStartList.sort()
                    needEndList.sort()
                if startDateSync:
                    activityInfoDict[ShareDefine.ActKey_StartDateSync] = startDateSync
                    GameWorld.Log("        startDateSync=%s" % (startDateSync))
                    
                dayIndex = (curDateTime - startDayDate).days
                actIDDateTime = startDayDate
                # 按时段开的默认每天重置
                if isDayRest or (startHMStrList and endHMStrList):
                    actIDDateTime += datetime.timedelta(days=dayIndex)
                if actIDDateTimeSpec:
                    actIDDateTime = actIDDateTimeSpec
                actID = int(time.mktime(actIDDateTime.timetuple())) # 默认取开始时间点的time值作为活动ID
                activityInfoDict[ShareDefine.ActKey_DayIndex] = dayIndex
                activityInfoDict[ShareDefine.ActKey_ID] = actID
                GameWorld.Log("        isDayRest=%s,actIDDateTime=%s,actID=%s" % (isDayRest, actIDDateTime, actID))
                GameWorld.Log("        activityInfoDict=%s" % (activityInfoDict))
                # 兼容新旧运营活动逻辑处理数据
                if actName in ShareDefine.MultiActNumOperationActNameList:
                    mapServerOperationActionDict[actName][actNum] = activityInfoDict
                else:
                    mapServerOperationActionDict[actName] = activityInfoDict
                    GameWorld.Log("        activityInfoDict=%s" % (activityInfoDict))
                    
        if activityIpyData or needStartList or needEndList or needNotifyDict:
            # activityIpyData 可能为None
            operationActionDict[actName] = [activityIpyData, needStartList, needEndList, needNotifyDict]
    OperationActionInfo = IpyGameDataPY.SetConfigEx(key, [reloadSign, operationActionDict, mapServerOperationActionDict])
    OperationActionInfo = IpyGameDataPY.SetConfigEx(key, [reloadSign, operationTodayActionDict, mapServerOperationActionDict])
    __SaveActWorldLVLimitInfo(actWorldLVLimitInfoNew)
    
    GameWorld.Log("本日运营活动信息加载完毕!reloadSign=%s,isRefreshState=%s" % (reloadSign, isRefreshState))
    GameWorld.Log("    operationActionDict=%s" % operationActionDict)
    GameWorld.Log("    operationTodayActionDict=%s" % operationTodayActionDict)
    GameWorld.Log("    mapServerOperationActionDict=%s" % mapServerOperationActionDict)
    GameWorld.Log("=============================================================")
    if isRefreshState:
@@ -409,7 +631,7 @@
        
    return True, OperationActionInfo
def __GetOperationActionServerIpyDataList(ipyDataMgr, serverGroupID, actName):
def __GetOperationActionServerIpyDataList(ipyDataMgr, platform, serverGroupID, actName):
    ## 获取运营活动本服务器对应的配置数据列表
    
    if not hasattr(ipyDataMgr, "Get%sCount" % actName):
@@ -420,8 +642,12 @@
    actCfgCount = getattr(ipyDataMgr, "Get%sCount" % actName)()
    for cfgIndex in xrange(actCfgCount):
        ipyData = getattr(ipyDataMgr, "Get%sByIndex" % actName)(cfgIndex)            
        serverGroupIDList = [] if not hasattr(ipyData, "ServerGroupIDList") else ipyData.GetServerGroupIDList()
        serverGroupIDListExcept = [] if not hasattr(ipyData, "ServerGroupIDListExcept") else ipyData.GetServerGroupIDListExcept()
        platformList = [] if not hasattr(ipyData, "GetPlatformList") else ipyData.GetPlatformList()
        serverGroupIDList = [] if not hasattr(ipyData, "GetServerGroupIDList") else ipyData.GetServerGroupIDList()
        serverGroupIDListExcept = [] if not hasattr(ipyData, "GetServerGroupIDListExcept") else ipyData.GetServerGroupIDListExcept()
        if platformList and platform not in platformList:
            continue
        
        # 排除的服务器组ID列表
        if serverGroupIDListExcept:
@@ -447,48 +673,121 @@
                
    return curServerActIpyDataList
def __GetOperationActionDisableWeekIpyDataInfo(curDateTime, curServerActIpyDataList):
def __GetOperationActionDisableIpyDataInfo(actName, curDateTime, curServerActIpyDataList):
    ## 获取不可用的按星期X开启的配置数据信息,按星期X开启的 活动优先级小于按日期的,当有重叠时以日期的为准
    disableWeekIpyDataInfo = {}
    curWeekday = curDateTime.weekday() + 1 # 今天星期几, 1代表星期1
    weekIpyDataList, ymdIpyDatList = [], []
    # 优先级 日期 > 按日期循环 > 按星期循环
    actNumYMDIpyDataInfo = {} # {actNum:ymdIpyDataList, ...} # 配置日期的
    actNumLoopIpyDataInfo = {} # {actNum:{loopKey:[loopCfgIDList, loopDateInfo]}, ...} # 按日期循环的
    actNumWeekIpyDataInfo = {} # {actNum:weekIpyDataList, ...} # 按星期循环的
    disableWeekCfgIDDict = {} # {cfgID:[startDateStr, endDateStr], ...} # 未开始循环的星期
    disableLoopCfgIDDict = {} # {cfgID:[startDateStr, endDateStr], ...} # 未开始循环的日期
    curDateTimeYmdStr = "%d-%d-%d" % (curDateTime.year, curDateTime.month, curDateTime.day)
    curDateTimeYmd = GameWorld.ChangeStrToDatetime(curDateTimeYmdStr, ChConfig.TYPE_Time_YmdFormat)
    for ipyData in curServerActIpyDataList:
        cfgID = ipyData.GetCfgID()
        startDateStr = ipyData.GetStartDate()
        endDateStr = ipyData.GetEndDate()
        if startDateStr.isdigit() or startDateStr.startswith("Mix"):
            # 开服天、合服天的不处理
            continue
        actNum = GetOperationActNum(actName, ipyData)
        
        # 按星期X的
        if startDateStr.startswith("W"):
            startWeekday = int(startDateStr[1:])
            endWeekday = int(endDateStr[1:])
            startWeekDate = curDateTime + datetime.timedelta(days=(startWeekday-curWeekday))
            endWeekDate = curDateTime + datetime.timedelta(days=(endWeekday-curWeekday))
            weekIpyDataList.append([ipyData, startWeekDate, endWeekDate])
        # 按日期的
        else:
            ymdIpyData = ipyData
            ymdStartDate = datetime.datetime.strptime("%s %02d:%02d:00" % (startDateStr, curDateTime.hour, curDateTime.minute), ChConfig.TYPE_Time_Format)
            ymdEndDate = datetime.datetime.strptime("%s %02d:%02d:00" % (endDateStr, curDateTime.hour, curDateTime.minute), ChConfig.TYPE_Time_Format)
            ymdIpyDatList.append([ymdIpyData, ymdStartDate, ymdEndDate])
            if actNum not in actNumWeekIpyDataInfo:
                actNumWeekIpyDataInfo[actNum] = []
            weekIpyDataList = actNumWeekIpyDataInfo[actNum]
            startDateStr, endDateStr = GameWorld.GetOperationActionDateStr(ipyData)
            startWeekDate = GameWorld.ChangeStrToDatetime(startDateStr, ChConfig.TYPE_Time_YmdFormat)
            endWeekDate = GameWorld.ChangeStrToDatetime(endDateStr, ChConfig.TYPE_Time_YmdFormat)
            if startWeekDate > curDateTimeYmd or curDateTimeYmd > endWeekDate: # 还未开始的循环 or 已经强制结束的循环
                disableWeekCfgIDDict[cfgID] = [startDateStr, endDateStr]
            else:
                weekIpyDataList.append([cfgID, startWeekDate, endWeekDate])
            
    for ipyData, startWeekDate, endWeekDate in weekIpyDataList:
        cfgID = ipyData.GetCfgID()
        for ymdIpyData, ymdStartDate, ymdEndDate in ymdIpyDatList:
            if ymdStartDate <= startWeekDate <= ymdEndDate or ymdStartDate <= endWeekDate <= ymdEndDate:
                ymdCfgID = ymdIpyData.GetCfgID()
                disableWeekIpyDataInfo[cfgID] = [startWeekDate, endWeekDate, ymdCfgID, ymdStartDate, ymdEndDate]
        # 按日期循环
        elif startDateStr.startswith("L"):
            loopStartDate, loopEndDate, loopTimes = GameWorld.GetOperationActionLoopDate(startDateStr, endDateStr, curDateTime)
            if loopStartDate > curDateTimeYmd or curDateTimeYmd > loopEndDate: # 还未开始的循环 or 已经强制结束的循环
                disableLoopCfgIDDict[cfgID] = [startDateStr, endDateStr]
            else:
                loopKey = (startDateStr, endDateStr) # 同个循环周期的视为同一组
                if actNum not in actNumLoopIpyDataInfo:
                    actNumLoopIpyDataInfo[actNum] = {}
                loopIpyDataDict = actNumLoopIpyDataInfo[actNum]
                if loopKey not in loopIpyDataDict:
                    loopCfgIDList, loopDateInfo = [], [loopStartDate, loopEndDate, loopTimes]
                    loopIpyDataDict[loopKey] = [loopCfgIDList, loopDateInfo]
                loopCfgIDList, loopDateInfo = loopIpyDataDict[loopKey]
                loopCfgIDList.append(cfgID)
                
    return disableWeekIpyDataInfo
        # 按日期的
        elif startDateStr.count("-") == 2:
            if actNum not in actNumYMDIpyDataInfo:
                actNumYMDIpyDataInfo[actNum] = []
            ymdIpyDataList = actNumYMDIpyDataInfo[actNum]
            ymdStartDate = GameWorld.ChangeStrToDatetime(startDateStr, ChConfig.TYPE_Time_YmdFormat)
            ymdEndDate = GameWorld.ChangeStrToDatetime(endDateStr, ChConfig.TYPE_Time_YmdFormat)
            ymdIpyDataList.append([cfgID, ymdStartDate, ymdEndDate])
        else:
            # 其他的不处理
            pass
    nowLoopYMDIpyDataInfo = {} # {actNum:loopIpyDataList, ...} # # 日期循环中当前在循环的
    otherLoopCfgIDDict = {} # {cfgID:[info], ...} # 日期循环中不是当前循环的其他循环
    coverDisableLoopIpyDataInfo = {} # {cfgID:[info], ...} # 被日期覆盖的日期循环
    for actNum, loopIpyDataDict in actNumLoopIpyDataInfo.items():
        ymdIpyDataList = actNumYMDIpyDataInfo.get(actNum, [])
        if actNum not in nowLoopYMDIpyDataInfo:
            nowLoopYMDIpyDataInfo[actNum] = []
        loopIpyDataList = nowLoopYMDIpyDataInfo[actNum]
        for loopKey, loopInfo in loopIpyDataDict.items():
            startDateStr, endDateStr = loopKey
            loopCfgIDList, loopDateInfo = loopInfo
            loopStartDate, loopEndDate, loopTimes = loopDateInfo
            loopIndex = (loopTimes - 1) % len(loopCfgIDList) # 当前循环次数对应的循环索引
            curLoopCfgID = 0
            for index, loopCfgID in enumerate(loopCfgIDList):
                if index == loopIndex: # 当前循环的
                    curLoopCfgID = loopCfgID
                    loopIpyDataList.append([loopCfgID, loopStartDate, loopEndDate])
                else:
                    otherLoopCfgIDDict[loopCfgID] = [loopCfgIDList, startDateStr, endDateStr, loopIndex, loopTimes]
            for ymdCfgID, ymdStartDate, ymdEndDate in ymdIpyDataList:
                if ymdStartDate <= loopStartDate <= ymdEndDate or ymdStartDate <= loopEndDate <= ymdEndDate:
                    coverDisableLoopIpyDataInfo[curLoopCfgID] = [loopStartDate, loopEndDate, ymdCfgID, ymdStartDate, ymdEndDate]
    coverDisableWeekIpyDataInfo = {} # {cfgID:[info], ...} # 被日期覆盖的星期循环
    for actNum, weekIpyDataList in actNumWeekIpyDataInfo.items():
        ymdIpyDataList = actNumYMDIpyDataInfo.get(actNum, [])
        loopIpyDatList = nowLoopYMDIpyDataInfo.get(actNum, [])
        for weekCfgID, startWeekDate, endWeekDate in weekIpyDataList:
            # 被常规日期覆盖的
            for ymdCfgID, ymdStartDate, ymdEndDate in ymdIpyDataList:
                if ymdStartDate <= startWeekDate <= ymdEndDate or ymdStartDate <= endWeekDate <= ymdEndDate:
                    coverDisableWeekIpyDataInfo[weekCfgID] = [startWeekDate, endWeekDate, ymdCfgID, ymdStartDate, ymdEndDate]
            # 被循环日期覆盖的
            for loopCfgID, loopStartDate, loopEndDate in loopIpyDatList:
                if loopStartDate <= startWeekDate <= loopEndDate or loopStartDate <= endWeekDate <= loopEndDate:
                    coverDisableWeekIpyDataInfo[weekCfgID] = [startWeekDate, endWeekDate, loopCfgID, loopStartDate, loopEndDate]
    return coverDisableLoopIpyDataInfo, disableLoopCfgIDDict, otherLoopCfgIDDict, coverDisableWeekIpyDataInfo, disableWeekCfgIDDict
def Dispose_OperationActionState(reloadRefresh=False):
    # 运营活动状态处理, 每天0点会强制同步当天的运营活动详情到地图服务器
    
    if GameWorld.IsCrossServer():
        # 跨服不处理运营活动
        CrossActionControl.Dispose_CrossActState(reloadRefresh)
        return
    isReload, OperationActionInfo = __GetOperationActionInfo(False) # 这里必须传False
    isReload = isReload or reloadRefresh
    operationActionDict = OperationActionInfo[OperationAction_TodayInfo]
    operationTodayActionDict = OperationActionInfo[OperationAction_TodayInfo]
    mapServerInfoDict = OperationActionInfo[OperationAction_MapServerInfo]
    
    gameWorld = GameWorld.GetGameWorld()
@@ -500,113 +799,224 @@
    
    for actName in ShareDefine.OperationActionNameList:
        
        state = 0 # 默认关闭
        ipyData = None
        if actName in operationActionDict:
            #startList = [] # [startDateTime, ...]
            #endList = [] # [endDateTime, ...]
            #notifyDict = {} # {notifyDateTime:[notifyKey, [参数]], ...}
            #ipyData 可能为 None
            ipyData, startList, endList, notifyDict = operationActionDict[actName]
            # 精确匹配开启
            if curDateTime in startList:
                state = startList.index(curDateTime) + 1 # 也是代表第几个时间段
            # 精确匹配关闭
            elif curDateTime in endList:
                state = 0
            # goon ״̬
            else:
                for dIndex, startDateTime in enumerate(startList):
                    endDateTime = endList[dIndex]
                    if startDateTime < curDateTime < endDateTime:
                        state = dIndex + 1
                        break
            # 全服广播提示信息
            if curDateTime in notifyDict:
                notifyKey, paramList = notifyDict[curDateTime]
                PlayerControl.WorldNotify(0, notifyKey, paramList)
        dictName = ChConfig.Def_WorldKey_OperationActionState % actName
        preState = gameWorld.GetDictByKey(dictName)
        if not isReload and preState == state:
            #已经是这个状态了
        if actName not in mapServerInfoDict:
            continue
        #更新字典值
        gameWorld.SetDict(dictName, state)
        sendMapServerMsgDict = mapServerInfoDict.get(actName, {})
        
        dbOperationActIDKey = PlayerDBGSEvent.Def_OperationActID % actName
        curActID = sendMapServerMsgDict.get(ShareDefine.ActKey_ID)
        dayIndex = sendMapServerMsgDict.get(ShareDefine.ActKey_DayIndex, 0)
        if curActID and PlayerDBGSEvent.GetDBGSTrig_ByKey(dbOperationActIDKey) != curActID:
            PlayerDBGSEvent.SetDBGSTrig_ByKey(dbOperationActIDKey, curActID)
            if actName in ShareDefine.NeedWorldLVOperationActNameList:
                #记录开启时世界等级
                worldLV = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_WorldAverageLv)
                PlayerDBGSEvent.SetDBGSTrig_ByKey(PlayerDBGSEvent.Def_OActWorldLV % actName, worldLV)
        if actName in ShareDefine.MultiActNumOperationActNameList:
            curActMapInfoDictList = mapServerInfoDict[actName].values()
        else:
            curActMapInfoDictList = [mapServerInfoDict[actName]]
        for sendMapServerMsgDict in curActMapInfoDictList:
            state = 0 # 默认关闭
            stateJoin = ShareDefine.ActStateJoin_None # 可参与状态,0-参与前;1-可参与;2-参与结束
            ipyData = None
            actNum = sendMapServerMsgDict.get(ShareDefine.ActKey_ActNum, 0)
            if actName in operationTodayActionDict:
                todayActInfoList = []
                if actName in ShareDefine.MultiActNumOperationActNameList:
                    if actNum in operationTodayActionDict[actName]:
                        todayActInfoList = operationTodayActionDict[actName][actNum]
                else:
                    todayActInfoList = operationTodayActionDict[actName]
                if isinstance(todayActInfoList, list) and len(todayActInfoList) == 6:
                    #startList = [] # [startDateTime, ...]
                    #endList = [] # [endDateTime, ...]
                    #notifyDict = {} # {notifyDateTime:[notifyKey, [参数]], ...}
                    #ipyData 可能为 None
                    ipyData, startList, endList, notifyDict, joinStartTimeList, joinEndTimeList = todayActInfoList
                    # ״̬
                    for dIndex, startDateTime in enumerate(startList):
                        endDateTime = endList[dIndex]
                        if startDateTime <= curDateTime < endDateTime:
                            state = dIndex + 1 # 代表第几个时间段
                            break
                    if joinStartTimeList:
                        for jIndex, joinStartDateTime in enumerate(joinStartTimeList):
                            endJoinDateTime = joinEndTimeList[jIndex]
                            if joinStartDateTime <= curDateTime < endJoinDateTime:
                                stateJoin = ShareDefine.ActStateJoin_Start
                                break
                            elif curDateTime >= endJoinDateTime:
                                stateJoin = ShareDefine.ActStateJoin_End
                    else:
                        stateJoin = ShareDefine.ActStateJoin_Start if state else ShareDefine.ActStateJoin_None
                    # 全服广播提示信息
                    if curDateTime in notifyDict:
                        notifyKey, paramList = notifyDict[curDateTime]
                        PlayerControl.WorldNotify(0, notifyKey, paramList)
            dictName = ChConfig.Def_WorldKey_OperationActionState % actName
            if actName in ShareDefine.MultiActNumOperationActNameList:
                dictName += "_%s" % actNum
            preState = gameWorld.GetDictByKey(dictName)
            dictNameJoin = ChConfig.Def_WorldKey_OperationActionStateJoin % actName
            if actName in ShareDefine.MultiActNumOperationActNameList:
                dictNameJoin += "_%s" % actNum
            preStateJoin = gameWorld.GetDictByKey(dictNameJoin)
            if not isReload and preState == state and preStateJoin == stateJoin:
                #已经是这个状态了
                continue
            GameWorld.Log("运营活动变更: actName=%s,actNum=%s,preState=%s,state=%s,preStateJoin=%s,stateJoin=%s" % (actName, actNum, preState, state, preStateJoin, stateJoin))
            #更新字典值
            gameWorld.SetDict(dictName, state)
            gameWorld.SetDict(dictNameJoin, stateJoin)
            dbOperationActIDKey = PlayerDBGSEvent.Def_OperationActID % actName
            dbOperationActWorldLVKey = PlayerDBGSEvent.Def_OActWorldLV % actName
            if actName in ShareDefine.MultiActNumOperationActNameList:
                dbOperationActIDKey += "_%s" % actNum
                dbOperationActWorldLVKey += "_%s" % actNum
                
            #此处为活动开启时
            if actName == ShareDefine.OperationActionName_BossReborn:
                #重置BOSS复活点
                GameWorldBoss.ResetBossRebornPoint()
            dbActID = PlayerDBGSEvent.GetDBGSTrig_ByKey(dbOperationActIDKey)
            curActID = sendMapServerMsgDict.get(ShareDefine.ActKey_ID, 0)
            dayIndex = sendMapServerMsgDict.get(ShareDefine.ActKey_DayIndex, 0)
            if dbActID != curActID:
                GameWorld.Log("    dbActID变更: dbActID=%s,curActID=%s" % (dbActID, curActID))
                PlayerDBGSEvent.SetDBGSTrig_ByKey(dbOperationActIDKey, curActID)
                # 结束旧的
                if dbActID:
                    if actName == ShareDefine.OperationActionName_BossTrial:
                        PlayerActBossTrial.OnActEnd(actNum, ipyData, dayIndex)
                    elif actName == ShareDefine.OperationActionName_XianXiaMJ:
                        PlayerActXianXiaMJ.OnActEnd(actNum, ipyData, dayIndex)
                    elif actName == ShareDefine.OperationActionName_Gubao:
                        PlayerActGubao.OnActEnd(actNum, ipyData, dayIndex)
                    elif actName == ShareDefine.OperationActionName_HorsePetTrain:
                        PlayerActHorsePetTrain.OnActEnd(actNum, ipyData, dayIndex)
                if curActID:
                    if actName in ShareDefine.NeedWorldLVOperationActNameList:
                        #记录开启时世界等级
                        worldLV = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_WorldAverageLv)
                        PlayerDBGSEvent.SetDBGSTrig_ByKey(dbOperationActWorldLVKey, worldLV)
                        GameWorld.Log("    记录活动开启时世界等级: worldLV=%s" % (worldLV))
                    #此处为活动开启时
                    if actName == ShareDefine.OperationActionName_BossReborn:
                        #重置BOSS复活点
                        GameWorldBoss.ResetBossRebornPoint()
                    elif actName == ShareDefine.OperationActionName_FairyCeremony:
                        #重置仙界盛典
                        PlayerFairyCeremony.ResetFairyCeremony()
                    elif actName == ShareDefine.OperationActionName_NewFairyCeremony:
                        #重置仙界盛典
                        PlayerNewFairyCeremony.ResetNewFairyCeremony()
                    elif actName == ShareDefine.OperationActionName_FeastRedPacket:
                        #重置节日红包
                        if ipyData:
                            PlayerFamilyRedPacket.OnResetFeastRedPacket(ipyData, dayIndex)
                    elif actName == ShareDefine.OperationActionName_FlashSale:
                        #限时抢购重置购买次数 本次活动每场不能重复
                        #dayIndex = sendMapServerMsgDict.get(ShareDefine.ActKey_DayIndex, 0)
                        PlayerStore.ResetFlashSaleBuyCnt(ipyData)
                    elif actName == ShareDefine.OperationActionName_GarbageSorting:
                        PlayerActGarbageSorting.OnActStart(actNum)
                    elif actName == ShareDefine.OperationActionName_BossTrial:
                        PlayerActBossTrial.OnActStart(actNum)
                    elif actName == ShareDefine.OperationActionName_XianXiaMJ:
                        PlayerActXianXiaMJ.OnActStart(actNum, ipyData)
                    elif actName == ShareDefine.OperationActionName_Gubao:
                        PlayerActGubao.OnActStart(actNum, ipyData)
                    elif actName == ShareDefine.OperationActionName_HorsePetTrain:
                        PlayerActHorsePetTrain.OnActStart(actNum, ipyData)
                    elif actName == ShareDefine.OperationActionName_FamilyCTGAssist:
                        PlayerActFamilyCTGAssist.OnActStart(actNum)
                else:
                    if actName == ShareDefine.OperationActionName_GarbageSorting:
                        PlayerActGarbageSorting.OnActEnd(actNum)
            else:
                GameWorld.Log("    dbActID不变: dbActID=%s,curActID=%s" % (dbActID, curActID))
            # 活动中刷新,每次都需要刷新的逻辑,包含重读配置等
            if state:
                if actName == ShareDefine.OperationActionName_BossTrial:
                    PlayerActBossTrial.OnActInStateRefresh(actNum, ipyData, dayIndex)
                elif actName == ShareDefine.OperationActionName_XianXiaMJ:
                    PlayerActXianXiaMJ.OnActInStateRefresh(actNum, ipyData)
                elif actName == ShareDefine.OperationActionName_Gubao:
                    PlayerActGubao.OnActInStateRefresh(actNum, ipyData)
                elif actName == ShareDefine.OperationActionName_HorsePetTrain:
                    PlayerActHorsePetTrain.OnActInStateRefresh(actNum, ipyData)
            # 仅活动有配置参与时间段的会触发
            if curActID and dbActID == curActID and preStateJoin != stateJoin:
                GameWorld.Log("    参与状态变更: preStateJoin=%s,stateJoin=%s" % (preStateJoin, stateJoin))
                # 参与开始
                if stateJoin == ShareDefine.ActStateJoin_Start:
                    pass
                # 参与结束
                elif stateJoin == ShareDefine.ActStateJoin_End:
                    if actName == ShareDefine.OperationActionName_BossTrial:
                        PlayerActBossTrial.OnActJoinEnd(actNum, ipyData, dayIndex)
                    elif actName == ShareDefine.OperationActionName_XianXiaMJ:
                        PlayerActXianXiaMJ.OnActJoinEnd(actNum, ipyData, dayIndex)
                    elif actName == ShareDefine.OperationActionName_Gubao:
                        PlayerActGubao.OnActJoinEnd(actNum, ipyData, dayIndex)
                    elif actName == ShareDefine.OperationActionName_HorsePetTrain:
                        PlayerActHorsePetTrain.OnActJoinEnd(actNum, ipyData, dayIndex)
            if ipyData and actName in ShareDefine.NeedWorldLVOperationActNameList:
                actWorldLV = PlayerDBGSEvent.GetDBGSTrig_ByKey(dbOperationActWorldLVKey)
                sendMapServerMsgDict[ShareDefine.ActKey_WorldLV] = actWorldLV
                GameWorld.Log("    活动世界等级: actWorldLV=%s" % (actWorldLV))
            if actName == ShareDefine.OperationActionName_ExpRate:
                if isReload and ipyData:
                    Sync_OperationAction_ExpRate(ipyData)
            elif actName == ShareDefine.OperationActionName_BossReborn:
                if isReload and ipyData:
                    GameWorldBoss.SetBossRebornNeedPoint(True)
            elif actName == ShareDefine.OperationActionName_HorsePetFeast:
                if preState != state and state:
                    relatedID = "%s|%s" % (actName, actNum)
                    GameWorld.Log("    骑宠盛宴开始: relatedID=%s" % (relatedID))
                    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_ActionBossRebornSign % relatedID, int(time.time()))
            elif actName == ShareDefine.OperationActionName_FairyCeremony:
                #重置仙界盛典
                PlayerFairyCeremony.ResetFairyCeremony()
                if isReload and ipyData:
                    PlayerFairyCeremony.Sync_OperationAction_FairyCeremony(ipyData)
                if preState != state and state == 0:
                    PlayerFairyCeremony.OnFairyCeremonyEnd()
            elif actName == ShareDefine.OperationActionName_NewFairyCeremony:
                #重置仙界盛典
                PlayerNewFairyCeremony.ResetNewFairyCeremony()
                if isReload and ipyData:
                    PlayerNewFairyCeremony.Sync_OperationAction_NewFairyCeremony(ipyData)
                if preState != state and state == 0:
                    PlayerNewFairyCeremony.OnNewFairyCeremonyEnd()
            elif actName == ShareDefine.OperationActionName_RealmPoint:
                if isReload and ipyData:
                    Sync_OperationAction_RealmPoint(ipyData)
            elif actName == ShareDefine.OperationActionName_FeastRedPacket:
                #重置节日红包
                if ipyData:
                    PlayerFamilyRedPacket.OnResetFeastRedPacket(ipyData, dayIndex)
            elif actName == ShareDefine.OperationActionName_FlashSale:
                #限时抢购重置购买次数 本次活动每场不能重复
                #dayIndex = sendMapServerMsgDict.get(ShareDefine.ActKey_DayIndex, 0)
                PlayerStore.ResetFlashSaleBuyCnt(ipyData)
        if actName in ShareDefine.NeedWorldLVOperationActNameList:
            actWorldLV = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_OActWorldLV % actName)
            sendMapServerMsgDict[ShareDefine.ActKey_WorldLV] = actWorldLV
        if actName == ShareDefine.OperationActionName_ExpRate:
            if isReload and ipyData:
                Sync_OperationAction_ExpRate(ipyData)
        elif actName == ShareDefine.OperationActionName_CostRebate:
            if isReload and ipyData:
                pass
        elif actName == ShareDefine.OperationActionName_BossReborn:
            if isReload and ipyData:
                GameWorldBoss.SetBossRebornNeedPoint(True)
        elif actName == ShareDefine.OperationActionName_FairyCeremony:
            if isReload and ipyData:
                PlayerFairyCeremony.Sync_OperationAction_FairyCeremony(ipyData)
            if preState != state and state == 0:
                PlayerFairyCeremony.OnFairyCeremonyEnd()
        elif actName == ShareDefine.OperationActionName_NewFairyCeremony:
            if isReload and ipyData:
                PlayerNewFairyCeremony.Sync_OperationAction_NewFairyCeremony(ipyData)
            if preState != state and state == 0:
                PlayerNewFairyCeremony.OnNewFairyCeremonyEnd()
        elif actName == ShareDefine.OperationActionName_RealmPoint:
            if isReload and ipyData:
                Sync_OperationAction_RealmPoint(ipyData)
        elif actName == ShareDefine.OperationActionName_FeastRedPacket:
            if isReload and ipyData:
                PlayerFamilyRedPacket.Sync_FeastRedPacket(ipyData)
        #通知Mapserver,设置字典
        #GameWorld.SendMapServerMsgEx(dictName, state) # 运营活动不单独通知活动状态,需与活动信息整合后一起通知
        sendMapServerMsgDict[ShareDefine.ActKey_State] = state
        GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_OperationActionInfo % actName, sendMapServerMsgDict)
        GameWorld.Log("运营活动变更: actName=%s,preState=%s,state=%s,dictName=%s, %s" % (actName, preState, state, dictName, sendMapServerMsgDict))
                if isReload and ipyData:
                    PlayerFamilyRedPacket.Sync_FeastRedPacket(ipyData)
            #通知Mapserver,设置字典
            #GameWorld.SendMapServerMsgEx(dictName, state) # 运营活动不单独通知活动状态,需与活动信息整合后一起通知
            sendMapServerMsgDict[ShareDefine.ActKey_State] = state
            sendMapServerMsgDict[ShareDefine.ActKey_StateJoin] = stateJoin
            GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_OperationActionInfo % actName, sendMapServerMsgDict)
            GameWorld.Log("    sendMapServerMsgDict: %s" % (sendMapServerMsgDict))
            if actName in ShareDefine.MultiActNumOperationActNameList:
                mapServerInfoDict[actName][actNum] = sendMapServerMsgDict
            else:
                mapServerInfoDict[actName] = sendMapServerMsgDict
    return
def Sync_OperationAction_ExpRate(ipyData, curPlayer=None):
@@ -614,11 +1024,11 @@
        return
    if len(ipyData.GetStartTimeList()) != len(ipyData.GetEndTimeList()):
        return
    openServerDay = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ServerDay) + 1
    startDateStr, endDateStr = GameWorld.GetOperationActionDateStr(ipyData)
    multiExpRateInfo = ChPyNetSendPack.tagGCMultiExpRateInfo()
    multiExpRateInfo.Clear()
    multiExpRateInfo.StartDate = GameWorld.GetOperationActionDateStr(ipyData.GetStartDate(), openServerDay)
    multiExpRateInfo.EndtDate = GameWorld.GetOperationActionDateStr(ipyData.GetEndDate(), openServerDay)
    multiExpRateInfo.StartDate = startDateStr
    multiExpRateInfo.EndtDate = endDateStr
    multiExpRateInfo.ActivityTime = []
    for i, startTime in enumerate(ipyData.GetStartTimeList()):
        endTime = ipyData.GetEndTimeList()[i]
@@ -647,11 +1057,11 @@
    ##多倍修行点活动信息通知
    if not ipyData:
        return
    openServerDay = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ServerDay) + 1
    startDateStr, endDateStr = GameWorld.GetOperationActionDateStr(ipyData)
    multiRealmPointInfo = ChPyNetSendPack.tagGCMultiRealmPointInfo()
    multiRealmPointInfo.Clear()
    multiRealmPointInfo.StartDate = GameWorld.GetOperationActionDateStr(ipyData.GetStartDate(), openServerDay)
    multiRealmPointInfo.EndtDate = GameWorld.GetOperationActionDateStr(ipyData.GetEndDate(), openServerDay)
    multiRealmPointInfo.StartDate = startDateStr
    multiRealmPointInfo.EndtDate = endDateStr
    multiRealmPointInfo.Multiple = ipyData.GetMultiple()
    multiRealmPointInfo.LimitLV = ipyData.GetLVLimit()
    multiRealmPointInfo.LimitPoint = ipyData.GetPointLimit()
@@ -690,7 +1100,7 @@
    curDateStr = GameWorld.ChangeTimeNumToStr(curTime, ChConfig.TYPE_Time_YmdFormat) # 当天日期
    
    openServerWeekday = GameWorldProcess.GetOpenServerWeekday() # 服务器开服时是星期几
    curMaxCustomServerDay = IpyGameDataPY.GetFuncCfg("OperationAction", 1) - openServerWeekday + 1 # 最大有效定制开服天
    curMaxCustomServerDay = IpyGameDataPY.GetFuncCfg("OperationAction", 1) # 最大有效定制开服天
    maxCustomServerDayMix = IpyGameDataPY.GetFuncCfg("MixServer", 1)
    GameWorld.Log("===== 加载今天日常活动信息 =====")
    GameWorld.Log("开服是星期%s, 开服第%s天, 当前星期%s" % (openServerWeekday, openServerDay, weekDay))
@@ -704,7 +1114,7 @@
        customIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("DailyActionCustom", openServerWeekday)
        if not customIpyDataList:
            customIpyDataList = []
        GameWorld.Log("    本周定制的开服日常活动条数: %s" % len(customIpyDataList))
        GameWorld.Log("    定制的开服日常活动条数: %s" % len(customIpyDataList))
        customIDList = []
        todayCustomIDList = []
        for customIpyData in customIpyDataList:
@@ -718,8 +1128,8 @@
                customType = 1
                dailyTimeInfoList.append([customType, customIpyData.GetOpenTimeList(), customIpyData])
                GameWorld.Log("    增加本日开服日常活动信息: customType=%s,dailyID=%s,dataID=%s" % (customType, dailyID, dataID))
        GameWorld.Log("    本周定制的开服日常配置表ID列表: %s" % (customIDList))
        GameWorld.Log("    本周定制的开服日常活动ID列表: %s" % (customDailyIDList))
        GameWorld.Log("    定制的开服日常配置表ID列表: %s" % (customIDList))
        GameWorld.Log("    定制的开服日常活动ID列表: %s" % (customDailyIDList))
        GameWorld.Log("    今天定制的开服日常表ID列表=%s" % (todayCustomIDList))
    elif isMixServer and mixServerDay <= maxCustomServerDayMix:
        todayCustomIDList = [] # 今天定制的数据表ID
@@ -746,6 +1156,10 @@
    for i in xrange(dailyActionCount):
        dailyIpyData = ipyDataMgr.GetDailyActionByIndex(i)
        dailyID = dailyIpyData.GetDailyID()
        if dailyID in [ShareDefine.DailyActionID_CrossBattlefield]:
            GameWorld.Log("    不需要处理的日常活动! dailyID=%s" % dailyID)
            continue
        
        # 是当天开服天定制活动的不处理常规活动
        if dailyID in customDailyIDList:
@@ -814,6 +1228,9 @@
def Dispose_DailyActionState():
    # 日常活动状态变更检查处理
    
    if GameWorld.IsCrossServer():
        CrossActionControl.Dispose_CrossDailyActionState()
    todayDailyActionInfo = __GetTodayDailyActionInfo()
    if not todayDailyActionInfo:
        return
@@ -879,11 +1296,20 @@
def SendMapServerDailyActionState():
    # 地图启动成功时通知本日进行中的日常活动状态
    
    gameWorld = GameWorld.GetGameWorld()
    if GameWorld.IsCrossServer():
        CrossActionControl.SendMapServerCrossDailyActionState()
    else:
        for dailyActionID in ShareDefine.CrossDailyActionIDList:
            dictName = ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % dailyActionID
            state = gameWorld.GetDictByKey(dictName)
            if state:
                GameWorld.SendMapServerMsgEx(dictName, state)
    todayDailyActionInfo = __GetTodayDailyActionInfo()
    if not todayDailyActionInfo:
        return
    
    gameWorld = GameWorld.GetGameWorld()
    for actionInfo in todayDailyActionInfo:
        dailyActionID = actionInfo[0]
        dictName = ShareDefine.Def_Notify_WorldKey_DailyActionState % dailyActionID 
@@ -992,7 +1418,7 @@
        GameWorld.ErrLog("获取开服是星期几数据错误!openServerWeekday=%s" % openServerWeekday)
        return []
    
    curMaxCustomServerDay = IpyGameDataPY.GetFuncCfg("OperationAction", 1) - openServerWeekday + 1 # 最大有效定制开服天
    curMaxCustomServerDay = IpyGameDataPY.GetFuncCfg("OperationAction", 1) # 最大有效定制开服天
    maxCustomServerDayMix = IpyGameDataPY.GetFuncCfg("MixServer", 1)
    GameWorld.Log("===== 加载今天副本状态时间表 =====")
    GameWorld.Log("开服是星期%s, 开服第%s天, 当前星期%s,%s点%s分 !" % (openServerWeekday, openServerDay, curWeekDay, curHour, curMinute))
@@ -1001,15 +1427,15 @@
    
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    
    customMapIDList = [] # 本周有定制的副本数据地图ID列表
    customMapIDList = [] # 有定制的副本数据地图ID列表
    fbStateTimeInfoList = []
    # 暂固定前2周定制有效, 为方便GM命令测试,这里用开服天做判断,不用开服周
    if openServerDay <= curMaxCustomServerDay:
        customIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("FBStateTimeCustom", openServerWeekday)
        if not customIpyDataList:
            customIpyDataList = []
        GameWorld.Log("    本周定制的开服副本活动条数: %s" % len(customIpyDataList))
        customIDList = [] # 本周定制的数据表ID
        GameWorld.Log("    定制的开服副本活动条数: %s" % len(customIpyDataList))
        customIDList = [] # 定制的数据表ID
        todayCustomIDList = [] # 今天定制的数据表ID
        for customIpyData in customIpyDataList:
            dataID = customIpyData.GetID()
@@ -1022,8 +1448,8 @@
                customType, startWeekDay, endWeekDay = 1, curWeekDay, curWeekDay
                fbStateTimeInfoList.append([customType, startWeekDay, endWeekDay, customIpyData])
                GameWorld.Log("    今天要处理的开服副本状态配置: customType=%s,dataID=%s" % (customType, dataID))
        GameWorld.Log("    本周定制的开服副本表ID列表: %s" % (customIDList))
        GameWorld.Log("    本周定制的开服副本地图列表: %s" % (customMapIDList))
        GameWorld.Log("    定制的开服副本表ID列表: %s" % (customIDList))
        GameWorld.Log("    定制的开服副本地图列表: %s" % (customMapIDList))
        GameWorld.Log("    今天定制的开服副本表ID列表=%s" % (todayCustomIDList))
    elif isMixServer and mixServerDay <= maxCustomServerDayMix:
        todayCustomIDList = [] # 今天定制的数据表ID
@@ -1051,7 +1477,7 @@
        dataMapID = fbStateTimeIpyData.GetDataMapID()
        # 是当天开服天定制活动的不处理常规活动
        if dataMapID in customMapIDList:
            GameWorld.Log("    dataID=%s,dataMapID=%s, 在本周定制的副本地图列表里,不处理!" % (dataID, dataMapID))
            GameWorld.Log("    dataID=%s,dataMapID=%s, 在定制的副本地图列表里,不处理!" % (dataID, dataMapID))
            continue
        # 暂不支持跨天的活动
        customType, startWeekDay, endWeekDay = 0, fbStateTimeIpyData.GetStartWeekday(), fbStateTimeIpyData.GetStartWeekday()