# 代码库

LLMS 索引： [llms.txt](/dvw/llms.txt)

---

与题材这个概念相关的是代码库。要说明两者如何关联，需要简短介绍一下虚拟世界服务器架构的几条原理。

代码库之所以出现，是因为创建一个虚拟世界所需的许多工作，在创建其他虚拟世界时可以复用。究竟能复用多少，取决于具体的代码库。代码库主要与虚拟世界的第三个时代相关联，不过这种做法早在 MUD1 就已经采用，而且至今仍有人在尝试为图形化虚拟世界造出同类方案（Asheron's Call 就是本着这个想法设计的 [^57]）。

运行一个虚拟世界的软件里，最基础的部分是驱动程序（driver）。它包含任何成熟的解释器都会有的那些常规例程，负责内存管理、解析、数据结构之类的事务。与之配套的还有更像操作系统的东西，比如输入／输出排队、超时、数据包处理等等。结果是，驱动程序可以向程序的更高层次提供两个基础概念：用来构建虚拟世界的实体的存在（例如物件），以及输入／输出与其中某些实体（例如玩家）的关联。

在这一层之上，是（出于历史原因）所谓的 mudlib（MUD 函数库）[^58]。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 归纳了若干代码库家族（为了对照，也包括一些个别的虚拟世界）在这些方面的差异。表中把每个组成部分的层次标为代码（由计算机硬件执行）、脚本（由代码执行 [^59]）或数据（不可执行）。

| 代码库 | 驱动程序 | mudlib | 世界模型 | 运行时 |
|---|---|---|---|---|
| DikuMUD | 代码 | 代码 | 代码 | 数据 |
| MUD1 | 代码 | 代码 | 脚本 | 数据 |
| MUD2 | 代码 | 脚本 | 脚本 | 数据 |
| Ultima Online | 代码 | 脚本 | 脚本 | 数据 |
| LPMUD | 代码 | 代码 | 脚本 | 脚本 |
| MUCK、MUSH 与 MOO | 代码 | 脚本 | 脚本 | 脚本 |
{#tbl-1.1 num="1.1" caption="代码库差异"}

这些又跟题材有什么关系？

嗯，代码库的实现方式会影响它们自己实现的虚拟世界。硬编码的功能灵活性较差，但更强大；软编码的功能写起来更快，跑起来更慢。

因此，如果你想要一个动作很多、能带来强烈体验的虚拟世界，就该选偏重代码的方案（比如 DikuMUD）；如果你想要一个即兴发挥和创造最重要的虚拟世界，就该选偏重脚本的方案（比如 MOO）；如果你想要一个有细致物理规则、但设定非主流的代码库，就该选用代码实现 mudlib、用脚本语言实现世界模型的方案（比如 LPMUD）。

大致说来，用于（文字）虚拟世界的主要代码库 [^60] 都符合下面这些典型印象：

- DikuMUD：偏重冒险，极其强调与计算机控制的敌人战斗。体验很刺激，但世界本身往往不会有太大变化。它们大多是奇幻设定，科幻远远排在第二位。

- LPMUD：同样偏重冒险，但对战斗的强调较少。它们常常会随时间不断扩展。它们也以奇幻为主，但范围更宽；科幻、恐怖和神话世界也为数不少。

- MUCK：偏重社交，极其看重角色扮演。它们通常以某部具体的奇幻、科幻或恐怖作品为蓝本。不依具体作品的那些，往往以原创的拟人化动物（furries）为题材。

- MUSH：偏重社交，大多着重角色扮演，但偶尔根本算不上游戏。MUSH 往往采用以书、漫画或电影为蓝本的科幻设定，奇幻则差着一截。

- MOO：最不偏向游戏的代码库，用它做出来的非游戏世界，比所有其他代码库加在一起还多。它偶尔产出的游戏通常是原创的（而非衍生的）奇幻，而且是为角色扮演、而不是为冒险设计的。

这些典型印象又被各个代码库的历史传承所强化。虽然（比方说）完全可以用 LPC 编出一个一模一样的 MOO 复刻品，但想写一个“类 MOO”虚拟世界的人，多半会干脆去用 MOO 代码库。再者，刚开一个 LPMUD 的人，多半会拿别人的 mudlib 当起点。于是，与某个代码库相关联的主题或题材含义，就会随着这个代码库的演进而延续下去，也就是说，新版本出现时，通常都会针对自己偏爱的题材加以打磨 [^61]。

代码库是给免费的文字虚拟世界（这类世界有几千个）分类的常用方式；但在大多数其他场合，它没多大用。比如几乎所有的图形化虚拟世界都有自己的专有代码库。因此，人们也常用别的办法来有效区分虚拟世界的不同性质。


[^57]: 正在开发中的主要开源 3D 图形引擎有：Crystal Space（http://crystal.sourceforge.net）；NeL（http://www.nevrax.org）；Quakeforge（http://sourceforge.net/projects/quake）；Worldforge（http://www.worldforge.org）。

[^58]: 即 “mud library”。MUD1 也有 mudlib，但它是把 BCPL 的输入／输出库改编而来，因此层次比今天的 mudlib 低。这个术语的现代用法是 LPMUD 独立提出的。

[^59]: 有一种观点认为，像 LPC 这种功能强大的脚本语言可以用于虚拟世界之外的应用（比如编写 WWW 服务器），应当同时算作代码和脚本语言。MUDDLE 甚至可以直接编译运行，而不必解释执行。

[^60]: 关于衍生代码库的更多细节和说明，见 rec.games.mud FAQ 第 2 部分和第 4 部分：http://www.mudconnect.com/mudfaq/mudfaq-p2.html#q6、http://www.mudconnect.com/mudfaq/mudfaq-p4.html。

[^61]: 从玩家和开发者两个角度对各类代码库的考察，见 Andrew Busey 的 Secrets of the MUD Wizards。
