agentmgr是管理agent的服务,它是登录过程的仲裁服务,控制着登录流程。agentmgr中含有一个列表players,里面保存着所有玩家的在线状态。
首先,在service/agentmgr目录下新建init.lua,然后开始编写它。
根据登录流程可知,玩家会有“登录中”“游戏中”和“登出中”这三种状态,代码如下所示:
--状态
STATUS = {
LOGIN = 2,
GAME = 3,
LOGOUT = 4,
}
定义玩家类mgrplayer和玩家列表players,players将会以playerid(玩家id)为索引,引用mgrplayer对象。代码如下所示:
--玩家列表
local players = {}
--玩家类
function mgrplayer()
local m = {
playerid = nil,
node = nil,
agent = nil,
status = nil,
gate = nil,
}
return m
end
mgrplayer属性的含义:
下图是mgrplayer对象的示意图:
总而言之,agentmgr会保存各玩家的节点信息和状态。
如下图所示,在阶段③,login服务会向agentmgr请求登录(reqlogin),agentmgr会主导阶段④⑤⑥:
编写reqlogin方法代码如下所示:
s.resp.reqlogin = function(source, playerid, node, gate)
local mplayer = players[playerid]
--登陆过程禁止顶替
if mplayer and mplayer.status == STATUS.LOGOUT then
skynet.error("reqlogin fail, at status LOGOUT " ..playerid )
return false
end
if mplayer and mplayer.status == STATUS.LOGIN then
skynet.error("reqlogin fail, at status LOGIN " ..playerid)
return false
end
--在线,顶替
if mplayer then
local pnode = mplayer.node
local pagent = mplayer.agent
local pgate = mplayer.gate
mplayer.status = STATUS.LOGOUT,
s.call(pnode, pagent, "kick")
s.send(pnode, pagent, "exit")
s.send(pnode, pgate, "send", playerid, {"kick","顶替下线"})
s.call(pnode, pgate, "kick", playerid)
end
--上线
local player = mgrplayer()
player.playerid = playerid
player.node = node
player.gate = gate
player.agent = nil
player.status = STATUS.LOGIN
players[playerid] = player
local agent = s.call(node, "nodemgr", "newservice", "agent", "agent", playerid)
player.agent = agent
player.status = STATUS.GAME
return true, agent
end
做了如下几件事情:
1)登录仲裁:判断玩家是否可以登录(仅STATUS.GAME状态)。
2)顶替已在线玩家:如果该角色已在线,需要先把它踢下线。对应表3-4的阶段④⑤。
3)记录在线信息:将新建的mgrplayer对象记录为STATUS.LOGIN(登录中)状态。
4)让nodemgr创建agent服务,对应流程图的阶段⑥。待创建完成且agent加载了角色数据后,才往下执行。
5)登录完成:设置mgrplayer为STATUS.GAME状态(游戏中),并返回true及agent服务的id。
除了登录,agentmgr还负责登出的仲裁。定义远程调用方法reqkick,代码如下所示:
s.resp.reqkick = function(source, playerid, reason)
local mplayer = players[playerid]
if not mplayer then
return false
end
if mplayer.status ~= STATUS.GAME then
return false
end
local pnode = mplayer.node
local pagent = mplayer.agent
local pgate = mplayer.gate
mplayer.status = STATUS.LOGOUT
s.call(pnode, pagent, "kick")
s.send(pnode, pagent, "exit")
s.send(pnode, pgate, "kick", playerid)
players[playerid] = nil
return true
end
它的功能对应流程图的阶段④⑤。agentmgr会先发送kick让agent处理保存数据等事情,再发送exit让agent退出服务。由于保存数据需要一小段时间,因此mgrplayer会保留一小段时间的LOGOUT状态。
nodemgr即节点管理服务,每个节点会开启一个。目前它只有一个功能,即提供创建服务的远程调用接口。
在service/nodemgr目录下新建init.lua,代码如下所示:
local skynet = require "skynet"
local s = require "service"
s.resp.newservice = function(source, name, ...)
local srv = skynet.newservice(name, ...)
return srv
end
s.start(...)
远程调用方法newservice只是简单地封装了skynet.newservice,并返回新建服务的id。
项目完整下载地址:https://gitee.com/frank-yangyu/ball-server