feat: add autodig tool switching

Agent-Logs-Url: https://github.com/MCCTeam/Minecraft-Console-Client/sessions/e83f9c2a-a85c-4763-821b-c5b4a0db75a6

Co-authored-by: milutinke <441903+milutinke@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-03-31 08:44:23 +00:00
parent 65dec59687
commit 1a86655dfb
4 changed files with 173 additions and 3 deletions

View file

@ -5,7 +5,9 @@ using System.Threading;
using Brigadier.NET.Builder;
using MinecraftClient.CommandHandler;
using MinecraftClient.CommandHandler.Patch;
using MinecraftClient.Inventory;
using MinecraftClient.Mapping;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
using MinecraftClient.Scripting;
using Tomlet.Attributes;
@ -25,15 +27,12 @@ namespace MinecraftClient.ChatBots
public bool Enabled = false;
[NonSerialized]
[TomlInlineComment("$ChatBot.AutoDig.Auto_Tool_Switch$")]
public bool Auto_Tool_Switch = false;
[NonSerialized]
[TomlInlineComment("$ChatBot.AutoDig.Durability_Limit$")]
public int Durability_Limit = 2;
[NonSerialized]
[TomlInlineComment("$ChatBot.AutoDig.Drop_Low_Durability_Tools$")]
public bool Drop_Low_Durability_Tools = false;
@ -65,6 +64,8 @@ namespace MinecraftClient.ChatBots
public void OnSettingUpdate()
{
Durability_Limit = Math.Max(0, Durability_Limit);
if (Auto_Start_Delay >= 0)
Auto_Start_Delay = Math.Max(0.1, Auto_Start_Delay);
@ -225,6 +226,102 @@ namespace MinecraftClient.ChatBots
}
}
private static int GetLegacyMaxDamage(ItemType itemType)
{
return itemType switch
{
ItemType.WoodenPickaxe or ItemType.WoodenAxe or ItemType.WoodenShovel or ItemType.WoodenSword or ItemType.WoodenHoe => 59,
ItemType.StonePickaxe or ItemType.StoneAxe or ItemType.StoneShovel or ItemType.StoneSword or ItemType.StoneHoe => 131,
ItemType.IronPickaxe or ItemType.IronAxe or ItemType.IronShovel or ItemType.IronSword or ItemType.IronHoe => 250,
ItemType.GoldenPickaxe or ItemType.GoldenAxe or ItemType.GoldenShovel or ItemType.GoldenSword or ItemType.GoldenHoe => 32,
ItemType.DiamondPickaxe or ItemType.DiamondAxe or ItemType.DiamondShovel or ItemType.DiamondSword or ItemType.DiamondHoe => 1561,
ItemType.NetheritePickaxe or ItemType.NetheriteAxe or ItemType.NetheriteShovel or ItemType.NetheriteSword or ItemType.NetheriteHoe => 2031,
ItemType.Shears => 238,
_ => 0
};
}
private static int GetMaxDamage(Item item)
{
if (item.Components is not null)
{
var maxDamageComponent = item.Components.OfType<MaxDamageComponent>().FirstOrDefault();
if (maxDamageComponent is not null)
return maxDamageComponent.MaxDamage;
}
return GetLegacyMaxDamage(item.Type);
}
private static int GetRemainingDurability(Item item)
{
int maxDamage = GetMaxDamage(item);
return maxDamage > 0 ? maxDamage - item.Damage : int.MaxValue;
}
private bool HasEnoughDurability(Item item)
{
return Config.Durability_Limit <= 0 || GetRemainingDurability(item) >= Config.Durability_Limit;
}
private bool IsBelowDurabilityLimit(Item? item)
{
return item is not null && Config.Durability_Limit > 0 && GetRemainingDurability(item) < Config.Durability_Limit;
}
private static bool IsRecommendedTool(Item? item, ItemType[] recommendedTools)
{
return item is not null && recommendedTools.Contains(item.Type);
}
private bool SwapToolIntoHand(int sourceSlot, int handSlot)
{
return WindowAction(0, sourceSlot, WindowActionType.LeftClick)
&& WindowAction(0, handSlot, WindowActionType.LeftClick)
&& WindowAction(0, sourceSlot, WindowActionType.LeftClick);
}
private bool EnsureSuitableTool(Material blockType)
{
if (!inventoryEnabled || !Config.Auto_Tool_Switch)
return true;
ItemType[] recommendedTools = Material2Tool.GetCorrectToolForBlock(blockType);
if (recommendedTools.Length == 0)
return true;
Container container = GetPlayerInventory();
int handSlot = 36 + GetCurrentSlot();
container.Items.TryGetValue(handSlot, out Item? currentTool);
if (IsRecommendedTool(currentTool, recommendedTools) && currentTool is not null && HasEnoughDurability(currentTool))
return true;
foreach (ItemType recommendedTool in recommendedTools)
{
foreach ((int slot, Item item) in container.Items)
{
if (slot == handSlot || item.Type != recommendedTool || !HasEnoughDurability(item))
continue;
if (!SwapToolIntoHand(slot, handSlot))
return false;
LogToConsole(GetTimestamp() + ": " + string.Format(Translations.bot_autodig_switch, slot, item.GetTypeString()));
if (Config.Drop_Low_Durability_Tools && IsBelowDurabilityLimit(currentTool) &&
WindowAction(0, slot, WindowActionType.DropItemStack))
{
LogToConsole(GetTimestamp() + ": " + string.Format(Translations.bot_autodig_drop_low_durability, currentTool!.GetTypeString(), slot));
}
return true;
}
}
return !IsBelowDurabilityLimit(currentTool);
}
public override void Update()
{
lock (stateLock)
@ -293,6 +390,9 @@ namespace MinecraftClient.ChatBots
if (Config.Mode == Configs.ModeType.lookat ||
(Config.Mode == Configs.ModeType.both && Config._Locations.Contains(blockLoc)))
{
if (!EnsureSuitableTool(block.Type))
return false;
if (DigBlock(blockLoc, Direction.Down, lookAtBlock: false))
{
currentDig = blockLoc;
@ -354,6 +454,9 @@ namespace MinecraftClient.ChatBots
if (minDistance <= 6.0)
{
if (!EnsureSuitableTool(targetBlock.Type))
return false;
if (DigBlock(target, Direction.Down, lookAtBlock: true))
{
currentDig = target;
@ -388,6 +491,9 @@ namespace MinecraftClient.ChatBots
((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) ||
(Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type))))
{
if (!EnsureSuitableTool(block.Type))
return false;
if (DigBlock(blockLoc, Direction.Down, lookAtBlock: true))
{
currentDig = blockLoc;

View file

@ -437,6 +437,15 @@ namespace MinecraftClient {
}
}
/// <summary>
/// Looks up a localized string similar to Dropped low durability {0} from slot {1}..
/// </summary>
internal static string bot_autodig_drop_low_durability {
get {
return ResourceManager.GetString("bot.autodig.drop_low_durability", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The block currently pointed to is not in the allowed list..
/// </summary>
@ -473,6 +482,15 @@ namespace MinecraftClient {
}
}
/// <summary>
/// Looks up a localized string similar to Switch to {1} from slot {0}..
/// </summary>
internal static string bot_autodig_switch {
get {
return ResourceManager.GetString("bot.autodig.switch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Added item {0}.
/// </summary>

View file

@ -243,6 +243,9 @@
<data name="bot.autodig.no_inv_handle" xml:space="preserve">
<value>Inventory handling is not enabled. Unable to switch tools automatically.</value>
</data>
<data name="bot.autodig.drop_low_durability" xml:space="preserve">
<value>Dropped low durability {0} from slot {1}.</value>
</data>
<data name="bot.autodig.start" xml:space="preserve">
<value>Automatic digging has started.</value>
</data>
@ -252,6 +255,9 @@
<data name="bot.autodig.stop" xml:space="preserve">
<value>Auto-digging has been stopped.</value>
</data>
<data name="bot.autodig.switch" xml:space="preserve">
<value>Switch to {1} from slot {0}.</value>
</data>
<data name="bot.autoDrop.added" xml:space="preserve">
<value>Added item {0}</value>
</data>

View file

@ -748,6 +748,46 @@ redirectFrom:
- **Default:** `3.0`
#### `Auto_Tool_Switch`
- **Description:**
Automatically switch to a more suitable tool from your inventory before digging.
When `Durability_Limit` is above zero, tools below that durability threshold are skipped.
- **Available values:** `true` and `false`
- **Type:** `boolean`
- **Default:** `false`
#### `Durability_Limit`
- **Description:**
Will not use tools with less durability than this.
Set to `0` to disable this durability check.
- **Type:** `integer`
- **Default:** `2`
#### `Drop_Low_Durability_Tools`
- **Description:**
Drop the replaced tool if its remaining durability is below `Durability_Limit`.
This setting is only useful when `Auto_Tool_Switch` is enabled.
- **Available values:** `true` and `false`
- **Type:** `boolean`
- **Default:** `false`
#### `Dig_Timeout`
- **Description:**