Xmldef Descriptor Spec
Status: draft / deferred tooling spec.
本文把 Xmldef Toolchain Design 进一步收敛成可实现规格,定义
xmldef/xml -> canonical IR -> descriptor package的输入、校验、IR、输出和稳定性规则。目标不是解释“为什么需要 descriptor”,而是给后续 compiler/runtime/generator 实现提供统一契约。
Scope
本文只定义:
xmldef/xml输入模型- semantic validation
- canonical IR
- descriptor package layout
- ID / hash / stability rules
- runtime 消费所需最小字段
- generator 消费所需最小字段
不定义:
shield_protocCLI UX- Unity/U3D 具体生成代码样式
- runtime
XmldefBodyCodec的二进制编码细节 - 多版本 patch / Merkle 增量协议
如果目标是“这一版先做什么、做到什么算完成”,请继续阅读 Xmldef Phase 1 Implementation Plan。 如果目标是“代码落在哪里、模块职责怎么拆”,请继续阅读 Xmldef Compiler / Runtime MVP。
Source Model
Layout
建议的 xmldef 源文件组织:
protocol/
manifest.xml
common/
types.xml
login/
messages.xml
avatar/
messages.xmlmanifest.xml 是唯一入口。
Manifest
最小 manifest 语义:
<protocol-manifest name="game"
version="1.0.0"
packageId="game"
codeNamespace="Game.Protocol">
<module name="common" path="common"/>
<module name="login" path="login"/>
<module name="avatar" path="avatar"/>
</protocol-manifest>最小字段:
nameversionpackageIdcodeNamespacemodule[]
约束:
packageId在 descriptor 中长期稳定codeNamespace不从name自动推导,必须显式声明module.name在 package 内唯一
Message Definition
xmldef 主要关注消息 / 方法 / 字段定义。最小消息模型示意:
<protocol name="avatar">
<message id="0x1001"
name="Avatar.move"
direction="client_to_server"
request="avatar.MoveRequest"
response=""/>
</protocol>本文不冻结 XML 标签名细节,只冻结编译后的语义字段。
最小语义字段:
route_idfull_namedirectionrequest_typeresponse_type?target_hint?binding_hint?
schema 寻址不再单独设字段:schema 的定位就是 request_type 指向的 类型图节点;导出到 host 路由表时按收敛后的 codec ABI 只产出 schema_name(见下方 Route 一节与 protocol-codec-plugins.md 的「Schema 寻址收敛」)。
Type Definition
复用 schema-design.md 的类型系统口径,xmldef 最小必须支持:
- scalar
- enum
- struct
- alias
- list
- map
其中 map key 第一版限制为:
string- integer scalar
- enum
Semantic Validation
编译器必须在 XML 阶段完成强校验,不能把错误推给 runtime。
Required Checks
必须校验:
route_id唯一method full_name唯一request_type/response_type引用存在- 字段 ID 在 struct 内唯一
- 字段名在 struct 内唯一
- enum value 唯一
- enum item 名唯一
- client-visible 类型不能引用 server-only 类型
direction值合法- XML 中声明的 reserved ID 不可复用
Direction Rules
支持的最小方向:
client_to_serverserver_to_clientbidirectionalserver_to_server
编译器要把它们归一化成稳定枚举,不保留原始字符串。
Method Kind
xmldef 在 descriptor 层必须明确 method kind:
sendcallstream
规则:
send必须有 request type,不能有 response/item typecall必须有 request + response typestream必须有 request + item type
Canonical IR
Top-level Shape
canonical IR 最小结构:
package {
package_id
package_name
package_version
code_namespace
modules[]
types[]
methods[]
routes[]
}Package
字段:
package_id: stringpackage_name: stringpackage_version: stringcode_namespace: stringcompiler_version: stringschema_root_hash: bytes[32]
Module
字段:
module_id: uint16name: stringpath: stringhash: bytes[32]dependencies[]: module_id
稳定性规则:
module_id必须来自 lock file 或显式声明- 发布后不能重分配
Type
字段:
type_id: uint32module_id: uint16full_name: stringkind: enumclient_visibility: enumhash: bytes[32]
支持的 kind:
scalarenumstructalias
Struct
字段:
type_idfields[]reserved_field_ids[]
Field 最小字段:
field_id: uint32name: stringtype_refcardinalitydefault_value?deprecated: boolvalidation_rules[]
TypeRef
最小模型:
type_ref {
kind // scalar | named | list | map
scalar_kind?
named_type_id?
list_item?
map_key?
map_value?
}Method
最小字段:
method_id: uint16route_id: uint32module_id: uint16full_name: stringshort_name: stringkind: enum(send/call/stream)direction: enumrequest_type_id: uint32response_type_id?: uint32item_type_id?: uint32target_hint?: stringbinding_hint?: stringhash: bytes[32]
Route
显式保留 route 表,避免 generator 或 runtime 自己从 method 里再推:
route_id: uint32method_id: uint16full_name: stringdirectionschema_name: string
schema_name 是 codec 管线的唯一寻址键,在编译期解析:显式 request_schema 覆盖优先,否则取 full_name(同名约定)。这与 host 侧 RouteEntry.schema_name 的收敛规则一致,catalog 导出的路由只 产出这个名字,不再暴露 schema_id 一类的数字寻址——那类 id 即使 存在于具体 schema 系统(如 protobuf 文件号)内部,也只是该系统的 私有概念,不进入 descriptor 通用契约,也不进入 codec ABI。
Canonical Ordering
为了保证稳定 hash,IR 写入 descriptor 前必须 canonical sort:
- modules 按
module_id - named types 按
type_id - methods 按
route_id - route table 按
route_id - struct fields 按
field_id - enum values 按
value
不允许:
- 按 XML 文件顺序输出
- 按 include 顺序输出
- 按字符串 locale 排序
Descriptor Outputs
Required Files
最小输出:
descriptor.bindescriptor.debug.jsonroute_constants.jsonmanifest.json
descriptor.bin
给 runtime 用,要求:
- 快速加载
- little-endian
- 稳定 offsets
- 不依赖目标语言反射
至少包含:
- package header
- module table
- type table
- method table
- route table
- string table
descriptor.debug.json
给 generator / debug / QA 工具使用。
最小要求:
- 可完全表达 binary descriptor 的语义内容
- 字段名明确
- 不丢失
route_id/schema_name/direction - 可供 generator 无损读取
route_constants.json
轻量产物,给简单工具、脚本和测试使用。
示意:
{
"package": "game",
"version": "1.0.0",
"routes": [
{
"route_id": 4097,
"name": "Avatar.move",
"schema_name": "Avatar.move",
"direction": "client_to_server"
}
]
}Hashing Rules
schema_root_hash
schema_root_hash 必须只依赖语义内容,不依赖:
- 编译时间
- 编译器版本
- 源码脏状态
- XML 格式化
- 注释
meta vs schema
必须区分:
MetaBlock: build info,不进入语义 hashSchemaBlock: 语义内容,进入语义 hash
Runtime Consumption Contract
runtime 加载 descriptor 后,最少能做这些查找:
find_method_by_route(route_id)find_method_by_name(full_name)find_type(type_id)find_request_type(route_id)find_response_type(route_id)
XmldefBodyCodec 后续实现不得再依赖 XML 源文件。
Generator Consumption Contract
generator 侧最少要能拿到:
- package id / version / namespace
- route constants
- method full name
- request/response/item type
- direction
- field layout
- optional binding hint
如果 descriptor 缺少这些字段,则视为 compiler 契约不完整。
Stability Rules
发布后以下 ID 不能复用:
module_idtype_idmethod_idroute_id- struct
field_id
删除只能:
- 标记 deprecated
- 或进入 reserved
不能删掉后重新分配给新语义。
Non-goals
本文不做这些事情:
- 定义 Unity 生成文件名细节
- 定义 runtime socket frame 格式
- 定义 Lua binding table 的最终 API 名字
- 定义 XML 标签是否必须叫
<message>/<method>
这些属于上层 generator/runtime 契约,不属于 descriptor compiler 的核心语义。