From cd1badb9d6f5c1416ece0ebaa4910aa8c654b64a Mon Sep 17 00:00:00 2001
From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com>
Date: Sat, 29 Aug 2020 23:53:29 +0800
Subject: [PATCH] Add item moving helper class (#1243)
* Create item moving helper class
* Change the method calling order for joined game
Dispatch bot after things are initialized such as inventories
* Add GetItemMovingHelper ChatBot method
---
MinecraftClient/ChatBot.cs | 10 +
MinecraftClient/Inventory/ItemMovingHelper.cs | 173 ++++++++++++++++++
MinecraftClient/McClient.cs | 3 +-
MinecraftClient/MinecraftClient.csproj | 1 +
4 files changed, 186 insertions(+), 1 deletion(-)
create mode 100644 MinecraftClient/Inventory/ItemMovingHelper.cs
diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs
index c288c814..d4604310 100644
--- a/MinecraftClient/ChatBot.cs
+++ b/MinecraftClient/ChatBot.cs
@@ -1182,6 +1182,16 @@ namespace MinecraftClient
return Handler.DoWindowAction(inventoryId, slot, actionType);
}
+ ///
+ /// Get inventory action helper
+ ///
+ /// Inventory Container
+ /// ItemMovingHelper instance
+ protected ItemMovingHelper GetItemMovingHelper(Container container)
+ {
+ return new ItemMovingHelper(container, Handler);
+ }
+
///
/// Change player selected hotbar slot
///
diff --git a/MinecraftClient/Inventory/ItemMovingHelper.cs b/MinecraftClient/Inventory/ItemMovingHelper.cs
new file mode 100644
index 00000000..08c9d5bd
--- /dev/null
+++ b/MinecraftClient/Inventory/ItemMovingHelper.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace MinecraftClient.Inventory
+{
+ ///
+ /// Class that contains useful methods to move item around in a container
+ ///
+ public class ItemMovingHelper
+ {
+ private Container c;
+ private McClient mc;
+
+ ///
+ /// Create a helper that contains useful methods to move item around in container
+ ///
+ /// Source container to use. All method will use this container for handling first slot parameter
+ /// McClient handler. Needed for sending WindowAction packet to the server
+ ///
+ /// If you are using ChatBot API and cannot have direct access to McClient handler, use as second parameter
+ ///
+ public ItemMovingHelper(Container c, McClient mc)
+ {
+ this.c = c;
+ this.mc = mc;
+ }
+
+ ///
+ /// Move an item fron source to dest. Source should contain an item and dest slot should be empty
+ ///
+ /// Source slot
+ /// Dest slot
+ /// Dest slot's container (only if dest slot is in other container)
+ /// True if success or false if Failed
+ public bool MoveTo(int source, int dest, Container destContainer = null)
+ {
+ // Condition: source has item and dest has no item
+ if (ValidateSlots(source, dest, destContainer) &&
+ HasItem(source) &&
+ ((destContainer != null && !HasItem(dest, destContainer)) || (destContainer == null && !HasItem(dest))))
+ return mc.DoWindowAction(c.ID, source, WindowActionType.LeftClick)
+ && mc.DoWindowAction(destContainer == null ? c.ID : destContainer.ID, dest, WindowActionType.LeftClick);
+ else return false;
+ }
+
+ ///
+ /// Swap two item. Both slots should contain item
+ ///
+ /// Slot 1
+ /// Slot 2
+ /// Slot2 container (only if slot2 is in other container)
+ /// True if success or False if Failed
+ public bool Swap(int slot1, int slot2, Container destContainer = null)
+ {
+ // Condition: Both slot1 and slot2 has item
+ if (ValidateSlots(slot1, slot2, destContainer) &&
+ HasItem(slot1) &&
+ (destContainer != null && HasItem(slot2, destContainer) || (destContainer == null && HasItem(slot2))))
+ return mc.DoWindowAction(c.ID, slot1, WindowActionType.LeftClick)
+ && mc.DoWindowAction(destContainer == null ? c.ID : destContainer.ID, slot2, WindowActionType.LeftClick)
+ && mc.DoWindowAction(c.ID, slot1, WindowActionType.LeftClick);
+ else return false;
+ }
+
+ ///
+ /// Drag a stack of item over a set of slots. Those slots should be empty or have the same item as source item
+ ///
+ /// Source item
+ /// Slots to be drag over
+ /// Which mouse key to use: StartDragLeft, StartDragRight or StartDragMiddle
+ /// True if success or false if Failed
+ ///
+ /// After calling this method, you will need to wait until the server have updated the player inventory before doing next inventory action
+ ///
+ public bool DragOverSlots(int source, IEnumerable slots, WindowActionType mouseKey = WindowActionType.StartDragLeft)
+ {
+ if (!HasItem(source))
+ return false;
+ List availableSlots = new List(slots.Count());
+ // filter out different item type or non-empty slots (they will be ignored silently)
+ foreach (var slot in slots)
+ if (ItemTypeEqual(source, slot) || !HasItem(slot))
+ availableSlots.Add(slot);
+ if (availableSlots.Count > 0)
+ {
+ WindowActionType startDragging = WindowActionType.StartDragLeft;
+ WindowActionType addDragging = WindowActionType.AddDragLeft;
+ WindowActionType endDragging = WindowActionType.EndDragLeft;
+ switch (mouseKey)
+ {
+ case WindowActionType.StartDragRight:
+ {
+ startDragging = WindowActionType.StartDragRight;
+ addDragging = WindowActionType.AddDragRight;
+ endDragging = WindowActionType.EndDragRight;
+ break;
+ }
+ case WindowActionType.StartDragMiddle:
+ {
+ startDragging = WindowActionType.StartDragMiddle;
+ addDragging = WindowActionType.AddDragMiddle;
+ endDragging = WindowActionType.EndDragMiddle;
+ break;
+ }
+ }
+ mc.DoWindowAction(c.ID, source, WindowActionType.LeftClick); // grab item
+ mc.DoWindowAction(c.ID, -999, startDragging);
+ foreach (var slot in availableSlots)
+ {
+ mc.DoWindowAction(c.ID, slot, addDragging);
+ }
+ mc.DoWindowAction(c.ID, -999, endDragging);
+ mc.DoWindowAction(c.ID, source, WindowActionType.LeftClick); // put down item left (if any)
+ return true;
+ }
+ else return false;
+ }
+
+ ///
+ /// Validate two slot by comparing they are different and within the maximum slot count of the container
+ ///
+ /// Slot 1
+ /// Slot 2
+ /// Second container (only if slot2 is in other container)
+ /// The compare result
+ private bool ValidateSlots(int s1, int s2, Container s2Container = null)
+ {
+ if (s2Container == null)
+ return (s1 != s2 && s1 < c.Type.SlotCount() && s2 < c.Type.SlotCount());
+ else
+ return (s1 < c.Type.SlotCount() && s2 < s2Container.Type.SlotCount());
+ }
+
+ ///
+ /// Check if the slot has an item inside
+ ///
+ /// Slot ID
+ /// Specify another contianer (only if the slot is in other container)
+ /// True if has item
+ private bool HasItem(int slot, Container c = null)
+ {
+ if (c == null)
+ c = this.c;
+ return c.Items.ContainsKey(slot);
+ }
+
+ ///
+ /// Check both slots item type are the same
+ ///
+ ///
+ ///
+ /// Second container (only if slot2 is in other container)
+ /// True if they are equal
+ private bool ItemTypeEqual(int slot1, int slot2, Container s2Container = null)
+ {
+ if (s2Container == null)
+ {
+ if (HasItem(slot1) && HasItem(slot2))
+ return c.Items[slot1].Type == c.Items[slot2].Type;
+ else return false;
+ }
+ else
+ {
+ if (HasItem(slot1) && HasItem(slot2, s2Container))
+ return c.Items[slot1].Type == s2Container.Items[slot2].Type;
+ else return false;
+ }
+ }
+ }
+}
diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs
index 3db0c5a2..06e5bcb6 100644
--- a/MinecraftClient/McClient.cs
+++ b/MinecraftClient/McClient.cs
@@ -1510,7 +1510,6 @@ namespace MinecraftClient
Settings.MCSettings_Skin_All,
Settings.MCSettings_MainHand);
- DispatchBotEvent(bot => bot.AfterGameJoined());
if (inventoryHandlingRequested)
{
@@ -1520,6 +1519,8 @@ namespace MinecraftClient
}
ClearInventories();
+
+ DispatchBotEvent(bot => bot.AfterGameJoined());
}
///
diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj
index 49f1e6b4..930e27ac 100644
--- a/MinecraftClient/MinecraftClient.csproj
+++ b/MinecraftClient/MinecraftClient.csproj
@@ -120,6 +120,7 @@
+