From 2e1e79bcef57eda8ca3dc36a4f02cc12e2afef2a Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Wed, 8 Apr 2020 00:28:03 +0800 Subject: [PATCH] Add AutoEat #923 --- MinecraftClient/ChatBot.cs | 10 +++ MinecraftClient/ChatBots/AutoAttack.cs | 21 +++--- MinecraftClient/Commands/Inventory.cs | 1 + MinecraftClient/Inventory/Item.cs | 19 ++++++ MinecraftClient/McTcpClient.cs | 65 ++++++++++++++++++- .../Protocol/Handlers/PacketIncomingType.cs | 1 + .../Protocol/Handlers/Protocol18.cs | 4 ++ .../Handlers/Protocol18PacketTypes.cs | 3 +- .../Protocol/IMinecraftComHandler.cs | 12 ++++ MinecraftClient/Settings.cs | 3 + 10 files changed, 128 insertions(+), 11 deletions(-) diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index 93f0201d..f30f1544 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -850,5 +850,15 @@ namespace MinecraftClient Container container = Handler.GetPlayerInventory(); return new Container(container.ID, container.Type, container.Title, container.Items); } + + /// + /// Check if player is eating or not + /// + /// Some bot like AutoAttack need this. We don't want to attack while eating + /// True if is eating + protected bool GetIsEating() + { + return Handler.GetIsEating(); + } } } diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index 1f18f245..f7b0a8d8 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -32,20 +32,23 @@ namespace MinecraftClient.ChatBots public override void Update() { - if (attackCooldownCounter == 0) + if (!GetIsEating()) { - attackCooldownCounter = attackCooldown; - if (entitiesToAttack.Count > 0) + if (attackCooldownCounter == 0) { - foreach (KeyValuePair a in entitiesToAttack) + attackCooldownCounter = attackCooldown; + if (entitiesToAttack.Count > 0) { - InteractEntity(a.Key, 1); + foreach (KeyValuePair a in entitiesToAttack) + { + InteractEntity(a.Key, 1); + } } } - } - else - { - attackCooldownCounter--; + else + { + attackCooldownCounter--; + } } } diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs index 8a7f260e..044b95b7 100644 --- a/MinecraftClient/Commands/Inventory.cs +++ b/MinecraftClient/Commands/Inventory.cs @@ -43,6 +43,7 @@ namespace MinecraftClient.Commands response.Add(String.Format(" #{0}: {1} x{2}", item.Key, item.Value.Type, item.Value.Count)); else response.Add(String.Format(" #{0}: {1} x{2} - {3}ยง8", item.Key, item.Value.Type, item.Value.Count, displayName)); } + response.Add("Your selected hotbar is " + (handler.GetCurrentSlot() + 1)); return String.Join("\n", response.ToArray()); case "click": if (args.Length == 3) diff --git a/MinecraftClient/Inventory/Item.cs b/MinecraftClient/Inventory/Item.cs index 0aefaadb..43661796 100644 --- a/MinecraftClient/Inventory/Item.cs +++ b/MinecraftClient/Inventory/Item.cs @@ -70,5 +70,24 @@ namespace MinecraftClient.Inventory return null; } } + + /// + /// Check item is a food + /// + /// True if is a food + public bool IsFood() + { + // non-poison and stackable food + // remarks: auto eat may works with non-stackable food <- not tested + int[] foods = { 524, 765, 821, 823, 562, 763, 680, 629, 801, 585, 788, 630, 670, 674, 588, 587, 768, 673, 764, 777, 677, 679, 625, 800, 584, 787, 626, 678, 876, 627 }; + if (foods.Contains((int)Type)) + { + return true; + } + else + { + return false; + } + } } } diff --git a/MinecraftClient/McTcpClient.cs b/MinecraftClient/McTcpClient.cs index 42377401..88a1cb1f 100644 --- a/MinecraftClient/McTcpClient.cs +++ b/MinecraftClient/McTcpClient.cs @@ -59,8 +59,13 @@ namespace MinecraftClient private int playerEntityID; + // player health and hunger private float playerHealth; private int playerFoodSaturation; + private bool Eating = false; + private int HungerThreshold = 6; + private byte CurrentSlot = 0; + private byte LastSlot = 0; // for switch back to origin slot after eating // Entity handling private Dictionary entities = new Dictionary(); @@ -80,6 +85,8 @@ namespace MinecraftClient public Double GetServerTPS() { return serverTPS; } public float GetHealth() { return playerHealth; } public int GetSaturation() { return playerFoodSaturation; } + public byte GetCurrentSlot() { return CurrentSlot; } + public bool GetIsEating() { return Eating; } // get bots list for unloading them by commands public List GetLoadedChatBots() @@ -1530,6 +1537,7 @@ namespace MinecraftClient { if (slot >= 0 && slot <= 8) { + CurrentSlot = Convert.ToByte(slot); return handler.SendHeldItemChange(slot); } else @@ -1537,7 +1545,7 @@ namespace MinecraftClient return false; } } - + /// /// Called when client player's health changed, e.g. getting attack /// @@ -1558,6 +1566,61 @@ namespace MinecraftClient ConsoleIO.WriteLogLine("You are dead. Type /respawn to respawn."); } } + if (Settings.AutoEat) + { + if (food <= HungerThreshold || (food < 20 && health < 20)) + { + Eating = true; + FindFoodAndEat(); + } + // keep eating until full + if (food < 20 && Eating) + { + FindFoodAndEat(); + } + if (food >= 20 && Eating) + { + Eating = false; + ChangeSlot(LastSlot); + } + } + } + + public void OnHeldItemChange(byte slot) + { + CurrentSlot = slot; + } + + /// + /// Try to find food in the hotbar and eat it + /// + /// True if found + public bool FindFoodAndEat() + { + Container inventory = inventories[0]; + bool found = false; + if (inventory.Items.ContainsKey(CurrentSlot + 36) && inventory.Items[CurrentSlot + 36].IsFood()) + { + // no need to change slot + found = true; + } + else + { + LastSlot = CurrentSlot; + for (int i = 36; i <= 44; i++) + { + if (!inventory.Items.ContainsKey(i)) continue; + if (inventory.Items[i].IsFood()) + { + int slot = i - 36; + ChangeSlot((short)slot); + found = true; + break; + } + } + } + if (found) UseItemOnHand(); + return found; } } } diff --git a/MinecraftClient/Protocol/Handlers/PacketIncomingType.cs b/MinecraftClient/Protocol/Handlers/PacketIncomingType.cs index aaecc466..3fe9a929 100644 --- a/MinecraftClient/Protocol/Handlers/PacketIncomingType.cs +++ b/MinecraftClient/Protocol/Handlers/PacketIncomingType.cs @@ -42,6 +42,7 @@ namespace MinecraftClient.Protocol.Handlers EntityTeleport, EntityStatus, UpdateHealth, + HeldItemChange, UnknownPacket } } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 61d0506a..49ba70d7 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -730,6 +730,10 @@ namespace MinecraftClient.Protocol.Handlers dataTypes.ReadNextFloat(packetData); handler.OnUpdateHealth(health, food); break; + case PacketIncomingType.HeldItemChange: + byte slot = dataTypes.ReadNextByte(packetData); + handler.OnHeldItemChange(slot); + break; default: return false; //Ignored packet } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs b/MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs index 210da952..68ffef0a 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs @@ -277,7 +277,8 @@ namespace MinecraftClient.Protocol.Handlers case 0x59: return PacketIncomingType.EntityProperties; case 0x57: return PacketIncomingType.EntityTeleport; case 0x1C: return PacketIncomingType.EntityStatus; - case 0x49: return PacketIncomingType.UpdateHealth; // TODO: Add backwards support for this packet + case 0x49: return PacketIncomingType.UpdateHealth; // TODO: Add backwards support + case 0x40: return PacketIncomingType.HeldItemChange; // TODO: Add backwards support default: return PacketIncomingType.UnknownPacket; } } diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index ca0a221f..e4790913 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -212,8 +212,20 @@ namespace MinecraftClient.Protocol /// Item (may be null for empty slot) void OnSetSlot(byte inventoryID, short slotID, Item item); + /// + /// Called when player health or hunger changed. + /// + /// + /// void OnUpdateHealth(float health, int food); + /// + /// Called when client need to change slot. + /// + /// Used for setting player slot after joining game + /// + void OnHeldItemChange(byte slot); + /// /// Called when the Player entity ID has been received from the server /// diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index f2122108..79fab392 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -97,6 +97,7 @@ namespace MinecraftClient public static bool ResolveSrvRecordsShortTimeout = true; public static bool EntityHandling = false; public static bool AutoRespawn = false; + public static bool AutoEat = false; //AntiAFK Settings public static bool AntiAFK_Enabled = false; @@ -244,6 +245,7 @@ namespace MinecraftClient case "botmessagedelay": botMessageDelay = TimeSpan.FromSeconds(str2int(argValue)); break; case "debugmessages": DebugMessages = str2bool(argValue); break; case "autorespawn": AutoRespawn = str2bool(argValue); break; + case "autoeat": AutoEat = str2bool(argValue); break; case "botowners": Bots_Owners.Clear(); @@ -589,6 +591,7 @@ namespace MinecraftClient + "scriptcache=true # Cache compiled scripts for faster load on low-end devices\r\n" + "timestamps=false # Prepend timestamps to chat messages\r\n" + "autorespawn=false # Toggle auto respawn if client player was dead (make sure your spawn point is safe)\r\n" + + "autoeat=false # Toggle auto eat when player is hungry\r\n" + "\r\n" + "[AppVars]\r\n" + "# yourvar=yourvalue\r\n"