4个版本 (2个重大更新)

0.2.0 2023年12月22日
0.1.1 2023年9月14日
0.1.0 2023年9月6日
0.0.0 2023年8月17日

#13 in #ea

每月28次下载
用于 tdf

MIT 许可证

36KB
564

Tdf

License Cargo Version Cargo Downloads

用于序列化和反序列化BlazeSDK(Heat1)使用的tdf网络格式,该格式存在于诸如《质量效应3》、《质量效应:安达鲁斯》、《战地3》等游戏中(许多EA游戏)的库。

这是我对之前的库blazepk的重新编写。这个版本提供了更精确的命名和不同结构支持,包括推导宏实现。此实现还将路由和报文封装分开,因为不同的版本使用不同的封装实现(Fire和Fire2)

Cargo

使用Cargo中的tdf

[dependencies]
tdf = "0.1"

cargo add tdf

存储库功能

默认功能是 ["serde", "derive"]

功能 描述
serde 为tdf类型添加了支持serde序列化
bytes 为从bytes crate中的BytesMut类型直接序列化添加了支持
derive 为使用tdf-derive推导宏来推导结构和枚举添加了支持

推导宏

Tdf包含功能标志derive(默认启用),允许自动推导反序列化和序列化实现。您可以使用以下3个推导宏:TdfDeserialize、TdfSerialize和TdfTyped

  • TdfDeserialize - 允许结构从tdf字节反序列化
  • TdfSerialize - 允许结构序列化到tdf字节
  • TdfTyped 为结构推导TdfType,以便它可以作为标记值使用
    • 这仅支持组、表示枚举和标记枚举

推导结构

在创建结构体时,例如用作数据包的内容,应使用本节。如果您想将结构体作为组使用(若要标记为值,结构体必须是组),请参阅派生组

以下是一个简单结构体的示例

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)来处理和序列化,因此它们内部的值都有与 Deriving Groups 相同的条件,并且从 Deriving Structures 中继承的大部分信息也适用。

具有未命名字段的变体(您只能指定一个未命名字段,如 ExampleTaggedEnum::Test2 所示)的值将按照提供的值的类型进行序列化,在这种情况下,Test2 将有一个标记为 VALU 的值,其类型为字符串。

默认变体

当使用上述枚举时,如果在运行时反序列化不在枚举中的辨别符,您将遇到 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

依赖项

~0.6–1MB
~25K SLoC