Added a Farmer Bot

Added a Farmer Bot
This commit is contained in:
Anon 2022-10-14 16:02:17 +00:00 committed by GitHub
commit 5495f71db7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 877 additions and 23 deletions

View file

@ -0,0 +1,826 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using MinecraftClient.Inventory;
using MinecraftClient.Mapping;
using MinecraftClient.Protocol.Handlers;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
enum State
{
SearchingForCropsToBreak = 0,
SearchingForFarmlandToPlant,
PlantingCrops,
BonemealingCrops
}
enum CropType
{
Beetroot,
Carrot,
Melon,
Netherwart,
Pumpkin,
Potato,
Wheat
}
public class Farmer : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "Farmer";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.Farmer.Delay_Between_Tasks$")]
public int Delay_Between_Tasks = 1;
public void OnSettingUpdate()
{
if (Delay_Between_Tasks <= 0)
Delay_Between_Tasks = 1;
}
}
private State state = State.SearchingForCropsToBreak;
private CropType cropType = CropType.Wheat;
private int farmingRadius = 30;
private bool running = false;
private bool allowUnsafe = false;
private bool allowTeleport = false;
private bool debugEnabled = false;
private const string commandDescription = "farmer <start <crop type> [radius:<radius = 30>|unsafe:<true/false>|teleport:<true/false>|debug:<true/false>]|stop>";
public override void Initialize()
{
if (GetProtocolVersion() < Protocol18Handler.MC_1_13_Version)
{
LogToConsole(Translations.TryGet("bot.farmer.not_implemented"));
return;
}
if (!GetTerrainEnabled())
{
LogToConsole("Terrain handling needed!");
return;
}
if (!GetInventoryEnabled())
{
LogToConsole("Inventory handling needed!");
return;
}
RegisterChatBotCommand("farmer", "bot.farmer.desc", commandDescription, OnFarmCommand);
}
private string OnFarmCommand(string cmd, string[] args)
{
if (args.Length > 0)
{
if (args[0].Equals("stop", StringComparison.OrdinalIgnoreCase))
{
if (!running)
return Translations.TryGet("bot.farmer.already_stopped");
running = false;
return Translations.TryGet("bot.farmer.stopping");
}
if (args[0].Equals("start", StringComparison.OrdinalIgnoreCase))
{
if (args.Length >= 2)
{
if (running)
return Translations.TryGet("bot.farmer.already_running");
if (!Enum.TryParse(args[1], true, out CropType whatToFarm))
return Translations.TryGet("bot.farmer.invalid_crop_type");
int radius = 30;
state = State.SearchingForFarmlandToPlant;
cropType = whatToFarm;
allowUnsafe = false;
allowTeleport = false;
debugEnabled = false;
if (args.Length >= 3)
{
for (int i = 2; i < args.Length; i++)
{
string currentArg = args[i].Trim().ToLower();
if (!currentArg.Contains(':'))
{
LogToConsole("§x§1§0" + Translations.TryGet("bot.farmer.warining_invalid_parameter", currentArg));
continue;
}
string[] parts = currentArg.Split(":", StringSplitOptions.TrimEntries);
if (parts.Length != 2)
{
LogToConsole("§x§1§0" + Translations.TryGet("bot.farmer.warining_invalid_parameter", currentArg));
continue;
}
switch (parts[0])
{
case "r":
case "radius":
if (!int.TryParse(parts[1], NumberStyles.Any, CultureInfo.CurrentCulture, out radius))
LogToConsole("§x§1§0" + Translations.TryGet("bot.farmer.invalid_radius"));
if (radius <= 0)
{
LogToConsole("§x§1§0" + Translations.TryGet("bot.farmer.invalid_radius"));
radius = 30;
}
break;
case "f":
case "unsafe":
if (allowUnsafe)
break;
if (parts[1].Equals("true") || parts[1].Equals("1"))
{
LogToConsole("§x§1§0" + Translations.TryGet("bot.farmer.warining_force_unsafe"));
allowUnsafe = true;
}
else allowUnsafe = false;
break;
case "t":
case "teleport":
if (allowTeleport)
break;
if (parts[1].Equals("true") || parts[1].Equals("1"))
{
LogToConsole("§w§1§f" + Translations.TryGet("bot.farmer.warining_allow_teleport"));
allowTeleport = true;
}
else allowTeleport = false;
break;
case "d":
case "debug":
if (debugEnabled)
break;
if (parts[1].Equals("true") || parts[1].Equals("1"))
{
LogToConsole("Debug enabled!");
debugEnabled = true;
}
else debugEnabled = false;
break;
}
}
}
farmingRadius = radius;
running = true;
new Thread(() => MainPorcess()).Start();
return "";
}
}
}
return Translations.TryGet("bot.farmer.desc") + ": " + commandDescription;
}
public override void AfterGameJoined()
{
running = false;
}
public override bool OnDisconnect(DisconnectReason reason, string message)
{
running = false;
return true;
}
private void MainPorcess()
{
LogToConsole("§y§1§f" + Translations.TryGet("bot.farmer.started"));
LogToConsole("§y§1§f " + Translations.TryGet("bot.farmer.crop_type") + ": " + cropType);
LogToConsole("§y§1§f " + Translations.TryGet("bot.farmer.radius") + ": " + farmingRadius);
while (running)
{
// Don't do anything if the bot is currently eating, we wait for 1 second
if (AutoEat.Eating)
{
LogDebug("Eating...");
Thread.Sleep(Config.Delay_Between_Tasks * 1000);
continue;
}
switch (state)
{
case State.SearchingForFarmlandToPlant:
LogDebug("Looking for farmland...");
ItemType cropTypeToPlant = GetSeedItemTypeForCropType(cropType);
// If we don't have any seeds on our hotbar, skip this step and try collecting some
if (!SwitchToItem(cropTypeToPlant))
{
LogDebug("No seeds, trying to find some crops to break");
state = State.SearchingForCropsToBreak;
Thread.Sleep(Config.Delay_Between_Tasks * 1000);
continue;
}
List<Location> farmlandToPlantOn = findEmptyFarmland(farmingRadius);
if (farmlandToPlantOn.Count == 0)
{
LogDebug("Could not find any farmland, trying to find some crops to break");
state = State.SearchingForCropsToBreak;
Thread.Sleep(Config.Delay_Between_Tasks * 1000);
continue;
}
int i = 0;
foreach (Location location in farmlandToPlantOn)
{
if (!running) break;
// Check only every second iteration, minor optimization xD
if (i % 2 == 0)
{
if (!HasItemOfTypeInInventory(cropTypeToPlant))
{
LogDebug("Ran out of seeds, looking for crops to break...");
state = State.SearchingForCropsToBreak;
Thread.Sleep(Config.Delay_Between_Tasks * 1000);
continue;
}
}
double yValue = Math.Floor(location.Y) + 1;
// TODO: Figure out why this is not working.
// Why we need this: sometimes the server kicks the player for "invalid movement" packets.
/*if (cropType == CropType.Netherwart)
yValue = (double)(Math.Floor(location.Y) - 1.0) + (double)0.87500;*/
Location location2 = new Location(Math.Floor(location.X) + 0.5, yValue, Math.Floor(location.Z) + 0.5);
if (WaitForMoveToLocation(location2))
{
LogDebug("Moving to: " + location2);
// Stop if we do not have any more seeds left
if (!SwitchToItem(GetSeedItemTypeForCropType(cropType)))
{
LogDebug("No seeds, trying to find some crops to break");
break;
}
Location loc = new Location(Math.Floor(location.X), Math.Floor(location2.Y), Math.Floor(location.Z));
LogDebug("Sending placeblock to: " + loc);
SendPlaceBlock(loc, Direction.Up);
Thread.Sleep(300);
}
else LogDebug("Can't move to: " + location2);
i++;
}
LogDebug("Finished planting crops!");
state = State.SearchingForCropsToBreak;
break;
case State.SearchingForCropsToBreak:
LogDebug("Searching for crops to break...");
List<Location> cropsToCollect = findCrops(farmingRadius, cropType, true);
if (cropsToCollect.Count == 0)
{
LogToConsole("No crops to break, trying to bonemeal ungrown ones");
state = State.BonemealingCrops;
Thread.Sleep(Config.Delay_Between_Tasks * 1000);
continue;
}
// Switch to an axe for faster breaking if the bot has one in his inventory
if (cropType == CropType.Melon || cropType == CropType.Pumpkin)
{
// Start from Diamond axe, if not found, try a tier lower axe
bool switched = SwitchToItem(ItemType.DiamondAxe);
if (!switched)
switched = SwitchToItem(ItemType.IronAxe);
if (!switched)
switched = SwitchToItem(ItemType.GoldenAxe);
if (!switched)
SwitchToItem(ItemType.StoneAxe);
}
foreach (Location location in cropsToCollect)
{
if (!running) break;
// God damn C# rounding it to 0.94
// This will be needed when bot bonemeals carrots or potatoes which are at the first stage of growth,
// because sometimes the bot walks over crops and breaks them
// TODO: Figure out a fix
// new Location(Math.Floor(location.X) + 0.5, (double)((location.Y - 1) + (double)0.93750), Math.Floor(location.Z) + 0.5)
if (WaitForMoveToLocation(location))
WaitForDigBlock(location);
// Allow some time to pickup the item
Thread.Sleep(cropType == CropType.Melon || cropType == CropType.Pumpkin ? 400 : 200);
}
LogDebug("Finished breaking crops!");
state = State.BonemealingCrops;
break;
case State.BonemealingCrops:
// Can't be bonemealed
if (cropType == CropType.Netherwart)
{
state = State.SearchingForFarmlandToPlant;
Thread.Sleep(Config.Delay_Between_Tasks * 1000);
continue;
}
// If we don't have any bonemeal on our hotbar, skip this step
if (!SwitchToItem(ItemType.BoneMeal))
{
LogDebug("No bonemeal, searching for some farmland to plant seeds on");
state = State.SearchingForFarmlandToPlant;
Thread.Sleep(Config.Delay_Between_Tasks * 1000);
continue;
}
List<Location> cropsToBonemeal = findCrops(farmingRadius, cropType, false);
if (cropsToBonemeal.Count == 0)
{
LogDebug("No crops to bonemeal, searching for farmland to plant seeds on");
state = State.SearchingForFarmlandToPlant;
Thread.Sleep(Config.Delay_Between_Tasks * 1000);
continue;
}
int i2 = 0;
foreach (Location location in cropsToBonemeal)
{
if (!running) break;
// Check only every second iteration, minor optimization xD
if (i2 % 2 == 0)
{
if (!HasItemOfTypeInInventory(ItemType.BoneMeal))
{
LogDebug("Ran out of Bone Meal, looking for farmland to plant on...");
state = State.SearchingForFarmlandToPlant;
Thread.Sleep(Config.Delay_Between_Tasks * 1000);
continue;
}
}
if (WaitForMoveToLocation(location))
{
// Stop if we do not have any more bonemeal left
if (!SwitchToItem(ItemType.BoneMeal))
{
LogDebug("No bonemeal, searching for some farmland to plant seeds on...");
break;
}
Location location2 = new Location(Math.Floor(location.X) + 0.5, location.Y, Math.Floor(location.Z) + 0.5);
LogDebug("Trying to bonemeal: " + location2);
// Send like 4 bonemeal attempts, it should do the job with 2-3, but sometimes doesn't do
for (int boneMealTimes = 0; boneMealTimes < 4; boneMealTimes++)
{
// TODO: Do a check if the carrot/potato is on the first growth stage
// if so, use: new Location(location.X, (double)(location.Y - 1) + (double)0.93750, location.Z)
SendPlaceBlock(location2, Direction.Down);
}
Thread.Sleep(100);
}
i2++;
}
LogDebug("Finished bonemealing crops!");
state = State.SearchingForFarmlandToPlant;
break;
}
LogDebug("Waiting for " + Config.Delay_Between_Tasks + " seconds for next cycle.");
Thread.Sleep(Config.Delay_Between_Tasks * 1000);
}
LogToConsole(Translations.TryGet("bot.farmer.stopped"));
}
private Material GetMaterialForCropType(CropType type)
{
switch (type)
{
case CropType.Beetroot:
return Material.Beetroots;
case CropType.Carrot:
return Material.Carrots;
case CropType.Melon:
return Material.Melon;
case CropType.Netherwart:
return Material.NetherWart;
case CropType.Pumpkin:
return Material.Pumpkin;
case CropType.Potato:
return Material.Potatoes;
case CropType.Wheat:
return Material.Wheat;
}
throw new Exception("Material type for " + type.GetType().Name + " has not been mapped!");
}
private ItemType GetSeedItemTypeForCropType(CropType type)
{
switch (type)
{
case CropType.Beetroot:
return ItemType.BeetrootSeeds;
case CropType.Carrot:
return ItemType.Carrot;
case CropType.Melon:
return ItemType.MelonSeeds;
case CropType.Netherwart:
return ItemType.NetherWart;
case CropType.Pumpkin:
return ItemType.PumpkinSeeds;
case CropType.Potato:
return ItemType.Potato;
case CropType.Wheat:
return ItemType.WheatSeeds;
}
throw new Exception("Seed type for " + type.GetType().Name + " has not been mapped!");
}
private List<Location> findEmptyFarmland(int radius)
{
return GetWorld()
.FindBlock(GetCurrentLocation(), cropType == CropType.Netherwart ? Material.SoulSand : Material.Farmland, radius)
.Where(location => GetWorld().GetBlock(new Location(location.X, location.Y + 1, location.Z)).Type == Material.Air)
.ToList();
}
private List<Location> findCrops(int radius, CropType cropType, bool fullyGrown)
{
Material material = GetMaterialForCropType(cropType);
// A bit of a hack to enable bonemealing melon and pumpkin stems
if (!fullyGrown && (cropType == CropType.Melon || cropType == CropType.Pumpkin))
material = cropType == CropType.Melon ? Material.MelonStem : Material.PumpkinStem;
return GetWorld()
.FindBlock(GetCurrentLocation(), material, radius)
.Where(location =>
{
if (fullyGrown && (material == Material.Melon || material == Material.Pumpkin))
return true;
bool isFullyGrown = IsCropFullyGrown(GetWorld().GetBlock(location), cropType);
return fullyGrown ? isFullyGrown : !isFullyGrown;
})
.ToList();
}
private bool IsCropFullyGrown(Block block, CropType cropType)
{
int protocolVersion = GetProtocolVersion();
switch (cropType)
{
case CropType.Beetroot:
if (protocolVersion >= Protocol18Handler.MC_1_19_Version && protocolVersion <= Protocol18Handler.MC_1_19_2_Version)
{
if (block.BlockId == 10103)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_17_Version && protocolVersion <= Protocol18Handler.MC_1_18_2_Version)
{
if (block.BlockId == 9472)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_16_Version && protocolVersion <= Protocol18Handler.MC_1_16_5_Version)
{
if (block.BlockId == 9226)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_14_Version && protocolVersion <= Protocol18Handler.MC_1_15_2_Version)
{
if (block.BlockId == 8686)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_13_Version && protocolVersion < Protocol18Handler.MC_1_14_Version)
{
if (block.BlockId == 8162)
return true;
}
break;
case CropType.Carrot:
if (protocolVersion >= Protocol18Handler.MC_1_19_Version && protocolVersion <= Protocol18Handler.MC_1_19_2_Version)
{
if (block.BlockId == 6930)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_17_Version && protocolVersion <= Protocol18Handler.MC_1_18_2_Version)
{
if (block.BlockId == 6543)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_16_Version && protocolVersion <= Protocol18Handler.MC_1_16_5_Version)
{
if (block.BlockId == 6341)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_14_Version && protocolVersion <= Protocol18Handler.MC_1_15_2_Version)
{
if (block.BlockId == 5801)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_13_Version && protocolVersion < Protocol18Handler.MC_1_14_Version)
{
if (block.BlockId == 5295)
return true;
}
break;
// Checkin for stems and attached stems instead of Melons themselves
case CropType.Melon:
if (protocolVersion >= Protocol18Handler.MC_1_19_Version && protocolVersion <= Protocol18Handler.MC_1_19_2_Version)
{
if (block.BlockId == 5166 || block.BlockId == 5150)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_17_Version && protocolVersion <= Protocol18Handler.MC_1_18_2_Version)
{
if (block.BlockId == 4860 || block.BlockId == 4844)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_16_Version && protocolVersion <= Protocol18Handler.MC_1_16_5_Version)
{
if (block.BlockId == 4791 || block.BlockId == 4775)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_14_Version && protocolVersion <= Protocol18Handler.MC_1_15_2_Version)
{
if (block.BlockId == 4771 || block.BlockId == 4755)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_13_Version && protocolVersion < Protocol18Handler.MC_1_14_Version)
{
if (block.BlockId == 4268 || block.BlockId == 4252)
return true;
}
break;
// Checkin for stems and attached stems instead of Melons themselves
case CropType.Netherwart:
if (protocolVersion >= Protocol18Handler.MC_1_19_Version && protocolVersion <= Protocol18Handler.MC_1_19_2_Version)
{
if (block.BlockId == 5718)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_17_Version && protocolVersion <= Protocol18Handler.MC_1_18_2_Version)
{
if (block.BlockId == 5332)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_16_Version && protocolVersion <= Protocol18Handler.MC_1_16_5_Version)
{
if (block.BlockId == 5135)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_14_Version && protocolVersion <= Protocol18Handler.MC_1_15_2_Version)
{
if (block.BlockId == 5115)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_13_Version && protocolVersion < Protocol18Handler.MC_1_14_Version)
{
if (block.BlockId == 4612)
return true;
}
break;
// Checkin for stems and attached stems instead of Pumpkins themselves
case CropType.Pumpkin:
if (protocolVersion >= Protocol18Handler.MC_1_19_Version && protocolVersion <= Protocol18Handler.MC_1_19_2_Version)
{
if (block.BlockId == 5158 || block.BlockId == 5146)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_17_Version && protocolVersion <= Protocol18Handler.MC_1_18_2_Version)
{
if (block.BlockId == 4852 || block.BlockId == 4840)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_16_Version && protocolVersion <= Protocol18Handler.MC_1_16_5_Version)
{
if (block.BlockId == 4783 || block.BlockId == 4771)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_14_Version && protocolVersion <= Protocol18Handler.MC_1_15_2_Version)
{
if (block.BlockId == 4763 || block.BlockId == 4751)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_13_Version && protocolVersion < Protocol18Handler.MC_1_14_Version)
{
if (block.BlockId == 4260 || block.BlockId == 4248)
return true;
}
break;
case CropType.Potato:
if (protocolVersion >= Protocol18Handler.MC_1_19_Version && protocolVersion <= Protocol18Handler.MC_1_19_2_Version)
{
if (block.BlockId == 6938)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_17_Version && protocolVersion <= Protocol18Handler.MC_1_18_2_Version)
{
if (block.BlockId == 6551)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_16_Version && protocolVersion <= Protocol18Handler.MC_1_16_5_Version)
{
if (block.BlockId == 6349)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_14_Version && protocolVersion <= Protocol18Handler.MC_1_15_2_Version)
{
if (block.BlockId == 5809)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_13_Version && protocolVersion < Protocol18Handler.MC_1_14_Version)
{
if (block.BlockId == 5303)
return true;
}
break;
case CropType.Wheat:
if (protocolVersion >= Protocol18Handler.MC_1_19_Version && protocolVersion <= Protocol18Handler.MC_1_19_2_Version)
{
if (block.BlockId == 3619)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_17_Version && protocolVersion <= Protocol18Handler.MC_1_18_2_Version)
{
if (block.BlockId == 3421)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_16_Version && protocolVersion <= Protocol18Handler.MC_1_16_5_Version)
{
if (block.BlockId == 3364)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_14_Version && protocolVersion <= Protocol18Handler.MC_1_15_2_Version)
{
if (block.BlockId == 3362)
return true;
}
else if (protocolVersion >= Protocol18Handler.MC_1_13_Version && protocolVersion < Protocol18Handler.MC_1_14_Version)
{
if (block.BlockId == 3059)
return true;
}
break;
}
return false;
}
// Yoinked from ReinforceZwei's AutoTree and adapted to search the whole of inventory in additon to the hotbar
private bool SwitchToItem(ItemType itemType)
{
Container playerInventory = GetPlayerInventory();
if (playerInventory.Items.ContainsKey(GetCurrentSlot() - 36)
&& playerInventory.Items[GetCurrentSlot() - 36].Type == itemType)
return true; // Already selected
// Search the full inventory
List<int> fullInventorySearch = new List<int>(playerInventory.SearchItem(itemType));
// Search for the seed in the hotbar
List<int> hotbarSerch = fullInventorySearch.Where(slot => slot >= 36 && slot <= 44).ToList();
if (hotbarSerch.Count > 0)
{
ChangeSlot((short)(hotbarSerch[0] - 36));
return true;
}
if (fullInventorySearch.Count == 0)
return false;
ItemMovingHelper movingHelper = new ItemMovingHelper(playerInventory, Handler);
movingHelper.Swap(fullInventorySearch[0], 36);
ChangeSlot(0);
return true;
}
// Yoinked from Daenges's Sugarcane Farmer
private bool WaitForMoveToLocation(Location pos, float tolerance = 2f)
{
if (MoveToLocation(location: pos, allowUnsafe: allowUnsafe, allowDirectTeleport: allowTeleport))
{
LogDebug("Moving to: " + pos);
while (GetCurrentLocation().Distance(pos) > tolerance)
Thread.Sleep(200);
return true;
}
else LogDebug("Can't move to: " + pos);
return false;
}
// Yoinked from Daenges's Sugarcane Farmer
private bool WaitForDigBlock(Location block, int digTimeout = 1000)
{
if (DigBlock(block.ToFloor()))
{
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;
}
return false;
}
private bool HasItemOfTypeInInventory(ItemType itemType)
{
return GetPlayerInventory().SearchItem(itemType).Length > 0;
}
private void LogDebug(object text)
{
if (debugEnabled)
LogToConsole(text);
else LogDebugToConsole(text);
}
}
}

View file

@ -4,7 +4,7 @@ using System.Text;
namespace MinecraftClient.Commands
{
class BotsCommand : Command
class Bots : Command
{
public override string CmdName { get { return "bots"; } }
public override string CmdUsage { get { return "bots [list|unload <bot name|all>]"; } }

View file

@ -223,7 +223,7 @@ namespace MinecraftClient
return;
Retry:
Retry:
if (timeoutdetector != null)
{
timeoutdetector.Item2.Cancel();
@ -263,6 +263,7 @@ namespace MinecraftClient
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.Farmer.Enabled) { BotLoad(new Farmer()); }
if (Config.ChatBot.FollowPlayer.Enabled) { BotLoad(new FollowPlayer()); }
if (Config.ChatBot.HangmanGame.Enabled) { BotLoad(new HangmanGame()); }
if (Config.ChatBot.Mailer.Enabled) { BotLoad(new Mailer()); }

View file

@ -612,6 +612,22 @@ bot.replayCapture.created=Replay file created.
bot.replayCapture.stopped=Record stopped.
bot.replayCapture.restart=Record was stopped. Restart the program to start another record.
# Farmer
bot.farmer.desc=Farming bot
bot.farmer.not_implemented=Not implemented bellow 1.13!
bot.farmer.already_stopped=The bot has already stopped farming!
bot.farmer.stopping=Stoping farming, this might take a second...
bot.farmer.stopped=Stopped farming!
bot.farmer.already_running=The bot is already farming!
bot.farmer.invalid_crop_type=Invalid crop type provided (Types which you can use: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat)!
bot.farmer.warining_invalid_parameter=Invalid parameter "{0}" provided (Use format: "key:value")!
bot.farmer.invalid_radius=Invalid radius provided, you must provide a valid integer number greater than 0!
bot.farmer.warining_force_unsafe=You have enabled un-safe movement, the bot might get hurt!
bot.farmer.warining_allow_teleport=You have enabled teleporting, this might get your bot account kicked and in the worst case scenario banned! Use with caution!
bot.farmer.started=Started farming!
bot.farmer.crop_type=Crop type
bot.farmer.radius=Radius
# Follow player
cmd.follow.desc=Makes the bot follow a specified player
cmd.follow.usage=follow <player name|stop> [-f] (Use -f to enable un-safe walking)
@ -845,12 +861,16 @@ config.ChatBot.AutoRelog.Ignore_Kick_Message=When set to true, autorelog will re
config.ChatBot.AutoRelog.Kick_Messages=If the kickout message matches any of the strings, then autorelog will be triggered.
# ChatBot.AutoRespond
config.ChatBot.AutoRespond=Run commands or send messages automatically when a specified pattern is detected in chat\n# /!\ Server admins can spoof chat messages (/nick, /tellraw) so keep this in mind when implementing AutoRespond rules\n# /!\ This bot may get spammy depending on your rules, although the global messagecooldown setting can help you avoiding accidental spam
config.ChatBot.AutoRespond=Run commands or send messages automatically when a specified pattern is detected in chat\n# Server admins can spoof chat messages (/nick, /tellraw) so keep this in mind when implementing AutoRespond rules\n# /!\ This bot may get spammy depending on your rules, although the global messagecooldown setting can help you avoiding accidental spam
config.ChatBot.AutoRespond.Match_Colors=Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work)
# ChatBot.ChatLog
config.ChatBot.ChatLog=Logs chat messages in a file on disk.
# ChatBot.Farmer
config.ChatBot.Farmer=Automatically farms crops for you (plants, breaks and bonemeals them).\n# Crop types available: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat.\n# Usage: "/farmer start" command and "/farmer stop" command.\n# NOTE: This a newly added bot, it is not perfect and was only tested in 1.19.2, there are some minor issues like not being able to bonemeal carrots/potatoes sometimes.\n# or bot jumps onto the farm land and breaks it (this happens rarely but still happens). We are looking forward at improving this.\n# It is recommended to keep the farming area walled off and flat to avoid the bot jumping.\n# Also, if you have your farmland that is one block high, make it 2 or more blocks high so the bot does not fall through, as it can happen sometimes when the bot reconnects.\n# The bot also does not pickup all items if they fly off to the side, we have a plan to implement this option in the future as well as drop off and bonemeal refill chest(s).
config.ChatBot.Farmer.Delay_Between_Tasks=Delay between tasks in seconds (Minimum 1 second)
# ChatBot.FollowPlayer
config.ChatBot.FollowPlayer=Enabled you to make the bot follow you\n# NOTE: This is an experimental feature, the bot can be slow at times, you need to walk with a normal speed and to sometimes stop for it to be able to keep up with you\n# It's similar to making animals follow you when you're holding food in your hand.\n# This is due to a slow pathfinding algorithm, we're working on getting a better one\n# You can tweak the update limit and find what works best for you. (NOTE: Do not but a very low one, because you might achieve the opposite,\n# this might clog the thread for terain handling) and thus slow the bot even more.\n# /!\ Make sure server rules allow an option like this in the rules of the server before using this bot
config.ChatBot.FollowPlayer.Update_Limit=The rate at which the bot does calculations (in seconds) (You can tweak this if you feel the bot is too slow)

View file

@ -43,7 +43,7 @@ namespace MinecraftClient
private readonly List<string> registeredCommands = new();
private readonly object delayTasksLock = new();
private readonly List<TaskWithDelay> delayedTasks = new();
private McClient Handler
protected McClient Handler
{
get
{

View file

@ -242,16 +242,16 @@ namespace MinecraftClient
{
switch (positionalIndex)
{
case 0:
case 0:
Config.Main.General.Account.Login = argument;
break;
case 1:
InternalConfig.Password = argument;
case 1:
InternalConfig.Password = argument;
break;
case 2:
Config.Main.SetServerIP(new MainConfig.ServerInfoConfig(argument), true);
case 2:
Config.Main.SetServerIP(new MainConfig.ServerInfoConfig(argument), true);
break;
case 3:
case 3:
// SingleCommand = argument;
break;
}
@ -269,7 +269,7 @@ namespace MinecraftClient
{
[TomlProperty("Current Version")]
public string CurrentVersion { get; set; } = Program.BuildInfo ?? "Development Build";
[TomlProperty("Latest Version")]
public string LatestVersion { get; set; } = "Unknown";
@ -297,15 +297,15 @@ namespace MinecraftClient
[NonSerialized]
public static readonly string[] AvailableLang =
{
"af_za", "ar_sa", "ast_es", "az_az", "ba_ru", "bar", "be_by", "bg_bg", "br_fr", "brb", "bs_ba", "ca_es",
"cs_cz", "cy_gb", "da_dk", "de_at", "de_ch", "de_de", "el_gr", "en_au", "en_ca", "en_gb", "en_nz", "eo_uy",
"es_ar", "es_cl", "es_ec", "es_es", "es_mx", "es_uy", "es_ve", "esan", "et_ee", "eu_es", "fa_ir", "fi_fi",
"fil_ph", "fo_fo", "fr_ca", "fr_fr", "fra_de", "fur_it", "fy_nl", "ga_ie", "gd_gb", "gl_es", "haw_us", "he_il",
"hi_in", "hr_hr", "hu_hu", "hy_am", "id_id", "ig_ng", "io_en", "is_is", "isv", "it_it", "ja_jp", "jbo_en",
"ka_ge", "kk_kz", "kn_in", "ko_kr", "ksh", "kw_gb", "la_la", "lb_lu", "li_li", "lmo", "lt_lt", "lv_lv", "lzh",
"mk_mk", "mn_mn", "ms_my", "mt_mt", "nds_de", "nl_be", "nl_nl", "nn_no", "oc_fr", "ovd", "pl_pl", "pt_br",
"pt_pt", "qya_aa", "ro_ro", "rpr", "ru_ru", "se_no", "sk_sk", "sl_si", "so_so", "sq_al", "sr_sp", "sv_se",
"sxu", "szl", "ta_in", "th_th", "tl_ph", "tlh_aa", "tok", "tr_tr", "tt_ru", "uk_ua", "val_es", "vec_it",
"af_za", "ar_sa", "ast_es", "az_az", "ba_ru", "bar", "be_by", "bg_bg", "br_fr", "brb", "bs_ba", "ca_es",
"cs_cz", "cy_gb", "da_dk", "de_at", "de_ch", "de_de", "el_gr", "en_au", "en_ca", "en_gb", "en_nz", "eo_uy",
"es_ar", "es_cl", "es_ec", "es_es", "es_mx", "es_uy", "es_ve", "esan", "et_ee", "eu_es", "fa_ir", "fi_fi",
"fil_ph", "fo_fo", "fr_ca", "fr_fr", "fra_de", "fur_it", "fy_nl", "ga_ie", "gd_gb", "gl_es", "haw_us", "he_il",
"hi_in", "hr_hr", "hu_hu", "hy_am", "id_id", "ig_ng", "io_en", "is_is", "isv", "it_it", "ja_jp", "jbo_en",
"ka_ge", "kk_kz", "kn_in", "ko_kr", "ksh", "kw_gb", "la_la", "lb_lu", "li_li", "lmo", "lt_lt", "lv_lv", "lzh",
"mk_mk", "mn_mn", "ms_my", "mt_mt", "nds_de", "nl_be", "nl_nl", "nn_no", "oc_fr", "ovd", "pl_pl", "pt_br",
"pt_pt", "qya_aa", "ro_ro", "rpr", "ru_ru", "se_no", "sk_sk", "sl_si", "so_so", "sq_al", "sr_sp", "sv_se",
"sxu", "szl", "ta_in", "th_th", "tl_ph", "tlh_aa", "tok", "tr_tr", "tt_ru", "uk_ua", "val_es", "vec_it",
"vi_vn", "yi_de", "yo_ng", "zh_cn", "zh_hk", "zh_tw", "zlm_arab"
};
@ -750,7 +750,7 @@ namespace MinecraftClient
}
else
{
return false;
return false;
}
}
@ -799,7 +799,7 @@ namespace MinecraftClient
/// <returns>A IDictionary<string, object> containing a name and a vlaue key pairs of variables</returns>
public Dictionary<string, object> GetVariables()
{
Dictionary<string, object> res = new(VarObject);
Dictionary<string, object> res = new(VarObject);
foreach ((string varName, string varData) in VarStirng)
res.Add(varName, varData);
return res;
@ -1054,6 +1054,13 @@ namespace MinecraftClient
set { ChatBots.ChatLog.Config = value; ChatBots.ChatLog.Config.OnSettingUpdate(); }
}
[TomlPrecedingComment("$config.ChatBot.Farmer$")]
public ChatBots.Farmer.Configs Farmer
{
get { return ChatBots.Farmer.Config; }
set { ChatBots.Farmer.Config = value; ChatBots.Farmer.Config.OnSettingUpdate(); }
}
[TomlPrecedingComment("$config.ChatBot.FollowPlayer$")]
public ChatBots.FollowPlayer.Configs FollowPlayer
{
@ -1228,7 +1235,7 @@ namespace MinecraftClient
};
}
}
public static class BrandInfoTypeExtensions
{
public static string? ToBrandString(this BrandInfoType info)