资讯动态

SpacetimeDB Unreal SDK 中的 BSATN 序列化库:零依赖 C++20 实现与实战指南

发布时间:2026/9/13 13:07:11 来源:尧图企业网站定制
SpacetimeDB Unreal SDK 中的 BSATN 序列化库零依赖 C20 实现与实战指南【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDBBSATNBinary SpacetimeDB Algebraic Type Notation是 SpacetimeDB 定义的跨语言二进制序列化格式本文聚焦 SpacetimeDB Unreal SDK 中提供的独立 C 实现。读完本文你将掌握如何在纯 C 客户端中利用SpacetimeDb::bsatn命名空间完成结构体、容器、特殊类型Identity、Timestamp 等的序列化与反序列化并理解其代数类型系统、Option 判别约定与大小计算等底层机制。本文以仓库内文档 BSATN C Library 为主体结合BSATN/Core目录下真实源码展开讲解。BSATN 是什么SpacetimeDB 的跨语言二进制类型记号BSATN 全称 Binary SpacetimeDB Algebraic Type Notation是 SpacetimeDB 项目定义的二进制序列化格式。它不依赖 JSON 或 Protocol Buffers而是直接基于代数数据类型Algebraic Data Type体系将类型本身编码为可传输的元数据再按紧凑的二进制布局写入值数据。该 C 实现的定位非常明确为一个自包含的、与 SpacetimeDB 模块运行时无关的序列化库目标是与 Rust、C# 等其他语言 SDK 保持二进制兼容。从 bsatn.h 的注释可以看到这套实现provides a complete serialization system compatible with Rust and C#提供与 Rust 和 C# 兼容的完整序列化系统覆盖原始类型、容器、用户自定义结构体、和类型sum types以及 SpacetimeDB 特殊类型。所有核心代码都位于sdks/unreal/src/SpacetimeDbSdk/Source/SpacetimeDbSdk/Public/BSATN/Core/目录客户端只需将该目录纳入编译即可无需引入任何 SpacetimeDB 模块依赖。面向 C 客户端需要什么、不需要什么原文档明确了独立使用的边界这里结合源码逐一展开你需要的东西该目录下的全部头文件核心实现位于Core/下标准 C 库且需要支持C20——因为实现大量使用了 C20 concepts 与requires约束见 serialization.h 中的Serializable/Deserializable概念定义以及 writer.h 中的write_primitive_le模板约束零外部依赖源码中的每个头文件只包含其他 BSATN 头文件或标准库头文件。你不需要的东西ITypeRegistrar.h——该接口仅用于 SpacetimeDB 模块侧的类型注册客户端完全不需要任何类型注册type registration功能目录之外的任何文件。关于ITypeRegistrar.h原文档的架构说明解释得很清楚把它保留在目录中是为了避免循环依赖、同时维持干净的架构见 Core/README.md。也就是说模块运行时与客户端共享同一套 BSATN 头文件但客户端只取其中序列化能力这一子集。读者在 Unreal Engine 集成时可直接#include bsatn/bsatn.h使用无需连接任何模块侧注册逻辑。快速开始定义结构体并完成序列化/反序列化原文档给出的最小示例是完整可运行的核心入口是三个要素包含主头文件bsatn/bsatn.h用SPACETIMEDB_STRUCT(MyData, id, name)宏为结构体声明序列化 trait用SpacetimeDb::bsatn::Writer/Reader配合serialize/deserialize完成编解码。#include bsatn/bsatn.h // Define your struct struct MyData { uint32_t id; std::string name; }; // Define serialization traits SPACETIMEDB_STRUCT(MyData, id, name) // Serialize MyData data{42, example}; std::vectoruint8_t buffer; SpacetimeDb::bsatn::Writer writer(buffer); SpacetimeDb::bsatn::serialize(writer, data); // Deserialize SpacetimeDb::bsatn::Reader reader(buffer); auto result SpacetimeDb::bsatn::deserializeMyData(reader);Writer提供两种构造方式无参构造使用内部缓冲随后通过get_buffer()/take_buffer()取出字节或直接绑定外部std::vectoruint8_t见 writer.h。Reader则接受裸指针 长度、std::spanconst uint8_t或std::vectoruint8_t三种形式见 reader.h。更便捷的助手函数除逐字节操作外serialization.h 还提供了三个高层助手适合网络传输或持久化场景// 一次序列化多个值C20 参数包 concepts 约束 SpacetimeDb::bsatn::Writer writer; SpacetimeDb::bsatn::serialize_all(writer, 42, hello, true, 3.14); // 值 → 字节向量 auto bytes SpacetimeDb::bsatn::to_bytes(data); // 字节向量 → 值 auto back SpacetimeDb::bsatn::from_bytesMyData(bytes);其中serialize_all通过折叠表达式(serialize(writer, args), ...)实现并要求每个参数都满足SerializableArgs概念serialization.h类型不满足时会在编译期报错而非运行时崩溃。源码级原理serialize/deserialize 的调度机制SpacetimeDb::bsatn::serialize(writer, value)与deserializeT(reader)并非简单地硬编码每种类型而是基于一套两层的 trait 调度第一层bsatn_traitsT主模板traits.h。主模板利用if constexpr检查类型是否有成员函数bsatn_serialize(Writer)HasMemberSerialize概念是否有静态函数T::bsatn_deserialize(Reader)HasStaticDeserialize概念是否提供algebraic_type_ofT::get()元数据HasAlgebraicType概念。三者都不满足时触发static_assert(sizeof(T) 0, ...)即编译期报错杜绝静默的不兼容序列化。第二层各类特化。基础类型bool、整数、浮点、string的特化集中在 primitive_traits.h它们直接委托给Writer/Reader的对应读写方法。容器与变体std::vectorT、std::optionalT、std::variantTs...的特化则在 traits.h。值得注意的底层事实Writer内部所有多字节整数均按**小端序little-endian**写入如write_u16_le/write_u32_le见 writer.h字符串与字节数组以uint32_t长度前缀 原始数据编码writer.hReader侧有严格的边界检查check_available并在数据不足或 tag 非法时std::abort()reader.h避免越界读取。代数类型系统AlgebraicType 与类型元数据BSATN 的另一半是类型记号本身——即如何描述一个类型的形状。AlgebraicType是一个带标签的联合tagged unionAlgebraicTypeTag枚举从 0 到 19 覆盖了引用、和类型、积类型、数组及全部原始类型algebraic_type.hTag名称含义0Ref对另一个类型的引用1Sum和类型带标签的联合/枚举2Product积类型结构体/元组3Array数组类型4StringUTF-8 字符串5Bool布尔6–17I8…U256各种宽度的有/无符号整数18–19F32/F64浮点数AlgebraicType的DataType是一个std::variant分别存储引用 IDuint32_t、SumTypeSchema、ProductType、ArrayType原始类型则为std::monostatealgebraic_type.h。工厂方法分为两组原始类型用模板工厂AlgebraicType::primitiveAlgebraicTypeTag::I32()并配套Bool()、I32()、String()等便捷别名algebraic_type.h复合类型用make_product、make_sum、Array、Ref、Unit、Option等algebraic_type.h。ProductTypeElement与SumTypeVariant都保存完整的AlgebraicType对象而非类型引用并配有一整套深拷贝构造/赋值实现通过deep_copy_ptr助手algebraic_type.h这消除了以往只存 ID、需要另行查找的歧义。每个 C 类型通过algebraic_type_ofT特化关联到其AlgebraicType基础类型用宏SPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(cpp_type, tag_value)批量生成特化容器类型std::vectorT、std::optionalT则递归取元素/内部类型的类型信息algebraic_type.h。容器与特殊类型Option 判别约定、SumType 与大整数非标准但必须遵守的 Option 判别值SpacetimeDB 的OptionT编码与标准 Rust 枚举约定相反这是最容易踩坑的兼容性细节。源码注释与实现都明确标注了这一点traits.h、writer.hSome(value)判别字节为0而非标准 Rust 的 1None判别字节为1而非标准 Rust 的 0。序列化时write_optional先写 1 字节判别值再写负载反序列化时read_optional读取 tag0 表示有值、1 表示空、其他值直接std::abort()。SumType 与 std::variant和类型类似 Rust 的带数据枚举由SumTypeTs...封装std::variant实现提供tag()当前变体索引、isT()、getT()、get_ifT()、visit()等操作sum_type.h。bsatn_traitsstd::variantTs...将变体索引作为 1 字节 tag 写入负载通过std::visit分派std::monostate变体只写 tag、不写负载traits.h。大整数与特殊类型大整数u128/i128以两个uint64_tlow/high表示并小端写入 16 字节u256/i256以 32 字节数组存储并整体写入types.h并提供任意精度十进制to_string()逐字节长除法实现。Identity32 字节串行化时带__identity__tag以 32 字节小端写入见 types.h 的类型声明与其bsatn_serialize/bsatn_deserialize方法。ConnectionId底层为u128带__connection_id__tag。源码注释特别指出这是从旧版uint64_t修正而来旧实现曾导致运行时序列化崩溃types.h。Timestampint64_t微秒数表示自 Unix 纪元起的时刻带__timestamp_micros_since_unix_epoch__tag提供now()、from_seconds_since_epoch、与std::chrono互转及 ISO 8601 格式to_string()timestamp.h。TimeDurationint64_t微秒数带__time_duration_micros__tag提供from_micros/from_seconds/from_chrono工厂、算术与比较运算符字符串格式与 RustDisplay对齐/-前缀 秒 6 位微秒见 time_duration.h。这些类型都实现bsatn_serialize/bsatn_deserialize成员方法实现位于 types_impl.h从而被bsatn_traits主模板自动拾取。零分配的大小预计算SizeWriter 与 static_bsatn_size对需要预分配缓冲区或估算消息长度的场景库提供了两种机制size_calculator.hSizeWriter实现与Writer相同的接口但只累加字节数、不存储任何数据。例如write_u32_le计 4 字节、write_string计4 s.length()长度前缀 数据、write_u256_le计 32 字节size_calculator.h。由于serialize函数只要求满足 Writer 接口因此可以传入SizeWriter完成一次纯计数的序列化。HasStaticSize概念与static_bsatn_sizeT()在 serialization.h 中定义static_bsatn_sizeT()是consteval函数编译期即求得固定尺寸类型如原始类型的字节大小为零开销优化提供依据。文件结构与架构总结原文档给出了清晰的目录职责划分结合源码可归纳如下目录根sdks/unreal/src/SpacetimeDbSdk/Source/SpacetimeDbSdk/Public/BSATN/Core/类别文件职责核心reader.h、writer.h、serialization.h字节读写、主 serialize/deserialize 入口类型系统algebraic_type.h、traits.h、primitive_traits.h类型元数据、trait 调度、基础类型特化特殊类型types.h、types_impl.h、timestamp.h、time_duration.hIdentity、ConnectionId、大整数、时间类型及其 BSATN 实现工具size_calculator.h、sum_type.h、monostate_traits.h大小计算、和类型封装、单元类型 trait模块专用ITypeRegistrar.h类型注册接口仅模块侧使用客户端可忽略架构上最值得记住的几点无外部依赖所有文件只 include 其他 BSATN 头或标准库头天然可移植模块/客户端解耦ITypeRegistrar保留在本目录但独立于序列化核心客户端不需要也不会意外引入模块依赖向后兼容命名空间SpacetimeDb::bsatn与旧命名空间并存bsatn.h 注明LegacySpacetimeDb::bsatnnamespace is available for backward compatibility宏驱动的结构体支持SPACETIMEDB_STRUCT通过 traits.h 中的ProductTypeBuilder自动构建字段元数据把类型记号和值编码统一起来。在 Unreal Engine 中的集成建议结合 UNREAL_BSATN_ADDITIONS.md 与 FEATURES.md同目录下的补充说明在 UE 项目中使用本库时有几点实践提示头文件位于 UE 模块的Public/BSATN/下可直接被其他 UE 模块引用符合 UE 头文件可见性规范序列化核心不依赖 UE 运行时仅在部分附加头中涉及 UE 辅助类型如UEBSATNHelpers.h因此纯逻辑层、网络层与 UI 层都可以复用同一套 BSATN 编码若需要与服务器模块交换数据务必使用本库内置的Option判别约定Some0 / None1与各特殊类型的固定 tag以保证与 Rust/C# 服务端二进制兼容由于依赖 C20 concepts需确认 Unreal Engine 编译器工具链已开启 C20 标准引擎较新版本默认支持。结语BSATN/Core目录下的这份实现为 Unreal/C 客户端提供了一条通往 SpacetimeDB 二进制协议的直通路径零外部依赖、C20 强类型约束、编译期类型检查、完整的代数类型系统以及与 Rust/C# 对齐的 Option 约定和特殊类型编码。无论是需要对接 SpacetimeDB 服务端还是仅仅希望获得一套跨语言兼容、紧凑高效的序列化方案都可以把SpacetimeDb::bsatn直接纳入自己的 C 工程。【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价