agent_queued.lua 3.69 KB
-- 排队系统

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