曲江轻舟

触发器配置

通常触发器是用来检测输入的数据是否符合预定义的规则,如果符合规则就执行某项操作,否则就忽略。

Mud游戏有些练功和任务系统是非常枯燥和乏味的重复性任务,这时可以借助触发器辅助玩家自动完成一部分重复性操作。如果运用得当,触发器比玩家手动操作效率高很多,为玩家节省出更多的时间与其他人交流。

Easymud应用中,触发器是用于测试来自Mud服务器的消息,是否匹配预定义的正则表达式以及特定的ANSI属性。当匹配正则表达式时,执行成功匹配脚本。额外的,还可以设置匹配失败时执行失败脚本。

例如,可以用触发器检测玩家状态信息中的饥饿值,当低于某个指标时自动执行进食的任务。

构成触发器的两个要素正则表达式执行脚本

  • 正则表达式Easymud的触发器中,正则表达式的语法规则与大部分编程语言中的正则表达式大同小异,但更多的与Python语言中的语法兼容。具体的内部实现是Rust语言中的Regex包和fancy-regex包,如果在使用过程中有疑问需要追根溯源,可前往其官方文档页面查询。链接地址为:https://docs.rs/regex/latest/regex/https://docs.rs/fancy-regex/latest/fancy_regex/

  • 脚本Lua脚本,分为成功脚本和可选的失败脚本。大部分情况下只需要配置成功脚本即可。当来自Mud服务器的消息匹配触发器中的正则表达式时,将执行成功脚本;若不匹配,且配置了失败脚本,则会执行失败脚本。

触发器列表

在触发器配置界面,通过触发器列表框可以管理触发器。

在触发器列表框中点击鼠标右键,从弹出的菜单中选择对应的操作进行增、删、改、查、启用、禁用、复制、剪切、粘贴等操作。

在弹出菜单中的操作名后面通过括号备注了该项操作的快捷键,当触发器列表栏具有焦点时,可以按下对应操作的快捷键来激活操作。

在触发器列表栏中的空白处点击鼠标右键,或按下对应的快捷键,可以创建新的触发器集合包。

为了方便管理触发器,Easymud中的触发器都归于某个集合包中。

在应用中每个连接档案(profile)最多允许创建255个触发器集合包,每个集合包下最多允许添加255个触发器,每个触发器最多可以包含255个正则表达式。

在触发器列表中,集合包可以被启用或禁用。被禁用的集合包内所有的触发器都不会进行测试消息。

当应用程序接收到服务器发来的一条消息时,这条消息会被所有有效的触发器进行测试。在列表中越靠上的触发器越优先进行测试。

有效触发器:自身处于启用状态,所在的集合包处于启用状态,至少包含一条处于启用状态的表达式。

触发器配置页

选择某个触发器,在触发器配置页签,可编写触发器匹配成功后执行的脚本,以及编写可选的匹配失败后执行的脚本。

在这里要根据情况选择表达式合并方式。

表达式合并方式

当玩家希望一个触发器需要进行多个规则的匹配时,需要选择恰当的合并方式对该触发器下多个表达式的匹配结果进行合并。

比如,Mud服务器可能会发送不同的描述信息,来表达相同的意思时,往往写一个表达式来处理多种不同的信息会使得表达式很复杂或很长,这时就可以将复杂逻辑拆分为多个简单的表达式进行匹配,然后将匹配结果按照合并方式合并。

举例说明:

当玩家角色处于饥渴状态需要拿出身上的葫芦喝水,但是身上没有葫芦或者有葫芦但里面没有水,Mud服务器可能会发送如下信息:

> 你身上也没有东西可以解渴。

> 你已经将葫芦里的清水喝得一滴也不剩了。

> 葫芦已经被喝得一滴也不剩了。

这时候可以设置一个触发器,在其中添加几个表达式,分别匹配不同的描述信息,只要任意一个表达式匹配,都表明角色应该获取一个葫芦并且灌上水,然后喝水。

我们创建一个触发器名叫灌水,然后添加三个表达式,可以不用为表达式特意命名,使用其自动序号作为名称即可。

第一个表达式用来匹配"> 你已经将葫芦里的清水喝得一滴也不剩了。"这条消息。

第二个表达式用来匹配"> 葫芦已经被喝得一滴也不剩了。"这条消息。

第三个表达式用来匹配"> 你身上也没有东西可以解渴。"这条消息。

好了,我们不想掉头发,只需要将表达相似含义的服务器消息进行穷举,其中任意一条表达式匹配中,就能执行成功脚本。在脚本中发送命令序列:捡起一个葫芦,给葫芦灌上水,喝水!

下图就是无脑匹配的触发器执行效果:

再看下合并选项和执行的脚本:

这个例子中,多个表达式的合并方式就要选择"部分成功",意思是任意一个表达式匹配就可以执行成功脚本。

表达式结果合并方式共有四种选项:

  1. 全部成功 意思是触发器下的所有表达式都要匹配成功,才能执行成功脚本。

  2. 部分成功 意思是触发器下的任意一个表达式匹配成功,就执行成功脚本。

  3. 部分失败 意思是触发器下的任意一个表达式匹配失败,就执行成功脚本。

  4. 全部失败 意思是触发器下的所有表达式都匹配失败了,才能执行成功脚本。

这个脚本只包含一个非常简单的函数调用语句:

cmd("#get hulu;fill hulu;drink")

这条语句的作用是调用应用内置函数cmd,传入一个表示命令序列的字符串,cmd函数会将传入的命令序列分解成单独的命令按顺序一条一条的发送给服务器,等同于我们在命令行输入如下命令:

get hulu ↵
fill hulu ↵
drink ↵
解码后匹配

Mud消息采用了ANSI标准+扩展的Mud文字协议,增强文字内容的视觉表现。Easymud程序从Mud服务器接收到的消息是普通文本加描述符号的混合数据,大部分情况下我们只是希望对纯文字内容进行检测匹配,就无需勾选"解码后匹配"这个选项。

如果想要检测文本消息内容,同时还要考察文本的样式,比如颜色、字号等,从而决定是否执行触发器脚本,就需要勾选"解码后匹配"这个选项。

勾选和不勾选这个选项,除了支持的匹配维度有差异外,还在触发时机和执行性能上有差异。

当默认不勾选时,来自服务器的消息到达应用程序后,就立刻将其中的纯文本内容抽离出来发送给触发器服务进行检测,保证最快的反应速度。

当勾选时,来自服务的消息到达应用程序后,先要进行表现样式处理,也就是解码,然后将解码后的数据按照一个样式一段数据的方式发给触发器服务进行检测。举例说明:假如接收到一个文本段落,其中的文字内容按顺序有三个表现样式,那么这个段落经过解码处理后就会拆分成三个数据段发送给触发器服务进行检测!

再以下图为例说明:

图中第一行"> 你拿起葫芦咕噜噜地喝了几口清水。"这条消息中,"> 你拿起"是第一个样式,"葫芦"与之前的文字颜色不同因此是第二个样式,"咕噜噜地喝了几口清水"又与"葫芦"的颜色不同因此是第三个样式,所以这句话最终会解码成为三段数据。

如果没有勾选"解码后匹配",整个一行文本"> 你拿起葫芦咕噜噜地喝了几口清水。"会一次性发给触发器服务。

而若勾选了"解码后匹配",这行文本经过解码处理后拆分为三段带有样式信息的数据发送给触发器服务。编写表达式要注意匹配的内容只有一个样式段的文本!

成功脚本与失败脚本

通常情况下,我们都是只考虑匹配成功后执行成功脚本的。

但如果,我就是想在匹配失败时执行某个操作,那就编写失败后执行的脚本,不要让它为空!

请慎用失败脚本!因为服务器消息千千万,匹配失败是绝大多数的情况,那就要不停的执行失败脚本啦🙃!除非可以精准的计算时机,通过其他脚本来启用这个触发器,然后及时的让其他脚本来关闭或者自我关闭触发器。

表达式配置页

从触发器列表中选中一个表达式,可以对表达式进行编辑和测试。

表达式有两项主要作用:匹配消息,和在匹配中的基础上捕获内容。

正则表达式可以是一条普通的纯文本,也可以用其特有的语法书写。对于没学习过正则表达式的玩家,可以直接写普通文本,若有必要就多建几个表达式就好了!😄

如果要使用正则表达式的语法书写,请注意行首"^"和行尾"$"两个符号的使用。

由于Mud服务器常常将多行消息一次性发送到客户端,而Easymud也会将多行文本作为整体发送给触发器服务,因此我们的表达式在使用行首和行尾符号时,一定要加上多行标记"(?m)"才能正确匹配目标文本。

例如客户端从服务器接收到如下所见的消息,我们希望用正则表达式匹配其中的"│【饮水】 219 / 300 [缺水]"这一段内容:

┌───个人状态────────────┬───────────────────┐
│【精神】 145 / 145 [100%] │【精力】 100 / 100 (+ 0) │
│【气血】 131 / 131 [100%] │【内力】 6 / 6 (+ 0) │
│【真气】 0 / 0 [ 0%] │【战意】 100% [正常] │
│【食物】 262 / 300 [缺食] │【潜能】 6253 │
│【饮水】 219 / 300 [缺水] │【经验】 780 │
├───────────────────┴───────────────────┤
│【状态】 健康 │
└──────────────────────────────北大侠客行────┘

需要这样写正则表达式:

(?m)^│【饮水】\s+(?<current>\d+)\s*/\s+(?<max>\d+).*

因为这一大段内容是作为一个整体发送给触发器服务的,正则表达式需要以多行模式来检测其中的某部分内容是否符合测试条件。

这条表达式中的"(?<current>\d+)"表示声明一个捕获分组,组名叫做"current",相当于在表达式中声明了一个变量名,这个变量可以保存由该组捕获的消息内容,其中的(?<组名>)是固定写法,在圆括号里面组名后面写上捕获目标。本例中捕获的目标用"\d+"进行限定。"\d"意思是digit数字,"+"表示一个及以上,合起来表示捕获一个或多个连续的数字。在上面的示例消息中,我们就可以捕获到"219"这串数字,并保存在叫做"current"的捕获组中。

注:正则表达式捕获组捕获到的内容都是字符串类型的,即使是"219"这样字面上表示数字的内容,也是以字符串保存的,在后续的Lua脚本使用这个内容时需要做相应的类型转换,比如var_1 = tonumber("219")

表达式属性
  • 扩展模式 若希望使用增强表达式语法,可勾选此项。具体有哪些增强,请参考官网文档:fancy_regex - Rust (docs.rs)

  • 捕获分组 在正则表达式中出现成对的圆括号,且圆括号未被"\"符号转义,就是分组,例如:表达式"(\d+)\s+/\s+(\d+)"中就出现了两个分组。如果希望将分组捕获的内容传入脚本中使用,就勾选"捕获分组"。

测试文本

在测试文本框中输入普通的文本消息,可以用来离线测试我们编写的表达式是否符合我们的设想那样匹配消息。

匹配结果

使用测试文本对表达式进行测试后,将测试的结果反馈在这里,便于我们识别表达式匹配和捕获数据的效果。

文字修饰项

如果希望在表达式匹配到目标内容后,还要进一步检查目标内容的样式,比如文字的颜色,可以勾选"启用文字修饰项中的"前景色",然后从"前景色列表"中选择颜色。

若希望匹配文字修饰项,请记得还需要勾选解码后匹配,因为文字修饰属性是在解码后才有的:

触发器测试

当编写完触发器脚本和表达式后,可以在触发器测试页进行离线测试触发器。

在测试脚本前,先要确保至少有一个表达式处于启用状态。在测试文本框里输入普通的文本内容,点击"测试"按钮。

在"合并结果"框里反馈测试的结果,主要关注捕获组的内容。

脚本接收捕获数据

当希望触发器的脚本能接收表达式捕获到的数据,需要在编写脚本时以特定的模式实现:

  1. 编写一个可处理捕获数据的函数,并声明一个参数,参数名随意;参数的类型是table,表示二维数组,第一维每个元素代表每一轮匹配结果,第二维每个元素对应捕获分组内容。

  2. 之后在脚本中调用这个函数,并传入特殊变量"...",即连续三个点符号。

例如:

function thristy(caps)
    if tonumber(caps[1][2]) / tonumber(caps[1][3]) < 0.7 then
        cmd("drink hulu")
    end
end

thristy(...)

在这个例子中定义了函数thristy,可接收捕获数据;然后调用函数thristy,通过"..."来传递捕获到的数据。传入的数据结构可参考下图:

触发器服务

Easymud中触发器服务对所有的触发器进行测试和运行。

与定时器服务设置类似,通过"开始服务"和"关闭服务"按钮,可以控制触发器服务的开启和关闭。

当保存对触发器的修改时,会将所有的触发器和定时器合并创建一个新的版本,保存到磁盘缓存中。

所有在触发器配置界面进行的修改和测试,都是在隔离的Lua虚拟机中进行,不会影响到在触发器服务中运行的触发器,直到点击"保存并更新触发器服务"按钮才会将最新的触发器版本加载到后台服务中。

历史版本

与定时器配置界面类似,从"历史版本下拉列表中选择的版本数据,只会在当前隔离的虚拟机中运行,直到保存并更新