深入浅出制作全自动Mud机器人-主逻辑
-
对于一个机器人来说,并不需要一个主逻辑,完全可以多套逻辑并行作用。
但对于写机器人的你我来说,大脑还是更适合单线程的模式。所以在机器的合适的地方维护一套主逻辑,即降低开发的难度,又增加维护的效率,个人认为是性价比很高的一种方式。
主逻辑的入口
正常情况下,主逻辑是在正常运行时不调用的调用的。所以,在写主逻辑时,从什么地方进入,调用主逻辑,是我们首先要考虑的点。
定时器入口
其实,通过定时器/心跳来调用主逻辑,在我看来是正道。市面上的各种机器人,自动驾驶,本质也是在每个tick计算当前的状态和应该进行的操作,从实时性,反应,效率来看,基于定时器的主逻辑都是最强的。
但是,万恶的但是来了,对于机器来说,完全基于tick来维护太难了。这需要能很细的拆解人物,分配到每个tick,然后还要为tick建立上下文,能保证持续性动作的正常实行。
这个难度太大了,很强,但性价比极地。
所以我完全不建议机器人的主逻辑以tick的方式来实现。
当然,比较简单又要求实时性的子系统可以基于tick处理,典型的就是战斗子系统。
触发入口
做一个特殊的触发,一般是利用Mud的回显机制,然后再执行玩任务是时候,想法让mud调用这个触发。
怎么说的,从一般客户端的设计,很容易的就会形成一套以出发为入口的代码,而且在初期很行之有效。
但是,如果要做一个足够复杂的机器,触发作为入口就会很勉强了,还要面对服务器本身的限制。
回调入口
回调作为入口,就纯靠代码来控制出发主循环。优点是不依赖mud,而且性能和可控性更高。
缺点就是需要有一个完整的统一的架构,将回调和回调后执行的代码组织起来,所有的代码也不能简单的通过出发执行,需要进行一定的抽象。
我的newhelljs就是利用回调入口的机制,执行完当前代码片段后,主动调用App.Next进行控制权的释放,由代码开始调用主逻辑。
主逻辑的组织
在决定主逻辑的调用方式后,我们就要决定主逻辑的代码怎么组织了。常见的可能有以下方法
触发多米洛骨牌
通过预先设置好的一系列触发,通过开关触发的形式,一步一步依次执行。
这种组织方式,配合合适的工具,在任务的小环节上极强的,我在newhelljs里也 做一个plan/task系统来更好的保证骨牌的稳定性。
但在宏观层面,整个机器人架构的层面,这种操作太细了,无法负担起过于复杂的机器架构
判断if队列。
就是收集可靠信息,然后通过一长串if(){}eleseif{} 的形式,判断当前应该做什么。
进一步的,可以采用责任链的实际模式,为不同的判断和对应的处理代码,封装为一个个的类,然后通过id进行全局注册。
这样可以通过一个id的数组,比如["hp","money","heal"]这样的形式,快速的定义和组合不同的工作流程了。
if队列在简单任务里效果极好。但复杂任务的话,复杂度就有点超过if队列的承受范围了。
在newhellljs里,我用责任链去实现了准备模块,效果非常好。
状态机模式
状态机模式是一个很有名的模式,但是在mud机器人里并不好用。
因为Mud机器人里的状态太多了,互相之间的转化太多,用状态机来处理mud,需要维护的状态会指数上升,是一个天文数字。
导致实际使用时都会进入一个中间态,而不是直接转换,很不适用。
我在pkuxkx.noob中尝试引入状态机,结果比较失败。
当然,状态机也有其优势,主要是可配置性强,可以显示的制定进入状态和离开状态时执行的代码。同时也的确客观存在状态这个存在。
所以我在newhelljs里还是小范围的引入了状态机。我给不同的任务设置了不同的stance,在进入不同组的stance会出发stance-leave和stance-enter,这样用户能很方便的在不同类型的任务(比如战斗/非战斗)任务之间执行一定的指令,这也是一个很明显的简化状态机模型。
指令队列缓冲模式。
指令队列缓冲模式指维护一个缓冲池,在空闲时依次弹出最前面的指令执行,然后缓冲池可以进行清除,插入,追加,压入新队列,快照/还原等操作。
这种模式,弹性很大,而且比较接近于人的思维模式,分配一系列任务,依次执行,遇到意外或者判断动态的切换之后的工作。
缺点一样,更复杂,而且更重要的是需要有完善的异常介入模式,对整个机器的完成度要求更高。
newhelljs中使用了Commands模块,把大部分功能都注册为Command,通过App.Next弹出新指令的方式来运行代码。
我的建议
我的建议是,主逻辑的入口,尽量用程序回调这种依赖最少的方式调用。
具体的组织,根据代码的规模
用责任链(固定流程)或者指令队列缓冲(动态维护流程)+意外处理的方式组织。
-
J jarlyyn 在 引用了 此主题