From 5cb97cc6de7422054e210fd8c757f7aefd6adb13 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Wed, 30 Apr 2025 19:33:15 -0700 Subject: [PATCH] fix: Fixes BitArray serialization (#2170) Fixes serialization/deserialization edge cases with BitArray. If you use BitArray, you will need to migrate. 1. Change the type in the migration JSON file (if there is one) from `BitArray` to `byte[]` for all the versions you need to migrate. 2. Then in the `MigrateFrom`, use the following function to convert the field from a byte[] back to the BitArray. Example Migration JSON: ```json { "name": "RestrictedSpells", "type": "byte[]", "rule": "ArrayMigrationRule", "ruleArguments": [ "byte", "PrimitiveTypeMigrationRule", "" ] }, ``` Migration function to use in MigrateFrom: ```cs public static BitArray MigrateBitArray(byte[] data, int bitLength) => new(data) { Length = bitLength }; ``` Example use: ```cs private void MigrateFrom(V0Content content) { // ... deserialize _restrictedSpells = content.RestrictedSpells.MigrateBitArray(SpellRegistry.Types.Length); _restrictedSkills = content.RestrictedSkills.MigrateBitArray(SkillInfo.Table.Length); // ... rest of deserialize } ``` --- .../Server/Serialization/IGenericReader.cs | 22 ++++++++++++------- .../Server/Serialization/IGenericWriter.cs | 19 +++++++++++----- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/Projects/Server/Serialization/IGenericReader.cs b/Projects/Server/Serialization/IGenericReader.cs index e3fe07deb..4e59e4e98 100644 --- a/Projects/Server/Serialization/IGenericReader.cs +++ b/Projects/Server/Serialization/IGenericReader.cs @@ -14,6 +14,7 @@ *************************************************************************/ using System; +using System.Buffers; using System.Collections; using System.IO; using System.Net; @@ -51,7 +52,7 @@ public interface IGenericReader var delta => new DateTime(delta + DateTime.UtcNow.Ticks, DateTimeKind.Utc) }; } - decimal ReadDecimal() => new(stackalloc int[4] { ReadInt(), ReadInt(), ReadInt(), ReadInt() }); + decimal ReadDecimal() => new([ReadInt(), ReadInt(), ReadInt(), ReadInt()]); int ReadEncodedInt() { int v = 0, shift = 0; @@ -119,14 +120,19 @@ public interface IGenericReader public BitArray ReadBitArray() { - var byteArrayLength = ReadEncodedInt(); + int bitLength = ReadEncodedInt(); + int byteLength = (bitLength + 7) / 8; - // We need an exact array size since the ctor doesn't allow for offset/length, not much we can do at this point. - var byteArray = new byte[byteArrayLength]; - - Read(byteArray); - - return new BitArray(byteArray); + var buffer = ArrayPool.Shared.Rent(byteLength); + try + { + Read(buffer.AsSpan(0, byteLength)); + return new BitArray(buffer) { Length = bitLength }; + } + finally + { + ArrayPool.Shared.Return(buffer); + } } TextDefinition ReadTextDefinition() diff --git a/Projects/Server/Serialization/IGenericWriter.cs b/Projects/Server/Serialization/IGenericWriter.cs index 63ac04970..72a8728d0 100644 --- a/Projects/Server/Serialization/IGenericWriter.cs +++ b/Projects/Server/Serialization/IGenericWriter.cs @@ -163,14 +163,21 @@ public interface IGenericWriter public void Write(BitArray bitArray) { - var bytesLength = (bitArray.Length - 1 + (1 << 3)) >>> 3; - var arrayBuffer = ArrayPool.Shared.Rent(bytesLength); + int bitLength = bitArray.Length; + int byteLength = (bitLength + 7) / 8; - WriteEncodedInt(bytesLength); - bitArray.CopyTo(arrayBuffer, 0); + WriteEncodedInt(bitLength); - Write(arrayBuffer.AsSpan(0, bytesLength)); - ArrayPool.Shared.Return(arrayBuffer); + var arrayBuffer = ArrayPool.Shared.Rent(byteLength); + try + { + bitArray.CopyTo(arrayBuffer, 0); + Write(arrayBuffer.AsSpan(0, byteLength)); + } + finally + { + ArrayPool.Shared.Return(arrayBuffer); + } } void Write(TextDefinition def)