|
|
@@ -20,6 +20,10 @@ local BzcsLog = require("baiZhanChengShen.BaiZhanChengShenLog")
|
|
|
------------------------------------ 活动周期调度 ------------------------------------
|
|
|
|
|
|
local wDay
|
|
|
+-- 本轮发奖 Timer 已调度(进程内防重; rewardIssued 在调度前落库防 oneMin 重复触发)
|
|
|
+local rewardIssueInProgress = false
|
|
|
+-- 上一 tick 是否处于可挑战窗口(用于 0:10 到点补广播, 跨服重启后首次 tick 也会触发一次)
|
|
|
+local prevIsRunning = false
|
|
|
|
|
|
-- 刷新缓存的当前星期
|
|
|
local function updateWDay()
|
|
|
@@ -73,7 +77,20 @@ local function isInOpenWday()
|
|
|
return wDay >= startW or wDay <= endW
|
|
|
end
|
|
|
|
|
|
--- 活动是否在时间窗内且处于开放星期
|
|
|
+-- 本轮在 DB 中仍有效(未过 endTime 且处于开放周), 含 startTime 前待开启时段
|
|
|
+local function hasActiveRound(now)
|
|
|
+ now = now or os.time()
|
|
|
+ local startTime, endTime = BaiZhanChengShenDB.GetActivityTimes()
|
|
|
+ if startTime <= 0 or endTime <= 0 then
|
|
|
+ return false
|
|
|
+ end
|
|
|
+ if now >= endTime then
|
|
|
+ return false
|
|
|
+ end
|
|
|
+ return isInOpenWday()
|
|
|
+end
|
|
|
+
|
|
|
+-- 活动是否处于可挑战窗口(已过本轮 startTime)
|
|
|
local function isRunning()
|
|
|
local now = os.time()
|
|
|
local startTime, endTime = BaiZhanChengShenDB.GetActivityTimes()
|
|
|
@@ -86,18 +103,69 @@ local function isRunning()
|
|
|
return isInOpenWday()
|
|
|
end
|
|
|
|
|
|
+-- 严格判断当前是否可开启新一轮: 周末 + 距上轮足够 + 已过本轮周六 0:10 + 上轮已结束
|
|
|
+local function canOpenNewRoundNow(now, lastReset, startTime, endTime)
|
|
|
+ now = now or os.time()
|
|
|
+ if not isInOpenWday() then
|
|
|
+ return false
|
|
|
+ end
|
|
|
+ local roundStart = alignRoundStart(now)
|
|
|
+ if now < roundStart then
|
|
|
+ return false
|
|
|
+ end
|
|
|
+ if lastReset == 0 then
|
|
|
+ return true
|
|
|
+ end
|
|
|
+ if Util.diffDay(lastReset) < BaiZhanChengShenDefine.BZCS_CYCLE_DAYS then
|
|
|
+ return false
|
|
|
+ end
|
|
|
+ if startTime > 0 and now < endTime then
|
|
|
+ return false
|
|
|
+ end
|
|
|
+ return true
|
|
|
+end
|
|
|
+
|
|
|
+-- 汇总本次 WL 广播已通知的逻辑服 serverId(MiddleManager_GetAllFD 返回 SVRINDEX_2_FD, key 即 serverId)
|
|
|
+local function formatNotifyServerIds(svrIndex2Fd)
|
|
|
+ local seen, serverIds = {}, {}
|
|
|
+ for serverId, _ in pairs(svrIndex2Fd or {}) do
|
|
|
+ if serverId and not seen[serverId] then
|
|
|
+ seen[serverId] = true
|
|
|
+ serverIds[#serverIds + 1] = serverId
|
|
|
+ end
|
|
|
+ end
|
|
|
+ -- 合服扩展连接(仅 MERGE 表有登记时补充)
|
|
|
+ for serverId, _ in pairs(SVRINDEX_2_FD_MERGE or {}) do
|
|
|
+ if serverId and not seen[serverId] then
|
|
|
+ seen[serverId] = true
|
|
|
+ serverIds[#serverIds + 1] = serverId
|
|
|
+ end
|
|
|
+ end
|
|
|
+ table.sort(serverIds)
|
|
|
+ return table.concat(serverIds, ",")
|
|
|
+end
|
|
|
+
|
|
|
-- 向单个逻辑服 fd 推送当前活动状态(重连/跨服重启后补偿, 避免错过广播)
|
|
|
local function sendActStateToFd(fd)
|
|
|
if not fd then return end
|
|
|
+ local serverId = MiddleManager.FD_2_SVRINDEX[fd] or 0
|
|
|
local startTime, endTime = BaiZhanChengShenDB.GetActivityTimes()
|
|
|
- if startTime > 0 and endTime > 0 and isRunning() then
|
|
|
+ if hasActiveRound() then
|
|
|
local msgData = InnerMsg.wl.WL_BZCS_ACT_START
|
|
|
msgData.startTime = startTime
|
|
|
msgData.endTime = endTime
|
|
|
InnerMsg.sendMsg(fd, msgData)
|
|
|
+ BzcsLog.logAction("act_open", string.format(
|
|
|
+ "tag=sync_fd serverId=%s fd=%s start=%s end=%s running=%s",
|
|
|
+ serverId, fd, startTime, endTime, isRunning() and 1 or 0
|
|
|
+ ))
|
|
|
else
|
|
|
local msgData = InnerMsg.wl.WL_BZCS_ACT_END
|
|
|
InnerMsg.sendMsg(fd, msgData)
|
|
|
+ BzcsLog.logAction("act_close", string.format(
|
|
|
+ "tag=sync_fd serverId=%s fd=%s start=%s end=%s running=0",
|
|
|
+ serverId, fd, startTime, endTime
|
|
|
+ ))
|
|
|
end
|
|
|
end
|
|
|
|
|
|
@@ -105,7 +173,7 @@ end
|
|
|
function onLogicServerConnect(fd)
|
|
|
if _G.is_middle ~= true or not fd then return end
|
|
|
sendActStateToFd(fd)
|
|
|
- local serverId = FD_2_SVRINDEX and FD_2_SVRINDEX[fd]
|
|
|
+ local serverId = MiddleManager.FD_2_SVRINDEX[fd]
|
|
|
if not serverId then return end
|
|
|
local pending = BaiZhanChengShenDB.TakePendingRewards(serverId)
|
|
|
if #pending > 0 then
|
|
|
@@ -125,25 +193,42 @@ function syncActStateToAllConnected()
|
|
|
end
|
|
|
end
|
|
|
|
|
|
--- 广播活动开启, 普通服写入 KEY_BZCS_START_TIME
|
|
|
-function ActOpen(startTime)
|
|
|
+-- 广播活动开启, 普通服写入 KEY_BZCS_START_TIME; tag 用于区分开轮/时间修正等场景
|
|
|
+function ActOpen(startTime, tag)
|
|
|
+ tag = tag or "broadcast"
|
|
|
local msgData = InnerMsg.wl.WL_BZCS_ACT_START
|
|
|
msgData.startTime = startTime or os.time()
|
|
|
local _, endTime = BaiZhanChengShenDB.GetActivityTimes()
|
|
|
msgData.endTime = endTime
|
|
|
- local fdList = MiddleManager.MiddleManager_GetAllFD()
|
|
|
- for _, fd in pairs(fdList) do
|
|
|
+ local svrIndex2Fd = MiddleManager.MiddleManager_GetAllFD()
|
|
|
+ local fdCnt = 0
|
|
|
+ for serverId, fd in pairs(svrIndex2Fd) do
|
|
|
InnerMsg.sendMsg(fd, msgData)
|
|
|
+ fdCnt = fdCnt + 1
|
|
|
end
|
|
|
+ BzcsLog.logAction("act_open", string.format(
|
|
|
+ "tag=%s start=%s end=%s fdCnt=%s running=%s servers=%s",
|
|
|
+ tag, msgData.startTime, msgData.endTime or 0, fdCnt, isRunning() and 1 or 0,
|
|
|
+ formatNotifyServerIds(svrIndex2Fd)
|
|
|
+ ))
|
|
|
end
|
|
|
|
|
|
--- 广播活动结束
|
|
|
-function ActEnd()
|
|
|
+-- 广播活动结束; tag 用于区分正常结算/放弃上轮等场景
|
|
|
+function ActEnd(tag)
|
|
|
+ tag = tag or "broadcast"
|
|
|
+ local startTime, endTime = BaiZhanChengShenDB.GetActivityTimes()
|
|
|
local msgData = InnerMsg.wl.WL_BZCS_ACT_END
|
|
|
- local fdList = MiddleManager.MiddleManager_GetAllFD()
|
|
|
- for _, fd in pairs(fdList) do
|
|
|
+ local svrIndex2Fd = MiddleManager.MiddleManager_GetAllFD()
|
|
|
+ local fdCnt = 0
|
|
|
+ for serverId, fd in pairs(svrIndex2Fd) do
|
|
|
InnerMsg.sendMsg(fd, msgData)
|
|
|
+ fdCnt = fdCnt + 1
|
|
|
end
|
|
|
+ BzcsLog.logAction("act_close", string.format(
|
|
|
+ "tag=%s start=%s end=%s fdCnt=%s rewardIssued=%s servers=%s",
|
|
|
+ tag, startTime or 0, endTime or 0, fdCnt, BaiZhanChengShenDB.IsRewardIssued() and 1 or 0,
|
|
|
+ formatNotifyServerIds(svrIndex2Fd)
|
|
|
+ ))
|
|
|
end
|
|
|
|
|
|
-- 按 serverId 汇总待发奖 {{uuid,rank},...}
|
|
|
@@ -178,18 +263,9 @@ local function issueRewardBatch(serverId, rewardList)
|
|
|
end
|
|
|
end
|
|
|
|
|
|
--- 末批发奖完成后标记 rewardIssued(1=最后一服)
|
|
|
-local function issueRewardBatchFinish(serverId, rewardList, markIssued)
|
|
|
- issueRewardBatch(serverId, rewardList)
|
|
|
- if markIssued == 1 then
|
|
|
- BaiZhanChengShenDB.SetRewardIssued(true)
|
|
|
- BzcsLog.logAction("reward_issue_done", string.format("serverId=%s lastBatchCnt=%s", serverId, #rewardList))
|
|
|
- end
|
|
|
-end
|
|
|
-
|
|
|
-- 活动结束发奖: 按逻辑服批量 WL_BZCS_ISSUE_REWARD(服与服之间 2s 节流)
|
|
|
function IssueRewardManager()
|
|
|
- if BaiZhanChengShenDB.IsRewardIssued() then
|
|
|
+ if BaiZhanChengShenDB.IsRewardIssued() or rewardIssueInProgress then
|
|
|
return
|
|
|
end
|
|
|
local rewardPlayers = BaiZhanChengShenDB.GetAllPlayersForReward()
|
|
|
@@ -202,13 +278,28 @@ function IssueRewardManager()
|
|
|
BaiZhanChengShenDB.SetRewardIssued(true)
|
|
|
return
|
|
|
end
|
|
|
+ rewardIssueInProgress = true
|
|
|
+ -- 先落库标记, 避免异步 Timer 完成前 timedStageHandle/oneMin 重复调度
|
|
|
+ BaiZhanChengShenDB.SetRewardIssued(true)
|
|
|
BzcsLog.logAction("reward_issue", string.format("playerCnt=%s serverCnt=%s", #rewardPlayers, #serverList))
|
|
|
local delay = 0
|
|
|
- for i, entry in ipairs(serverList) do
|
|
|
- delay = delay + 2
|
|
|
- local markIssued = (i == #serverList) and 1 or 0
|
|
|
- Timer.addLater(delay, issueRewardBatchFinish, entry[1], entry[2], markIssued)
|
|
|
+ local onlineCnt, pendingCnt = 0, 0
|
|
|
+ for _, entry in ipairs(serverList) do
|
|
|
+ local serverId, rewardList = entry[1], entry[2]
|
|
|
+ local fd = MiddleManager.getFDBySvrIndex(serverId)
|
|
|
+ if not fd then
|
|
|
+ BaiZhanChengShenDB.AddPendingRewards(serverId, rewardList)
|
|
|
+ pendingCnt = pendingCnt + 1
|
|
|
+ else
|
|
|
+ delay = delay + 2
|
|
|
+ onlineCnt = onlineCnt + 1
|
|
|
+ Timer.addLater(delay, issueRewardBatch, serverId, rewardList)
|
|
|
+ end
|
|
|
end
|
|
|
+ rewardIssueInProgress = false
|
|
|
+ BzcsLog.logAction("reward_issue_scheduled", string.format(
|
|
|
+ "onlineCnt=%s pendingCnt=%s delaySec=%s", onlineCnt, pendingCnt, delay
|
|
|
+ ))
|
|
|
end
|
|
|
|
|
|
-- 开启新周期: 重置 DB 并 ActOpen
|
|
|
@@ -217,18 +308,17 @@ local function newRoundHandle(now)
|
|
|
local startTime = alignRoundStart(now)
|
|
|
local endTime = calcRoundEnd(startTime)
|
|
|
BaiZhanChengShenDB.ResetForNewRound(startTime, endTime)
|
|
|
- ActOpen(startTime)
|
|
|
- BzcsLog.logAction("act_open", string.format("start=%s end=%s", startTime, endTime))
|
|
|
+ ActOpen(startTime, "new_round")
|
|
|
end
|
|
|
|
|
|
-- 结束当前周期: 发奖 + ActEnd
|
|
|
local function endRoundHandle()
|
|
|
- if BaiZhanChengShenDB.IsRewardIssued() then
|
|
|
+ if BaiZhanChengShenDB.IsRewardIssued() or rewardIssueInProgress then
|
|
|
return
|
|
|
end
|
|
|
- BzcsLog.logAction("act_end", "begin_issue_reward")
|
|
|
+ BzcsLog.logAction("act_settle", "begin_issue_reward")
|
|
|
IssueRewardManager()
|
|
|
- ActEnd()
|
|
|
+ ActEnd("end_round")
|
|
|
end
|
|
|
|
|
|
-- 放弃上轮未完成的周期发奖(满21天开新轮时不再补发)
|
|
|
@@ -239,25 +329,15 @@ local function abandonUnissuedRewards()
|
|
|
BzcsLog.logAction("reward_abandon", "new_round_skip")
|
|
|
BaiZhanChengShenDB.SetRewardIssued(true)
|
|
|
BaiZhanChengShenDB.ClearAllPendingRewards()
|
|
|
- ActEnd()
|
|
|
+ ActEnd("abandon_new_round")
|
|
|
end
|
|
|
|
|
|
--- 是否应开启新轮(首次开轮 / 满21天开放日); 成功则已执行 newRoundHandle
|
|
|
+-- 是否应开启新轮; 条件满足则执行 newRoundHandle 并广播
|
|
|
local function tryOpenNewRound(now, lastReset, startTime, endTime)
|
|
|
- if not isInOpenWday() then
|
|
|
+ if not canOpenNewRoundNow(now, lastReset, startTime, endTime) then
|
|
|
return false
|
|
|
end
|
|
|
- if lastReset == 0 then
|
|
|
- newRoundHandle(now)
|
|
|
- return true
|
|
|
- end
|
|
|
- if Util.diffDay(lastReset) < BaiZhanChengShenDefine.BZCS_CYCLE_DAYS then
|
|
|
- return false
|
|
|
- end
|
|
|
- if startTime > 0 and now < endTime then
|
|
|
- return false
|
|
|
- end
|
|
|
- if not BaiZhanChengShenDB.IsRewardIssued() then
|
|
|
+ if lastReset > 0 and not BaiZhanChengShenDB.IsRewardIssued() then
|
|
|
abandonUnissuedRewards()
|
|
|
end
|
|
|
newRoundHandle(now)
|
|
|
@@ -265,31 +345,30 @@ local function tryOpenNewRound(now, lastReset, startTime, endTime)
|
|
|
end
|
|
|
|
|
|
-- 周期阶段机 (oneMin/onHour 调用):
|
|
|
--- 1) 满足新轮条件(含满21天且上轮未发奖则放弃发奖直接开轮) -> newRoundHandle
|
|
|
--- 2) 已过 endTime 且未发奖且未满21天新轮 -> endRoundHandle
|
|
|
--- 3) 活动记录中但 isRunning 为假(如跨天) -> 修正时间并 ActOpen
|
|
|
+-- 1) 严格满足开新轮条件(周末/间隔/已过周六0:10/上轮已结束) -> newRoundHandle + ActOpen
|
|
|
+-- 2) 已过 endTime 且未发奖 -> endRoundHandle
|
|
|
+-- 3) 刚进入可挑战窗口(如周六0:10) -> ActOpen 补偿逻辑服
|
|
|
local function timedStageHandle()
|
|
|
local now = os.time()
|
|
|
local lastReset = BaiZhanChengShenDB.GetLastResetTime()
|
|
|
local startTime, endTime = BaiZhanChengShenDB.GetActivityTimes()
|
|
|
+ local running = isRunning()
|
|
|
|
|
|
if tryOpenNewRound(now, lastReset, startTime, endTime) then
|
|
|
+ prevIsRunning = isRunning()
|
|
|
return
|
|
|
end
|
|
|
|
|
|
if startTime > 0 and now >= endTime and not BaiZhanChengShenDB.IsRewardIssued() then
|
|
|
endRoundHandle()
|
|
|
+ prevIsRunning = false
|
|
|
return
|
|
|
end
|
|
|
|
|
|
- if startTime > 0 and now >= startTime and now < endTime and not isRunning() then
|
|
|
- if isInOpenWday() then
|
|
|
- local ts = alignRoundStart(now)
|
|
|
- local te = calcRoundEnd(ts)
|
|
|
- BaiZhanChengShenDB.SetActivityTimes(ts, te)
|
|
|
- ActOpen(ts)
|
|
|
- end
|
|
|
+ if running and not prevIsRunning then
|
|
|
+ ActOpen(startTime, "window_open")
|
|
|
end
|
|
|
+ prevIsRunning = running
|
|
|
end
|
|
|
|
|
|
-- Timer 每分钟(跳过整点)检查阶段
|