【从零开始学Skynet】实战篇《球球大作战》(十四):agent跨服务器版

news/2024/10/17 14:25:15/
        至此,我们已完成了《球球大作战》的绝大部分功能,只剩下完 善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 thenreturn {"enter",1,"已在场景"}endlocal snode, sid = random_scene()local sname = "scene"..sidlocal isok = s.call(snode, sname, "enter", s.id, mynode, skynet.self())if not isok thenreturn {"enter",1,"进入失败"}ends.snode = snodes.sname = snamereturn nil
end
这段代码可实现如下几个功能:
  • 定义 s.snode s.name 这两个变量,如果玩家尚未进入战场,这两个值为空;如果已进入,分别存储对应场景服务的节点和名字
  • 调用 random_scene (稍后实现)随机获取一个场景服务。变量snode代表场景服务所在的节点, sid 代表场景服务的 id
  • 向场景服务发送 enter 消息 ,请求进入场景。如果成功进入场景,会给s.snode s.sname 赋值。

        随机选择场景的random_scene方法,我们之前提到过, agent 应尽可能地进入同节点的scene。如下代码所示。
local function random_scene()--选择nodelocal nodes = {}for i, v in pairs(runconfig.scene) dotable.insert(nodes, i)if runconfig.scene[mynode] thentable.insert(nodes, mynode)endendlocal 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 thenreturnends.call(s.snode, s.sname, "leave", s.id)s.snode = nils.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 thenreturnendlocal x = msg[2] or 0local y = msg[3] or 0s.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​​​​​​​

http://www.ppmy.cn/news/47277.html

相关文章

初中级测试工程师,软件测试面试题总结大全(功能/接口/自动化测试)你要的都有...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 一般软件测试的面…

程序员只能吃青春饭?3条晋升之路帮你摆脱程序员中年魔咒!

作为一个程序员 尤其是在这些中 不管是中国 还是美国的这些大的公司里边呢 往往呢 有大概两条这样的一个境界之路 根据你自己个人的这种能力兴趣 其实你可以进行自己的选择 科技大佬们其实往往呢也都是从 这个比较年轻的时候对吧 归国创业也好 还是自己出自于草根 然后一下子凝…

【Python_Scrapy学习笔记(十三)】基于Scrapy框架的图片管道实现图片抓取

基于Scrapy框架的图片管道实现图片抓取 前言 本文中介绍 如何基于 Scrapy 框架的图片管道实现图片抓取,并以抓取 360 图片为例进行展示。 正文 1、Scrapy框架抓取图片原理 利用 Scrapy 框架提供的图片管道类 ImagesPipeline 抓取页面图片,在使用时需…

今天面了个字阿里拿38K出来的,真是纹身师闭眼,秀了我一脸啊

公司前段缺人,也面了不少测试,前面一开始瞄准的就是中级的水准,也没指望来大牛,提供的薪资在15-20k,面试的人很多,但平均水平很让人失望。看简历很多都是4年工作经验,但面试中,不提测…

【已解决】Field ‘id‘ doesn‘t have a default value 错误的解决办法

介绍 这里是小编成长之路的历程,也是小编的学习之路。希望和各位大佬们一起成长! 以下为小编最喜欢的两句话: 要有最朴素的生活和最遥远的梦想,即使明天天寒地冻,山高水远,路远马亡。 一个人为什么要努力&a…

Qt 项目A调用项目B方法(项目架构管理)

前言 项目开发中,如果项目比较大,大多采用多项目的方式,主要是为了方便代码管理,也更开发变得更加方便。操作如下: 注:我用的版本是Qt 5.12.3 一、建立项目目录 要求: 1、项目A为主&#xff…

Java之~ Aop自定义注解日志

大纲步骤: 一,创建需要记录的日志表,创建基础方法。(省略) 二,在需要加记录日志的方法上加Aop注解1,创建一个注解类,Aop中定义一个注解import java.lang.annotation.*; /*** http 请…

Spring IoC注解开发

Component 组件 Controller 控制器 Service 业务 Repository 仓库 这四个标签是为了创建对象 其实他们四个本质都一样,只不过另外三个是Component的别名,在不同层使用容易区分 首先需要加入aop依赖,如果你事先加入spring-context依赖…