xfox 发表于 2023-4-4 06:02:26

mushclient - 基于wait编程的机器人

本帖最后由 xfox 于 2023-4-4 07:04 AM 编辑

最早开始用mushclient写机器人是用的javascript,当时就听说lua的wait很厉害,但是由于当时我用javascript已经写了好多行代码了,而且也用javascript"完美的解决"了mushclient没有wait的问题,所以就拒绝学习lua,直到最近重玩研究过lua后才发现当时是多么的无知,lua是多么的强大。今天发一点心得供大家参考吧。大牛们请绕道~

最重要的心得就是利用wait来实现同步模式的机器人,而如今大量的机器人都是基于触发器,定时器来实现,根据我的理解,这些都是异步模式。因为人脑的思维是偏向于同步模式的,所以维护一个大型的异步模式的机器人会非常困难,例如你很快就会忘了哪个触发器在什么时候需要关闭,当然你可以实时的创建触发器和定时器,但是代码本质上还是异步的,你无法把触发前的code和触发后的code放到一起。

如今有了wait,那么绝大部分的触发器就都可以说拜拜了,关键是代码流程非常清晰,可读性很高,下面我会用几个例子来演示一下。


xfox 发表于 2023-4-4 06:03:41

本帖最后由 xfox 于 2023-4-4 08:41 AM 编辑

上文提到了大量的触发器会说拜拜,大家可能会问:没有触发器能做机器人吗?答案是,能,非常能。
首先要解释一下触发器的原理,mushclient收到服务器端发过来的文本,第一件事情就是去检查所有的触发器(包括插件),让这些文本的每一行都去匹配那些触发器,如果你处于激活状态的触发器非常多,那么可想而知,你的系统负担也会非常重,使用多行触发会导致更大的开销。机器人高手会只在必要的时候激活触发器,但是这也是需要维护成本,调试的工作量也是巨大。

如果不使用触发器,那我们怎么写机器人?答案是,自己写触发器

听起来好像有点过于玄乎了,其实很简单,原理就是当你需要一个触发器的时候,你开一个定时器,不停的去查询输出窗口的最后几行文本,看是否与你的正则表达式匹配,这个定时器受到mushclient本身的限制,最小精度是0.1秒,所以这篇文章的前提是你对触发器灵敏度要求不能太高,如果想要在0.1秒之内就被触发的话,您请绕行~

这样做的开销看上去很大,其实非常小,尤其是在大型机器人中,你省掉了很多正则表达式匹配的开销。下面上个具体的例子:

<div style="color: rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: Consolas, "Courier New", monospace; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;">-- cmd (string): the cmd will be sent to pkuxkx</span></div><div><span style="color: #6a9955;">-- regex (table): a collection of regex, when any line matches any regex, return </span></div><div><span style="color: #6a9955;">-- timeout (number): timeout</span></div><div><span style="color: #c586c0;">function</span> <span style="color: #dcdcaa;">PkuxkxWaitForNextMatchingLine</span>(<span style="color: #9cdcfe;">cmd</span>, <span style="color: #9cdcfe;">regex</span>, <span style="color: #9cdcfe;">timeout</span>)</div><div>    timeout = timeout <span style="color: #c586c0;">or</span> <span style="color: #b5cea8;">3</span></div><div>    world.<span style="color: #dcdcaa;">Note</span>(<span style="color: #ce9178;">"WaitForNextMatchingLine - never comment this line out, we need an output to eliminate previous partial line"</span>)</div><div>    <span style="color: #c586c0;">local</span> startLineCount = world.<span style="color: #dcdcaa;">GetLineCount</span>()</div><div>    <span style="color: #c586c0;">local</span> deadline = <span style="color: #dcdcaa;">os.time</span>() + timeout</div><div>    <span style="color: #dcdcaa;">Info</span>(<span style="color: #ce9178;">"Executing "</span>..cmd)</div><div>    world.<span style="color: #dcdcaa;">Send</span>(cmd)</div><div>    <span style="color: #c586c0;">repeat</span></div><div>      <span style="color: #c586c0;">local</span> endIndex = world.<span style="color: #dcdcaa;">GetLinesInBufferCount</span>()</div><div>      <span style="color: #c586c0;">local</span> lineCount = world.<span style="color: #dcdcaa;">GetLineCount</span>() - startLineCount</div><div>      <span style="color: #c586c0;">local</span> startIndex = endIndex - lineCount</div><div>      <span style="color: #c586c0;">for</span> i = startIndex, endIndex <span style="color: #c586c0;">do</span></div><div>            <span style="color: #c586c0;">local</span> line = world.<span style="color: #dcdcaa;">GetLineInfo</span>(i, <span style="color: #b5cea8;">1</span>)</div><div>            <span style="color: #c586c0;">for</span> k, v <span style="color: #c586c0;">in</span> <span style="color: #dcdcaa;">ipairs</span>(regex) <span style="color: #c586c0;">do</span></div><div>                <span style="color: #c586c0;">if</span> <span style="color: #dcdcaa;">string.match</span>(line, v) <span style="color: #c586c0;">then</span></div><div>                  <span style="color: #c586c0;">return</span> startIndex, i, k</div><div>                <span style="color: #c586c0;">end</span></div><div>            <span style="color: #c586c0;">end</span></div><div>      <span style="color: #c586c0;">end</span></div><div>      wait.<span style="color: #dcdcaa;">time</span>(<span style="color: #b5cea8;">0.1</span>)</div><div>    <span style="color: #c586c0;">until</span> <span style="color: #dcdcaa;">os.time</span>() > deadline</div><div><span style="color: #c586c0;">end</span></div>
</div>




解释一下:
1. 这个函数上来首先读取输出窗口有多少行,从这行以后开始,所有的输出都需要跟我们预期的正则表达式匹配
2. 向mud发出一条命令
3. 循环查询输出窗口,一旦发现有匹配的行出现,立即返回 (行号,以及匹配到的正则表达式的索引),直至超时

由于输出窗口有个缓冲区的概念,缓冲区是有大小的,例如5000行,而你的真实行号却是自从这个窗口打开算起,可能早已超过10万行,也就是说不是所有的输出都在缓冲区里,太早的内容早就已经被抹掉了,但是这并不影响我们,因为我们关心的是缓冲区的底部。正是因为这个缓冲区的存在,所以我们调用mush API去读取指定行号的时候需要做点小算术,具体参见code,不多解释了

xfox 发表于 2023-4-4 06:04:14

本帖最后由 xfox 于 2023-4-4 06:36 AM 编辑

大家看到上面的例子可能还没太明白有什么用,下面上个有真正用处的例子:
<div style="color: rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: Consolas, &quot;Courier New&quot;, monospace; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">function</span> <span style="color: #dcdcaa;">Sync</span>()</div><div>    <span style="color: #c586c0;">local</span> pattern = <span style="color: #ce9178;">"系统回馈:sync = 0"</span></div><div>    <span style="color: #c586c0;">local</span> line1, line2, index = <span style="color: #dcdcaa;">PkuxkxWaitForNextMatchingLine</span>(<span style="color: #ce9178;">"response sync"</span>, {pattern})</div><div>    <span style="color: #dcdcaa;">print</span>(line1, line2, index)</div><div><span style="color: #c586c0;">end</span></div><div><span style="color: #c586c0;">function</span> <span style="color: #dcdcaa;">PkuxkxTest</span>()</div><div>    wait.<span style="color: #dcdcaa;">make</span>(<span style="color: #c586c0;">function</span>()</div><div>      <span style="color: #dcdcaa;">Sync</span>()</div><div>    <span style="color: #c586c0;">end</span>)</div><div><span style="color: #c586c0;">end</span></div>
</div>

大家看完是不是一脸懵逼?就这有啥用?

其实这个Sync()函数是大大的有用,很多机器人现在被限流就是因为没有很好的跟服务器同步,这个Sync就是用来同步的。

大家看到我这里可没有使用mushclient自带的触发器。

xfox 发表于 2023-4-4 06:04:53

本帖最后由 xfox 于 2023-4-4 07:06 AM 编辑

有人可能会说了,你这样一次只能匹配一个触发器,要是我需要匹配多个触发器怎么办?那么我们再来一个例子看看,同时大家可以看到同步模式的威力:

<div style="color: rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: Consolas, "Courier New", monospace; line-height: 19px; white-space: pre;"><div style="line-height: 19px;"><div><span style="color: #c586c0;">function</span> <span style="color: #dcdcaa;">PkuxkxTest</span>()</div><div>    wait.<span style="color: #dcdcaa;">make</span>(<span style="color: #c586c0;">function</span>()</div><div>      <span style="color: #c586c0;">local</span> patterns = {</div><div>            <span style="color: #ce9178;">"你捡起一根烤鸡腿。"</span>,</div><div>            <span style="color: #ce9178;">"你附近没有这样东西。"</span></div><div>      }</div><div>      <span style="color: #c586c0;">local</span> _, _, index = <span style="color: #dcdcaa;">PkuxkxWaitForNextMatchingLine</span>(<span style="color: #ce9178;">"get jitui"</span>, patterns)</div><div>      <span style="color: #c586c0;">if</span> index == <span style="color: #b5cea8;">1</span> <span style="color: #c586c0;">then</span></div><div>            world.<span style="color: #dcdcaa;">Send</span>(<span style="color: #ce9178;">"do 3 eat jitui"</span>)</div><div>      <span style="color: #c586c0;">else</span></div><div>            world.<span style="color: #dcdcaa;">Send</span>(<span style="color: #ce9178;">"chat 求哪位大大给点买鸡腿的钱吧~"</span>)</div><div>      <span style="color: #c586c0;">end</span></div><div>    <span style="color: #c586c0;">end</span>)</div><div><span style="color: #c586c0;">end</span></div></div></div>

WaitForNextMatchingLine - never comment this line out, we need an output to eliminate previous partial line
Executing get jitui
get jitui
你附近没有这样东西。
> say 求哪位大大给点买鸡腿的钱吧~
你说道:「求哪位大大给点买鸡腿的钱吧~」
> drop jitui
你丢下一根烤鸡腿。
>
WaitForNextMatchingLine - never comment this line out, we need an output to eliminate previous partial line
Executing get jitui
get jitui
你捡起一根烤鸡腿。
> do 3 eat jitui
你拿起烤鸡腿咬了几口。
你拿起烤鸡腿咬了几口。
你拿起烤鸡腿咬了几口。
>


xfox 发表于 2023-4-4 06:51:41

本帖最后由 xfox 于 2023-4-4 06:54 AM 编辑

下面是一个看雪涨轻功的机器人,仅仅使用了一条别名,流程是不是很清晰?


<div style="color: rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: Consolas, &quot;Courier New&quot;, monospace; line-height: 19px; white-space: pre;"><div>__KANXUE = {}</div><div><span style="color: #c586c0;">function</span> <span style="color: #dcdcaa;">OnAliasKanXue</span>(<span style="color: #9cdcfe;">param</span>)</div><div>    wait.<span style="color: #dcdcaa;">make</span>(<span style="color: #c586c0;">function</span>()</div><div>      <span style="color: #c586c0;">if</span> param == <span style="color: #ce9178;">"stop" </span><span style="color: #c586c0;">then</span></div><div>            __KANXUE.<span style="color: #9cdcfe;">running</span> = <span style="color: #569cd6;">false</span>;</div><div>            <span style="color: #dcdcaa;">Info</span>(<span style="color: #ce9178;">"stopping __KANXUE"</span>)</div><div>      <span style="color: #c586c0;">else</span></div><div>            __KANXUE.<span style="color: #9cdcfe;">running</span> = <span style="color: #569cd6;">true</span>;</div><div>            <span style="color: #c586c0;">repeat</span> </div><div>                <span style="color: #c586c0;">local</span> readyForXingzou = <span style="color: #569cd6;">false</span></div><div>                <span style="color: #c586c0;">repeat</span></div><div>                  <span style="color: #c586c0;">local</span> pattern = {</div><div>                        <span style="color: #ce9178;">"你突然发现在路旁的一片积雪上行走%(xingzou%)似乎可以用来练习轻功。"</span>,</div><div>                        <span style="color: #ce9178;">"突然间,你被积雪闪耀着的刺眼的光芒灼伤,只觉头痛欲裂,眼前什么也看不到了!"</span>, </div><div>                        <span style="color: #ce9178;">"雪,雪,还是雪。"</span>,</div><div>                        <span style="color: #ce9178;">"洁白的雪地上零星的散落着几朵脚印。"</span>,</div><div>                        <span style="color: #ce9178;">"环顾四周,到处都是白皑皑的积雪。"</span></div><div>                  }</div><div>                  <span style="color: #c586c0;">local</span> index, result = <span style="color: #dcdcaa;">WaitForMultipleOutputV2</span>(<span style="color: #ce9178;">"l snow"</span>, pattern, pattern)</div><div>                  <span style="color: #c586c0;">if</span> index == <span style="color: #b5cea8;">1</span> <span style="color: #c586c0;">then</span></div><div>                        readyForXingzou = <span style="color: #569cd6;">true</span></div><div>                  <span style="color: #c586c0;">elseif</span> index == <span style="color: #b5cea8;">2</span> <span style="color: #c586c0;">then</span></div><div>                        <span style="color: #dcdcaa;">Error</span>(<span style="color: #ce9178;">"retry in 3 seconds"</span>)</div><div>                        wait.<span style="color: #dcdcaa;">time</span>(<span style="color: #b5cea8;">3</span>)</div><div>                  <span style="color: #c586c0;">end</span></div><div>                <span style="color: #c586c0;">until</span> readyForXingzou <span style="color: #c586c0;">or</span> __KANXUE.<span style="color: #9cdcfe;">running</span> == <span style="color: #569cd6;">false</span></div><div>                </div><div>                <span style="color: #c586c0;">local</span> pattern = {</div><div>                  <span style="color: #ce9178;">"你一路走下来,看着脚印回想方才的步法,轻功水平提高了!"</span>,</div><div>                  <span style="color: #ce9178;">"你摇摇摆摆走在雪地上,忽然脚底一滑,“扑通”摔在了雪堆里,骨碌碌滚了五、六步才停下。"</span></div><div>                }</div><div>                <span style="color: #c586c0;">local</span> index, result = <span style="color: #dcdcaa;">WaitForMultipleOutputV2</span>(<span style="color: #ce9178;">"xingzou snow"</span>, pattern, pattern, <span style="color: #b5cea8;">10</span>)</div><div>                <span style="color: #c586c0;">if</span> index == <span style="color: #b5cea8;">2</span> <span style="color: #c586c0;">then</span></div><div>                  wait.<span style="color: #dcdcaa;">time</span>(<span style="color: #b5cea8;">3</span>)</div><div>                <span style="color: #c586c0;">end</span></div><div>            <span style="color: #c586c0;">until</span> __KANXUE.<span style="color: #9cdcfe;">running</span> == <span style="color: #569cd6;">false</span></div><div>            <span style="color: #dcdcaa;">Info</span>(<span style="color: #ce9178;">"Done"</span>)</div><div>      <span style="color: #c586c0;">end</span></div><div>    <span style="color: #c586c0;">end</span>)</div><div><span style="color: #c586c0;">end</span></div></div>

xfox 发表于 2023-4-4 06:57:19

原以为可以写很多的,发现没有必要继续写了,如果反响不错的话我会继续加一些内容,目前看来就这样吧,欢迎批评指正交流

kkena 发表于 2023-4-4 07:37:01

哇哦,太酷了

kingyeli 发表于 2023-4-21 11:54:17

大神啊,膜拜了

jarlyyn 发表于 2023-4-21 12:34:13

用js的话需要了解下promise

然后再按那个方向自己写一下。

kingyeli 发表于 2023-4-28 16:33:52

大神,等待更新哈。
页: [1] 2
查看完整版本: mushclient - 基于wait编程的机器人