背景:
阅读文章

add_action()的BUG

[日期:2007-05-06] 来源:  作者: [字体: ]

   几年前,add_action()的当机BUG是令众多巫师极为头痛的事情,如今各显神通,有从MUDOS上解决,有从MUDLIB里调整。基本上已经看不到这种问题了。但是,如果未能了解这个BUG的产生原理,那么还有可能在其它的很多地方再次产生各种各样的新BUG,结合本人的摸索感受,试图全面介绍一下这方面的知识。
  首先我们来了解一下MUD对玩家输入信息的处理流程。玩家在客户端的指令行里输入一些或长或短的字符后,系统接收到之后首先会调用在/feature/目录下的alias.c里的process_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_acti
on("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_action。那么在上一层调用的函数如果是返回0的情况下,系统会自动再去执行下一层的add_action()调用的函数,如果是其中任意一层返回是1,就表示到此中止,不会再执行下一层的add_action(),关于这一点特性可以灵活地使用。比如一个kill指令,本身通过command_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()如果与/cmds目录下的指令相同的话,都会优先于指令先行。而且如果是后加的,肯定优先于前加的。而其中任意一层一旦有返回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_action()依旧存在于他身上,他依旧可以执行这个指令,如果执行的对象,比如鸡腿还在游戏中,不论这只鸡腿被玩家B带到了多远的地方,A都可以通过eat jitui吃得到这只鸡腿。这种情况只是有点滑稽而已。但是如果这个鸡腿已经消失了,比如B吃掉了,那么它只能消除在B身上的add_action(),这时A再执行eat,系统一下子找不到jitui这个物体,
就会从内存中载入一大堆莫名其妙乱七八糟的东西,迅速进入死循环,直接导致当机。所以要实现这个BUG的条件有二,一是找一件有add_action()并且可以通过正常方法摧毁的,比如食物,不值钱的东西。二是用两个ID执行。
  目前新版本的MUDOS据说已经从底层上修改掉了这个BUG。同时明白了其中的原理也可以在MUDLIB上用很多方法来避免这种情况的发生。
  再下面就谈一下较少有人知的另一个BUG,这个BUG表面上看起来问题不大,实际运用中有时会产生很大的问题,就是sleep对add_action()的影响。大家可以仔细看看sleep.c文件,玩家进入睡眠状态就会调用一个函数me->disable_player();这个函数原型在/feature/command.c里,最终调用disable_commands();这个外部函数,disable_comma
nds()的用处就是让一个活物件变成「非活着」,一是add_actions 失效,二是livingp()返回0值......也就是说,去掉了身上所有的add_action()。然
后在醒来之后,再次调用me->enable_player();这个函数我在前面文章的第五段里介绍过用法与作用,它只是恢复了玩家的add_action("command_hook", "", 1);也就是所有的系统固定指令。比如玩家身上物品的add_action,所处环境的add_action都是在init
()里加载的,玩家在sleep之后并没有呼叫到init(),自然就没有这些。那么问题就会出现了。假如我们在一个房间里或是在一个物体上作了一个企图覆盖掉正常指令的add_action(想覆盖掉正常指令,只要让这个add_action()调用的函数总是返回1就行)。那么,玩家只需sleep一下之后,就会让这个覆盖无效。无效之后产生的问题大小就与你当初覆盖的目的有关了。要解决这一问题,可以修改sleep.c,在玩家醒来后的一瞬间,让玩家离开原地再重新move回到所处的环境,再让玩家身上所有的东西也同样移出去再重新move回到玩家身上,这样就让系统再次加载玩家身上应该加载的add_action。
  总之,MUDOS当初对于add_action的考虑并不是很完善。再加MUDLIB里的处理手法,对于象过去的昏迷、睡觉以及今后要发展的点穴、捆绑等等的处理都需要屏蔽掉指令,

发信人: cloner (!!!!!!!), 信区: Mud_Builder
标  题: Re: add_action()的bug问题(转载自谁与争锋)
发信站: 一塌糊涂 BBS (Thu May  3 16:26:30 2001)

带着一个disable的玩家进去,当时就没有触发init(),所以
add_action()没有在他身上,所以enable的时候就可以避开
刻意屏蔽的命令,这个问题我很早就碰到过,不过没有
仔细考虑过,现在看来似乎确实会有些问题的。解决的办法,
按丁当的说法:enable后move出去,再move进来一次。
事实上对于add_action造成当机的bug,很多lib都是用这样的
方法解决的。也有人从mudos改的,可惜我看不懂,//blush
另外也可以重载阿add_action,不过很多人觉得酱紫
效率太差,:P。重载enable_player,disable_player可能会
好一些。

发信人: shure (韩姝儿), 信区: Mud_Builder
标  题: Re: add_action()的bug问题(转载自谁与争锋)
发信站: 一塌糊涂站 (Thu May  3 19:25:00 2001) , 站内信件

我在想是不是还有一种不太彻底的解决途径,比如说在disable_commands();之后马上enab
le_commands();,这样的话,应该可以解决再次enable后不能呼叫ini(),但是在玩家醒来
的时候[调用enable_player()函数,实现enable_commands()并且add_action("command_ho
ok", "", 1);]这时候倘若房间里有覆盖掉/cmds/里的action的话,这个action就会重新被
command_hook覆盖掉,所以我说它不太彻底。呵呵。

nomask void disable_player(string type)
{
   if( geteuid(previous_object())!=ROOT_UID
   &&   previous_object()!=this_object()) return;

   set("disable_type", type);
   set_temp("disabled", 1);
   disable_commands(); 
   enable_commands(); 
}

可以try try,看看是不是这样。

--以下转载自www.niub.net之mud制作论坛

有关昏迷与睡觉的处理   

作者:llm  发表时间:2001年7月6日 20:52

--------------------------------------------------------------------------------

由于昏迷与睡觉里面的disable_commands,会导致很多麻烦的事,在前面的一些讨论
中已经说过,我也下决心彻底抛弃这种处理法,尝试用一种简单的方法:

先修改damage.c里的void unconcious()
......
    set_temp("block_msg/all", 1);//屏敝外界输入
    set_temp("block_input",1);//屏敝玩家输入,在command里判断
    set_temp("disable",1);//设置不醒的标志
    set("disable_type"," <昏迷不醒>");//by llm,直接设置
    set("jing", 0);
......
有了set_temp("block_msg/all", 1);玩家就不会接受到信息,然后有了
set_temp("block_input",1);我在command.c里的int command_hook()
函数里if ((verb = remove_leading_space(verb)) == "") return 0;
后面加上一句if(query_temp("block_input")) return 0;这样就可以实现
了对玩家输入的屏敝。
  sleep里的修改类似。
  在唤醒的函数里,直接把三个_temp和"disable_type")都删掉就行了。

  这样子改过后,我作了测试,发现效果很好,代码也相当地简化。不会
有各种以往的问题。接下来的问题就是如何修正有关living()这个efun.因为
我这样子处理之后,living是识别不出来的。
  于是,我在/adm/simul_efun/object.c里新增了一个living()的函数如下:
int living(object me)
{
    if(me&&me->query_temp("disable")) return 0;
    return efun::living(me);
}
    但是,一重启机器,发现问题来了,连线后,出现WELCOME后,系统启动时
间,以及**位巫师,**位玩家..........之后,应该出现
请输入你的英文名这一句却出不来了。
把object.c里的 living()删了就正常了。

--------------------------------------------------------------------------------
作者:llm  发表时间:2001年7月7日 02:23

我在单机版上做了相同的修改,重启后很正常。

然后再到主机上进行修改,在object.c里新增了living()后,update object.c
成功。再到/adm/obj/里update simul_efun.c,成功!
此时,发现新加的living()的判断已经生效。以为成功了。
再次reboot 主机,又出问题了。依旧卡在老地方。那里是一个write()。
分析应该是simul_efun未能成功加载,找不到write()这个函数了。

究竟问题出在哪里呢?

--------------------------------------------------------------------------------
作者:darks  发表时间:2001年7月7日 09:22

int living(object me)
{
    if(me&&me->query_temp("disable")) return 0;
    return efun::living(me);
}
看上去没有涉及到任何的外部函数.....

对了 其实living对于你的这个判断 就是鸡肋效果

类似ghost 我们定义一些设定就行了
int unconscious = 0;
int is_unconscious() { return unconscious;}
void be_unconscious(inf flag) { unconscious = flag;}

通过player->is_unconscious() 来判断是不是昏迷的 就行了

所有不昏迷的孩子

array object ob = filter_array( users(),(: interactive($1) && !$1->is_unconscious() :) );

嘻嘻 设定由人不由天。  
 

尊重作者 转载请注明出处52mud.com

收藏 推荐 打印 | 录入:sbso | 阅读:
相关内容      
本文评论   [发表评论]   全部评论 (0)
内容推送
52mud提供
一起回忆泥巴游戏QQ群68186072
52mud官方微信公众平台
热门评论