From 5ac0f0f957b4b27eaf42c44947f04a2fa3c79ef4 Mon Sep 17 00:00:00 2001 From: danil <61111955+danilwhale@users.noreply.github.com> Date: Fri, 28 Nov 2025 20:46:47 +0200 Subject: [PATCH 1/8] feat: update to .net 10 --- Ignitron.Loader/Ignitron.Loader.csproj | 4 ++-- Ignitron.TestMod/Ignitron.TestMod.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Ignitron.Loader/Ignitron.Loader.csproj b/Ignitron.Loader/Ignitron.Loader.csproj index df6de26..40c3214 100644 --- a/Ignitron.Loader/Ignitron.Loader.csproj +++ b/Ignitron.Loader/Ignitron.Loader.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable Loader @@ -9,7 +9,7 @@ - + diff --git a/Ignitron.TestMod/Ignitron.TestMod.csproj b/Ignitron.TestMod/Ignitron.TestMod.csproj index 9d465bf..37a1cf6 100644 --- a/Ignitron.TestMod/Ignitron.TestMod.csproj +++ b/Ignitron.TestMod/Ignitron.TestMod.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable From 0b9c12eeaaad87a9624a9e8653614b6470a63fec Mon Sep 17 00:00:00 2001 From: danil <61111955+danilwhale@users.noreply.github.com> Date: Fri, 28 Nov 2025 20:48:16 +0200 Subject: [PATCH 2/8] feat(loader): handle mp test versions as 0.12.0.X --- Ignitron.Loader/IgnitronLoader.cs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/Ignitron.Loader/IgnitronLoader.cs b/Ignitron.Loader/IgnitronLoader.cs index a29d0e4..30937b4 100644 --- a/Ignitron.Loader/IgnitronLoader.cs +++ b/Ignitron.Loader/IgnitronLoader.cs @@ -31,6 +31,9 @@ public sealed partial class IgnitronLoader : IExternalLoader [GeneratedRegex(@"(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?")] private static partial Regex VersionRegex(); + [GeneratedRegex(@"MP TEST v(\d+)")] + private static partial Regex MpTestVersionRegex(); + /// /// Version of the game installation /// @@ -95,13 +98,22 @@ void IExternalLoader.Init() Instance = this; // resolve game version - Match versionMatch = VersionRegex().Match(Game.VERSION); - GameVersion = new Version( - int.Parse(versionMatch.Groups[1].ValueSpan), - int.Parse(versionMatch.Groups[2].ValueSpan), - versionMatch.Groups.Count > 5 ? int.Parse(versionMatch.Groups[3].ValueSpan) : 0, - versionMatch.Groups.Count > 6 ? int.Parse(versionMatch.Groups[4].ValueSpan) : 0 - ); + if (Game.VERSION.StartsWith("MP TEST v")) + { + // edge case for obscure version format + Match versionMatch = MpTestVersionRegex().Match(Game.VERSION); + GameVersion = new Version(0, 12, 0, int.Parse(versionMatch.Groups[1].ValueSpan)); + } + else + { + Match versionMatch = VersionRegex().Match(Game.VERSION); + GameVersion = new Version( + int.Parse(versionMatch.Groups[1].ValueSpan), + int.Parse(versionMatch.Groups[2].ValueSpan), + versionMatch.Groups.Count > 5 ? int.Parse(versionMatch.Groups[3].ValueSpan) : 0, + versionMatch.Groups.Count > 6 ? int.Parse(versionMatch.Groups[4].ValueSpan) : 0 + ); + } // leave loader watermark Game.VERSION = $"{Game.VERSION}/ignitron {Version}"; From 4a0bade6eb909332423e3f1b0b9d5b3c1241b4a0 Mon Sep 17 00:00:00 2001 From: danil <61111955+danilwhale@users.noreply.github.com> Date: Fri, 28 Nov 2025 20:58:31 +0200 Subject: [PATCH 3/8] feat(loader): bump to 0.5.0 --- Ignitron.Loader/IgnitronLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ignitron.Loader/IgnitronLoader.cs b/Ignitron.Loader/IgnitronLoader.cs index 30937b4..9a2f1fa 100644 --- a/Ignitron.Loader/IgnitronLoader.cs +++ b/Ignitron.Loader/IgnitronLoader.cs @@ -18,7 +18,7 @@ public sealed partial class IgnitronLoader : IExternalLoader /// /// Installed version of the mod loader /// - public static Version Version { get; } = new(0, 4, 0); + public static Version Version { get; } = new(0, 5, 0); /// /// Current instance of the mod loader From bed72bc65e712f3631dfc99137efff136766cc57 Mon Sep 17 00:00:00 2001 From: danil <61111955+danilwhale@users.noreply.github.com> Date: Fri, 28 Nov 2025 20:58:56 +0200 Subject: [PATCH 4/8] test: update metadata to use mp test versions --- Ignitron.TestMod/Metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ignitron.TestMod/Metadata.json b/Ignitron.TestMod/Metadata.json index 03a5b42..ca0d01f 100644 --- a/Ignitron.TestMod/Metadata.json +++ b/Ignitron.TestMod/Metadata.json @@ -15,7 +15,7 @@ "dependencies": [ { "id": "allumeria", - "version": "0.11" + "version": "0.12.0.*" }, { "id": "harmony", From 8903a8a28a1537dc3bc14132b9d08502d9d48ef0 Mon Sep 17 00:00:00 2001 From: danil <61111955+danilwhale@users.noreply.github.com> Date: Fri, 28 Nov 2025 20:59:26 +0200 Subject: [PATCH 5/8] chore(readme): update for 0.5.0 --- README.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9905783..d8d6d44 100644 --- a/README.md +++ b/README.md @@ -18,20 +18,15 @@ done! ### for developers -> [!CAUTION] -> ***until 0.10***, you can ***NOT*** use Harmony patches or MonoMod in production environment. -> see https://github.com/MonoMod/MonoMod/issues/129 for more information. -> -> go [here](https://github.com/danilwhale/ignitron/blob/7a70196e36a65ebd7c7378ba54ad2c6dd738d1f3/README.md) to view -> steps for pre-0.10 setup +> [!IMPORTANT] +> since 0.5.0, you **CAN NOT** use modloader with versions before 0.12 or MP TEST due to upgrade to .NET 10.0 from .NET 9.0 #### workspace configuration > [!IMPORTANT] -> you need to have [.NET SDK 9.0](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) installed! +> you need to have [.NET SDK **10.0**](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) installed! -1. get and download [allumeria](https://store.steampowered.com/app/3516590/Allumeria/) (modloader is targetted for 0.11, - which is latest as of 06-11-2025) +1. get and download [allumeria](https://store.steampowered.com/app/3516590/Allumeria/) (modloader is targetted for MP TEST, which is in closed beta as of 2025-11-28) 2. set environment variable `ALLUMERIA_GAME_DIR` to game installation directory > [!TIP] From ad5ef9d4ffbf18519b94822632d16b0e4c1e2cf1 Mon Sep 17 00:00:00 2001 From: danil <61111955+danilwhale@users.noreply.github.com> Date: Fri, 28 Nov 2025 21:37:11 +0200 Subject: [PATCH 6/8] chore(readme): add note about mp test --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d8d6d44..2ed8dcf 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,14 @@ now look for `Ignitron.Loader.zip` link, under version changelog, and click on i make `mods` folder in the game directory and unpack `Ignitron.Loader.zip` into it. -done! - ### for developers -> [!IMPORTANT] +> [!CAUTION] > since 0.5.0, you **CAN NOT** use modloader with versions before 0.12 or MP TEST due to upgrade to .NET 10.0 from .NET 9.0 +> [!IMPORTANT] +> `MP TEST vX` versions are parsed as `0.12.0.X` + #### workspace configuration > [!IMPORTANT] @@ -44,4 +45,4 @@ dotnet build Ignitron.Loader/Ignitron.Loader.csproj #### developing a mod -use [the mod template](https://github.com/danilwhale/ignitron-mod-template) to make a new mod! \ No newline at end of file +use [the mod template](https://github.com/danilwhale/ignitron-mod-template) to make a new mod! From 852ee6c06ad3147708e59919f1a83abf92037672 Mon Sep 17 00:00:00 2001 From: danil <61111955+danilwhale@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:00:00 +0200 Subject: [PATCH 7/8] build: add missing assembles --- build/CommonAssemblies.props | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/CommonAssemblies.props b/build/CommonAssemblies.props index ab5b0e5..a8d8032 100644 --- a/build/CommonAssemblies.props +++ b/build/CommonAssemblies.props @@ -22,6 +22,11 @@ + + + + + From f050513b8b6d0064ae1e5add69ead7e8d7342422 Mon Sep 17 00:00:00 2001 From: danil <61111955+danilwhale@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:05:44 +0200 Subject: [PATCH 8/8] feat(loader): wip modded handshake --- .../Networking/ClientModDescription.cs | 3 + .../Networking/NetworkingHelper.cs | 44 +++++++++++++ .../Networking/PacketModdedRequest.cs | 65 +++++++++++++++++++ .../Networking/PacketModdedResponse.cs | 61 +++++++++++++++++ Ignitron.Loader/Patches/ClientPatches.cs | 50 ++++++++++++++ .../Patches/NetworkManagerPatches.cs | 19 ++++++ 6 files changed, 242 insertions(+) create mode 100644 Ignitron.Loader/Networking/ClientModDescription.cs create mode 100644 Ignitron.Loader/Networking/NetworkingHelper.cs create mode 100644 Ignitron.Loader/Networking/PacketModdedRequest.cs create mode 100644 Ignitron.Loader/Networking/PacketModdedResponse.cs create mode 100644 Ignitron.Loader/Patches/ClientPatches.cs create mode 100644 Ignitron.Loader/Patches/NetworkManagerPatches.cs diff --git a/Ignitron.Loader/Networking/ClientModDescription.cs b/Ignitron.Loader/Networking/ClientModDescription.cs new file mode 100644 index 0000000..8f08629 --- /dev/null +++ b/Ignitron.Loader/Networking/ClientModDescription.cs @@ -0,0 +1,3 @@ +namespace Ignitron.Loader.Networking; + +public readonly record struct ClientModDescription(string Id, Version Version); \ No newline at end of file diff --git a/Ignitron.Loader/Networking/NetworkingHelper.cs b/Ignitron.Loader/Networking/NetworkingHelper.cs new file mode 100644 index 0000000..d1fd4db --- /dev/null +++ b/Ignitron.Loader/Networking/NetworkingHelper.cs @@ -0,0 +1,44 @@ +namespace Ignitron.Loader.Networking; + +internal static class NetworkingHelper +{ + public static void DecodeMods(BinaryReader reader, ref List? destination) + { + if (destination == null) + { + destination = []; + } + else + { + destination.Clear(); + } + + int count = reader.ReadInt32(); + for (int i = 0; i < count ; i++) + { + string id = reader.ReadString(); + int major = reader.ReadInt32(), + minor = reader.ReadInt32(), + build = reader.ReadInt32(), + revision = reader.ReadInt32(); + destination.Add(new ClientModDescription( + id, + build >= 0 && revision >= 0 ? new Version(major, minor, build, revision) + : build >= 0 ? new Version(major, minor, build) + : new Version(major, minor))); + } + } + + public static void EncodeMods(BinaryWriter writer, List source) + { + writer.Write(source.Count); + foreach (ClientModDescription mod in source) + { + writer.Write(mod.Id); + writer.Write(mod.Version.Major); + writer.Write(mod.Version.Minor); + writer.Write(mod.Version.Build); + writer.Write(mod.Version.Revision); + } + } +} \ No newline at end of file diff --git a/Ignitron.Loader/Networking/PacketModdedRequest.cs b/Ignitron.Loader/Networking/PacketModdedRequest.cs new file mode 100644 index 0000000..0d3850d --- /dev/null +++ b/Ignitron.Loader/Networking/PacketModdedRequest.cs @@ -0,0 +1,65 @@ +using System.Text; +using Allumeria.Networking; +using Allumeria.Networking.Packets; + +namespace Ignitron.Loader.Networking; + +public struct PacketModdedRequest(List mods) : IPacket +{ + public PlayerConnection sender { get; set; } + + private List? _mods = mods; + + public void Decode(BinaryReader reader) + { + NetworkingHelper.DecodeMods(reader, ref _mods); + } + + public void Encode(BinaryWriter writer) + { + if (_mods == null) + { + throw new InvalidOperationException(); + } + + NetworkingHelper.EncodeMods(writer, _mods); + } + + public void PerformAction() + { + if (!NetworkManager.IsServer()) + { + return; + } + + if (_mods == null) + { + throw new InvalidOperationException(); + } + + // verify that all mods are present + List? missingMods = null; + + foreach (ModBox mod in IgnitronLoader.Instance.Mods) + { + bool missed = true; + + foreach (ClientModDescription other in _mods) + { + if (other.Id == mod.Metadata.Id && other.Version == mod.Metadata.Version) + { + missed = false; + break; + } + } + + if (missed) + { + missingMods ??= []; + missingMods.Add(new ClientModDescription(mod.Metadata.Id, mod.Metadata.Version)); + } + } + + NetworkManager.server.SendPacketTo(new PacketModdedResponse(missingMods == null, missingMods), sender); + } +} \ No newline at end of file diff --git a/Ignitron.Loader/Networking/PacketModdedResponse.cs b/Ignitron.Loader/Networking/PacketModdedResponse.cs new file mode 100644 index 0000000..2e708b6 --- /dev/null +++ b/Ignitron.Loader/Networking/PacketModdedResponse.cs @@ -0,0 +1,61 @@ +using System.Text; +using Allumeria; +using Allumeria.Networking; +using Allumeria.Networking.Packets; +using Allumeria.UI.Menus; + +namespace Ignitron.Loader.Networking; + +public struct PacketModdedResponse(bool success, List? missingMods) : IPacket +{ + public PlayerConnection sender { get; set; } + + private bool _success = success; + private List? _missingMods = missingMods; + + public void Decode(BinaryReader reader) + { + _success = reader.ReadBoolean(); + if (!_success) + { + NetworkingHelper.DecodeMods(reader, ref _missingMods); + } + } + + public void Encode(BinaryWriter writer) + { + writer.Write(_success); + if (!_success) + { + NetworkingHelper.EncodeMods(writer, _missingMods); + } + } + + public void PerformAction() + { + if (!NetworkManager.IsClient()) + { + return; + } + + if (_success) + { + NetworkManager.client.SendPacketToServer(new PacketHandshake()); + } + else + { + StringBuilder sb = new(); + sb.Append("Ignitron handshake failure.\nYou're missing the following mods:"); + foreach (ClientModDescription mod in _missingMods) + { + sb.AppendLine($" - {mod.Id} ({mod.Version})"); + } + + PauseMenu.LeaveGame(); + NetworkManager.client.Stop(); + Game.menu_mainMenu.show = false; + Game.menu_kicked.show = true; + Game.menu_kicked.card_reason.displayText = sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Ignitron.Loader/Patches/ClientPatches.cs b/Ignitron.Loader/Patches/ClientPatches.cs new file mode 100644 index 0000000..5cfe314 --- /dev/null +++ b/Ignitron.Loader/Patches/ClientPatches.cs @@ -0,0 +1,50 @@ +using System.Reflection; +using System.Reflection.Emit; +using Allumeria.Networking; +using Allumeria.Networking.Packets; +using HarmonyLib; +using Ignitron.Loader.Networking; + +namespace Ignitron.Loader.Patches; + +[HarmonyPatch] +internal static class ClientPatches +{ + private static readonly FieldInfo NetworkManagerClient = AccessTools.DeclaredField(typeof(NetworkManager), nameof(NetworkManager.client)); + private static readonly MethodInfo ClientSendPacketToServer = AccessTools.DeclaredMethod(typeof(Client), nameof(Client.SendPacketToServer)); + + private static readonly ConstructorInfo PacketHandshakeCtor = AccessTools.DeclaredConstructor(typeof(PacketHandshake)); + + [HarmonyPatch(typeof(Client), nameof(Client.Start))] + private static class StartPatch + { + private static IEnumerable Transpiler(IEnumerable instructions) + { + return new CodeMatcher(instructions) + .MatchStartForward( + new CodeMatch(OpCodes.Ldsfld, NetworkManagerClient), + new CodeMatch(OpCodes.Newobj, PacketHandshakeCtor), + new CodeMatch(OpCodes.Box, typeof(PacketHandshake)), + new CodeMatch(OpCodes.Callvirt, ClientSendPacketToServer)) + .ThrowIfInvalid("couldn't find NetworkManager.client.SendPacketToServer(new PacketHandshake())") + .Advance() + .RemoveInstructions(2) + .Insert( + CodeInstruction.Call(() => CreateRequest()), + new CodeInstruction(OpCodes.Box, typeof(PacketModdedRequest))) + .Instructions(); + } + + private static PacketModdedRequest CreateRequest() + { + List mods = []; + + foreach (ModBox mod in IgnitronLoader.Instance.Mods) + { + mods.Add(new ClientModDescription(mod.Metadata.Id, mod.Metadata.Version)); + } + + return new PacketModdedRequest(mods); + } + } +} \ No newline at end of file diff --git a/Ignitron.Loader/Patches/NetworkManagerPatches.cs b/Ignitron.Loader/Patches/NetworkManagerPatches.cs new file mode 100644 index 0000000..ad3d73b --- /dev/null +++ b/Ignitron.Loader/Patches/NetworkManagerPatches.cs @@ -0,0 +1,19 @@ +using Allumeria.Networking; +using HarmonyLib; +using Ignitron.Loader.Networking; + +namespace Ignitron.Loader.Patches; + +[HarmonyPatch] +internal static class NetworkManagerPatches +{ + [HarmonyPatch(typeof(NetworkManager), nameof(NetworkManager.RegisterPackets))] + private static class RegisterPacketsPatch + { + private static void Postfix() + { + IPacket.RegisterPacket(new PacketModdedRequest()); + IPacket.RegisterPacket(new PacketModdedResponse()); + } + } +} \ No newline at end of file