魔兽世界

工程师座谈:重现安其拉战争捐献

工程师座谈:重现安其拉战争捐献

战争一触即发。在这个月的早些时候,《魔兽世界》经典怀旧服迎来了万众瞩目的重大事件——安其拉战争捐献。整个经典怀旧服务器都动员起来,联盟和部落勠力同心,玩家们募集了海量的资源,解锁了安其拉团队副本。当流沙之战在2006年首次(也是唯一一次)打响的时候,每个服务器都有成千上万名玩家踏上了希利苏斯的土地,参与了这个无比重要的事件,也见证了前所未有的混乱。事件的发展远远超出了开发团队的想象,简而言之,我们那是自寻死路。服务器很快就人满为患,许多玩家陷入了“登录就掉线、上线等半天”的怪圈,我们的工程师忙于修复错误,想方设法地帮助玩家重新连线。我们成功让服务器在事件期间稳定了下来,得到了许多宝贵的教训,也发现了改进的机会。十五年过去了,我们在《魔兽世界》经典怀旧服中重现了这个《魔兽世界》历史中的史诗时刻。通过对服务器的不断优化,我们克服了网络延迟,避免了服务器崩溃,希利苏斯可以容纳的人数也比2006年翻了一倍。

在本篇文章中,我们会分享这个焦点事件的还原历程。我们通过自动化玩家测试与压力测试确定了人数节点,并定制了优化解决方案。我们凭借软件解决方案攻克了硬件无法逾越的障碍。我们组织了遍及全球的大型事件,而服务器几乎没有崩溃。这一切都是在为《魔兽世界》经典怀旧服的游戏体验保驾护航。

重现第二次流沙战争

在考虑如何设计这个事件时,我们确立了三个明确的目标:避免连锁奔溃,提高区域中的预期玩家限额,以及确定可以接受的最大延迟时间,并在超出延迟限制后将玩家送出希利苏斯。在详细介绍提升服务器性能的方式之前,我们需要先明确约束条件:《魔兽世界》经典怀旧服代码库的限制、人数管理解决方案的原理,以及对游戏体验的影响。

阿努比萨斯入侵艾泽拉斯

突破边界

如今的《魔兽世界》建立在15年前发布的原始代码库之上。从这款游戏发布开始,我们已经开发出了许多更为现代的方式,能在“争霸艾泽拉斯”中承载大量玩家。其中最主要的技术就是分流。分流技术使《魔兽世界》服务器能比2006年时容纳更多玩家。在“争霸艾泽拉斯”中,我们使用这种技术来管理服务器的玩家负荷。当玩家数量超过特定限额后,我们就会对当前区域(例如祖达萨)进行复制。玩家间的互动会不断地向服务器发送海量数据包,以此准确地判断自己的移动和施法状态,所以会极大地占用CPU性能。分流技术将玩家分散到了同一地区的不同版本中,从而解决了延迟问题。玩家进入一个人数超过限额的新区域时可能产生延迟问题,而分流技术同样避免了此类问题的发生。这听起来很简单,但是问题来了——《魔兽世界》经典怀旧服忠实地还原了1.12版本的游戏数据,甚至包括各种古怪之处。在极少数情况下,你在进入一个新区域时,分流会导致你的目标(如敌方玩家或某个NPC)消失。如果我们继续使用分流技术,许多记忆中的游戏时刻都将无法重现,比如追逐敌方玩家或NPC穿过地图边界。所以我们需要找到一种解决方案,它不能影响到原本的游戏体验,必须能让服务器承载更多玩家,而且不能让玩家在过高的延迟中受尽煎熬。

为了解决这个问题,我们决定使用分层技术。该技术可以复制整个大陆(例如东部王国),使我们得以控制玩家数量,解决延迟问题,并且保留了原版游戏发布时的魅力。玩家可以和以前一样,拖着世界首领穿过整个地区、追逐敌方玩家跨越边界,无须担心自己会被分配到另一个位面去了。然而,分层并非长久之计。因为1.12版本的游戏并没有使用分层或分流技术,而我们曾经向玩家承诺,我们只会在《魔兽世界》经典怀旧服发布时使用分层技术,并且随着玩家逐渐分散到世界各地,我们将逐步停用这一技术。我们还是会在少数情况下使用分层技术,因为活跃玩家的人数太多了(比如北美地区的法琳娜服务器)。但是从游戏发布以来,我们一直在减少服务器中激活的分层数量。经过了15年的口口相传,安其拉战争成为了《魔兽世界》经典怀旧服最受玩家期待的事件。在希利苏斯聚集的玩家数量可能会创下纪录,仅次于游戏刚发布时的新手区,而我们又不能使用分层技术来控制人数。没有分流与分层技术的帮助,我们必须发挥创造力,而且要快。

聚众敲锣

创造难以忘怀的体验

我们开始寻找一种无需分层或分流的人数管理解决方案。我们生成了无头的客户端——自动化玩家——指示它们模仿真实玩家的活动,例如施法、和NPC战斗,以及四处闲逛。通过这种方式,我们大致了解了数千名玩家挤在同一个区域中互动时的游戏性能。在多次模拟后,我们开始邀请志愿者进行压力测试,这样就能收集真实玩家的行为并进行比较。这让我们确定了人数节点,找到了在玩家人数过多时最容易出错的服务器代码。我们仔细检查了服务器的帧周期,以了解它们是否可能导致服务器停止响应(又被称为“死锁”)。

接下来,我们需要分析影响服务器性能的因素,这样就能将一个艰巨的任务拆分为可以理解的目标了。我们面对的是一个多项式问题,仅凭运算速度更快的硬件是无法解决的,因为硬件的性能无法实现指数级提升。相反,我们必须定制优化,精心选择需要传送给玩家的数据和传输频率。为了让这个难题更容易理解,我们假设有20个玩家排成一圈,不停地跳跃着。服务器会通过数据包(可传输数据),将每个玩家的行为发送给另外19个玩家。在这20个人之间,服务器需要处理380个数据包(20个玩家 x 19个接收者 = 380个数据包)。在该区域中进行相同行为的玩家越多,这个问题就会越显著。如果有500个玩家,服务器就要发送249500个数据包。如果有1500个玩家,服务器就要发送2248500个数据包。取决于玩家的行为,每秒还可能发送多个数据包——别忘了,我们刚才的例子中,所有人的行为是一样的。服务器收到了更多的数据包,也需要为单个玩家花费更长时间,随后才能去处理其他玩家的行为。随着这个问题不断加剧,服务器也逐渐濒临死锁。在《魔兽世界》经典怀旧服中,每个服务器中的玩家数量远远超过2006年,甲虫之门附近预计接待的玩家数量也远超往日。

优化服务器性能

我们的服务器在发生死锁时会自动崩溃并重启,所以我们必须尽力缩短处理时间。在进行了一番测试后,我们发现“移动”对服务器产生的压力最大。我们首先停用了方向信息更新(显示出玩家角色面朝的方向),并且只在玩家开始、停止或使用键盘移动时更新信息。因为当玩家人数过多时,延迟已经相当严重了,如果继续使用CPU时间来发送微乎其微的方向信息更新,情况只会雪上加霜,所以还不如停止发送。我们决定降低发送移动信息更新的频率,以便在区域中容纳更多玩家。别忘记,我们的目标是找到服务器崩溃前的节点,同时在希利苏斯容纳尽可能多的玩家。毕竟,就算有时候移动信息无法更新,也总比角色完全无法登录好。我们还限制了优先级较低的数据传输。如果你的行为“不那么重要”,其发送频率就会低于“更重要”的行为。我们发现有许多讯息会被同时发送,而其重要性并没有得到区分。因此我们优化了代码,以较低的频次批量发送“不那么重要”的信息。

另一个严重拖累性能的因素是增减益效果。在整个世界中,尤其是和敌人交战的时候,增减益效果会同时作用于所有单位。这看起来没什么大不了的,但如果一大堆玩家聚集在一起,这类信息就必须发送给每一个玩家。我们采用了与低优先级数据类似的限流方法,将增减益效果批量发送,以避免连续向玩家发送多个数据包。

管理玩家人数

除了优化服务器、让每个区域能容纳更多玩家之外,我们也意识到,希利苏斯不可能容纳整个服务器的玩家(比《魔兽世界》1.12服务器的玩家数量翻倍还不止)。我们做出了一个艰难的决定:控制能够进入该地区的玩家和人数,限制玩家前往该地区。我们决定只让60级的玩家进入希利苏斯,并且人满为患时拒绝更多玩家进入。设立这个限制是一个明智的决定,因为希利苏斯是游戏的终局内容,低级角色可以在其他区域中参加战争,例如20级至30级的玩家可以在贫瘠之地挑战游荡的阿努比萨斯。第二个关键点是,我们已经知道了服务器崩溃前可以在该地区承载的最大人数,那么将人数控制在多少才能获得最好的“性能与玩家比”呢。我们在测试中发现,如果玩家们叠在一起的话,这个数字大约是1500人。但由于战争事件遍布整个地区,所以当玩家们分散开之后,基本不会产生什么性能问题。

这个事件应该涉及所有区域,所以它必须能够在多个分层中同时开始。这意味着流沙节杖的持有者在某个分层中敲响甲虫之锣后,该服务器的所有分层都应该开启这个事件。因为事件的触发取决于玩家互动,我们需要确保节杖持有者能出现多个分层中,让这个服务器的所有玩家都能看见这位英雄。这就产生了一个有趣的问题。服务器现在需要传输这些平时无需传输的信息。我们需要在服务器中汇集并发送信息,确保这个数据能够在多个分层、甚至数千个分层中体现出来。这无疑会导致许多复杂问题。

在开放荆棘谷钓鱼大赛的时候,我们开发了这种技术,后来的奥妮克希亚、奈法利安、祖尔格拉布和雷德的世界增益效果也都运用了这种技术。当这个技术符合了我们的预期之后,我们就准备在安其拉战争事件中测试这些技术了。

希利苏斯的部落玩家

尝试解决方案

我们已经攻克了主要技术难关,并通过多种方式优化了服务器性能,这时就该对我们的努力进行测试了。我们制作了一个简短版的“10小时战争”,将它缩短到了一个小时。

在第一次压力测试中,我们让几乎所有玩家一同进入希利苏斯,看看会发生什么情况。在某个时间点,我们容纳的玩家数量达到了整个1.12服务器的150%。测试服务器在此时崩溃了。我们当时设定的玩家人数上限很高,结果实际进入该地区的玩家数量却超过了上限。我们调查了这个问题,发现负责将玩家送入及送出该地区的代码其实是一个队列,它无法同时处理大量玩家。所以有些玩家无法被传送走,有些玩家则会在飞行途中卡上半天。我们恢复了服务器,继续进行压力测试,并且开始逐步调整。我们慢慢降低人数上限,直到这个地区依然存在延迟,但是可以比较正常地进行游戏,其容纳的玩家数量也不同往日。测试事件本来应该持续一个半小时,但是因为服务器崩溃,最终用了四个小时才结束。

第二次压力测试在一周之后举行,我们也得以看到优化的成果。在载入压力测试后,我们立刻感受到了优化的效果,玩家们飞进希利苏斯的时候不会再被卡住了!我们也收集到了足够的数据,确定了希利苏斯能够正常容纳的玩家数量。这两次测试结束后,我们都获得了可靠的数据,能够在网络延迟和服务器稳定性之间把握精妙的平衡。测试使我们看到了优化的成效,我们在测试中确定了区域人数限制并进行了优化,所以两次测试都可以算是圆满收场。

推及全艾泽拉斯

最初,我们仅计划在流沙战争期间的希利苏斯启用这些优化。后来我们认为,这些优化可以安全地推广到整个世界,于是1.13.5的整个世界都采用了这些优化。战争捐献开始后,玩家开始捐献物资,同时也在大规模地收割异种蝎。我们发现希利苏斯、主城和城外区域都出现了蜂拥的玩家。优化使这些游戏体验变得更为流畅,大规模的PvP战斗也得以在艾泽拉斯打响。有些玩家甚至召唤了世界首领桑德兰,来清除某个虫巢的对立阵营玩家。

虽然甲虫之门还没有开启,但是部分服务器已经出现了奇怪的问题,他们的捐献进度停滞不前。有些服务器的捐献进展神速,结果导致系统逻辑出现了错乱,每次捐献物资都会导致五天的倒计时更难开始。因为这种问题发生的可能性极小,我们得以手动修复了这些服务器,随后解决了这个问题,使未来的服务器能够正常完成捐献。

战争捐献结束五天后,甲虫之门即将开启,我们开始监控全球第一个打开甲虫之门的中国服务器。中国第一个敲响甲虫之锣的服务器是奥罗。我们监控着分层人数,发现在每个分层中,大部分玩家都集中在希利苏斯。事件开始后,多个分层瞬间被数千名玩家挤满。这种事情我们还从未见到过。虽然网络延迟明显,但是当甲虫之门在中国的第一组服务器开启时,服务器并没有崩溃。

敲响甲虫之锣!

时间来到8月5日,北美地区的服务器重启结束后不久,多个服务器就敲响了甲虫之锣。我们登录了GM账号,通过观察工具监控着这些服务器,以此监视并应对任何可能发生的问题。每个服务器都顺利地打开了甲虫之门,开启了事件。节杖持有者获得了尊贵的黑色其拉作战坦克坐骑,玩家们和大虫子拼死搏斗,我们也对服务器的稳定性十分满意。我们等待着重启后的第一个服务器结束五天的等待,并在那时发现了一个重大问题:服务器重启之后,这个事件不会持续下去。这意味着如果服务器崩溃或者重启了,这个事件的所有进度都会一并丢失。虽然这个问题从《魔兽世界》经典怀旧服开发以来就一直存在,但是大部分事件都不需要持续到服务器重启之后。虽然我们的团队迅速解决了这个问题,但是我们需要保证服务器接下来不会重启,直到我们能够完成修复,在不打扰玩家的情况下,将战争捐献的当前状态录入数据库。


有些人可能会说,当年的安其拉战争因为服务器崩溃而格外混乱,所以才令人难以忘怀。然而,我们打造出了更稳定的体验,让每个服务器的1500名玩家能同时在希利苏斯分享那种热情。我们想让经典怀旧服的安其拉战争成为更多玩家的回忆,让大家畅快地体验10小时的事件。虽然确实有少数服务器崩溃了,但我们也立刻重连了服务器。这些服务器在数分钟内就完成修复并重新上线了,后续也没有再出现崩溃的问题。

全球已经有超过4000名玩家成为了甲虫之王,许多服务器的战争捐献还在推进,这个数字也将继续增长。自安其拉战争捐献开始,我们在经典怀旧服见证了难以置信的热情与玩家互动。我们很感谢所有在第二次流沙之战中与我们相伴的勇士!

下一篇

    专题新闻