23
2013
01

MudOS中的内存管理

 

有关内存管理的算法实在是太多了,多到什么程度呢?基本上能想得到的数据结构,都能出现在各式各样的内存管理算法之中,数组、链表、散列表、二叉树等等都在这里大放异彩。研究内存管理实在是一件有趣的事情,同时也能极大的提高自己的编程能力。

内存管理方案 

MudOS中定义了至少3套内存分配函数库:

1.         Build-in system malloc——系统内建函数库,即malloc,realloc,calloc,free;

23
2013
01

ubuntu下MUDOS v22.2b14 完全编译

 运行./build.MudOS
vi socket_efuns.c

       o 修改Ln:1198的代码为 
       addr_in = (local ? &lpc_socks[fd].l_addr : &lpc_socks[fd].r_addr);
下面几个文件添加stdlib.h去掉一些警告(找不到exit()之类的)
vi edit_source.c
       添加 #include <stdlib.h>
vi preprocess.c
       添加 #include <stdlib.h>
vi make_finc.y
       添加 #include <stdlib.h>
修改一下option.h文件兼容老的lib
vi options.h
      
   添加巫师的功能
         修改 Ln:225 #undef NO_WIZARDS
         
采用老的lpc语法(关于nosave - static之类的)
         修改 Ln:266 #undef SENSIBLE_MODIFIERS
         
启用driver 的权限机制
         修改 Ln:659 #define PACKAGE_UIDS
   创建三个文件、否则会找不到  malloc.c 目标啥的  
    touch malloc.c
    touch mallocwrapper.c
    touch applies_table.c
    * 需要新建的三个文件是在make过程生成的,由于ubuntu下的make工具似乎不能识别make过程中的
      文件,所以需要新建三个空白文件,这样在make开始的时候就会识别到,当然在make过程中会复写这三个文件
    数据库部分、拷自nt2的driver 、(特此声明)
    由于以前的数据库api太老connect换成real_connect之类的

    添加数据库部分
    option.h修改下列配置
    #define PACKAGE_DB
    #ifdef PACKAGE_DB
    #undef USE_MSQL        /* MiniSQL, it's small; it's free */
    #define USE_MYSQL 2        /* MySQL, bigger; it's free */
    #define DEFAULT_DB USE_MYSQL    /* default database */
    #endif
    
    修改 db.h 头文件:
    将 24 行至 37 行注释掉,紧接着写上你自己的 MySQL 头文件的位置,
    例如在 /usr/local/mysql/include/mysql.h
    就写上:
    #include "/usr/local/mysql/include/mysql.h"
    
    在源代码目录里的 system_libs 文件里加上 mysql 连接库的位置,
    例如在 /usr/local/mysql/lib
    就加上:
    -L/usr/local/mysql/lib -lmysqlclient
   编译好的diver可能会遇到count(*)返零的问题
    MySQL_fetch对数据类型进行判断时、不包含Count(*)
的数据类型
    最后default赋值为零
[font=楷体_gb2312;]    count(*)返回的数据了类型为FIELD_TYPE_LONGLONG[font=楷体_gb2312;]
    在db.c 650行左右添加
[font=楷体_gb2312;] case FIELD_TYPE_LONGLONG:如下
switch (field->type) {

        case FIELD_TYPE_TINY:
        case FIELD_TYPE_SHORT:
        case FIELD_TYPE_DECIMAL:
        case FIELD_TYPE_LONG:
       case FIELD_TYPE_LONGLONG:
            v->item.type = T_NUMBER;
            v->item.u.number = atoi(target_row);
            break;

        case FIELD_TYPE_FLOAT:
        case FIELD_TYPE_DOUBLE:
            v->item.type = T_REAL;
            v->item.u.real = atof(target_row);
            break;

        case FIELD_TYPE_TINY_BLOB:
        case FIELD_TYPE_MEDIUM_BLOB:
        case FIELD_TYPE_LONG_BLOB:
        case FIELD_TYPE_BLOB:
        case FIELD_TYPE_STRING:
        case FIELD_TYPE_VAR_STRING:
                    if (field->flags & BINARY_FLAG) {
#ifndef NO_BUFFER_TYPE
                v->item.type = T_BUFFER;
                v->item.u.buf = allocate_buffer(field->length);
                write_buffer(v->item.u.buf, 0, target_row, field->length);
#else
                        v->item = const0u;
#endif
                    } else {
                v->item.type = T_STRING;
                if (target_row) {
                v->item.subtype = STRING_MALLOC;
                v->item.u.string = string_copy(target_row, "MySQL_fetch");
                } else {
                v->item.subtype = STRING_CONSTANT;
                v->item.u.string = "";
                }
                    }
            break;

        default:
            v->item = const0u;
            break;
    }
    最后是time()不跑的问题
    在driver里面,有个变量current_time标示时间
    每次心跳时、会current_time++
    心跳的触发要看backend.c 
186行左右 heart_beat_flag的标志变量
    
heart_beat_flag为1才会出发
    
heart_beat_flag 置1、是通过
    
backend.c-> call_haert_beat()
     391行左右
    signal(SIGALRM, sigalrm_handler);
    ualarm(HEARTBEAT_INTERVAL, 0);
    到时触发函数
sigalrm_handler()heart_beat_flag 置1
     在v22.2b14中ualarm没有调用成功、没有设置时钟、就不触发函数sigalrm_handler()
   心跳会停掉
    原因是ualarm.c文件#include "std.h"放的位置不正确
    因为 #ifndef HAS_UALARM 且 std.h里定义了HAS_UALARM 所以放到 #ifndef HAS_UALARM前
    就不会编译ualarm.c文件、就掉不到
ualarm()
    应该将#include "std.h"放到 #ifndef HAS_UALARM 之后

08
2013
01

Mudos V22pre11在windows xp下的编译

网上找的mudos编译完全手册(包括本论坛转载的)有些关键地方需要修正
这次编译,并不完全是在windows下完成的,因为有些文件在windows下无法生成。
编译环境:
Windows Xp sp2,VC6.0(最好)和VS2003均可。还有一个工具是UltraEdit  v15.10.0.1019(用来转换格式)

至于什么bison这些,我没有需要

现在来说步骤
    1,首先下一份mudos V22pre11源代码,首先在lfreebsd环境下编译,linux下应该也可以,如果你没有,你可以叫朋友帮忙编译好
然后把所有的编译好的代码文件都发给你,这里面就会有关键的文件,比如类似grammar.tab.h等
    2。这里是最关键的。把windows下的Mudos.mak文件放到上层代码目录下,然后,用UltraEdit 打开mak文件,利用里面的替换,把^n替换成^p,为什么要做这一步?主要是把unix格式的换行符变成windows下的换行符,然后保存。用VC6.0打开mudos.mak,应该就可以看到代码文件了,如果不转换,是无法看到代码文件的,我当初就卡在这里。
    3,现在,如果没有cc.h,那你就添加一个cc.h文件,如果有了,那么替换一下,内容如下:
#define COMPILER "cl" 
#define OPTIMIZE "-G4" 
#define CFLAGS "-DWIN32 -D_ALL_SOURCE -G4" 
#define OBJDIR "obj" 
4,删除 工程里的malloc.c,mallocwrapper.c,在工程里添加package/uids.c,package/mudlib_stats.c

5,修改 backend.c 中的函数 call_heart_beat(): 
删除以下: 
#ifdef WIN32 
static long Win32Thread = -1; 
#endif 
删除以下: 
if (Win32Thread == -1) Win32Thread = _beginthread(alarm_loop, 256, 0); 

6,修改 crc32.h 在 compute_crc32 函数之前加入: 
typedef unsigned int UINT32; 
修改 crc32.c 在前面增加: 
#include "crc32.h" 

7,修改 crypt.h 中的句子 
char *custom_crypt( CONST char *key, CONST char *salt, byte *rawout);
为 
char *custom_crypt( char *key, char *salt, byte *rawout); 
(即删除所有的 CONST) 

8,将 interface.h, efuns_main.c, compile_file.c, mudlib/interface.c 中所有名为 interface 的变量名都替换成 if_t_array。 

9,修改configure.h文件为下列内容,如果没有,请创建:
#define INCL_STDLIB_H
#define INCL_TIME_H
#define INCL_FCNTL_H
#define INCL_DOS_H
#define INCL_SYS_STAT_H
#define INCL_LIMITS_H
#define USE_STRUCT_DIRENT
#define INCL_STDARG_H
#define HAS_MEMMOVE
#define RAND
#define HAS_STRERROR
#define HAS_GETCWD

#define SIZEOF_INT 4
#define SIZEOF_PTR 4
#define SIZEOF_SHORT 2
#define SIZEOF_FLOAT 4
#define SIZEOF_INT 4
#define SIZEOF_PTR 4
#define SIZEOF_SHORT 2
#define SIZEOF_FLOAT 4
#define CONFIGURE_VERSION    5

好了。编译你的Mudos吧

编译好之后,是一个win32控制台的mudos,在winxp下可以运行,其他平台没测试

运行办法:
把生成好的mudos放到你mudlib的bin目录下,然后在创建一个快捷方式在当前目录,查看快捷方式的属性,并在目标那一栏mudos.exe后面空格加上你的config文件,比如:
F:\xkx\bin\MudOS.exe config.xkx
然后运行你的快捷方式,成功

 

08
2013
01

详谈add_action()及其BUG

   几年前,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

检测有多少物件objects()在内存的代码

 int main(object me)
{
    object *all_objs;
    int size;

    all_objs = objects();
    size = sizeof(all_objs);

    tell_object(me,sprintf("内存中一共有:%d 个物件。\n",size));
    return 1;
}

08
2013
01

一个引起当机的BUG的讨论。

 

一个引起当机的BUG的讨论。 


作者:jackyboy  发表时间:2001年7月31日 12:45
--------------------------------------------------------------------------------

在我自己的环境上,我编写下面的命令代码,运行后自己即刻断线,然后在第一个call_out
未调用前自己再次上线,再次执行改命令,在第一个call_out到达时间开始执行时,将会因
为me的值由object变为0导致MUDOS的段失败崩溃。如果运行两次命令不会当,可以多运行几
次看看。在我自己的OS + LIB里这个问题似乎是屡试屡“爽”,但是我不能100%肯定,所以
贴出来请大家也试试看。我用的是v22.2b11版本。

08
2013
01

在Linux或者Unix下的编译mudos过程

 

   MudOS 是整个Mud的基础。一个成功Mud 不只需要一个成熟的MudLIB,而且需要一个稳定的MudOS。而对于不同版本的MudOS,MudLIB也需要一定的修改。如何修改就不是本文的主要介绍内容,以后有时间继续和大家谈谈如何对于不同版本的MudOS来修改MudLIB。本次的主要内容是如何在 Linux上成功的编译MudOS和使用编译好的MudOS成功架设一个Mud。 


   现在流行的MudOS有两个版本:MudOS v22pre11和 MudOS v22.1b22。这两个版本的MudOS差异甚大,但是在不同版本的Linux下编译过程基本上是一样的。下面就以v22pre11来介绍如何编译MudOS。 
   1、获得MudOS 源代码: 
对于C ++功力不够的巫师还无法修改。而从这里下载的源代码已经修改好了,不需要修改就可以直接编译。 
这里经常见到的错误有 
●没有下载原始MudOS源代码的补丁(MudOS V22pre11源代码)。 
●原始的MudOS 源代码中的几个文件有错误必须经过修改后才能成功架设Mud。否则就虽然能成功编译MudOS 但是启动Mud 时会出现各种的错误。 

08
2013
01

自动重启MudOS

 

这要自己做一个自动重新启动的脚本,因为UNIX下的MUDOS一般不会自动启动的. 
西游记2000里有一个比较好的自动重起脚本,可以拿来参考. 
下面给出一个最简单的脚本,如果想偷懒的话.... 

while [ 0 -eq 0 ] 
do 
ps -u mudadm>/export/home0/mudadm/fy3/bin/file1 
grep fy3driver /export/home0/mudadm/fy3/bin/file1 
if [ $? -eq 1 ] 
  then 
cd /export/home0/mudadm/fy3/bin 
limit descriptors 256 
fy3driver config.fy3 
else 
  sleep 150 
fi 
done