#序列化 #反序列化 #ea #数据包 #宏推导 #网络 #blazesdk

tdf

用于从 BlazeSDK 反序列化和序列化 tdf 值的库

7 个版本

0.4.0 2023 年 12 月 22 日
0.3.0 2023 年 12 月 22 日
0.1.4 2023 年 9 月 17 日
0.0.0 2023 年 8 月 17 日

#442 in 解析器实现

Download history 22/week @ 2024-03-11 7/week @ 2024-03-18 4/week @ 2024-03-25 25/week @ 2024-04-01 3/week @ 2024-04-08 9/week @ 2024-04-15 9/week @ 2024-04-22 5/week @ 2024-04-29 4/week @ 2024-05-13 15/week @ 2024-05-20 22/week @ 2024-05-27 20/week @ 2024-06-03 17/week @ 2024-06-10 5/week @ 2024-06-17 11/week @ 2024-06-24

每月 55 次下载
pocket-relay-client-share… 中使用

MIT 许可证

165KB
3K SLoC

Tdf

License Cargo Version Cargo Downloads

用于序列化和反序列化 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