MUD游戏开发进阶:exec 和 bind 外部函数

本文节选自《LPC语言基础教程:从零学习游戏开发》,版权归@mudren,欢迎转载,但必须注明来源(mud.ren)。

exec 外部函数

玩家连接游戏时会触发主控对象中的 connect(),这个方法返回的对象就是玩家对象,但实际开发中我们通过 connect() 返回的对象处理登录,然后登录后再切换到正在的玩家对象。

玩家控制的对象可以使用 exec() 外部函数切换,我们看看本教程配套的源码。

主控对象中处理登录的代码:

// 主控对象中的 connect 方法
object connect(int port)
{
    return new(LOGIN_OB);
}

连线对象(LOGIN_OB) /system/object/login 的代码:

#define WELCOME "/README"

void setup(string arg)
{
    object from, to;

    if (!arg || arg == "")
    {
        write("ID不能为空,请重新输入:");
        input_to("setup");
    }
    else
    {
        from = this_object();
        to = new(USER_OB, arg);
        exec(to, from);
    }
}

void logon()
{
    write(read_file(WELCOME));
    write("请输入你的ID:");
    input_to("setup");
}

登录对象返回的时 LOGIN_OB,在 LOGIN_OB 中使用 exec(to, from) 把玩家对象从 LOGIN_OB 改为 USER_OB。外部函数 exec() 的语法说明如下:

名称

exec() - 把连线玩家从一个对象切换到另一个对象

语法

int exec( object to, object from );

描述

这个外部函数允许把玩家连接从一个对象迁移到另一个对象。也就是说,在成功执行 exec(to, from) 后,interactive(to) 会返回 1,interactive(from) 会返回 0,控制 from 的玩家改为控制 to。如果切换成功 exec() 会返回 1,否则返回 0。

请注意,这个外部函数的功能过于强大,为保证游戏安全,请谨慎使用。较为适合的限制使用 exec() 的方式是定义一个同名模拟外部函数(simul_efun),并使用 valid_override() apply 方法来限制这个模拟外部函数的重写(efun::exec())。

我们来测试一下,在 /cmds/test/ 目录下新建测试对象 test_user.c,代码如下:

// test_user
inherit USER_OB;

void create()
{
    enable_commands(0);
    add_action("command_hook", "", 1);
    seteuid("头号玩家");
    set_living_name("test_user");
}

int main(object me, string arg)
{
    if (me == this_object())
    {
        return notify_fail("你已经是头号玩家了。\n");
    }

    exec(this_object(), me);

    return 1;
}

先输入 test_user,会把自己切换到这个 /cmds/test/test_user.c,然后可以输入 users 指令看看当前在线玩家有什么变化。

在游戏中,可以使用这个功能附身NPC并控制NPC行动。

bind 外部函数

我们知道,move_object() 外部函数是移动当前对象到指定位置,问题来了,如果要移动指定对象 ob 该怎么办?正常情况我们需要在对象 ob 中实现一个方法,比如:

void move(mixed dest)
{
    move_object(dest);
}

然后通过 ob->move(room) 的方式移动对象。除了这种方式还能怎么办?我们看看本教程给的示例指令 move_object,其中有这样的代码:

    fun = bind((: move_object :), ob1);
    evaluate(fun, ob2);

这里使用 bind() 外部函数定义了一个函数指针,以上代码的作用是把对象 ob1 绑定到外部函数 move_object,这样对 move_object 来说当前对象就是 ob1。外部函数 bind() 的语法说明如下:

名称

bind() - 改变一个函数指针的所属对象

语法

function bind(function f, object ob)

描述

返回一个和函数指针 f 完全相同的函数指针,但是所属对象从创建 f 的对象变为 ob。这在函数 f 的创建者被摧毁或 f 是需要由其它对象调用的外部函数时非常有用。

示例:

void make_living(object ob)
{
    function f;

    f = bind((: enable_commands :), ob);

    evaluate(f);
}

以上示例的效果和由对象 ob 自己执行 enable_commands() 外部函数完全一样。因为 bind() 允许你强制其它对象执行一些代码,这会造成一些安全风险,为了防止被滥用,需要 MASTER 对象中的 valid_bind() apply 方法返回1,否则 bind() 无法执行。

在本教程中提供的测试指令中有不少指令使用了 bind() 外部函数实现功能,比如 enable_commandsdisable_commandsmove_object等。

对比以上内容,我们会发现 execbind 功能有一些神似的地方,一个切换连线玩家,一个切换函数执行对象。需要强调的是这二个外部函数的功能都很强大,在正式游戏开发中都需要做好安全审查。

另外,还有一个功能有一些类似的外部函数 shadow,具体用法在以后的章节讲解。

京ICP备13031296号-4