local skynet = require "skynet" -- local socket = require "skynet.socket" -- local sprotoloader = require "sprotoloader" local logger = require "logger" local timer = require "timer" local player = require "model.player" local cjson = require "cjson" -- local JsSProto = require "JsSProto" local websocket = require "http.websocket" local traceback = debug.traceback local skynet_retpack = skynet.retpack local skynet_call = skynet.call local skynet_send = skynet.send local string_pack = string.pack local string_format = string.format local websocket_write = websocket.write local trace = logger.trace local warn = logger.warn local handle_request = player.request local handle_response = player.response local handle_command = player.command local handle_disconnect = player.disconnect local pbSproto = require "pbSproto" local cjson_decode = cjson.decode -- 解码 local cjson_encode = cjson.encode -- 编码 -- local mydispatch = JsSProto.decode -- local mypack = JsSProto.encode local mydispatch = pbSproto.decode local mypack = pbSproto.encode local fd -- local host = sprotoloader.load(1):host( "package") -- local pack = host:attach(sprotoloader.load(2)) local abandon local watchdog local protocol local window = 0 local tracker = {} local function write(data) return websocket_write(fd, data, "binary") end local function send(pname, args, callback) return write(mypack(pname, args)) end local function kick(reason, ...) if abandon then return end if reason then logger.error(reason, ...) end abandon = true skynet_call(watchdog, "lua", "close", fd) end local kill_timer local function throw_exception(type, brief, linger) assert(type) brief = brief or "unknown" if abandon then return end -- send('exception', { type=type, brief=brief }) send('exception_nty', { errno=brief }) linger = linger or 500 kill_timer = timer(linger, function() kick("exception: { type=%s, brief=%s }", type, brief) end) end local function dispatch_request(pname, args, response) if abandon then trace("drop a request message: %s", pname) return end local r = handle_request(pname, args) if response and r then write(response(r)) end end local function dispatch_response(session, args) local ctx = assert(tracker[session]) local pname = assert(ctx.pname) local callback = assert(ctx.callback) tracker[session] = nil if abandon then trace("drop a response message: %s", pname) return end handle_response(pname, args, callback) end skynet.register_protocol { name = "client", id = skynet.PTYPE_CLIENT, unpack = function (msg, sz) return mydispatch(msg, sz) end, dispatch = function(_, _, type, pname, ...) skynet.ignoreret() local f = (type == "REQUEST") and dispatch_request if f then local ok, msg = xpcall(f, traceback, pname, ...) if not ok then kick("pname: %s, err: %s", pname, msg) end end end } local CMD = {} function CMD.start(ctx) local args = assert(ctx.args) local gate = assert(ctx.gate) local ipaddr = assert(ctx.ipaddr) fd = assert(ctx.fd) watchdog = assert(ctx.watchdog) protocol = ctx.protocol websocket.forward(fd, protocol, ipaddr) local resp = player.start({ args = args, ipaddr = ipaddr, send = send, kick = kick, throw_exception = throw_exception, pbSproto = pbSproto, }) skynet_call(gate, "lua", "forward", fd) local skynet_sleep = skynet.sleep skynet.fork(function() collectgarbage "collect" while true do skynet_sleep(60*100) local kb, bytes = collectgarbage "count" if kb >= 4096 then logger.trace("内存超过4M,将启动垃圾回收") collectgarbage "collect" end end end) return resp end local suspend = nil local dispose = nil local function coroutine_yield() local co = coroutine.running() suspend = suspend or {} table.insert(suspend, co) skynet.wait(co) if dispose then dispose() end end local function coroutine_resume() suspend = suspend or {} local ref = 0 for _, co in ipairs(suspend) do ref = ref + 1 skynet.wakeup(co) end if ref > 0 then local co = coroutine.running() dispose = function() ref = ref - 1 if ref == 0 then skynet.wakeup(co) end end skynet.wait(co) end end function CMD.disconnect() -- todo: do something before exit -- skynet.sleep(1000) if kill_timer then kill_timer() end handle_disconnect() websocket.clear_pool(fd) coroutine_resume() skynet.exit() end -- Reference: watchdog.command.hotfix function CMD.exit() skynet.exit() end -- Reference: usercenter.command.maintain function CMD.maintain(...) local args=select(1, ...) local key if select("#",...)==0 then key=STD_ERR.SERVER_START_MAINTENANCE --"服务器开始维护" else key=args end throw_exception("maintain",key, 300) coroutine_yield() end -- Reference: usercenter.command.login function CMD.multilogin() throw_exception("multilogin", STD_ERR.ACCOUNT_LOGGING_ANOTHER_DEVICE, 100) --"帐号正在其它设备登录" coroutine_yield() end function CMD.broad_cast_msg(pname, args) assert(pname) trace("CMD.broad_cast_msg: %s", pname) send(pname, args or {}) end function CMD.kick(reason, ...) -- send('exception',{ type="kick", brief=reason }) kick(reason, ...) end skynet.start(function() logger.label("") skynet.dispatch("lua", function(session,_, cmd, ...) trace("handle command: '%s'", cmd) local f = CMD[cmd] if f then if session == 0 then f(...) else skynet_retpack(f(...)) end else handle_command(session,cmd, ...) end end) end)