about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEmma [it/its]@Rory& <root@rory.gay>2023-12-07 07:26:02 +0100
committerEmma [it/its]@Rory& <root@rory.gay>2023-12-07 07:26:02 +0100
commit83f9a4df147ef58c884f43092527f5cb6fa2f0a9 (patch)
tree336f0cc008723f8dba11a480249de7c76ad3e9ea
parentCleanup, move ArcaneLibs to submodule instead of parent submodule (diff)
downloadLibMatrix-83f9a4df147ef58c884f43092527f5cb6fa2f0a9.tar.xz
Temp state
Diffstat (limited to '')
m---------ArcaneLibs0
-rw-r--r--LibMatrix.sln16
-rw-r--r--LibMatrix.sln.DotSettings.user1
-rw-r--r--LibMatrix/Helpers/SyncHelper.cs28
-rw-r--r--LibMatrix/Helpers/SyncStateResolver.cs2
-rw-r--r--LibMatrix/RoomTypes/GenericRoom.cs3
-rw-r--r--LibMatrix/StateEvent.cs42
-rw-r--r--Utilities/LibMatrix.JsonSerializerContextGenerator/EventSerializerContexts.g.cs99
-rw-r--r--Utilities/LibMatrix.JsonSerializerContextGenerator/LibMatrix.JsonSerializerContextGenerator.csproj15
-rw-r--r--Utilities/LibMatrix.JsonSerializerContextGenerator/Program.cs22
10 files changed, 199 insertions, 29 deletions
diff --git a/ArcaneLibs b/ArcaneLibs
-Subproject 3b815f2bffc65d7b42e8845464d6f07391d612e
+Subproject 788516bf3253902b56dfe335c3d2166733969ba
diff --git a/LibMatrix.sln b/LibMatrix.sln
index 87398f3..f94131b 100644
--- a/LibMatrix.sln
+++ b/LibMatrix.sln
@@ -27,6 +27,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.Tests", "Tests\Li
 EndProject

 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestDataGenerator", "Tests\TestDataGenerator\TestDataGenerator.csproj", "{0B9B34D1-9362-45A9-9C21-816FD6959110}"

 EndProject

+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.JsonSerializerContextGenerator", "Utilities\LibMatrix.JsonSerializerContextGenerator\LibMatrix.JsonSerializerContextGenerator.csproj", "{4D9B5227-48DC-4A30-9263-AFB51DC01ABB}"

+EndProject

+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ArcaneLibs", "ArcaneLibs", "{01A126FE-9D50-40F2-817B-E55F4065EA76}"

+EndProject

+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcaneLibs", "ArcaneLibs\ArcaneLibs\ArcaneLibs.csproj", "{13A797D1-7E13-4789-A167-8628B1641AC0}"

+EndProject

 Global

 	GlobalSection(SolutionConfigurationPlatforms) = preSolution

 		Debug|Any CPU = Debug|Any CPU

@@ -72,6 +78,14 @@ Global
 		{0B9B34D1-9362-45A9-9C21-816FD6959110}.Debug|Any CPU.Build.0 = Debug|Any CPU

 		{0B9B34D1-9362-45A9-9C21-816FD6959110}.Release|Any CPU.ActiveCfg = Release|Any CPU

 		{0B9B34D1-9362-45A9-9C21-816FD6959110}.Release|Any CPU.Build.0 = Release|Any CPU

+		{4D9B5227-48DC-4A30-9263-AFB51DC01ABB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

+		{4D9B5227-48DC-4A30-9263-AFB51DC01ABB}.Debug|Any CPU.Build.0 = Debug|Any CPU

+		{4D9B5227-48DC-4A30-9263-AFB51DC01ABB}.Release|Any CPU.ActiveCfg = Release|Any CPU

+		{4D9B5227-48DC-4A30-9263-AFB51DC01ABB}.Release|Any CPU.Build.0 = Release|Any CPU

+		{13A797D1-7E13-4789-A167-8628B1641AC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

+		{13A797D1-7E13-4789-A167-8628B1641AC0}.Debug|Any CPU.Build.0 = Debug|Any CPU

+		{13A797D1-7E13-4789-A167-8628B1641AC0}.Release|Any CPU.ActiveCfg = Release|Any CPU

+		{13A797D1-7E13-4789-A167-8628B1641AC0}.Release|Any CPU.Build.0 = Release|Any CPU

 	EndGlobalSection

 	GlobalSection(NestedProjects) = preSolution

 		{1B1B2197-61FB-416F-B6C8-845F2E5A0442} = {840309F0-435B-43A7-8471-8C2BE643889D}

@@ -81,5 +95,7 @@ Global
 		{FB8FE4EB-B53B-464B-A5FD-9BF9D0F3EF9B} = {840309F0-435B-43A7-8471-8C2BE643889D}

 		{345934FF-CA81-4A4B-B137-9F198102C65F} = {BFE16D8E-EFC5-49F6-9854-DB001309B3B4}

 		{0B9B34D1-9362-45A9-9C21-816FD6959110} = {BFE16D8E-EFC5-49F6-9854-DB001309B3B4}

+		{4D9B5227-48DC-4A30-9263-AFB51DC01ABB} = {A6345ECE-4C5E-400F-9130-886E343BF314}

+		{13A797D1-7E13-4789-A167-8628B1641AC0} = {01A126FE-9D50-40F2-817B-E55F4065EA76}

 	EndGlobalSection

 EndGlobal

diff --git a/LibMatrix.sln.DotSettings.user b/LibMatrix.sln.DotSettings.user
index 822b0e8..f720bf5 100644
--- a/LibMatrix.sln.DotSettings.user
+++ b/LibMatrix.sln.DotSettings.user
@@ -1,4 +1,5 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=_002Fhome_002FRory_002Fgit_002Fmatrix_002FMatrixRoomUtils_002FLibMatrix_002FArcaneLibs_002FArcaneLibs_002Fbin_002FDebug_002Fnet8_002E0_002FArcaneLibs_002Edll/@EntryIndexedValue">True</s:Boolean>
 	<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt;
   &lt;Assembly Path="/home/root@Rory/.cache/NuGetPackages/microsoft.extensions.hosting.abstractions/7.0.0/lib/net7.0/Microsoft.Extensions.Hosting.Abstractions.dll" /&gt;
 &lt;/AssemblyExplorer&gt;</s:String></wpf:ResourceDictionary>
\ No newline at end of file
diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs
index a05b915..334c288 100644
--- a/LibMatrix/Helpers/SyncHelper.cs
+++ b/LibMatrix/Helpers/SyncHelper.cs
@@ -1,4 +1,5 @@
 using System.Diagnostics;
+using System.Net.Http.Json;
 using ArcaneLibs.Extensions;
 using LibMatrix.Filters;
 using LibMatrix.Homeservers;
@@ -12,23 +13,41 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
     public int Timeout { get; set; } = 30000;
     public string? SetPresence { get; set; } = "online";
     public SyncFilter? Filter { get; set; }
-    public bool FullState { get; set; } = false;
+    public bool FullState { get; set; }
 
     public bool IsInitialSync { get; set; } = true;
 
+    public TimeSpan MinimumDelay { get; set; } = new(0);
+
     public async Task<SyncResponse?> SyncAsync(CancellationToken? cancellationToken = null) {
         if (homeserver is null) {
             Console.WriteLine("Null passed as homeserver for SyncHelper!");
-            throw new ArgumentNullException("Null passed as homeserver for SyncHelper!");
+            throw new ArgumentNullException(nameof(homeserver), "Null passed as homeserver for SyncHelper!");
+        }
+        if (homeserver.ClientHttpClient is null) {
+            Console.WriteLine("Homeserver for SyncHelper is not properly configured!");
+            throw new ArgumentNullException(nameof(homeserver.ClientHttpClient), "Null passed as homeserver for SyncHelper!");
         }
 
+
+        var sw = Stopwatch.StartNew();
+
         var url = $"/_matrix/client/v3/sync?timeout={Timeout}&set_presence={SetPresence}&full_state={(FullState ? "true" : "false")}";
         if (!string.IsNullOrWhiteSpace(Since)) url += $"&since={Since}";
         if (Filter is not null) url += $"&filter={Filter.ToJson(ignoreNull: true, indent: false)}";
         // Console.WriteLine("Calling: " + url);
         logger?.LogInformation("SyncHelper: Calling: {}", url);
         try {
-            return await homeserver?.ClientHttpClient?.GetFromJsonAsync<SyncResponse>(url, cancellationToken: cancellationToken ?? CancellationToken.None)!;
+            var httpResp = await homeserver.ClientHttpClient.GetAsync(url, cancellationToken: cancellationToken ?? CancellationToken.None)!;
+            if (httpResp is null) throw new NullReferenceException("Failed to send HTTP request");
+            logger?.LogInformation("Got sync response: {} bytes, {} elapsed", httpResp?.Content.Headers.ContentLength ?? -1, sw.Elapsed);
+            var deserializeSw = Stopwatch.StartNew();
+            var resp = await httpResp.Content.ReadFromJsonAsync<SyncResponse>(cancellationToken: cancellationToken ?? CancellationToken.None)!;
+            logger?.LogInformation("Deserialized sync response: {} bytes, {} elapsed, {} total", httpResp?.Content.Headers.ContentLength ?? -1, deserializeSw.Elapsed, sw.Elapsed);
+            var timeToWait = MinimumDelay.Subtract(sw.Elapsed);
+            if (timeToWait.TotalMilliseconds > 0)
+                await Task.Delay(timeToWait);
+            return resp;
         }
         catch (TaskCanceledException) {
             Console.WriteLine("Sync cancelled!");
@@ -57,7 +76,6 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
         var oldTimeout = Timeout;
         Timeout = 0;
         await foreach (var sync in EnumerateSyncAsync(cancellationToken)) {
-            logger?.LogInformation("Got sync response: {} bytes, {} elapsed", sync?.ToJson(ignoreNull: true, indent: false).Length ?? -1, sw.Elapsed);
             if (sync?.ToJson(ignoreNull: true, indent: false).Length < 250) {
                 emptyInitialSyncCount++;
                 if (emptyInitialSyncCount > 5) {
@@ -125,4 +143,4 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
     /// Event fired when an account data event is received
     /// </summary>
     public List<Func<StateEventResponse, Task>> AccountDataReceivedHandlers { get; } = new();
-}
+}
\ No newline at end of file
diff --git a/LibMatrix/Helpers/SyncStateResolver.cs b/LibMatrix/Helpers/SyncStateResolver.cs
index 3482be3..f40fa22 100644
--- a/LibMatrix/Helpers/SyncStateResolver.cs
+++ b/LibMatrix/Helpers/SyncStateResolver.cs
@@ -13,7 +13,7 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge
     public SyncFilter? Filter { get; set; }
     public bool FullState { get; set; } = false;
 
-    public SyncResponse? MergedState { get; set; } = null!;
+    public SyncResponse? MergedState { get; set; }
 
     private SyncHelper _syncHelper = new SyncHelper(homeserver, logger);
 
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index a64e167..9804e78 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -173,7 +173,8 @@ public class GenericRoom {
         var resText = await res.Content.ReadAsStringAsync();
         Console.WriteLine($"Members call response read in {sw.GetElapsedAndRestart()}");
         var result = await JsonSerializer.DeserializeAsync<ChunkedStateEventResponse>(await res.Content.ReadAsStreamAsync(), new JsonSerializerOptions() {
-            TypeInfoResolver = ChunkedStateEventResponseSerializerContext.Default
+            TypeInfoResolver = ChunkedStateEventResponseSerializerContext.Default,
+            
         });
         Console.WriteLine($"Members call deserialised in {sw.GetElapsedAndRestart()}");
         foreach (var resp in result.Chunk) {
diff --git a/LibMatrix/StateEvent.cs b/LibMatrix/StateEvent.cs
index 6d69820..6ca82f4 100644
--- a/LibMatrix/StateEvent.cs
+++ b/LibMatrix/StateEvent.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Text.Json;
 using System.Text.Json.Nodes;
@@ -29,26 +30,26 @@ public class StateEvent {
             return typeof(Dictionary<string, JsonObject>);
         }
 
-        // var eventType = KnownStateEventTypes.FirstOrDefault(x =>
-        // x.GetCustomAttributes<MatrixEventAttribute>()?.Any(y => y.EventName == type) ?? false);
-        var eventType = KnownStateEventTypesByName.GetValueOrDefault(type);
-
-        return eventType ?? typeof(UnknownEventContent);
+        return KnownStateEventTypesByName.GetValueOrDefault(type) ?? typeof(UnknownEventContent);
     }
 
+    private static readonly JsonSerializerOptions TypedContentSerializerOptions = new JsonSerializerOptions() {
+        Converters = {
+            new JsonFloatStringConverter(),
+            new JsonDoubleStringConverter(),
+            new JsonDecimalStringConverter()
+        }
+    };
+
     [JsonIgnore]
-    public EventContent TypedContent {
+    [SuppressMessage("ReSharper", "PropertyCanBeMadeInitOnly.Global")]
+    public EventContent? TypedContent {
         get {
-            if (Type == "m.receipt") {
-                return null!;
-            }
+            // if (Type == "m.receipt") {
+                // return null;
+            // }
             try {
-                var jse = new JsonSerializerOptions();
-                jse ??= new JsonSerializerOptions();
-                jse.Converters.Add(new JsonFloatStringConverter());
-                jse.Converters.Add(new JsonDoubleStringConverter());
-                jse.Converters.Add(new JsonDecimalStringConverter());
-                return (EventContent)RawContent.Deserialize(GetType, jse)!;
+                return (EventContent)RawContent.Deserialize(GetType, TypedContentSerializerOptions)!;
             }
             catch (JsonException e) {
                 Console.WriteLine(e);
@@ -59,7 +60,7 @@ public class StateEvent {
         }
         set {
             if (value is null) {
-                RawContent = null;
+                RawContent?.Clear();
             }
             else RawContent = JsonSerializer.Deserialize<JsonObject>(JsonSerializer.Serialize(value, value.GetType()));
         }
@@ -69,7 +70,7 @@ public class StateEvent {
     public string StateKey { get; set; } = "";
 
     [JsonPropertyName("type")]
-    public string Type { get; set; }
+    public required string Type { get; set; }
 
     [JsonPropertyName("replaces_state")]
     public string? ReplacesState { get; set; }
@@ -121,7 +122,7 @@ public class StateEvent {
 
     //debug
     [JsonIgnore]
-    public string dtype {
+    public string InternalSelfTypeName {
         get {
             var res = GetType().Name switch {
                 "StateEvent`1" => "StateEvent",
@@ -132,7 +133,7 @@ public class StateEvent {
     }
 
     [JsonIgnore]
-    public string cdtype => TypedContent.GetType().Name;
+    public string InternalContentTypeName => TypedContent?.GetType().Name ?? "null";
 }
 
 
@@ -152,9 +153,6 @@ public class StateEventResponse : StateEvent {
     [JsonPropertyName("event_id")]
     public string EventId { get; set; }
 
-    // [JsonPropertyName("user_id")]
-    // public string UserId { get; set; }
-
     [JsonPropertyName("replaces_state")]
     public new string ReplacesState { get; set; }
 
diff --git a/Utilities/LibMatrix.JsonSerializerContextGenerator/EventSerializerContexts.g.cs b/Utilities/LibMatrix.JsonSerializerContextGenerator/EventSerializerContexts.g.cs
new file mode 100644
index 0000000..1ca32dd
--- /dev/null
+++ b/Utilities/LibMatrix.JsonSerializerContextGenerator/EventSerializerContexts.g.cs
@@ -0,0 +1,99 @@
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.UnknownEventContent))]
+internal partial class UnknownEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.RoomMessageEventContent))]
+internal partial class RoomMessageEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.PresenceEventContent))]
+internal partial class PresenceEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomTypingEventContent))]
+internal partial class RoomTypingEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.ServerPolicyRuleEventContent))]
+internal partial class ServerPolicyRuleEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.UserPolicyRuleEventContent))]
+internal partial class UserPolicyRuleEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomPolicyRuleEventContent))]
+internal partial class RoomPolicyRuleEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomAliasEventContent))]
+internal partial class RoomAliasEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomAvatarEventContent))]
+internal partial class RoomAvatarEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomCanonicalAliasEventContent))]
+internal partial class RoomCanonicalAliasEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomCreateEventContent))]
+internal partial class RoomCreateEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomEncryptionEventContent))]
+internal partial class RoomEncryptionEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomGuestAccessEventContent))]
+internal partial class RoomGuestAccessEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomHistoryVisibilityEventContent))]
+internal partial class RoomHistoryVisibilityEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomJoinRulesEventContent))]
+internal partial class RoomJoinRulesEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomMemberEventContent))]
+internal partial class RoomMemberEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomNameEventContent))]
+internal partial class RoomNameEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomPinnedEventContent))]
+internal partial class RoomPinnedEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomPowerLevelEventContent))]
+internal partial class RoomPowerLevelEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomServerACLEventContent))]
+internal partial class RoomServerACLEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.RoomTopicEventContent))]
+internal partial class RoomTopicEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.SpaceChildEventContent))]
+internal partial class SpaceChildEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Spec.State.SpaceParentEventContent))]
+internal partial class SpaceParentEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Common.MjolnirShortcodeEventContent))]
+internal partial class MjolnirShortcodeEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+[System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+[System.Text.Json.Serialization.JsonSerializable(typeof(LibMatrix.EventTypes.Common.RoomEmotesEventContent))]
+internal partial class RoomEmotesEventContentSerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
diff --git a/Utilities/LibMatrix.JsonSerializerContextGenerator/LibMatrix.JsonSerializerContextGenerator.csproj b/Utilities/LibMatrix.JsonSerializerContextGenerator/LibMatrix.JsonSerializerContextGenerator.csproj
new file mode 100644
index 0000000..b90bf20
--- /dev/null
+++ b/Utilities/LibMatrix.JsonSerializerContextGenerator/LibMatrix.JsonSerializerContextGenerator.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net8.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+        <OutputType>exe</OutputType>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <ProjectReference Include="..\..\ArcaneLibs\ArcaneLibs\ArcaneLibs.csproj" />
+      <ProjectReference Include="..\..\LibMatrix\LibMatrix.csproj" />
+    </ItemGroup>
+
+</Project>
diff --git a/Utilities/LibMatrix.JsonSerializerContextGenerator/Program.cs b/Utilities/LibMatrix.JsonSerializerContextGenerator/Program.cs
new file mode 100644
index 0000000..76d6357
--- /dev/null
+++ b/Utilities/LibMatrix.JsonSerializerContextGenerator/Program.cs
@@ -0,0 +1,22 @@
+using System.Reflection;
+using ArcaneLibs;
+using ArcaneLibs.Extensions.Streams;
+using LibMatrix;
+using LibMatrix.Interfaces;
+
+// string binary = args.Length > 1 ? args[0] : Console.ReadLine()!;
+
+// var asm = Assembly.LoadFrom(binary);
+File.Delete("EventSerializerContexts.g.cs");
+var stream = File.OpenWrite("EventSerializerContexts.g.cs");
+var eventContentTypes = new ClassCollector<EventContent>().ResolveFromAllAccessibleAssemblies();
+stream.WriteString(string.Join('\n', eventContentTypes//.DistinctBy(x => x.Namespace)
+    .Select(x => $$"""
+                  [System.Text.Json.Serialization.JsonSourceGenerationOptions(WriteIndented = true)]
+                  [System.Text.Json.Serialization.JsonSerializable(typeof({{x.FullName}}))]
+                  internal partial class {{x.Name}}SerializerContext : System.Text.Json.Serialization.JsonSerializerContext { }
+
+                  """)));
+
+await stream.FlushAsync();
+stream.Close();
\ No newline at end of file