From 84cf749344c8958dbc1a1e168835e99db5b8f13c Mon Sep 17 00:00:00 2001 From: BruceChen Date: Tue, 6 Dec 2022 15:50:17 +0800 Subject: [PATCH] Implement command completion suggestions. --- MinecraftClient/ChatBots/Alerts.cs | 1 + MinecraftClient/ChatBots/AntiAFK.cs | 5 +- MinecraftClient/ChatBots/AutoAttack.cs | 5 +- MinecraftClient/ChatBots/AutoCraft.cs | 151 +++-- MinecraftClient/ChatBots/AutoDig.cs | 112 ++-- MinecraftClient/ChatBots/AutoDrop.cs | 222 +++--- MinecraftClient/ChatBots/AutoEat.cs | 1 + MinecraftClient/ChatBots/AutoFishing.cs | 192 +++--- MinecraftClient/ChatBots/AutoRelog.cs | 12 +- MinecraftClient/ChatBots/AutoRespond.cs | 12 +- MinecraftClient/ChatBots/ChatLog.cs | 4 +- MinecraftClient/ChatBots/DiscordBridge.cs | 115 ++-- MinecraftClient/ChatBots/Farmer.cs | 308 +++++---- MinecraftClient/ChatBots/FollowPlayer.cs | 114 ++-- MinecraftClient/ChatBots/HangmanGame.cs | 1 + MinecraftClient/ChatBots/Mailer.cs | 179 +++-- MinecraftClient/ChatBots/Map.cs | 134 ++-- MinecraftClient/ChatBots/PlayerListLogger.cs | 1 + MinecraftClient/ChatBots/RemoteControl.cs | 14 +- MinecraftClient/ChatBots/ReplayCapture.cs | 109 ++- MinecraftClient/ChatBots/Script.cs | 5 +- MinecraftClient/ChatBots/ScriptScheduler.cs | 1 + MinecraftClient/ChatBots/TelegramBridge.cs | 114 ++-- MinecraftClient/ChatBots/TestBot.cs | 4 +- MinecraftClient/Command.cs | 44 +- .../ArgumentType/AccountNickArgumentType.cs | 25 + .../AutoCraftRecipeNameArgumentType.cs | 25 + .../ArgumentType/BotNameArgumentType.cs | 29 + .../ArgumentType/EntityTypeArgumentType.cs | 31 + .../FarmerCropTypeArgumentType.cs | 31 + .../InventoryActionArgumentType.cs | 41 ++ .../ArgumentType/InventoryIdArgumentType.cs | 34 + .../ArgumentType/ItemTypeArgumentType.cs | 31 + .../ArgumentType/LocationArgumentType.cs | 99 +++ .../ArgumentType/MapBotMapIdArgumentType.cs | 39 ++ .../ArgumentType/PlayerNameArgumentType.cs | 36 + .../ArgumentType/ServerNickArgumentType.cs | 25 + .../ArgumentType/TupleArgumentType.cs | 18 + MinecraftClient/CommandHandler/CmdResult.cs | 96 +++ .../CommandHandler/MccArguments.cs | 104 +++ .../Patch/CommandDispatcherExtensions.cs | 36 + .../Patch/CommandNodeExtensions.cs | 28 + MinecraftClient/Commands/Animation.cs | 95 +-- MinecraftClient/Commands/Bed.cs | 276 ++++---- MinecraftClient/Commands/BlockInfo.cs | 72 +- MinecraftClient/Commands/Bots.cs | 125 ++-- MinecraftClient/Commands/ChangeSlot.cs | 63 +- MinecraftClient/Commands/Chunk.cs | 482 ++++++------- MinecraftClient/Commands/CommandSource.cs | 13 - MinecraftClient/Commands/Connect.cs | 76 ++- MinecraftClient/Commands/Debug.cs | 47 +- MinecraftClient/Commands/Dig.cs | 95 +-- MinecraftClient/Commands/DropItem.cs | 81 ++- MinecraftClient/Commands/Enchant.cs | 149 +++-- MinecraftClient/Commands/Entitycmd.cs | 413 ++++++++---- MinecraftClient/Commands/ExecIf.cs | 131 ++-- MinecraftClient/Commands/ExecMulti.cs | 59 +- MinecraftClient/Commands/Exit.cs | 50 +- MinecraftClient/Commands/Health.cs | 30 +- MinecraftClient/Commands/Help.cs | 33 + MinecraftClient/Commands/Inventory.cs | 632 ++++++++++-------- MinecraftClient/Commands/List.cs | 30 +- MinecraftClient/Commands/Log.cs | 36 +- MinecraftClient/Commands/Look.cs | 153 +++-- MinecraftClient/Commands/Move.cs | 255 ++++--- MinecraftClient/Commands/Reco.cs | 50 +- MinecraftClient/Commands/Reload.cs | 30 +- MinecraftClient/Commands/Respawn.cs | 32 +- MinecraftClient/Commands/Script.cs | 34 +- MinecraftClient/Commands/Send.cs | 35 +- MinecraftClient/Commands/Set.cs | 50 +- MinecraftClient/Commands/SetRnd.cs | 98 ++- MinecraftClient/Commands/Sneak.cs | 44 +- MinecraftClient/Commands/Tps.cs | 32 +- MinecraftClient/Commands/UseItem.cs | 40 +- MinecraftClient/Commands/Useblock.cs | 53 +- MinecraftClient/ConsoleIO.cs | 158 ++++- .../Inventory/EnchantmentMapping.cs | 3 +- MinecraftClient/Inventory/Item.cs | 13 +- MinecraftClient/Inventory/ItemMovingHelper.cs | 1 + MinecraftClient/Inventory/VillagerInfo.cs | 2 +- MinecraftClient/Json.cs | 2 +- MinecraftClient/Logger/FileLogLogger.cs | 1 + MinecraftClient/Mapping/Block.cs | 3 +- MinecraftClient/Mapping/Dimension.cs | 6 +- MinecraftClient/Mapping/Entity.cs | 3 +- MinecraftClient/Mapping/Location.cs | 35 + MinecraftClient/Mapping/RaycastHelper.cs | 4 +- MinecraftClient/Mapping/World.cs | 4 +- MinecraftClient/McClient.cs | 220 +++--- MinecraftClient/MinecraftClient.csproj | 13 +- MinecraftClient/Program.cs | 20 +- .../Handlers/Packet/s2c/DeclareCommands.cs | 19 +- .../Protocol/Handlers/Protocol16.cs | 32 +- .../Protocol/Handlers/Protocol18.cs | 50 +- .../Protocol/Handlers/Protocol18Forge.cs | 2 + MinecraftClient/Protocol/IMinecraftCom.cs | 2 +- .../Protocol/IMinecraftComHandler.cs | 8 + .../Protocol/Message/ChatParser.cs | 17 +- MinecraftClient/Protocol/MojangAPI.cs | 2 +- MinecraftClient/Protocol/PlayerInfo.cs | 2 +- .../Protocol/ProfileKey/KeyUtils.cs | 18 +- .../Protocol/ProfileKey/KeysCache.cs | 14 +- .../Protocol/ProfileKey/PlayerKeyPair.cs | 8 +- .../Protocol/ProfileKey/PrivateKey.cs | 2 +- .../Protocol/ProfileKey/PublicKey.cs | 2 +- .../Protocol/Session/SessionToken.cs | 1 + .../Translations/Translations.Designer.cs | 17 +- .../Resources/Translations/Translations.resx | 17 +- MinecraftClient/Scripting/AssemblyResolver.cs | 40 +- MinecraftClient/Scripting/CSharpRunner.cs | 30 +- MinecraftClient/Scripting/ChatBot.cs | 105 ++- .../DynamicRun/Builder/CompileRunner.cs | 3 +- .../Scripting/DynamicRun/Builder/Compiler.cs | 16 +- .../SimpleUnloadableAssemblyLoadContext.cs | 2 +- 115 files changed, 4684 insertions(+), 2695 deletions(-) create mode 100644 MinecraftClient/CommandHandler/ArgumentType/AccountNickArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/AutoCraftRecipeNameArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/FarmerCropTypeArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/ServerNickArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/TupleArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/CmdResult.cs create mode 100644 MinecraftClient/CommandHandler/MccArguments.cs create mode 100644 MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs create mode 100644 MinecraftClient/CommandHandler/Patch/CommandNodeExtensions.cs delete mode 100644 MinecraftClient/Commands/CommandSource.cs create mode 100644 MinecraftClient/Commands/Help.cs diff --git a/MinecraftClient/ChatBots/Alerts.cs b/MinecraftClient/ChatBots/Alerts.cs index fabdc3b4..e503d52f 100644 --- a/MinecraftClient/ChatBots/Alerts.cs +++ b/MinecraftClient/ChatBots/Alerts.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots diff --git a/MinecraftClient/ChatBots/AntiAFK.cs b/MinecraftClient/ChatBots/AntiAFK.cs index aac824e4..bbf73637 100644 --- a/MinecraftClient/ChatBots/AntiAFK.cs +++ b/MinecraftClient/ChatBots/AntiAFK.cs @@ -1,5 +1,8 @@ using System; +using Brigadier.NET; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -90,7 +93,7 @@ namespace MinecraftClient.ChatBots count = 0; } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (Config.Use_Terrain_Handling) { diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index e6fd19af..f7bed4ed 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using Brigadier.NET; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -109,7 +112,7 @@ namespace MinecraftClient.ChatBots attackPassive = Config.Attack_Passive; } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetEntityHandlingEnabled()) { diff --git a/MinecraftClient/ChatBots/AutoCraft.cs b/MinecraftClient/ChatBots/AutoCraft.cs index 13478736..9c700160 100644 --- a/MinecraftClient/ChatBots/AutoCraft.cs +++ b/MinecraftClient/ChatBots/AutoCraft.cs @@ -2,8 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; using static MinecraftClient.ChatBots.AutoCraft.Configs; @@ -11,6 +16,8 @@ namespace MinecraftClient.ChatBots { public class AutoCraft : ChatBot { + public const string CommandName = "autocraft"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -40,7 +47,7 @@ namespace MinecraftClient.ChatBots Name: "Recipe-Name-2", Type: CraftTypeConfig.table, Result: ItemType.StoneBricks, - Slots: new ItemType[9] { + Slots: new ItemType[9] { ItemType.Stone, ItemType.Stone, ItemType.Null, ItemType.Stone, ItemType.Stone, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, @@ -117,7 +124,7 @@ namespace MinecraftClient.ChatBots public ItemType Result = ItemType.Air; - public ItemType[] Slots = new ItemType[9] { + public ItemType[] Slots = new ItemType[9] { ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, @@ -279,7 +286,7 @@ namespace MinecraftClient.ChatBots } } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetInventoryEnabled()) { @@ -288,81 +295,93 @@ namespace MinecraftClient.ChatBots UnloadBot(); return; } - RegisterChatBotCommand("autocraft", Translations.bot_autoCraft_cmd, GetHelp(), CommandHandler); - RegisterChatBotCommand("ac", Translations.bot_autoCraft_alias, GetHelp(), CommandHandler); + + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + .Then(l => l.Literal("list") + .Executes(r => OnCommandHelp(r.Source, "list"))) + .Then(l => l.Literal("start") + .Executes(r => OnCommandHelp(r.Source, "start"))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandHelp(r.Source, "stop"))) + .Then(l => l.Literal("help") + .Executes(r => OnCommandHelp(r.Source, "help"))) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("list") + .Executes(r => OnCommandList(r.Source))) + .Then(l => l.Literal("start") + .Then(l => l.Argument("RecipeName", MccArguments.AutoCraftRecipeName()) + .Executes(r => OnCommandStart(r.Source, Arguments.GetString(r, "RecipeName"))))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); } - public string CommandHandler(string cmd, string[] args) + public override void OnUnload(CommandDispatcher dispatcher) { - if (args.Length > 0) - { - switch (args[0]) - { - case "list": - StringBuilder nameList = new(); - foreach (RecipeConfig recipe in Config.Recipes) - nameList.Append(recipe.Name).Append(", "); - return string.Format(Translations.bot_autoCraft_cmd_list, Config.Recipes.Length, nameList.ToString()); - case "start": - if (args.Length >= 2) - { - string name = args[1]; - - bool hasRecipe = false; - RecipeConfig? recipe = null; - foreach (RecipeConfig recipeConfig in Config.Recipes) - { - if (recipeConfig.Name == name) - { - hasRecipe = true; - recipe = recipeConfig; - break; - } - } - - if (hasRecipe) - { - ResetVar(); - PrepareCrafting(recipe!); - return ""; - } - else - return Translations.bot_autoCraft_recipe_not_exist; - } - else - return Translations.bot_autoCraft_no_recipe_name; - case "stop": - StopCrafting(); - return Translations.bot_autoCraft_stop; - case "help": - return GetCommandHelp(args.Length >= 2 ? args[1] : ""); - default: - return GetHelp(); - } - } - else return GetHelp(); + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } - private static string GetHelp() + private int OnCommandHelp(CmdResult r, string? cmd) { - return string.Format(Translations.bot_autoCraft_available_cmd, "load, list, reload, resetcfg, start, stop, help"); - } - - private string GetCommandHelp(string cmd) - { - return cmd.ToLower() switch + return r.SetAndReturn(cmd switch { #pragma warning disable format // @formatter:off - "load" => Translations.bot_autoCraft_help_load, "list" => Translations.bot_autoCraft_help_list, - "reload" => Translations.bot_autoCraft_help_reload, - "resetcfg" => Translations.bot_autoCraft_help_resetcfg, "start" => Translations.bot_autoCraft_help_start, "stop" => Translations.bot_autoCraft_help_stop, "help" => Translations.bot_autoCraft_help_help, - _ => GetHelp(), + _ => string.Format(Translations.bot_autoCraft_available_cmd, "load, list, reload, resetcfg, start, stop, help") + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on - }; + }); + } + + private int OnCommandList(CmdResult r) + { + StringBuilder nameList = new(); + foreach (RecipeConfig recipe in Config.Recipes) + nameList.Append(recipe.Name).Append(", "); + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_autoCraft_cmd_list, Config.Recipes.Length, nameList.ToString())); + } + + private int OnCommandStart(CmdResult r, string name) + { + bool hasRecipe = false; + RecipeConfig? recipe = null; + foreach (RecipeConfig recipeConfig in Config.Recipes) + { + if (recipeConfig.Name == name) + { + hasRecipe = true; + recipe = recipeConfig; + break; + } + } + + if (hasRecipe) + { + ResetVar(); + PrepareCrafting(recipe!); + return r.SetAndReturn(CmdResult.Status.Done); + } + else + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_autoCraft_recipe_not_exist); + } + } + + private int OnCommandStop(CmdResult r) + { + StopCrafting(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoCraft_stop); } #region Core part of auto-crafting @@ -442,7 +461,7 @@ namespace MinecraftClient.ChatBots ItemType ResultItem = recipeConfig.Result; - ContainerType CraftingAreaType = + ContainerType CraftingAreaType = (recipeConfig.Type == CraftTypeConfig.player) ? ContainerType.PlayerInventory : ContainerType.Crafting; PrepareCrafting(new Recipe(materials, ResultItem, CraftingAreaType)); diff --git a/MinecraftClient/ChatBots/AutoDig.cs b/MinecraftClient/ChatBots/AutoDig.cs index 21f93803..740a9053 100644 --- a/MinecraftClient/ChatBots/AutoDig.cs +++ b/MinecraftClient/ChatBots/AutoDig.cs @@ -1,13 +1,20 @@ using System; using System.Collections.Generic; using System.Linq; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots { public class AutoDig : ChatBot { + public const string CommandName = "autodig"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -60,7 +67,7 @@ namespace MinecraftClient.ChatBots { if (Auto_Start_Delay >= 0) Auto_Start_Delay = Math.Max(0.1, Auto_Start_Delay); - + if (Dig_Timeout >= 0) Dig_Timeout = Math.Max(0.1, Dig_Timeout); @@ -103,7 +110,7 @@ namespace MinecraftClient.ChatBots Stopping, } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetTerrainEnabled()) { @@ -117,32 +124,67 @@ namespace MinecraftClient.ChatBots if (!inventoryEnabled && Config.Auto_Tool_Switch) LogToConsole(Translations.bot_autodig_no_inv_handle); - RegisterChatBotCommand("digbot", Translations.bot_autodig_cmd, GetHelp(), CommandHandler); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + .Then(l => l.Literal("start") + .Executes(r => OnCommandHelp(r.Source, "start"))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandHelp(r.Source, "stop"))) + .Then(l => l.Literal("help") + .Executes(r => OnCommandHelp(r.Source, "help"))) + ) + ); + + var cmd = dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("start") + .Executes(r => OnCommandStart(r.Source))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); + + dispatcher.Register(l => l.Literal("digbot") + .Redirect(cmd) + ); } - public string CommandHandler(string cmd, string[] args) + public override void OnUnload(CommandDispatcher dispatcher) { - if (args.Length > 0) + dispatcher.Unregister("digbot"); + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch { - switch (args[0]) - { - case "start": - lock (stateLock) - { - counter = 0; - state = State.WaitingStart; - } - return Translations.bot_autodig_start; - case "stop": - StopDigging(); - return Translations.bot_autodig_stop; - case "help": - return GetCommandHelp(args.Length >= 2 ? args[1] : ""); - default: - return GetHelp(); - } +#pragma warning disable format // @formatter:off + "start" => Translations.bot_autodig_help_start, + "stop" => Translations.bot_autodig_help_stop, + "help" => Translations.bot_autodig_help_help, + _ => string.Format(Translations.bot_autodig_available_cmd, "start, stop, help") + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandStart(CmdResult r) + { + lock (stateLock) + { + counter = 0; + state = State.WaitingStart; } - else return GetHelp(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autodig_start); + } + + private int OnCommandStop(CmdResult r) + { + StopDigging(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autodig_stop); } private void StartDigging() @@ -240,7 +282,7 @@ namespace MinecraftClient.ChatBots else if ((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) || (Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type))) { - if (Config.Mode == Configs.ModeType.lookat || + if (Config.Mode == Configs.ModeType.lookat || (Config.Mode == Configs.ModeType.both && Config._Locations.Contains(blockLoc))) { if (DigBlock(blockLoc, lookAtBlock: false)) @@ -288,8 +330,8 @@ namespace MinecraftClient.ChatBots foreach (Location location in Config._Locations) { Block block = GetWorld().GetBlock(location); - if (block.Type != Material.Air && - ((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) || + if (block.Type != Material.Air && + ((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) || (Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type)))) { double distance = current.Distance(location); @@ -401,23 +443,5 @@ namespace MinecraftClient.ChatBots return base.OnDisconnect(reason, message); } - - private static string GetHelp() - { - return string.Format(Translations.bot_autodig_available_cmd, "start, stop, help"); - } - - private string GetCommandHelp(string cmd) - { - return cmd.ToLower() switch - { -#pragma warning disable format // @formatter:off - "start" => Translations.bot_autodig_help_start, - "stop" => Translations.bot_autodig_help_stop, - "help" => Translations.bot_autodig_help_help, - _ => GetHelp(), -#pragma warning restore format // @formatter:on - }; - } } } diff --git a/MinecraftClient/ChatBots/AutoDrop.cs b/MinecraftClient/ChatBots/AutoDrop.cs index 1ad79917..5781af9e 100644 --- a/MinecraftClient/ChatBots/AutoDrop.cs +++ b/MinecraftClient/ChatBots/AutoDrop.cs @@ -1,7 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; +using MinecraftClient.Scripting; using Tomlet.Attributes; using static MinecraftClient.ChatBots.AutoDrop.Configs; @@ -9,6 +14,8 @@ namespace MinecraftClient.ChatBots { public class AutoDrop : ChatBot { + public const string CommandName = "autodrop"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -38,111 +45,7 @@ namespace MinecraftClient.ChatBots private readonly int updateDebounceValue = 2; private int inventoryUpdated = -1; - public string CommandHandler(string cmd, string[] args) - { - if (args.Length > 0) - { - switch (args[0].ToLower()) - { - case "on": - Config.Enabled = true; - inventoryUpdated = 0; - OnUpdateFinish(); - return Translations.bot_autoDrop_on; - case "off": - Config.Enabled = false; - return Translations.bot_autoDrop_off; - case "add": - if (args.Length >= 2) - { - if (Enum.TryParse(args[1], true, out ItemType item)) - { - Config.Items.Add(item); - return string.Format(Translations.bot_autoDrop_added, item.ToString()); - } - else - { - return string.Format(Translations.bot_autoDrop_incorrect_name, args[1]); - } - } - else - { - return Translations.cmd_inventory_help_usage + ": add "; - } - case "remove": - if (args.Length >= 2) - { - if (Enum.TryParse(args[1], true, out ItemType item)) - { - if (Config.Items.Contains(item)) - { - Config.Items.Remove(item); - return string.Format(Translations.bot_autoDrop_removed, item.ToString()); - } - else - { - return Translations.bot_autoDrop_not_in_list; - } - } - else - { - return string.Format(Translations.bot_autoDrop_incorrect_name, args[1]); - } - } - else - { - return Translations.cmd_inventory_help_usage + ": remove "; - } - case "list": - if (Config.Items.Count > 0) - { - return string.Format(Translations.bot_autoDrop_list, Config.Items.Count, string.Join("\n", Config.Items)); - } - else - { - return Translations.bot_autoDrop_no_item; - } - case "mode": - if (args.Length >= 2) - { - switch (args[1].ToLower()) - { - case "include": - Config.Mode = DropMode.include; - break; - case "exclude": - Config.Mode = DropMode.exclude; - break; - case "everything": - Config.Mode = DropMode.everything; - break; - default: - return Translations.bot_autoDrop_unknown_mode; // Unknwon mode. Available modes: Include, Exclude, Everything - } - inventoryUpdated = 0; - OnUpdateFinish(); - return string.Format(Translations.bot_autoDrop_switched, Config.Mode.ToString()); // Switched to {0} mode. - } - else - { - return Translations.bot_autoDrop_unknown_mode; - } - default: - return GetHelp(); - } - } - else - { - return GetHelp(); - } - } - - private static string GetHelp() - { - return string.Format(Translations.general_available_cmd, "on, off, add, remove, list, mode"); - } - - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetInventoryEnabled()) { @@ -151,8 +54,113 @@ namespace MinecraftClient.ChatBots UnloadBot(); return; } - RegisterChatBotCommand("autodrop", Translations.bot_autoDrop_cmd, GetHelp(), CommandHandler); - RegisterChatBotCommand("ad", Translations.bot_autoDrop_alias, GetHelp(), CommandHandler); + + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + .Then(l => l.Literal("add") + .Executes(r => OnCommandHelp(r.Source, "add"))) + .Then(l => l.Literal("remove") + .Executes(r => OnCommandHelp(r.Source, "remove"))) + .Then(l => l.Literal("mode") + .Executes(r => OnCommandHelp(r.Source, "mode"))) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("on") + .Executes(r => OnCommandEnable(r.Source, true))) + .Then(l => l.Literal("off") + .Executes(r => OnCommandEnable(r.Source, false))) + .Then(l => l.Literal("add") + .Then(l => l.Argument("ItemType", MccArguments.ItemType()) + .Executes(r => OnCommandAdd(r.Source, MccArguments.GetItemType(r, "ItemType"))))) + .Then(l => l.Literal("remove") + .Then(l => l.Argument("ItemType", MccArguments.ItemType()) + .Executes(r => OnCommandRemove(r.Source, MccArguments.GetItemType(r, "ItemType"))))) + .Then(l => l.Literal("list") + .Executes(r => OnCommandList(r.Source))) + .Then(l => l.Literal("mode") + .Then(l => l.Literal("include") + .Executes(r => OnCommandMode(r.Source, DropMode.include))) + .Then(l => l.Literal("exclude") + .Executes(r => OnCommandMode(r.Source, DropMode.exclude))) + .Then(l => l.Literal("everything") + .Executes(r => OnCommandMode(r.Source, DropMode.everything)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); + } + + public override void OnUnload(CommandDispatcher dispatcher) + { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + "add" => Translations.cmd_inventory_help_usage + ": add ", + "remove" => Translations.cmd_inventory_help_usage + ": remove ", + "mode" => Translations.bot_autoDrop_unknown_mode, + _ => string.Format(Translations.general_available_cmd, "on, off, add, remove, list, mode") + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandEnable(CmdResult r, bool enable) + { + if (enable) + { + Config.Enabled = true; + inventoryUpdated = 0; + OnUpdateFinish(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoDrop_on); + } + else + { + Config.Enabled = false; + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoDrop_off); + } + } + + private int OnCommandAdd(CmdResult r, ItemType item) + { + Config.Items.Add(item); + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_autoDrop_added, item.ToString())); + } + + private int OnCommandRemove(CmdResult r, ItemType item) + { + if (Config.Items.Contains(item)) + { + Config.Items.Remove(item); + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_autoDrop_removed, item.ToString())); + } + else + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_autoDrop_not_in_list); + } + } + + private int OnCommandList(CmdResult r) + { + if (Config.Items.Count > 0) + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_autoDrop_list, Config.Items.Count, string.Join("\n", Config.Items))); + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_autoDrop_no_item); + } + + private int OnCommandMode(CmdResult r, DropMode mode) + { + Config.Mode = mode; + inventoryUpdated = 0; + OnUpdateFinish(); + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_autoDrop_switched, Config.Mode.ToString())); } public override void Update() diff --git a/MinecraftClient/ChatBots/AutoEat.cs b/MinecraftClient/ChatBots/AutoEat.cs index eb9c3762..eb9eea07 100644 --- a/MinecraftClient/ChatBots/AutoEat.cs +++ b/MinecraftClient/ChatBots/AutoEat.cs @@ -1,5 +1,6 @@ using System; using MinecraftClient.Inventory; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs index 73b0af3f..963cc9ad 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -2,8 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; using static MinecraftClient.ChatBots.AutoFishing.Configs; @@ -15,6 +20,8 @@ namespace MinecraftClient.ChatBots /// public class AutoFishing : ChatBot { + public const string CommandName = "autofishing"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -169,87 +176,119 @@ namespace MinecraftClient.ChatBots Stopping, } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetEntityHandlingEnabled()) { LogToConsole(Translations.extra_entity_required); state = FishingState.WaitJoinGame; } + inventoryEnabled = GetInventoryEnabled(); if (!inventoryEnabled) LogToConsole(Translations.bot_autoFish_no_inv_handle); - RegisterChatBotCommand("fish", Translations.bot_autoFish_cmd, GetHelp(), CommandHandler); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + .Then(l => l.Literal("start") + .Executes(r => OnCommandHelp(r.Source, "start"))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandHelp(r.Source, "stop"))) + .Then(l => l.Literal("status") + .Executes(r => OnCommandHelp(r.Source, "status"))) + .Then(l => l.Literal("help") + .Executes(r => OnCommandHelp(r.Source, "help"))) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("start") + .Executes(r => OnCommandStart(r.Source))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("status") + .Executes(r => OnCommandStatus(r.Source)) + .Then(l => l.Literal("clear") + .Executes(r => OnCommandStatusClear(r.Source)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); } - public string CommandHandler(string cmd, string[] args) + public override void OnUnload(CommandDispatcher dispatcher) { - if (args.Length >= 1) + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch { - switch (args[0]) - { - case "start": - isFishing = false; - lock (stateLock) - { - isFishing = false; - counter = 0; - state = FishingState.StartMove; - } - return Translations.bot_autoFish_start; - case "stop": - isFishing = false; - lock (stateLock) - { - isFishing = false; - if (state == FishingState.WaitingFishToBite) - UseFishRod(); - state = FishingState.Stopping; - } - StopFishing(); - return Translations.bot_autoFish_stop; - case "status": - if (args.Length >= 2) - { - if (args[1] == "clear") - { - fishItemCnt = new(); - return Translations.bot_autoFish_status_clear; - } - else - { - return GetCommandHelp("status"); - } - } - else - { - if (fishItemCnt.Count == 0) - return Translations.bot_autoFish_status_info; +#pragma warning disable format // @formatter:off + "start" => Translations.bot_autoFish_help_start, + "stop" => Translations.bot_autoFish_help_stop, + "status" => Translations.bot_autoFish_help_status, + "help" => Translations.bot_autoFish_help_help, + _ => string.Format(Translations.bot_autoFish_available_cmd, "start, stop, status, help") + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } - List> orderedList = fishItemCnt.OrderBy(x => x.Value).ToList(); - int maxLen = orderedList[^1].Value.ToString().Length; - StringBuilder sb = new(); - sb.Append(Translations.bot_autoFish_status_info); - foreach ((ItemType type, uint cnt) in orderedList) - { - sb.Append(Environment.NewLine); - - string cntStr = cnt.ToString(); - sb.Append(' ', maxLen - cntStr.Length).Append(cntStr); - sb.Append(" x "); - sb.Append(Item.GetTypeString(type)); - } - return sb.ToString(); - } - case "help": - return GetCommandHelp(args.Length >= 2 ? args[1] : ""); - default: - return GetHelp(); - } + private int OnCommandStart(CmdResult r) + { + isFishing = false; + lock (stateLock) + { + isFishing = false; + counter = 0; + state = FishingState.StartMove; } - else - return GetHelp(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoFish_start); + } + + private int OnCommandStop(CmdResult r) + { + isFishing = false; + lock (stateLock) + { + isFishing = false; + if (state == FishingState.WaitingFishToBite) + UseFishRod(); + state = FishingState.Stopping; + } + StopFishing(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoFish_stop); + } + + private int OnCommandStatus(CmdResult r) + { + if (fishItemCnt.Count == 0) + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoFish_status_info); + + List> orderedList = fishItemCnt.OrderBy(x => x.Value).ToList(); + int maxLen = orderedList[^1].Value.ToString().Length; + StringBuilder sb = new(); + sb.Append(Translations.bot_autoFish_status_info); + foreach ((ItemType type, uint cnt) in orderedList) + { + sb.Append(Environment.NewLine); + + string cntStr = cnt.ToString(); + sb.Append(' ', maxLen - cntStr.Length).Append(cntStr); + sb.Append(" x "); + sb.Append(Item.GetTypeString(type)); + } + LogToConsole(sb.ToString()); + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int OnCommandStatusClear(CmdResult r) + { + fishItemCnt = new(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoFish_status_clear); } private void StartFishing() @@ -386,7 +425,7 @@ namespace MinecraftClient.ChatBots public override void OnEntitySpawn(Entity entity) { if (fishItemCounter < 15 && entity.Type == EntityType.Item && Math.Abs(entity.Location.Y - LastPos.Y) < 2.2 && - Math.Abs(entity.Location.X - LastPos.X) < 0.12 && Math.Abs(entity.Location.Z - LastPos.Z) < 0.12) + Math.Abs(entity.Location.X - LastPos.X) < 0.12 && Math.Abs(entity.Location.Z - LastPos.Z) < 0.12) { if (Config.Log_Fish_Bobber) LogToConsole(string.Format("Item ({0}) spawn at {1}, distance = {2:0.00}", entity.ID, entity.Location, entity.Location.Distance(LastPos))); @@ -440,7 +479,7 @@ namespace MinecraftClient.ChatBots public override void OnEntityMove(Entity entity) { - if (isFishing && entity != null && fishingBobber!.ID == entity.ID && + if (isFishing && entity != null && fishingBobber!.ID == entity.ID && (state == FishingState.WaitingFishToBite || state == FishingState.WaitingFishingBobber)) { Location Pos = entity.Location; @@ -620,24 +659,5 @@ namespace MinecraftClient.ChatBots return false; } } - - private static string GetHelp() - { - return string.Format(Translations.bot_autoFish_available_cmd, "start, stop, status, help"); - } - - private string GetCommandHelp(string cmd) - { - return cmd.ToLower() switch - { -#pragma warning disable format // @formatter:off - "start" => Translations.bot_autoFish_help_start, - "stop" => Translations.bot_autoFish_help_stop, - "status" => Translations.bot_autoFish_help_status, - "help" => Translations.bot_autoFish_help_help, - _ => GetHelp(), -#pragma warning restore format // @formatter:on - }; - } } } diff --git a/MinecraftClient/ChatBots/AutoRelog.cs b/MinecraftClient/ChatBots/AutoRelog.cs index 2795366e..ae69d76f 100644 --- a/MinecraftClient/ChatBots/AutoRelog.cs +++ b/MinecraftClient/ChatBots/AutoRelog.cs @@ -1,4 +1,7 @@ using System; +using Brigadier.NET; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -43,7 +46,7 @@ namespace MinecraftClient.ChatBots if (Delay.min > Delay.max) (Delay.min, Delay.max) = (Delay.max, Delay.min); - + if (Retries == -1) Retries = int.MaxValue; @@ -82,7 +85,12 @@ namespace MinecraftClient.ChatBots LogDebugToConsole(string.Format(Translations.bot_autoRelog_launch, Config.Retries)); } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) + { + Initialize(); + } + + private void Initialize() { McClient.ReconnectionAttemptsLeft = Config.Retries; if (Config.Ignore_Kick_Message) diff --git a/MinecraftClient/ChatBots/AutoRespond.cs b/MinecraftClient/ChatBots/AutoRespond.cs index 91279191..bb5ff233 100644 --- a/MinecraftClient/ChatBots/AutoRespond.cs +++ b/MinecraftClient/ChatBots/AutoRespond.cs @@ -4,6 +4,10 @@ using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; +using Brigadier.NET; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; +using PInvoke; using Tomlet.Attributes; using static MinecraftClient.Settings; @@ -183,7 +187,7 @@ namespace MinecraftClient.ChatBots /// /// Initialize the AutoRespond bot from the matches file /// - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (File.Exists(Config.Matches_File)) { @@ -304,12 +308,12 @@ namespace MinecraftClient.ChatBots { Dictionary localVars = new(); string? toPerform = rule.Match(sender, message, msgType, localVars); - if (!String.IsNullOrEmpty(toPerform)) + if (!string.IsNullOrEmpty(toPerform)) { - string? response = null; + CmdResult response = new(); LogToConsole(string.Format(Translations.bot_autoRespond_match_run, toPerform)); PerformInternalCommand(toPerform, ref response, localVars); - if (!String.IsNullOrEmpty(response)) + if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) LogToConsole(response); } } diff --git a/MinecraftClient/ChatBots/ChatLog.cs b/MinecraftClient/ChatBots/ChatLog.cs index b6ad94ef..37aecd36 100644 --- a/MinecraftClient/ChatBots/ChatLog.cs +++ b/MinecraftClient/ChatBots/ChatLog.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -115,7 +117,7 @@ namespace MinecraftClient.ChatBots } } - public override void OnInternalCommand(string commandName, string commandParams, string result) + public override void OnInternalCommand(string commandName, string commandParams, CmdResult result) { if (saveInternal) { diff --git a/MinecraftClient/ChatBots/DiscordBridge.cs b/MinecraftClient/ChatBots/DiscordBridge.cs index 644be296..848fb86b 100644 --- a/MinecraftClient/ChatBots/DiscordBridge.cs +++ b/MinecraftClient/ChatBots/DiscordBridge.cs @@ -3,16 +3,23 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.Builder; using DSharpPlus; using DSharpPlus.Entities; using DSharpPlus.Exceptions; using Microsoft.Extensions.Logging; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots { public class DiscordBridge : ChatBot { + public const string CommandName = "dscbridge"; + private enum BridgeDirection { Both = 0, @@ -68,19 +75,75 @@ namespace MinecraftClient.ChatBots instance = this; } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { - RegisterChatBotCommand("dscbridge", "bot.DiscordBridge.desc", "dscbridge direction ", OnDscCommand); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("direction") + .Then(l => l.Literal("both") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Both))) + .Then(l => l.Literal("mc") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Minecraft))) + .Then(l => l.Literal("discord") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Discord))) + ) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); Task.Run(async () => await MainAsync()); } - ~DiscordBridge() + public override void OnUnload(CommandDispatcher dispatcher) { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); Disconnect(); } - public override void OnUnload() + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => "dscbridge direction " + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandDirection(CmdResult r, BridgeDirection direction) + { + string bridgeName; + switch (direction) + { + case BridgeDirection.Both: + bridgeName = Translations.bot_DiscordBridge_direction_both; + bridgeDirection = BridgeDirection.Both; + break; + + case BridgeDirection.Minecraft: + bridgeName = Translations.bot_DiscordBridge_direction_minecraft; + bridgeDirection = BridgeDirection.Minecraft; + break; + + case BridgeDirection.Discord: + bridgeName = Translations.bot_DiscordBridge_direction_discord; + bridgeDirection = BridgeDirection.Discord; + break; + + default: + goto case BridgeDirection.Both; + } + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_DiscordBridge_direction, bridgeName)); + } + + ~DiscordBridge() { Disconnect(); } @@ -114,47 +177,6 @@ namespace MinecraftClient.ChatBots return instance; } - private string OnDscCommand(string cmd, string[] args) - { - if (args.Length == 2) - { - if (args[0].ToLower().Equals("direction")) - { - string direction = args[1].ToLower().Trim(); - - string bridgeName; - switch (direction) - { - case "b": - case "both": - bridgeName = Translations.bot_DiscordBridge_direction_both; - bridgeDirection = BridgeDirection.Both; - break; - - case "mc": - case "minecraft": - bridgeName = Translations.bot_DiscordBridge_direction_minecraft; - bridgeDirection = BridgeDirection.Minecraft; - break; - - case "d": - case "dcs": - case "discord": - bridgeName = Translations.bot_DiscordBridge_direction_discord; - bridgeDirection = BridgeDirection.Discord; - break; - - default: - return Translations.bot_DiscordBridge_invalid_direction; - } - - return string.Format(Translations.bot_DiscordBridge_direction, bridgeName); - }; - } - - return "dscbridge direction "; - } - public override void GetText(string text) { if (!CanSendMessages()) @@ -369,9 +391,8 @@ namespace MinecraftClient.ChatBots message = message[1..]; await e.Message.CreateReactionAsync(DiscordEmoji.FromName(discordBotClient, ":gear:")); - string? result = ""; + CmdResult result = new(); PerformInternalCommand(message, ref result); - result = string.IsNullOrEmpty(result) ? "-" : result; await e.Message.DeleteOwnReactionAsync(DiscordEmoji.FromName(discordBotClient, ":gear:")); await e.Message.CreateReactionAsync(DiscordEmoji.FromName(discordBotClient, ":white_check_mark:")); diff --git a/MinecraftClient/ChatBots/Farmer.cs b/MinecraftClient/ChatBots/Farmer.cs index 94af1191..e54e5ed7 100644 --- a/MinecraftClient/ChatBots/Farmer.cs +++ b/MinecraftClient/ChatBots/Farmer.cs @@ -3,34 +3,22 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; using MinecraftClient.Mapping; using MinecraftClient.Protocol.Handlers; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots { - enum State - { - SearchingForCropsToBreak = 0, - SearchingForFarmlandToPlant, - PlantingCrops, - BonemealingCrops - } - - enum CropType - { - Beetroot, - Carrot, - Melon, - Netherwart, - Pumpkin, - Potato, - Wheat - } - public class Farmer : ChatBot { + public const string CommandName = "farmer"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -42,15 +30,34 @@ namespace MinecraftClient.ChatBots public bool Enabled = false; [TomlInlineComment("$config.ChatBot.Farmer.Delay_Between_Tasks$")] - public int Delay_Between_Tasks = 1; + public double Delay_Between_Tasks = 1.0; public void OnSettingUpdate() { - if (Delay_Between_Tasks <= 0) - Delay_Between_Tasks = 1; + if (Delay_Between_Tasks < 1.0) + Delay_Between_Tasks = 1.0; } } + public enum State + { + SearchingForCropsToBreak = 0, + SearchingForFarmlandToPlant, + PlantingCrops, + BonemealingCrops + } + + public enum CropType + { + Beetroot, + Carrot, + Melon, + Netherwart, + Pumpkin, + Potato, + Wheat + } + private State state = State.SearchingForCropsToBreak; private CropType cropType = CropType.Wheat; private int farmingRadius = 30; @@ -59,9 +66,11 @@ namespace MinecraftClient.ChatBots private bool allowTeleport = false; private bool debugEnabled = false; + public int Delay_Between_Tasks_Millisecond => (int)Math.Round(Config.Delay_Between_Tasks * 1000); + private const string commandDescription = "farmer [radius:] [unsafe:] [teleport:] [debug:]|stop>"; - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (GetProtocolVersion() < Protocol18Handler.MC_1_13_Version) { @@ -81,130 +90,152 @@ namespace MinecraftClient.ChatBots return; } - RegisterChatBotCommand("farmer", "bot.farmer.desc", commandDescription, OnFarmCommand); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("start") + .Then(l => l.Argument("CropType", MccArguments.FarmerCropType()) + .Executes(r => OnCommandStart(r.Source, MccArguments.GetFarmerCropType(r, "CropType"), null)) + .Then(l => l.Argument("OtherArgs", Arguments.GreedyString()) + .Executes(r => OnCommandStart(r.Source, MccArguments.GetFarmerCropType(r, "CropType"), Arguments.GetString(r, "OtherArgs")))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); } - private string OnFarmCommand(string cmd, string[] args) + public override void OnUnload(CommandDispatcher dispatcher) { - if (args.Length > 0) + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch { - if (args[0].Equals("stop", StringComparison.OrdinalIgnoreCase)) - { - if (!running) - return Translations.bot_farmer_already_stopped; +#pragma warning disable format // @formatter:off + _ => Translations.bot_farmer_desc + ": " + commandDescription + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } - running = false; - return Translations.bot_farmer_stopping; - } + private int OnCommandStop(CmdResult r) + { + if (!running) + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_farmer_already_stopped); + } + else + { + running = false; + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_farmer_stopping); + } + } - if (args[0].Equals("start", StringComparison.OrdinalIgnoreCase)) + private int OnCommandStart(CmdResult r, CropType whatToFarm, string? otherArgs) + { + if (running) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_farmer_already_running); + + int radius = 30; + + state = State.SearchingForFarmlandToPlant; + cropType = whatToFarm; + allowUnsafe = false; + allowTeleport = false; + debugEnabled = false; + + if (!string.IsNullOrWhiteSpace(otherArgs)) + { + string[] args = otherArgs.ToLower().Split(' ', StringSplitOptions.TrimEntries); + foreach (string currentArg in args) { - if (args.Length >= 2) + if (!currentArg.Contains(':')) { - if (running) - return Translations.bot_farmer_already_running; + LogToConsole("§x§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); + continue; + } - if (!Enum.TryParse(args[1], true, out CropType whatToFarm)) - return Translations.bot_farmer_invalid_crop_type; + string[] parts = currentArg.Split(":", StringSplitOptions.TrimEntries); - int radius = 30; + if (parts.Length != 2) + { + LogToConsole("§x§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); + continue; + } - state = State.SearchingForFarmlandToPlant; - cropType = whatToFarm; - allowUnsafe = false; - allowTeleport = false; - debugEnabled = false; + switch (parts[0]) + { + case "r": + case "radius": + if (!int.TryParse(parts[1], NumberStyles.Any, CultureInfo.CurrentCulture, out radius)) + LogToConsole("§x§1§0" + Translations.bot_farmer_invalid_radius); - if (args.Length >= 3) - { - for (int i = 2; i < args.Length; i++) + if (radius <= 0) { - string currentArg = args[i].Trim().ToLower(); - - if (!currentArg.Contains(':')) - { - LogToConsole("§x§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); - continue; - } - - string[] parts = currentArg.Split(":", StringSplitOptions.TrimEntries); - - if (parts.Length != 2) - { - LogToConsole("§x§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); - continue; - } - - switch (parts[0]) - { - case "r": - case "radius": - if (!int.TryParse(parts[1], NumberStyles.Any, CultureInfo.CurrentCulture, out radius)) - LogToConsole("§x§1§0" + Translations.bot_farmer_invalid_radius); - - if (radius <= 0) - { - LogToConsole("§x§1§0" + Translations.bot_farmer_invalid_radius); - radius = 30; - } - - break; - - case "f": - case "unsafe": - if (allowUnsafe) - break; - - if (parts[1].Equals("true") || parts[1].Equals("1")) - { - LogToConsole("§x§1§0" + Translations.bot_farmer_warining_force_unsafe); - allowUnsafe = true; - } - else allowUnsafe = false; - - break; - - case "t": - case "teleport": - if (allowTeleport) - break; - - if (parts[1].Equals("true") || parts[1].Equals("1")) - { - LogToConsole("§w§1§f" + Translations.bot_farmer_warining_allow_teleport); - allowTeleport = true; - } - else allowTeleport = false; - - break; - - case "d": - case "debug": - if (debugEnabled) - break; - - if (parts[1].Equals("true") || parts[1].Equals("1")) - { - LogToConsole("Debug enabled!"); - debugEnabled = true; - } - else debugEnabled = false; - - break; - } + LogToConsole("§x§1§0" + Translations.bot_farmer_invalid_radius); + radius = 30; } - } - farmingRadius = radius; - running = true; - new Thread(() => MainPorcess()).Start(); + break; - return ""; + case "f": + case "unsafe": + if (allowUnsafe) + break; + + if (parts[1].Equals("true") || parts[1].Equals("1")) + { + LogToConsole("§x§1§0" + Translations.bot_farmer_warining_force_unsafe); + allowUnsafe = true; + } + else allowUnsafe = false; + + break; + + case "t": + case "teleport": + if (allowTeleport) + break; + + if (parts[1].Equals("true") || parts[1].Equals("1")) + { + LogToConsole("§w§1§f" + Translations.bot_farmer_warining_allow_teleport); + allowTeleport = true; + } + else allowTeleport = false; + + break; + + case "d": + case "debug": + if (debugEnabled) + break; + + if (parts[1].Equals("true") || parts[1].Equals("1")) + { + LogToConsole("Debug enabled!"); + debugEnabled = true; + } + else debugEnabled = false; + + break; } } } - return Translations.bot_farmer_desc + ": " + commandDescription; + farmingRadius = radius; + running = true; + new Thread(() => MainPorcess()).Start(); + + return r.SetAndReturn(CmdResult.Status.Done); } public override void AfterGameJoined() @@ -230,7 +261,7 @@ namespace MinecraftClient.ChatBots if (AutoEat.Eating) { LogDebug("Eating..."); - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -246,7 +277,7 @@ namespace MinecraftClient.ChatBots { LogDebug("No seeds, trying to find some crops to break"); state = State.SearchingForCropsToBreak; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -256,7 +287,7 @@ namespace MinecraftClient.ChatBots { LogDebug("Could not find any farmland, trying to find some crops to break"); state = State.SearchingForCropsToBreak; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -272,7 +303,7 @@ namespace MinecraftClient.ChatBots { LogDebug("Ran out of seeds, looking for crops to break..."); state = State.SearchingForCropsToBreak; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } } @@ -321,7 +352,7 @@ namespace MinecraftClient.ChatBots { LogToConsole("No crops to break, trying to bonemeal ungrown ones"); state = State.BonemealingCrops; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -367,7 +398,7 @@ namespace MinecraftClient.ChatBots if (cropType == CropType.Netherwart) { state = State.SearchingForFarmlandToPlant; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -376,7 +407,7 @@ namespace MinecraftClient.ChatBots { LogDebug("No bonemeal, searching for some farmland to plant seeds on"); state = State.SearchingForFarmlandToPlant; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -386,7 +417,7 @@ namespace MinecraftClient.ChatBots { LogDebug("No crops to bonemeal, searching for farmland to plant seeds on"); state = State.SearchingForFarmlandToPlant; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -402,7 +433,7 @@ namespace MinecraftClient.ChatBots { LogDebug("Ran out of Bone Meal, looking for farmland to plant on..."); state = State.SearchingForFarmlandToPlant; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } } @@ -438,8 +469,8 @@ namespace MinecraftClient.ChatBots break; } - LogDebug("Waiting for " + Config.Delay_Between_Tasks + " seconds for next cycle."); - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + LogDebug(string.Format("Waiting for {0:0.00} seconds for next cycle.", Config.Delay_Between_Tasks)); + Thread.Sleep(Delay_Between_Tasks_Millisecond); } LogToConsole(Translations.bot_farmer_stopped); @@ -815,6 +846,7 @@ namespace MinecraftClient.ChatBots { return GetPlayerInventory().SearchItem(itemType).Length > 0; } + private void LogDebug(object text) { if (debugEnabled) diff --git a/MinecraftClient/ChatBots/FollowPlayer.cs b/MinecraftClient/ChatBots/FollowPlayer.cs index ebbf2c90..231332b4 100644 --- a/MinecraftClient/ChatBots/FollowPlayer.cs +++ b/MinecraftClient/ChatBots/FollowPlayer.cs @@ -1,12 +1,19 @@ using System; using System.Linq; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots { public class FollowPlayer : ChatBot { + public const string CommandName = "follow"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -37,7 +44,7 @@ namespace MinecraftClient.ChatBots private int _updateCounter = 0; private bool _unsafeEnabled = false; - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetEntityHandlingEnabled()) { @@ -55,58 +62,85 @@ namespace MinecraftClient.ChatBots return; } - RegisterChatBotCommand("follow", "cmd.follow.desc", "follow ", OnFollowCommand); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("start") + .Then(l => l.Argument("PlayerName", MccArguments.PlayerName()) + .Executes(r => OnCommandStart(r.Source, Arguments.GetString(r, "PlayerName"), takeRisk: false)) + .Then(l => l.Literal("-f") + .Executes(r => OnCommandStart(r.Source, Arguments.GetString(r, "PlayerName"), takeRisk: true))))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); } - private string OnFollowCommand(string cmd, string[] args) + public override void OnUnload(CommandDispatcher dispatcher) { - if (args.Length > 0) + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch { - if (args[0].Equals("stop", StringComparison.OrdinalIgnoreCase)) - { - if (_playerToFollow == null) - return Translations.cmd_follow_already_stopped; +#pragma warning disable format // @formatter:off + _ => Translations.cmd_follow_desc + ": " + Translations.cmd_follow_usage + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } - _playerToFollow = null; - return Translations.cmd_follow_stopping; - } - else - { - if (!IsValidName(args[0])) - return Translations.cmd_follow_invalid_name; + private int OnCommandStart(CmdResult r, string name, bool takeRisk) + { + if (!IsValidName(name)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_invalid_name); - Entity? player = GetEntities().Values.ToList().Find(entity => - entity.Type == EntityType.Player && !string.IsNullOrEmpty(entity.Name) && entity.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase)); + Entity? player = GetEntities().Values.ToList().Find(entity => + entity.Type == EntityType.Player + && !string.IsNullOrEmpty(entity.Name) + && entity.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - if (player == null) - return Translations.cmd_follow_invalid_player; + if (player == null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_invalid_player); - if (!CanMoveThere(player.Location)) - return Translations.cmd_follow_cant_reach_player; + if (!CanMoveThere(player.Location)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_cant_reach_player); - if (_playerToFollow != null && _playerToFollow.Equals(args[0], StringComparison.OrdinalIgnoreCase)) - return string.Format(Translations.cmd_follow_already_following, _playerToFollow); + if (_playerToFollow != null && _playerToFollow.Equals(name, StringComparison.OrdinalIgnoreCase)) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_follow_already_following, _playerToFollow)); - string result; - if (_playerToFollow != null) - result = string.Format(Translations.cmd_follow_switched, player.Name!); - else - result = string.Format(Translations.cmd_follow_started, player.Name!); - _playerToFollow = args[0].Trim().ToLower(); + string result; + if (_playerToFollow != null) + result = string.Format(Translations.cmd_follow_switched, player.Name!); + else + result = string.Format(Translations.cmd_follow_started, player.Name!); + _playerToFollow = name.ToLower(); - LogToConsole(Translations.cmd_follow_note); - - if (args.Length == 2 && args[1].Equals("-f", StringComparison.OrdinalIgnoreCase)) - { - _unsafeEnabled = true; - LogToConsole(Translations.cmd_follow_unsafe_enabled); - } - - return result; - } + if (takeRisk) + { + _unsafeEnabled = true; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_follow_note + '\n' + Translations.cmd_follow_unsafe_enabled); } + else + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_follow_note); + } - return Translations.cmd_follow_desc + ": " + Translations.cmd_follow_usage; + private int OnCommandStop(CmdResult r) + { + if (_playerToFollow == null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_already_stopped); + + _playerToFollow = null; + + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_follow_stopping); } public override void Update() diff --git a/MinecraftClient/ChatBots/HangmanGame.cs b/MinecraftClient/ChatBots/HangmanGame.cs index 0eb923c9..ac04dd9e 100644 --- a/MinecraftClient/ChatBots/HangmanGame.cs +++ b/MinecraftClient/ChatBots/HangmanGame.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots diff --git a/MinecraftClient/ChatBots/Mailer.cs b/MinecraftClient/ChatBots/Mailer.cs index b6f2ee12..7da1a312 100644 --- a/MinecraftClient/ChatBots/Mailer.cs +++ b/MinecraftClient/ChatBots/Mailer.cs @@ -3,6 +3,11 @@ using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -12,6 +17,8 @@ namespace MinecraftClient.ChatBots /// public class Mailer : ChatBot { + public const string CommandName = "mailer"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -216,7 +223,7 @@ namespace MinecraftClient.ChatBots /// /// Initialization of the Mailer bot /// - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { LogDebugToConsole(Translations.bot_mailer_init); LogDebugToConsole(Translations.bot_mailer_init_db + Config.DatabaseFile); @@ -251,7 +258,123 @@ namespace MinecraftClient.ChatBots mailDbFileMonitor = new FileMonitor(Path.GetDirectoryName(Config.DatabaseFile)!, Path.GetFileName(Config.DatabaseFile), FileMonitorCallback); ignoreListFileMonitor = new FileMonitor(Path.GetDirectoryName(Config.IgnoreListFile)!, Path.GetFileName(Config.IgnoreListFile), FileMonitorCallback); - RegisterChatBotCommand("mailer", Translations.bot_mailer_cmd, "mailer ", ProcessInternalCommand); + dispatcher.Register(l => + l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(string.Empty)) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp(string.Empty))) + ) + ); + + dispatcher.Register(l => + l.Literal(CommandName) + .Executes(r => OnCommandHelp(string.Empty)) + .Then(l => l.Literal("getmails") + .Executes(r => OnCommandGetMails()) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("getmails"))) + ) + .Then(l => l.Literal("getignored") + .Executes(r => OnCommandGetIgnored()) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("getignored"))) + ) + .Then(l => l.Literal("addignored") + .Executes(r => OnCommandHelp("addignored")) + .Then(l => l.Argument("username", Arguments.String()) + .Executes(r => OnCommandAddIgnored(Arguments.GetString(r, "username"))) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("addignored"))) + ) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("addignored"))) + ) + .Then(l => l.Literal("removeignored") + .Executes(r => OnCommandHelp("removeignored")) + .Then(l => l.Argument("username", Arguments.String()) + .Executes(r => OnCommandRemoveIgnored(Arguments.GetString(r, "username"))) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("removeignored"))) + ) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("removeignored"))) + ) + .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(string.Empty)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName)) + ) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp(string.Empty))) + ); + } + + public override void OnUnload(CommandDispatcher dispatcher) + { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(string cmd) + { + LogToConsole(cmd switch + { +#pragma warning disable format // @formatter:off + _ => Translations.bot_mailer_cmd_help + ": /mailer " + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + return 1; + } + + private int OnCommandGetMails() + { + LogToConsole(string.Format(Translations.bot_mailer_cmd_getmails, string.Join("\n", mailDatabase))); + return 1; + } + + private int OnCommandGetIgnored() + { + LogToConsole(string.Format(Translations.bot_mailer_cmd_getignored, string.Join("\n", ignoreList))); + return 1; + } + + private int OnCommandAddIgnored(string username) + { + if (IsValidName(username)) + { + username = username.ToLower(); + lock (readWriteLock) + { + if (!ignoreList.Contains(username)) + { + ignoreList.Add(username); + ignoreList.SaveToFile(Config.IgnoreListFile); + } + } + LogToConsole(string.Format(Translations.bot_mailer_cmd_ignore_added, username)); + return 1; + } + else + { + LogToConsole(string.Format(Translations.bot_mailer_cmd_ignore_invalid, "addignored")); + return 0; + } + } + + private int OnCommandRemoveIgnored(string username) + { + if (IsValidName(username)) + { + username = username.ToLower(); + lock (readWriteLock) + { + if (ignoreList.Contains(username)) + { + ignoreList.Remove(username); + ignoreList.SaveToFile(Config.IgnoreListFile); + } + } + LogToConsole(string.Format(Translations.bot_mailer_cmd_ignore_removed, username)); + return 1; + } + else + { + LogToConsole(string.Format(Translations.bot_mailer_cmd_ignore_invalid, "removeignored")); + return 0; + } } /// @@ -365,57 +488,5 @@ namespace MinecraftClient.ChatBots ignoreList = IgnoreList.FromFile(Config.IgnoreListFile); } } - - /// - /// Interprets local commands. - /// - private string ProcessInternalCommand(string cmd, string[] args) - { - if (args.Length > 0) - { - string commandName = args[0].ToLower(); - switch (commandName) - { - case "getmails": // Sorry, I (ReinforceZwei) replaced "=" to "-" because it would affect the parsing of translation file (key=value) - return string.Format(Translations.bot_mailer_cmd_getmails, string.Join("\n", mailDatabase)); - - case "getignored": - return string.Format(Translations.bot_mailer_cmd_getignored, string.Join("\n", ignoreList)); - - case "addignored": - case "removeignored": - if (args.Length > 1 && IsValidName(args[1])) - { - string username = args[1].ToLower(); - if (commandName == "addignored") - { - lock (readWriteLock) - { - if (!ignoreList.Contains(username)) - { - ignoreList.Add(username); - ignoreList.SaveToFile(Config.IgnoreListFile); - } - } - return string.Format(Translations.bot_mailer_cmd_ignore_added, args[1]); - } - else - { - lock (readWriteLock) - { - if (ignoreList.Contains(username)) - { - ignoreList.Remove(username); - ignoreList.SaveToFile(Config.IgnoreListFile); - } - } - return string.Format(Translations.bot_mailer_cmd_ignore_removed, args[1]); - } - } - else return string.Format(Translations.bot_mailer_cmd_ignore_invalid, commandName); - } - } - return Translations.bot_mailer_cmd_help + ": /help mailer"; - } } } diff --git a/MinecraftClient/ChatBots/Map.cs b/MinecraftClient/ChatBots/Map.cs index d0d01a64..fd06c769 100644 --- a/MinecraftClient/ChatBots/Map.cs +++ b/MinecraftClient/ChatBots/Map.cs @@ -1,18 +1,24 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.IO; using System.Text; using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.Builder; using ImageMagick; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots { public class Map : ChatBot { + public const string CommandName = "maps"; + public static Configs Config = new(); public struct QueuedMap @@ -63,25 +69,89 @@ namespace MinecraftClient.ChatBots private readonly string baseDirectory = @"Rendered_Maps"; - private readonly Dictionary cachedMaps = new(); + internal readonly Dictionary cachedMaps = new(); private readonly Queue discordQueue = new(); - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!Directory.Exists(baseDirectory)) Directory.CreateDirectory(baseDirectory); DeleteRenderedMaps(); - RegisterChatBotCommand("maps", "bot.map.cmd.desc", "maps list|render or maps l|r ", OnMapCommand); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Executes(r => OnCommandList(r.Source)) + .Then(l => l.Literal("list") + .Executes(r => OnCommandList(r.Source))) + .Then(l => l.Literal("render") + .Then(l => l.Argument("MapID", MccArguments.MapBotMapId()) + .Executes(r => OnCommandRender(r.Source, Arguments.GetInteger(r, "MapID"))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); } - public override void OnUnload() + public override void OnUnload(CommandDispatcher dispatcher) { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); DeleteRenderedMaps(); } + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => Translations.error_usage + ": /maps >" + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandList(CmdResult r) + { + if (cachedMaps.Count == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_map_no_maps); + + LogToConsole(Translations.bot_map_received); + + foreach (var (key, value) in new SortedDictionary(cachedMaps)) + LogToConsole(string.Format(Translations.bot_map_list_item, key, value.LastUpdated)); + + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int OnCommandRender(CmdResult r, int mapId) + { + if (!cachedMaps.ContainsKey(mapId)) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.bot_map_cmd_not_found, mapId)); + + try + { + McMap map = cachedMaps[mapId]; + if (Config.Save_To_File) + SaveToFile(map); + + if (Config.Render_In_Console) + RenderInConsole(map); + + return r.SetAndReturn(CmdResult.Status.Done); + } + catch (Exception e) + { + LogDebugToConsole(e.StackTrace!); + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.bot_map_failed_to_render, mapId)); + } + } + private void DeleteRenderedMaps() { if (Config.Delete_All_On_Unload) @@ -94,56 +164,6 @@ namespace MinecraftClient.ChatBots } } - public string OnMapCommand(string command, string[] args) - { - if (args.Length == 0 || (args.Length == 1 && (args[0].ToLower().Equals("list") || args[0].ToLower().Equals("l")))) - { - if (cachedMaps.Count == 0) - return Translations.bot_map_no_maps; - - LogToConsole(Translations.bot_map_received); - - foreach (var (key, value) in new SortedDictionary(cachedMaps)) - LogToConsole(string.Format(Translations.bot_map_list_item, key, value.LastUpdated)); - - return ""; - } - - if (args.Length > 1) - { - if (args[0].ToLower().Equals("render") || args[0].ToLower().Equals("r")) - { - if (args.Length < 2) - return "maps > | maps >"; - - if (int.TryParse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture, out int mapId)) - { - if (!cachedMaps.ContainsKey(mapId)) - return string.Format(Translations.bot_map_cmd_not_found, mapId); - - try - { - McMap map = cachedMaps[mapId]; - if (Config.Save_To_File) - SaveToFile(map); - - if (Config.Render_In_Console) - RenderInConsole(map); - - return ""; - } - catch (Exception e) - { - LogDebugToConsole(e.StackTrace!); - return string.Format(Translations.bot_map_failed_to_render, mapId); - } - } - return Translations.bot_map_cmd_invalid_id; - } - } - return ""; - } - public override void OnMapData(int mapid, byte scale, bool trackingPosition, bool locked, List icons, byte columnsUpdated, byte rowsUpdated, byte mapCoulmnX, byte mapRowZ, byte[]? colors) { if (columnsUpdated == 0 && cachedMaps.ContainsKey(mapid)) @@ -320,7 +340,7 @@ namespace MinecraftClient.ChatBots } } - private void RenderInConsole(McMap map) + private static void RenderInConsole(McMap map) { StringBuilder sb = new(); @@ -406,7 +426,7 @@ namespace MinecraftClient.ChatBots public DateTime LastUpdated { get; set; } } - class MapColors + internal class MapColors { // When colors are updated in a new update, you can get them using the game code: net\minecraft\world\level\material\MaterialColor.java public static Dictionary Colors = new() diff --git a/MinecraftClient/ChatBots/PlayerListLogger.cs b/MinecraftClient/ChatBots/PlayerListLogger.cs index 8c59af89..4b0ad7d4 100644 --- a/MinecraftClient/ChatBots/PlayerListLogger.cs +++ b/MinecraftClient/ChatBots/PlayerListLogger.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots diff --git a/MinecraftClient/ChatBots/RemoteControl.cs b/MinecraftClient/ChatBots/RemoteControl.cs index ac1d544e..301e16f8 100644 --- a/MinecraftClient/ChatBots/RemoteControl.cs +++ b/MinecraftClient/ChatBots/RemoteControl.cs @@ -1,4 +1,6 @@ using System; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -32,17 +34,9 @@ namespace MinecraftClient.ChatBots string command = "", sender = ""; if (IsPrivateMessage(text, ref command, ref sender) && Settings.Config.Main.Advanced.BotOwners.Contains(sender.ToLower().Trim())) { - string? response = ""; + CmdResult response = new(); PerformInternalCommand(command, ref response); - response = GetVerbatim(response); - foreach (char disallowedChar in McClient.GetDisallowedChatCharacters()) - { - response = response.Replace(disallowedChar.ToString(), String.Empty); - } - if (response.Length > 0) - { - SendPrivateMessage(sender, response); - } + SendPrivateMessage(sender, response.ToString()); } else if (Config.AutoTpaccept && IsTeleportRequest(text, ref sender) diff --git a/MinecraftClient/ChatBots/ReplayCapture.cs b/MinecraftClient/ChatBots/ReplayCapture.cs index 26315ef2..ee0c0a55 100644 --- a/MinecraftClient/ChatBots/ReplayCapture.cs +++ b/MinecraftClient/ChatBots/ReplayCapture.cs @@ -1,6 +1,11 @@ using System; using System.Collections.Generic; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Protocol; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -10,6 +15,8 @@ namespace MinecraftClient.ChatBots /// public class ReplayCapture : ChatBot { + public const string CommandName = "replay"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -33,14 +40,80 @@ namespace MinecraftClient.ChatBots private ReplayHandler? replay; private int backupCounter = -1; - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { SetNetworkPacketEventEnabled(true); replay = new ReplayHandler(GetProtocolVersion()); replay.MetaData.serverName = GetServerHost() + GetServerPort(); backupCounter = Settings.DoubleToTick(Config.Backup_Interval); - RegisterChatBotCommand("replay", Translations.bot_replayCapture_cmd, "replay ", Command); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("save") + .Executes(r => OnCommandSave(r.Source))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); + } + + public override void OnUnload(CommandDispatcher dispatcher) + { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => string.Format(Translations.general_available_cmd, "save, stop") + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandSave(CmdResult r) + { + try + { + if (replay!.RecordRunning) + { + replay.CreateBackupReplay(@"replay_recordings\" + replay.GetReplayDefaultName()); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_replayCapture_created); + } + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_replayCapture_restart); + } + catch (Exception e) + { + return r.SetAndReturn(CmdResult.Status.Fail, e.Message); + } + } + + private int OnCommandStop(CmdResult r) + { + try + { + if (replay!.RecordRunning) + { + replay.OnShutDown(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_replayCapture_stopped); + } + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_replayCapture_restart); + } + catch (Exception e) + { + return r.SetAndReturn(CmdResult.Status.Fail, e.Message); + } } public override void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound) @@ -66,37 +139,5 @@ namespace MinecraftClient.ChatBots replay!.OnShutDown(); return base.OnDisconnect(reason, message); } - - public string Command(string cmd, string[] args) - { - try - { - if (replay!.RecordRunning) - { - if (args.Length > 0) - { - switch (args[0].ToLower()) - { - case "save": - { - replay.CreateBackupReplay(@"replay_recordings\" + replay.GetReplayDefaultName()); - return Translations.bot_replayCapture_created; - } - case "stop": - { - replay.OnShutDown(); - return Translations.bot_replayCapture_stopped; - } - } - } - return string.Format(Translations.general_available_cmd, "save, stop"); - } - else return Translations.bot_replayCapture_restart; - } - catch (Exception e) - { - return e.Message; - } - } } } diff --git a/MinecraftClient/ChatBots/Script.cs b/MinecraftClient/ChatBots/Script.cs index 11b9d97e..8f706e11 100644 --- a/MinecraftClient/ChatBots/Script.cs +++ b/MinecraftClient/ChatBots/Script.cs @@ -5,6 +5,9 @@ using System.IO; using System.Reflection; using System.Text; using System.Threading; +using Brigadier.NET; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; namespace MinecraftClient.ChatBots { @@ -124,7 +127,7 @@ namespace MinecraftClient.ChatBots return false; } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { //Load the given file from the startup parameters if (LookForScript(ref file!)) diff --git a/MinecraftClient/ChatBots/ScriptScheduler.cs b/MinecraftClient/ChatBots/ScriptScheduler.cs index 21e9a977..bf8680a4 100644 --- a/MinecraftClient/ChatBots/ScriptScheduler.cs +++ b/MinecraftClient/ChatBots/ScriptScheduler.cs @@ -1,4 +1,5 @@ using System; +using MinecraftClient.Scripting; using Tomlet.Attributes; using static MinecraftClient.ChatBots.ScriptScheduler.Configs; diff --git a/MinecraftClient/ChatBots/TelegramBridge.cs b/MinecraftClient/ChatBots/TelegramBridge.cs index fb1a93ca..8c98d4cf 100644 --- a/MinecraftClient/ChatBots/TelegramBridge.cs +++ b/MinecraftClient/ChatBots/TelegramBridge.cs @@ -3,6 +3,11 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; +using MinecraftClient.Scripting; using Telegram.Bot; using Telegram.Bot.Exceptions; using Telegram.Bot.Polling; @@ -16,6 +21,8 @@ namespace MinecraftClient.ChatBots { public class TelegramBridge : ChatBot { + public const string CommandName = "tgbridge"; + private enum BridgeDirection { Both = 0, @@ -68,19 +75,74 @@ namespace MinecraftClient.ChatBots instance = this; } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { - RegisterChatBotCommand("tgbridge", "bot.TelegramBridge.desc", "tgbridge direction ", OnTgCommand); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("direction") + .Then(l => l.Literal("both") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Both))) + .Then(l => l.Literal("mc") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Minecraft))) + .Then(l => l.Literal("telegram") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Telegram)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); Task.Run(async () => await MainAsync()); } - ~TelegramBridge() + public override void OnUnload(CommandDispatcher dispatcher) { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); Disconnect(); } - public override void OnUnload() + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => Translations.error_usage + ": /tgbridge direction " + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandDirection(CmdResult r, BridgeDirection direction) + { + string bridgeName; + switch (direction) + { + case BridgeDirection.Both: + bridgeName = Translations.bot_TelegramBridge_direction_both; + bridgeDirection = BridgeDirection.Both; + break; + + case BridgeDirection.Minecraft: + bridgeName = Translations.bot_TelegramBridge_direction_minecraft; + bridgeDirection = BridgeDirection.Minecraft; + break; + + case BridgeDirection.Telegram: + bridgeName = Translations.bot_TelegramBridge_direction_Telegram; + bridgeDirection = BridgeDirection.Telegram; + break; + + default: + goto case BridgeDirection.Both; + } + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_TelegramBridge_direction, bridgeName)); + } + + ~TelegramBridge() { Disconnect(); } @@ -110,47 +172,6 @@ namespace MinecraftClient.ChatBots return instance; } - private string OnTgCommand(string cmd, string[] args) - { - if (args.Length == 2) - { - if (args[0].ToLower().Equals("direction")) - { - string direction = args[1].ToLower().Trim(); - - string bridgeName; - switch (direction) - { - case "b": - case "both": - bridgeName = Translations.bot_TelegramBridge_direction_both; - bridgeDirection = BridgeDirection.Both; - break; - - case "mc": - case "minecraft": - bridgeName = Translations.bot_TelegramBridge_direction_minecraft; - bridgeDirection = BridgeDirection.Minecraft; - break; - - case "t": - case "tg": - case "telegram": - bridgeName = Translations.bot_TelegramBridge_direction_Telegram; - bridgeDirection = BridgeDirection.Telegram; - break; - - default: - return Translations.bot_TelegramBridge_invalid_direction; - } - - return string.Format(Translations.bot_TelegramBridge_direction, bridgeName); - }; - } - - return "dscbridge direction "; - } - public override void GetText(string text) { if (!CanSendMessages()) @@ -324,9 +345,8 @@ namespace MinecraftClient.ChatBots { var command = text[1..]; - string? result = ""; + CmdResult result = new(); PerformInternalCommand(command, ref result); - result = string.IsNullOrEmpty(result) ? "-" : result; await botClient.SendTextMessageAsync( chatId: chatId, diff --git a/MinecraftClient/ChatBots/TestBot.cs b/MinecraftClient/ChatBots/TestBot.cs index f96e9e8a..8b103535 100644 --- a/MinecraftClient/ChatBots/TestBot.cs +++ b/MinecraftClient/ChatBots/TestBot.cs @@ -1,4 +1,6 @@ -namespace MinecraftClient.ChatBots +using MinecraftClient.Scripting; + +namespace MinecraftClient.ChatBots { /// /// Example of message receiving. diff --git a/MinecraftClient/Command.cs b/MinecraftClient/Command.cs index ddb99ac3..6a6e8ae5 100644 --- a/MinecraftClient/Command.cs +++ b/MinecraftClient/Command.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Text; using Brigadier.NET; -using Microsoft.Extensions.Logging; -using MinecraftClient.Commands; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; namespace MinecraftClient { @@ -25,34 +25,19 @@ namespace MinecraftClient /// public abstract string CmdDesc { get; } - public abstract void RegisterCommand(McClient handler, CommandDispatcher dispatcher); - /// /// Get the translated version of command description. /// /// Translated command description public string GetCmdDescTranslated() { - string s = (string.IsNullOrEmpty(CmdUsage) || string.IsNullOrEmpty(CmdDesc)) ? "" : ": "; // If either one is empty, no colon : - return CmdUsage + s + CmdDesc; - } + char cmdChar = Settings.Config.Main.Advanced.InternalCmdChar.ToChar(); - public void LogUsage(Logger.ILogger logger) - { - logger.Info($"{Translations.Get("error.usage")}: {Settings.Config.Main.Advanced.InternalCmdChar.ToChar()}{CmdUsage}"); - } - - - public static int LogExecuteResult(Logger.ILogger logger, bool result) - { - logger.Info(Translations.Get(result ? "general.done" : "general.fail")); - return result ? 1 : 0; - } - - public static int LogExecuteResult(Logger.ILogger logger, int result) - { - logger.Info(Translations.Get(result > 0 ? "general.done" : "general.fail")); - return result; + StringBuilder sb = new(); + string s = (string.IsNullOrEmpty(CmdUsage) || string.IsNullOrEmpty(CmdDesc)) ? string.Empty : ": "; // If either one is empty, no colon : + sb.Append("§e").Append(cmdChar).Append(CmdUsage).Append("§r").Append(s).AppendLine(CmdDesc); + sb.Append(McClient.dispatcher.GetAllUsageString(CmdName, false)); + return sb.ToString(); } /// @@ -61,18 +46,9 @@ namespace MinecraftClient public abstract string CmdUsage { get; } /// - /// Perform the command + /// Register the command. /// - /// The full command, eg: 'mycommand arg1 arg2' - /// Local variables passed along with the command (may be null) - /// A confirmation/error message, or "" if no message - public abstract string Run(McClient handler, string command, Dictionary? localVars); - - /// - /// Return a list of aliases for this command. - /// Override this method if you wish to put aliases to the command - /// - public virtual IEnumerable GetCMDAliases() { return Array.Empty(); } + public abstract void RegisterCommand(McClient handler, CommandDispatcher dispatcher); /// /// Check if at least one argument has been passed to the command diff --git a/MinecraftClient/CommandHandler/ArgumentType/AccountNickArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/AccountNickArgumentType.cs new file mode 100644 index 00000000..09d11de1 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/AccountNickArgumentType.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class AccountNickArgumentType : ArgumentType + { + public override string Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadString(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + var accountList = Settings.Config.Main.Advanced.AccountList; + foreach (var account in accountList) + builder.Suggest(account.Key); + return builder.BuildFuture(); + } + } +} \ No newline at end of file diff --git a/MinecraftClient/CommandHandler/ArgumentType/AutoCraftRecipeNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/AutoCraftRecipeNameArgumentType.cs new file mode 100644 index 00000000..0d2bc0eb --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/AutoCraftRecipeNameArgumentType.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class AutoCraftRecipeNameArgumentType : ArgumentType + { + public override string Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadString(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + var recipeList = Settings.Config.ChatBot.AutoCraft.Recipes; + foreach (var recipe in recipeList) + builder.Suggest(recipe.Name); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs new file mode 100644 index 00000000..0681ae0d --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class BotNameArgumentType : ArgumentType + { + public override string Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadString(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + if (client != null) + { + var botList = client.GetLoadedChatBots(); + foreach (var bot in botList) + builder.Suggest(bot.GetType().Name); + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs new file mode 100644 index 00000000..9ba6f999 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Exceptions; +using Brigadier.NET.Suggestion; +using MinecraftClient.Mapping; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class EntityTypeArgumentType : ArgumentType + { + public override EntityType Parse(IStringReader reader) + { + reader.SkipWhitespace(); + string entity = reader.ReadString(); + if (Enum.TryParse(entity, true, out EntityType entityType)) + return entityType; + else + throw CommandSyntaxException.BuiltInExceptions.LiteralIncorrect().CreateWithContext(reader, entity); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + foreach (var result in Enum.GetNames(typeof(EntityType))) + builder.Suggest(result); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/FarmerCropTypeArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/FarmerCropTypeArgumentType.cs new file mode 100644 index 00000000..16ecdad4 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/FarmerCropTypeArgumentType.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Exceptions; +using Brigadier.NET.Suggestion; +using static MinecraftClient.ChatBots.Farmer; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class FarmerCropTypeArgumentType : ArgumentType + { + public override CropType Parse(IStringReader reader) + { + reader.SkipWhitespace(); + string inputStr = reader.ReadString(); + if (Enum.TryParse(inputStr, true, out CropType cropType)) + return cropType; + else + throw CommandSyntaxException.BuiltInExceptions.LiteralIncorrect().CreateWithContext(reader, inputStr); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + foreach (var result in Enum.GetNames(typeof(CropType))) + builder.Suggest(result); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs new file mode 100644 index 00000000..89616af6 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Exceptions; +using Brigadier.NET.Suggestion; +using MinecraftClient.Inventory; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class InventoryActionArgumentType : ArgumentType + { + private WindowActionType[] SupportActions = new WindowActionType[] + { + WindowActionType.LeftClick, + WindowActionType.RightClick, + WindowActionType.MiddleClick, + WindowActionType.ShiftClick, + }; + + public override WindowActionType Parse(IStringReader reader) + { + reader.SkipWhitespace(); + string inputStr = reader.ReadString(); + foreach (var action in SupportActions) + { + string actionStr = action.ToString(); + if (string.Compare(inputStr, actionStr, true) == 0) + return action; + } + throw CommandSyntaxException.BuiltInExceptions.LiteralIncorrect().CreateWithContext(reader, inputStr); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + foreach (var action in SupportActions) + builder.Suggest(action.ToString()); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs new file mode 100644 index 00000000..c697b493 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class InventoryIdArgumentType : ArgumentType + { + public override int Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadInt(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + if (client != null) + { + var invList = client.GetInventories(); + foreach (var inv in invList) + { + string invName = inv.Key.ToString(); + if (invName.StartsWith(builder.RemainingLowerCase, StringComparison.InvariantCultureIgnoreCase)) + builder.Suggest(invName); + } + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs new file mode 100644 index 00000000..28ab93a3 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Exceptions; +using Brigadier.NET.Suggestion; +using MinecraftClient.Inventory; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class ItemTypeArgumentType : ArgumentType + { + public override ItemType Parse(IStringReader reader) + { + reader.SkipWhitespace(); + string entity = reader.ReadString(); + if (Enum.TryParse(entity, true, out ItemType itemType)) + return itemType; + else + throw CommandSyntaxException.BuiltInExceptions.LiteralIncorrect().CreateWithContext(reader, entity); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + foreach (var result in Enum.GetNames(typeof(ItemType))) + builder.Suggest(result); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs new file mode 100644 index 00000000..79f51d62 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs @@ -0,0 +1,99 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; +using MinecraftClient.Mapping; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class LocationArgumentType : ArgumentType + { + public override Location Parse(IStringReader reader) + { + int[] status = new int[3]; + double[] coords = new double[3]; + for (int i = 0; i < 3; ++i) + { + reader.SkipWhitespace(); + if (reader.Peek() == '~' || reader.Peek() == '~') + { + status[i] = 1; + reader.Next(); + if (reader.CanRead()) + { + char next = reader.Peek(); + if (char.IsDigit(next) || next == '.' || next == '-') + coords[i] = reader.ReadDouble(); + else if (next == '+') + { + reader.Next(); + coords[i] = reader.ReadDouble(); + } + else coords[i] = 0; + } + else coords[i] = 0; + } + else + { + status[i] = 0; + coords[i] = reader.ReadDouble(); + } + } + + return new Location(coords[0], coords[1], coords[2], (byte)(status[0] | status[1] << 1 | status[2] << 2)); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + string[] args = builder.Remaining.Split(' ', StringSplitOptions.TrimEntries); + if (args.Length == 0 || (args.Length == 1 && string.IsNullOrWhiteSpace(args[0]))) + { + if (client != null) + { + Location current = client.GetCurrentLocation(); + builder.Suggest(string.Format("{0:0.00}", current.X)); + builder.Suggest(string.Format("{0:0.00} {1:0.00}", current.X, current.Y)); + builder.Suggest(string.Format("{0:0.00} {1:0.00} {2:0.00}", current.X, current.Y, current.Z)); + } + else + { + builder.Suggest("~"); + builder.Suggest("~ ~"); + builder.Suggest("~ ~ ~"); + } + } + else if (args.Length == 1 || (args.Length == 2 && string.IsNullOrWhiteSpace(args[1]))) + { + string add = args.Length == 1 ? " " : string.Empty; + if (client != null) + { + Location current = client.GetCurrentLocation(); + builder.Suggest(string.Format("{0}{2}{1:0.00}", builder.Remaining, current.Y, add)); + builder.Suggest(string.Format("{0}{3}{1:0.00} {2:0.00}", builder.Remaining, current.Y, current.Z, add)); + } + else + { + builder.Suggest(builder.Remaining + add + "~"); + builder.Suggest(builder.Remaining + add + "~ ~"); + } + } + else if (args.Length == 2 || (args.Length == 3 && string.IsNullOrWhiteSpace(args[2]))) + { + string add = args.Length == 2 ? " " : string.Empty; + if (client != null) + { + Location current = client.GetCurrentLocation(); + builder.Suggest(string.Format("{0}{2}{1:0.00}", builder.Remaining, current.Z, add)); + } + else + { + builder.Suggest(builder.Remaining + add + "~"); + } + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs new file mode 100644 index 00000000..55c05020 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; +using MinecraftClient.ChatBots; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class MapBotMapIdArgumentType : ArgumentType + { + public override int Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadInt(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + if (client != null) + { + var bot = (Map?)client.GetLoadedChatBots().Find(bot => bot.GetType().Name == "Map"); + if (bot != null) + { + var mapList = bot.cachedMaps; + foreach (var map in mapList) + { + string mapName = map.Key.ToString(); + if (mapName.StartsWith(builder.RemainingLowerCase, StringComparison.InvariantCultureIgnoreCase)) + builder.Suggest(mapName); + } + } + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs new file mode 100644 index 00000000..d8b88093 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs @@ -0,0 +1,36 @@ +using System.Linq; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; +using MinecraftClient.Mapping; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class PlayerNameArgumentType : ArgumentType + { + public override string Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadString(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + if (client != null) + { + var entityList = client.GetEntities().Values.ToList(); + foreach (var entity in entityList) + { + if (entity.Type != EntityType.Player || string.IsNullOrWhiteSpace(entity.Name)) + continue; + builder.Suggest(entity.Name); + } + builder.Suggest(client.GetUsername()); + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/ServerNickArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/ServerNickArgumentType.cs new file mode 100644 index 00000000..ae520f7d --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/ServerNickArgumentType.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class ServerNickArgumentType : ArgumentType + { + public override string Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadString(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + var serverList = Settings.Config.Main.Advanced.ServerList; + foreach (var server in serverList) + builder.Suggest(server.Key); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/TupleArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/TupleArgumentType.cs new file mode 100644 index 00000000..55157d99 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/TupleArgumentType.cs @@ -0,0 +1,18 @@ +using System; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class TupleArgumentType : ArgumentType> + { + public override Tuple Parse(IStringReader reader) + { + reader.SkipWhitespace(); + int int1 = reader.ReadInt(); + reader.SkipWhitespace(); + int int2 = reader.ReadInt(); + return new(int1, int2); + } + } +} diff --git a/MinecraftClient/CommandHandler/CmdResult.cs b/MinecraftClient/CommandHandler/CmdResult.cs new file mode 100644 index 00000000..b5477763 --- /dev/null +++ b/MinecraftClient/CommandHandler/CmdResult.cs @@ -0,0 +1,96 @@ +using System; + +namespace MinecraftClient.CommandHandler +{ + public class CmdResult + { + public static readonly CmdResult Empty = new(); + + internal static McClient? client; + + public enum Status + { + NotRun = int.MinValue, + FailChunkNotLoad = -4, + FailNeedEntity = -3, + FailNeedInventory = -2, + FailNeedTerrain = -1, + Fail = 0, + Done = 1, + } + + public CmdResult() + { + this.status = Status.NotRun; + this.result = null; + } + + public Status status; + + public string? result; + + public int SetAndReturn(Status status) + { + this.status = status; + this.result = status switch + { +#pragma warning disable format // @formatter:off + Status.NotRun => null, + Status.FailChunkNotLoad => null, + Status.FailNeedEntity => Translations.extra_entity_required, + Status.FailNeedInventory => Translations.extra_inventory_required, + Status.FailNeedTerrain => Translations.extra_terrainandmovement_required, + Status.Fail => Translations.general_fail, + Status.Done => null, + _ => null, +#pragma warning restore format // @formatter:on + }; + return Convert.ToInt32(this.status); + } + + public int SetAndReturn(Status status, string? result) + { + this.status = status; + this.result = result; + return Convert.ToInt32(this.status); + } + + public int SetAndReturn(int code, string? result) + { + this.status = (Status)Enum.ToObject(typeof(Status), code); + if (!Enum.IsDefined(typeof(Status), status)) + throw new InvalidOperationException($"{code} is not a legal return value."); + this.result = result; + return code; + } + + public int SetAndReturn(bool result) + { + this.status = result ? Status.Done : Status.Fail; + this.result = result ? Translations.general_done : Translations.general_fail; + return Convert.ToInt32(this.status); + } + + public int SetAndReturn(string? result) + { + this.status = Status.Done; + this.result = result; + return Convert.ToInt32(this.status); + } + + public int SetAndReturn(string? resultstr, bool result) + { + this.status = result ? Status.Done : Status.Fail; + this.result = resultstr; + return Convert.ToInt32(this.status); + } + + public override string ToString() + { + if (result != null) + return result; + else + return status.ToString(); + } + } +} diff --git a/MinecraftClient/CommandHandler/MccArguments.cs b/MinecraftClient/CommandHandler/MccArguments.cs new file mode 100644 index 00000000..ef6fbf8a --- /dev/null +++ b/MinecraftClient/CommandHandler/MccArguments.cs @@ -0,0 +1,104 @@ +using System; +using Brigadier.NET.Context; +using MinecraftClient.CommandHandler.ArgumentType; + +namespace MinecraftClient.CommandHandler +{ + public static class MccArguments + { + public static LocationArgumentType Location() + { + return new LocationArgumentType(); + } + + public static Mapping.Location GetLocation(CommandContext context, string name) + { + return context.GetArgument(name); + } + + public static TupleArgumentType Tuple() + { + return new TupleArgumentType(); + } + + public static Tuple GetTuple(CommandContext context, string name) + { + return context.GetArgument>(name); + } + + public static EntityTypeArgumentType EntityType() + { + return new EntityTypeArgumentType(); + } + + public static Mapping.EntityType GetEntityType(CommandContext context, string name) + { + return context.GetArgument(name); + } + + public static ItemTypeArgumentType ItemType() + { + return new ItemTypeArgumentType(); + } + + public static Inventory.ItemType GetItemType(CommandContext context, string name) + { + return context.GetArgument(name); + } + + public static BotNameArgumentType BotName() + { + return new BotNameArgumentType(); + } + + public static ServerNickArgumentType ServerNick() + { + return new ServerNickArgumentType(); + } + + public static AccountNickArgumentType AccountNick() + { + return new AccountNickArgumentType(); + } + + public static InventoryIdArgumentType InventoryId() + { + return new InventoryIdArgumentType(); + } + + public static InventoryActionArgumentType InventoryAction() + { + return new InventoryActionArgumentType(); + } + + public static Inventory.WindowActionType GetInventoryAction(CommandContext context, string name) + { + return context.GetArgument(name); + } + + public static AutoCraftRecipeNameArgumentType AutoCraftRecipeName() + { + return new AutoCraftRecipeNameArgumentType(); + } + + public static FarmerCropTypeArgumentType FarmerCropType() + { + return new FarmerCropTypeArgumentType(); + } + + public static ChatBots.Farmer.CropType GetFarmerCropType(CommandContext context, string name) + { + return context.GetArgument(name); + } + + public static PlayerNameArgumentType PlayerName() + { + return new PlayerNameArgumentType(); + } + + public static MapBotMapIdArgumentType MapBotMapId() + { + return new MapBotMapIdArgumentType(); + } + } +} diff --git a/MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs b/MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs new file mode 100644 index 00000000..341fa4ae --- /dev/null +++ b/MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs @@ -0,0 +1,36 @@ +using System.Text; +using Brigadier.NET; + +namespace MinecraftClient.CommandHandler.Patch +{ + public static class CommandDispatcherExtensions + { + /** + * This method unregisteres a previously declared command + * + * @param The name of the command to remove + */ + public static void Unregister(this CommandDispatcher commandDispatcher, string commandname) + { + commandDispatcher.GetRoot().RemoveChild(commandname); + } + + public static string GetAllUsageString(this CommandDispatcher commandDispatcher, string commandName, bool restricted) + { + char cmdChar = Settings.Config.Main.Advanced.InternalCmdChar.ToChar(); + string[] usages = commandDispatcher.GetAllUsage(commandDispatcher.GetRoot().GetChild(commandName), new(), restricted); + StringBuilder sb = new(); + sb.AppendLine("All Usages:"); + foreach (var usage in usages) + { + sb.Append(cmdChar).Append(commandName).Append(' '); + if (usage.Length > 0 && usage[0] == '_') + sb.AppendLine(usage.Replace("_help -> ", $"_help -> {cmdChar}help ")); + else + sb.AppendLine(usage); + } + sb.Remove(sb.Length - 1, 1); + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/MinecraftClient/CommandHandler/Patch/CommandNodeExtensions.cs b/MinecraftClient/CommandHandler/Patch/CommandNodeExtensions.cs new file mode 100644 index 00000000..23e2a87f --- /dev/null +++ b/MinecraftClient/CommandHandler/Patch/CommandNodeExtensions.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Reflection; +using Brigadier.NET.Tree; + +namespace MinecraftClient.CommandHandler.Patch +{ + public static class CommandNodeExtensions + { + public static void RemoveChild(this CommandNode commandNode, string name) + { + var children = (IDictionary>) + typeof(CommandNode) + .GetField("_children", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)! + .GetValue(commandNode)!; + var literals = (IDictionary>) + typeof(CommandNode) + .GetField("_literals", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)! + .GetValue(commandNode)!; + var arguments = (IDictionary>) + typeof(CommandNode) + .GetField("_arguments", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)! + .GetValue(commandNode)!; + + children.Remove(name); + literals.Remove(name); + } + } +} \ No newline at end of file diff --git a/MinecraftClient/Commands/Animation.cs b/MinecraftClient/Commands/Animation.cs index 54efe765..41f7affe 100644 --- a/MinecraftClient/Commands/Animation.cs +++ b/MinecraftClient/Commands/Animation.cs @@ -1,7 +1,6 @@ -using System; -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -11,76 +10,44 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "animation "; } } public override string CmdDesc { get { return Translations.cmd_animation_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { - dispatcher.Register(l => - l.Literal("help").Then(l => - l.Literal(CmdName).Executes(c => { - LogUsage(handler.Log); - return 1; - }) + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("mainhand") + .Executes(r => GetUsage(r.Source, "mainhand"))) + .Then(l => l.Literal("offhand") + .Executes(r => GetUsage(r.Source, "offhand"))) ) ); - dispatcher.Register(l => - l.Literal(CmdName).Then(l => - l.Literal("mainhand") - .Executes(c => { - return LogExecuteResult(handler.Log, handler.DoAnimation(0)); - }) - ) - ); - dispatcher.Register(l => - l.Literal(CmdName).Then(l => - l.Literal("0") - .Redirect(dispatcher.GetRoot().GetChild(CmdName).GetChild("mainhand")) - ) - ); - - dispatcher.Register(l => - l.Literal(CmdName).Then(l => - l.Literal("offhand") - .Executes(c => { - return LogExecuteResult(handler.Log, handler.DoAnimation(1)); - }) - ) - ); - dispatcher.Register(l => - l.Literal(CmdName).Then(l => - l.Literal("1") - .Redirect(dispatcher.GetRoot().GetChild(CmdName).GetChild("offhand")) - ) + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoAnimation(r.Source, handler, mainhand: true)) + .Then(l => l.Literal("mainhand") + .Executes(r => DoAnimation(r.Source, handler, mainhand: true))) + .Then(l => l.Literal("offhand") + .Executes(r => DoAnimation(r.Source, handler, mainhand: false))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length > 0) - { - if (args[0] == "mainhand" || args[0] == "0") - { - handler.DoAnimation(0); - return Translations.general_done; - } - else if (args[0] == "offhand" || args[0] == "1") - { - handler.DoAnimation(1); - return Translations.general_done; - } - else - { - return GetCmdDescTranslated(); - } - } - else - { - return GetCmdDescTranslated(); - } - } - else return GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + "mainhand" => GetCmdDescTranslated(), + "offhand" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private static int DoAnimation(CmdResult r, McClient handler, bool mainhand) + { + return r.SetAndReturn(handler.DoAnimation(mainhand ? 1 : 0)); } } } diff --git a/MinecraftClient/Commands/Bed.cs b/MinecraftClient/Commands/Bed.cs index 6d7e774c..fa6e6958 100644 --- a/MinecraftClient/Commands/Bed.cs +++ b/MinecraftClient/Commands/Bed.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading.Tasks; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -14,142 +16,162 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "bed leave|sleep |sleep "; } } public override string CmdDesc { get { return Translations.cmd_bed_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("leave") + .Executes(r => GetUsage(r.Source, "leave"))) + .Then(l => l.Literal("sleep") + .Executes(r => GetUsage(r.Source, "sleep"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Literal("leave") + .Executes(r => DoLeaveBed(r.Source, handler))) + .Then(l => l.Literal("sleep") + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => DoSleepBedWithLocation(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Argument("Radius", Arguments.Double()) + .Executes(r => DoSleepBedWithRadius(r.Source, handler, Arguments.GetDouble(r, "Radius"))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - string[] args = GetArgs(command); - - if (args.Length >= 1) + return r.SetAndReturn(cmd switch { - string subcommand = args[0].ToLower().Trim(); +#pragma warning disable format // @formatter:off + "leave" => GetCmdDescTranslated(), + "sleep" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (subcommand.Equals("leave") || subcommand.Equals("l")) + private static int DoLeaveBed(CmdResult r, McClient handler) + { + return r.SetAndReturn(Translations.cmd_bed_leaving, handler.SendEntityAction(Protocol.EntityActionType.LeaveBed)); + } + + private static int DoSleepBedWithRadius(CmdResult r, McClient handler, double radius) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + handler.Log.Info(string.Format(Translations.cmd_bed_searching, radius)); + + Location current = handler.GetCurrentLocation(); + Location bedLocation = current; + + Material[] bedMaterialList = new Material[] + { + Material.BlackBed, + Material.BlueBed, + Material.BrownBed, + Material.CyanBed, + Material.GrayBed, + Material.GreenBed, + Material.LightBlueBed, + Material.LightGrayBed, + Material.LimeBed, + Material.MagentaBed, + Material.OrangeBed, + Material.PinkBed, + Material.PurpleBed, + Material.RedBed, + Material.WhiteBed, + Material.YellowBed + }; + + bool found = false; + foreach (Material material in bedMaterialList) + { + List beds = handler.GetWorld().FindBlock(current, material, radius); + + if (beds.Count > 0) { - handler.SendEntityAction(Protocol.EntityActionType.LeaveBed); - return Translations.cmd_bed_leaving; - } - - if (subcommand.Equals("sleep") || subcommand.Equals("s")) - { - if (!handler.GetTerrainEnabled()) - return Translations.error_terrain_not_enabled; - - if (args.Length == 2) - { - if (!int.TryParse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture, out int radius)) - return CmdUsage; - - handler.GetLogger().Info(string.Format(Translations.cmd_bed_searching, radius)); - - Location current = handler.GetCurrentLocation(); - Location bedLocation = current; - - Material[] bedMaterialList = new Material[]{ - Material.BlackBed, - Material.BlueBed, - Material.BrownBed, - Material.CyanBed, - Material.GrayBed, - Material.GreenBed, - Material.LightBlueBed, - Material.LightGrayBed, - Material.LimeBed, - Material.MagentaBed, - Material.OrangeBed, - Material.PinkBed, - Material.PurpleBed, - Material.RedBed, - Material.WhiteBed, - Material.YellowBed - }; - - bool found = false; - foreach (Material material in bedMaterialList) - { - List beds = handler.GetWorld().FindBlock(current, material, radius); - - if (beds.Count > 0) - { - found = true; - bedLocation = beds.First(); - break; - } - } - - if (!found) - return Translations.cmd_bed_bed_not_found; - - handler.Log.Info(string.Format(Translations.cmd_bed_found_a_bed_at, bedLocation.X, bedLocation.Y, bedLocation.Z)); - - if (!Movement.CheckChunkLoading(handler.GetWorld(), current, bedLocation)) - return string.Format(Translations.cmd_move_chunk_not_loaded, bedLocation.X, bedLocation.Y, bedLocation.Z); - - if (handler.MoveTo(bedLocation)) - { - Task.Factory.StartNew(() => - { - bool atTheLocation = false; - DateTime timeout = DateTime.Now.AddSeconds(60); - - while (DateTime.Now < timeout) - { - if (handler.GetCurrentLocation() == bedLocation || handler.GetCurrentLocation().Distance(bedLocation) <= 2.0) - { - atTheLocation = true; - break; - } - } - - if (!atTheLocation) - { - handler.Log.Info(string.Format(Translations.cmd_bed_failed_to_reach_in_time, bedLocation.X, bedLocation.Y, bedLocation.Z)); - return; - } - - handler.Log.Info(string.Format(Translations.cmd_bed_moving, bedLocation.X, bedLocation.Y, bedLocation.Z)); - - bool res = handler.PlaceBlock(bedLocation, Direction.Down); - - handler.Log.Info(string.Format( - Translations.cmd_bed_trying_to_use, - bedLocation.X, - bedLocation.Y, - bedLocation.Z, - res ? Translations.cmd_bed_in : Translations.cmd_bed_not_in - )); - }); - - return ""; - } - - return Translations.cmd_bed_cant_reach_safely; - } - - if (args.Length >= 3) - { - Location block = Location.Parse(handler.GetCurrentLocation(), args[1], args[2], args[3]).ToFloor(); - Location blockCenter = block.ToCenter(); - - if (!handler.GetWorld().GetBlock(block).Type.IsBed()) - return string.Format(Translations.cmd_bed_not_a_bed, blockCenter.X, blockCenter.Y, blockCenter.Z); - - bool res = handler.PlaceBlock(block, Direction.Down); - - return string.Format( - Translations.cmd_bed_trying_to_use, - blockCenter.X, - blockCenter.Y, - blockCenter.Z, - res ? Translations.cmd_bed_in : Translations.cmd_bed_not_in - ); - } + found = true; + bedLocation = beds.First(); + break; } } - return CmdUsage; + if (!found) + return r.SetAndReturn(Status.Fail, Translations.cmd_bed_bed_not_found); + + handler.Log.Info(string.Format(Translations.cmd_bed_found_a_bed_at, bedLocation.X, bedLocation.Y, bedLocation.Z)); + + if (!Movement.CheckChunkLoading(handler.GetWorld(), current, bedLocation)) + return r.SetAndReturn(Status.FailChunkNotLoad, + string.Format(Translations.cmd_move_chunk_not_loaded, bedLocation.X, bedLocation.Y, bedLocation.Z)); + + if (handler.MoveTo(bedLocation)) + { + Task.Factory.StartNew(() => + { + bool atTheLocation = false; + DateTime timeout = DateTime.Now.AddSeconds(60); + + while (DateTime.Now < timeout) + { + if (handler.GetCurrentLocation() == bedLocation || handler.GetCurrentLocation().Distance(bedLocation) <= 2.0) + { + atTheLocation = true; + break; + } + } + + if (!atTheLocation) + { + handler.Log.Info(string.Format(Translations.cmd_bed_failed_to_reach_in_time, bedLocation.X, bedLocation.Y, bedLocation.Z)); + return; + } + + handler.Log.Info(string.Format(Translations.cmd_bed_moving, bedLocation.X, bedLocation.Y, bedLocation.Z)); + + bool res = handler.PlaceBlock(bedLocation, Direction.Down); + + handler.Log.Info(string.Format( + Translations.cmd_bed_trying_to_use, + bedLocation.X, + bedLocation.Y, + bedLocation.Z, + res ? Translations.cmd_bed_in : Translations.cmd_bed_not_in + )); + }); + + return r.SetAndReturn(Status.Done); + } + else + { + return r.SetAndReturn(Status.Fail, Translations.cmd_bed_cant_reach_safely); + } + } + + private static int DoSleepBedWithLocation(CmdResult r, McClient handler, Location block) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + block.ToAbsolute(handler.GetCurrentLocation()); + Location blockCenter = block.ToCenter(); + + if (!handler.GetWorld().GetBlock(block).Type.IsBed()) + return r.SetAndReturn(Status.Fail, + string.Format(Translations.cmd_bed_not_a_bed, blockCenter.X, blockCenter.Y, blockCenter.Z)); + + return r.SetAndReturn(Status.Done, string.Format( + Translations.cmd_bed_trying_to_use, + blockCenter.X, + blockCenter.Y, + blockCenter.Z, + handler.PlaceBlock(block, Direction.Down) ? Translations.cmd_bed_in : Translations.cmd_bed_not_in + )); } } } \ No newline at end of file diff --git a/MinecraftClient/Commands/BlockInfo.cs b/MinecraftClient/Commands/BlockInfo.cs index d2165eee..dcc82d61 100644 --- a/MinecraftClient/Commands/BlockInfo.cs +++ b/MinecraftClient/Commands/BlockInfo.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; -using System.Text; +using System.Text; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -11,40 +13,60 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "blockinfo [-s]"; } } public override string CmdDesc { get { return Translations.cmd_blockinfo_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("-s") + .Executes(r => GetUsage(r.Source, "-s"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => LogBlockInfo(r.Source, handler, handler.GetCurrentLocation(), false)) + .Then(l => l.Literal("-s") + .Executes(r => LogBlockInfo(r.Source, handler, handler.GetCurrentLocation(), true))) + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => LogBlockInfo(r.Source, handler, MccArguments.GetLocation(r, "Location"), false)) + .Then(l => l.Literal("-s") + .Executes(r => LogBlockInfo(r.Source, handler, MccArguments.GetLocation(r, "Location"), true)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + "-s" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private static int LogBlockInfo(CmdResult r, McClient handler, Location targetBlock, bool reportSurrounding) { if (!handler.GetTerrainEnabled()) - return Translations.error_terrain_not_enabled; + return r.SetAndReturn(Status.FailNeedTerrain); - string[] args = GetArgs(command); - - if (args.Length < 3) - return CmdUsage; - - bool reportSurrounding = args.Length >= 4 && args[3].Equals("-s", System.StringComparison.OrdinalIgnoreCase); - - Location current = handler.GetCurrentLocation(); - Location targetBlockLocation = Location.Parse(current, args[0], args[1], args[2]); - - Block block = handler.GetWorld().GetBlock(targetBlockLocation); + targetBlock.ToAbsolute(handler.GetCurrentLocation()); + Block block = handler.GetWorld().GetBlock(targetBlock); handler.Log.Info($"{Translations.cmd_blockinfo_BlockType}: {block.GetTypeString()}"); - if (reportSurrounding) { StringBuilder sb = new(); sb.AppendLine($"{Translations.cmd_blockinfo_BlocksAround}:"); - Block blockXPositive = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X + 1, targetBlockLocation.Y, targetBlockLocation.Z)); - Block blockXNegative = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X - 1, targetBlockLocation.Y, targetBlockLocation.Z)); - Block blockYPositive = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X, targetBlockLocation.Y + 1, targetBlockLocation.Z)); - Block blockYNegative = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X, targetBlockLocation.Y - 1, targetBlockLocation.Z)); - Block blockZPositive = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X, targetBlockLocation.Y, targetBlockLocation.Z + 1)); - Block blockZNegative = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X, targetBlockLocation.Y, targetBlockLocation.Z - 1)); + Block blockXPositive = handler.GetWorld().GetBlock(new Location(targetBlock.X + 1, targetBlock.Y, targetBlock.Z)); + Block blockXNegative = handler.GetWorld().GetBlock(new Location(targetBlock.X - 1, targetBlock.Y, targetBlock.Z)); + Block blockYPositive = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y + 1, targetBlock.Z)); + Block blockYNegative = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y - 1, targetBlock.Z)); + Block blockZPositive = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y, targetBlock.Z + 1)); + Block blockZNegative = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y, targetBlock.Z - 1)); sb.AppendLine($"[X {Translations.cmd_blockinfo_Positive}] {Translations.cmd_blockinfo_BlockType}: {blockXPositive.GetTypeString()}"); sb.AppendLine($"[X {Translations.cmd_blockinfo_Negative}] {Translations.cmd_blockinfo_BlockType}: {blockXNegative.GetTypeString()}"); @@ -61,9 +83,7 @@ namespace MinecraftClient.Commands handler.Log.Info(sb.ToString()); } - - - return ""; + return r.SetAndReturn(Status.Done); } } } diff --git a/MinecraftClient/Commands/Bots.cs b/MinecraftClient/Commands/Bots.cs index 9d4233f6..58057552 100644 --- a/MinecraftClient/Commands/Bots.cs +++ b/MinecraftClient/Commands/Bots.cs @@ -1,7 +1,9 @@ using System; -using System.Collections.Generic; using System.Text; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; namespace MinecraftClient.Commands { @@ -11,69 +13,82 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "bots [list|unload ]"; } } public override string CmdDesc { get { return Translations.cmd_bots_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("list") + .Executes(r => GetUsage(r.Source, "list"))) + .Then(l => l.Literal("unload") + .Executes(r => GetUsage(r.Source, "unload"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoListBot(r.Source, handler)) + .Then(l => l.Literal("list") + .Executes(r => DoListBot(r.Source, handler))) + .Then(l => l.Literal("unload") + .Then(l => l.Argument("BotName", MccArguments.BotName()) + .Executes(r => DoUnloadBot(r.Source, handler, Arguments.GetString(r, "BotName"))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); +#pragma warning disable format // @formatter:off + "list" => GetCmdDescTranslated(), + "unload" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (args.Length == 1) - { - if (args[0].Equals("list", StringComparison.OrdinalIgnoreCase)) - { - StringBuilder sb = new(); + private int DoListBot(CmdResult r, McClient handler) + { + int length = handler.GetLoadedChatBots().Count; + if (length == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_bots_noloaded); - int length = handler.GetLoadedChatBots().Count; - - if (length == 0) - return Translations.cmd_bots_noloaded; - - for (int i = 0; i < length; i++) - { - sb.Append(handler.GetLoadedChatBots()[i].GetType().Name); - - if (i != length - 1) - sb.Append(" ,"); - - } - - return Translations.cmd_bots_list + ": " + sb.ToString(); - } - - } - else if (args.Length == 2) - { - if (args[0].Equals("unload", StringComparison.OrdinalIgnoreCase)) - { - string botName = args[1].Trim(); - - if (botName.ToLower().Equals("all", StringComparison.OrdinalIgnoreCase)) - { - if (handler.GetLoadedChatBots().Count == 0) - return Translations.cmd_bots_noloaded; - - handler.UnloadAllBots(); - return Translations.cmd_bots_unloaded_all; - } - else - { - ChatBot? bot = handler.GetLoadedChatBots().Find(bot => bot.GetType().Name.ToLower() == botName.ToLower()); - - if (bot == null) - return string.Format(Translations.cmd_bots_notfound, botName); - - handler.BotUnLoad(bot); - return string.Format(Translations.cmd_bots_unloaded, botName); - } - } - } + StringBuilder sb = new(); + for (int i = 0; i < length; i++) + { + sb.Append(handler.GetLoadedChatBots()[i].GetType().Name); + if (i != length - 1) + sb.Append(" ,"); } - return GetCmdDescTranslated(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_bots_list + ": " + sb.ToString()); + } + + private int DoUnloadBot(CmdResult r, McClient handler, string botName) + { + if (botName.ToLower().Equals("all", StringComparison.OrdinalIgnoreCase)) + { + if (handler.GetLoadedChatBots().Count == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_bots_noloaded); + else + { + handler.UnloadAllBots(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_bots_unloaded_all); + } + } + else + { + ChatBot? bot = handler.GetLoadedChatBots().Find(bot => bot.GetType().Name.ToLower() == botName.ToLower()); + if (bot == null) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_bots_notfound, botName)); + else + { + handler.BotUnLoad(bot); + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_bots_unloaded, botName)); + } + } } } } diff --git a/MinecraftClient/Commands/ChangeSlot.cs b/MinecraftClient/Commands/ChangeSlot.cs index ae74604f..4a565de8 100644 --- a/MinecraftClient/Commands/ChangeSlot.cs +++ b/MinecraftClient/Commands/ChangeSlot.cs @@ -1,6 +1,7 @@ -using System; -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -10,39 +11,41 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "changeslot <1-9>"; } } public override string CmdDesc { get { return Translations.cmd_changeSlot_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Slot", Arguments.Integer(1, 9)) + .Executes(r => DoChangeSlot(r.Source, handler, Arguments.GetInteger(r, "Slot")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoChangeSlot(CmdResult r, McClient handler, int slot) { if (!handler.GetInventoryEnabled()) - return Translations.extra_inventory_required; + return r.SetAndReturn(Status.FailNeedInventory); - if (HasArg(command)) - { - short slot; - try - { - slot = Convert.ToInt16(GetArg(command)); - } - catch (FormatException) - { - return Translations.cmd_changeSlot_nan; - } - if (slot >= 1 && slot <= 9) - { - if (handler.ChangeSlot(slot -= 1)) - { - return string.Format(Translations.cmd_changeSlot_changed, (slot += 1)); - } - else - { - return Translations.cmd_changeSlot_fail; - } - } - } - return GetCmdDescTranslated(); + if (handler.ChangeSlot((short)(slot - 1))) + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_changeSlot_changed, slot)); + else + return r.SetAndReturn(Status.Fail, Translations.cmd_changeSlot_fail); } } } diff --git a/MinecraftClient/Commands/Chunk.cs b/MinecraftClient/Commands/Chunk.cs index a84fea87..5d225db1 100644 --- a/MinecraftClient/Commands/Chunk.cs +++ b/MinecraftClient/Commands/Chunk.cs @@ -1,9 +1,10 @@ using System; -using System.Collections.Generic; -using System.Globalization; using System.Text; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -13,252 +14,267 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "chunk status [chunkX chunkZ|locationX locationY locationZ]"; } } public override string CmdDesc { get { return Translations.cmd_chunk_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("status") + .Executes(r => GetUsage(r.Source, "status"))) + .Then(l => l.Literal("_setloading") + .Executes(r => GetUsage(r.Source, "_setloading"))) + .Then(l => l.Literal("_setloaded") + .Executes(r => GetUsage(r.Source, "_setloaded"))) + .Then(l => l.Literal("_delete") + .Executes(r => GetUsage(r.Source, "_delete"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Literal("status") + .Executes(r => LogChunkStatus(r.Source, handler)) + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => LogChunkStatus(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Argument("Chunk", MccArguments.Tuple()) + .Executes(r => LogChunkStatus(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Then(l => l.Literal("_setloading") + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => DebugSetLoading(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Argument("Chunk", MccArguments.Tuple()) + .Executes(r => DebugSetLoading(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Then(l => l.Literal("_setloaded") + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => DebugSetLoaded(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Argument("Chunk", MccArguments.Tuple()) + .Executes(r => DebugSetLoaded(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Then(l => l.Literal("_delete") + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => DebugDelete(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Argument("Chunk", MccArguments.Tuple()) + .Executes(r => DebugDelete(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length > 0) +#pragma warning disable format // @formatter:off + "status" => GetCmdDescTranslated(), + "_setloading" => GetCmdDescTranslated(), + "_setloaded" => GetCmdDescTranslated(), + "_delete" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private static int LogChunkStatus(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + World world = handler.GetWorld(); + Location current = handler.GetCurrentLocation(); + if (pos.HasValue) + pos.Value.ToAbsolute(current); + + (int markChunkX, int markChunkZ) = markedChunkPos ?? + (pos.HasValue ? new(pos.Value.ChunkX, pos.Value.ChunkZ) : new(current.ChunkX, current.ChunkZ)); + + StringBuilder sb = new(); + + sb.Append(World.GetChunkLoadingStatus(handler.GetWorld())); + sb.Append('\n'); + + sb.AppendLine(string.Format(Translations.cmd_chunk_current, current, current.ChunkX, current.ChunkZ)); + if (markedChunkPos != null) + { + sb.Append(Translations.cmd_chunk_marked); + if (pos.HasValue) + sb.Append(string.Format("X:{0:0.00} Y:{1:0.00} Z:{2:0.00}, ", pos.Value.X, pos.Value.Y, pos.Value.Z)); + sb.AppendLine(string.Format(Translations.cmd_chunk_chunk_pos, markChunkX, markChunkZ)); ; + } + + int consoleHeight = Math.Max(Math.Max(Console.BufferHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 2, 25); + if (consoleHeight % 2 == 0) + --consoleHeight; + + int consoleWidth = Math.Max(Math.Max(Console.BufferWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2, 17); + if (consoleWidth % 2 == 0) + --consoleWidth; + + int startZ = current.ChunkZ - consoleHeight, endZ = current.ChunkZ + consoleHeight; + int startX = current.ChunkX - consoleWidth, endX = current.ChunkX + consoleWidth; + + int leftMost = endX, rightMost = startX, topMost = endZ, bottomMost = startZ; + for (int z = startZ; z <= endZ; z++) + { + for (int x = startX; x <= endX; ++x) { - if (args[0] == "status") + if (world[x, z] != null) { - World world = handler.GetWorld(); - Location current = handler.GetCurrentLocation(); - - Tuple? markedChunkPos = ParseChunkPos(args); - (int markChunkX, int markChunkZ) = markedChunkPos ?? (new(current.ChunkX, current.ChunkZ)); - - StringBuilder sb = new(); - - sb.Append(World.GetChunkLoadingStatus(handler.GetWorld())); - sb.Append('\n'); - - sb.AppendLine(string.Format(Translations.cmd_chunk_current, current, current.ChunkX, current.ChunkZ)); - if (markedChunkPos != null) - { - sb.Append(Translations.cmd_chunk_marked); - if (args.Length == 1 + 3) - sb.Append(String.Format("X:{0:0.00} Y:{1:0.00} Z:{2:0.00}, ", - double.Parse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture), - double.Parse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture), - double.Parse(args[3], NumberStyles.Any, CultureInfo.CurrentCulture))); - sb.AppendLine(string.Format(Translations.cmd_chunk_chunk_pos, markChunkX, markChunkZ));; - } - - int consoleHeight = Math.Max(Math.Max(Console.BufferHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 2, 25); - if (consoleHeight % 2 == 0) - --consoleHeight; - - int consoleWidth = Math.Max(Math.Max(Console.BufferWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2, 17); - if (consoleWidth % 2 == 0) - --consoleWidth; - - int startZ = current.ChunkZ - consoleHeight, endZ = current.ChunkZ + consoleHeight; - int startX = current.ChunkX - consoleWidth, endX = current.ChunkX + consoleWidth; - - int leftMost = endX, rightMost = startX, topMost = endZ, bottomMost = startZ; - for (int z = startZ; z <= endZ; z++) - { - for (int x = startX; x <= endX; ++x) - { - if (world[x, z] != null) - { - leftMost = Math.Min(leftMost, x); - rightMost = Math.Max(rightMost, x); - topMost = Math.Min(topMost, z); - bottomMost = Math.Max(bottomMost, z); - } - } - } - - // Include the player's location - topMost = Math.Min(topMost, current.ChunkZ); - bottomMost = Math.Max(bottomMost, current.ChunkZ); - leftMost = Math.Min(leftMost, current.ChunkX); - rightMost = Math.Max(rightMost, current.ChunkX); - - // Empty one row and one column each - --leftMost; ++rightMost; --topMost; ++bottomMost; - - // Resize according to limitations - if ((bottomMost - topMost + 1) > consoleHeight) - { - int delta = (bottomMost - topMost + 1) - consoleHeight; - if (bottomMost - (delta + 1) / 2 < current.ChunkZ + 1) - { - int bottomReduce = bottomMost - (current.ChunkZ + 1); - bottomMost -= bottomReduce; - topMost += delta - bottomReduce; - } - else if (topMost + delta / 2 > current.ChunkZ - 1) - { - int topAdd = topMost - (current.ChunkZ - 1); - topMost += topAdd; - bottomMost -= delta - topAdd; - } - else - { - topMost += delta / 2; - bottomMost -= (delta + 1) / 2; - } - } - if ((rightMost - leftMost + 1) > consoleWidth) - { - int delta = (rightMost - leftMost + 1) - consoleWidth; - if (rightMost - (delta + 1) / 2 < current.ChunkX + 1) - { - int rightReduce = rightMost - (current.ChunkX + 1); - rightMost -= rightReduce; - leftMost += delta - rightReduce; - } - else if (leftMost + delta / 2 > current.ChunkX - 1) - { - int leftAdd = leftMost - (current.ChunkX - 1); - leftMost += leftAdd; - rightMost -= delta - leftAdd; - } - else - { - leftMost += delta / 2; - rightMost -= (delta + 1) / 2; - } - } - - // Try to include the marker chunk - if (markedChunkPos != null && - (((Math.Max(bottomMost, markChunkZ) - Math.Min(topMost, markChunkZ) + 1) > consoleHeight) || - ((Math.Max(rightMost, markChunkX) - Math.Min(leftMost, markChunkX) + 1) > consoleWidth))) - sb.AppendLine(Translations.cmd_chunk_outside); - else - { - topMost = Math.Min(topMost, markChunkZ); - bottomMost = Math.Max(bottomMost, markChunkZ); - leftMost = Math.Min(leftMost, markChunkX); - rightMost = Math.Max(rightMost, markChunkX); - } - - - // \ud83d\udd33: 🔳, \ud83d\udfe8: 🟨, \ud83d\udfe9: 🟩, \u25A1: □, \u25A3: ▣, \u25A0: ■ - string[] chunkStatusStr = Settings.Config.Main.Advanced.EnableEmoji ? - new string[] { "\ud83d\udd33", "\ud83d\udfe8", "\ud83d\udfe9" } : new string[] { "\u25A1", "\u25A3", "\u25A0" }; - - // Output - for (int z = topMost; z <= bottomMost; ++z) - { - for (int x = leftMost; x <= rightMost; ++x) - { - if (z == current.ChunkZ && x == current.ChunkX) - sb.Append("§z"); // Player Location: background gray - else if (z == markChunkZ && x == markChunkX) - sb.Append("§w"); // Marked chunk: background red - - ChunkColumn? chunkColumn = world[x, z]; - if (chunkColumn == null) - sb.Append(chunkStatusStr[0]); - else if (chunkColumn.FullyLoaded) - sb.Append(chunkStatusStr[2]); - else - sb.Append(chunkStatusStr[1]); - - if ((z == current.ChunkZ && x == current.ChunkX) || (z == markChunkZ && x == markChunkX)) - sb.Append("§r"); // Reset background color - } - sb.Append('\n'); - } - - sb.AppendLine(string.Format(Translations.cmd_chunk_icon, "§z §r", "§w §r", chunkStatusStr[0], chunkStatusStr[1], chunkStatusStr[2])); - return sb.ToString(); + leftMost = Math.Min(leftMost, x); + rightMost = Math.Max(rightMost, x); + topMost = Math.Min(topMost, z); + bottomMost = Math.Max(bottomMost, z); } - else if (args[0] == "setloading") // For debugging - { - Tuple? chunkPos = ParseChunkPos(args); - if (chunkPos != null) - { - handler.Log.Info(Translations.cmd_chunk_for_debug); - World world = handler.GetWorld(); - (int chunkX, int chunkZ) = chunkPos; - ChunkColumn? chunkColumn = world[chunkX, chunkZ]; - if (chunkColumn != null) - chunkColumn.FullyLoaded = false; - return (chunkColumn == null) ? "Fail: chunk dosen't exist!" : - String.Format("Successfully marked chunk ({0}, {1}) as loading.", chunkX, chunkZ); - } - else - return GetCmdDescTranslated(); - } - else if (args[0] == "setloaded") // For debugging - { - Tuple? chunkPos = ParseChunkPos(args); - if (chunkPos != null) - { - handler.Log.Info(Translations.cmd_chunk_for_debug); - World world = handler.GetWorld(); - (int chunkX, int chunkZ) = chunkPos; - ChunkColumn? chunkColumn = world[chunkX, chunkZ]; - if (chunkColumn != null) - chunkColumn.FullyLoaded = true; - return (chunkColumn == null) ? "Fail: chunk dosen't exist!" : - String.Format("Successfully marked chunk ({0}, {1}) as loaded.", chunkX, chunkZ); - } - else - return GetCmdDescTranslated(); - } - else if (args[0] == "delete") // For debugging - { - Tuple? chunkPos = ParseChunkPos(args); - if (chunkPos != null) - { - handler.Log.Info(Translations.cmd_chunk_for_debug); - World world = handler.GetWorld(); - (int chunkX, int chunkZ) = chunkPos; - world[chunkX, chunkZ] = null; - return String.Format("Successfully deleted chunk ({0}, {1}).", chunkX, chunkZ); - } - else - return GetCmdDescTranslated(); - } - else - return GetCmdDescTranslated(); + } + } + + // Include the player's location + topMost = Math.Min(topMost, current.ChunkZ); + bottomMost = Math.Max(bottomMost, current.ChunkZ); + leftMost = Math.Min(leftMost, current.ChunkX); + rightMost = Math.Max(rightMost, current.ChunkX); + + // Empty one row and one column each + --leftMost; ++rightMost; --topMost; ++bottomMost; + + // Resize according to limitations + if ((bottomMost - topMost + 1) > consoleHeight) + { + int delta = (bottomMost - topMost + 1) - consoleHeight; + if (bottomMost - (delta + 1) / 2 < current.ChunkZ + 1) + { + int bottomReduce = bottomMost - (current.ChunkZ + 1); + bottomMost -= bottomReduce; + topMost += delta - bottomReduce; + } + else if (topMost + delta / 2 > current.ChunkZ - 1) + { + int topAdd = topMost - (current.ChunkZ - 1); + topMost += topAdd; + bottomMost -= delta - topAdd; } else - return GetCmdDescTranslated(); + { + topMost += delta / 2; + bottomMost -= (delta + 1) / 2; + } } + if ((rightMost - leftMost + 1) > consoleWidth) + { + int delta = (rightMost - leftMost + 1) - consoleWidth; + if (rightMost - (delta + 1) / 2 < current.ChunkX + 1) + { + int rightReduce = rightMost - (current.ChunkX + 1); + rightMost -= rightReduce; + leftMost += delta - rightReduce; + } + else if (leftMost + delta / 2 > current.ChunkX - 1) + { + int leftAdd = leftMost - (current.ChunkX - 1); + leftMost += leftAdd; + rightMost -= delta - leftAdd; + } + else + { + leftMost += delta / 2; + rightMost -= (delta + 1) / 2; + } + } + + // Try to include the marker chunk + if (markedChunkPos != null && + (((Math.Max(bottomMost, markChunkZ) - Math.Min(topMost, markChunkZ) + 1) > consoleHeight) || + ((Math.Max(rightMost, markChunkX) - Math.Min(leftMost, markChunkX) + 1) > consoleWidth))) + sb.AppendLine(Translations.cmd_chunk_outside); else - return GetCmdDescTranslated(); + { + topMost = Math.Min(topMost, markChunkZ); + bottomMost = Math.Max(bottomMost, markChunkZ); + leftMost = Math.Min(leftMost, markChunkX); + rightMost = Math.Max(rightMost, markChunkX); + } + + + // \ud83d\udd33: 🔳, \ud83d\udfe8: 🟨, \ud83d\udfe9: 🟩, \u25A1: □, \u25A3: ▣, \u25A0: ■ + string[] chunkStatusStr = Settings.Config.Main.Advanced.EnableEmoji ? + new string[] { "\ud83d\udd33", "\ud83d\udfe8", "\ud83d\udfe9" } : new string[] { "\u25A1", "\u25A3", "\u25A0" }; + + // Output + for (int z = topMost; z <= bottomMost; ++z) + { + for (int x = leftMost; x <= rightMost; ++x) + { + if (z == current.ChunkZ && x == current.ChunkX) + sb.Append("§z"); // Player Location: background gray + else if (z == markChunkZ && x == markChunkX) + sb.Append("§w"); // Marked chunk: background red + + ChunkColumn? chunkColumn = world[x, z]; + if (chunkColumn == null) + sb.Append(chunkStatusStr[0]); + else if (chunkColumn.FullyLoaded) + sb.Append(chunkStatusStr[2]); + else + sb.Append(chunkStatusStr[1]); + + if ((z == current.ChunkZ && x == current.ChunkX) || (z == markChunkZ && x == markChunkX)) + sb.Append("§r"); // Reset background color + } + sb.Append('\n'); + } + + sb.Append(string.Format(Translations.cmd_chunk_icon, "§z §r", "§w §r", chunkStatusStr[0], chunkStatusStr[1], chunkStatusStr[2])); + handler.Log.Info(sb.ToString()); + + return r.SetAndReturn(Status.Done); } - private static Tuple? ParseChunkPos(string[] args) + private static int DebugSetLoading(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) { - try - { - int chunkX, chunkZ; - if (args.Length == 1 + 3) - { - Location pos = new( - double.Parse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture), - double.Parse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture), - double.Parse(args[3], NumberStyles.Any, CultureInfo.CurrentCulture) - ); - chunkX = pos.ChunkX; - chunkZ = pos.ChunkZ; - } - else if (args.Length == 1 + 2) - { - chunkX = int.Parse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture); - chunkZ = int.Parse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture); - } - else - return null; - return new(chunkX, chunkZ); - } - catch (FormatException) - { - return null; - } + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + if (pos.HasValue) + pos.Value.ToAbsolute(handler.GetCurrentLocation()); + handler.Log.Info(Translations.cmd_chunk_for_debug); + (int chunkX, int chunkZ) = markedChunkPos ?? new(pos!.Value.ChunkX, pos!.Value.ChunkZ); + ChunkColumn? chunkColumn = handler.GetWorld()[chunkX, chunkZ]; + if (chunkColumn != null) + chunkColumn.FullyLoaded = false; + + if (chunkColumn == null) + return r.SetAndReturn(Status.Fail, "Fail: chunk dosen't exist!"); + else + return r.SetAndReturn(Status.Done, string.Format("Successfully marked chunk ({0}, {1}) as loading.", chunkX, chunkZ)); + } + + private static int DebugSetLoaded(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + if (pos.HasValue) + pos.Value.ToAbsolute(handler.GetCurrentLocation()); + handler.Log.Info(Translations.cmd_chunk_for_debug); + (int chunkX, int chunkZ) = markedChunkPos ?? new(pos!.Value.ChunkX, pos!.Value.ChunkZ); + ChunkColumn? chunkColumn = handler.GetWorld()[chunkX, chunkZ]; + if (chunkColumn != null) + chunkColumn.FullyLoaded = false; + + if (chunkColumn == null) + return r.SetAndReturn(Status.Fail, "Fail: chunk dosen't exist!"); + else + return r.SetAndReturn(Status.Done, string.Format("Successfully marked chunk ({0}, {1}) as loaded.", chunkX, chunkZ)); + } + + private static int DebugDelete(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + if (pos.HasValue) + pos.Value.ToAbsolute(handler.GetCurrentLocation()); + handler.Log.Info(Translations.cmd_chunk_for_debug); + (int chunkX, int chunkZ) = markedChunkPos ?? new(pos!.Value.ChunkX, pos!.Value.ChunkZ); + handler.GetWorld()[chunkX, chunkZ] = null; + + return r.SetAndReturn(Status.Done, string.Format("Successfully deleted chunk ({0}, {1}).", chunkX, chunkZ)); } } } diff --git a/MinecraftClient/Commands/CommandSource.cs b/MinecraftClient/Commands/CommandSource.cs deleted file mode 100644 index 89f1f1ff..00000000 --- a/MinecraftClient/Commands/CommandSource.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MinecraftClient.Commands -{ - public class CommandSource - { - public string UserName { get; set; } = string.Empty; - } -} diff --git a/MinecraftClient/Commands/Connect.cs b/MinecraftClient/Commands/Connect.cs index 9640eb9d..79892fc4 100644 --- a/MinecraftClient/Commands/Connect.cs +++ b/MinecraftClient/Commands/Connect.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -9,31 +11,65 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "connect [account]"; } } public override string CmdDesc { get { return Translations.cmd_connect_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("ServerNick", MccArguments.ServerNick()) + .Executes(r => DoConnect(r.Source, handler, Arguments.GetString(r, "ServerNick"), string.Empty)) + .Then(l => l.Argument("AccountNick", MccArguments.AccountNick()) + .Executes(r => DoConnect(r.Source, handler, Arguments.GetString(r, "ServerNick"), Arguments.GetString(r, "AccountNick"))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient? handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length > 1) - { - if (!Settings.Config.Main.Advanced.SetAccount(args[1])) - { - return string.Format(Translations.cmd_connect_unknown, args[1]); - } - } +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (Settings.Config.Main.SetServerIP(new Settings.MainConfigHealper.MainConfig.ServerInfoConfig(args[0]), true)) - { - Program.Restart(keepAccountAndServerSettings: true); - return ""; - } - else return string.Format(Translations.cmd_connect_invalid_ip, args[0]); + private int DoConnect(CmdResult r, McClient handler, string server, string account) + { + if (!string.IsNullOrWhiteSpace(account) && !Settings.Config.Main.Advanced.SetAccount(account)) + return r.SetAndReturn(Status.Fail, string.Format(Translations.cmd_connect_unknown, account)); + + if (Settings.Config.Main.SetServerIP(new Settings.MainConfigHealper.MainConfig.ServerInfoConfig(server), true)) + { + Program.Restart(keepAccountAndServerSettings: true); + return r.SetAndReturn(Status.Done); + } + else + { + return r.SetAndReturn(Status.Fail, string.Format(Translations.cmd_connect_invalid_ip, server)); + } + } + + internal static string DoConnect(string command) + { + string[] args = GetArgs(command); + if (args.Length > 1 && !Settings.Config.Main.Advanced.SetAccount(args[1])) + return string.Format(Translations.cmd_connect_unknown, args[1]); + + if (Settings.Config.Main.SetServerIP(new Settings.MainConfigHealper.MainConfig.ServerInfoConfig(args[0]), true)) + { + Program.Restart(keepAccountAndServerSettings: true); + return string.Empty; + } + else + { + return string.Format(Translations.cmd_connect_invalid_ip, args[0]); } - else return GetCmdDescTranslated(); } } } diff --git a/MinecraftClient/Commands/Debug.cs b/MinecraftClient/Commands/Debug.cs index 18122daa..acb6f7e9 100644 --- a/MinecraftClient/Commands/Debug.cs +++ b/MinecraftClient/Commands/Debug.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,20 +10,46 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "debug [on|off]"; } } public override string CmdDesc { get { return Translations.cmd_debug_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => SetDebugMode(r.Source, true)) + .Then(l => l.Literal("on") + .Executes(r => SetDebugMode(r.Source, false, true))) + .Then(l => l.Literal("off") + .Executes(r => SetDebugMode(r.Source, false, false))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) - Settings.Config.Logging.DebugMessages = (GetArg(command).ToLower() == "on"); - else + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int SetDebugMode(CmdResult r, bool flip, bool mode = false) + { + if (flip) Settings.Config.Logging.DebugMessages = !Settings.Config.Logging.DebugMessages; - if (Settings.Config.Logging.DebugMessages) - return Translations.cmd_debug_state_on; else - return Translations.cmd_debug_state_off; + Settings.Config.Logging.DebugMessages = mode; + + if (Settings.Config.Logging.DebugMessages) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_debug_state_on); + else + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_debug_state_off); } } } diff --git a/MinecraftClient/Commands/Dig.cs b/MinecraftClient/Commands/Dig.cs index bf68743c..2903a817 100644 --- a/MinecraftClient/Commands/Dig.cs +++ b/MinecraftClient/Commands/Dig.cs @@ -1,7 +1,9 @@ using System; -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -11,53 +13,68 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "dig "; } } public override string CmdDesc { get { return Translations.cmd_dig_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DigLookAt(r.Source, handler)) + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => DigAt(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DigAt(CmdResult r, McClient handler, Location blockToBreak) { if (!handler.GetTerrainEnabled()) - return Translations.extra_terrainandmovement_required; + return r.SetAndReturn(Status.FailNeedTerrain); - string[] args = GetArgs(command); - if (args.Length == 0) + Location current = handler.GetCurrentLocation(); + blockToBreak = blockToBreak.ToAbsolute(current); + if (blockToBreak.DistanceSquared(current.EyesLocation()) > 25) + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_too_far); + Block block = handler.GetWorld().GetBlock(blockToBreak); + if (block.Type == Material.Air) + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_no_block); + else if (handler.DigBlock(blockToBreak)) { - (bool hasBlock, Location blockLoc, Block block) = RaycastHelper.RaycastBlock(handler, 4.5, false); - if (!hasBlock) - return Translations.cmd_dig_too_far; - else if (block.Type == Material.Air) - return Translations.cmd_dig_no_block; - else if (handler.DigBlock(blockLoc, lookAtBlock: false)) - return string.Format(Translations.cmd_dig_dig, blockLoc.X, blockLoc.Y, blockLoc.Z, block.GetTypeString()); - else - return Translations.cmd_dig_fail; - } - else if (args.Length == 3) - { - try - { - Location current = handler.GetCurrentLocation(); - Location blockToBreak = Location.Parse(current.ToFloor(), args[0], args[1], args[2]); - if (blockToBreak.DistanceSquared(current.EyesLocation()) > 25) - return Translations.cmd_dig_too_far; - Block block = handler.GetWorld().GetBlock(blockToBreak); - if (block.Type == Material.Air) - return Translations.cmd_dig_no_block; - else if (handler.DigBlock(blockToBreak)) - { - blockToBreak = blockToBreak.ToCenter(); - return string.Format(Translations.cmd_dig_dig, blockToBreak.X, blockToBreak.Y, blockToBreak.Z, block.GetTypeString()); - } - else - return Translations.cmd_dig_fail; - } - catch (FormatException) { return GetCmdDescTranslated(); } + blockToBreak = blockToBreak.ToCenter(); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dig_dig, blockToBreak.X, blockToBreak.Y, blockToBreak.Z, block.GetTypeString())); } else - { - return GetCmdDescTranslated(); - } + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_fail); + } + + private int DigLookAt(CmdResult r, McClient handler) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + (bool hasBlock, Location blockLoc, Block block) = RaycastHelper.RaycastBlock(handler, 4.5, false); + if (!hasBlock) + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_too_far); + else if (block.Type == Material.Air) + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_no_block); + else if (handler.DigBlock(blockLoc, lookAtBlock: false)) + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dig_dig, blockLoc.X, blockLoc.Y, blockLoc.Z, block.GetTypeString())); + else + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_fail); } } } diff --git a/MinecraftClient/Commands/DropItem.cs b/MinecraftClient/Commands/DropItem.cs index fa085d47..01e9ecb1 100644 --- a/MinecraftClient/Commands/DropItem.cs +++ b/MinecraftClient/Commands/DropItem.cs @@ -1,57 +1,64 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Inventory; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { class DropItem : Command { public override string CmdName { get { return "dropitem"; } } - public override string CmdUsage { get { return "/dropitem "; } } + public override string CmdUsage { get { return "dropitem "; } } public override string CmdDesc { get { return Translations.cmd_dropItem_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("ItemType", MccArguments.ItemType()) + .Executes(r => DoDropItem(r.Source, handler, MccArguments.GetItemType(r, "ItemType")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoDropItem(CmdResult r, McClient handler, ItemType itemType) { if (!handler.GetInventoryEnabled()) - { - return Translations.extra_inventory_required; - } - if (HasArg(command)) - { - string arg = GetArg(command); - if (Enum.TryParse(arg, true, out ItemType itemType)) - { - int inventoryId; - var inventories = handler.GetInventories(); - List availableIds = inventories.Keys.ToList(); - availableIds.Remove(0); // remove player inventory ID from list - if (availableIds.Count == 1) - inventoryId = availableIds[0]; // one container, use it - else - inventoryId = 0; - var p = inventories[inventoryId]; - int[] targetItems = p.SearchItem(itemType); - foreach (int slot in targetItems) - { - handler.DoWindowAction(inventoryId, slot, WindowActionType.DropItemStack); - } - return string.Format(Translations.cmd_dropItem_dropped, itemType.ToString(), inventoryId); - } - else - { - return string.Format(Translations.cmd_dropItem_unknown_item, arg); - } - } + return r.SetAndReturn(Status.FailNeedTerrain); + + int inventoryId; + var inventories = handler.GetInventories(); + List availableIds = inventories.Keys.ToList(); + availableIds.Remove(0); // remove player inventory ID from list + if (availableIds.Count == 1) + inventoryId = availableIds[0]; // one container, use it else - { - return CmdUsage; - } + inventoryId = 0; + var p = inventories[inventoryId]; + int[] targetItems = p.SearchItem(itemType); + foreach (int slot in targetItems) + handler.DoWindowAction(inventoryId, slot, WindowActionType.DropItemStack); + + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dropItem_dropped, Item.GetTypeString(itemType), inventoryId)); } } } diff --git a/MinecraftClient/Commands/Enchant.cs b/MinecraftClient/Commands/Enchant.cs index 108a8b95..bc7c7e10 100644 --- a/MinecraftClient/Commands/Enchant.cs +++ b/MinecraftClient/Commands/Enchant.cs @@ -1,6 +1,7 @@ -using System.Collections.Generic; -using System.Linq; +using System.Linq; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Inventory; namespace MinecraftClient.Commands @@ -11,80 +12,96 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "enchant "; } } public override string CmdDesc { get { return Translations.cmd_enchant_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("top") + .Executes(r => GetUsage(r.Source, "top"))) + .Then(l => l.Literal("middle") + .Executes(r => GetUsage(r.Source, "middle"))) + .Then(l => l.Literal("bottom") + .Executes(r => GetUsage(r.Source, "bottom"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Literal("top") + .Executes(r => DoEnchant(r.Source, handler, slotId: 0))) + .Then(l => l.Literal("middle") + .Executes(r => DoEnchant(r.Source, handler, slotId: 1))) + .Then(l => l.Literal("bottom") + .Executes(r => DoEnchant(r.Source, handler, slotId: 2))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (!handler.GetInventoryEnabled()) - return Translations.error_inventoryhandling_not_enabled; - - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string slot = GetArg(command).ToLower().Trim(); +#pragma warning disable format // @formatter:off + "top" => GetCmdDescTranslated(), + "middle" => GetCmdDescTranslated(), + "bottom" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - int slotId = slot switch + private int DoEnchant(CmdResult r, McClient handler, int slotId) + { + Container? enchantingTable = null; + + foreach (var (id, container) in handler.GetInventories()) + { + if (container.Type == ContainerType.Enchantment) { - "top" => 0, - "middle" => 1, - "bottom" => 2, - _ => -1 - }; - - if (slotId == -1) - return Translations.cmd_enchant_invalid_slot; - - Container? enchantingTable = null; - - foreach (var (id, container) in handler.GetInventories()) - { - if (container.Type == ContainerType.Enchantment) - { - enchantingTable = container; - break; - } + enchantingTable = container; + break; } - - if (enchantingTable == null) - return Translations.cmd_enchant_enchanting_table_not_opened; - - int[] emptySlots = enchantingTable.GetEmpytSlots(); - - if (emptySlots.Contains(0)) - return Translations.cmd_enchant_enchanting_no_item; - - if (emptySlots.Contains(1)) - return Translations.cmd_enchant_enchanting_no_lapis; - - Item lapisSlot = enchantingTable.Items[1]; - - if (lapisSlot.Type != ItemType.LapisLazuli) - return Translations.cmd_enchant_enchanting_no_lapis; - - if (lapisSlot.Count < 3) - return Translations.cmd_enchant_enchanting_no_lapis; - - EnchantmentData? enchantment = handler.GetLastEnchantments(); - - if (enchantment == null) - return Translations.cmd_enchant_no_enchantments; - - short requiredLevel = slotId switch - { - 0 => enchantment.TopEnchantmentLevelRequirement, - 1 => enchantment.MiddleEnchantmentLevelRequirement, - 2 => enchantment.BottomEnchantmentLevelRequirement, - _ => 9999 - }; - - if (handler.GetLevel() < requiredLevel) - return string.Format(Translations.cmd_enchant_no_levels, handler.GetLevel(), requiredLevel); - - return handler.ClickContainerButton(enchantingTable.ID, slotId) ? Translations.cmd_enchant_clicked : Translations.cmd_enchant_not_clicked; } - return GetCmdDescTranslated(); + if (enchantingTable == null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_enchanting_table_not_opened); + + int[] emptySlots = enchantingTable.GetEmpytSlots(); + + if (emptySlots.Contains(0)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_enchanting_no_item); + + if (emptySlots.Contains(1)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_enchanting_no_lapis); + + Item lapisSlot = enchantingTable.Items[1]; + + if (lapisSlot.Type != ItemType.LapisLazuli || lapisSlot.Count < 3) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_enchanting_no_lapis); + + EnchantmentData? enchantment = handler.GetLastEnchantments(); + + if (enchantment == null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_no_enchantments); + + short requiredLevel = slotId switch + { + 0 => enchantment.TopEnchantmentLevelRequirement, + 1 => enchantment.MiddleEnchantmentLevelRequirement, + 2 => enchantment.BottomEnchantmentLevelRequirement, + _ => 9999 + }; + + if (handler.GetLevel() < requiredLevel) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_enchant_no_levels, handler.GetLevel(), requiredLevel)); + else + { + if (handler.ClickContainerButton(enchantingTable.ID, slotId)) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_enchant_clicked); + else + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_enchant_not_clicked); + } } } } diff --git a/MinecraftClient/Commands/Entitycmd.cs b/MinecraftClient/Commands/Entitycmd.cs index 6fc21bb4..ad9b8c7e 100644 --- a/MinecraftClient/Commands/Entitycmd.cs +++ b/MinecraftClient/Commands/Entitycmd.cs @@ -1,172 +1,321 @@ using System; using System.Collections.Generic; -using System.Globalization; +using System.Diagnostics.CodeAnalysis; using System.Text; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { class Entitycmd : Command { public override string CmdName { get { return "entity"; } } - public override string CmdUsage { get { return "entity "; } } + public override string CmdUsage { get { return "entity [near] "; } } public override string CmdDesc { get { return string.Empty; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + private enum ActionType { Attack, Use, List }; + + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("near") + .Executes(r => GetUsage(r.Source, "near"))) + .Then(l => l.Literal("attack") + .Executes(r => GetUsage(r.Source, "attack"))) + .Then(l => l.Literal("use") + .Executes(r => GetUsage(r.Source, "use"))) + .Then(l => l.Literal("list") + .Executes(r => GetUsage(r.Source, "list"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => GetFullEntityList(r.Source, handler)) + .Then(l => l.Literal("near") + .Executes(r => GetClosetEntityList(r.Source, handler)) + .Then(l => l.Argument("EntityID", Arguments.Integer()) + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)) + .Then(l => l.Literal("attack") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Attack))) + .Then(l => l.Literal("use") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Use))) + .Then(l => l.Literal("list") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)))) + .Then(l => l.Argument("EntityType", MccArguments.EntityType()) + .Executes(r => OperateWithType(r.Source, handler, true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)) + .Then(l => l.Literal("attack") + .Executes(r => OperateWithType(r.Source, handler, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.Attack))) + .Then(l => l.Literal("use") + .Executes(r => OperateWithType(r.Source, handler, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.Use))) + .Then(l => l.Literal("list") + .Executes(r => OperateWithType(r.Source, handler, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List))))) + .Then(l => l.Argument("EntityID", Arguments.Integer()) + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)) + .Then(l => l.Literal("attack") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Attack))) + .Then(l => l.Literal("use") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Use))) + .Then(l => l.Literal("list") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)))) + .Then(l => l.Argument("EntityType", MccArguments.EntityType()) + .Executes(r => OperateWithType(r.Source, handler, true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)) + .Then(l => l.Literal("attack") + .Executes(r => OperateWithType(r.Source, handler, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.Attack))) + .Then(l => l.Literal("use") + .Executes(r => OperateWithType(r.Source, handler, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.Use))) + .Then(l => l.Literal("list") + .Executes(r => OperateWithType(r.Source, handler, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (handler.GetEntityHandlingEnabled()) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length >= 1) +#pragma warning disable format // @formatter:off + "near" => GetCmdDescTranslated(), + "attack" => GetCmdDescTranslated(), + "use" => GetCmdDescTranslated(), + "list" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int GetFullEntityList(CmdResult r, McClient handler) + { + if (!handler.GetEntityHandlingEnabled()) + return r.SetAndReturn(Status.FailNeedEntity); + + Dictionary entities = handler.GetEntities(); + StringBuilder response = new(); + response.AppendLine(Translations.cmd_entityCmd_entities); + foreach (var entity2 in entities) + response.AppendLine(GetEntityInfoShort(entity2.Value)); + response.Append(GetCmdDescTranslated()); + handler.Log.Info(response.ToString()); + + return r.SetAndReturn(Status.Done); + } + + private int GetClosetEntityList(CmdResult r, McClient handler) + { + if (!handler.GetEntityHandlingEnabled()) + return r.SetAndReturn(Status.FailNeedEntity); + + if (TryGetClosetEntity(handler.GetEntities(), handler.GetCurrentLocation(), null, out Entity? closest)) + { + handler.Log.Info(GetEntityInfoDetailed(handler, closest)); + return r.SetAndReturn(Status.Done); + } + else + return r.SetAndReturn(Status.Fail, Translations.cmd_entityCmd_not_found); + } + + private int OperateWithId(CmdResult r, McClient handler, int entityID, ActionType action) + { + if (!handler.GetEntityHandlingEnabled()) + return r.SetAndReturn(Status.FailNeedEntity); + + if (handler.GetEntities().TryGetValue(entityID, out Entity? entity)) + { + handler.Log.Info(InteractionWithEntity(handler, entity, action)); + return r.SetAndReturn(Status.Done); + } + else + return r.SetAndReturn(Status.Fail, Translations.cmd_entityCmd_not_found); + } + + private int OperateWithType(CmdResult r, McClient handler, bool near, EntityType entityType, ActionType action) + { + if (!handler.GetEntityHandlingEnabled()) + return r.SetAndReturn(Status.FailNeedEntity); + + if (near) + { + if (TryGetClosetEntity(handler.GetEntities(), handler.GetCurrentLocation(), entityType, out Entity? closest)) { - try + handler.Log.Info(InteractionWithEntity(handler, closest, action)); + return r.SetAndReturn(Status.Done); + } + else + return r.SetAndReturn(Status.Fail, Translations.cmd_entityCmd_not_found); + } + else + { + if (action == ActionType.Attack || action == ActionType.Use) + { + string actionst = Translations.cmd_entityCmd_attacked; + int actioncount = 0; + foreach (var entity2 in handler.GetEntities()) { - int entityID = int.Parse(args[0], NumberStyles.Any, CultureInfo.CurrentCulture); - if (entityID != 0) + if (entity2.Value.Type == entityType) { - if (handler.GetEntities().ContainsKey(entityID)) + if (action == ActionType.Attack) { - string action = args.Length > 1 - ? args[1].ToLower() - : "list"; - switch (action) - { - case "attack": - handler.InteractEntity(entityID, InteractType.Attack); - return Translations.cmd_entityCmd_attacked; - case "use": - handler.InteractEntity(entityID, InteractType.Interact); - return Translations.cmd_entityCmd_used; - default: - Entity entity = handler.GetEntities()[entityID]; - int id = entity.ID; - float health = entity.Health; - int latency = entity.Latency; - Item item = entity.Item; - string? nickname = entity.Name; - string? customname = entity.CustomName; - EntityPose pose = entity.Pose; - EntityType type = entity.Type; - double distance = Math.Round(entity.Location.Distance(handler.GetCurrentLocation()), 2); - - string color = "§a"; // Green - if (health < 10) - color = "§c"; // Red - else if (health < 15) - color = "§e"; // Yellow - - string location = $"X:{Math.Round(entity.Location.X, 2)}, Y:{Math.Round(entity.Location.Y, 2)}, Z:{Math.Round(entity.Location.Z, 2)}"; - StringBuilder done = new(); - done.Append($"{Translations.cmd_entityCmd_entity}: {id}\n [MCC] Type: {entity.GetTypeString()}"); - if (!string.IsNullOrEmpty(nickname)) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_nickname}: {nickname}"); - else if (!string.IsNullOrEmpty(customname)) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_customname}: {customname.Replace("&", "§")}§8"); - if (type == EntityType.Player) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_latency}: {latency}"); - else if (type == EntityType.Item || type == EntityType.ItemFrame || type == Mapping.EntityType.EyeOfEnder || type == Mapping.EntityType.Egg || type == Mapping.EntityType.EnderPearl || type == Mapping.EntityType.Potion || type == Mapping.EntityType.Fireball || type == Mapping.EntityType.FireworkRocket) - { - string? displayName = item.DisplayName; - if (string.IsNullOrEmpty(displayName)) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_item}: {item.GetTypeString()} x{item.Count}"); - else - done.Append($"\n [MCC] {Translations.cmd_entityCmd_item}: {item.GetTypeString()} x{item.Count} - {displayName}§8"); - } - - if (entity.Equipment.Count >= 1 && entity.Equipment != null) - { - done.Append($"\n [MCC] {Translations.cmd_entityCmd_equipment}:"); - if (entity.Equipment.ContainsKey(0) && entity.Equipment[0] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_mainhand}: {entity.Equipment[0].GetTypeString()} x{entity.Equipment[0].Count}"); - if (entity.Equipment.ContainsKey(1) && entity.Equipment[1] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_offhand}: {entity.Equipment[1].GetTypeString()} x{entity.Equipment[1].Count}"); - if (entity.Equipment.ContainsKey(5) && entity.Equipment[5] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_helmet}: {entity.Equipment[5].GetTypeString()} x{entity.Equipment[5].Count}"); - if (entity.Equipment.ContainsKey(4) && entity.Equipment[4] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_chestplate}: {entity.Equipment[4].GetTypeString()} x{entity.Equipment[4].Count}"); - if (entity.Equipment.ContainsKey(3) && entity.Equipment[3] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_leggings}: {entity.Equipment[3].GetTypeString()} x{entity.Equipment[3].Count}"); - if (entity.Equipment.ContainsKey(2) && entity.Equipment[2] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_boots}: {entity.Equipment[2].GetTypeString()} x{entity.Equipment[2].Count}"); - } - done.Append($"\n [MCC] {Translations.cmd_entityCmd_pose}: {pose}"); - done.Append($"\n [MCC] {Translations.cmd_entityCmd_health}: {color}{health}§8"); - done.Append($"\n [MCC] {Translations.cmd_entityCmd_distance}: {distance}"); - done.Append($"\n [MCC] {Translations.cmd_entityCmd_location}: {location}"); - return done.ToString(); - } + handler.InteractEntity(entity2.Key, InteractType.Attack); + actionst = Translations.cmd_entityCmd_attacked; } - else return Translations.cmd_entityCmd_not_found; - } - else - { - EntityType interacttype = Enum.Parse(args[0]); - string actionst = Translations.cmd_entityCmd_attacked; - int actioncount = 0; - foreach (var entity2 in handler.GetEntities()) + else if (action == ActionType.Use) { - if (entity2.Value.Type == interacttype) - { - string action = args.Length > 1 - ? args[1].ToLower() - : "list"; - if (action == "attack") - { - handler.InteractEntity(entity2.Key, InteractType.Attack); - actionst = Translations.cmd_entityCmd_attacked; - actioncount++; - } - else if (action == "use") - { - handler.InteractEntity(entity2.Key, InteractType.Interact); - actionst = Translations.cmd_entityCmd_used; - actioncount++; - } - else return GetCmdDescTranslated(); - } + handler.InteractEntity(entity2.Key, InteractType.Interact); + actionst = Translations.cmd_entityCmd_used; } - return actioncount + " " + actionst; + actioncount++; } } - catch (FormatException) { return GetCmdDescTranslated(); } + handler.Log.Info(actioncount + " " + actionst); + return r.SetAndReturn(Status.Done); } else { - Dictionary entities = handler.GetEntities(); StringBuilder response = new(); response.AppendLine(Translations.cmd_entityCmd_entities); - foreach (var entity2 in entities) + foreach (var entity2 in handler.GetEntities()) { - int id = entity2.Key; - float health = entity2.Value.Health; - int latency = entity2.Value.Latency; - string? nickname = entity2.Value.Name; - string? customname = entity2.Value.CustomName; - EntityPose pose = entity2.Value.Pose; - EntityType type = entity2.Value.Type; - Item item = entity2.Value.Item; - string location = $"X:{Math.Round(entity2.Value.Location.X, 2)}, Y:{Math.Round(entity2.Value.Location.Y, 2)}, Z:{Math.Round(entity2.Value.Location.Z, 2)}"; - - if (type == EntityType.Item || type == EntityType.ItemFrame || type == EntityType.EyeOfEnder || type == EntityType.Egg || type == EntityType.EnderPearl || type == EntityType.Potion || type == EntityType.Fireball || type == EntityType.FireworkRocket) - response.AppendLine($" #{id}: {Translations.cmd_entityCmd_type}: {entity2.Value.GetTypeString()}, {Translations.cmd_entityCmd_item}: {item.GetTypeString()}, {Translations.cmd_entityCmd_location}: {location}"); - else if (type == EntityType.Player && !string.IsNullOrEmpty(nickname)) - response.AppendLine($" #{id}: {Translations.cmd_entityCmd_type}: {entity2.Value.GetTypeString()}, {Translations.cmd_entityCmd_nickname}: §8{nickname}§8, {Translations.cmd_entityCmd_latency}: {latency}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_pose}: {pose}, {Translations.cmd_entityCmd_location}: {location}"); - else if (type == EntityType.Player && !string.IsNullOrEmpty(customname)) - response.AppendLine($" #{id}: {Translations.cmd_entityCmd_type}: {entity2.Value.GetTypeString()}, {Translations.cmd_entityCmd_customname}: §8{customname.Replace("&", "§")}§8, {Translations.cmd_entityCmd_latency}: {latency}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_pose}: {pose}, {Translations.cmd_entityCmd_location}: {location}"); - else - response.AppendLine($" #{id}: {Translations.cmd_entityCmd_type}: {entity2.Value.GetTypeString()}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_location}: {location}"); + if (entity2.Value.Type == entityType) + { + response.AppendLine(GetEntityInfoShort(entity2.Value)); + } } response.Append(GetCmdDescTranslated()); - return response.ToString(); + handler.Log.Info(response.ToString()); + return r.SetAndReturn(Status.Done); } } - else return Translations.extra_entity_required; + } + + private static string GetEntityInfoShort(Entity entity) + { + int id = entity.ID; + float health = entity.Health; + int latency = entity.Latency; + string? nickname = entity.Name; + string? customname = entity.CustomName; + EntityPose pose = entity.Pose; + EntityType type = entity.Type; + Item item = entity.Item; + string location = $"X:{Math.Round(entity.Location.X, 2)}, Y:{Math.Round(entity.Location.Y, 2)}, Z:{Math.Round(entity.Location.Z, 2)}"; + + if (type == EntityType.Item || type == EntityType.ItemFrame || type == EntityType.EyeOfEnder || type == EntityType.Egg || type == EntityType.EnderPearl || type == EntityType.Potion || type == EntityType.Fireball || type == EntityType.FireworkRocket) + return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_item}: {item.GetTypeString()}, {Translations.cmd_entityCmd_location}: {location}"; + else if (type == EntityType.Player && !string.IsNullOrEmpty(nickname)) + return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_nickname}: §8{nickname}§8, {Translations.cmd_entityCmd_latency}: {latency}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_pose}: {pose}, {Translations.cmd_entityCmd_location}: {location}"; + else if (type == EntityType.Player && !string.IsNullOrEmpty(customname)) + return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_customname}: §8{customname.Replace("&", "§")}§8, {Translations.cmd_entityCmd_latency}: {latency}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_pose}: {pose}, {Translations.cmd_entityCmd_location}: {location}"; + else + return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_location}: {location}"; + } + + private static string GetEntityInfoDetailed(McClient handler, Entity entity) + { + StringBuilder sb = new(); + int id = entity.ID; + float health = entity.Health; + int latency = entity.Latency; + Item item = entity.Item; + string? nickname = entity.Name; + string? customname = entity.CustomName; + EntityPose pose = entity.Pose; + EntityType type = entity.Type; + double distance = Math.Round(entity.Location.Distance(handler.GetCurrentLocation()), 2); + string location = $"X:{Math.Round(entity.Location.X, 2)}, Y:{Math.Round(entity.Location.Y, 2)}, Z:{Math.Round(entity.Location.Z, 2)}"; + + string color = "§a"; // Green + if (health < 10) + color = "§c"; // Red + else if (health < 15) + color = "§e"; // Yellow + + sb.Append($"{Translations.cmd_entityCmd_entity}: {id}\n [MCC] Type: {entity.GetTypeString()}"); + + if (!string.IsNullOrEmpty(nickname)) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_nickname}: {nickname}"); + else if (!string.IsNullOrEmpty(customname)) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_customname}: {customname.Replace("&", "§")}§8"); + + if (type == EntityType.Player) + { + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_latency}: {latency}"); + } + else if (type == EntityType.Item || type == EntityType.ItemFrame || type == Mapping.EntityType.EyeOfEnder || type == Mapping.EntityType.Egg || type == Mapping.EntityType.EnderPearl || type == Mapping.EntityType.Potion || type == Mapping.EntityType.Fireball || type == Mapping.EntityType.FireworkRocket) + { + string? displayName = item.DisplayName; + if (string.IsNullOrEmpty(displayName)) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_item}: {item.GetTypeString()} x{item.Count}"); + else + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_item}: {item.GetTypeString()} x{item.Count} - {displayName}§8"); + } + + if (entity.Equipment.Count >= 1 && entity.Equipment != null) + { + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_equipment}:"); + if (entity.Equipment.ContainsKey(0) && entity.Equipment[0] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_mainhand}: {entity.Equipment[0].GetTypeString()} x{entity.Equipment[0].Count}"); + if (entity.Equipment.ContainsKey(1) && entity.Equipment[1] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_offhand}: {entity.Equipment[1].GetTypeString()} x{entity.Equipment[1].Count}"); + if (entity.Equipment.ContainsKey(5) && entity.Equipment[5] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_helmet}: {entity.Equipment[5].GetTypeString()} x{entity.Equipment[5].Count}"); + if (entity.Equipment.ContainsKey(4) && entity.Equipment[4] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_chestplate}: {entity.Equipment[4].GetTypeString()} x{entity.Equipment[4].Count}"); + if (entity.Equipment.ContainsKey(3) && entity.Equipment[3] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_leggings}: {entity.Equipment[3].GetTypeString()} x{entity.Equipment[3].Count}"); + if (entity.Equipment.ContainsKey(2) && entity.Equipment[2] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_boots}: {entity.Equipment[2].GetTypeString()} x{entity.Equipment[2].Count}"); + } + + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_pose}: {pose}"); + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_health}: {color}{health}§8"); + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_distance}: {distance}"); + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_location}: {location}"); + + return sb.ToString(); + } + + private static bool TryGetClosetEntity(Dictionary entities, Location location, EntityType? entityType, [NotNullWhen(true)] out Entity? closest) + { + closest = null; + bool find = false; + double closest_distance = double.PositiveInfinity; + foreach (var entity in entities) + { + if (entityType.HasValue && entity.Value.Type != entityType) + continue; + + double distance = Math.Round(entity.Value.Location.Distance(location), 2); + if (distance < closest_distance) + { + find = true; + closest = entity.Value; + closest_distance = distance; + } + } + return find; + } + + private static string InteractionWithEntity(McClient handler, Entity entity, ActionType action) + { + switch (action) + { + case ActionType.Attack: + handler.InteractEntity(entity.ID, InteractType.Attack); + return Translations.cmd_entityCmd_attacked; + case ActionType.Use: + handler.InteractEntity(entity.ID, InteractType.Interact); + return Translations.cmd_entityCmd_used; + case ActionType.List: + return GetEntityInfoDetailed(handler, entity); + default: + goto case ActionType.List; + } } } -} +} \ No newline at end of file diff --git a/MinecraftClient/Commands/ExecIf.cs b/MinecraftClient/Commands/ExecIf.cs index e19e5bc3..3557e200 100644 --- a/MinecraftClient/Commands/ExecIf.cs +++ b/MinecraftClient/Commands/ExecIf.cs @@ -1,99 +1,94 @@ using System; using System.Collections.Generic; -using System.Linq; using Brigadier.NET; +using Brigadier.NET.Builder; using DynamicExpresso; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { class ExecIf : Command { public override string CmdName { get { return "execif"; } } - public override string CmdUsage { get { return "execif ---> "; } } + public override string CmdUsage { get { return "execif \"\" \"\""; } } public override string CmdDesc { get { return Translations.cmd_execif_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Condition", Arguments.String()) + .Then(l => l.Argument("Command", Arguments.String()) + .Executes(r => HandleCommand(r.Source, handler, Arguments.GetString(r, "Condition"), Arguments.GetString(r, "Command"))))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string commandsString = GetArg(command); +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (!commandsString.Contains("--->")) - return GetCmdDescTranslated(); + private int HandleCommand(CmdResult r, McClient handler, string expressionText, string resultCommand) + { + try + { + var interpreter = new Interpreter(); + interpreter.SetVariable("MCC", handler); - string[] parts = commandsString.Split("--->", StringSplitOptions.TrimEntries) - .ToList() - .ConvertAll(command => command.Trim()) - .ToArray(); + foreach (KeyValuePair entry in Settings.Config.AppVar.GetVariables()) + interpreter.SetVariable(entry.Key, entry.Value); - if (parts.Length == 0) - return GetCmdDescTranslated(); + var result = interpreter.Eval(expressionText); - string expressionText = parts[0]; - string resultCommand = parts[1]; + bool shouldExec = result; - try + /*if (result is bool) + shouldExec = (bool)result; + else if (result is string) + shouldExec = !string.IsNullOrEmpty((string)result) && ((string)result).Trim().Contains("true", StringComparison.OrdinalIgnoreCase); + else if (result is int) + shouldExec = (int)result > 0; + else if (result is double) + shouldExec = (double)result > 0; + else if (result is float) + shouldExec = (float)result > 0; + else if (result is Int16) + shouldExec = (Int16)result > 0; + else if (result is Int32) + shouldExec = (Int32)result > 0; + else if (result is Int64) + shouldExec = (Int64)result > 0; + */ + + handler.Log.Debug("[Execif] Result Type: " + result.GetType().Name); + + if (shouldExec) { - var interpreter = new Interpreter(); - interpreter.SetVariable("MCC", handler); + CmdResult output = new(); + handler.PerformInternalCommand(resultCommand, ref output); - foreach (KeyValuePair entry in Settings.Config.AppVar.GetVariables()) - interpreter.SetVariable(entry.Key, entry.Value); - - var result = interpreter.Eval(expressionText); - - bool shouldExec = result; - - /*if (result is bool) - shouldExec = (bool)result; - else if (result is string) - shouldExec = !string.IsNullOrEmpty((string)result) && ((string)result).Trim().Contains("true", StringComparison.OrdinalIgnoreCase); - else if (result is int) - shouldExec = (int)result > 0; - else if (result is double) - shouldExec = (double)result > 0; - else if (result is float) - shouldExec = (float)result > 0; - else if (result is Int16) - shouldExec = (Int16)result > 0; - else if (result is Int32) - shouldExec = (Int32)result > 0; - else if (result is Int64) - shouldExec = (Int64)result > 0; - */ - - handler.Log.Debug("[Execif] Result Type: " + result.GetType().Name); - - if (shouldExec) - { - string? output = ""; - handler.PerformInternalCommand(resultCommand, ref output); - - if (string.IsNullOrEmpty(output)) - handler.Log.Debug(string.Format(Translations.cmd_execif_executed_no_output, expressionText, resultCommand)); - else - handler.Log.Debug(string.Format(Translations.cmd_execif_executed, expressionText, resultCommand, output)); - - return ""; - } - - return ""; + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_execif_executed, expressionText, resultCommand, output)); } - catch (Exception e) + else { - handler.Log.Error(string.Format(Translations.cmd_execif_error_occured, command)); - handler.Log.Error(string.Format(Translations.cmd_execif_error, e.Message)); - return ""; + return r.SetAndReturn(CmdResult.Status.Done); } } - - return GetCmdDescTranslated(); + catch (Exception e) + { + handler.Log.Error(string.Format(Translations.cmd_execif_error, e.Message)); + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_execif_error_occured, expressionText + " ---> " + resultCommand)); + } } - - } } diff --git a/MinecraftClient/Commands/ExecMulti.cs b/MinecraftClient/Commands/ExecMulti.cs index 983e5f85..bebff1a1 100644 --- a/MinecraftClient/Commands/ExecMulti.cs +++ b/MinecraftClient/Commands/ExecMulti.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -11,42 +13,47 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "execmulti -> -> -> ..."; } } public override string CmdDesc { get { return Translations.cmd_execmulti_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Commands", Arguments.GreedyString()) + .Executes(r => HandleCommand(r.Source, handler, Arguments.GetString(r, "Commands")))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string commandsString = GetArg(command); +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (commandsString.Contains("execmulti", StringComparison.OrdinalIgnoreCase) || commandsString.Contains("execif", StringComparison.OrdinalIgnoreCase)) - return Translations.cmd_execmulti_prevent; + private int HandleCommand(CmdResult r, McClient handler, string commandsString) + { + if (commandsString.Contains("execmulti", StringComparison.OrdinalIgnoreCase) || commandsString.Contains("execif", StringComparison.OrdinalIgnoreCase)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_execmulti_prevent); - IEnumerable commands = commandsString.Split("->", StringSplitOptions.TrimEntries) - .ToList() - .FindAll(command => !string.IsNullOrEmpty(command)); + IEnumerable commands = commandsString.Split("->", StringSplitOptions.TrimEntries) + .ToList() + .FindAll(command => !string.IsNullOrEmpty(command)); - foreach (string cmd in commands) - { - string? output = ""; - handler.PerformInternalCommand(cmd, ref output); - - string log = string.Format( - Translations.cmd_execmulti_executed, cmd, - string.IsNullOrEmpty(output) ? Translations.cmd_execmulti_no_result : string.Format(Translations.cmd_execmulti_result, output)); - - if (output != null && output.Contains("unknown command", StringComparison.OrdinalIgnoreCase)) - handler.Log.Error(log); - else - handler.Log.Info(log); - } - - return ""; + foreach (string cmd in commands) + { + CmdResult output = new(); + handler.PerformInternalCommand(cmd, ref output); + handler.Log.Info(string.Format(Translations.cmd_execmulti_executed, cmd, string.Format(Translations.cmd_execmulti_result, output))); } - return GetCmdDescTranslated(); + return r.SetAndReturn(CmdResult.Status.Done); } } } diff --git a/MinecraftClient/Commands/Exit.cs b/MinecraftClient/Commands/Exit.cs index 9a8ade0d..5618b248 100644 --- a/MinecraftClient/Commands/Exit.cs +++ b/MinecraftClient/Commands/Exit.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,19 +10,48 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "exit"; } } public override string CmdDesc { get { return Translations.cmd_exit_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + var exit = dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoExit(r.Source, 0)) + .Then(l => l.Argument("ExitCode", Arguments.Integer()) + .Executes(r => DoExit(r.Source, Arguments.GetInteger(r, "ExitCode")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); + + dispatcher.Register(l => l.Literal("quit") + .Executes(r => DoExit(r.Source, 0)) + .Redirect(exit) + ); } - public override string Run(McClient? handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoExit(CmdResult r, int code = 0) + { + Program.Exit(code); + return r.SetAndReturn(CmdResult.Status.Done); + } + + internal static string DoExit(string command) { Program.Exit(); - return ""; - } - - public override IEnumerable GetCMDAliases() - { - return new string[] { "quit" }; + return string.Empty; } } } diff --git a/MinecraftClient/Commands/Health.cs b/MinecraftClient/Commands/Health.cs index 18f0f67c..29c95598 100644 --- a/MinecraftClient/Commands/Health.cs +++ b/MinecraftClient/Commands/Health.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,13 +10,34 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "health"; } } public override string CmdDesc { get { return Translations.cmd_health_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => LogHealth(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - return string.Format(Translations.cmd_health_response, handler.GetHealth(), handler.GetSaturation(), handler.GetLevel(), handler.GetTotalExperience()); + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int LogHealth(CmdResult r, McClient handler) + { + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_health_response, handler.GetHealth(), handler.GetSaturation(), handler.GetLevel(), handler.GetTotalExperience())); } } } diff --git a/MinecraftClient/Commands/Help.cs b/MinecraftClient/Commands/Help.cs new file mode 100644 index 00000000..4727f2bb --- /dev/null +++ b/MinecraftClient/Commands/Help.cs @@ -0,0 +1,33 @@ +using System; +using System.Text; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; + +namespace MinecraftClient.Commands +{ + internal class Help : Command + { + public override string CmdName => throw new NotImplementedException(); + public override string CmdDesc => throw new NotImplementedException(); + public override string CmdUsage => throw new NotImplementedException(); + + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + dispatcher.Register(l => + l.Literal("help") + .Executes(r => LogHelp(r.Source, handler, dispatcher)) + ); + } + + private int LogHelp(CmdResult r, McClient handler, CommandDispatcher dispatcher) + { + var usage = dispatcher.GetSmartUsage(dispatcher.GetRoot(), CmdResult.Empty); + StringBuilder sb = new(); + foreach (var item in usage) + sb.AppendLine(Settings.Config.Main.Advanced.InternalCmdChar.ToChar() + item.Value); + handler.Log.Info(string.Format(Translations.icmd_list, sb.ToString(), Settings.Config.Main.Advanced.InternalCmdChar.ToChar())); + return r.SetAndReturn(CmdResult.Status.Done); + } + } +} diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs index 17f45c7d..89f48c6e 100644 --- a/MinecraftClient/Commands/Inventory.cs +++ b/MinecraftClient/Commands/Inventory.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Text; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Inventory; namespace MinecraftClient.Commands @@ -14,270 +15,374 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return GetBasicUsage(); } } public override string CmdDesc { get { return Translations.cmd_inventory_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("list") + .Executes(r => GetUsage(r.Source, "list"))) + .Then(l => l.Literal("close") + .Executes(r => GetUsage(r.Source, "close"))) + .Then(l => l.Literal("click") + .Executes(r => GetUsage(r.Source, "click"))) + .Then(l => l.Literal("drop") + .Executes(r => GetUsage(r.Source, "drop"))) + .Then(l => l.Literal("creativegive") + .Executes(r => GetUsage(r.Source, "creativegive"))) + .Then(l => l.Literal("creativedelete") + .Executes(r => GetUsage(r.Source, "creativedelete"))) + .Then(l => l.Literal("inventories") + .Executes(r => GetUsage(r.Source, "inventories"))) + .Then(l => l.Literal("search") + .Executes(r => GetUsage(r.Source, "search"))) + .Then(l => l.Literal("help") + .Executes(r => GetUsage(r.Source, "help"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => ListAllInventories(r.Source, handler)) + .Then(l => l.Literal("creativegive") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Then(l => l.Argument("ItemType", MccArguments.ItemType()) + .Then(l => l.Argument("Count", Arguments.Integer(min: 1)) + .Executes(r => DoCreativeGive(r.Source, handler, Arguments.GetInteger(r, "Slot"), MccArguments.GetItemType(r, "ItemType"), Arguments.GetInteger(r, "Count"))))))) + .Then(l => l.Literal("creativedelete") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Executes(r => DoCreativeDelete(r.Source, handler, Arguments.GetInteger(r, "Slot"))))) + .Then(l => l.Literal("inventories") + .Executes(r => ListAvailableInventories(r.Source, handler))) + .Then(l => l.Literal("search") + .Then(l => l.Argument("ItemType", MccArguments.ItemType()) + .Executes(r => SearchItem(r.Source, handler, MccArguments.GetItemType(r, "ItemType"), null)) + .Then(l => l.Argument("Count", Arguments.Integer(0, 64)) + .Executes(r => SearchItem(r.Source, handler, MccArguments.GetItemType(r, "ItemType"), Arguments.GetInteger(r, "Count")))))) + .Then(l => l.Argument("InventoryId", MccArguments.InventoryId()) + .Then(l => l.Literal("close") + .Executes(r => DoCloseAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId")))) + .Then(l => l.Literal("list") + .Executes(r => DoListAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId")))) + .Then(l => l.Literal("click") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Executes(r => DoClickAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) + .Then(l => l.Argument("Action", MccArguments.InventoryAction()) + .Executes(r => DoClickAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) + .Then(l => l.Literal("drop") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Executes(r => DoDropAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) + .Then(l => l.Literal("all") + .Executes(r => DoDropAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) + .Then(l => l.Literal("player") + .Then(l => l.Literal("list") + .Executes(r => DoListAction(r.Source, handler, inventoryId: 0))) + .Then(l => l.Literal("click") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 45)) + .Executes(r => DoClickAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) + .Then(l => l.Argument("Action", MccArguments.InventoryAction()) + .Executes(r => DoClickAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) + .Then(l => l.Literal("drop") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 45)) + .Executes(r => DoDropAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) + .Then(l => l.Literal("all") + .Executes(r => DoDropAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) + .Then(l => l.Literal("container") + .Then(l => l.Literal("close") + .Executes(r => DoCloseAction(r.Source, handler, inventoryId: null))) + .Then(l => l.Literal("list") + .Executes(r => DoListAction(r.Source, handler, inventoryId: null))) + .Then(l => l.Literal("click") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Executes(r => DoClickAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) + .Then(l => l.Argument("Action", MccArguments.InventoryAction()) + .Executes(r => DoClickAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) + .Then(l => l.Literal("drop") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Executes(r => DoDropAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) + .Then(l => l.Literal("all") + .Executes(r => DoDropAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (handler.GetInventoryEnabled()) + string usageStr = ' ' + Translations.cmd_inventory_help_usage + ": "; + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length >= 1) - { - int inventoryId; - if (args[0].ToLower() == "creativegive") - { - if (args.Length >= 4) - { - if (!int.TryParse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture, out int slot)) - return GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + "list" => Translations.cmd_inventory_help_list + usageStr + "/inventory > list", + "close" => Translations.cmd_inventory_help_close + usageStr + "/inventory > close", + "click" => Translations.cmd_inventory_help_click + usageStr + "/inventory > click [left|right|middle|shift]\nDefault is left click", + "drop" => Translations.cmd_inventory_help_drop + usageStr + "/inventory > drop [all]\nAll means drop full stack", + "creativegive" => Translations.cmd_inventory_help_creativegive + usageStr + "/inventory creativegive ", + "creativedelete" => Translations.cmd_inventory_help_creativedelete + usageStr + "/inventory creativedelete ", + "inventories" => Translations.cmd_inventory_help_inventories + usageStr + "/inventory inventories", + "search" => Translations.cmd_inventory_help_search + usageStr + "/inventory search [count]", + "help" => GetCmdDescTranslated(), + "action" => Translations.cmd_inventory_help_unknown + GetAvailableActions(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (Enum.TryParse(args[2], true, out ItemType itemType)) - { - if (handler.GetGamemode() == 1) - { - if (!int.TryParse(args[3], NumberStyles.Any, CultureInfo.CurrentCulture, out int count)) - return GetCmdDescTranslated(); + private int GetMaximumInventoryId(McClient handler) + { + List availableIds = handler.GetInventories().Keys.ToList(); + return availableIds.Max(); // use foreground container + } - if (handler.DoCreativeGive(slot, itemType, count, null)) - return string.Format(Translations.cmd_inventory_creative_done, itemType, count, slot); - else - return Translations.cmd_inventory_creative_fail; - } - else - return Translations.cmd_inventory_need_creative; - } - else - return GetCmdDescTranslated(); - } - else - return GetCmdDescTranslated(); - } - else if (args[0].ToLower() == "creativedelete") - { - if (args.Length >= 2) - { - if (!int.TryParse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture, out int slot)) - return GetCmdDescTranslated(); + private int ListAllInventories(CmdResult r, McClient handler) + { + if (!handler.GetInventoryEnabled()) + { + handler.Log.Info(Translations.extra_inventory_required); + return -1; + } + StringBuilder response = new(); + response.Append(Translations.cmd_inventory_inventories).Append(":\n"); + foreach ((int invId, Container inv) in handler.GetInventories()) + response.AppendLine(String.Format(" #{0}: {1}§8", invId, inv.Title)); + response.Append(CmdUsage); + handler.Log.Info(response.ToString()); + return r.SetAndReturn(CmdResult.Status.Done); + } - if (handler.GetGamemode() == 1) - { - if (handler.DoCreativeGive(slot, ItemType.Null, 0, null)) - return string.Format(Translations.cmd_inventory_creative_delete, slot); - else - return Translations.cmd_inventory_creative_fail; - } - else - return Translations.cmd_inventory_need_creative; - } - else - return GetCmdDescTranslated(); - } - else if (args[0].ToLower().StartsWith("p")) - { - // player inventory is always ID 0 - inventoryId = 0; - } - else if (args[0].ToLower().StartsWith("c")) - { - List availableIds = handler.GetInventories().Keys.ToList(); - availableIds.Remove(0); // remove player inventory ID from list - if (availableIds.Count > 0) - inventoryId = availableIds.Max(); // use foreground container - else - return Translations.cmd_inventory_container_not_found; - } - else if (args[0].ToLower().StartsWith("inventories") || args[0].ToLower().StartsWith("i")) - { - Dictionary inventories = handler.GetInventories(); - List availableIds = inventories.Keys.ToList(); - StringBuilder response = new(); - response.AppendLine(Translations.cmd_inventory_inventories_available); + private int DoCreativeGive(CmdResult r, McClient handler, int slot, ItemType itemType, int count) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); - foreach (int id in availableIds) - response.AppendLine(String.Format(" #{0} - {1}§8", id, inventories[id].Title)); - - return response.ToString(); - } - else if (args[0].ToLower().StartsWith("search") || args[0].ToLower().StartsWith("s")) - { - if (args.Length < 2) - return GetCmdDescTranslated(); - - if (!Enum.TryParse(args[1], true, out ItemType parsedItemType)) - return GetCmdDescTranslated(); - - bool shouldUseItemCount = args.Length >= 3; - int itemCount = 0; - - if (shouldUseItemCount && !int.TryParse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture, out itemCount)) - return GetCmdDescTranslated(); - - Dictionary inventories = handler.GetInventories(); - Dictionary> foundItems = new(); - - List availableInventories = inventories.Values.ToList(); - - availableInventories.ForEach(inventory => - { - inventory.Items.Values - .ToList() - .FindAll(item => item.Type == parsedItemType && (!shouldUseItemCount || item.Count == itemCount)) - .ForEach(item => - { - if (!foundItems.ContainsKey(inventory.ID)) - { - foundItems.Add(inventory.ID, new List() { item }); - return; - } - - List invItems = foundItems[inventory.ID]; - invItems.Add(item); - foundItems.Remove(inventory.ID); - foundItems.Add(inventory.ID, invItems); - }); - }); - - if (foundItems.Count == 0) - return Translations.cmd_inventory_no_found_items; - - StringBuilder response = new(); - - response.AppendLine(Translations.cmd_inventory_found_items + ":"); - - foreach ((int invId, List itemsList) in new SortedDictionary>(foundItems)) - { - if (itemsList.Count > 0) - { - response.AppendLine(String.Format("{0} (#{1}):", inventories[invId].Title, invId)); - - foreach (Item item in itemsList) - response.AppendLine(String.Format("\t- {0}", item.ToFullString())); - - response.AppendLine(" "); - } - } - - return response.ToString(); - } - else if (args[0].ToLower() == "help") - { - if (args.Length >= 2) - return GetSubCommandHelp(args[1]); - else - return GetHelp(); - } - else if (!int.TryParse(args[0], NumberStyles.Any, CultureInfo.CurrentCulture, out inventoryId)) - return GetCmdDescTranslated(); - - Container? inventory = handler.GetInventory(inventoryId); - if (inventory == null) - return string.Format(Translations.cmd_inventory_not_exist, inventoryId); - - string action = args.Length > 1 ? args[1].ToLower() : "list"; - if (action == "close") - { - if (handler.CloseInventory(inventoryId)) - return string.Format(Translations.cmd_inventory_close, inventoryId); - else - return string.Format(Translations.cmd_inventory_close_fail, inventoryId); - } - else if (action == "list") - { - StringBuilder response = new(); - response.Append(Translations.cmd_inventory_inventory); - response.AppendLine(String.Format(" #{0} - {1}§8", inventoryId, inventory.Title)); - - string? asciiArt = inventory.Type.GetAsciiArt(); - if (asciiArt != null && Settings.Config.Main.Advanced.ShowInventoryLayout) - response.AppendLine(asciiArt); - - int selectedHotbar = handler.GetCurrentSlot() + 1; - foreach ((int itemId, Item item) in new SortedDictionary(inventory.Items)) - { - bool isHotbar = inventory.IsHotbar(itemId, out int hotbar); - string hotbarString = isHotbar ? (hotbar + 1).ToString() : " "; - if ((hotbar + 1) == selectedHotbar) - hotbarString = ">" + hotbarString; - response.AppendLine(String.Format("{0,2} | #{1,-2}: {2}", hotbarString, itemId, item.ToFullString())); - } - - if (inventoryId == 0) - response.AppendLine(string.Format(Translations.cmd_inventory_hotbar, (handler.GetCurrentSlot() + 1))); - - response.Remove(response.Length - 1, 1); // Remove last '\n' - return response.ToString(); - } - else if (action == "click" && args.Length >= 3) - { - if (!int.TryParse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture, out int slot)) - return GetCmdDescTranslated(); - - WindowActionType actionType = WindowActionType.LeftClick; - string keyName = Translations.cmd_inventory_left; - if (args.Length >= 4) - { - string b = args[3]; - if (b.ToLower()[0] == 'r') - (actionType, keyName) = (WindowActionType.RightClick, Translations.cmd_inventory_right); - else if (b.ToLower()[0] == 'm') - (actionType, keyName) = (WindowActionType.MiddleClick, Translations.cmd_inventory_middle); - } - - handler.DoWindowAction(inventoryId, slot, actionType); - return string.Format(Translations.cmd_inventory_clicking, keyName, slot, inventoryId); - } - else if (action == "shiftclick" && args.Length >= 3) - { - if (!int.TryParse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture, out int slot)) - return GetCmdDescTranslated(); - - if (!handler.DoWindowAction(inventoryId, slot, WindowActionType.ShiftClick)) - return Translations.cmd_inventory_shiftclick_fail; - - return string.Format(Translations.cmd_inventory_shiftclick, slot, inventoryId); - } - else if (action == "drop" && args.Length >= 3) - { - if (!int.TryParse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture, out int slot)) - return GetCmdDescTranslated(); - - // check item exist - if (!inventory.Items.ContainsKey(slot)) - return string.Format(Translations.cmd_inventory_no_item, slot); - - WindowActionType actionType = WindowActionType.DropItem; - if (args.Length >= 4 && args[3].ToLower() == "all") - actionType = WindowActionType.DropItemStack; - - if (handler.DoWindowAction(inventoryId, slot, actionType)) - { - if (actionType == WindowActionType.DropItemStack) - return string.Format(Translations.cmd_inventory_drop_stack, slot); - else - return string.Format(Translations.cmd_inventory_drop, slot); - } - else - return "Failed"; - } - else - return GetCmdDescTranslated(); - } + if (handler.GetGamemode() == 1) + { + if (handler.DoCreativeGive(slot, itemType, count, null)) + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_creative_done, itemType, count, slot)); else - { - StringBuilder response = new(); - response.Append(Translations.cmd_inventory_inventories).Append(":\n"); - foreach ((int invId, Container inv) in handler.GetInventories()) - response.AppendLine(String.Format(" #{0}: {1}§8", invId, inv.Title)); - response.Append(CmdUsage); - return response.ToString(); - } + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_creative_fail); } else - return Translations.extra_inventory_required; + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_need_creative); + } } + private int DoCreativeDelete(CmdResult r, McClient handler, int slot) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (handler.GetGamemode() == 1) + { + if (handler.DoCreativeGive(slot, ItemType.Null, 0, null)) + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_creative_delete, slot)); + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_creative_fail); + } + else + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_need_creative); + } + } + + private int ListAvailableInventories(CmdResult r, McClient handler) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + Dictionary inventories = handler.GetInventories(); + List availableIds = inventories.Keys.ToList(); + StringBuilder response = new(); + response.AppendLine(Translations.cmd_inventory_inventories_available); + + foreach (int id in availableIds) + response.AppendLine(String.Format(" #{0} - {1}§8", id, inventories[id].Title)); + + handler.Log.Info(response.ToString()); + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int SearchItem(CmdResult r, McClient handler, ItemType itemType, int? itemCount) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + Dictionary inventories = handler.GetInventories(); + Dictionary> foundItems = new(); + + List availableInventories = inventories.Values.ToList(); + + availableInventories.ForEach(inventory => + { + inventory.Items.Values + .ToList() + .FindAll(item => item.Type == itemType && (!itemCount.HasValue || item.Count == itemCount)) + .ForEach(item => + { + if (!foundItems.ContainsKey(inventory.ID)) + { + foundItems.Add(inventory.ID, new List() { item }); + return; + } + + List invItems = foundItems[inventory.ID]; + invItems.Add(item); + foundItems.Remove(inventory.ID); + foundItems.Add(inventory.ID, invItems); + }); + }); + + if (foundItems.Count == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_no_found_items); + + StringBuilder response = new(); + + response.AppendLine(Translations.cmd_inventory_found_items + ":"); + + foreach ((int invId, List itemsList) in new SortedDictionary>(foundItems)) + { + if (itemsList.Count > 0) + { + response.AppendLine(String.Format("{0} (#{1}):", inventories[invId].Title, invId)); + + foreach (Item item in itemsList) + response.AppendLine(String.Format("\t- {0}", item.ToFullString())); + + response.AppendLine(" "); + } + } + + handler.Log.Info(response.ToString()); + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int DoCloseAction(CmdResult r, McClient handler, int? inventoryId) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (!inventoryId.HasValue) + { + inventoryId = GetMaximumInventoryId(handler); + if (inventoryId == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_container_not_found); + } + + Container? inventory = handler.GetInventory(inventoryId.Value); + if (inventory == null) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); + + if (handler.CloseInventory(inventoryId.Value)) + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_close, inventoryId)); + else + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_close_fail, inventoryId)); + } + + private int DoListAction(CmdResult r, McClient handler, int? inventoryId) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (!inventoryId.HasValue) + { + inventoryId = GetMaximumInventoryId(handler); + if (inventoryId == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_container_not_found); + } + + Container? inventory = handler.GetInventory(inventoryId.Value); + if (inventory == null) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); + + StringBuilder response = new(); + response.Append(Translations.cmd_inventory_inventory); + response.AppendLine(String.Format(" #{0} - {1}§8", inventoryId, inventory.Title)); + + string? asciiArt = inventory.Type.GetAsciiArt(); + if (asciiArt != null && Settings.Config.Main.Advanced.ShowInventoryLayout) + response.AppendLine(asciiArt); + + int selectedHotbar = handler.GetCurrentSlot() + 1; + foreach ((int itemId, Item item) in new SortedDictionary(inventory.Items)) + { + bool isHotbar = inventory.IsHotbar(itemId, out int hotbar); + string hotbarString = isHotbar ? (hotbar + 1).ToString() : " "; + if ((hotbar + 1) == selectedHotbar) + hotbarString = ">" + hotbarString; + response.AppendLine(String.Format("{0,2} | #{1,-2}: {2}", hotbarString, itemId, item.ToFullString())); + } + + if (inventoryId == 0) + response.AppendLine(string.Format(Translations.cmd_inventory_hotbar, (handler.GetCurrentSlot() + 1))); + + response.Remove(response.Length - 1, 1); // Remove last '\n' + handler.Log.Info(response.ToString()); + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int DoClickAction(CmdResult r, McClient handler, int? inventoryId, int slot, WindowActionType actionType) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (!inventoryId.HasValue) + { + inventoryId = GetMaximumInventoryId(handler); + if (inventoryId == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_container_not_found); + } + + Container? inventory = handler.GetInventory(inventoryId.Value); + if (inventory == null) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); + + string keyName = actionType switch + { + WindowActionType.LeftClick => Translations.cmd_inventory_left, + WindowActionType.RightClick => Translations.cmd_inventory_right, + WindowActionType.MiddleClick => Translations.cmd_inventory_middle, + WindowActionType.ShiftClick => Translations.cmd_inventory_shiftclick, + _ => "unknown", + }; + + handler.Log.Info(string.Format(Translations.cmd_inventory_clicking, keyName, slot, inventoryId)); + return r.SetAndReturn(handler.DoWindowAction(inventoryId.Value, slot, actionType)); + } + + private int DoDropAction(CmdResult r, McClient handler, int? inventoryId, int slot, WindowActionType actionType) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (!inventoryId.HasValue) + { + inventoryId = GetMaximumInventoryId(handler); + if (inventoryId == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_container_not_found); + } + + Container? inventory = handler.GetInventory(inventoryId.Value); + if (inventory == null) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); + + // check item exist + if (!inventory.Items.ContainsKey(slot)) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_no_item, slot)); + + if (handler.DoWindowAction(inventoryId.Value, slot, actionType)) + { + if (actionType == WindowActionType.DropItemStack) + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_drop_stack, slot)); + else + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_drop, slot)); + } + else + { + return r.SetAndReturn(CmdResult.Status.Fail, "Drop Failed"); + } + } + + #region Methods for commands help private static string GetAvailableActions() @@ -290,31 +395,6 @@ namespace MinecraftClient.Commands return Translations.cmd_inventory_help_basic + ": /inventory > ."; } - private static string GetHelp() - { - return string.Format(Translations.cmd_inventory_help_help, GetAvailableActions()); - } - - private static string GetSubCommandHelp(string cmd) - { - string usageStr = ' ' + Translations.cmd_inventory_help_usage + ": "; - return cmd switch - { -#pragma warning disable format // @formatter:off - "list" => Translations.cmd_inventory_help_list + usageStr + "/inventory > list", - "close" => Translations.cmd_inventory_help_close + usageStr + "/inventory > close", - "click" => Translations.cmd_inventory_help_click + usageStr + "/inventory > click [left|right|middle]\nDefault is left click", - "shiftclick" => Translations.cmd_inventory_help_shiftclick + usageStr + "/inventory > shiftclick ", - "drop" => Translations.cmd_inventory_help_drop + usageStr + "/inventory > drop [all]\nAll means drop full stack", - "creativegive" => Translations.cmd_inventory_help_creativegive + usageStr + "/inventory creativegive ", - "creativedelete" => Translations.cmd_inventory_help_creativedelete + usageStr + "/inventory creativedelete ", - "inventories" => Translations.cmd_inventory_help_inventories + usageStr + "/inventory inventories", - "search" => Translations.cmd_inventory_help_search + usageStr + "/inventory search [count]", - "help" => GetHelp(), - _ => Translations.cmd_inventory_help_unknown + GetAvailableActions(), -#pragma warning restore format // @formatter:on - }; - } #endregion } } diff --git a/MinecraftClient/Commands/List.cs b/MinecraftClient/Commands/List.cs index 64273a89..78b3c9d2 100644 --- a/MinecraftClient/Commands/List.cs +++ b/MinecraftClient/Commands/List.cs @@ -1,6 +1,7 @@ using System; -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -10,13 +11,34 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "list"; } } public override string CmdDesc { get { return Translations.cmd_list_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoListPlayers(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - return string.Format(Translations.cmd_list_players, String.Join(", ", handler.GetOnlinePlayers())); + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoListPlayers(CmdResult r, McClient handler) + { + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_list_players, String.Join(", ", handler.GetOnlinePlayers()))); } } } diff --git a/MinecraftClient/Commands/Log.cs b/MinecraftClient/Commands/Log.cs index 4b03e93f..e9181ed3 100644 --- a/MinecraftClient/Commands/Log.cs +++ b/MinecraftClient/Commands/Log.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,18 +10,35 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "log "; } } public override string CmdDesc { get { return Translations.cmd_log_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("String", Arguments.GreedyString()) + .Executes(r => DoLog(r.Source, Arguments.GetString(r, "String")))) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - ConsoleIO.WriteLogLine(GetArg(command)); - return ""; - } - else return GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoLog(CmdResult r, string command) + { + return r.SetAndReturn(CmdResult.Status.Done, command); } } } diff --git a/MinecraftClient/Commands/Look.cs b/MinecraftClient/Commands/Look.cs index f03e69d3..2376bbc1 100644 --- a/MinecraftClient/Commands/Look.cs +++ b/MinecraftClient/Commands/Look.cs @@ -1,8 +1,9 @@ using System; -using System.Collections.Generic; -using System.Globalization; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -12,74 +13,102 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "look "; } } public override string CmdDesc { get { return Translations.cmd_look_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("direction") + .Executes(r => GetUsage(r.Source, "direction"))) + .Then(l => l.Literal("angle") + .Executes(r => GetUsage(r.Source, "angle"))) + .Then(l => l.Literal("location") + .Executes(r => GetUsage(r.Source, "location"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => LogCurrentLooking(r.Source, handler)) + .Then(l => l.Literal("up") + .Executes(r => LookAtDirection(r.Source, handler, Direction.Up))) + .Then(l => l.Literal("down") + .Executes(r => LookAtDirection(r.Source, handler, Direction.Down))) + .Then(l => l.Literal("east") + .Executes(r => LookAtDirection(r.Source, handler, Direction.East))) + .Then(l => l.Literal("west") + .Executes(r => LookAtDirection(r.Source, handler, Direction.West))) + .Then(l => l.Literal("north") + .Executes(r => LookAtDirection(r.Source, handler, Direction.North))) + .Then(l => l.Literal("south") + .Executes(r => LookAtDirection(r.Source, handler, Direction.South))) + .Then(l => l.Argument("Yaw", Arguments.Float()) + .Then(l => l.Argument("Pitch", Arguments.Float()) + .Executes(r => LookAtAngle(r.Source, handler, Arguments.GetFloat(r, "Yaw"), Arguments.GetFloat(r, "Pitch"))))) + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => LookAtLocation(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (handler.GetTerrainEnabled()) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length == 0) - { - const double maxDistance = 8.0; - (bool hasBlock, Location target, Block block) = RaycastHelper.RaycastBlock(handler, maxDistance, false); - if (!hasBlock) - return string.Format(Translations.cmd_look_noinspection, maxDistance); - else - { - Location current = handler.GetCurrentLocation(), target_center = target.ToCenter(); - return string.Format(Translations.cmd_look_inspection, block.Type, target.X, target.Y, target.Z, - current.Distance(target_center), current.EyesLocation().Distance(target_center)); - } - } - else if (args.Length == 1) - { - string dirStr = GetArg(command).Trim().ToLower(); - Direction direction; - switch (dirStr) - { - case "up": direction = Direction.Up; break; - case "down": direction = Direction.Down; break; - case "east": direction = Direction.East; break; - case "west": direction = Direction.West; break; - case "north": direction = Direction.North; break; - case "south": direction = Direction.South; break; - default: return string.Format(Translations.cmd_look_unknown, dirStr); - } +#pragma warning disable format // @formatter:off + "direction" => GetCmdDescTranslated(), + "angle" => GetCmdDescTranslated(), + "location" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - handler.UpdateLocation(handler.GetCurrentLocation(), direction); - return "Looking " + dirStr; - } - else if (args.Length == 2) - { - try - { - float yaw = float.Parse(args[0], NumberStyles.Any, CultureInfo.CurrentCulture); - float pitch = float.Parse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture); + private int LogCurrentLooking(CmdResult r, McClient handler) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); - handler.UpdateLocation(handler.GetCurrentLocation(), yaw, pitch); - return string.Format(Translations.cmd_look_at, yaw.ToString("0.00"), pitch.ToString("0.00")); - } - catch (FormatException) { return GetCmdDescTranslated(); } - } - else if (args.Length == 3) - { - try - { - Location current = handler.GetCurrentLocation(); - Location block = Location.Parse(current, args[0], args[1], args[2]); - handler.UpdateLocation(current, block); - - return string.Format(Translations.cmd_look_block, block); - } - catch (FormatException) { return CmdUsage; } - - } - else return GetCmdDescTranslated(); + const double maxDistance = 8.0; + (bool hasBlock, Location target, Block block) = RaycastHelper.RaycastBlock(handler, maxDistance, false); + if (!hasBlock) + { + return r.SetAndReturn(Status.Fail, string.Format(Translations.cmd_look_noinspection, maxDistance)); } - else return Translations.extra_terrainandmovement_required; + else + { + Location current = handler.GetCurrentLocation(), target_center = target.ToCenter(); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_look_inspection, block.Type, target.X, target.Y, target.Z, + current.Distance(target_center), current.EyesLocation().Distance(target_center))); + } + } + + private int LookAtDirection(CmdResult r, McClient handler, Direction direction) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + handler.UpdateLocation(handler.GetCurrentLocation(), direction); + return r.SetAndReturn(Status.Done, "Looking " + direction.ToString()); + } + + private int LookAtAngle(CmdResult r, McClient handler, float yaw, float pitch) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + handler.UpdateLocation(handler.GetCurrentLocation(), yaw, pitch); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_look_at, yaw.ToString("0.00"), pitch.ToString("0.00"))); + } + + private int LookAtLocation(CmdResult r, McClient handler, Location location) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + Location current = handler.GetCurrentLocation(); + handler.UpdateLocation(current, location); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_look_block, location)); } } } diff --git a/MinecraftClient/Commands/Move.cs b/MinecraftClient/Commands/Move.cs index 679f51f6..d3b0897b 100644 --- a/MinecraftClient/Commands/Move.cs +++ b/MinecraftClient/Commands/Move.cs @@ -1,8 +1,9 @@ using System; -using System.Collections.Generic; -using System.Linq; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -12,111 +13,183 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "move [-f]"; } } public override string CmdDesc { get { return Translations.cmd_move_desc + " \"-f\": " + Translations.cmd_move_desc_force; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("enable") + .Executes(r => GetUsage(r.Source, "enable"))) + .Then(l => l.Literal("gravity") + .Executes(r => GetUsage(r.Source, "gravity"))) + .Then(l => l.Literal("direction") + .Executes(r => GetUsage(r.Source, "direction"))) + .Then(l => l.Literal("center") + .Executes(r => GetUsage(r.Source, "center"))) + .Then(l => l.Literal("get") + .Executes(r => GetUsage(r.Source, "get"))) + .Then(l => l.Literal("location") + .Executes(r => GetUsage(r.Source, "location"))) + .Then(l => l.Literal("-f") + .Executes(r => GetUsage(r.Source, "-f"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Literal("on") + .Executes(r => SetMovementEnable(r.Source, handler, enable: true))) + .Then(l => l.Literal("off") + .Executes(r => SetMovementEnable(r.Source, handler, enable: false))) + .Then(l => l.Literal("gravity") + .Executes(r => SetGravityEnable(r.Source, handler, enable: null)) + .Then(l => l.Literal("on") + .Executes(r => SetGravityEnable(r.Source, handler, enable: true))) + .Then(l => l.Literal("off") + .Executes(r => SetGravityEnable(r.Source, handler, enable: false)))) + .Then(l => l.Literal("up") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.Up, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.Up, true)))) + .Then(l => l.Literal("down") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.Down, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.Down, true)))) + .Then(l => l.Literal("east") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.East, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.East, true)))) + .Then(l => l.Literal("west") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.West, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.West, true)))) + .Then(l => l.Literal("north") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.North, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.North, true)))) + .Then(l => l.Literal("south") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.South, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.South, true)))) + .Then(l => l.Literal("center") + .Executes(r => MoveToCenter(r.Source, handler))) + .Then(l => l.Literal("get") + .Executes(r => GetCurrentLocation(r.Source, handler))) + .Then(l => l.Argument("location", MccArguments.Location()) + .Executes(r => MoveToLocation(r.Source, handler, MccArguments.GetLocation(r, "location"), false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveToLocation(r.Source, handler, MccArguments.GetLocation(r, "location"), true)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - List args = GetArgs(command.ToLower()).ToList(); - bool takeRisk = false; - - if (args.Count < 1) + return r.SetAndReturn(cmd switch { - string desc = GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + "enable" => GetCmdDescTranslated(), + "gravity" => GetCmdDescTranslated(), + "direction" => GetCmdDescTranslated(), + "center" => GetCmdDescTranslated(), + "get" => GetCmdDescTranslated(), + "location" => GetCmdDescTranslated(), + "-f" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (handler.GetTerrainEnabled()) - handler.Log.Info(World.GetChunkLoadingStatus(handler.GetWorld())); - - return desc; - } - - if (args.Contains("-f")) - { - takeRisk = true; - args.Remove("-f"); - } - - if (args[0] == "on") + private int SetMovementEnable(CmdResult r, McClient handler, bool enable) + { + if (enable) { handler.SetTerrainEnabled(true); - return Translations.cmd_move_enable; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_move_enable); } - else if (args[0] == "off") + else { handler.SetTerrainEnabled(false); - return Translations.cmd_move_disable; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_move_disable); } - else if (args[0] == "gravity") + } + + private int SetGravityEnable(CmdResult r, McClient handler, bool? enable) + { + if (enable.HasValue) + Settings.InternalConfig.GravityEnabled = enable.Value; + + if (Settings.InternalConfig.GravityEnabled) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_move_gravity_enabled); + else + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_move_gravity_disabled); + } + + private int GetCurrentLocation(CmdResult r, McClient handler) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + return r.SetAndReturn(Status.Done, handler.GetCurrentLocation().ToString()); + } + + private int MoveToCenter(CmdResult r, McClient handler) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + Location current = handler.GetCurrentLocation(); + Location currentCenter = new(Math.Floor(current.X) + 0.5, current.Y, Math.Floor(current.Z) + 0.5); + handler.MoveTo(currentCenter, allowDirectTeleport: true); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_move_walk, currentCenter, current)); + } + + private int MoveOnDirection(CmdResult r, McClient handler, Direction direction, bool takeRisk) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + Location goal = Movement.Move(handler.GetCurrentLocation(), direction); + + if (!Movement.CheckChunkLoading(handler.GetWorld(), handler.GetCurrentLocation(), goal)) + return r.SetAndReturn(Status.FailChunkNotLoad, string.Format(Translations.cmd_move_chunk_not_loaded, goal.X, goal.Y, goal.Z)); + + if (Movement.CanMove(handler.GetWorld(), handler.GetCurrentLocation(), direction)) { - if (args.Count >= 2) - Settings.InternalConfig.GravityEnabled = (args[1] == "on"); - if (Settings.InternalConfig.GravityEnabled) - return Translations.cmd_move_gravity_enabled; - else return Translations.cmd_move_gravity_disabled; + if (handler.MoveTo(goal, allowUnsafe: takeRisk)) + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_move_moving, direction.ToString())); + else + return r.SetAndReturn(Status.Fail, takeRisk ? Translations.cmd_move_dir_fail : Translations.cmd_move_suggestforce); } - else if (handler.GetTerrainEnabled()) + else { - if (args.Count == 1) - { - Direction direction; - switch (args[0]) - { - case "up": direction = Direction.Up; break; - case "down": direction = Direction.Down; break; - case "east": direction = Direction.East; break; - case "west": direction = Direction.West; break; - case "north": direction = Direction.North; break; - case "south": direction = Direction.South; break; - case "center": - Location current = handler.GetCurrentLocation(); - Location currentCenter = new(Math.Floor(current.X) + 0.5, current.Y, Math.Floor(current.Z) + 0.5); - handler.MoveTo(currentCenter, allowDirectTeleport: true); - return string.Format(Translations.cmd_move_walk, currentCenter, current); - case "get": return handler.GetCurrentLocation().ToString(); - default: return string.Format(Translations.cmd_look_unknown, args[0]); - } - - Location goal = Movement.Move(handler.GetCurrentLocation(), direction); - - if (!Movement.CheckChunkLoading(handler.GetWorld(), handler.GetCurrentLocation(), goal)) - return string.Format(Translations.cmd_move_chunk_not_loaded, goal.X, goal.Y, goal.Z); - - if (Movement.CanMove(handler.GetWorld(), handler.GetCurrentLocation(), direction)) - { - if (handler.MoveTo(goal, allowUnsafe: takeRisk)) - return string.Format(Translations.cmd_move_moving, args[0]); - else - return takeRisk ? Translations.cmd_move_dir_fail : Translations.cmd_move_suggestforce; - } - else return Translations.cmd_move_dir_fail; - } - else if (args.Count == 3) - { - try - { - Location current = handler.GetCurrentLocation(), currentCenter = current.ToCenter(); - Location goal = Location.Parse(current, args[0], args[1], args[2]); - - if (!Movement.CheckChunkLoading(handler.GetWorld(), current, goal)) - return string.Format(Translations.cmd_move_chunk_not_loaded, goal.X, goal.Y, goal.Z); - - if (takeRisk || Movement.PlayerFitsHere(handler.GetWorld(), goal)) - { - if (current.ToFloor() == goal.ToFloor()) - handler.MoveTo(goal, allowDirectTeleport: true); - else if (!handler.MoveTo(goal, allowUnsafe: takeRisk)) - return takeRisk ? string.Format(Translations.cmd_move_fail, goal) : string.Format(Translations.cmd_move_suggestforce, goal); - return string.Format(Translations.cmd_move_walk, goal, current); - } - else - return string.Format(Translations.cmd_move_suggestforce, goal); - } - catch (FormatException) { return GetCmdDescTranslated(); } - } - else return GetCmdDescTranslated(); + return r.SetAndReturn(Status.Fail, Translations.cmd_move_dir_fail); + } + } + + private int MoveToLocation(CmdResult r, McClient handler, Location goal, bool takeRisk) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + Location current = handler.GetCurrentLocation(), currentCenter = current.ToCenter(); + goal.ToAbsolute(current); + + if (!Movement.CheckChunkLoading(handler.GetWorld(), current, goal)) + return r.SetAndReturn(Status.FailChunkNotLoad, string.Format(Translations.cmd_move_chunk_not_loaded, goal.X, goal.Y, goal.Z)); + + if (takeRisk || Movement.PlayerFitsHere(handler.GetWorld(), goal)) + { + if (current.ToFloor() == goal.ToFloor()) + handler.MoveTo(goal, allowDirectTeleport: true); + else if (!handler.MoveTo(goal, allowUnsafe: takeRisk)) + return r.SetAndReturn(Status.Fail, takeRisk ? string.Format(Translations.cmd_move_fail, goal) : string.Format(Translations.cmd_move_suggestforce, goal)); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_move_walk, goal, current)); + } + else + { + return r.SetAndReturn(Status.Fail, string.Format(Translations.cmd_move_suggestforce, goal)); } - else return Translations.extra_terrainandmovement_required; } } } diff --git a/MinecraftClient/Commands/Reco.cs b/MinecraftClient/Commands/Reco.cs index b92b4fcb..c2ef3081 100644 --- a/MinecraftClient/Commands/Reco.cs +++ b/MinecraftClient/Commands/Reco.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using System; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,22 +11,58 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "reco [account]"; } } public override string CmdDesc { get { return Translations.cmd_reco_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoReconnect(r.Source, string.Empty)) + .Then(l => l.Argument("AccountNick", MccArguments.AccountNick()) + .Executes(r => DoReconnect(r.Source, Arguments.GetString(r, "AccountNick")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient? handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoReconnect(CmdResult r, string account) + { + if (string.IsNullOrWhiteSpace(account)) + { + account = account.Trim(); + if (!Settings.Config.Main.Advanced.SetAccount(account)) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_connect_unknown, account)); + } + Program.Restart(keepAccountAndServerSettings: true); + return r.SetAndReturn(CmdResult.Status.Done); + } + + internal static string DoReconnect(string command) { string[] args = GetArgs(command); if (args.Length > 0) { - if (!Settings.Config.Main.Advanced.SetAccount(args[0])) + string account = args[0].Trim(); + if (!Settings.Config.Main.Advanced.SetAccount(account)) { - return string.Format(Translations.cmd_connect_unknown, args[0]); + return string.Format(Translations.cmd_connect_unknown, account); } } Program.Restart(keepAccountAndServerSettings: true); - return ""; + return String.Empty; } } } diff --git a/MinecraftClient/Commands/Reload.cs b/MinecraftClient/Commands/Reload.cs index 2409827e..6ab4cfaf 100644 --- a/MinecraftClient/Commands/Reload.cs +++ b/MinecraftClient/Commands/Reload.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,11 +10,32 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "reload"; } } public override string CmdDesc { get { return Translations.cmd_reload_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoReload(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoReload(CmdResult r, McClient handler) { handler.Log.Info(Translations.cmd_reload_started); handler.ReloadSettings(); @@ -22,7 +44,7 @@ namespace MinecraftClient.Commands handler.Log.Warn(Translations.cmd_reload_warning3); handler.Log.Warn(Translations.cmd_reload_warning4); - return Translations.cmd_reload_finished; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_reload_finished); } } } diff --git a/MinecraftClient/Commands/Respawn.cs b/MinecraftClient/Commands/Respawn.cs index 2eedc088..d9bfbcbf 100644 --- a/MinecraftClient/Commands/Respawn.cs +++ b/MinecraftClient/Commands/Respawn.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,14 +10,35 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "respawn"; } } public override string CmdDesc { get { return Translations.cmd_respawn_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoRespawn(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoRespawn(CmdResult r, McClient handler) { handler.SendRespawnPacket(); - return Translations.cmd_respawn_done; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_respawn_done); } } } diff --git a/MinecraftClient/Commands/Script.cs b/MinecraftClient/Commands/Script.cs index 239f7737..f7f850f2 100644 --- a/MinecraftClient/Commands/Script.cs +++ b/MinecraftClient/Commands/Script.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,18 +11,36 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "script "; } } public override string CmdDesc { get { return Translations.cmd_script_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Script", Arguments.GreedyString()) + .Executes(r => DoExecuteScript(r.Source, handler, Arguments.GetString(r, "Script"), null))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - handler.BotLoad(new ChatBots.Script(GetArg(command), null, localVars)); - return ""; - } - else return GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoExecuteScript(CmdResult r, McClient handler, string command, Dictionary? localVars) + { + handler.BotLoad(new ChatBots.Script(command.Trim(), null, localVars)); + return r.SetAndReturn(CmdResult.Status.Done); } } } diff --git a/MinecraftClient/Commands/Send.cs b/MinecraftClient/Commands/Send.cs index 2e868904..d17bc3b5 100644 --- a/MinecraftClient/Commands/Send.cs +++ b/MinecraftClient/Commands/Send.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,18 +10,34 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "send "; } } public override string CmdDesc { get { return Translations.cmd_send_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("any", Arguments.GreedyString()) + .Executes(r => DoSendText(r.Source, handler, Arguments.GetString(r, "any")))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - handler.SendText(GetArg(command)); - return ""; - } - else return GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoSendText(CmdResult r, McClient handler, string command) + { + handler.SendText(command); + return r.SetAndReturn(CmdResult.Status.Done); } } } diff --git a/MinecraftClient/Commands/Set.cs b/MinecraftClient/Commands/Set.cs index 09db64e7..3bf6d86f 100644 --- a/MinecraftClient/Commands/Set.cs +++ b/MinecraftClient/Commands/Set.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,27 +10,50 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "set varname=value"; } } public override string CmdDesc { get { return Translations.cmd_set_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Expression", Arguments.GreedyString()) + .Executes(r => DoSetVar(r.Source, Arguments.GetString(r, "Expression")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] temp = GetArg(command).Split('='); - if (temp.Length > 1) +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoSetVar(CmdResult r, string command) + { + string[] temp = command.Trim().Split('='); + if (temp.Length > 1) + { + if (Settings.Config.AppVar.SetVar(temp[0], command[(temp[0].Length + 1)..])) { - if (Settings.Config.AppVar.SetVar(temp[0], GetArg(command).Substring(temp[0].Length + 1))) - return ""; //Success - else - return Translations.cmd_set_format; + return r.SetAndReturn(CmdResult.Status.Done); //Success } else - return GetCmdDescTranslated(); + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_set_format); + } } else - return GetCmdDescTranslated(); + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_set_format); + } } } } diff --git a/MinecraftClient/Commands/SetRnd.cs b/MinecraftClient/Commands/SetRnd.cs index d6f23b7c..73e8673b 100644 --- a/MinecraftClient/Commands/SetRnd.cs +++ b/MinecraftClient/Commands/SetRnd.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -11,65 +13,61 @@ namespace MinecraftClient.Commands public override string CmdDesc { get { return Translations.cmd_setrnd_desc; } } private static readonly Random rand = new(); - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("range") + .Executes(r => GetUsage(r.Source, "range"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("VarName", Arguments.String()) + .Then(l => l.Argument("Min", Arguments.Long()) + .Then(l => l.Literal("to") + .Then(l => l.Argument("Max", Arguments.Long()) + .Executes(r => DoSetRnd(r.Source, Arguments.GetString(r, "VarName"), Arguments.GetLong(r, "Min"), Arguments.GetLong(r, "Max")))))) + .Then(l => l.Argument("Expression", Arguments.GreedyString()) + .Executes(r => DoSetRnd(r.Source, Arguments.GetString(r, "VarName"), Arguments.GetString(r, "Expression"))))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] args = GetArg(command).Split(' '); +#pragma warning disable format // @formatter:off + "range" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (args.Length > 1) - { - // detect "to" keyword in string - if (args.Length == 2 && args[1].Contains("to")) - { - int num1; - int num2; + private int DoSetRnd(CmdResult r, string var, string argString) + { + // process all arguments similar to regular terminals with quotes and escaping + List values = ParseCommandLine(argString); - // try to extract the two numbers from the string - try - { - num1 = Convert.ToInt32(args[1][..args[1].IndexOf('t')]); - num2 = Convert.ToInt32(args[1].Substring(args[1].IndexOf('o') + 1, args[1].Length - 1 - args[1].IndexOf('o'))); - } - catch (Exception) - { - return Translations.cmd_setrndnum_format; - } + // create a variable or set it to one of the values + if (values.Count > 0 && Settings.Config.AppVar.SetVar(var, values[rand.Next(0, values.Count)])) + return r.SetAndReturn(CmdResult.Status.Done, string.Format("Set %{0}% to {1}.", var, Settings.Config.AppVar.GetVar(var))); + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_setrndstr_format); + } - // switch the values if they were entered in the wrong way - if (num2 < num1) - (num2, num1) = (num1, num2); + private int DoSetRnd(CmdResult r, string var, long min, long max) + { + // switch the values if they were entered in the wrong way + if (max < min) + (max, min) = (min, max); - // create a variable or set it to num1 <= varlue < num2 - if (Settings.Config.AppVar.SetVar(args[0], rand.Next(num1, num2))) - { - return string.Format("Set %{0}% to {1}.", args[0], Settings.Config.AppVar.GetVar(args[0])); //Success - } - else return Translations.cmd_setrndnum_format; - } - else - { - // extract all arguments of the command - string argString = command[(8 + command.Split(' ')[1].Length)..]; - - // process all arguments similar to regular terminals with quotes and escaping - List values = ParseCommandLine(argString); - - // create a variable or set it to one of the values - if (values.Count > 0 && Settings.Config.AppVar.SetVar(args[0], values[rand.Next(0, values.Count)])) - { - return string.Format("Set %{0}% to {1}.", args[0], Settings.Config.AppVar.GetVar(args[0])); //Success - } - else return Translations.cmd_setrndstr_format; - } - } - else return GetCmdDescTranslated(); - } - else return GetCmdDescTranslated(); + // create a variable or set it to num1 <= varlue < num2 + if (Settings.Config.AppVar.SetVar(var, rand.NextInt64(min, max))) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format("Set %{0}% to {1}.", var, Settings.Config.AppVar.GetVar(var))); + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_setrndstr_format); } } } diff --git a/MinecraftClient/Commands/Sneak.cs b/MinecraftClient/Commands/Sneak.cs index eac0a7cd..d21bea8a 100644 --- a/MinecraftClient/Commands/Sneak.cs +++ b/MinecraftClient/Commands/Sneak.cs @@ -1,34 +1,62 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { public class Sneak : Command { private bool sneaking = false; - public override string CmdName { get { return "Sneak"; } } - public override string CmdUsage { get { return "Sneak"; } } + public override string CmdName { get { return "sneak"; } } + public override string CmdUsage { get { return "sneak"; } } public override string CmdDesc { get { return Translations.cmd_sneak_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoSneak(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoSneak(CmdResult r, McClient handler) { if (sneaking) { var result = handler.SendEntityAction(Protocol.EntityActionType.StopSneaking); if (result) sneaking = false; - return result ? Translations.cmd_sneak_off : Translations.general_fail; + if (result) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_sneak_off); + else + return r.SetAndReturn(CmdResult.Status.Fail); } else { var result = handler.SendEntityAction(Protocol.EntityActionType.StartSneaking); if (result) sneaking = true; - return result ? Translations.cmd_sneak_on : Translations.general_fail; + if (result) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_sneak_on); + else + return r.SetAndReturn(CmdResult.Status.Fail); } } } diff --git a/MinecraftClient/Commands/Tps.cs b/MinecraftClient/Commands/Tps.cs index 029fe22d..92fb0924 100644 --- a/MinecraftClient/Commands/Tps.cs +++ b/MinecraftClient/Commands/Tps.cs @@ -1,6 +1,7 @@ using System; -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -10,11 +11,32 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "tps"; } } public override string CmdDesc { get { return Translations.cmd_tps_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoLogTps(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoLogTps(CmdResult r, McClient handler) { var tps = Math.Round(handler.GetServerTPS(), 2); string color; @@ -22,9 +44,9 @@ namespace MinecraftClient.Commands color = "§c"; // Red else if (tps < 15) color = "§e"; // Yellow - else + else color = "§a"; // Green - return Translations.cmd_tps_current + ": " + color + tps; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_tps_current + ": " + color + tps); } } } diff --git a/MinecraftClient/Commands/UseItem.cs b/MinecraftClient/Commands/UseItem.cs index 52ea97c3..41f056da 100644 --- a/MinecraftClient/Commands/UseItem.cs +++ b/MinecraftClient/Commands/UseItem.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -9,18 +11,38 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "useitem"; } } public override string CmdDesc { get { return Translations.cmd_useitem_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoUseItem(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (handler.GetInventoryEnabled()) + return r.SetAndReturn(cmd switch { - handler.UseItemOnHand(); - return Translations.cmd_useitem_use; - } - else return Translations.extra_inventory_required; +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoUseItem(CmdResult r, McClient handler) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(Status.FailNeedInventory); + + handler.UseItemOnHand(); + return r.SetAndReturn(Status.Done, Translations.cmd_useitem_use); } } } diff --git a/MinecraftClient/Commands/Useblock.cs b/MinecraftClient/Commands/Useblock.cs index 10125f43..784185ee 100644 --- a/MinecraftClient/Commands/Useblock.cs +++ b/MinecraftClient/Commands/Useblock.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -10,29 +12,42 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "useblock "; } } public override string CmdDesc { get { return Translations.cmd_useblock_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => UseBlockAtLocation(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int UseBlockAtLocation(CmdResult r, McClient handler, Location block) { if (!handler.GetTerrainEnabled()) - return Translations.extra_terrainandmovement_required; - else if (HasArg(command)) - { - string[] args = GetArgs(command); - if (args.Length >= 3) - { - Location block = Location.Parse(handler.GetCurrentLocation().ToFloor(), args[0], args[1], args[2]).ToFloor(); - Location blockCenter = block.ToCenter(); - bool res = handler.PlaceBlock(block, Direction.Down); - return string.Format(Translations.cmd_useblock_use, blockCenter.X, blockCenter.Y, blockCenter.Z, res ? "succeeded" : "failed"); - } - else - return GetCmdDescTranslated(); - } - else - return GetCmdDescTranslated(); + return r.SetAndReturn(Status.FailNeedTerrain); + + Location current = handler.GetCurrentLocation(); + block = block.ToAbsolute(current).ToFloor(); + Location blockCenter = block.ToCenter(); + bool res = handler.PlaceBlock(block, Direction.Down); + return r.SetAndReturn(string.Format(Translations.cmd_useblock_use, blockCenter.X, blockCenter.Y, blockCenter.Z, res ? "succeeded" : "failed"), res); } } } diff --git a/MinecraftClient/ConsoleIO.cs b/MinecraftClient/ConsoleIO.cs index 022ccaf4..5561e003 100644 --- a/MinecraftClient/ConsoleIO.cs +++ b/MinecraftClient/ConsoleIO.cs @@ -1,6 +1,13 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; +using System.Threading; +using System.Threading.Tasks; +using FuzzySharp; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; +using static MinecraftClient.Settings; namespace MinecraftClient { @@ -175,16 +182,151 @@ namespace MinecraftClient #endregion - public static void AutocompleteHandler(object? sender, ConsoleKey e) + + internal static bool AutoCompleteDone = false; + internal static string[] AutoCompleteResult = Array.Empty(); + + private static HashSet Commands = new(); + private static string[] CommandsFromAutoComplete = Array.Empty(); + private static string[] CommandsFromDeclareCommands = Array.Empty(); + + private static Task _latestTask = Task.CompletedTask; + private static CancellationTokenSource? _cancellationTokenSource; + + private static void MccAutocompleteHandler(ConsoleInteractive.ConsoleReader.Buffer buffer) { - if (e != ConsoleKey.Tab) return; - - if (autocomplete_engine == null) + string fullCommand = buffer.Text; + if (string.IsNullOrEmpty(fullCommand)) + { + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); return; + } - var buffer = ConsoleInteractive.ConsoleReader.GetBufferContent(); - ConsoleIO.WriteLine("AutoComplete " + buffer); - autocomplete_engine.AutoComplete(buffer.Text[..buffer.CursorPosition]); + var InternalCmdChar = Config.Main.Advanced.InternalCmdChar; + if (InternalCmdChar == MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none || fullCommand[0] == InternalCmdChar.ToChar()) + { + int offset = InternalCmdChar == MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none ? 0 : 1; + if (buffer.CursorPosition - offset < 0) + { + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + return; + } + _cancellationTokenSource?.Cancel(); + using var cts = new CancellationTokenSource(); + _cancellationTokenSource = cts; + var previousTask = _latestTask; + var newTask = new Task(async () => + { + string command = fullCommand[offset..]; + if (command.Length == 0) + { + var childs = McClient.dispatcher.GetRoot().Children; + int index = 0; + var sugList = new ConsoleInteractive.ConsoleSuggestion.Suggestion[childs.Count + Commands.Count + 1]; + + sugList[index++] = new("/"); + foreach (var child in childs) + sugList[index++] = new(child.Name); + foreach (var cmd in Commands) + sugList[index++] = new(cmd); + ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, new(offset, offset)); + } + else if (command.Length > 0 && command[0] == '/' && !command.Contains(' ')) + { + var sorted = Process.ExtractSorted(command[1..], Commands); + var sugList = new ConsoleInteractive.ConsoleSuggestion.Suggestion[sorted.Count()]; + + int index = 0; + foreach (var sug in sorted) + sugList[index++] = new(sug.Value); + ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, new(offset, offset + command.Length)); + } + else + { + var parse = McClient.dispatcher.Parse(command, CmdResult.Empty); + + var suggestion = await McClient.dispatcher.GetCompletionSuggestions(parse, buffer.CursorPosition - offset); + + int sugLen = suggestion.List.Count; + if (sugLen == 0) + { + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + return; + } + + var sugList = new ConsoleInteractive.ConsoleSuggestion.Suggestion[sugLen]; + if (cts.IsCancellationRequested) + return; + + Tuple range = new(suggestion.Range.Start + offset, suggestion.Range.End + offset); + var sorted = Process.ExtractSorted(fullCommand[range.Item1..range.Item2], suggestion.List.Select(_ => _.Text).ToList()); + if (cts.IsCancellationRequested) + return; + + int index = 0; + foreach (var sug in sorted) + sugList[index++] = new(sug.Value); + + ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, range); + } + }, cts.Token); + _latestTask = newTask; + try { newTask.Start(); } catch { } + if (_cancellationTokenSource == cts) _cancellationTokenSource = null; + } + else + { + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + return; + } + } + + public static void AutocompleteHandler(object? sender, ConsoleInteractive.ConsoleReader.Buffer buffer) + { + MccAutocompleteHandler(buffer); + } + + public static void CancelAutocomplete() + { + _cancellationTokenSource?.Cancel(); + _latestTask = Task.CompletedTask; + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + + AutoCompleteDone = false; + AutoCompleteResult = Array.Empty(); + CommandsFromAutoComplete = Array.Empty(); + CommandsFromDeclareCommands = Array.Empty(); + } + + private static void MergeCommands() + { + Commands.Clear(); + foreach (string cmd in CommandsFromAutoComplete) + Commands.Add('/' + cmd); + foreach (string cmd in CommandsFromDeclareCommands) + Commands.Add('/' + cmd); + } + + public static void OnAutoCompleteDone(int transactionId, string[] result) + { + AutoCompleteResult = result; + if (transactionId == 0) + { + CommandsFromAutoComplete = result; + MergeCommands(); + } + AutoCompleteDone = true; + } + + public static void OnDeclareMinecraftCommand(string[] rootCommands) + { + CommandsFromDeclareCommands = rootCommands; + MergeCommands(); + } + + public static void InitAutocomplete() + { + autocomplete_engine!.AutoComplete("/"); } } @@ -199,6 +341,6 @@ namespace MinecraftClient /// /// Text behind the cursor, e.g. "my input comm" /// List of auto-complete words, e.g. ["command", "comment"] - IEnumerable AutoComplete(string BehindCursor); + int AutoComplete(string BehindCursor); } } diff --git a/MinecraftClient/Inventory/EnchantmentMapping.cs b/MinecraftClient/Inventory/EnchantmentMapping.cs index d6ffba32..badadb99 100644 --- a/MinecraftClient/Inventory/EnchantmentMapping.cs +++ b/MinecraftClient/Inventory/EnchantmentMapping.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using MinecraftClient.Protocol.Handlers; +using MinecraftClient.Protocol.Message; namespace MinecraftClient.Inventory { @@ -158,7 +159,7 @@ namespace MinecraftClient.Inventory public static string GetEnchantmentName(Enchantment enchantment) { - string? trans = Protocol.ChatParser.TranslateString("enchantment.minecraft." + enchantment.ToString().ToUnderscoreCase()); + string? trans = ChatParser.TranslateString("enchantment.minecraft." + enchantment.ToString().ToUnderscoreCase()); if (string.IsNullOrEmpty(trans)) return "Unknown Enchantment with ID: " + ((short)enchantment) + " (Probably not named in the code yet)"; else diff --git a/MinecraftClient/Inventory/Item.cs b/MinecraftClient/Inventory/Item.cs index 28a5754e..d0c523b3 100644 --- a/MinecraftClient/Inventory/Item.cs +++ b/MinecraftClient/Inventory/Item.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; +using MinecraftClient.Protocol.Message; namespace MinecraftClient.Inventory { @@ -64,7 +65,7 @@ namespace MinecraftClient.Inventory { string? displayName = displayProperties["Name"] as string; if (!String.IsNullOrEmpty(displayName)) - return MinecraftClient.Protocol.ChatParser.ParseText(displayProperties["Name"].ToString() ?? string.Empty); + return ChatParser.ParseText(displayProperties["Name"].ToString() ?? string.Empty); } } return null; @@ -85,7 +86,7 @@ namespace MinecraftClient.Inventory { object[] displayName = (object[])displayProperties["Lore"]; lores.AddRange(from string st in displayName - let str = MinecraftClient.Protocol.ChatParser.ParseText(st.ToString()) + let str = ChatParser.ParseText(st.ToString()) select str); return lores.ToArray(); } @@ -117,10 +118,10 @@ namespace MinecraftClient.Inventory { string type_str = type.ToString(); string type_renamed = type_str.ToUnderscoreCase(); - string? res1 = Protocol.ChatParser.TranslateString("item.minecraft." + type_renamed); + string? res1 = ChatParser.TranslateString("item.minecraft." + type_renamed); if (!string.IsNullOrEmpty(res1)) return res1; - string? res2 = Protocol.ChatParser.TranslateString("block.minecraft." + type_renamed); + string? res2 = ChatParser.TranslateString("block.minecraft." + type_renamed); if (!string.IsNullOrEmpty(res2)) return res2; return type_str; @@ -145,8 +146,8 @@ namespace MinecraftClient.Inventory short level = (short)enchantment["lvl"]; string id = ((string)enchantment["id"]).Replace(':', '.'); sb.AppendFormat(" | {0} {1}", - Protocol.ChatParser.TranslateString("enchantment." + id) ?? id, - Protocol.ChatParser.TranslateString("enchantment.level." + level) ?? level.ToString()); + ChatParser.TranslateString("enchantment." + id) ?? id, + ChatParser.TranslateString("enchantment.level." + level) ?? level.ToString()); } } } diff --git a/MinecraftClient/Inventory/ItemMovingHelper.cs b/MinecraftClient/Inventory/ItemMovingHelper.cs index 07b1e686..da9a0097 100644 --- a/MinecraftClient/Inventory/ItemMovingHelper.cs +++ b/MinecraftClient/Inventory/ItemMovingHelper.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using MinecraftClient.Scripting; namespace MinecraftClient.Inventory { diff --git a/MinecraftClient/Inventory/VillagerInfo.cs b/MinecraftClient/Inventory/VillagerInfo.cs index 28f793af..c93781e8 100644 --- a/MinecraftClient/Inventory/VillagerInfo.cs +++ b/MinecraftClient/Inventory/VillagerInfo.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Mapping +namespace MinecraftClient.Inventory { /// /// Properties of a villager diff --git a/MinecraftClient/Json.cs b/MinecraftClient/Json.cs index e77e32bf..be70b48e 100644 --- a/MinecraftClient/Json.cs +++ b/MinecraftClient/Json.cs @@ -103,7 +103,7 @@ namespace MinecraftClient && IsHex(toparse[cursorpos + 5])) { //"abc\u0123abc" => "0123" => 0123 => Unicode char n°0123 => Add char to string - data.StringValue += char.ConvertFromUtf32(int.Parse(toparse.Substring(cursorpos + 2, 4), + data.StringValue += char.ConvertFromUtf32(int.Parse(toparse.Substring(cursorpos + 2, 4), System.Globalization.NumberStyles.HexNumber)); cursorpos += 6; continue; } diff --git a/MinecraftClient/Logger/FileLogLogger.cs b/MinecraftClient/Logger/FileLogLogger.cs index 8418ec6c..9614a5c4 100644 --- a/MinecraftClient/Logger/FileLogLogger.cs +++ b/MinecraftClient/Logger/FileLogLogger.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using MinecraftClient.Scripting; namespace MinecraftClient.Logger { diff --git a/MinecraftClient/Mapping/Block.cs b/MinecraftClient/Mapping/Block.cs index 065699a7..ffbe499c 100644 --- a/MinecraftClient/Mapping/Block.cs +++ b/MinecraftClient/Mapping/Block.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using MinecraftClient.Mapping.BlockPalettes; +using MinecraftClient.Protocol.Message; namespace MinecraftClient.Mapping { @@ -115,7 +116,7 @@ namespace MinecraftClient.Mapping public string GetTypeString() { string typeStr = Type.ToString(); - string? trans = Protocol.ChatParser.TranslateString("block.minecraft." + typeStr.ToUnderscoreCase()); + string? trans = ChatParser.TranslateString("block.minecraft." + typeStr.ToUnderscoreCase()); return string.IsNullOrEmpty(trans) ? typeStr : trans; } diff --git a/MinecraftClient/Mapping/Dimension.cs b/MinecraftClient/Mapping/Dimension.cs index 3aea6744..fe52b0e0 100644 --- a/MinecraftClient/Mapping/Dimension.cs +++ b/MinecraftClient/Mapping/Dimension.cs @@ -139,9 +139,9 @@ namespace MinecraftClient.Mapping try { var monsterSpawnLightLevelObj = nbt["monster_spawn_light_level"]; - try - { - monsterSpawnMinLightLevel = monsterSpawnMaxLightLevel = Convert.ToInt32(monsterSpawnLightLevelObj); + try + { + monsterSpawnMinLightLevel = monsterSpawnMaxLightLevel = Convert.ToInt32(monsterSpawnLightLevelObj); } catch (Exception) { diff --git a/MinecraftClient/Mapping/Entity.cs b/MinecraftClient/Mapping/Entity.cs index fa7646b7..8d11ba27 100644 --- a/MinecraftClient/Mapping/Entity.cs +++ b/MinecraftClient/Mapping/Entity.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using MinecraftClient.Inventory; +using MinecraftClient.Protocol.Message; namespace MinecraftClient.Mapping { @@ -158,7 +159,7 @@ namespace MinecraftClient.Mapping public string GetTypeString() { string typeStr = Type.ToString(); - string? trans = Protocol.ChatParser.TranslateString("entity.minecraft." + typeStr.ToUnderscoreCase()); + string? trans = ChatParser.TranslateString("entity.minecraft." + typeStr.ToUnderscoreCase()); return string.IsNullOrEmpty(trans) ? typeStr : trans; } } diff --git a/MinecraftClient/Mapping/Location.cs b/MinecraftClient/Mapping/Location.cs index 5094648f..4a6a7320 100644 --- a/MinecraftClient/Mapping/Location.cs +++ b/MinecraftClient/Mapping/Location.cs @@ -26,6 +26,15 @@ namespace MinecraftClient.Mapping /// public double Z; + /// + /// Identifies whether the coordinates are absolute or relative. + /// true for relative coordinates, false for absolute coordinates. + /// X-axis: ((Status & (1 << 0)) > 0) + /// Y-axis: ((Status & (1 << 1)) > 0) + /// Z-axis: ((Status & (1 << 2)) > 0) + /// + public byte Status; + /// /// Create a new location /// @@ -34,6 +43,18 @@ namespace MinecraftClient.Mapping X = x; Y = y; Z = z; + Status = 0; + } + + /// + /// Create a new location + /// + public Location(double x, double y, double z, byte status) + { + X = x; + Y = y; + Z = z; + Status = status; } /// @@ -44,6 +65,7 @@ namespace MinecraftClient.Mapping X = loc.X; Y = loc.Y; Z = loc.Z; + Status = loc.Status; } /// @@ -60,6 +82,19 @@ namespace MinecraftClient.Mapping X = chunkX * Chunk.SizeX + blockX; Y = blockY; Z = chunkZ * Chunk.SizeZ + blockZ; + Status = 0; + } + + public Location ToAbsolute(Location based) + { + if ((Status & (1 << 0)) > 0) + X += based.X; + if ((Status & (1 << 1)) > 0) + Y += based.Y; + if ((Status & (1 << 2)) > 0) + Z += based.Z; + Status = 0; + return this; } /// diff --git a/MinecraftClient/Mapping/RaycastHelper.cs b/MinecraftClient/Mapping/RaycastHelper.cs index a1afecef..483b0b86 100644 --- a/MinecraftClient/Mapping/RaycastHelper.cs +++ b/MinecraftClient/Mapping/RaycastHelper.cs @@ -36,7 +36,7 @@ namespace MinecraftClient.Mapping { if (start == end) return new(false, Location.Zero, Block.Air); - + double start_x = MathHelper.Lerp(-1.0E-7, start.X, end.X); double start_y = MathHelper.Lerp(-1.0E-7, start.Y, end.Y); double start_z = MathHelper.Lerp(-1.0E-7, start.Z, end.Z); @@ -87,7 +87,7 @@ namespace MinecraftClient.Mapping res_location.Z += dz_sign; z_frac += z_step; } - + block = CheckRaycastResult(world, res_location, includeFluids); if (block.Type != Material.Air) return new(true, res_location, block); diff --git a/MinecraftClient/Mapping/World.cs b/MinecraftClient/Mapping/World.cs index 97792cfe..0b2e02f9 100644 --- a/MinecraftClient/Mapping/World.cs +++ b/MinecraftClient/Mapping/World.cs @@ -153,7 +153,7 @@ namespace MinecraftClient.Mapping /// Block type /// Search radius - larger is slower: O^3 complexity /// Block matching the specified block type - public List FindBlock(Location from, Material block, int radius) + public List FindBlock(Location from, Material block, double radius) { return FindBlock(from, block, radius, radius, radius); } @@ -167,7 +167,7 @@ namespace MinecraftClient.Mapping /// Search radius on the Y axis /// Search radius on the Z axis /// Block matching the specified block type - public List FindBlock(Location from, Material block, int radiusx, int radiusy, int radiusz) + public List FindBlock(Location from, Material block, double radiusx, double radiusy, double radiusz) { Location minPoint = new Location(from.X - radiusx, from.Y - radiusy, from.Z - radiusz); Location maxPoint = new Location(from.X + radiusx, from.Y + radiusy, from.Z + radiusz); diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index f33e6456..a33f0fae 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -2,22 +2,23 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; -using System.Security.Cryptography.Pkcs; using System.Text; using System.Threading; using Brigadier.NET; using Brigadier.NET.Exceptions; using MinecraftClient.ChatBots; -using MinecraftClient.Commands; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; using MinecraftClient.Protocol; using MinecraftClient.Protocol.Handlers.Forge; -using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; +using MinecraftClient.Protocol.ProfileKey; using MinecraftClient.Protocol.Session; using MinecraftClient.Proxy; +using MinecraftClient.Scripting; using static MinecraftClient.Settings; namespace MinecraftClient @@ -29,10 +30,7 @@ namespace MinecraftClient { public static int ReconnectionAttemptsLeft = 0; - public static readonly CommandSource cmd_source = new(); - public static readonly CommandDispatcher dispatcher = new(); - //private static readonly List cmd_names = new(); - //private static readonly Dictionary cmds = new(); + public static CommandDispatcher dispatcher = new(); private readonly Dictionary onlinePlayers = new(); private static bool commandsLoaded = false; @@ -152,6 +150,8 @@ namespace MinecraftClient /// ForgeInfo item stating that Forge is enabled public McClient(SessionToken session, PlayerKeyPair? playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo? forgeInfo) { + dispatcher = new(); + CmdResult.client = this; terrainAndMovementsEnabled = Config.Main.Advanced.TerrainAndMovements; inventoryHandlingEnabled = Config.Main.Advanced.InventoryHandling; entityHandlingEnabled = Config.Main.Advanced.EntityHandling; @@ -206,7 +206,7 @@ namespace MinecraftClient cmdprompt = new CancellationTokenSource(); ConsoleInteractive.ConsoleReader.BeginReadThread(cmdprompt); ConsoleInteractive.ConsoleReader.MessageReceived += ConsoleReaderOnMessageReceived; - ConsoleInteractive.ConsoleReader.OnKeyInput += ConsoleIO.AutocompleteHandler; + ConsoleInteractive.ConsoleReader.OnInputChange += ConsoleIO.AutocompleteHandler; } else { @@ -230,7 +230,7 @@ namespace MinecraftClient return; - Retry: + Retry: if (timeoutdetector != null) { timeoutdetector.Item2.Cancel(); @@ -247,7 +247,7 @@ namespace MinecraftClient { ConsoleInteractive.ConsoleReader.StopReadThread(); ConsoleInteractive.ConsoleReader.MessageReceived -= ConsoleReaderOnMessageReceived; - ConsoleInteractive.ConsoleReader.OnKeyInput -= ConsoleIO.AutocompleteHandler; + ConsoleInteractive.ConsoleReader.OnInputChange -= ConsoleIO.AutocompleteHandler; Program.HandleFailure(); } @@ -456,6 +456,8 @@ namespace MinecraftClient /// public void OnConnectionLost(ChatBot.DisconnectReason reason, string message) { + ConsoleIO.CancelAutocomplete(); + handler.Dispose(); world.Clear(); @@ -514,7 +516,7 @@ namespace MinecraftClient { ConsoleInteractive.ConsoleReader.StopReadThread(); ConsoleInteractive.ConsoleReader.MessageReceived -= ConsoleReaderOnMessageReceived; - ConsoleInteractive.ConsoleReader.OnKeyInput -= ConsoleIO.AutocompleteHandler; + ConsoleInteractive.ConsoleReader.OnInputChange -= ConsoleIO.AutocompleteHandler; Program.HandleFailure(); } } @@ -553,7 +555,9 @@ namespace MinecraftClient switch (command[0].ToLower()) { case "autocomplete": - if (command.Length > 1) { ConsoleIO.WriteLine((char)0x00 + "autocomplete" + (char)0x00 + handler.AutoComplete(command[1])); } + int id = handler.AutoComplete(command[1]); + while (!ConsoleIO.AutoCompleteDone) { Thread.Sleep(100); } + if (command.Length > 1) { ConsoleIO.WriteLine((char)0x00 + "autocomplete" + (char)0x00 + ConsoleIO.AutoCompleteResult); } else ConsoleIO.WriteLine((char)0x00 + "autocomplete" + (char)0x00); break; } @@ -561,70 +565,44 @@ namespace MinecraftClient else { text = text.Trim(); - if (text.Length > 0) + + if (text.Length > 1 + && Config.Main.Advanced.InternalCmdChar == MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none + && text[0] == '/') { - if (Config.Main.Advanced.InternalCmdChar.ToChar() == ' ' || text[0] == Config.Main.Advanced.InternalCmdChar.ToChar()) + SendText(text); + } + else if (text.Length > 2 + && Config.Main.Advanced.InternalCmdChar != MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none + && text[0] == Config.Main.Advanced.InternalCmdChar.ToChar() + && text[1] == '/') + { + SendText(text[1..]); + } + else if (text.Length > 0) + { + if (Config.Main.Advanced.InternalCmdChar == MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none + || text[0] == Config.Main.Advanced.InternalCmdChar.ToChar()) { - string? response_msg = ""; + CmdResult result = new(); string command = Config.Main.Advanced.InternalCmdChar.ToChar() == ' ' ? text : text[1..]; - if (!PerformInternalCommand(Config.AppVar.ExpandVars(command), ref response_msg, Settings.Config.AppVar.GetVariables()) && Config.Main.Advanced.InternalCmdChar.ToChar() == '/') + if (!PerformInternalCommand(Config.AppVar.ExpandVars(command), ref result, Settings.Config.AppVar.GetVariables()) && Config.Main.Advanced.InternalCmdChar.ToChar() == '/') { SendText(text); } - else if (!String.IsNullOrEmpty(response_msg)) + else if (result.status != CmdResult.Status.NotRun && (result.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(result.result))) { - Log.Info(response_msg); + Log.Info(result); } } - else SendText(text); + else + { + SendText(text); + } } } } - /// - /// Register a custom console command - /// - /// Name of the command - /// Description/usage of the command - /// Method for handling the command - /// True if successfully registered - public bool RegisterCommand(string cmdName, string cmdDesc, string cmdUsage, ChatBot.CommandRunner callback) - { - // if (cmds.ContainsKey(cmdName.ToLower())) - // { - // return false; - // } - // else - // { - // Command cmd = new ChatBot.ChatBotCommand(cmdName, cmdDesc, cmdUsage, callback); - // cmds.Add(cmdName.ToLower(), cmd); - // cmd_names.Add(cmdName.ToLower()); - // return true; - // } - return true; - } - - /// - /// Unregister a console command - /// - /// - /// There is no check for the command is registered by above method or is embedded command. - /// Which mean this can unload any command - /// - /// The name of command to be unregistered - /// - public bool UnregisterCommand(string cmdName) - { - // if (cmds.ContainsKey(cmdName.ToLower())) - // { - // cmds.Remove(cmdName.ToLower()); - // cmd_names.Remove(cmdName.ToLower()); - // return true; - // } - // else return false; - return true; - } - /// /// Perform an internal MCC command (not a server command, use SendText() instead for that!) /// @@ -632,82 +610,55 @@ namespace MinecraftClient /// May contain a confirmation or error message after processing the command, or "" otherwise. /// Local variables passed along with the command /// TRUE if the command was indeed an internal MCC command - public bool PerformInternalCommand(string command, ref string? response_msg, Dictionary? localVars = null) + public bool PerformInternalCommand(string command, ref CmdResult result, Dictionary? localVars = null) { /* Process the provided command */ - ParseResults parse; + ParseResults parse; try { - parse = dispatcher.Parse(command, cmd_source); + parse = dispatcher.Parse(command, result); } catch (Exception e) { - Log.Error(e.GetFullMessage()); - return true; + Log.Debug("Parse fail: " + e.GetFullMessage()); + return false; } try { - int res = dispatcher.Execute(parse); - Log.Info("res = " + res); + dispatcher.Execute(parse); + + foreach (ChatBot bot in bots.ToArray()) + { + try + { + bot.OnInternalCommand(command, string.Join(" ", Command.GetArgs(command)), result); + } + catch (Exception e) + { + if (e is not ThreadAbortException) + { + Log.Warn(string.Format(Translations.icmd_error, bot.ToString() ?? string.Empty, e.ToString())); + } + else throw; //ThreadAbortException should not be caught + } + } + return true; } catch (CommandSyntaxException e) { - Log.Warn(e.GetFullMessage()); - return true; + if (parse.Context.Nodes.Count == 0) + { + return false; + } + else + { + Log.Info("§e" + e.Message ?? e.StackTrace ?? "Incorrect argument."); + Log.Info(dispatcher.GetAllUsageString(parse.Context.Nodes[0].Node.Name, false)); + return true; + } } - catch (Exception e) - { - Log.Error(e.GetFullMessage()); - return true; - } - - //string command_name = command.Split(' ')[0].ToLower(); - //if (command_name == "help") - //{ - // if (Command.HasArg(command)) - // { - // string help_cmdname = Command.GetArgs(command)[0].ToLower(); - // if (help_cmdname == "help") - // { - // response_msg = Translations.icmd_help; - // } - // else if (cmds.ContainsKey(help_cmdname)) - // { - // response_msg = cmds[help_cmdname].GetCmdDescTranslated(); - // } - // else response_msg = string.Format(Translations.icmd_unknown, command_name); - // } - // else response_msg = string.Format(Translations.icmd_list, String.Join(", ", cmd_names.ToArray()), Config.Main.Advanced.InternalCmdChar.ToChar()); - //} - //else if (cmds.ContainsKey(command_name)) - //{ - // response_msg = cmds[command_name].Run(this, command, localVars); - - // foreach (ChatBot bot in bots.ToArray()) - // { - // try - // { - // bot.OnInternalCommand(command_name, string.Join(" ", Command.GetArgs(command)), response_msg); - // } - // catch (Exception e) - // { - // if (e is not ThreadAbortException) - // { - // Log.Warn(string.Format(Translations.icmd_error, bot.ToString() ?? string.Empty, e.ToString())); - // } - // else throw; //ThreadAbortException should not be caught - // } - // } - //} - //else - //{ - // response_msg = string.Format(Translations.icmd_unknown, command_name); - // return false; - //} - - //return true; } public void LoadCommands() @@ -725,10 +676,6 @@ namespace MinecraftClient { Command cmd = (Command)Activator.CreateInstance(type)!; cmd.RegisterCommand(this, dispatcher); - // cmds[Settings.ToLowerIfNeed(cmd.CmdName)] = cmd; - // cmd_names.Add(Settings.ToLowerIfNeed(cmd.CmdName)); - // foreach (string alias in cmd.GetCMDAliases()) - // cmds[Settings.ToLowerIfNeed(alias)] = cmd; } catch (Exception e) { @@ -863,7 +810,7 @@ namespace MinecraftClient b.SetHandler(this); bots.Add(b); if (init) - DispatchBotEvent(bot => bot.Initialize(), new ChatBot[] { b }); + DispatchBotEvent(bot => bot.Initialize(dispatcher), new ChatBot[] { b }); if (handler != null) DispatchBotEvent(bot => bot.AfterGameJoined(), new ChatBot[] { b }); } @@ -879,8 +826,9 @@ namespace MinecraftClient return; } - b.OnUnload(); - bots.RemoveAll(item => object.ReferenceEquals(item, b)); + b.OnUnload(dispatcher); + + bots.RemoveAll(item => ReferenceEquals(item, b)); // ToList is needed to avoid an InvalidOperationException from modfiying the list while it's being iterated upon. var botRegistrations = registeredBotPluginChannels.Where(entry => entry.Value.Contains(b)).ToList(); @@ -2449,6 +2397,8 @@ namespace MinecraftClient ClearInventories(); DispatchBotEvent(bot => bot.AfterGameJoined()); + + ConsoleIO.InitAutocomplete(); } /// @@ -3471,6 +3421,16 @@ namespace MinecraftClient DispatchBotEvent(bot => bot.OnBlockChange(location, block)); } + /// + /// Called when "AutoComplete" completes. + /// + /// The number of this result. + /// All commands. + public void OnAutoCompleteDone(int transactionId, string[] result) + { + ConsoleIO.OnAutoCompleteDone(transactionId, result); + } + /// /// Send a click container button packet to the server. /// Used for Enchanting table, Lectern, stone cutter and loom diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index e6f2477c..f0a01aaa 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -28,20 +28,21 @@ - + - - + + + - - + + NU1701 - + diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index d8ae2250..52aaf0f4 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -14,8 +14,9 @@ using MinecraftClient.Mapping.BlockPalettes; using MinecraftClient.Mapping.EntityPalettes; using MinecraftClient.Protocol; using MinecraftClient.Protocol.Handlers.Forge; -using MinecraftClient.Protocol.Keys; +using MinecraftClient.Protocol.ProfileKey; using MinecraftClient.Protocol.Session; +using MinecraftClient.Scripting; using MinecraftClient.WinAPI; using Tomlet; using static MinecraftClient.Settings; @@ -573,7 +574,7 @@ namespace MinecraftClient } //Retrieve server info if version is not manually set OR if need to retrieve Forge information - if (!isRealms && (protocolversion == 0 || (Config.Main.Advanced.EnableForge == ForgeConfigType.auto) || + if (!isRealms && (protocolversion == 0 || (Config.Main.Advanced.EnableForge == ForgeConfigType.auto) || ((Config.Main.Advanced.EnableForge == ForgeConfigType.force) && !ProtocolHandler.ProtocolMayForceForge(protocolversion)))) { if (protocolversion != 0) @@ -587,9 +588,9 @@ namespace MinecraftClient } } - if (Config.Main.General.AccountType == LoginType.microsoft + if (Config.Main.General.AccountType == LoginType.microsoft && (InternalConfig.Account.Password != "-" || Config.Main.General.Method == LoginMethod.browser) - && Config.Signature.LoginWithSecureProfile + && Config.Signature.LoginWithSecureProfile && protocolversion >= 759 /* 1.19 and above */) { // Load cached profile key from disk if necessary @@ -683,7 +684,7 @@ namespace MinecraftClient /// public static void ReloadSettings(bool keepAccountAndServerSettings = false) { - if(Settings.LoadFromFile(settingsIniPath, keepAccountAndServerSettings).Item1) + if (Settings.LoadFromFile(settingsIniPath, keepAccountAndServerSettings).Item1) ConsoleIO.WriteLine(string.Format(Translations.config_load, settingsIniPath)); } @@ -720,6 +721,7 @@ namespace MinecraftClient public static void DoExit(int exitcode = 0) { WriteBackSettings(true); + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); ConsoleIO.WriteLineFormatted(string.Format(Translations.config_saving, settingsIniPath)); if (client != null) { client.Disconnect(); ConsoleIO.Reset(); } @@ -807,7 +809,7 @@ namespace MinecraftClient if (command.StartsWith("reco")) { - message = new Commands.Reco().Run(null, Config.AppVar.ExpandVars(command), null); + message = Commands.Reco.DoReconnect(Config.AppVar.ExpandVars(command)); if (message == "") { exitThread = true; @@ -816,7 +818,7 @@ namespace MinecraftClient } else if (command.StartsWith("connect")) { - message = new Commands.Connect().Run(null, Config.AppVar.ExpandVars(command), null); + message = Commands.Connect.DoConnect(Config.AppVar.ExpandVars(command)); if (message == "") { exitThread = true; @@ -825,7 +827,7 @@ namespace MinecraftClient } else if (command.StartsWith("exit") || command.StartsWith("quit")) { - message = new Commands.Exit().Run(null, Config.AppVar.ExpandVars(command), null); + message = Commands.Exit.DoExit(Config.AppVar.ExpandVars(command)); } else if (command.StartsWith("help")) { @@ -844,7 +846,7 @@ namespace MinecraftClient } else { - _ = new Commands.Exit().Run(null, Config.AppVar.ExpandVars(command), null); + Commands.Exit.DoExit(Config.AppVar.ExpandVars(command)); } } diff --git a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs index a850dd2a..22835b61 100644 --- a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs +++ b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs @@ -54,7 +54,22 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c Nodes[i] = new(flags, childs, redirectNode, name, paser, suggestionsType); } - RootIdx = dataTypes.ReadNextVarInt(packetData); + RootIdx = dataTypes.ReadNextVarInt(packetData); + + ConsoleIO.OnDeclareMinecraftCommand(ExtractRootCommand()); + } + + private static string[] ExtractRootCommand() + { + List commands = new(); + CommandNode root = Nodes[RootIdx]; + foreach (var child in root.Clildren) + { + string? childName = Nodes[child].Name; + if (childName != null) + commands.Add(childName); + } + return commands.ToArray(); } public static List> CollectSignArguments(string command) @@ -113,7 +128,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c public string? Name; public Paser? Paser; public string? SuggestionsType; - + public CommandNode(byte Flags, int[] Clildren, diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index 9db07050..1bf99fdc 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -9,10 +9,11 @@ using System.Threading; using MinecraftClient.Crypto; using MinecraftClient.Inventory; using MinecraftClient.Mapping; -using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; +using MinecraftClient.Protocol.ProfileKey; using MinecraftClient.Protocol.Session; using MinecraftClient.Proxy; +using MinecraftClient.Scripting; using static MinecraftClient.Settings; namespace MinecraftClient.Protocol.Handlers @@ -24,8 +25,6 @@ namespace MinecraftClient.Protocol.Handlers class Protocol16Handler : IMinecraftCom { readonly IMinecraftComHandler handler; - private bool autocomplete_received = false; - private string autocomplete_result = ""; private bool encrypted = false; private readonly int protocolversion; private Tuple? netRead = null; @@ -193,7 +192,14 @@ namespace MinecraftClient.Protocol.Handlers if (online) { handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); } else { handler.OnPlayerLeave(FakeUUID); } break; case 0xCA: if (protocolversion >= 72) { ReadData(9); } else ReadData(3); break; - case 0xCB: autocomplete_result = ReadNextString(); autocomplete_received = true; break; + case 0xCB: + string resultString = ReadNextString(); + if (!string.IsNullOrEmpty(resultString)) + { + string[] result = resultString.Split((char)0x00); + handler.OnAutoCompleteDone(0, result); + } + break; case 0xCC: ReadNextString(); ReadData(4); break; case 0xCD: ReadData(1); break; case 0xCE: if (protocolversion > 51) { ReadNextString(); ReadNextString(); ReadData(1); } break; @@ -820,27 +826,21 @@ namespace MinecraftClient.Protocol.Handlers catch (System.IO.IOException) { return false; } } - IEnumerable IAutoComplete.AutoComplete(string BehindCursor) + int IAutoComplete.AutoComplete(string BehindCursor) { if (String.IsNullOrEmpty(BehindCursor)) - return Array.Empty(); + return -1; byte[] autocomplete = new byte[3 + (BehindCursor.Length * 2)]; autocomplete[0] = 0xCB; byte[] msglen = BitConverter.GetBytes((short)BehindCursor.Length); - Array.Reverse(msglen); msglen.CopyTo(autocomplete, 1); + Array.Reverse(msglen); + msglen.CopyTo(autocomplete, 1); byte[] msg = Encoding.BigEndianUnicode.GetBytes(BehindCursor); msg.CopyTo(autocomplete, 3); - - autocomplete_received = false; - autocomplete_result = BehindCursor; + ConsoleIO.AutoCompleteDone = false; Send(autocomplete); - - int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms) - while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; } - if (!String.IsNullOrEmpty(autocomplete_result) && autocomplete_received) - ConsoleIO.WriteLineFormatted("§8" + autocomplete_result.Replace((char)0x00, ' '), false); - return autocomplete_result.Split((char)0x00); + return 0; } private static byte[] ConcatBytes(params byte[][] bytes) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 5f0f855d..de33417e 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -19,10 +19,11 @@ using MinecraftClient.Mapping.EntityPalettes; using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Protocol.Handlers.packet.s2c; using MinecraftClient.Protocol.Handlers.PacketPalettes; -using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; +using MinecraftClient.Protocol.ProfileKey; using MinecraftClient.Protocol.Session; using MinecraftClient.Proxy; +using MinecraftClient.Scripting; using static MinecraftClient.Settings; namespace MinecraftClient.Protocol.Handlers @@ -64,9 +65,7 @@ namespace MinecraftClient.Protocol.Handlers internal const int MC_1_19_2_Version = 760; private int compression_treshold = 0; - private bool autocomplete_received = false; private int autocomplete_transaction_id = 0; - private readonly List autocomplete_result = new(); private readonly Dictionary window_actions = new(); private bool login_phase = true; private readonly int protocolVersion; @@ -1325,6 +1324,7 @@ namespace MinecraftClient.Protocol.Handlers } break; case PacketTypesIn.TabComplete: + int old_transaction_id = autocomplete_transaction_id; if (protocolVersion >= MC_1_13_Version) { autocomplete_transaction_id = dataTypes.ReadNextVarInt(packetData); @@ -1333,20 +1333,19 @@ namespace MinecraftClient.Protocol.Handlers } int autocomplete_count = dataTypes.ReadNextVarInt(packetData); - autocomplete_result.Clear(); + string[] autocomplete_result = new string[autocomplete_count]; for (int i = 0; i < autocomplete_count; i++) { - autocomplete_result.Add(dataTypes.ReadNextString(packetData)); + autocomplete_result[i] = dataTypes.ReadNextString(packetData); if (protocolVersion >= MC_1_13_Version) { - // Skip optional tooltip for each tab-complete result + // Skip optional tooltip for each tab-complete resul`t if (dataTypes.ReadNextBool(packetData)) dataTypes.SkipNextString(packetData); } } - - autocomplete_received = true; + handler.OnAutoCompleteDone(old_transaction_id, autocomplete_result); break; case PacketTypesIn.PluginMessage: String channel = dataTypes.ReadNextString(packetData); @@ -2110,18 +2109,10 @@ namespace MinecraftClient.Protocol.Handlers /// /// Text behind cursor /// Completed text - IEnumerable IAutoComplete.AutoComplete(string BehindCursor) + int IAutoComplete.AutoComplete(string BehindCursor) { - var sug = McClient.dispatcher.GetCompletionSuggestions(McClient.dispatcher.Parse(BehindCursor[1..], McClient.cmd_source)); - sug.Wait(); - foreach (var hint in sug.Result.List) - { - log.Info(hint); - } - //log.Info(McClient.dispatcher.GetSmartUsage(McClient.dispatcher.Parse(BehindCursor, McClient.cmd_source), McClient.cmd_source)); - - if (String.IsNullOrEmpty(BehindCursor)) - return Array.Empty(); + if (string.IsNullOrEmpty(BehindCursor)) + return -1; byte[] transaction_id = dataTypes.GetVarInt(autocomplete_transaction_id); byte[] assume_command = new byte[] { 0x00 }; @@ -2134,16 +2125,14 @@ namespace MinecraftClient.Protocol.Handlers if (protocolVersion >= MC_1_13_Version) { tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, transaction_id); - tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor)); + tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor.Replace(' ', (char)0x00))); } else { tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor)); if (protocolVersion >= MC_1_9_Version) - { tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, assume_command); - } tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, has_position); } @@ -2152,22 +2141,9 @@ namespace MinecraftClient.Protocol.Handlers { tabcomplete_packet = dataTypes.ConcatBytes(dataTypes.GetString(BehindCursor)); } - - autocomplete_received = false; - autocomplete_result.Clear(); - autocomplete_result.Add(BehindCursor); + ConsoleIO.AutoCompleteDone = false; SendPacket(PacketTypesOut.TabComplete, tabcomplete_packet); - - int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms) - ThreadStart start = new(delegate - { - while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; } - if (autocomplete_result.Count > 0) - ConsoleIO.WriteLineFormatted("§8" + String.Join(" ", autocomplete_result), false); - }); - Thread t1 = new(start); - t1.Start(); - return autocomplete_result; + return autocomplete_transaction_id; } /// diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs index 382ba5c5..922496d1 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Text; using System.Threading; using MinecraftClient.Protocol.Handlers.Forge; +using MinecraftClient.Protocol.Message; +using MinecraftClient.Scripting; namespace MinecraftClient.Protocol.Handlers { diff --git a/MinecraftClient/Protocol/IMinecraftCom.cs b/MinecraftClient/Protocol/IMinecraftCom.cs index afe5b1ba..5597dafb 100644 --- a/MinecraftClient/Protocol/IMinecraftCom.cs +++ b/MinecraftClient/Protocol/IMinecraftCom.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using MinecraftClient.Inventory; using MinecraftClient.Mapping; -using MinecraftClient.Protocol.Keys; +using MinecraftClient.Protocol.ProfileKey; namespace MinecraftClient.Protocol { diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 1ac38a39..2e8986bd 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -4,6 +4,7 @@ using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; using MinecraftClient.Protocol.Message; +using MinecraftClient.Scripting; namespace MinecraftClient.Protocol { @@ -456,6 +457,13 @@ namespace MinecraftClient.Protocol /// The block public void OnBlockChange(Location location, Block block); + /// + /// Called when "AutoComplete" completes. + /// + /// The number of this result. + /// All commands. + public void OnAutoCompleteDone(int transactionId, string[] result); + /// /// Send a click container button packet to the server. /// Used for Enchanting table, Lectern, stone cutter and loom diff --git a/MinecraftClient/Protocol/Message/ChatParser.cs b/MinecraftClient/Protocol/Message/ChatParser.cs index 47e02256..5255e957 100644 --- a/MinecraftClient/Protocol/Message/ChatParser.cs +++ b/MinecraftClient/Protocol/Message/ChatParser.cs @@ -4,10 +4,9 @@ using System.IO; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using MinecraftClient.Protocol.Message; using static MinecraftClient.Settings; -namespace MinecraftClient.Protocol +namespace MinecraftClient.Protocol.Message { /// /// This class parses JSON chat data from MC 1.6+ and returns the appropriate string to be printed. @@ -223,7 +222,7 @@ namespace MinecraftClient.Protocol HttpClient httpClient = new(); try { - Task fetch_index = httpClient.GetStringAsync(Settings.TranslationsFile_Website_Index); + Task fetch_index = httpClient.GetStringAsync(TranslationsFile_Website_Index); fetch_index.Wait(); string assets_index = fetch_index.Result; fetch_index.Dispose(); @@ -231,8 +230,8 @@ namespace MinecraftClient.Protocol string[] tmp = assets_index.Split(new string[] { "minecraft/lang/" + Config.Main.Advanced.Language.ToLower() + ".json" }, StringSplitOptions.None); tmp = tmp[1].Split(new string[] { "hash\": \"" }, StringSplitOptions.None); string hash = tmp[1].Split('"')[0]; //Translations file identifier on Mojang's servers - string translation_file_location = Settings.TranslationsFile_Website_Download + '/' + hash[..2] + '/' + hash; - if (Settings.Config.Logging.DebugMessages) + string translation_file_location = TranslationsFile_Website_Download + '/' + hash[..2] + '/' + hash; + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.chat_request, translation_file_location)); Task fetch_file = httpClient.GetStringAsync(translation_file_location); @@ -242,7 +241,7 @@ namespace MinecraftClient.Protocol StringBuilder stringBuilder = new(); foreach (KeyValuePair entry in Json.ParseJson(translation_file).Properties) - stringBuilder.Append(entry.Key).Append('=').Append(entry.Value.StringValue.Replace("\n", "\\n").Replace("\r", String.Empty)).Append(Environment.NewLine); + stringBuilder.Append(entry.Key).Append('=').Append(entry.Value.StringValue.Replace("\n", "\\n").Replace("\r", string.Empty)).Append(Environment.NewLine); File.WriteAllText(Language_File, stringBuilder.ToString()); ConsoleIO.WriteLineFormatted(string.Format(Translations.chat_done, Language_File)); @@ -256,9 +255,9 @@ namespace MinecraftClient.Protocol //Download Failed? Defaulting to en_GB.lang if the game is installed if (!File.Exists(Language_File) //Try en_GB.lang - && File.Exists(Settings.TranslationsFile_FromMCDir)) + && File.Exists(TranslationsFile_FromMCDir)) { - Language_File = Settings.TranslationsFile_FromMCDir; + Language_File = TranslationsFile_FromMCDir; ConsoleIO.WriteLineFormatted(Translations.chat_from_dir, acceptnewlines: true); } @@ -277,7 +276,7 @@ namespace MinecraftClient.Protocol } } - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.chat_loaded, acceptnewlines: true); } else //No external dictionnary found. diff --git a/MinecraftClient/Protocol/MojangAPI.cs b/MinecraftClient/Protocol/MojangAPI.cs index e0dade07..8ee28b31 100644 --- a/MinecraftClient/Protocol/MojangAPI.cs +++ b/MinecraftClient/Protocol/MojangAPI.cs @@ -214,7 +214,7 @@ namespace MinecraftClient.Protocol fetchTask.Dispose(); } catch (Exception) - { + { return new MojangServiceStatus(); } diff --git a/MinecraftClient/Protocol/PlayerInfo.cs b/MinecraftClient/Protocol/PlayerInfo.cs index b77aea58..31e4300e 100644 --- a/MinecraftClient/Protocol/PlayerInfo.cs +++ b/MinecraftClient/Protocol/PlayerInfo.cs @@ -1,7 +1,7 @@ using System; using System.Linq; -using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; +using MinecraftClient.Protocol.ProfileKey; namespace MinecraftClient.Protocol { diff --git a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs index 94cce827..fdd68560 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs @@ -4,7 +4,7 @@ using System.Security.Cryptography; using System.Text; using MinecraftClient.Protocol.Message; -namespace MinecraftClient.Protocol.Keys +namespace MinecraftClient.Protocol.ProfileKey { static class KeyUtils { @@ -45,7 +45,7 @@ namespace MinecraftClient.Protocol.Keys } catch (Exception e) { - int code = (response == null) ? 0 : response.StatusCode; + int code = response == null ? 0 : response.StatusCode; ConsoleIO.WriteLineFormatted("§cFetch profile key failed: HttpCode = " + code + ", Error = " + e.Message); if (Settings.Config.Logging.DebugMessages) { @@ -55,7 +55,7 @@ namespace MinecraftClient.Protocol.Keys } } - public static byte[] DecodePemKey(String key, String prefix, String suffix) + public static byte[] DecodePemKey(string key, string prefix, string suffix) { int i = key.IndexOf(prefix); if (i != -1) @@ -64,8 +64,8 @@ namespace MinecraftClient.Protocol.Keys int j = key.IndexOf(suffix, i); key = key[i..j]; } - key = key.Replace("\r", String.Empty); - key = key.Replace("\n", String.Empty); + key = key.Replace("\r", string.Empty); + key = key.Replace("\n", string.Empty); return Convert.FromBase64String(key); } @@ -135,11 +135,11 @@ namespace MinecraftClient.Protocol.Keys char c = src[i]; bool needEscape = c < 32 || c == '"' || c == '\\'; // Broken lead surrogate - needEscape = needEscape || (c >= '\uD800' && c <= '\uDBFF' && - (i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF')); + needEscape = needEscape || c >= '\uD800' && c <= '\uDBFF' && + (i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF'); // Broken tail surrogate - needEscape = needEscape || (c >= '\uDC00' && c <= '\uDFFF' && - (i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF')); + needEscape = needEscape || c >= '\uDC00' && c <= '\uDFFF' && + (i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF'); // To produce valid JavaScript needEscape = needEscape || c == '\u2028' || c == '\u2029'; diff --git a/MinecraftClient/Protocol/ProfileKey/KeysCache.cs b/MinecraftClient/Protocol/ProfileKey/KeysCache.cs index 9962e2aa..4b052f96 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeysCache.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeysCache.cs @@ -6,7 +6,7 @@ using System.Timers; using static MinecraftClient.Settings; using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig; -namespace MinecraftClient.Protocol.Keys +namespace MinecraftClient.Protocol.ProfileKey { /// /// Handle keys caching and storage. @@ -115,7 +115,7 @@ namespace MinecraftClient.Protocol.Keys //User-editable keys cache file in text format if (File.Exists(KeysCacheFilePlaintext)) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loading_keys, KeysCacheFilePlaintext)); try @@ -134,27 +134,27 @@ namespace MinecraftClient.Protocol.Keys { PlayerKeyPair playerKeyPair = PlayerKeyPair.FromString(value); keys[login] = playerKeyPair; - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loaded_keys, playerKeyPair.ExpiresAt.ToString())); } catch (InvalidDataException e) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_string_keys, value, e.Message)); } catch (FormatException e) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_string_keys, value, e.Message)); } catch (ArgumentNullException e) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_string_keys, value, e.Message)); } } - else if (Settings.Config.Logging.DebugMessages) + else if (Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_line_keys, line)); } diff --git a/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs b/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs index d7b0fe39..572b0d06 100644 --- a/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs +++ b/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.IO; -namespace MinecraftClient.Protocol.Keys +namespace MinecraftClient.Protocol.ProfileKey { public class PlayerKeyPair { @@ -74,17 +74,17 @@ namespace MinecraftClient.Protocol.Keys List datas = new(); datas.Add(Convert.ToBase64String(PublicKey.Key)); if (PublicKey.Signature == null) - datas.Add(String.Empty); + datas.Add(string.Empty); else datas.Add(Convert.ToBase64String(PublicKey.Signature)); if (PublicKey.SignatureV2 == null) - datas.Add(String.Empty); + datas.Add(string.Empty); else datas.Add(Convert.ToBase64String(PublicKey.SignatureV2)); datas.Add(Convert.ToBase64String(PrivateKey.Key)); datas.Add(ExpiresAt.ToString(DataTimeFormat)); datas.Add(RefreshedAfter.ToString(DataTimeFormat)); - return String.Join(",", datas.ToArray()); + return string.Join(",", datas.ToArray()); } } } diff --git a/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs b/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs index b3dd4093..6cb8103b 100644 --- a/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs +++ b/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs @@ -2,7 +2,7 @@ using System.Security.Cryptography; using MinecraftClient.Protocol.Message; -namespace MinecraftClient.Protocol.Keys +namespace MinecraftClient.Protocol.ProfileKey { public class PrivateKey { diff --git a/MinecraftClient/Protocol/ProfileKey/PublicKey.cs b/MinecraftClient/Protocol/ProfileKey/PublicKey.cs index 429c3962..2f01fa84 100644 --- a/MinecraftClient/Protocol/ProfileKey/PublicKey.cs +++ b/MinecraftClient/Protocol/ProfileKey/PublicKey.cs @@ -2,7 +2,7 @@ using System.Security.Cryptography; using MinecraftClient.Protocol.Message; -namespace MinecraftClient.Protocol.Keys +namespace MinecraftClient.Protocol.ProfileKey { public class PublicKey { diff --git a/MinecraftClient/Protocol/Session/SessionToken.cs b/MinecraftClient/Protocol/Session/SessionToken.cs index e48567d9..c3c0aee9 100644 --- a/MinecraftClient/Protocol/Session/SessionToken.cs +++ b/MinecraftClient/Protocol/Session/SessionToken.cs @@ -2,6 +2,7 @@ using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; +using MinecraftClient.Scripting; namespace MinecraftClient.Protocol.Session { diff --git a/MinecraftClient/Resources/Translations/Translations.Designer.cs b/MinecraftClient/Resources/Translations/Translations.Designer.cs index 3f5208f1..fd93a259 100644 --- a/MinecraftClient/Resources/Translations/Translations.Designer.cs +++ b/MinecraftClient/Resources/Translations/Translations.Designer.cs @@ -151,7 +151,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Available commands: {0}. Use /autocraft help <cmd name> for more information. You may use /ac as command alias.. + /// Looks up a localized string similar to Available commands: {0}. Use /autocraft help <cmd name> for more information.. /// internal static string bot_autoCraft_available_cmd { get { @@ -403,7 +403,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Available commands: {0}. Use /digbot help <cmd name> for more information.. + /// Looks up a localized string similar to Available commands: {0}. Use /autodig help <cmd name> for more information.. /// internal static string bot_autodig_available_cmd { get { @@ -430,7 +430,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Get the command description. Usage: /digbot help <command name>. + /// Looks up a localized string similar to Get the command description. Usage: /autodig help <command name>. /// internal static string bot_autodig_help_help { get { @@ -2488,7 +2488,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Searching for a bed in radius of {0}.... + /// Looks up a localized string similar to Searching for a bed in radius of {0:0.00}.... /// internal static string cmd_bed_searching { get { @@ -3599,7 +3599,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Unknown action. . + /// Looks up a localized string similar to Unknown action.. /// internal static string cmd_inventory_help_unknown { get { @@ -4068,7 +4068,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to setrnd variable -7to17 OR setrnd variable string1 "\"string2\" string3". + /// Looks up a localized string similar to setrnd variable -7 to 17 OR setrnd variable string1 "\"string2\" string3". /// internal static string cmd_setrnd_format { get { @@ -4077,7 +4077,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to setrnd variable -7to17. + /// Looks up a localized string similar to setrnd variable -7 to 17. /// internal static string cmd_setrndnum_format { get { @@ -6875,7 +6875,8 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to help <cmdname>. Available commands: {0}. For server help, use '{1}send /help' instead.. + /// Looks up a localized string similar to help <cmdname>. Available commands: + ///{0}For server help, use '{1}send /help' instead.. /// internal static string icmd_list { get { diff --git a/MinecraftClient/Resources/Translations/Translations.resx b/MinecraftClient/Resources/Translations/Translations.resx index 8e041df0..2beab3d2 100644 --- a/MinecraftClient/Resources/Translations/Translations.resx +++ b/MinecraftClient/Resources/Translations/Translations.resx @@ -148,7 +148,7 @@ Auto-crafting ChatBot command alias - Available commands: {0}. Use /autocraft help <cmd name> for more information. You may use /ac as command alias. + Available commands: {0}. Use /autocraft help <cmd name> for more information. Inventory #{0} was closed by AutoCraft @@ -232,7 +232,7 @@ Action timeout! Reason: {0} - Available commands: {0}. Use /digbot help <cmd name> for more information. + Available commands: {0}. Use /autodig help <cmd name> for more information. Auto-digging ChatBot command @@ -241,7 +241,7 @@ Digging block timeout, retry. - Get the command description. Usage: /digbot help <command name> + Get the command description. Usage: /autodig help <command name> Start the automatic digging bot. @@ -937,7 +937,7 @@ Some messages won't be properly printed without this file. Could not lay in bed. Are you trying to sleep in a bed? (PS: You must use the head block coordinates of the bed) - Searching for a bed in radius of {0}... + Searching for a bed in radius of {0:0.00}... Trying to sleep in a bed on location (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}). Result: {3} @@ -1307,7 +1307,7 @@ Note that parameters in '[]' are optional. Shift click an item. - Unknown action. + Unknown action. Usage @@ -1461,7 +1461,7 @@ You can use "/chunk status {0:0.0} {1:0.0} {2:0.0}" to check the chunk loading s set a custom %variable% randomly to a given value. - setrnd variable -7to17 + setrnd variable -7 to 17 setrnd variable string1 "\"string2\" string3" @@ -2471,7 +2471,8 @@ If the connection to the Minecraft game server is blocked by the firewall, set E help <cmdname>: show brief help about a command. - help <cmdname>. Available commands: {0}. For server help, use '{1}send /help' instead. + help <cmdname>. Available commands: +{0}For server help, use '{1}send /help' instead. Unknown command '{0}'. Use 'help' for command list. @@ -2644,7 +2645,7 @@ Logging in... Load translations applied to MCC when available, turn it off to use English only. - setrnd variable -7to17 OR setrnd variable string1 "\"string2\" string3" + setrnd variable -7 to 17 OR setrnd variable string1 "\"string2\" string3" Please update health field handling for this Minecraft version. diff --git a/MinecraftClient/Scripting/AssemblyResolver.cs b/MinecraftClient/Scripting/AssemblyResolver.cs index f9f970ea..85b16405 100644 --- a/MinecraftClient/Scripting/AssemblyResolver.cs +++ b/MinecraftClient/Scripting/AssemblyResolver.cs @@ -4,34 +4,36 @@ using System.Reflection; namespace MinecraftClient.Scripting; -public static class AssemblyResolver { +public static class AssemblyResolver +{ private static Dictionary ScriptAssemblies = new(); - static AssemblyResolver() { + static AssemblyResolver() + { // Manually resolve assemblies that .NET can't resolve automatically. - AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => + AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { - var asmReqName = new AssemblyName(args.Name); - - // Check the script-referenced assemblies if we have the DLL that is required. - foreach (var dll in ScriptAssemblies) - { - // If we have the assembly, load it. - if (asmReqName.FullName == dll.Key) - { - return Assembly.LoadFile(dll.Value); - } - } + var asmReqName = new AssemblyName(args.Name); - ConsoleIO.WriteLogLine($"[Script Error] Failed to resolve assembly {args.Name} (are you missing a DLL file?)"); - return null; - }; + // Check the script-referenced assemblies if we have the DLL that is required. + foreach (var dll in ScriptAssemblies) + { + // If we have the assembly, load it. + if (asmReqName.FullName == dll.Key) + { + return Assembly.LoadFile(dll.Value); + } + } + + ConsoleIO.WriteLogLine($"[Script Error] Failed to resolve assembly {args.Name} (are you missing a DLL file?)"); + return null; + }; } - internal static void AddAssembly(string AssemblyFullName, string AssemblyPath) + internal static void AddAssembly(string AssemblyFullName, string AssemblyPath) { if (ScriptAssemblies.ContainsKey(AssemblyFullName)) return; - + ScriptAssemblies.Add(AssemblyFullName, AssemblyPath); } } \ No newline at end of file diff --git a/MinecraftClient/Scripting/CSharpRunner.cs b/MinecraftClient/Scripting/CSharpRunner.cs index 026a29fa..1d6f258a 100644 --- a/MinecraftClient/Scripting/CSharpRunner.cs +++ b/MinecraftClient/Scripting/CSharpRunner.cs @@ -4,10 +4,10 @@ using System.ComponentModel; using System.IO; using System.Linq; using System.Text; -using DynamicRun.Builder; +using MinecraftClient.Scripting.DynamicRun.Builder; using static MinecraftClient.Settings; -namespace MinecraftClient +namespace MinecraftClient.Scripting { /// /// C# Script runner - Compile on-the-fly and run C# scripts @@ -77,7 +77,7 @@ namespace MinecraftClient script.Add("return null;"); //Generate a class from the given script - string code = String.Join("\n", new string[] + string code = string.Join("\n", new string[] { "using System;", "using System.Collections.Generic;", @@ -90,15 +90,15 @@ namespace MinecraftClient "using MinecraftClient;", "using MinecraftClient.Mapping;", "using MinecraftClient.Inventory;", - String.Join("\n", libs), + string.Join("\n", libs), "namespace ScriptLoader {", "public class Script {", "public CSharpAPI MCC;", "public object __run(CSharpAPI __apiHandler, string[] args) {", "this.MCC = __apiHandler;", - String.Join("\n", script), + string.Join("\n", script), "}", - String.Join("\n", extensions), + string.Join("\n", extensions), "}}", }); @@ -108,11 +108,13 @@ namespace MinecraftClient var result = compiler.Compile(code, Guid.NewGuid().ToString(), dlls); //Process compile warnings and errors - if (result.Failures != null) { - + if (result.Failures != null) + { + ConsoleIO.WriteLogLine("[Script] Compilation failed with error(s):"); - foreach (var failure in result.Failures) { + foreach (var failure in result.Failures) + { ConsoleIO.WriteLogLine($"[Script] Error in {scriptName}, line:col{failure.Location.GetMappedLineSpan()}: [{failure.Id}] {failure.GetMessage()}"); } @@ -226,7 +228,7 @@ namespace MinecraftClient /// TRUE if successfully sent (Deprectated, always returns TRUE for compatibility purposes with existing scripts) public bool SendText(object text) { - return base.SendText(text is string str ? str : (text.ToString() ?? string.Empty)); + return base.SendText(text is string str ? str : text.ToString() ?? string.Empty); } /// @@ -305,7 +307,7 @@ namespace MinecraftClient if (localVars != null && localVars.ContainsKey(varName)) return localVars[varName]; else - return Settings.Config.AppVar.GetVar(varName); + return Config.AppVar.GetVar(varName); } /// @@ -317,7 +319,7 @@ namespace MinecraftClient { if (localVars != null && localVars.ContainsKey(varName)) localVars.Remove(varName); - return Settings.Config.AppVar.SetVar(varName, varValue); + return Config.AppVar.SetVar(varName, varValue); } /// @@ -359,7 +361,7 @@ namespace MinecraftClient /// True if the account was found and loaded public bool SetAccount(string accountAlias, bool andReconnect = false) { - bool result = Settings.Config.Main.Advanced.SetAccount(accountAlias); + bool result = Config.Main.Advanced.SetAccount(accountAlias); if (result && andReconnect) ReconnectToTheServer(keepAccountAndServerSettings: true); return result; @@ -372,7 +374,7 @@ namespace MinecraftClient /// True if the server IP was valid and loaded, false otherwise public bool SetServer(string server, bool andReconnect = false) { - bool result = Settings.Config.Main.SetServerIP(new MainConfigHealper.MainConfig.ServerInfoConfig(server), true); + bool result = Config.Main.SetServerIP(new MainConfigHealper.MainConfig.ServerInfoConfig(server), true); if (result && andReconnect) ReconnectToTheServer(keepAccountAndServerSettings: true); return result; diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs index 61cd85c7..245a1a4a 100644 --- a/MinecraftClient/Scripting/ChatBot.cs +++ b/MinecraftClient/Scripting/ChatBot.cs @@ -5,12 +5,12 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using Brigadier.NET; -using MinecraftClient.Commands; +using MinecraftClient.CommandHandler; using MinecraftClient.Inventory; using MinecraftClient.Mapping; using static MinecraftClient.Settings; -namespace MinecraftClient +namespace MinecraftClient.Scripting { /// /// Welcome to the Bot API file ! @@ -42,7 +42,6 @@ namespace MinecraftClient private McClient? _handler = null; private ChatBot? master = null; private readonly List registeredPluginChannels = new(); - private readonly List registeredCommands = new(); private readonly object delayTasksLock = new(); private readonly List delayedTasks = new(); protected McClient Handler @@ -101,12 +100,12 @@ namespace MinecraftClient /// NOTE: Chat messages cannot be sent at this point in the login process. /// If you want to send a message when the bot is loaded, use AfterGameJoined. /// - public virtual void Initialize() { } + public virtual void Initialize(CommandDispatcher dispatcher) { } /// /// This method is called when the bot is being unloaded, you can use it to free up resources like DB connections /// - public virtual void OnUnload() { } + public virtual void OnUnload(CommandDispatcher dispatcher) { } /// /// Called after the server has been joined successfully and chat messages are able to be sent. @@ -175,13 +174,13 @@ namespace MinecraftClient /// Called when properties for the Player entity are received from the server /// /// Dictionary of player properties - public virtual void OnPlayerProperty(Dictionary prop) { } + public virtual void OnPlayerProperty(Dictionary prop) { } /// /// Called when server TPS are recalculated by MCC based on world time updates /// /// New estimated server TPS (between 0 and 20) - public virtual void OnServerTpsUpdate(Double tps) { } + public virtual void OnServerTpsUpdate(double tps) { } /// /// Called when a time changed @@ -194,7 +193,7 @@ namespace MinecraftClient /// Called when an entity moved nearby /// /// Entity with updated location - public virtual void OnEntityMove(Mapping.Entity entity) { } + public virtual void OnEntityMove(Entity entity) { } /// /// Called after an internal MCC command has been performed @@ -202,19 +201,19 @@ namespace MinecraftClient /// MCC Command Name /// MCC Command Parameters /// MCC command result - public virtual void OnInternalCommand(string commandName, string commandParams, string Result) { } + public virtual void OnInternalCommand(string commandName, string commandParams, CmdResult Result) { } /// /// Called when an entity spawned nearby /// /// New Entity - public virtual void OnEntitySpawn(Mapping.Entity entity) { } + public virtual void OnEntitySpawn(Entity entity) { } /// /// Called when an entity despawns/dies nearby /// /// Entity wich has just disappeared - public virtual void OnEntityDespawn(Mapping.Entity entity) { } + public virtual void OnEntityDespawn(Entity entity) { } /// /// Called when the player held item has changed @@ -515,7 +514,7 @@ namespace MinecraftClient /// TRUE if the command was indeed an internal MCC command protected bool PerformInternalCommand(string command, Dictionary? localVars = null) { - string? temp = ""; + CmdResult temp = new(); return Handler.PerformInternalCommand(command, ref temp, localVars); } @@ -526,9 +525,9 @@ namespace MinecraftClient /// May contain a confirmation or error message after processing the command, or "" otherwise. /// Local variables passed along with the command /// TRUE if the command was indeed an internal MCC command - protected bool PerformInternalCommand(string command, ref string? response_msg, Dictionary? localVars = null) + protected bool PerformInternalCommand(string command, ref CmdResult result, Dictionary? localVars = null) { - return Handler.PerformInternalCommand(command, ref response_msg, localVars); + return Handler.PerformInternalCommand(command, ref result, localVars); } /// @@ -536,8 +535,8 @@ namespace MinecraftClient /// public static string GetVerbatim(string? text) { - if (String.IsNullOrEmpty(text)) - return String.Empty; + if (string.IsNullOrEmpty(text)) + return string.Empty; int idx = 0; var data = new char[text.Length]; @@ -556,13 +555,13 @@ namespace MinecraftClient /// public static bool IsValidName(string username) { - if (String.IsNullOrEmpty(username)) + if (string.IsNullOrEmpty(username)) return false; foreach (char c in username) - if (!((c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c >= '0' && c <= '9') + if (!(c >= 'a' && c <= 'z' + || c >= 'A' && c <= 'Z' + || c >= '0' && c <= '9' || c == '_')) return false; @@ -578,7 +577,7 @@ namespace MinecraftClient /// Returns true if the text is a private message protected static bool IsPrivateMessage(string text, ref string message, ref string sender) { - if (String.IsNullOrEmpty(text)) + if (string.IsNullOrEmpty(text)) return false; text = GetVerbatim(text); @@ -689,7 +688,7 @@ namespace MinecraftClient /// Returns true if the text is a chat message protected static bool IsChatMessage(string text, ref string message, ref string sender) { - if (String.IsNullOrEmpty(text)) + if (string.IsNullOrEmpty(text)) return false; text = GetVerbatim(text); @@ -792,7 +791,7 @@ namespace MinecraftClient /// Returns true if the text is a teleport request protected static bool IsTeleportRequest(string text, ref string sender) { - if (String.IsNullOrEmpty(text)) + if (string.IsNullOrEmpty(text)) return false; text = GetVerbatim(text); @@ -820,8 +819,8 @@ namespace MinecraftClient { // Username has requested... //[Rank] Username has requested... - if (((tmp[0].StartsWith("<") && tmp[0].EndsWith(">")) - || (tmp[0].StartsWith("[") && tmp[0].EndsWith("]"))) + if ((tmp[0].StartsWith("<") && tmp[0].EndsWith(">") + || tmp[0].StartsWith("[") && tmp[0].EndsWith("]")) && tmp.Length > 1) sender = tmp[1]; else //Username has requested.. @@ -847,12 +846,12 @@ namespace MinecraftClient { string botName = Translations.ResourceManager.GetString("botname." + GetType().Name) ?? GetType().Name; if (_handler == null || master == null) - ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", botName, text)); + ConsoleIO.WriteLogLine(string.Format("[{0}] {1}", botName, text)); else - Handler.Log.Info(String.Format("[{0}] {1}", botName, text)); - string logfile = Settings.Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile); + Handler.Log.Info(string.Format("[{0}] {1}", botName, text)); + string logfile = Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile); - if (!String.IsNullOrEmpty(logfile)) + if (!string.IsNullOrEmpty(logfile)) { if (!File.Exists(logfile)) { @@ -869,10 +868,10 @@ namespace MinecraftClient protected static void LogToConsole(string originBotName, object? text) { string botName = Translations.ResourceManager.GetString(originBotName) ?? originBotName; - ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", botName, text)); - string logfile = Settings.Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile); + ConsoleIO.WriteLogLine(string.Format("[{0}] {1}", botName, text)); + string logfile = Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile); - if (!String.IsNullOrEmpty(logfile)) + if (!string.IsNullOrEmpty(logfile)) { if (!File.Exists(logfile)) { @@ -892,7 +891,7 @@ namespace MinecraftClient /// Debug log text to write protected void LogDebugToConsole(object text) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) LogToConsole(text); } @@ -924,7 +923,7 @@ namespace MinecraftClient /// Optional delay, in seconds, before restarting protected void ReconnectToTheServer(int ExtraAttempts = 3, int delaySeconds = 0, bool keepAccountAndServerSettings = false) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) { string botName = Translations.ResourceManager.GetString("botname." + GetType().Name) ?? GetType().Name; ConsoleIO.WriteLogLine(string.Format(Translations.chatbot_reconnect, botName)); @@ -946,10 +945,6 @@ namespace MinecraftClient /// protected void UnloadBot() { - foreach (string cmdName in registeredCommands) - { - Handler.UnregisterCommand(cmdName); - } Handler.BotUnLoad(this); } @@ -960,7 +955,7 @@ namespace MinecraftClient /// Message protected void SendPrivateMessage(string player, string message) { - SendText(String.Format("/{0} {1} {2}", Config.Main.Advanced.PrivateMsgsCmdName, player, message)); + SendText(string.Format("/{0} {1} {2}", Config.Main.Advanced.PrivateMsgsCmdName, player, message)); } /// @@ -1079,7 +1074,7 @@ namespace MinecraftClient /// Get the current location of the player (Feet location) /// /// Minecraft world or null if associated setting is disabled - protected Mapping.Location GetCurrentLocation() + protected Location GetCurrentLocation() { return Handler.GetCurrentLocation(); } @@ -1095,7 +1090,7 @@ namespace MinecraftClient /// How long to wait before stopping computation (default: 5 seconds) /// When location is unreachable, computation will reach timeout, then optionally fallback to a close location within maxOffset /// True if a path has been found - protected bool MoveToLocation(Mapping.Location location, bool allowUnsafe = false, bool allowDirectTeleport = false, int maxOffset = 0, int minOffset = 0, TimeSpan? timeout = null) + protected bool MoveToLocation(Location location, bool allowUnsafe = false, bool allowDirectTeleport = false, int maxOffset = 0, int minOffset = 0, TimeSpan? timeout = null) { return Handler.MoveTo(location, allowUnsafe, allowDirectTeleport, maxOffset, minOffset, timeout); } @@ -1113,7 +1108,7 @@ namespace MinecraftClient /// Look at the specified location /// /// Location to look at - protected void LookAtLocation(Mapping.Location location) + protected void LookAtLocation(Location location) { Handler.UpdateLocation(Handler.GetCurrentLocation(), location); } @@ -1167,13 +1162,13 @@ namespace MinecraftClient //Read all lines from file, remove lines with no text, convert to lowercase, //remove duplicate entries, convert to a string array, and return the result. return File.ReadAllLines(file, Encoding.UTF8) - .Where(line => !String.IsNullOrWhiteSpace(line)) + .Where(line => !string.IsNullOrWhiteSpace(line)) .Select(line => line.ToLower()) .Distinct().ToArray(); } else { - LogToConsole("File not found: " + System.IO.Path.GetFullPath(file)); + LogToConsole("File not found: " + Path.GetFullPath(file)); return Array.Empty(); } } @@ -1315,7 +1310,7 @@ namespace MinecraftClient /// Get server current TPS (tick per second) /// /// tps - protected Double GetServerTPS() + protected double GetServerTPS() { return Handler.GetServerTPS(); } @@ -1541,21 +1536,6 @@ namespace MinecraftClient return Handler.UpdateCommandBlock(location, command, mode, flags); } - /// - /// Register a command in command prompt. Command will be automatically unregistered when unloading ChatBot - /// - /// Name of the command - /// Description/usage of the command - /// Method for handling the command - /// True if successfully registered - protected bool RegisterChatBotCommand(string cmdName, string cmdDesc, string cmdUsage, CommandRunner callback) - { - bool result = Handler.RegisterCommand(cmdName, cmdDesc, cmdUsage, callback); - if (result) - registeredCommands.Add(cmdName.ToLower()); - return result; - } - /// /// Close a opened inventory /// @@ -1687,11 +1667,12 @@ namespace MinecraftClient public override string CmdUsage { get { return _cmdUsage; } } public override string CmdDesc { get { return _cmdDesc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + } - public override string Run(McClient handler, string command, Dictionary? localVars) + public string Run(McClient handler, string command, Dictionary? localVars) { return Runner(command, GetArgs(command)); } diff --git a/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs b/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs index 8a1e492c..ee5bf460 100644 --- a/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs +++ b/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs @@ -8,9 +8,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; -using MinecraftClient; -namespace DynamicRun.Builder +namespace MinecraftClient.Scripting.DynamicRun.Builder { internal class CompileRunner { diff --git a/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs b/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs index 4bb02b07..078f97a0 100644 --- a/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs +++ b/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs @@ -14,11 +14,9 @@ using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; -using MinecraftClient; -using MinecraftClient.Scripting; using SingleFileExtractor.Core; -namespace DynamicRun.Builder +namespace MinecraftClient.Scripting.DynamicRun.Builder { internal class Compiler { @@ -38,7 +36,7 @@ namespace DynamicRun.Builder Failures = failures.ToList() }; } - + peStream.Seek(0, SeekOrigin.Begin); return new CompileResult() @@ -55,20 +53,20 @@ namespace DynamicRun.Builder var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9); var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options); - + var references = new List(); // Find if any additional assembly DLL exists in the base directory where the .exe exists. - foreach (var assembly in additionalAssemblies) + foreach (var assembly in additionalAssemblies) { var dllPath = Path.Combine(AppContext.BaseDirectory, assembly); - if (File.Exists(dllPath)) + if (File.Exists(dllPath)) { references.Add(MetadataReference.CreateFromFile(dllPath)); // Store the reference in our Assembly Resolver for future reference. AssemblyResolver.AddAssembly(Assembly.LoadFile(dllPath).FullName!, dllPath); } - else + else { ConsoleIO.WriteLogLine($"[Script Error] {assembly} is defined in script, but cannot find DLL! Script may not run."); } @@ -79,7 +77,7 @@ namespace DynamicRun.Builder var SystemPrivateCoreLib = typeof(object).Assembly.Location; // System.Private.CoreLib var SystemConsole = typeof(Console).Assembly.Location; // System.Console var MinecraftClientDll = typeof(Program).Assembly.Location; // The path to MinecraftClient.dll - + // We're on a self-contained binary, so we need to extract the executable to get the assemblies. if (string.IsNullOrEmpty(MinecraftClientDll)) { diff --git a/MinecraftClient/Scripting/DynamicRun/Builder/SimpleUnloadableAssemblyLoadContext.cs b/MinecraftClient/Scripting/DynamicRun/Builder/SimpleUnloadableAssemblyLoadContext.cs index de44652c..e210bcac 100644 --- a/MinecraftClient/Scripting/DynamicRun/Builder/SimpleUnloadableAssemblyLoadContext.cs +++ b/MinecraftClient/Scripting/DynamicRun/Builder/SimpleUnloadableAssemblyLoadContext.cs @@ -7,7 +7,7 @@ https://github.com/laurentkempe/DynamicRun/blob/master/LICENSE using System.Reflection; using System.Runtime.Loader; -namespace DynamicRun.Builder +namespace MinecraftClient.Scripting.DynamicRun.Builder { internal class SimpleUnloadableAssemblyLoadContext : AssemblyLoadContext {