代码库
与题材这个概念相关的是代码库。要说明两者如何关联,需要简短介绍一下虚拟世界服务器架构的几条原理。
代码库之所以出现,是因为创建一个虚拟世界所需的许多工作,在创建其他虚拟世界时可以复用。究竟能复用多少,取决于具体的代码库。代码库主要与虚拟世界的第三个时代相关联,不过这种做法早在 MUD1 就已经采用,而且至今仍有人在尝试为图形化虚拟世界造出同类方案(Asheron’s Call 就是本着这个想法设计的 1)。
运行一个虚拟世界的软件里,最基础的部分是驱动程序(driver)。它包含任何成熟的解释器都会有的那些常规例程,负责内存管理、解析、数据结构之类的事务。与之配套的还有更像操作系统的东西,比如输入/输出排队、超时、数据包处理等等。结果是,驱动程序可以向程序的更高层次提供两个基础概念:用来构建虚拟世界的实体的存在(例如物件),以及输入/输出与其中某些实体(例如玩家)的关联。
在这一层之上,是(出于历史原因)所谓的 mudlib(MUD 函数库)2。mudlib 定义虚拟世界的物理规则,其中包括质量/重量、计时器、移动、通信之类的东西,以及(在游戏语境下)魔法与战斗机制这类更高层的概念。
mudlib 之上的一层是世界定义。借助物理规则建立起来的模型,可以加入这个世界特有的概念。新的功能与这些物件关联起来,而这些功能同样是由物理规则推导出来的(尽管未必由它直接定义)。世界定义完全是描述性的:可以用它创建这个世界的多个化身。
最后是这个世界本身的特定实例化。真实的数据项定义了这个个别的世界,把它与所有可能被定义出来的世界区分开来。
下面用一个例子说明这些部分如何拼在一起。假设有一个世界,其中(除了许多别的东西)一把银钥匙能打开一个银匣子。驱动程序定义这些概念:离散的物件;操纵这类物件的动作;能够触发这类动作的行动者(也就是玩家)。mudlib 定义能够容纳其他物件的物件这个概念,以及这种情况发生的条件。世界定义则给出匣子和钥匙这两个概念,并规定把正确的钥匙用在某个匣子上就能让那个匣子打开。世界的实例化则为 object32(一个银匣子)和 object19(一把银钥匙)给出显式的表示;实例化世界里某个银匣子的状态,取决于有没有人用银钥匙打开或关上过它。
好,以上就是服务器的各个组成部分:
驱动程序
mudlib(物理规则)
世界模型
实例化
所有代码库都必须实现这些层次,但做法不必相同。它们通常把三种技术组合起来使用:
用 C 或 C++ 这类真正的编程语言把它们硬编码。
用 LPC 或 MUF(Multi-User Forth——MUCK 所用)这类脚本语言把它们软编码,由硬编码部分来解释执行。
把它们作为数据显式存放在文件里,这些文件对硬编码部分、软编码部分或两者都有意义。
具体怎么做,取决于特定的代码库。
凡是硬编码的部分,合起来构成引擎。对 MUSH 和 MOO 来说,引擎就是驱动程序;对 LPMUD 来说,引擎是驱动程序加 mudlib;对 DikuMUD 来说,引擎是驱动程序、mudlib 和世界定义。把更高层的功能放进引擎,好处是易用、运行时效率也高;坏处是缺乏灵活性,也缺乏表达力。
凡是存放到数据文件里的,就是数据库(它可能是、也可能不是一个带有正式查询语言之类的第三方数据库)。这可能会让人有点糊涂,因为构成这个数据库的,最多可以有三类完全不同的数据库:
脚本语言的代码
模板
实例化
这三类中的任何一类或全部,都可以静态使用(只在初始化时使用)或动态使用(每次使用时即时查阅)。
下面把这几种数据库形态再看得仔细一点。
脚本语言数据库由虚拟世界所需的脚本文件构成。对 MUSH 来说,这些脚本本身就定义了 mudlib、世界模型和世界的实例化;对 LPMUD 来说,它们只定义世界模型。
模板数据库包含物件的定义,一个世界可以据此构建出来。有了这种设置,就可以做一些事,比如通过改一改当初生成某个虚拟世界居民时所用的那个模板,来改变它的行为——你不必改动或重新编译任何代码。DikuMUD 用的就是模板数据库。
实例化数据库(更广为人知的叫法是运行时数据库)存放已实例化的世界的状态,具体说就是那些能跨越服务器关机而持续存在、且无法从脚本数据库或模板数据库生成的值。一个很好的例子是玩家角色的数据:玩家不在虚拟世界里的那段时间,这些数据通常就收在数据库中。连续运行的世界会定期把整个状态存入实例化数据库,以便在机器发生灾难性故障后能够恢复。
注意,一个代码库可以把这三类数据库合而为一。比如 MOO 的面向对象实现意味着,把所有数据对象转储出来(也就是创建一个运行时数据库)时,会连同所有模板和脚本一并转储出来,因为它们就是这样被定义为对象的。
表 1.1 归纳了若干代码库家族(为了对照,也包括一些个别的虚拟世界)在这些方面的差异。表中把每个组成部分的层次标为代码(由计算机硬件执行)、脚本(由代码执行 3)或数据(不可执行)。
| 代码库 | 驱动程序 | mudlib | 世界模型 | 运行时 |
|---|---|---|---|---|
| DikuMUD | 代码 | 代码 | 代码 | 数据 |
| MUD1 | 代码 | 代码 | 脚本 | 数据 |
| MUD2 | 代码 | 脚本 | 脚本 | 数据 |
| Ultima Online | 代码 | 脚本 | 脚本 | 数据 |
| LPMUD | 代码 | 代码 | 脚本 | 脚本 |
| MUCK、MUSH 与 MOO | 代码 | 脚本 | 脚本 | 脚本 |
这些又跟题材有什么关系?
嗯,代码库的实现方式会影响它们自己实现的虚拟世界。硬编码的功能灵活性较差,但更强大;软编码的功能写起来更快,跑起来更慢。
因此,如果你想要一个动作很多、能带来强烈体验的虚拟世界,就该选偏重代码的方案(比如 DikuMUD);如果你想要一个即兴发挥和创造最重要的虚拟世界,就该选偏重脚本的方案(比如 MOO);如果你想要一个有细致物理规则、但设定非主流的代码库,就该选用代码实现 mudlib、用脚本语言实现世界模型的方案(比如 LPMUD)。
大致说来,用于(文字)虚拟世界的主要代码库 4 都符合下面这些典型印象:
DikuMUD:偏重冒险,极其强调与计算机控制的敌人战斗。体验很刺激,但世界本身往往不会有太大变化。它们大多是奇幻设定,科幻远远排在第二位。
LPMUD:同样偏重冒险,但对战斗的强调较少。它们常常会随时间不断扩展。它们也以奇幻为主,但范围更宽;科幻、恐怖和神话世界也为数不少。
MUCK:偏重社交,极其看重角色扮演。它们通常以某部具体的奇幻、科幻或恐怖作品为蓝本。不依具体作品的那些,往往以原创的拟人化动物(furries)为题材。
MUSH:偏重社交,大多着重角色扮演,但偶尔根本算不上游戏。MUSH 往往采用以书、漫画或电影为蓝本的科幻设定,奇幻则差着一截。
MOO:最不偏向游戏的代码库,用它做出来的非游戏世界,比所有其他代码库加在一起还多。它偶尔产出的游戏通常是原创的(而非衍生的)奇幻,而且是为角色扮演、而不是为冒险设计的。
这些典型印象又被各个代码库的历史传承所强化。虽然(比方说)完全可以用 LPC 编出一个一模一样的 MOO 复刻品,但想写一个“类 MOO”虚拟世界的人,多半会干脆去用 MOO 代码库。再者,刚开一个 LPMUD 的人,多半会拿别人的 mudlib 当起点。于是,与某个代码库相关联的主题或题材含义,就会随着这个代码库的演进而延续下去,也就是说,新版本出现时,通常都会针对自己偏爱的题材加以打磨 5。
代码库是给免费的文字虚拟世界(这类世界有几千个)分类的常用方式;但在大多数其他场合,它没多大用。比如几乎所有的图形化虚拟世界都有自己的专有代码库。因此,人们也常用别的办法来有效区分虚拟世界的不同性质。
正在开发中的主要开源 3D 图形引擎有:Crystal Space(http://crystal.sourceforge.net);NeL(http://www.nevrax.org);Quakeforge(http://sourceforge.net/projects/quake);Worldforge(http://www.worldforge.org)。 ↩︎
即 “mud library”。MUD1 也有 mudlib,但它是把 BCPL 的输入/输出库改编而来,因此层次比今天的 mudlib 低。这个术语的现代用法是 LPMUD 独立提出的。 ↩︎
有一种观点认为,像 LPC 这种功能强大的脚本语言可以用于虚拟世界之外的应用(比如编写 WWW 服务器),应当同时算作代码和脚本语言。MUDDLE 甚至可以直接编译运行,而不必解释执行。 ↩︎
关于衍生代码库的更多细节和说明,见 rec.games.mud FAQ 第 2 部分和第 4 部分:http://www.mudconnect.com/mudfaq/mudfaq-p2.html#q6、http://www.mudconnect.com/mudfaq/mudfaq-p4.html。 ↩︎
从玩家和开发者两个角度对各类代码库的考察,见 Andrew Busey 的 Secrets of the MUD Wizards。 ↩︎