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"