报错信息里写得很清楚,问题出在QFunction脚本的@GetExp触发段,执行了GOTO@宗派经验这条命令,而且一秒触发一次。@GetExp是引擎内置的经验获取触发点,角色每次获得经验都会自动执行这一段。脚本里连续写了三个goto,其中跳转到@宗派经验之后,执行完那一整段逻辑,脚本流程并没有真正切断,又回到了触发入口,再次执行GOTO,如此反复就形成了闭环。
**GOTO与BREAK的执行逻辑**
要理解为什么会循环,得先搞明白这两个命令的区别。goto是无条件跳转,跳到指定标签后从那一行开始继续往下执行。执行完标签内的代码后,脚本不会自动停住,还会继续往下走,或者在某些情况下回溯到触发点。break的作用是立即终止当前脚本块的执行,执行到break之后,后面所有的语句都不会再解析。
原脚本@GetExp段里写了三个goto,最后才跟一个break。这个写法的问题在于,前两个goto跳出去执行完,如果目标段里没有break或者没有把流程彻底断掉,脚本会回到@GetExp继续跑,跑到第三个goto,又跳走,又回来,循环就开始了。而且三个goto连续堆叠本身就容易出问题,引擎在处理多重跳转时,执行顺序和返回逻辑容易混乱。
**@宗派经验段本身的问题**
再往下看@宗派经验这段脚本,里面做了几件事:从宗主名单里判断当前角色是不是宗主,然后用GetRandomName从经验文件里随机取一行名字,用mov和inc做经验数值的累加,最后用DelTextList和AddTextList把旧数据删掉、把新数据写进去。
这段逻辑本身没有明显的语法错误,但有一个隐患。GetRandomName这个命令在文件为空或者读取不到有效行的时候,赋值行为可能不符合预期。有技术文章提到过类似的情况:GetRandomText在读取到空值的时候,不会把变量清空,而是保留上一次的值。如果@宗派经验被高频触发,前一次读到的名字还留在变量里,下一次读取又拿到同样的旧值,变量内容不变,脚本判断不出“该停了”,就会一直循环下去。解决办法是在使用GetRandomName之前,先用mov把S28清空,比如写一行`movS28`,确保每次读取前变量是干净的。
**脚本执行顺序的修正**
@GetExp段的核心问题是触发机制和跳转逻辑冲突。@GetExp是经验获取时自动触发的,角色只要在打怪、吃经验丹、做任务,这个触发点就会不断被激活。如果每次激活都无条件跳转到@宗派经验,而@宗派经验内部又没有任何条件判断来阻止重复执行,那这个循环就无法打破。
修正的思路是在@GetExp里加上条件判断,只有满足特定条件时才执行跳转。比如用CHECKNAMELIST先判断当前角色是否在宗主名单里,只有宗主角色才需要执行宗派经验的计算逻辑,非宗主角色触发@GetExp时直接跳过,不执行GOTO。这样一来,大部分角色获得经验时不会触发这段逻辑,循环的压力就小了很多。
另外,@GetExp里三个goto连着写本身就不规范。一个#ACT块里最好只用一个goto命令,多个跳转容易导致执行顺序错乱。如果确实需要触发多个不同的逻辑段,应该用条件判断分开处理,或者把它们合并到一个统一的流程节点里,按顺序执行。
**UPGRADEITEMEX之外的配置检查**
除了脚本本身的逻辑,服务端的配置参数也值得检查一下。在Mir200文件夹下的!Setup.txt文件里,找到ScriptGotoCountLimit这个参数,默认值一般是10,意思是脚本循环跳转超过10次就会被引擎强制中断并报错。如果脚本确实需要多次循环,可以把这个数值调大一些,比如改成10000到50000之间,但不能改得太大,否则真遇到死循环的时候,引擎要跑很久才能检测到,期间会持续消耗服务器资源。
还有一个替代方案是用delaygoto代替普通的goto。delaygoto的语法是`delaygoto时间@标签`,时间单位是毫秒。比如`delaygoto1000@宗派经验`表示延迟1秒后再跳转。延迟跳转的好处是给引擎留出了处理时间,不会在同一瞬间反复触发同一个逻辑段,能有效降低死循环的触发概率。
**修正后的脚本结构**
综合以上分析,@GetExp段应该改成先判断、再跳转、最后用break终止的结构。@宗派经验段需要在GetRandomName之前加一行mov清空变量,并且确保这段逻辑执行完毕后有明确的终止指令。
如果服务端支持,把普通goto换成delaygoto是最稳妥的做法。延迟跳转加上条件判断,双保险基本可以杜绝这类死循环报错。修改完成后记得重启M2引擎让脚本重新加载,否则改动的效果不会立即生效。
**GOTO与BREAK的执行逻辑**
要理解为什么会循环,得先搞明白这两个命令的区别。goto是无条件跳转,跳到指定标签后从那一行开始继续往下执行。执行完标签内的代码后,脚本不会自动停住,还会继续往下走,或者在某些情况下回溯到触发点。break的作用是立即终止当前脚本块的执行,执行到break之后,后面所有的语句都不会再解析。
原脚本@GetExp段里写了三个goto,最后才跟一个break。这个写法的问题在于,前两个goto跳出去执行完,如果目标段里没有break或者没有把流程彻底断掉,脚本会回到@GetExp继续跑,跑到第三个goto,又跳走,又回来,循环就开始了。而且三个goto连续堆叠本身就容易出问题,引擎在处理多重跳转时,执行顺序和返回逻辑容易混乱。
**@宗派经验段本身的问题**
再往下看@宗派经验这段脚本,里面做了几件事:从宗主名单里判断当前角色是不是宗主,然后用GetRandomName从经验文件里随机取一行名字,用mov和inc做经验数值的累加,最后用DelTextList和AddTextList把旧数据删掉、把新数据写进去。
这段逻辑本身没有明显的语法错误,但有一个隐患。GetRandomName这个命令在文件为空或者读取不到有效行的时候,赋值行为可能不符合预期。有技术文章提到过类似的情况:GetRandomText在读取到空值的时候,不会把变量清空,而是保留上一次的值。如果@宗派经验被高频触发,前一次读到的名字还留在变量里,下一次读取又拿到同样的旧值,变量内容不变,脚本判断不出“该停了”,就会一直循环下去。解决办法是在使用GetRandomName之前,先用mov把S28清空,比如写一行`movS28`,确保每次读取前变量是干净的。
**脚本执行顺序的修正**
@GetExp段的核心问题是触发机制和跳转逻辑冲突。@GetExp是经验获取时自动触发的,角色只要在打怪、吃经验丹、做任务,这个触发点就会不断被激活。如果每次激活都无条件跳转到@宗派经验,而@宗派经验内部又没有任何条件判断来阻止重复执行,那这个循环就无法打破。
修正的思路是在@GetExp里加上条件判断,只有满足特定条件时才执行跳转。比如用CHECKNAMELIST先判断当前角色是否在宗主名单里,只有宗主角色才需要执行宗派经验的计算逻辑,非宗主角色触发@GetExp时直接跳过,不执行GOTO。这样一来,大部分角色获得经验时不会触发这段逻辑,循环的压力就小了很多。
另外,@GetExp里三个goto连着写本身就不规范。一个#ACT块里最好只用一个goto命令,多个跳转容易导致执行顺序错乱。如果确实需要触发多个不同的逻辑段,应该用条件判断分开处理,或者把它们合并到一个统一的流程节点里,按顺序执行。
**UPGRADEITEMEX之外的配置检查**
除了脚本本身的逻辑,服务端的配置参数也值得检查一下。在Mir200文件夹下的!Setup.txt文件里,找到ScriptGotoCountLimit这个参数,默认值一般是10,意思是脚本循环跳转超过10次就会被引擎强制中断并报错。如果脚本确实需要多次循环,可以把这个数值调大一些,比如改成10000到50000之间,但不能改得太大,否则真遇到死循环的时候,引擎要跑很久才能检测到,期间会持续消耗服务器资源。
还有一个替代方案是用delaygoto代替普通的goto。delaygoto的语法是`delaygoto时间@标签`,时间单位是毫秒。比如`delaygoto1000@宗派经验`表示延迟1秒后再跳转。延迟跳转的好处是给引擎留出了处理时间,不会在同一瞬间反复触发同一个逻辑段,能有效降低死循环的触发概率。
**修正后的脚本结构**
综合以上分析,@GetExp段应该改成先判断、再跳转、最后用break终止的结构。@宗派经验段需要在GetRandomName之前加一行mov清空变量,并且确保这段逻辑执行完毕后有明确的终止指令。
如果服务端支持,把普通goto换成delaygoto是最稳妥的做法。延迟跳转加上条件判断,双保险基本可以杜绝这类死循环报错。修改完成后记得重启M2引擎让脚本重新加载,否则改动的效果不会立即生效。

