123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- --节日排行榜服务
- local skynet = require "skynet"
- require "skynet.manager"
- local util = require "util"
- local logger = require "logger"
- local common_fun = require "model.common_fun"
- local stringify = require "stringify"
- local pipeline = require "pipeline"
- local redisdriver = require "skynet.db.redis"
- local asset = require "model.asset"
- local redis
- local usercenter
- local activitytime
- local mailbox
- local KEY_STR = "act_ranking:list:%d"
- local KEY_ACT_INFO = "act_ranking:act_info"
- local THIS_OPEN = 1 -- 开启
- local THIS_SYN = 2 -- 最后一次同步数据
- local THIS_AWARD = 3 -- 发奖
- local THIS_CLOSE = 4 -- 结束
- local cjson = require "cjson"
- local cjson_decode = cjson.decode -- 解码
- local cjson_encode = cjson.encode -- 编码
- local MAX_RANK = 300
- local SHOW_RANK = 100
- local compare = function(a, b)
- if a.num > b.num then
- return true
- end
- if a.num == b.num and a.num2 and b.num2 then
- return a.num2 > b.num2
- end
- return false
- end
- local function inserting(t, n, f)
- assert(type(t) == "table")
- local f = f or compare
- local size = #t
- local n = n or size + 1
- for i = 2, size do
- local num = math.min(i, n)
- if i > n and f(t[i], t[num]) then
- t[i], t[num] = t[num], t[i]
- end
- local temp = t[num]
- local j = num -1
- while j >= 1 and f(temp, t[j]) do
- t[j+1] = t[j]
- j = j - 1
- end
- t[j+1] = temp
- end
- -- if n < size then
- -- t[n+1] = nil
- -- end
- for i = n + 1, size do
- t[i] = nil
- end
- return t
- end
- local CMD = {}
- local ranklist = {
- }
- local showlist = {
- }
- -- 活动状态表
- local act_list = {
- --[[
- [id] = {
- state = 开启, 发奖, 结束
- list = { -- 参与人员
- [uid] = 1
- }
- }
- ]]
- }
- local function find_uid(list, uid)
- for k, info in ipairs(list or {})do
- if info.role.uid == uid then
- return k, info
- end
- end
- end
- local function init_act_info(id, state)
- return {
- id = id,
- state = state or THIS_OPEN,
- list = {}
- }
- end
- local function get_act_conf(id)
- return asset.ActivityConfig_proto[id]
- end
- local function save_act_info(id)
- if act_list[id] then
- redis:hset(KEY_ACT_INFO, id, cjson_encode(act_list[id]))
- else
- redis:hdel(KEY_ACT_INFO, id)
- end
- end
- local function send_award(id)
- local conf = assert(get_act_conf(id), id)
- local act_type = conf.type
- mailbox = mailbox or skynet.localname(".mailbox")
- local send_list = {-- 已发送奖励的玩家
- --[[
- [uid] = 1
- ]]
- }
- local join_list = (act_list[id] or {}).list or {}
- local rank_list = showlist[id] or {}
- local mail_type_1_100
- local mail_type_100_n
- local source = {
- module = "act_ranking",
- brief = "排行活动奖励",
- }
- if act_type == MODULE_ID_HERO_RANK then
- source.brief = "英雄排行奖励"
- mail_type_1_100 = 11
- mail_type_100_n = 15
- elseif act_type == MODULE_ID_SIMPLE_BATTLE_RANK then
- source.brief = "闯关排行奖励"
- mail_type_1_100 = 12
- mail_type_100_n = 16
- elseif act_type == MODULE_ID_POWER_RANK then
- source.brief = "战力排行奖励"
- mail_type_1_100 = 13
- mail_type_100_n = 17
- elseif act_type == MODULE_ID_ELITE_BATTLE_RANK then
- source.brief = "精英关卡排行奖励"
- mail_type_1_100 = 14
- mail_type_100_n = 18
- else
- assert(false, act_type)
- end
- for i=1, #conf.reward do
- local r1 = assert(conf.parameter1[i*2-1], i)
- local r2 = assert(conf.parameter1[i*2], i)
- local award_id = assert(conf.reward[i], i)
- local _, award_list = common_fun.get_award(award_id, "NewawardConfig_proto")
- for rank = r1, r2 do
- local rankinfo = rank_list[rank]
- if not rankinfo then
- break
- end
- if rank <= SHOW_RANK then
- if rankinfo.role and rankinfo.role.uid then
- local uid = rankinfo.role.uid
- source.context = string.format("第%s名奖励", rank)
- send_list[uid] = 1
- skynet.call(mailbox, "lua", "off_line_mail", {uid}, mail_type_1_100, rank, award_list, source)
- else
- logger.error("错误的玩家数据,%s,%s",id, rank)
- end
- else
- if rank_list[SHOW_RANK] then
- local uids = {}
- source.context = string.format("第%s名奖励", rank)
- for uid in pairs(join_list) do
- if not send_list[uid] then
- table.insert(uids, uid)
- end
- end
- if next(uids) then
- skynet.call(mailbox, "lua", "off_line_mail", uids, mail_type_100_n, "", award_list, source)
- end
- end
- break
- end
- end
- end
- end
- local function update()
- for act_id, list in pairs(ranklist) do
- inserting(list, MAX_RANK)
- showlist[act_id] = {}
- local key = string.format(KEY_STR, act_id)
- redis:del(key)
- local batch = pipeline(100)
- for rank, data in ipairs(list) do
- batch.add("hset", key, data.role.uid, cjson_encode(data))
- showlist[act_id][rank] = common_fun.scopy(data)
- end
- batch.execute(redis,{})
- end
- end
- local function check_over()
- local now = os.time()
- for id, data in pairs(act_list) do
- if data.state ~= THIS_CLOSE then
- activitytime = activitytime or skynet.localname(".activitytime")
- usercenter = usercenter or skynet.localname(".usercenter")
- local info = skynet.call(activitytime, "lua", "get_sid_info", id)
- local conf = get_act_conf(id)
- if info and conf then
- if data.state == THIS_OPEN then
- if now > info.endtime - conf.parameter3[1]*HOUR_SEC then
- data.state = THIS_SYN
- -- 通知在线玩家同步
- skynet.call(usercenter, "lua", "command", "syn_act_rank", conf.type)
- save_act_info(id)
- end
- elseif data.state == THIS_SYN then
- if now > info.endtime - conf.parameter3[1]*HOUR_SEC + 10 * MIN_SEC then
- -- 发奖
- local ok, msg = xpcall(send_award, debug.traceback, id)
- data.state = THIS_AWARD
- save_act_info(id)
- if not ok then
- logger.errno(msg)
- end
- end
- elseif data.state == THIS_AWARD then
- if now > info.endtime then
- data.state = THIS_CLOSE
- -- 清空数据
- save_act_info(id)
- end
- end
- else
- logger.error("活动排行,错误的数据info:%s, conf%s, id:%d", type(info), type(conf), id)
- end
- end
- end
- end
- local function init()
- local conf = assert(option.redis)
- redis = redisdriver.connect(conf)
- redis:select(12)
- local retnuma = redis:hgetall(KEY_ACT_INFO)
- for k = 1, #retnuma, 2 do
- local key = tonumber(retnuma[k])
- local data = cjson_decode(retnuma[k+1])
- if key and data then
- act_list[key] = data
- end
- end
- for act_id, info in pairs(act_list) do
- if info.state ~= THIS_CLOSE then
- ranklist[act_id] = {}
- local key = string.format(KEY_STR, act_id)
- local retnuma = redis:hgetall(key)
- for k = 1, #retnuma, 2 do
- local data = cjson_decode(retnuma[k+1])
- ranklist[act_id][#ranklist[act_id]+1] = data
- end
-
- end
- end
- end
- local function watchtime()
- skynet.fork(function()
- local interval = 60*2 -- 更新间隔为2分钟
- local up_time = os.time()
- while(true) do
- local now = os.time()
- if now > up_time then
- up_time = now + interval
- update()
- check_over()
- end
- skynet.sleep(300)
- end
- end)
- end
- function CMD.player_rename(uid, name)
- for _, list in pairs(ranklist) do
- local _, info = find_uid(list, uid)
- if info then
- info.role.nickname = name
- end
- end
- end
- function CMD.upload(act_id, data)
- if not act_list[act_id] then
- activitytime = activitytime or skynet.localname(".activitytime")
- local info = skynet.call(activitytime, "lua", "get_sid_info", act_id)
- if not info then
- logger.error("活动排行 未找到活动数据:%s", act_id)
- return
- end
- if info.state == STATE_OPEN then
- act_list[act_id] = init_act_info(act_id)
- else
- act_list[act_id] = init_act_info(act_id, THIS_CLOSE)
- end
- save_act_info(act_id)
- end
- if act_list[act_id].state ~= THIS_OPEN and act_list[act_id].state ~= THIS_SYN then
- return
- end
- act_list[act_id].list = act_list[act_id].list or {}
- if not act_list[act_id].list[data.role.uid] then
- act_list[act_id].list[data.role.uid] = 1
- save_act_info(act_id)
- end
- local key = string.format(KEY_STR, act_id)
- ranklist[act_id] = ranklist[act_id] or {}
- local list = ranklist[act_id]
- local k = find_uid(list, data.role.uid)
- if k then
- list[k] = data
- redis:hset(key, data.role.uid, cjson_encode(data))
- return
- end
- local show = showlist[act_id] or {}
- if show[MAX_RANK] and show[MAX_RANK].num > data.num then
- return
- end
- list[#list+1] = data
- redis:hset(key, data.role.uid, cjson_encode(data))
- end
- function CMD.get_ranking(act_id)
- logger.trace("showlist"..stringify(showlist))
- return showlist[act_id] or {}
- end
- -- 从排行榜中删除玩家
- function CMD.del(uid)
- for act_id, list in pairs(showlist) do
- local act_info = act_list[act_id]
- if not act_info or (act_info.state == THIS_AWARD or act_info.state == THIS_SYN) then
- local k = find_uid(list, uid)
- if k then
- table.remove(list, k)
- end
- end
- end
- for act_id, list in pairs(ranklist) do
- local act_info = act_list[act_id]
- if not act_info or (act_info.state == THIS_AWARD or act_info.state == THIS_SYN) then
- local key = string.format(KEY_STR, act_id)
- local k = find_uid(list, uid)
- if k then
- table.remove(list, k)
- redis:hdel(key, uid)
- end
- end
- end
- end
- function CMD.start()
- init()
- watchtime()
- end
- skynet.init(function()
- skynet.register(".act_rankinglist")
- end)
- skynet.start(function()
- skynet.dispatch("lua", function(session, _, cmd, ...)
- local f = assert(CMD[cmd])
- if 0==session then
- f(...)
- else
- skynet.retpack(f(...))
- end
- end)
- end)
|