2022-09-09 15:39:41 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Linq;
|
2022-12-06 15:50:17 +08:00
|
|
|
|
using Brigadier.NET;
|
|
|
|
|
|
using Brigadier.NET.Builder;
|
|
|
|
|
|
using MinecraftClient.CommandHandler;
|
|
|
|
|
|
using MinecraftClient.CommandHandler.Patch;
|
2022-09-09 15:39:41 +02:00
|
|
|
|
using MinecraftClient.Mapping;
|
2022-12-06 15:50:17 +08:00
|
|
|
|
using MinecraftClient.Scripting;
|
2022-10-05 15:02:30 +08:00
|
|
|
|
using Tomlet.Attributes;
|
2022-09-09 15:39:41 +02:00
|
|
|
|
|
|
|
|
|
|
namespace MinecraftClient.ChatBots
|
|
|
|
|
|
{
|
|
|
|
|
|
public class FollowPlayer : ChatBot
|
|
|
|
|
|
{
|
2022-12-06 15:50:17 +08:00
|
|
|
|
public const string CommandName = "follow";
|
|
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public static Configs Config = new();
|
2022-09-09 15:39:41 +02:00
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
[TomlDoNotInlineObject]
|
|
|
|
|
|
public class Configs
|
2022-09-09 15:39:41 +02:00
|
|
|
|
{
|
2023-06-23 16:25:18 +02:00
|
|
|
|
[NonSerialized] private const string BotName = "FollowPlayer";
|
2022-10-05 15:02:30 +08:00
|
|
|
|
|
|
|
|
|
|
public bool Enabled = false;
|
|
|
|
|
|
|
2022-11-30 16:22:48 +08:00
|
|
|
|
[TomlInlineComment("$ChatBot.FollowPlayer.Update_Limit$")]
|
2022-10-12 13:03:57 +08:00
|
|
|
|
public double Update_Limit = 1.5;
|
2022-10-05 15:02:30 +08:00
|
|
|
|
|
2022-11-30 16:22:48 +08:00
|
|
|
|
[TomlInlineComment("$ChatBot.FollowPlayer.Stop_At_Distance$")]
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public double Stop_At_Distance = 3.0;
|
|
|
|
|
|
|
|
|
|
|
|
public void OnSettingUpdate()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Update_Limit < 0)
|
|
|
|
|
|
Update_Limit = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (Stop_At_Distance < 0)
|
|
|
|
|
|
Stop_At_Distance = 0;
|
|
|
|
|
|
}
|
2022-09-09 15:39:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
private string? _playerToFollow = null;
|
|
|
|
|
|
private int _updateCounter = 0;
|
|
|
|
|
|
private bool _unsafeEnabled = false;
|
|
|
|
|
|
|
2022-09-09 15:39:41 +02:00
|
|
|
|
public override void Initialize()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!GetEntityHandlingEnabled())
|
|
|
|
|
|
{
|
2022-10-28 11:13:20 +08:00
|
|
|
|
LogToConsole(Translations.extra_entity_required);
|
|
|
|
|
|
LogToConsole(Translations.general_bot_unload);
|
2022-09-09 15:39:41 +02:00
|
|
|
|
UnloadBot();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!GetTerrainEnabled())
|
|
|
|
|
|
{
|
2022-10-28 11:13:20 +08:00
|
|
|
|
LogToConsole(Translations.extra_terrainandmovement_required);
|
|
|
|
|
|
LogToConsole(Translations.general_bot_unload);
|
2022-09-09 15:39:41 +02:00
|
|
|
|
UnloadBot();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-12-11 16:30:45 +08:00
|
|
|
|
McClient.dispatcher.Register(l => l.Literal("help")
|
2022-12-06 15:50:17 +08:00
|
|
|
|
.Then(l => l.Literal(CommandName)
|
|
|
|
|
|
.Executes(r => OnCommandHelp(r.Source, string.Empty))
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2022-12-11 16:30:45 +08:00
|
|
|
|
McClient.dispatcher.Register(l => l.Literal(CommandName)
|
2022-12-06 15:50:17 +08:00
|
|
|
|
.Then(l => l.Literal("start")
|
|
|
|
|
|
.Then(l => l.Argument("PlayerName", MccArguments.PlayerName())
|
|
|
|
|
|
.Executes(r => OnCommandStart(r.Source, Arguments.GetString(r, "PlayerName"), takeRisk: false))
|
|
|
|
|
|
.Then(l => l.Literal("-f")
|
2023-06-23 16:25:18 +02:00
|
|
|
|
.Executes(r =>
|
|
|
|
|
|
OnCommandStart(r.Source, Arguments.GetString(r, "PlayerName"), takeRisk: true)))))
|
2022-12-06 15:50:17 +08:00
|
|
|
|
.Then(l => l.Literal("stop")
|
|
|
|
|
|
.Executes(r => OnCommandStop(r.Source)))
|
|
|
|
|
|
.Then(l => l.Literal("_help")
|
2022-12-11 17:31:37 +08:00
|
|
|
|
.Executes(r => OnCommandHelp(r.Source, string.Empty))
|
2022-12-11 16:30:45 +08:00
|
|
|
|
.Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName)))
|
2022-12-06 15:50:17 +08:00
|
|
|
|
);
|
2022-09-09 15:39:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-12-06 20:32:46 +08:00
|
|
|
|
public override void OnUnload()
|
2022-09-09 15:39:41 +02:00
|
|
|
|
{
|
2023-06-23 16:25:18 +02:00
|
|
|
|
BotMovementLock.Instance?.UnLock("Follow Player");
|
2022-12-11 16:30:45 +08:00
|
|
|
|
McClient.dispatcher.Unregister(CommandName);
|
|
|
|
|
|
McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName);
|
2022-12-06 15:50:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private int OnCommandHelp(CmdResult r, string? cmd)
|
|
|
|
|
|
{
|
|
|
|
|
|
return r.SetAndReturn(cmd switch
|
|
|
|
|
|
{
|
|
|
|
|
|
#pragma warning disable format // @formatter:off
|
|
|
|
|
|
_ => Translations.cmd_follow_desc + ": " + Translations.cmd_follow_usage
|
2022-12-11 16:30:45 +08:00
|
|
|
|
+ '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false),
|
2022-12-06 15:50:17 +08:00
|
|
|
|
#pragma warning restore format // @formatter:on
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private int OnCommandStart(CmdResult r, string name, bool takeRisk)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!IsValidName(name))
|
|
|
|
|
|
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_invalid_name);
|
|
|
|
|
|
|
2023-06-23 16:25:18 +02:00
|
|
|
|
var player = GetEntities().Values.ToList().Find(entity =>
|
2022-12-06 15:50:17 +08:00
|
|
|
|
entity.Type == EntityType.Player
|
|
|
|
|
|
&& !string.IsNullOrEmpty(entity.Name)
|
|
|
|
|
|
&& entity.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
|
|
|
|
|
if (player == null)
|
|
|
|
|
|
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_invalid_player);
|
|
|
|
|
|
|
|
|
|
|
|
if (!CanMoveThere(player.Location))
|
|
|
|
|
|
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_cant_reach_player);
|
|
|
|
|
|
|
|
|
|
|
|
if (_playerToFollow != null && _playerToFollow.Equals(name, StringComparison.OrdinalIgnoreCase))
|
2023-06-23 16:25:18 +02:00
|
|
|
|
return r.SetAndReturn(CmdResult.Status.Fail,
|
|
|
|
|
|
string.Format(Translations.cmd_follow_already_following, _playerToFollow));
|
|
|
|
|
|
|
|
|
|
|
|
var movementLock = BotMovementLock.Instance;
|
|
|
|
|
|
if (movementLock is { IsLocked: true })
|
|
|
|
|
|
return r.SetAndReturn(CmdResult.Status.Fail,
|
|
|
|
|
|
string.Format(Translations.bot_common_movement_lock_held, "Follow Player", movementLock.LockedBy));
|
|
|
|
|
|
|
|
|
|
|
|
var result =
|
|
|
|
|
|
string.Format(
|
|
|
|
|
|
_playerToFollow != null ? Translations.cmd_follow_switched : Translations.cmd_follow_started,
|
|
|
|
|
|
player.Name!);
|
2022-12-06 15:50:17 +08:00
|
|
|
|
_playerToFollow = name.ToLower();
|
|
|
|
|
|
|
2023-06-23 16:25:18 +02:00
|
|
|
|
switch (movementLock)
|
2022-09-09 15:39:41 +02:00
|
|
|
|
{
|
2023-06-23 16:25:18 +02:00
|
|
|
|
case { IsLocked: false }:
|
|
|
|
|
|
if (!movementLock.Lock("Follow Player"))
|
|
|
|
|
|
{
|
|
|
|
|
|
LogToConsole($"§§6§1§0Follow Player bot failed to obtain the movement lock for some reason!");
|
|
|
|
|
|
LogToConsole($"§§6§1§0Disable other bots who have movement mechanics, and try again!");
|
|
|
|
|
|
return r.SetAndReturn(CmdResult.Status.Fail);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
2022-09-09 15:39:41 +02:00
|
|
|
|
}
|
2023-06-23 16:25:18 +02:00
|
|
|
|
|
|
|
|
|
|
if (!takeRisk) return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_follow_note);
|
|
|
|
|
|
_unsafeEnabled = true;
|
|
|
|
|
|
return r.SetAndReturn(CmdResult.Status.Done,
|
|
|
|
|
|
Translations.cmd_follow_note + '\n' + Translations.cmd_follow_unsafe_enabled);
|
2022-12-06 15:50:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private int OnCommandStop(CmdResult r)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_playerToFollow == null)
|
|
|
|
|
|
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_already_stopped);
|
|
|
|
|
|
|
2023-06-23 16:25:18 +02:00
|
|
|
|
var movementLock = BotMovementLock.Instance;
|
|
|
|
|
|
movementLock?.UnLock("Follow Player");
|
2022-12-06 15:50:17 +08:00
|
|
|
|
_playerToFollow = null;
|
2022-09-09 15:39:41 +02:00
|
|
|
|
|
2022-12-06 15:50:17 +08:00
|
|
|
|
return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_follow_stopping);
|
2022-09-09 15:39:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void Update()
|
|
|
|
|
|
{
|
|
|
|
|
|
_updateCounter++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnEntityMove(Entity entity)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (entity.Type != EntityType.Player)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if (_playerToFollow == null || string.IsNullOrEmpty(entity.Name))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if (_playerToFollow != entity.Name.ToLower())
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2023-03-25 17:29:30 +08:00
|
|
|
|
if (_updateCounter < Settings.DoubleToTick(Config.Update_Limit))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
_updateCounter = 0;
|
|
|
|
|
|
|
2022-09-09 15:39:41 +02:00
|
|
|
|
if (!CanMoveThere(entity.Location))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2023-06-23 16:25:18 +02:00
|
|
|
|
// Stop at specified distance from player (prevents pushing player around)
|
|
|
|
|
|
var distance = entity.Location.Distance(GetCurrentLocation());
|
2022-09-09 15:39:41 +02:00
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
if (distance < Config.Stop_At_Distance)
|
2022-09-09 15:39:41 +02:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
MoveToLocation(entity.Location, _unsafeEnabled);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnEntitySpawn(Entity entity)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (entity.Type != EntityType.Player)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2023-06-23 16:25:18 +02:00
|
|
|
|
if (_playerToFollow != null && !string.IsNullOrEmpty(entity.Name) &&
|
|
|
|
|
|
_playerToFollow.Equals(entity.Name, StringComparison.OrdinalIgnoreCase))
|
2022-09-09 15:39:41 +02:00
|
|
|
|
{
|
2022-10-28 11:13:20 +08:00
|
|
|
|
LogToConsole(string.Format(Translations.cmd_follow_player_came_to_the_range, _playerToFollow));
|
|
|
|
|
|
LogToConsole(Translations.cmd_follow_resuming);
|
2022-09-09 15:39:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnEntityDespawn(Entity entity)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (entity.Type != EntityType.Player)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2023-06-23 16:25:18 +02:00
|
|
|
|
if (_playerToFollow != null && !string.IsNullOrEmpty(entity.Name) &&
|
|
|
|
|
|
_playerToFollow.Equals(entity.Name, StringComparison.OrdinalIgnoreCase))
|
2022-09-09 15:39:41 +02:00
|
|
|
|
{
|
2022-10-28 11:13:20 +08:00
|
|
|
|
LogToConsole(string.Format(Translations.cmd_follow_player_left_the_range, _playerToFollow));
|
|
|
|
|
|
LogToConsole(Translations.cmd_follow_pausing);
|
2022-09-09 15:39:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnPlayerLeave(Guid uuid, string? name)
|
|
|
|
|
|
{
|
2023-06-23 16:25:18 +02:00
|
|
|
|
if (_playerToFollow != null && !string.IsNullOrEmpty(name) &&
|
|
|
|
|
|
_playerToFollow.Equals(name, StringComparison.OrdinalIgnoreCase))
|
2022-09-09 15:39:41 +02:00
|
|
|
|
{
|
2022-10-28 11:13:20 +08:00
|
|
|
|
LogToConsole(string.Format(Translations.cmd_follow_player_left, _playerToFollow));
|
|
|
|
|
|
LogToConsole(Translations.cmd_follow_stopping);
|
2022-09-09 15:39:41 +02:00
|
|
|
|
_playerToFollow = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private bool CanMoveThere(Location location)
|
|
|
|
|
|
{
|
2023-06-23 16:25:18 +02:00
|
|
|
|
var chunkColumn = GetWorld().GetChunkColumn(location);
|
|
|
|
|
|
return chunkColumn != null && chunkColumn.FullyLoaded != false;
|
2022-09-09 15:39:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|