player.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. local skynet = require "skynet"
  2. require "skynet.manager"
  3. local queue = require "skynet.queue"
  4. local util = require "util"
  5. local logger = require "logger"
  6. local stringify = require "stringify"
  7. local character = require "model.character"
  8. local log = require "model.log"
  9. local reward = require "model.reward"
  10. local payment = require "model.payment"
  11. local mailbox
  12. local shengtian
  13. local pbSproto = {}
  14. local skynet_retpack = skynet.retpack
  15. local skynet_now = skynet.now
  16. local skynet_sleep = skynet.sleep
  17. local string_format = string.format
  18. local traceback = debug.traceback
  19. local max = util.max
  20. local trace = logger.trace
  21. -- local manage_proxy = skynet.localname(".manage_proxy")
  22. local loginserver = skynet.localname(".loginserver")
  23. local s_public = skynet.localname(".public")
  24. local module = {
  25. require "model.rechargemod",
  26. require "model.currency",
  27. require "model.role",
  28. require "model.adventure",
  29. require "model.mailbox",
  30. require "model.hero",
  31. require "model.embattle",
  32. require "model.equip",
  33. require "model.talent",
  34. require "model.time_box",
  35. require "model.red_point",
  36. require "model.presence",
  37. -- 必须在最下面
  38. require "model.mq",
  39. require "model.gm",
  40. }
  41. local REQUEST = setmetatable({}, {
  42. __newindex = function (t, k, v)
  43. local f = pbSproto.register_msg
  44. if f then
  45. f(k)
  46. end
  47. rawset(t, k, v)
  48. end}
  49. )
  50. local CMD = {}
  51. local ipaddr -- 玩家的ip地址
  52. local kick -- 将玩家踢下线的函数
  53. local send -- 发送网络消息的函数
  54. local throw_exception -- 抛出异常的函数
  55. local lasttime -- 接受上一条消息的时间戳
  56. local synchronized = queue() -- 临界区
  57. local namecenter
  58. local usercenter
  59. local player = {}
  60. local function keepalive()
  61. local args = { time=0 }
  62. local lastping = 0
  63. lasttime = skynet_now()
  64. while true do
  65. local now = skynet.now()
  66. if lasttime+36000 > now then
  67. lastping = max(lastping, lasttime)
  68. if lastping+1000 <= now then
  69. args.time = now
  70. lastping = now
  71. send('ping', args)
  72. end
  73. skynet_sleep(100)
  74. else
  75. kick("heartbeat timeout")
  76. break
  77. end
  78. end
  79. end
  80. local function watchtime()
  81. local os_time = os.time
  82. local dispatch_event = character.dispatch
  83. local daily_refresh = util.today(character.daily_refresh)
  84. local weekly_refresh = util.today(character.weekly_refresh)
  85. local today = util.today()
  86. local four = today + 4*3600 -- 凌晨4点
  87. local minute = os_time() + 60 -- 每分钟(不是零秒)
  88. while true do
  89. local now = os_time()
  90. if now >= four then
  91. dispatch_event("timer.four", four)
  92. four = four + 86400 -- 明天凌晨4点
  93. end
  94. if now >= minute then
  95. dispatch_event("timer.minute", now)
  96. minute = minute+60
  97. end
  98. if now >= daily_refresh then
  99. dispatch_event("daily_refresh", now)
  100. daily_refresh = util.today(now)+24*3600
  101. character.set_daily_refresh(daily_refresh+12*3600)--防止冬令夏令时的问题选个中午12点作为起始时间
  102. end
  103. if now >= weekly_refresh then
  104. dispatch_event("weekly_refresh", now)
  105. local add_day = 1 - tonumber(os.date("%w"))
  106. if add_day <= 0 then
  107. add_day = add_day + 7
  108. end
  109. weekly_refresh = util.today(now)+add_day*24*3600
  110. character.set_weekly_refresh(weekly_refresh+12*3600)--防止冬令夏令时的问题选个中午12点作为起始时间
  111. end
  112. dispatch_event("timer.sec", now)
  113. skynet.sleep(100)
  114. end
  115. end
  116. local isready = false
  117. local function ready(bfirst)
  118. -- 侦听事件
  119. for _, elem in ipairs(module) do
  120. if elem.monitor then
  121. local ok, msg = xpcall(elem.monitor, traceback, character)
  122. if not ok then
  123. logger.error(msg)
  124. end
  125. end
  126. end
  127. -- 上线处理逻辑
  128. for _, elem in ipairs(module) do
  129. if elem.launch then
  130. local ok, msg = xpcall(elem.launch, traceback, character)
  131. if not ok then
  132. logger.error(msg)
  133. end
  134. end
  135. end
  136. -- 同步角色基础数据
  137. local currency = require "model.currency"
  138. send('user', {
  139. uid = character.uid,
  140. rename_time = character.rename_time,
  141. nickname = character.nickname,
  142. level = character.level,
  143. exp = character.exp,
  144. avatar = character.avater,
  145. currency = currency.get_client_data(character),
  146. svrtime = os.time(),
  147. createtime = character.createtime,
  148. sid = character.sid,
  149. bfirst = bfirst,
  150. })
  151. -- 前后端同步数据
  152. for _, elem in ipairs(module) do
  153. if elem.ready then
  154. local ok, msg = xpcall(elem.ready, traceback, character)
  155. if not ok then
  156. logger.error(msg)
  157. end
  158. end
  159. end
  160. -- 执行到这里,标志着用户真正进入了游戏
  161. isready = true
  162. character.set_lastlogin(os.time())
  163. -- character.dispatch("ready")
  164. skynet.fork(function()
  165. watchtime()
  166. end)
  167. -- mailbox = mailbox or require "model.mailbox"
  168. -- local detail = {{id = CURRENCY_ID_COINS, num = 100}}
  169. -- local desc = {
  170. -- module = "login",
  171. -- brief = "登录奖励",
  172. -- context = "登录奖励",
  173. -- }
  174. -- mailbox.send(character, 2, "", detail, desc)
  175. -- skynet.fork(function()
  176. -- skynet.sleep(300)
  177. -- logger.trace("login_over")
  178. -- -- send('login_over',{})
  179. -- end)
  180. -- 检查玩家是否上报过角色创建
  181. -- if not character.reported then
  182. -- log.create(character, ipaddr, deviceid)
  183. -- character.set_reported(os.time())
  184. -- end
  185. character.dispatch("ready")
  186. return {errno = 0}
  187. end
  188. -- IOS 转移开发团队后, 用户迁移
  189. local IOS_MI_SIGN = 2 -- 0:功能关闭 1:生成转移标识 2:账号转移阶段
  190. local function migration_ios(args)
  191. local ret =skynet.call(s_public, "lua","migration_ios", "migration", args,IOS_MI_SIGN)
  192. if IOS_MI_SIGN ==1 then
  193. return ret
  194. elseif IOS_MI_SIGN ==2 then
  195. if ret then
  196. args.account = ret
  197. end
  198. else
  199. logger.trace("##### migration_ios 功能暂未开启")
  200. end
  201. end
  202. local function enter(args)
  203. local sid = args.sid -- 服务器唯一标识
  204. local account = args.account -- 帐号唯一标识(渠道分配)
  205. local channel = args.channel -- 渠道唯一标识
  206. -- local appid = args.appid -- App唯一标识
  207. -- local platform = args.platform -- 操作系统(pc,ios,android等)
  208. -- local version = args.version -- 客户端版本号
  209. -- local deviceid = args.deviceid or "NULL-device" -- 玩家设备号
  210. -- 检查登录参数是否完整
  211. if sid == nil then return { errno = 255 } end
  212. if account == nil then return { errno = 255 } end
  213. -- if appid == nil then return { errno=STD_ERR.PLYAER_PARM_LIMIT or 255 } end
  214. if channel == nil then return { errno = 255 } end
  215. -- if platform == nil then return { errno=STD_ERR.PLYAER_PARM_LIMIT or 255 } end
  216. -- if version == nil then return { errno=STD_ERR.PLYAER_PARM_LIMIT or 255 } end
  217. -- IOS 转移开发团队后用户迁移 CDPK3-297 IOS用户迁移
  218. -- local migration
  219. -- logger.trace("############## args.migration %s", args.migration or 'nil')
  220. -- -- if (args.sdk or 0) == 4 or (args.sdk or 0) == "4" then -- 使用苹果登录
  221. -- if true then
  222. -- logger.trace("####### migration_ios 处理")
  223. -- migration = migration_ios(args)
  224. -- end
  225. -- 登记到用户中心
  226. local errno, rets = skynet.call(usercenter, "lua", "login", args, skynet.self())
  227. if errno == 1 then return { errno=STD_ERR.PLYAER_ERR_SERVERID or 8 } end -- 不支持的服务器ID
  228. if errno == 2 then return { errno=STD_ERR.PLYAER_OTHER_LOGIN or 4 } end -- 其它设备正在登录
  229. if errno == 3 then return { errno=STD_ERR.PLYAER_MAINTAIN or 9 } end -- 正在维护
  230. assert(errno == 0)
  231. -- 初始化 character 对象并载入角色数据
  232. character.loadfrom(rets)
  233. character.login_args = args -- 玩家登录传递的参数
  234. local now = os.time()
  235. if character.forbidden > now then
  236. return { errno=STD_ERR.PLYAER_FORBID or 3 } -- 被禁用的帐号
  237. end
  238. -- 给模块一个解析数据的机会
  239. for _, elem in ipairs(module) do
  240. if elem.parse then
  241. local ok, msg = xpcall(elem.parse, traceback, character)
  242. if not ok then
  243. logger.error(msg)
  244. end
  245. end
  246. end
  247. -- 生成登录log, 并关联一些函数
  248. -- log.login(character, ipaddr)
  249. character.send = send
  250. character.kick = kick
  251. character.throw_exception = throw_exception
  252. local first = false
  253. if character.nickname then
  254. if character.login_args.channel== "shengtian" then
  255. shengtian = shengtian or require "service.loginserver.shengtian"
  256. local content = {
  257. account = character.account,
  258. uid = character.uid,
  259. name = character.nickname,
  260. server = character.sid,
  261. level = character.level,
  262. power = character.power,
  263. }
  264. shengtian.upload(5, content)
  265. end
  266. else
  267. first = true
  268. local ok, name = skynet.call(namecenter, "lua", "register_nickname", "", character.uid)
  269. if not ok then
  270. return { errno=STD_ERR.PLYAER_DUPLICATION_NAME}
  271. end
  272. character.set_nickname(name)
  273. if character.login_args.channel== "shengtian" then
  274. shengtian = shengtian or require "service.loginserver.shengtian"
  275. local content = {
  276. account = character.account,
  277. uid = character.uid,
  278. name = character.nickname,
  279. server = character.sid,
  280. level = character.level,
  281. power = character.power,
  282. }
  283. shengtian.upload(1, content)
  284. end
  285. end
  286. logger.label(string_format("<Agent %s@%s.%s>", character.channel, character.uid, character.nickname))
  287. return ready(first)
  288. -- if character.nickname then
  289. -- character.set_nickname("player")
  290. -- logger.label(string_format("<Agent %s@%s.%s>", character.channel, character.uid, character.nickname))
  291. -- return ready()
  292. -- end
  293. -- return { errno=STD_ERR.PLYAER_NO_PLAYER} -- 未创建角色
  294. end
  295. function player.start(ctx)
  296. local args = assert(ctx.args)
  297. ipaddr = assert(ctx.ipaddr)
  298. kick = assert(ctx.kick)
  299. send = assert(ctx.send)
  300. throw_exception = assert(ctx.throw_exception)
  301. namecenter = skynet.localname(".namecenter")
  302. usercenter = skynet.localname(".usercenter")
  303. pbSproto = ctx.pbSproto
  304. for key in pairs(REQUEST) do
  305. pbSproto.register_msg(key)
  306. end
  307. for _, elem in ipairs(module) do
  308. local list_request_interests = elem.list_request_interests
  309. if list_request_interests then
  310. local rets = list_request_interests() or {}
  311. for k, f in pairs(rets) do
  312. assert(not rawget(REQUEST, k),k)
  313. REQUEST[k] = f
  314. end
  315. end
  316. local list_command_interests = elem.list_command_interests
  317. if list_command_interests then
  318. local rets = list_command_interests() or {}
  319. for k, f in pairs(rets) do
  320. assert(not rawget(CMD, k))
  321. CMD[k] = f
  322. end
  323. end
  324. end
  325. -- 心跳检查
  326. -- skynet.fork(keepalive)
  327. return enter(args)
  328. end
  329. local func_switch
  330. local function dispatch_request(pname, args)
  331. local f = REQUEST[pname]
  332. assert(f, string_format("Not found request '%s'", pname))
  333. -- func_switch = func_switch or require "model.func_switch"
  334. -- local errno = func_switch.check_condition(character, args)
  335. -- logger.trace("dispatch_request:%s",pname)
  336. -- if errno == 0 then
  337. return f(character, args)
  338. -- end
  339. -- return {errno = errno}
  340. end
  341. function player.request(pname, args)
  342. -- 以串行化的方式执行玩家请求(简化协程挂起带来的复杂性)
  343. lasttime = skynet_now()
  344. return synchronized(dispatch_request, pname, args)
  345. end
  346. function player.response(pname, args, callback)
  347. trace("dispath response '%s'", pname)
  348. lasttime = skynet_now()
  349. return synchronized(callback, character, args)
  350. end
  351. function player.command(session, cmd, ...)
  352. local f = CMD[cmd]
  353. assert(f, string_format("Not found command '%s'", cmd))
  354. if session == 0 then
  355. f(character, ...)
  356. else
  357. skynet_retpack(f(character, ...))
  358. end
  359. end
  360. function player.disconnect()
  361. if not isready then
  362. skynet.call(usercenter, "lua", "logout", character.uid, skynet.self())
  363. return
  364. end
  365. -- 先派发离线事件
  366. repeat
  367. local ok, msg = xpcall(character.dispatch, traceback, "disconnect")
  368. if not ok then
  369. logger.error(msg)
  370. end
  371. until 0
  372. -- 调用每个模块的下线接口(倒序)
  373. for i=#module, 1, -1 do
  374. local elem = module[i]
  375. if elem.saybye then
  376. local ok, msg = xpcall(elem.saybye, traceback, character)
  377. if not ok then
  378. logger.error(msg)
  379. end
  380. end
  381. end
  382. local ok, msg = xpcall(function()
  383. -- if character.level >= 20 and character.mission > 10030 then -- PK3-919
  384. -- local backups_role = skynet.localname(".backups_role")
  385. -- skynet.send(backups_role, "lua", "logout", character.uid,character.sid,character.backups_all())
  386. -- end
  387. end, traceback)
  388. if not ok then
  389. logger.error(msg)
  390. end
  391. -- 下线时的最后处理
  392. character.set_lastlogout(os.time())
  393. character.flushall()
  394. -- log.logout(character, ipaddr)
  395. skynet.call(usercenter, "lua", "logout", character.uid, skynet.self())
  396. end
  397. function REQUEST.create(_, args)
  398. local nickname = args.nickname or "" -- 角色名
  399. local deviceid = args.deviceid or "NULL-device" -- 玩家设备号
  400. -- local elf = assert(args.elf) -- 初始精灵
  401. local len = string.len(nickname)
  402. if len < 3 or len > 32 then return { errno=STD_ERR.PLYAER_LLLEGAL_NAME } end -- 非法角色名
  403. if character.nickname then return { errno=STD_ERR.PLYAER_LLLEGAL_OPERATION } end -- 非法操作
  404. -- 注册角色名
  405. -- local ok = skynet.call(namecenter, "lua", "register_nickname", nickname, character.uid)
  406. -- if not ok then
  407. -- return { errno=STD_ERR.PLYAER_DUPLICATION_NAME } -- 角色重名
  408. -- end
  409. logger.label(string_format("<Agent %s@%s.%s>", character.channel, character.uid, nickname))
  410. character.set_nickname(nickname)
  411. return ready()
  412. end
  413. -- function REQUEST.pong(_, args)
  414. -- -- Nothing
  415. -- end
  416. local facebook = nil
  417. -- function REQUEST.binding(_, args)
  418. -- local account = assert(character.account)
  419. -- local channel = assert(character.channel)
  420. -- local new_account = assert(args.cloud_id)
  421. -- local passwd = assert(args.cloud_pass)
  422. -- if character.fb then
  423. -- return { errno=6 } -- 已绑定
  424. -- end
  425. -- facebook = facebook or skynet.localname(".facebook")
  426. -- local errno = skynet.call(facebook, "lua", "redirect", account, channel, new_account, passwd)
  427. -- if errno == 400 then
  428. -- return { errno=2 } -- 参数错误
  429. -- elseif errno == 409 then
  430. -- return { errno=1 } -- 帐号已存在
  431. -- elseif errno == 502 then
  432. -- return { errno=5 } -- 网络错误
  433. -- end
  434. -- character.set_fb(new_account)
  435. -- return { errno=0 }
  436. -- end
  437. -- 处理广播消息
  438. function CMD.broadcast(character, pname, args)
  439. if isready then
  440. return send(pname, args)
  441. end
  442. end
  443. --兑换CDK
  444. local mailbox
  445. local parse
  446. -- function REQUEST.gdkReq(character,args)
  447. -- local serial = args.serial
  448. -- -- local cdktypenum = character.cdktypenum
  449. -- -- cdktypenum = cdktypenum or {}
  450. -- -- if cdktypenum[serial] then
  451. -- -- return {errno = STD_ERR.PLYAER_CDK_YET_SET} -- CDK 已经使用过了
  452. -- -- end
  453. -- local serial = string.upper(serial) --PK3-1388 增加CDK统计
  454. -- local ok, errno, awardinfo = skynet.call(manage_proxy, "lua","rpc_call",
  455. -- "span_cdk","use_cdk_state",
  456. -- character.uid, serial, option.sid, character.nickname, character.vip --PK3-1388 增加CDK统计 PK3-1400 VIP等级限制
  457. -- )
  458. -- if not ok then
  459. -- logger.trace(" 使用ckd 服务器异常报错 %s", errno)
  460. -- errno = STD_ERR.PLYAER_CDK_ERR
  461. -- end
  462. -- if errno ~= 0 then
  463. -- return {errno = errno}
  464. -- end
  465. -- -- 检查玩家是否使用过该cdk
  466. -- mailbox = mailbox or require "model.mailbox"
  467. -- mailbox.send(character, 20, "", awardinfo,{
  468. -- module = "cdk", -- 发送模块
  469. -- brief = "礼包", -- 说明
  470. -- context=string.format("cdk_type:%s", serial), -- 上下文
  471. -- })
  472. -- -- cdktypenum[serial] = awardinfo
  473. -- -- character.set_cdktypenum(cdktypenum)
  474. -- return {errno = 0}
  475. -- end
  476. -- function REQUEST.login(character)
  477. -- return {errno = 2000001} -- 异常的登陆请求
  478. -- end
  479. -- function REQUEST.logout(character)
  480. -- logger.trace("## 玩家主动离线")
  481. -- kick("heartbeat logout")
  482. -- return {errno = 0}
  483. -- end
  484. function REQUEST.ping(character)
  485. return {time = os.time()}
  486. end
  487. -- TODO: 游客绑定facebook 或 google 账号
  488. -- function REQUEST.tourist_binding(_, args)
  489. -- logger.trace(" #### 请求账号绑定 %s", stringify(args))
  490. -- local account = args.account
  491. -- local token = args.token
  492. -- local state = args.state--1是facebook 2是google 3 IOS
  493. -- if not(account and token and state) then
  494. -- return {errno = STD_ERR.PLYAER_PARM_LIMIT} -- 参数异常
  495. -- end
  496. -- -- 检查账号是否绑定
  497. -- if character.facebook then
  498. -- return {errno = STD_ERR.PLYAER_BING_FACEBOOK}
  499. -- end
  500. -- if character.google then
  501. -- return {errno = STD_ERR.PLYAER_BING_GOOGLE}
  502. -- end
  503. -- if character.ios then
  504. -- return {errno = STD_ERR.PLYAER_BING_IOS}
  505. -- end
  506. -- -- 检查玩家是否为游客
  507. -- if type(character.tourist) ~= "number" or character.tourist == 1 then -- nil/1:非游客账号 2: 游客账号
  508. -- return {errno = STD_ERR.PLYAER_BING_NO_TOURIST} -- 玩家非游客 绑定失败
  509. -- end
  510. -- local par = {channel = character.channel, token = token, account = account, appid = args.appid, sdk = nil}
  511. -- if state == 1 then -- Facebook
  512. -- -- par.sdk = 'facebook'
  513. -- par.sdk = 2
  514. -- -- 登陆账号
  515. -- elseif state == 2 then -- Google
  516. -- -- par.sdk = 'google'
  517. -- par.sdk = 3
  518. -- elseif state == 3 then
  519. -- par.sdk = 4
  520. -- else
  521. -- return {errno = STD_ERR.PLYAER_PARM_LIMIT} -- 参数异常
  522. -- end
  523. -- -- Verify login token
  524. -- local succ = skynet.call(loginserver, "lua", "login", par)
  525. -- if succ then
  526. -- -- usercenter 账号指向修改
  527. -- local errno = skynet.call(usercenter, "lua", "bingding", character.channel, character.sid, account,character.uid)
  528. -- if errno ~= 0 then return {errno = errno} end
  529. -- if 1 == state then
  530. -- character.set_facebook(account)
  531. -- elseif 2 == state then
  532. -- character.set_google(account)
  533. -- elseif 3 == state then
  534. -- character.set_ios(account)
  535. -- end
  536. -- character.set_account(account)
  537. -- character.set_tourist(1) -- 游客标识修改
  538. -- return {errno = 0}
  539. -- else
  540. -- return {errno = STD_ERR.PLYAER_BING_LOGIN_FAIL} -- 登陆验证异常 登陆失败
  541. -- end
  542. -- end
  543. return player