-- 排队系统 require "ProtocolCode" require "shared.init" require "utils.init" require "GlobalVar" require "RedisKeys" require "skynet.manager" local queue = require "skynet.queue" local netpack = require "skynet.netpack" local socket = require "skynet.socket" local xxtea = require "xxtea" skynet = require "skynet" local MAX_COUNT = tonumber(skynet.getenv("max_queue")) -- 心跳定时间隔 local HEART_TIMER_INTERVAL = 30 local HEART_TIMEOUT_COUNT_MAX = 3 local CMD = {} local f2u = {} local u2i = {} -- {idx, fd, {lastHeart, timeOutCount, nextCheck}} local idx2u = {} local curIdx = 0 -- 下一个即将进入游戏的玩家索引 local nextIdx = 0 -- 新加的位置 local function getRank(uid) local info = u2i[uid] if not info then return -1 end return (info[1] + MAX_COUNT - curIdx) % MAX_COUNT + 1 end function SendPacket(actionCode, bin, client_fd) if #bin > 0 then bin = xxtea.encrypt(bin, XXTEA_KEY) end local head = string.pack("H", actionCode) return socket.write(client_fd, netpack.pack(head .. bin)) end local function checkQueue(fd) if not f2u[fd] then return end local info = u2i[f2u[fd]] if info then info[3][1] = skynet.timex() end local rank = getRank(f2u[fd]) SendPacket(actionCodes.Sys_checkQueue, MsgPack.pack({rank = rank}), fd) end skynet.register_protocol { name = "client", id = skynet.PTYPE_CLIENT, unpack = function (msg, sz) local data = skynet.tostring(msg, sz) local cmd = string.unpack("H", string.sub(data, 1, 2)) return cmd, string.sub(data, 3) end, dispatch = function(session, address, cmd, data) skynet.ignoreret() if cmd == actionCodes.Sys_checkQueue then checkQueue(session) end end } function CMD.push(uid, fd) uid = tostring(uid) if u2i[uid] then -- 存在] local oldfd = u2i[uid][2] if oldfd and oldfd ~= fd then -- 踢掉老的 SendPacket(actionCodes.Sys_maintainNotice, MsgPack.pack({body = "server_accountOccupied", iskey = true}), oldfd) skynet.timeout(10, function () skynet.call(gate_serv, "lua", "kick", oldfd) end) f2u[oldfd] = nil end u2i[uid][2] = fd f2u[fd] = uid u2i[uid][3] = {skynet.timex(), 0, skynet.timex() + HEART_TIMER_INTERVAL} else -- 新排队的用户 if nextIdx == curIdx and next(idx2u) then -- 满了 return end u2i[uid] = {nextIdx, fd, {skynet.timex(), 0, skynet.timex() + HEART_TIMER_INTERVAL}} f2u[fd] = uid idx2u[nextIdx] = uid nextIdx = (nextIdx + 1) % MAX_COUNT end skynet.call(gate_serv, "lua", "forward", fd, 0, skynet.self()) return getRank(uid) end function CMD.pop() while true do local uid = idx2u[curIdx] if not uid then return end -- 空的 local info = u2i[uid] if not info then idx2u[curIdx] = nil else if info[2] then -- 找到合适的了 u2i[uid] = nil idx2u[curIdx] = nil f2u[info[2]] = nil curIdx = (curIdx + 1) % MAX_COUNT return uid, info[2] else idx2u[curIdx] = nil u2i[uid] = nil end end curIdx = (curIdx + 1) % MAX_COUNT end end -- 下线了 function CMD.socket_close(fd) local uid = f2u[fd] if not uid then return end f2u[fd] = nil local info = u2i[uid] info[2] = nil end function CMD.handle_timeout() local now = skynet.timex() for uid, info in pairs(u2i) do if info[2] and info[3] and now >= info[3][3] then --存在fd 检查心跳 if info[3][1] - now > HEART_TIMER_INTERVAL or now - info[3][1] > HEART_TIMER_INTERVAL then info[3][2] = info[3][2] + 1 info[3][3] = now + HEART_TIMER_INTERVAL if info[3][2] >= HEART_TIMEOUT_COUNT_MAX then skynet.error("timeout! then queued will closed", info[2], uid) skynet.call(gate_serv, "lua", "kick", info[2]) end end end end end function CMD.count() return (nextIdx + MAX_COUNT - curIdx) % MAX_COUNT end return CMD