Schema Implementation Layout
Status: deferred extension design.
This document predates the current single-node runtime boundary. Mentions of
shield_core,shield_extensions, plugin integration, mapper runtime, or protocol runtime should be read as historical design vocabulary. Schema work must not enter the current core unless it is explicitly re-approved in the refactor roadmap.
本文定义 schema protocol 的源码组织、namespace、库边界、工具链位置和生成物目录。它补齐协议设计从“概念可行”到“可以开始实现”的工程约束。
完整性判断
当前 schema protocol 设计已经覆盖:
- XML 契约模型
types.xmlservices.xmlmappers.xmldescriptor.bin- Merkle 增量
- 强校验
- 客户端运行时和开发期产物
还必须补齐的实现约束是:
- runtime 源码放在哪里
- compiler 工具放在哪里
- namespace 如何拆
- 哪些内容属于未来 schema runtime
- 哪些内容必须留在 optional extension
- client SDK 如何与服务端运行时隔离
- generated 文件落到哪里
库边界
Schema protocol 拆成四类组件:
future schema runtime
descriptor runtime
wire codec
rpc pending/stream runtime
descriptor loader
future schema extensions
optional mapper runtime integration if it depends on database stack
optional tooling integrations
shield_protoc
XML parser
XSD validation
semantic validation
compatibility checker
descriptor generator
code/doc generators
client SDKs
cpp client core
web/typescript package
unity/csharp package规则:
- 运行时解码、descriptor registry、
send/call/stream分发属于未来 schema runtime,不属于当前 refactor core。 - XML 编译器不进入当前 runtime 热路径。
- mapper descriptor 和 mapper runtime 均按后续扩展处理,不能进入当前最小启动路径。
- 客户端 SDK 不依赖服务端运行时或插件系统。
shield_protoc默认随开发构建启用,并通过SHIELD_BUILD_TOOLS=OFF关闭。- Merkle root、module hash 和 node hash 第一版使用 SHA-256;CRC32 只用于快速损坏检测。
推荐源码目录
现有 include/shield/protocol/schema_protocol.hpp 可以作为早期兼容入口,但新实现不应继续堆在一个文件里。
推荐结构:
include/shield/protocol/
schema_protocol.hpp # 兼容 facade,逐步变薄
schema/
descriptor.hpp
descriptor_package.hpp
descriptor_registry.hpp
type_descriptor.hpp
service_descriptor.hpp
error_descriptor.hpp
merkle_tree.hpp
compatibility.hpp
validation.hpp
wire/
frame.hpp
frame_codec.hpp
protobuf_wire.hpp
payload_codec.hpp
rpc/
pending_call.hpp
pending_call_registry.hpp
stream.hpp
stream_registry.hpp
src/protocol/
schema_protocol.cpp # 兼容 facade 实现
schema/
descriptor_package.cpp
descriptor_registry.cpp
merkle_tree.cpp
compatibility.cpp
validation.cpp
wire/
frame_codec.cpp
protobuf_wire.cpp
payload_codec.cpp
rpc/
pending_call_registry.cpp
stream_registry.cppMapper 运行时:
include/shield/data/mapper/
mapper_descriptor.hpp
mapper_registry.hpp
mapper_runtime.hpp
sql_binder.hpp
result_mapper.hpp
transaction_policy.hpp
src/data/mapper/
mapper_registry.cpp
mapper_runtime.cpp
sql_binder.cpp
result_mapper.cpp编译器工具链:
tools/schema_compiler/
CMakeLists.txt
main.cpp
compiler/
manifest_loader.hpp
xml_loader.hpp
xsd_validator.hpp
semantic_validator.hpp
canonical_ir.hpp
descriptor_writer.hpp
debug_json_writer.hpp
merkle_writer.hpp
compatibility_checker.hpp
generators/
cpp_generator.hpp
lua_generator.hpp
typescript_generator.hpp
csharp_generator.hpp
markdown_generator.hpp
schemas/xsd/
manifest.xsd
types.xsd
services.xsd
mappers.xsd客户端 SDK:
client/
cpp/
include/shield/client/
client.hpp
connection.hpp
descriptor_loader.hpp
call.hpp
stream.hpp
src/
web/
package.json
src/
client.ts
descriptor.ts
codec.ts
call.ts
stream.ts
unity/
package.json
Runtime/
ShieldClient.cs
DescriptorLoader.cs
Codec.cs
Call.cs
Stream.cs生成物:
build/protocol/
descriptor.server.bin
descriptor.client.bin
descriptor.debug.json
merkle.json
docs/
generated/
cpp/
lua/
ts/
csharp/C++ Namespace
现有模块已经使用 shield::protocol、shield::gateway、shield::core、shield::data 和 shield::database。Schema protocol 新代码应避免继续平铺到 shield::protocol。
推荐 namespace:
namespace shield::protocol::schema {
// descriptor, registry, validation, compatibility, merkle
}
namespace shield::protocol::wire {
// frame codec, protobuf-compatible payload codec
}
namespace shield::protocol::rpc {
// pending call, stream state, correlation registry
}
namespace shield::data::mapper {
// mapper runtime, SQL binder, result mapper
}
namespace shield::tools::schema {
// shield_protoc compiler internals
}
namespace shield::client {
// native C++ client SDK
}Facade API 可以保留在 shield::protocol:
namespace shield::protocol {
using SchemaRegistry = schema::DescriptorRegistry;
}这样旧代码可以过渡,新代码有明确层次。
命名规则
文件:
- C++ header/source 使用
lower_snake_case。 - XML 文件使用
lower_snake_case.xml。 - 生成文件使用目标语言习惯,但保留 schema namespace。
C++ 类型:
- 类型名使用
PascalCase。 - 函数名遵循仓库现有风格,优先
snake_case。 - enum class 使用
PascalCase类型名,枚举值使用UPPER_SNAKE_CASE或跟随现有模块风格。
XML:
- namespace 使用小写业务域名,例如
player、room、common。 - XML type name 使用
PascalCase。 - XML field name 使用
snake_case。 - service/method name 使用
PascalCase。
示例:
<types namespace="player">
<struct name="PlayerProfile">
<field name="player_id" id="1" type="string"/>
</struct>
</types>
<services namespace="player">
<service name="PlayerService" id="100">
<call name="GetProfile" id="1" request="player.GetProfileRequest" response="player.GetProfileReply"/>
</service>
</services>Runtime Object Model
运行时对象关系:
DescriptorPackage
-> DescriptorRegistry
-> TypeRegistry
-> ServiceRegistry
-> ErrorRegistry
-> MapperRegistry(server only)
FrameCodec
-> reads Shield frame
PayloadCodec
-> uses TypeDescriptor
-> protobuf-compatible encode/decode
RpcRuntime
-> PendingCallRegistry
-> StreamRegistry
-> DescriptorRegistryGateway 集成:
GatewayService
-> FrameCodec
-> DescriptorRegistry
-> RpcRuntime
-> GatewayRequestDispatcher
-> Lua/C++ service handlerCore Startup
Schema runtime 应作为 gateway 前置依赖:
ServerCommand::run()
-> ConfigManager
-> SchemaStarter
load descriptor.server.bin
create DescriptorRegistry
create FrameCodec/PayloadCodec
create RpcRuntime
-> ScriptStarter
-> ActorStarter
-> ServiceStarter
-> GatewayStarter如果没有配置 descriptor,gateway 可以退回现有 legacy protocol 模式。
SchemaStarter 是独立 starter,不由 GatewayStarter 内部隐式创建。这样 mapper、gateway、client handshake 和 runtime validation 都能共享同一份 DescriptorRegistry。
Config
配置:
schema:
enabled: true
server_descriptor: "build/protocol/descriptor.server.bin"
client_descriptor: "build/protocol/descriptor.client.bin"
compatibility:
allow_patch: true
allow_minor_mismatch: true
runtime:
max_frame_size: 1048576
max_string_size: 65536
max_repeated_items: 10000
retained_descriptor_versions: 2Mapper 配置:
mapper:
enabled: true
datasource: "game"
default_timeout_ms: 1000
max_rows: 1000CMake Structure
分阶段接入:
Phase 1:
- 新增 runtime 源码到
shield_core。 - 新增
shield_protoc可执行文件。 - 不移动现有
schema_protocol.hpp/cpp,先作为 facade。 - 默认构建
shield_protoc,可通过SHIELD_BUILD_TOOLS=OFF关闭。
Phase 2:
- mapper runtime 接入
shield_extensions或数据访问模块。 - client cpp core 独立 target。
- TS/C# SDK 不进入主 CMake 构建。
targets:
shield_core
shield_extensions
shield_protoc
shield_client_cppGenerated Code Namespace
生成代码不应污染运行时 namespace。
C++:
namespace game::protocol::player {
struct PlayerProfile;
class PlayerServiceClient;
}TypeScript:
import { ShieldClient } from "@shield/client";
export namespace player {
export interface PlayerProfile {}
export class PlayerServiceClient {}
}C#:
namespace Game.Protocol.Player {
public sealed class PlayerProfile {}
public sealed class PlayerServiceClient {}
}Lua:
local player = require("generated.player")生成命名空间来自 manifest:
<protocol-manifest name="game" codeNamespace="Game.Protocol" version="1.8.3">Migration From Current Prototype
当前原型:
include/shield/protocol/schema_protocol.hpp
src/protocol/schema_protocol.cpp迁移步骤:
- 保留
schema_protocol.hpp作为 facade。 - 抽出
ProtocolValue到schema/value.hpp或wire/value.hpp。 - 抽出
SchemaRegistry到schema/descriptor_registry.hpp。 - 抽出
PendingRpcRegistry到rpc/pending_call_registry.hpp。 - 替换当前自定义字段编码为 protobuf-compatible payload codec。
- 添加 descriptor package loader。
- 逐步让旧 XML 单文件加载变成测试兼容路径。
不应做的事情
- 不要把 XML parser 链接进 gateway 热路径。
- 不要让 mapper SQL 下发到客户端 descriptor。
- 不要把 client SDK 放进服务端 plugin 系统。
- 不要继续扩大
schema_protocol.hpp单文件。 - 不要让 generated code 成为 runtime 必需依赖。
- 不要让数据库 entity 自动暴露为 client DTO。
已定规则
SchemaStarter独立存在,并在GatewayStarter前启动。- mapper runtime 不进入
shield_core最小启动路径,作为 bundled extension 或 data 模块能力提供。 shield_protoc作为主仓库 target 默认构建。- hash 使用 SHA-256;
compiled_at、source_revision不参与 schema content hash。 service_id、method_id、field_id必须显式;module_id由protocol.lock管理,也允许 manifest 显式指定。schema_protocol.hppfacade 只保留一个 minor release 兼容期;新代码必须迁移到目录化 schema runtime/tooling 入口。- C++ generated namespace 必须由 manifest
codeNamespace显式声明;不从 manifestname自动推导。