mailbox.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. local skynet = require "skynet"
  2. local schema = require "model.schema"
  3. local logger = require "logger"
  4. local keygen = require "model.keygen"
  5. local cjson = require "cjson"
  6. local reward = require "model.reward"
  7. local util = require "util"
  8. local asset = require "model.asset"
  9. local log = require "model.log"
  10. local common_fun = require "model.common_fun"
  11. local equip
  12. local hero
  13. cjson.encode_sparse_array(true, 1)
  14. local s_mailbox = skynet.localname(".mailbox")
  15. local cjson_decode = cjson.decode -- 解码
  16. local cjson_encode = cjson.encode -- 编码
  17. local CMD = {}
  18. local REQUEST = {}
  19. local mailbox = {}
  20. local TYPE_TAB = 'table'
  21. local _M = schema.new('mail', {
  22. tm = 0, -- 玩家最近全服邮件收取时间
  23. list = {
  24. --[[
  25. [id] = {
  26. read = false, 是否已读
  27. attach = true, 是否有附件
  28. receive = true, 是否已领取
  29. }
  30. ]]
  31. }
  32. })
  33. function mailbox.list_request_interests() return REQUEST end
  34. function mailbox.list_command_interests() return CMD end
  35. --
  36. function mailbox.parse(character)
  37. local d = _M.load(character)
  38. if not d.list then
  39. d.list = {}
  40. _M.persist(character)
  41. end
  42. end
  43. local function optimize_array(a)
  44. if a and next(a) then
  45. return a
  46. end
  47. end
  48. ------------------------------ mailbox -------------------------------------
  49. -- 更新全局邮件收取时间
  50. local function update_tm(character)
  51. local data = _M.assert_get(character)
  52. data.tm = os.time()
  53. _M.persist(character)
  54. end
  55. local function update_state(character, id, attachment)
  56. local d = _M.assert_get(character)
  57. d.list[id] = {
  58. attach = next(attachment or {}) and true or false,
  59. read = false,
  60. receive = false,
  61. }
  62. _M.persist(character)
  63. return d.list[id]
  64. end
  65. -- 取出服务器中的所有邮件
  66. local function get_ser_mail(character)
  67. local data = _M.assert_get(character)
  68. local tm = os.time()
  69. data.tm = (data.tm == 0) and tm or data.tm
  70. _M.persist(character)
  71. return skynet.call(s_mailbox, "lua", "get_all_mail", data.tm, character.uid)
  72. end
  73. -- 检查是否能添加成功
  74. local function check_bag(attachment, bag_residue)
  75. for k, v in pairs(attachment) do
  76. local id = v.id
  77. local num = v.num
  78. local item_type = common_fun.goods_type(id)
  79. if item_type == GOODS_HERO then
  80. bag_residue.hero = (bag_residue.hero or 0) - num
  81. if bag_residue.hero <= 0 then
  82. return STD_ERR.HERO_FULL
  83. end
  84. elseif item_type == GOODS_EQUIP then
  85. bag_residue.equip = (bag_residue.equip or 0) - num
  86. if bag_residue.equip <= 0 then
  87. return STD_ERR.EQUIP_FULL
  88. end
  89. end
  90. end
  91. return 0
  92. end
  93. local function will_send_award(attachment, detail)
  94. for k, v in pairs(attachment) do
  95. table.insert(detail, v)
  96. end
  97. end
  98. local function tidy_award(character, list, ret_id, detail, log)
  99. local bag_residue = { -- 剩余空间
  100. hero = hero.hero_surplus(character),
  101. equip = equip.equip_surplus(character),
  102. }
  103. local sign, temp
  104. for _, v in pairs(list) do
  105. temp = cjson_decode(v)
  106. temp.source = temp.source or {}
  107. sign = check_bag(temp.attachment, bag_residue)
  108. if temp.attachment and sign == 0 then
  109. will_send_award(temp.attachment, detail)
  110. table.insert(log, {
  111. module = temp.source.module,
  112. brief = temp.source.brief,
  113. context = temp.source.context,
  114. detail = temp.attachment or {},
  115. })
  116. table.insert(ret_id, temp.id)
  117. elseif not temp.attachment then
  118. -- return -6 -- 邮件的附件为空
  119. table.insert(ret_id, temp.id)
  120. else
  121. return sign
  122. end
  123. end
  124. end
  125. local function send_log(character, temp_log)
  126. skynet.fork(function()
  127. for _, data in pairs(temp_log) do
  128. local record = {
  129. currency = data.detail.currency,
  130. }
  131. if next(record) then
  132. log.reward(character, data.module, data.brief, data.context, record)
  133. end
  134. end
  135. end)
  136. end
  137. ------------------------------ mailbox -------------------------------------
  138. --[[
  139. 统一邮件发送接口
  140. number subject: 邮件的主题, 使用参数表的 mail_proto[k].type
  141. string body: 邮件需要的参数,多参数使用 “ ,” 分割 , 无参数,为 ""
  142. table attachment: 邮件的附件,格式按照物品添加格式处理
  143. table source: 邮件的来源{
  144. module, -- 发送模块
  145. brief, -- 说明
  146. context, -- 上下文
  147. }
  148. ]]
  149. function mailbox.send(character, subject, body, attachment, source, must_mail)
  150. local id = keygen()
  151. local now = os.time()
  152. local content = {
  153. id = id,
  154. date = now,
  155. subject = subject,
  156. body = body,
  157. attachment = attachment or {},
  158. source = source,
  159. }
  160. skynet.call(s_mailbox,"lua","save_mail", id, cjson_encode(content), character.uid, must_mail)
  161. local data = update_state(character, id, attachment)
  162. content.read = data.read
  163. content.attach = data.attach
  164. content.receive = data.receive
  165. if character.send then
  166. content.source = nil
  167. character.send('send_mail', {list = {[1] = content}})
  168. end
  169. end
  170. function mailbox.ready(character)
  171. local data = _M.assert_get(character)
  172. if type(data) ~= TYPE_TAB then
  173. assert(false,string.format("邮件系统取出玩家数据失败 %s", character.uid))
  174. end
  175. -- 玩家上线将自己的地址保存到服务器
  176. skynet.call(s_mailbox,"lua","online", skynet.self(), character.uid, (data.tm == 0) and os.time() or data.tm)
  177. update_tm(character)
  178. mailbox.noread_mail(character)
  179. end
  180. function mailbox.saybye(character)
  181. -- TODO: 玩家下线时的处理
  182. update_tm(character)
  183. skynet.call(s_mailbox,"lua","offline", skynet.self(), character.uid)
  184. end
  185. function mailbox.launch(character)
  186. equip = require "model.equip"
  187. hero = require "model.hero"
  188. end
  189. function CMD.newmail(character, content, id)
  190. -- 修改玩家全服邮件最后收取时间
  191. update_tm(character)
  192. -- 发送邮件
  193. local temp_mail = cjson_decode(content)
  194. local data = update_state(character, temp_mail.id, temp_mail.attachment)
  195. temp_mail.read = data.read
  196. temp_mail.attach = data.attach
  197. temp_mail.receive = data.receive
  198. temp_mail.source = nil
  199. character.send('send_mail', {list = {[1] = temp_mail}})
  200. end
  201. -- 删除邮件
  202. function mailbox.del_mail(character, list)
  203. if #list <= 0 then
  204. return STD_ERR.COMMON_PARM_ERR
  205. end
  206. local d = _M.assert_get(character)
  207. local del = {}
  208. for _, v in ipairs(list or {}) do
  209. if d.list[v] and
  210. (d.list[v].attach and d.list[v].receive or false) or
  211. (not d.list[v].attach and d.list[v].read) or
  212. false then
  213. table.insert(del, v)
  214. d.list[v] = nil
  215. end
  216. end
  217. if not next(del) then
  218. return STD_ERR.COMMON_PARM_ERR
  219. end
  220. local err = skynet.call(s_mailbox,"lua","del_mail", character.uid, del)
  221. _M.persist(character)
  222. return err, del
  223. end
  224. -- 取出玩家邮件
  225. function mailbox.get_mail(character)
  226. local err, list = get_ser_mail(character)
  227. local temp = {}
  228. local temp_mail = {}
  229. local d = _M.assert_get(character)
  230. for k, v in pairs(list or {}) do
  231. temp_mail = cjson_decode(v)
  232. local data = d.list[temp_mail.id]
  233. if not data then
  234. data = update_state(character, temp_mail.id, temp_mail.attachment)
  235. end
  236. temp_mail.attach = data.attach
  237. temp_mail.read = data.read
  238. temp_mail.receive = data.receive
  239. temp_mail.source = nil
  240. table.insert( temp, temp_mail)
  241. end
  242. return err, temp
  243. end
  244. -- TODO: 统计未读取邮件,并推送给客户端
  245. function mailbox.noread_mail(character)
  246. local err, list = get_ser_mail(character)
  247. local temp_mail = {}
  248. local d = _M.assert_get(character)
  249. local noread = 0 -- 未读的文件
  250. local needread = 0 -- 有文本的文件
  251. local sum = 0
  252. local change = false
  253. local temp_list = {}
  254. for _, v in pairs(list or {}) do
  255. temp_mail = cjson_decode(v)
  256. local id = temp_mail.id
  257. temp_list[id] = 1
  258. if not d.list[id] then
  259. change = true
  260. d.list[id] = {
  261. read = false,
  262. attach = next(temp_mail.attachment or {}) and true or false
  263. }
  264. end
  265. if not d.list[id].read then
  266. noread = noread + 1
  267. end
  268. if d.list[id].attach and not d.list[id].receive then
  269. needread = needread + 1
  270. end
  271. sum = sum + 1
  272. end
  273. -- 清楚因过期被删掉的邮件
  274. for k in pairs(d.list or {}) do
  275. if not temp_list[k] then
  276. change = true
  277. d.list[k] = nil
  278. end
  279. end
  280. if change then
  281. _M.persist(character)
  282. end
  283. character.send("simple_mail", {num = sum, noread = noread, needread = needread})
  284. return sum, noread
  285. end
  286. -- 提取邮件附件
  287. function mailbox.get_mail_goods(character, list, bonekey)
  288. if #list <= 0 then
  289. return STD_ERR.MAILBOX_PARM_LIMIT -- 参数错误
  290. end
  291. local del_list = {}
  292. local d = _M.assert_get(character)
  293. for _, v in ipairs(list or {}) do
  294. if d.list[v] and d.list[v].attach and not d.list[v].receive then
  295. table.insert(del_list, v)
  296. end
  297. end
  298. local err, list = skynet.call(s_mailbox,"lua","get_goods",character.uid, del_list)
  299. if not err then
  300. return STD_ERR.MAILBOX_FAIL -- 取出邮件失败
  301. end
  302. local ret_id = {}
  303. local detail = {}
  304. local temp_log = {}
  305. local err = 0
  306. -- 整理邮件奖励
  307. err = tidy_award(character, list, ret_id, detail, temp_log) or 0
  308. if #ret_id > 0 then
  309. local _, lost = reward(character, {
  310. module = "mailbox",
  311. brief = 'mailbox.get_mail_goods', --数据统计
  312. context = string.format("批量领取邮件的附件,领取邮件的数量 num = %s", #ret_id),
  313. detail = detail,
  314. notify = {flags="get_mail_goods"},
  315. })
  316. assert(lost == nil, string.format("邮件系统发送奖励,奖励物品超过了背包的上限。ids = %s", cjson_encode(ret_id)))
  317. -- 发送log
  318. send_log(character, temp_log)
  319. local flag = false
  320. for _, mid in ipairs(ret_id) do
  321. d.list[mid] = d.list[mid] or {}
  322. d.list[mid].receive = true
  323. flag = true
  324. end
  325. if flag then
  326. _M.persist(character)
  327. end
  328. end
  329. return err, optimize_array(ret_id)
  330. end
  331. function mailbox.flag(character, list)
  332. list = list or {}
  333. local d = _M.assert_get(character)
  334. for _, mid in ipairs(list) do
  335. d.list[mid].read = true
  336. end
  337. _M.persist(character)
  338. return {errno = 0}
  339. end
  340. -- 取出玩家邮件
  341. function REQUEST.get_mail(character)
  342. local err, list = mailbox.get_mail(character)
  343. return {errno = err, list = list}
  344. end
  345. -- 取出邮件附件
  346. function REQUEST.get_mail_goods(character, args)
  347. local err, ids = mailbox.get_mail_goods(character, args.list, args.bonekey)
  348. return {errno = err, list = ids}
  349. end
  350. -- 删除邮件
  351. function REQUEST.del_mail(character, args)
  352. local err, list = mailbox.del_mail(character, args.list)
  353. return {errno = err, list = list}
  354. end
  355. -- 已读邮件标记
  356. function REQUEST.flag_mail(character, args)
  357. local err = mailbox.flag(character, args.list)
  358. return {errno = err}
  359. end
  360. return mailbox