diff --git a/docs/release-notes.md b/docs/release-notes.md index b7bd7b53..8dac1d0c 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -10,6 +10,9 @@ * Fixed update-check error if a mod's Chucklefish page has no version. * Fixed SMAPI beta versions not showing update alert on next launch (thanks to danvolchek!). +For the Console Commands mod: + * Added `test_input` command to view button codes in the console. + For modders: * Asset propagation for player sprites now affects other players' sprites, and updates recolor maps (e.g. sleeves). * Removed invalid-schedule validation which had false positives. @@ -31,13 +34,14 @@ Released 05 January 2019 for Stardew Valley 1.4 or later. * Fixed compatibility with Linux Mint 18 (thanks to techge!), Arch Linux, and Linux systems with libhybris-utils installed. * Fixed memory leak when repeatedly loading a save and returning to title. * Fixed memory leak when mods reload assets. - * Fixes for Console Commands mod: - * added new clothing items; - * fixed spawning new flooring and rings (thanks to Mizzion!); - * fixed spawning custom rings added by mods; - * Fixed errors when some item data is invalid. * Updated translations. Thanks to L30Bola (added Portuguese), PlussRolf (added Spanish), and shirutan (added Japanese)! +* For the Console Commands mod: + * Added new clothing items. + * Fixed spawning new flooring and rings (thanks to Mizzion!). + * Fixed spawning custom rings added by mods. + * Fixed errors when some item data is invalid. + * For the web UI: * Added option to edit & reupload in the JSON validator. * File uploads are now stored in Azure storage instead of Pastebin, due to ongoing Pastebin perfomance issues. diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ITrainerCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ITrainerCommand.cs index a0b739f8..d4d36e5d 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ITrainerCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ITrainerCommand.cs @@ -12,8 +12,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands /// The command description. string Description { get; } - /// Whether the command needs to perform logic when the game updates. - bool NeedsUpdate { get; } + /// Whether the command may need to perform logic when the game updates. This value shouldn't change. + bool MayNeedUpdate { get; } + + /// Whether the command may need to perform logic when the player presses a button. This value shouldn't change. + bool MayNeedInput { get; } /********* @@ -27,6 +30,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands /// Perform any logic needed on update tick. /// Writes messages to the console and log file. - void Update(IMonitor monitor); + void OnUpdated(IMonitor monitor); + + /// Perform any logic when input is received. + /// Writes messages to the console and log file. + /// The button that was pressed. + void OnButtonPressed(IMonitor monitor, SButton button); } } diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs new file mode 100644 index 00000000..11aa10c3 --- /dev/null +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs @@ -0,0 +1,59 @@ +using System; + +namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other +{ + /// A command which logs the keys being pressed for 30 seconds once enabled. + internal class TestInputCommand : TrainerCommand + { + /********* + ** Fields + *********/ + /// The number of seconds for which to log input. + private readonly int LogSeconds = 30; + + /// When the command should stop printing input, or null if currently disabled. + private long? ExpiryTicks; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + public TestInputCommand() + : base("test_input", "Prints all input to the console for 30 seconds.", mayNeedUpdate: true, mayNeedInput: true) { } + + /// Handle the command. + /// Writes messages to the console and log file. + /// The command name. + /// The command arguments. + public override void Handle(IMonitor monitor, string command, ArgumentParser args) + { + this.ExpiryTicks = DateTime.UtcNow.Add(TimeSpan.FromSeconds(this.LogSeconds)).Ticks; + monitor.Log($"OK, logging all player input for {this.LogSeconds} seconds.", LogLevel.Info); + } + + /// Perform any logic needed on update tick. + /// Writes messages to the console and log file. + public override void OnUpdated(IMonitor monitor) + { + // handle expiry + if (this.ExpiryTicks == null) + return; + if (this.ExpiryTicks <= DateTime.UtcNow.Ticks) + { + monitor.Log("No longer logging input.", LogLevel.Info); + this.ExpiryTicks = null; + return; + } + } + + /// Perform any logic when input is received. + /// Writes messages to the console and log file. + /// The button that was pressed. + public override void OnButtonPressed(IMonitor monitor, SButton button) + { + if (this.ExpiryTicks != null) + monitor.Log($"Pressed {button}", LogLevel.Info); + } + } +} diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs index 1abb82b5..59bda5dd 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player @@ -13,19 +13,12 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player private bool InfiniteHealth; - /********* - ** Accessors - *********/ - /// Whether the command needs to perform logic when the game updates. - public override bool NeedsUpdate => this.InfiniteHealth; - - /********* ** Public methods *********/ /// Construct an instance. public SetHealthCommand() - : base("player_sethealth", "Sets the player's health.\n\nUsage: player_sethealth [value]\n- value: an integer amount, or 'inf' for infinite health.") { } + : base("player_sethealth", "Sets the player's health.\n\nUsage: player_sethealth [value]\n- value: an integer amount, or 'inf' for infinite health.", mayNeedUpdate: true) { } /// Handle the command. /// Writes messages to the console and log file. @@ -62,9 +55,9 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player /// Perform any logic needed on update tick. /// Writes messages to the console and log file. - public override void Update(IMonitor monitor) + public override void OnUpdated(IMonitor monitor) { - if (this.InfiniteHealth) + if (this.InfiniteHealth && Context.IsWorldReady) Game1.player.health = Game1.player.maxHealth; } } diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs index 1706bbc1..6e3d68b6 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs @@ -13,19 +13,12 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player private bool InfiniteMoney; - /********* - ** Accessors - *********/ - /// Whether the command needs to perform logic when the game updates. - public override bool NeedsUpdate => this.InfiniteMoney; - - /********* ** Public methods *********/ /// Construct an instance. public SetMoneyCommand() - : base("player_setmoney", "Sets the player's money.\n\nUsage: player_setmoney \n- value: an integer amount, or 'inf' for infinite money.") { } + : base("player_setmoney", "Sets the player's money.\n\nUsage: player_setmoney \n- value: an integer amount, or 'inf' for infinite money.", mayNeedUpdate: true) { } /// Handle the command. /// Writes messages to the console and log file. @@ -62,9 +55,9 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player /// Perform any logic needed on update tick. /// Writes messages to the console and log file. - public override void Update(IMonitor monitor) + public override void OnUpdated(IMonitor monitor) { - if (this.InfiniteMoney) + if (this.InfiniteMoney && Context.IsWorldReady) Game1.player.Money = 999999; } } diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs index 009cb1de..60a1dcb1 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player @@ -13,19 +13,12 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player private bool InfiniteStamina; - /********* - ** Accessors - *********/ - /// Whether the command needs to perform logic when the game updates. - public override bool NeedsUpdate => this.InfiniteStamina; - - /********* ** Public methods *********/ /// Construct an instance. public SetStaminaCommand() - : base("player_setstamina", "Sets the player's stamina.\n\nUsage: player_setstamina [value]\n- value: an integer amount, or 'inf' for infinite stamina.") { } + : base("player_setstamina", "Sets the player's stamina.\n\nUsage: player_setstamina [value]\n- value: an integer amount, or 'inf' for infinite stamina.", mayNeedUpdate: true) { } /// Handle the command. /// Writes messages to the console and log file. @@ -62,9 +55,9 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player /// Perform any logic needed on update tick. /// Writes messages to the console and log file. - public override void Update(IMonitor monitor) + public override void OnUpdated(IMonitor monitor) { - if (this.InfiniteStamina) + if (this.InfiniteStamina && Context.IsWorldReady) Game1.player.stamina = Game1.player.MaxStamina; } } diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/TrainerCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/TrainerCommand.cs index 466b8f6e..6d5cae97 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/TrainerCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/TrainerCommand.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; @@ -16,8 +16,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands /// The command description. public string Description { get; } - /// Whether the command needs to perform logic when the game updates. - public virtual bool NeedsUpdate { get; } = false; + /// Whether the command may need to perform logic when the player presses a button. This value shouldn't change. + public bool MayNeedInput { get; } + + /// Whether the command may need to perform logic when the game updates. This value shouldn't change. + public bool MayNeedUpdate { get; } /********* @@ -31,7 +34,12 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands /// Perform any logic needed on update tick. /// Writes messages to the console and log file. - public virtual void Update(IMonitor monitor) { } + public virtual void OnUpdated(IMonitor monitor) { } + + /// Perform any logic when input is received. + /// Writes messages to the console and log file. + /// The button that was pressed. + public virtual void OnButtonPressed(IMonitor monitor, SButton button) { } /********* @@ -40,10 +48,14 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands /// Construct an instance. /// The command name the user must type. /// The command description. - protected TrainerCommand(string name, string description) + /// Whether the command may need to perform logic when the player presses a button. + /// Whether the command may need to perform logic when the game updates. + protected TrainerCommand(string name, string description, bool mayNeedInput = false, bool mayNeedUpdate = false) { this.Name = name; this.Description = description; + this.MayNeedInput = mayNeedInput; + this.MayNeedUpdate = mayNeedUpdate; } /// Log an error indicating incorrect usage. diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs index 6a7ab162..736a93a0 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World @@ -16,19 +16,12 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World private bool FreezeTime; - /********* - ** Accessors - *********/ - /// Whether the command needs to perform logic when the game updates. - public override bool NeedsUpdate => this.FreezeTime; - - /********* ** Public methods *********/ /// Construct an instance. public FreezeTimeCommand() - : base("world_freezetime", "Freezes or resumes time.\n\nUsage: world_freezetime [value]\n- value: one of 0 (resume), 1 (freeze), or blank (toggle).") { } + : base("world_freezetime", "Freezes or resumes time.\n\nUsage: world_freezetime [value]\n- value: one of 0 (resume), 1 (freeze), or blank (toggle).", mayNeedUpdate: true) { } /// Handle the command. /// Writes messages to the console and log file. @@ -57,9 +50,9 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World /// Perform any logic needed on update tick. /// Writes messages to the console and log file. - public override void Update(IMonitor monitor) + public override void OnUpdated(IMonitor monitor) { - if (this.FreezeTime) + if (this.FreezeTime && Context.IsWorldReady) Game1.timeOfDay = FreezeTimeCommand.FrozenTime; } } diff --git a/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs b/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs index 4807c46d..5c4f3bba 100644 --- a/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs +++ b/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using StardewModdingAPI.Events; using StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands; namespace StardewModdingAPI.Mods.ConsoleCommands @@ -14,6 +15,12 @@ namespace StardewModdingAPI.Mods.ConsoleCommands /// The commands to handle. private ITrainerCommand[] Commands; + /// The commands which may need to handle update ticks. + private ITrainerCommand[] UpdateHandlers; + + /// The commands which may need to handle input. + private ITrainerCommand[] InputHandlers; + /********* ** Public methods @@ -27,27 +34,35 @@ namespace StardewModdingAPI.Mods.ConsoleCommands foreach (ITrainerCommand command in this.Commands) helper.ConsoleCommands.Add(command.Name, command.Description, (name, args) => this.HandleCommand(command, name, args)); + // cache commands + this.InputHandlers = this.Commands.Where(p => p.MayNeedInput).ToArray(); + this.UpdateHandlers = this.Commands.Where(p => p.MayNeedUpdate).ToArray(); + // hook events helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked; + helper.Events.Input.ButtonPressed += this.OnButtonPressed; } /********* ** Private methods *********/ + /// The method invoked when a button is pressed. + /// The event sender. + /// The event arguments. + private void OnButtonPressed(object sender, ButtonPressedEventArgs e) + { + foreach (ITrainerCommand command in this.InputHandlers) + command.OnButtonPressed(this.Monitor, e.Button); + } + /// The method invoked when the game updates its state. /// The event sender. /// The event arguments. private void OnUpdateTicked(object sender, EventArgs e) { - if (!Context.IsWorldReady) - return; - - foreach (ITrainerCommand command in this.Commands) - { - if (command.NeedsUpdate) - command.Update(this.Monitor); - } + foreach (ITrainerCommand command in this.UpdateHandlers) + command.OnUpdated(this.Monitor); } /// Handle a console command.