From e0f4a2781cb942a7050b2da383427a1a1ef883de Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Sep 2025 13:09:45 +0200 Subject: [PATCH 1/8] Implemented #97, removed custom skins handeling for now, made custom json converters public --- src/Json/JsonMerger.cs | 107 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/Json/JsonMerger.cs diff --git a/src/Json/JsonMerger.cs b/src/Json/JsonMerger.cs new file mode 100644 index 0000000..68334c1 --- /dev/null +++ b/src/Json/JsonMerger.cs @@ -0,0 +1,107 @@ +using Il2CppSystem.IO; +using Newtonsoft.Json.Linq; + +namespace PolyMod.Json; +public static class JsonMerger +{ + public static JObject Merge(JObject original, JObject patch) + { + foreach (var property in patch.Properties().ToArray().ToList()) + { + if (property == null || property.Name == null) + continue; + + string propName = property.Name; + JToken? originalValue = original[propName]; + JToken patchValue = property.Value; + + if (originalValue == null) + { + original[propName] = patchValue; + continue; + } + + JObject? originalObj = originalValue.TryCast(); + JObject? patchObj = patchValue.TryCast(); + if (originalObj != null && patchObj != null) + { + Merge(originalObj, patchObj); + continue; + } + + JArray? originalArr = originalValue.TryCast(); + JArray? patchArr = patchValue.TryCast(); + if (originalArr != null && patchArr != null) + { + bool isSkins = propName.Equals("skins", StringComparison.OrdinalIgnoreCase); + JArray merged = MergeArrays(originalArr, patchArr, isSkins); + original[propName] = merged; + continue; + } + original[propName] = patchValue; + } + + return original; + } + + private static JArray MergeArrays(JArray original, JArray patch, bool isSkins) + { + var result = new JArray(original); + var patchList = patch._values.ToArray().ToList(); + + bool hasDirectValues = patchList.Any(v => + v.Type == JTokenType.String && + !v.ToString().StartsWith("+") && + !v.ToString().StartsWith("-")); + + if (!isSkins && hasDirectValues) + { + result = new JArray(); + } + + foreach (var token in patchList) + { + if (token.Type != JTokenType.String) + { + result.Add(token); + continue; + } + + string str = token.ToString(); + + if (str.StartsWith("+")) + { + string value = str.Substring(1); + if (!result._values.ToArray().Any(t => t.Type == JTokenType.String && t.ToString() == value)) + { + result.Add(value); + } + } + else if (str.StartsWith("-")) + { + string value = str.Substring(1); + var toRemove = result._values.ToArray() + .Where(t => t.Type == JTokenType.String && t.ToString() == value) + .ToList(); + foreach (var rem in toRemove) + result.Remove(rem); + } + else + { + if (isSkins) + { + if (!result._values.ToArray().Any(t => t.Type == JTokenType.String && t.ToString() == str)) + { + result.Add(str); + } + } + else + { + result.Add(str); + } + } + } + + return result; + } +} From 004277406badf76e06aa77d303c743128966de45 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Sep 2025 13:10:43 +0200 Subject: [PATCH 2/8] Forgot to commit.... --- src/Json/EnumCacheJson.cs | 2 +- src/Json/Vector2Json.cs | 2 +- src/Json/VersionJson.cs | 2 +- src/Loader.cs | 72 +-------------------------------------- 4 files changed, 4 insertions(+), 74 deletions(-) diff --git a/src/Json/EnumCacheJson.cs b/src/Json/EnumCacheJson.cs index 9b98432..399a557 100644 --- a/src/Json/EnumCacheJson.cs +++ b/src/Json/EnumCacheJson.cs @@ -3,7 +3,7 @@ namespace PolyMod.Json; -internal class EnumCacheJson : JsonConverter where T : struct, Enum +public class EnumCacheJson : JsonConverter where T : struct, Enum { public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { diff --git a/src/Json/Vector2Json.cs b/src/Json/Vector2Json.cs index 847cc62..4e46d53 100644 --- a/src/Json/Vector2Json.cs +++ b/src/Json/Vector2Json.cs @@ -4,7 +4,7 @@ namespace PolyMod.Json; -internal class Vector2Json : JsonConverter +public class Vector2Json : JsonConverter { public override Vector2 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { diff --git a/src/Json/VersionJson.cs b/src/Json/VersionJson.cs index 5ccf7ec..13959b7 100644 --- a/src/Json/VersionJson.cs +++ b/src/Json/VersionJson.cs @@ -3,7 +3,7 @@ namespace PolyMod.Json; -internal class VersionJson : JsonConverter +public class VersionJson : JsonConverter { public override Version? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { diff --git a/src/Loader.cs b/src/Loader.cs index bdf79a2..05111eb 100644 --- a/src/Loader.cs +++ b/src/Loader.cs @@ -571,7 +571,6 @@ public static void LoadGameLogicDataPatch(Mod mod, JObject gld, JObject patch) { try { - HandleSkins(gld, patch); foreach (JToken jtoken in patch.SelectTokens("$.*.*").ToArray()) { JObject? token = jtoken.TryCast(); @@ -616,7 +615,7 @@ public static void LoadGameLogicDataPatch(Mod mod, JObject gld, JObject patch) } } } - gld.Merge(patch, new() { MergeArrayHandling = MergeArrayHandling.Replace, MergeNullValueHandling = MergeNullValueHandling.Merge }); + gld = JsonMerger.Merge(gld, patch); Plugin.logger.LogInfo($"Registered patch from {mod.id} mod"); } catch (Exception e) @@ -633,73 +632,4 @@ public static void LoadAssetBundle(Mod mod, Mod.File file) AssetBundle.LoadFromMemory(file.bytes) ); } - - public static void HandleSkins(JObject gld, JObject patch) - { - foreach (JToken jtoken in patch.SelectTokens("$.tribeData.*").ToArray()) - { - JObject token = jtoken.Cast(); - - if (token["skins"] != null) - { - JArray skins = token["skins"].Cast(); - List skinsToRemove = new(); - List skinValues = skins._values.ToArray().ToList(); - foreach (var skin in skinValues) - { - string skinValue = skin.ToString(); - if (skinValue.StartsWith('-') && Enum.TryParse(skinValue.Substring(1), out _)) - { - skinsToRemove.Add(skinValue.Substring(1)); - } - else if (!Enum.TryParse(skinValue, out _)) - { - EnumCache.AddMapping(skinValue.ToLowerInvariant(), (SkinType)Registry.autoidx); - EnumCache.AddMapping(skinValue.ToLowerInvariant(), (SkinType)Registry.autoidx); - Registry.skinInfo.Add(new Visual.SkinInfo(Registry.autoidx, skinValue, null)); - Plugin.logger.LogInfo("Created mapping for skinType with id " + skinValue + " and index " + Registry.autoidx); - Registry.autoidx++; - } - } - foreach (var skin in Registry.skinInfo) - { - if (skins._values.Contains(skin.id)) - { - skins._values.Remove(skin.id); - skins._values.Add(skin.idx); - } - } - JToken originalSkins = gld.SelectToken(skins.Path, false); - if (originalSkins != null) - { - skins.Merge(originalSkins); - foreach (var skin in skinsToRemove) - { - skins._values.Remove(skin); - skins._values.Remove("-" + skin); - } - } - } - } - foreach (JToken jtoken in patch.SelectTokens("$.skinData.*").ToArray()) - { - JObject token = jtoken.Cast(); - string id = Util.GetJTokenName(token); - int index = Registry.skinInfo.FindIndex(t => t.id == id); - if (Registry.skinInfo.ElementAtOrDefault(index) != null) - { - SkinData skinData = new(); - if (token["color"] != null) - { - skinData.color = (int)token["color"]; - } - if (token["language"] != null) - { - skinData.language = token["language"].ToString(); - } - Registry.skinInfo[index] = new Visual.SkinInfo(Registry.skinInfo[index].idx, Registry.skinInfo[index].id, skinData); - } - } - patch.Remove("skinData"); - } } From 6da56c7af1a167f95edcca57682272aea27c71b5 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Sep 2025 13:47:48 +0200 Subject: [PATCH 3/8] Refactored patch loading (Skins handling doesnt work yet) --- src/Loader.cs | 128 +++++++++++++++++++++++-------------------- src/Managers/Loc.cs | 4 +- src/Managers/Main.cs | 52 +++++++++++++++++- 3 files changed, 121 insertions(+), 63 deletions(-) diff --git a/src/Loader.cs b/src/Loader.cs index 05111eb..305402e 100644 --- a/src/Loader.cs +++ b/src/Loader.cs @@ -20,23 +20,25 @@ namespace PolyMod; public static class Loader { - internal static Dictionary typeMappings = new() + internal record TypeMapping(Type type, bool shouldCreateCache = true); + internal static Dictionary typeMappings = new() { - { "tribeData", typeof(TribeData.Type) }, - { "techData", typeof(TechData.Type) }, - { "unitData", typeof(UnitData.Type) }, - { "improvementData", typeof(ImprovementData.Type) }, - { "terrainData", typeof(Polytopia.Data.TerrainData.Type) }, - { "resourceData", typeof(ResourceData.Type) }, - { "taskData", typeof(TaskData.Type) }, - { "tribeAbility", typeof(TribeAbility.Type) }, - { "unitAbility", typeof(UnitAbility.Type) }, - { "improvementAbility", typeof(ImprovementAbility.Type) }, - { "playerAbility", typeof(PlayerAbility.Type) }, - { "weaponData", typeof(UnitData.WeaponEnum) } + { "tribeData", new TypeMapping(typeof(TribeData.Type)) }, + { "techData", new TypeMapping(typeof(TechData.Type)) }, + { "unitData", new TypeMapping(typeof(UnitData.Type)) }, + { "improvementData", new TypeMapping(typeof(ImprovementData.Type)) }, + { "terrainData", new TypeMapping(typeof(Polytopia.Data.TerrainData.Type)) }, + { "resourceData", new TypeMapping(typeof(ResourceData.Type)) }, + { "taskData", new TypeMapping(typeof(TaskData.Type)) }, + { "tribeAbility", new TypeMapping(typeof(TribeAbility.Type)) }, + { "unitAbility", new TypeMapping(typeof(UnitAbility.Type)) }, + { "improvementAbility", new TypeMapping(typeof(ImprovementAbility.Type)) }, + { "playerAbility", new TypeMapping(typeof(PlayerAbility.Type)) }, + { "weaponData", new TypeMapping(typeof(UnitData.WeaponEnum)) }, + { "skinData", new TypeMapping(typeof(SkinData), false) } }; internal static List gamemodes = new(); - private static readonly Dictionary> typeHandlers = new() + internal static readonly Dictionary> typeHandlers = new() { [typeof(TribeData.Type)] = new((token, duringEnumCacheCreation) => { @@ -49,6 +51,31 @@ public static class Loader } else { + if (token["skins"] != null) // Doesnt work, and I do not know why! Good luck. + { + JArray skins = token["skins"].Cast(); + List skinValues = skins._values.ToArray().ToList(); + foreach (var skin in skinValues) + { + string skinValue = skin.ToString(); + if (!Enum.TryParse(CultureInfo.CurrentCulture.TextInfo.ToTitleCase(skinValue), out _)) + { + EnumCache.AddMapping(skinValue.ToLowerInvariant(), (SkinType)Registry.autoidx); + EnumCache.AddMapping(skinValue.ToLowerInvariant(), (SkinType)Registry.autoidx); + Registry.skinInfo.Add(new Visual.SkinInfo(Registry.autoidx, skinValue, null)); + Plugin.logger.LogInfo("Created mapping for skinType with id " + skinValue + " and index " + Registry.autoidx); + Registry.autoidx++; + } + } + foreach (var skin in Registry.skinInfo) + { + if (skins._values.Contains(skin.id)) + { + skins._values.Remove(skin.id); + skins._values.Add(skin.idx); + } + } + } if (token["preview"] != null) { Visual.PreviewTile[] preview = JsonSerializer.Deserialize(token["preview"].ToString())!; @@ -128,7 +155,26 @@ public static class Loader } PrefabManager.resources.TryAdd((ResourceData.Type)Registry.autoidx, PrefabManager.resources[resourcePrefabType]); } - }) + }), + + [typeof(SkinData)] = new((token, duringEnumCacheCreation) => + { + string id = Util.GetJTokenName(token); + int index = Registry.skinInfo.FindIndex(t => t.id == id); + if (Registry.skinInfo.ElementAtOrDefault(index) != null) + { + SkinData skinData = new(); + if (token["color"] != null) + { + skinData.color = (int)token["color"]; + } + if (token["language"] != null) + { + skinData.language = token["language"].ToString(); + } + Registry.skinInfo[index] = new Visual.SkinInfo(Registry.skinInfo[index].idx, Registry.skinInfo[index].id, skinData); + } + }), }; public record GameModeButtonsInformation(int gameModeIndex, UIButtonBase.ButtonAction action, int? buttonIndex, Sprite? sprite); @@ -144,7 +190,13 @@ public static void AddGameModeButton(string id, UIButtonBase.ButtonAction action public static void AddPatchDataType(string typeId, Type type) { if (!typeMappings.ContainsKey(typeId)) - typeMappings.Add(typeId, type); + typeMappings.Add(typeId, new TypeMapping(type)); + } + + public static void AddPatchDataType(string typeId, Type type, bool shouldCreateCache) + { + if (!typeMappings.ContainsKey(typeId)) + typeMappings.Add(typeId, new TypeMapping(type, shouldCreateCache)); } internal static void LoadMods(Dictionary mods) @@ -571,50 +623,6 @@ public static void LoadGameLogicDataPatch(Mod mod, JObject gld, JObject patch) { try { - foreach (JToken jtoken in patch.SelectTokens("$.*.*").ToArray()) - { - JObject? token = jtoken.TryCast(); - if (token != null) - { - string dataType = Util.GetJTokenName(token, 2); - if (typeMappings.TryGetValue(dataType, out Type? targetType)) - { - if (token["idx"] != null && (int)token["idx"] == -1) - { - string id = Util.GetJTokenName(token); - token["idx"] = Registry.autoidx; - MethodInfo? methodInfo = typeof(EnumCache<>).MakeGenericType(targetType).GetMethod("AddMapping"); - if (methodInfo != null) - { - methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); - methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); - - if (typeHandlers.TryGetValue(targetType, out var handler)) - { - handler(token, true); - } - Plugin.logger.LogInfo("Created mapping for " + targetType.ToString() + " with id " + id + " and index " + Registry.autoidx); - Registry.autoidx++; - } - } - } - } - } - foreach (JToken jtoken in patch.SelectTokens("$.*.*").ToArray()) - { - JObject? token = jtoken.TryCast(); - if (token != null) - { - string dataType = Util.GetJTokenName(token, 2); - if (typeMappings.TryGetValue(dataType, out Type? targetType)) - { - if (typeHandlers.TryGetValue(targetType, out var handler)) - { - handler(token, false); - } - } - } - } gld = JsonMerger.Merge(gld, patch); Plugin.logger.LogInfo($"Registered patch from {mod.id} mod"); } diff --git a/src/Managers/Loc.cs b/src/Managers/Loc.cs index 5b1c70f..d347c1f 100644 --- a/src/Managers/Loc.cs +++ b/src/Managers/Loc.cs @@ -50,9 +50,9 @@ private static bool Localization_Get(ref string key, Il2CppReferenceArray).MakeGenericType(targetType).GetMethod("TryGetName"); + MethodInfo? methodInfo = typeof(EnumCache<>).MakeGenericType(typeMapping.type).GetMethod("TryGetName"); if (methodInfo != null) { object?[] parameters = { idx, null }; diff --git a/src/Managers/Main.cs b/src/Managers/Main.cs index 68892ed..43390e0 100644 --- a/src/Managers/Main.cs +++ b/src/Managers/Main.cs @@ -1,8 +1,10 @@ using BepInEx.Unity.IL2CPP.Logging; using HarmonyLib; +using Il2CppSystem.Linq; using Newtonsoft.Json.Linq; using Polytopia.Data; using PolytopiaBackendBase.Game; +using System.Reflection; using System.Diagnostics; using System.Text; using System.Text.Json; @@ -27,11 +29,56 @@ public static class Main [HarmonyPrefix] [HarmonyPatch(typeof(GameLogicData), nameof(GameLogicData.AddGameLogicPlaceholders))] - private static void GameLogicData_Parse(GameLogicData __instance, JObject rootObject) + private static void GameLogicData_Parse(GameLogicData __instance, ref JObject rootObject) { if (!fullyInitialized) { Load(rootObject); + foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) + { + JObject? token = jtoken.TryCast(); + if (token != null) + { + string dataType = Util.GetJTokenName(token, 2); + if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) + { + if (token["idx"] != null && (int)token["idx"] == -1 && typeMapping.shouldCreateCache) + { + Type targetType = typeMapping.type; + string id = Util.GetJTokenName(token); + token["idx"] = Registry.autoidx; + MethodInfo? methodInfo = typeof(EnumCache<>).MakeGenericType(targetType).GetMethod("AddMapping"); + if (methodInfo != null) + { + methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); + methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); + + if (Loader.typeHandlers.TryGetValue(targetType, out var handler)) + { + handler(token, true); + } + Plugin.logger.LogInfo("Created mapping for " + targetType.ToString() + " with id " + id + " and index " + Registry.autoidx); + Registry.autoidx++; + } + } + } + } + } + foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) + { + JObject? token = jtoken.TryCast(); + if (token != null) + { + string dataType = Util.GetJTokenName(token, 2); + if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) + { + if (Loader.typeHandlers.TryGetValue(typeMapping.type, out var handler)) + { + handler(token, false); + } + } + } + } foreach (System.Collections.Generic.KeyValuePair item in Registry.prefabNames) { UnitData.Type unitPrefabType = UnitData.Type.Scout; @@ -102,6 +149,9 @@ private static void GameLogicData_Parse(GameLogicData __instance, JObject rootOb } } fullyInitialized = true; + string formattedJson = rootObject.ToString(Newtonsoft.Json.Formatting.Indented); + File.WriteAllText(Path.Combine(Plugin.BASE_PATH, "output.json"), formattedJson); + Console.Write("Saved"); } } From 6eb116689832ae7bf6b62e22f8eff0c3fe2746d3 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Sep 2025 14:15:14 +0200 Subject: [PATCH 4/8] Fixed custom skins processing, finished refactor, closes #97, closes #102 --- src/Loader.cs | 15 +++++++++++---- src/Managers/Main.cs | 3 --- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Loader.cs b/src/Loader.cs index 305402e..4e239a8 100644 --- a/src/Loader.cs +++ b/src/Loader.cs @@ -51,7 +51,7 @@ internal record TypeMapping(Type type, bool shouldCreateCache = true); } else { - if (token["skins"] != null) // Doesnt work, and I do not know why! Good luck. + if (token["skins"] != null) { JArray skins = token["skins"].Cast(); List skinValues = skins._values.ToArray().ToList(); @@ -67,14 +67,21 @@ internal record TypeMapping(Type type, bool shouldCreateCache = true); Registry.autoidx++; } } + Il2CppSystem.Collections.Generic.List modifiedSkins = skins._values; foreach (var skin in Registry.skinInfo) { - if (skins._values.Contains(skin.id)) + if (modifiedSkins.Contains(skin.id)) { - skins._values.Remove(skin.id); - skins._values.Add(skin.idx); + modifiedSkins.Remove(skin.id); + modifiedSkins.Add(skin.idx.ToString()); } } + JArray newSkins = new JArray(); + foreach (var item in modifiedSkins) + { + newSkins.Add(item); + } + token["skins"] = newSkins; } if (token["preview"] != null) { diff --git a/src/Managers/Main.cs b/src/Managers/Main.cs index 43390e0..5d8d892 100644 --- a/src/Managers/Main.cs +++ b/src/Managers/Main.cs @@ -149,9 +149,6 @@ private static void GameLogicData_Parse(GameLogicData __instance, ref JObject ro } } fullyInitialized = true; - string formattedJson = rootObject.ToString(Newtonsoft.Json.Formatting.Indented); - File.WriteAllText(Path.Combine(Plugin.BASE_PATH, "output.json"), formattedJson); - Console.Write("Saved"); } } From 14a8d57c8f448e2375571f9df29787bc1736a041 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Sep 2025 14:28:41 +0200 Subject: [PATCH 5/8] Restructured a bit --- src/Loader.cs | 125 +++++++++++++++++++++++++++++++++++++++++++ src/Managers/Main.cs | 121 ++--------------------------------------- 2 files changed, 129 insertions(+), 117 deletions(-) diff --git a/src/Loader.cs b/src/Loader.cs index 4e239a8..6214461 100644 --- a/src/Loader.cs +++ b/src/Loader.cs @@ -647,4 +647,129 @@ public static void LoadAssetBundle(Mod mod, Mod.File file) AssetBundle.LoadFromMemory(file.bytes) ); } + + internal static void ProcessGameLogicData(GameLogicData gameLogicData, JObject rootObject) + { + try + { + foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) + { + JObject? token = jtoken.TryCast(); + if (token != null) + { + string dataType = Util.GetJTokenName(token, 2); + if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) + { + if (token["idx"] != null && (int)token["idx"] == -1 && typeMapping.shouldCreateCache) + { + Type targetType = typeMapping.type; + string id = Util.GetJTokenName(token); + token["idx"] = Registry.autoidx; + MethodInfo? methodInfo = typeof(EnumCache<>).MakeGenericType(targetType).GetMethod("AddMapping"); + if (methodInfo != null) + { + methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); + methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); + + if (Loader.typeHandlers.TryGetValue(targetType, out var handler)) + { + handler(token, true); + } + Plugin.logger.LogInfo("Created mapping for " + targetType.ToString() + " with id " + id + " and index " + Registry.autoidx); + Registry.autoidx++; + } + } + } + } + } + foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) + { + JObject? token = jtoken.TryCast(); + if (token != null) + { + string dataType = Util.GetJTokenName(token, 2); + if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) + { + if (Loader.typeHandlers.TryGetValue(typeMapping.type, out var handler)) + { + handler(token, false); + } + } + } + } + foreach (System.Collections.Generic.KeyValuePair item in Registry.prefabNames) + { + UnitData.Type unitPrefabType = UnitData.Type.Scout; + string prefabId = item.Value; + if (Enum.TryParse(prefabId, out UnitData.Type parsedType)) + { + unitPrefabType = parsedType; + PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); + } + else + { + KeyValuePair prefabInfo = Registry.unitPrefabs.FirstOrDefault(kv => kv.Key.name == prefabId); + if (!EqualityComparer.Default.Equals(prefabInfo.Key, default)) + { + PrefabManager.units.TryAdd(item.Key, prefabInfo.Value); + } + else + { + PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); + } + } + } + foreach (Visual.SkinInfo skin in Registry.skinInfo) + { + if (skin.skinData != null) + gameLogicData.skinData[(SkinType)skin.idx] = skin.skinData; + } + foreach (KeyValuePair entry in Main.embarkNames) + { + try + { + UnitData.Type unit = EnumCache.GetType(entry.Key); + UnitData.Type newUnit = EnumCache.GetType(entry.Value); + Main.embarkOverrides[unit] = newUnit; + Plugin.logger.LogInfo($"Embark unit type for {entry.Key} is now {entry.Value}"); + } + catch + { + Plugin.logger.LogError($"Embark unit type for {entry.Key} is not valid: {entry.Value}"); + } + } + foreach (KeyValuePair entry in Main.attractsResourceNames) + { + try + { + ImprovementData.Type improvement = EnumCache.GetType(entry.Key); + ResourceData.Type resource = EnumCache.GetType(entry.Value); + Main.attractsResourceOverrides[improvement] = resource; + Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts {entry.Value}"); + } + catch + { + Plugin.logger.LogError($"Improvement {entry.Key} resource type is not valid: {entry.Value}"); + } + } + foreach (KeyValuePair entry in Main.attractsTerrainNames) + { + try + { + ImprovementData.Type improvement = EnumCache.GetType(entry.Key); + Polytopia.Data.TerrainData.Type terrain = EnumCache.GetType(entry.Value); + Main.attractsTerrainOverrides[improvement] = terrain; + Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts on {entry.Value}"); + } + catch + { + Plugin.logger.LogError($"Improvement {entry.Key} terrain type is not valid: {entry.Value}"); + } + } + } + catch (Exception e) + { + Plugin.logger.LogError($"Error on processing modified game logic data : {e.Message}"); + } + } } diff --git a/src/Managers/Main.cs b/src/Managers/Main.cs index 5d8d892..fbfcceb 100644 --- a/src/Managers/Main.cs +++ b/src/Managers/Main.cs @@ -33,121 +33,7 @@ private static void GameLogicData_Parse(GameLogicData __instance, ref JObject ro { if (!fullyInitialized) { - Load(rootObject); - foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) - { - JObject? token = jtoken.TryCast(); - if (token != null) - { - string dataType = Util.GetJTokenName(token, 2); - if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) - { - if (token["idx"] != null && (int)token["idx"] == -1 && typeMapping.shouldCreateCache) - { - Type targetType = typeMapping.type; - string id = Util.GetJTokenName(token); - token["idx"] = Registry.autoidx; - MethodInfo? methodInfo = typeof(EnumCache<>).MakeGenericType(targetType).GetMethod("AddMapping"); - if (methodInfo != null) - { - methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); - methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); - - if (Loader.typeHandlers.TryGetValue(targetType, out var handler)) - { - handler(token, true); - } - Plugin.logger.LogInfo("Created mapping for " + targetType.ToString() + " with id " + id + " and index " + Registry.autoidx); - Registry.autoidx++; - } - } - } - } - } - foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) - { - JObject? token = jtoken.TryCast(); - if (token != null) - { - string dataType = Util.GetJTokenName(token, 2); - if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) - { - if (Loader.typeHandlers.TryGetValue(typeMapping.type, out var handler)) - { - handler(token, false); - } - } - } - } - foreach (System.Collections.Generic.KeyValuePair item in Registry.prefabNames) - { - UnitData.Type unitPrefabType = UnitData.Type.Scout; - string prefabId = item.Value; - if (Enum.TryParse(prefabId, out UnitData.Type parsedType)) - { - unitPrefabType = parsedType; - PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); - } - else - { - KeyValuePair prefabInfo = Registry.unitPrefabs.FirstOrDefault(kv => kv.Key.name == prefabId); - if (!EqualityComparer.Default.Equals(prefabInfo.Key, default)) - { - PrefabManager.units.TryAdd(item.Key, prefabInfo.Value); - } - else - { - PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); - } - } - } - foreach (Visual.SkinInfo skin in Registry.skinInfo) - { - if (skin.skinData != null) - __instance.skinData[(SkinType)skin.idx] = skin.skinData; - } - foreach (KeyValuePair entry in embarkNames) - { - try - { - UnitData.Type unit = EnumCache.GetType(entry.Key); - UnitData.Type newUnit = EnumCache.GetType(entry.Value); - embarkOverrides[unit] = newUnit; - Plugin.logger.LogInfo($"Embark unit type for {entry.Key} is now {entry.Value}"); - } - catch - { - Plugin.logger.LogError($"Embark unit type for {entry.Key} is not valid: {entry.Value}"); - } - } - foreach (KeyValuePair entry in attractsResourceNames) - { - try - { - ImprovementData.Type improvement = EnumCache.GetType(entry.Key); - ResourceData.Type resource = EnumCache.GetType(entry.Value); - attractsResourceOverrides[improvement] = resource; - Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts {entry.Value}"); - } - catch - { - Plugin.logger.LogError($"Improvement {entry.Key} resource type is not valid: {entry.Value}"); - } - } - foreach (KeyValuePair entry in attractsTerrainNames) - { - try - { - ImprovementData.Type improvement = EnumCache.GetType(entry.Key); - Polytopia.Data.TerrainData.Type terrain = EnumCache.GetType(entry.Value); - attractsTerrainOverrides[improvement] = terrain; - Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts on {entry.Value}"); - } - catch - { - Plugin.logger.LogError($"Improvement {entry.Key} terrain type is not valid: {entry.Value}"); - } - } + Load(__instance, rootObject); fullyInitialized = true; } } @@ -408,7 +294,7 @@ internal static void Init() stopwatch.Stop(); } - internal static void Load(JObject gameLogicdata) + internal static void Load(GameLogicData gameLogicData, JObject json) { stopwatch.Start(); Loc.BuildAndLoadLocalization( @@ -432,7 +318,7 @@ internal static void Load(JObject gameLogicdata) { Loader.LoadGameLogicDataPatch( mod, - gameLogicdata, + json, JObject.Parse(new StreamReader(new MemoryStream(file.bytes)).ReadToEnd()) ); continue; @@ -465,6 +351,7 @@ internal static void Load(JObject gameLogicdata) { TechItem.techTierFirebaseId.Add($"tech_research_{i}"); } + Loader.ProcessGameLogicData(gameLogicData, json); stopwatch.Stop(); Plugin.logger.LogInfo($"Loaded all mods in {stopwatch.ElapsedMilliseconds}ms"); } From 12069d0259d099e9844fdbfca2135b6623b8ff12 Mon Sep 17 00:00:00 2001 From: IExploitableMan Date: Sat, 6 Sep 2025 21:02:54 +0300 Subject: [PATCH 6/8] Add missing docstrings --- src/Json/JsonMerger.cs | 17 +++++++++++++++++ src/Loader.cs | 5 +++++ src/Managers/Main.cs | 1 + src/Managers/Visual.cs | 10 +++++++++- 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/Json/JsonMerger.cs b/src/Json/JsonMerger.cs index 68334c1..3db0e7c 100644 --- a/src/Json/JsonMerger.cs +++ b/src/Json/JsonMerger.cs @@ -2,8 +2,18 @@ using Newtonsoft.Json.Linq; namespace PolyMod.Json; + +/// +/// Provides functionality to merge JSON objects, with special handling for arrays. +/// public static class JsonMerger { + /// + /// Merges a patch JObject into an original JObject. + /// + /// The original JObject. + /// The patch JObject to merge. + /// The merged JObject. public static JObject Merge(JObject original, JObject patch) { foreach (var property in patch.Properties().ToArray().ToList()) @@ -44,6 +54,13 @@ public static JObject Merge(JObject original, JObject patch) return original; } + /// + /// Merges two JArrays, with special handling for adding and removing elements. + /// + /// The original JArray. + /// The patch JArray to merge. + /// A flag to indicate if the array is a 'skins' array, which has special merging logic. + /// The merged JArray. private static JArray MergeArrays(JArray original, JArray patch, bool isSkins) { var result = new JArray(original); diff --git a/src/Loader.cs b/src/Loader.cs index 8734eaa..1869822 100644 --- a/src/Loader.cs +++ b/src/Loader.cs @@ -759,6 +759,11 @@ public static void LoadAssetBundle(Mod mod, Mod.File file) ); } + /// + /// Processes the merged game logic data after all mods have been loaded and patched. + /// + /// The game logic data object to populate. + /// The root JObject of the merged game logic data. internal static void ProcessGameLogicData(GameLogicData gameLogicData, JObject rootObject) { try diff --git a/src/Managers/Main.cs b/src/Managers/Main.cs index fd1ae74..d62bcf8 100644 --- a/src/Managers/Main.cs +++ b/src/Managers/Main.cs @@ -385,6 +385,7 @@ internal static void Init() /// Loads all mod content. /// /// The game logic data to patch. + /// The JSON object representing the game logic data. internal static void Load(GameLogicData gameLogicData, JObject json) { stopwatch.Start(); diff --git a/src/Managers/Visual.cs b/src/Managers/Visual.cs index f2ae379..acd12ba 100644 --- a/src/Managers/Visual.cs +++ b/src/Managers/Visual.cs @@ -643,12 +643,16 @@ private static void UpdateVisualPart(SkinVisualsReference.VisualPart visualPart, { if (visualPart.outlineRenderer.spriteRenderer != null) visualPart.outlineRenderer.spriteRenderer.sprite = outlineSprite; - else if (visualpart.outlineRenderer.polytopiaSpriteRenderer != null) + else if (visualPart.outlineRenderer.polytopiaSpriteRenderer != null) visualPart.outlineRenderer.polytopiaSpriteRenderer.sprite = outlineSprite; } } /// Builds a sprite from raw byte data. + /// The raw byte data of the image. + /// The pivot point of the sprite. + /// The number of pixels per unit for the sprite. + /// The created sprite. public static Sprite BuildSprite(byte[] data, Vector2? pivot = null, float pixelsPerUnit = 2112f) { Texture2D texture = new(1, 1, TextureFormat.RGBA32, true); @@ -665,6 +669,10 @@ public static Sprite BuildSprite(byte[] data, Vector2? pivot = null, float pixel } /// Builds a sprite from a texture. + /// The texture to create the sprite from. + /// The pivot point of the sprite. + /// The number of pixels per unit for the sprite. + /// The created sprite. public static Sprite BuildSpriteWithTexture(Texture2D texture, Vector2? pivot = null, float? pixelsPerUnit = 2112f) { return Sprite.Create( From 1acbe4666cf95f28800482aa9315d21422e62f0e Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 7 Sep 2025 11:06:43 +0200 Subject: [PATCH 7/8] Fixed skinData processing, split gld processing method, added stack traces in errors instead of error messages --- src/Loader.cs | 206 +++++++++++++++++++++++++------------------------- 1 file changed, 102 insertions(+), 104 deletions(-) diff --git a/src/Loader.cs b/src/Loader.cs index 1869822..5b285d6 100644 --- a/src/Loader.cs +++ b/src/Loader.cs @@ -181,21 +181,8 @@ internal record TypeMapping(Type type, bool shouldCreateCache = true); [typeof(SkinData)] = new((token, duringEnumCacheCreation) => { - string id = Util.GetJTokenName(token); - int index = Registry.skinInfo.FindIndex(t => t.id == id); - if (Registry.skinInfo.ElementAtOrDefault(index) != null) - { - SkinData skinData = new(); - if (token["color"] != null) - { - skinData.color = (int)token["color"]; - } - if (token["language"] != null) - { - skinData.language = token["language"].ToString(); - } - Registry.skinInfo[index] = new Visual.SkinInfo(Registry.skinInfo[index].idx, Registry.skinInfo[index].id, skinData); - } + var prop = token.Parent.Cast(); + prop.Replace(new JProperty(prop.Name.ToLower(), prop.Value)); }), }; @@ -488,7 +475,7 @@ public static void LoadLocalizationFile(Mod mod, Mod.File file) } catch (Exception e) { - Plugin.logger.LogError($"Error on loading locatization from {mod.id} mod: {e.Message}"); + Plugin.logger.LogError($"Error on loading locatization from {mod.id} mod: {e.StackTrace}"); } } @@ -569,7 +556,7 @@ public static void UpdateSprite(string name) } catch (Exception e) { - Plugin.logger.LogError($"Error on loading sprite data from {mod.id} mod: {e.Message}"); + Plugin.logger.LogError($"Error on loading sprite data from {mod.id} mod: {e.StackTrace}"); return null; } } @@ -628,7 +615,7 @@ public static void LoadPrefabInfoFile(Mod mod, Mod.File file) } catch (Exception e) { - Plugin.logger.LogError($"Error on loading prefab info from {mod.id} mod: {e.Message}"); + Plugin.logger.LogError($"Error on loading prefab info from {mod.id} mod: {e.StackTrace}"); } } @@ -741,7 +728,7 @@ public static void LoadGameLogicDataPatch(Mod mod, JObject gld, JObject patch) } catch (Exception e) { - Plugin.logger.LogError($"Error on loading patch from {mod.id} mod: {e.Message}"); + Plugin.logger.LogError($"Error on loading patch from {mod.id} mod: {e.StackTrace}"); mod.status = Mod.Status.Error; } } @@ -768,124 +755,135 @@ internal static void ProcessGameLogicData(GameLogicData gameLogicData, JObject r { try { - foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) + CreateMappings(rootObject); + ProcessPrefabs(); + ProcessEmbark(); + ProcessAttract(); + } + catch (Exception e) + { + Plugin.logger.LogError($"Error on processing modified game logic data : {e.StackTrace}"); + } + } + internal static void CreateMappings(JObject rootObject) + { + foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) + { + JObject? token = jtoken.TryCast(); + if (token != null) { - JObject? token = jtoken.TryCast(); - if (token != null) + string dataType = Util.GetJTokenName(token, 2); + if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) { - string dataType = Util.GetJTokenName(token, 2); - if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) + if (token["idx"] != null && (int)token["idx"] == -1 && typeMapping.shouldCreateCache) { - if (token["idx"] != null && (int)token["idx"] == -1 && typeMapping.shouldCreateCache) + Type targetType = typeMapping.type; + string id = Util.GetJTokenName(token); + token["idx"] = Registry.autoidx; + MethodInfo? methodInfo = typeof(EnumCache<>).MakeGenericType(targetType).GetMethod("AddMapping"); + if (methodInfo != null) { - Type targetType = typeMapping.type; - string id = Util.GetJTokenName(token); - token["idx"] = Registry.autoidx; - MethodInfo? methodInfo = typeof(EnumCache<>).MakeGenericType(targetType).GetMethod("AddMapping"); - if (methodInfo != null) + methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); + methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); + + if (Loader.typeHandlers.TryGetValue(targetType, out var handler)) { - methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); - methodInfo.Invoke(null, new object[] { id, Registry.autoidx }); - - if (Loader.typeHandlers.TryGetValue(targetType, out var handler)) - { - handler(token, true); - } - Plugin.logger.LogInfo("Created mapping for " + targetType.ToString() + " with id " + id + " and index " + Registry.autoidx); - Registry.autoidx++; + handler(token, true); } + Plugin.logger.LogInfo("Created mapping for " + targetType.ToString() + " with id " + id + " and index " + Registry.autoidx); + Registry.autoidx++; } } } } - foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) + } + foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) + { + JObject? token = jtoken.TryCast(); + if (token != null) { - JObject? token = jtoken.TryCast(); - if (token != null) + string dataType = Util.GetJTokenName(token, 2); + if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) { - string dataType = Util.GetJTokenName(token, 2); - if (Loader.typeMappings.TryGetValue(dataType, out Loader.TypeMapping? typeMapping)) + if (Loader.typeHandlers.TryGetValue(typeMapping.type, out var handler)) { - if (Loader.typeHandlers.TryGetValue(typeMapping.type, out var handler)) - { - handler(token, false); - } + handler(token, false); } } } - foreach (System.Collections.Generic.KeyValuePair item in Registry.prefabNames) + } + } + internal static void ProcessPrefabs() + { + foreach (System.Collections.Generic.KeyValuePair item in Registry.prefabNames) + { + UnitData.Type unitPrefabType = UnitData.Type.Scout; + string prefabId = item.Value; + if (Enum.TryParse(prefabId, out UnitData.Type parsedType)) + { + unitPrefabType = parsedType; + PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); + } + else { - UnitData.Type unitPrefabType = UnitData.Type.Scout; - string prefabId = item.Value; - if (Enum.TryParse(prefabId, out UnitData.Type parsedType)) + KeyValuePair prefabInfo = Registry.unitPrefabs.FirstOrDefault(kv => kv.Key.name == prefabId); + if (!EqualityComparer.Default.Equals(prefabInfo.Key, default)) { - unitPrefabType = parsedType; - PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); + PrefabManager.units.TryAdd(item.Key, prefabInfo.Value); } else { - KeyValuePair prefabInfo = Registry.unitPrefabs.FirstOrDefault(kv => kv.Key.name == prefabId); - if (!EqualityComparer.Default.Equals(prefabInfo.Key, default)) - { - PrefabManager.units.TryAdd(item.Key, prefabInfo.Value); - } - else - { - PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); - } + PrefabManager.units.TryAdd(item.Key, PrefabManager.units[(int)unitPrefabType]); } } - foreach (Visual.SkinInfo skin in Registry.skinInfo) + } + } + internal static void ProcessEmbark() + { + foreach (KeyValuePair entry in Main.embarkNames) + { + try { - if (skin.skinData != null) - gameLogicData.skinData[(SkinType)skin.idx] = skin.skinData; + UnitData.Type unit = EnumCache.GetType(entry.Key); + UnitData.Type newUnit = EnumCache.GetType(entry.Value); + Main.embarkOverrides[unit] = newUnit; + Plugin.logger.LogInfo($"Embark unit type for {entry.Key} is now {entry.Value}"); } - foreach (KeyValuePair entry in Main.embarkNames) + catch { - try - { - UnitData.Type unit = EnumCache.GetType(entry.Key); - UnitData.Type newUnit = EnumCache.GetType(entry.Value); - Main.embarkOverrides[unit] = newUnit; - Plugin.logger.LogInfo($"Embark unit type for {entry.Key} is now {entry.Value}"); - } - catch - { - Plugin.logger.LogError($"Embark unit type for {entry.Key} is not valid: {entry.Value}"); - } + Plugin.logger.LogError($"Embark unit type for {entry.Key} is not valid: {entry.Value}"); } - foreach (KeyValuePair entry in Main.attractsResourceNames) + } + } + internal static void ProcessAttract() + { + foreach (KeyValuePair entry in Main.attractsResourceNames) + { + try { - try - { - ImprovementData.Type improvement = EnumCache.GetType(entry.Key); - ResourceData.Type resource = EnumCache.GetType(entry.Value); - Main.attractsResourceOverrides[improvement] = resource; - Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts {entry.Value}"); - } - catch - { - Plugin.logger.LogError($"Improvement {entry.Key} resource type is not valid: {entry.Value}"); - } + ImprovementData.Type improvement = EnumCache.GetType(entry.Key); + ResourceData.Type resource = EnumCache.GetType(entry.Value); + Main.attractsResourceOverrides[improvement] = resource; + Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts {entry.Value}"); } - foreach (KeyValuePair entry in Main.attractsTerrainNames) + catch { - try - { - ImprovementData.Type improvement = EnumCache.GetType(entry.Key); - Polytopia.Data.TerrainData.Type terrain = EnumCache.GetType(entry.Value); - Main.attractsTerrainOverrides[improvement] = terrain; - Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts on {entry.Value}"); - } - catch - { - Plugin.logger.LogError($"Improvement {entry.Key} terrain type is not valid: {entry.Value}"); - } + Plugin.logger.LogError($"Improvement {entry.Key} resource type is not valid: {entry.Value}"); } } - catch (Exception e) + foreach (KeyValuePair entry in Main.attractsTerrainNames) { - Plugin.logger.LogError($"Error on processing modified game logic data : {e.Message}"); + try + { + ImprovementData.Type improvement = EnumCache.GetType(entry.Key); + Polytopia.Data.TerrainData.Type terrain = EnumCache.GetType(entry.Value); + Main.attractsTerrainOverrides[improvement] = terrain; + Plugin.logger.LogInfo($"Improvement {entry.Key} now attracts on {entry.Value}"); + } + catch + { + Plugin.logger.LogError($"Improvement {entry.Key} terrain type is not valid: {entry.Value}"); + } } } } From 549e4153529656c1b3737a6372953cb820c76e4d Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 7 Sep 2025 11:21:39 +0200 Subject: [PATCH 8/8] Added doc comments --- src/Loader.cs | 25 +++++++++++++++++++++---- src/Managers/Main.cs | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Loader.cs b/src/Loader.cs index 5b285d6..dc3cd61 100644 --- a/src/Loader.cs +++ b/src/Loader.cs @@ -757,14 +757,19 @@ internal static void ProcessGameLogicData(GameLogicData gameLogicData, JObject r { CreateMappings(rootObject); ProcessPrefabs(); - ProcessEmbark(); - ProcessAttract(); + ProcessEmbarkOverrides(); + ProcessAttractOverrides(); } catch (Exception e) { Plugin.logger.LogError($"Error on processing modified game logic data : {e.StackTrace}"); } } + + /// + /// Creates EnumCache mappings for custom enum values and invokes type handlers. + /// + /// internal static void CreateMappings(JObject rootObject) { foreach (JToken jtoken in rootObject.SelectTokens("$.*.*").ToArray()) @@ -813,6 +818,10 @@ internal static void CreateMappings(JObject rootObject) } } } + + /// + /// Processes the prefab registry and populates the PrefabManager with custom prefabs. + /// internal static void ProcessPrefabs() { foreach (System.Collections.Generic.KeyValuePair item in Registry.prefabNames) @@ -838,7 +847,11 @@ internal static void ProcessPrefabs() } } } - internal static void ProcessEmbark() + + /// + /// Processes embark overrides by mapping original embark unit types to configured overrides. + /// + internal static void ProcessEmbarkOverrides() { foreach (KeyValuePair entry in Main.embarkNames) { @@ -855,7 +868,11 @@ internal static void ProcessEmbark() } } } - internal static void ProcessAttract() + + /// + /// Processes attract overrides by mapping improvements to resources and terrain types based on configured overrides. + /// + internal static void ProcessAttractOverrides() { foreach (KeyValuePair entry in Main.attractsResourceNames) { diff --git a/src/Managers/Main.cs b/src/Managers/Main.cs index d62bcf8..b9e19f4 100644 --- a/src/Managers/Main.cs +++ b/src/Managers/Main.cs @@ -78,7 +78,7 @@ public static class Main /// [HarmonyPrefix] [HarmonyPatch(typeof(GameLogicData), nameof(GameLogicData.AddGameLogicPlaceholders))] - private static void GameLogicData_Parse(GameLogicData __instance, ref JObject rootObject) + private static void GameLogicData_AddGameLogicPlaceholders(GameLogicData __instance, ref JObject rootObject) { if (!fullyInitialized) {