From 163eebd92e21075698986a843850f0850514e778 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 22 May 2020 19:57:22 -0400 Subject: move internal commands out of SCore --- src/SMAPI/Framework/Commands/HelpCommand.cs | 64 +++++++++++++++++++++++ src/SMAPI/Framework/Commands/IInternalCommand.cs | 24 +++++++++ src/SMAPI/Framework/Commands/ReloadI18nCommand.cs | 44 ++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 src/SMAPI/Framework/Commands/HelpCommand.cs create mode 100644 src/SMAPI/Framework/Commands/IInternalCommand.cs create mode 100644 src/SMAPI/Framework/Commands/ReloadI18nCommand.cs (limited to 'src/SMAPI/Framework/Commands') diff --git a/src/SMAPI/Framework/Commands/HelpCommand.cs b/src/SMAPI/Framework/Commands/HelpCommand.cs new file mode 100644 index 00000000..b8730a00 --- /dev/null +++ b/src/SMAPI/Framework/Commands/HelpCommand.cs @@ -0,0 +1,64 @@ +using System.Linq; + +namespace StardewModdingAPI.Framework.Commands +{ + /// The 'help' SMAPI console command. + internal class HelpCommand : IInternalCommand + { + /********* + ** Fields + *********/ + /// Manages console commands. + private readonly CommandManager CommandManager; + + + /********* + ** Accessors + *********/ + /// The command name, which the user must type to trigger it. + public string Name { get; } = "help"; + + /// The human-readable documentation shown when the player runs the built-in 'help' command. + public string Description { get; } = "Lists command documentation.\n\nUsage: help\nLists all available commands.\n\nUsage: help \n- cmd: The name of a command whose documentation to display."; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Manages console commands. + public HelpCommand(CommandManager commandManager) + { + this.CommandManager = commandManager; + } + + /// Handle the console command when it's entered by the user. + /// The command arguments. + /// Writes messages to the console. + public void HandleCommand(string[] args, IMonitor monitor) + { + if (args.Any()) + { + Command result = this.CommandManager.Get(args[0]); + if (result == null) + monitor.Log("There's no command with that name.", LogLevel.Error); + else + monitor.Log($"{result.Name}: {result.Documentation}{(result.Mod != null ? $"\n(Added by {result.Mod.DisplayName}.)" : "")}", LogLevel.Info); + } + else + { + string message = "The following commands are registered:\n"; + IGrouping[] groups = (from command in this.CommandManager.GetAll() orderby command.Mod?.DisplayName, command.Name group command.Name by command.Mod?.DisplayName).ToArray(); + foreach (var group in groups) + { + string modName = group.Key ?? "SMAPI"; + string[] commandNames = group.ToArray(); + message += $"{modName}:\n {string.Join("\n ", commandNames)}\n\n"; + } + message += "For more information about a command, type 'help command_name'."; + + monitor.Log(message, LogLevel.Info); + } + } + } +} diff --git a/src/SMAPI/Framework/Commands/IInternalCommand.cs b/src/SMAPI/Framework/Commands/IInternalCommand.cs new file mode 100644 index 00000000..abf105b6 --- /dev/null +++ b/src/SMAPI/Framework/Commands/IInternalCommand.cs @@ -0,0 +1,24 @@ +namespace StardewModdingAPI.Framework.Commands +{ + /// A core SMAPI console command. + interface IInternalCommand + { + /********* + ** Accessors + *********/ + /// The command name, which the user must type to trigger it. + string Name { get; } + + /// The human-readable documentation shown when the player runs the built-in 'help' command. + string Description { get; } + + + /********* + ** Methods + *********/ + /// Handle the console command when it's entered by the user. + /// The command arguments. + /// Writes messages to the console. + void HandleCommand(string[] args, IMonitor monitor); + } +} diff --git a/src/SMAPI/Framework/Commands/ReloadI18nCommand.cs b/src/SMAPI/Framework/Commands/ReloadI18nCommand.cs new file mode 100644 index 00000000..12328bb6 --- /dev/null +++ b/src/SMAPI/Framework/Commands/ReloadI18nCommand.cs @@ -0,0 +1,44 @@ +using System; + +namespace StardewModdingAPI.Framework.Commands +{ + /// The 'reload_i18n' SMAPI console command. + internal class ReloadI18nCommand : IInternalCommand + { + /********* + ** Fields + *********/ + /// Reload translations for all mods. + private readonly Action ReloadTranslations; + + + /********* + ** Accessors + *********/ + /// The command name, which the user must type to trigger it. + public string Name { get; } = "reload_i18n"; + + /// The human-readable documentation shown when the player runs the built-in 'help' command. + public string Description { get; } = "Reloads translation files for all mods.\n\nUsage: reload_i18n"; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Reload translations for all mods.. + public ReloadI18nCommand(Action reloadTranslations) + { + this.ReloadTranslations = reloadTranslations; + } + + /// Handle the console command when it's entered by the user. + /// The command arguments. + /// Writes messages to the console. + public void HandleCommand(string[] args, IMonitor monitor) + { + this.ReloadTranslations(); + monitor.Log("Reloaded translation files for all mods. This only affects new translations the mods fetch; if they cached some text, it may not be updated.", LogLevel.Info); + } + } +} -- cgit From b074eb279a649030e71ed2192bb6a528a9801fce Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 22 May 2020 20:00:33 -0400 Subject: add harmony_summary command --- .../Framework/Commands/HarmonySummaryCommand.cs | 167 +++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs (limited to 'src/SMAPI/Framework/Commands') diff --git a/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs new file mode 100644 index 00000000..bc8f4aa2 --- /dev/null +++ b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using HarmonyLib; + +namespace StardewModdingAPI.Framework.Commands +{ + /// The 'harmony_summary' SMAPI console command. + internal class HarmonySummaryCommand : IInternalCommand + { + /********* + ** Accessors + *********/ + /// The command name, which the user must type to trigger it. + public string Name { get; } = "harmony_summary"; + + /// The human-readable documentation shown when the player runs the built-in 'help' command. + public string Description { get; } = "Harmony is a library which rewrites game code, used by SMAPI and some mods. This command lists current Harmony patches.\n\nUsage: harmony_summary\nList all Harmony patches.\n\nUsage: harmony_summary \n- search: one more more words to search. If any word matches a method name, the method and all its patchers will be listed; otherwise only matching patchers will be listed for the method."; + + + /********* + ** Public methods + *********/ + /// Handle the console command when it's entered by the user. + /// The command arguments. + /// Writes messages to the console. + public void HandleCommand(string[] args, IMonitor monitor) + { + SearchResult[] matches = this.FilterPatches(args).OrderBy(p => p.Method).ToArray(); + + StringBuilder result = new StringBuilder(); + + if (!matches.Any()) + result.AppendLine("No current patches match your search."); + else + { + result.AppendLine(args.Any() ? "Harmony patches which match your search terms:" : "Current Harmony patches:"); + result.AppendLine(); + foreach (var match in matches) + { + result.AppendLine($" {match.Method}"); + foreach (var ownerGroup in match.PatchTypesByOwner) + { + var sortedTypes = ownerGroup.Value + .OrderBy(p => p switch { PatchType.Prefix => 0, PatchType.Postfix => 1, PatchType.Finalizer => 2, PatchType.Transpiler => 3, _ => 4 }); + + result.AppendLine($" - {ownerGroup.Key} ({string.Join(", ", sortedTypes).ToLower()})"); + } + } + } + + monitor.Log(result.ToString(), LogLevel.Info); + } + + + /********* + ** Private methods + *********/ + /// Get all current Harmony patches matching any of the given search terms. + /// The search terms to match. + private IEnumerable FilterPatches(string[] searchTerms) + { + bool hasSearch = searchTerms.Any(); + bool IsMatch(string target) => searchTerms.Any(search => target != null && target.IndexOf(search, StringComparison.OrdinalIgnoreCase) > -1); + + foreach (var patch in this.GetAllPatches()) + { + if (!hasSearch) + yield return patch; + + // matches entire patch + if (IsMatch(patch.Method)) + { + yield return patch; + continue; + } + + // matches individual patchers + foreach (var pair in patch.PatchTypesByOwner.ToArray()) + { + if (!IsMatch(pair.Key) && !pair.Value.Any(type => IsMatch(type.ToString()))) + patch.PatchTypesByOwner.Remove(pair.Key); + } + + if (patch.PatchTypesByOwner.Any()) + yield return patch; + } + } + + /// Get all current Harmony patches. + private IEnumerable GetAllPatches() + { + foreach (MethodBase method in Harmony.GetAllPatchedMethods()) + { + // get metadata for method + string methodLabel = method.FullDescription(); + HarmonyLib.Patches patchInfo = Harmony.GetPatchInfo(method); + IDictionary> patchGroups = new Dictionary> + { + [PatchType.Prefix] = patchInfo.Prefixes, + [PatchType.Postfix] = patchInfo.Postfixes, + [PatchType.Finalizer] = patchInfo.Finalizers, + [PatchType.Transpiler] = patchInfo.Transpilers + }; + + // get patch types by owner + var typesByOwner = new Dictionary>(); + foreach (var group in patchGroups) + { + foreach (var patch in group.Value) + { + if (!typesByOwner.TryGetValue(patch.owner, out ISet patchTypes)) + typesByOwner[patch.owner] = patchTypes = new HashSet(); + patchTypes.Add(group.Key); + } + } + + // create search result + yield return new SearchResult(methodLabel, typesByOwner); + } + } + + /// A Harmony patch type. + private enum PatchType + { + /// A prefix patch. + Prefix, + + /// A postfix patch. + Postfix, + + /// A finalizer patch. + Finalizer, + + /// A transpiler patch. + Transpiler + } + + /// A patch search result for a method. + private class SearchResult + { + /********* + ** Accessors + *********/ + /// A detailed human-readable label for the patched method. + public string Method { get; } + + /// The patch types by the Harmony instance ID that added them. + public IDictionary> PatchTypesByOwner { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// A detailed human-readable label for the patched method. + /// The patch types by the Harmony instance ID that added them. + public SearchResult(string method, IDictionary> patchTypesByOwner) + { + this.Method = method; + this.PatchTypesByOwner = patchTypesByOwner; + } + } + } +} -- cgit From 7bbbef7687d958a351b5491e2bb20116c23c31bd Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 30 May 2020 22:45:43 -0400 Subject: simplify harmony_search output, tweak SMAPI's Harmony ID --- .../Framework/Commands/HarmonySummaryCommand.cs | 33 +++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'src/SMAPI/Framework/Commands') diff --git a/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs index bc8f4aa2..08233feb 100644 --- a/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs +++ b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs @@ -28,7 +28,7 @@ namespace StardewModdingAPI.Framework.Commands /// Writes messages to the console. public void HandleCommand(string[] args, IMonitor monitor) { - SearchResult[] matches = this.FilterPatches(args).OrderBy(p => p.Method).ToArray(); + SearchResult[] matches = this.FilterPatches(args).OrderBy(p => p.MethodName).ToArray(); StringBuilder result = new StringBuilder(); @@ -40,18 +40,19 @@ namespace StardewModdingAPI.Framework.Commands result.AppendLine(); foreach (var match in matches) { - result.AppendLine($" {match.Method}"); - foreach (var ownerGroup in match.PatchTypesByOwner) + result.AppendLine($" {match.MethodName}"); + foreach (var ownerGroup in match.PatchTypesByOwner.OrderBy(p => p.Key)) { var sortedTypes = ownerGroup.Value .OrderBy(p => p switch { PatchType.Prefix => 0, PatchType.Postfix => 1, PatchType.Finalizer => 2, PatchType.Transpiler => 3, _ => 4 }); result.AppendLine($" - {ownerGroup.Key} ({string.Join(", ", sortedTypes).ToLower()})"); } + result.AppendLine(); } } - monitor.Log(result.ToString(), LogLevel.Info); + monitor.Log(result.ToString().TrimEnd(), LogLevel.Info); } @@ -63,15 +64,12 @@ namespace StardewModdingAPI.Framework.Commands private IEnumerable FilterPatches(string[] searchTerms) { bool hasSearch = searchTerms.Any(); - bool IsMatch(string target) => searchTerms.Any(search => target != null && target.IndexOf(search, StringComparison.OrdinalIgnoreCase) > -1); + bool IsMatch(string target) => !hasSearch || searchTerms.Any(search => target != null && target.IndexOf(search, StringComparison.OrdinalIgnoreCase) > -1); foreach (var patch in this.GetAllPatches()) { - if (!hasSearch) - yield return patch; - // matches entire patch - if (IsMatch(patch.Method)) + if (IsMatch(patch.MethodDescription)) { yield return patch; continue; @@ -95,7 +93,6 @@ namespace StardewModdingAPI.Framework.Commands foreach (MethodBase method in Harmony.GetAllPatchedMethods()) { // get metadata for method - string methodLabel = method.FullDescription(); HarmonyLib.Patches patchInfo = Harmony.GetPatchInfo(method); IDictionary> patchGroups = new Dictionary> { @@ -118,7 +115,7 @@ namespace StardewModdingAPI.Framework.Commands } // create search result - yield return new SearchResult(methodLabel, typesByOwner); + yield return new SearchResult(method, typesByOwner); } } @@ -144,8 +141,11 @@ namespace StardewModdingAPI.Framework.Commands /********* ** Accessors *********/ - /// A detailed human-readable label for the patched method. - public string Method { get; } + /// A simple human-readable name for the patched method. + public string MethodName { get; } + + /// A detailed description for the patched method. + public string MethodDescription { get; } /// The patch types by the Harmony instance ID that added them. public IDictionary> PatchTypesByOwner { get; } @@ -155,11 +155,12 @@ namespace StardewModdingAPI.Framework.Commands ** Public methods *********/ /// Construct an instance. - /// A detailed human-readable label for the patched method. + /// The patched method. /// The patch types by the Harmony instance ID that added them. - public SearchResult(string method, IDictionary> patchTypesByOwner) + public SearchResult(MethodBase method, IDictionary> patchTypesByOwner) { - this.Method = method; + this.MethodName = $"{method.DeclaringType?.FullName}.{method.Name}"; + this.MethodDescription = method.FullDescription(); this.PatchTypesByOwner = patchTypesByOwner; } } -- cgit From dcd2c647a2abd836e8ee20f8ddad6568c9b4fbf2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 15 Jun 2020 22:17:32 -0400 Subject: temporarily restore Harmony 1.x support with compile flag (#711) --- src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/SMAPI/Framework/Commands') diff --git a/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs index 08233feb..8c20fbdd 100644 --- a/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs +++ b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs @@ -1,3 +1,4 @@ +#if HARMONY_2 using System; using System.Collections.Generic; using System.Linq; @@ -166,3 +167,4 @@ namespace StardewModdingAPI.Framework.Commands } } } +#endif -- cgit From aeab19f4aca366be3446b9f1ee097d51d21b5fde Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 19 Jun 2020 21:28:44 -0400 Subject: backport harmony_summary command to Harmony 1.x (#711) --- .../Framework/Commands/HarmonySummaryCommand.cs | 38 ++++++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) (limited to 'src/SMAPI/Framework/Commands') diff --git a/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs index 8c20fbdd..8fdd4282 100644 --- a/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs +++ b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs @@ -1,16 +1,27 @@ -#if HARMONY_2 using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; +#if HARMONY_2 using HarmonyLib; +#else +using Harmony; +#endif namespace StardewModdingAPI.Framework.Commands { /// The 'harmony_summary' SMAPI console command. internal class HarmonySummaryCommand : IInternalCommand { +#if !HARMONY_2 + /********* + ** Fields + *********/ + /// The Harmony instance through which to fetch patch info. + private readonly HarmonyInstance HarmonyInstance = HarmonyInstance.Create($"SMAPI.{nameof(HarmonySummaryCommand)}"); +#endif + /********* ** Accessors *********/ @@ -45,7 +56,16 @@ namespace StardewModdingAPI.Framework.Commands foreach (var ownerGroup in match.PatchTypesByOwner.OrderBy(p => p.Key)) { var sortedTypes = ownerGroup.Value - .OrderBy(p => p switch { PatchType.Prefix => 0, PatchType.Postfix => 1, PatchType.Finalizer => 2, PatchType.Transpiler => 3, _ => 4 }); + .OrderBy(p => p switch + { + PatchType.Prefix => 0, + PatchType.Postfix => 1, +#if HARMONY_2 + PatchType.Finalizer => 2, +#endif + PatchType.Transpiler => 3, + _ => 4 + }); result.AppendLine($" - {ownerGroup.Key} ({string.Join(", ", sortedTypes).ToLower()})"); } @@ -91,15 +111,26 @@ namespace StardewModdingAPI.Framework.Commands /// Get all current Harmony patches. private IEnumerable GetAllPatches() { +#if HARMONY_2 foreach (MethodBase method in Harmony.GetAllPatchedMethods()) +#else + foreach (MethodBase method in this.HarmonyInstance.GetPatchedMethods()) +#endif { // get metadata for method +#if HARMONY_2 HarmonyLib.Patches patchInfo = Harmony.GetPatchInfo(method); +#else + Harmony.Patches patchInfo = this.HarmonyInstance.GetPatchInfo(method); +#endif + IDictionary> patchGroups = new Dictionary> { [PatchType.Prefix] = patchInfo.Prefixes, [PatchType.Postfix] = patchInfo.Postfixes, +#if HARMONY_2 [PatchType.Finalizer] = patchInfo.Finalizers, +#endif [PatchType.Transpiler] = patchInfo.Transpilers }; @@ -129,8 +160,10 @@ namespace StardewModdingAPI.Framework.Commands /// A postfix patch. Postfix, +#if HARMONY_2 /// A finalizer patch. Finalizer, +#endif /// A transpiler patch. Transpiler @@ -167,4 +200,3 @@ namespace StardewModdingAPI.Framework.Commands } } } -#endif -- cgit