SMAPI - Stardew Modding API

SMAPI - Stardew Modding API

971k Downloads

Create interfaces to manage console commands

Pathoschild opened this issue · 6 comments

commented

Add an ICommand interface and encapsulate command management into the mod helper, so SMAPI can track which mods are managing them (e.g. for error-handling).

commented

Current API

// create command instance
Command command = new Command("list_items", "Lists items in the game data | list_items [search]", new[] { "(String)<search>" });

// add command
Command.RegisterCommand(command.Name, command.Description, command.CommandArgs).CommandFired += this.HandleListItems;

// trigger command programmatically
Command.CallCommand($"{command.Name} arguments here", this.Monitor);

Proposed API

// create command instance
ICommand command = new Command("list_items", "Lists items in the game data | list_items [search] | search: optional string containing search text");

// add command
helper.Commands.Add(command, this.HandleListItems);

// trigger command programmatically
helper.Commands.Invoke($"{command.Name} arguments here");
commented

Based on the proposed example it looks as if helper contains a list of commands that you add directly to, but also another Command member which is used to invoke commands? Would it not make more sense for the helper's Command member to encapsulate all command functionality including maintaining the commands themselves? It would also allow you to expose a similar Register method which can ensure uniqueness in the commands as a regular list would not.

commented

In that case should helper.Command.Invoke($"{command.Name} arguments here"); be helper.Commands.Invoke($"{command.Name} arguments here");?

commented

@tstaples you'd access and manage commands entirely through helper.Commands; the ICommand instance is just a model representing the command details.

commented

@tstaples yep, fixed.

commented

Done in the upcoming 1.9 release (with backward compatibility).

The implemented API is simpler that what we discussed here — I eliminated the Command object and moved away from multiple handlers (which is not the intended usage). Mods have only two available methods accessible through helper.ConsoleCommands:

/// <summary>Add a console command.</summary>
/// <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>
/// <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>
ICommandHelper Add(string name, string documentation, Action<string, string[]> callback);

/// <summary>Trigger a command.</summary>
/// <param name="name">The command name.</param>
/// <param name="arguments">The command arguments.</param>
/// <returns>Returns whether a matching command was triggered.</returns>
bool Trigger(string name, string[] arguments);

For example, here's how TrainerMod registers its commands:

helper.ConsoleCommands
    .Add("player_setname", "Sets the player's name.\n\nUsage: player_setname <target> <name>\n- target: what to rename (one of 'player' or 'farm').\n- name: the new name to set.", this.HandleCommand)
    .Add("player_setmoney", "Sets the player's money.\n\nUsage: player_setmoney <value>\n- value: an integer amount, or 'inf' for infinite money.", this.HandleCommand)
    .Add("player_setstamina", "Sets the player's stamina.\n\nUsage: player_setstamina <value>\n- value: an integer amount, or 'inf' for infinite stamina.", this.HandleCommand)
    .Add("player_setmaxstamina", "Sets the player's max stamina.\n\nUsage: player_setmaxstamina <value>\n- value: an integer amount.", this.HandleCommand)
    .Add("player_sethealth", "Sets the player's health.\n\nUsage: player_sethealth <value>\n- value: an integer amount, or 'inf' for infinite health.", this.HandleCommand)
    .Add("player_setmaxhealth", "Sets the player's max health.\n\nUsage: player_setmaxhealth <value>\n- value: an integer amount.", this.HandleCommand)
    .Add("player_setimmunity", "Sets the player's immunity.\n\nUsage: player_setimmunity <value>\n- value: an integer amount.", this.HandleCommand);