private static bool HasActivePatches(HarmonyLib.Patches patches) { return(patches != null && ((patches.Prefixes != null && patches.Prefixes.Count != 0) || (patches.Postfixes != null && patches.Postfixes.Count != 0) || (patches.Transpilers != null && patches.Transpilers.Count != 0))); }
public MethodMeta(MethodBase m, HarmonyLib.Patches p) { this.method = m; this.patches = p; this.methodStr = Utility.GetSignature(method); if (p != null) { this.summaryStr = StackTraceUtility.SummaryString(p); this.patchCount = p.Owners.Where(Utility.IsNotAnalyzerPatch).Count(); } else { this.summaryStr = ""; this.patchCount = 0; } var foundMod = StackTraceUtility.mods.TryGetValue(m.DeclaringType.Assembly, out mod); if (foundMod) { return; } // We add Assembly-CSharp into the `mods` dict, so all we // need to check is the UnityEngine components. if (!foundMod && m.DeclaringType.Assembly.FullName.Contains("UnityEngine")) { this.mod = "Rimworld"; } else { this.mod = "Unknown"; } }
public static bool CheckForCompetition(this HarmonyLib.Patches patches, HarmonyLib.Harmony domesticHarmoy, out string debugInfo, ReadOnlyCollection <string> ignoreList = null) { bool Result = CheckForCompetition(patches, domesticHarmoy, out StringBuilder debugInfoBuilder, ignoreList); debugInfo = debugInfoBuilder.ToString(); return(Result); }
private static string GetPatchStrings(MethodBase method, HarmonyLib.Patches p) { const string spacePrefix = " - "; // 6 spaces var retString = new StringBuilder(); retString.Append("\n" + spacePrefix); retString.Append(SummaryString(p)); return(retString.ToString()); }
public static bool CheckForCompetition(this HarmonyLib.Patches patches, HarmonyLib.Harmony domesticHarmoy, out StringBuilder debugInfoBuilder, ReadOnlyCollection <string> ignoreList = null) { debugInfoBuilder = new StringBuilder(string.Empty); patches.GetBoolPrefixes(out ReadOnlyCollection <Patch> BoolPrefixes); return (CheckCollectionForCompetition(BoolPrefixes, domesticHarmoy, "prefixes", ref debugInfoBuilder, ignoreList) || CheckCollectionForCompetition(patches.Postfixes, domesticHarmoy, "postfixes", ref debugInfoBuilder, ignoreList) || CheckCollectionForCanceling(patches.Postfixes, BoolPrefixes, "postfixes", ref debugInfoBuilder, ignoreList) || CheckCollectionForCompetition(patches.Transpilers, domesticHarmoy, "transpilers", ref debugInfoBuilder, ignoreList) || CheckCollectionForCanceling(patches.Transpilers, BoolPrefixes, "transpilers", ref debugInfoBuilder, ignoreList)); }
public static void ProfilePatch() { var jiff = AccessTools.Method(typeof(StatExtension), nameof(StatExtension.GetStatValueAbstract), new[] { typeof(BuildableDef), typeof(StatDef), typeof(ThingDef) }); var pre = new HarmonyMethod(typeof(H_GetStatValue), nameof(PrefixAb)); var post = new HarmonyMethod(typeof(H_GetStatValue), nameof(Postfix)); Modbase.Harmony.Patch(jiff, pre, post); jiff = AccessTools.Method(typeof(StatExtension), nameof(StatExtension.GetStatValueAbstract), new[] { typeof(AbilityDef), typeof(StatDef), typeof(Pawn) }); pre = new HarmonyMethod(typeof(H_GetStatValue), nameof(PrefixAbility)); Modbase.Harmony.Patch(jiff, pre, post); jiff = AccessTools.Method(typeof(StatWorker), nameof(StatWorker.GetValue), new[] { typeof(StatRequest), typeof(bool) }); pre = new HarmonyMethod(typeof(H_GetStatValue), nameof(GetValueDetour)); Modbase.Harmony.Patch(jiff, pre); HarmonyMethod go = new HarmonyMethod(typeof(H_GetStatValue), nameof(PartPrefix)); HarmonyMethod biff = new HarmonyMethod(typeof(H_GetStatValue), nameof(PartPostfix)); foreach (Type allLeafSubclass in typeof(StatPart).AllSubclassesNonAbstract()) { try { MethodInfo mef = AccessTools.Method(allLeafSubclass, nameof(StatPart.TransformValue), new Type[] { typeof(StatRequest), typeof(float).MakeByRefType() }); if (mef.DeclaringType == allLeafSubclass) { HarmonyLib.Patches info = Harmony.GetPatchInfo(mef); bool F = true; if (info != null) { foreach (Patch infoPrefix in info.Prefixes) { if (infoPrefix.PatchMethod == go.method) { F = false; } } } if (F) { Modbase.Harmony.Patch(mef, go, biff); } } } catch (Exception e) { ThreadSafeLogger.ReportException(e, $"Failed to patch {allLeafSubclass} from {allLeafSubclass.Assembly.FullName} for profiling"); } } }
/// <summary> /// Returns all patched methods by NPC Adventures mod. /// </summary> /// <returns>Enumerable patched methods</returns> public IEnumerable <PatchedMethod> GetPatchedMethods() { var methods = this.harmony .GetPatchedMethods() .ToArray(); foreach (var method in methods) { HarmonyLib.Patches info = Harmony.GetPatchInfo(method); if (info != null && info.Owners.Contains(this.harmony.Id)) { yield return(new PatchedMethod(method, info)); } } }
public static void UnpatchMethod(MethodInfo method) { foreach (MethodBase methodBase in Harmony.GetAllPatchedMethods()) { HarmonyLib.Patches infos = Harmony.GetPatchInfo(methodBase); var allPatches = infos.Prefixes.Concat(infos.Postfixes, infos.Transpilers, infos.Finalizers); if (!allPatches.Any(patch => patch.PatchMethod == method)) { continue; } Modbase.Harmony.Unpatch(methodBase, method); return; } Warn("Failed to locate method to unpatch"); }
internal static void Apply(Harmony harmony) { harmony.PatchAll(typeof(Patches)); harmony.PatchAll(typeof(PlanPiece)); if (Chainloader.PluginInfos.ContainsKey(buildCameraGUID)) { logger.LogInfo("Applying BuildCamera patches"); harmony.PatchAll(typeof(PatcherBuildCamera)); } if (Chainloader.PluginInfos.ContainsKey(craftFromContainersGUID)) { logger.LogInfo("Applying CraftFromContainers patches"); harmony.PatchAll(typeof(PatcherCraftFromContainers)); } HarmonyLib.Patches patches = Harmony.GetPatchInfo(typeof(Player).GetMethod("OnSpawned")); if (patches?.Owners.Contains(buildShareGUID) == true) { logger.LogInfo("Applying BuildShare patches"); harmony.PatchAll(typeof(PatcherBuildShare)); } }
public static IEnumerable <MethodInfo> GetPatchMethods() { var patches = Harmony.GetAllPatchedMethods().ToList(); foreach (var patch in patches) { HarmonyLib.Patches p = null; try { p = Harmony.GetPatchInfo(patch); } catch (Exception e) { ThreadSafeLogger.ReportException(e, $"Failed to get patch info for {Utility.GetSignature(patch)}"); continue; } if (patch is MethodInfo info && p.Transpilers.Any(p => Utility.IsNotAnalyzerPatch(p.owner))) { yield return(info); } } }
public static string SummaryString(HarmonyLib.Patches patch) { var sb = new StringBuilder(255); void ProcessPatches(ReadOnlyCollection <Patch> patches, string type) { if (patches.Any() is false) { return; } sb.Append($"{type}: {{ "); var first = true; foreach (var p in patches.Where(p => Utility.IsNotAnalyzerPatch(p.owner)).OrderBy(s => s.priority)) { sb.Append((first ? "" : ", ") + $"{ModFromPatchId(p.owner)}"); if (type == "Prefixes") { sb.Append((p.PatchMethod.ReturnType == typeof(bool) ? " - destructive" : null)); } first = false; } sb.Append(" } "); } ProcessPatches(patch.Prefixes, "Prefixes"); ProcessPatches(patch.Postfixes, "Postfixes"); ProcessPatches(patch.Transpilers, "Transpilers"); ProcessPatches(patch.Finalizers, "Finalizers"); return(sb.ToString()); }
public PatchedMethod(MethodBase method, HarmonyLib.Patches patchInfo) { this.Method = method; this.PatchInfo = patchInfo; }
public static void GetBoolPrefixes(this HarmonyLib.Patches patches, out ReadOnlyCollection <Patch> boolPrefixes) { boolPrefixes = patches.Prefixes.Where(patch => patch.PatchMethod.ReturnType == typeof(bool)).ToList().AsReadOnly(); }
internal static void EnableQueuedGadgets() { List <GadgetInfo> preFilteredQueuedGadgets = QueuedGadgets; QueuedGadgets = new List <GadgetInfo>(); foreach (GadgetInfo gadget in preFilteredQueuedGadgets) { bool valid = true; foreach (string dependency in gadget.Attribute.Dependencies) { string[] splitDependency = dependency.Split(':'); GadgetInfo dependencyGadget = Gadgets.GetGadgetInfo(splitDependency[0]); valid = dependencyGadget != null && dependencyGadget.Gadget.Enabled; if (valid) { if (splitDependency.Length == 2) { string versionString = splitDependency[1].TrimStart('v'); int[] targetVersionNums = versionString.Split('.').Select(x => int.Parse(x)).ToArray(); if (targetVersionNums.Length > 4) { continue; } int[] actualVersionNums = dependencyGadget.Mod.Version.ToString().Split('.').Select(x => int.Parse(x)).ToArray(); if (targetVersionNums.Length != 4) { Array.Resize(ref targetVersionNums, 4); } if (actualVersionNums.Length != 4) { Array.Resize(ref actualVersionNums, 4); } valid = actualVersionNums[0] == targetVersionNums[0] && actualVersionNums[1] == targetVersionNums[1] && (actualVersionNums[2] > targetVersionNums[2] || (actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] >= targetVersionNums[3])); } else if (splitDependency.Length == 3) { string versionString = splitDependency[1].TrimStart('v'); int[] targetVersionNums = versionString.Split('.').Select(x => int.Parse(x)).ToArray(); if (targetVersionNums.Length > 4) { continue; } int[] actualVersionNums = dependencyGadget.Mod.Version.ToString().Split('.').Select(x => int.Parse(x)).ToArray(); VersionSpecificity versionSpecificity; try { versionSpecificity = (VersionSpecificity)Enum.Parse(typeof(VersionSpecificity), splitDependency[2], true); } catch (ArgumentException) { Logger.LogWarning("Gadget " + gadget.Attribute.Name + " has an improperly-formatted dependency version string: " + dependency); versionSpecificity = VersionSpecificity.MINOR; } if (targetVersionNums.Length != 4) { Array.Resize(ref targetVersionNums, 4); } if (actualVersionNums.Length != 4) { Array.Resize(ref actualVersionNums, 4); } valid = (versionSpecificity == VersionSpecificity.MAJOR && actualVersionNums[0] == targetVersionNums[0] && (actualVersionNums[1] > targetVersionNums[1] || (actualVersionNums[1] == targetVersionNums[1] && (actualVersionNums[2] > targetVersionNums[2] || (actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] >= targetVersionNums[3]))))) || (versionSpecificity == VersionSpecificity.MINOR && actualVersionNums[0] == targetVersionNums[0] && actualVersionNums[1] == targetVersionNums[1] && (actualVersionNums[2] > targetVersionNums[2] || (actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] >= targetVersionNums[3]))) || (versionSpecificity == VersionSpecificity.NONBREAKING && actualVersionNums[0] == targetVersionNums[0] && actualVersionNums[1] == targetVersionNums[1] && actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] >= targetVersionNums[3]) || (versionSpecificity == VersionSpecificity.BUGFIX && actualVersionNums[0] == targetVersionNums[0] && actualVersionNums[1] == targetVersionNums[1] && actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] == targetVersionNums[3]); } } } if (valid) { QueuedGadgets.Add(gadget); } else { gadget.Gadget.Enabled = false; GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false; Gadgets.UnregisterGadget(gadget); Logger.LogWarning("Aborted loading Gadget " + gadget.Attribute.Name + " due to missing dependencies."); } } Logger.Log("Loading Gadget configs..."); foreach (GadgetInfo gadget in QueuedGadgets.ToList()) { Logger.Log("Loading Config for Gadget '" + gadget.Attribute.Name + "'"); try { gadget.Gadget.LoadConfig(); } catch (Exception e) { gadget.Gadget.Enabled = false; GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false; Gadgets.UnregisterGadget(gadget); QueuedGadgets.Remove(gadget); Logger.LogError("Exception Loading Config For Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString()); } } Logger.Log("Done loading Gadget configs."); Logger.Log("Preparing Gadgets for patching..."); foreach (GadgetInfo gadget in QueuedGadgets.ToList()) { Logger.Log("PrePatching Gadget '" + gadget.Attribute.Name + "'"); try { gadget.Gadget.HarmonyInstance = new Harmony(gadget.Mod.Name + "." + gadget.Attribute.Name + ".gadget"); gadget.Gadget.PrePatch(); } catch (Exception e) { gadget.Gadget.Enabled = false; GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false; Gadgets.UnregisterGadget(gadget); QueuedGadgets.Remove(gadget); Logger.LogError("Exception PrePatching Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString()); } } Logger.Log("Done preparing Gadgets for patching."); Logger.Log("Patching Gadgets..."); foreach (GadgetInfo gadget in QueuedGadgets.ToList()) { int patches = 0, totalMethods = 0, errorPatching = 0, targetMissing = 0; try { gadget.Mod.Assembly.GetExportedTypes().Do(delegate(Type type) { HarmonyGadgetAttribute attribute = type.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(HarmonyGadgetAttribute)) as HarmonyGadgetAttribute; if (attribute?.Gadget == gadget.Attribute.Name && attribute.RequiredGadgets.All(x => Gadgets.GetGadget(x) != null)) { try { List <MethodInfo> methods = gadget.Gadget.HarmonyInstance.CreateClassProcessor(type).Patch(); totalMethods += methods?.Count ?? 0; if (methods == null || methods.Count == 0) { Logger.Log("Skipping patch '" + type.Name + "' for Gadget '" + gadget.Attribute.Name + "': Attribute target does not exist."); targetMissing++; } else { patches++; } } catch (Exception e) { if (e.InnerException == null || !e.InnerException.Message.EndsWith("returned an unexpected result: null")) { Logger.LogError("Exception running patch '" + type.Name + "' for Gadget '" + gadget.Attribute.Name + "': " + Environment.NewLine + e.ToString()); errorPatching++; } else { Logger.Log("Skipping patch '" + type.Name + "' for Gadget '" + gadget.Attribute.Name + "': TargetMethod returned null."); targetMissing++; } } } }); Logger.Log("Performed " + patches + " patches for '" + gadget.Attribute.Name + "'" + (totalMethods > 0 ? $" ({totalMethods} total methods patched)" : string.Empty) + (errorPatching > 0 ? targetMissing > 0 ? $" ({errorPatching} skipped due to errors, {targetMissing} skipped due to missing targets)" : $" ({errorPatching} skipped due to errors)" : targetMissing > 0 ? $" ({targetMissing} skipped due to missing targets)" : string.Empty)); } catch (Exception e) { gadget.Gadget.Enabled = false; GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false; Gadgets.UnregisterGadget(gadget); QueuedGadgets.Remove(gadget); Logger.LogError("Exception patching Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString()); } } Logger.Log("Done patching Gadgets."); bool logOverrideWarnings = false; foreach (MethodBase patchedMethod in Harmony.GetAllPatchedMethods()) { HarmonyLib.Patches patches = Harmony.GetPatchInfo(patchedMethod); if (patches == null) { continue; } Dictionary <string, string> owners = new Dictionary <string, string>(); List <Patch> overridingPrefixes = patches.Prefixes.Where(x => x.PatchMethod.ReturnType == typeof(bool) && (x.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverridesAttribute)) as HarmonyOverridesAttribute)?.Overrides.Length != 0).ToList(); if (overridingPrefixes != null && overridingPrefixes.Count > 0) { foreach (string ownerID in patches.Owners) { if (ownerID == GadgetCore.HarmonyInstance.Id) { owners[ownerID] = "GadgetCore"; } else { string[] splitOwnerID = ownerID.Split('.'); if (splitOwnerID.Length == 3 && splitOwnerID[2] == "gadget") { GadgetMod mod = GadgetMods.GetModByName(splitOwnerID[0]); GadgetInfo gadget = mod?.LoadedGadgets.SingleOrDefault(x => x.Attribute.Name == splitOwnerID[1]); if (gadget != null) { owners[ownerID] = $"'{splitOwnerID[1]}' from the mod {{{splitOwnerID[0]}}}"; } else { owners[ownerID] = $"Unrecognized patcher '{ownerID}'"; } } else { owners[ownerID] = $"Unrecognized patcher '{ownerID}'"; } } } IEnumerable <Patch> problematicPrefixes = patches.Prefixes.Where(x => !overridingPrefixes.Contains(x) && (x.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverriddenAttribute)) as HarmonyOverriddenAttribute)?.Overrides.Length != 0 && overridingPrefixes.Any( p => x.owner != p.owner && (x.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverriddenAttribute)) as HarmonyOverriddenAttribute)?.Overrides.Contains(p.owner) != true && (p.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverridesAttribute)) as HarmonyOverridesAttribute)?.Overrides.Contains(x.owner) != true && x.priority <= p.priority && !x.before.Contains(p.owner) && !p.after.Contains(x.owner))); IEnumerable <Patch> problematicTranspilers = patches.Transpilers.Where(x => (x.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverriddenAttribute)) as HarmonyOverriddenAttribute)?.Overrides.Length != 0 && overridingPrefixes.Any( p => x.owner != p.owner && (x.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverriddenAttribute)) as HarmonyOverriddenAttribute)?.Overrides.Contains(p.owner) != true && (p.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverridesAttribute)) as HarmonyOverridesAttribute)?.Overrides.Contains(x.owner) != true)); if (problematicPrefixes != null && problematicPrefixes.Any() || problematicTranspilers != null && problematicTranspilers.Any()) { if (!logOverrideWarnings) { logOverrideWarnings = true; Logger.LogWarning("Possibly problematic patch overrides detected!", false); } Logger.Log($"Patches to {patchedMethod.DeclaringType.FullName}.{patchedMethod.Name} by {overridingPrefixes.Select(x => owners[x.owner]).Concat()} may override the following patch{(problematicPrefixes.Count() + problematicTranspilers.Count() > 1 ? "es" : "")}:"); if (problematicPrefixes != null) { foreach (Patch patch in problematicPrefixes) { Logger.Log($" - Prefix from {owners[patch.owner]}"); } } if (problematicTranspilers != null) { foreach (Patch patch in problematicTranspilers) { Logger.Log($" - Transpiler from {owners[patch.owner]}"); } } } } } if (logOverrideWarnings) { Logger.Log("End of possibly problematic patch overrides."); } Logger.Log("Creating registries..."); foreach (GadgetInfo gadget in QueuedGadgets.ToList()) { if (gadget.Gadget.Enabled) { foreach (Registry registry in gadget.Gadget.CreateRegistries()) { GameRegistry.RegisterRegistry(registry); } } } Logger.Log("Done creating registries."); Logger.Log("Initializing Gadgets..."); foreach (GadgetInfo gadget in QueuedGadgets.ToList()) { Logger.Log("Initializing Gadget '" + gadget.Attribute.Name + "'"); try { Registry.gadgetRegistering = gadget.Gadget.ModID; gadget.Gadget.LoadInternal(); } catch (Exception e) { gadget.Gadget.Enabled = false; GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false; Gadgets.UnregisterGadget(gadget); QueuedGadgets.Remove(gadget); Logger.LogError("Exception Initializing Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString()); } finally { Registry.gadgetRegistering = -1; } } Logger.Log("Done initializing Gadgets."); QueuedGadgets.Clear(); GadgetCoreConfig.Update(); }