扩展MUD 的efun函数

搞定MUDOS (EFUN)by [email protected] 
大家对LPC多少都有些熟悉了,这里就讲讲LPC里面的EFUN是怎么来的。
任何一个EFUN都有一个入口,这个入口就是interpret.c里面的eval_instruction()函数,在这里面会把EFUN传入的参数进行初始化,并把LPC中的数据类型转换成相应的结构体。经过eval_instruction()之后,就是EFUN的主体了,关于主体如何声明,将在后面提到。这里有两个全局变量是EFUN里面必不可少的,一个是int st_num_arg和svalue_t * sp,其中st_num_arg是该EFUN传入的参数的个数,然后SP就是参数的堆栈。这里有一点值得注意,就是任何EFUN的主体都是不能直接接参数的,所有的参数都是放在sp这个全局变量里面。
sp的格式如下:
sp    最后一个参数
sp-1   倒数第二个参数
sp-2   倒数第三个参数
..
sp-st_num_arg+1  第一个参数
那么还有人会问,sp是svalue_t类型的,怎么转换成我要的int,char*,或者object_t型呢?在svalue_t里面,定义了一个叫u的联合体,和一个叫type的int值,根据这两个值,我们就能取出我们想要的东西了,比如有一个EFUN叫void foo(int),然后在LPC里面是这样调用的:foo(5),那么在foo这个EFUN的主体里面,我们就这样获得那个5:
int num;
if(sp->type == T_NUMBER) num = sp->u.number;
这个时候num就是5了,sp的type有以下几种:T_NUMBER, T_STRING, T_REAL, T_BUFFER, T_OBJECT, T_MAPPING, T_FUNCTION, T_ARRAY,相对应的结构体分别为:int, char*, float, buffer_t, object_t, mapping_t, function_t 和 array_t,下面来讲讲这几种结构体的使用方法:
buffer_t, 访问buffer_t的方法有下面一些:
read_buffer (buffer_t * buf, int start, int len, int * rlen)
这个返回的是一个char *的东东,意思是从buffer_t里面的start开始读,读len个字节,rlen是读完以后的长度。
write_buffer (buffer_t * buf, int start, char * str, int theLength)
返回1是成功,返回0是失败,这个是把str写入进buffer里,start是开始的点,len是长度。
allocate_buffer (int size)
分配一个size大小的空buffer。
free_buffer (buffer_t * buf)
如果你用了allocate_buffer的话,那么一定要记得用free_buffer,否则可能会内存泄露导致当机,血的教训,俺就有几次忘记free了。
object_t, 对这个结构体进行操作的东西就多了,这里就不一一介绍了,相关的大家可以去看看object.c这个文件。讲讲这个结构体中一些东东的作用吧,比如说我在LPC里面写了一个object ob,我知道ob身上有一个叫int num的全局变量,那么该怎么来获得呢,首先得用
find_global_variable((program_t*)p, "num", &(unsigned short)type /*这个是用来返回类型的*/, 0)
来获得一个index,然后直接ob->variables[index]就能获得一个svalue_t的指针。其他的关于什么inherit_list什么的就不多讲了,大家有空去看看object.c,和object.h里面关于object_t的声明。
mapping_t, 如果大家想在MUDOS层对玩家身上的dbase进行操作的话,那么是一定得学会用mapping_t这个结构体的,首先用上面object_t的方法获得玩家身上的dbase的svalue指针。然后用mapping_t * map = svalue->u.mapping来声明一个指向dbase的指针。然后由于 mapping 是一个hash表数据结构,地址是需要通过hash函数来计算出来的,所以无法直接访问,必须用一个叫find_string_in_mapping(mapping_t, char*)来返回一个svalue的指针,比如说我想获得map里面的"id"的value,就用char * id_value = find_string_in_mapping(map,"id"); 就可以找到"id"的值。关于mapping的更多操作这里就不一一介绍了,感兴趣的同胞们就打开mapping.c和mapping.h看看吧。
function_t,这个没有太多可以介绍的,大家只要知道function_t的可以通过:call_program(current_prog, ((function*)funp)->address); 来呼叫就可以了,其中的current_prog是一个program_t类型的变量,这个东东一般是通过((object_t *)ob)->prog来获得的,其实我们平常所谓的binary代码就是把program_t类型的东东写入文件出来的东东。
array_t,要掌握array_t,只要掌握array_t里面的item即可,下面来看段代码:
array_t * arr;
arr = allocate_empty_array(n);
while (--n) {
arr->item[n].type = T_NUMBER;
arr->item[n].subtype= 0;
arr->item[n].u.number = n;
}
这个时候一个大小为n的int型数组就弄完了,其中的元素是:({ 0, 1, 2, 3, .... n })大家明白地干活?
好,这么多类型都讲完了,相信大家基本上都可以完成自己想要的EFUN了,什么?还不能完成。。哦,对,还没告诉大家怎么把计算出来的记过返回出来呢。
MUDOS对EFUN的传入和传出是使用的同一个堆栈,也就是说,读入参数是从根据sp读出来,返回的参数也是在sp里面,返回的时候就需要先把读入进来的参数清空掉,对于非地址类型的参数呢,不需要清空,程序结束,自然就没了,但是对于string,object,buffer,mapping,array之类的参数呢,在传入进来的时候是分配了空间的,这个时候就需要先free再清空了,把传进来的参数一个个free_svalue()掉,然后再把sp的指针指向堆栈头,然后把堆栈顶部的东东指向你要传回的值,就搞定了。。。恩,废话不多说了,来看段例子代码:
char *fname;
int x,y;
array_t * result;
fname = (sp-2)->u.string;
x = (sp-1)->u.number;
y = sp->u.number;
....// 一堆运算过程,然后需要返回result这个array.
free_string_svalue(sp-2);
sp -= 3;
sp->u.type = T_ARRAY;
sp->u.array = arr;
OK,搞定,呵呵,很麻烦,是吧?其实MUDOS里面已经把这一系列的东西都封装好了的,在macros.h里面,可以用pop_n_elems(st_num_arg)来实现free_svalue() + sp-=st_num_arg的功能,然后可以用push_array(arr)来实现sp->u.TYPE和sp->u.array的功能,记得,如果用pop_n_elems的话,就一定要用push_xxx,而不要用put_xxx,大家会说,我看其他EFUN里面也是写的put_xxx()啊,(猪,俺锤你)因为其他EFUN没有用pop_n_elems()啊,因为在put_xxx里面会自动把sp++,所以如果把pop_n_elems()和put_xxx混用的话,就会导致执行完EFUN以后马上crash...呵呵。
好,相信写到这里,大家都知道怎么新加一个EFUN的主体了,那么怎么让这个EFUN生效呢?这里有两种方法,一种是直接写到efun_main.c这个文件里面,然后在efun_main_spec.c里面加一个关于该函数的声明,就马上可以用了,另外一种方法是在package/下面写一个myefun.c然后再写一个myefun_spec.c,把函数主体写在myefun.c里面,然后在myefun_spec.c里面写上函数的声明,然后在options.h里面加一句:
#define PACKAGE_MYEFUN
即可,简单,实在,又好用,呵呵,牙好胃口就好什么的。
恩,下面来说说调试时候的相关问题,大家把EFUN写好了,心情激动吧,重新编译吧,发现编译通过了,高兴吧,然后运行把,发现当机了,SB了吧,呵呵,不要急,crash是正常的,只有crash了才能找到问题的根本。那么怎么调试呢?首先,编译的时候先./BuildMudOS debug然后再gmake,这个时候编译出来的就是debug版,在crash的时候会抛出一串看不懂的东东,呵呵,既然看不懂,有什么用呢,虽然看不懂,不过多少还是抛出了错误,-_-b,不要打脸,不过编译成DEBUG版最大的好处不是它自己抛出的错误,而是在使用gdb调试的时候可以step by step的调试。恩下面简单介绍一下用gdb调试driver的过程。
比如我新写了个EFUN,叫f_heihei,然后编译通过了,获得了driver,执行的时候就这样执行:
gdb driver
b f_heihei
run config.cfg
然后driver就跑起来了,这个时候你再用heihei这个EFUN的时候,driver就会阻塞,等你来输入命令,这个时候可以用print 打印出你想看的变量的值,然后可以用n或者next来执行下一句,当机了的话可以用backtrace来抛出最后一次呼叫的堆栈。好了,先说这么多吧,要把底都掏完了的话偶以后还混个P啊,当然要留两手的说,HOHO。。偶很邪恶吧~~~
附:返回"hello world"的EFUN:
myefun_spec.c
string hello_world(int type);
myefun.c
#include "../std.h"
#ifdef F_HELLO_WORLD
void f_hello_world() //有些EFUN都带PROT什么的,别理它,俺们操作系统认识不带PROT的。
{
char * res;
int type = sp->u.number;
pop_n_elems(st_num_arg);
switch(type)
{
  case 1: res = "hello world, I am the first type."; break;
  case 2: res = "hello world, I am the second type."; break;
  default : res = "hello world, I am nothing."; 
}
copy_and_push_string(res);
}
#endif
在 options.h 里面加一个#define PACKAGE_MYEFUN
重新编译,搞定。

时间: 2024-07-30 06:13:03

扩展MUD 的efun函数的相关文章

【扩展知识2】函数strlen()和非函数sizeof的使用

[扩展知识2]函数strlen()和非函数sizeof的使用 [扩展目录] strlen函数 sizeof ( 1 )函数strlen() 原型:size_tstrlen ( const char * str ); 返回C字符串(仅仅支持此类型)的长度. //strlen()的使用 #include <stdio.h> int main( void ) { chararray[ ]= "zhijiandeweixiao"; //指尖的微笑 //array为数组的首个地址 p

javascript 数组扩展实现 php array_count_values() 函数功能

在PHP中,array_count_values() 这个函数可以统计数组元素出现的次数,这个函数会返回一个数组,键名是原数组的值,键值是这个值出现的次数. 但是JavaScript中没有这样的函数.不过大神无数,前些日子发现这样的一个扩展: /** javascript 数组扩展实现 php array_count_values() 函数功能 */ (function(window){ if ( window.ActiveXObject ) { window.Array.prototype.i

使用prototype扩展的JavaScript常用函数库

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 /** * 检索数组元素(原型扩展或重载) * @param {o} 被检索的元素值 * @type int * @returns 元素索引 */ Array.prototype.contains = function(o) { var index = -1; for(var i=0;i<thi

c扩展调用php的函数(调用实现php函数的c函数)

上一次是写的c扩展调用c的标准函数,但是只能调用头文件中申明的函数,今天来说下c扩展调用实现php函数的c函数,比方说,c扩展要用到php中ip2long这个函数,但是c不可能去php中调用,肯定是去调用实现php函数的c函数.那么c扩展如何调用c内核对php的API呢? 这里要用到一个函数:ZEND_API int call_user_function_ex(HashTable *function_table, zval **object_pp, zval *function_name, zv

sqlserver 只有函数和扩展存储过程才能从函数内部执行

一个SQLServer的自定义函数中调用一个自定义的存储过程,执行此函数后发出如下提示:“只有函数和扩展存储过程才能从函数内部执行". 原因:函数只能使用简单的sql语句,逻辑控制语句,复杂一点的存储过程是不能调用的,在函数里也不能使用execute  sp_executesql  或者execute .解决方法把函数改为存储过程,然后在另一个存储过程中象调用函数一样使用此存储过程就可以了. 下面是一个存储过程调用另一个存储过程的实例,有参数传递的. --存储过程sp_B    create 

ES6随笔--各数据类型的扩展(3)--函数

ES6随笔--各数据类型的扩展(3)--函数 1. 函数参数的默认值 可以在函数参数中指定默认值,函数内不能再用let或const声明: 使用参数默认值时,不能出现同名参数: 参数默认值惰性求值,每次调用函数会重新计算参数默认值表达式: let x = 99; function foo(p = x + 1) { console.log(p); } foo() // 100 x = 100; foo() // 101 可以与解构赋值默认值结合使用: function ({x, y = 5} = {

easyui扩展正则验证,函数验证

用easyui做业务系统,对于默认的几个验证规则,肯定是不够的,难免会增加几种规则.可是问题来了,往往是我们在开发会遇到很多各种各样的验证,时间久了才发现,这些扩展的正则无非就是添加一个正则验证规则,那我为啥不将正则放到前端呢?想到这个说干就干,于是有了REGEX这个验证规则,愉快的调用几次后,感觉这功能还不错,心里贼爽了下.一段时间后,发现有些验证居然还和数据业务有关系,这下问题又来,难道我又要些一堆的规则!到底能不能象我的正则验证一样统一呢.果不其然,在苦思冥想半小时后,我这FUN验证规则浮

jquery 扩展方法,自定义函数等一些写法

// 传参数 var aa = function(x){ //弹出对象 x 里的 aa 变量和 bb 变量 alert(x.aa + " 我成功啦 " + x.bb); } $.windowbox = aa; $.windowbox({ aa: "哈哈", bb: "啦啦" }); //方法定义 $.windowbox = { //定义一个方法aa aa: function(){ alert("aa"); }, //定义一个方

扩展JMeter - 创建自定义函数 - String Joiner (翻译)

JMeter是测试自动化社区中最好的开源工具之一.它提供了所有可能的扩展,可以快速提供我们的测试脚本.为了让我们的生活更轻松,它还让我们通过实现几个接口来提出我们自己的插件. 在本文中,让我们看看如何创建自定义函数并使其出现在下面的JMeter 函数帮助器对话框中. 目标: 我的目标是创建一个简单的Join函数,它将使用给定的分隔符连接2个给定的字符串并将其存储在用户定义的变量中. 用法是   $ {__ join(string1,string2,delimiter,resultVariable