using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; using MinecraftClient.Mapping; using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots; public class ItemsCollector : ChatBot { public const string CommandName = "itemscollector"; public static Configs Config = new(); [TomlDoNotInlineObject] public class Configs { [NonSerialized] private const string BotName = "ItemsCollector"; public bool Enabled = false; [TomlInlineComment("$ChatBot.ItemsCollector.Collect_All_Item_Types$")] public bool Collect_All_Item_Types = true; [TomlInlineComment("$ChatBot.ItemsCollector.Items_Whitelist$")] public List Items_Whitelist = new() { ItemType.Diamond, ItemType.NetheriteIngot }; [TomlInlineComment("$ChatBot.ItemsCollector.Delay_Between_Tasks$")] public int Delay_Between_Tasks = 300; [TomlInlineComment("$ChatBot.ItemsCollector.Collection_Radius$")] public double Collection_Radius = 30.0; [TomlInlineComment("$ChatBot.ItemsCollector.Always_Return_To_Start$")] public bool Always_Return_To_Start = true; [TomlInlineComment("$ChatBot.ItemsCollector.Prioritize_Clusters$")] public bool Prioritize_Clusters = false; public void OnSettingUpdate() { if (Delay_Between_Tasks < 100) Delay_Between_Tasks = 100; } } private bool running = false; private Thread? mainProcessThread; public override void Initialize() { if (!GetEntityHandlingEnabled()) { LogToConsole(Translations.extra_entity_required); LogToConsole(Translations.general_bot_unload); UnloadBot(); return; } if (!GetTerrainEnabled()) { LogToConsole(Translations.extra_terrainandmovement_required); LogToConsole(Translations.general_bot_unload); UnloadBot(); return; } McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty))) ); McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("start") .Executes(r => OnCommandStart(r.Source))) .Then(l => l.Literal("stop") .Executes(r => OnCommandStop(r.Source))) .Then(l => l.Literal("_help") .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } public override void OnUnload() { McClient.dispatcher.Unregister(CommandName); McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } private int OnCommandHelp(CmdResult r, string? cmd) { return r.SetAndReturn(cmd switch { #pragma warning disable format // @formatter:off _ => Translations.cmd_items_collector_desc + ": " + Translations.cmd_items_collector_usage + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); } private int OnCommandStart(CmdResult r) { if (running) return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_items_collector_already_collecting); StartTheMainProcess(); return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_items_collector_started); } private int OnCommandStop(CmdResult r) { if (!running) return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_items_collector_already_not_collecting); StopTheMainProcess(); return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_items_collector_stopping); } private void StartTheMainProcess() { running = true; mainProcessThread = new Thread(MainProcess); mainProcessThread.Start(); } private void StopTheMainProcess() { running = false; } private void MainProcess() { var startingLocation = GetCurrentLocation(); while (running) { var currentLocation = GetCurrentLocation(); var items = GetEntities() .Where(x => x.Value.Type == EntityType.Item && x.Value.Location.Distance(currentLocation) <= Config.Collection_Radius && (Config.Collect_All_Item_Types || Config.Items_Whitelist.Contains(x.Value.Item.Type))) .Select(x => x.Value) .ToList(); if (Config.Prioritize_Clusters && items.Count > 1) { var centroid = new Location( items.Average(x => x.Location.X), items.Average(x => x.Location.Y), items.Average(x => x.Location.Z) ); items = items.OrderBy(x => x.Location.Distance(centroid) / items.Count).ToList(); } else items = items.OrderBy(x => x.Location.Distance(currentLocation)).ToList(); if (items.Any()) { foreach (var entity in items) { if (!running) break; WaitForMoveToLocation(entity.Location); } } else { if (startingLocation.Distance(currentLocation) > 2f && Config.Always_Return_To_Start) WaitForMoveToLocation(startingLocation); } if (!running) break; Thread.Sleep(Config.Delay_Between_Tasks); } LogToConsole(Translations.cmd_items_collector_stopped); } public override void AfterGameJoined() { StopTheMainProcess(); } public override bool OnDisconnect(DisconnectReason reason, string message) { StopTheMainProcess(); return true; } private bool WaitForMoveToLocation(Location location, float tolerance = 1f) { if (!MoveToLocation(location)) return false; while (GetCurrentLocation().Distance(location) > tolerance) Thread.Sleep(200); return true; } }