diff --git a/MinecraftClient/ChatBots/AntiAFK.cs b/MinecraftClient/ChatBots/AntiAFK.cs index a54e8bcc..d078aae5 100644 --- a/MinecraftClient/ChatBots/AntiAFK.cs +++ b/MinecraftClient/ChatBots/AntiAFK.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using MinecraftClient.Mapping; namespace MinecraftClient.ChatBots { @@ -12,28 +10,158 @@ namespace MinecraftClient.ChatBots public class AntiAFK : ChatBot { private int count; - private int timeping; + private string pingparam; + private int timeping = 600; + private int timepingMax = -1; + private bool useTerrainHandling = false; + private bool previousSneakState = false; + private int walkRange = 5; + private int walkRetries = 10; + private Random random = new Random(); /// /// This bot sends a /ping command every X seconds in order to stay non-afk. /// - /// Time amount between each ping (10 = 1s, 600 = 1 minute, etc.) + /// Time amount between each ping (10 = 1s, 600 = 1 minute, etc.) Can be a range of numbers eg. 10-600 - public AntiAFK(int pingparam) + public AntiAFK(string pingparam, bool useTerrainHandling, int walkRange, int walkRetries) { count = 0; - timeping = pingparam; + this.pingparam = pingparam; + this.useTerrainHandling = useTerrainHandling; + this.walkRange = walkRange; + this.walkRetries = walkRetries; + } + + public override void Initialize() + { + if (useTerrainHandling) + { + if (!GetTerrainEnabled()) + { + useTerrainHandling = false; + LogToConsole(Translations.TryGet("bot.antiafk.not_using_terrain_handling")); + } + else + { + if (walkRange <= 0) + { + walkRange = 5; + LogToConsole(Translations.TryGet("bot.antiafk.invalid_walk_range")); + } + } + } + + if (string.IsNullOrEmpty(pingparam)) + LogToConsole(Translations.TryGet("bot.antiafk.invalid_time")); + else + { + // Handle the random range + if (pingparam.Contains("-")) + { + string[] parts = pingparam.Split("-"); + + if (parts.Length == 2) + { + if (int.TryParse(parts[0].Trim(), out int firstTime)) + { + timeping = firstTime; + + if (int.TryParse(parts[1].Trim(), out int secondTime)) + timepingMax = secondTime; + else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range_partial", timeping)); + } + else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range")); + } + else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range")); + } + else + { + if (int.TryParse(pingparam.Trim(), out int value)) + timeping = value; + else LogToConsole(Translations.TryGet("bot.antiafk.invalid_value")); + } + } + + if (timepingMax != -1 && timeping > timepingMax) + { + int temporary = timepingMax; + timepingMax = timeping; + timeping = temporary; + + LogToConsole(Translations.TryGet("bot.antiafk.swapping")); + } + if (timeping < 10) { timeping = 10; } //To avoid flooding } public override void Update() { count++; - if (count == timeping) + + if ((timepingMax != -1 && count == random.Next(timeping, timepingMax)) || count == timeping) { - SendText(Settings.AntiAFK_Command); + DoAntiAfkStuff(); count = 0; } + + } + + private void DoAntiAfkStuff() + { + if (useTerrainHandling) + { + Location currentLocation = GetCurrentLocation(); + Location goal; + + bool moved = false; + bool useAlternativeMethod = false; + int triesCounter = 0; + + while (!moved) + { + if (triesCounter++ >= walkRetries) + { + useAlternativeMethod = true; + break; + } + + goal = GetRandomLocationWithinRangeXZ(currentLocation, walkRange); + + // Prevent getting the same location + while ((currentLocation.X == goal.X) && (currentLocation.Y == goal.Y) && (currentLocation.Z == goal.Z)) + { + LogToConsole("Same location!, generating new one"); + goal = GetRandomLocationWithinRangeXZ(currentLocation, walkRange); + } + + if (!Movement.CheckChunkLoading(GetWorld(), currentLocation, goal)) + { + useAlternativeMethod = true; + break; + } + else moved = MoveToLocation(goal, allowUnsafe: false, allowDirectTeleport: false); + } + + if (!useAlternativeMethod) + { + // Solve the case when the bot was closed in 1x2, was sneaking, but then he was freed, this will make him not sneak anymore + previousSneakState = false; + Sneak(false); + + return; + } + } + + SendText(Settings.AntiAFK_Command); + Sneak(previousSneakState); + previousSneakState = !previousSneakState; + count = 0; + } + + private Location GetRandomLocationWithinRangeXZ(Location currentLocation, int range) + { + return new Location(currentLocation.X + random.Next(range * -1, range), currentLocation.Y, currentLocation.Z + random.Next(range * -1, range)); } } } diff --git a/MinecraftClient/ChatBots/Map.cs b/MinecraftClient/ChatBots/Map.cs index 0969daba..a6793e1f 100644 --- a/MinecraftClient/ChatBots/Map.cs +++ b/MinecraftClient/ChatBots/Map.cs @@ -33,7 +33,7 @@ namespace MinecraftClient.ChatBots deleteAllOnExit = Settings.Map_Delete_All_On_Unload; notifyOnFirstUpdate = Settings.Map_Notify_On_First_Update; - RegisterChatBotCommand("maps", "bot.map.cmd.desc", "maps > | maps >", OnMapCommand); + RegisterChatBotCommand("maps", "bot.map.cmd.desc", "maps list|render or maps l|r ", OnMapCommand); } public override void OnUnload() diff --git a/MinecraftClient/Commands/Bed.cs b/MinecraftClient/Commands/Bed.cs new file mode 100644 index 00000000..89f6c7a7 --- /dev/null +++ b/MinecraftClient/Commands/Bed.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MinecraftClient.Mapping; + +namespace MinecraftClient.Commands +{ + public class BedCommand : Command + { + public override string CmdName { get { return "bed"; } } + public override string CmdUsage { get { return "bed leave|sleep |sleep "; } } + public override string CmdDesc { get { return "cmd.bed.desc"; } } + + public override string Run(McClient handler, string command, Dictionary localVars) + { + string[] args = getArgs(command); + + if (args.Length >= 1) + { + string subcommand = args[0].ToLower().Trim(); + + if (subcommand.Equals("leave") || subcommand.Equals("l")) + { + handler.SendEntityAction(Protocol.EntityActionType.LeaveBed); + return Translations.TryGet("cmd.bed.leaving"); + } + + if (subcommand.Equals("sleep") || subcommand.Equals("s")) + { + if (args.Length == 2) + { + if (!int.TryParse(args[1], out int radius)) + return CmdUsage; + + handler.GetLogger().Info(Translations.TryGet("cmd.bed.searching", radius)); + + Location current = handler.GetCurrentLocation(); + Location bedLocation = current; + + Material[] bedMaterialList = new Material[]{ + Material.BlackBed, + Material.BlueBed, + Material.BrownBed, + Material.CyanBed, + Material.GrayBed, + Material.GreenBed, + Material.LightBlueBed, + Material.LightGrayBed, + Material.LimeBed, + Material.MagentaBed, + Material.OrangeBed, + Material.PinkBed, + Material.PurpleBed, + Material.RedBed, + Material.WhiteBed, + Material.YellowBed + }; + + bool found = false; + foreach (Material material in bedMaterialList) + { + List beds = handler.GetWorld().FindBlock(current, material, radius); + + if (beds.Count > 0) + { + found = true; + bedLocation = beds.First(); + break; + } + } + + if (!found) + return Translations.TryGet("cmd.bed.bed_not_found"); + + handler.Log.Info(Translations.TryGet("cmd.bed.found_a_bed_at", bedLocation.X, bedLocation.Y, bedLocation.Z)); + + if (!Movement.CheckChunkLoading(handler.GetWorld(), current, bedLocation)) + return Translations.Get("cmd.move.chunk_not_loaded", bedLocation.X, bedLocation.Y, bedLocation.Z); + + if (handler.MoveTo(bedLocation)) + { + Task.Factory.StartNew(() => + { + bool atTheLocation = false; + DateTime timeout = DateTime.Now.AddSeconds(60); + + while (DateTime.Now < timeout) + { + if (handler.GetCurrentLocation() == bedLocation || handler.GetCurrentLocation().Distance(bedLocation) <= 2.0) + { + atTheLocation = true; + break; + } + } + + if (!atTheLocation) + { + handler.Log.Info(Translations.TryGet("cmd.bed.failed_to_reach_in_time", bedLocation.X, bedLocation.Y, bedLocation.Z)); + return; + } + + handler.Log.Info(Translations.TryGet("cmd.bed.moving", bedLocation.X, bedLocation.Y, bedLocation.Z)); + + bool res = handler.PlaceBlock(bedLocation, Direction.Down); + + handler.Log.Info(Translations.TryGet( + "cmd.bed.trying_to_use", + bedLocation.X, + bedLocation.Y, + bedLocation.Z, + Translations.TryGet(res ? "cmd.bed.in" : "cmd.bed.not_in") + )); + }); + + return ""; + } + + return Translations.Get("cmd.bed.cant_reach_safely"); + } + + if (args.Length >= 3) + { + Location current = handler.GetCurrentLocation(); + double x = args[1].StartsWith('~') ? current.X + (args[1].Length > 1 ? double.Parse(args[1][1..]) : 0) : double.Parse(args[1]); + double y = args[2].StartsWith('~') ? current.Y + (args[2].Length > 1 ? double.Parse(args[2][1..]) : 0) : double.Parse(args[2]); + double z = args[3].StartsWith('~') ? current.Z + (args[3].Length > 1 ? double.Parse(args[3][1..]) : 0) : double.Parse(args[3]); + + Location block = new Location(x, y, z).ToFloor(), blockCenter = block.ToCenter(); + + if (!handler.GetWorld().GetBlock(block).Type.IsBed()) + return Translations.TryGet("cmd.bed.not_a_bed", blockCenter.X, blockCenter.Y, blockCenter.Z); + + bool res = handler.PlaceBlock(block, Direction.Down); + + return Translations.TryGet( + "cmd.bed.trying_to_use", + blockCenter.X, + blockCenter.Y, + blockCenter.Z, + Translations.TryGet(res ? "cmd.bed.in" : "cmd.bed.not_in") + ); + } + } + } + + return CmdUsage; + } + } +} \ No newline at end of file diff --git a/MinecraftClient/Mapping/MaterialExtensions.cs b/MinecraftClient/Mapping/MaterialExtensions.cs index fafa4894..f1b9129a 100644 --- a/MinecraftClient/Mapping/MaterialExtensions.cs +++ b/MinecraftClient/Mapping/MaterialExtensions.cs @@ -752,6 +752,7 @@ namespace MinecraftClient.Mapping return false; } } + /// /// Check if the provided material is a liquid a player can swim into /// @@ -768,5 +769,36 @@ namespace MinecraftClient.Mapping return false; } } + + /// + /// Check if the provided material is a bed + /// + /// Material to test + /// True if the material is a bed + public static bool IsBed(this Material m) + { + switch (m) + { + case Material.BlackBed: + case Material.BlueBed: + case Material.BrownBed: + case Material.CyanBed: + case Material.GrayBed: + case Material.GreenBed: + case Material.LightBlueBed: + case Material.LightGrayBed: + case Material.LimeBed: + case Material.MagentaBed: + case Material.OrangeBed: + case Material.PinkBed: + case Material.PurpleBed: + case Material.RedBed: + case Material.WhiteBed: + case Material.YellowBed: + return true; + default: + return false; + } + } } } diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index f5305d2e..89809d3f 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -271,7 +271,7 @@ namespace MinecraftClient /// private void RegisterBots(bool reload = false) { - if (Settings.AntiAFK_Enabled) { BotLoad(new AntiAFK(Settings.AntiAFK_Delay)); } + if (Settings.AntiAFK_Enabled) { BotLoad(new AntiAFK(Settings.AntiAFK_Delay, Settings.AntiAFK_UseTerrain_Handling, Settings.AntiAFK_Walk_Range, Settings.AntiAFK_Walk_Retries)); } if (Settings.Hangman_Enabled) { BotLoad(new HangmanGame(Settings.Hangman_English)); } if (Settings.Alerts_Enabled) { BotLoad(new Alerts()); } if (Settings.ChatLog_Enabled) { BotLoad(new ChatLog(Settings.ExpandVars(Settings.ChatLog_File), Settings.ChatLog_Filter, Settings.ChatLog_DateTime)); } diff --git a/MinecraftClient/Resources/config/MinecraftClient.ini b/MinecraftClient/Resources/config/MinecraftClient.ini index 55562ea7..5ec3aeb8 100644 --- a/MinecraftClient/Resources/config/MinecraftClient.ini +++ b/MinecraftClient/Resources/config/MinecraftClient.ini @@ -138,11 +138,15 @@ logtofile=false # Log alerts info a file logfile=alerts-log.txt # The name of a file where alers logs will be written [AntiAFK] -# Send a command on a regular basis to avoid automatic AFK disconnection +# Send a command on a regular or random basis or make the bot walk around randomly to avoid automatic AFK disconnection # /!\ Make sure your server rules do not forbid anti-AFK mechanisms! +# /!\ Make sure you keep the bot in an enclosure to prevent it wandering off if you're using terrain handling! (Recommended size 5x5x5) enabled=false -delay=600 #10 = 1s -command=/ping +delay=600 # 10 = 1s (Can also be a random number between 2 numbers, example: 50-600) (Default: 600) +command=/ping # Command to send to the server +use_terrain_handling=false # Use terrain handling to enable the bot to move around +walk_range=5 # The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be) +walk_retries=20 # How many timec can the bot fail trying to move before using the command method [AutoRelog] # Automatically relog when disconnected by server, for example because the server is restating @@ -282,6 +286,7 @@ backupinterval=300 # How long should replay file be auto-saved, i # This is due to a slow pathfinding algorithm, we're working on getting a better one # 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, # this might clog the thread for terain handling) and thus slow the bot even more. +# /!\ Make sure server rules allow an option like this in the rules of the server before using this bot enabled=false update_limit=10 # The rate at which the bot does calculations (10 = 1s) (Default: 5) (You can tweak this if you feel the bot is too slow) stop_at_distance=3 # Do not follow the player if he is in the range of 3 blocks (prevents the bot from pushing a player in an infinite loop) @@ -295,15 +300,17 @@ log_delay=600 # 10 = 1s [Map] # Allows you to render maps into .jpg images # This is useful for solving captchas which use maps -# NOTE: This is a new feature, we could not find the proper color mappings, but we are continuing with the search -# The colors are not like in minecraft and might look ugly -# This feature is currently only useful for solving captchas, which is it's primary purpose for the time being. +# The maps are rendered into Rendered_Maps folder. +# NOTE: +# This feature is currently only useful for solving captchas which use maps. # If some servers have a very short time for solving captchas, enabe auto_render_on_update and prepare to open the file quickly. # On linux you can use FTP to access generated files. +# In the future it might will be possible to display maps directly in the console with a separate command. +# /!\ Make sure server rules allow bots to be used on the server, or you risk being punished. enabled=false resize_map=false # Should the map be resized? (Default one is small 128x128) resize_to=256 # The size to resize the map to (Note: the bigger it is, the lower the quallity is) auto_render_on_update=false # Automatically render the map once it's received or updated from/by the server -delete_rendered_on_unload=true # Delete all rendered maps on unload or exit +delete_rendered_on_unload=true # Delete all rendered maps on unload/reload (Does not delete the images if you exit the client) notify_on_first_update=false # Get a notification when you have gotten a map from the server for the first time - # Note: Will be printed for each map in vicinity, could cause spam if there are a lot of maps \ No newline at end of file + # Note: Will be printed for each map in vicinity, could cause spam if there are a lot of maps \ No newline at end of file diff --git a/MinecraftClient/Resources/lang/en.ini b/MinecraftClient/Resources/lang/en.ini index 9cbda134..1a05a119 100644 --- a/MinecraftClient/Resources/lang/en.ini +++ b/MinecraftClient/Resources/lang/en.ini @@ -344,6 +344,20 @@ cmd.inventory.help.unknown=Unknown action. cmd.inventory.found_items=Found items cmd.inventory.no_found_items=Could not find the specified item in any of avaliable Inventories! +# Leave bed +cmd.bed.desc=Used to sleep in or leave a bed. +cmd.bed.leaving=Sending a command to leave a bed to the server. +cmd.bed.trying_to_use=Trying to sleep in a bed on location (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}). Result: {3} +cmd.bed.in=Succesfully laid in bed! +cmd.bed.not_in=Could not lay in bed. Are you trying to sleep in a bed? (PS: You must use the head block coordinates of the bed) +cmd.bed.not_a_bed=The block on (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}) is not a bed! +cmd.bed.searching=Searching for a bed in radius of {0}... +cmd.bed.bed_not_found=Could not find a bed! +cmd.bed.found_a_bed_at=Found a bet at (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}). +cmd.bed.cant_reach_safely=Can not reach the bed safely! +cmd.bed.moving=Moving to (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}) where the bed is located. +cmd.bed.failed_to_reach_in_time=Failed to reach the bed position (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}) in time (30 seconds). Giving up! + # List cmd.list.desc=get the player list. cmd.list.players=PlayerList: {0} @@ -428,6 +442,14 @@ cmd.useitem.use=Used an item [bot] # ChatBots. Naming style: bot.. +# Anti AFK +bot.antiafk.not_using_terrain_handling=The terrain handling is not enabled in the settings of the client, enable it if you want to use it with this bot. Using alternative (command) method. +bot.antiafk.invalid_range_partial=Invalid time range provided, using the first part of the range {0} as the time! +bot.antiafk.invalid_range=Invalid time range provided, using default time of 600! +bot.antiafk.invalid_value=Invalid time provided, using default time of 600! +bot.antiafk.swapping=The time range begins with a bigger value, swapped them around. +bot.antiafk.invalid_walk_range=Invalid walk range provided, must be a positive integer greater than 0, using default value of 5! + # AutoAttack bot.autoAttack.mode=Unknown attack mode: {0}. Using single mode as default. bot.autoAttack.priority=Unknown priority: {0}. Using distance priority as default. diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 74e2bf28..70ff4a5b 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -141,8 +141,11 @@ namespace MinecraftClient //AntiAFK Settings public static bool AntiAFK_Enabled = false; - public static int AntiAFK_Delay = 600; + public static string AntiAFK_Delay = "600"; public static string AntiAFK_Command = "/ping"; + public static bool AntiAFK_UseTerrain_Handling = false; + public static int AntiAFK_Walk_Range = 5; + public static int AntiAFK_Walk_Retries = 20; //Hangman Settings public static bool Hangman_Enabled = false; @@ -598,8 +601,11 @@ namespace MinecraftClient switch (ToLowerIfNeed(argName)) { case "enabled": AntiAFK_Enabled = str2bool(argValue); return true; - case "delay": AntiAFK_Delay = str2int(argValue); return true; + case "delay": AntiAFK_Delay = argValue; return true; case "command": AntiAFK_Command = argValue == "" ? "/ping" : argValue; return true; + case "use_terrain_handling": AntiAFK_UseTerrain_Handling = str2bool(argValue); return true; + case "walk_range": AntiAFK_Walk_Range = str2int(argValue); return true; + case "walk_retries": AntiAFK_Walk_Retries = str2int(argValue); return true; } break;