【科普|翻译】Source引擎插件开发:SourceHook的前世今生 开发者相关

我不当学长 管理员组 3月前 507

译者注:

0. 说在前面的话

SourceHook 已经19岁了。我想你们可能会有兴趣了解一下 Source 引擎的历史。

SourceHook 是 Metamod 的强大核心程序,是 Sourcemod 和所有 Source 2 模组的核心插件管理器。SourceHook 最初由 SourceMod 的核心作者于 2005-2006 年开发,旨在实现新的 Half-Life 2 引擎的快速开发,此后一直是所有 Source 引擎模组的主要内容。你玩过的每个服务器都可能在某种程度上依赖 SourceHook,但不幸的是,在模组开发的历史中,它仍然是一个被大众遗忘的,并不是那么优雅的历史产物。


1. 最原始的 Metamod

当GoldSrc(金源引擎)发布时,模组制作者开始创建我们现在所知的GameDLL模组。GameDLL模组会欺骗引擎加载模组而不是真正的游戏,使得模组可以位于引擎和真正游戏之间。模组可以对游戏调用引擎的过程进行监控或挂钩(反之亦然)。然而,即便能做到欺骗引擎让其加载插件,但这种加载却只能加载一个插件,这导致在需要使用多个插件时产生冲突和兼容性问题。这促使原始的Metamod开发出来,它是一个简单的插件,可以加载其他插件,并允许它们共同侵入引擎的隐私。

Metamod.drawio.png

Metamod 虽然运行缓慢,但还能用。由于 GoldSrc 引擎架构简单,Metamod 本身不必非常复杂,并且最初的 Metamod 也成为了插件开发的标准。遗憾的是,后来 Valve 用 Source 引擎搞砸了一切。


2. Source 引擎

新的 Source 引擎摒弃了简单的引擎-游戏通信设计,采用了接口模式;该模式下,每个对象(或类实例)【Object (or class instance) 】都拥有一个指向虚函数表(Virtual Table)的指针。每个虚函数表包含一个函数列表,如果知道要调用的方法编号(编号)或偏移量(offset) ,就可以调用这些函数。

释义:

  • 接口模式 : interface pattern (computer science concept)
  • 虚函数表 : virtual table
  • 指针 : pointer
  • 偏移量 : offset

VirtualTable.drawio.png

 

虚函数表的运用让 Valve 得以构建一个远比之前复杂得多的引擎,并为游戏加入了更多功能。然而,这却是以模组制作者的利益为代价的:尽管 Valve 在游戏发布时非常慷慨地公开了每个虚函数表的定义,但新引擎的复杂性使得几乎不可能介于整个引擎之间;可覆盖的范围实在太广,需要复制的功能也太多。更糟糕的是,引擎为了适应新功能而飞速更新,破坏了兼容性,让模组制作者感到愤怒。2005 年,BAILOPAN 写道:

Valve 经常修改接口版本用于他们自己的第一方模组,却不会发布这些改动。这种情况最早出现在 PlayerInfoManager 上,该接口在 CS:S 中是 002 版本,而在 HL2MP 中是 001 版本。更糟糕的是 DoD:S 的问题,Valve 在其中同时使用了两个不同版本,并且 Valve 甚至没有公开发布接口改动信息。因此,人们要么只能假设 003->004 版本跳跃保持向后兼容性(版本号增加通常应该保持兼容,但是这里的实际设计却无法保证这一点,因为需要提前知道未来版本的信息),要么就只能假设接口已经改动导致无法兼容。无论哪种方式,都意味着既丢失了与未来版本向后兼容性,也失去了模组独立支持,因为 Valve 声称他们的接口设计并非总是需要公开的,更糟糕的是,他们可以随时为了自家第一方模组而改动接口。这令人沮丧,混乱不堪,简而言之,这就是 HL2SDK 的问题所在。

 

此外,如果不介于引擎和游戏之间,就无法确保插件在开始对游戏进行 插桩 时互不干扰。到 2005 年 3 月,模组制作者们已经厌倦了与 Valve 的庞大新接口打交道,并决定通过开发 Metamod: Source(以下简称“Metamod”)自己掌控局面。

释义:

  • 插桩 : 在程序运行过程中注入额外的代码

2005 年 5 月发布的 Metamod: Source 得益于其针对 Valve 新接口的独特解决方案,在众多模组开发工具中脱颖而出。它没有尝试介于整个引擎和游戏之间,而是完全不介于任何东西之间。它什么都不做,没有插桩,没有修改。它只是呆在那里观察。

Metamod 的真正力量来自本文的主角 - SourceHook。当插件加载时,SourceHook 会开始用 SourceHook 表替换引擎的虚函数表。然后,SourceHook 可以用插件的代码替换单个方法,同时保持其余虚函数表条目与游戏原始版本一致。由于 SourceHook 会跟踪所有所做的更改,因此它可以通过防止插件相互撤销对方的钩子来让插件彼此协同工作。并且,由于它仅在插件需要监听时才介于引擎之间,因此运行速度 很快

释义:

  • 钩子: 在程序运行过程中拦截并修改函数执行流程的一种技术(对应原文中的 detour)

SourceHook.drawio.png

得益于 SourceHook,Metamod 不必再担心太多游戏兼容性问题:只要它能够加载游戏并了解一些游戏状态的基本信息,它就可以正常运行。即使引擎的接口发生巨大变化,Metamod 也可以继续运行下去(不过插件方面就另当别论了……)。这种基本特性使 Metamod 能够快速移植到 Source 2 引擎,并且是其在反恐精英 2 领域迅速扩展的重要助力。

由于 SourceHook 只针对单个方法工作,许多插件(例如 SourceMod!)实际上并不需要了解太多它所要钩取的对象;它们只需要告诉 SourceHook 需要钩取哪个方法以及被钩取的方法期望接收哪些参数即可。插件维护工作量开始减少,逆向工程工作可以更多地集中在新特性上,而不是确保引擎与游戏的兼容性。直到今天,你仍然可以在 HL2SDK 中找到大量标有“未知”的方法:这些方法没有 Valve 的文档说明,对逆向工程师来说是个谜,但由于 SourceHook 的存在,它们变得完全无关紧要。


3. 今日的 SourceHook

自 2005 年以来,SourceHook 经历了数次重大更新,兼容所有主流 C++ 调用约定,可以很好地兼容 GCC 和 MSVC 的一些怪癖。即使在今天,SourceHook 仍然是所有 Source 引擎开发的核心,从 Sourcemod 充满活力的插件场景到 CS2Fixes 和 Counter-Strike Sharp 等更新开发项目都离不开它。SourceHook 代码库简洁、优雅且令人印象深刻的稳定性,可以加速我们的开发并缩短迭代时间。

尽管 SourceHook 是一个与 Metamod 基本独立的项目,但它从未真正离开过 Source 社区。由于许多游戏放弃了基于接口的架构,许多模组制作者转而使用静态钩子和实时反汇编器来进行修改。虚拟钩取在很大程度上已经成为 Mod 制作界过时的工具。

尽管如此,我们仍然期待在进军 Source 2 引擎时看到 SourceHook 迷人的小宏指令。让我们一起祝 SourceHook 再创辉煌二十年,生日快乐!

参考:


CSGO插件分享-申明 1、本网站名称:CSGO插件分享-中文站  网址:https://bbs.csgocn.net
2、本站的宗旨在于为CSGO玩家提供一个插件分享的中文资源平台,多数插件来源于SourceMod论坛,并配以中文介绍和安装教程。
3、欢迎有能力的朋友共享有趣的CSGO插件资源。
4、本站资源大多为百度网盘,如发现链接失效,可以点: 这里进行反馈,我们会第一时间更新。
最新回复 (0)
返回