在指挥与征服的引擎盖下窥视

2020-06-08 11:48:14

Command&;Conquer和Command&;Conquior:Red Alert的原始源代码已经由电子艺界发布。你可以在GitHub上这里拿到它。

所有内容都是按照GPLv3授权的,源代码包含所有原始注释。无论使用什么VSC,原始的ChangeLog都不存在。看起来好像所有的东西都是最近才放到Git里的。

我决定先看看这个游戏引擎内部发生了什么。我不会把时间花在检查每一行代码上,但至少了解一下20世纪90年代初C++中的游戏开发是什么样子应该很有趣。

我现在只看Command&;Conquer:Red Alert的源代码,因为它看起来像是原版的一个分支。您可以在REDALERT目录下找到它。

我通过对非空行进行计数,然后减去显然只是注释的行,得到了代码图的行数。

还有一个";RedAlert.vcxproj;文件,因此可以在较新版本的visual studio中构建该文件。我没有证实这一点。

首先,根本没有资产。甚至连测试集都没有。因此,您需要以某种方式合法获取这些,例如,您可以从EA购买游戏。直到您这样做,即使您编译代码,您也不能真正证明它的工作。

代码中到处都是#ifdef Win32,实际上至少有430次。有一个文件夹WIN32LIB,其中包含一些针对Microsoft Windows的平台特定实现。没有实现其他平台。我在其他平台上看到的#ifdef Win32的编译器指令没有任何其他地方。所以我不完全确定重点是什么,但DOS是一个受支持的平台。因此,也许所有的棋盘都是这款游戏的版本,可以在DOS上运行,但不能在Windows上运行。

但是PSX实际上指的是POSIX标准,其首字母缩写并不常见。在LCWCOMP.ASM中,甚至还有来自LCW.CPP的相同压缩函数LCW_COMP的另一个实现。我认为C++实现一定只是在汇编代码不起作用的一些平台上使用过。

所以我猜他们没有发布Playstation端口。我甚至不知道游戏站的港口是由韦斯特伍德做的,还是只是转包出去的。

有趣的是,还实现了LZO&;LZW压缩算法。因此,我猜韦斯特伍德制片厂选择哪种技术是上述所有技术的策略本应足以避开Unisys对LZW的专利主张,但LZW代码可能只是停留在那里。LZW代码的唯一用法是在定义LZWPipe类的LZWPIPE.CPP中使用。该代码唯一用法被注释掉。

在这种情况下,似乎尝试了几种不同的算法,最终确定了LZO。因此,LZW似乎是可用的,但没有使用。

Unisys对LZW的专利权利要求早就到期了,因此对此不再有任何担忧。

这里的反击指的是命令与征服:反击,这是一个扩展包。有215次#ifdef Fixit_CSII以某种方式改变了扩展包的内容。完整的解释来自1998年9月28日的一篇评论。

//从DEFINES.H#定义FIXIT_CSII//添加余波反击II单位//AWW 9/28/98-关于FIXIT_CSII的说明。似乎已经对后果(#34;反恐II&34;)做出了改变:a)修复了//错误,不应该回滚;b)改变了游戏的性质,至少在多人游戏中是这样。这意味着//不能再生成红色警报可执行文件(==CounterStrike可执行文件)。显然,在当时,//这是合理的,因为人们认为RA可执行文件再也不需要打补丁了。//鉴于Denzil的DVD更改和我的WOLAPI集成本质上是一个补丁,我们遇到了一个问题。//我们已决定平整该字段,并确保每个获取或修补到新版Red Alert、CS、AM(和//其DVD等价物)的人都将拥有相同的可执行文件。因此,我们重新假设所有FIXIT_CSII更改都是//永久性的(事实上,之前所有的FIXIT_CSII更改都是-让我想知道为什么旧的非编译代码必须//永远存在),并修复代码,以便假设这是一个事后游戏不再是硬编码的,但可以//在运行时更改。(这是创建后果时应该做的事情。)//<;这也适用于以下三个定义。>;#定义FIXIT_CARLER//添加后果航母#DEFINE FIXIT_PHASETRANSPORT//添加后果伪装APC//ajw-发现aftrmath.ini中专门省略了工程师更改字段,因此这没有任何影响。//工程师更改(以及其他游戏规则更改)都在mplayer.ini中,它是在仅限后果的mplayer游戏之前加载的。#定义FIXIT_ENGINEER//添加工程师rules.ini覆盖。

还有WOLAPI.dll的问题,这似乎意味着韦斯特伍德在线API。这是用来在线相亲的。Command&;Conquer:Red Alert的作者显然非常讨厌这个库,因此在WOLAPIOB.H中编写了一个完整的包装器。您很可能只需要找到原始的wolsetup.exe文件并从中提取DLL即可。但它也可能不是很有用。

这不是一个完整的源代码发布,但应该足以让人们了解内部的游戏机制。EA表示,发布它们的原因是为了帮助Modelers,所以这可能就是为了实现这一点。有了大量的工作,就可以开发出一款独立的游戏。

我决定先看看这个游戏是如何开始的。启动代码通常会让您很好地了解游戏引擎如何与操作系统通信。这大致按照它在代码中的实际顺序来表示,但并不完全是这样。任何不有趣的东西都会被跳过。

Main函数在STARTUP.CPP中定义。游戏似乎最近被重新构建为DLL,大概是针对即将发布的重新掌握的版本。因此,它做的第一件事是分配RunningAsDLL=true;

main函数检查所有地方的#ifdef MPEGMOVIE以及#ifdef MCIMPEG。在DEFINES.H中,这被完全注释掉。

//定义DVD以启用RADVD添加/更改-Denzil#ifdef DVD//#定义INTERNET_OFF//#定义MPEGMOVIE//PG//#定义MCIMPEG#endif。

发生的第一件事是它检查正在运行的游戏的另一个副本,并拒绝启动。这似乎已使用#if(0)禁用。有许多以这种方式禁用的代码段,每段代码都有一个//pg。Git中的一个提交人是PG-SteveT,所以我猜把这个项目放到Git中的同一个人负责将它移植为DLL,而不是独立的可执行文件。在main函数中还有更多类似的部分,我指的是从这里开始禁用的部分。

下一步是检查空闲内存。第一次检查只分配13兆字节的内存,如果有效,就释放它。还有另一个被禁用的检查,它尝试在循环中分配13兆字节的内存。循环的每一次迭代都会将请求的分配减少1千字节,直到它通过为止。我认为可以肯定地认为,运行Command&;Conquer的现代计算机将拥有所需的13MB内存,因此禁用此功能是明智的。

下一步检查命令行是否包含f:\\Projects\\c&;c0或F:\\Projects\C&;c0,如果是这样,则拒绝启动。如果是,则不允许从网络播放。";弹出,游戏退出。快速检查一些头文件后,我发现了以下内容

由于";VCS";在这条路上似乎使用了早期版本的控制系统,在Microsoft Windows中充当挂载驱动器。上次我使用IBM rational ClearCase时,它仍然是这样工作的。这样做可能是为了防止人们从网络共享运行游戏,并不断覆盖彼此的保存文件。

游戏实现了自己的命令行解析逻辑,在main函数中以内联方式实现。

//从STARTUP.CPP/*获取指向命令行参数的指针,就像我们在DOS中一样*我们得到的命令行是cr/Zero?终止。*/COMMAND_SCAN=0;DO{/*扫描命令行上的非空格字符*/DO{COMMAND_CHAR=*(COMMAND_LINE+COMMAND_SCAN++);}WHILE(COMMAND_CHAR==';';);IF(COMMAND_CHAR!=0&;&;COMMAND_CHAR!=13){argv[argc++]=command_line+command_scan-1;/*扫描命令行上的空格字符*/bool in_quotes=false;do{command_char=*(command_line+command_scan++);if(command_char==';";';){in_quotes=!in_quotes;}}WHILE((IN_QUOTES||command_char!=';';)&;&;command_char!=0&;&;command_char!=13);*(command_line+command_scan-1)=0;}}While(command_char!=0&;&;command_char!=13&;&;argc<;20);

我并没有真的费心去证明这是有效的,但是看起来它会扫描命令行中带引号的内容,并且如果使用引号将它们作为组来对待。我猜Windows命令行无法处理这个问题,所以它是在游戏中直接处理的。事实上,我甚至不知道cmd.exe是否实现了这个引用逻辑。

Main然后保存启动游戏的原始工作目录和驱动器,然后切换到包含可执行文件的目录。有趣的是,这似乎默认为A:在Windows上通常是软盘驱动器。此代码路径现在已完全禁用。

接下来是第一个#ifdef WOLAPI_INTEGRATION。这没有定义,并且Westwood Online API的源代码不可用。如果定义了它,游戏将尝试找到名为wolsetup.exe的文件并运行它,然后检查是否有Windows注册表项指示安装已完成。这里显示了一个相当有趣的评论。

//来自STARTUP.CPP//我在获取要删除";conquer.eng&34;的修补程序时遇到问题,该补丁存在于1.08的游戏//目录中,但不能出现在此版本中(AFTERVER MIX文件提供1.08单独的conquer.eng在AFTER之前所做的//字符串覆盖)。//如果找到conquier.eng,请将其删除。IF(FindFirstFile(";conquener.eng";,&;wfd)!=INVALID_HANDLE_VALUE)DeleteFile(";conquener.eng";);

显然,游戏1.08版之后的开发者真的不需要有征服者在场。所以每次游戏开始时他们都会试着把它删除。我不知道为什么这会和韦斯特伍德的在线代码打包在一起。

现在我们继续检查未定义的#if(Ten)。如果定义了它,游戏将设置为支持Total Entertainment Network。

在该检查之后,将立即检查#if(MPATH),该检查也未定义。如果是这样的话,这款游戏就会支持MPlayer。

在20世纪90年代后半期,这两家公司都是颇受欢迎的在线游戏平台。我的猜测是有一个MPlayer&;ten支持的定制版本。今天两者都不存在。我稍后将在本文中详细讨论这些问题。

现在,游戏启动Windows计时器,休眠1000毫秒,并确保系统时钟前进。如果这失败了,它就会退出游戏。我猜一些平台上的睡眠实现有足够的缺陷,足以保证在启动期间进行这样的检查。此代码现在已被禁用。

//from STARTUP.CPP#if(0)//pg int time_test=WindowsTimer->;get_system_tick_count();睡眠(1000);if(WindowsTimer->;get_system_tick_count()==time_test){MessageBox(0,TEXT_ERROR_TIMER,TEXT_SHORT_TITLE,MB_OK|MB_ICONSTOP);RETURN(EXIT_FAILURE);}#enageBox。

然后,游戏检查是否有空闲的磁盘空间,如果没有足够的空间,则退出。此代码现在已被禁用。

现在它正在加载ccsetup程序生成的配置,该配置与原始游戏一起出现。这并没有做什么太有趣的事情,但我确实注意到了这一点。

:cpp//来自启动。CPP如果(!DEBUG_QUIET){Audio_Init(NewConfig.。数字卡,NewConfig。端口、新配置。IRQ、NewConfig.。DMA,Playback_Rate_Normal,//(NewConfig.。速度)?Playback_Rate_Slow:Playback_Rate_Normal,NewConfig。BitsPerSample,//4,(get_cpu()<;5)?3:5,//(NewConfig.。速度)?3:5,新配置。反向);SoundOn=TRUE;}ELSE{Audio_Init(0,-1,-1,-1,Playback_Rate_Normal,8,5,False);}

添加了DebugQuiet&34;配置,该配置似乎选择了不执行任何操作的音频输出。我想,如果你在测试游戏时不断地受到噪音的影响,那就太烦人了。

此时,游戏已准备好配置显示分辨率。这段代码非常有趣。第一步是检查屏幕高度是否为400,这意味着已经选择了640x400的模式。如果无法设置,则会回退到640x480。任何其他屏幕分辨率都是直接使用的。640x400的模式有什么特别之处?

要实际渲染游戏,需要访问图形内存。320的屏幕宽度会导致运行特殊的代码路径来初始化显示缓冲区。我的猜测是,在320x240(仍然是VGA)视频模式下运行的任何人都运行在如此旧的硬件上,以至于他们不会费心尝试使用硬件缓冲区。任何其他屏幕高度都会导致尝试为可见内存页和隐藏内存页分配硬件缓冲区。我怀疑图形使用了双缓冲,因此使用了两个缓冲区。对于可见内存页,硬件内存必须可用。围绕这个分配有一些有趣的逻辑。

//来自STARTUP.CPP VisiblePage。init(ScreenWidth,ScreenHeight,NULL,0,(GBC_Enum)(GBC_Visible|GBC_VIDEOMEM));/*检查是否真的有视频内存页。失败是致命的。*/Memset(&;Surface_Capability,0,sizeof(Surface_Capability));VisiblePage。Get_DD_Surface()->;GetCaps(&;Surface_Capability);IF(Surface_Capability。dwCaps&;DDSCAPS_SYSTEMMEMORY){/*Aaaarrgghh!*/WWDebugString(TEXT_DDRAW_ERROR);WWDebugString(";\n";);MessageBox(MainWindow,TEXT_DDRAW_ERROR,TEXT_SHORT_TITLE,MB_ICONEXCLAMATION|MB_OK);IF(WindowsTimer)DELETE WindowsTime。

我只能想象,这条注释是在对某些机器上的性能问题进行了长时间的调试之后添加的。在某个时候,有人意识到,即使请求硬件视频页,DirectDraw API(DirectX的一部分)也可以返回系统内存页。

隐藏的页面稍微宽松一些,但有逻辑,甚至有更好的注释。

//从STARTUP.CPP/*如果我们有足够的剩余空间,则将隐藏页放入视频内存中,除非.。*如果没有阻塞器,则使用系统内存隐藏页会获得更好的性能*如果用户通过ccsetup程序指定了系统内存页,则使用系统内存页。*/UNSIGNED VIDEO_MEMORY=Get_Free_Video_Memory();UNSIGNED VIDEO_CAPABILITIONS=Get_Video_Hardware_Capability();IF(VIDEO_MEMORY<;(UNSIGNED INT)(ScreenWidth*ScreenHeight)||(!(VIDEO_CAPABLES&;VIDEO_BLITER))||(VIDEO_CAPABILITS&;VIDEO_NO_HAR。VideoBackBufferAllowed){HiddenPage。init(ScreenWidth,ScreenHeight,NULL,0,(GBC_Enum)0);}Else{//HiddenPage.Init(ScreenWidth,ScreenHeight,NULL,0,(GBC_Enum)0);HiddenPage。init(ScreenWidth,ScreenHeight,NULL,0,(GBC_Enum)GBC_VIDEOMEM);/*确保我们真的得到了视频内存HID页。如果我们不这样做,事情**就会运行得很慢。*/Memset(&;Surface_Capability,0,sizeof(Surface_Capability));HiddenPage。Get_DD_Surface()->;GetCaps(&;Surface_Capability);IF(Surface_Capability。dwCaps&;DDSCAPS_SYSTEMMEMORY){/*哦,亲爱的,大块头。这一定是一台IBM Aptiva或类似的破旧设备。**我们必须将隐藏页重做为系统内存。*/所有曲面。Remove_DD_Surface(HiddenPage.。Get_DD_Surface());//从所有曲面列表HiddenPage中删除旧曲面。Get_DD_Surface()->;Release();HiddenPage。init(ScreenWidth,ScreenHeight,NULL,0,(GBC_Enum)0);}否则{VisiblePage。ATTACH_DD_Surface(&;HiddenPage);}}。

同样,仔细检查也适用于此页面,但可以退回到系统内存。注释特别指出IBM Aptiva是这些问题的根源。我怀疑IBM Aptiva在开发期间一定是许多笑话的笑柄,因为DEFINES.H甚至有一条#Define FIXIT_APTIVA_MODEM的线路,它实际上使用特殊逻辑来处理IBM Aptiva的调制解调器。

此时,游戏现在将ScreenHeight配置为精确到3072。将尺寸为3072 x 3072的图形缓冲区附加到可见&;隐藏页面。据我所知,获取硬件内存的几乎所有上述代码实际上都是通过使用#if语句禁用的。它是如此的混乱,以至于很难分辨什么是启用的,什么是没有启用的。但是所有关于Remaster的新闻稿都提到了对它的支持。因此,也许这个Remaster的真正分辨率实际上是3072x3072。

就在2019年,还做了一些修改,注明了日期和时间。首字母为JAS的人添加了一个检查,以查看它们是否从编辑器(大概是关卡编辑器)运行,如果是这样,则避免接管Mosue。REMASTER是在2018年通知中宣布的,因此对于需要使用新资产文件实施和测试4K支持的各种更改来说,这一时间表是有意义的。

有各种各样现在被禁用的逻辑,关于强制在第一遍放映导言电影。它几乎是两极的,无法决定用户是否真的需要看这部电影。在某种程度上,这可能是在重新掌握开发期间被注释掉的。我想开发人员和测试人员会非常厌倦快速地观看介绍电影,所以我怀疑它在最初的开发过程中也会被删除。

游戏现在可以玩了!然后它运行Main_Game(来自CONQUER.CPP),这是实际的游戏循环。此文件的注释中的开始日期是1991年4月3日&34;,所以最初的Command&;Conquer在1995年发布之前有4年的开发时间!

游戏完成后,main_Game函数可以返回。有一堆清理代码,但在我们到达那里之前,我们要检查一下。

因此,DLL可能会使图形处于一种奇怪的状态,并且可能会到处泄漏内存。

我决定根据文件名,花一些时间看看我觉得有趣的其他东西。

这似乎表明开发是在Windows NT上完成的,但测试可能是在Windows 95上完成的,以确保它能为目标受众工作。这将是有意义的,因为NT最早将于1993年7月提供给游戏开发人员。我怀疑与Windows NT可以提供的功能相比,Windows 95堆栈跟踪毫无帮助。

还有WATCOM.H定义了大量的";#杂注&34;,这些显然都是特定于当时流行的Watcom编译器的。

这有点有趣,因为Watcom编译器的第一个发行版是1993年,支持C++。还有其他的。

..