private static void PatchInternalMethodFull(MethodInfo method, Category category) { try { var guiEntry = method.DeclaringType + ":" + method.Name + "-int"; GUIController.AddEntry(guiEntry, category); GUIController.SwapToEntry(guiEntry); InternalMethodUtility.PatchedInternals.Add(method); Task.Factory.StartNew(() => { try { Modbase.Harmony.Patch(method, transpiler: InternalMethodUtility.InternalProfiler); } catch (Exception e) { Error($"Failed to internal patch method {method.DeclaringType.FullName}:{method.Name}, failed with the exep " + e.Message); } }); } catch (Exception e) { Error("Failed to patch internal methods, failed with the error " + e.Message); InternalMethodUtility.PatchedInternals.Remove(method); } }
public static void PatchAssembly(string name, Category type) { var mod = LoadedModManager.RunningMods.FirstOrDefault(m => m.Name == name || m.PackageId == name.ToLower()); if (mod == null) { Error($"Failed to locate the mod {name}"); return; } Notify($"Assembly count: {mod.assemblies?.loadedAssemblies?.Count ?? 0}"); var assemblies = mod?.assemblies?.loadedAssemblies?.Where(ValidAssembly).ToList(); Notify($"Assembly count after removing Harmony/Cecil/Multiplayer/UnityEngine: {assemblies?.Count}"); if (assemblies != null && assemblies.Count() != 0) { GUIController.AddEntry(mod.Name + "-prof", type); GUIController.SwapToEntry(mod.Name + "-prof"); Task.Factory.StartNew(() => PatchAssemblyFull(mod.Name + "-prof", assemblies.ToList())); } else { Error($"Failed to patch {name} - There are no assemblies"); } }
public static void Draw(Rect rect) { var row = rect.LeftPartPixels(25f); if (Widgets.ButtonImage(row, TexButton.SpeedButtonTextures[Analyzer.CurrentlyPaused ? 1 : 0])) { Analyzer.CurrentlyPaused = !Analyzer.CurrentlyPaused; GUIController.CurrentEntry.SetActive(!Analyzer.CurrentlyPaused); } TooltipHandler.TipRegion(row, Strings.top_pause_analyzer); rect.AdjustHorizonallyBy(25f); row = rect.LeftPartPixels(25); if (Widgets.ButtonImage(row, Textures.refresh)) { GUIController.ResetProfilers(); } TooltipHandler.TipRegion(row, Strings.top_refresh); var searchbox = rect.LeftPartPixels(rect.width - 300f); searchbox.x += 25f; DubGUI.InputField(searchbox, Strings.top_search, ref TimesFilter, DubGUI.MintSearch); rect.AdjustHorizonallyBy(rect.width - 250f); Text.Anchor = TextAnchor.UpperLeft; Text.Font = GameFont.Tiny; var cat = GUIController.CurrentCategory == Category.Tick ? "tick" : "update"; var str = $"{ProfileController.updateAverage:F3}ms/{cat}"; var strLen = str.GetWidthCached(); var periodLen = rect.LeftPartPixels(130); rect.AdjustHorizonallyBy(130); Widgets.Label(periodLen, str); var tpsFpsRect = rect; tpsFpsRect.width = 50f; Widgets.Label(tpsFpsRect, $"FPS: {GUIElement_TPS.FPS}"); TooltipHandler.TipRegion(tpsFpsRect, Strings.top_fps_tip); tpsFpsRect.x = tpsFpsRect.xMax + 5; tpsFpsRect.width = 90f; Widgets.Label(tpsFpsRect, $"TPS: {GUIElement_TPS.TPS}({GUIElement_TPS.TPSTarget})"); TooltipHandler.TipRegion(tpsFpsRect, Strings.top_tps_tip); tpsFpsRect.x = tpsFpsRect.xMax + 5; tpsFpsRect.width = 30f; Text.Font = GameFont.Medium; }
public static void Draw(Rect rect) { var row = rect.LeftPartPixels(25f); if (Widgets.ButtonImage(row, TexButton.SpeedButtonTextures[Analyzer.CurrentlyPaused ? 1 : 0])) { Analyzer.CurrentlyPaused = !Analyzer.CurrentlyPaused; GUIController.CurrentEntry.SetActive(!Analyzer.CurrentlyPaused); } TooltipHandler.TipRegion(row, Strings.top_pause_analyzer); rect.AdjustHorizonallyBy(25f); row = rect.LeftPartPixels(25); if (Widgets.ButtonImage(row, Textures.refresh)) { GUIController.ResetProfilers(); } TooltipHandler.TipRegion(row, Strings.top_refresh); var searchbox = rect.LeftPartPixels(rect.width - 220f); searchbox.x += 25f; DubGUI.InputField(searchbox, Strings.top_search, ref TimesFilter, DubGUI.MintSearch); // searchbox.x = searchbox.xMax; // searchbox.width = 150; // GUI.color = Color.grey; // Widgets.Label(searchbox, MatchType); // GUI.color = Color.white; // bit shitty and distracting, replace with a mini graph and or an entire page dedicated to garbage if it even matters realistically now which it probably doesn't so why bother aye just keep it clean //row.x = searchbox.xMax + 5; // row.width = 130f; //Text.Anchor = TextAnchor.MiddleCenter; //Widgets.FillableBar(row, Mathf.Clamp01(Mathf.InverseLerp(H_RootUpdate.LastMinGC, H_RootUpdate.LastMaxGC, H_RootUpdate.totalBytesOfMemoryUsed)), Textures.darkgrey); //Widgets.Label(row, H_RootUpdate.GarbageCollectionInfo); //TooltipHandler.TipRegion(row, Strings.top_gc_tip); Text.Anchor = TextAnchor.UpperLeft; Text.Font = GameFont.Tiny; row.width = 50f; row.x = searchbox.xMax + 10; Widgets.Label(row, $"FPS: {GUIElement_TPS.FPS}"); TooltipHandler.TipRegion(row, Strings.top_fps_tip); row.x = row.xMax + 5; row.width = 90f; Widgets.Label(row, $"TPS: {GUIElement_TPS.TPS}({GUIElement_TPS.TPSTarget})"); TooltipHandler.TipRegion(row, Strings.top_tps_tip); row.x = row.xMax + 5; row.width = 30f; Text.Font = GameFont.Medium; }
private static void PatchInternalMethodFull(MethodInfo method, Category category) { try { bool Valid() { var bytes = method.GetMethodBody()?.GetILAsByteArray(); if (bytes == null) { return(false); } if (bytes.Length == 0) { return(false); } if (bytes.Length == 1 && bytes.First() == 0x2A) { return(false); } return(true); } if (Valid() == false) { Error("Can not patch this method, this is likely a method which is virtually dispatched or marked as external, and thus can not be generically examined."); return; } var guiEntry = method.DeclaringType + ":" + method.Name + "-int"; GUIController.AddEntry(guiEntry, category); GUIController.SwapToEntry(guiEntry); InternalMethodUtility.PatchedInternals.Add(method); Task.Factory.StartNew(() => { try { Modbase.Harmony.Patch(method, transpiler: InternalMethodUtility.InternalProfiler); } catch (Exception e) { ReportException(e, $"Failed to patch the internal methods within {Utility.GetSignature(method, false)}"); } }); } catch (Exception e) { ReportException(e, "Failed to set up state to patch internal methods"); InternalMethodUtility.PatchedInternals.Remove(method); } }
public static void ExecutePatch(CurrentInput mode, string strinput, Category cat) { try { var entry = cat == Category.Tick ? typeof(CustomProfilersTick) : typeof(CustomProfilersUpdate); List <MethodInfo> methods = null; var temp = mode switch { CurrentInput.Method => Utility.GetMethods(strinput), CurrentInput.Type => Utility.GetTypeMethods(AccessTools.TypeByName(strinput)), CurrentInput.MethodHarmony => Utility.GetMethodsPatching(strinput), CurrentInput.SubClasses => Utility.SubClassImplementationsOf(AccessTools.TypeByName(strinput), m => true), CurrentInput.TypeHarmony => Utility.GetMethodsPatchingType(AccessTools.TypeByName(strinput)), _ => null, }; if (temp is null) { if (mode == CurrentInput.InternalMethod) { Utility.PatchInternalMethod(strinput, cat); } else { Utility.PatchAssembly(strinput, cat); } return; } methods = temp.ToList(); if (methods.Count == 0) { Messages.Message($"Failed to find the method(s) represented by {strinput}", MessageTypeDefOf.CautionInput, false); return; } Messages.Message(methods.Count != 1 ? $"Patching {methods.Count} methods in the category {cat}" : $"Patching {Utility.GetSignature(methods[0])} in the category {cat}", MessageTypeDefOf.CautionInput, false); MethodTransplanting.UpdateMethods(entry, methods.ToList()); GUIController.Tab(cat).collapsed = false; var entryName = (cat == Category.Tick) ? "Custom Tick" : "Custom Update"; GUIController.SwapToEntry(entryName); } catch (Exception e) { ThreadSafeLogger.ReportException(e, $"Failed to process search bar input"); } }
public static void Draw(Rect rect) { Rect row = rect.LeftPartPixels(25f); if (Widgets.ButtonImage(row, TexButton.SpeedButtonTextures[Analyzer.CurrentlyPaused ? 1 : 0])) { Analyzer.CurrentlyPaused = !Analyzer.CurrentlyPaused; GUIController.CurrentEntry.SetActive(!Analyzer.CurrentlyPaused); } TooltipHandler.TipRegion(row, ResourceCache.Strings.top_pause_analyzer); rect.AdjustHorizonallyBy(25f); row = rect.LeftPartPixels(25); if (Widgets.ButtonImage(row, ResourceCache.GUI.refresh)) { GUIController.ResetProfilers(); } TooltipHandler.TipRegion(row, ResourceCache.Strings.top_refresh); Rect searchbox = rect.LeftPartPixels(rect.width - 350f); searchbox.x += 25f; DubGUI.InputField(searchbox, ResourceCache.Strings.top_search, ref TimesFilter, DubGUI.MintSearch); row.x = searchbox.xMax + 5; row.width = 130f; Text.Anchor = TextAnchor.MiddleCenter; Text.Font = GameFont.Tiny; Widgets.FillableBar(row, Mathf.Clamp01(Mathf.InverseLerp(H_RootUpdate.LastMinGC, H_RootUpdate.LastMaxGC, H_RootUpdate.totalBytesOfMemoryUsed)), ResourceCache.GUI.darkgrey); Widgets.Label(row, H_RootUpdate.GarbageCollectionInfo); Text.Anchor = TextAnchor.UpperLeft; TooltipHandler.TipRegion(row, ResourceCache.Strings.top_gc_tip); row.x = row.xMax + 5; row.width = 50f; Widgets.Label(row, "FPS: " + GUIElement_TPS.FPS.ToString()); TooltipHandler.TipRegion(row, ResourceCache.Strings.top_fps_tip); row.x = row.xMax + 5; row.width = 90f; Widgets.Label(row, "TPS: " + GUIElement_TPS.TPS.ToString() + "(" + GUIElement_TPS.TPSTarget.ToString() + ")"); TooltipHandler.TipRegion(row, ResourceCache.Strings.top_tps_tip); row.x = row.xMax + 5; row.width = 30f; Text.Font = GameFont.Medium; }
// Iterates through each child element in the document and attempts to extract method(s) from the strings inside the children private static void Parse(XmlDocument doc) { foreach (XmlNode node in doc.DocumentElement.ChildNodes) // entries should be { var meths = new HashSet <MethodInfo>(); foreach (XmlNode child in node.ChildNodes) { switch (child.Name.ToLower()) { case "methods": case "method": foreach (XmlNode method in child.ChildNodes) { meths.Add(ParseMethod(method.InnerText)); } break; case "types": case "type": foreach (XmlNode type in child.ChildNodes) { meths.AddRange(ParseTypeMethods(type.InnerText)); } break; case "nestedtype": case "nestedtypes": foreach (XmlNode type in child.ChildNodes) { meths.AddRange(ParseSubTypeTypeMethods(type.InnerText)); } break; default: ThreadSafeLogger.Error($"[Analyzer] Attempting to read unknown value from an Analyzer.xml, the given input was {child.Name}, it should have been either '(M/m)ethods', '(T/t)ypes' '(N/n)estedTypes"); break; } } Type myType = DynamicTypeBuilder.CreateType(node.Name, meths); GUIController.Tab(Category.Modder).entries.Add(Entry.Create(myType.Name, Category.Modder, myType, false, true), myType); } }
public static void ExecutePatch() { try { if (patchType == Category.Tick) { switch (input) { case CurrentInput.Method: MethodTransplanting.UpdateMethods(typeof(CustomProfilersTick), Utility.GetMethods(currentInput)); break; case CurrentInput.Type: MethodTransplanting.UpdateMethods(typeof(CustomProfilersTick), Utility.GetTypeMethods(AccessTools.TypeByName(currentInput))); break; case CurrentInput.MethodHarmony: MethodTransplanting.UpdateMethods(typeof(CustomProfilersTick), Utility.GetMethodsPatching(currentInput)); break; case CurrentInput.SubClasses: MethodTransplanting.UpdateMethods(typeof(CustomProfilersTick), Utility.SubClassImplementationsOf(AccessTools.TypeByName(currentInput), m => true)); break; case CurrentInput.TypeHarmony: MethodTransplanting.UpdateMethods(typeof(CustomProfilersTick), Utility.GetMethodsPatchingType(AccessTools.TypeByName(currentInput))); break; case CurrentInput.InternalMethod: Utility.PatchInternalMethod(currentInput, Category.Tick); return; case CurrentInput.Assembly: Utility.PatchAssembly(currentInput, Category.Tick); return; } GUIController.SwapToEntry("Custom Tick"); } else { switch (input) { case CurrentInput.Method: MethodTransplanting.UpdateMethods(typeof(CustomProfilersUpdate), Utility.GetMethods(currentInput)); break; case CurrentInput.Type: MethodTransplanting.UpdateMethods(typeof(CustomProfilersUpdate), Utility.GetTypeMethods(AccessTools.TypeByName(currentInput))); break; case CurrentInput.MethodHarmony: MethodTransplanting.UpdateMethods(typeof(CustomProfilersUpdate), Utility.GetMethodsPatching(currentInput)); break; case CurrentInput.SubClasses: MethodTransplanting.UpdateMethods(typeof(CustomProfilersUpdate), Utility.SubClassImplementationsOf(AccessTools.TypeByName(currentInput), m => true)); break; case CurrentInput.TypeHarmony: MethodTransplanting.UpdateMethods(typeof(CustomProfilersUpdate), Utility.GetMethodsPatchingType(AccessTools.TypeByName(currentInput))); break; case CurrentInput.InternalMethod: Utility.PatchInternalMethod(currentInput, Category.Update); return; case CurrentInput.Assembly: Utility.PatchAssembly(currentInput, Category.Update); return; } GUIController.SwapToEntry("Custom Update"); } } catch (Exception e) { ThreadSafeLogger.Error($"Failed to process input, failed with the error {e.Message}"); } }
private static void DrawEntry(ref Rect row, KeyValuePair <Entry, Type> entry) { row = listing.GetRect(30f); Widgets.DrawHighlightIfMouseover(row); if (GUIController.CurrentEntry == entry.Key) { Widgets.DrawOptionSelected(row); } row.x += 20f; yOffset += 30f; Widgets.Label(row, entry.Key.name); if (Widgets.ButtonInvisible(row)) { GUIController.SwapToEntry(entry.Key.name); } if (entry.Key.isClosable) { if (Input.GetMouseButtonDown(1) && row.Contains(Event.current.mousePosition)) { List <FloatMenuOption> options = new List <FloatMenuOption>() { new FloatMenuOption("Close", () => GUIController.RemoveEntry(entry.Key.name)) }; Find.WindowStack.Add(new FloatMenu(options)); } } TooltipHandler.TipRegion(row, entry.Key.tip); if (GUIController.CurrentEntry == entry.Key) { bool firstEntry = true; foreach (KeyValuePair <FieldInfo, Setting> keySetting in entry.Key.Settings) { if (keySetting.Key.FieldType == typeof(bool)) { row = listing.GetRect(30f); row.x += 20f; GUI.color = Widgets.OptionSelectedBGBorderColor; Widgets.DrawLineVertical(row.x, row.y, 15f); if (!firstEntry) { Widgets.DrawLineVertical(row.x, row.y - 15f, 15f); } row.x += 10f; Widgets.DrawLineHorizontal(row.x - 10f, row.y + 15f, 10f); GUI.color = Color.white; yOffset += 30f; bool cur = (bool)keySetting.Key.GetValue(null); if (DubGUI.Checkbox(row, keySetting.Value.name, ref cur)) { keySetting.Key.SetValue(null, cur); GUIController.ResetProfilers(); } } if (keySetting.Value.tip != null) { TooltipHandler.TipRegion(row, keySetting.Value.tip); } firstEntry = false; } } }