From 996d75b425cc073ae2e8670a9d0c4a3f75e08e3c Mon Sep 17 00:00:00 2001 From: James Frowen Date: Fri, 7 Jun 2024 15:20:54 +0100 Subject: [PATCH] feat: feature to send strings as index will save bandwidth when sending many strings that are the same --- .../Runtime/Serialization/NetworkReader.cs | 1 + .../Runtime/Serialization/NetworkWriter.cs | 4 +- .../Runtime/Serialization/StringExtensions.cs | 84 ++++++++++++++++++- 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/Assets/Mirage/Runtime/Serialization/NetworkReader.cs b/Assets/Mirage/Runtime/Serialization/NetworkReader.cs index b4dbb2c46ac..898d4aa62d8 100644 --- a/Assets/Mirage/Runtime/Serialization/NetworkReader.cs +++ b/Assets/Mirage/Runtime/Serialization/NetworkReader.cs @@ -35,6 +35,7 @@ namespace Mirage.Serialization /// public unsafe class NetworkReader : IDisposable { + public StringStore StringStore; private byte[] _managedBuffer; private GCHandle _handle; private ulong* _longPtr; diff --git a/Assets/Mirage/Runtime/Serialization/NetworkWriter.cs b/Assets/Mirage/Runtime/Serialization/NetworkWriter.cs index 9ba842518ac..600cd252e75 100644 --- a/Assets/Mirage/Runtime/Serialization/NetworkWriter.cs +++ b/Assets/Mirage/Runtime/Serialization/NetworkWriter.cs @@ -35,6 +35,8 @@ namespace Mirage.Serialization /// public unsafe class NetworkWriter { + public StringStore StringStore; + /// /// Max buffer size = 0.5MB /// @@ -42,7 +44,7 @@ public unsafe class NetworkWriter private byte[] _managedBuffer; private int _bitCapacity; - /// Allow internal buffer to resize if capcity is reached + /// Allow internal buffer to resize if capacity is reached private readonly bool _allowResize; private GCHandle _handle; private ulong* _longPtr; diff --git a/Assets/Mirage/Runtime/Serialization/StringExtensions.cs b/Assets/Mirage/Runtime/Serialization/StringExtensions.cs index 12eb1a80b55..06c1990dbe6 100644 --- a/Assets/Mirage/Runtime/Serialization/StringExtensions.cs +++ b/Assets/Mirage/Runtime/Serialization/StringExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Text; @@ -31,11 +32,29 @@ public static int MaxStringLength private static byte[] stringBuffer = new byte[MaxStringLength]; /// string or null - public static void WriteString(this NetworkWriter writer, string value) => WriteString(writer, value, defaultEncoding); + public static void WriteString(this NetworkWriter writer, string value) + { + // note, only use StringStore for default encoding + if (writer.StringStore != null) + { + writer.StringStore.WriteString(writer, value); + return; + } + + WriteString(writer, value, defaultEncoding); + } /// string or null /// Throws if invalid utf8 string is received - public static string ReadString(this NetworkReader reader) => ReadString(reader, defaultEncoding); + public static string ReadString(this NetworkReader reader) + { + if (reader.StringStore != null) + { + return reader.StringStore.ReadString(reader); + } + + return ReadString(reader, defaultEncoding); + } /// Use this for encoding other than the default (UTF8). Make sure to use same encoding for ReadString /// string or null @@ -90,5 +109,66 @@ public static string ReadString(this NetworkReader reader, Encoding encoding) // convert directly from buffer to string via encoding return encoding.GetString(data.Array, data.Offset, data.Count); } + public static void WriteStringStore(this NetworkWriter writer, StringStore store) + { + var count = (uint)store.Strings.Count; + writer.WritePackedUInt32(count); + for (var i = 0; i < count; i++) + // use defaultEncoding, so we use the real write method and not the one that uses StringStore + writer.WriteString(store.Strings[i], defaultEncoding); + } + public static StringStore ReadStringStore(this NetworkReader reader) + { + var store = new StringStore(); + var list = store.Strings; + var count = reader.ReadPackedUInt32(); + for (var i = 0; i < count; i++) + list.Add(reader.ReadString(defaultEncoding)); + return store; + } + } + + public class StringStore + { + public Dictionary Lookup = new Dictionary(); + public List Strings = new List(); + + public int GetKey(string value) + { + if (Lookup.TryGetValue(value, out var index)) + { + return index; + } + else + { + index = Strings.Count; + Strings.Add(value); + Lookup.Add(value, index); + return index; + } + } + + public void WriteString(NetworkWriter writer, string value) + { + if (value == null) + { + writer.WritePackedUInt32(0); + } + else + { + var key = GetKey(value); + writer.WritePackedUInt32(checked((uint)(key + 1))); + } + } + + public string ReadString(NetworkReader reader) + { + var key = reader.ReadPackedUInt32(); + if (key == 0) + return null; + + var index = checked((int)(key - 1)); + return Strings[index]; + } } }