123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607 |
- 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.login_args.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.login_args.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("<Agent %s@%s.%s>", character.channel, character.uid, character.nickname))
- return ready(first)
- -- if character.nickname then
- -- character.set_nickname("player")
- -- logger.label(string_format("<Agent %s@%s.%s>", 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("<Agent %s@%s.%s>", 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
|