From 97d732593905f6c6f3bbe1706f1745b35aa40ab3 Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Thu, 9 Jul 2020 22:21:39 +0800 Subject: [PATCH] Finish AutoCraft core functional part --- MinecraftClient/ChatBot.cs | 10 + MinecraftClient/ChatBots/AutoCarft.cs | 235 ++++++++++++++---- MinecraftClient/Inventory/Container.cs | 19 ++ MinecraftClient/McClient.cs | 7 +- .../Protocol/Handlers/Protocol18.cs | 1 + 5 files changed, 220 insertions(+), 52 deletions(-) diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index f0dca0e6..90173a6f 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -1181,6 +1181,16 @@ namespace MinecraftClient return Handler.RegisterCommand(cmdName, cmdDesc, callback); } + /// + /// Close a opened inventory + /// + /// + /// True if success + protected bool CloseInventory(int inventoryID) + { + return Handler.CloseInventory(inventoryID); + } + /// /// Command runner definition. /// Returned string will be the output of the command diff --git a/MinecraftClient/ChatBots/AutoCarft.cs b/MinecraftClient/ChatBots/AutoCarft.cs index d9c56400..a9d52c83 100644 --- a/MinecraftClient/ChatBots/AutoCarft.cs +++ b/MinecraftClient/ChatBots/AutoCarft.cs @@ -9,21 +9,86 @@ namespace MinecraftClient.ChatBots { class AutoCarft : ChatBot { - private bool waitingForResult = false; - private int inventoryInUse; + private bool waitingForUpdate = false; + private int inventoryInUse = -2; + private int index = 0; + private Recipe recipeInUse; + + private int updateDebounce = 0; + + private bool craftingFailed = false; private enum ActionType { - MoveTo, + LeftClick, + ShiftClick, WaitForUpdate, + ResetCraftArea, Repeat } private class ActionStep { - public ActionType Action; - public int Slot; - public int InventoryID; + public ActionType ActionType; + public int Slot = -2; + public int InventoryID = -2; + public ItemType ItemType; + + public ActionStep(ActionType actionType) + { + ActionType = actionType; + } + public ActionStep(ActionType actionType, int inventoryID) + { + ActionType = actionType; + InventoryID = inventoryID; + } + public ActionStep(ActionType actionType, int inventoryID, int slot) + { + ActionType = actionType; + Slot = slot; + InventoryID = inventoryID; + } + public ActionStep(ActionType actionType, int inventoryID, ItemType itemType) + { + ActionType = actionType; + InventoryID = inventoryID; + ItemType = itemType; + } + } + + private List actionSteps = new List(); + + private class Recipe + { + public ItemType ResultItem; + public ContainerType CraftingAreaType; + public Dictionary Materials; + + public Recipe(Dictionary materials, ItemType resultItem, ContainerType type) + { + Materials = materials; + ResultItem = resultItem; + CraftingAreaType = type; + } + + public static Recipe ConvertToCraftingTable(Recipe recipe) + { + if (recipe.CraftingAreaType == ContainerType.PlayerInventory) + { + if (recipe.Materials.ContainsKey(4)) + { + recipe.Materials[5] = recipe.Materials[4]; + recipe.Materials.Remove(4); + } + if (recipe.Materials.ContainsKey(3)) + { + recipe.Materials[4] = recipe.Materials[3]; + recipe.Materials.Remove(3); + } + } + return recipe; + } } public override void Initialize() @@ -55,64 +120,134 @@ namespace MinecraftClient.ChatBots public string CraftCommand(string command, string[] args) { - Dictionary recipe = new Dictionary + Dictionary materials = new Dictionary { - { 1, ItemType.Stone } + { 1, ItemType.Coal }, + { 3, ItemType.Stick } }; - var inventory = GetInventories()[0]; - int slotToPut = -2; - int slotToTake = -2; + Recipe recipe = new Recipe(materials, ItemType.StoneButton, ContainerType.PlayerInventory); inventoryInUse = 0; - foreach (KeyValuePair slot in recipe) + + recipeInUse = recipe; + craftingFailed = false; + waitingForUpdate = false; + index = 0; + + var inventory = GetInventories()[inventoryInUse]; + foreach (KeyValuePair slot in recipe.Materials) { - slotToPut = slot.Key + 1; - slotToTake = -2; - // Find material in our inventory - foreach (KeyValuePair item in inventory.Items) - { - if (slot.Value == item.Value.Type) - { - slotToTake = item.Key; - break; - } - } - if (slotToTake != -2) - { - // move found material to correct crafting slot - WindowAction(0, slotToTake, WindowActionType.LeftClick); - WindowAction(0, slotToPut, WindowActionType.LeftClick); - } + actionSteps.Add(new ActionStep(ActionType.LeftClick, inventoryInUse, slot.Value)); + actionSteps.Add(new ActionStep(ActionType.LeftClick, inventoryInUse, slot.Key)); } - if (slotToPut != -2 && slotToTake != -2) + if (actionSteps.Count > 0) { - waitingForResult = true; - // Now wait for server to update the slot 0, craft result - return "Waiting for result"; + actionSteps.Add(new ActionStep(ActionType.WaitForUpdate, inventoryInUse, 0)); + actionSteps.Add(new ActionStep(ActionType.ShiftClick, inventoryInUse, 0)); + actionSteps.Add(new ActionStep(ActionType.WaitForUpdate, inventoryInUse)); + actionSteps.Add(new ActionStep(ActionType.Repeat)); + HandleNextStep(); + return "AutoCraft start!"; } - else return "Failed before waiting for result"; + else return "AutoCraft cannot be started. Check your available materials"; } public override void OnInventoryUpdate(int inventoryId) { - ConsoleIO.WriteLine("Inventory " + inventoryId + " is being updated"); - if (waitingForResult && inventoryInUse == inventoryId) + if (waitingForUpdate && inventoryInUse == inventoryId) { - var inventory = GetInventories()[inventoryId]; - if (inventory.Items.ContainsKey(0)) + updateDebounce = 2; + } + } + + public override void Update() + { + if (updateDebounce > 0) + { + updateDebounce--; + if (updateDebounce <= 0) + InventoryUpdateFinished(); + } + } + + private void InventoryUpdateFinished() + { + waitingForUpdate = false; + HandleNextStep(); + } + + private void HandleNextStep() + { + while (actionSteps.Count > 0) + { + if (waitingForUpdate) break; + ActionStep step = actionSteps[index]; + index++; + switch (step.ActionType) { - // slot 0 have item, click on it - WindowAction(0, 0, WindowActionType.LeftClick); - // Now wait for server to update our inventory - ConsoleIO.WriteLine("Crafting success"); - } - else if (inventory.Items.ContainsKey(-1)) - { - // Server have updated our cursor to the item we want to take out from craft result - // Now put the item back to our inventory - WindowAction(0, 37, WindowActionType.LeftClick); - ConsoleIO.WriteLine("Moved crafted item to inventory"); - waitingForResult = false; + case ActionType.LeftClick: + if (step.Slot != -2) + { + WindowAction(step.InventoryID, step.Slot, WindowActionType.LeftClick); + } + else + { + int[] slots = GetInventories()[step.InventoryID].SearchItem(step.ItemType); + if (slots.Count() > 0) + { + int ignoredSlot; + if (recipeInUse.CraftingAreaType == ContainerType.PlayerInventory) + ignoredSlot = 9; + else + ignoredSlot = 10; + slots = slots.Where(slot => slot >= ignoredSlot).ToArray(); + if (slots.Count() > 0) + WindowAction(step.InventoryID, slots[0], WindowActionType.LeftClick); + else + craftingFailed = true; + } + else craftingFailed = true; + } + break; + + case ActionType.ShiftClick: + if (step.Slot == 0) + { + WindowAction(step.InventoryID, step.Slot, WindowActionType.ShiftClick); + } + else craftingFailed = true; + break; + + case ActionType.WaitForUpdate: + if (step.InventoryID != -2) + { + waitingForUpdate = true; + } + else craftingFailed = true; + break; + + case ActionType.ResetCraftArea: + if (step.InventoryID != -2) + CloseInventory(step.InventoryID); + else + craftingFailed = true; + break; + + case ActionType.Repeat: + index = 0; + break; } + HandleError(); + } + + } + + private void HandleError() + { + if (craftingFailed) + { + actionSteps.Clear(); + CloseInventory(inventoryInUse); + ConsoleIO.WriteLogLine("Crafting aborted! Check your available materials."); } } } diff --git a/MinecraftClient/Inventory/Container.cs b/MinecraftClient/Inventory/Container.cs index 97f97e27..4547c50c 100644 --- a/MinecraftClient/Inventory/Container.cs +++ b/MinecraftClient/Inventory/Container.cs @@ -153,5 +153,24 @@ namespace MinecraftClient.Inventory default: return ContainerType.Unknown; } } + + /// + /// Search an item in the container + /// + /// The item to search + /// An array of slot ID + public int[] SearchItem(ItemType itemType) + { + List result = new List(); + if (Items != null) + { + foreach (var item in Items) + { + if (item.Value.Type == itemType) + result.Add(item.Key); + } + } + return result.ToArray(); + } } } diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index b9f65ece..7ab24d97 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -1113,6 +1113,7 @@ namespace MinecraftClient } break; case WindowActionType.ShiftClick: + if (slotId == 0) break; if (inventory.Items.ContainsKey(slotId)) { /* Target slot have item */ @@ -1192,11 +1193,13 @@ namespace MinecraftClient /// /// Window ID /// TRUE if the window was successfully closed + /// Sending close window for inventory 0 can cause server to update our inventory if there are any item in the crafting area public bool CloseInventory(int windowId) { - if (windowId != 0 && inventories.ContainsKey(windowId)) + if (inventories.ContainsKey(windowId)) { - inventories.Remove(windowId); + if (windowId != 0) + inventories.Remove(windowId); return handler.SendCloseWindow(windowId); } return false; diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 3051d39e..02a138c4 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -1573,6 +1573,7 @@ namespace MinecraftClient.Protocol.Handlers case WindowActionType.LeftClick: button = 0; break; case WindowActionType.RightClick: button = 1; break; case WindowActionType.MiddleClick: button = 2; mode = 3; break; + case WindowActionType.ShiftClick: button = 0; mode = 1; break; case WindowActionType.DropItem: button = 0; mode = 4;