第 29 章

回滚的奇迹

在太平洋底,光缆像一条没有尽头的蛇。它躺在海床上,外壳包裹着人类能制造的最纯净的玻璃纤维,每一根细如发丝,却能在零点几秒内将一盘对战的全部指令从大阪传送到洛杉矶。这些光缆织成了互联网的底层网格——不是虚无的云,而是浸在海水里、埋在冻土下、绕过大洲边缘的真实物理设施。它们的存在提醒着任何愿意停下来想一想的人:所谓网络对战,本质上就是两个神经系统在利用这些海底缆线和陆地基站进行一场漫长的接力赛跑。而在这根接力的跑道上,有一个不变的常数。光在真空中每秒走三十万公里。在光纤中,由于介质折射,这个数字下降到约二十万公里。从东京到旧金山,直线距离八千二百公里,光走完这段路程大约需要四十一毫秒——往返则是八十二毫秒。再加上路由器处理数据包的时间、光电转换的损耗、网络拥堵造成的排队延迟,实际延迟在一百毫秒上下波动。一百毫秒。六帧。在《街霸III: 3rd Strike》里,一个普通重拳的出招时间是四到六帧。

确认一个跳入攻击并接入必杀技的窗口通常不超过十五帧。这意味着当东京的玩家按下重拳按钮时,他需要等六帧——整整零点一秒——才能看到旧金山对手的防御反应。而在那六帧里,一个更快的对手可能已经完成了从防御到反击的转换。这不是带宽问题。视频流媒体和文件下载在二十世纪头一个十年末期就已经相当流畅。延迟是另一个量纲上的事。它由光速和路由跳数决定,与数据包的大小关系甚微。格斗游戏的数据包极小——几个字节的输入信息足矣——但这几个字节跑得再精简也跑不过光。这不是技术瓶颈,这是物理学的墙。2009年,这堵墙已经完美地矗立在格斗游戏的世界里快十年了。自从2001年前后家用主机进入网络时代以来,格斗游戏的对战体验就困在同一个困境中:要么你和对手住在同一个地区,延迟控制在三十毫秒以内,游戏还能玩;要么你连上一个异地甚至跨洋的对手,屏幕上的画面便开始变得像结冰的河面——它还在动,但动作之间掺杂着看不见的摩擦力。

大部分格斗游戏采用的延迟基网络代码将每一次输入都卡在确认对方指令到达的那一帧上,迫使整个游戏状态以最慢的那条链接为基准推进。两百毫秒的延迟意味着你的每一次按键都要多等两百毫秒才能看到画面反应。对于一个手指记忆已经刻进肌肉纤维里的街机玩家来说,这两百毫秒等于把他的大脑和手指之间的通道拉长了一百倍。玩家们描述这种体验时使用的语言千奇百怪,但总是指向同一种身体感受:沉重。“像是用冻僵的手指在打。”“隆在泥里走路。”“对手在我的过去里活动,我在他的过去里反应。”在论坛上的某个深夜讨论帖中,一条回复被反复引用,发帖者声称他已经放弃了网络对战,因为每次打完他都感到一种不属于身体的疲劳——一种大脑在不断地预测、补偿、重新校准中耗尽了燃料的酸胀感。这不是游戏本该有的感觉。街机厅里的对战是一种即时的碰撞:神经系统直接通过导线和对方短接,帧与帧之间的时长刚好够指尖感受到微动开关的脆响传递回手腕。

任何在街机上打过一百局以上的人都知道那种节奏——它不等于快,而是一种可以信赖的连续性。你的手指知道什么时候按下,角色就什么时候出拳。网络延迟将这种连续性砍成了无数截,每一截之间的空隙里填充的是不确定的等待。这个问题从一开始就不是秘密。千禧年头一个十年的中期,当宽带网络刚在发达国家的城市家庭普及时,格斗游戏的开发商们就已经试图解决它。卡普空在2006年发售的Xbox 360版《街霸II Hyper Fighting》是第一批提供官方网络对战的格斗游戏之一。它的网络代码是经典的延迟基方案:尽最大可能确保双方画面同步,而代价就是双方的操作都被延迟。当年有玩家进行了实测——在旧金山和洛杉矶之间,约六十毫秒的物理延迟,换算下来大约四帧的额外输入等待。四帧听起来不多,但在那个以确认反击和目押连招为核心玩法的年代,四帧足以让整个游戏的最顶级技巧变成不可能完成的任务。

一些在日本街机厅里能用肯打出十连胜的玩家,在网上打到第三局就放弃了——不是输给对手,是输给了那个看不见的泥沼。随后的几年里,这个问题反复出现,也反复被搁置。开发商们对此的态度既含混又顽固。在技术层面,延迟基方案是“诚实”的做法:它不猜测,不假设,不欺骗玩家的眼睛。每一帧的画面都严格对应双方真实的操作时间线。它在设计哲学上是干净的,在工程实现上是成熟的。网络条件好的时候——两个玩家都在同一座城市,都使用有线连接——它的表现尚可忍受。但格斗游戏的玩家群从来不是平均分布的。美国的竞技场景集中在西海岸和东海岸的几个都市圈,中间隔着整个大陆。日本的玩家和韩国、台湾、香港的玩家想要对战时,隔着一整片东海。欧洲的场景则碎裂在国家和语言之间,法国和西班牙的玩家要连上英国或德国的服务器,每一场对战都伴随着不可预测的延迟波动。诚实变成了折磨。延迟基方案保住了设计上的道德完整,却牺牲了对战体验中最珍贵的部分:那种你和另一个人正在同一个当下交手的感受。

托尼·坎农在2006年的时候还不是EVO的联合创始人。彼时EVO还叫B3,刚搬到加州州立大学洛杉矶分校的学生活动中心没多久,规模不过几百人。坎农是一个频繁出现在北美格斗游戏论坛上的ID,头像是他自己画的一个简笔画角色,签名档里写着“软件工程师,主修《3rd Strike》”。和当时许多核心玩家一样,他在街机厅衰落的年代里把精神寄托转移到了家用机和模拟器上。《街霸III: 3rd Strike》是一款在1999年发售的游戏,到2006年已经超过了七岁。卡普空没有给它做任何网络对战功能。它的生命力完全依靠散布在全球各地的一小群死忠玩家——他们在二手市场上搜刮街机基板,在模拟器上反复练习,在Super Battle Opera(斗剧)的DVD录像带里逐帧研究高手对局。模拟器社区很早就开始尝试给老游戏添加网络对战功能。这些尝试的成果粗糙但充满活力。

最常见的方式是卡诺普斯(Kaillera)——一个在2000年代初被集成进多个模拟器的网络对战客户端。它的做法和延迟基方案类似:两端同步,等待输入。结果也类似:只要延迟超过五十毫秒,游戏就变得近乎无法操作。对于像《3rd Strike》这样以精准确认和严密立回著称的游戏来说,卡诺普斯的体验就是一场灾难。但模拟器社区坚持了下来。那些年里,不同城市的玩家会约定时间连上同一个服务器,尽可能找到距离最近的对手,在勉强能玩的条件下打上几十局。他们的标准已经低到了“只要能偶尔发出一个确认升龙拳就值得欢呼”的程度——因为在延迟基方案的糖浆里,升龙拳的指令从输入到发动之间的时空裂缝足以让任何一个正在跳跃的对手落到地面上。坎农的编程背景让他无法接受这种局面。他不是那种会写长篇抱怨帖的玩家。他开始做的事情是打开模拟器的源代码,仔细研究输入同步的逻辑。他的观察很朴素:延迟基方案的问题不在于等待本身,而在于无差别地等待。

游戏中的大多数帧并不需要帧级精确的同步。一个角色在执行跳重脚时,起跳到落地之间大约四十五帧的动画是锁定的,对手在这段时间内的输入不会改变这个动作的结果。一个蹲防可能持续数百帧——玩家只是按住蹲下和向后,没有任何新信息。在这些时间里等待对手的输入是纯粹的浪费。真正需要精确同步的只是那些改变状态的帧:一个必杀技的启动、一个前冲的取消、一个在防御硬直结束瞬间发动的反击。这个观察的结果指向一个激进的替代方案:不等待。在每一帧,游戏假设对方的输入和前一帧一致,直接推进。当对方的真实输入数据晚几帧到达时,游戏比对真实输入和假设输入。如果它们一致——在绝大多数情况下它们就是一致的——玩家已经感受到了零延迟的即时反馈。如果不一致——对手其实在几帧前按下了升龙拳而不是继续防御——游戏就把这几帧的状态回卷,用真实输入重新计算,再将修正后的画面显示出来。这就是回滚的逻辑。它不消除延迟,它只在延迟的缝隙中尽可能地给玩家提供即时反应的错觉。

而这个错觉在大多数时候是真的——因为预测本身就是正确的。格斗游戏中的人类行为具有高度的连续性和可预测性。一个正在冲刺的玩家大概率会继续冲刺。一个被打浮空的玩家在受创硬直解除前根本不能做出任何动作。一个正在出重拳的玩家在动画结束前无法取消这个动作。在这些时刻,回滚代码的假设和真实输入完全吻合,玩家体验到的就是原生的、无延迟的响应。偶尔出现的修正——那个被社区称为“回滚抽搐”的瞬间画面跳动——只是一个小小的代价,换取其他所有时间里的流畅。2009年,托尼·坎农将这套逻辑封装成了一个可以挂接到模拟器上的中间件,取名GGPO。发布帖出现在Shoryuken.com的模拟器分区,时间是某个北美深夜,帖子的标题平淡得像一条系统维护通知。坎农的自我介绍口吻几乎是谦逊的——他说自己做了个网络对战的插件,可能有人会觉得好用,欢迎大家试试看。他没有用“革命”这个词。他甚至没有解释回滚算法的原理。

他只是放了一个安装指南的链接,一个已知问题的列表,和一句附言:用《3rd Strike》测试过,感觉还可以。第一个回帖出现在六分钟后。第二条出现在十二分钟后。到第二天早上,帖子已经翻了十几页。回复的内容有两种。一种是技术型的提问——如何在各种模拟器版本上挂载、路由器端口怎么设置、不同操作系统下的兼容性。另一种则是更本能的东西。一个从纽约连上某个加州玩家的测试者写道:“我试着发了几个升龙拳,它们真的出来了。我是说,在我按下的那一瞬间。”另一个ID来自欧洲的用户说他打完了十局,只看到画面抽搐了一次——“一次!从马德里连到芝加哥!”在帖子第三十七页的某个回复里,有人用全部大写写了一句当时还没有流行起来的短语:BLACK MAGIC FUCKERY。黑魔法。这群人不理解预测算法和状态机回卷的具体实现,但他们理解自己的身体。他们的手指记得在街机厅里练出的操作节奏。当那个节奏在模拟器的网络对战中第一次被复现出来时,他们认出了它。

它的形状还在。这就是GGPO在2009年的意义——在技术方案上它也许不完全是首创,但在格斗游戏的语境下,它是第一次将“可接受的网络对战体验”这个目标原原本本地交到了玩家手里。它不是某个大厂内部研发项目,没有产品管理者的市场分析报告,没有面向投资者的演示文档。它就是一个玩家为了让另一个玩家能隔着大洋和自己打上一盘《3rd Strike》而写的代码。那种感觉,在2009年之后的很长一段时间里,仍然是格斗游戏网络对战体验的孤例。GGPO的影响在发布头几年仅限于模拟器社区和核心玩家圈。这是一个小而狂热的群体。他们会在论坛上花几十页讨论一个必杀技的帧数优势,会把十几年前的比赛录像逐帧剖析,会用数学公式证明某个角色的立回理论。他们对待GGPO的态度近乎信仰——不是因为它的代码写得完美,而是因为它是唯一有效的东西。在接下来的几年里,每逢有新格斗游戏公布,社区的第一批提问里必然包含同一个问题:“支持回滚吗?”答案几乎总是否定的。

不是开发者不知道GGPO的存在,而是他们选择了另一条路。其中的原因需要仔细拆解。日本格斗游戏产业在本世纪第一个十年的末期面临一个结构性的矛盾。在日本国内市场,街机厅仍然维持着数量庞大的活跃玩家群体。格斗游戏的设计、测试和商业验证全部围绕街机基板展开。家用机版本通常是街机运营周期结束之后的副产品——移植项目工期紧、预算低,网络对战功能只是列表上待勾选的一项,而不是核心支柱。在这样一个体系中,延迟基方案足够“完成功能”——它可以被写到游戏包装盒背面,可以作为“支持在线对战”的卖点。销量报告看不见玩家的延迟投诉。更深一层的是开发生态中的技术保守主义。日本的街机游戏开发团队拥有深厚的硬件级优化传承——在资源极度受限的基板上榨出超规格的动画帧数和打击感,这种能力本身就是街机产业的护城河。但网络编程是一个完全不同的领域。它不属于基板,不属于框体,不属于长期以来积累的任何核心能力。

对于很多街机出身的开发者来说,延迟基方案是那个“熟悉”的选项——它本质上只是把程序暂停一会儿,等待数据到达,逻辑上干净得像在街机框体内多插了一根信号线。回滚代码却要求引擎支持状态保存和恢复,要求每一帧的逻辑都可以被反复重新执行而不产生偏差,要求大量的边缘情况测试——两个人同时出招、网络突然抖动、预测窗口和实际输入产生系统性偏差时会发生什么?这些问题没有标准答案,因为它们从未在街机基板的物理限制内被提出过。还有那些语言无法抵达的地方。一些后来在访谈中流传出来的只言片语暗示了另一种态度。2010年代末,有日本开发者在被问及回滚代码时表示,他们担心玩家看到画面跳动会产生误解,认为游戏“出了bug”。也有观点认为,回滚代码本质上是让两位玩家在各自屏幕上看到不同的画面——哪怕只是临时的、在一帧之后就修正了的差异——而这在街机对战的哲学里是无法接受的。街机的契约是绝对的画面共享:两台显示器、一场对战、一个共同的视觉事实。

延迟基方案至少保留了这层诚实——画面虽然慢,但两人看到的东西一模一样。回滚代码在这层意义上是一个背叛:它用一种微小的、瞬间的欺骗换来了即时反应。对于开发者中那些在街机文化里成长起来的人而言,这种背叛并不是一个显而易见的代价。结果就是长达近十年的分裂。在模拟器社区和独立游戏场景中,回滚代码逐渐被验证、打磨、标准化。2012年,《骷髅少女》以一个独立小团队的身份把回滚网络代码直接写进了商用游戏引擎,证明了它在商业产品中的可行性。但主流格斗游戏开发商仍然按兵不动。2015年卡普空发布《街霸V》时,玩家们发现它搭载的网络代码甚至比上一代《街霸IV》更不稳定。论坛上开始出现大量实测视频——同一个人在同一网络环境下测试《街霸V》和GGPO支持的模拟器版《3rd Strike》,前者的操作延迟比后者高出可测量的数帧。证据堆在桌面上一览无余,但决策房间里的灯光似乎照不到这些角落。这段漫长的抵抗期并不是一段技术空白史。对抗也在别处发生着。

在那些年里,回滚代码从一个工程方案逐渐转化为一个社区口号。大型赛事的现场,选手在采访中被问到“你对官方游戏的网络对战怎么看”时,犹豫片刻然后说“不好说”,台下爆发出笑声和掌声——一种只有经历过无数次幻灯片对战才能心领神会的苦涩幽默。在EVO的比赛间歇,坎农有时会出现在会场边缘——他也已经从一个模拟器插件作者成长为EVO的技术负责人——被认出的玩家们会走上来感谢他。他们感谢的不是GGPO这个软件本身,而是它证明了“事情可以不是那样”——延迟不必被忍受,对抗不必在泥沼中进行。这种证明的价值在一个依靠身体记忆的社群里,比任何技术说明都更持久。2020年初,一个意外因素介入。全球新冠疫情的暴发在几周之内关闭了全世界的线下赛事。EVO取消了拉斯维加斯的年度大会。Combo Breaker取消。CEO取消。所有区域性的周赛、月赛、线下聚会全部中断。

格斗游戏的竞技生态在此之前已经花了近二十年建立了一套与街机厅传统接续的线下仪式——选手飞行参赛、酒店房间野战、舞台上两排对战框体、观众屏住呼吸的静默和爆发。所有这些在一瞬间消失。如果格斗游戏的电竞想要在封闭的公寓房间里活下来,它只剩一个选择:网络对战必须是能用的。不是凑合能用,不是“如果你住在大城市且对手在同一个州就能用”,而是任何人连上任何人,在任何时间,都能打一场像样的比赛。压力在几个月内就转化成了行动。2020年5月,Arc System Works宣布《罪恶装备 Strive》将引入回滚网络代码——此前这款游戏的测试版仍在用延迟基方案,社区的反馈邮件和论坛帖在开发团队的收件箱里堆积如山。八月,SNK确认《拳皇XV》会使用回滚代码。同月,卡普空启动了《街霸V》网络代码的大规模修订——他们雇了Mike Zaimont,那个在《骷髅少女》里从零实现了回滚系统的人。一系列动作密集到不像是多年来以谨慎著称的日本开发商所为。

事实是,在生死存亡的压力面前,那些关于“诚实”和画面一致性的哲学顾虑以惊人的速度溶解了。回滚代码在2020年到2021年间完成了从边缘方案到行业标配的跨越,这个跨越在技术上并没有任何新的突破——2009年的GGPO原理完全够用——变化的是决策者的紧迫感。2021年6月《罪恶装备 Strive》发售后,玩家们涌入服务器。回滚代码在其中运行,大多数人都不会注意它的存在——一项好的基础设施本身就是隐形的。他们按下按钮,角色出拳。他们连上一个几百公里外的对手,没有察觉到差异。那些曾经盯着延迟数字一格一格爬升的老玩家会在某个夜晚突然意识到:自己已经打了三小时,没有一次停下来在内心咒骂网络。而那个偶尔在屏幕角落一闪而过的数字告诉他们,他们的对手在另一个国家。这就是回滚代码真正带来的东西。它不是一项提高游戏体验的功能。它是一项修复。它在物理定律的墙上凿出一个壁龛,让人类的对抗欲望可以在里面继续存在。

在GGPO发布帖的第三页,坎农开始回复技术问题。他的回答简洁到近乎冷淡——端口转发、帧延迟设置、推荐的双向带宽——但每一条回复底下都跟着新的测试报告。一个巴西玩家说他连上了日本西海岸的服务器,延迟显示一百七十毫秒,但游戏“能动”。这个描述本身就说明了一切:在延迟基方案的年代,一百七十毫秒意味着游戏画面每隔十帧才更新一次,角色在屏幕上做的是断续的布朗运动。而“能动”——这个最低限度的评价——在这里是一个赞美。它意味着回滚代码的预测机制正在工作,意味着那些被节省下来的帧数正在被玩家的手指感受到。另一个测试者来自澳大利亚,他报告说在悉尼和墨尔本之间(约九百公里)的对战“几乎和线下一样”。他用的词是“almost local”,这个短语后来在社区里被反复使用,最终凝结成一个半开玩笑的术语——“局域网错觉”。在那些早期的测试帖里,玩家们正在用一种经验主义的方式绘制回滚代码的能力边界:什么样的距离是“可玩的”,什么样的距离是“能动的”,什么样的距离是“别试了”。这张地图不是由理论计算绘制的,而是由几百个深夜、几千局对战、无数次“再来一局”累积出来的。

但GGPO的发布也暴露了一个问题,这个问题在接下来的十年里会反复出现:回滚代码对网络条件的要求和延迟基方案完全不同。延迟基方案害怕的是高延迟——光走得太远,等待就太长。回滚代码害怕的是延迟抖动——光走的时间忽长忽短,预测窗口就难以校准。一个稳定的两百毫秒延迟在回滚代码下可能比一个在三十到八十毫秒之间波动的连接更可玩。这意味着回滚代码对Wi-Fi的容忍度远低于延迟基方案。Wi-Fi的本质就是抖动——无线电波在墙壁、家具和邻居的路由器之间反射折射,数据包到达的时间在一个不可预测的范围内漂移。在延迟基方案下,Wi-Fi的抖动被均匀地摊进了每一帧的等待里,玩家感受到的只是一种模糊的迟钝。但在回滚代码下,抖动意味着预测错误率飙升,意味着画面频繁抽搐,意味着那个被社区称为“回滚地狱”的体验——角色在屏幕上不断跳跃修正,音效重复触发,整个游戏变成一帧一帧的频闪照片。这个问题在2009年就已经被识别出来。GGPO的发布帖里,坎农在已知问题列表中写了一条:“Wi-Fi用户可能会遇到画面不稳定,建议使用有线连接。”这条建议在接下来的十五年里被重复了无数次,但从未被完全遵守。

日本开发者的沉默和偶尔的公开表态构成了这段历史中最耐人寻味的部分。2011年,卡普空的一位网络工程师在日本游戏开发者大会(CEDEC)上做了一个关于格斗游戏网络代码的技术演讲。

那堵墙——光速的极限——仍然矗立在每一个跨大陆的数据包背后。回滚代码没有拆除它,只是在它面前铺了一层镜面,让玩家在绝大多数时间里看不到墙的存在,偶尔才被一次画面抽搐提醒。在那个意识清醒的瞬间,玩家看到角色短暂地闪烁了一下,一个明明刚才还在防御的对手突然变成了出拳姿态,时间被偷走了一帧又还了回来。然后对战继续。两个人重新回到同一个节奏里。2023年6月,《街霸6》发售。在网络对战大厅的虚拟街机厅里,玩家们穿着在单人模式中获得的虚拟服装,走向一台台的框体。大厅的地砖反射着模拟的灯光,远处有海景,有投篮机,有可以坐下休息的长椅。一个真实的玩家会从另一端走进这个空间。你按下一个键发出挑战。对面的虚拟形象点了点头。加载画面一闪而过,两个角色站在了对战舞台上。Ready?Fight!在那个瞬间,当第一次重拳和第一次蹲防同时出现在屏幕上时,光缆仍在大西洋和太平洋的底部承受着海水的压力。路由器在几十个节点上闪烁着绿色的指示灯。

数以百万计的数据包在其中奔涌,每一个都比光速慢一点,但快过任何人类的神经传导。在两侧的屏幕前,两个人——各自坐在自己的房间里,没有围观者,没有投币的声音,只有一盏台灯照在摇杆上——同时进入了同一个对战节奏。那种节奏的开端和结尾都由帧数度量,中间是一条看不见的、用代码编织的线。它很细,偶尔会断,但它连着。