mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Added a Follow Player chat bot
Implemented the Follow Player chat bot.
This commit is contained in:
commit
7f49ee3120
5 changed files with 225 additions and 4 deletions
175
MinecraftClient/ChatBots/FollowPlayer.cs
Normal file
175
MinecraftClient/ChatBots/FollowPlayer.cs
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using MinecraftClient.Mapping;
|
||||
|
||||
namespace MinecraftClient.ChatBots
|
||||
{
|
||||
public class FollowPlayer : ChatBot
|
||||
{
|
||||
private string? _playerToFollow = null;
|
||||
private int _updateCounter = 0;
|
||||
private int _updateLimit;
|
||||
private int _stopAtDistance;
|
||||
private bool _unsafeEnabled = false;
|
||||
|
||||
public FollowPlayer(int updateLimit = 15, int stopAtDistance = 3)
|
||||
{
|
||||
_updateLimit = updateLimit;
|
||||
_stopAtDistance = stopAtDistance;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (!GetEntityHandlingEnabled())
|
||||
{
|
||||
LogToConsoleTranslated("extra.entity_required");
|
||||
LogToConsoleTranslated("general.bot_unload");
|
||||
UnloadBot();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetTerrainEnabled())
|
||||
{
|
||||
LogToConsoleTranslated("extra.terrainandmovement_required");
|
||||
LogToConsoleTranslated("general.bot_unload");
|
||||
UnloadBot();
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterChatBotCommand("follow", "cmd.follow.desc", "follow <player name|stop>", OnFollowCommand);
|
||||
}
|
||||
|
||||
private string OnFollowCommand(string cmd, string[] args)
|
||||
{
|
||||
if (args.Length > 0)
|
||||
{
|
||||
if (args[0].Equals("stop", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (_playerToFollow == null)
|
||||
return Translations.TryGet("cmd.follow.already_stopped");
|
||||
|
||||
_playerToFollow = null;
|
||||
return Translations.TryGet("cmd.follow.stopping");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsValidName(args[0]))
|
||||
return Translations.TryGet("cmd.follow.invalid_name");
|
||||
|
||||
Entity? player = GetEntities().Values.ToList().Find(entity =>
|
||||
entity.Type == EntityType.Player && !string.IsNullOrEmpty(entity.Name) && entity.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (player == null)
|
||||
return Translations.TryGet("cmd.follow.invalid_player");
|
||||
|
||||
if (!CanMoveThere(player.Location))
|
||||
return Translations.TryGet("cmd.follow.cant_reach_player");
|
||||
|
||||
if (_playerToFollow != null && _playerToFollow.Equals(args[0], StringComparison.OrdinalIgnoreCase))
|
||||
return Translations.TryGet("cmd.follow.already_following", _playerToFollow);
|
||||
|
||||
string result = Translations.TryGet(_playerToFollow != null ? "cmd.follow.switched" : "cmd.follow.started", player.Name!);
|
||||
_playerToFollow = args[0].Trim().ToLower();
|
||||
|
||||
LogToConsoleTranslated("cmd.follow.note");
|
||||
|
||||
if (args.Length == 2 && args[1].Equals("-f", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_unsafeEnabled = true;
|
||||
LogToConsoleTranslated("cmd.follow.unsafe_enabled");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return Translations.TryGet("cmd.follow.desc") + ": " + Translations.TryGet("cmd.follow.usage");
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
_updateCounter++;
|
||||
}
|
||||
|
||||
public override void OnEntityMove(Entity entity)
|
||||
{
|
||||
|
||||
if (_updateCounter < _updateLimit)
|
||||
return;
|
||||
|
||||
_updateCounter = 0;
|
||||
|
||||
if (entity.Type != EntityType.Player)
|
||||
return;
|
||||
|
||||
if (_playerToFollow == null || string.IsNullOrEmpty(entity.Name))
|
||||
return;
|
||||
|
||||
if (_playerToFollow != entity.Name.ToLower())
|
||||
return;
|
||||
|
||||
if (!CanMoveThere(entity.Location))
|
||||
return;
|
||||
|
||||
// Stop at specified distance from plater (prevents pushing player around)
|
||||
double distance = Distance(entity.Location, GetCurrentLocation());
|
||||
|
||||
if (distance < _stopAtDistance)
|
||||
return;
|
||||
|
||||
MoveToLocation(entity.Location, _unsafeEnabled);
|
||||
}
|
||||
|
||||
public override void OnEntitySpawn(Entity entity)
|
||||
{
|
||||
if (entity.Type != EntityType.Player)
|
||||
return;
|
||||
|
||||
if (_playerToFollow != null && !string.IsNullOrEmpty(entity.Name) && _playerToFollow.Equals(entity.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
LogToConsoleTranslated("cmd.follow.player_came_to_the_range", _playerToFollow);
|
||||
LogToConsoleTranslated("cmd.follow.resuming");
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEntityDespawn(Entity entity)
|
||||
{
|
||||
if (entity.Type != EntityType.Player)
|
||||
return;
|
||||
|
||||
if (_playerToFollow != null && !string.IsNullOrEmpty(entity.Name) && _playerToFollow.Equals(entity.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
LogToConsoleTranslated("cmd.follow.player_left_the_range", _playerToFollow);
|
||||
LogToConsoleTranslated("cmd.follow.pausing");
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnPlayerLeave(Guid uuid, string? name)
|
||||
{
|
||||
if (_playerToFollow != null && !string.IsNullOrEmpty(name) && _playerToFollow.Equals(name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
LogToConsoleTranslated("cmd.follow.player_left", _playerToFollow);
|
||||
LogToConsoleTranslated("cmd.follow.stopping");
|
||||
_playerToFollow = null;
|
||||
}
|
||||
}
|
||||
|
||||
private double Distance(Location location1, Location location2)
|
||||
{
|
||||
return Math.Sqrt(
|
||||
((location1.X - location2.X) * (location1.X - location2.X)) +
|
||||
((location1.Y - location2.Y) * (location1.Y - location2.Y)) +
|
||||
((location1.Z - location2.Z) * (location1.Z - location2.Z)));
|
||||
}
|
||||
|
||||
private bool CanMoveThere(Location location)
|
||||
{
|
||||
ChunkColumn? chunkColumn = GetWorld().GetChunkColumn(location);
|
||||
|
||||
if (chunkColumn == null || chunkColumn.FullyLoaded == false)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -195,6 +195,7 @@ namespace MinecraftClient
|
|||
if (Settings.AutoCraft_Enabled) { BotLoad(new AutoCraft(Settings.AutoCraft_configFile)); }
|
||||
if (Settings.AutoDrop_Enabled) { BotLoad(new AutoDrop(Settings.AutoDrop_Mode, Settings.AutoDrop_items)); }
|
||||
if (Settings.ReplayMod_Enabled) { BotLoad(new ReplayCapture(Settings.ReplayMod_BackupInterval)); }
|
||||
if (Settings.FollowPlayer_Enabled) { BotLoad(new FollowPlayer(Settings.FollowPlayer_UpdateLimit, Settings.FollowPlayer_UpdateLimit)); }
|
||||
|
||||
//Add your ChatBot here by uncommenting and adapting
|
||||
//BotLoad(new ChatBots.YourBot());
|
||||
|
|
@ -316,7 +317,7 @@ namespace MinecraftClient
|
|||
{
|
||||
if (e is not ThreadAbortException)
|
||||
Log.Warn("Update: Got error from " + bot.ToString() + ": " + e.ToString());
|
||||
else
|
||||
else
|
||||
throw; //ThreadAbortException should not be caught
|
||||
}
|
||||
}
|
||||
|
|
@ -1667,8 +1668,8 @@ namespace MinecraftClient
|
|||
upper2backpack = true;
|
||||
lowerStartSlot = 1;
|
||||
}
|
||||
else if (item != null && item.Count == 1 && (item.Type == ItemType.NetheriteIngot ||
|
||||
item.Type == ItemType.Emerald || item.Type == ItemType.Diamond || item.Type == ItemType.GoldIngot ||
|
||||
else if (item != null && item.Count == 1 && (item.Type == ItemType.NetheriteIngot ||
|
||||
item.Type == ItemType.Emerald || item.Type == ItemType.Diamond || item.Type == ItemType.GoldIngot ||
|
||||
item.Type == ItemType.IronIngot) && !inventory.Items.ContainsKey(0))
|
||||
{
|
||||
lower2upper = true;
|
||||
|
|
|
|||
|
|
@ -264,3 +264,14 @@ items= # separate each item name with a comma ',': It
|
|||
# /!\ You SHOULD use /replay stop or exit the program gracefully with /quit OR THE REPLAY FILE MAY GET CORRUPT!
|
||||
enabled=false
|
||||
backupinterval=300 # How long should replay file be auto-saved, in seconds. Use -1 to disable
|
||||
|
||||
[FollowPlayer]
|
||||
# Enabled you to make the bot follow you
|
||||
# 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
|
||||
# It's similar to making animals follow you when you're holding food in your hand.
|
||||
# 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.
|
||||
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)
|
||||
|
|
@ -525,6 +525,26 @@ bot.replayCapture.created=Replay file created.
|
|||
bot.replayCapture.stopped=Record stopped.
|
||||
bot.replayCapture.restart=Record was stopped. Restart the program to start another record.
|
||||
|
||||
# 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)
|
||||
cmd.follow.already_stopped=Already stopped
|
||||
cmd.follow.stopping=Stopped following!
|
||||
cmd.follow.invalid_name=Invalid or empty player name provided!
|
||||
cmd.follow.invalid_player=The specified player is either not connected out out of the range!
|
||||
cmd.follow.cant_reach_player=Can\'t reach the player, he is either in chunks that are not loaded, too far away or not reachable by a bot due to obstacles like gaps or water bodies!
|
||||
cmd.follow.already_following=Already following {0}!
|
||||
cmd.follow.switched=Switched to following {0}!
|
||||
cmd.follow.started=Started following {0}!
|
||||
cmd.follow.unsafe_enabled=Enabled us-safe walking (NOTE: The bot might die or get hurt!)
|
||||
cmd.follow.note=NOTE: The bot is quite slow, you need to walk slowly and at a close distance for it to be able to keep up, kinda like when you make animals follow you by holding food in your hand. This is a limitation due to a pathfinding algorithm, we are working to get a better one.
|
||||
cmd.follow.player_came_to_the_range=The player {0} came back to the range!
|
||||
cmd.follow.resuming=Resuming to follow!
|
||||
cmd.follow.player_left_the_range=The player {0} has left the range!
|
||||
cmd.follow.pausing=Pausing!
|
||||
cmd.follow.player_left=The player {0} left the server!
|
||||
cmd.follow.stopping=Stopped!
|
||||
|
||||
# Script
|
||||
bot.script.not_found=§8[MCC] [{0}] Cannot find script file: {1}
|
||||
bot.script.file_not_found=File not found: '{0}'
|
||||
|
|
|
|||
|
|
@ -241,6 +241,11 @@ namespace MinecraftClient
|
|||
public static bool ReplayMod_Enabled = false;
|
||||
public static int ReplayMod_BackupInterval = 3000;
|
||||
|
||||
// Follow Player
|
||||
public static bool FollowPlayer_Enabled = false;
|
||||
public static int FollowPlayer_UpdateLimit = 10;
|
||||
public static int FollowPlayer_StopAtDistance = 3;
|
||||
|
||||
//Custom app variables and Minecraft accounts
|
||||
private static readonly Dictionary<string, object> AppVars = new Dictionary<string, object>();
|
||||
private static readonly Dictionary<string, KeyValuePair<string, string>> Accounts = new Dictionary<string, KeyValuePair<string, string>>();
|
||||
|
|
@ -250,7 +255,7 @@ namespace MinecraftClient
|
|||
private static string ServerAliasTemp = null;
|
||||
|
||||
//Mapping for settings sections in the INI file
|
||||
private enum Section { Default, Main, AppVars, Proxy, MCSettings, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl, ChatFormat, AutoRespond, AutoAttack, AutoFishing, AutoEat, AutoCraft, AutoDrop, Mailer, ReplayMod, Logging, Signature };
|
||||
private enum Section { Default, Main, AppVars, Proxy, MCSettings, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl, ChatFormat, AutoRespond, AutoAttack, AutoFishing, AutoEat, AutoCraft, AutoDrop, Mailer, ReplayMod, FollowPlayer, Logging, Signature };
|
||||
|
||||
/// <summary>
|
||||
/// Get settings section from name
|
||||
|
|
@ -838,6 +843,15 @@ namespace MinecraftClient
|
|||
case "backupinterval": ReplayMod_BackupInterval = str2int(argValue); return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case Section.FollowPlayer:
|
||||
switch (ToLowerIfNeed(argName))
|
||||
{
|
||||
case "enabled": FollowPlayer_Enabled = str2bool(argValue); return true;
|
||||
case "update_limit": FollowPlayer_UpdateLimit = str2int(argValue); return true;
|
||||
case "stop_at_distance": FollowPlayer_StopAtDistance = str2int(argValue); return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue