Skip to content

Conversation

@Controllerdestiny
Copy link

@Controllerdestiny Controllerdestiny commented Jan 30, 2026

Summary by Sourcery

更新网络协议实现以匹配协议版本 145/315,弃用旧的消息 ID,新增消息/NetModule 类型和数据包,并使数据包字段和序列化器与最新的 Terraria 服务器/客户端行为保持一致。

新功能:

  • 新增用于旁观玩家、物品位置/消失、箱子容量同步、延迟(ping)、额外生成区域、区域请求、主机令牌处理、死亡细胞展示瓶放置、从 UI 更改队伍、TE 拴绳实体锚点放置、NPC 减益伤害以及物品使用音效等的消息 ID 和数据包类型。
  • 引入新的 NetModule 类型及其实现,用于旗帜、合成请求、标签效果状态以及拴绳实体处理,并包含相应的模型、序列化器和适配器类型。
  • 为世界和玩家相关的数据包扩展新数据,例如多种树背景、蘑菇/地狱背景、额外出生点、坐骑类型、相机目标、语音选项、队伍信息、物品持有者位置以及附加的特效(FX)元数据。

漏洞修复:

  • 修正消息 ID 映射和 NetModule 分发逻辑(例如使用 LoadNetModule 而不是 NetModules,以及更新后的枚举值),以匹配更新后的协议。
  • 修复模型和数据包中的多种字段长度和结束标记(例如 Buff 时间宽度、序列化长度、NPC ID 和玩家槽位),使其与实际的线缆传输格式一致,避免反序列化错误。
  • 调整成就、Buff、渔夫任务、昆虫捕捉/释放、门的使用、NPC 捕捉/释放、传送以及传送门相关的数据包消息 ID,使其对应到协议更新后的正确值。

增强改进:

  • 标记废弃或即将废弃的消息 ID 和数据包,明确其替代方案(如 NetLiquidModuleNetTextModule),并文档化这些被弃用的行为。
  • 为现有数据包补充额外字段(例如队伍信息、城镇 NPC 数量、未知填充/标记),并移除不再适用的未使用接口,从而提升协议的清晰度和未来兼容性。
  • 为复杂数据结构添加自定义序列化器,例如必需的合成材料、快速堆叠箱子数据以及额外出生点数据,并使用紧凑编码方式进行处理。
Original summary in English

Summary by Sourcery

Update the network protocol implementation to match protocol 145/315, deprecating old message IDs, adding new message/NetModule types and packets, and aligning packet fields and serializers with the latest Terraria server/client behavior.

New Features:

  • Add new message IDs and packet types for spectating players, item position/despawn, chest size sync, ping, extra spawn sections, request sections, host token handling, Dead Cells display jar placement, team changes from UI, TE leashed entity anchor placement, NPC debuff damage, and item use sound.
  • Introduce new NetModule types and implementations for banners, crafting requests, tag effect state, and leashed entity handling, including corresponding models, serializers, and adapter types.
  • Extend world and player-related packets with new data such as multiple tree backgrounds, mushroom/underworld backgrounds, extra spawn points, mount type, camera targets, voice options, team info, item owner positions, and additional FX metadata.

Bug Fixes:

  • Correct message ID mappings and NetModule dispatch (e.g. using LoadNetModule instead of NetModules, and updated enum values) to match the updated protocol.
  • Fix various field sizes and end markers in models and packets (e.g. buff time widths, serialization sizes, NPC IDs and player slots) to align with the actual wire format and prevent deserialization errors.
  • Adjust achievement, buff, angler quest, bug catching/releasing, door use, NPC catch/release, teleport, and portal-related packet message IDs to their correct updated protocol equivalents.

Enhancements:

  • Mark obsolete or deprecated message IDs and packets, clarifying their replacements like NetLiquidModule or NetTextModule and documenting deprecated behaviors.
  • Refine existing packets with additional fields (e.g. team, town NPC counts, unknown padding/markers) and remove unused interfaces where no longer applicable, improving protocol clarity and future compatibility.
  • Add custom serializers for complex data structures such as required crafting items, quick stack chest data, and extra spawn point data using compact encodings.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 3 个问题,并给出了一些整体性反馈:

  • QuickStackChestDataSerializer.WriteOverride 在写入槽位 ID 之前同时写了 ItemCountSlotIds.Length,而 ReadOverride 只读取了一个计数值然后按该计数读取若干个 short,因此序列化布局无法往返,需要对齐(要么去掉多余的长度写入,要么在 ReadOverride 中也读取这一长度)。
  • ExtraSpawnPointDataSerializer.WriteOverrideCount 写了两次(一次是 Count 本身,一次是 SpawnPoints.Length),而 ReadOverride 只读了一个计数字节,这会导致格式不一致并使流反序列化错位;需要调整一侧的实现,使双方在字段顺序和数量上完全一致。
  • InternalTypeSerializers.ArraySerializer.Configure 现在会把声明类型和属性名写到控制台,看起来像是遗留的调试日志,在正常使用时会刷屏 stdout;建议移除,或放在一个受调试开关控制的日志里。
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- QuickStackChestDataSerializer.WriteOverride writes both ItemCount and SlotIds.Length before the slot IDs, but ReadOverride only reads a single count and then that many shorts, so the serialized layout does not round-trip and should be aligned (either drop the extra length write or mirror it in ReadOverride).
- ExtraSpawnPointDataSerializer.WriteOverride writes Count twice (once as Count and once as SpawnPoints.Length), while ReadOverride only reads a single count byte, so the format is inconsistent and will desynchronize the stream; adjust one side so they agree on the exact sequence of fields.
- InternalTypeSerializers.ArraySerializer.Configure now writes the declaring type and property name to Console, which looks like leftover debug logging and will spam stdout in normal use; consider removing or gating it behind a debug flag.

## Individual Comments

### Comment 1
<location> `TrProtocol/Serializers/QuickStackChestSerrailizer.cs:23-31` </location>
<code_context>
+            data.ChestSlot = br.ReadInt16();
+            return data;
+        }
+        protected override void WriteOverride(BinaryWriter bw, QuickStackChestData t)
+        {
+            bw.Write(t.ItemCount);
+            bw.Write(t.SlotIds.Length);
+            foreach (short slotId in t.SlotIds)
+            {
+                bw.Write(slotId);
+            }
+            bw.Write(t.ChestSlot);
+        }
+    }
</code_context>

<issue_to_address>
**issue (bug_risk):** QuickStackChestData write format does not match the read format, which will corrupt serialization.

`ReadOverride` expects `[ItemCount:int] [ItemCount shorts for SlotIds] [ChestSlot:short]`, but `WriteOverride` writes `[ItemCount:int] [SlotIds.Length:int] [SlotIds...] [ChestSlot]`. The extra `SlotIds.Length` causes the reader to treat it as the first slot ID and misaligns all subsequent data. Make the formats symmetric by either removing `bw.Write(t.SlotIds.Length);` or updating the reader to consume this length and use it for array allocation.
</issue_to_address>

### Comment 2
<location> `TrProtocol/Packets/Modules/TagEffectStateNetModule.cs:35-39` </location>
<code_context>
+        return module;
+    }
+
+    public readonly void Serialize(BinaryWriter bw)
+    {
+        bw.Write(KillCount);
</code_context>

<issue_to_address>
**issue (bug_risk):** TagEffectFullStateModule.Serialize assumes ProcTimeLeftOnNPCs is non-null, but Deserialize may leave it null.

In `Deserialize`, `ProcTimeLeftOnNPCs` is only created when there’s remaining stream data; otherwise it stays null. `Serialize` then always passes it to `WriteSparseNPCTimeArray`, causing a possible null reference. Either always allocate a 200‑length array in `Deserialize`, or add a null check in `Serialize` (and corresponding write) so missing proc data round‑trips safely.
</issue_to_address>

### Comment 3
<location> `TrProtocol/Serializers/InternalTypeSerializers.cs:91-92` </location>
<code_context>

         public IConfigurable Configure(PropertyInfo prop, string version)
         {
+            Console.WriteLine(prop.DeclaringType);
+            Console.WriteLine(prop.Name);
             if (elementSerializer is IConfigurable conf)
                 conf.Configure(prop, version);
</code_context>

<issue_to_address>
**suggestion (performance):** Console.WriteLine calls in serializer configuration will cause noisy stdout logging in production.

Because `Configure` is called for every property, these `Console.WriteLine` calls will run frequently, cluttering stdout for library consumers and slowing down serialization. If they’re only for debugging, please remove them or switch to a configurable logger guarded by a debug/trace flag.
</issue_to_address>

Sourcery 对开源项目免费 —— 如果你觉得这些 review 有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据这些反馈改进后续的 review。
Original comment in English

Hey - I've found 3 issues, and left some high level feedback:

  • QuickStackChestDataSerializer.WriteOverride writes both ItemCount and SlotIds.Length before the slot IDs, but ReadOverride only reads a single count and then that many shorts, so the serialized layout does not round-trip and should be aligned (either drop the extra length write or mirror it in ReadOverride).
  • ExtraSpawnPointDataSerializer.WriteOverride writes Count twice (once as Count and once as SpawnPoints.Length), while ReadOverride only reads a single count byte, so the format is inconsistent and will desynchronize the stream; adjust one side so they agree on the exact sequence of fields.
  • InternalTypeSerializers.ArraySerializer.Configure now writes the declaring type and property name to Console, which looks like leftover debug logging and will spam stdout in normal use; consider removing or gating it behind a debug flag.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- QuickStackChestDataSerializer.WriteOverride writes both ItemCount and SlotIds.Length before the slot IDs, but ReadOverride only reads a single count and then that many shorts, so the serialized layout does not round-trip and should be aligned (either drop the extra length write or mirror it in ReadOverride).
- ExtraSpawnPointDataSerializer.WriteOverride writes Count twice (once as Count and once as SpawnPoints.Length), while ReadOverride only reads a single count byte, so the format is inconsistent and will desynchronize the stream; adjust one side so they agree on the exact sequence of fields.
- InternalTypeSerializers.ArraySerializer.Configure now writes the declaring type and property name to Console, which looks like leftover debug logging and will spam stdout in normal use; consider removing or gating it behind a debug flag.

## Individual Comments

### Comment 1
<location> `TrProtocol/Serializers/QuickStackChestSerrailizer.cs:23-31` </location>
<code_context>
+            data.ChestSlot = br.ReadInt16();
+            return data;
+        }
+        protected override void WriteOverride(BinaryWriter bw, QuickStackChestData t)
+        {
+            bw.Write(t.ItemCount);
+            bw.Write(t.SlotIds.Length);
+            foreach (short slotId in t.SlotIds)
+            {
+                bw.Write(slotId);
+            }
+            bw.Write(t.ChestSlot);
+        }
+    }
</code_context>

<issue_to_address>
**issue (bug_risk):** QuickStackChestData write format does not match the read format, which will corrupt serialization.

`ReadOverride` expects `[ItemCount:int] [ItemCount shorts for SlotIds] [ChestSlot:short]`, but `WriteOverride` writes `[ItemCount:int] [SlotIds.Length:int] [SlotIds...] [ChestSlot]`. The extra `SlotIds.Length` causes the reader to treat it as the first slot ID and misaligns all subsequent data. Make the formats symmetric by either removing `bw.Write(t.SlotIds.Length);` or updating the reader to consume this length and use it for array allocation.
</issue_to_address>

### Comment 2
<location> `TrProtocol/Packets/Modules/TagEffectStateNetModule.cs:35-39` </location>
<code_context>
+        return module;
+    }
+
+    public readonly void Serialize(BinaryWriter bw)
+    {
+        bw.Write(KillCount);
</code_context>

<issue_to_address>
**issue (bug_risk):** TagEffectFullStateModule.Serialize assumes ProcTimeLeftOnNPCs is non-null, but Deserialize may leave it null.

In `Deserialize`, `ProcTimeLeftOnNPCs` is only created when there’s remaining stream data; otherwise it stays null. `Serialize` then always passes it to `WriteSparseNPCTimeArray`, causing a possible null reference. Either always allocate a 200‑length array in `Deserialize`, or add a null check in `Serialize` (and corresponding write) so missing proc data round‑trips safely.
</issue_to_address>

### Comment 3
<location> `TrProtocol/Serializers/InternalTypeSerializers.cs:91-92` </location>
<code_context>

         public IConfigurable Configure(PropertyInfo prop, string version)
         {
+            Console.WriteLine(prop.DeclaringType);
+            Console.WriteLine(prop.Name);
             if (elementSerializer is IConfigurable conf)
                 conf.Configure(prop, version);
</code_context>

<issue_to_address>
**suggestion (performance):** Console.WriteLine calls in serializer configuration will cause noisy stdout logging in production.

Because `Configure` is called for every property, these `Console.WriteLine` calls will run frequently, cluttering stdout for library consumers and slowing down serialization. If they’re only for debugging, please remove them or switch to a configurable logger guarded by a debug/trace flag.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +23 to +31
protected override void WriteOverride(BinaryWriter bw, QuickStackChestData t)
{
bw.Write(t.ItemCount);
bw.Write(t.SlotIds.Length);
foreach (short slotId in t.SlotIds)
{
bw.Write(slotId);
}
bw.Write(t.ChestSlot);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): QuickStackChestData 的写入格式与读取格式不匹配,会破坏序列化结果。

ReadOverride 期望的格式是 [ItemCount:int] [ItemCount 个 SlotIds 的 short] [ChestSlot:short],但 WriteOverride 写的是 [ItemCount:int] [SlotIds.Length:int] [SlotIds...] [ChestSlot]。多出来的 SlotIds.Length 会被读取端当成第一个槽位 ID,从而导致后续数据全部错位。请让读写格式保持对称:要么移除 bw.Write(t.SlotIds.Length);,要么更新读取逻辑,先读入该长度并据此分配数组。

Original comment in English

issue (bug_risk): QuickStackChestData write format does not match the read format, which will corrupt serialization.

ReadOverride expects [ItemCount:int] [ItemCount shorts for SlotIds] [ChestSlot:short], but WriteOverride writes [ItemCount:int] [SlotIds.Length:int] [SlotIds...] [ChestSlot]. The extra SlotIds.Length causes the reader to treat it as the first slot ID and misaligns all subsequent data. Make the formats symmetric by either removing bw.Write(t.SlotIds.Length); or updating the reader to consume this length and use it for array allocation.

Comment on lines +35 to +39
public readonly void Serialize(BinaryWriter bw)
{
bw.Write(Type);
WriteSparseNPCTimeArray(bw, TimeLeftOnNPCs);
WriteSparseNPCTimeArray(bw, ProcTimeLeftOnNPCs);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): TagEffectFullStateModule.Serialize 假定 ProcTimeLeftOnNPCs 一定非空,但 Deserialize 可能会让它保持为 null。

Deserialize 中,只有当流中还有剩余数据时才会创建 ProcTimeLeftOnNPCs;否则它保持为 null。而 Serialize 总是把它传给 WriteSparseNPCTimeArray,这可能导致空引用异常。可以考虑:要么在 Deserialize 中始终分配一个长度为 200 的数组,要么在 Serialize 中加入空值检查(以及相应的写入逻辑),以便在缺少 proc 数据时仍然能够安全往返序列化。

Original comment in English

issue (bug_risk): TagEffectFullStateModule.Serialize assumes ProcTimeLeftOnNPCs is non-null, but Deserialize may leave it null.

In Deserialize, ProcTimeLeftOnNPCs is only created when there’s remaining stream data; otherwise it stays null. Serialize then always passes it to WriteSparseNPCTimeArray, causing a possible null reference. Either always allocate a 200‑length array in Deserialize, or add a null check in Serialize (and corresponding write) so missing proc data round‑trips safely.

Comment on lines +91 to +92
Console.WriteLine(prop.DeclaringType);
Console.WriteLine(prop.Name);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): 在序列化器配置中调用 Console.WriteLine 会在生产环境产生大量 stdout 噪声。

由于 Configure 会对每个属性调用,这些 Console.WriteLine 会被频繁执行,不但会为库的使用者制造很多 stdout 日志,还会拖慢序列化过程。如果这些输出只是为了调试,建议将其移除,或改用受调试/追踪开关控制的可配置日志系统。

Original comment in English

suggestion (performance): Console.WriteLine calls in serializer configuration will cause noisy stdout logging in production.

Because Configure is called for every property, these Console.WriteLine calls will run frequently, cluttering stdout for library consumers and slowing down serialization. If they’re only for debugging, please remove them or switch to a configurable logger guarded by a debug/trace flag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants