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"><AssemblyExplorer>
<Assembly Path="/home/root@Rory/.cache/NuGetPackages/microsoft.extensions.hosting.abstractions/7.0.0/lib/net7.0/Microsoft.Extensions.Hosting.Abstractions.dll" />
</AssemblyExplorer></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
|