第三讲:dbase和登录过程
这几天过10.1,反而比较忙,不常在家。
所以估计这八天里也就是今天这一讲了。
实际上lib的内容昨天就写完了。关键是怎么讲的问题。
第二讲里,本来说是准备接着把指令的权限部分完成,结果仔细想了一下,发现没法搞。原因有二:
第一个是,还没有登录过程,登进来的user_ob都不知道谁是谁,如何定权限?
第二个是,一旦说到登录,那事情就变得复杂了,最起码的,“是谁”的问题,也就是id怎么存到user_ob身上呢?这起码应该有个dbase。
所以想了半天,决定把这两块先补充上,然后再继续写权限的部分。
因此这一讲有两个重点,一个是dbase和相关的函数,另外一个就是做一个简单的登录过程。在这之后后再按顺序把save restore,security这些搞定,我们就可以继续回到指令权限这个内容上来了。
请看目录(对应的文件见附件newlib.0.3.tar.gz):
.
|-- adm
| |-- daemons
| | |-- cmd_d.c
| | `-- login_d.c
| `-- obj
| |-- master.c
| `-- simul_efun.c
|-- cmds
| `-- usr
| `-- test.c
|-- include
| `-- globals.h
|-- log
|-- obj
| `-- user.c
`-- std
`-- body
`-- user_dbase.c
重点1.dbase。
我们依然秉承这样的原则:所有新添加的内容都是暂时够用就好,打底子的东西我尽量细致的讲。这块熟悉了,以后我们根据需要添加细节就容易了,那个时候反而可以一笔带过。
请看/std/body/user_dbase.c。一般来说,es2继承过来的lib都是爱用/feature/dbase.c这样的结构。我自己受到kok lib的影响比较大,所以倾向于用/std/body这样的结构,这个不是很关键的东西。大家只要知道,如果要对照,就去看/feature/dbase.c就好了(一般来说dbase又是从treemap继承过来的,不过我个人比较喜欢不超过两层的继承关系)。
dbase是什么?
如我们之前所讲过的,lpc并不是完全意义上的面向对象,他不支持对数据域的直接访问,因此这个dbase就是对ob身上的数据以及数据处理接口函数的一个集合。
这里我们这个lib第一次用到了继承(inherit),具体如果想了解,大家可以搜一下面向对象有关的说明,一般性的,只要知道被继承的文件里的数据和函数可以直接当作继承后的对象身上的数据和函数就好。
以我们现在使用的user_ob为例,我们总是需要知道、或者设置他“是谁”,“有多少hp“这样的问题,归纳起来,最常用的可能就是“哪个属性”是“多少”这样的话题了。对比lpc的数据类型,这正是mapping(映射)的拿手好戏。
所谓mapping,其实在更通用的数据结构书里,也就是hash表(哈希表)。这东西的内部机制不讲了,大家只要了解,他分为key和value两部分通常可以写成(["a":1,"b":2])这样的形式,并且可以用map["a"]这样的形式来引用和赋值。
所以我们第一步就是给user_ob设计了一个叫做dbase的mapping,用来存储大部分的数据(事实上,包括ROOM,NPC,item,都是有类似的结构,一般的lib也都是继承了同样的dbase.c,但是我们这里暂时只给user_ob使用,因此叫做user_dbase.c,至于以后其他的对象是否也继承它、还是单独写各自的xxx_dbase,要根据需求再定)
具体请看user_dbase.c
mapping dbase = ([]);
mixed set(mixed prop,mixed data)
{
return dbase[prop] = data;
}
mixed query(mixed prop)
{
return dbase[prop];
}
void delete(mixed prop)
{
map_delete(dbase,prop);
}
现在这个版本相当粗略,我们只定义了一个叫做dbase的“全局”mapping,和三个函数set(),query(),delete()。
事实上,真正使用的lib里,往往要比这个复杂的多,至少有三处:
a. 多一个temp_dbase的static mapping(在不同的driver下,static也可能被写作nosave,表示不会被save_object()所整理存盘),用来存储那些只在对象存在过程中被使用,但是不需要存盘的数据;
b.至少还应该有一个add()函数(实际上就是set(xxx,query(xxx)+data))
c.这个dbase应该支持多层结构,也就是他的任何一个data都可以又是个mapping,这就是我们的prop用mixed来定义的原因
关于这个,我们多讲一句,set("aaa/bbb",xxx)这种结构大家可能都见过,这就是表示aaa这个key的内容是(["bbb":xxx])。这会使得我们的这几个函数变得负责不好读,而且暂时也用不到,因此我们把这个需求留待以后再处理。现在我们只要能支持读写单纯的key = value形式就好了。
有了dbase,我们就可以读/写用户物件上的数据了。
重点2.登录过程
很遗憾,现在这个版本的lib,我们依然没法把它写完整。为什么呢?因为我们还没有save/restore机制。
请看user.c
int setup();
void create()
{
// setup();
}
/*
string process_input(string arg)
{
write(arg+"\n");
}
*/
// apply of user_ob :: logon()
// after master->connect(),mudos calls user_ob->logon()
// we use this to put user_ob into LOGIN_D
// and start Login authentication
void logon()
{
call_out("login_timeout",60);
this_object()->set("login_temp",1);
LOGIN_D->logon(this_object());
}
void login_timeout()
{
if(this_object()->query("login_temp"))
destruct(this_object());
}
这里我们调整了一下,把原来在create()里的setup()过程干掉了,增加了一个叫做logon()的函数。
注意,这是一个apply,它在master apply::connect()之后会被调用。
我们用这个函数把已经获得了连线物件权限的user_ob发给了LOGIN_D。
void logon()
{
call_out("login_timeout",60);
this_object()->set("login_temp",1);
LOGIN_D->logon(this_object());
}
注意这里有一个call_out,用来防止有用户连进来什么都不干,所以有个60秒的超时处理。然而为了防止正常登录用户也在60秒之后被踢。所以用一个login_temp的标记来做判断,这个标记会在LOGIN_D成功登录之后被清理掉。从而保证登录进来的用户不会被踢。(这里本来应该是个temp mark,不过咱们没有,就先凑合用dbase存)
未来/adm/daemons/login_d.c会被用来处理整个的登录握手或新用户创建过程。只是目前还是一个很粗糙的结构。
void logon(object ob)
{
write("Welcome to my New mudlib\n");
input_to("get_id", ob);
}
这是被user_ob的logon()所呼叫的处理函数,你看多好。。。连WELCOME都有了。。。。
我们直接用input_to获取用户输入的id。
正常过程,这里应该判断是否有该用户,用户id是否合法,然后是获取密码,对用户data做restore等流程。这里就简化了。
总之最后是进入到enter_world()过程,应该把用户丢到合适的room(可惜我们还没有room。。。),给用户适当的权限(我们也没有。。。),打开用户的指令输入权利(这个有了,就是user_ob->setup(),大家还记得吧,之前被我们从create()里干掉的setup()终于又被我们找了个机会用回来了)。
这里打一个补丁,setup()里我们增加了set_living_name()这个函数,是为了让find_player()可以找到这个“玩家”。
如此,一个完整的登录过程,虽然粗糙,但是结构有了。
下面我们就可以从容的完成权限分配,save/restore等内容了。。。请期待下一讲~
ps.随着lib逐渐搭起来,我也很犹豫,到底是把一些我们后来发展起来的比较新的模式放进去,还是尽可能贴近大家比较熟悉的老lib。
不过考虑到这个lib的讲解的性质,还是决定尽可能贴近老lib,毕竟大家用的熟了,更多还是在这些体系下写东西。