activitytime.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. --活动时间服务
  2. local skynet = require "skynet"
  3. require "skynet.manager"
  4. local redisdriver = require "skynet.db.redis"
  5. local logger = require "logger"
  6. local common_fun = require "model.common_fun"
  7. local util = require "util"
  8. local asset = require "model.asset"
  9. local cjson = require "cjson"
  10. local stringify = require "stringify"
  11. local queue = require "skynet.queue"
  12. local trace = logger.trace
  13. --local trace = function(...) end
  14. local string_format = string.format
  15. local skynet_retpack = skynet.retpack
  16. local synchronized = queue()
  17. cjson.encode_sparse_array(true, 1)
  18. local STATE_OPEN = 0 -- 活动开启
  19. local STATE_CLOSE = 1 -- 活动结束
  20. local CMD = {}
  21. local activity = {}
  22. local THIS = {}
  23. local finishtime = { --记录已经开启活动,或者即将开启的活动
  24. --[[
  25. [activ_tyep] = {
  26. -- 活动信息
  27. }
  28. ]]
  29. }
  30. local recent_finishtime = { -- 开启/待开启/结束 (先筛选开启中 - 筛选待开启 - 筛选以结束)
  31. }
  32. local all_activity_time = {} -- 所有活动的数据 [sid] = {}
  33. local usercenter --玩家服务
  34. local redis --数据库
  35. local KEY_FORMAT = "activity:%s"
  36. local globaltime --开服时间
  37. local TEN_YEAR -- 开服十年后的时间节点
  38. local cache_acti = { -- 内存活动数据 配置表转发而来
  39. --[[
  40. [id] = {
  41. 活动信息
  42. }
  43. ]]
  44. }
  45. local IMM_SEND = false -- 立即推送活动数据
  46. local calculate = {}
  47. -- TODO: 活动时间类型 1
  48. --[[
  49. opentype = 1 永久活动 直接开启十年
  50. ]]
  51. calculate[1] = function(aconfig, t_type, act_info)
  52. act_info[1] = STATE_OPEN
  53. act_info[2] = globaltime
  54. act_info[3] = TEN_YEAR
  55. end
  56. -- TODO: 活动类型 2
  57. --[[
  58. opentype = 2 限时活动
  59. ]]
  60. calculate[2] = function(aconfig, t_type, act_info, now)
  61. local opentime = common_fun.split(aconfig.starttime)
  62. local endtime = common_fun.split(aconfig.endtime)
  63. if now >= opentime and now < endtime then
  64. act_info[1] = STATE_OPEN
  65. else
  66. act_info[1] = STATE_CLOSE
  67. end
  68. act_info[2] = opentime -- 活动开启时间
  69. act_info[3] = endtime -- 活动结束时间
  70. end
  71. function THIS.split_unknown(str)
  72. local ret = {}
  73. for t in string.gmatch(str, "(%d+)%/?") do
  74. table.insert(ret, 1, tonumber(t))
  75. end
  76. return ret
  77. end
  78. local function math_type6(tab)
  79. return tab[1]+ (tab[2] or 0)*MIN_SEC + (tab[3] or 0) * HOUR_SEC + ((tab[4] or 0)-1) *DAY_SEC
  80. end
  81. -- TODO: 活动类型 3
  82. --[[
  83. opentype = 3 开服活动
  84. ]]
  85. calculate[3] = function(aconfig, t_type, act_info, now)
  86. local list1 = THIS.split_unknown(aconfig.starttime) -- 活动开启配置
  87. local list2 = THIS.split_unknown(aconfig.endtime) -- 活动结束配置
  88. local opentime = globaltime + math_type6(list1)
  89. local endtime = globaltime + math_type6(list2)
  90. if now >= opentime and now < endtime then
  91. act_info[1] = STATE_OPEN
  92. else
  93. act_info[1] = STATE_CLOSE
  94. end
  95. act_info[2] = opentime -- 活动开启时间
  96. act_info[3] = endtime -- 活动结束时间
  97. end
  98. -- TODO: 计算活动开启时间, 及活动现在状态
  99. local function calculate_time(aconfig)
  100. local t_type = aconfig.opentype -- 活动开启类型
  101. -- globaltime -- 开服时间
  102. local now = os.time()
  103. local act_info = {
  104. [1] = STATE_CLOSE, -- 活动状态
  105. [2] = 0, -- 活动开启时间
  106. [3] = 0, -- 活动结束时间
  107. }
  108. calculate[t_type](aconfig, t_type, act_info, now)
  109. logger.trace(" id = %s, type = %s, opentype = %s, state = %s, open = %s, end = %s",
  110. aconfig.ID, aconfig.type, t_type, act_info[1], os.date("%Y-%m-%d %H:%M:%S",tonumber(act_info[2])),
  111. os.date("%Y-%m-%d %H:%M:%S",tonumber(act_info[3]))
  112. )
  113. return table.unpack(act_info)
  114. end
  115. -- TODO: 活动状态发生变化,推送活动数据
  116. function activity.send_active_info()
  117. --通知在线的玩家有活动的状态发生了变更
  118. skynet.send(usercenter, "lua", "command", "update", finishtime)--向服务器推送
  119. trace("限时活动时间new_finishtime:%s",stringify(finishtime))
  120. end
  121. -- TODO: 热更接口
  122. function CMD.hotfix(list)
  123. if list.ActivityConfig_proto then
  124. synchronized(function()
  125. logger.trace(" ========== 热更活动配置表")
  126. -- 读取配置,活动时间数据
  127. local temp = activity.load_conf()
  128. cache_acti = temp
  129. IMM_SEND = true
  130. end)
  131. end
  132. end
  133. -- TODO: 检查有活动状态, 并返回已经开启的活动 及 是否有活动状态变化
  134. function THIS.check_active_state()
  135. local open_act = {} -- 开启中得活动
  136. local finsi_act = {} -- 结束了得活动
  137. local recent_act = {} -- 待开启得活动
  138. local now = os.time()
  139. local nstate
  140. local change = false
  141. for sid, ainfo in pairs(cache_acti) do
  142. if ainfo.opentime <= now and ainfo.endtime > now then
  143. nstate = STATE_OPEN
  144. if open_act[ainfo.activity_type] then
  145. if ainfo.sid < open_act[ainfo.activity_type].sid then
  146. open_act[ainfo.activity_type] = ainfo
  147. change = true
  148. end
  149. else
  150. open_act[ainfo.activity_type] = ainfo
  151. end
  152. else
  153. nstate = STATE_CLOSE
  154. end
  155. if now < ainfo.opentime then -- 待开启活动
  156. if recent_act[ainfo.activity_type] then
  157. if recent_act[ainfo.activity_type].opentime > ainfo.opentime then -- 取时间小得
  158. recent_act[ainfo.activity_type] = ainfo
  159. end
  160. else
  161. recent_act[ainfo.activity_type] = ainfo
  162. end
  163. end
  164. if now >= ainfo.endtime then -- 已经结束得活动
  165. if finsi_act[ainfo.activity_type] then
  166. if finsi_act[ainfo.activity_type].endtime < ainfo.endtime then -- 取时间大得
  167. finsi_act[ainfo.activity_type] = ainfo
  168. end
  169. else
  170. finsi_act[ainfo.activity_type] = ainfo
  171. end
  172. end
  173. -- 检查状态是否变化
  174. if nstate ~= ainfo.state then
  175. logger.trace(" -- 活动状态改变 sid = %s, ainfo = %s", sid, stringify(ainfo))
  176. change = true
  177. ainfo.state = nstate
  178. end
  179. end
  180. return open_act, finsi_act, recent_act, change
  181. end
  182. --创建检查活动配置时间的协程
  183. function activity.detection()
  184. skynet.fork(function()
  185. local change, finsi_act, recent_act, recent_temp
  186. -- 处理 最近活动数据
  187. local dis_recent = function(tt, act)
  188. for sid, v in pairs(act) do
  189. if not tt[sid] then
  190. tt[sid] = v
  191. end
  192. end
  193. end
  194. while true do
  195. synchronized(function()
  196. finishtime, finsi_act, recent_act, change = THIS.check_active_state()
  197. -- 有活动发生了变化
  198. -- logger.trace(" ===============finishtime = %s", stringify(finishtime))
  199. recent_temp = {}
  200. dis_recent(recent_temp, finishtime)
  201. dis_recent(recent_temp, finsi_act)
  202. dis_recent(recent_temp, recent_act)
  203. recent_finishtime = recent_temp
  204. if change or IMM_SEND then
  205. activity.send_active_info()
  206. IMM_SEND = false
  207. end
  208. redis:set("activity:activity_time", cjson.encode(finishtime))
  209. end)
  210. skynet.sleep(100)
  211. end
  212. end)
  213. end
  214. --获得限时活动指定的时间
  215. function CMD.getassigntime(name)
  216. return synchronized(function()
  217. return finishtime[name]--state0是关闭1是开启
  218. end)
  219. end
  220. --玩家上线的时候获取一次所有限时活动的时间
  221. function CMD.getactivitytime()
  222. return synchronized(function()
  223. return finishtime
  224. end)
  225. end
  226. -- 获取对接活动数据
  227. function CMD.get_recent_activitytime()
  228. return synchronized(function()
  229. return recent_finishtime
  230. end)
  231. end
  232. -- 获取对接活动数据
  233. function CMD.get_recent_assigntime(name)
  234. return synchronized(function()
  235. return recent_finishtime[name]
  236. end)
  237. end
  238. function CMD.get_sid_info(sid)
  239. return all_activity_time[sid]
  240. end
  241. -- TODO: 准备-加载数据库数据
  242. function activity.prepare()
  243. local key = string_format(KEY_FORMAT, "global")
  244. local res = redis:hget(key, "starttime")
  245. if res then
  246. globaltime = util.today(tonumber(res)+HOUR_SEC) -- 防止冬夏令时导致的时间异常
  247. else
  248. res = util.today()
  249. globaltime = res
  250. redis:hmset(key, "starttime", res)
  251. end
  252. end
  253. function CMD.query()
  254. return globaltime
  255. end
  256. function CMD.open_day()
  257. return math.max(1, (util.today() - globaltime)/DAY_SEC + 1)
  258. end
  259. -----------------------------------------------------------------------
  260. -- TODO: 筛选本服得活动
  261. function activity.filtrate_activity()
  262. local ret = {}
  263. local act_conf = asset.ActivityConfig_proto
  264. for sid, info in pairs(act_conf) do
  265. ret[sid] = common_fun.scopy(info)
  266. end
  267. return ret
  268. end
  269. -- TODO: 加载活动配置数据,并生成时间节点 及活动状态
  270. function activity.load_conf()
  271. local act_conf = activity.filtrate_activity()
  272. local temp_act = {} -- 配置表转发活动数据结构, 临时存储
  273. for sid, aconfig in pairs(act_conf) do
  274. local astate, aopne_tm, aend_tm = calculate_time(aconfig)
  275. temp_act[sid] = {
  276. sid = sid, -- 活动sid
  277. state = astate, -- 活动状态
  278. switch = aconfig.switch, -- 临时关闭标记
  279. activity_type = aconfig.type, -- 活动类型
  280. name = aconfig.name, -- 活动名字
  281. opentime = aopne_tm,
  282. endtime = aend_tm,
  283. sort = aconfig.sort,
  284. icon = aconfig.icon -- 活动icon
  285. }
  286. all_activity_time[sid] = temp_act[sid]
  287. end
  288. return temp_act
  289. end
  290. function CMD.start()
  291. usercenter = usercenter or skynet.localname(".usercenter")
  292. local conf = assert(option.redis)
  293. redis = redisdriver.connect(conf)
  294. redis:select(1)--切换到数据库db1
  295. activity.prepare()
  296. TEN_YEAR = globaltime + DAY_SEC*365*10
  297. -- 读取配置,活动时间数据
  298. cache_acti = activity.load_conf()
  299. IMM_SEND = true
  300. activity.detection()--检查时间
  301. -- 冲关
  302. -- skynet.fork(function()
  303. -- skynet.sleep(300)
  304. -- CMD.hotfix()
  305. -- end)
  306. end
  307. skynet.info_func(function()
  308. return {
  309. open_activie = finishtime, -- 已经开启的活动关
  310. cache_acti = cache_acti, -- 所有活动情况
  311. }
  312. end)
  313. skynet.init(function()
  314. skynet.register(".activitytime")
  315. end)
  316. skynet.start(function()
  317. skynet.dispatch("lua", function(session, _, cmd, ...)
  318. local f = assert(CMD[cmd])
  319. if session == 0 then
  320. f(...)
  321. else
  322. skynet_retpack(f(...))
  323. end
  324. end)
  325. end)