Esempio n. 1
0
        public ModInjector()
        {
            // Only attempt injection once.
            if (ranOnce)
            {
                return;
            }
            ranOnce = true;

            // Inform an injection attempt is taking place.
            BuildLog.Info("Injecting Harmony...");
            CurrentlyInjecting = true;

            // Queue the injection on the UI-thread.  If initialization using the previous
            // mod assembly is still occuring, this will wait for it to complete before we
            // start mucking things up.
            QueueUITask(() => WaitFor(() => GameIsReady, DoInject, 0));
        }
Esempio n. 2
0
        private static void DoInject()
        {
            // Cache the previous assembly.  It can't be unloaded and isn't going anywhere.
            var previousAssembly = ModManager.modAssembly;

            try
            {
                // Once the harmony assembly is loaded into the app-domain, Caves of Qud
                // will automatically reference it in the mod assembly.
                BuildLog.Info("Pulling the Harmony assembly into the current AppDomain.");
                var harmonyAssembly = Assembly.LoadFrom(HarmonyAssemblyLocation);

                // Add an `AppDomain.AssemblyResolve` handler that matches the mod assembly.
                // Harmony will need this to successfully perform un-patching.
                AppDomain.CurrentDomain.AssemblyResolve += (obj, args) =>
                                                           args.Name == ModManager.modAssembly?.FullName ? ModManager.modAssembly : null;

                BuildLog.Info("Rebuilding mods into a Harmony-injected assembly.");

                // Refresh the mods; this rebuilds `ModManager.Mods`, where we can then alter
                // this mod's source code into the "Harmony-injected" version.
                ModManager.Refresh();
                BuildLog.Info($"Adjusting the scripts of {ModID} for the injected mod assembly.");
                AdjustModForInjection();
                ModManager.BuildScriptMods();

                // Check to make sure the mod assembly has updated.
                if (ModManager.modAssembly == null)
                {
                    throw new InjectionException($"The Harmony-injected mod assembly failed to build.");
                }
                if (ModManager.modAssembly == typeof(ModInjector).Assembly)
                {
                    throw new InjectionException($"The mod assembly still points to this assembly; this is not expected.");
                }

                BuildLog.Info("Harmony-injected mod assembly built successfully.");

                BuildLog.Info("Calling static constructors in the mod assembly...");
                RunStaticConstructors();

                // A config reload ensures the game begins to use the types from the rebuilt assembly.
                BuildLog.Info("Reloading the game configuration...");
                XRLCore.Core.HotloadConfiguration();

                BuildLog.Info("Injection successful.");
            }
            catch (InjectionException ex)
            {
                BuildLog.Error(ex.Message);
                BuildLog.Error("Injection failed.");

                ShowErrorPopup(
                    "&YHarmony could not be injected properly.",
                    $"&R{ex.Message}"
                    );
            }
            catch (StaticInitializationException ex)
            {
                // The mod assembly built successfully, but it is having trouble initializing.
                BuildLog.Error(ex.Message);
                BuildLog.Error(ex.InnerException);
                BuildLog.Error("Injection failed.");

                // Restore the old mod assembly.
                ModManager.modAssembly = previousAssembly;

                // We can't really know what mod failed, but hopefully we can give enough
                // information such that it is obvious to the player.
                var lastEx = EnumerateExceptions(ex.InnerException).Last();
                ShowErrorPopup(
                    $"&YStatic construction of &W{ex.TargetType.FullName}&Y failed.",
                    $"&wError &W{lastEx.GetType().Name}",
                    $"&R{lastEx.Message}",
                    TraceThrough(ex.InnerException).Select((s, i) => $"&w{(i > 0 ? "Via" : "At")} &W{s}")
                    );
            }
            catch (Exception ex)
            {
                BuildLog.Error("An unexpected exception was raised during the injection process.");
                BuildLog.Error(ex);
                BuildLog.Error("Injection failed.");

                ShowErrorPopup(
                    "&YHarmony could not be injected properly.",
                    "&RAn unhandled error occurred during the injection process."
                    );
            }
            finally
            {
                // Restore the previous assembly, in case of a failure, just in case
                // some kind of error recovery triggers and Qud still expects mod-types
                // to be retrievable.
                if (ModManager.modAssembly == null)
                {
                    ModManager.modAssembly = previousAssembly;
                    ModManager.bCompiled   = true;
                }
                CurrentlyInjecting = false;
            }
        }