local skynet = require "skynet" require "skynet.manager" local queue = require "skynet.queue" local util = require "util" local logger = require "logger" local stringify = require "stringify" local character = require "model.character" local log = require "model.log" local reward = require "model.reward" local payment = require "model.payment" local mailbox local shengtian local pbSproto = {} local skynet_retpack = skynet.retpack local skynet_now = skynet.now local skynet_sleep = skynet.sleep local string_format = string.format local traceback = debug.traceback local max = util.max local trace = logger.trace -- local manage_proxy = skynet.localname(".manage_proxy") local loginserver = skynet.localname(".loginserver") local s_public = skynet.localname(".public") local module = { require "model.rechargemod", require "model.currency", require "model.role", require "model.adventure", require "model.mailbox", require "model.hero", require "model.embattle", require "model.equip", require "model.talent", require "model.time_box", require "model.red_point", require "model.presence", -- 必须在最下面 require "model.mq", require "model.gm", } local REQUEST = setmetatable({}, { __newindex = function (t, k, v) local f = pbSproto.register_msg if f then f(k) end rawset(t, k, v) end} ) local CMD = {} local ipaddr -- 玩家的ip地址 local kick -- 将玩家踢下线的函数 local send -- 发送网络消息的函数 local throw_exception -- 抛出异常的函数 local lasttime -- 接受上一条消息的时间戳 local synchronized = queue() -- 临界区 local namecenter local usercenter local player = {} local function keepalive() local args = { time=0 } local lastping = 0 lasttime = skynet_now() while true do local now = skynet.now() if lasttime+36000 > now then lastping = max(lastping, lasttime) if lastping+1000 <= now then args.time = now lastping = now send('ping', args) end skynet_sleep(100) else kick("heartbeat timeout") break end end end local function watchtime() local os_time = os.time local dispatch_event = character.dispatch local daily_refresh = util.today(character.daily_refresh) local weekly_refresh = util.today(character.weekly_refresh) local today = util.today() local four = today + 4*3600 -- 凌晨4点 local minute = os_time() + 60 -- 每分钟(不是零秒) while true do local now = os_time() if now >= four then dispatch_event("timer.four", four) four = four + 86400 -- 明天凌晨4点 end if now >= minute then dispatch_event("timer.minute", now) minute = minute+60 end if now >= daily_refresh then dispatch_event("daily_refresh", now) daily_refresh = util.today(now)+24*3600 character.set_daily_refresh(daily_refresh+12*3600)--防止冬令夏令时的问题选个中午12点作为起始时间 end if now >= weekly_refresh then dispatch_event("weekly_refresh", now) local add_day = 1 - tonumber(os.date("%w")) if add_day <= 0 then add_day = add_day + 7 end weekly_refresh = util.today(now)+add_day*24*3600 character.set_weekly_refresh(weekly_refresh+12*3600)--防止冬令夏令时的问题选个中午12点作为起始时间 end dispatch_event("timer.sec", now) skynet.sleep(100) end end local isready = false local function ready(bfirst) -- 侦听事件 for _, elem in ipairs(module) do if elem.monitor then local ok, msg = xpcall(elem.monitor, traceback, character) if not ok then logger.error(msg) end end end -- 上线处理逻辑 for _, elem in ipairs(module) do if elem.launch then local ok, msg = xpcall(elem.launch, traceback, character) if not ok then logger.error(msg) end end end -- 同步角色基础数据 local currency = require "model.currency" send('user', { uid = character.uid, rename_time = character.rename_time, nickname = character.nickname, level = character.level, exp = character.exp, avatar = character.avater, currency = currency.get_client_data(character), svrtime = os.time(), createtime = character.createtime, sid = character.sid, bfirst = bfirst, }) -- 前后端同步数据 for _, elem in ipairs(module) do if elem.ready then local ok, msg = xpcall(elem.ready, traceback, character) if not ok then logger.error(msg) end end end -- 执行到这里,标志着用户真正进入了游戏 isready = true character.set_lastlogin(os.time()) -- character.dispatch("ready") skynet.fork(function() watchtime() end) -- mailbox = mailbox or require "model.mailbox" -- local detail = {{id = CURRENCY_ID_COINS, num = 100}} -- local desc = { -- module = "login", -- brief = "登录奖励", -- context = "登录奖励", -- } -- mailbox.send(character, 2, "", detail, desc) -- skynet.fork(function() -- skynet.sleep(300) -- logger.trace("login_over") -- -- send('login_over',{}) -- end) -- 检查玩家是否上报过角色创建 -- if not character.reported then -- log.create(character, ipaddr, deviceid) -- character.set_reported(os.time()) -- end character.dispatch("ready") return {errno = 0} end -- IOS 转移开发团队后, 用户迁移 local IOS_MI_SIGN = 2 -- 0:功能关闭 1:生成转移标识 2:账号转移阶段 local function migration_ios(args) local ret =skynet.call(s_public, "lua","migration_ios", "migration", args,IOS_MI_SIGN) if IOS_MI_SIGN ==1 then return ret elseif IOS_MI_SIGN ==2 then if ret then args.account = ret end else logger.trace("##### migration_ios 功能暂未开启") end end local function enter(args) local sid = args.sid -- 服务器唯一标识 local account = args.account -- 帐号唯一标识(渠道分配) local channel = args.channel -- 渠道唯一标识 -- local appid = args.appid -- App唯一标识 -- local platform = args.platform -- 操作系统(pc,ios,android等) -- local version = args.version -- 客户端版本号 -- local deviceid = args.deviceid or "NULL-device" -- 玩家设备号 -- 检查登录参数是否完整 if sid == nil then return { errno = 255 } end if account == nil then return { errno = 255 } end -- if appid == nil then return { errno=STD_ERR.PLYAER_PARM_LIMIT or 255 } end if channel == nil then return { errno = 255 } end -- if platform == nil then return { errno=STD_ERR.PLYAER_PARM_LIMIT or 255 } end -- if version == nil then return { errno=STD_ERR.PLYAER_PARM_LIMIT or 255 } end -- IOS 转移开发团队后用户迁移 CDPK3-297 IOS用户迁移 -- local migration -- logger.trace("############## args.migration %s", args.migration or 'nil') -- -- if (args.sdk or 0) == 4 or (args.sdk or 0) == "4" then -- 使用苹果登录 -- if true then -- logger.trace("####### migration_ios 处理") -- migration = migration_ios(args) -- end -- 登记到用户中心 local errno, rets = skynet.call(usercenter, "lua", "login", args, skynet.self()) if errno == 1 then return { errno=STD_ERR.PLYAER_ERR_SERVERID or 8 } end -- 不支持的服务器ID if errno == 2 then return { errno=STD_ERR.PLYAER_OTHER_LOGIN or 4 } end -- 其它设备正在登录 if errno == 3 then return { errno=STD_ERR.PLYAER_MAINTAIN or 9 } end -- 正在维护 assert(errno == 0) -- 初始化 character 对象并载入角色数据 character.loadfrom(rets) character.login_args = args -- 玩家登录传递的参数 local now = os.time() if character.forbidden > now then return { errno=STD_ERR.PLYAER_FORBID or 3 } -- 被禁用的帐号 end -- 给模块一个解析数据的机会 for _, elem in ipairs(module) do if elem.parse then local ok, msg = xpcall(elem.parse, traceback, character) if not ok then logger.error(msg) end end end -- 生成登录log, 并关联一些函数 -- log.login(character, ipaddr) character.send = send character.kick = kick character.throw_exception = throw_exception local first = false if character.nickname then if character.channel == "shengtian" then shengtian = shengtian or require "service.loginserver.shengtian" local content = { account = character.account, uid = character.uid, name = character.nickname, server = character.sid, level = character.level, power = character.power, } shengtian.upload(5, content) end else first = true local ok, name = skynet.call(namecenter, "lua", "register_nickname", "", character.uid) if not ok then return { errno=STD_ERR.PLYAER_DUPLICATION_NAME} end character.set_nickname(name) if character.channel == "shengtian" then shengtian = shengtian or require "service.loginserver.shengtian" local content = { account = character.account, uid = character.uid, name = character.nickname, server = character.sid, level = character.level, power = character.power, } shengtian.upload(1, content) end end logger.label(string_format("", character.channel, character.uid, character.nickname)) return ready(first) -- if character.nickname then -- character.set_nickname("player") -- logger.label(string_format("", character.channel, character.uid, character.nickname)) -- return ready() -- end -- return { errno=STD_ERR.PLYAER_NO_PLAYER} -- 未创建角色 end function player.start(ctx) local args = assert(ctx.args) ipaddr = assert(ctx.ipaddr) kick = assert(ctx.kick) send = assert(ctx.send) throw_exception = assert(ctx.throw_exception) namecenter = skynet.localname(".namecenter") usercenter = skynet.localname(".usercenter") pbSproto = ctx.pbSproto for key in pairs(REQUEST) do pbSproto.register_msg(key) end for _, elem in ipairs(module) do local list_request_interests = elem.list_request_interests if list_request_interests then local rets = list_request_interests() or {} for k, f in pairs(rets) do assert(not rawget(REQUEST, k),k) REQUEST[k] = f end end local list_command_interests = elem.list_command_interests if list_command_interests then local rets = list_command_interests() or {} for k, f in pairs(rets) do assert(not rawget(CMD, k)) CMD[k] = f end end end -- 心跳检查 -- skynet.fork(keepalive) return enter(args) end local func_switch local function dispatch_request(pname, args) local f = REQUEST[pname] assert(f, string_format("Not found request '%s'", pname)) -- func_switch = func_switch or require "model.func_switch" -- local errno = func_switch.check_condition(character, args) -- logger.trace("dispatch_request:%s",pname) -- if errno == 0 then return f(character, args) -- end -- return {errno = errno} end function player.request(pname, args) -- 以串行化的方式执行玩家请求(简化协程挂起带来的复杂性) lasttime = skynet_now() return synchronized(dispatch_request, pname, args) end function player.response(pname, args, callback) trace("dispath response '%s'", pname) lasttime = skynet_now() return synchronized(callback, character, args) end function player.command(session, cmd, ...) local f = CMD[cmd] assert(f, string_format("Not found command '%s'", cmd)) if session == 0 then f(character, ...) else skynet_retpack(f(character, ...)) end end function player.disconnect() if not isready then skynet.call(usercenter, "lua", "logout", character.uid, skynet.self()) return end -- 先派发离线事件 repeat local ok, msg = xpcall(character.dispatch, traceback, "disconnect") if not ok then logger.error(msg) end until 0 -- 调用每个模块的下线接口(倒序) for i=#module, 1, -1 do local elem = module[i] if elem.saybye then local ok, msg = xpcall(elem.saybye, traceback, character) if not ok then logger.error(msg) end end end local ok, msg = xpcall(function() -- if character.level >= 20 and character.mission > 10030 then -- PK3-919 -- local backups_role = skynet.localname(".backups_role") -- skynet.send(backups_role, "lua", "logout", character.uid,character.sid,character.backups_all()) -- end end, traceback) if not ok then logger.error(msg) end -- 下线时的最后处理 character.set_lastlogout(os.time()) character.flushall() -- log.logout(character, ipaddr) skynet.call(usercenter, "lua", "logout", character.uid, skynet.self()) end function REQUEST.create(_, args) local nickname = args.nickname or "" -- 角色名 local deviceid = args.deviceid or "NULL-device" -- 玩家设备号 -- local elf = assert(args.elf) -- 初始精灵 local len = string.len(nickname) if len < 3 or len > 32 then return { errno=STD_ERR.PLYAER_LLLEGAL_NAME } end -- 非法角色名 if character.nickname then return { errno=STD_ERR.PLYAER_LLLEGAL_OPERATION } end -- 非法操作 -- 注册角色名 -- local ok = skynet.call(namecenter, "lua", "register_nickname", nickname, character.uid) -- if not ok then -- return { errno=STD_ERR.PLYAER_DUPLICATION_NAME } -- 角色重名 -- end logger.label(string_format("", character.channel, character.uid, nickname)) character.set_nickname(nickname) return ready() end -- function REQUEST.pong(_, args) -- -- Nothing -- end local facebook = nil -- function REQUEST.binding(_, args) -- local account = assert(character.account) -- local channel = assert(character.channel) -- local new_account = assert(args.cloud_id) -- local passwd = assert(args.cloud_pass) -- if character.fb then -- return { errno=6 } -- 已绑定 -- end -- facebook = facebook or skynet.localname(".facebook") -- local errno = skynet.call(facebook, "lua", "redirect", account, channel, new_account, passwd) -- if errno == 400 then -- return { errno=2 } -- 参数错误 -- elseif errno == 409 then -- return { errno=1 } -- 帐号已存在 -- elseif errno == 502 then -- return { errno=5 } -- 网络错误 -- end -- character.set_fb(new_account) -- return { errno=0 } -- end -- 处理广播消息 function CMD.broadcast(character, pname, args) if isready then return send(pname, args) end end --兑换CDK local mailbox local parse -- function REQUEST.gdkReq(character,args) -- local serial = args.serial -- -- local cdktypenum = character.cdktypenum -- -- cdktypenum = cdktypenum or {} -- -- if cdktypenum[serial] then -- -- return {errno = STD_ERR.PLYAER_CDK_YET_SET} -- CDK 已经使用过了 -- -- end -- local serial = string.upper(serial) --PK3-1388 增加CDK统计 -- local ok, errno, awardinfo = skynet.call(manage_proxy, "lua","rpc_call", -- "span_cdk","use_cdk_state", -- character.uid, serial, option.sid, character.nickname, character.vip --PK3-1388 增加CDK统计 PK3-1400 VIP等级限制 -- ) -- if not ok then -- logger.trace(" 使用ckd 服务器异常报错 %s", errno) -- errno = STD_ERR.PLYAER_CDK_ERR -- end -- if errno ~= 0 then -- return {errno = errno} -- end -- -- 检查玩家是否使用过该cdk -- mailbox = mailbox or require "model.mailbox" -- mailbox.send(character, 20, "", awardinfo,{ -- module = "cdk", -- 发送模块 -- brief = "礼包", -- 说明 -- context=string.format("cdk_type:%s", serial), -- 上下文 -- }) -- -- cdktypenum[serial] = awardinfo -- -- character.set_cdktypenum(cdktypenum) -- return {errno = 0} -- end -- function REQUEST.login(character) -- return {errno = 2000001} -- 异常的登陆请求 -- end -- function REQUEST.logout(character) -- logger.trace("## 玩家主动离线") -- kick("heartbeat logout") -- return {errno = 0} -- end function REQUEST.ping(character) return {time = os.time()} end -- TODO: 游客绑定facebook 或 google 账号 -- function REQUEST.tourist_binding(_, args) -- logger.trace(" #### 请求账号绑定 %s", stringify(args)) -- local account = args.account -- local token = args.token -- local state = args.state--1是facebook 2是google 3 IOS -- if not(account and token and state) then -- return {errno = STD_ERR.PLYAER_PARM_LIMIT} -- 参数异常 -- end -- -- 检查账号是否绑定 -- if character.facebook then -- return {errno = STD_ERR.PLYAER_BING_FACEBOOK} -- end -- if character.google then -- return {errno = STD_ERR.PLYAER_BING_GOOGLE} -- end -- if character.ios then -- return {errno = STD_ERR.PLYAER_BING_IOS} -- end -- -- 检查玩家是否为游客 -- if type(character.tourist) ~= "number" or character.tourist == 1 then -- nil/1:非游客账号 2: 游客账号 -- return {errno = STD_ERR.PLYAER_BING_NO_TOURIST} -- 玩家非游客 绑定失败 -- end -- local par = {channel = character.channel, token = token, account = account, appid = args.appid, sdk = nil} -- if state == 1 then -- Facebook -- -- par.sdk = 'facebook' -- par.sdk = 2 -- -- 登陆账号 -- elseif state == 2 then -- Google -- -- par.sdk = 'google' -- par.sdk = 3 -- elseif state == 3 then -- par.sdk = 4 -- else -- return {errno = STD_ERR.PLYAER_PARM_LIMIT} -- 参数异常 -- end -- -- Verify login token -- local succ = skynet.call(loginserver, "lua", "login", par) -- if succ then -- -- usercenter 账号指向修改 -- local errno = skynet.call(usercenter, "lua", "bingding", character.channel, character.sid, account,character.uid) -- if errno ~= 0 then return {errno = errno} end -- if 1 == state then -- character.set_facebook(account) -- elseif 2 == state then -- character.set_google(account) -- elseif 3 == state then -- character.set_ios(account) -- end -- character.set_account(account) -- character.set_tourist(1) -- 游客标识修改 -- return {errno = 0} -- else -- return {errno = STD_ERR.PLYAER_BING_LOGIN_FAIL} -- 登陆验证异常 登陆失败 -- end -- end return player