第 9 章

第9章 被入侵的城堡(约 1994 年)

杰伊·威尔伯坐在办公室里,屏幕已经暗了,但他没有起身。窗外是德克萨斯州梅斯基特市的停车场,路灯把空无一人的车位照成橘黄色的棋盘。最后一封邮件在三分钟前发出去了——回复一个德国分销商关于《Doom II》货架摆放位置的问题,措辞礼貌、精确、不留任何可以继续追问的余地。他关掉邮件客户端,看着操作系统那个蓝灰色的桌面背景,让手指从键盘上移开,放在桌沿上。安静。整栋办公楼只有空调系统的低频嗡鸣,和远处某台没关的电脑风扇转动的声音。威尔伯已经连续工作了十四个小时,处理了四十七封邮件、三通越洋电话、一份需要逐条修改的分销合同,以及两起关于《Doom》在网络上的非法分发投诉。他的防火墙正在运转。卡马克今天没有找过他,罗梅罗今天没有找过他,这意味着他的工作完成了——他挡掉了所有可能打断他们创作的东西,让那两个人在各自的屏幕前继续做他们擅长的事。但他知道自己建立的这套系统有一个漏洞。

不是技术漏洞——FTP服务器的上传权限、媒体的采访过滤、分销商的分级响应制度,这些东西都是他一手设计的,七年商务运营的直觉让他能把每一条可能的干扰路径都计算在内。真正的漏洞是架构性的:他的防火墙建立在两个约翰对外部世界的需求相对一致的假设之上。如果有一天,他们对“外部”的定义产生了分歧,他就没有工具可以处理了。因为防火墙只能挡掉外面的人,挡不住里面的人看向不同的窗外。威尔伯在黑暗中坐了一会儿。然后他站起来,关掉办公室的灯,锁门,走进停车场。他开车回家的时候,脑子里有一个清晰的念头:今天又平安度过了。明天继续。但这个念头边缘有一圈他刻意不去触碰的东西——他听见了,不是今天,而是几周前,卡马克转发那封邮件时整个公司发出的声音。那粒石子已经落水,涟漪还在扩散。他还不知道那涟漪最终会冲刷出什么形状,但他确定它不会停留在他的防火墙之外。卡马克是在十月的一个下午发现那个文件的。

FTP服务器的上传目录里出现了一个.WAD后缀的文件包,名字叫CROSS.WAD。没有签名,没有说明文档,没有邮件事先通知任何人。这个目录本来是用来接收用户的错误报告和感谢信的,威尔伯的助手每隔几天清理一次,把那些用德语、日语、芬兰语写的信分类存档,删掉显然损坏的文件。CROSS.WAD被上传了三次。前两次被当成损坏的档案删掉了,第三次恰好落在清理周期的间隔里,在服务器硬盘的某个扇区静卧了三天,直到有人从内部注意到它。没人记得是谁先看到的。也许是某个程序员在检查服务器空间占用时发现了那个异常的文件大小,也许是一个下载《Doom》共享版的大学生在上传区翻找补丁时偶然点开了它。最终,它落到了卡马克的屏幕前。那台二十一英寸的CRT显示器分辨率是1280x1024,在1994年是一面奢侈的窗户。卡马克坐在窗前,左手无意识地敲着桌面——快三下,停,快两下,停——右手食指双击了那个文件。关卡载入。

墙壁纹理贴错了,灰绿色的大理石被上下颠倒铺在地面上,接缝处露出像素级的错位。三个房间的连接没有任何逻辑可言,像一个人闭着眼睛画出来的迷宫。怪物被塞在无法移动的角落里互相卡住,天花板高度在某个拐角处突然多了两个单位,导致一整面墙的纹理被拉伸成模糊的色块。但这个关卡在运行。它杀死了卡马克的角色三次——第一次死于门外塞进的六个火球骷髅,第二次死于一个他后退时踩进去的不可见陷阱,第三次死于他自己设计的火箭发射器的溅射伤害。卡马克按下ESC退出游戏。他花了大约两分钟用上帝视角穿透墙壁检查了这个蹩脚房间的每一处结构,然后打开公司内部邮件,把CROSS.WAD拖进附件栏,在正文里只打了三个词。看看这个。他点击发送。石子入水。反应几乎立刻到来。阿德里安·卡马克从走廊尽头的工作站发出声音,不是喊叫,但音量远超平时。他站在卡马克办公室门口,手里握着鼠标,线还连着,拖在门框上蹭出沙沙的声响。

屏幕上那个倒置的大理石纹理——那是阿德里安花了三个通宵手绘的,一种暗示地狱入侵科技设施的视觉语言,每一处纹理的走向都经过选择。现在它被一个不知道名字的人倒过来铺在地板上当瓷砖用。阿德里安的声音不大,但每个词都像在钉钉子:这些纹理不是为了让某个孩子把它们翻过来用的。卡马克转过头看着他,眼镜反射着屏幕的光,说这不是纹理的问题——问题是我们没有预料到有人会倒置它。罗梅罗也在看那个关卡,但他的屏幕在办公室的另一头,旁边堆着《Doom II》的关卡设计草图。他点开CROSS.WAD的时候正在画第二十一关的地图,一个他命名为“地狱之下”的六层垂直竞技场。他运行了两遍那个玩家自制的关卡,在七分钟的游戏时间里杀死了所有怪物,踩过那些错位的纹理,站在那个没有出口的房间里陷入沉默。那个玩家没有给关卡设计出口。罗梅罗旋转视角,用左手控制键盘、右手疯狂移动鼠标的惯性姿势,仔细观察这间粗制滥造的房间。

三处纹理错位,一处光照值设置错误,怪物放置毫无节奏可言。这是一份粗糙的模仿,技术上低于他任何一天的工作产出。但它也是建造。一个不认识罗梅罗的人,在不知道id设计文档长什么样、不知道地图编辑器需要商业渠道购买的情况下,用某种方法解开了WAD文件的打包方式,往里面塞进了三个自己的房间,然后把它上传到FTP服务器,让整个互联网下载。这个陌生人在做罗梅罗两年来一直在做的事:构建空间让另一个人经历。粗糙一百倍,但动作的本质是相同的。罗梅罗的右手本能地移动到删除键上,但没有按下去。他把文件存进自己的目录,建了一个子文件夹,然后把CROSS.WAD拖进去。然后他打开BBS。id Software的FTP服务器建立于1991年,最初只是用来分发共享版《指挥官基恩》的传输通道。1993年《Doom》共享版发布后,流量暴增数百倍。

威尔伯在后台统计里看到过那些数字——每天超过三万次下载请求,来自.edu、.com、.de、.uk、.jp的域名,还有一些地址需要翻AT&T的国际代码本才能辨认出国家。他架设了镜像服务器,协调了威斯康星大学的带宽,处理了来自德国和日本的合法拷贝请求,把一切外部噪音挡在卡马克和罗梅罗的听力范围之外。但上传权限这个入口他没有封死——那本是用来接收用户反馈的常规功能。罗梅罗在BBS上找到了那个上传CROSS.WAD的人。一个网名叫DeadMeat的用户,自称大一新生,正在学编程。1994年10月7日,DeadMeat在《Doom》讨论版发了一张帖子,问有没有办法让自制的关卡在游戏里运行。一个叫MegaDeth的版友回复说拆开WAD就行,用十六进制编辑器。DeadMeat问什么是十六进制编辑器。MegaDeth贴了一段命令行。DeadMeat花了四个晚上弄明白那段命令的意思,然后上传了CROSS.WAD。罗梅罗把这张帖子从头读到尾。

他看到了整个过程——每一步都稚嫩,每一步都有错误,但每一步都在往他不曾规划的方向前进。他回复了一句话:我是约翰·罗梅罗,你做的那个关卡有一些问题,但你能做出来这本身就很难得。他没有批评那个倒置的大理石纹理,没有指出房间里没有出口,没有以关卡设计之王的身份降下技术审判。他只说这很难得。二十分钟后,BBS服务器崩溃了。不是因为罗梅罗的回复本身——而是因为同一时间涌进来的一百多个人。有人问DeadMeat是怎么做到的,有人贴出了自己拆解WAD的十六进制代码,有人说他试过类似的事情但失败了,有人声称玩家自制关卡会毁了游戏因为所有人都会做出垃圾覆盖掉真正的作品。二十四小时内回复数突破两千,版主不得不开了一个新的子版块叫“Modding”,然后那个子版块在一周内涌入超过一万张贴子。罗梅罗在第二天早上六点打开电脑——他习惯在这个时间工作,办公室空无一人,咖啡机刚发出第一声运转的嗡鸣。他登录BBS,看到那个万级数字时,第一反应是数零。一万。

刷新。一万零三百。再刷新。一万零五百。数字在涨,以不可逆转的速度涨。在达拉斯秋天凌晨的这个时刻,罗梅罗感到两种截然相反的东西同时抓住了他。一种是兴奋。那些人是因为他构建的世界而来。《Doom》是他和卡马克和整个id团队的成果,但那些贴子喊的是他的名字——“罗梅罗的关卡设计是这个时代最厉害的”——那些十六岁的、二十岁的、三十岁的人在凌晨三点不睡觉,拆解他的文件格式,往里面塞自己的东西,然后争论他为什么不给玩家提供自制工具。他是摇滚明星,BBS是他的万人体育馆,每一次刷新都是掌声。另一种是不安。那些人不再满足于听他的演奏了。他们想上台拿乐器。他们带着跑调的嗓子和没调准弦的琴,执意要加入这场演出。舞台是有边界的,台下的人应该欢呼,应该用他的名字命名他们养的猫,但不应该爬上来。DeadMeat爬上来了,且身后还跟着一千个人,再后面可能还有一万人。罗梅罗关了BBS。又打开了。又关了。那种眩晕来自方向感的丧失。

他习惯的坐标系是id的办公室,信息的流向是他和卡马克做出决定,从中心向外辐射。他是那个中心的一半。现在外部世界不再满足于接收辐射了。海量的、未过滤的、相互矛盾的、热情到窒息的信号正在反向涌入接收器。这不是技术问题——技术层面上他可以学会任何东西——这是角色问题。他原来是作曲者,现在听众想要和他一起写交响乐,而且听众有几十万人。卡马克没有去BBS上看帖子。他在追溯那条门的铰链。那个周末他坐在办公室,面前三台机器:一台跑着CROSS.WAD的关卡分析,一台开着WAD格式的十六进制结构图,一台在写一个新函数。WAD格式内部结构是分段的——纹理数据、平面地图数据、怪物属性表、音效采样、关卡脚本的触发器表——每一段有自己的起始地址和偏移量表,像一栋建筑的蓝图,每一道承重墙的位置都精确到字节。玩家们破解的不是加密算法,《DOOM》的WAD根本没有加密。

他们破解的是结构的逻辑:通过反复试验弄清楚了哪个字节控制墙壁纹理,哪个字节决定地板高度,哪个字节触发陷阱。卡马克把CROSS.WAD一层一层拆开,手指敲着桌面,节奏不均匀。他看到了那个匿名设计师犯的每一个错误——数据段偏移量写错了导致纹理映射到错误的面,怪物属性表里两个敌人在同一坐标点生成导致互相卡死,地图数据块末端没有正确封口理论上可能导致内存溢出。这是一份糟糕的工程。但它的核心动作是正确的。一个外部人员,在没有文档、没有SDK、没有任何官方支持的情况下,通过逆向工程理解了系统的接口。卡马克的工程哲学里有一个基本命题:如果你设计了一个足够好的系统,它会做出你没想到的事情——不是随机行为,而是由系统自身规则推导出来的新行为,你未曾明确编程但已经完全蕴含在结构之中。WAD格式就是一个意外。

当初他决定把所有游戏数据从可执行文件里分离出去,不是因为预见了玩家自制关卡,而是因为他和罗梅罗每天要迭代数十个版本的地图,每次改几个房间就得重新编译整个程序,太慢了。他把数据打包进独立的档案文件,让引擎运行时读取,这样罗梅罗可以用DoomEd随时修改关卡,保存成新的WAD,不用等卡马克重新编译。那个接口是为内部速度而生的,不是为开放。但它恰好具备了开放的形状。他给团队发那封邮件时还没有完全想清楚这件事的意义。他只是运行了CROSS.WAD,确认了一个技术事实——系统的接口被人从外面找到了——然后做了他在发现有趣问题时总是做的事:把事实扔出去,看它落在谁身上。但石子落水之后,涟漪开始改变整栋建筑的形状。首先是阿德里安。他从走廊快步走进卡马克的办公室,用那种介于恼怒和困惑之间的表情说,不能鼓励这种行为。玩家修改游戏文件会破坏完整性,如果某个用户下载了一个有问题的WAD导致游戏崩溃,他们会以为是id的错。

视觉纯度和责任边界,两者都属于阿德里安的领地。卡马克没有从屏幕上抬起头,听完后只说了两句话。WAD格式本身没有保护机制,他们迟早会破解,现在他们已经做到了。他需要想一想。“我需要想一想”在卡马克的词汇表里不是敷衍,它意味着问题已被标记,进入了后台处理队列,算法启动了但尚未输出结果。阿德里安了解这个规则。他把那份用红色箭头标注了所有纹理错误的截图从白板上取下来,没有扔掉,只是翻了个面,让那些箭头对着墙壁。罗梅罗已经陷入了另一个问题。BBS上有人贴出了完整的WAD格式解析表——一个名叫Galahad的玩家用了三个晚上逆向出来的,每一个十六进制字段都标得清清楚楚,末尾附带感谢id做出这款游戏的句子。有人开始讨论伦理问题:玩家有没有权利修改他们花钱买的游戏?有人反驳说共享版是免费的,没有花钱就没有修改权。第三个人说不管花没花钱你都已经往里面塞自己的房间了还在乎什么权利。帖子跑了二十多页。

罗梅罗看到自己的名字被反复提及,有人要他评判合法性问题,有人只是喊他的名字像在球场外高喊球星的名字只为听到回声。他关了BBS。又打开。又关了。发现自己在想一个不该想的问题:如果每个玩家都能做关卡,那他有什么是不可替代的?现在他之所以是约翰·罗梅罗,部分是因为只有他能用DoomEd,只有他能把那些十六进制数字翻译成空间体验,那种稀缺性构成了他身份的一部分。如果工具完全开放,技术屏障消失,特殊性在哪里?他关掉电脑,没有继续处理《Doom II》的地图。不是累了,而是他不确定刚才那几个小时的关卡设计有多少是自己的判断,有多少是为了对抗那些想象中的BBS评论而做的调整。他无法分辨。这正是问题。物理世界开始回应这场入侵。大学机房最先表现出症状。密歇根大学的一个系统管理员发现,学校计算中心的太阳微系统工作站集群在深夜时段的使用率突然飙升至百分之九十以上。

追踪之后发现所有负载都集中在十几个节点上,每个节点运行着同一个程序的不同实例——DOOM.EXE,通过IPX协议在局域网内交换着每秒十五到二十个、每个只有几十字节的小数据包。这位管理员给id发了一封邮件,委婉地询问是否有人知道为什么研究生们凌晨三点还在机房“做研究”。几乎同时,卡内基梅隆大学、麻省理工学院、伊利诺伊大学厄巴纳-香槟分校的计算中心都出现了类似模式。没有人协调这件事。它是自己发生的:有人把《Doom》拷进了学校主机的共享目录,有人在物理系休息室的PC上运行了它,有人发现只要加一个参数就可以无限复活,然后某个人把路由器的IP地址抄在黑板上了,然后整个楼层的周末都被吃掉了。调制解调器是第二条路径。那些不在大学机房的人带着软盘去朋友家,复制《Doom》,设置串行端口的参数,拨号。电话线不是为这种事设计的——那些铜线承载的是语音信号,频率被优化用来传递人类说话的带宽,但现在它们负载的是位置坐标、射击向量、玩家状态。

两个朋友各自坐在自己的屏幕前,听着调制解调器建立连接时那种金属嘶吼声,然后屏幕上的世界出现了另一个人——从一个五英里外的地下室或隔壁镇的公寓里,通过电话线的嘶嘶声浮现出来,在虚拟走廊里开一枪然后消失。这部分游戏体验不是id设计的。《DOOM》的引擎让位置和射击向量可以被打包成数据,通信协议让两个人可以通过串行端口同步游戏状态,但他们没有设计“和从未谋面的陌生人隔着半个城市互相残杀”这个场景。它是系统衍生出来的行为。十一月初,id办公室的白板上出现了一张手写清单,标题是“已知的MOD站点”。字迹是肖恩·格林的——他在《DOOM》发售后转为全职,负责技术支持。他做了调查,把网络上所有出现《DOOM》自制关卡的FTP站点、BBS版块和私人主页汇总在一起。十四天前这个清单是空的。现在上面写着十七个地址。

阿姆斯特丹大学的FTP存档,德克萨斯大学奥斯汀分校长角牛BBS的《DOOM》子区,匹兹堡一个私人BBS,一个AOL用户组的文件共享区,芬兰赫尔辛基理工大学的一个匿名FTP节点。每个地址旁边标注了可获取的WAD文件数量,从一个到十几个不等。第二天早上,有人在十七个地址下面又加了四个。不是肖恩写的,不是罗梅罗写的,不是公司里的任何人。清洁人员在晚上打扫时看到了白板,以为那是开放笔记,把自己侄子告诉他的一个CompuServe论坛地址写了上去。id的墙壁正在被渗透。不是被一个敌人有计划地围攻,而是被成千上万个陌生人同时从成千上万个方向敲击,每一处敲击都很微弱,但合力正在改变墙壁本身的形状。城堡内的居民反应各不相同。阿德里安加固城墙——他询问威尔伯是否有法律手段禁止未经授权分发修改过的游戏内容,得到的答案是共享软件协议里没有覆盖这种情况,因为起草协议时没人想到这种情况。

凯文·克劳德输出数据——他下载了所有能找到的WAD文件测试引擎在极端情况下的稳定性,发现有一个关卡的怪物数量超过了两百个,帧率暴跌到每秒五帧,他拿这个数据去和卡马克讨论渲染引擎的裁剪算法效率。肖恩·格林绘制地图——他用系统管理员的本能追踪每一处泄漏点,不是为了堵住它们,只是想知道水已经蔓延到了哪里。威尔伯在半夜回来查看那张白板,不是为了干涉,而是为了在脑子里更新防火墙的配置。他知道这件事不在他的处理范围内,但会在他的影响范围内。圣诞节前三周,卡马克照常发布了引擎的进度更新。他在开发周期中保持着一个纪律——每周一次向团队同步工作成果和接下来的计划。这次同步时,他展示了那个测试网格。它现在有纹理了,虽然粗糙,但已经可以看出三维房间的雏形:倾斜的墙壁,高处的走廊,真正悬空在空中的平台,这些是《Doom》永远做不到的事情。

然后他花了几十秒解释了引擎-2的一个目录结构设计:外部文件优先级高于内部存档,如果引擎检测到某个特定名字的子目录,它会跳过主数据文件里的同一资源,转而加载外部版本。这意味着任何人都可以替换游戏里的任何资源——不只是地图,还有纹理、音效、模型、脚本。卡马克不是说“我们将支持玩家模组”。他没有发布任何政策声明。他只是选择了门的铰链应该装在哪个方向,然后把结论写进了代码架构。那个目录的名字现在是空的,但逻辑结构已经刻在骨架上了。罗梅罗是第二个反应过来的。他坐在工作站前,没有说话,但他意识到了这个改变意味着什么:一旦引擎支持外部优先加载,玩家自制内容和官方内容将在同一套规则下运行,拥有同样的性能优化、同样的视觉效果、同样的权重。他的一部分看到这个未来时感到一种兴奋——社区会爆炸性地增长,他会成为那个世界的中心,因为是他设计了那套关卡语法,所有人都在模仿他的作品。但另一部分感到寒冷。

如果工具完全开放,技术屏障彻底消失,那么他用DoomEd建立起来的护城河也会消失。特殊性被稀释。他不知道哪一种感觉更真实。他看着卡马克切回代码编辑器的动作——对他来说,这个决定已经完成了,不需要讨论——然后关掉了自己的屏幕,坐在黑暗中,体验着两种方向感的同时撕裂。威尔伯那天晚上走进办公室时,手里拿着需要罗梅罗签字的GT Interactive分销文件。他看了一眼房间里的状态——卡马克在隔间里写代码,阿德里安在画下一批怪物的草图,罗梅罗坐在椅子上一动不动,屏幕是黑的——没有把文件递过去。他站在罗梅罗身边,听了两秒对方的沉默,把文件夹放在桌子角落,说了一句话:明天再说。然后他走回自己的办公室,关上门,坐在桌前,窗外是达拉斯十二月的稀疏灯光。他闭上眼睛,在心里更新的那份无法量化的资产清单上又划了一道。公司现金储备充足,《Doom II》开发进度正常,分销协议基本谈妥,媒体关系稳定,员工没有离职倾向。

每一项指标都在说id Software处于成立以来最好的状态。唯有那两个合伙人之间的距离不在任何一份报告里。他无法度量它,但可以感知到它——像在很安静的房间里听一根发丝被拉紧的声音。CROSS.WAD。一个没有签名的文件,被一个不知道在哪个州哪个大学宿舍里的人上传到一台服务器,绕过了他的过滤器,被一个对社交不感兴趣的工程师打开,转发给整个公司。那粒石子落水的声音到现在还在扩大。他已经不能假装它是一个外部事件了。它已经住进了这栋建筑物里面。圣诞节前的最后一周,威尔伯在白板上更新了那个数字。三十九个已知站点,覆盖三大洲十三个国家。清洁人员没有再往上添加地址,但那张表自己在生长——每周都会有人发现新的FTP存档和新的BBS子版块,每周都有更多WAD文件被上传到这些节点上。威斯康星大学、赫尔辛基理工大学、某个用德语命名的私人服务器、一个CompuServe用户组、两个他至今无法确定地理位置的匿名节点。没有一个是他搭建的。

没有一个需要他的授权。这个网络自己组装了自己,用的材料是卡马克写的引擎、罗梅罗设计的关卡语法、以及几万个玩家在凌晨三点不睡觉破译文件格式的狂热。id公司仍然是这个网络的中心,但中心不再能控制边界的生长速度。威尔伯有时候会在深夜工作结束后,打开FTP客户端,匿名登录那些站点中的一个,看那些自制关卡的列表。文件名是玩家取的,带着那个年代典型的网名风格:黑暗城堡、死亡迷宫、血腥地狱。每个文件都很小——几十KB到几百KB不等——但总数量正在逼近四位数。他想象过那些坐在屏幕前的脸。不同的年龄、不同的语言、不同程度的编程能力,唯一共同的是他们都在做同一件事:撬开id的城堡大门,往里面放自己的房间。DeadMeat是第一个,但不是最后一个,也不可能是最后一个。卡马克已经从架构层面把门轴的方向改成了向外开。罗梅罗还在BBS上和那些入侵者对话——既渴望回响,又害怕被淹没。阿德里安把翻过去的尸检报告又翻回来了,但没有再贴新的。

凯文和肖恩把这件事纳入了各自的日常工作流程,像应对一种新的天气模式。威尔伯关掉FTP客户端,最后看了一眼屏幕上的数字时钟。再过七天就是圣诞节。外面在下冻雨,冰粒敲打着停车场的沥青地面,发出细密的、持续的声响。他拿起文件夹,看了一眼罗梅罗桌子上的那份文件——还放在原来的位置,没有签名——然后把它放回自己的公文包,关灯,走向停车场。在锁门之前,他回头看了一眼走廊尽头那扇还亮着灯的门。卡马克还在写代码。那台二十英寸显示器上的光从门缝漏出来,细长的一条,像某种正在缓慢生长的裂缝。