zoukankan      html  css  js  c++  java
  • 从头开发MUDLIB

    跟Akuma一起从头打造mudlib--【第一讲】

    第一讲:让它跑起来
    注:每一讲我都会上传一个相符的lib,有些文件是旧的,有些是新的,我尽可能在lib里写清楚注释。更详细的内容则在每讲的正文里写。


    一个最简单的能跑的lib应该长成什么样子?每个基于mudos写lpc的人可能都会给出不同的答案。我记得曾经有个朋友释出过一个不到5k的lib。

    我这个则还要小一点,tgz之后是1851个字节。嗯。。。还好。
    我们对这个lib基本上不会有什么期待,但是他至少应该完成如下两个事情:
    1.能跑起来,并且接受用户的连接(你用zmud也好,telnet也好,总之是可以连到端口上)
    2.连接后的用户可以输入,并且lib应该给予一定的反应(那么最简单的做法就是完成一个所谓的echo server了---你输入什么,server就给你返回什么)。

    【配合本讲的lib版本为0.1,文件名则是newlib.0.1.tar.gz,见附件】
    以下是目录结构:
    .
    |-- adm
    |  `-- obj
    |      |-- master.c
    |      `-- simul_efun.c
    |-- include
    |  `-- globals.h
    |-- log
    `-- obj
        `-- user.c

    首先说目录结构,一个好的清晰的目录结构用起来很舒服,我自己一般推崇单层目录结构,但是考虑到习惯问题,我在/adm下采用了和xkx类似的方法,即把master和simul放到了/adm/obj/下。
    大致讲一下:
    目前我们只创建了四个基本目录:/adm /include /log /obj。
    adm放的都是“独一无二”的东西,比如最关键的两个物件master.c和simul_efun.c,还有以后会慢慢出现的各种daemons。
    include是所有头文件所在的目录,目前只有一个globals.h。
    log是用来存放各种log输出的,这没啥可讲的。
    obj是所有lib当中最终被用到的物件的实体文件,也就是会被最终new()或者load_object()出来的东西。暂时只有一个连线物件,也就是user.c。(你看,现在我们连login.c都省掉了,因为我们的当前目标仅仅是能让mud接受连接而已,断线重连、帐号认证等工作要放到以后完成)

    这里我们先讲一点基础的东西:mudos如何启动?
    我们不讲mudos自己,只说和lib有关的部分。
    有人认为,mudos最先载入的是master.c(请注意,这个master.c不是那个可以用来拜师的master),其实这个看法是有点问题的。
    一般来说,mudos最先载入的文件是simul_efun.c。
    我们知道,要想跑起一个mud,除了lib以外,还需要driver和一个给driver用的配置文件config.cfg。在config.cfg里,有三个文件是要被定义的:simul_efun.c,master.c和globals.h。如果愿意的话,我们当然可以给这三个文件起个其他的名字,比如master.c改名叫core.c,globals.h可以叫做dangzhongyang.h之类的,随意。。。不过大家都习惯了,我就不改了。

    重点1.关于simul_efun.c
    刚才我们说到,mudos启动之后,会先尝试载入这个simul_efun.c。
    这里有一个概念就是所谓的simul。众所周知,mudos为了游戏的目的,提供了大量的函数(efun)来帮助我们完成工作,比如说像判断是否为玩家的userp(),像是对字符串做操作的一系列函数等等。
    然而mudos也不是万能的,他不可能为预先想到所有可能的需求,有时候我们必须自己动手封装一些功能(比如说,log_file()这么方便而好用的功能,mudos就没有提供),如果应用范围很窄,我们当然可以直接把他写到代码里,但是像log_file这么大众化而且常用的功能,我们不可能每次用到就c/p到对应的文件里去,一个是不方便,一个也容易出错不是?
    于是,mudos提供了一个方法来解决这个问题,这就是simul efun,简单说就是“模拟的efun”,这个功能可以帮助我们封装合心意的函数,并且在lib的任何地方像使用efun一样的使用他(虽然说慢点吧。。。。当然从效率上说,最快的办法是把efun移植到mudos里,真正做成一个efun,然而这是另外一个话题,不是我们讨论的范畴)。
    完成这个工作所需要干的事儿不多,也很简单,就是把函数定义和体写到config.cfg指定的simul_efun.c当中就好了。(具体请参考我的lib当中simul_efun.c里的log_file()函数),这样我们就能让他像个efun一样的工作了。
    【题外话】很多mudlib经过了很长时间的发展之后,积累了大量的simul,统统塞到simul_efun.c当中也很困扰,不美观也不便于管理,因此他们会采用另外一个做法:继承。
    简单说就是把simul_efun.c当作一个入口文件,把所有的simul函数分门别类的写到其他文件当中去,在主文件里只用继承(inherit)的方法来把这些“其他文件”包含进来。(用include貌似也可吧。。。我不是很确定)

    ============分割线=====题外话2纯熟废话,有兴趣的同学可以自己参考OOP===============
    【题外话2】关于lpc的OOP特性。
    lpc这门语言出现的时间较早,虽然一般来说我们认为他是面向对象的,但是也并不完全符合面向对象的所有观念。
    比方说,他只支持对对象的函数引用,而不支持对对象的元素引用,当我们想获取或者改变一个对象中的变量时,我们只能通过函数来实现;
    再比方说,对于封装来讲,LPC并不能完好的实现多态,并且只有限的支持变长参数表(varargs,lpc只支持一个变长参数)。
    ============分割线=====题外话2纯熟废话,有兴趣的同学可以自己参考OOP===============

    重点2.master.c当中的connect()函数
    我们本讲开篇就说了,当下最重要的是让mudos能够接受连接。所以这里我们有必要讲讲mudos接受用户连接的机制。
    我们知道,LPC最有趣的地方就在于他是OOP的(不完全无所谓,反正了解到lib里所有的东西都是对象就ok了),那么给每个用户连线分配一个object是很正常也是很方便的管理方法。
    问题来了,如何让mudos知道“这是一个连线物件”呢?
    这里就用到了一个很重要的master_apply(这玩意儿我们下边讲到),master_apply::connect()
    他的原型是:
    object connect()
    这个函数的作用就在于,当mud接受到一个连接之后(不管你是用zmud还是telnet,总之你连上了mudos提供的端口服务),mudos会调用master的connect()函数,并且期望返回一个对象。这个对象就相当于在mudos里挂了号了。mudos会把它当作一个用户连线对象来对待,比如说会认为他是userp()和interactive()之类的(这几个函数有一些细微而且诡异的差别,有空我们再说)。
    请看我的lib里的connect():
    object connect()
    {
            log_file("new_user_login",time()+" ");
            return new("/obj/user.c");
    }
    很简单,master.c只要被动的等待mudos的呼叫,并且在合适的时候造一个user_ob就ok了。

    这里多讲一句:
    一个完整的登录过程,在mudos当中包括两步:
    a.调用master apply的connect()获取连线物件;
    b.调用这个连线物件身上的另外一个apply函数:logon()
    其中第二步是为了给登录验证过程一个合适的调用接口用的。目前我们0.1版的lib还用不到这么牛b的技术,所以他就悬空了。
    未来适当的时候我们再补充上。

    =======================分割线===============================================
    重点3.master的apply
    实际上master.c当中除了构造函数create()之外,基本都是apply。
    什么是apply呢?
    大概说一下,所谓apply,就是那种由mudos隐形调用的函数接口,这些接口是为mudos提供服务的,我们通常在lib当中,只是被动的通过这个接口告诉mudos在某些情况下“可以”或者“不可以”,或者“应该是谁”这样子。
    在普通对象身上的这种接口我们就把他叫做apply,在master身上的就是所谓的master apply了。

    通常大家见到的比较多的apply包括像 id()(被present()隐含调用),像reset()(被reset机制调用)。
    master里更多是跟权限有关的master apply,比如valid_xxx一族~~~
    ok,这里我们大概知道有这么个事情就ok了,具体以后碰到需要用的apply,我们再像这次的connect()一样讲解。

    =======================分割线===============================================
    重点4.globals.h
    大家会不会遇到这样的问题,我定义了一个宏,却忘了他在哪个头文件里?或者是写一个.c的时候经常要包含无数的头文件,其实就为了他当中一两个宏而已?
    感谢mudos的作者,他通过globals.h帮我们在一定程度上解决了这个问题:
    当我们有些宏定义是非常全句化的(比如说对目录的定义,对一些重要ob的定义等等),我们可以把它们丢到globals.h里。
    并且,更方便的在这里:我们不需要显性的在程序里include这个globals.h,mudos自动的帮我们在每个.c当中都包含了它。
    所以,当你有一些宏很全局的时候,尽管塞给globals.h吧。。。

    另外一点,请看现在这个非常简单的globals.h
    #ifndef __GLOBALS_H__
    #define __GLOBALS_H__

    #define LOG_DIR                "/log/"

    #endif

    发现有什么不一样没有?
    我们使用了#ifdnef #define #endif的方式。
    这样做的好处是,避免出现由于.h嵌套而导致的redefine(比如说a.c包含了a.h和b.h,不幸的是a.c同时继承了b.c,而b.c自己也包含了b.h,这样就出现了实际上的嵌套)

    =======================分割线===============================================
    重点5.user.c
    我们的user.c现在功能很简单。在正常的lib当中,为了方便登录认证和断线重连,这个文件被分成了两个。即login.c和user.c(也许有人直接是用body.c?我不太确定,总之是这么个意思)。一个负责密码认证,一个负责真正的用户行为和数据。并且通过exec()函数把mudos认为的用户标签在两个之间切换。

    现在我们这里非常简单,不认证,所以直接就只有一个user.c,未来再细化这里。

    接下来,mudos如何获取用户的指令呢?
    在正常的mudlib当中,我们通常通过一系列复杂的行为来实现之,例如通过一个“全时的”的add_action(command_hook)钩子来获取用户输入,并且通过commandd.c之类名字的一个daemon来找到恰当的指令文件,并执行之。
    未来我们也会完善这样的一套结构,不过现在么。。。既然我们只想完成一个echo server。我们暂时用一个更简单的方式来处理他:string process_input(string arg)
    这又是一个apply,在“正常的”mudlib里,我们通常用这个apply来实现global alias的解析。
    这里我们就直接通过
    string process_input(string arg)
    {
            write(arg+" ");
    }
    这样的模式来完成“echo server”的工作了。

    这边有个问题,大家有没有发现一个string型的函数我没有return,这直接导致跑起来之后,我们每个指令敲进去都会返回一个>what?
    不要紧,反正这个版本我们只是大概的演示,下一次我们修好他。

    好了,这次一共就四个文件,我们构建了一个可以跑起来并且有反应的lib了。大家可以跑一下看看(附件里我提供了一个bin文件,包含了driver和config.cfg,是linux版的,由于我在我的服务器上跑的driver非常多,所以我给driver改名叫mud以避免不幸被killall掉。)
    如果您想在linux下跑,只要chmod +x mud,并且重新配置一下config.cfg里的mudlib和bin的目录就ok了。
    如果您想在win下测试,那么很抱歉,我手头没有合适的mudos.exe版本(哪位好心的老大提供一个)

    =================分了又割===================================================
    小结和预告:
    第一次写这种东西(声明一下,我没写过语言类的书,所以不太会写,可能结构太乱,内容也很分散,抱歉),大家有啥建议和意见欢迎跟帖,我尽量改进。
    附件是两个文件:
    bin.tar.gz 是linux版的mudos和config
    newlib.0.1.tar.gz是和本讲座同步的lib。可能有个问题,就是里边涉及到的中文是utf8格式的,大家用起来可能是乱码,这个倒是可以用uedit转一下码(主要是我习惯了在shell下直接敲代码,也就不改编码了,好在作为一个讲解用的lib,不会有太多中文内容在里边)。
    下一讲,我想我会把登录验证和指令系统补全。这样我们就可以有空间做一些实验了(用指令来写测试代码很方便的说)。
    另外,如果可能,也许会补一个不那么完善的权限系统,也许不会,我看进度把。。。

  • 相关阅读:
    HDU 5938 Four Operations 【贪心】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5935 Car 【模拟】 (2016年中国大学生程序设计竞赛(杭州))
    HDU 5934 Bomb 【图论缩点】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5933 ArcSoft's Office Rearrangement 【模拟】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5929 Basic Data Structure 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    【转】LaTeX 符号命令大全
    HDU 5922 Minimum’s Revenge 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    HDU 5927 Auxiliary Set 【DFS+树】(2016CCPC东北地区大学生程序设计竞赛)
    数据结构之稀疏矩阵
    C++中引用(&)的用法和应用实例
  • 原文地址:https://www.cnblogs.com/cfas/p/5877846.html
Copyright © 2011-2022 走看看