local skynet = require "skynet" local schema = require "model.schema" local logger = require "logger" local keygen = require "model.keygen" local cjson = require "cjson" local reward = require "model.reward" local util = require "util" local asset = require "model.asset" local log = require "model.log" local common_fun = require "model.common_fun" local equip local hero cjson.encode_sparse_array(true, 1) local s_mailbox = skynet.localname(".mailbox") local cjson_decode = cjson.decode -- 解码 local cjson_encode = cjson.encode -- 编码 local CMD = {} local REQUEST = {} local mailbox = {} local TYPE_TAB = 'table' local _M = schema.new('mail', { tm = 0, -- 玩家最近全服邮件收取时间 list = { --[[ [id] = { read = false, 是否已读 attach = true, 是否有附件 receive = true, 是否已领取 } ]] } }) function mailbox.list_request_interests() return REQUEST end function mailbox.list_command_interests() return CMD end -- function mailbox.parse(character) local d = _M.load(character) if not d.list then d.list = {} _M.persist(character) end end local function optimize_array(a) if a and next(a) then return a end end ------------------------------ mailbox ------------------------------------- -- 更新全局邮件收取时间 local function update_tm(character) local data = _M.assert_get(character) data.tm = os.time() _M.persist(character) end local function update_state(character, id, attachment) local d = _M.assert_get(character) d.list[id] = { attach = next(attachment or {}) and true or false, read = false, receive = false, } _M.persist(character) return d.list[id] end -- 取出服务器中的所有邮件 local function get_ser_mail(character) local data = _M.assert_get(character) local tm = os.time() data.tm = (data.tm == 0) and tm or data.tm _M.persist(character) return skynet.call(s_mailbox, "lua", "get_all_mail", data.tm, character.uid) end -- 检查是否能添加成功 local function check_bag(attachment, bag_residue) for k, v in pairs(attachment) do local id = v.id local num = v.num local item_type = common_fun.goods_type(id) if item_type == GOODS_HERO then bag_residue.hero = (bag_residue.hero or 0) - num if bag_residue.hero <= 0 then return STD_ERR.HERO_FULL end elseif item_type == GOODS_EQUIP then bag_residue.equip = (bag_residue.equip or 0) - num if bag_residue.equip <= 0 then return STD_ERR.EQUIP_FULL end end end return 0 end local function will_send_award(attachment, detail) for k, v in pairs(attachment) do table.insert(detail, v) end end local function tidy_award(character, list, ret_id, detail, log) local bag_residue = { -- 剩余空间 hero = hero.hero_surplus(character), equip = equip.equip_surplus(character), } local sign, temp for _, v in pairs(list) do temp = cjson_decode(v) temp.source = temp.source or {} sign = check_bag(temp.attachment, bag_residue) if temp.attachment and sign == 0 then will_send_award(temp.attachment, detail) table.insert(log, { module = temp.source.module, brief = temp.source.brief, context = temp.source.context, detail = temp.attachment or {}, }) table.insert(ret_id, temp.id) elseif not temp.attachment then -- return -6 -- 邮件的附件为空 table.insert(ret_id, temp.id) else return sign end end end local function send_log(character, temp_log) skynet.fork(function() for _, data in pairs(temp_log) do local record = { currency = data.detail.currency, } if next(record) then log.reward(character, data.module, data.brief, data.context, record) end end end) end ------------------------------ mailbox ------------------------------------- --[[ 统一邮件发送接口 number subject: 邮件的主题, 使用参数表的 mail_proto[k].type string body: 邮件需要的参数,多参数使用 “ ,” 分割 , 无参数,为 "" table attachment: 邮件的附件,格式按照物品添加格式处理 table source: 邮件的来源{ module, -- 发送模块 brief, -- 说明 context, -- 上下文 } ]] function mailbox.send(character, subject, body, attachment, source, must_mail) local id = keygen() local now = os.time() local content = { id = id, date = now, subject = subject, body = body, attachment = attachment or {}, source = source, } skynet.call(s_mailbox,"lua","save_mail", id, cjson_encode(content), character.uid, must_mail) local data = update_state(character, id, attachment) content.read = data.read content.attach = data.attach content.receive = data.receive if character.send then content.source = nil character.send('send_mail', {list = {[1] = content}}) end end function mailbox.ready(character) local data = _M.assert_get(character) if type(data) ~= TYPE_TAB then assert(false,string.format("邮件系统取出玩家数据失败 %s", character.uid)) end -- 玩家上线将自己的地址保存到服务器 skynet.call(s_mailbox,"lua","online", skynet.self(), character.uid, (data.tm == 0) and os.time() or data.tm) update_tm(character) mailbox.noread_mail(character) end function mailbox.saybye(character) -- TODO: 玩家下线时的处理 update_tm(character) skynet.call(s_mailbox,"lua","offline", skynet.self(), character.uid) end function mailbox.launch(character) equip = require "model.equip" hero = require "model.hero" end function CMD.newmail(character, content, id) -- 修改玩家全服邮件最后收取时间 update_tm(character) -- 发送邮件 local temp_mail = cjson_decode(content) local data = update_state(character, temp_mail.id, temp_mail.attachment) temp_mail.read = data.read temp_mail.attach = data.attach temp_mail.receive = data.receive temp_mail.source = nil character.send('send_mail', {list = {[1] = temp_mail}}) end -- 删除邮件 function mailbox.del_mail(character, list) if #list <= 0 then return STD_ERR.COMMON_PARM_ERR end local d = _M.assert_get(character) local del = {} for _, v in ipairs(list or {}) do if d.list[v] and (d.list[v].attach and d.list[v].receive or false) or (not d.list[v].attach and d.list[v].read) or false then table.insert(del, v) d.list[v] = nil end end if not next(del) then return STD_ERR.COMMON_PARM_ERR end local err = skynet.call(s_mailbox,"lua","del_mail", character.uid, del) _M.persist(character) return err, del end -- 取出玩家邮件 function mailbox.get_mail(character) local err, list = get_ser_mail(character) local temp = {} local temp_mail = {} local d = _M.assert_get(character) for k, v in pairs(list or {}) do temp_mail = cjson_decode(v) local data = d.list[temp_mail.id] if not data then data = update_state(character, temp_mail.id, temp_mail.attachment) end temp_mail.attach = data.attach temp_mail.read = data.read temp_mail.receive = data.receive temp_mail.source = nil table.insert( temp, temp_mail) end return err, temp end -- TODO: 统计未读取邮件,并推送给客户端 function mailbox.noread_mail(character) local err, list = get_ser_mail(character) local temp_mail = {} local d = _M.assert_get(character) local noread = 0 -- 未读的文件 local needread = 0 -- 有文本的文件 local sum = 0 local change = false local temp_list = {} for _, v in pairs(list or {}) do temp_mail = cjson_decode(v) local id = temp_mail.id temp_list[id] = 1 if not d.list[id] then change = true d.list[id] = { read = false, attach = next(temp_mail.attachment or {}) and true or false } end if not d.list[id].read then noread = noread + 1 end if d.list[id].attach and not d.list[id].receive then needread = needread + 1 end sum = sum + 1 end -- 清楚因过期被删掉的邮件 for k in pairs(d.list or {}) do if not temp_list[k] then change = true d.list[k] = nil end end if change then _M.persist(character) end character.send("simple_mail", {num = sum, noread = noread, needread = needread}) return sum, noread end -- 提取邮件附件 function mailbox.get_mail_goods(character, list, bonekey) if #list <= 0 then return STD_ERR.MAILBOX_PARM_LIMIT -- 参数错误 end local del_list = {} local d = _M.assert_get(character) for _, v in ipairs(list or {}) do if d.list[v] and d.list[v].attach and not d.list[v].receive then table.insert(del_list, v) end end local err, list = skynet.call(s_mailbox,"lua","get_goods",character.uid, del_list) if not err then return STD_ERR.MAILBOX_FAIL -- 取出邮件失败 end local ret_id = {} local detail = {} local temp_log = {} local err = 0 -- 整理邮件奖励 err = tidy_award(character, list, ret_id, detail, temp_log) or 0 if #ret_id > 0 then local _, lost = reward(character, { module = "mailbox", brief = 'mailbox.get_mail_goods', --数据统计 context = string.format("批量领取邮件的附件,领取邮件的数量 num = %s", #ret_id), detail = detail, notify = {flags="get_mail_goods"}, }) assert(lost == nil, string.format("邮件系统发送奖励,奖励物品超过了背包的上限。ids = %s", cjson_encode(ret_id))) -- 发送log send_log(character, temp_log) local flag = false for _, mid in ipairs(ret_id) do d.list[mid] = d.list[mid] or {} d.list[mid].receive = true flag = true end if flag then _M.persist(character) end end return err, optimize_array(ret_id) end function mailbox.flag(character, list) list = list or {} local d = _M.assert_get(character) for _, mid in ipairs(list) do d.list[mid].read = true end _M.persist(character) return {errno = 0} end -- 取出玩家邮件 function REQUEST.get_mail(character) local err, list = mailbox.get_mail(character) return {errno = err, list = list} end -- 取出邮件附件 function REQUEST.get_mail_goods(character, args) local err, ids = mailbox.get_mail_goods(character, args.list, args.bonekey) return {errno = err, list = ids} end -- 删除邮件 function REQUEST.del_mail(character, args) local err, list = mailbox.del_mail(character, args.list) return {errno = err, list = list} end -- 已读邮件标记 function REQUEST.flag_mail(character, args) local err = mailbox.flag(character, args.list) return {errno = err} end return mailbox