Blame view

src/services/queued.lua 4.12 KB
5e6af9d6   zhouhaihai   排队功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  -- 排队系统
  
  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 gate_serv
  
  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()
  		cs(function()
  			if cmd == actionCodes.Sys_checkQueue then
  				checkQueue(session)
  			end
  		end)
  	end
  }
  
  function CMD.open(serv)
  	gate_serv = serv
  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
  
  
  local function 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
  	skynet.timeout(100, handle_timeout)
  end
  
  
  skynet.start(function()
  	skynet.dispatch("lua", function(session, source, command, ...)
  		-- skynet.trace() --执行序的跟踪统计
  		local f = CMD[command]
  		if f then
  			skynet.ret(skynet.pack(f(...)))
  		end
  	end)
  
  	skynet.info_func(function()
  		local info = {}
  		info.count = (nextIdx + MAX_COUNT - curIdx) % MAX_COUNT
  		return info
  	end)
  
  	cs = queue()
  
  	skynet.timeout(100, handle_timeout)
  end)