QUAKE 3源代码审查:虚拟机

来源:互联网 发布:大冠军杯女排数据 编辑:程序博客网 时间:2024/05/21 17:47

QUAKE 3源代码审查:虚拟机(第4部分,共5部分)>>

如果以前的引擎仅将游戏委托给虚拟机,idtech3会严重依赖它们进行基本任务。除其他事项外:

  • Rendition由Client VM触发。
  • 滞后补偿机制完全在客户端VM中。

此外,他们的设计更加详细:它们将Quake1虚拟机的安全性/可移植性与Quake2的本机DLL的高性能相结合。这是通过将字节码编译为x86指令来实现的。

琐事:虚拟机最初应该是一个简单的字节码解释器,但表现令人失望,所以开发团队写了一个运行时x86编译器。根据1999年8月16日的计划, 这是在一天之内完成的。

建筑

在Quake III中,虚拟机被称为QVM:其中三个可随时加载:

  • 客户端:加载了两台虚拟机。消息发送到一个或另一个取决于gamestate:
    • cgame:在战斗阶段接收消息。执行实体剔除,预测和触发renderer.lib
    • q3_ui:在菜单阶段接收消息。使用系统调用绘制菜单。
  • 服务器端:
    • game:总是收到消息:执行游戏,然后点击bot.lib执行AI。

QVM内部

在描述如何使用QVM之前,我们来看看如何生成字节码。像往常一样,我喜欢用一点补充文本绘制:

quake3.exe它的字节码解释器是通过Visual Studio生成的,但是VM字节码需要一个非常不同的路径:

  1. 每个.c文件(翻译单元)通过LCC单独编译。
  2. LCC与特殊参数一起使用,因此不会输出PE(Windows Portable Executable),而是它的基于文本的堆栈机器组装的中间表示。生产的每个文件都拥有textdatabss用符号出口和进口部分。
  3. 来自id Software的一个特殊工具可以q3asm.exe将所有文本汇编文件复制到一个.qvm文件中。它还可以将所有内容从文本转换成二进制文件(为了速度,如果本机转换不能启动)。q3asm.exe还可以识别哪些方法是系统调用,并给出那些负号码。
  4. 加载二进制字节码时,quake3.exe将其转换为x86指令(不是强制性的)。

LCC内部

这是一个具体的例子,从我们想在虚拟机中运行的函数开始:

       extern int variableA;        int variableB;        int variableC = 0;        int fooFunction(char * string){            return variableA + strlen(string);            }        

保存在module.c翻译单元中,lcc.exe用特殊标志调用,以避免生成Windows PE对象,而是输出中间表示。这是LCC .obj输出匹配上面的C函数:

       数据    导出变量C    对齐4    LABELV变量C    字节4 0    导出fooFunction     代码    proc fooFunction 4 4    ADDRFP4 0    INDIRP4    ARGP4    ADDRLP4 0    ADDRGP4 strlen    CALLI4    ASGNI4    ARGP4变量A.    INDIRI4    ADDRLP4 0    INDIRI4    ADDI4    RETI4    LABELV $ 1    endproc fooFunction 4 4     import strlen     bss     export variableB    对齐4    LABELV变量B    跳过4    导入变量A    

几点意见:

  • 字节码以部分(标记为红色)组织:我们可以清楚地看到bss(未初始化的变量),data(已初始化的变量)和code(通常称为text但不管...)
  • 功能是通过定义procendproc夹心(标记为蓝色)。
  • LCC的中间表示是一个堆栈机器:所有操作都是在堆栈上完成的,没有关于CPU寄存器的假设。
  • 在LCC短语结尾,我们有一堆文件导入/导出变量/函数。
  • 每个语句与操作型开始(即:ARGP4ADDRGP4CALLI4...)。每个参数和结果将被传递给堆栈。
  • 导入和导出在这里,所以汇编器可以将“翻译单元”链接在一起。请注意import strlen,由于q3asm.exe和VM Interpreter都不喜欢C标准库,strlen所以被认为是系统调用,必须由虚拟机提供。

为VM模块中的每个.c生成此类文本文件。

q3asm.exe内部

q3asm.exe使用LCC中间表示文本文件并将它们组合在一起.qvm文件:


需要注意的几件事情:

  • q3asm对每个文本文件的导入/导出符号都有意义。
  • 一些方法是通过系统调用文本文件预定义的。您可以看到客户端虚拟机和服务器虚拟机的系统调用。系统调用符号被归为负整数值,因此可以由解释器识别。
  • q3asm将文本更改为二进制,以获得空间和速度,但这是几乎没有优化。
  • 要组装的第一种方法必须是vmMain因为它是输入消息分派器。此外,它必须位于0x2D字节码的文本段。


QVM:它如何工作

再次绘制一幅图,说明独特的入口点和独特的退出点作为调度:


一些细节:

消息(Quake3 - > VM)将发送到虚拟机如下:

  • Quake3的任何部分都可以打电话VM_Call( vm_t *vm, int callnum, ... )
  • VMCall最多可以使用11个参数,并将VM bytecode(vm_t *vm)中的每个4字节值从0x00 写入0x26。
  • VMCall 将消息ID写入0x2A。
  • 解释器开始将操作码解释为0x2D(vmMain放置在哪里q3asm.exe)。
  • vmMain 作为调度并将消息路由到适当的字节码方法。

您可以找到可以发送到客户端VM和服务器VM(位于每个文件底部)的消息列表。

系统调用(VM - > Quake3)以这种方式出来:

  • 解释器一个接一个执行VM操作码(VM_CallInterpreted)。
  • 当遇到CALLI4操作码时,它会检查int方法索引。
  • 如果值为负,那么它是一个系统调用。
  • 使用参数调用“系统调用函数指针”(int (*systemCall)( int *parms ))。
  • 指定的功能systemCall是作为一个调度,并将系统调用路由到quake3.exe的正确部分

您可以找到由客户端VM和服务器虚拟机(每个文件顶部)提供的系统调用列表。

参数:参数总是非常简单的类型:基元类型(char,int,float)或指向基本类型的指针(char *,int [])。我怀疑这是为了最小化Visual Studio和LCC之间的结构对齐问题。

琐事: Quake3 VM不执行动态链接,所以QVM mod的开发人员无法访问任何库,甚至没有C标准库(strlen,memset函数都在这里,但实际上是系统调用)。有些人仍然设法用预先分配的缓冲区来伪造它: Malloc在QVM中!

前所未有的自由

随着对虚拟机的任务偏移,修改社区的能力要比修改更多。Neil“匆忙”的多伦多,Unlagged的“反向和解” 改写了预测系统。

生产力问题和解决方案

使用这么长的工具链,开发VM代码是困难的:

  • 工具很慢
  • 工具链未集成到Visual Studio中。
  • 使用命令行工具构建QVM。这很麻烦,中断了工作流程。
  • 在工具链中有很多元素,很难确定哪些部分在出现错误的情况下是错误的。

因此,idTech3还能够为VM部件加载本机DLL,并解决了所有问题:

整体而言,VM系统非常灵活,因为虚拟机能够运行:

  • 解释的字节码
  • 字节码编译为x86指令
  • 代码编译为Windows DLL

推荐读数

  

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 性价 哪个牌子的表性价比高 什么马桶好用性价比高 电脑哪个牌子性价比高 婴儿推车哪个性价比高 哪个牌子电脑性价比高 三星哪款手机性价比高 现在那些手机性价比高 哪个品牌空调性价比高 哪个牌子的拉杆箱性价比高 瓷砖哪个牌子的性价比比较好 什么牌子的羽毛球拍性价比高 什么牌子的蓝牙耳机性价比高 男士钱包什么牌子性价比高 抽油烟机哪个牌子性价比高 液晶电视什么牌子性价比高 哪个牌子的油烟机性价比高 森海塞尔哪款耳机性价比高 哪个牌子的平板性价比高 蓝牙耳机什么牌子性价比高 什么牌子的充电宝性价比高 哪个牌子的按摩椅性价比高 机械键盘什么牌子性价比高 什么牌子台式电脑性价比高 哪个牌子的电动车性价比高 什么牌子的机械键盘性价比高 跑步机哪个牌子的性价比高 现在买什么电视性价比高 哪个牌子的粉底液性价比高 thinkpad哪个系列性价比高 什么牌子的平板电脑性价比高 什么牌子固态硬盘性价比高 哪个牌子的羽绒服性价比高 整体橱柜什么牌子性价比高 哪个牌子的微波炉性价比高 法国性大片在线播放联合传播 性传播疾病专科 接触性传播 通过性传播的疾病 性传播疾病医院 性传播疾病挂什么科