diff options
Diffstat (limited to 'src/TrainerMod')
34 files changed, 290 insertions, 282 deletions
diff --git a/src/TrainerMod/Framework/Commands/ArgumentParser.cs b/src/TrainerMod/Framework/Commands/ArgumentParser.cs new file mode 100644 index 00000000..bce068f1 --- /dev/null +++ b/src/TrainerMod/Framework/Commands/ArgumentParser.cs @@ -0,0 +1,158 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using StardewModdingAPI; + +namespace TrainerMod.Framework.Commands +{ + /// <summary>Provides methods for parsing command-line arguments.</summary> + internal class ArgumentParser : IReadOnlyList<string> + { + /********* + ** Properties + *********/ + /// <summary>The command name for errors.</summary> + private readonly string CommandName; + + /// <summary>The arguments to parse.</summary> + private readonly string[] Args; + + /// <summary>Writes messages to the console and log file.</summary> + private readonly IMonitor Monitor; + + + /********* + ** Accessors + *********/ + /// <summary>Get the number of arguments.</summary> + public int Count => this.Args.Length; + + /// <summary>Get the argument at the specified index in the list.</summary> + /// <param name="index">The zero-based index of the element to get.</param> + public string this[int index] => this.Args[index]; + + /// <summary>A method which parses a string argument into the given value.</summary> + /// <typeparam name="T">The expected argument type.</typeparam> + /// <param name="input">The argument to parse.</param> + /// <param name="output">The parsed value.</param> + /// <returns>Returns whether the argument was successfully parsed.</returns> + public delegate bool ParseDelegate<T>(string input, out T output); + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="commandName">The command name for errors.</param> + /// <param name="args">The arguments to parse.</param> + /// <param name="monitor">Writes messages to the console and log file.</param> + public ArgumentParser(string commandName, string[] args, IMonitor monitor) + { + this.CommandName = commandName; + this.Args = args; + this.Monitor = monitor; + } + + /// <summary>Try to read a string argument.</summary> + /// <param name="index">The argument index.</param> + /// <param name="name">The argument name for error messages.</param> + /// <param name="value">The parsed value.</param> + /// <param name="required">Whether to show an error if the argument is missing.</param> + /// <param name="oneOf">Require that the argument match one of the given values.</param> + public bool TryGet(int index, string name, out string value, bool required = true, string[] oneOf = null) + { + value = null; + + // validate + if (this.Args.Length < index + 1) + { + if (required) + this.LogError($"Argument {index} ({name}) is required."); + return false; + } + if (oneOf?.Any() == true && !oneOf.Contains(this.Args[index])) + { + this.LogError($"Argument {index} ({name}) must be one of {string.Join(", ", oneOf)}."); + return false; + } + + // get value + value = this.Args[index]; + return true; + } + + /// <summary>Try to read an integer argument.</summary> + /// <param name="index">The argument index.</param> + /// <param name="name">The argument name for error messages.</param> + /// <param name="value">The parsed value.</param> + /// <param name="required">Whether to show an error if the argument is missing.</param> + /// <param name="min">The minimum value allowed.</param> + /// <param name="max">The maximum value allowed.</param> + public bool TryGetInt(int index, string name, out int value, bool required = true, int? min = null, int? max = null) + { + value = 0; + + // get argument + if (!this.TryGet(index, name, out string raw, required)) + return false; + + // parse + if (!int.TryParse(raw, out value)) + { + this.LogIntFormatError(index, name, min, max); + return false; + } + + // validate + if ((min.HasValue && value < min) || (max.HasValue && value > max)) + { + this.LogIntFormatError(index, name, min, max); + return false; + } + + return true; + } + + /// <summary>Returns an enumerator that iterates through the collection.</summary> + /// <returns>An enumerator that can be used to iterate through the collection.</returns> + public IEnumerator<string> GetEnumerator() + { + return ((IEnumerable<string>)this.Args).GetEnumerator(); + } + + /// <summary>Returns an enumerator that iterates through a collection.</summary> + /// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns> + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + + /********* + ** Private methods + *********/ + /// <summary>Log a usage error.</summary> + /// <param name="message">The message describing the error.</param> + private void LogError(string message) + { + this.Monitor.Log($"{message} Type 'help {this.CommandName}' for usage.", LogLevel.Error); + } + + /// <summary>Print an error for an invalid int argument.</summary> + /// <param name="index">The argument index.</param> + /// <param name="name">The argument name for error messages.</param> + /// <param name="min">The minimum value allowed.</param> + /// <param name="max">The maximum value allowed.</param> + private void LogIntFormatError(int index, string name, int? min, int? max) + { + if (min.HasValue && max.HasValue) + this.LogError($"Argument {index} ({name}) must be an integer between {min} and {max}."); + else if (min.HasValue) + this.LogError($"Argument {index} ({name}) must be an integer and at least {min}."); + else if (max.HasValue) + this.LogError($"Argument {index} ({name}) must be an integer and at most {max}."); + else + this.LogError($"Argument {index} ({name}) must be an integer."); + } + } +} diff --git a/src/TrainerMod/Framework/Commands/ITrainerCommand.cs b/src/TrainerMod/Framework/Commands/ITrainerCommand.cs index 55f36ceb..3d97e799 100644 --- a/src/TrainerMod/Framework/Commands/ITrainerCommand.cs +++ b/src/TrainerMod/Framework/Commands/ITrainerCommand.cs @@ -25,7 +25,7 @@ namespace TrainerMod.Framework.Commands /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - void Handle(IMonitor monitor, string command, string[] args); + void Handle(IMonitor monitor, string command, ArgumentParser args); /// <summary>Perform any logic needed on update tick.</summary> /// <param name="monitor">Writes messages to the console and log file.</param> diff --git a/src/TrainerMod/Framework/Commands/Other/DebugCommand.cs b/src/TrainerMod/Framework/Commands/Other/DebugCommand.cs index ad38d1ba..8c6e9f3b 100644 --- a/src/TrainerMod/Framework/Commands/Other/DebugCommand.cs +++ b/src/TrainerMod/Framework/Commands/Other/DebugCommand.cs @@ -17,7 +17,7 @@ namespace TrainerMod.Framework.Commands.Other /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { // submit command string debugCommand = string.Join(" ", args); diff --git a/src/TrainerMod/Framework/Commands/Other/ShowDataFilesCommand.cs b/src/TrainerMod/Framework/Commands/Other/ShowDataFilesCommand.cs index b2985bb1..367a70c6 100644 --- a/src/TrainerMod/Framework/Commands/Other/ShowDataFilesCommand.cs +++ b/src/TrainerMod/Framework/Commands/Other/ShowDataFilesCommand.cs @@ -17,7 +17,7 @@ namespace TrainerMod.Framework.Commands.Other /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { Process.Start(Constants.DataPath); monitor.Log($"OK, opening {Constants.DataPath}.", LogLevel.Info); diff --git a/src/TrainerMod/Framework/Commands/Other/ShowGameFilesCommand.cs b/src/TrainerMod/Framework/Commands/Other/ShowGameFilesCommand.cs index 5695ce9a..67fa83a3 100644 --- a/src/TrainerMod/Framework/Commands/Other/ShowGameFilesCommand.cs +++ b/src/TrainerMod/Framework/Commands/Other/ShowGameFilesCommand.cs @@ -17,7 +17,7 @@ namespace TrainerMod.Framework.Commands.Other /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { Process.Start(Constants.ExecutionPath); monitor.Log($"OK, opening {Constants.ExecutionPath}.", LogLevel.Info); diff --git a/src/TrainerMod/Framework/Commands/Player/AddFlooringCommand.cs b/src/TrainerMod/Framework/Commands/Player/AddFlooringCommand.cs index 57bd39e3..1bc96466 100644 --- a/src/TrainerMod/Framework/Commands/Player/AddFlooringCommand.cs +++ b/src/TrainerMod/Framework/Commands/Player/AddFlooringCommand.cs @@ -1,5 +1,4 @@ -using System.Linq; -using StardewModdingAPI; +using StardewModdingAPI; using StardewValley; using StardewValley.Objects; @@ -19,24 +18,11 @@ namespace TrainerMod.Framework.Commands.Player /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { - // validate - if (!args.Any()) - { - this.LogArgumentsInvalid(monitor, command); + // read arguments + if (!args.TryGetInt(0, "floor ID", out int floorID, min: 0, max: 39)) return; - } - if (!int.TryParse(args[0], out int floorID)) - { - this.LogArgumentNotInt(monitor, command); - return; - } - if (floorID < 0 || floorID > 39) - { - monitor.Log("There is no such flooring ID (must be between 0 and 39).", LogLevel.Error); - return; - } // handle Wallpaper wallpaper = new Wallpaper(floorID, isFloor: true); diff --git a/src/TrainerMod/Framework/Commands/Player/AddItemCommand.cs b/src/TrainerMod/Framework/Commands/Player/AddItemCommand.cs index 6d3cf968..190d040a 100644 --- a/src/TrainerMod/Framework/Commands/Player/AddItemCommand.cs +++ b/src/TrainerMod/Framework/Commands/Player/AddItemCommand.cs @@ -1,5 +1,4 @@ -using System.Linq; -using StardewModdingAPI; +using StardewModdingAPI; using StardewValley; namespace TrainerMod.Framework.Commands.Player @@ -18,39 +17,15 @@ namespace TrainerMod.Framework.Commands.Player /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { - // validate - if (!args.Any()) - { - this.LogArgumentsInvalid(monitor, command); + // read arguments + if (!args.TryGetInt(0, "item ID", out int itemID, min: 0)) return; - } - if (!int.TryParse(args[0], out int itemID)) - { - this.LogUsageError(monitor, "The item ID must be an integer.", command); - return; - } - - // parse arguments - int count = 1; - int quality = 0; - if (args.Length > 1) - { - if (!int.TryParse(args[1], out count)) - { - this.LogUsageError(monitor, "The optional count is invalid.", command); - return; - } - } - if (args.Length > 2) - { - if (!int.TryParse(args[2], out quality)) - { - this.LogUsageError(monitor, "The optional quality is invalid.", command); - return; - } - } + if (!args.TryGetInt(1, "count", out int count, min: 1, required: false)) + count = 1; + if (!args.TryGetInt(2, "quality", out int quality, min: Object.lowQuality, max: Object.bestQuality, required: false)) + quality = Object.lowQuality; // spawn item var item = new Object(itemID, count) { quality = quality }; diff --git a/src/TrainerMod/Framework/Commands/Player/AddRingCommand.cs b/src/TrainerMod/Framework/Commands/Player/AddRingCommand.cs index d62d8b5b..93c5b2a5 100644 --- a/src/TrainerMod/Framework/Commands/Player/AddRingCommand.cs +++ b/src/TrainerMod/Framework/Commands/Player/AddRingCommand.cs @@ -1,5 +1,4 @@ -using System.Linq; -using StardewModdingAPI; +using StardewModdingAPI; using StardewValley; using StardewValley.Objects; @@ -19,24 +18,11 @@ namespace TrainerMod.Framework.Commands.Player /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { - // validate - if (!args.Any()) - { - this.LogArgumentsInvalid(monitor, command); + // parse arguments + if (!args.TryGetInt(0, "ring ID", out int ringID, min: Ring.ringLowerIndexRange, max: Ring.ringUpperIndexRange)) return; - } - if (!int.TryParse(args[0], out int ringID)) - { - monitor.Log("<item> is invalid", LogLevel.Error); - return; - } - if (ringID < Ring.ringLowerIndexRange || ringID > Ring.ringUpperIndexRange) - { - monitor.Log($"There is no such ring ID (must be between {Ring.ringLowerIndexRange} and {Ring.ringUpperIndexRange}).", LogLevel.Error); - return; - } // handle Ring ring = new Ring(ringID); diff --git a/src/TrainerMod/Framework/Commands/Player/AddWallpaperCommand.cs b/src/TrainerMod/Framework/Commands/Player/AddWallpaperCommand.cs index e02b05a4..dddb9ffd 100644 --- a/src/TrainerMod/Framework/Commands/Player/AddWallpaperCommand.cs +++ b/src/TrainerMod/Framework/Commands/Player/AddWallpaperCommand.cs @@ -1,5 +1,4 @@ -using System.Linq; -using StardewModdingAPI; +using StardewModdingAPI; using StardewValley; using StardewValley.Objects; @@ -19,24 +18,11 @@ namespace TrainerMod.Framework.Commands.Player /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { - // validate - if (!args.Any()) - { - this.LogArgumentsInvalid(monitor, command); + // parse arguments + if (!args.TryGetInt(0, "wallpaper ID", out int wallpaperID, min: 0, max: 111)) return; - } - if (!int.TryParse(args[0], out int wallpaperID)) - { - this.LogArgumentNotInt(monitor, command); - return; - } - if (wallpaperID < 0 || wallpaperID > 111) - { - monitor.Log("There is no such wallpaper ID (must be between 0 and 111).", LogLevel.Error); - return; - } // handle Wallpaper wallpaper = new Wallpaper(wallpaperID); diff --git a/src/TrainerMod/Framework/Commands/Player/AddWeaponCommand.cs b/src/TrainerMod/Framework/Commands/Player/AddWeaponCommand.cs index ee94093f..c4ea3d6f 100644 --- a/src/TrainerMod/Framework/Commands/Player/AddWeaponCommand.cs +++ b/src/TrainerMod/Framework/Commands/Player/AddWeaponCommand.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using StardewModdingAPI; using StardewValley; using StardewValley.Tools; @@ -20,19 +19,11 @@ namespace TrainerMod.Framework.Commands.Player /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { - // validate - if (!args.Any()) - { - this.LogArgumentsInvalid(monitor, command); + // parse arguments + if (!args.TryGetInt(0, "weapon ID", out int weaponID, min: 0)) return; - } - if (!int.TryParse(args[0], out int weaponID)) - { - this.LogUsageError(monitor, "The weapon ID must be an integer.", command); - return; - } // get raw weapon data if (!Game1.content.Load<Dictionary<int, string>>("Data\\weapons").TryGetValue(weaponID, out string data)) diff --git a/src/TrainerMod/Framework/Commands/Player/ListItemsCommand.cs b/src/TrainerMod/Framework/Commands/Player/ListItemsCommand.cs index a1b9aceb..68adf8c2 100644 --- a/src/TrainerMod/Framework/Commands/Player/ListItemsCommand.cs +++ b/src/TrainerMod/Framework/Commands/Player/ListItemsCommand.cs @@ -22,9 +22,9 @@ namespace TrainerMod.Framework.Commands.Player /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { - var matches = this.GetItems(args).ToArray(); + var matches = this.GetItems(args.ToArray()).ToArray(); // show matches string summary = "Searching...\n"; diff --git a/src/TrainerMod/Framework/Commands/Player/SetColorCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetColorCommand.cs index 00907fba..28ace0df 100644 --- a/src/TrainerMod/Framework/Commands/Player/SetColorCommand.cs +++ b/src/TrainerMod/Framework/Commands/Player/SetColorCommand.cs @@ -18,22 +18,23 @@ namespace TrainerMod.Framework.Commands.Player /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { - // validate - if (args.Length <= 2) - { - this.LogArgumentsInvalid(monitor, command); + // parse arguments + if (!args.TryGet(0, "target", out string target, oneOf: new[] { "hair", "eyes", "pants" })) return; - } - if (!this.TryParseColor(args[1], out Color color)) + if (!args.TryGet(1, "color", out string rawColor)) + return; + + // parse color + if (!this.TryParseColor(rawColor, out Color color)) { - this.LogUsageError(monitor, "The color should be an RBG value like '255,150,0'.", command); + this.LogUsageError(monitor, "Argument 1 (color) must be an RBG value like '255,150,0'."); return; } // handle - switch (args[0]) + switch (target) { case "hair": Game1.player.hairstyleColor = color; @@ -49,10 +50,6 @@ namespace TrainerMod.Framework.Commands.Player Game1.player.pantsColor = color; monitor.Log("OK, your pants color is updated.", LogLevel.Info); break; - - default: - this.LogArgumentsInvalid(monitor, command); - break; } } diff --git a/src/TrainerMod/Framework/Commands/Player/SetHealthCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetHealthCommand.cs index d3f06459..f64e9035 100644 --- a/src/TrainerMod/Framework/Commands/Player/SetHealthCommand.cs +++ b/src/TrainerMod/Framework/Commands/Player/SetHealthCommand.cs @@ -32,9 +32,9 @@ namespace TrainerMod.Framework.Commands.Player /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { - // validate + // no-argument mode if (!args.Any()) { monitor.Log($"You currently have {(this.InfiniteHealth ? "infinite" : Game1.player.health.ToString())} health. Specify a value to change it.", LogLevel.Info); @@ -57,7 +57,7 @@ namespace TrainerMod.Framework.Commands.Player monitor.Log($"OK, you now have {Game1.player.health} health.", LogLevel.Info); } else - this.LogArgumentNotInt(monitor, command); + this.LogArgumentNotInt(monitor); } } diff --git a/src/TrainerMod/Framework/Commands/Player/SetImmunityCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetImmunityCommand.cs index ff74f981..59b28a3c 100644 --- a/src/TrainerMod/Framework/Commands/Player/SetImmunityCommand.cs +++ b/src/TrainerMod/Framework/Commands/Player/SetImmunityCommand.cs @@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.Player /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="command">The command name.</param> /// <param name="args">The command arguments.</param> - public override void Handle(IMonitor monitor, string command, string[] args) + public override void Handle(IMonitor monitor, string command, ArgumentParser args) { // validate if (!args.Any()) @@ -28,13 +28,11 @@ namespace TrainerMod.Framework.Commands.Player } // handle - if (int.TryParse(args[0], out int amount)) + if (args.TryGetInt(0, "amount", out int amount, min: 0)) { Game1.player.immunity = amount; monitor.Log($"OK, you now have {Game1.player.immunity} immunity.", LogLevel.Info); } - else - this.LogArgumentNotInt(monitor, command); } } } diff --git a/src/TrainerMod/Framework/Commands/Player/SetLevelCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetLevelCommand.cs index 4982a0b8..b223aa9f 100644 --- a/src/TrainerMod/Framework/Commands/Player/SetLevelCommand.cs +++ b/src/TrainerMod/Framework/Commands/Player/SetLevelCommand.cs @@ -17,22 +17,16 @@ namespace TrainerMod.Framework.Commands.Player /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="c |
