#nop vim: set filetype=tt:; /* 本文件属于 PaoTin++ 的一部分。 PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 */ #nop 本文件是 xtintin 的一部分,实现了一些实用命令。; ///=== { ///// 实用命令: // // ## clear // 清屏。类似于 DOS/Unix,也可以用 cls,作用相同。 // }; #alias {cls} {clear}; #alias {clear} { #system {tput clear}; tmux.SetTheme GAME; prompt.refresh; }; ///=== { // ## exit // 退出客户端,但不退出游戏角色。也可以用 quit,作用相同。 // 一些中文 MUD 服务器在接收到 quit 命令时会让角色从服务器上下线, // 这往往会导致玩家丢失背包里的物品。新手玩家常常因此懊恼万分。 // 为了避免悲剧发生,这里特别映射一下,改成仅断开连接,而不退出服务器角色。 // 如果玩家真的需要向服务器发送 quit 指令,请输入 #send quit。exit 同理。 // }; #alias {exit} {#end}; #alias {quit} {#end}; ///=== { // ## xtt.Tick <代码> <间隔时间> [<触发次数>] // 跟 #tick 功能类似,但是会立即执行一次代码。对于间隔时间比较长的定时器来说尤其有用。 // 可选的触发次数会导致定时器在次数达到限制后自动销毁。省略此参数时将不限制触发次数。 // 你也可以通过 Tick 别名来使用本别名。 // }; #alias {Tick} {xtt.Tick}; #alias {xtt.Tick} { #local id {%1}; #local code {%2}; #local interval {%3}; #local times {@defaultNum{%4;0}}; #math times {$times - 1}; #line sub var #untick {$id}; #if { $times > 0 } { #line sub {var;escapes;functions} #line multishot {$times} #tick {$id} {$code} $interval; }; #elseif { $times < 0 } { #line sub var #tick {$id} {$code} $interval; }; #line sub {var;escapes;functions} $code; }; ///=== { // ## xtt.ListTicker // 列出系统中所有的定时器。因为定时器一般不会很多因此暂时没有做过滤功能。 // 你也可以通过 TICKS 别名来使用本别名。 // }; #alias {TICKS} {xtt.ListTicker}; #alias {xtt.ListTicker} { #info tickers save; #echo {<128> %-30s %+20s %+10s %+20s} {所属模块} {定时器名称} {执行周期} {距离下次执行(s)}; #draw Yellow scroll line 1 1 1 90; #format utime %U; #local index {}; #loop {1} {&info[TICKERS][]} {index} { #local uval {}; #math uval $info[TICKERS][+$index][arg3] * 1000000; #local name {$info[TICKERS][+$index][arg1]}; #echo { %-30s %+20s %+10s %+20m} {@genModuleLink{$info[TICKERS][+$index][class];MOD}} {$name @mslp.Exec{{xtt.delTrigger tick $name};<119>✗<269>;false}} {$info[TICKERS][+$index][arg3]} {($uval - ($utime - $info[TICKERS][+$index][arg4]) % $uval) / 1000000.00}; }; }; ///=== { // ## xtt.ListAlias [<正则表达式>] // 列出系统中符合条件的别名,如果省略条件则列出所有别名。 // 一些不够规整的别名不会被列出。只有符合 PaoTin++ 规范的别名才会被列出。 // 正则表达式会被运用到别名所属模块名称和别名名称上,两者符合其一即可被列出。 // 你也可以通过 ALIS 别名来使用本别名。 // }; #alias {ALIS} {xtt.ListAlias}; #alias {xtt.ListAlias} { #local pattern {%1}; #info aliases save; #local aliasTable {}; #local index {}; #loop {1} {&info[ALIASES][]} {index} { #local name {$info[ALIASES][+$index][arg1]}; #local class {$info[ALIASES][+$index][class]}; #if { "$class" == "" && "$pattern" != "all" } { #continue; }; #if { "$class" == "" } { #local class {未分组}; }; #if { "$name" == "%*{[^a-zA-Z0-9-_.]}%*" } { #continue; }; #if { "$pattern" != "{|all}" && "$class/$name" != "%*$pattern%*" } { #continue; }; #list {aliasTable[$class]} sort {$name}; }; #local format { %-30s %-40s %-10s}; #echo {<128>$format} {class} {别名} {类型}; #draw Yellow scroll line 1 1 1 90; #local classList {@list.Sort{*aliasTable[]}}; #local class {}; #foreach {$classList} {class} { #local name {}; #foreach {${aliasTable[$class][]}} {name} { #local type {<139>自定义<299>}; #if { "$name" == "%*.{[A-Z][a-zA-Z0-9]+}" } { #local type {<129>开放API<299>}; }; #if { "$class" == "module-loader" } { #if { "$name" == "{[A-Z]+}" } { #local type {<169>快捷方式<299>}; }; #else { #local type {<229>语法增强<299>}; } }; #if { "${class}.$name" == "main.class.%*" } { #local type {<229>语法增强<299>}; }; #if { "${class}.$name" == "main.load-{file|config}" } { #local type {<229>语法增强<299>}; }; #if { "${class}.$name" == "main.%*Log" } { #local type {<129>日志接口<299>}; }; #if { "$pattern" == "" && "$type" == "%*自定义%*" } { #continue; }; #echo {<060>$format} {@genModuleLink{$class;MOD}<060>} {@mslp.Exec{{xtt.delTrigger alias $name};<119>✗<269>;false} @linkToHelp{$class;$name}} {$type}; }; }; }; ///=== { // ## xtt.ListVar [<正则表达式>] // 列出系统中符合条件的变量,如果省略条件则列出所有变量。 // 一些不够规整的变量不会被列出。只有符合 PaoTin++ 规范的变量才会被列出。 // 正则表达式会被运用到变量所属模块名称和变量名称上,两者符合其一即可被列出。 // 你也可以通过 VARS 别名来使用本别名。 // }; #alias {VARS} {xtt.ListVar}; #alias {xtt.ListVar} { #local pattern {%1}; #info variable save; #local varTable {}; #local index {}; #loop {1} {&info[VARIABLES][]} {index} { #local name {$info[VARIABLES][+$index][arg1]}; #local class {$info[VARIABLES][+$index][class]}; #local value {$info[VARIABLES][+$index][arg2]}; #local nest {$info[VARIABLES][+$index][nest]}; #if { "$class" == "" && "$pattern" != "all" } { #continue; }; #if { "$class" == "" } { #local class {未分组}; }; #if { "$pattern" != "{|all}" && "$class/$name" != "%*$pattern%*" } { #continue; }; #local {varTable[$class][$name]} { {nest}{$nest} {value}{$value} }; }; #local format { %-35s %-30s %-10s %s}; #echo {<128>$format} {class} {变量} {类型} {值}; #draw Yellow scroll line 1 1 1 95; #local classList {@slist.Sort{*varTable[]}}; #local class {}; #foreach {$classList} {class} { #local name {}; #local nameList {@slist.Sort{*varTable[$class][]}}; #foreach {$nameList} {name} { #local type {<229>字符串<299>}; #local value {<169>$varTable[$class][$name][value]<299>}; #local nest {$varTable[$class][$name][nest]}; #if { $nest > 0 } { #local type {<259>表格<299>}; #local value {@mslp.Var{$name;<139>[... 共 $nest 项数据]<299>}}; }; #local showClass {$class}; #replace showClass {^data/} {}; #echo {<060>$format} {@genModuleLink{$showClass;MOD}<060>} {@mslp.Exec{{xtt.delTrigger var $name};<119>✗<269>;false} @linkToHelp{$showClass;$name}} {$type} {$value}; #if { "$gPaoTinVars[$name]" != "" } { #echo {$format} {} { ╰── $gPaoTinVars[$name][cnName]} {} {}; }; }; }; }; #alias {xtt.delTrigger} { #local type {%1}; #local trigger {%2}; #local cmd {#un$type {$trigger}}; #replace cmd {\\} {}; $cmd; }; ///=== { // ## xtt.ListFunc [<正则表达式>] // 列出系统中符合条件的函数,如果省略条件则列出所有函数。 // 正则表达式会被运用到函数所属模块名称和函数名称上,两者符合其一即可被列出。 // 一些不够规整的函数不会被列出。只有符合 PaoTin++ 规范的函数才会被列出。 // 你也可以通过 FUNCS 别名来使用本别名。 // }; #alias {FUNCS} {xtt.ListFunc}; #alias {xtt.ListFunc} { #local pattern {%1}; #info functions save; #local funcsTable {}; #local index {}; #loop {1} {&info[FUNCTIONS][]} {index} { #local name {$info[FUNCTIONS][+$index][arg1]}; #local class {$info[FUNCTIONS][+$index][class]}; #if { "$class" == "" && "$pattern" != "all" } { #continue; }; #if { "$class" == "" } { #local class {未分组}; }; #if { "$name" == "%*{[^a-zA-Z0-9_./-]}%*" } { #continue; }; #if { "$pattern" != "{|all}" && "$class/$name" != "%*$pattern%*" } { #continue; }; #list {funcsTable[$class]} sort {$name}; }; #local format { %-30s %-40s %-10s}; #echo {<128>$format} {class} {函数} {类型}; #draw Yellow scroll line 1 1 1 90; #local classList {@list.Sort{*funcsTable[]}}; #local class {}; #foreach {$classList} {class} { #local name {}; #foreach {${funcsTable[$class][]}} {name} { #local type {<139>自定义<299>}; #if { "$name" == "%*.{[A-Z][a-zA-Z0-9]+}" } { #local type {<129>开放API<299>}; }; #if { "$class" == "{lib/xtintin|main}" } { #local type {<229>语法增强<299>}; }; #if { "${class}.$name" == "main.%*Log" } { #local type {<129>日志接口<299>}; }; #if { "$pattern" == "" && "$type" == "%*自定义%*" } { #continue; }; #echo {<060>$format} {@genModuleLink{$class;MOD}<060>} {@mslp.Exec{{xtt.delTrigger func $name};<119>✗<269>;false} @linkToHelp{$class;$name}} {$type}; }; }; }; ///=== { // ## xtt.ListAction [<正则表达式>] // 列出系统中符合条件的文本触发,如果省略条件则列出所有文本触发。 // 正则表达式会被运用到文本触发所属模块名称和文本触发的条件上,两者符合其一即可被列出。 // 你也可以通过 ACTS 别名来使用本别名。 // }; #alias {ACTS} {xtt.ListAction}; #alias {xtt.ListAction} { #local pattern {%1}; #info actions save; #local actionTable {}; #local index {}; #loop {1} {&info[ACTIONS][]} {index} { #local name {$info[ACTIONS][+$index][arg1]}; #local class {$info[ACTIONS][+$index][class]}; #if { "$class" == "" && "$pattern" != "all" } { #continue; }; #if { "$class" == "" } { #local class {未分组}; }; #if { "$pattern" != "{|all}" && "$class/$name" != "%*$pattern%*" } { #continue; }; #list {actionTable[$class]} sort {$name}; }; #local format { %-30s %-40s %-10s}; #echo {<128>$format} {class} {文本触发}; #draw Yellow scroll line 1 1 1 90; #local classList {@list.Sort{*actionTable[]}}; #local class {}; #foreach {$classList} {class} { #local name {}; #foreach {${actionTable[$class][]}} {name} { #echo {<060>$format} {@genModuleLink{$class;MOD}<060>} {@mslp.Exec{{xtt.delTrigger action {$name}};<119>✗<269>;false} $name} }; }; }; ///=== { // ## xtt.Var <变量中文含义> <变量名> <值> // 声明并初始化变量。和 #var 不同,如果该变量已存在,则不会修改它的值。 // 另外,如果在模块中使用本方法,则声明的变量会自动存放在 #class data/$MODULE 中。 // 这意味着即使你重新载入模块代码,也不会破坏该变量的值。 // 因此建议将通过触发抓取到的任务进度信息用本方法存储,可以有效避免机器代码迭代 // 开发过程中,丢失任务信息从而导致任务失败。 // // 也可以通过短名称 VAR 来使用本命令,效果相同。 // }; #alias {VAR} {xtt.Var}; #alias {xtt.Var} { #local cnName {%1}; #local name {%2}; #local value {%3}; #if { @existsVar{$name} } { #return; }; #if { @existsVar{MODULE} } { #class data/$MODULE open; #var {$name} {$value}; #class data/$MODULE close; }; #else { #var {$name} {$value}; }; #var gPaoTinVars[$name] { {name} {$name} {cnName} {$cnName} }; }; VAR {用 VAR 关键字定义的 PaoTin++ 变量清单,包含其中文含义} gPaoTinVars {}; load-lib option; option.Define {DisableOutput} {Bool} {是否禁止向服务器发送命令} {false}; ///=== { // ## xtt.Send <命令> [<参数> ...] // 向服务器发送命令。如果命令拦截总开关被打开,则不会真的向服务器发送。 // }; #alias {xtt.Send} { #if { @option.IsEnable{DisableOutput} } { #echo {<169>命令已被抑制: <429>%p<299>} {%0}; #return; }; #send %0; }; ///=== { // ## xtt.SendAtOnce <分号分隔的命令序列> // 一次性向服务器发送多条命令。 // 有的 MUD 服务器专门为这种方式开辟了通道,本方法可以使用这种通道。 // TODO: 需要区分 MUD,需要支持定制的命令分隔符。 // }; #alias {xtt.SendAtOnce} { #local cmds {%1}; xtt.Send {#$cmds#}; }; ///=== { // ## xtt.Answer <问题答复> // 如果游戏中有需要回答的问题,并且给出了特定的提示符(就是不带换行的文本), // 那么由于 TinTin++ 的某种机制,导致回答的内容和问题就会有重叠,不能正确显示。 // 此时建议用 xtt.Answer 来回答此类问题。将会留下美观的 buffer 记录。 // }; #alias {xtt.Answer} { #delay 0 { #echo {}; #buffer end; xtt.Send {%1}; }; }; ///=== { // ## xtt.Stop <命令> // 暂时阻断某个命令的执行。 // 某些基于触发的机器会周而复始地执行动作。本命令可以用来终止它的运行,并保留状态。 // 例如新手机器人会循环执行 ask job/do/finish 流程,那么只需要输入 xtt.Stop ask, // 就可以在下一次 ask NPC 时,暂停机器执行,但并不影响机器状态。此时玩家可以手动 // 操作角色,临时去做点别的,比如收个包袱之类的,然后回到房间,手动执行一次 ask 命令, // 就可以继续机器运行了。 // }; #alias {xtt.Stop} { #if { "%1" == "" } { xtt.Usage xtt.Stop; #return; }; #line oneshot #alias {%1} {halt; #echo {<119>任务已暂停,请输入 <139>%1 %s <119>继续运行。<299>} {%%0}} }; option.Define {DisableAllCommands} {Bool} {是否禁用所有的触发器和定时器} {false}; ///=== { // ## xtt.DisableAllCommands // 禁止发送任何命令。 // 某些游戏模式下,玩家必须小心地输入,否则一旦输错会造成损失。此时可以用本 // 命令来禁止所有的触发、定时器、事件,防止误发命令。 // 此时玩家只能用 #send {.....} 命令来发送命令。 // 注意快捷键并不会被禁止,所以玩家可以通过快捷键来切换状态。 // }; #alias {xtt.DisableAllCommands} { #class disable-all-commands open; option.Enable DisableAllCommands; warnLog 所有定时器、触发器和事件句柄已被禁用。; warnLog 600 秒后自动解除该状态。; #alias {^%*{|ID=paotin/disable-all-commands}$} { #echo {<119>命令已被抑制,可用 <129>#send<119> 强制发送。撤销请用 <129>xtt.UndoDisableAllCommands<119> : <139>%s<299>} {%%0} } {1.001}; #gts {#delay 600 {#ats xtt.UndoDisableAllCommands}}; #line quiet #ignore actions on; #line quiet #ignore tickers on; #line quiet #ignore events on; #class disable-all-commands close; }; ///=== { // ## xtt.UndoDisableAllCommands // 取消 xtt.DisableAllCommands 效果,恢复正常状态。 // 参见 HELP xtt.DisableAllCommands // }; #alias {xtt.UndoDisableAllCommands} { #class disable-all-commands kill; #line quiet #ignore actions off; #line quiet #ignore tickers off; #line quiet #ignore events off; okLog 命令已恢复正常。; option.Disable DisableAllCommands; } {1.000}; ///=== { // ## xtt.ToggleDisableCommands // 在禁止和打开所有的定时器、触发器和事件句柄之间来回切换。 // 本命令可用作绑定快捷键使用。 // }; #alias {xtt.ToggleDisableCommands} { #if { @option.IsEnable{DisableAllCommands} } { xtt.UndoDisableAllCommands; }; #else { xtt.DisableAllCommands; }; } {1.000}; #func {linkToHelp} { #local module {%1}; #local keyword {%2}; #local text {$keyword}; #local cmd {HELP $keyword}; #if { "$keyword" == "" } { #local text {$module}; #local keyword {MODULE}; #local cmd {HELP $module}; }; #if { "$module" == "" } { #return {$text}; }; #if { &xtt.module-doc[$module][] == 0 } { #return {$text}; }; #if { &xtt.module-doc[$module][$keyword][] == 0 } { #return {$text}; }; #return {<149>@mslp.Exec{{$cmd};{$text}}<299>}; }; ///=== { // ## cli.SmartToggle // 命令行智能切换。 // 出于方便起见,该命令实际上集成了三个作用,并推荐使用快捷键来调用本命令。 // - 作用1: 当命令行尚未输入内容时,用来切换 PaoTin++ 的调试开关。 // 调试开关开启时,玩家可以看到触发的执行细节,方便调试代码。 // - 作用2: 当命令行输入了 #alias/#action 等 TinTin++ 可被取消的触发时,会 // 自动修改成对应的 #unalias/#unaction 命令,利用这一点可以方便取 // 消已有触发。可被取消的触发列表见下方完整清单。 // - 作用3: 当命令行输入了其它命令时,会自动开启或关闭以调试方式执行该命令。 // 该切换并不会影响全局调试开关。因此不会受到来自其它触发的干扰, // 内容更精准,方便查看。 // // 作用2中可被取消的命令清单: // - 常用 TinTin++ 命令: action alias tick variable delay gag // - 不常用 TinTin++ 命令: button event function highlight macro pathdir // prompt substitute tab // 以上命令的 3 字母以上简写,无论大小写,都会被自动添加或者删除 un, // 以达到切换的目的。 // - 常用的 PaoTin++ 命令: event.Handle/event.HandleOnce VS event.UnHandle // }; #alias {cli.SmartToggle} { #local input {}; #cursor get input; #line quiet #ignore function on; #if { {$input} == {} } { #line quiet #ignore function off; xtt.ToggleDebug; #return; }; #if { {$input} == {#line debug %*} } { #replace input {^#line debug } {}; }; #else { #local cmds {act(|i(|o(|n)))|ali(|a(|s))|tic(|k(|e(|r))) |var(|i(|a(|b(|l(|e))))) |del(|a(|y))|gag|tab |but(|t(|o(|n)))|eve(|n(|t)) |fun(|c(|t(|i(|o(|n))))) |hig(|h(|l(|i(|g(|h(|t)))))) |mac(|r(|o))|pat(|h(|d(|i(|r)))) |pro(|m(|p(|t))) |sub(|s(|t(|i(|t(|u(|t(|e)))))))}; #replace cmds { } {}; #if { {$input} == {{?i}#un{$cmds} %*} } { #replace input {{?i}#un} {#}; }; #elseif { {$input} == {{?i}#{$cmds} %*} } { #replace input {^#} {#un}; }; #elseif { {$input} == {{tt|}event.{Class|}Handle{|Once} %*} } { #replace input {^{tt|}event.{Class|}Handle{Once|}} {&1event.UnHandle}; }; #elseif { {$input} == {{tt|}event.UnHandle %*} } { #replace input {^{tt|}event.UnHandle} {&1event.Handle}; }; #else { #local input {#line debug $input}; }; }; #cursor clear; #local cmd { #line quiet #ignore variable on; #line quiet #line sub escapes #cursor set {$input}; #line quiet #ignore variable off; }; $cmd; #line quiet #ignore function off; }; ///=== { // ## xtt.Ping [<次数>] // 计算服务器延迟,输出以毫秒为单位的统计数据; // }; #alias {PING} {xtt.Ping}; #alias {xtt.Ping} { #class xtt.Ping open; #list xtt-ping-samples create {}; #var xtt-ping-send-time {}; #var xtt-ping-times {10}; #alias {xtt.Ping.ping} { #if { $xtt-ping-times <= 0 } { xtt.Ping.done; #return; }; #var xtt-ping-send-time {@str.Format{%U}}; sync.Wait xtt.Ping.pong; }; #alias {xtt.Ping.pong} { #math xtt-ping-times {$xtt-ping-times - 1}; #list xtt-ping-samples add {@math.Eval{ @str.Format{%U} - $xtt-ping-send-time }}; xtt.Ping.ping; }; #alias {xtt.Ping.done} { #list xtt-ping-samples order; #list xtt-ping-samples delete -1; #list xtt-ping-samples delete 1; #local min {@math.Eval{ $xtt-ping-samples[1] / 1000 }}; #local max {@math.Eval{ $xtt-ping-samples[-1] / 1000 }}; #local avg {@math.Eval{ @math.Sum{$xtt-ping-samples[]} / &xtt-ping-samples[] / 1000 }}; #class xtt.Ping kill; okLog 网络测速结果如右:最大值 $max 毫秒,最小值 $min 毫秒,平均值 $avg 毫秒; }; #class xtt.Ping close; sync.Wait {xtt.Ping.ping}; };