摘要:话说有个叫Minki的极客,某天翻到篇讲《DOOM》引擎的文章,发现个冷知识:游戏里有个记录演示播放的变量,每次启动都会往上累加,从没清过零。这事儿搁一般人眼里,可能觉得“关我啥事”,但Minki偏不,他琢磨着,这变量要是一直加下去,会不会把游戏搞崩?
话说有个叫Minki的极客,某天翻到篇讲《DOOM》引擎的文章,发现个冷知识:游戏里有个记录演示播放的变量,每次启动都会往上累加,从没清过零。这事儿搁一般人眼里,可能觉得“关我啥事”,但Minki偏不,他琢磨着,这变量要是一直加下去,会不会把游戏搞崩?
为了验证,他翻出台2003年的华硕MyPalA620掌机,这老古董跑着WindowsMobile系统,处理器还是IntelXScale,搁现在连看网页都卡。更绝的是,他怕断电,自己动手改了块18650锂电池当UPS,接路由器USB口供电,活脱脱搞成个“永动机”。
然后这掌机就被他扔在角落吃灰,一扔就是两年半,直到某天他路过瞅了眼,屏幕上赫然弹出“应用程序崩溃”的提示。嚯,还真让他盼到了!按游戏每秒35次的计数频率算,理论上1.95年就该溢出,但实际跑了两年半才崩,这误差估计连开发者都没想到。
要说这崩溃的根源,还得扒拉《DOOM》的代码,里面有个叫gametic的计时器变量,以35Hz的频率往上蹦跶,关键它是个32位有符号整数,啥概念?这数最大能到21亿多,再加1就会“爆表”,从正数跳到负数最小值。
当年《DOOM》引擎大佬JohnCarmack肯定知道这事儿,但他估计觉得:谁没事能把游戏开两年?21亿次计数,正常玩家一辈子都遇不上。可他没算到,C语言里整数溢出属于“未定义行为”,虽说在x86架构下大概率会数值回环,但保不齐哪天就出岔子。
就像Minki这实验,变量跳到负数后,游戏逻辑彻底乱套,好比你手表时间突然从12点跳到凌晨3点,闹钟肯定响错点。这事儿其实和早期Unix系统的“2038年问题”一个道理,都是32位时间戳惹的祸。
Minki这实验一爆出来,玩家圈炸了锅,好多人跳出来分享类似经历。有个老哥研究《古惑狼3》,发现里面有个计数器只在角色死亡时清零,要是一直开着,2.26年后也得溢出。更绝的是《最终幻想9》,有把隐藏武器要求游戏时间少于10小时,PAL版甚至卡到10小时以内,正常人根本完不成。但有玩家发现,把游戏挂着不动,等两年多计时器溢出归零,就能轻松拿到武器,合着这游戏还藏着“挂机两年通关”的彩蛋?
这事儿说白了,暴露出不少开发者的惯性思维:总觉得程序不会跑那么久,就忽略了边界条件。现在新游戏好多改用64位整数,或者循环计数,就是吃一堑长一智。但反过来想,这些老bug反而成了游戏的“时间印记”,说不定几十年后,还会有人翻出《DOOM》这茬,当个技术段子讲呢。
来源:念寒尘缘