Example #1
0
        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;
            }
        }
Example #2
0
        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();
            }
        }