From 95378dca8eafb63060e1faee1dff76f258a0de41 Mon Sep 17 00:00:00 2001 From: Daenges <57369924+Daenges@users.noreply.github.com> Date: Tue, 4 Jan 2022 15:34:09 +0100 Subject: [PATCH] Automatic sugar cane farming (#1882) * Automatic sugar cane farming Inspired by #1871 I wrote a script to farm a field of sugar cane fully autonomous. * Implement suggestions * Remove unused comment * Remove comment and improve return value --- .../config/ChatBots/SugarCaneFarmer.cs | 236 ++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 MinecraftClient/config/ChatBots/SugarCaneFarmer.cs diff --git a/MinecraftClient/config/ChatBots/SugarCaneFarmer.cs b/MinecraftClient/config/ChatBots/SugarCaneFarmer.cs new file mode 100644 index 00000000..6ccda760 --- /dev/null +++ b/MinecraftClient/config/ChatBots/SugarCaneFarmer.cs @@ -0,0 +1,236 @@ +//MCCScript 1.0 + +MCC.LoadBot(new SugarCaneFarmer()); + +//MCCScript Extensions + +class SugarCaneFarmerBase : ChatBot +{ + /// + /// MoveToLocation() + waiting until the bot is near the location. + /// + /// Location to walk to + /// Distance of blocks from the goal that is accepted as "arrived" + /// True if walking was successful + public bool WaitForMoveToLocation(Location pos, float tolerance = 2f) + { + if (MoveToLocation(new Location(pos.X, pos.Y, pos.Z))) + { + while (GetCurrentLocation().Distance(pos) > tolerance) + { + Thread.Sleep(200); + } + return true; + } + else + { + return false; + } + } + + /// + /// DigBlock + waiting until the block is broken + /// + /// Location of the block that should be broken + /// Switch to the correct tool, if it is in hotbar + /// Minimum time until the client gives up + /// True if mining was successful + public bool WaitForDigBlock(Location block, bool useCorrectTool = false, int digTimeout = 1000) + { + if (Material2Tool.IsUnbreakable(GetWorld().GetBlock(block).Type)) + return false; + + if (useCorrectTool && GetInventoryEnabled()) + { + // Search this tool in hotbar and select the correct slot + SelectCorrectSlotInHotbar( + // Returns the correct tool for this type + Material2Tool.GetCorrectToolForBlock( + // returns the type of the current block + GetWorld().GetBlock(block).Type)); + } + + // Unable to check when breaking is over. + if (DigBlock(block)) + { + short i = 0; // Maximum wait time of 10 sec. + while (GetWorld().GetBlock(block).Type != Material.Air && i <= digTimeout) + { + Thread.Sleep(100); + i++; + } + return i <= digTimeout; + } + else + { + return false; + } + } + + /// + /// Selects the corect tool in the hotbar + /// + /// List of tools that can be applied to mine a block + public void SelectCorrectSlotInHotbar(ItemType[] tools) + { + if (GetInventoryEnabled()) + { + foreach (ItemType tool in tools) + { + int[] tempArray = GetPlayerInventory().SearchItem(tool); + // Check whether an item could be found and make sure that it is in + // a hotbar slot (36-44). + if (tempArray.Length > 0 && tempArray[0] > 35) + { + // Changeslot takes numbers from 0-8 + ChangeSlot(Convert.ToInt16(tempArray[0] - 36)); + break; + } + } + } + else + { + LogToConsole("Activate Inventory Handling."); + } + } + + /// + /// Returns the head position from the current location + /// + public static Func GetHeadLocation = locFeet => new Location(locFeet.X, locFeet.Y + 1, locFeet.Z); +} + +class SugarCaneFarmer : SugarCaneFarmerBase +{ + public enum CoordinateType { X, Y, Z }; + + /// + /// Used to stop the farming process on demand. + /// + private bool farming = true; + + /// + /// Returns all sugar canes that are above a solid block and another sugar cane + /// + /// Radius of the search + /// Order of the returned sugar canes + /// list of sugar canes, sorted after given parameter + private List collectSugarCaneBlocks(int radius, CoordinateType coordinateOrder = CoordinateType.X) + { + IEnumerable breakingCoordinates = GetWorld().FindBlock( + new Location(Math.Floor(GetCurrentLocation().X), Math.Floor(GetCurrentLocation().Y), Math.Floor(GetCurrentLocation().Z)), + Material.SugarCane, radius).Where(block => + GetWorld().GetBlock(new Location(block.X, block.Y - 1, block.Z)).Type == Material.SugarCane && + GetWorld().GetBlock(new Location(block.X, block.Y - 2, block.Z)).Type.IsSolid()); + + switch (coordinateOrder) + { + case CoordinateType.Z: + return breakingCoordinates.OrderBy(coordinate => coordinate.Z).ToList(); + case CoordinateType.Y: + return breakingCoordinates.OrderBy(coordinate => coordinate.Y).ToList(); + default: + return breakingCoordinates.OrderBy(coordinate => coordinate.X).ToList(); + } + } + + public override void Initialize() + { + LogToConsole("Sugar Cane farming bot created by Daenges."); + RegisterChatBotCommand("sugarcane", "Farm sugar cane automatically", "/sugarcane [range x|y|z]/[stop]", commandHandler); + } + + /// + /// Start the farming process + /// + /// Range of farming + /// In which order the plants should be farmed + private void startBot(int range, CoordinateType sortOrder) + { + List sugarCanePositions; + + // create a loop so the bot keeps farming if nothing else is stated + // uses the same range as initially given + do + { + sugarCanePositions = collectSugarCaneBlocks(range, sortOrder); + + LogToConsole(string.Format("Found: {0} Sugar Cane", sugarCanePositions.Count)); + + if (sugarCanePositions.Count > 0) + { + foreach (Location loc in sugarCanePositions) + { + if (!farming) + break; + + if (GetHeadLocation(GetCurrentLocation()).Distance(loc) > 5) + { + if (!WaitForMoveToLocation(new Location(loc.X, loc.Y - 1, loc.Z))) + { + LogToConsole("Moving failed."); + continue; + } + } + + if (!WaitForDigBlock(new Location(loc.X, loc.Y, loc.Z))) + LogDebugToConsole("Unable to break this block: " + loc.ToString()); + } + } + else { LogToConsole("Nothing found. Stop farming..."); } + + if (farming && sugarCanePositions.Count > 0) + LogToConsole("Finished farming. Searching for further work... Use '/sugarcane stop' to stop farming."); + + } while (farming && sugarCanePositions.Count > 0); + + farming = true; + LogToConsole("[FARMING STOPPED]"); + } + + private string commandHandler(string command, string[] args) + { + if (args.Length == 1) + { + if (args[0] == "stop") + { + farming = false; + return "Stop farming..."; + } + else + { + int range; + + try { range = Convert.ToInt32(args[0]); } + catch (Exception) { return "Range must be a number"; } + + Thread workThread = new Thread(() => startBot(range, CoordinateType.X)); + workThread.Start(); + + return "Start farming."; + } + } + else if (args.Length == 2) + { + int range; + + try { range = Convert.ToInt32(args[0]); } + catch (Exception) { return "Range must be a number"; } + + Thread workThread; + + if (args[1].ToLower() == "x") + workThread = new Thread(() => startBot(range, CoordinateType.X)); + else if (args[1].ToLower() == "y") + workThread = new Thread(() => startBot(range, CoordinateType.Y)); + else + workThread = new Thread(() => startBot(range, CoordinateType.Z)); + + workThread.Start(); + + return "Start farming."; + } + else + return "Usage: /sugarcane [range x|y|z]/[stop]"; + } +}