7 个版本
0.4.0 | 2023 年 12 月 22 日 |
---|---|
0.3.0 |
|
0.1.4 | 2023 年 9 月 17 日 |
0.0.0 | 2023 年 8 月 17 日 |
#442 in 解析器实现
每月 55 次下载
在 pocket-relay-client-share… 中使用
165KB
3K SLoC
Tdf
用于序列化和反序列化 BlazeSDK(Heat1)使用的 tdf 网络格式,该格式存在于《质量效应 3》、《质量效应:安达露西亚》、《战地 3》等游戏中(许多 EA 游戏)的库。
这是对之前库 blazepk 的重写。这个版本提供了更精确的命名和对不同结构的支持,以及 derive 宏的实现。此实现还将路由和数据包封装分开,因为不同的版本使用不同的封装实现(Fire 和 Fire2)。
Cargo
使用 cargo 使用 tdf
[dependencies]
tdf = "0.1"
或
cargo add tdf
软件包功能
默认功能是 ["serde", "derive"]
功能 | 描述 |
---|---|
serde | 为 tdf 类型添加了 serde 序列化支持 |
bytes | 为 bytes 软件包中的 BytesMut 类型添加了直接序列化的支持 |
derive | 为使用 tdf-derive derive 宏推导结构体和枚举添加了支持 |
推导宏
Tdf 包含功能标志 derive
(默认启用),允许自动推导反序列化和序列化实现。您可以使用以下 3 个推导宏:TdfDeserialize、TdfSerialize 和 TdfTyped
- TdfDeserialize - 允许结构体从 tdf 字节反序列化
- TdfSerialize - 允许结构体将数据序列化到 tdf 字节
- TdfTyped 为结构体推导 TdfType,以便它可以用作标签值
- 这仅支持 Group、Repr 枚举和 Tagged 枚举
导出结构
当创建结构,例如用作数据包的内容时,应使用本节。如果您想将结构作为一组(如果您想将结构标记为值,结构必须是组)请参阅导出组。
以下是一个包含简单结构的示例
use tdf::prelude::*;
#[derive(TdfSerialize, TdfDeserialize)]
pub struct ExampleStructure {
#[tdf(tag = "TEST")]
pub my_value: u32
}
生存期
您可以为TdfDeserialize结构提供生存期(生存期可以是任何您喜欢的,不必是'de')
生存期可以用于字符串和Blob等值,以防止需要复制底层数据,而是直接从反序列化缓冲区借用
use tdf::prelude::*;
#[derive(TdfSerialize, TdfDeserialize)]
pub struct ExampleStructure<'de> {
#[tdf(tag = "TEST")]
pub my_value: &'de str
}
警告 TdfDeserialize结构不能有超过一个生存期,超过一个生存期会导致编译时错误。但是,如果结构仅实现了TdfSerialize,您可以有任意多的生存期
泛型
只要在结构上直接指定where
子句,您也可以为您的结构提供泛型参数
use tdf::prelude::*;
#[derive(TdfSerialize)]
pub struct ExampleStructure<T>
where T: TdfSerialize + TdfTyped
{
#[tdf(tag = "TEST")]
pub my_value: T
}
注意 您想用作标记的任何泛型类型都必须实现
TdfTyped
特质
当使用TdfDeserialize泛型类型时,您必须指定生存期,就像在生存期示例中那样,如果您的结构没有使用反序列化生存期,您将得到一个错误,您可以通过使用PhantomData
并将其标记为跳过来解决此问题(参见跳过字段)
use tdf::prelude::*;
use std::marker::PhantomData;
#[derive(TdfSerialize, TdfDeserialize)]
pub struct ExampleStructure<'de, T>
where T: TdfSerialize + TdfDeserialize<'de> + TdfTyped
{
#[tdf(tag = "TEST")]
pub my_value: T,
#[tdf(skip)]
pub _marker: PhantomData<&'de T>,
}
跳过字段
您可以使用#[tdf(skip)]
来告诉宏跳过任何您不想包含在序列化中的字段
use tdf::prelude::*;
#[derive(TdfSerialize, TdfDeserialize)]
pub struct ExampleStructure {
#[tdf(tag = "TEST")]
pub my_value: u32,
#[tdf(skip)]
pub skip_me: u32
}
带有命名字段的标记枚举变体也可以跳过字段
注意 如果您正在导出
TdfDeserialize
,任何跳过的类型都必须实现Default
,因为反序列化器将使用该类型的Default::default
实现来填充结构
导出组
为了将结构序列化为可以标记的组值,它们必须具有特殊的前导和尾随数据,如果您的结构上指定了#[tdf(group)]
,则创建这些数据
use tdf::prelude::*;
#[derive(TdfSerialize, TdfDeserialize, TdfTyped)]
#[tdf(group)]
pub struct ExampleStructure {
#[tdf(tag = "TEST")]
pub my_value: u32
}
一旦将结构标记为组,就可以导出TdfTyped
并将其用作标记中的组
对于需要(2)前缀的结构,您可以在结构上使用#[tdf(prefix_two)]
属性
use tdf::prelude::*;
#[derive(TdfSerialize, TdfDeserialize, TdfTyped)]
#[tdf(group, prefix_two)]
pub struct ExampleStructure {
#[tdf(tag = "TEST")]
pub my_value: u32
}
警告 当结构被标记为组结构时,您不能将其用作数据包主体,因为它包含表示该结构的额外字节,这可能在其他工具/客户端尝试解析时导致错误
表示枚举
如果您想定义一个枚举,它的值映射到现有的基本类型之一(u8、i8、u16、i16、u32、i32、u64、i64、usize、isize),您可以定义一个repr枚举
注意 Repr 枚举被序列化为变长整数,即 TdfType::VarInt
注意 实现 TdfSerialize 的 Repr 枚举也必须实现 Clone + Copy
use tdf::prelude::*;
#[derive(Clone, Copy, TdfSerialize, TdfDeserialize, TdfTyped)]
#[repr(u8)]
pub enum ExampleReprEnum {
Test = 0x0,
Test2 = 0x1,
Test3 = 0x3
}
别忘了从 TdfTyped
派生以在标签中使用此值
注意 定义 repr 枚举时,您必须使用
#[repr(%TYPE%)]
,否则类型将无法识别
警告 与标准 repr 枚举不同,您不能省略每个值的区分符,因为在这个阶段,宏无法推断下一个值。
默认变体
当使用上述枚举时,如果遇到一个在枚举中不存在的区分符,则在运行时会遇到 DecodeError
。为了防止这种行为,您可以指定一个默认变体
use tdf::prelude::*;
#[derive(Clone, Copy, TdfSerialize, TdfDeserialize, TdfTyped)]
#[repr(u8)]
pub enum ExampleReprEnum {
Test = 0x0,
Test2 = 0x1,
Test3 = 0x3,
#[tdf(default)]
MyDefault = 0x4,
}
标记枚举
您可以使用带有字段的 Rust 枚举来表示 BlazeSDK 标记联合
use tdf::prelude::*;
#[derive(TdfSerialize, TdfDeserialize, TdfTyped)]
pub enum ExampleTaggedEnum {
#[tdf(key = 0x0, tag = "VALU")]
Test {
#[tdf(tag = "TEST")]
test: String,
},
#[tdf(key = 0x1, tag = "VALU")]
Test2(String)
}
在上面的例子中,每个枚举变体的 "key" 属性是该变体使用的区分符。每个枚举变体的 "tag" 属性是当标记联合的值时使用的标记
在上面的例子中,有一个具有命名字段的变体和一个只有一个未命名字段的变体。
具有命名字段的变体被处理和序列化为组(TdfType::Group),因此它们内部的所有值都具有与 派生组 相同的条件,并且大多数信息也来自 派生结构。
具有未命名字段的变体(您只能指定一个未命名字段,如 ExampleTaggedEnum::Test2
所示)的值被序列化为提供的值的类型,在这种情况下,Test2 将有一个标记为 VALU 的类型为字符串的值。
默认变体
当使用上述枚举,就像在 repr 例子中一样,如果在运行时反序列化了一个不在枚举中出现的区分符,您将遇到 DecodeError
。为了防止这种行为,您可以指定一个默认变体
use tdf::prelude::*;
#[derive(TdfSerialize, TdfDeserialize, TdfTyped)]
pub enum ExampleTaggedEnum {
#[tdf(key = 0x0, tag = "VALU")]
Test {
#[tdf(tag = "TEST")]
test: String,
},
#[tdf(key = 0x1, tag = "VALU")]
Test2(String),
#[tdf(default)]
DefaultValue
}
对于实现 TdfSerialize 的标记枚举,如果序列化了默认值,它将使用
unset
区分符
未设置变体
Tdf 标记联合有一个特殊的未设置类型区分符,因此如果反序列化了一个未设置的区分符,则在运行时会遇到 DecodeError
。为了防止这种行为,您可以指定一个未设置变体
use tdf::prelude::*;
#[derive(TdfSerialize, TdfDeserialize, TdfTyped)]
pub enum ExampleTaggedEnum {
#[tdf(key = 0x0, tag = "VALU")]
Test {
#[tdf(tag = "TEST")]
test: String,
},
#[tdf(key = 0x1, tag = "VALU")]
Test2(String),
#[tdf(unset)]
Unset
}
注意 您不能将默认变体用作未设置变体,它们必须是分开的,因为默认变体处理可能存在的额外值,这会导致失败
实现 TdfSerialize
请参阅 docs.rs/tdf
实现 TdfDeserialize
请参阅 docs.rs/tdf
依赖关系
~110–450KB