Skip to content

fix: empty SpecialSeparator deserializer crash + widen CModePacket.MorphSkin#446

Merged
erwan-joly merged 5 commits intomasterfrom
fix/deserializer-empty-separator-and-cmode
Apr 24, 2026
Merged

fix: empty SpecialSeparator deserializer crash + widen CModePacket.MorphSkin#446
erwan-joly merged 5 commits intomasterfrom
fix/deserializer-empty-separator-and-cmode

Conversation

@erwan-joly
Copy link
Copy Markdown
Contributor

@erwan-joly erwan-joly commented Apr 24, 2026

Bumps to 17.0.0 — breaking because of the `CModePacket.MorphSkin` field type change.

Deserializer — empty `SpecialSeparator` (non-breaking bug fix)

Four declarations use `[PacketIndex(n, SpecialSeparator = "")]` around a `UpgradeRareSubPacket`:

  • `EqPacket.WeaponUpgradeRarePacket` / `ArmorUpgradeRarePacket`
  • `EquipPacket.WeaponUpgradeRareSubPacket` / `ArmorUpgradeRareSubPacket`
  • `InCharacterSubPacket.WeaponUpgradeRareSubPacket` / `ArmorUpgradeRareSubPacket`

The serializer handles `SpecialSeparator = ""` correctly: it joins the sub-packet's fields with no separator (e.g. `Upgrade=7, Rare=5` → `"75"` on the wire). The deserializer was reaching `matches[currentIndex].Replace("", " ")`, which .NET rejects with `ArgumentException: The value cannot be an empty string`. Any incoming `equip` / `eq` / `in` with an upgrade-rare field would blow up.

Fix branches on empty separator and inserts a space between every character via `string.Join(' ', chars)`. Existing separators (`.`, `|`, `^`) are untouched.

Regression test added: round-trips `equip 75 35` and asserts `Upgrade=7, Rare=5` / `Upgrade=3, Rare=5`.

`CModePacket.MorphSkin` byte → short (BREAKING)

Real captures consistently carry 4-digit values (e.g. `7583`, `8151`, `8558`, `9471`) in the last field, which overflows `byte`. Widening to `short` matches the observed range and stops the "Value was either too large or too small for an unsigned byte" failures. Source-level breaking — consumers pinning the property type to `byte` stop compiling; at runtime the wider type is always safe.

Test plan

  • `dotnet test` — 109 / 109 pass (including the new regression test)

Follow-ups

The packet validator surfaced more schema bugs that are not in this PR:

  • Direction-pair packets: `gidx` server-side, `mall` client-side, `npinfo` client-side, `rsfi` server-side, `bpm` / `bpt` server-side, `NsTeST` client-side (wire formats differ from existing direction, need RE work).
  • `msgi` / `sayi` / `sayi2` / `sayitemt` — optional-field / enum-parse issues.
  • `fish` / `qstlist` / `inv` / `qslot` — list-of-sub-packet structure bugs.
  • `clinit` / `flinit` / `kdlinit` / `skyinit` — a separate Deserializer bug in the list-of-sub-packets path (`Deserializer.cs`:347-372): each item's field-split loop looks up `packSeperators[index+1].SpecialSeparator` but the sub-packet's properties don't carry per-property separators, so the whole item string is appended N times.
  • `qnamli` — packet class doesn't exist.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed nested packet deserialization when special separators are empty.
  • Data/Protocol Changes

    • Expanded player morph-skin value range.
    • Added/renamed fields to improve item/character visibility, drop quest flag, team membership and level XP handling.
    • Extended inventory/item sub-packet fields and tightened element nullability.
  • Tests

    • Added deserialization and serializer tests for empty-separator and inventory scenarios.
  • Chores

    • Bumped package version.

**Deserializer — empty SpecialSeparator (non-breaking bug fix)**

Four packet declarations use [PacketIndex(n, SpecialSeparator = "")]
around an UpgradeRareSubPacket: EqPacket.WeaponUpgradeRarePacket /
ArmorUpgradeRarePacket, EquipPacket.WeaponUpgradeRareSubPacket /
ArmorUpgradeRareSubPacket, and InCharacterSubPacket.{Weapon,Armor}
UpgradeRareSubPacket. The serializer handles this correctly (joins
the sub-packet's two fields with no separator — e.g. Upgrade=7,
Rare=5 → "75" on the wire). The deserializer was doing
`matches[currentIndex].Replace("", " ")`, which .NET rejects with
ArgumentException. So every equip / eq / in packet with an
upgrade-rare field failed to parse.

Fix: branch on empty SpecialSeparator and insert a space between
every character via `string.Join(' ', chars)`. Existing separators
(".", "|", etc.) are unchanged.

**CModePacket.MorphSkin byte → short (BREAKING)**

Real captures consistently carry values of 7583/8151/8558/9471/etc.
in the last field, overflowing byte. Widening to short matches the
observed range. Source-level breaking because consumers pinning the
property type to byte stop compiling; runtime conversions widen safely.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

Warning

Rate limit exceeded

@erwan-joly has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 54 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 18 minutes and 54 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 91ccb5bb-dcaa-49b7-98a3-1c1bcb435dc1

📥 Commits

Reviewing files that changed from the base of the PR and between e42dde1 and 35adab4.

📒 Files selected for processing (1)
  • src/NosCore.Packets/NosCore.Packets.csproj

Walkthrough

Fixes IPacket deserialization when the special separator is empty by splitting matched tokens into single-character fields; package version bumped to 16.11.0; multiple packet field types and nullability adjusted; two fields added to IvnSubPacket; tests added/updated for serializer/deserializer behaviors.

Changes

Cohort / File(s) Summary
Deserializer Logic
src/NosCore.Packets/Deserializer.cs
When SpecialSeparator == "" reconstruct field boundaries by inserting spaces between every character of the matched token (avoids calling string.Replace with empty oldValue), then recursively deserialize.
Project Metadata
src/NosCore.Packets/NosCore.Packets.csproj
NuGet <Version> updated 16.10.016.11.0.
Player Packet
src/NosCore.Packets/ServerPackets/Player/CModePacket.cs
Field type change: MorphSkin at [PacketIndex(7)] changed from byteshort.
Inventory / Stash Nullability
src/NosCore.Packets/ServerPackets/Inventory/InvPacket.cs, src/NosCore.Packets/ServerPackets/Inventory/IvnPacket.cs, src/NosCore.Packets/ServerPackets/Warehouse/*Stash*Packet.cs
Tightened nullability: IvnSubPackets element type changed from IvnSubPacket?IvnSubPacket (some properties also adjusted for outer list nullability).
IvnSubPacket Fields
src/NosCore.Packets/ServerPackets/Inventory/IvnSubPacket.cs
Added two serialized properties: Unknown5 (byte, [PacketIndex(5)]) and Unknown6 (short, [PacketIndex(6)]).
Entity / Mates Packet Adjustments
src/NosCore.Packets/ServerPackets/Entities/DropPacket.cs, src/NosCore.Packets/ServerPackets/Mates/ScnPacket.cs
Type/semantic changes: DropPacket index 5 byte Unknownbool IsQuest; ScnPacket index 34 int Unknown21bool IsTeamMember, index 35 int Unknown22int LevelXp.
Visibility Packet Field Rework
src/NosCore.Packets/ServerPackets/Visibility/InCharacterSubPacket.cs, src/NosCore.Packets/ServerPackets/Visibility/InItemSubPacket.cs
Reordered/renamed fields and replaced several unknowns with named booleans/bytes; swapped Owner and Unknown indices in InItemSubPacket.
Deserializer Tests
test/NosCore.Packets.Tests/DeserializerTest.cs
Added PacketWithEmptySpecialSeparatorSplitsEachCharIntoField() to validate deserialization when special separator is empty and nested sub-packets parsed per-character.
Serializer Tests
test/NosCore.Packets.Tests/SerializerTest.cs
Registered IvnPacket in serializer config; updated tests to use non-nullable List<IvnSubPacket> and added/adjusted tests asserting specific IvnPacket/InPacket wire formats and equipment pocket behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the two main changes: fixing a deserializer crash with empty SpecialSeparator and widening CModePacket.MorphSkin from byte to short.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/deserializer-empty-separator-and-cmode

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/NosCore.Packets.Tests/DeserializerTest.cs (1)

420-441: Add a targeted c_mode regression test for MorphSkin > 255.

Nice coverage for the empty-separator bug. I’d also add one test that deserializes a c_mode payload containing a 4-digit MorphSkin (e.g., 7583) to lock in the byteshort fix.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/NosCore.Packets.Tests/DeserializerTest.cs` around lines 420 - 441, Add a
focused regression test method in the same test class that uses the Deserializer
(same constructor pattern as
PacketWithEmptySpecialSeparatorSplitsEachCharIntoField) to Deserialize a c_mode
payload containing a 4-digit MorphSkin (e.g., "c_mode 7583 ..."), then assert
the resulting packet's MorphSkin property (or equivalent) is stored as a value >
255 (type short) to lock in the byte→short fix; reference the
Deserializer.Deserialize call and the MorphSkin property in your assertions to
locate the code to update.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/NosCore.Packets/NosCore.Packets.csproj`:
- Line 15: The package version must be bumped to a new major release because
CModePacket.MorphSkin changed its public type from byte to short; update the
<Version> element in the project file (currently <Version>16.11.0</Version>) to
the next major version (e.g. <Version>17.0.0</Version>) so the NuGet package
reflects the breaking API change involving CModePacket.MorphSkin.

---

Nitpick comments:
In `@test/NosCore.Packets.Tests/DeserializerTest.cs`:
- Around line 420-441: Add a focused regression test method in the same test
class that uses the Deserializer (same constructor pattern as
PacketWithEmptySpecialSeparatorSplitsEachCharIntoField) to Deserialize a c_mode
payload containing a 4-digit MorphSkin (e.g., "c_mode 7583 ..."), then assert
the resulting packet's MorphSkin property (or equivalent) is stored as a value >
255 (type short) to lock in the byte→short fix; reference the
Deserializer.Deserialize call and the MorphSkin property in your assertions to
locate the code to update.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7c79cbab-9be1-4fca-9edd-296ff02a5d82

📥 Commits

Reviewing files that changed from the base of the PR and between 32944c1 and 06c6178.

📒 Files selected for processing (4)
  • src/NosCore.Packets/Deserializer.cs
  • src/NosCore.Packets/NosCore.Packets.csproj
  • src/NosCore.Packets/ServerPackets/Player/CModePacket.cs
  • test/NosCore.Packets.Tests/DeserializerTest.cs

Comment thread src/NosCore.Packets/NosCore.Packets.csproj Outdated
… -1)

Real trace captures show an empty inventory slot as ivn {type} {slot}.0.0...
but NosCore was generating {slot}.-1.0... because the extension in NosCore
treats a null item-instance as a marker and hard-codes VNum = -1. The -1
sentinel is the Serializer's null-sub-packet output — which makes sense
for a nullable element, but the wire format never uses -1 here.

Tightening the packet contract: List<IvnSubPacket?>? -> List<IvnSubPacket>?
on IvnPacket, InvPacket, StashClientPacket, PStashClientPacket,
FStashClientPacket, FStashAllPacket. Now null entries are a compile error;
callers must produce a concrete sub-packet with VNum = 0 for cleared slots.
StashAllPacket and PStashAllPacket were already non-nullable — this aligns
the rest.

Regression test SerializeIvnPacketWithClearedSlotUsesZeroVNum asserts
"ivn 0 19.0.0.0.0" for a zeroed sub-packet so the wire output is pinned.

This unblocks the NosCore-side fix: ItemInstanceExtension.GenerateIvnSubPacket
returns VNum = 0 (not -1) when the item is null, so the client actually
clears the source slot after mvi / mve.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
test/NosCore.Packets.Tests/SerializerTest.cs (1)

1236-1249: Optional: add packet validity assertion for clearer diagnostics.

Line [1237] onward already verifies wire output; adding Assert.IsTrue(packet.IsValid, ...) makes failures easier to triage when schema constraints change.

💡 Proposed improvement
         public void SerializeIvnPacketWithClearedSlotUsesZeroVNum()
         {
             var packet = new IvnPacket
             {
                 Type = PocketType.Equipment,
                 IvnSubPackets = new List<IvnSubPacket>
                 {
                     new() { Slot = 19, VNum = 0, RareAmount = 0, UpgradeDesign = 0, SecondUpgrade = 0 }
                 }
             };
 
+            Assert.IsTrue(packet.IsValid, string.Join("; ",
+                packet.ValidationResult.Select(v => $"{string.Join(",", v.MemberNames)}: {v.ErrorMessage}")));
             Assert.AreEqual("ivn 0 19.0.0.0.0", Serializer.Serialize(packet));
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/NosCore.Packets.Tests/SerializerTest.cs` around lines 1236 - 1249, Add a
packet validity assertion before serializing in the
SerializeIvnPacketWithClearedSlotUsesZeroVNum test: verify the IvnPacket
instance is valid (e.g., Assert.IsTrue(packet.IsValid, "expected packet to be
valid before serialization")) so test failures show schema/validation problems
earlier; place this check just before the existing Assert.AreEqual that compares
Serializer.Serialize(packet).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/NosCore.Packets.Tests/SerializerTest.cs`:
- Around line 1236-1249: Add a packet validity assertion before serializing in
the SerializeIvnPacketWithClearedSlotUsesZeroVNum test: verify the IvnPacket
instance is valid (e.g., Assert.IsTrue(packet.IsValid, "expected packet to be
valid before serialization")) so test failures show schema/validation problems
earlier; place this check just before the existing Assert.AreEqual that compares
Serializer.Serialize(packet).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c36c5ee-52da-4d76-9731-05c2d6fff4ef

📥 Commits

Reviewing files that changed from the base of the PR and between 06c6178 and 0cd5aeb.

📒 Files selected for processing (7)
  • src/NosCore.Packets/ServerPackets/Inventory/InvPacket.cs
  • src/NosCore.Packets/ServerPackets/Inventory/IvnPacket.cs
  • src/NosCore.Packets/ServerPackets/Warehouse/FStashAllPacket.cs
  • src/NosCore.Packets/ServerPackets/Warehouse/FStashClientPacket.cs
  • src/NosCore.Packets/ServerPackets/Warehouse/PStashClientPacket.cs
  • src/NosCore.Packets/ServerPackets/Warehouse/StashClientPacket.cs
  • test/NosCore.Packets.Tests/SerializerTest.cs

erwan-joly and others added 3 commits April 24, 2026 16:51
Real trace captures of ivn with inventory type 0 (equipment) carry seven
dot-separated values — e.g. ivn 0 20.4998.0.0.0.0.0 — but the sub-packet
only declared five (Slot, VNum, RareAmount, UpgradeDesign, SecondUpgrade),
so NosCore was shipping slot.vnum.rare.upgrade.0 with two fields missing.
The client never cleared the source slot after an mvi because the packet
it received didn't match the expected shape.

Added Unknown5 (byte) and Unknown6 (short) at indices 5 and 6. Names are
placeholders — reverse-engineering suggests index 5 may be an IsBound
flag and index 6 the rune/option count — but without confirmation we
keep them opaque.

Tests pin both sides of a move to the exact trace:
- SerializeIvnPacketWithClearedSlotUsesZeroVNum → "ivn 0 19.0.0.0.0.0.0"
- SerializeIvnPacketWithPopulatedEquipmentSlotMatchesTrace → "ivn 0 20.4998.0.0.0.0.0"

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ch trace

Audit against vanosilla (C:\Users\erwan\Desktop\vanosilla) named the
following fields that NosCore.Packets left as Unknown. Each rename is
backed by a direct vanosilla generator line at the same wire position.

Renames (all source-breaking, wire-identical):
- DropPacket.Unknown (idx 5, byte) -> IsQuest (bool).
  vanosilla MapExtensions.cs: $"drop {vnum} {id} {x} {y} {amount} {(isQuest ? 1 : 0)} {owner}"
- InCharacterSubPacket.Unknown (idx 11, byte) -> FairyBooster (bool).
  vanosilla CharacterPacketExtension.cs line 733: (IsUsingFairyBooster() ? 1 : 0)
- InCharacterSubPacket.Morph (idx 12) -> FairyMorph. Old name was wrong;
  vanosilla line 734 writes Fairy.GameItem.Morph here, not the character's.
- InCharacterSubPacket.Unknown2 (idx 13, byte) -> ShowInEffect (bool).
  vanosilla line 735: (showInEffect ? 0 : 1)
- InCharacterSubPacket.Unknown3 (idx 14, byte) -> Morph. This is the
  character's actual Morph (vanosilla line 736: character.Morph).
- ScnPacket.Unknown21 (idx 34, int) -> IsTeamMember (bool).
  vanosilla MatePacketExtensions.cs line 254: (mateEntity.IsTeamMember ? 1 : 0)
- ScnPacket.Unknown22 (idx 35, int) -> LevelXp. vanosilla line 255:
  _algorithm.GetLevelXp(mateEntity).

InItemSubPacket position fix
The current layout emits {amount} {isQuest} {owner} {0} but the real
trace for "in 9" and vanosilla both end {amount} {isQuest} 0 {owner}:
  trace: in 9 1046 6467573 52 154 10 0 0 14643732
The Unknown byte at index 3 was effectively the "0" that NosTale inserted
before ownerId in a protocol revision. NosCore put it at the wrong end.
Fix: move Unknown to index 2, Owner to index 3. Added regression test
SerializeInPacketForItemPutsOwnerAfterUnknownZero pinning the trace.

Not attempted (no vanosilla evidence at that index): ClistPacket,
NInvPacket, AtPacket, EquipmentSubPacket, PinitSubPacket, MlInfoPacket,
BfePacket, ScPStcPacket, EffObPacket, EffTPacket, RsfiPacket, CtlPacket,
StpSubPacket, LevPacket, ScnPacket.Unknown/Unknown2 (vanosilla hardcodes 0),
CInfoPacket.Unknown1 (vanosilla hardcodes a dash), IvnSubPacket.Unknown5/6
(vanosilla knows RunesCount but trace has one extra unidentified field,
so the position of RunesCount is ambiguous without a runes-populated
capture).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Major because this PR now carries several source-breaking changes beyond
the original CModePacket.MorphSkin widening: IvnPacket/InvPacket/Stash*
move to List<IvnSubPacket> (non-nullable items), InItemSubPacket swaps
indices 2 and 3 so Owner sits after the Unknown zero, and the audited
rename pass touches DropPacket / InCharacterSubPacket / ScnPacket.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/NosCore.Packets/ServerPackets/Mates/ScnPacket.cs (1)

120-124: Add a focused sc_n regression test for indices 34/35.

Since this is a wire-schema semantic change, add a deserialize/serialize round-trip test that pins Line 120 as 0/1 and Line 124 with a large value (e.g., 285816) to prevent future index/type drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NosCore.Packets/ServerPackets/Mates/ScnPacket.cs` around lines 120 - 124,
Add a focused round-trip regression test for the ScnPacket wire schema that
ensures PacketIndex 34/35 don't drift: create a test (e.g.,
ScnPacket_Index34_35_RoundTrip) that crafts or loads a raw packet with index 34
set to a boolean byte (0 or 1) and index 35 set to the large integer 285816,
then deserialize into the ScnPacket type, assert ScnPacket.IsTeamMember equals
the pinned boolean and ScnPacket.LevelXp equals 285816, re-serialize the
ScnPacket and assert the output bytes match the original raw bytes exactly to
lock the schema.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/NosCore.Packets/ServerPackets/Mates/ScnPacket.cs`:
- Around line 120-124: Add a focused round-trip regression test for the
ScnPacket wire schema that ensures PacketIndex 34/35 don't drift: create a test
(e.g., ScnPacket_Index34_35_RoundTrip) that crafts or loads a raw packet with
index 34 set to a boolean byte (0 or 1) and index 35 set to the large integer
285816, then deserialize into the ScnPacket type, assert ScnPacket.IsTeamMember
equals the pinned boolean and ScnPacket.LevelXp equals 285816, re-serialize the
ScnPacket and assert the output bytes match the original raw bytes exactly to
lock the schema.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b82b4034-f0a6-4a85-a54e-b1ca1f744ba8

📥 Commits

Reviewing files that changed from the base of the PR and between 3d0901b and e42dde1.

📒 Files selected for processing (5)
  • src/NosCore.Packets/ServerPackets/Entities/DropPacket.cs
  • src/NosCore.Packets/ServerPackets/Mates/ScnPacket.cs
  • src/NosCore.Packets/ServerPackets/Visibility/InCharacterSubPacket.cs
  • src/NosCore.Packets/ServerPackets/Visibility/InItemSubPacket.cs
  • test/NosCore.Packets.Tests/SerializerTest.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/NosCore.Packets.Tests/SerializerTest.cs

@erwan-joly erwan-joly merged commit 2fc7c46 into master Apr 24, 2026
3 checks passed
@erwan-joly erwan-joly deleted the fix/deserializer-empty-separator-and-cmode branch April 24, 2026 05:28
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.

1 participant