gitxsm 2 недель назад
Родитель
Сommit
1aedf900e9

+ 3 - 1
script/common/LogDefine.lua

@@ -281,7 +281,9 @@ DEFINE = {
 	war_order_get           = 651,           -- 战令奖励获取
     hero_merge         = 701,           -- 英雄融合
 	skin_buy                = 702,           -- 皮肤活动购买
-	newhero_reward			= 703,			 -- 新英雄礼包
+	newhero_reward			= 703,			 -- 新英雄礼包 EVA来袭1
+	newhero_reward_9002		= 790,			 -- 新英雄礼包 EVA来袭2
+	newhero_reward_9003		= 791,			 -- 新英雄礼包 EVA来袭3
 	gift_buy                = 704,           -- 弹窗礼包购买
 	xianzhi_buy             = 705,           -- 先知商店购买
 	heroupgrade_get         = 706,           -- 英雄升星限时活动获得

+ 2 - 0
script/module/absAct/AbsActDefine.lua

@@ -33,6 +33,8 @@ ABS_ACT_TYPE_32 = 32 -- 限时夺宝/夺宝奇兵活动
 ABS_ACT_TYPE_33 = 33 -- 元宵喜庆来年
 ABS_ACT_TYPE_34 = 34 -- 魔兽双倍洗练
 ABS_ACT_TYPE_35 = 35 -- 新英雄抽卡
+ABS_ACT_TYPE_36 = 36 -- 新英雄抽卡
+ABS_ACT_TYPE_37 = 37 -- 新英雄抽卡
 
 
 ABS_ACT_TYPE_46 = 46 -- 新服-七天战力达标

+ 4 - 1
script/module/absAct/AbsActLogic.lua

@@ -74,6 +74,7 @@ function initAfterHot()
         ABS_ACT_TYPE_LIST[config.type] = ABS_ACT_TYPE_LIST[config.type] or {}
         ABS_ACT_TYPE_LIST[config.type][#ABS_ACT_TYPE_LIST[config.type] + 1] = k
     end
+    NewHeroLogic.initAfterHot()
 end
 
 function onLogin(human)
@@ -398,7 +399,9 @@ function actDetailQuery(human, id)
         DuoBaoQiBingLogic.Query(human, id)
     elseif config.type == AbsActDefine.ABS_ACT_TYPE_33 then
         FestivalSevenDayCardLogic.getAndSendMsg(human, id)
-    elseif config.type == AbsActDefine.ABS_ACT_TYPE_35 then
+    elseif config.type == AbsActDefine.ABS_ACT_TYPE_35
+            or config.type == AbsActDefine.ABS_ACT_TYPE_36
+            or config.type == AbsActDefine.ABS_ACT_TYPE_37 then
         NewHeroLogic.getAndSendMsg(human, id)
     elseif config.type == AbsActDefine.ABS_ACT_TYPE_75 then
         AbsLoginRewardLogic.Query(human, id)

+ 152 - 39
script/module/absAct/NewHeroLogic.lua

@@ -13,9 +13,41 @@ local Log = require("common.Log")
 local YunYingLogic = require("yunying.YunYingLogic")
 
 ABS_ACT_ID = 9001
+NEW_HERO_MODULE_FN = "absAct.NewHeroLogic"
+NEW_HERO_ACT_TYPES = {}
 --
 function initAfterHot()
-    
+    NEW_HERO_ACT_TYPES = {}
+    for _, config in pairs(AbsActExcel.absActivity) do
+        if config.moduleFn == NEW_HERO_MODULE_FN then
+            NEW_HERO_ACT_TYPES[#NEW_HERO_ACT_TYPES + 1] = config.type
+        end
+    end
+end
+
+function getActiveActId(human)
+    for _, actType in ipairs(NEW_HERO_ACT_TYPES) do
+        local state, actId = AbsActLogic.isStartedByType(human, actType)
+        if state and actId then
+            return actId
+        end
+    end
+end
+
+local function getGiftConfig(giftId, actId)
+    local config = AbsActExcel.absEva[giftId]
+    if not config or config.actId ~= actId then
+        return
+    end
+    return config
+end
+
+local function copyRewardList(reward)
+    local items = {}
+    for _, v in ipairs(reward) do
+        items[#items + 1] = {v[1], v[2], v[3]}
+    end
+    return items
 end
 
 --
@@ -35,7 +67,11 @@ end
 
 --
 function getLeftTime(human, YInfo, funcConfig)
-    local ret, endTime, startTime = AbsActLogic.isStarted(human, ABS_ACT_ID)
+    local actId = funcConfig and funcConfig.funcID or getActiveActId(human)
+    if not actId then
+        return 0
+    end
+    local ret, endTime, startTime = AbsActLogic.isStarted(human, actId)
     if ret == true then
         return endTime - os.time()
     else
@@ -44,31 +80,66 @@ function getLeftTime(human, YInfo, funcConfig)
 end
 
 function getNewHeroID(human)
-    local startedFlag, endTime, startTime = AbsActLogic.isStarted(human, ABS_ACT_ID)
-    if not startedFlag then return 0 end
-    local absActConfig = AbsActExcel.absActivity[ABS_ACT_ID]
+    local actId = getActiveActId(human)
+    if not actId then return 0 end
+    local absActConfig = AbsActExcel.absActivity[actId]
     if not absActConfig then return 0 end
 
     return absActConfig.icon
 end
 
+local function getActClientIndex(actId)
+    if actId == 9001 then
+        return 1
+    elseif actId == 9002 then
+        return 2
+    elseif actId == 9003 then
+        return 3
+    end
+    return 0
+end
+
+local function getActLogType(actId)
+    if actId == 9002 then
+        return "newhero_reward_9002"
+    elseif actId == 9003 then
+        return "newhero_reward_9003"
+    end
+    return "newhero_reward"
+end
+
 function getAndSendMsg(human, id, actId)
     local now = os.time()
     local startedFlag, endTime, startTime = AbsActLogic.isStarted(human, id)
-    if not startedFlag then return end
+    if not startedFlag then
+        return Broadcast.sendErr(human, Lang.YUNYING_ERR_TIME)
+    end
     local absActConfig = AbsActExcel.absActivity[id]
-    if not absActConfig then return end
+    if not absActConfig then
+        return Broadcast.sendErr(human, Lang.COMMON_COMFIG_ERROR)
+    end
 
     local absAct = human.db.absAct[id]
-    if not absAct then return end
+    if not absAct then
+        return Broadcast.sendErr(human, Lang.COMMON_COMFIG_ERROR)
+    end
 
     local msgRet = Msg.gc.GC_ABS_NEWHERO
     msgRet.id = absActConfig.icon
     msgRet.startTime = startTime
     msgRet.endTime = endTime
+    msgRet.actIndex = getActClientIndex(id)
 
     local index = 0
-    for giftId, v in ipairs(AbsActExcel.absEva) do
+    local giftIds = {}
+    for giftId, v in pairs(AbsActExcel.absEva) do
+        if v.actId == id then
+            giftIds[#giftIds + 1] = giftId
+        end
+    end
+    table.sort(giftIds)
+    for _, giftId in ipairs(giftIds) do
+        local v = AbsActExcel.absEva[giftId]
         index = index + 1
         for j = 1, #v.reward do
             Grid.makeItem(msgRet.giftlist[index].item[j], v.reward[j][1], v.reward[j][2])
@@ -105,13 +176,16 @@ function getAndSendMsg(human, id, actId)
         end
     end
     msgRet.giftlist[0] = index
+    if index == 0 then
+        return Broadcast.sendErr(human, Lang.COMMON_COMFIG_ERROR)
+    end
 
     Msg.send(msgRet, human.fd)
 end
 
-function getgiftbybuyid(buyid)
-    for giftId, v in ipairs(AbsActExcel.absEva) do
-        if v.buyID == buyid then
+function getgiftbybuyid(buyid, actId)
+    for giftId, v in pairs(AbsActExcel.absEva) do
+        if v.buyID == buyid and v.actId == actId then
             return giftId
         end
     end
@@ -120,14 +194,35 @@ end
 function newheroGift(human, id, buyConf, isFirst, cnt, buyNum)
     local state = AbsActLogic.isStarted(human, id)
     local absConfig = AbsActExcel.absActivity[id]
-    if absConfig == nil or not state then return end
+    if absConfig == nil then
+        Log.write(Log.LOGID_OSS_COMMON_ACT, string.format(
+            "[NewHeroLogic.newheroGift] absActivity config not found, actId=%s, buyID=%s, newUniqueTag=%s",
+            tostring(id), tostring(buyConf and buyConf.id), tostring(human.db.newUniqueTag)))
+        return
+    end
+    if not state then
+        Log.write(Log.LOGID_OSS_COMMON_ACT, string.format(
+            "[NewHeroLogic.newheroGift] act not started, actId=%s, buyID=%s, newUniqueTag=%s, now=%s",
+            tostring(id), tostring(buyConf.id), tostring(human.db.newUniqueTag), os.time()))
+        return
+    end
 
     -- 存在多个特惠礼包 同时   开放
-    local giftid = getgiftbybuyid(buyConf.id)
-    if not giftid then return end
+    local giftid = getgiftbybuyid(buyConf.id, id)
+    if not giftid then
+        Log.write(Log.LOGID_OSS_COMMON_ACT, string.format(
+            "[NewHeroLogic.newheroGift] absEva gift not found, actId=%s, buyID=%s, newUniqueTag=%s",
+            tostring(id), tostring(buyConf.id), tostring(human.db.newUniqueTag)))
+        return
+    end
 
-    local config = AbsActExcel.absEva[giftid]
-    if not config then return end
+    local config = getGiftConfig(giftid, id)
+    if not config then
+        Log.write(Log.LOGID_OSS_COMMON_ACT, string.format(
+            "[NewHeroLogic.newheroGift] absEva gift config invalid, actId=%s, giftId=%s, buyID=%s, newUniqueTag=%s",
+            tostring(id), tostring(giftid), tostring(buyConf.id), tostring(human.db.newUniqueTag)))
+        return
+    end
 
     AbsActLogic.checkAbsActClean(human, id)
 
@@ -167,8 +262,8 @@ function newheroGift(human, id, buyConf, isFirst, cnt, buyNum)
     -- BagLogic.addItemList(human, config.reward, "newhero_reward")
     human.db.absAct[id].newheroCnt[giftid] = nowBuyCnt + 1
     -- 发放物品
-    -- local items = { }
-    BagLogic.addItemList(human, config.reward, "newhero_reward")
+    local items = copyRewardList(config.reward)
+    BagLogic.addItemList(human, items, getActLogType(id))
 
     Broadcast.sendErr(human, Lang.ITEM_BUY_SUCCESS)
     AbsActLogic.actDetailQuery(human, id)
@@ -176,20 +271,34 @@ end
 
 
 function giftBuy(human, giftid)
-    local state = AbsActLogic.isStarted(human, ABS_ACT_ID)
-    if state ~= true then return end
-    local absConfig = AbsActExcel.absActivity[ABS_ACT_ID]
-    if not absConfig then return end
+    local actId = getActiveActId(human)
+    if not actId then
+        return Broadcast.sendErr(human, Lang.YUNYING_ERR_TIME)
+    end
+
+    local state = AbsActLogic.isStarted(human, actId)
+    if state ~= true then
+        return Broadcast.sendErr(human, Lang.YUNYING_ERR_TIME)
+    end
+    local absConfig = AbsActExcel.absActivity[actId]
+    if not absConfig then
+        return Broadcast.sendErr(human, Lang.COMMON_COMFIG_ERROR)
+    end
 
-    AbsActLogic.checkAbsActClean(human, ABS_ACT_ID)
+    AbsActLogic.checkAbsActClean(human, actId)
 
     -- 存在多个特惠礼包 同时   开放
-    local config = AbsActExcel.absEva[giftid]
-    if config.buyID ~= 0 then return end
+    local config = getGiftConfig(giftid, actId)
+    if not config then
+        return Broadcast.sendErr(human, Lang.COMMON_COMFIG_ERROR)
+    end
+    if config.buyID ~= 0 then
+        return Broadcast.sendErr(human, Lang.COMMON_ARGUMENT_ERROR)
+    end
 
     -- 初始化已购买次数
-    human.db.absAct[ABS_ACT_ID].newheroCnt = human.db.absAct[ABS_ACT_ID].newheroCnt or { }
-    local nowBuyCnt = human.db.absAct[ABS_ACT_ID].newheroCnt[giftid] or 0
+    human.db.absAct[actId].newheroCnt = human.db.absAct[actId].newheroCnt or { }
+    local nowBuyCnt = human.db.absAct[actId].newheroCnt[giftid] or 0
     -- 判断是否达到购买上限
     if nowBuyCnt >= config.limit then
         Broadcast.sendErr(human, Lang.HERO_BAG_BUY_CAP_NO_CNT)
@@ -214,28 +323,32 @@ function giftBuy(human, giftid)
         end
     end
 
-    BagLogic.delItem(human, itemID, itemCnt, "newhero_reward")
+    BagLogic.delItem(human, itemID, itemCnt, getActLogType(actId))
 
     -- 增加已购买次数
-    human.db.absAct[ABS_ACT_ID].newheroCnt[giftid] = nowBuyCnt + 1
+    human.db.absAct[actId].newheroCnt[giftid] = nowBuyCnt + 1
     -- 发放物品
-    -- local items = { }
-    BagLogic.addItemList(human, AbsActExcel.absEva[giftid].reward, "newhero_reward")
+    local items = copyRewardList(config.reward)
+    BagLogic.addItemList(human, items, getActLogType(actId))
 
     Broadcast.sendErr(human, Lang.ITEM_BUY_SUCCESS)
 
-    AbsActLogic.actDetailQuery(human, ABS_ACT_ID)
+    AbsActLogic.actDetailQuery(human, actId)
 end
 
 function updateDaily(human, id)
-    if human.db.absAct[ABS_ACT_ID] then
-        human.db.absAct[ABS_ACT_ID] = {}
+    if not id then return end
+
+    if human.db.absAct[id] then
+        human.db.absAct[id] = {}
     end
-    local state = AbsActLogic.isStarted(human, ABS_ACT_ID)
+    local state = AbsActLogic.isStarted(human, id)
     if state ~= true then
-        human.db.drawCard.list[8] = {}
+        if getActiveActId(human) == nil then
+            human.db.drawCard.list[8] = {}
+        end
     end
-    AbsActLogic.actDetailQuery(human, ABS_ACT_ID)
+    AbsActLogic.actDetailQuery(human, id)
  end
 
 -- function GetRemainNum(human, nBuyID)
@@ -256,4 +369,4 @@ function updateDaily(human, id)
 --     else 
 --         return config.limit - human.db.absAct[ABS_ACT_ID].newheroCnt[giftid]
 --     end
--- end
+-- end

+ 1 - 0
script/module/absAct/Proto.lua

@@ -931,6 +931,7 @@ GC_ABS_NEWHERO = {
     {"startTime",       1,  "int"},         -- 开启时间
     {"endTime",         1,  "int"},         -- 结束时间
     {"giftlist",        14,  NewHeroGift},   -- 礼包列表
+    {"actIndex",        1,  "byte"},        -- 当前活动期数 9001=1 9002=2 9003=3
 }
 
 CG_ABS_NEWHERO_BUYGIFT = {

+ 2 - 1
script/module/drawCard/DrawCardLogic.lua

@@ -724,7 +724,8 @@ local function draw(human, id, op, actConfig, skip,isAct)
         return
     end
 
-    local state, endTime, starTime = AbsActLogic.isStarted(human, NewHeroLogic.ABS_ACT_ID)
+    local actId = NewHeroLogic.getActiveActId(human)
+    local state = actId and AbsActLogic.isStarted(human, actId)
 
     if id == DRAWCARD_ID8 and not state then
         return Broadcast.sendErr(human, Lang.ABS_JYZH_NOT_OPEN)

+ 164 - 79
script/module/mail/MailManager.lua

@@ -15,6 +15,9 @@ SYSTEM = 1 				-- 系统
 GONGGAO = 2 				-- 公告
 
 MAIL_MAX_CNT = 100			-- 邮件最大值
+MAIL_LOOKBACK_SEC = 90 * 86400	-- 查询回溯上限(覆盖较长 expireTime)
+MAIL_SCAN_LIMIT = 2000		-- 单时间窗游标扫描上限
+MAIL_SORT_DESC = {time = -1}	-- find 第4参: 按时间倒序,优先拿到最新邮件
 
 local FIELD_ID = {_id = nil}
 local FIELD_RECEIVER = {uuid = nil,type = nil}
@@ -158,6 +161,68 @@ local function cmpMail(a, b)
 end
 
 local mails = {}
+local seenMailIds = {}
+
+local function isMailExpired(m, now)
+	if type(m.time) ~= "number" then
+		m.time = 0
+	end
+	local expireTime = m.expireTime
+	if expireTime and type(expireTime) == "number" then
+		return m.time <= now - expireTime
+	end
+	return m.time <= now - 7 * 86400
+end
+
+local function countValidThreshold(sortedMails, now)
+	local cnt = 0
+	for _, m in ipairs(sortedMails) do
+		if not isMailExpired(m, now) then
+			cnt = cnt + 1
+			if cnt >= MAIL_MAX_CNT then
+				return cnt, m.time
+			end
+		end
+	end
+	return cnt, nil
+end
+
+-- 缓存过大时只保留最新 MAIL_MAX_CNT 条未过期,降低内存与排序成本
+local function trimTempMails(tempMails, now)
+	if #tempMails <= MAIL_MAX_CNT * 2 then
+		return
+	end
+	table.sort(tempMails, cmpMail)
+	local writeIdx = 0
+	for _, m in ipairs(tempMails) do
+		if not isMailExpired(m, now) then
+			writeIdx = writeIdx + 1
+			tempMails[writeIdx] = m
+			if writeIdx >= MAIL_MAX_CNT then
+				break
+			end
+		end
+	end
+	for i = writeIdx + 1, #tempMails do
+		tempMails[i] = nil
+	end
+end
+
+local function buildMailQuery(receiverUuid, mailType, startTime, endTime, useLte)
+	local query = {receiverUuid = receiverUuid}
+	if mailType ~= nil then
+		query.type = mailType
+	end
+	local timeCond = {["$gte"] = startTime}
+	if useLte then
+		timeCond["$lte"] = endTime
+	else
+		timeCond["$lt"] = endTime
+	end
+	query.time = timeCond
+	return query
+end
+
 -- function getMails(receiverUuid,mailType)
 -- 	for key in ipairs(mails) do 
 -- 		mails[key] = nil
@@ -209,93 +274,113 @@ local mails = {}
 -- 	return mails
 -- end
 
--- 替换原有的 getMails 函数
+-- 分段 + 倒序游标:兼顾 C 驱动稳定性与“取最新 100 封未过期”语义
 function getMails(receiverUuid, mailType)
-    -- 1. 清空缓存表
-    for i = 1, #mails do mails[i] = nil end
-
-    local now = os.time()
-    local mailCnt = 0
-    local tempMails = {}
-    local SCAN_LIMIT = 2000  -- 单次分段查询安全上限
-    local foundEnough = false
-
-    -- 2.将时间划分为多个小窗口,避免单次 find 触发底层 OOM
-    -- 窗口设计:最近7天 -> 7~14天 -> 14~30天 -> 30~60天
-    local timeWindows = {
-        {now - 7*86400,   now},
-        {now - 14*86400,  now - 7*86400},
-        {now - 30*86400,  now - 14*86400},
-        {now - 60*86400,  now - 30*86400}
-    }
-	-- 60天 -> 开服
+	for i = 1, #mails do
+		mails[i] = nil
+	end
+	for k in pairs(seenMailIds) do
+		seenMailIds[k] = nil
+	end
+
+	local now = os.time()
+	local mailCnt = 0
+	local tempMails = {}
+	local thresholdTime = nil
+	local lookbackStart = now - MAIL_LOOKBACK_SEC
+
+	-- 半开区间 [start, end);首窗含 now 用 lte
+	local timeWindows = {
+		{now - 7 * 86400,   now, true},
+		{now - 14 * 86400,  now - 7 * 86400, false},
+		{now - 30 * 86400,  now - 14 * 86400, false},
+		{now - 60 * 86400,  now - 30 * 86400, false},
+		{now - MAIL_LOOKBACK_SEC, now - 60 * 86400, false},
+	}
 	local serverOpenTime = CommonDB.getServerOpenTime()
-	timeWindows[#timeWindows+1] = { serverOpenTime, now - 60*86400 }
-
-    for _, window in ipairs(timeWindows) do
-        if foundEnough then break end
-
-        -- 使用最简平铺查询,彻底避开 C 扩展解析缺陷
-        local query = {
-            receiverUuid = receiverUuid,
-            type = mailType,
-            time = { ["$gte"] = window[1], ["$lte"] = window[2] }
-        }
-
-        LuaMongo.find(DB.db_mail, query)
-
-        local mail = {}
-        local scanCnt = 0
-        while true do
-            local has_next = LuaMongo.next(mail)
-            if not has_next then break end
-            
-            scanCnt = scanCnt + 1
-            if scanCnt > SCAN_LIMIT then 
-                Log.write(Log.LOGID_DEBUG, string.format("Mail segment limit hit! uuid=%s window=%d~%d", 
-                    receiverUuid, window[1], window[2]))
-                break 
-            end
-
-            -- 浅拷贝隔离 C 驱动内部 Buffer
-            local m = {}
-            for k, v in pairs(mail) do m[k] = v end
-            tempMails[#tempMails + 1] = m
-
-            -- 清空复用表
-            for k in pairs(mail) do mail[k] = nil end
-        end
+	if serverOpenTime < lookbackStart then
+		timeWindows[#timeWindows + 1] = {serverOpenTime, lookbackStart, false}
+	end
 
-        -- 如果已收集足够多数据(留足过滤冗余),提前终止后续窗口查询
-        if #tempMails >= 500 then 
-            foundEnough = true 
-            break 
-        end
-    end
+	for _, window in ipairs(timeWindows) do
+		local startTime, endTime, useLte = window[1], window[2], window[3]
+		if startTime >= endTime then
+			goto continue_window
+		end
 
-    -- 3.按时间倒序排序,确保后续取到的是“最新”的
-    table.sort(tempMails, cmpMail)
+		if thresholdTime then
+			local windowMaxTime = useLte and endTime or (endTime - 1)
+			if windowMaxTime < thresholdTime then
+				break
+			end
+		end
 
-    -- 4. 过滤过期邮件,精准截取最新 100 条
-    for _, m in ipairs(tempMails) do
-        if type(m.time) ~= "number" then m.time = 0 end
+		local query = buildMailQuery(receiverUuid, mailType, startTime, endTime, useLte)
+		LuaMongo.find(DB.db_mail, query, nil, MAIL_SORT_DESC)
+
+		local mail = {}
+		local scanCnt = 0
+		while true do
+			local ok, hasNext = pcall(function()
+				return LuaMongo.next(mail)
+			end)
+			if not ok then
+				Log.write(Log.LOGID_DEBUG, "MailManager.getMails next err: " .. tostring(hasNext))
+				break
+			end
+			if not hasNext then
+				break
+			end
+
+			scanCnt = scanCnt + 1
+			if scanCnt > MAIL_SCAN_LIMIT then
+				Log.write(Log.LOGID_DEBUG, string.format(
+					"Mail segment limit hit! uuid=%s window=%d~%d",
+					receiverUuid, startTime, endTime))
+				break
+			end
+
+			if thresholdTime and type(mail.time) == "number" and mail.time < thresholdTime then
+				break
+			end
+
+			local mailId = mail._id
+			if not mailId or not seenMailIds[mailId] then
+				if mailId then
+					seenMailIds[mailId] = true
+				end
+				local m = {}
+				for k, v in pairs(mail) do
+					m[k] = v
+				end
+				tempMails[#tempMails + 1] = m
+				trimTempMails(tempMails, now)
+				local validCnt, th = countValidThreshold(tempMails, now)
+				if th then
+					thresholdTime = th
+				end
+			end
+
+			for k in pairs(mail) do
+				mail[k] = nil
+			end
+		end
 
-        local isExpired = false
-        local expireTime = m.expireTime
-        if expireTime and type(expireTime) == "number" then
-            if m.time <= now - expireTime then isExpired = true end
-        elseif m.time <= now - 7 * 86400 then
-            isExpired = true
-        end
+		::continue_window::
+	end
 
-        if not isExpired then
-            mailCnt = mailCnt + 1
-            mails[mailCnt] = m
-            if mailCnt >= MAIL_MAX_CNT then break end -- 达到 100 条立即退出
-        end
-    end
+	table.sort(tempMails, cmpMail)
+	for _, m in ipairs(tempMails) do
+		if not isMailExpired(m, now) then
+			mailCnt = mailCnt + 1
+			mails[mailCnt] = m
+			if mailCnt >= MAIL_MAX_CNT then
+				break
+			end
+		end
+	end
 
-    return mails
+	return mails
 end