深入浅出制作全自动Mud机器人-移动失败处理
-
对于整个移动模块来说,最核心的可能就是移动失败处理了。
本身对于大部分系统来说,失败/意外流程处理都是最复杂的。而移动模块的失败处理大部分面对的是wiz的恶意,所以更强调可扩展性和可维护性。
对于移动失败,本质来说,就是:
- 发出移动指令
2.预期会收到成功信号,或者各种失败信号
3.如果收到成功信号,则对当前移动的这一步进行核销。
4.如果受到的是失败信号,则根据具体的失败信号数据进行处理,重规划路线/重试/放弃失败。
因此,移动失败是一个很典型是预期管理
我做的预期管理模块有很大一部分目的就是为了移动失败处理设计的。如果是其他语言/客户端的机器,也能通过各种类库或者触发器分组的功能进行预期管理。
以newhelljs为例,代码地址
App.Map.StepPlan = new App.Plan( App.Map.Position, function (task) { App.Move.RetryStep = false let tt = task.AddTimer(App.Map.StepTimeout, function (timer) { return App.Map.OnStepTimeout() }).WithName("timeout") task.AddCatcher("core.longtimestep", function () { tt.Reset(App.Move.LongtimeStepDelay) return true }) task.AddCatcher("core.retrymove", function () { App.Move.RetryStep = true return true }) task.AddCatcher("core.movereset").WithName("movereset") task.AddCatcher("core.wrongway").WithName("wrongway") task.AddCatcher("core.walkbusy").WithName("walkbusy") task.AddCatcher("core.walkresend").WithName("walkresend") task.AddCatcher("core.walkretry").WithName("walkretry") task.AddCatcher("core.walkfail").WithName("walkfail") task.AddCatcher("core.blocked2", (catcher, event) => { if (App.Core.Room.Current.ID == "") { return true; } }).WithName("blocked2") task.AddCatcher("core.blocked", (catcher, event) => { catcher.WithData(event.Data) }).WithName("blocked") task.AddCatcher("core.needrest").WithName("needrest") }, function (result) { switch (result.Type) { case "cancel": break default: switch (result.Name) { case "timeout": break case "movereset": App.Map.Room.ID = "" App.Map.Retry() break case "wrongway": if (App.Move.RetryStep) { App.Map.Resend(0) return } App.Map.Room.ID = "" App.Sync(() => { App.Map.Retry() }) break case "walkbusy": App.Map.Resend() break case "walkresend": App.Map.Resend(0) break case "walkretry": App.Map.Retry() break case "blocked": App.Move.OnBlocker(result.Data) break case "blocked2": App.Core.Blocker.BlockStepRetry() break case "needrest": App.Move.NeedRest() break case "walkfail": App.Move.OnWalkFail() break default: } } } )很明显,是对于整个移动做了预期。
首先这个预期是基于App.Map.Position的
Map库中有两个Position
- App.Map.Position 当前房间范围
- App.Map.MovePosition 当前移动的范围
所以,当成功移动时,整个移动处理会因为离开范围而被"cancel"
当没有Cacnel时,说明意外出现了。
不同的信号(事件)会根据在switch里做分支判断,调用各种异常。
这也就是整个失败处理的底层逻辑
在 完全基于hmm开发的hongchengjs中,我为了可维护性,做了些调整,将常见的移动失败作为一个文本保存,启动时调入内存放在一个Hash表里,直接做表匹配模拟事件,算是一个小的结构优化。
在失败处理中,有四个处理是最为典型的
第一个是路线错误
也就是"core.wrongway"事件。
触发后会清空当前房间信息,调用当前移动的Retry方法进行重新规划和移动。
第二中是路线禁用
也就是"core.blocked2"事件。
出发该事件后,说明被拦截,而且不该击杀然路Npc,会把当前房间和目标房间的出口临时禁用,然后重新规划路线。
第三种是插入战斗
"core.blocked"事件
会把当前移动进行快照,然后击杀拦路npc,再对快照进行还原,继续移动。也是很经典的形式。
最后是重新规划
"walkretry"事件
这个事件和路线错误一样都是直接调用重新规划。
区别是这个事件本质是一个衍生事件,在我的事件系统中,一般会调用Filter进行处理,一般是调整当前Move的上下文标签。然后再根据最新上下文标签进行重新规划。
这四种基本就是主要的移动失败处理的内容。当然,不同的Mud可能还会有一些细节上的调整。
移动失败处理的主要处理方式就是
- 重试。
- 调整上下文,重新规划,避免失败出口。
- 挂起移动,解决意外,还原移动并继续,
- 发出移动指令
-
J jarlyyn 被引用 于这个主题