【从零开始学Skynet】实战篇《球球大作战》(九):agentmgr和nodemgr代码设计

        agentmgr是管理agent的服务,它是登录过程的仲裁服务,控制着登录流程。agentmgr中含有一个列表players,里面保存着所有玩家的在线状态。

        首先,在service/agentmgr目录下新建init.lua,然后开始编写它。

1、玩家类

        根据登录流程可知,玩家会有“登录中”“游戏中”“登出中”这三种状态,代码如下所示:

--状态
STATUS = {
	LOGIN = 2,
	GAME = 3,
	LOGOUT = 4,
}

        定义玩家类mgrplayer和玩家列表playersplayers将会以playerid(玩家id)为索引,引用mgrplayer对象。代码如下所示:

--玩家列表
local players = {}

--玩家类
function mgrplayer()
    local m = {
        playerid = nil,
		node = nil,
        agent = nil,
		status = nil,
		gate = nil,
    }
    return m
end

mgrplayer属性的含义:

  • playerid:玩家id。
  • node:该玩家对应gatewayagent所在的节点。
  • agent:该玩家对应agent服务的id
  • status:状态,例如“登录中”
  • gate:该玩家对应gateway的id。

 下图是mgrplayer对象的示意图:

【从零开始学Skynet】实战篇《球球大作战》(九):agentmgr和nodemgr代码设计_第1张图片

  • agentmgr包含着players表;
  • players表引用着mgrplayer对象;
  • mgrplayer对象的属性gate代表与该玩家对应的gateway服务;
  • 属性agent代表对应的agent服务

 总而言之,agentmgr会保存各玩家的节点信息和状态。

2、请求登录接口

        如下图所示,在阶段③login服务会向agentmgr请求登录(reqlogin),agentmgr会主导阶段④⑤⑥:

【从零开始学Skynet】实战篇《球球大作战》(九):agentmgr和nodemgr代码设计_第2张图片

 编写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。

 

3、请求登出接口 

        除了登录,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会先发送kickagent处理保存数据等事情,再发送exitagent退出服务。由于保存数据需要一小段时间,因此mgrplayer会保留一小段时间的LOGOUT状态。

4、nodemgr代码实现

        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

你可能感兴趣的:(从零开始学Skynet,lua,skynet,服务器开发)