From bc449b404ee061de3327af61aba0ffe086054b38 Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Thu, 26 Mar 2020 15:01:42 +0800 Subject: [PATCH] Inventory handling --- MinecraftClient/ChatBot.cs | 12 ++ MinecraftClient/ChatBots/AutoFishing.cs | 29 ++++- MinecraftClient/Commands/ChangeSlot.cs | 42 +++++++ MinecraftClient/Commands/GetInventory.cs | 24 ++++ MinecraftClient/Inventory/Container.cs | 83 +++++++++++++ MinecraftClient/Inventory/ContainerType.cs | 38 ++++++ MinecraftClient/Inventory/Item.cs | 34 ++++++ MinecraftClient/Mapping/Entity.cs | 29 +++-- MinecraftClient/Mapping/EntityType.cs | 14 +++ MinecraftClient/McTcpClient.cs | 91 ++++++++++++--- MinecraftClient/MinecraftClient.csproj | 7 ++ MinecraftClient/MinecraftClient.csproj.user | 2 +- .../Protocol/Handlers/PacketIncomingType.cs | 6 +- .../Protocol/Handlers/PacketOutgoingType.cs | 3 +- .../Protocol/Handlers/Protocol16.cs | 8 ++ .../Protocol/Handlers/Protocol18.cs | 110 ++++++++++++++++-- .../Handlers/Protocol18PacketTypes.cs | 13 +++ MinecraftClient/Protocol/IMinecraftCom.cs | 6 +- .../Protocol/IMinecraftComHandler.cs | 10 +- MinecraftClient/config/README.md | 21 ++++ 20 files changed, 538 insertions(+), 44 deletions(-) create mode 100644 MinecraftClient/Commands/ChangeSlot.cs create mode 100644 MinecraftClient/Commands/GetInventory.cs create mode 100644 MinecraftClient/Inventory/Container.cs create mode 100644 MinecraftClient/Inventory/ContainerType.cs create mode 100644 MinecraftClient/Inventory/Item.cs create mode 100644 MinecraftClient/Mapping/EntityType.cs diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index ec9b1cbf..8d5ff48d 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -5,6 +5,7 @@ using System.Text; using System.IO; using System.Threading; using System.Text.RegularExpressions; +using MinecraftClient.Inventory; namespace MinecraftClient { @@ -620,6 +621,11 @@ namespace MinecraftClient return Handler.GetEntityHandlingEnabled(); } + public bool GetInventoryEnabled() + { + return Handler.GetInventoryEnabled(); + } + /// /// Get the current Minecraft World /// @@ -806,5 +812,11 @@ namespace MinecraftClient { return Handler.UseItemOnHand(); } + + protected Container GetPlayerInventory() + { + Container container = Handler.GetPlayerInventory(); + return new Container(container.ID,container.Type,container.Title,container.Items); + } } } diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs index 94567318..84304674 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -14,6 +14,7 @@ namespace MinecraftClient.ChatBots private Double fishingHookThreshold = 0.2; private Location LastPos = new Location(); private DateTime CaughtTime = DateTime.Now; + private bool inventoryEnabled; public override void Initialize() { @@ -23,11 +24,12 @@ namespace MinecraftClient.ChatBots ConsoleIO.WriteLine("[AutoFishing] This bot will be unloaded."); UnloadBot(); } + inventoryEnabled = GetInventoryEnabled(); } public override void OnEntitySpawn(Entity entity) { - if (entity.Type == 102) + if (entity.TypeID == 102) { ConsoleIO.WriteLine("Threw a fishing rod"); fishingRod.Add(entity.ID, entity); @@ -69,15 +71,36 @@ namespace MinecraftClient.ChatBots ConsoleIO.WriteLine("Caught a fish!"); // retract fishing rod UseItemOnHand(); + if (inventoryEnabled) + { + if (!hasFishingRod()) + { + ConsoleIO.WriteLine("No Fishing Rod on hand. Maybe broken?"); + return; + } + } // non-blocking delay Task.Factory.StartNew(delegate { // retract fishing rod need some time - Thread.Sleep(500); + Thread.Sleep(800); // throw again - // TODO: to check if hand have fishing rod UseItemOnHand(); }); } + + public bool hasFishingRod() + { + if (!inventoryEnabled) return false; + int start = 36; + int end = 44; + Inventory.Container container = GetPlayerInventory(); + foreach(KeyValuePair a in container.Items) + { + if (a.Key < start || a.Key > end) continue; + if (a.Value.ID == 622) return true; + } + return false; + } } } diff --git a/MinecraftClient/Commands/ChangeSlot.cs b/MinecraftClient/Commands/ChangeSlot.cs new file mode 100644 index 00000000..0f3bebe6 --- /dev/null +++ b/MinecraftClient/Commands/ChangeSlot.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Commands +{ + class ChangeSlot : Command + { + public override string CMDName { get { return "changeslot"; } } + public override string CMDDesc { get { return "changeslot <1-9>: Change hotbar"; } } + + public override string Run(McTcpClient handler, string command) + { + if (!handler.GetInventoryEnabled()) return "Please enable InventoryHandling in the config file first."; + if (hasArg(command)) + { + short slot; + try + { + slot = Convert.ToInt16(getArg(command)); + } + catch (FormatException) + { + return "Could not change slot: Not a Number"; + } + if (slot >= 1 && slot <= 9) + { + if (handler.ChangeSlot(slot-=1)) + { + return "Changed to slot " + (slot+=1); + } + else + { + return "Could not change slot"; + } + } + } + return CMDDesc; + } + } +} diff --git a/MinecraftClient/Commands/GetInventory.cs b/MinecraftClient/Commands/GetInventory.cs new file mode 100644 index 00000000..96c4d948 --- /dev/null +++ b/MinecraftClient/Commands/GetInventory.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MinecraftClient.Inventory; + +namespace MinecraftClient.Commands +{ + class GetInventory : Command + { + public override string CMDName { get { return "getinventory"; } } + public override string CMDDesc { get { return "getinventory: Show your inventory."; } } + + public override string Run(McTcpClient handler, string command) + { + Dictionary items = handler.GetPlayerInventory().Items; + foreach(KeyValuePair a in items) + { + ConsoleIO.WriteLine("Slot: "+a.Key+" ItemID: " + a.Value.ID + ", Count: " + a.Value.Count); + } + return ""; + } + } +} diff --git a/MinecraftClient/Inventory/Container.cs b/MinecraftClient/Inventory/Container.cs new file mode 100644 index 00000000..9e93be8a --- /dev/null +++ b/MinecraftClient/Inventory/Container.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Inventory +{ + public class Container + { + public int ID; + public ContainerType Type; + public string Title; + public Dictionary Items; + + public Container() { } + public Container(int id, ContainerType type, string title) + { + ID = id; + Type = type; + Title = title; + } + public Container(int id, ContainerType type, string title,Dictionary items) + { + ID = id; + Type = type; + Title = title; + Items = items; + } + public Container(int id, Protocol.InventoryType type, string title) + { + ID = id; + Title = title; + } + public Container(int id, int typeID, string title) + { + ID = id; + Type = GetContainerType(typeID); + Title = title; + } + // for player inventory because they dont have ID and title + public Container(ContainerType type) + { + Type = type; + } + public Container(ContainerType type, Dictionary items) + { + Type = type; + Items = items; + } + + public static ContainerType GetContainerType(int typeID) + { + // https://wiki.vg/Inventory didn't state the inventory ID, assume that list start with 0 + switch (typeID) + { + case 0: return ContainerType.Generic_9x1; + case 1: return ContainerType.Generic_9x2; + case 2: return ContainerType.Generic_9x3; + case 3: return ContainerType.Generic_9x4; + case 4: return ContainerType.Generic_9x5; + case 5: return ContainerType.Generic_9x6; + case 6: return ContainerType.Generic_3x3; + case 7: return ContainerType.Anvil; + case 8: return ContainerType.Beacon; + case 9: return ContainerType.BlastFurnace; + case 10: return ContainerType.BrewingStand; + case 11: return ContainerType.Crafting; + case 12: return ContainerType.Enchantment; + case 13: return ContainerType.Furnace; + case 14: return ContainerType.Grindstone; + case 15: return ContainerType.Hopper; + case 16: return ContainerType.Lectern; + case 17: return ContainerType.Loom; + case 18: return ContainerType.Merchant; + case 19: return ContainerType.ShulkerBox; + case 20: return ContainerType.Smoker; + case 21: return ContainerType.Cartography; + case 22: return ContainerType.Stonecutter; + default: return ContainerType.Unknown; + } + } + } +} diff --git a/MinecraftClient/Inventory/ContainerType.cs b/MinecraftClient/Inventory/ContainerType.cs new file mode 100644 index 00000000..c1276a7b --- /dev/null +++ b/MinecraftClient/Inventory/ContainerType.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Inventory +{ + // For MC 1.14 after ONLY + public enum ContainerType + { + Generic_9x1, + Generic_9x2, + Generic_9x3, // chest, ender chest, minecart with chest, barrel + Generic_9x4, + Generic_9x5, + Generic_9x6, + Generic_3x3, + Anvil, + Beacon, + BlastFurnace, + BrewingStand, + Crafting, + Enchantment, + Furnace, + Grindstone, + Hopper, + Lectern, + Loom, + Merchant, + ShulkerBox, + Smoker, + Cartography, + Stonecutter, + // not in the list + PlayerInventory, + Unknown + } +} diff --git a/MinecraftClient/Inventory/Item.cs b/MinecraftClient/Inventory/Item.cs new file mode 100644 index 00000000..2048641d --- /dev/null +++ b/MinecraftClient/Inventory/Item.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Inventory +{ + public class Item + { + public int ID; + public int Count; + public int SlotID = -1; // which slot is this item at, -1 = not specified + public Dictionary NBT; + + public Item(int ID,int Count,int SlotID, Dictionary NBT) + { + this.ID = ID; + this.Count = Count; + this.SlotID = SlotID; + this.NBT = NBT; + } + public Item(int ID, int Count, int SlotID) + { + this.ID = ID; + this.Count = Count; + this.SlotID = SlotID; + } + public Item(int ID, int Count) + { + this.ID = ID; + this.Count = Count; + } + } +} diff --git a/MinecraftClient/Mapping/Entity.cs b/MinecraftClient/Mapping/Entity.cs index 289fdf3c..5439d271 100644 --- a/MinecraftClient/Mapping/Entity.cs +++ b/MinecraftClient/Mapping/Entity.cs @@ -8,7 +8,8 @@ namespace MinecraftClient.Mapping public class Entity { public int ID; - public int Type; + public int TypeID; + public EntityType Type; public string Name; public Location Location; public Entity(int ID, Location location) @@ -16,17 +17,31 @@ namespace MinecraftClient.Mapping this.ID = ID; this.Location = location; } - public Entity(int ID, int Type, Location location) + public Entity(int ID, int TypeID, Location location) { this.ID = ID; - this.Type = Type; - this.Name = GetMobName(Type); + this.TypeID = TypeID; + this.Name = GetMobName(TypeID); this.Location = location; } - public Entity(int ID, int Type, string Name, Location location) + public Entity(int ID, int TypeID, EntityType type, Location location) { this.ID = ID; - this.Type = Type; + this.TypeID = TypeID; + this.Type = type; + this.Name = GetMobName(TypeID); + this.Location = location; + } + public Entity(int ID, EntityType type, Location location) + { + this.ID = ID; + this.Type = type; + this.Location = location; + } + public Entity(int ID, int TypeID, string Name, Location location) + { + this.ID = ID; + this.TypeID = TypeID; this.Name = Name; this.Location = location; } @@ -80,7 +95,7 @@ namespace MinecraftClient.Mapping } public string GetMobName() { - return GetMobName(Type); + return GetMobName(TypeID); } } } diff --git a/MinecraftClient/Mapping/EntityType.cs b/MinecraftClient/Mapping/EntityType.cs new file mode 100644 index 00000000..9333f222 --- /dev/null +++ b/MinecraftClient/Mapping/EntityType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Mapping +{ + public enum EntityType + { + MobAndAnimal, + Player, + NonLivingThings, + } +} diff --git a/MinecraftClient/McTcpClient.cs b/MinecraftClient/McTcpClient.cs index fb5c6984..3b28c704 100644 --- a/MinecraftClient/McTcpClient.cs +++ b/MinecraftClient/McTcpClient.cs @@ -10,6 +10,7 @@ using MinecraftClient.Protocol; using MinecraftClient.Proxy; using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Mapping; +using MinecraftClient.Inventory; namespace MinecraftClient { @@ -26,7 +27,7 @@ namespace MinecraftClient private readonly List bots = new List(); private static readonly List botsOnHold = new List(); - private static List inventories = new List(); + private static List inventories = new List(); private readonly Dictionary> registeredBotPluginChannels = new Dictionary>(); private readonly List registeredServerPluginChannels = new List(); @@ -52,14 +53,14 @@ namespace MinecraftClient private string username; private string uuid; private string sessionid; - private Inventory playerInventory; + private Container playerInventory = new Container(ContainerType.PlayerInventory); private DateTime lastKeepAlive; private object lastKeepAliveLock = new object(); private int playerEntityID; // not really understand the Inventory Class // so I use a Dict instead for player inventory - private Dictionary playerItems; + //private Dictionary playerItems; // Entity handling private Dictionary entities = new Dictionary(); @@ -567,9 +568,9 @@ namespace MinecraftClient /// Get client player's inventory items /// /// Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID) - public Dictionary GetPlayerInventory() + public Container GetPlayerInventory() { - return playerItems; + return playerInventory; } // TODO: add command for displaying player inventory @@ -737,13 +738,14 @@ namespace MinecraftClient /// When an inventory is opened /// /// Location to reach - public void OnInventoryOpen(Inventory inventory) + public void OnInventoryOpen(Container inventory) { //TODO: Handle Inventory if (!inventories.Contains(inventory)) { inventories.Add(inventory); } + ConsoleIO.WriteLine(inventory.Type.ToString()); } /// @@ -754,9 +756,9 @@ namespace MinecraftClient { for (int i = 0; i < inventories.Count; i++) { - Inventory inventory = inventories[i]; + Container inventory = inventories[i]; if (inventory == null) continue; - if (inventory.id == inventoryID) + if (inventory.Type == Container.GetContainerType(inventoryID)) { inventories.Remove(inventory); return; @@ -769,11 +771,27 @@ namespace MinecraftClient /// /// /// - public void OnWindowItems(int type, Dictionary itemList) + public void OnWindowItems(int type, Dictionary itemList) { // 0 is player inventory if (type == 0) - playerItems = itemList; + playerInventory.Items = itemList; + } + + public void OnSetSlot(byte WindowID, short SlotID, bool Present) + { + if(WindowID == 0) + { + if (playerInventory.Items.ContainsKey(SlotID)) + playerInventory.Items.Remove(SlotID); + } + } + public void OnSetSlot(byte WindowID, short SlotID, bool Present, int ItemID, byte Count, Dictionary NBT) + { + if (WindowID == 0) + { + playerInventory.Items[SlotID] = new Inventory.Item(ItemID, Count, SlotID, NBT); + } } /// @@ -1081,12 +1099,12 @@ namespace MinecraftClient /// Called when a non-living entity spawned (fishing hook, minecart, etc) /// /// - /// + /// /// /// - public void OnSpawnEntity(int EntityID, int EntityType, Guid UUID, Location location) + public void OnSpawnEntity(int EntityID, int TypeID, Guid UUID, Location location) { - Entity entity = new Entity(EntityID, EntityType, location); + Entity entity = new Entity(EntityID, TypeID, EntityType.NonLivingThings, location); entities.Add(EntityID, entity); foreach (ChatBot bot in bots.ToArray()) bot.OnEntitySpawn(entity); @@ -1096,12 +1114,29 @@ namespace MinecraftClient /// Called when an Entity was created/spawned. /// /// - /// + /// /// /// - public void OnSpawnLivingEntity(int EntityID, int EntityType, Guid UUID, Location location) + /// Cannot determine is a Mob or a Cuty Animal + public void OnSpawnLivingEntity(int EntityID, int TypeID, Guid UUID, Location location) { - Entity entity = new Entity(EntityID, EntityType, location); + Entity entity = new Entity(EntityID, TypeID, EntityType.MobAndAnimal, location); + entities.Add(EntityID, entity); + foreach (ChatBot bot in bots.ToArray()) + bot.OnEntitySpawn(entity); + } + + /// + /// Called when a player was spawned/in the render distance + /// + /// + /// + /// + /// + /// + public void OnSpawnPlayer(int EntityID, Guid UUID, Location location, byte Yaw, byte Pitch) + { + Entity entity = new Entity(EntityID, EntityType.Player, location); entities.Add(EntityID, entity); foreach (ChatBot bot in bots.ToArray()) bot.OnEntitySpawn(entity); @@ -1118,7 +1153,7 @@ namespace MinecraftClient if (entities.ContainsKey(a)) { foreach (ChatBot bot in bots.ToArray()) - bot.OnEntityDespawn(new Entity(entities[a].ID, entities[a].Type, entities[a].Location)); + bot.OnEntityDespawn(new Entity(entities[a].ID, entities[a].TypeID, entities[a].Type, entities[a].Location)); entities.Remove(a); } } @@ -1143,7 +1178,7 @@ namespace MinecraftClient entities[EntityID].Location = L; foreach (ChatBot bot in bots.ToArray()) - bot.OnEntityMove(new Entity(entities[EntityID].ID, entities[EntityID].Type, entities[EntityID].Location)); + bot.OnEntityMove(new Entity(entities[EntityID].ID, entities[EntityID].TypeID, entities[EntityID].Type, entities[EntityID].Location)); } } @@ -1163,7 +1198,7 @@ namespace MinecraftClient entities[EntityID].Location = location; foreach (ChatBot bot in bots.ToArray()) - bot.OnEntityMove(new Entity(entities[EntityID].ID, entities[EntityID].Type, entities[EntityID].Location)); + bot.OnEntityMove(new Entity(entities[EntityID].ID, entities[EntityID].TypeID, entities[EntityID].Type, entities[EntityID].Location)); } } @@ -1236,5 +1271,23 @@ namespace MinecraftClient { return handler.SendInteractEntityPacket(EntityID, type); } + // not work :( + public bool PlaceBlock(Location location) + { + ConsoleIO.WriteLine(location.ToString()); + return handler.SendPlayerBlockPlacement(0, location, 1, 0.5f, 0.5f, 0.5f, false); + } + + public bool ChangeSlot(short slot) + { + if (slot >= 0 && slot <= 8) + { + return handler.SendHeldItemChange(slot); + } + else + { + return false; + } + } } } diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index f5ffe2d4..9773fe0b 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -88,8 +88,10 @@ + + @@ -100,6 +102,9 @@ + + + @@ -107,6 +112,7 @@ + @@ -286,6 +292,7 @@ +