mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-29 13:04:59 +00:00
Merge origin/master into feat/mcp-server
This commit is contained in:
commit
f4c160979c
86 changed files with 16765 additions and 2758 deletions
|
|
@ -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 (currentTool is not null && IsRecommendedTool(currentTool, recommendedTools) && 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, item.GetTypeString(), slot));
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -62,6 +62,21 @@ namespace MinecraftClient.ChatBots
|
|||
[TomlInlineComment("$ChatBot.AutoFishing.Hook_Threshold$")]
|
||||
public double Hook_Threshold = 0.2;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Enable_Velocity_Detection$")]
|
||||
public bool Enable_Velocity_Detection = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Velocity_Hook_Threshold$")]
|
||||
public double Velocity_Hook_Threshold = -0.2;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Enable_Sound_Detection$")]
|
||||
public bool Enable_Sound_Detection = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Sound_Distance$")]
|
||||
public double Sound_Distance = 5.0;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Detection_Warmup$")]
|
||||
public double Detection_Warmup = 1.0;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Log_Fish_Bobber$")]
|
||||
public bool Log_Fish_Bobber = false;
|
||||
|
||||
|
|
@ -97,6 +112,15 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
if (Hook_Threshold < 0)
|
||||
Hook_Threshold = -Hook_Threshold;
|
||||
|
||||
if (Velocity_Hook_Threshold > 0)
|
||||
Velocity_Hook_Threshold = -Velocity_Hook_Threshold;
|
||||
|
||||
if (Sound_Distance < 0)
|
||||
Sound_Distance = -Sound_Distance;
|
||||
|
||||
if (Detection_Warmup < 0)
|
||||
Detection_Warmup = 0;
|
||||
}
|
||||
|
||||
public struct LocationConfig
|
||||
|
|
@ -171,6 +195,7 @@ namespace MinecraftClient.ChatBots
|
|||
private Entity? fishingBobber;
|
||||
private Location LastPos = Location.Zero;
|
||||
private DateTime CaughtTime = DateTime.Now;
|
||||
private DateTime BobberSpawnTime = DateTime.MinValue;
|
||||
private int fishItemCounter = 15;
|
||||
private Dictionary<ItemType, uint> fishItemCnt = new();
|
||||
private Entity fishItem = new(-1, EntityType.Item, Location.Zero);
|
||||
|
|
@ -464,6 +489,7 @@ namespace MinecraftClient.ChatBots
|
|||
fishingBobber = entity;
|
||||
LastPos = entity.Location;
|
||||
isFishing = true;
|
||||
BobberSpawnTime = DateTime.Now;
|
||||
|
||||
castTimeout = 24;
|
||||
counter = 0;
|
||||
|
|
@ -500,7 +526,7 @@ namespace MinecraftClient.ChatBots
|
|||
public override void OnEntityMove(Entity entity)
|
||||
{
|
||||
if (isFishing && entity is not null && fishingBobber!.ID == entity.ID &&
|
||||
(state == FishingState.WaitingFishToBite || state == FishingState.WaitingFishingBobber))
|
||||
state == FishingState.WaitingFishToBite)
|
||||
{
|
||||
Location Pos = entity.Location;
|
||||
double Dx = LastPos.X - Pos.X;
|
||||
|
|
@ -515,13 +541,7 @@ namespace MinecraftClient.ChatBots
|
|||
Math.Abs(Dz) < Math.Abs(Config.Stationary_Threshold) &&
|
||||
Math.Abs(Dy) > Math.Abs(Config.Hook_Threshold))
|
||||
{
|
||||
// prevent triggering multiple time
|
||||
if ((DateTime.Now - CaughtTime).TotalSeconds > 1)
|
||||
{
|
||||
isFishing = false;
|
||||
CaughtTime = DateTime.Now;
|
||||
OnCaughtFish();
|
||||
}
|
||||
TryCatchFish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -540,6 +560,38 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnEntityVelocity(Entity entity, double velocityX, double velocityY, double velocityZ)
|
||||
{
|
||||
if (!Config.Enable_Velocity_Detection || !CanUseAdvancedDetection())
|
||||
return;
|
||||
|
||||
if (fishingBobber is null || entity.ID != fishingBobber.ID)
|
||||
return;
|
||||
|
||||
if (velocityY <= Config.Velocity_Hook_Threshold)
|
||||
TryCatchFish();
|
||||
}
|
||||
|
||||
public override void OnSoundEffect(string? soundName, Location? location, int category, float volume, float pitch,
|
||||
Entity? sourceEntity)
|
||||
{
|
||||
if (!Config.Enable_Sound_Detection || !CanUseAdvancedDetection())
|
||||
return;
|
||||
|
||||
if (!IsFishingBobberSplashSound(soundName))
|
||||
return;
|
||||
|
||||
Location? soundLocation = location;
|
||||
if (soundLocation is null && sourceEntity is not null)
|
||||
soundLocation = sourceEntity.Location;
|
||||
|
||||
if (soundLocation is null || fishingBobber is null)
|
||||
return;
|
||||
|
||||
if (soundLocation.Value.Distance(fishingBobber.Location) <= Config.Sound_Distance)
|
||||
TryCatchFish();
|
||||
}
|
||||
|
||||
public override void AfterGameJoined()
|
||||
{
|
||||
StartFishing();
|
||||
|
|
@ -562,10 +614,42 @@ namespace MinecraftClient.ChatBots
|
|||
fishingBobber = null;
|
||||
LastPos = Location.Zero;
|
||||
CaughtTime = DateTime.Now;
|
||||
BobberSpawnTime = DateTime.MinValue;
|
||||
|
||||
return base.OnDisconnect(reason, message);
|
||||
}
|
||||
|
||||
private bool CanUseAdvancedDetection()
|
||||
{
|
||||
if (!isFishing || fishingBobber is null || state != FishingState.WaitingFishToBite)
|
||||
return false;
|
||||
|
||||
return (DateTime.Now - BobberSpawnTime).TotalSeconds >= Config.Detection_Warmup;
|
||||
}
|
||||
|
||||
private void TryCatchFish()
|
||||
{
|
||||
if (!CanUseAdvancedDetection())
|
||||
return;
|
||||
|
||||
// Prevent repeated catches from multiple packets of the same bite.
|
||||
if ((DateTime.Now - CaughtTime).TotalSeconds <= 1)
|
||||
return;
|
||||
|
||||
isFishing = false;
|
||||
CaughtTime = DateTime.Now;
|
||||
OnCaughtFish();
|
||||
}
|
||||
|
||||
private static bool IsFishingBobberSplashSound(string? soundName)
|
||||
{
|
||||
return string.Equals(soundName, "minecraft:entity.fishing_bobber.splash",
|
||||
StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(soundName, "entity.fishing_bobber.splash", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(soundName, "minecraft:entity.bobber.splash", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(soundName, "entity.bobber.splash", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when detected a fish is caught
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using MinecraftClient.Scripting;
|
||||
using Tomlet.Attributes;
|
||||
|
||||
|
|
@ -95,6 +95,11 @@ namespace MinecraftClient.ChatBots
|
|||
_Initialize();
|
||||
}
|
||||
|
||||
public override void AfterGameJoined()
|
||||
{
|
||||
Configs._BotRecoAttempts = 0;
|
||||
}
|
||||
|
||||
private void _Initialize()
|
||||
{
|
||||
McClient.ReconnectionAttemptsLeft = Config.Retries;
|
||||
|
|
@ -144,10 +149,17 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
double delay = random.NextDouble() * (Config.Delay.max - Config.Delay.min) + Config.Delay.min;
|
||||
LogDebugToConsole(string.Format(string.IsNullOrEmpty(msg) ? Translations.bot_autoRelog_reconnect_always : Translations.bot_autoRelog_reconnect, msg));
|
||||
|
||||
// TODO: Change this translation string to add the retries left text
|
||||
LogToConsole(string.Format(Translations.bot_autoRelog_wait, delay) + $" ({Config.Retries - Configs._BotRecoAttempts} retries left)");
|
||||
ReconnectToTheServer(Config.Retries - Configs._BotRecoAttempts, (int)Math.Floor(delay), true);
|
||||
|
||||
int retriesLeft = Config.Retries - Configs._BotRecoAttempts;
|
||||
if (retriesLeft < 0)
|
||||
retriesLeft = 0;
|
||||
|
||||
string retriesDisplay = Config.Retries == int.MaxValue
|
||||
? Translations.bot_autoRelog_retries_unlimited
|
||||
: retriesLeft.ToString();
|
||||
|
||||
LogToConsole(string.Format(Translations.bot_autoRelog_wait_with_retries, delay, retriesDisplay));
|
||||
ReconnectToTheServer(retriesLeft, (int)Math.Floor(delay), true);
|
||||
}
|
||||
|
||||
public static bool OnDisconnectStatic(DisconnectReason reason, string message)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Brigadier.NET.Builder;
|
||||
using DSharpPlus;
|
||||
|
|
@ -34,6 +37,9 @@ namespace MinecraftClient.ChatBots
|
|||
private DiscordChannel? discordChannel;
|
||||
private BridgeDirection bridgeDirection = BridgeDirection.Both;
|
||||
|
||||
private readonly ConcurrentQueue<string> aggregationBuffer = new();
|
||||
private Timer? aggregationTimer;
|
||||
|
||||
public static Configs Config = new();
|
||||
|
||||
[TomlDoNotInlineObject]
|
||||
|
|
@ -62,6 +68,12 @@ namespace MinecraftClient.ChatBots
|
|||
[TomlInlineComment("$ChatBot.DiscordBridge.AllowOtherBotMessages$")]
|
||||
public bool Allow_Other_Bot_Messages = false;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordBridge.RelayAllMessages$")]
|
||||
public bool Relay_All_Messages = false;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordBridge.MessageAggregationInterval$")]
|
||||
public double Message_Aggregation_Interval = 3.0;
|
||||
|
||||
[TomlPrecedingComment("$ChatBot.DiscordBridge.Formats$")]
|
||||
public string PrivateMessageFormat = "**[Private Message]** {username}: {message}";
|
||||
public string PublicMessageFormat = "{username}: {message}";
|
||||
|
|
@ -70,6 +82,8 @@ namespace MinecraftClient.ChatBots
|
|||
public void OnSettingUpdate()
|
||||
{
|
||||
Message_Send_Timeout = Message_Send_Timeout <= 0 ? 3 : Message_Send_Timeout;
|
||||
if (Message_Aggregation_Interval < 0)
|
||||
Message_Aggregation_Interval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,6 +114,12 @@ namespace MinecraftClient.ChatBots
|
|||
.Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName)))
|
||||
);
|
||||
|
||||
if (Config.Message_Aggregation_Interval > 0)
|
||||
{
|
||||
var intervalMs = (int)(Config.Message_Aggregation_Interval * 1000);
|
||||
aggregationTimer = new Timer(_ => FlushAggregationBuffer(), null, intervalMs, intervalMs);
|
||||
}
|
||||
|
||||
Task.Run(async () => await MainAsync());
|
||||
}
|
||||
|
||||
|
|
@ -107,6 +127,7 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
McClient.dispatcher.Unregister(CommandName);
|
||||
McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName);
|
||||
StopAggregation();
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +168,40 @@ namespace MinecraftClient.ChatBots
|
|||
return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_DiscordBridge_direction, bridgeName));
|
||||
}
|
||||
|
||||
private void FlushAggregationBuffer()
|
||||
{
|
||||
if (aggregationBuffer.IsEmpty || !CanSendMessages())
|
||||
return;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
while (aggregationBuffer.TryDequeue(out var line))
|
||||
{
|
||||
if (sb.Length + line.Length + 1 > 1900)
|
||||
{
|
||||
SendMessage(sb.ToString());
|
||||
sb.Clear();
|
||||
}
|
||||
|
||||
if (sb.Length > 0)
|
||||
sb.AppendLine();
|
||||
sb.Append(line);
|
||||
}
|
||||
|
||||
if (sb.Length > 0)
|
||||
SendMessage(sb.ToString());
|
||||
}
|
||||
|
||||
private void StopAggregation()
|
||||
{
|
||||
if (aggregationTimer is not null)
|
||||
{
|
||||
aggregationTimer.Dispose();
|
||||
aggregationTimer = null;
|
||||
}
|
||||
|
||||
FlushAggregationBuffer();
|
||||
}
|
||||
|
||||
~DiscordBridge()
|
||||
{
|
||||
Disconnect();
|
||||
|
|
@ -188,7 +243,6 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
text = GetVerbatim(text).Trim();
|
||||
|
||||
// Stop the crash when an empty text is recived somehow
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return;
|
||||
|
||||
|
|
@ -205,7 +259,10 @@ namespace MinecraftClient.ChatBots
|
|||
message = Config.TeleportRequestMessageFormat.Replace("{username}", username).Replace("{timestamp}", GetTimestamp()).Trim();
|
||||
teleportRequest = true;
|
||||
}
|
||||
else message = text;
|
||||
else if (Config.Relay_All_Messages)
|
||||
message = text;
|
||||
else
|
||||
return;
|
||||
|
||||
if (teleportRequest)
|
||||
{
|
||||
|
|
@ -223,7 +280,13 @@ namespace MinecraftClient.ChatBots
|
|||
SendMessage(messageBuilder);
|
||||
return;
|
||||
}
|
||||
else SendMessage(GetDiscordText(message));
|
||||
|
||||
string discordText = GetDiscordText(message);
|
||||
|
||||
if (Config.Message_Aggregation_Interval > 0)
|
||||
aggregationBuffer.Enqueue(discordText);
|
||||
else
|
||||
SendMessage(discordText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue