几年前,add_action()的当机BUG是令众多巫师极为头痛的事情,如今各显神通,有
从MUDOS上解决,有从MUDLIB里调整。基本上已经看不到这种问题了。但是,如果未能了
解这个BUG的产生原理,那么还有可能在其它的很多地方再次产生各种各样的新BUG,结
合本人的摸索感受,试图全面介绍一下这方面的知识。
首先我们来了解一下MUD对玩家输入信息的处理流程。玩家在客户端的指令行里输入
一些或长或短的字符后,系统接收到之后首先会调用在/feature/目录下的alias.c里的p
rocess_input(string str)函数
进行预处理。而那些字符也就是参数str。
(例一:玩家输入gall str==gall
例二:玩家输入c 我要go str==我要go
例三:玩家输入out str==out
例四:玩家输入kill llm str==kill llm)
这个函数首先要对玩家的信息进行一些过滤判断,例如对于连续重复指令方面的判
断呀之类的,主要是对机器人的限制。然后就是调用玩家自己设定的alias以及系统设定
的alias(主要由/adm/daemons/下的aliasd.c定义)看看str里面是否有事先设定的alias
,有的话就要转换成原先真正的指令,最后返回
这个经过处理过的新的字符串str。
(例一:gall 经检查发现与玩家设定gall==get all,因此str==get all
例二:c 我要go 经检查发现玩家设定c==chat,因此str==chat 我要go
例三:out 检查没发现alias,因此out==out
例四:kill llm 检查后没发现alias,因此str==kill llm )
在玩家进入MUD之后,连线程序logind.c在成功创造玩家的身体之后,会调用一个函
数enable_player(),这个函数原型是在/feature/command.c里。该函数首先调用一个外
部函数enable_commands(),允许它使用 add_action()所加入的命令。然后就add_actio
n("command_hook", "", 1);
add_action()这是一个外部函数,格式如add_action(A,B,C);就是表示如果玩家输
入指令第一个空格之前的单词与B相同的话,就是调用函数A,后面的参数C一般用不着,
这里不细讲了。那么我们看看这里的就表示,如果玩家输入的第一个单词是"",其实就
是所有的指令都符合这个条件的,那么就会调用到
函数command_hook()。而command_hook()函数就是在command.c里。str如果超过一个单
词,也就是有空格,就会分成第一个为verb,后面的为arg。开始按顺序判断verb是否是
方向、固定指令、emote动作、频道指令,如果是的话,就会把arg作为相应的参数传入
。如果都不是,就会返回0,也就是出现“什麽”的
字样。
(例一:str==get all,get为一固定指令,调用get.c->main()参数是"all"
例二:str==chat 我要go,chat为频道名,调用channeld.c里的do_chat,
参数arg是"我要go"
例三:str==out,玩家所在场景发现有名叫out的出口,因此调用go.c->main()
参数arg是"out"
例四:str==kill llm,kill是一固定指令,调kill.c->main(),arg是"llm" )
以上是MUD处理信息的经过。
因此,MUD里所有的指令都是通过add_action()来实现的。而add_action()可以增加
相同名称的指令,如果指令相同,则后加的会先执行,请注意这里,并不是说后加的“
覆盖”先加的,而是“先执行”。关于一个同样的动作单词就可以有好几层的add_actio
n。那么在上一层调用的函数如果是返回0的情况下,系统会自动再去执行下一层的add_a
ction()调用的函数,如果是其中任意一层返回是1,就表示到此中止,不会再执行下一
层的add_action(),关于这一点特性可以灵活地使用。比如一个kill指令,本身通过com
mand_hook已经加了一个,有的房间里再次调用一个add_action("do_kill","kill"),后
来进来一个NPC,NPC身上也带有一个新的add_action("do_kill","kill"),那么只要进
入这个房间后,玩家身上就会有了三层有关kill的add_action()。如果这里输入kill,
自然是先执行NPC身上的do_kill()。返回是0的话,再执行房间里的do_kill(),再是0的
话再执行kill.c。所以,在一般我们在房间,NPC以及OBJ里做的add_action()如果与/cm
ds目录下的指令相同的话,都会优先于指令先行。而且如果是后加的,肯定优先于前加
的。而其中任意一层一旦有返回1的话,就会立即中止。
LPMUD里基本上所有的谜题和很多特殊效果都需要借助add_action()来实现,认真理
解并掌握它的用法是相当重要的。
下面我们来谈谈add_action()的 BUG吧!很多老的玩家都知道它的用法,先由一个A
买一只鸡腿(包子也可以),由另一个B打昏它,然后B从A身上搜走鸡腿,再吃光鸡腿扔
掉。等A醒来输入eat jitui指令,系统便会立即当机。
原因分析,传统MUDLIB的eat是一个add_action(),做在食物的标准继承food.c里。
玩家买下一只鸡腿,那么这个eat的add_action()就加到了玩家身上。在正常情况下,这
个物体消失(比如吃掉)或离开玩家所处环境(比如扔掉并离开),那么add_action()
都会正常去掉。但是在玩家昏迷时,其它人从它身上拿走这个带add_action()的物体,
这个add_action()并不能正常地从玩家身上去掉。而当玩家苏醒后,这个eat的add_acti
on()依旧存在于他身上,他依旧可以执行这个指令,如果执行的对象,比如鸡腿还在游
戏中,不论这只鸡腿被玩家B带到了多远的地方,A都可以通过eat jitui吃得到这只鸡腿
。这种情况只是有点滑稽而已。但是如果这个鸡腿已经消失了,比如B吃掉了,那么它只
能消除在B身上的add_action(),这时A再执行eat,系统一下子找不到jitui这个物体,
就会从内存中载入一大堆莫名其妙乱七八糟的东西,迅速进入死循环,直接导致当机。
所以要实现这个BUG的条件有二,一是找一件有add_action()并且可以通过正常方法摧毁
的,比如食物,不值钱的东西。二是用两个ID执行。
目前新版本的MUDOS据说已经从底层上修改掉了这个BUG。同时明白了其中的原理也
可以在MUDLIB上用很多方法来避免这种情况的发生。
再下面就谈一下较少有人知的另一个BUG,这个BUG表面上看起来问题不大,实际运
用中有时会产生很大的问题,就是sleep对add_action()的影响。大家可以仔细看看slee
p.c文件,玩家进入睡眠状态就会调用一个函数me->disable_player();这个函数原型在/
feature/command.c里,最终调用disable_commands();这个外部函数,disable_command
s()的用处就是让一个活物件变成「非活着」,一是add_actions 失效,二是livingp()
返回0值......也就是说,去掉了身上所有的add_action()。然
后在醒来之后,再次调用me->enable_player();这个函数我在前面文章的第五段里介绍
过用法与作用,它只是恢复了玩家的add_action("command_hook", "", 1);也就是所有
的系统固定指令。比如玩家身上物品的add_action,所处环境的add_action都是在init(
)里加载的,玩家在sleep之后并没有呼叫到init(),自然就没有这些。那么问题就会出
现了。假如我们在一个房间里或是在一个物体上作了一个企图覆盖掉正常指令的add_act
ion(想覆盖掉正常指令,只要让这个add_action()调用的函数总是返回1就行)。那么
,玩家只需sleep一下之后,就会让这个覆盖无效。无效之后产生的问题大小就与你当初
覆盖的目的有关了。要解决这一问题,可以修改sleep.c,在玩家醒来后的一瞬间,让玩
家离开原地再重新move回到所处的环境,再让玩家身上所有的东西也同样移出去再重新m
ove回到玩家身上,这样就让系统再次加载玩家身上应该加载的add_action。
总之,MUDOS当初对于add_action的考虑并不是很完善。再加MUDLIB里的处理手法,
对于象过去的昏迷、睡觉以及今后要发展的点穴、捆绑等等的处理都需要屏蔽掉指令,
那么处理的前后则一定要小心,否则新东东一上,新BUG也就隆重登场。
谁与争锋 叮当 2001、04、21
08
2013
01
详谈add_action()及其BUG
发布:xiaqiang | 分类:Mud | 评论:0 | 浏览:
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。