Skip to content

Commit 7db975b

Browse files
committed
feat(Networking): Refactor ContentModInventoryPayloadCodec and JoinDiagnosticsPayload
- Introduced `Compact` and `Expand` methods in `ContentModInventoryPayloadCodec` for better handling of content mod inventory entries. - Updated `JoinDiagnosticsPayload` to version 5, incorporating `JoinDiagnosticsPayloadV5` for enhanced payload structure. - Implemented compression and decompression methods for payloads, improving data transmission efficiency. - Refactored payload reading and writing logic to support the new structure and ensure backward compatibility.
1 parent d1386e9 commit 7db975b

2 files changed

Lines changed: 99 additions & 25 deletions

File tree

src/Networking/ContentModInventoryPayloadCodec.cs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,37 @@ internal static class ContentModInventoryPayloadCodec
1717

1818
internal static string Encode(IReadOnlyList<ContentModInventoryEntry> entries)
1919
{
20-
var compact = entries.Select(entry => new CompactEntry(
20+
var compact = Compact(entries);
21+
var json = JsonSerializer.Serialize(compact, JsonOptions);
22+
return Convert.ToBase64String(Gzip(Encoding.UTF8.GetBytes(json)));
23+
}
24+
25+
internal static IReadOnlyList<CompactEntry> Compact(IReadOnlyList<ContentModInventoryEntry> entries)
26+
{
27+
return entries.Select(entry => new CompactEntry(
2128
entry.Id,
2229
entry.Version,
2330
entry.Name,
2431
entry.Source,
2532
entry.WorkshopItemId,
2633
BuildFlags(entry)))
2734
.ToArray();
28-
var json = JsonSerializer.Serialize(compact, JsonOptions);
29-
return Convert.ToBase64String(Gzip(Encoding.UTF8.GetBytes(json)));
35+
}
36+
37+
internal static IReadOnlyList<ContentModInventoryEntry> Expand(IReadOnlyList<CompactEntry> compact)
38+
{
39+
return compact
40+
.Select((entry, index) => new ContentModInventoryEntry(
41+
index,
42+
entry.Id,
43+
entry.Version,
44+
entry.Name,
45+
entry.Source,
46+
entry.WorkshopItemId,
47+
(entry.Flags & 1) != 0,
48+
(entry.Flags & 2) != 0,
49+
(entry.Flags & 4) != 0))
50+
.ToArray();
3051
}
3152

3253
internal static bool TryDecode(string? encoded, out IReadOnlyList<ContentModInventoryEntry> entries)
@@ -42,18 +63,7 @@ internal static bool TryDecode(string? encoded, out IReadOnlyList<ContentModInve
4263
if (compact == null)
4364
return false;
4465

45-
entries = compact
46-
.Select((entry, index) => new ContentModInventoryEntry(
47-
index,
48-
entry.Id,
49-
entry.Version,
50-
entry.Name,
51-
entry.Source,
52-
entry.WorkshopItemId,
53-
(entry.Flags & 1) != 0,
54-
(entry.Flags & 2) != 0,
55-
(entry.Flags & 4) != 0))
56-
.ToArray();
66+
entries = Expand(compact);
5767
return true;
5868
}
5969
catch (Exception ex)
@@ -95,7 +105,7 @@ private static byte[] Gunzip(byte[] data)
95105
return output.ToArray();
96106
}
97107

98-
private sealed record CompactEntry(
108+
internal sealed record CompactEntry(
99109
string Id,
100110
string Version,
101111
string Name,

src/Networking/JoinDiagnostics/JoinDiagnosticsPayload.cs

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.IO.Compression;
2+
using System.Text;
13
using System.Text.Json;
24
using System.Text.Json.Serialization;
35
using MegaCrit.Sts2.Core.Debug;
@@ -31,6 +33,17 @@ internal sealed record JoinDiagnosticsModEntry(
3133
string Source,
3234
ulong? WorkshopItemId);
3335

36+
internal sealed record JoinDiagnosticsPayloadV5(
37+
string GameVersion,
38+
uint ModelDbHash,
39+
string GameMode,
40+
string SessionState,
41+
IReadOnlyList<JoinDiagnosticsModEntry> GameplayMods,
42+
IReadOnlyList<ContentModInventoryPayloadCodec.CompactEntry> ContentMods,
43+
bool ModelDbHashUsesDeterministicCache,
44+
string? ModelDbHashModeDetail,
45+
bool? SavedPropertyNetIdUsesDeterministicSort);
46+
3447
internal sealed record JoinPeerSnapshot(
3548
string GameVersion,
3649
uint ModelDbHash,
@@ -46,7 +59,7 @@ internal sealed record JoinPeerSnapshot(
4659
internal static class JoinDiagnosticsPayloadCodec
4760
{
4861
private const string ExtensionId = "ritsulib.joinDiagnostics";
49-
private const int PayloadVersion = 4;
62+
private const int PayloadVersion = 5;
5063
private static int _registered;
5164

5265
private static readonly JsonSerializerOptions JsonOptions = new()
@@ -85,17 +98,17 @@ public static void Read(PacketReader reader)
8598
try
8699
{
87100
var cacheStatus = ModelIdSerializationCacheDynamicContentPatch.GetDeterministicCacheStatus();
88-
var payload = new JoinDiagnosticsPayload(
101+
var payload = new JoinDiagnosticsPayloadV5(
89102
message.version,
90103
message.idDatabaseHash,
91104
message.gameMode.ToString(),
92105
message.sessionState.ToString(),
93106
CreateLocalModEntries(),
94-
ContentModInventoryPayloadCodec.Encode(CreateLocalContentModEntries()),
107+
ContentModInventoryPayloadCodec.Compact(CreateLocalContentModEntries()),
95108
cacheStatus.IsActive,
96109
cacheStatus.Detail,
97110
SavedPropertiesTypeCacheInjectionPatch.UsesDeterministicNetIdTable);
98-
return JsonSerializer.Serialize(payload, JsonOptions);
111+
return EncodeCompressed(payload);
99112
}
100113
catch (Exception ex)
101114
{
@@ -104,32 +117,83 @@ public static void Read(PacketReader reader)
104117
}
105118
}
106119

107-
private static void ReadPayload(int version, string json)
120+
private static void ReadPayload(int version, string payload)
108121
{
109122
try
110123
{
111124
if (version == 1)
112125
{
113126
JoinFailureDiagnosticsService.ObserveHostPayload(ConvertLegacyPayload(
114-
JsonSerializer.Deserialize<JoinDiagnosticsPayloadV1>(json, JsonOptions)));
127+
JsonSerializer.Deserialize<JoinDiagnosticsPayloadV1>(payload, JsonOptions)));
115128
return;
116129
}
117130

118-
if (version != 2 && version != 3 && version != PayloadVersion)
131+
if (version != 2 && version != 3 && version != 4 && version != PayloadVersion)
119132
{
120133
RitsuLibFramework.Logger.Warn($"[JoinDiagnostics] Unsupported payload version: {version}");
121134
return;
122135
}
123136

124-
JoinFailureDiagnosticsService.ObserveHostPayload(
125-
JsonSerializer.Deserialize<JoinDiagnosticsPayload>(json, JsonOptions));
137+
var json = version == PayloadVersion ? DecodeCompressed(payload) : payload;
138+
var parsed = version == PayloadVersion
139+
? FromWirePayload(JsonSerializer.Deserialize<JoinDiagnosticsPayloadV5>(json, JsonOptions))
140+
: JsonSerializer.Deserialize<JoinDiagnosticsPayload>(json, JsonOptions);
141+
JoinFailureDiagnosticsService.ObserveHostPayload(parsed);
126142
}
127143
catch (Exception ex)
128144
{
129145
RitsuLibFramework.Logger.Warn($"[JoinDiagnostics] Failed to read payload: {ex.Message}");
130146
}
131147
}
132148

149+
private static string EncodeCompressed(JoinDiagnosticsPayloadV5 payload)
150+
{
151+
var json = JsonSerializer.Serialize(payload, JsonOptions);
152+
return Convert.ToBase64String(Gzip(Encoding.UTF8.GetBytes(json)));
153+
}
154+
155+
private static JoinDiagnosticsPayload? FromWirePayload(JoinDiagnosticsPayloadV5? payload)
156+
{
157+
if (payload == null)
158+
return null;
159+
160+
return new(
161+
payload.GameVersion,
162+
payload.ModelDbHash,
163+
payload.GameMode,
164+
payload.SessionState,
165+
payload.GameplayMods,
166+
ContentModInventoryPayloadCodec.Encode(ContentModInventoryPayloadCodec.Expand(payload.ContentMods)),
167+
payload.ModelDbHashUsesDeterministicCache,
168+
payload.ModelDbHashModeDetail,
169+
payload.SavedPropertyNetIdUsesDeterministicSort);
170+
}
171+
172+
private static string DecodeCompressed(string encoded)
173+
{
174+
return Encoding.UTF8.GetString(Gunzip(Convert.FromBase64String(encoded)));
175+
}
176+
177+
private static byte[] Gzip(byte[] data)
178+
{
179+
using var output = new MemoryStream();
180+
using (var gzip = new GZipStream(output, CompressionLevel.SmallestSize, true))
181+
{
182+
gzip.Write(data, 0, data.Length);
183+
}
184+
185+
return output.ToArray();
186+
}
187+
188+
private static byte[] Gunzip(byte[] data)
189+
{
190+
using var input = new MemoryStream(data, false);
191+
using var gzip = new GZipStream(input, CompressionMode.Decompress);
192+
using var output = new MemoryStream();
193+
gzip.CopyTo(output);
194+
return output.ToArray();
195+
}
196+
133197
public static JoinPeerSnapshot CreateLocalSnapshot()
134198
{
135199
var cacheStatus = ModelIdSerializationCacheDynamicContentPatch.GetDeterministicCacheStatus();

0 commit comments

Comments
 (0)