act_rankinglist.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. --节日排行榜服务
  2. local skynet = require "skynet"
  3. require "skynet.manager"
  4. local util = require "util"
  5. local logger = require "logger"
  6. local common_fun = require "model.common_fun"
  7. local stringify = require "stringify"
  8. local pipeline = require "pipeline"
  9. local redisdriver = require "skynet.db.redis"
  10. local asset = require "model.asset"
  11. local redis
  12. local usercenter
  13. local activitytime
  14. local mailbox
  15. local KEY_STR = "act_ranking:list:%d"
  16. local KEY_ACT_INFO = "act_ranking:act_info"
  17. local THIS_OPEN = 1 -- 开启
  18. local THIS_SYN = 2 -- 最后一次同步数据
  19. local THIS_AWARD = 3 -- 发奖
  20. local THIS_CLOSE = 4 -- 结束
  21. local cjson = require "cjson"
  22. local cjson_decode = cjson.decode -- 解码
  23. local cjson_encode = cjson.encode -- 编码
  24. local MAX_RANK = 300
  25. local SHOW_RANK = 100
  26. local compare = function(a, b)
  27. if a.num > b.num then
  28. return true
  29. end
  30. if a.num == b.num and a.num2 and b.num2 then
  31. return a.num2 > b.num2
  32. end
  33. return false
  34. end
  35. local function inserting(t, n, f)
  36. assert(type(t) == "table")
  37. local f = f or compare
  38. local size = #t
  39. local n = n or size + 1
  40. for i = 2, size do
  41. local num = math.min(i, n)
  42. if i > n and f(t[i], t[num]) then
  43. t[i], t[num] = t[num], t[i]
  44. end
  45. local temp = t[num]
  46. local j = num -1
  47. while j >= 1 and f(temp, t[j]) do
  48. t[j+1] = t[j]
  49. j = j - 1
  50. end
  51. t[j+1] = temp
  52. end
  53. -- if n < size then
  54. -- t[n+1] = nil
  55. -- end
  56. for i = n + 1, size do
  57. t[i] = nil
  58. end
  59. return t
  60. end
  61. local CMD = {}
  62. local ranklist = {
  63. }
  64. local showlist = {
  65. }
  66. -- 活动状态表
  67. local act_list = {
  68. --[[
  69. [id] = {
  70. state = 开启, 发奖, 结束
  71. list = { -- 参与人员
  72. [uid] = 1
  73. }
  74. }
  75. ]]
  76. }
  77. local function find_uid(list, uid)
  78. for k, info in ipairs(list or {})do
  79. if info.role.uid == uid then
  80. return k, info
  81. end
  82. end
  83. end
  84. local function init_act_info(id, state)
  85. return {
  86. id = id,
  87. state = state or THIS_OPEN,
  88. list = {}
  89. }
  90. end
  91. local function get_act_conf(id)
  92. return asset.ActivityConfig_proto[id]
  93. end
  94. local function save_act_info(id)
  95. if act_list[id] then
  96. redis:hset(KEY_ACT_INFO, id, cjson_encode(act_list[id]))
  97. else
  98. redis:hdel(KEY_ACT_INFO, id)
  99. end
  100. end
  101. local function send_award(id)
  102. local conf = assert(get_act_conf(id), id)
  103. local act_type = conf.type
  104. mailbox = mailbox or skynet.localname(".mailbox")
  105. local send_list = {-- 已发送奖励的玩家
  106. --[[
  107. [uid] = 1
  108. ]]
  109. }
  110. local join_list = (act_list[id] or {}).list or {}
  111. local rank_list = showlist[id] or {}
  112. local mail_type_1_100
  113. local mail_type_100_n
  114. local source = {
  115. module = "act_ranking",
  116. brief = "排行活动奖励",
  117. }
  118. if act_type == MODULE_ID_HERO_RANK then
  119. source.brief = "英雄排行奖励"
  120. mail_type_1_100 = 11
  121. mail_type_100_n = 15
  122. elseif act_type == MODULE_ID_SIMPLE_BATTLE_RANK then
  123. source.brief = "闯关排行奖励"
  124. mail_type_1_100 = 12
  125. mail_type_100_n = 16
  126. elseif act_type == MODULE_ID_POWER_RANK then
  127. source.brief = "战力排行奖励"
  128. mail_type_1_100 = 13
  129. mail_type_100_n = 17
  130. elseif act_type == MODULE_ID_ELITE_BATTLE_RANK then
  131. source.brief = "精英关卡排行奖励"
  132. mail_type_1_100 = 14
  133. mail_type_100_n = 18
  134. else
  135. assert(false, act_type)
  136. end
  137. for i=1, #conf.reward do
  138. local r1 = assert(conf.parameter1[i*2-1], i)
  139. local r2 = assert(conf.parameter1[i*2], i)
  140. local award_id = assert(conf.reward[i], i)
  141. local _, award_list = common_fun.get_award(award_id, "NewawardConfig_proto")
  142. for rank = r1, r2 do
  143. local rankinfo = rank_list[rank]
  144. if not rankinfo then
  145. break
  146. end
  147. if rank <= SHOW_RANK then
  148. if rankinfo.role and rankinfo.role.uid then
  149. local uid = rankinfo.role.uid
  150. source.context = string.format("第%s名奖励", rank)
  151. send_list[uid] = 1
  152. skynet.call(mailbox, "lua", "off_line_mail", {uid}, mail_type_1_100, rank, award_list, source)
  153. else
  154. logger.error("错误的玩家数据,%s,%s",id, rank)
  155. end
  156. else
  157. if rank_list[SHOW_RANK] then
  158. local uids = {}
  159. source.context = string.format("第%s名奖励", rank)
  160. for uid in pairs(join_list) do
  161. if not send_list[uid] then
  162. table.insert(uids, uid)
  163. end
  164. end
  165. if next(uids) then
  166. skynet.call(mailbox, "lua", "off_line_mail", uids, mail_type_100_n, "", award_list, source)
  167. end
  168. end
  169. break
  170. end
  171. end
  172. end
  173. end
  174. local function update()
  175. for act_id, list in pairs(ranklist) do
  176. inserting(list, MAX_RANK)
  177. showlist[act_id] = {}
  178. local key = string.format(KEY_STR, act_id)
  179. redis:del(key)
  180. local batch = pipeline(100)
  181. for rank, data in ipairs(list) do
  182. batch.add("hset", key, data.role.uid, cjson_encode(data))
  183. showlist[act_id][rank] = common_fun.scopy(data)
  184. end
  185. batch.execute(redis,{})
  186. end
  187. end
  188. local function check_over()
  189. local now = os.time()
  190. for id, data in pairs(act_list) do
  191. if data.state ~= THIS_CLOSE then
  192. activitytime = activitytime or skynet.localname(".activitytime")
  193. usercenter = usercenter or skynet.localname(".usercenter")
  194. local info = skynet.call(activitytime, "lua", "get_sid_info", id)
  195. local conf = get_act_conf(id)
  196. if info and conf then
  197. if data.state == THIS_OPEN then
  198. if now > info.endtime - conf.parameter3[1]*HOUR_SEC then
  199. data.state = THIS_SYN
  200. -- 通知在线玩家同步
  201. skynet.call(usercenter, "lua", "command", "syn_act_rank", conf.type)
  202. save_act_info(id)
  203. end
  204. elseif data.state == THIS_SYN then
  205. if now > info.endtime - conf.parameter3[1]*HOUR_SEC + 10 * MIN_SEC then
  206. -- 发奖
  207. local ok, msg = xpcall(send_award, debug.traceback, id)
  208. data.state = THIS_AWARD
  209. save_act_info(id)
  210. if not ok then
  211. logger.errno(msg)
  212. end
  213. end
  214. elseif data.state == THIS_AWARD then
  215. if now > info.endtime then
  216. data.state = THIS_CLOSE
  217. -- 清空数据
  218. save_act_info(id)
  219. end
  220. end
  221. else
  222. logger.error("活动排行,错误的数据info:%s, conf%s, id:%d", type(info), type(conf), id)
  223. end
  224. end
  225. end
  226. end
  227. local function init()
  228. local conf = assert(option.redis)
  229. redis = redisdriver.connect(conf)
  230. redis:select(12)
  231. local retnuma = redis:hgetall(KEY_ACT_INFO)
  232. for k = 1, #retnuma, 2 do
  233. local key = tonumber(retnuma[k])
  234. local data = cjson_decode(retnuma[k+1])
  235. if key and data then
  236. act_list[key] = data
  237. end
  238. end
  239. for act_id, info in pairs(act_list) do
  240. if info.state ~= THIS_CLOSE then
  241. ranklist[act_id] = {}
  242. local key = string.format(KEY_STR, act_id)
  243. local retnuma = redis:hgetall(key)
  244. for k = 1, #retnuma, 2 do
  245. local data = cjson_decode(retnuma[k+1])
  246. ranklist[act_id][#ranklist[act_id]+1] = data
  247. end
  248. end
  249. end
  250. end
  251. local function watchtime()
  252. skynet.fork(function()
  253. local interval = 60*2 -- 更新间隔为2分钟
  254. local up_time = os.time()
  255. while(true) do
  256. local now = os.time()
  257. if now > up_time then
  258. up_time = now + interval
  259. update()
  260. check_over()
  261. end
  262. skynet.sleep(300)
  263. end
  264. end)
  265. end
  266. function CMD.player_rename(uid, name)
  267. for _, list in pairs(ranklist) do
  268. local _, info = find_uid(list, uid)
  269. if info then
  270. info.role.nickname = name
  271. end
  272. end
  273. end
  274. function CMD.upload(act_id, data)
  275. if not act_list[act_id] then
  276. activitytime = activitytime or skynet.localname(".activitytime")
  277. local info = skynet.call(activitytime, "lua", "get_sid_info", act_id)
  278. if not info then
  279. logger.error("活动排行 未找到活动数据:%s", act_id)
  280. return
  281. end
  282. if info.state == STATE_OPEN then
  283. act_list[act_id] = init_act_info(act_id)
  284. else
  285. act_list[act_id] = init_act_info(act_id, THIS_CLOSE)
  286. end
  287. save_act_info(act_id)
  288. end
  289. if act_list[act_id].state ~= THIS_OPEN and act_list[act_id].state ~= THIS_SYN then
  290. return
  291. end
  292. act_list[act_id].list = act_list[act_id].list or {}
  293. if not act_list[act_id].list[data.role.uid] then
  294. act_list[act_id].list[data.role.uid] = 1
  295. save_act_info(act_id)
  296. end
  297. local key = string.format(KEY_STR, act_id)
  298. ranklist[act_id] = ranklist[act_id] or {}
  299. local list = ranklist[act_id]
  300. local k = find_uid(list, data.role.uid)
  301. if k then
  302. list[k] = data
  303. redis:hset(key, data.role.uid, cjson_encode(data))
  304. return
  305. end
  306. local show = showlist[act_id] or {}
  307. if show[MAX_RANK] and show[MAX_RANK].num > data.num then
  308. return
  309. end
  310. list[#list+1] = data
  311. redis:hset(key, data.role.uid, cjson_encode(data))
  312. end
  313. function CMD.get_ranking(act_id)
  314. logger.trace("showlist"..stringify(showlist))
  315. return showlist[act_id] or {}
  316. end
  317. -- 从排行榜中删除玩家
  318. function CMD.del(uid)
  319. for act_id, list in pairs(showlist) do
  320. local act_info = act_list[act_id]
  321. if not act_info or (act_info.state == THIS_AWARD or act_info.state == THIS_SYN) then
  322. local k = find_uid(list, uid)
  323. if k then
  324. table.remove(list, k)
  325. end
  326. end
  327. end
  328. for act_id, list in pairs(ranklist) do
  329. local act_info = act_list[act_id]
  330. if not act_info or (act_info.state == THIS_AWARD or act_info.state == THIS_SYN) then
  331. local key = string.format(KEY_STR, act_id)
  332. local k = find_uid(list, uid)
  333. if k then
  334. table.remove(list, k)
  335. redis:hdel(key, uid)
  336. end
  337. end
  338. end
  339. end
  340. function CMD.start()
  341. init()
  342. watchtime()
  343. end
  344. skynet.init(function()
  345. skynet.register(".act_rankinglist")
  346. end)
  347. skynet.start(function()
  348. skynet.dispatch("lua", function(session, _, cmd, ...)
  349. local f = assert(CMD[cmd])
  350. if 0==session then
  351. f(...)
  352. else
  353. skynet.retpack(f(...))
  354. end
  355. end)
  356. end)