예제 #1
0
            /// <summary>
            /// Transpiles UpdateMods to postpone the report.
            /// </summary>
            internal static IEnumerable <CodeInstruction> Transpiler(
                IEnumerable <CodeInstruction> method)
            {
#if DEBUG
                DebugLogger.LogDebug("Transpiling Steam.UpdateMods()");
#endif
                var report = typeof(Manager).GetMethodSafe(nameof(Manager.Report), false,
                                                           typeof(GameObject));
                var sanitize = typeof(Manager).GetMethodSafe(nameof(Manager.Sanitize), false,
                                                             typeof(GameObject));
                var newReport = typeof(QueuedReportManager).GetMethodSafe(nameof(
                                                                              QueuedReportManager.QueueDelayedReport), true, typeof(Manager),
                                                                          typeof(GameObject));
                var newSanitize = typeof(QueuedReportManager).GetMethodSafe(nameof(
                                                                                QueuedReportManager.QueueDelayedSanitize), true, typeof(Manager),
                                                                            typeof(GameObject));
                foreach (var instruction in method)
                {
                    if (instruction.opcode == OpCodes.Callvirt)
                    {
                        var callee = instruction.operand as MethodInfo;
                        if (callee == report && newReport != null)
                        {
                            instruction.opcode  = OpCodes.Call;
                            instruction.operand = newReport;
                        }
                        else if (callee == sanitize && newSanitize != null)
                        {
                            instruction.opcode  = OpCodes.Call;
                            instruction.operand = newSanitize;
                        }
                    }
                    yield return(instruction);
                }
            }
예제 #2
0
        private static void PostloadHandler(HarmonyInstance instance)
        {
            if (DebugNotIncludedOptions.Instance?.PowerUserMode ?? false)
            {
                instance.Patch(typeof(ModsScreen), "BuildDisplay",
                               new HarmonyMethod(typeof(DebugNotIncludedPatches), nameof(HidePopups)),
                               new HarmonyMethod(typeof(DebugNotIncludedPatches), nameof(BuildDisplay)));
            }
            KInputHandler.Add(Global.Instance.GetInputManager().GetDefaultController(),
                              new UISnapshotHandler(), 1024);
            // New postload architecture requires going back a little ways
            var      st       = new System.Diagnostics.StackTrace(6);
            Assembly assembly = null;

            if (st.FrameCount > 0)
            {
                assembly = st.GetFrame(0).GetMethod()?.DeclaringType?.Assembly;
            }
            PUtil.LogDebug(assembly?.FullName ?? "none");
            RunningPLibAssembly = assembly ?? Assembly.GetCallingAssembly();
            // Log which mod is running PLib
            var latest = ModDebugRegistry.Instance.OwnerOfAssembly(RunningPLibAssembly);

            if (latest != null)
            {
                DebugLogger.LogDebug("Executing version of PLib is from: " + latest.ModName);
            }
            HarmonyPatchInspector.Check();
        }
예제 #3
0
        /// <summary>
        /// Registers an assembly as loaded by a particular mod.
        /// </summary>
        /// <param name="assembly">The assembly to register.</param>
        /// <param name="owner">The owning mod.</param>
        internal void RegisterAssembly(Assembly assembly, ModDebugInfo owner)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException(nameof(assembly));
            }
            if (owner == null)
            {
                throw new ArgumentNullException(nameof(owner));
            }
            string fullName = assembly.FullName;
            var    oldMod   = modAssemblies.GetOrAdd(fullName, owner);

            if (oldMod != owner)
            {
                // Possible if multiple mods include the same dependency DLL
                DebugLogger.LogDebug("Assembly \"{0}\" is used by multiple mods:", fullName);
                DebugLogger.LogDebug("First loaded by {0} (used), also loaded by {1} (ignored)",
                                     oldMod.ModName, owner.ModName);
            }
            else
            {
                owner.ModAssemblies.Add(assembly);
            }
        }
예제 #4
0
        /// <summary>
        /// When creating a Harmony instance, ignores the silly constant OxygenNotIncluded_v0.1
        /// and instead fills in a name sensible to the current mod.
        /// </summary>
        internal static HarmonyInstance CreateHarmonyInstance(string name)
        {
            HarmonyInstance instance;

            if (CurrentMod != null)
            {
                // This mod's changes to names were integrated into the game!
                if (PUtil.GameVersion >= 397125u)
                {
                    CurrentMod.HarmonyIdentifier = name;
                }
                else
                {
                    name = CurrentMod.HarmonyIdentifier;
                }
            }
            instance = HarmonyInstance.Create(name);
            if (CurrentMod != null)
            {
                CurrentMod.HarmonyInstance = instance;
            }
#if DEBUG
            DebugLogger.LogDebug("Created Harmony instance with ID {0} for mod {1}",
                                 name, CurrentModTitle);
#endif
            return(instance);
        }
예제 #5
0
            /// <summary>
            /// Transpiles Spawn to add more error logging.
            /// </summary>
            internal static IEnumerable <CodeInstruction> Transpiler(
                IEnumerable <CodeInstruction> method)
            {
#if DEBUG
                DebugLogger.LogDebug("Transpiling Spawn()");
#endif
                return(TranspileSpawn(method));
            }
예제 #6
0
            /// <summary>
            /// Transpiles RegisterBuilding to catch exceptions and log them.
            /// </summary>
            internal static IEnumerable <CodeInstruction> Transpiler(ILGenerator generator,
                                                                     IEnumerable <CodeInstruction> method)
            {
#if DEBUG
                DebugLogger.LogDebug("Transpiling BuildingConfigManager.RegisterBuilding()");
#endif
                var logger = typeof(DebugLogger).GetMethodSafe(nameof(DebugLogger.
                                                                      LogBuildingException), true, typeof(Exception), typeof(IBuildingConfig));
                var ee = method.GetEnumerator();
                // Emit all but the last instruction
                if (ee.MoveNext())
                {
                    CodeInstruction last;
                    bool            hasNext, isFirst = true;
                    var             endMethod = generator.DefineLabel();
                    do
                    {
                        last = ee.Current;
                        if (isFirst)
                        {
                            last.blocks.Add(new ExceptionBlock(ExceptionBlockType.
                                                               BeginExceptionBlock, null));
                        }
                        hasNext = ee.MoveNext();
                        isFirst = false;
                        if (hasNext)
                        {
                            yield return(last);
                        }
                    } while (hasNext);
                    // Preserves the labels "ret" might have had
                    last.opcode  = OpCodes.Nop;
                    last.operand = null;
                    yield return(last);

                    // Add a "leave"
                    yield return(new CodeInstruction(OpCodes.Leave, endMethod));

                    // The exception is already on the stack
                    var startHandler = new CodeInstruction(OpCodes.Ldarg_1);
                    startHandler.blocks.Add(new ExceptionBlock(ExceptionBlockType.
                                                               BeginCatchBlock, typeof(Exception)));
                    yield return(startHandler);

                    yield return(new CodeInstruction(OpCodes.Call, logger));

                    // End catch block, quash the exception
                    var endCatch = new CodeInstruction(OpCodes.Leave, endMethod);
                    endCatch.blocks.Add(new ExceptionBlock(ExceptionBlockType.
                                                           EndExceptionBlock, null));
                    yield return(endCatch);

                    // Actual new ret
                    var ret = new CodeInstruction(OpCodes.Ret);
                    ret.labels.Add(endMethod);
                    yield return(ret);
                }
            }
예제 #7
0
        /// <summary>
        /// Checks all types currently loaded for issues.
        /// </summary>
        public static void Check()
        {
            var types = GetAllTypes();

            DebugLogger.LogDebug("Inspecting {0:D} types".F(types.Count));
            foreach (var type in types)
            {
                CheckType(type);
            }
        }
예제 #8
0
 /// <summary>
 /// Logs mod load information to the debug log.
 /// </summary>
 /// <param name="mod">The mod that was loaded.</param>
 private static void LogModScanned(Mod mod)
 {
     if (mod != null)
     {
         string path = mod.relative_root;
         DebugLogger.LogDebug("{3} ({0}): Successfully loaded {2} from '{1}'".F(
                                  mod.label.title, string.IsNullOrEmpty(path) ? "root" : path,
                                  mod.available_content.ToString(), mod.staticID));
     }
 }
예제 #9
0
 /// <summary>
 /// Handles an exception thrown when loading a mod.
 /// </summary>
 /// <param name="ex">The exception thrown.</param>
 internal static void HandleModException(object ex)
 {
     if (ex is Exception e)
     {
         string title = CurrentModTitle;
         if (!string.IsNullOrEmpty(title))
         {
             DebugLogger.LogDebug("When loading mod {0}:", title);
         }
         DebugLogger.LogException(e);
     }
 }
예제 #10
0
        /// <summary>
        /// Runs the required postload patches after all other mods load.
        /// </summary>
        /// <param name="instance">The Harmony instance to execute patches.</param>
        public override void OnAllModsLoaded(Harmony harmony, IReadOnlyList <Mod> mods)
        {
            var options = DebugNotIncludedOptions.Instance;

            if (options?.PowerUserMode == true)
            {
                harmony.Patch(typeof(ModsScreen), "BuildDisplay",
                              new HarmonyMethod(typeof(DebugNotIncludedPatches), nameof(HidePopups)),
                              new HarmonyMethod(typeof(DebugNotIncludedPatches), nameof(BuildDisplay)));
            }
            if (mods != null)
            {
                ModDebugRegistry.Instance.Populate(mods);
            }
            else
            {
                DebugLogger.LogWarning("Mods list is empty! Attribution will not work");
            }

            var runningCore = PRegistry.Instance.GetLatestVersion(
                "PeterHan.PLib.Core.PLibCorePatches")?.GetOwningAssembly();

            if (runningCore != null)
            {
                RunningPLibAssembly = runningCore;
            }
            // Log which mod is running PLib
            var latest = ModDebugRegistry.Instance.OwnerOfAssembly(RunningPLibAssembly);

            if (latest != null)
            {
                DebugLogger.LogDebug("Executing version of PLib is from: " + latest.ModName);
            }

            HarmonyPatchInspector.Check();
#if DEBUG
            harmony.ProfileMethod(typeof(SaveLoader).GetMethodSafe("Load", false, typeof(
                                                                       IReader)));
            harmony.ProfileMethod(typeof(SaveLoader).GetMethodSafe("Save", false, typeof(
                                                                       BinaryWriter)));
            harmony.ProfileMethod(typeof(SaveManager).GetMethodSafe("Load", false,
                                                                    PPatchTools.AnyArguments));
            harmony.ProfileMethod(typeof(SaveManager).GetMethodSafe("Save", false,
                                                                    PPatchTools.AnyArguments));
#endif
            if (options?.LocalizeMods == true)
            {
                typeof(PLocalization).GetMethodSafe("DumpAll", false)?.Invoke(loc, null);
            }
        }
예제 #11
0
        /// <summary>
        /// Loads the assembly from the path just like Assembly.LoadFrom, but saves the mod
        /// associated with that assembly.
        /// </summary>
        /// <param name="path">The path to load.</param>
        /// <returns>The assembly loaded.</returns>
        internal static Assembly LoadAssembly(string path)
        {
            var assembly = string.IsNullOrEmpty(path) ? null : Assembly.LoadFrom(path);

            if (assembly != null && CurrentMod != null)
            {
#if DEBUG
                DebugLogger.LogDebug("Loaded assembly {0} for mod {1}", assembly.FullName,
                                     CurrentModTitle);
#endif
                ModDebugRegistry.Instance.RegisterModAssembly(assembly, CurrentMod);
            }
            return(assembly);
        }
예제 #12
0
 /// <summary>
 /// Profiles a method, outputting how many milliseconds it took to run on each use.
 /// </summary>
 /// <param name="instance">The Harmony instance to use for the patch.</param>
 /// <param name="target">The method to profile.</param>
 internal static void ProfileMethod(this HarmonyInstance instance, MethodBase target)
 {
     if (target == null)
     {
         DebugLogger.LogWarning("No method specified to profile!");
     }
     else
     {
         instance.Patch(target, new HarmonyMethod(typeof(DebugNotIncludedPatches),
                                                  nameof(ProfilerPrefix)), new HarmonyMethod(typeof(DebugNotIncludedPatches),
                                                                                             nameof(ProfilerPostfix)));
         DebugLogger.LogDebug("Profiling method {0}.{1}".F(target.DeclaringType, target.
                                                           Name));
     }
 }
예제 #13
0
        /// <summary>
        /// Adds the assemblies in the loaded mod data to the active mod.
        /// </summary>
        /// <param name="data">The assemblies loaded.</param>
        internal static void LoadAssemblies(object data)
        {
            var dllList = data.GetType().GetFieldSafe("dlls", false);

            if (dllList != null && dllList.GetValue(data) is ICollection <Assembly> dlls)
            {
                foreach (var assembly in dlls)
                {
#if DEBUG
                    DebugLogger.LogDebug("Attributed assembly {0} to mod {1}", assembly.
                                         FullName, CurrentModTitle);
#endif
                    ModDebugRegistry.Instance.RegisterModAssembly(assembly, CurrentMod);
                }
            }
        }
예제 #14
0
        /// <summary>
        /// Runs the required postload patches after all other mods load.
        /// </summary>
        /// <param name="instance">The Harmony instance to execute patches.</param>
        private static void PostloadHandler(HarmonyInstance instance)
        {
            if (DebugNotIncludedOptions.Instance?.PowerUserMode ?? false)
            {
                instance.Patch(typeof(ModsScreen), "BuildDisplay", postfix:
                               new HarmonyMethod(typeof(DebugNotIncludedPatches), nameof(BuildDisplay)));
            }
            KInputHandler.Add(Global.Instance.GetInputManager().GetDefaultController(),
                              new UISnapshotHandler(), 1024);
            // Log which mod is running PLib
            RunningPLibAssembly = Assembly.GetCallingAssembly();
            var latest = ModDebugRegistry.Instance.OwnerOfAssembly(RunningPLibAssembly);

            if (latest != null)
            {
                DebugLogger.LogDebug("Executing version of PLib is from: " + latest.ModName);
            }
        }
예제 #15
0
            /// <summary>
            /// Transpiles UpdateMods to postpone the report.
            /// </summary>
            internal static IEnumerable <CodeInstruction> Transpiler(
                IEnumerable <CodeInstruction> method)
            {
#if DEBUG
                DebugLogger.LogDebug("Transpiling Steam.UpdateMods()");
#endif
                return(PPatchTools.ReplaceMethodCall(method, new Dictionary <MethodInfo,
                                                                             MethodInfo>()
                {
                    { typeof(Manager).GetMethodSafe(nameof(Manager.Report), false,
                                                    typeof(GameObject)), typeof(QueuedReportManager).GetMethodSafe(nameof(
                                                                                                                       QueuedReportManager.QueueDelayedReport), true, typeof(Manager),
                                                                                                                   typeof(GameObject)) },
                    { typeof(Manager).GetMethodSafe(nameof(Manager.Sanitize), false,
                                                    typeof(GameObject)), typeof(QueuedReportManager).GetMethodSafe(nameof(
                                                                                                                       QueuedReportManager.QueueDelayedSanitize), true, typeof(Manager),
                                                                                                                   typeof(GameObject)) }
                }));
            }
예제 #16
0
        /// <summary>
        /// Logs all failed asserts to the error log.
        /// </summary>
        private static void LogAllFailedAsserts()
        {
            var handler = new HarmonyMethod(typeof(DebugLogger), nameof(DebugLogger.
                                                                        OnAssertFailed));
            var        inst = ModDebugRegistry.Instance.DebugInstance;
            MethodInfo assert;

            try {
                // Assert(bool)
                assert = typeof(Debug).GetMethodSafe("Assert", true, typeof(bool));
                if (assert != null)
                {
                    inst.Patch(assert, handler);
                }
                // Assert(bool, object)
                assert = typeof(Debug).GetMethodSafe("Assert", true, typeof(bool), typeof(
                                                         object));
                if (assert != null)
                {
                    inst.Patch(assert, handler);
                }
                // Assert(bool, object, UnityEngine.Object)
                assert = typeof(Debug).GetMethodSafe("Assert", true, typeof(bool), typeof(
                                                         object), typeof(UnityEngine.Object));
                if (assert != null)
                {
                    inst.Patch(assert, handler);
                }
                // Assert(bool, string)
                assert = typeof(KCrashReporter).GetMethodSafe("Assert", true, typeof(bool),
                                                              typeof(string));
                if (assert != null)
                {
                    inst.Patch(assert, handler);
                }
#if DEBUG
                DebugLogger.LogDebug("Logging all failed asserts");
#endif
            } catch (Exception e) {
                DebugLogger.BaseLogException(e, null);
            }
        }
예제 #17
0
            /// <summary>
            /// Applied before OnPrefabInit runs.
            /// </summary>
            internal static MethodBase TargetMethod()
            {
                MethodBase target = null;

#if DEBUG
                DebugLogger.LogDebug("Transpiling LoadDLLs()");
#endif
                try {
                    target = typeof(Mod).Assembly.GetType("KMod.DLLLoader", false)?.
                             GetMethodSafe("LoadDLLs", true, PPatchTools.AnyArguments);
                    if (target == null)
                    {
                        DebugLogger.LogError("Unable to transpile LoadDLLs: Method not found");
                    }
                } catch (IOException e) {
                    // This should theoretically be impossible since the type is loaded
                    DebugLogger.BaseLogException(e, null);
                }
                return(target);
            }
예제 #18
0
        private static void PostloadHandler(HarmonyInstance instance)
        {
            if (DebugNotIncludedOptions.Instance?.PowerUserMode ?? false)
            {
                instance.Patch(typeof(ModsScreen), "BuildDisplay",
                               new HarmonyMethod(typeof(DebugNotIncludedPatches), nameof(HidePopups)),
                               new HarmonyMethod(typeof(DebugNotIncludedPatches), nameof(BuildDisplay)));
            }
            KInputHandler.Add(Global.Instance.GetInputManager().GetDefaultController(),
                              new UISnapshotHandler(), 1024);
            // New postload architecture requires going back a little ways
            var      st       = new StackTrace(6);
            Assembly assembly = null;

            if (st.FrameCount > 0)
            {
                assembly = st.GetFrame(0).GetMethod()?.DeclaringType?.Assembly;
            }
            RunningPLibAssembly = assembly ?? Assembly.GetCallingAssembly();
            // Log which mod is running PLib
            var latest = ModDebugRegistry.Instance.OwnerOfAssembly(RunningPLibAssembly);

            if (latest != null)
            {
                DebugLogger.LogDebug("Executing version of PLib is from: " + latest.ModName);
            }
            HarmonyPatchInspector.Check();
#if DEBUG
            // SaveManager.Load:: 13831 ms
            instance.ProfileMethod(typeof(SaveLoader).GetMethodSafe("Load", false, typeof(
                                                                        IReader)));
            instance.ProfileMethod(typeof(SaveLoader).GetMethodSafe("Save", false, typeof(
                                                                        BinaryWriter)));
            instance.ProfileMethod(typeof(SaveManager).GetMethodSafe("Load", false,
                                                                     PPatchTools.AnyArguments));
            instance.ProfileMethod(typeof(SaveManager).GetMethodSafe("Save", false,
                                                                     PPatchTools.AnyArguments));
#endif
        }
예제 #19
0
 /// <summary>
 /// A postfix method for instrumenting methods in the code base. Logs the total time
 /// taken in milliseconds.
 /// </summary>
 private static void ProfilerPostfix(MethodBase __originalMethod, Stopwatch __state)
 {
     DebugLogger.LogDebug("{1}.{2}:: {0:D} ms".F(__state.ElapsedMilliseconds,
                                                 __originalMethod.DeclaringType, __originalMethod.Name));
 }
예제 #20
0
 /// <summary>
 /// Applied after OnSpawn runs.
 /// </summary>
 internal static void Postfix(ScheduleManager __instance)
 {
     DebugLogger.LogDebug("Sorting schedules");
     __instance.GetSchedules().Sort((a, b) => a.name.CompareTo(b.name));
 }
예제 #21
0
 /// <summary>
 /// Applied after CreateSound runs.
 /// </summary>
 internal static void Postfix(string file_name, string anim_name, string sound_name)
 {
     // Add sound "GasPump_intake" to anim pumpgas_kanim.working_loop
     DebugLogger.LogDebug("Add sound \"{0}\" to anim {1}.{2}".F(sound_name,
                                                                file_name, anim_name));
 }
예제 #22
0
 /// <summary>
 /// Applied after AddKAnimMod runs.
 /// </summary>
 internal static void Postfix(string name)
 {
     DebugLogger.LogDebug("Adding anim \"{0}\"", name);
 }