#silkroad #serde #serialization #packet #depending #online #list

skrillax-serde

用于Silkroad Online数据包的序列化和反序列化库

2个版本

0.1.1 2024年4月28日
0.1.0 2024年4月21日

#590编码


3 个crates 使用

MIT 许可证

20KB
279

skrillax-serde

Crates.io Docs.rs

serde 是一个知名且受尊敬的Rust序列化和反序列化框架。虽然我们 可以 使其在我们的上下文中工作,但不幸的是,Silkroad在几个方面有点让人烦恼,这使得使用 serde 有点繁琐。因此,这为Silkroad上下文中的序列化和反序列化提供了一种更专注的方法。

Silkroad做了一些事情,使得一般地序列化数据包变得困难

  • 它们不是自描述的 - 虽然这是可以预料的
  • 列表可以以不同的方式表示,具体取决于特定的操作
  • 属性可能会根据先前值出现或隐藏
  • 属性可能会根据外部信息出现或隐藏

例如,Silkroad中有三种列表表示方式:基于长度的、基于断点的或基于续集的。第一种非常明显;前面加上长度,然后所有元素都跟随,没有任何分隔符。"基于断点的"和"基于续集的"在元素之间有分隔符,但最后一个元素的分隔符不同。两者都使用 1 作为分隔符,但分别使用 20 作为结束元素。为什么你需要多种方式来编码值列表?我不知道。其他奇怪之处包括一些字符串使用宽字符(2字节)编码,或者内容完全取决于它属于哪种类型的项。

使用serde来实现这一点并不困难,实际上,实现很简单,但使用起来会更累,因为你必须不断查找 deserialize_with 并记住确切的功能。特别是当你必须序列化/反序列化一个依赖于先前字段的字段时,你会遇到困难(例如:一个 ChatMessage 数据包有一个可选字段,该字段取决于已经编码在先前的、非相邻字段中的消息类型)。为了使工作更容易,并使这些怪癖“可实现”,存在这个定制的 'serde'。

Silkroad操作序列化/反序列化

大部分的序列化/反序列化逻辑都实现在 -derive 库中。这个库只包含原始值的序列化和反序列化定义,例如时间、u8 等类似类型。然而,我想解释一下在 Silkroad 世界中数据序列化的基本原理。

让我们从基础开始:Silkroad 以小端字节序(little endian byte order)编码数据(大部分情况下),所以一个长度为 256 的数据包(这是一个 u16),将显示为 0x00 0x01,而不是 0x01 0x00。数据字段之间没有分隔符,所以值直接相邻。如果你有三个不同大小的字段,序列化的大小将始终等于这些字段在内存中占用的总空间。固定长度的数组也只编码它包含的数据(元素的数量是已知的,每个元素的大小也是已知的)。可变长度的列表(在 Rust 术语中称为 Vec)有三种不同的编码方式。要么长度前缀,通常为 u8 的大小,但也可以是 u16,然后所有元素都跟在后面,没有进一步的分隔符。或者,使用一个 u8 来表示是否还有更多(值为 1),然后是下一个元素,或者它是结束(值为 02),这取决于它被用于何处。类似于枚举的元素(例如,错误代码或通常,某物的不同变体)通常包含一个表示“变体 ID”的 u8,后面跟该变体的具体字段。因此,不包含任何数据的变体将只包含变体 ID 而没有其他内容。最后,还有可选字段,它们包含一个指定数据是否存在(值为 1)或不存在(值为 0)的 u8。其他所有内容都使用之前定义的原子(atoms)的任何组合。

依赖关系

~1.4–2.3MB
~41K SLoC