Skip to content

Commit 981a996

Browse files
authored
bugfix: Fixed and tested all structured components and issues with Empty packets sent by some servers
bugfix: Fixed and tested all structured components and issues with Empty packets sent by some servers
2 parents 02b34a8 + 98dfde6 commit 981a996

27 files changed

Lines changed: 816 additions & 260 deletions

.skills/mcc-integration-testing/SKILL.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,31 @@ Use this for TPS, movement-cadence, or packet-cadence work:
115115

116116
Run them against a real server with a temp config and summarize counts from the captured logs.
117117

118-
### 4. Full inventory regression sweep
118+
### 4. Structured components test
119+
120+
Use this after touching any `StructuredComponents` code (registries, component
121+
parsers, subcomponents, codec helpers) to prove every component type in a
122+
version parses on the wire without error:
123+
124+
```bash
125+
bash tools/run-structured-components-test.sh 1.21.11
126+
```
127+
128+
Run a single version (fast, ~2 min) or a matrix:
129+
130+
```bash
131+
for v in 1.20.6 1.21 1.21.2 1.21.5 1.21.11 26.1; do
132+
bash tools/run-structured-components-test.sh "$v"
133+
done
134+
```
135+
136+
The script gives items with every registered component via RCON `/give`, reads
137+
them back with `inventory player list`, and asserts no parse errors in the MCC
138+
log. Version-gated components (v1212+, v1215+, v12111+, v261) are tested only
139+
on the versions that support them. See `SC_Integration_Test_Report.md` for a
140+
reference run across all 6 version groups.
141+
142+
### 5. Full inventory regression sweep
119143

120144
Use this when touching inventory snapshots, player/container slot sync, creative inventory, item-slot serialization, packet palettes, game-mode updates, or block-use paths that open containers:
121145

@@ -198,6 +222,8 @@ Optionally override the login name with the fourth argument to the config helper
198222
- ordered creative-mode E2E regression scenario
199223
- `tools/run-inventory-full-sweep.sh`
200224
- full inventory command/API sweep across one or more versions
225+
- `tools/run-structured-components-test.sh`
226+
- exercises every structured component via RCON `/give` across versions 1.20.6-26.1
201227

202228
## Evidence Discipline
203229

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,4 @@ Read `docs/guide/ai-assisted-development.md` before starting development work on
123123
- Don't trust older docs over current code for supported versions or feature gates. When AGENTS.md, skills, and older docs disagree, prefer current code and current tool behavior, then update the stale source.
124124
- Don't hardcode user-facing strings (messages, labels, help text) directly in source code; always use `Translations.*` resources so the text can be localized.
125125
- Never use "—" ("em dash"), unless specifically being instructed to do so!
126+
- Never generate MCC config files (`.ini`) in the repo root. When `dotnet run --project MinecraftClient -- --help` is used to generate a config template, it writes `MinecraftClient.ini` to the current directory. Always run this command from a system temp directory (e.g. `mktemp -d`) or use `prepare_offline_mcc_config.sh` which already handles output routing.

MinecraftClient/Inventory/BookContent.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ private static bool TryReadWritable(Item item, out BookContent content)
100100
{
101101
if (item.Components is not null)
102102
{
103-
var component = item.Components.OfType<WritableBlookContentComponent>().FirstOrDefault();
103+
var component = item.Components.OfType<WritableBookContentComponent>().FirstOrDefault();
104104
if (component is not null)
105105
{
106106
content = new BookContent(
@@ -121,7 +121,7 @@ private static bool TryReadWritten(Item item, out BookContent content)
121121
{
122122
if (item.Components is not null)
123123
{
124-
var component = item.Components.OfType<WrittenBlookContentComponent>().FirstOrDefault();
124+
var component = item.Components.OfType<WrittenBookContentComponent>().FirstOrDefault();
125125
if (component is not null)
126126
{
127127
content = new BookContent(

MinecraftClient/Protocol/Handlers/Protocol18.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,10 @@ internal void PacketReader(object? o)
362362
{
363363
while (socketWrapper.HasDataAvailable())
364364
{
365-
packetQueue.Add(ReadNextPacket(), cancelToken);
365+
var packet = ReadNextPacket();
366+
if (packet.Item1 == -1)
367+
continue;
368+
packetQueue.Add(packet, cancelToken);
366369

367370
if (cancelToken.IsCancellationRequested)
368371
break;
@@ -418,7 +421,8 @@ internal void PacketReader(object? o)
418421
internal Tuple<int, Queue<byte>> ReadNextPacket()
419422
{
420423
var size = dataTypes.ReadNextVarIntRAW(socketWrapper); //Packet size
421-
Queue<byte> packetData = new(socketWrapper.ReadDataRAW(size)); //Packet contents
424+
var rawBytes = socketWrapper.ReadDataRAW(size);
425+
Queue<byte> packetData = new(rawBytes); //Packet contents
422426
var compressed = false;
423427
var sizeUncompressed = 0;
424428

@@ -436,6 +440,13 @@ internal Tuple<int, Queue<byte>> ReadNextPacket()
436440
}
437441
}
438442

443+
if (packetData.Count == 0)
444+
{
445+
var rawHex = rawBytes.Length > 0 ? BitConverter.ToString(rawBytes).Replace("-", " ") : "(empty)";
446+
log.Debug("Empty packet after decompress: size={0}, sizeUncompressed={1}, protocol={2}, state={3}, rawBytes=[{4}]", size, sizeUncompressed, protocolVersion, currentState, rawHex);
447+
return new(-1, packetData);
448+
}
449+
439450
var packetId = dataTypes.ReadNextVarInt(packetData); // Packet ID
440451
LogIncomingPacket(packetId, packetData.Count, size, compressed, sizeUncompressed);
441452
if (handler.GetNetworkPacketCaptureEnabled())
@@ -4147,12 +4158,21 @@ private void SetCurrentState(CurrentState newState)
41474158
log.PacketDebug(string.Format(Translations.debug_packet_state_change, previousState, newState));
41484159
}
41494160

4161+
private static bool IsPacketExcluded(string packetType)
4162+
{
4163+
var exclusions = Settings.Config.Logging.PacketDebugExclusions;
4164+
return exclusions.Count > 0 && exclusions.Contains(packetType, StringComparer.OrdinalIgnoreCase);
4165+
}
4166+
41504167
private void LogIncomingPacket(int packetId, int payloadLength, int frameLength, bool compressed, int uncompressedLength)
41514168
{
41524169
if (!log.DebugEnabled)
41534170
return;
41544171

41554172
var packetType = ResolveIncomingPacketType(packetId);
4173+
if (IsPacketExcluded(packetType))
4174+
return;
4175+
41564176
var compressionInfo = compression_treshold < 0
41574177
? Translations.debug_packet_compression_disabled
41584178
: compressed
@@ -4173,10 +4193,14 @@ private void LogOutgoingPacket(int packetId, int payloadLength, string? packetTy
41734193
if (!log.DebugEnabled)
41744194
return;
41754195

4196+
var resolvedType = packetType ?? ResolveOutgoingPacketType(packetId);
4197+
if (IsPacketExcluded(resolvedType))
4198+
return;
4199+
41764200
log.PacketDebug(string.Format(Translations.debug_packet_outgoing,
41774201
currentState,
41784202
packetId,
4179-
packetType ?? ResolveOutgoingPacketType(packetId),
4203+
resolvedType,
41804204
payloadLength,
41814205
compression_treshold));
41824206
}

MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FoodComponentComponent.cs renamed to MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/FoodComponent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
99

10-
public class FoodComponentComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
10+
public class FoodComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
1111
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
1212
{
1313
public int Nutrition { get; set; }

MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/IntangibleProjectileComponent.cs

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,6 @@
55
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
66

77
public class IntangibleProjectileComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
8-
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
8+
: EmptyComponent(dataTypes, itemPalette, subComponentRegistry)
99
{
10-
public Dictionary<string, object>? Nbt { get; set; } = new();
11-
12-
public override void Parse(Queue<byte> data)
13-
{
14-
Nbt = DataTypes.ReadNextNbt(data);
15-
}
16-
17-
public override Queue<byte> Serialize()
18-
{
19-
var data = new List<byte>();
20-
data.AddRange(DataTypes.GetNbt(Nbt));
21-
return new Queue<byte>(data);
22-
}
23-
}
10+
}

MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/OmniousBottleAmplifierComponent.cs renamed to MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/OminousBottleAmplifierComponent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
66

7-
public class OmniousBottleAmplifierComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
7+
public class OminousBottleAmplifierComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
88
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
99
{
1010
public int Amplifier { get; set; }

MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/UnbreakableComponent.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@
44

55
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
66

7-
public class UnbrekableComponent1206(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
7+
public class UnbreakableComponent1206(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
88
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
99
{
10-
public bool Unbrekable { get; set; }
10+
public bool Unbreakable { get; set; }
1111

1212
public override void Parse(Queue<byte> data)
1313
{
14-
Unbrekable = DataTypes.ReadNextBool(data);
14+
Unbreakable = DataTypes.ReadNextBool(data);
1515
}
1616

1717
public override Queue<byte> Serialize()
1818
{
1919
var data = new List<byte>();
20-
data.AddRange(DataTypes.GetBool(Unbrekable));
20+
data.AddRange(DataTypes.GetBool(Unbreakable));
2121
return new Queue<byte>(data);
2222
}
2323
}

MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/WritableBlookContentComponent.cs renamed to MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/WritableBookContentComponent.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
88

9-
public class WritableBlookContentComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
9+
public class WritableBookContentComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
1010
{
1111
public List<BookPage> Pages { get; set; } = [];
1212

@@ -41,7 +41,7 @@ public override Queue<byte> Serialize()
4141
if (page.HasFilteredContent)
4242
{
4343
if (page.FilteredContent is null)
44-
throw new InvalidOperationException("Can not serialize WritableBlookContentComponent because page.HasFilteredContent = true, but FilteredContent is null!");
44+
throw new InvalidOperationException("Can not serialize WritableBookContentComponent because page.HasFilteredContent = true, but FilteredContent is null!");
4545

4646
data.AddRange(DataTypes.GetString(page.FilteredContent));
4747
}

MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/WrittenBlookContentComponent.cs renamed to MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/WrittenBookContentComponent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
99

10-
public class WrittenBlookContentComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
10+
public class WrittenBookContentComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
1111
{
1212
public string RawTitle { get; set; } = null!;
1313
public bool HasFilteredTitle { get; set; }

0 commit comments

Comments
 (0)