news 2026/10/9 4:50:56

sbox InteropGen 的 .def 绑定描述文件格式全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
sbox InteropGen 的 .def 绑定描述文件格式全解析

【免费下载链接】sbox-public

s&box is a modern game engine, built on Valve's Source 2 and the latest .NET technology, it provides a modern intuitive editor for creating games

项目地址:https://gitcode.com/gh_mirrors/sbo/sbox-public
点击查看免费下载

s&box 引擎使用engine/Tools/InteropGen工具从.def文件生成 C++↔C# 双向互操作绑定代码。本文以 .def 文件格式文档 为主体,逐条讲解文件语法、顶层指令、include / inherit / skipall 组合机制与 manifest 清单,并结合仓库内真实 def 文件(如 engine.def、tools.def)与 GlobalParser.cs 源码,让你完整掌握如何阅读、编写与组织 binding set,并理解绑定集之间如何复用与排除定义。

一、.def 文件的基本语法规则

一个.def文件按行解析,规则非常简单:

  • 空行被跳过,不产生任何语义;
  • 以//开头的行是注释,同样被忽略;
  • 其余每一行必然是以下三种之一:
    • 指令(directive):行内第一个词是关键字,行内其余部分是它的参数;
    • 类型声明(type declaration):如native class ...、managed struct ...等(详见 classes.md);
    • 属性(attribute):如[nogc]、[Handle:...],作用于紧随其后的下一条声明。

值得注意的细节:

  • 参数可以用双引号包裹,解析时引号会被剥离。例如ident "engine"与ident engine等价;
  • 关键字不必位于行首:#include "foo.h"也能工作,因为解析器只匹配它找到的第一个"单词段"。这让 def 文件可以兼容 C++ 语法高亮——写#include时既满足了 C++ 高亮器,又被 InteropGen 识别为include指令。

这一解析行为在源码中有直接对应:GlobalParser.cs 中维护了一张Directives字典,把关键字映射到处理回调;ParseLine则依次尝试匹配native|managed类型声明正则、属性正则,最后才落到base.ParseLine处理通用指令。

二、顶层指令(Top-level directives)

下表完整列出了 def 文件可用的顶层指令:

指令示例含义
identident "engine"本绑定集的名称。会成为原生初始化导出符号igen_engine,并用于命名 handle 类型。
nativedllnativedll engine2.dll托管端启动时加载的原生 DLL(扩展名会被剥离,按平台解析实际文件名)。
cscs "../Sandbox.Engine/Interop.Engine.cs"托管输出写入位置,路径相对 def 文件所在目录。
cppcpp "../../src/engine2/interop.engine.cpp"原生源码输出位置。
hpphpp "../../src/engine2/interop.engine.h"原生头文件输出位置。每个类还会在旁边生成独立子头文件(interop.engine.<class>.h)。
namespacenamespace "Managed.SandboxEngine"生成NativeInterop/Exports类所属的命名空间——托管侧是 C# 命名空间,原生侧是 C++ 命名空间。
exceptionsexceptions "Sandbox.Interop.BindingException"导出托管函数抛异常时调用的托管方法,签名为(string className, string functionName, Exception e)。
pchpch "cbase.h"生成 .cpp 顶部#include的预编译头。
includeinclude "engine/*"包含一个头文件、def 文件或文件夹——详见下一节。
inheritinherit "engine.def"本绑定集构建在另一个绑定集之上——详见后文。
skipallskipall "tools.def"跳过另一个 def 已覆盖的全部内容——详见后文。
delegatedelegate DebugDrawDelegate_t;声明原生委托类型名,使其可用作参数类型(以IntPtr传递,原生侧用FunctionPointerToDelegate<T>转换)。

以仓库中的真实文件 engine.def 为例,可以看到全部核心指令的典型组合:

ident "engine" nativedll engine2.dll exceptions "Sandbox.Interop.BindingException" namespace "Managed.SandboxEngine" cpp "../../src/engine2/interop.engine.cpp" hpp "../../src/engine2/interop.engine.h" cs "../Sandbox.Engine/Interop.Engine.cs" include "dbg.h" include "color.h" include "engine/*" include "tier3" include "common/*" include "resources"

注意其中ident、nativedll、exceptions、namespace、cs/cpp/hpp五类指令与文档描述一一对应,include则混合使用了头文件与文件夹两种形式。结合 Program.cs 可以看到这些指令的最终去向:cs决定ManagedWriter的输出路径,hpp/cpp决定NativeHeaderWriter与NativeWriter的输出路径,ident生成的igen_engine则是原生侧初始化导出符号。

另外从源码看,GlobalParser.cs 中的NativeDll处理还有一个细节:nativedll的参数会剥离扩展名并统一为/分隔的正向路径,也就是说nativedll engine2.dll实际记录为engine2,跨平台解析时再补上对应平台的扩展名。

三、include 指令的三种行为

include根据参数的不同,做三件完全不同的事:

1. 参数以.h结尾 → 头文件生成的原生头文件中会发出#include "..."。惯例写法是#include "vphysics2/iphysicsbody.h",与include "vphysics2/iphysicsbody.h"解析结果完全相同——前者让你在 def 文件里写 C++ 风格代码时保持语法高亮正确。

2. 参数以.def结尾 → 内联解析该文件会被原地解析,如同其内容直接粘贴在当前位置。路径相对于包含它的文件(包括文件)解析。

3. 其他参数 → 当作文件夹目录下每一个*.def都按字母顺序包含进来;参数带尾随/*(如include "common/*")时递归进入子文件夹。

哈希联动:被包含的 def 文本会计入本绑定集的定义哈希。这意味着任何被包含文件的改动都会正确使"二进制配对"失效——这是 s&box 防止原生/托管两侧二进制不同步的关键机制之一。

四、inherit 与 skipall:绑定集之间的复用与排除

当多个 def 覆盖同一段原生代码时,需要处理重复定义问题。仓库中tools.def构建在engine.def之上,正是文档所述模式的实例(见 tools.def):

ident "tools" nativedll toolframework2.dll inherit "engine.def" include "tier3" include "common/*" // 与 engine.def 包含的文件夹相同 include "resources" include "tools/*"

inherit的语义:加载另一个 def 并记住它声明过什么。这里被继承 def 声明的类型仍然会被解析(两边 include 有意重叠,这样才能把那些类型作为参数和返回类型引用),但不会被重新发射——它们的包装器已经存在于被继承 def 的 assembly 中,并且会被排除在本 def 的导出表和结构体尺寸检查之外。

skipall的语义:当 def 继承链变得很深时使用更强力的形式。例如tools.hammer.def同时继承engine.def、tools.def、tools.assetsystem.def时:

inherit "tools.def" skipall "tools.def"

skipall的效果是:出现在被命名 def 中的一切——类(原生和托管)、结构体、以及它的.hinclude——在本 def 发射时全部跳过。inherit与skipall在源码中共享同一个加载路径:GlobalParser.cs 的LoadInto通过InteropPipeline.Build递归构建被引用 def,然后分别加入IncludedDefinitions或SkipAll列表;inherit只保留"可引用",skipall则进一步影响SkipPolicy的发射决策。

从仓库的 manifest.def 可以看到这种层级结构:

Definitions/tools.def Definitions/tools.assetsystem.def Definitions/tools.hammer.def Definitions/tools.modeldoc.def Definitions/tools.animgraph.def Definitions/engine.def

tools.*系列 def 都通过inherit站在engine.def的肩膀上,共享同一套基础绑定,同时各自只发射自己独有的部分。

五、manifest.def:绑定集构建清单

engine/manifest.def(即 manifest.def)本质上只是一份待构建 def 的列表,每行一个:

Definitions/tools.def Definitions/engine.def ...
  • 不以.def结尾的行会被忽略;
  • 列表中的每个 def 会被并行处理。

这与 Program.cs 中的ProcessManifest实现完全对应:读取 manifest 后,用EndsWith(".def")过滤行、拼上 manifest 所在目录作为路径,然后为每个 defTask.Run一个ProcessDefinitionFile,全部完成后汇总成功状态。构建系统(Tools/SboxBuild)在仓库根目录调用Facepunch.InteropGen.Program.ProcessManifest( "engine" ),即驱动整个生成流程的入口。

六、输出与构建流程中的关键约束

把 def 与生成器联系起来,有几个工程上必须理解的事实:

  • 生成产物不入库:cs/cpp/hpp指向的输出文件由构建时重新生成(仓库.gitignore排除了Interop.*.cs/interop.*),内容未变化的文件不会被重写;
  • 两侧必须成对重建:原生导出表(casts、函数、变量 get/set)的槽位顺序在 C++ 与 C# 之间按索引匹配,统一由Writer/NativeExportTable.cs驱动,是槽位顺序的唯一权威来源;
  • 哈希防错:运行时交换导出表时会校验 def 哈希,陈旧的二进制会快速失败,而不是运行到一半才崩;
  • 改名会影响导出符号:mangled 名按遇到顺序用数字后缀去重碰撞,因此在 def 中重排类或成员可能重命名导出符号——两侧同时重新生成即可,这正是输出必须成对再生的原因。

引用路径提示:需要深入了解声明语法时,请继续阅读同目录的 classes.md(类、结构体、枚举、函数、变量与属性声明)和 types.md(参数类型、编组与out/ref/CastTo[...]等标志);生成器的整体流程与项目布局可参见 InteropGen 文档索引。

【免费下载链接】sbox-public

s&box is a modern game engine, built on Valve's Source 2 and the latest .NET technology, it provides a modern intuitive editor for creating games

项目地址:https://gitcode.com/gh_mirrors/sbo/sbox-public
点击查看免费下载
上一篇:如何用Midscene.js在3分钟内实现零代码视觉自动化测试:终极跨平台解决方案
下一篇:接入联邦宇宙:NodeBB ActivityPub联邦与Mastodon互通完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 4:46:10

JSP驾校管理系统源码详解:从环境搭建到预约开发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/9 4:46:08

Android Studio五子棋开发:自定义View、触摸事件与人机对战完整指南

简介&#xff1a;Android Studio五子棋项目是一份面向Android初学者的完整源码与工程文件&#xff0c;涵盖从界面布局到游戏规则实现的典型开发链路。项目基于Android Studio IDE构建&#xff0c;包含MainActivity、棋盘逻辑、胜负判断与落子校验等核心模块&#xff0c;适合用于…

作者头像 李华
网站建设 2026/10/9 4:45:18

2026毕设攻略:SSM+Vue民宿管理系统设计与实现全解析

每年到这个时间点&#xff0c;总有一批人开始为毕业设计发愁。如果你正在看“2026毕设ssmvue民宿管理系统论文程序”这类标题&#xff0c;大概率是已经定好了题目、下载了几份源码&#xff0c;但不知道该怎么下手。我可以先给你吃个定心丸&#xff1a;这个选题属于典型的业务型…

作者头像 李华