Schema Types
Status: deferred extension design.
Schema types are not part of the current refactor core. Treat this document as future protocol/tooling design, separate from the initial
shield.*Lua runtime contract.
types.xml 是 Shield schema protocol 的公共类型定义层。它为 RPC、客户端 SDK、mapper、错误模型和文档生成提供统一的数据模型来源。
设计目标
- 类型定义可读、可验证、可生成。
- XML 结构由 XSD 强校验。
- 类型引用、字段 ID、兼容性由语义校验保证。
- 运行时使用 descriptor,不直接解析 XML。
- RPC DTO、DB entity 和 view 能共享基础类型,但边界清晰。
文件位置
推荐按业务域拆分:
protocol/
common/
types.xml
errors.xml
player/
types.xml
room/
types.xml每个 types.xml 必须声明 namespace:
<types namespace="player">
...
</types>类型全名为 namespace.name,例如 player.PlayerProfile。
基础类型
第一版内置 scalar:
boolint32int64uint32uint64sint32sint64floatdoublestringbytes
说明:
sint32和sint64使用 zigzag 编码。int32、int64使用普通 varint 编码。string必须是 UTF-8。bytes不做字符集假设。
Enum
<enum name="PlayerState">
<item name="Offline" value="0"/>
<item name="Online" value="1"/>
<item name="InRoom" value="2"/>
</enum>规则:
- enum name 在 namespace 内唯一。
- enum value 在 enum 内唯一。
- enum item name 在 enum 内唯一。
- 发布后的 value 不能复用。
- 删除 enum item 时必须标记
deprecated="true"或进入 reserved。 - 客户端必须能处理未知 enum value。
保留值:
<enum name="PlayerState">
<reserved value="3"/>
<reserved range="100-199"/>
<item name="Offline" value="0"/>
<item name="Online" value="1"/>
</enum>Struct
<struct name="PlayerProfile">
<field name="player_id" id="1" type="string"/>
<field name="nickname" id="2" type="string"/>
<field name="level" id="3" type="int32" default="1"/>
<field name="state" id="4" type="player.PlayerState"/>
</struct>规则:
- struct name 在 namespace 内唯一。
- field id 在 struct 内唯一。
- field name 在 struct 内唯一。
- field id 发布后不能复用。
- 字段删除后必须保留 field id。
- 字段默认 optional。
required只能作为 validation 规则,不能成为 wire format required。
保留字段:
<struct name="PlayerProfile">
<reserved id="5"/>
<reserved range="100-199"/>
<field name="player_id" id="1" type="string"/>
</struct>Field
字段属性:
<field name="nickname"
id="2"
type="string"
optional="true"
default="guest"
deprecated="false"/>建议字段属性:
name: 字段名。id: wire field number。type: 类型引用。optional: 是否可缺省。default: 默认值。deprecated: 是否废弃。since: 首次出现版本。doc: 短文档,可选。
字段命名建议统一使用 snake_case。生成器可以为 C#、TS 转换为目标语言习惯命名。
集合类型
列表:
<field name="items" id="1" type="list<player.ItemInfo>"/>Map:
<field name="attrs" id="2" type="map<string,string>"/>规则:
- list 元素可以是 scalar、enum、struct。
- map key 第一版只允许
string、整数和 enum。 - map value 可以是 scalar、enum、struct。
- map 在 wire format 中按 repeated entry message 编码。
Alias
Alias 用于表达领域语义,不改变 wire encoding。
<alias name="PlayerId" type="string"/>
<alias name="RoomId" type="uint64"/>生成器可以用 alias 生成更明确的类型别名。运行时 descriptor 保留 alias 信息用于文档和校验。
Entity
entity 是持久化模型,通常在 mapper 文件中定义,也可以放在 types 文件中集中管理。
<entity name="PlayerEntity" table="player">
<field name="player_id" column="player_id" type="string" id="1" primaryKey="true"/>
<field name="nickname" column="nickname" type="string" id="2"/>
</entity>规则:
- entity 可以参与 mapper result mapping。
- entity 默认不暴露给客户端 descriptor。
- entity 不应直接作为公网 RPC response,除非显式
expose="client"。
View / DTO
对外协议建议使用 struct 作为 DTO 或 view:
<struct name="PlayerProfile" expose="client">
<field name="player_id" id="1" type="string"/>
<field name="nickname" id="2" type="string"/>
<field name="level" id="3" type="int32"/>
</struct>建议:
- 客户端可见类型显式
expose="client"。 - 服务端内部类型默认
expose="server"。 - 公共错误类型默认
expose="both"。
错误类型
公共错误结构通常放在 common/errors.xml,但语义上仍属于类型系统。
<struct name="ShieldError" expose="both">
<field name="code" id="1" type="int32"/>
<field name="name" id="2" type="string"/>
<field name="category" id="3" type="common.ErrorCategory"/>
<field name="message" id="4" type="string"/>
<field name="retryable" id="5" type="bool" default="false"/>
<field name="details" id="6" type="map<string,string>" optional="true"/>
</struct>错误码集合:
<error-set name="PlayerErrors">
<error code="20001" name="PLAYER_NOT_FOUND" category="Business"/>
<error code="20002" name="PLAYER_BANNED" category="Business"/>
</error-set>Validation
Validation 分为四层:
- XML 结构校验。
- 类型语义校验。
- 兼容性校验。
- 运行时数据校验。
XML 结构校验
由 XSD 完成:
- 根节点是否合法。
- 必填属性是否存在。
- 属性类型是否正确。
- 子节点顺序和层级是否合法。
- enum、struct、field 等元素是否出现在允许位置。
XSD 不能完成跨文件引用、ID 唯一性和兼容性判断,这些由语义校验完成。
类型语义校验
编译器必须检查:
- namespace 合法。
- 类型名合法。
- 同 namespace 内类型名唯一。
- 类型引用存在。
- field id 唯一。
- field id 不在 reserved 范围。
- field type 合法。
- default value 能转换为字段类型。
- map key 类型合法。
- enum value 唯一。
- error code 唯一。
- expose profile 不泄漏 server-only 类型。
兼容性校验
编译器对比旧 descriptor 和新 descriptor:
安全变更:
- 新增 optional field。
- 新增 struct。
- 新增 enum value。
- 新增 error code。
- 新增 server-only 类型。
条件变更:
- 新增 validation rule。
- 修改默认值。
- 标记 deprecated。
- 修改 expose profile。
破坏性变更:
- 修改 field id。
- 修改 field type。
- 删除 field 但未 reserved。
- 复用 reserved id。
- 修改 enum value 的含义。
- 删除客户端仍可见类型。
- 将 optional 字段改成 required validation。
运行时数据校验
运行时按 descriptor 校验:
- required validation。
- string 长度。
- bytes 长度。
- 数值范围。
- list 长度。
- map 长度。
- enum 是否在已知范围。
- 已启用的内置 validation rule。
运行时校验失败统一返回 common.ShieldError,category 为 Validation。
Validation Rule
字段级规则:
<field name="nickname" id="2" type="string">
<validate required="true" minLength="2" maxLength="16"/>
</field>数值范围:
<field name="level" id="3" type="int32">
<validate min="1" max="999"/>
</field>集合长度:
<field name="items" id="4" type="list<player.ItemInfo>">
<validate maxItems="200"/>
</field>Phase 1 支持:
requiredminmaxminLengthmaxLengthminItemsmaxItems
Phase 1 不支持:
- 通用
pattern正则。 - 任意脚本表达式。
- 跨字段复杂约束。
- 数据库唯一性校验。
- 远程服务校验。
复杂业务规则应放在 service 实现中。
通用 pattern 固定放到 Phase 2。启用时必须使用 RE2 语义或受限正则子集,避免不同语言 regex 行为不一致和 ReDoS 风险。
XSD 范围
XSD 应覆盖结构性规则:
<xs:element name="types">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="enum" type="EnumType"/>
<xs:element name="struct" type="StructType"/>
<xs:element name="alias" type="AliasType"/>
<xs:element name="entity" type="EntityType"/>
<xs:element name="error-set" type="ErrorSetType"/>
</xs:choice>
<xs:attribute name="namespace" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>XSD 不应尝试表达所有语义规则。比如跨文件类型引用、reserved ID 和兼容性检查必须由 shield protoc 完成。
Descriptor 映射
types.xml 编译后进入 descriptor:
TypeDescriptor
id
module_id
name
full_name
kind
flags
hash
StructDescriptor
type_id
fields[]
FieldDescriptor
id
name
type_ref
cardinality
default_value
validation_rules
deprecated
reserved字段按 field id 排序后参与 canonical hash。
Client Profile
类型暴露规则:
expose="client": 进入客户端 descriptor。expose="server": 只进入服务端 descriptor。expose="both": 同时进入。- 未声明时,struct 默认 server,RPC request/response 引用到的类型自动提升到 client-visible。
编译器必须防止 client-visible 类型引用 server-only 类型。
Debug JSON
开发期生成的 descriptor.debug.json 应包含类型详情:
{
"types": [
{
"fullName": "player.PlayerProfile",
"kind": "struct",
"expose": "client",
"fields": [
{
"id": 1,
"name": "player_id",
"type": "string",
"optional": true
}
]
}
]
}这个文件用于 IDE 插件、协议 diff、调试面板和联调工具。
Phase 1 范围
Phase 1 实现:
enumstructaliaserror-set- scalar 类型
list<T>map<K,V>- field id / reserved 校验
- 类型引用校验
- default value 校验
required/min/max/minLength/maxLength/minItems/maxItems- client/server expose profile
推迟:
- oneof
- union
- recursive struct
- 复杂泛型
- 跨字段 validation
- 通用
pattern - 自定义 validation plugin
- schema migration 生成
已定规则
- 第一版禁止 recursive struct。
expose默认按引用自动推导,显式expose="server"被 client-visible service 引用时编译失败。- 通用
pattern不进入 Phase 1。 - persistence-specific
entity优先放在mappers.xml。 - JSON Schema 生成不进入 Phase 1;调试和工具生态先使用
descriptor.debug.json。