至此,我们已完成了《球球大作战》的绝大部分功能,只剩下完 善agent
,让它和
scene
服务联动了。
1、多个模块
一般而言,代理服务会承载很多系统,比如邮件、成就等,此处涉及的代码较多,容易混
乱,需划分模块。之前实现的service
模块能让服务带有分模块的潜力。
新建
service/agent/scene.lua
用于处理
agent的战斗逻辑,
只需在
init.lua
中引入(
require
)新增的文件,即可使用新文件提供的功能,
service/agent/init.lua
中新增的内容:
require "scene"
拓展知识: 如果后续开发邮件、成就等系统,同样要新建一个文件,每个文件处理一项功能
2、进入战斗
现在进入比赛的功能。在
scene.lua文件中
编写战斗协议处理方法s.client.enter
:
s.client.enter = function(msg)
if s.sname then
return {"enter",1,"已在场景"}
end
local snode, sid = random_scene()
local sname = "scene"..sid
local isok = s.call(snode, sname, "enter", s.id, mynode, skynet.self())
if not isok then
return {"enter",1,"进入失败"}
end
s.snode = snode
s.sname = sname
return nil
end
这段代码可实现如下几个功能:
-
定义 s.snode 和 s.name 这两个变量,如果玩家尚未进入战场,这两个值为空;如果已进入,分别存储对应场景服务的节点和名字。
-
调用 random_scene (稍后实现)随机获取一个场景服务。变量snode代表场景服务所在的节点, sid 代表场景服务的 id 。
-
向场景服务发送 enter 消息 ,请求进入场景。如果成功进入场景,会给s.snode 和 s.sname 赋值。
local function random_scene()
--选择node
local nodes = {}
for i, v in pairs(runconfig.scene) do
table.insert(nodes, i)
if runconfig.scene[mynode] then
table.insert(nodes, mynode)
end
end
local idx = math.random( 1, #nodes)
local scenenode = nodes[idx]
--具体场景
local scenelist = runconfig.scene[scenenode]
local idx = math.random( 1, #scenelist)
local sceneid = scenelist[idx]
return scenenode, sceneid
end
为了模拟合适的匹配机制,random_scene
返回同节点场景服务的概率是其他节点的数倍。
具体做法: 先把所有配置了场景服务的节点都放在表 nodes 中, 同一节点(mynode )会插入多次,使它能有更高被选中的概率。插入 完成后在nodes 表随机选择一个节点( scenenode )。再在选出的节点中 随机选出一个场景(sceneid )
3、退出战斗
当客户端掉线时,agent
需要向场景服务请求退出。要实现该功能,首先得修改resp.kick
,使
agent
在退出前调用s.leave_scene方法。
service/agent/init.lua
中修改的内容:
s.resp.kick = function(source)
s.leave_scene()
--在此处保存角色数据
skynet.sleep(200)
end
然后编写
的
s.leave_scene
方法,它会给场景服务发 送leave消息。
service/agent/scene.lua
中新增的内容:
s.leave_scene = function()
--不在场景
if not s.sname then
return
end
s.call(s.snode, s.sname, "leave", s.id)
s.snode = nil
s.sname = nil
end
4、最后的辅助方法
最后完成几个简单方法。
scene
调用了agent的远程调用方法
send
给客户端发送消息,它的实现如下
所示,这里仅仅是将消息转发到gateway上。
service/agent/init.lua
中新增的内容:
s.resp.send = function(source, msg)
skynet.send(s.gate, "lua", "send", s.id, msg)
end
当玩家要改变移动方向时,客户端会发送 shift协议,经由agent转发,实现如下代码。
service/agent/scene.lua
中新增的内容:
--改变方向
s.client.shift = function(msg)
if not s.sname then
return
end
local x = msg[2] or 0
local y = msg[3] or 0
s.call(s.snode, s.sname, "shift", s.id, x, y)
end
5、运行结果
我们成功编写完所有代码,可以测试了。运行客户端,然后登录、进入场景。可以看到服务端回应的“
进入成功
”等消息,如下图所示:
客户端A(
101
)先登录游戏,然后进入场景,进入时服务端会回应enter
协议并发送
balllist
和
foodlist
协议告诉客户端A
当前的战场信息。服务端会随机添加食物,发送
addfood
协 议。当客户端A
改变移动方向(
shift
)时,服务端会一直广播
move
协 议。稍后客户端B
(
102
)登录,如果进入同一场景,客户端
A
会收 到“enter,102...”
的信息。客户端
B
获得的战场信息
balllist
也会包含玩家101(客户端
A
)的信息,且收到客户端
A
的移动协议。
完整项目地址:
https://gitee.com/frank-yangyu/ball-server