local Msg = require("core.Msg") local Timer = require("core.Timer") local DB = require("common.DB") local CommonDB = require("common.CommonDB") local Log = require("common.Log") local LuaMongo = _G.lua_mongo local Util = require("common.Util") local Grid = require("bag.Grid") local AbsActLogic = require("absAct.AbsActLogic") local RoleDBLogic = require("role.RoleDBLogic") local YunYingLogic = require("yunying.YunYingLogic") local MailManager = require("mail.MailManager") local MailExcel = require("excel.mail") local AbsActExcel = require("excel.absAct") --[[ abs公共排行类活动DB操作,封装了一些公共处理方法 db字段只是缓存排名好的数据(只是内存数据) db = { [rankType] = { -- 排名类型 rankType = xxx, -- 排名类型 uuid2rank = { -- 根据UUID获取名次 [uuid] = rank, -- 玩家uuid = 排行名次 ... }, sendUuid2rank = { -- 根据UUID获取发送名次 [uuid] = rank, -- 玩家uuid = 真实发送的排行名次 ... }, rank2data = { -- 根据名次获取DATA [rank] = { -- 排行名次 = 玩家排名数据 uuid = xxx, -- 玩家uuid rankType = xxx, -- 排名类型 value = xxx, -- 排名值 time = xxx, -- 排名值更新时间 _id = xxx, -- db记录中的uuid }, ... }, sendRank2data = { -- 根据名次获取发送DATA [rank] = { -- 排行名次 = 真实发送玩家排名数据 uuid = xxx, -- 玩家uuid rankType = xxx, -- 排名类型 value = xxx, -- 排名值 time = xxx, -- 排名值更新时间 _id = xxx, -- db记录中的uuid }, ... }, funcID = xxx, -- 活动id funcRankType = xxx, -- 活动排名类型 }, ... } local: getActConfig() -- 得到指定排名类型的活动配置 getMaxRank() -- 得到活动最多排名名次 getRankType() -- 拼接排名类型 resertBoard() -- 重置排行数据 initBoard() -- 初始排行榜 getBoard() -- 获取指定排名类型DB数据 getRankInfo() -- 得到指定排行榜指定名次的配置信息 getRewardInfo() -- 得到指定排行榜指定配置信息的奖励信息 getRankNeedValue() -- 得到排行榜中名次对应需要达到的条件数 getMailID() -- 获取发送奖励的邮件ID getRewardInfoByInfo() -- 得到指定排行榜指定配置信息的奖励信息 getRank() -- 获取指定排名类型、指定用户的排名 getSendRank() -- 获取指定排名类型、指定用户的发送排名 cmpRank() -- 排名算法 updateRank() -- 更新排名 updateData() -- 更新数据 loadBoard() -- 加载排行榜 getDBData() -- 根据类型+uuid从db中取data billboardSet() -- 设置排名数据 rewardSend() -- 奖励发送 wrapRankRewardList -- 包装档位奖励详情数据 public: getMaxSendRank() -- 得到最大发送排行名次数据 getMinRankValue() -- 得到排行榜中最低上榜条件数 wrapRankList() -- 包装档位名次数据 wrapOwnerData() -- 包装自己名次数据 rewardQuery() -- 发送奖励详情 onValueAdd() -- 排行值增加回调 onRewardSend() -- 奖励发送回调 --]] FUNC_RANK_DAY_TYPE = 1 -- 同活动每日榜类型 FUNC_RANK_TOTAL_TYPE = 2 -- 同活动累计榜类型 FUNC_RANK_TYPE_INFOS = { -- 不同配置命名映射 [FUNC_RANK_DAY_TYPE] = { rankValueName = "dayNeedValue", rewardName = "dayRewards", mailIDName = "dayMailID", minRankValueName = "dayMinRankValue", isDayReward = true, }, [FUNC_RANK_TOTAL_TYPE] = { rankValueName = "totalNeedValue", rewardName = "totalRewards", mailIDName = "totalMailID", minRankValueName = "totalMinRankValue", } } MAX_SEND_RANK = 10 -- 最大发送排名数 DEFAULT_MAX_RANK = 200 db = db or {} -- [rankType] = board QueryForData = {rankType = 1, uuid = 1} -- 通过排名类型和玩家uuid查找DB数据 QueryByUuid = {_id = 1} -- 通过DB的uuid更新DB数据 QueryByRankType = {rankType = 1} -- 通过排名类型查找DB数据 local funcConfigs local function getActConfig(funcID, actId) local funcConfig = AbsActExcel.absActivity[funcID] if not funcConfig then return end local tempConfig = AbsActExcel[funcConfig.param[1]] if not tempConfig then return end if not actId then actId = funcConfig.actId end if not actId or actId == 0 then return end funcConfigs = funcConfigs or {} funcConfigs[funcID] = funcConfigs[funcID] or {} if funcConfigs[funcID][actId] then return funcConfigs[funcID][actId] end funcConfigs[funcID][actId] = {configs={}} local actConfigs = funcConfigs[funcID][actId] local dayMinRankValue,totalMinRankValue,maxRank local len = 0 for id,info in Util.pairsByKeys(tempConfig) do if actId == info.actId then len = len + 1 info.id = id actConfigs.configs[len] = info -- 缓存abs活动指定期配置信息 -- 缓存最低上榜需要达到的排行值 if info.dayNeedValue then -- 活动有单日榜 if not dayMinRankValue or dayMinRankValue > info.dayNeedValue then dayMinRankValue = info.dayNeedValue end elseif info.totalNeedValue then -- 活动有累计榜 if not totalMinRankValue or totalMinRankValue > info.totalNeedValue then totalMinRankValue = info.totalNeedValue end end if not maxRank or maxRank <= info.ranks[2] then maxRank = info.ranks[2] end end end actConfigs.maxRank = maxRank actConfigs.dayMinRankValue = dayMinRankValue actConfigs.totalMinRankValue = totalMinRankValue return funcConfigs[funcID][actId] end local function getMaxRank(funcID) return DEFAULT_MAX_RANK end local function getRankType(funcID, funcRankType) return funcID .. "-" .. funcRankType end local function resertBoard(board) board.uuid2rank = {} -- 根据UUID获取名次 board.sendUuid2rank = {} -- 根据UUID获取发送名次 board.rank2data = {} -- 根据名次获取DATA board.sendRank2data = {} -- 根据名次获取发送DATA end local function initBoard(funcID, funcRankType) local rankType = getRankType(funcID, funcRankType) if db[rankType] then return end local board = {} resertBoard(board) board.rankType = rankType board.funcID = funcID board.funcRankType = funcRankType db[rankType] = board end local function getBoard(funcID, funcRankType) local rankType = getRankType(funcID, funcRankType) local board = db[rankType] if not board then initBoard(funcID, funcRankType) end return db[rankType] end local function getRankInfo(funcID, rank, actId) local actConfig = getActConfig(funcID, actId) if not actConfig then return end local rankInfo for _,info in ipairs(actConfig.configs) do if rank >= info.ranks[1] and rank <= info.ranks[2] then rankInfo = info break end end return rankInfo end local function getRewardInfo(funcID, rank, funcRankType) local rankInfo = getRankInfo(funcID, rank) if not rankInfo then return end local info = FUNC_RANK_TYPE_INFOS[funcRankType] return info and rankInfo[info.rewardName] end local function getRankNeedValue(funcID, funcRankType, rank) local rankInfo = getRankInfo(funcID, rank) if not rankInfo then return end local info = FUNC_RANK_TYPE_INFOS[funcRankType] return info and rankInfo[info.rankValueName] end local function getMailID(funcRankType, rankInfo) local info = FUNC_RANK_TYPE_INFOS[funcRankType] return info and rankInfo[info.mailIDName] end local function getRewardInfoByInfo(funcRankType, rankInfo) local info = FUNC_RANK_TYPE_INFOS[funcRankType] return info and rankInfo[info.rewardName] end local function getRank(funcID, funcRankType, uuid) local board = getBoard(funcID, funcRankType) return board.uuid2rank[uuid] end local function getSendRank(funcID, funcRankType, uuid) local board = getBoard(funcID, funcRankType) if not board then return end return board.sendUuid2rank[uuid] end local function cmpRank(data1, data2) if data1.value ~= data2.value then return data1.value > data2.value end return data1.time < data2.time end local function updateRank(board) if not board then return end -- 清空普通排名和发送排名玩家数据 for uuid in pairs(board.uuid2rank) do board.uuid2rank[uuid] = nil board.sendUuid2rank[uuid] = nil end -- 设置普通排名 for rank, data in ipairs(board.rank2data) do board.uuid2rank[data.uuid] = rank end -- 清空发送排名 for rank in ipairs(board.sendRank2data) do board.sendRank2data[rank] = nil end if not getActConfig(board.funcID) then return end local minRankValue = getMinRankValue(board.funcID, board.funcRankType)-- 排名最小需要的排名值 local aleadyRankCnt = 0 -- 已经排好的玩家数 local maxRank = getMaxRank(board.funcID) -- 最多排名玩家数量 for rank = 1, maxRank do local rankNeedValue = getRankNeedValue(board.funcID, board.funcRankType, rank) -- 取当前排名需要的排名值 local rank2data = board.rank2data[aleadyRankCnt + 1] -- 当前排的玩家 if rank2data then -- 还有玩家需要排 if rank2data.value >= rankNeedValue then -- 达到排名值,可以排名 board.sendUuid2rank[rank2data.uuid] = rank -- 设置玩家发送排名 board.sendRank2data[rank] = rank2data -- 设置发送排名数据 aleadyRankCnt = aleadyRankCnt + 1 -- 排名玩家数量+1 elseif rankNeedValue <= minRankValue then -- 未达到排名值 且 当前排名值 小于等于 最小需要的排名值了,则后面的玩家都不需要排了 break else -- 未到达最低排名值,当前名次未有玩家达到 board.sendRank2data[rank] = -1 end else -- 没玩家排了直接停止 break end end end local function updateData(data) local board = getBoard(data.funcID, data.funcRankType) if not board then return end local uuid = data.uuid local oldRank = getRank(data.funcID, data.funcRankType, uuid) or (#board.rank2data + 1) board.rank2data[oldRank] = data -- 向后比较(data.value 减少时),往排名低方向-> for i = oldRank + 1, #board.rank2data do local data1 = board.rank2data[i - 1] local data2 = board.rank2data[i] if cmpRank(data1, data2) then break end board.rank2data[i - 1] = data2 board.rank2data[i] = data1 end -- 向前比较(data.value 增加时),往排名高方向<- for i = oldRank - 1, 1, -1 do local data1 = board.rank2data[i] local data2 = board.rank2data[i + 1] if cmpRank(data1, data2) then break end board.rank2data[i] = data2 board.rank2data[i + 1] = data1 end local maxRank = getMaxRank(board.funcID) board.rank2data[maxRank + 1] = nil return true end function loadBoardData(absAct, funcID, uuid, now) local boardData = absAct.boardData if boardData == nil then --QueryForData.funcID = funcID --QueryForData.uuid = uuid else if absAct.endTime > now then for funcRankType, data in pairs(boardData) do local info = FUNC_RANK_TYPE_INFOS[funcRankType] if info.isDayReward then if data.time and Util.isSameDay(data.time) then initBoard(data.funcID, data.funcRankType) updateData(data) end else initBoard(data.funcID, data.funcRankType) updateData(data) end end end end end function loadBoardRank() for _, board in pairs(db) do updateRank(board) end end local function loadBoard() for _, board in pairs(db) do updateRank(board) end end local function getDBData(funcID, funcRankType, uuid) local boardDB = getBoard(funcID, funcRankType) local rank = boardDB.uuid2rank[uuid] local uidDB = boardDB.rank2data[rank] return uidDB end local function billboardSet(data) local board = getBoard(data.funcID, data.funcRankType) if not board then return end updateData(data) updateRank(board) end MAIL_SEND_CACHE = MAIL_SEND_CACHE or nil local function rewardSend(funcID, funcRankType, actId) local board = getBoard(funcID, funcRankType) if not board then return end local rankType = getRankType(funcID, funcRankType) MAIL_SEND_CACHE = MAIL_SEND_CACHE or {} MAIL_SEND_CACHE[rankType] = {} local mailID for rank, rankData in ipairs(board.sendRank2data) do if rankData and rankData ~= -1 then local rankInfo = getRankInfo(funcID, rank, actId) if rankInfo then MAIL_SEND_CACHE[rankType][rank] = rankData.uuid if not mailID then mailID = getMailID(funcRankType, rankInfo) end end end end resertBoard(board) Timer.addLater(10, boardReward, mailID, funcID, funcRankType, actId) -- 日志 Log.write(Log.LOGID_DEBUG, "AbsReachRank", rankType) end local function wrapRankRewardList(funcID, funcRankType, net, info) net.minRank = info.ranks[1] net.maxRank = info.ranks[2] local typeInfo = FUNC_RANK_TYPE_INFOS[funcRankType] local rankInfo = getRankInfo(funcID, info.ranks[1]) local rewardInfo = rankInfo[typeInfo.rewardName] local rankNeedValue = rankInfo[typeInfo.rankValueName] net.rankNeedValue = rankNeedValue local len = 0 for index,itemInfo in ipairs(rewardInfo) do len = len + 1 Grid.makeItem(net.items[index], itemInfo[1], itemInfo[2]) end net.items[0] = len end _init = false function init() if _G.is_middle == true then return end if not _init then _init = true --db = {} loadBoard() end end function getMaxSendRank(funcID, funcRankType) return MAX_SEND_RANK end function getMinRankValue(funcID, funcRankType) local actConfig = getActConfig(funcID) if not actConfig then return end local info = FUNC_RANK_TYPE_INFOS[funcRankType] return info and actConfig[info.minRankValueName] end function wrapRankList(funcID, funcRankType, net, rank) local board = getBoard(funcID, funcRankType) local sendRankData = board.sendRank2data[rank] net.rank = rank net.rankNeedValue = getRankNeedValue(funcID, funcRankType, rank) if sendRankData and sendRankData ~= -1 then local userDB = RoleDBLogic.getDb(sendRankData.uuid) net.uid = sendRankData.uuid net.name = userDB.name net.head = userDB.head net.rankValue = sendRankData.value net.headFrame = userDB.headFrame net.lv = userDB.lv else net.uid = "-1" net.name = "" net.head = -1 net.rankValue = 0 net.headFrame = -1 net.lv = -1 end local rewardInfo = getRewardInfo(funcID, rank, funcRankType) local len = 0 if rewardInfo then for index,itemInfo in ipairs(rewardInfo) do len = len + 1 Grid.makeItem(net.items[index], itemInfo[1], itemInfo[2]) end end net.items[0] = len end function wrapOwnerData(funcID, funcRankType, net, uuid) local actConfig = getActConfig(funcID) if not actConfig then return end local maxRank = actConfig.maxRank local sendRank = getSendRank(funcID, funcRankType, uuid) local board = getBoard(funcID, funcRankType) local rankData if sendRank then rankData = board.sendRank2data[sendRank] else local rank = getRank(funcID, funcRankType, uuid) rankData = rank and board.rank2data[rank] end net.rank = sendRank or -1 net.rankValue = rankData and rankData.value or 0 local len = 0 if sendRank then local rewardInfo = getRewardInfo(funcID, sendRank, funcRankType) for index,itemInfo in ipairs(rewardInfo) do len = len + 1 Grid.makeItem(net.items[index], itemInfo[1], itemInfo[2]) end end net.items[0] = len end function rewardQuery(human, funcID, funcRankType, msgRet) local actConfig = getActConfig(funcID) if not actConfig then return end msgRet.actId = funcID msgRet.funcRankType = funcRankType local len = 0 for _,info in ipairs(actConfig.configs) do len = len + 1 wrapRankRewardList(funcID, funcRankType, msgRet.list[len], info) end msgRet.list[0] = len Msg.send(msgRet, human.fd) end function onValueAdd(human, funcID, value) local actConfig = getActConfig(funcID) if not actConfig then return end AbsActLogic.checkAbsActClean(human, funcID) for funcRankType,info in ipairs(FUNC_RANK_TYPE_INFOS) do if getMinRankValue(funcID, funcRankType) then local rankType = getRankType(funcID, funcRankType) human.db.absAct[funcID].boardData = human.db.absAct[funcID].boardData or {} human.db.absAct[funcID].boardData[funcRankType] = human.db.absAct[funcID].boardData[funcRankType] or { rankType = rankType, uuid = human.db._id, funcID = funcID, funcRankType = funcRankType, } local dbData = human.db.absAct[funcID].boardData[funcRankType] dbData.value = (dbData.value or 0) + value dbData.time = os.time() billboardSet(dbData) end end end function onRewardSend(funcID) local funcConfig = AbsActExcel.absActivity[funcID] if not funcConfig then return end local actConfig = getActConfig(funcID) if not actConfig then return end local state = AbsActLogic.isStarted(human, funcID) local diffDay if not state then -- 活动结束 diffDay = Util.diffDay(funcConfig.realEndTime) if diffDay ~= 1 then return end end for funcRankType,info in ipairs(FUNC_RANK_TYPE_INFOS) do if getMinRankValue(funcID, funcRankType) then -- 判断此活动是否有指定功能类型 if info.isDayReward then -- 每天一结算 rewardSend(funcID, funcRankType, funcConfig.actId) elseif diffDay == 1 then -- 活动结束时跨天结算 rewardSend(funcID, funcRankType, funcConfig.actId) end end end end -- 定时器 邮件分开发,预防崩溃 function boardReward(mailID, funcID, funcRankType, actId) local rankType = getRankType(funcID, funcRankType) if mailID and MAIL_SEND_CACHE and MAIL_SEND_CACHE[rankType] then local mailConfig = MailExcel.mail[mailID] local title = mailConfig.title local senderName = mailConfig.senderName local content = mailConfig.content local mailSends = MAIL_SEND_CACHE[rankType] for rank,uuid in pairs(mailSends) do local rankInfo = getRankInfo(funcID, rank, actId) if rankInfo then local rewardInfo = getRewardInfoByInfo(funcRankType, rankInfo) if rewardInfo then MailManager.add(MailManager.SYSTEM, uuid, title, Util.format(content, rank), rewardInfo, senderName) end end end MAIL_SEND_CACHE[rankType] = nil end end function updateDaily(human, funcID) local funcConfig = AbsActExcel.absActivity[funcID] if not funcConfig then return end if not human.db.absAct[funcID] or not human.db.absAct[funcID].boardData then return end for funcRankType,info in ipairs(FUNC_RANK_TYPE_INFOS) do if info.isDayReward and human.db.absAct[funcID].boardData[funcRankType] then -- 每天一结算 human.db.absAct[funcID].boardData[funcRankType] = nil end end end --[[ function gmSendReward() print("send reward start") local funcID = 40006 local actID = 101 local now = os.time() local cnt = 0 LuaMongo.find(DB.db_char, nil, require("billboard.BillboardDB").fields) while true do local humanDb = {} if not LuaMongo.next(humanDb) then break end cnt = cnt + 1 if cnt % 1000 == 0 then _G.collectgarbage("step", 100000) end local absAct = humanDb.absAct[funcID] if absAct and absAct.boardData then for funcRankType, data in pairs(absAct.boardData) do local info = FUNC_RANK_TYPE_INFOS[funcRankType] if info.isDayReward then if data.time and Util.isSameDay(data.time) then initBoard(data.funcID, data.funcRankType) updateData(data) end else initBoard(data.funcID, data.funcRankType) updateData(data) end end end end for _, board in pairs(db) do if board then -- 清空普通排名和发送排名玩家数据 for uuid in pairs(board.uuid2rank) do board.uuid2rank[uuid] = nil board.sendUuid2rank[uuid] = nil end -- 设置普通排名 for rank, data in ipairs(board.rank2data) do board.uuid2rank[data.uuid] = rank end -- 清空发送排名 for rank in ipairs(board.sendRank2data) do board.sendRank2data[rank] = nil end if getActConfig(board.funcID, actID) then local minRankValue = 1 -- 排名最小需要的排名值 local aleadyRankCnt = 0 -- 已经排好的玩家数 local maxRank = getMaxRank(board.funcID) -- 最多排名玩家数量 for rank = 1, maxRank do local rankNeedValue if rank >= 1 and rank <= 3 then rankNeedValue = 500 elseif rank >= 4 and rank <= 20 then rankNeedValue = 100 else rankNeedValue = 1 end local rank2data = board.rank2data[aleadyRankCnt + 1] -- 当前排的玩家 if rank2data then -- 还有玩家需要排 if rank2data.value >= rankNeedValue then -- 达到排名值,可以排名 board.sendUuid2rank[rank2data.uuid] = rank -- 设置玩家发送排名 board.sendRank2data[rank] = rank2data -- 设置发送排名数据 aleadyRankCnt = aleadyRankCnt + 1 -- 排名玩家数量+1 elseif rankNeedValue <= minRankValue then -- 未达到排名值 且 当前排名值 小于等于 最小需要的排名值了,则后面的玩家都不需要排了 break else -- 未到达最低排名值,当前名次未有玩家达到 board.sendRank2data[rank] = -1 end else -- 没玩家排了直接停止 break end end end end end rewardSend(funcID, FUNC_RANK_TOTAL_TYPE, actID) print("send reward end") end --]]