From 7d7da6a8246577a11c38219eaedb451178769ec8 Mon Sep 17 00:00:00 2001 From: hxp <ale99527@vip.qq.com> Date: 星期五, 14 九月 2018 17:15:52 +0800 Subject: [PATCH] 3494 【后端】开服前14天运营活动定制 3443 【后端】Boss复活活动,时间段支持日期跨天或日期支持重置时间点设置(如0点或5点) 1972 【后端】运营活动支持循环广播 --- ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldActionControl.py | 218 ++++++++++++++++++++++++++++-------------------------- 1 files changed, 114 insertions(+), 104 deletions(-) diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldActionControl.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldActionControl.py index a78c850..46d63f1 100644 --- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldActionControl.py +++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldActionControl.py @@ -54,7 +54,7 @@ ATTR_CALL_TIME = ['weekday', 'year', 'month', 'day', 'hour', 'minute'] ( -OperationAction_OpenServerDay, # 信息所属开服天 +OperationAction_ReloadSign, # 信息重载标记 OperationAction_TodayInfo, # 当日的活动信息 OperationAction_MapServerInfo, # 同步地图的活动信息 ) = range(3) @@ -101,7 +101,7 @@ dictName = ChConfig.Def_WorldKey_OperationActionState % actName state = gameWorld.GetDictByKey(dictName) - sendMapServerMsgDict = mapServerInfoDict.get(actName, {}) if state else {} + sendMapServerMsgDict = mapServerInfoDict.get(actName, {}) sendMapServerMsgDict[ShareDefine.ActKey_State] = state GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_OperationActionInfo % actName, sendMapServerMsgDict) @@ -113,26 +113,32 @@ key = "OperationActionInfo" openServerDay = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ServerDay) + 1 OperationActionInfo = IpyGameDataPY.GetConfigEx(key) - if OperationActionInfo and OperationActionInfo[OperationAction_OpenServerDay] == openServerDay: + serverTime = GameWorld.GetServerTime() + curHour = serverTime.hour + reloadSignHour = 0 if curHour < 5 else 5 + reloadSign = "%s_%s" % (openServerDay, reloadSignHour) + if OperationActionInfo and OperationActionInfo[OperationAction_ReloadSign] == reloadSign: #GameWorld.DebugLog("已经加载过本日运营活动处理信息!openServerDay=%s" % openServerDay) return False, OperationActionInfo # 因为后面的时间判断都是精确到分的,而处理此逻辑的时候可能不是0秒,所以这里的datetime取当前时间精确到分的 - serverTime = GameWorld.GetServerTime() 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) - GameWorld.Log("加载本日运营活动信息: %s, openServerDay=%s" % (curDateTime, openServerDay)) + customMaxServerDay = 14 # 定制运营活动最大开服天 operationActionDict = {} mapServerOperationActionDict = {} serverID = GameWorld.GetServerID() ipyDataMgr = IpyGameDataPY.IPY_Data() + GameWorld.Log("===== 加载本日运营活动信息: %s, serverID=%s,openServerDay=%s,customMaxServerDay=%s,reloadSign=%s =====" + % (curDateTime, serverID, openServerDay, customMaxServerDay, reloadSign)) + for actName in ShareDefine.OperationActionNameList: # 取出本活动所属本服ID的所有配置 curServerActIpyDataList = __GetOperationActionServerIpyDataList(ipyDataMgr, serverID, actName) - + GameWorld.Log("加载运营活动: actName=%s,可处理条数=%s" % (actName, len(curServerActIpyDataList))) needStartList = [] # [startDateTime, ...] needEndList = [] # [endDateTime, ...] needNotifyDict = {} # {notifyDateTime:[notifyKey, [参数]], ...} @@ -140,8 +146,26 @@ # 注意:每个活动配置会有多个活动时间点,但是有且只能生效一条活动配置,换句话说就是每个活动配置时间不允许交叉 for ipyData in curServerActIpyDataList: + cfgID = ipyData.GetCfgID() startDateStr = ipyData.GetStartDate() endDateStr = ipyData.GetEndDate() + GameWorld.Log(" cfgID=%s,mark=%s,serverIDList=%s,startDateStr=%s,endDateStr=%s,openServerDay=%s,curDateTime=%s" + % (cfgID, ipyData.GetActMark(), ipyData.GetServerIDList(), startDateStr, endDateStr, openServerDay, curDateTime)) + # 按开服天开的 + if startDateStr.isdigit() and endDateStr.isdigit(): + startServerDay, endServerDay = int(startDateStr), int(endDateStr) + if openServerDay > endServerDay: + 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)) + + elif openServerDay <= customMaxServerDay: + GameWorld.Log(" 按日期开的在开服定制限制天内,不处理! cfgID=%s,%s ~ %s,openServerDay=%s" % (cfgID, startDateStr, endDateStr, openServerDay)) + continue if hasattr(ipyData, "GetStartTimeList") and hasattr(ipyData, "GetEndTimeList"): startHMStrList = ipyData.GetStartTimeList() @@ -152,23 +176,41 @@ notifyInfoDictStart = ipyData.GetNotifyInfoStart() if hasattr(ipyData, "GetNotifyInfoStart") else {} notifyInfoDictEnd = ipyData.GetNotifyInfoEnd() if hasattr(ipyData, "GetNotifyInfoEnd") else {} + notifyInfoLoopInfo = ipyData.GetNotifyInfoLoop() if hasattr(ipyData, "GetNotifyInfoLoop") else {} # [循环分钟, 广播key] if len(startHMStrList) != len(endHMStrList): - GameWorld.ErrLog("活动配置开始及结束时间个数不匹配! actName=%s,cfgID=%s" % (actName, ipyData.GetCfgID())) + GameWorld.ErrLog(" 活动配置开始及结束时间个数不匹配! actName=%s,cfgID=%s,startHMStrList=%s,endHMStrList=%s" + % (actName, cfgID, startHMStrList, endHMStrList)) continue + + 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) + endDayDate = datetime.datetime.strptime("%s 05:00:00" % (endDateStr), ChConfig.TYPE_Time_Format) # 结束日期5点 + else: + startDayDate = datetime.datetime.strptime("%s 00:00:00" % (startDateStr), ChConfig.TYPE_Time_Format) + endDayDate = datetime.datetime.strptime("%s 00:00:00" % (endDateStr), ChConfig.TYPE_Time_Format) + datetime.timedelta(days=1) # 结束日期隔天0点 + + 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)) startList = [] # [startDateTime, ...] endList = [] # [endDateTime, ...] + startNotifyDict = {} # {notifyDateTime:notifyInfo, ...} + endNotifyDict = {} # {notifyDateTime:notifyInfo, ...} + loopNotifyDict = {} # {notifyDateTime:notifyInfo, ...} isActivity = False # 有需要处理开关时间的(开始、结束) isNotify = False + isAdvanceNotice = False # 没配置时分的代表全天, 只要开始或结束时分没配都算 if not startHMStrList or not endHMStrList: - startDayDate = datetime.datetime.strptime("%s 00:00:00" % (startDateStr), ChConfig.TYPE_Time_Format) # 开始天0点 - endDayDate = datetime.datetime.strptime("%s 00:00:00" % (endDateStr), ChConfig.TYPE_Time_Format) + datetime.timedelta(days=1) # 结束天隔天0点 startDateTime = startDayDate endDateTime = endDayDate startList.append(startDateTime) endList.append(endDateTime) + # 同一个活动类型是不允许时间上配置有重叠的,所以一个活动类型满足活动中的时间的有且仅有一条配置 + # 策划可能同时配置多个时间,但是活动中的有且仅有一条配置 if startDayDate <= curDateTime <= endDayDate: needStartList.append(startDateTime) needEndList.append(endDateTime) @@ -176,8 +218,6 @@ # 每天按时段开启的,支持多时段 else: - startDayDate = datetime.datetime.strptime("%s 00:00:00" % (startDateStr), ChConfig.TYPE_Time_Format) # 开始天0点 - endDayDate = datetime.datetime.strptime("%s 23:59:59" % (endDateStr), ChConfig.TYPE_Time_Format) # 结束天23点59分59秒 if startDayDate <= curDateTime <= endDayDate: for hmIndex, startHMStr in enumerate(startHMStrList): endHMStr = endHMStrList[hmIndex] @@ -194,42 +234,93 @@ needEndList.append(endDateTime) isActivity = True + if advanceMinutes and startDayDate: + advanceNoticeDateTime = startDayDate + datetime.timedelta(minutes=-advanceMinutes) + 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)) + for dtIndex, startDateTime in enumerate(startList): endDateTime = endList[dtIndex] # 广播 - 相对实际开始时间 for notifyMinute, notifyInfo in notifyInfoDictStart.items(): notifyDateTime = startDateTime + datetime.timedelta(minutes=notifyMinute) if notifyDateTime.year == curDateTime.year and notifyDateTime.month == curDateTime.month and notifyDateTime.day == curDateTime.day: - needNotifyDict[notifyDateTime] = notifyInfo + startNotifyDict[notifyDateTime] = notifyInfo isNotify = True # 广播 - 相对实际结束时间 for notifyMinute, notifyInfo in notifyInfoDictEnd.items(): notifyDateTime = endDateTime + datetime.timedelta(minutes=notifyMinute) if notifyDateTime.year == curDateTime.year and notifyDateTime.month == curDateTime.month and notifyDateTime.day == curDateTime.day: - needNotifyDict[notifyDateTime] = notifyInfo + endNotifyDict[notifyDateTime] = notifyInfo isNotify = True - - if not isActivity and not isNotify: + + # 广播 - 循环广播 + if notifyInfoLoopInfo and len(notifyInfoLoopInfo) == 2: + loopMinutes, loopNotifyKey = notifyInfoLoopInfo + notifyInfo = [loopNotifyKey, []] # 循环广播的默认无参数,不做支持 + loopCount, loopMaxCount = 0, 100 + while loopMinutes and loopNotifyKey and loopCount < loopMaxCount: + loopCount += 1 + 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: + loopNotifyDict[notifyDateTime] = notifyInfo + isNotify = True + + if startNotifyDict or endNotifyDict or loopNotifyDict: + 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) + + if not isActivity and not isNotify and not isAdvanceNotice: continue - GameWorld.Log(" 本日需要处理的运营活动信息: actName=%s,cfgID=%s,actMark=%s" % (actName, ipyData.GetCfgID(), ipyData.GetActMark())) - if isActivity: + GameWorld.Log(" 需要处理的运营活动信息: cfgID=%s,isAdvanceNotice=%s,isActivity=%s,isNotify=%s" % (cfgID, isAdvanceNotice, isActivity, isNotify)) + # 当天有需要激活活动或者提前预告活动的取开始时间较早的为准 + if isActivity or isAdvanceNotice: if not activityIpyData or activityIpyData.GetStartDate() < ipyData.GetStartDate(): activityIpyData = ipyData + activityInfoDict = {ShareDefine.ActKey_CfgID:cfgID} + + 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() + + mapServerOperationActionDict[actName] = activityInfoDict + GameWorld.Log(" activityInfoDict=%s" % (activityInfoDict)) if activityIpyData or needStartList or needEndList or needNotifyDict: # activityIpyData 可能为None operationActionDict[actName] = [activityIpyData, needStartList, needEndList, needNotifyDict] - # 新增运营活动只要在此处添加同步MapServer所需要的数据字典即可,其他逻辑均为公共逻辑 - mapServerOperationActionDict[actName] = __GetOperationActionMapServerInfoDict(curDateTime, actName, activityIpyData) - OperationActionInfo = IpyGameDataPY.SetConfigEx(key, [openServerDay, operationActionDict, mapServerOperationActionDict]) + OperationActionInfo = IpyGameDataPY.SetConfigEx(key, [reloadSign, operationActionDict, mapServerOperationActionDict]) - GameWorld.Log("本日运营活动信息加载完毕!isRefreshState=%s" % isRefreshState) + GameWorld.Log("本日运营活动信息加载完毕!reloadSign=%s,isRefreshState=%s" % (reloadSign, isRefreshState)) GameWorld.Log(" operationActionDict=%s" % operationActionDict) GameWorld.Log(" mapServerOperationActionDict=%s" % mapServerOperationActionDict) - + GameWorld.Log("=============================================================") if isRefreshState: Dispose_OperationActionState(True) @@ -271,87 +362,6 @@ if isGeneral: curServerActIpyDataList += generalIpyDataList return curServerActIpyDataList - -def __GetOperationActionMapServerInfoDict(curDateTime, actName, ipyData): - ## 获取运营活动同步到地图服务器的信息字典 - if not ipyData: - return {} - - startDateStr = ipyData.GetStartDate() - startDayDate = datetime.datetime.strptime("%s 00:00:00" % (startDateStr), ChConfig.TYPE_Time_Format) # 开始天0点 - dayIndex = (curDateTime - startDayDate).days # 当前日期在活动期间的天索引 - if dayIndex < 0: - GameWorld.DebugLog(" 不处理运营地图数据: startDateStr=%s,curDateTime=%s,startDayDate=%s,dayIndex=%s" - % (startDateStr, curDateTime, startDayDate, dayIndex)) - return {} - - actID = int(time.mktime(startDayDate.timetuple())) - - mapServerInfoDict = {} - # 多倍经验活动 - if actName == ShareDefine.OperationActionName_ExpRate: - mapServerInfoDict = {ShareDefine.ActKey_LVLimit:ipyData.GetLVLimit(), - ShareDefine.ActKey_AddExpRate:ipyData.GetAddExpRate()} - # 消费返利活动 - elif actName == ShareDefine.OperationActionName_CostRebate: - isDayReset = ipyData.GetIsDayReset() - if isDayReset: - # 每天重置的活动ID取当天的time值 - actIDDateStr = "%d-%d-%d 00:00:00" % (curDateTime.year, curDateTime.month, curDateTime.day) - actIDDateTime = datetime.datetime.strptime(actIDDateStr, ChConfig.TYPE_Time_Format) - actID = int(time.mktime(actIDDateTime.timetuple())) - templateIDList = ipyData.GetTemplateIDList() - templateID = templateIDList[-1] if dayIndex >= len(templateIDList) else templateIDList[dayIndex] - mapServerInfoDict = {ShareDefine.ActKey_ID:actID, - ShareDefine.ActKey_StartDate:ipyData.GetStartDate(), - ShareDefine.ActKey_EndDate:ipyData.GetEndDate(), - ShareDefine.ActKey_LVLimit:ipyData.GetLVLimit(), - ShareDefine.ActKey_IsDayReset:ipyData.GetIsDayReset(), - ShareDefine.ActKey_TemplateID:templateID, - ShareDefine.ActKey_TemplateIDList:templateIDList} - # 限时特惠活动 - elif actName == ShareDefine.OperationActionName_SpringSale: - isDayReset = ipyData.GetIsDayReset() - if isDayReset: - # 每天重置的活动ID取当天的time值 - actIDDateStr = "%d-%d-%d 00:00:00" % (curDateTime.year, curDateTime.month, curDateTime.day) - actIDDateTime = datetime.datetime.strptime(actIDDateStr, ChConfig.TYPE_Time_Format) - actID = int(time.mktime(actIDDateTime.timetuple())) - shopTypeList = ipyData.GetShopTypeList() - todayShopType = shopTypeList[-1] if dayIndex >= len(shopTypeList) else shopTypeList[dayIndex] - mapServerInfoDict = {ShareDefine.ActKey_ID:actID, - ShareDefine.ActKey_CfgID:ipyData.GetCfgID(), - ShareDefine.ActKey_ShopTypeList:todayShopType} - # Boss复活活动 - elif actName == ShareDefine.OperationActionName_BossReborn: - mapServerInfoDict = {ShareDefine.ActKey_ID:actID, - ShareDefine.ActKey_TemplateID:ipyData.GetTemplateID(), - ShareDefine.ActKey_StartDate:ipyData.GetStartDate(), - ShareDefine.ActKey_EndDate:ipyData.GetEndDate(), - ShareDefine.ActKey_LVLimit:ipyData.GetLVLimit(),} - # 限时礼包活动 - elif actName == ShareDefine.OperationActionName_FlashGiftbag: - isDayReset = ipyData.GetIsDayReset() - if isDayReset: - # 每天重置的活动ID取当天的time值 - actIDDateStr = "%d-%d-%d 00:00:00" % (curDateTime.year, curDateTime.month, curDateTime.day) - actIDDateTime = datetime.datetime.strptime(actIDDateStr, ChConfig.TYPE_Time_Format) - actID = int(time.mktime(actIDDateTime.timetuple())) - giftbagTypeList = ipyData.GetGiftbagTypeList() - todayGiftbag = giftbagTypeList[-1] if dayIndex >= len(giftbagTypeList) else giftbagTypeList[dayIndex] - mapServerInfoDict = {ShareDefine.ActKey_ID:actID, - ShareDefine.ActKey_CfgID:ipyData.GetCfgID(), - ShareDefine.ActKey_GiftbagTypeList:todayGiftbag} - # 仙界盛典活动 - elif actName == ShareDefine.OperationActionName_FairyCeremony: - mapServerInfoDict = {ShareDefine.ActKey_ID:actID} - # 多倍修行点活动 - if actName == ShareDefine.OperationActionName_RealmPoint: - mapServerInfoDict = {ShareDefine.ActKey_LVLimit:ipyData.GetLVLimit(), - ShareDefine.ActKey_RealmPointMulti:ipyData.GetMultiple(), - ShareDefine.ActKey_StartDate:ipyData.GetStartDate(), - ShareDefine.ActKey_EndDate:ipyData.GetEndDate(),} - return mapServerInfoDict def Dispose_OperationActionState(reloadRefresh=False): # 运营活动状态处理, 每天0点会强制同步当天的运营活动详情到地图服务器 @@ -406,7 +416,7 @@ continue #更新字典值 gameWorld.SetDict(dictName, state) - sendMapServerMsgDict = mapServerInfoDict.get(actName, {}) if state else {} + sendMapServerMsgDict = mapServerInfoDict.get(actName, {}) dbOperationActIDKey = PlayerDBGSEvent.Def_OperationActID % actName curActID = sendMapServerMsgDict.get(ShareDefine.ActKey_ID) -- Gitblit v1.8.0