static Installer() { try { UnityEngine.Debug.Log($"Installing Harmony {typeof(Harmony).GetAssemblyVersion()}!"); var go = UnityEngine.GameObject.Find(PatchedMarker); if (go != null) { UnityEngine.Debug.Log("Harmony 1.x has already been patched!"); return; } // Create a marker to ensure that this code only runs once UnityEngine.Object.DontDestroyOnLoad(new UnityEngine.GameObject(PatchedMarker)); // Patch assembly resolver to make sure that missing 2.x Harmony assembly references are never resolved to 1.x Harmony! // This will naturally occur when this mod gets updated to newer Harmony versions. // This should be done before we use harmony because harmony itself needs assembly resolver. Resolver.InstallHarmonyResolver(); var harmony = new Harmony("CitiesHarmony"); // Self-patch Harmony 1.x assemblies var oldHarmonyStateTransferred = false; void ProcessAssembly(Assembly assembly) { try { if (assembly == typeof(Harmony).Assembly) { return; // skip our own Harmony assembly } var assemblyName = assembly.GetName(); if (assemblyName.Name == "0Harmony") { if (assemblyName.Version < new Version(1, 1, 0, 0)) { UnityEngine.Debug.Log($"Detected stone age version of Harmony ({assemblyName.Version}). Skipping this one!"); } else if (assemblyName.Version < new Version(1, 3, 0, 0)) { try { if (!oldHarmonyStateTransferred) { oldHarmonyStateTransferred = true; var patcher = new Harmony1StateTransfer(harmony, assembly); patcher.Patch(); } else { Harmony1SelfPatcher.Apply(harmony, assembly); } } catch (Exception e) { UnityEngine.Debug.LogException(e); throw e; } } else { //UnityEngine.Debug.LogError($"Detected conflicting Harmony 2.x assembly ({assemblyName.Version})!"); } } } catch (Exception e) { UnityEngine.Debug.LogException(e); throw e; } } var assemblies = AppDomain.CurrentDomain.GetAssemblies(); Array.Sort(assemblies, (a, b) => - a.GetName().Version.CompareTo(b.GetName().Version)); // higher Harmony versions first! foreach (var assembly in assemblies) { ProcessAssembly(assembly); } // Process all assemblies that are loaded after this AppDomain.CurrentDomain.AssemblyLoad += (object sender, AssemblyLoadEventArgs args) => ProcessAssembly(args.LoadedAssembly); UnityEngine.Debug.Log($"Installed Harmony {typeof(Harmony).GetAssemblyVersion()} successfully!"); } catch (Exception e) { UnityEngine.Debug.LogException(e); throw e; } }
public void Patch() { var patchedMethods = new List <MethodBase>((HarmonySharedState_GetPatchedMethods.Invoke(null, new object[0]) as IEnumerable <MethodBase>)); UnityEngine.Debug.Log($"{patchedMethods.Count} patched methods found."); var processors = new List <PatchProcessor>(); foreach (var method in patchedMethods) { var patchInfo = HarmonySharedState_GetPatchInfo.Invoke(null, new object[] { method }); if (patchInfo == null) { continue; } var prefixes = (object[])PatchInfo_prefixed.GetValue(patchInfo); foreach (var patch in prefixes) { processors.Add(CreateHarmony(patch) .CreateProcessor(method) .AddPrefix(CreateHarmonyMethod(patch))); } var postfixes = (object[])PatchInfo_postfixes.GetValue(patchInfo); foreach (var patch in postfixes) { processors.Add(CreateHarmony(patch) .CreateProcessor(method) .AddPostfix(CreateHarmonyMethod(patch))); } var transpilers = (object[])PatchInfo_transpilers.GetValue(patchInfo); foreach (var patch in transpilers) { processors.Add(CreateHarmony(patch) .CreateProcessor(method) .AddTranspiler(CreateHarmonyMethod(patch))); } } UnityEngine.Debug.Log($"Reverting patches..."); var oldInstance = HarmonyInstance_Create.Invoke(null, new object[] { "CitiesHarmony" }); foreach (var method in patchedMethods.ToList()) { HarmonyInstance_Unpatch.Invoke(oldInstance, new object[] { method, HarmonyPatchType_All, null }); } // Reset is not needed while we are using a Harmony 2 fork that uses a different assembly name! /* * // Reset shared state * var sharedStateAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name.Contains("HarmonySharedState")); * if (sharedStateAssembly != null) { * var stateField = sharedStateAssembly.GetType("HarmonySharedState")?.GetField("state"); * if (stateField != null) { * UnityEngine.Debug.Log("Resetting HarmonySharedState..."); * stateField.SetValue(null, null); * } * } */ // Apply patches to old Harmony Harmony1SelfPatcher.Apply(harmony, assembly); // Apply patches using Harmony 2.x foreach (var processor in processors) { processor.Patch(); } }