Minecraft-Console-Client/MinecraftClient/ChatBots/AutoDig.cs
2022-12-11 16:30:45 +08:00

446 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Brigadier.NET.Builder;
using MinecraftClient.CommandHandler;
using MinecraftClient.CommandHandler.Patch;
using MinecraftClient.Mapping;
using MinecraftClient.Scripting;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
public class AutoDig : ChatBot
{
public const string CommandName = "autodig";
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoDig";
public bool Enabled = false;
[NonSerialized]
[TomlInlineComment("$ChatBot.AutoDig.Auto_Tool_Switch$")]
public bool Auto_Tool_Switch = false;
[NonSerialized]
[TomlInlineComment("$ChatBot.AutoDig.Durability_Limit$")]
public int Durability_Limit = 2;
[NonSerialized]
[TomlInlineComment("$ChatBot.AutoDig.Drop_Low_Durability_Tools$")]
public bool Drop_Low_Durability_Tools = false;
[TomlInlineComment("$ChatBot.AutoDig.Mode$")]
public ModeType Mode = ModeType.lookat;
[TomlPrecedingComment("$ChatBot.AutoDig.Locations$")]
public Coordination[] Locations = new Coordination[] { new(123.5, 64, 234.5), new(124.5, 63, 235.5) };
[TomlInlineComment("$ChatBot.AutoDig.Location_Order$")]
public OrderType Location_Order = OrderType.distance;
[TomlInlineComment("$ChatBot.AutoDig.Auto_Start_Delay$")]
public double Auto_Start_Delay = 3.0;
[TomlInlineComment("$ChatBot.AutoDig.Dig_Timeout$")]
public double Dig_Timeout = 60.0;
[TomlInlineComment("$ChatBot.AutoDig.Log_Block_Dig$")]
public bool Log_Block_Dig = true;
[TomlInlineComment("$ChatBot.AutoDig.List_Type$")]
public ListType List_Type = ListType.whitelist;
public List<Material> Blocks = new() { Material.Cobblestone, Material.Stone };
[NonSerialized]
public Location[] _Locations = Array.Empty<Location>();
public void OnSettingUpdate()
{
if (Auto_Start_Delay >= 0)
Auto_Start_Delay = Math.Max(0.1, Auto_Start_Delay);
if (Dig_Timeout >= 0)
Dig_Timeout = Math.Max(0.1, Dig_Timeout);
_Locations = new Location[Locations.Length];
for (int i = 0; i < Locations.Length; ++i)
_Locations[i] = new(Locations[i].x, Locations[i].y, Locations[i].z);
}
public enum ModeType { lookat, fixedpos, both };
public enum ListType { blacklist, whitelist };
public enum OrderType { distance, index };
public struct Coordination
{
public double x, y, z;
public Coordination(double x, double y, double z)
{
this.x = x; this.y = y; this.z = z;
}
}
}
private bool inventoryEnabled;
private int counter = 0;
private readonly object stateLock = new();
private State state = State.WaitJoinGame;
bool AlreadyWaitting = false;
private Location currentDig = Location.Zero;
private enum State
{
WaitJoinGame,
WaitingStart,
Digging,
Stopping,
}
public override void Initialize()
{
if (!GetTerrainEnabled())
{
LogToConsole(Translations.extra_terrainandmovement_required);
LogToConsole(Translations.general_bot_unload);
UnloadBot();
return;
}
inventoryEnabled = GetInventoryEnabled();
if (!inventoryEnabled && Config.Auto_Tool_Switch)
LogToConsole(Translations.bot_autodig_no_inv_handle);
McClient.dispatcher.Register(l => l.Literal("help")
.Then(l => l.Literal(CommandName)
.Executes(r => OnCommandHelp(r.Source, string.Empty))
.Then(l => l.Literal("start")
.Executes(r => OnCommandHelp(r.Source, "start")))
.Then(l => l.Literal("stop")
.Executes(r => OnCommandHelp(r.Source, "stop")))
.Then(l => l.Literal("help")
.Executes(r => OnCommandHelp(r.Source, "help")))
)
);
var cmd = McClient.dispatcher.Register(l => l.Literal(CommandName)
.Then(l => l.Literal("start")
.Executes(r => OnCommandStart(r.Source)))
.Then(l => l.Literal("stop")
.Executes(r => OnCommandStop(r.Source)))
.Then(l => l.Literal("_help")
.Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName)))
);
McClient.dispatcher.Register(l => l.Literal("digbot")
.Redirect(cmd)
);
}
public override void OnUnload()
{
McClient.dispatcher.Unregister("digbot");
McClient.dispatcher.Unregister(CommandName);
McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName);
}
private int OnCommandHelp(CmdResult r, string? cmd)
{
return r.SetAndReturn(cmd switch
{
#pragma warning disable format // @formatter:off
"start" => Translations.bot_autodig_help_start,
"stop" => Translations.bot_autodig_help_stop,
"help" => Translations.bot_autodig_help_help,
_ => string.Format(Translations.bot_autodig_available_cmd, "start, stop, help")
+ '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false),
#pragma warning restore format // @formatter:on
});
}
private int OnCommandStart(CmdResult r)
{
lock (stateLock)
{
counter = 0;
state = State.WaitingStart;
}
return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autodig_start);
}
private int OnCommandStop(CmdResult r)
{
StopDigging();
return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autodig_stop);
}
private void StartDigging()
{
if (Config.Auto_Start_Delay > 0)
{
double delay = Config.Auto_Start_Delay;
LogToConsole(string.Format(Translations.bot_autodig_start_delay, delay));
lock (stateLock)
{
counter = Settings.DoubleToTick(delay);
state = State.WaitingStart;
}
}
else
{
lock (stateLock)
{
state = State.WaitJoinGame;
}
}
}
private void StopDigging()
{
state = State.Stopping;
lock (stateLock)
{
state = State.Stopping;
}
}
public override void Update()
{
lock (stateLock)
{
switch (state)
{
case State.WaitJoinGame:
break;
case State.WaitingStart:
if (--counter < 0)
{
if (DoDigging())
{
AlreadyWaitting = false;
state = State.Digging;
}
else
{
counter = 0;
state = State.WaitingStart;
}
}
break;
case State.Digging:
if (++counter > Settings.DoubleToTick(Config.Dig_Timeout))
{
LogToConsole(GetTimestamp() + ": " + Translations.bot_autodig_dig_timeout);
state = State.WaitingStart;
counter = 0;
}
break;
case State.Stopping:
break;
}
}
}
private bool DoDigging()
{
if (Config.Mode == Configs.ModeType.lookat || Config.Mode == Configs.ModeType.both)
{
(bool hasBlock, Location blockLoc, Block block) = GetLookingBlock(4.5, false);
if (!hasBlock)
{
if (!AlreadyWaitting)
{
AlreadyWaitting = true;
if (Config.Log_Block_Dig)
LogToConsole(Translations.cmd_dig_too_far);
}
return false;
}
else if (block.Type == Material.Air)
{
if (!AlreadyWaitting)
{
AlreadyWaitting = true;
if (Config.Log_Block_Dig)
LogToConsole(Translations.cmd_dig_no_block);
}
return false;
}
else if ((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) ||
(Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type)))
{
if (Config.Mode == Configs.ModeType.lookat ||
(Config.Mode == Configs.ModeType.both && Config._Locations.Contains(blockLoc)))
{
if (DigBlock(blockLoc, lookAtBlock: false))
{
currentDig = blockLoc;
if (Config.Log_Block_Dig)
LogToConsole(string.Format(Translations.cmd_dig_dig, blockLoc.X, blockLoc.Y, blockLoc.Z, block.GetTypeString()));
return true;
}
else
{
LogToConsole(Translations.cmd_dig_fail);
return false;
}
}
else
{
if (!AlreadyWaitting)
{
AlreadyWaitting = true;
if (Config.Log_Block_Dig)
LogToConsole(Translations.bot_autodig_not_allow);
}
return false;
}
}
else
{
if (!AlreadyWaitting)
{
AlreadyWaitting = true;
if (Config.Log_Block_Dig)
LogToConsole(Translations.bot_autodig_not_allow);
}
return false;
}
}
else if (Config.Mode == Configs.ModeType.fixedpos && Config.Location_Order == Configs.OrderType.distance)
{
Location current = GetCurrentLocation();
double minDistance = double.MaxValue;
Location target = Location.Zero;
Block targetBlock = Block.Air;
foreach (Location location in Config._Locations)
{
Block block = GetWorld().GetBlock(location);
if (block.Type != Material.Air &&
((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) ||
(Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type))))
{
double distance = current.Distance(location);
if (distance < minDistance)
{
minDistance = distance;
target = location;
targetBlock = block;
}
}
}
if (minDistance <= 6.0)
{
if (DigBlock(target, lookAtBlock: true))
{
currentDig = target;
if (Config.Log_Block_Dig)
LogToConsole(string.Format(Translations.cmd_dig_dig, target.X, target.Y, target.Z, targetBlock.GetTypeString()));
return true;
}
else
{
LogToConsole(Translations.cmd_dig_fail);
return false;
}
}
else
{
if (!AlreadyWaitting)
{
AlreadyWaitting = true;
if (Config.Log_Block_Dig)
LogToConsole(Translations.cmd_dig_no_block);
}
return false;
}
}
else if (Config.Mode == Configs.ModeType.fixedpos && Config.Location_Order == Configs.OrderType.index)
{
for (int i = 0; i < Config._Locations.Length; ++i)
{
Location blockLoc = Config._Locations[i];
Block block = GetWorld().GetBlock(blockLoc);
if (block.Type != Material.Air &&
((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) ||
(Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type))))
{
if (DigBlock(blockLoc, lookAtBlock: true))
{
currentDig = blockLoc;
if (Config.Log_Block_Dig)
LogToConsole(string.Format(Translations.cmd_dig_dig, blockLoc.X, blockLoc.Y, blockLoc.Z, block.GetTypeString()));
return true;
}
else
{
LogToConsole(Translations.cmd_dig_fail);
return false;
}
}
}
if (!AlreadyWaitting)
{
AlreadyWaitting = true;
if (Config.Log_Block_Dig)
LogToConsole(Translations.cmd_dig_no_block);
}
return false;
}
return false;
}
public override void OnBlockChange(Location location, Block block)
{
if (location == currentDig)
{
lock (stateLock)
{
if (state == State.Digging && location == currentDig)
{
currentDig = Location.Zero;
counter = 0;
state = State.WaitingStart;
}
}
}
}
public override void AfterGameJoined()
{
StartDigging();
}
public override void OnRespawn()
{
StartDigging();
}
public override void OnDeath()
{
StopDigging();
}
public override bool OnDisconnect(DisconnectReason reason, string message)
{
StopDigging();
return base.OnDisconnect(reason, message);
}
}
}