<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[深入浅出制作全自动Mud机器人-指令队列]]></title><description><![CDATA[<p dir="auto">机器人的代码组织有很多种方式。</p>
<p dir="auto">我自己的机器人其实经历过3个阶段</p>
<ol>
<li>helllua ,使用的是回调链</li>
<li>pkuxkx.noob ,试图尝试使用状态机，后来切换到指令队列</li>
<li>newhelljs 精简的指令队列</li>
</ol>
<p dir="auto">这里就说下我目前使用的指令队列结构。</p>
<p dir="auto">老规矩，线上代码</p>
<p dir="auto"><a href="https://github.com/hellclient-scripts/newhelljs/blob/main/script/helllibjs/command/command.js" target="_blank" rel="noopener noreferrer nofollow ugc">代码地址</a></p>
<h2>什么是指令队列</h2>
<p dir="auto">其实指令队列在Rts游戏以及衍生出的Moba游戏里是很常见的。</p>
<p dir="auto">就是在war3,starcraft或者dota2里，按住shift，依次发布多个指令，这些指令就会进入队列，一个一个执行完。</p>
<p dir="auto">这很策略，巧的是，写Mud全自动机器人也是个策略活。很自然的，我就引入了指令这个概念。</p>
<h2>指令的实现</h2>
<p dir="auto">指令其实很简单</p>
<pre><code class="language-javascript">    class Command {
        constructor(name, data) {
            this.Name = name
            this.Data = data
            if (module.Debug) {
                this.Stack = (new Error()).stack
            }
        }
        Context = {}
        Name = ""
        Stack = ""
        Data = null
        OnEvent = null
    }
</code></pre>
<p dir="auto">主体就是Name和Data,以及后续会被队列引入的上下文Context。</p>
<p dir="auto">那么执行的代码呢？</p>
<p dir="auto">执行的代码是分离开的概念，放在指令执行器里.</p>
<p dir="auto">执行其的作用是传入队列和 指令，然后初始化对应指令功能的模块</p>
<p dir="auto">比如</p>
<pre><code class="language-javascript">    module.ExecutorFunction = function (commands, running) {
        running.OnStart = function (arg) {
            running.Command.Data()
        }
    }
</code></pre>
<p dir="auto">为什么把指令和执行其分开呢？</p>
<p dir="auto">因为这样指令队列就是纯数据不包含代码，方便Debug和进行快照/恢复</p>
<h2>基础队列</h2>
<p dir="auto">很明显，我们的队列，就是一个Command数组</p>
<p dir="auto">我们可以对数组进行Insert和Append操作。</p>
<p dir="auto">可以通过Next操作弹出第一个指令执行</p>
<p dir="auto">然后，我们还有一个压入指令组的功能(PushCommands)</p>
<p dir="auto">就是冻结当前队列，执行新队列，可以Pop中断继续执行前一个队列，同时切换队列的上下文(Context, 不同指令之间共享的运行时数据)</p>
<p dir="auto">这个结构下，队列Quesus，本质是一个 Command的二位数组</p>
<h2>控制结构</h2>
<p dir="auto">当我们有了多层的数组后，我们就可以实现简单的控制结构了。</p>
<p dir="auto">我定义了以下控制接口</p>
<ul>
<li>Push/PushCommands 压入Queue</li>
<li>Pop 放弃当前Queue剩余内容，返回之前的调用队列</li>
<li>Snap/Rollback 做快照/回滚快照。由于 Command是纯数据结构，就是克隆一份Queue出来</li>
<li>Append/Insert 在当前队列的最前端/后端插入指令</li>
<li>Fail 报错失败，会废弃所以没有定义FailCommand的队列，直到队列全空。FailCommand相当于一般语言的try..catch</li>
<li>FinalCommand 就算Pop中断也会执行的指令</li>
</ul>
<h2>意外应对机制</h2>
<p dir="auto">本质来说，指令队列是预先定义蓝图，在顺利的情况下完全实行。</p>
<p dir="auto">但实际上，往往会有意外发生，需要跳过当前指令和指令队列强行执行其他的。</p>
<p dir="auto">这时候，从逻辑上我们必须提供一个快照和回滚的机制，确保意外处理后有可能继续执行当前工作的方法。</p>
<p dir="auto">这就是我对指令队列打的补丁。</p>
<p dir="auto">相当于 Mud机器中不止有一个主逻辑，只是平时其他的逻辑都是蛰伏状态。当意外发生时，副逻辑介入，先保留住逻辑现场，处理完之后，再根据实际情况决定是否要恢复主逻辑运行(实际很少这样处理)</p>
<h2>异步与同步</h2>
<p dir="auto">实际上Command本质是一个轻量级的Promise。是一个对未来的期望，是异步(在将来)执行的代码。</p>
<p dir="auto">所以，很多时候，我们需要和实际指令进行同步，就是等待Mud服务器返回一个特殊的返回，确保之前的指令都已经执行完毕了。</p>
<p dir="auto">我一般会用nobusy和sync 的Command来实现这个功能</p>
<h2>典型代码</h2>
<p dir="auto">以我的炼药采买为例</p>
<pre><code class="language-javascript">    Lianyao.BuyAll = (cmds) =&gt; {
        $.PushCommands(
            $.To("65"),
            $.Do(cmds.join("\n")),
            $.Do("i"),
            $.Wait(1000),
            $.Sync(),
            $.Function(Lianyao.Make)
        )
        $.Next()
    }
</code></pre>
<p dir="auto">这个能很明显的看出队列的用法</p>
<p dir="auto">规划一堆指令，Sync去订都执行完毕了，然后继续后续。</p>
<p dir="auto">最后$.Next释放控制权</p>
<h2>指令队列的挑战</h2>
<p dir="auto">指令队列或者其他纯代码逻辑控制的驱动方式，最大的挑战其实是内存泄漏。</p>
<p dir="auto">比如如果要循环执行指令的话，不停PushCommands会堆很多很多层空队列，直到爆栈为止。</p>
<p dir="auto">这是必须通过在最外层Insert指令，而不是PushCommands,才能避免内存泄漏</p>
]]></description><link>https://forum.hellclient.com/topic/22/深入浅出制作全自动mud机器人-指令队列</link><generator>RSS for Node</generator><lastBuildDate>Mon, 08 Jun 2026 10:33:53 GMT</lastBuildDate><atom:link href="https://forum.hellclient.com/topic/22.rss" rel="self" type="application/rss+xml"/><pubDate>Tue, 20 Jan 2026 09:33:33 GMT</pubDate><ttl>60</ttl></channel></rss>