From c57ac183d587c0ce3fb14cd6b3d45c5fcaf4d038 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 8 Oct 2022 17:56:32 +0800 Subject: [PATCH] Add AutoDig ChatBot --- MinecraftClient/ChatBots/AntiAFK.cs | 8 +- MinecraftClient/ChatBots/AutoDig.cs | 392 ++++++++++++++++++ MinecraftClient/ChatBots/AutoFishing.cs | 16 +- MinecraftClient/ChatBots/AutoRelog.cs | 2 +- MinecraftClient/ChatBots/FollowPlayer.cs | 2 +- MinecraftClient/ChatBots/PlayerListLogger.cs | 2 +- MinecraftClient/ChatBots/ScriptScheduler.cs | 44 +- MinecraftClient/McClient.cs | 37 +- .../Protocol/Handlers/Protocol18.cs | 31 +- .../Protocol/IMinecraftComHandler.cs | 7 + MinecraftClient/Resources/lang/en.ini | 24 ++ MinecraftClient/Resources/lang/zh-Hans.ini | 26 +- MinecraftClient/Resources/lang/zh-Hant.ini | 24 ++ MinecraftClient/Scripting/ChatBot.cs | 8 + MinecraftClient/Settings.cs | 13 + MinecraftClient/WinAPI/ConsoleIcon.cs | 7 +- 16 files changed, 579 insertions(+), 64 deletions(-) create mode 100644 MinecraftClient/ChatBots/AutoDig.cs diff --git a/MinecraftClient/ChatBots/AntiAFK.cs b/MinecraftClient/ChatBots/AntiAFK.cs index e1056f5e..0f89d202 100644 --- a/MinecraftClient/ChatBots/AntiAFK.cs +++ b/MinecraftClient/ChatBots/AntiAFK.cs @@ -38,9 +38,6 @@ namespace MinecraftClient.ChatBots [TomlInlineComment("$config.ChatBot.AntiAfk.Walk_Retries$")] public int Walk_Retries = 20; - [NonSerialized] - public int _DelayMin, _DelayMax; - public void OnSettingUpdate() { if (Walk_Range <= 0) @@ -61,9 +58,6 @@ namespace MinecraftClient.ChatBots LogToConsole(BotName, Translations.TryGet("bot.antiafk.swapping")); } - _DelayMin = (int)Math.Round(Delay.min * 10); - _DelayMax = (int)Math.Round(Delay.max * 10); - Command ??= string.Empty; } @@ -115,7 +109,7 @@ namespace MinecraftClient.ChatBots { DoAntiAfkStuff(); count = 0; - nextrun = random.Next(Config._DelayMin, Config._DelayMax); + nextrun = random.Next(Settings.DoubleToTick(Config.Delay.min), Settings.DoubleToTick(Config.Delay.max)); } } diff --git a/MinecraftClient/ChatBots/AutoDig.cs b/MinecraftClient/ChatBots/AutoDig.cs new file mode 100644 index 00000000..1e6f0936 --- /dev/null +++ b/MinecraftClient/ChatBots/AutoDig.cs @@ -0,0 +1,392 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MinecraftClient.Inventory; +using MinecraftClient.Mapping; +using MinecraftClient.Mapping.BlockPalettes; +using Tomlet.Attributes; +using static MinecraftClient.ChatBots.AutoCraft.Configs; + +namespace MinecraftClient.ChatBots +{ + public class AutoDig : ChatBot + { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "AutoDig"; + + public bool Enabled = false; + + [NonSerialized] + [TomlInlineComment("$config.ChatBot.AutoDig.Auto_Tool_Switch$")] + public bool Auto_Tool_Switch = false; + + [NonSerialized] + [TomlInlineComment("$config.ChatBot.AutoDig.Durability_Limit$")] + public int Durability_Limit = 2; + + [NonSerialized] + [TomlInlineComment("$config.ChatBot.AutoDig.Drop_Low_Durability_Tools$")] + public bool Drop_Low_Durability_Tools = false; + + [TomlInlineComment("$config.ChatBot.AutoDig.Mode$")] + public ModeType Mode = ModeType.lookat; + + [TomlInlineComment("$config.ChatBot.AutoDig.Locations$")] + public Coordination[] Locations = new Coordination[] { new(123.5, 64, 234.5), new(124.5, 63, 235.5) }; + + [TomlInlineComment("$config.ChatBot.AutoDig.Auto_Start_Delay$")] + public double Auto_Start_Delay = 3.0; + + [TomlInlineComment("$config.ChatBot.AutoDig.Dig_Timeout$")] + public double Dig_Timeout = 30.0; + + [TomlInlineComment("$config.ChatBot.AutoDig.Log_Block_Dig$")] + public bool Log_Block_Dig = true; + + [TomlInlineComment("$config.ChatBot.AutoDig.List_Type$")] + public ListType List_Type = ListType.whitelist; + + public List Blocks = new() { Material.Cobblestone, Material.Stone }; + + [NonSerialized] + public Location[] _Locations = Array.Empty(); + + public void OnSettingUpdate() + { + if (Auto_Start_Delay >= 0) + Auto_Start_Delay = Math.Max(0.1, Auto_Start_Delay); + + if (Dig_Timeout >= 0) + Dig_Timeout = Math.Max(0.1, Dig_Timeout); + + _Locations = new Location[Locations.Length]; + for (int i = 0; i < Locations.Length; ++i) + _Locations[i] = new(Locations[i].x, Locations[i].y, Locations[i].z); + } + + public enum ModeType { lookat, fixedpos, both }; + + public enum ListType { blacklist, whitelist }; + + public struct Coordination + { + public double x, y, z; + + public Coordination(double x, double y, double z) + { + this.x = x; this.y = y; this.z = z; + } + } + } + + private bool inventoryEnabled; + + private int counter = 0; + private readonly object stateLock = new(); + private State state = State.WaitJoinGame; + + bool AlreadyWaitting = false; + private Location currentDig = Location.Zero; + + private enum State + { + WaitJoinGame, + WaitingStart, + Digging, + Stopping, + } + + public override void Initialize() + { + if (!GetTerrainEnabled()) + { + LogToConsoleTranslated("extra.terrainandmovement_required"); + LogToConsoleTranslated("general.bot_unload"); + UnloadBot(); + return; + } + + inventoryEnabled = GetInventoryEnabled(); + if (!inventoryEnabled && Config.Auto_Tool_Switch) + LogToConsoleTranslated("bot.autodig.no_inv_handle"); + + RegisterChatBotCommand("digbot", Translations.Get("bot.digbot.cmd"), GetHelp(), CommandHandler); + } + + public string CommandHandler(string cmd, string[] args) + { + if (args.Length > 0) + { + switch (args[0]) + { + case "start": + lock (stateLock) + { + counter = 0; + state = State.WaitingStart; + } + return Translations.Get("bot.autodig.start"); + case "stop": + StopDigging(); + return Translations.Get("bot.autodig.stop"); + case "help": + return GetCommandHelp(args.Length >= 2 ? args[1] : ""); + default: + return GetHelp(); + } + } + else return GetHelp(); + } + + + private void StartDigging() + { + if (Config.Auto_Start_Delay > 0) + { + double delay = Config.Auto_Start_Delay; + LogToConsole(Translations.Get("bot.autodig.start_delay", delay)); + lock (stateLock) + { + counter = Settings.DoubleToTick(delay); + state = State.WaitingStart; + } + } + else + { + lock (stateLock) + { + state = State.WaitJoinGame; + } + } + } + + private void StopDigging() + { + state = State.Stopping; + lock (stateLock) + { + state = State.Stopping; + } + } + + public override void Update() + { + lock (stateLock) + { + switch (state) + { + case State.WaitJoinGame: + break; + case State.WaitingStart: + if (--counter < 0) + { + if (DoDigging()) + { + AlreadyWaitting = false; + state = State.Digging; + } + else + { + counter = 0; + state = State.WaitingStart; + } + } + break; + case State.Digging: + if (++counter > Settings.DoubleToTick(Config.Dig_Timeout)) + { + LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autodig.dig_timeout")); + state = State.WaitingStart; + counter = 0; + } + break; + case State.Stopping: + break; + } + } + } + + private bool DoDigging() + { + if (Config.Mode == Configs.ModeType.lookat || Config.Mode == Configs.ModeType.both) + { + (bool hasBlock, Location blockLoc, Block block) = GetLookingBlock(4.5, false); + if (!hasBlock) + { + if (!AlreadyWaitting) + { + AlreadyWaitting = true; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.too_far")); + } + return false; + } + else if (block.Type == Material.Air) + { + if (!AlreadyWaitting) + { + AlreadyWaitting = true; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.no_block")); + } + return false; + } + else if ((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) || + (Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type))) + { + if (Config.Mode == Configs.ModeType.lookat || + (Config.Mode == Configs.ModeType.both && Config._Locations.Contains(blockLoc))) + { + if (DigBlock(blockLoc, lookAtBlock: false)) + { + currentDig = blockLoc; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.dig", blockLoc.X, blockLoc.Y, blockLoc.Z, block.Type)); + return true; + } + else + { + LogToConsole(Translations.Get("cmd.dig.fail")); + return false; + } + } + else + { + if (!AlreadyWaitting) + { + AlreadyWaitting = true; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("bot.autodig.not_allow")); + } + return false; + } + } + else + { + if (!AlreadyWaitting) + { + AlreadyWaitting = true; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("bot.autodig.not_allow")); + } + return false; + } + } + else if (Config.Mode == Configs.ModeType.fixedpos) + { + Location current = GetCurrentLocation(); + + double minDistance = double.MaxValue; + Location target = Location.Zero; + Block targetBlock = Block.Air; + foreach (Location location in Config._Locations) + { + Block block = GetWorld().GetBlock(location); + if (block.Type != Material.Air && + ((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) || + (Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type)))) + { + double distance = current.Distance(location); + if (distance < minDistance) + { + minDistance = distance; + target = location; + targetBlock = block; + } + } + } + + if (minDistance <= 6.0) + { + if (DigBlock(target, lookAtBlock: true)) + { + currentDig = target; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.dig", target.X, target.Y, target.Z, targetBlock.Type)); + return true; + } + else + { + LogToConsole(Translations.Get("cmd.dig.fail")); + return false; + } + } + else + { + if (!AlreadyWaitting) + { + AlreadyWaitting = true; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.no_block")); + } + return false; + } + } + return false; + } + + public override void OnBlockChange(Location location, Block block) + { + if (location == currentDig) + { + lock (stateLock) + { + if (state == State.Digging && location == currentDig) + { + currentDig = Location.Zero; + counter = 0; + state = State.WaitingStart; + } + } + } + } + + public override void AfterGameJoined() + { + StartDigging(); + } + + public override void OnRespawn() + { + StartDigging(); + } + + public override void OnDeath() + { + StopDigging(); + } + + public override bool OnDisconnect(DisconnectReason reason, string message) + { + StopDigging(); + + return base.OnDisconnect(reason, message); + } + + private static string GetHelp() + { + return Translations.Get("bot.autodig.available_cmd", "start, stop, help"); + } + + private string GetCommandHelp(string cmd) + { + return cmd.ToLower() switch + { +#pragma warning disable format // @formatter:off + "start" => Translations.Get("bot.autodig.help.start"), + "stop" => Translations.Get("bot.autodig.help.stop"), + "help" => Translations.Get("bot.autodig.help.help"), + _ => GetHelp(), +#pragma warning restore format // @formatter:on + }; + } + } +} diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs index 91e4b4d3..609ced3b 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -184,7 +184,7 @@ namespace MinecraftClient.ChatBots LogToConsole(Translations.Get("bot.autoFish.start", delay)); lock (stateLock) { - counter = (int)(delay * 10); + counter = Settings.DoubleToTick(delay); state = FishingState.StartMove; } } @@ -224,7 +224,7 @@ namespace MinecraftClient.ChatBots break; case FishingState.WaitingToCast: if (AutoEat.Eating) - counter = (int)(Config.Cast_Delay * 10); + counter = Settings.DoubleToTick(Config.Cast_Delay); else if (--counter < 0) state = FishingState.CastingRod; break; @@ -240,16 +240,16 @@ namespace MinecraftClient.ChatBots castTimeout *= 2; // Exponential backoff LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.cast_timeout", castTimeout / 10.0)); - counter = (int)(Config.Cast_Delay * 10); + counter = Settings.DoubleToTick(Config.Cast_Delay); state = FishingState.WaitingToCast; } break; case FishingState.WaitingFishToBite: - if (++counter > (int)(Config.Fishing_Timeout * 10)) + if (++counter > Settings.DoubleToTick(Config.Fishing_Timeout)) { LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.fishing_timeout")); - counter = (int)(Config.Cast_Delay * 10); + counter = Settings.DoubleToTick(Config.Cast_Delay); state = FishingState.WaitingToCast; } break; @@ -271,7 +271,7 @@ namespace MinecraftClient.ChatBots } else { - counter = (int)(Config.Cast_Delay * 10); + counter = Settings.DoubleToTick(Config.Cast_Delay); state = FishingState.DurabilityCheck; goto case FishingState.DurabilityCheck; } @@ -290,7 +290,7 @@ namespace MinecraftClient.ChatBots case FishingState.DurabilityCheck: if (DurabilityCheck()) { - counter = (int)(Config.Cast_Delay * 10); + counter = Settings.DoubleToTick(Config.Cast_Delay); state = FishingState.WaitingToCast; } break; @@ -338,7 +338,7 @@ namespace MinecraftClient.ChatBots lock (stateLock) { - counter = (int)(Config.Cast_Delay * 10); + counter = Settings.DoubleToTick(Config.Cast_Delay); state = FishingState.WaitingToCast; } } diff --git a/MinecraftClient/ChatBots/AutoRelog.cs b/MinecraftClient/ChatBots/AutoRelog.cs index 071873ce..ba16264a 100644 --- a/MinecraftClient/ChatBots/AutoRelog.cs +++ b/MinecraftClient/ChatBots/AutoRelog.cs @@ -129,7 +129,7 @@ namespace MinecraftClient.ChatBots private void LaunchDelayedReconnection(string? msg) { - int delay = random.Next((int)Config.Delay.min, (int)Config.Delay.max); + int delay = random.Next(Settings.DoubleToTick(Config.Delay.min), Settings.DoubleToTick(Config.Delay.max)); LogDebugToConsoleTranslated(String.IsNullOrEmpty(msg) ? "bot.autoRelog.reconnect_always" : "bot.autoRelog.reconnect", msg); LogToConsoleTranslated("bot.autoRelog.wait", delay); System.Threading.Thread.Sleep(delay * 1000); diff --git a/MinecraftClient/ChatBots/FollowPlayer.cs b/MinecraftClient/ChatBots/FollowPlayer.cs index c05355d0..5d202ab1 100644 --- a/MinecraftClient/ChatBots/FollowPlayer.cs +++ b/MinecraftClient/ChatBots/FollowPlayer.cs @@ -113,7 +113,7 @@ namespace MinecraftClient.ChatBots public override void OnEntityMove(Entity entity) { - if (_updateCounter < (int)(Config.Update_Limit * 10)) + if (_updateCounter < Settings.DoubleToTick(Config.Update_Limit)) return; _updateCounter = 0; diff --git a/MinecraftClient/ChatBots/PlayerListLogger.cs b/MinecraftClient/ChatBots/PlayerListLogger.cs index 4d6424ce..8c59af89 100644 --- a/MinecraftClient/ChatBots/PlayerListLogger.cs +++ b/MinecraftClient/ChatBots/PlayerListLogger.cs @@ -39,7 +39,7 @@ namespace MinecraftClient.ChatBots public override void Update() { count++; - if (count == (int)(Config.Delay * 10)) + if (count >= Settings.DoubleToTick(Config.Delay)) { DateTime now = DateTime.Now; diff --git a/MinecraftClient/ChatBots/ScriptScheduler.cs b/MinecraftClient/ChatBots/ScriptScheduler.cs index a977874d..642d2223 100644 --- a/MinecraftClient/ChatBots/ScriptScheduler.cs +++ b/MinecraftClient/ChatBots/ScriptScheduler.cs @@ -26,7 +26,7 @@ namespace MinecraftClient.ChatBots Trigger_On_First_Login: false, Trigger_On_Login: false, Trigger_On_Times: new(true, new TimeSpan[] { new(14, 00, 00) }), - Trigger_On_Interval: new(true, 10, 20), + Trigger_On_Interval: new(true, 3.6, 4.8), Action: "send /hello" ), new TaskConfig( @@ -43,8 +43,8 @@ namespace MinecraftClient.ChatBots { foreach (TaskConfig task in TaskList) { - task.Trigger_On_Interval.MinTime = Math.Max(1, task.Trigger_On_Interval.MinTime); - task.Trigger_On_Interval.MaxTime = Math.Max(1, task.Trigger_On_Interval.MaxTime); + task.Trigger_On_Interval.MinTime = Math.Max(0.1, task.Trigger_On_Interval.MinTime); + task.Trigger_On_Interval.MaxTime = Math.Max(0.1, task.Trigger_On_Interval.MaxTime); if (task.Trigger_On_Interval.MinTime > task.Trigger_On_Interval.MaxTime) (task.Trigger_On_Interval.MinTime, task.Trigger_On_Interval.MaxTime) = (task.Trigger_On_Interval.MaxTime, task.Trigger_On_Interval.MinTime); @@ -59,7 +59,7 @@ namespace MinecraftClient.ChatBots { if (Settings.Config.Logging.DebugMessages) LogToConsole(BotName, Translations.TryGet("bot.scriptScheduler.loaded_task", Task2String(task))); - task.Trigger_On_Interval_Countdown = task.Trigger_On_Interval.MinTime; //Init countdown for interval + task.Trigger_On_Interval_Countdown = Settings.DoubleToTick(task.Trigger_On_Interval.MinTime); //Init countdown for interval } else { @@ -130,27 +130,27 @@ namespace MinecraftClient.ChatBots public struct TriggerOnIntervalConfig { public bool Enable = false; - public int MinTime, MaxTime; + public double MinTime, MaxTime; - public TriggerOnIntervalConfig(int value) + public TriggerOnIntervalConfig(double value) { this.Enable = true; MinTime = MaxTime = value; } - public TriggerOnIntervalConfig(bool Enable, int value) + public TriggerOnIntervalConfig(bool Enable, double value) { this.Enable = Enable; MinTime = MaxTime = value; } - public TriggerOnIntervalConfig(int min, int max) + public TriggerOnIntervalConfig(double min, double max) { this.MinTime = min; this.MaxTime = max; } - public TriggerOnIntervalConfig(bool Enable, int min, int max) + public TriggerOnIntervalConfig(bool Enable, double min, double max) { this.Enable = Enable; this.MinTime = min; @@ -199,16 +199,6 @@ namespace MinecraftClient.ChatBots task.Trigger_On_Time_Already_Triggered = false; } - if (task.Trigger_On_Interval.Enable) - { - if (task.Trigger_On_Interval_Countdown == 0) - { - task.Trigger_On_Interval_Countdown = random.Next(task.Trigger_On_Interval.MinTime, task.Trigger_On_Interval.MaxTime); - LogDebugToConsoleTranslated("bot.scriptScheduler.running_inverval", task.Action); - PerformInternalCommand(task.Action); - } - else task.Trigger_On_Interval_Countdown--; - } } } else @@ -227,6 +217,22 @@ namespace MinecraftClient.ChatBots } } else verifytasks_timeleft--; + + foreach (TaskConfig task in Config.TaskList) + { + if (task.Trigger_On_Interval.Enable) + { + if (task.Trigger_On_Interval_Countdown == 0) + { + task.Trigger_On_Interval_Countdown = random.Next( + Settings.DoubleToTick(task.Trigger_On_Interval.MinTime), Settings.DoubleToTick(task.Trigger_On_Interval.MaxTime) + ); + LogDebugToConsoleTranslated("bot.scriptScheduler.running_inverval", task.Action); + PerformInternalCommand(task.Action); + } + else task.Trigger_On_Interval_Countdown--; + } + } } public override bool OnDisconnect(DisconnectReason reason, string message) diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 6f9e85c5..c70ecb5c 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -252,25 +252,25 @@ namespace MinecraftClient /// private void RegisterBots(bool reload = false) { - if (Config.ChatBot.AntiAFK.Enabled) { BotLoad(new AntiAFK()); } - if (Config.ChatBot.HangmanGame.Enabled) { BotLoad(new HangmanGame()); } if (Config.ChatBot.Alerts.Enabled) { BotLoad(new Alerts()); } - if (Config.ChatBot.ChatLog.Enabled) { BotLoad(new ChatLog()); } - if (Config.ChatBot.PlayerListLogger.Enabled) { BotLoad(new PlayerListLogger()); } - if (Config.ChatBot.AutoRelog.Enabled) { BotLoad(new AutoRelog()); } - if (Config.ChatBot.ScriptScheduler.Enabled) { BotLoad(new ScriptScheduler()); } - if (Config.ChatBot.RemoteControl.Enabled) { BotLoad(new RemoteControl()); } - if (Config.ChatBot.AutoRespond.Enabled) { BotLoad(new AutoRespond()); } + if (Config.ChatBot.AntiAFK.Enabled) { BotLoad(new AntiAFK()); } if (Config.ChatBot.AutoAttack.Enabled) { BotLoad(new AutoAttack()); } - if (Config.ChatBot.AutoFishing.Enabled) { BotLoad(new AutoFishing()); } - if (Config.ChatBot.AutoEat.Enabled) { BotLoad(new AutoEat()); } - if (Config.ChatBot.Mailer.Enabled) { BotLoad(new Mailer()); } if (Config.ChatBot.AutoCraft.Enabled) { BotLoad(new AutoCraft()); } + if (Config.ChatBot.AutoDig.Enabled) { BotLoad(new AutoDig()); } if (Config.ChatBot.AutoDrop.Enabled) { BotLoad(new AutoDrop()); } - if (Config.ChatBot.ReplayCapture.Enabled && reload) { BotLoad(new ReplayCapture()); } + if (Config.ChatBot.AutoEat.Enabled) { BotLoad(new AutoEat()); } + if (Config.ChatBot.AutoFishing.Enabled) { BotLoad(new AutoFishing()); } + if (Config.ChatBot.AutoRelog.Enabled) { BotLoad(new AutoRelog()); } + if (Config.ChatBot.AutoRespond.Enabled) { BotLoad(new AutoRespond()); } + if (Config.ChatBot.ChatLog.Enabled) { BotLoad(new ChatLog()); } if (Config.ChatBot.FollowPlayer.Enabled) { BotLoad(new FollowPlayer()); } + if (Config.ChatBot.HangmanGame.Enabled) { BotLoad(new HangmanGame()); } + if (Config.ChatBot.Mailer.Enabled) { BotLoad(new Mailer()); } if (Config.ChatBot.Map.Enabled) { BotLoad(new Map()); } - + if (Config.ChatBot.PlayerListLogger.Enabled) { BotLoad(new PlayerListLogger()); } + if (Config.ChatBot.RemoteControl.Enabled) { BotLoad(new RemoteControl()); } + if (Config.ChatBot.ReplayCapture.Enabled && reload) { BotLoad(new ReplayCapture()); } + if (Config.ChatBot.ScriptScheduler.Enabled) { BotLoad(new ScriptScheduler()); } //Add your ChatBot here by uncommenting and adapting //BotLoad(new ChatBots.YourBot()); } @@ -3293,6 +3293,17 @@ namespace MinecraftClient } } + /// + /// Called when a block is changed. + /// + /// The location of the block. + /// The block + public void OnBlockChange(Location location, Block block) + { + world.SetBlock(location, block); + DispatchBotEvent(bot => bot.OnBlockChange(location, block)); + } + #endregion } } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 86fd1b86..f8f20206 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -976,18 +976,20 @@ namespace MinecraftClient.Protocol.Handlers int blocksSize = dataTypes.ReadNextVarInt(packetData); for (int i = 0; i < blocksSize; i++) { - ulong block = (ulong)dataTypes.ReadNextVarLong(packetData); - int blockId = (int)(block >> 12); - int localX = (int)((block >> 8) & 0x0F); - int localZ = (int)((block >> 4) & 0x0F); - int localY = (int)(block & 0x0F); + ulong chunkSectionPosition = (ulong)dataTypes.ReadNextVarLong(packetData); + int blockId = (int)(chunkSectionPosition >> 12); + int localX = (int)((chunkSectionPosition >> 8) & 0x0F); + int localZ = (int)((chunkSectionPosition >> 4) & 0x0F); + int localY = (int)(chunkSectionPosition & 0x0F); - Block b = new((ushort)blockId); + Block block = new((ushort)blockId); int blockX = (sectionX * 16) + localX; int blockY = (sectionY * 16) + localY; int blockZ = (sectionZ * 16) + localZ; - var l = new Location(blockX, blockY, blockZ); - handler.GetWorld().SetBlock(l, b); + + Location location = new(blockX, blockY, blockZ); + + handler.OnBlockChange(location, block); } } else @@ -1019,8 +1021,10 @@ namespace MinecraftClient.Protocol.Handlers int blockX = locationXZ >> 4; int blockZ = locationXZ & 0x0F; + + Location location = new(chunkX, chunkZ, blockX, blockY, blockZ); Block block = new(blockIdMeta); - handler.GetWorld().SetBlock(new Location(chunkX, chunkZ, blockX, blockY, blockZ), block); + handler.OnBlockChange(location, block); } } } @@ -1050,11 +1054,16 @@ namespace MinecraftClient.Protocol.Handlers int blockZ = dataTypes.ReadNextInt(packetData); short blockId = (short)dataTypes.ReadNextVarInt(packetData); byte blockMeta = dataTypes.ReadNextByte(packetData); - handler.GetWorld().SetBlock(new Location(blockX, blockY, blockZ), new Block(blockId, blockMeta)); + + Location location = new(blockX, blockY, blockZ); + Block block = new(blockId, blockMeta); + handler.OnBlockChange(location, block); } else { - handler.GetWorld().SetBlock(dataTypes.ReadNextLocation(packetData), new Block((ushort)dataTypes.ReadNextVarInt(packetData))); + Location location = dataTypes.ReadNextLocation(packetData); + Block block = new((ushort)dataTypes.ReadNextVarInt(packetData)); + handler.OnBlockChange(location, block); } } break; diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 724055d8..089fe342 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -432,5 +432,12 @@ namespace MinecraftClient.Protocol /// Event type /// Depends on Reason public void OnGameEvent(byte reason, float value); + + /// + /// Called when a block is changed. + /// + /// The location of the block. + /// The block + public void OnBlockChange(Location location, Block block); } } diff --git a/MinecraftClient/Resources/lang/en.ini b/MinecraftClient/Resources/lang/en.ini index 18567123..97880954 100644 --- a/MinecraftClient/Resources/lang/en.ini +++ b/MinecraftClient/Resources/lang/en.ini @@ -495,6 +495,18 @@ bot.autoCraft.debug.no_config=No config found. Writing a new one. bot.autocraft.invaild_slots=The number of slots does not match and has been adjusted automatically. bot.autocraft.invaild_invaild_result=Invalid result item! +# AutoDig +bot.autodig.start_delay=Digging will start in {0:0.0} second(s). +bot.autodig.dig_timeout=Digging block timeout, retry. +bot.autodig.not_allow=The block currently pointed to is not in the allowed list. +bot.autodig.cmd=Auto-digging ChatBot command +bot.autodig.available_cmd=Available commands: {0}. Use /digbot help for more information. +bot.autodig.start=Automatic digging has started. +bot.autodig.stop=Auto-digging has been stopped. +bot.autodig.help.start=Start the automatic digging bot. +bot.autodig.help.stop=Deactivate the automatic digging bot. +bot.autodig.help.help=Get the command description. Usage: /digbot help + # AutoDrop bot.autoDrop.cmd=AutoDrop ChatBot command bot.autoDrop.alias=AutoDrop ChatBot command alias @@ -786,6 +798,18 @@ config.ChatBot.AutoCraft.CraftingTable=Location of the crafting table if you int config.ChatBot.AutoCraft.OnFailure=What to do on crafting failure, "abort" or "wait". config.ChatBot.AutoCraft.Recipes=Recipes.Name: The name can be whatever you like and it is used to represent the recipe.\n# Recipes.Type: crafting table type: "player" or "table"\n# Recipes.Result: the resulting item\n# Recipes.Slots: All slots, counting from left to right, top to bottom. Please fill in "Null" for empty slots.\n# For the naming of the items, please see:\n# https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs +# AutoDig +config.ChatBot.AutoDig=Auto-digging blocks.\n# You can use "/digbot start" and "/digbot stop" to control the start and stop of AutoDig.\n# Since MCC does not yet support accurate calculation of the collision volume of blocks, all blocks are considered as complete cubes when obtaining the position of the lookahead.\n# For the naming of the block, please see https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/Material.cs +config.ChatBot.AutoDig.Auto_Tool_Switch=Automatically switch to the appropriate tool. +config.ChatBot.AutoDig.Durability_Limit=Will not use tools with less durability than this. Set to zero to disable this feature. +config.ChatBot.AutoDig.Drop_Low_Durability_Tools=Whether to drop the current tool when its durability is too low. +config.ChatBot.AutoDig.Mode="lookat", "fixedpos" or "both". Digging the block being looked at, the block in a fixed position, or the block that needs to be all met. +config.ChatBot.AutoDig.Locations=The position of the blocks when using "fixedpos" or "both" mode. +config.ChatBot.AutoDig.Auto_Start_Delay=How many seconds to wait after entering the game to start digging automatically, set to -1 to disable automatic start. +config.ChatBot.AutoDig.Dig_Timeout=Mining a block for more than "Dig_Timeout" seconds will be considered a timeout. +config.ChatBot.AutoDig.Log_Block_Dig=Whether to output logs when digging blocks. +config.ChatBot.AutoDig.List_Type=Wether to treat the blocks list as a "whitelist" or as a "blacklist". + # ChatBot.AutoDrop config.ChatBot.AutoDrop=Automatically drop items in inventory\n# You need to enable Inventory Handling to use this bot\n# See this file for an up-to-date list of item types you can use with this bot:\n# https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs config.ChatBot.AutoDrop.Mode="include", "exclude" or "everything". Include: drop item IN the list. Exclude: drop item NOT IN the list diff --git a/MinecraftClient/Resources/lang/zh-Hans.ini b/MinecraftClient/Resources/lang/zh-Hans.ini index 848c73e9..300fc366 100644 --- a/MinecraftClient/Resources/lang/zh-Hans.ini +++ b/MinecraftClient/Resources/lang/zh-Hans.ini @@ -478,7 +478,7 @@ bot.autoCraft.help.reload=重新加载配置文件。 bot.autoCraft.help.resetcfg=将默认示例配置写入默认位置。 bot.autoCraft.help.start=开始制作。用法:/autocraft start <配方名称> bot.autoCraft.help.stop=停止当前正在进行的制作过程 -bot.autoCraft.help.help=获取命令描述。用法: /autocraft help <命令名> +bot.autoCraft.help.help=获取命令描述。用法:/autocraft help <命令名> bot.autoCraft.loaded=已成功加载 bot.autoCraft.start=AutoCraft启动中:{0} bot.autoCraft.start_fail=无法启动AutoCraft。请检查用于制作{0}的可用材料 @@ -495,6 +495,18 @@ bot.autoCraft.debug.no_config=找不到配置。请新建一个。 bot.autocraft.invaild_slots=配方的物品数量不匹配,已自动调整。 bot.autocraft.invaild_invaild_result=无效的输出物品! +# AutoDig +bot.autodig.start_delay=将在 {0:0.0} 秒后开始自动挖掘。 +bot.autodig.dig_timeout=挖掘方块超时,重试。 +bot.autodig.not_allow=当前所看向的方块不在允许挖掘列表中。 +bot.autodig.cmd=自动挖掘 ChatBot 命令 +bot.autodig.available_cmd=可用命令:{0}。可使用 /digbot help <命令名> 了解更多信息。 +bot.autodig.start=开始自动挖掘。 +bot.autodig.stop=停止自动挖掘。 +bot.autodig.help.start=开始运行自动挖掘。 +bot.autodig.help.stop=停止运行自动挖掘。 +bot.autodig.help.help=获取命令描述。用法:/digbot help <命令名> + # AutoDrop bot.autoDrop.cmd=AutoDrop ChatBot命令 bot.autoDrop.alias=AutoDrop ChatBot命令别名 @@ -786,6 +798,18 @@ config.ChatBot.AutoCraft.CraftingTable=如果你打算使用工作台,请填 config.ChatBot.AutoCraft.OnFailure=合成失败时应该怎么处理,"abort"(中止)或 "wait"(等待)。 config.ChatBot.AutoCraft.Recipes=Recipes.Name:给该配方起一个独一无二的名字。(不能包含空白字符)Recipes.Type:合成类型,"player"(背包2x2)或 "table"(工作台3x3)\n# Recipes.Result:合成的目标物品\n# Recipes.Slots:合成的物品摆放方式,以从左到右、从上到下的格式填写。需留空请填写"Null"。\n# 最新的物品命名请看:https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs +# AutoDig +config.ChatBot.AutoDig=自动挖掘方块。\n# 你可以使用 "/digbot start" 和 "/digbot stop" 指令来控制 AutoDig 的启停。\n# 由于MCC目前还不支持精确计算方块的碰撞体积,在获取看向的方块时,视线上所有的方块都被看作是完整的立方体。\n# 查询方块的名字,请访问 https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/Material.cs +config.ChatBot.AutoDig.Auto_Tool_Switch=自动切换到合适的工具。 +config.ChatBot.AutoDig.Durability_Limit=不会使用低于此耐久度的工具。(需要启用库存处理) +config.ChatBot.AutoDig.Drop_Low_Durability_Tools=在当前使用的工具耐久度过低后,是否丢掉它。 +config.ChatBot.AutoDig.Mode="lookat","fixedpos" 或 "both"。挖掘看向的方块还是固定位置的方块,或者是两个条件都满足的方块。 +config.ChatBot.AutoDig.Locations=使用 "fixedpos" 或 "both" 模式时,方块的坐标。 +config.ChatBot.AutoDig.Auto_Start_Delay=进入游戏后等待多少秒后开始自动挖掘,设置为-1禁用自动开始。 +config.ChatBot.AutoDig.Dig_Timeout=若挖掘一个方块用时超过这个值,将会重新获取目标进行挖掘。 +config.ChatBot.AutoDig.Log_Block_Dig=是否输出挖掘方块的相关信息。 +config.ChatBot.AutoDig.List_Type=将方块列表作为 "whitelist"(白名单)还是 "blacklist"(黑名单)。 + # ChatBot.AutoDrop config.ChatBot.AutoDrop=自动从背包/库存中丢弃指定的物品\n# 你需要启用库存处理来使用这个功能。\n# 可用物品请看 https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs config.ChatBot.AutoDrop.Mode="include"(丢弃列表中的物品),"exclude"(丢弃列表外的所有物品) 或 "everything"(丢弃所有物品) diff --git a/MinecraftClient/Resources/lang/zh-Hant.ini b/MinecraftClient/Resources/lang/zh-Hant.ini index f4d745ef..345e3d13 100644 --- a/MinecraftClient/Resources/lang/zh-Hant.ini +++ b/MinecraftClient/Resources/lang/zh-Hant.ini @@ -495,6 +495,18 @@ bot.autoCraft.debug.no_config=找不到配置。請新建一個。 bot.autocraft.invaild_slots=配方的物品數量不匹配,已自動調整。 bot.autocraft.invaild_invaild_result=無效的輸出物品! +# AutoDig +bot.autodig.start_delay=將在 {0:0.0} 秒後開始自動挖掘。 +bot.autodig.dig_timeout=挖掘方塊超時,重試。 +bot.autodig.not_allow=當前所看向的方塊不在允許挖掘列表中。 +bot.autodig.cmd=自動挖掘 ChatBot 命令 +bot.autodig.available_cmd=可用命令:{0}。可使用 /digbot help <命令名> 瞭解更多資訊。 +bot.autodig.start=開始自動挖掘。 +bot.autodig.stop=停止自動挖掘。 +bot.autodig.help.start=開始執行自動挖掘。 +bot.autodig.help.stop=停止執行自動挖掘。 +bot.autodig.help.help=獲取命令描述。用法:/digbot help <命令名> + # AutoDrop bot.autoDrop.cmd=AutoDrop ChatBot命令 bot.autoDrop.alias=AutoDrop ChatBot命令別名 @@ -786,6 +798,18 @@ config.ChatBot.AutoCraft.CraftingTable=如果你打算使用工作臺,請填 config.ChatBot.AutoCraft.OnFailure=合成失敗時應該怎麼處理,"abort"(中止)或 "wait"(等待)。 config.ChatBot.AutoCraft.Recipes=Recipes.Name:給該配方起一個獨一無二的名字。(不能包含空白字元)Recipes.Type:合成型別,"player"(揹包2x2)或 "table"(工作臺3x3)\n# Recipes.Result:合成的目標物品\n# Recipes.Slots:合成的物品擺放方式,以從左到右、從上到下的格式填寫。需留空請填寫"Null"。\n# 最新的物品命名請看:https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs +# AutoDig +config.ChatBot.AutoDig=自動挖掘方塊。\n# 你可以使用 "/digbot start" 和 "/digbot stop" 指令來控制 AutoDig 的啟停。\n# 由於MCC目前還不支援精確計算方塊的碰撞體積,在獲取看向的方塊時,視線上所有的方塊都被看作是完整的立方體。\n# 查詢方塊的名字,請訪問 https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/Material.cs +config.ChatBot.AutoDig.Auto_Tool_Switch=自動切換到合適的工具。 +config.ChatBot.AutoDig.Durability_Limit=不會使用低於此耐久度的工具。(需要啟用庫存處理) +config.ChatBot.AutoDig.Drop_Low_Durability_Tools=在當前使用的工具耐久度過低後,是否丟掉它。 +config.ChatBot.AutoDig.Mode="lookat","fixedpos" 或 "both"。挖掘看向的方塊還是固定位置的方塊,或者是兩個條件都滿足的方塊。 +config.ChatBot.AutoDig.Locations=使用 "fixedpos" 或 "both" 模式時,方塊的座標。 +config.ChatBot.AutoDig.Auto_Start_Delay=進入遊戲後等待多少秒後開始自動挖掘,設定為-1禁用自動開始。 +config.ChatBot.AutoDig.Dig_Timeout=若挖掘一個方塊用時超過這個值,將會重新獲取目標進行挖掘。 +config.ChatBot.AutoDig.Log_Block_Dig=是否輸出挖掘方塊的相關資訊。 +config.ChatBot.AutoDig.List_Type=將方塊列表作為 "whitelist"(白名單)還是 "blacklist"(黑名單)。 + # ChatBot.AutoDrop config.ChatBot.AutoDrop=自動從揹包/庫存中丟棄指定的物品\n# 你需要啟用庫存處理來使用這個功能。\n# 可用物品請看 https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs config.ChatBot.AutoDrop.Mode="include"(丟棄列表中的物品),"exclude"(丟棄列表外的所有物品) 或 "everything"(丟棄所有物品) diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs index d996af1a..7730d274 100644 --- a/MinecraftClient/Scripting/ChatBot.cs +++ b/MinecraftClient/Scripting/ChatBot.cs @@ -426,6 +426,14 @@ namespace MinecraftClient /// public virtual void OnThunderLevelChange(float level) { } + + /// + /// Called when a block is changed. + /// + /// The location of the block. + /// The block + public virtual void OnBlockChange(Location location, Block block) { } + /* =================================================================== */ /* ToolBox - Methods below might be useful while creating your bot. */ /* You should not need to interact with other classes of the program. */ diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index d90ba8b8..45093a0d 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -986,6 +986,13 @@ namespace MinecraftClient set { ChatBots.AutoCraft.Config = value; ChatBots.AutoCraft.Config.OnSettingUpdate(); } } + [TomlPrecedingComment("$config.ChatBot.AutoDig$")] + public ChatBots.AutoDig.Configs AutoDig + { + get { return ChatBots.AutoDig.Config; } + set { ChatBots.AutoDig.Config = value; ChatBots.AutoDig.Config.OnSettingUpdate(); } + } + [TomlPrecedingComment("$config.ChatBot.AutoDrop$")] public ChatBots.AutoDrop.Configs AutoDrop { @@ -1170,6 +1177,12 @@ namespace MinecraftClient else return false; } + + public static int DoubleToTick(double time) + { + time = Math.Min(int.MaxValue / 10, time); + return (int)Math.Round(time * 10); + } } public static class InternalCmdCharTypeExtensions diff --git a/MinecraftClient/WinAPI/ConsoleIcon.cs b/MinecraftClient/WinAPI/ConsoleIcon.cs index e2a6bfc0..e66eca89 100644 --- a/MinecraftClient/WinAPI/ConsoleIcon.cs +++ b/MinecraftClient/WinAPI/ConsoleIcon.cs @@ -69,11 +69,14 @@ namespace MinecraftClient.WinAPI } catch (AggregateException ae) { + bool needRevert = false; foreach (var ex in ae.InnerExceptions) { - if (ex is HttpRequestException) //Skin not found? Reset to default icon - RevertToMCCIcon(); + if (ex is HttpRequestException || ex is TaskCanceledException) //Skin not found? Reset to default icon + needRevert = true; } + if (needRevert) + RevertToMCCIcon(); } catch (HttpRequestException) //Skin not found? Reset to default icon {