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;