fix command errors logged as SMAPI instead of the affected mod

This commit is contained in:
Jesse Plamondon-Willard 2018-09-29 18:30:14 -04:00
parent 91b3344fea
commit c531acb659
6 changed files with 61 additions and 32 deletions

View File

@ -16,6 +16,7 @@
* Fixed transparency issues on Linux/Mac for some mod images.
* Fixed translation issues not shown as warnings.
* Fixed dependencies not correctly enforced if the dependency is installed but failed to load.
* Fixed some errors logged as SMAPI instead of the affected mod.
* Updated compatibility list.
* For modders:

View File

@ -1,4 +1,4 @@
using System;
using System;
namespace StardewModdingAPI.Framework
{
@ -8,8 +8,8 @@ namespace StardewModdingAPI.Framework
/*********
** Accessor
*********/
/// <summary>The friendly name for the mod that registered the command.</summary>
public string ModName { get; }
/// <summary>The mod that registered the command (or <c>null</c> if registered by SMAPI).</summary>
public IModMetadata Mod { get; }
/// <summary>The command name, which the user must type to trigger it.</summary>
public string Name { get; }
@ -25,13 +25,13 @@ namespace StardewModdingAPI.Framework
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="modName">The friendly name for the mod that registered the command.</param>
/// <param name="mod">The mod that registered the command (or <c>null</c> if registered by SMAPI).</param>
/// <param name="name">The command name, which the user must type to trigger it.</param>
/// <param name="documentation">The human-readable documentation shown when the player runs the built-in 'help' command.</param>
/// <param name="callback">The method to invoke when the command is triggered. This method is passed the command name and arguments submitted by the user.</param>
public Command(string modName, string name, string documentation, Action<string, string[]> callback)
public Command(IModMetadata mod, string name, string documentation, Action<string, string[]> callback)
{
this.ModName = modName;
this.Mod = mod;
this.Name = name;
this.Documentation = documentation;
this.Callback = callback;

View File

@ -19,7 +19,7 @@ namespace StardewModdingAPI.Framework
** Public methods
*********/
/// <summary>Add a console command.</summary>
/// <param name="modName">The friendly mod name for this instance.</param>
/// <param name="mod">The mod adding the command (or <c>null</c> for a SMAPI command).</param>
/// <param name="name">The command name, which the user must type to trigger it.</param>
/// <param name="documentation">The human-readable documentation shown when the player runs the built-in 'help' command.</param>
/// <param name="callback">The method to invoke when the command is triggered. This method is passed the command name and arguments submitted by the user.</param>
@ -27,7 +27,7 @@ namespace StardewModdingAPI.Framework
/// <exception cref="ArgumentNullException">The <paramref name="name"/> or <paramref name="callback"/> is null or empty.</exception>
/// <exception cref="FormatException">The <paramref name="name"/> is not a valid format.</exception>
/// <exception cref="ArgumentException">There's already a command with that name.</exception>
public void Add(string modName, string name, string documentation, Action<string, string[]> callback, bool allowNullCallback = false)
public void Add(IModMetadata mod, string name, string documentation, Action<string, string[]> callback, bool allowNullCallback = false)
{
name = this.GetNormalisedName(name);
@ -44,7 +44,7 @@ namespace StardewModdingAPI.Framework
throw new ArgumentException(nameof(callback), $"Can't register the '{name}' command because there's already a command with that name.");
// add command
this.Commands.Add(name, new Command(modName, name, documentation, callback));
this.Commands.Add(name, new Command(mod, name, documentation, callback));
}
/// <summary>Get a command by its unique name.</summary>
@ -65,19 +65,30 @@ namespace StardewModdingAPI.Framework
.OrderBy(p => p.Name);
}
/// <summary>Trigger a command.</summary>
/// <param name="input">The raw command input.</param>
/// <returns>Returns whether a matching command was triggered.</returns>
public bool Trigger(string input)
/// <summary>Try to parse a raw line of user input into an executable command.</summary>
/// <param name="input">The raw user input.</param>
/// <param name="name">The parsed command name.</param>
/// <param name="args">The parsed command arguments.</param>
/// <param name="command">The command which can handle the input.</param>
/// <returns>Returns true if the input was successfully parsed and matched to a command; else false.</returns>
public bool TryParse(string input, out string name, out string[] args, out Command command)
{
// ignore if blank
if (string.IsNullOrWhiteSpace(input))
{
name = null;
args = null;
command = null;
return false;
}
string[] args = this.ParseArgs(input);
string name = args[0];
// parse input
args = this.ParseArgs(input);
name = this.GetNormalisedName(args[0]);
args = args.Skip(1).ToArray();
return this.Trigger(name, args);
// get command
return this.Commands.TryGetValue(name, out command);
}
/// <summary>Trigger a command.</summary>

View File

@ -8,8 +8,8 @@ namespace StardewModdingAPI.Framework.ModHelpers
/*********
** Accessors
*********/
/// <summary>The friendly mod name for this instance.</summary>
private readonly string ModName;
/// <summary>The mod using this instance.</summary>
private readonly IModMetadata Mod;
/// <summary>Manages console commands.</summary>
private readonly CommandManager CommandManager;
@ -19,13 +19,12 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="modID">The unique ID of the relevant mod.</param>
/// <param name="modName">The friendly mod name for this instance.</param>
/// <param name="mod">The mod using this instance.</param>
/// <param name="commandManager">Manages console commands.</param>
public CommandHelper(string modID, string modName, CommandManager commandManager)
: base(modID)
public CommandHelper(IModMetadata mod, CommandManager commandManager)
: base(mod?.Manifest?.UniqueID ?? "SMAPI")
{
this.ModName = modName;
this.Mod = mod;
this.CommandManager = commandManager;
}
@ -38,7 +37,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <exception cref="ArgumentException">There's already a command with that name.</exception>
public ICommandHelper Add(string name, string documentation, Action<string, string[]> callback)
{
this.CommandManager.Add(this.ModName, name, documentation, callback);
this.CommandManager.Add(this.Mod, name, documentation, callback);
return this;
}

View File

@ -427,8 +427,8 @@ namespace StardewModdingAPI.Framework
{
// prepare console
this.Monitor.Log("Type 'help' for help, or 'help <cmd>' for a command's usage", LogLevel.Info);
this.GameInstance.CommandManager.Add("SMAPI", "help", "Lists command documentation.\n\nUsage: help\nLists all available commands.\n\nUsage: help <cmd>\n- cmd: The name of a command whose documentation to display.", this.HandleCommand);
this.GameInstance.CommandManager.Add("SMAPI", "reload_i18n", "Reloads translation files for all mods.\n\nUsage: reload_i18n", this.HandleCommand);
this.GameInstance.CommandManager.Add(null, "help", "Lists command documentation.\n\nUsage: help\nLists all available commands.\n\nUsage: help <cmd>\n- cmd: The name of a command whose documentation to display.", this.HandleCommand);
this.GameInstance.CommandManager.Add(null, "reload_i18n", "Reloads translation files for all mods.\n\nUsage: reload_i18n", this.HandleCommand);
// start handling command line input
Thread inputThread = new Thread(() =>
@ -973,7 +973,7 @@ namespace StardewModdingAPI.Framework
IModHelper modHelper;
{
IModEvents events = new ModEvents(mod, this.EventManager);
ICommandHelper commandHelper = new CommandHelper(manifest.UniqueID, mod.DisplayName, this.GameInstance.CommandManager);
ICommandHelper commandHelper = new CommandHelper(mod, this.GameInstance.CommandManager);
IContentHelper contentHelper = new ContentHelper(contentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, monitor);
IDataHelper dataHelper = new DataHelper(manifest.UniqueID, mod.DirectoryPath, jsonHelper);
IReflectionHelper reflectionHelper = new ReflectionHelper(manifest.UniqueID, mod.DisplayName, this.Reflection, this.DeprecationManager);
@ -1216,15 +1216,15 @@ namespace StardewModdingAPI.Framework
if (result == null)
this.Monitor.Log("There's no command with that name.", LogLevel.Error);
else
this.Monitor.Log($"{result.Name}: {result.Documentation}\n(Added by {result.ModName}.)", LogLevel.Info);
this.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<string, string>[] groups = (from command in this.GameInstance.CommandManager.GetAll() orderby command.ModName, command.Name group command.Name by command.ModName).ToArray();
IGrouping<string, string>[] groups = (from command in this.GameInstance.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;
string modName = group.Key ?? "SMAPI";
string[] commandNames = group.ToArray();
message += $"{modName}:\n {string.Join("\n ", commandNames)}\n\n";
}

View File

@ -268,14 +268,32 @@ namespace StardewModdingAPI.Framework
*********/
while (this.CommandQueue.TryDequeue(out string rawInput))
{
// parse command
string name;
string[] args;
Command command;
try
{
if (!this.CommandManager.Trigger(rawInput))
if (!this.CommandManager.TryParse(rawInput, out name, out args, out command))
this.Monitor.Log("Unknown command; type 'help' for a list of available commands.", LogLevel.Error);
}
catch (Exception ex)
{
this.Monitor.Log($"The handler registered for that command failed:\n{ex.GetLogSummary()}", LogLevel.Error);
this.Monitor.Log($"Failed parsing that command:\n{ex.GetLogSummary()}", LogLevel.Error);
continue;
}
// execute command
try
{
command.Callback.Invoke(name, args);
}
catch (Exception ex)
{
if (command.Mod != null)
command.Mod.LogAsMod($"Mod failed handling that command:\n{ex.GetLogSummary()}", LogLevel.Error);
else
this.Monitor.Log($"Failed handling that command:\n{ex.GetLogSummary()}", LogLevel.Error);
}
}