mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Upgrade Auto Fishing
* Supports changing position and angle after catching fish * Support automatic rod switching
This commit is contained in:
commit
0907958ded
8 changed files with 510 additions and 82 deletions
|
|
@ -13,61 +13,237 @@ namespace MinecraftClient.ChatBots
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class AutoFishing : ChatBot
|
class AutoFishing : ChatBot
|
||||||
{
|
{
|
||||||
private Entity fishingRod;
|
private int fishCount = 0;
|
||||||
private Double fishingHookThreshold = 0.2;
|
|
||||||
private Location LastPos = new Location();
|
|
||||||
private DateTime CaughtTime = DateTime.Now;
|
|
||||||
private bool inventoryEnabled;
|
private bool inventoryEnabled;
|
||||||
private bool isFishing = false;
|
private int castTimeout = 12;
|
||||||
private int useItemCounter = 0;
|
|
||||||
|
private bool isFishing = false, isWaitingRod = false;
|
||||||
|
private Entity? fishingBobber;
|
||||||
|
private Location LastPos = Location.Zero;
|
||||||
|
private DateTime CaughtTime = DateTime.Now;
|
||||||
|
|
||||||
|
private int counter = 0;
|
||||||
|
private readonly object stateLock = new();
|
||||||
|
private FishingState state = FishingState.WaitJoinGame;
|
||||||
|
|
||||||
|
private int curLocationIdx = 0, moveDir = 1;
|
||||||
|
float nextYaw = 0, nextPitch = 0;
|
||||||
|
|
||||||
|
private enum FishingState
|
||||||
|
{
|
||||||
|
WaitJoinGame,
|
||||||
|
WaitingToCast,
|
||||||
|
CastingRod,
|
||||||
|
WaitingFishingBobber,
|
||||||
|
WaitingFishToBite,
|
||||||
|
StartMove,
|
||||||
|
WaitingMovement,
|
||||||
|
DurabilityCheck,
|
||||||
|
Stopping,
|
||||||
|
}
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
if (!GetEntityHandlingEnabled())
|
if (!GetEntityHandlingEnabled())
|
||||||
{
|
{
|
||||||
LogToConsoleTranslated("extra.entity_required");
|
LogToConsoleTranslated("extra.entity_required");
|
||||||
LogToConsoleTranslated("general.bot_unload");
|
state = FishingState.WaitJoinGame;
|
||||||
UnloadBot();
|
|
||||||
}
|
}
|
||||||
inventoryEnabled = GetInventoryEnabled();
|
inventoryEnabled = GetInventoryEnabled();
|
||||||
|
if (!inventoryEnabled)
|
||||||
|
LogToConsoleTranslated("bot.autoFish.no_inv_handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update settings when reloaded
|
||||||
|
/// </summary>
|
||||||
|
public /* override */ void OnSettingsReload()
|
||||||
|
{
|
||||||
|
if (Settings.AutoFishing_Enabled)
|
||||||
|
{
|
||||||
|
if (!GetEntityHandlingEnabled())
|
||||||
|
{
|
||||||
|
LogToConsoleTranslated("extra.entity_required");
|
||||||
|
state = FishingState.WaitJoinGame;
|
||||||
|
}
|
||||||
|
inventoryEnabled = GetInventoryEnabled();
|
||||||
|
if (!inventoryEnabled)
|
||||||
|
LogToConsoleTranslated("bot.autoFish.no_inv_handle");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UnloadBot();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartFishing()
|
||||||
|
{
|
||||||
|
isFishing = false;
|
||||||
|
if (Settings.AutoFishing_AutoStart)
|
||||||
|
{
|
||||||
|
double delay = Settings.AutoFishing_FishingDelay;
|
||||||
|
LogToConsole(Translations.Get("bot.autoFish.start", delay));
|
||||||
|
lock (stateLock)
|
||||||
|
{
|
||||||
|
counter = (int)(delay * 10);
|
||||||
|
state = FishingState.StartMove;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lock (stateLock)
|
||||||
|
{
|
||||||
|
state = FishingState.WaitJoinGame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopFishing()
|
||||||
|
{
|
||||||
|
isFishing = false;
|
||||||
|
lock (stateLock)
|
||||||
|
{
|
||||||
|
state = FishingState.Stopping;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UseFishRod()
|
||||||
|
{
|
||||||
|
if (Settings.AutoFishing_Mainhand)
|
||||||
|
UseItemInHand();
|
||||||
|
else
|
||||||
|
UseItemInLeftHand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
if (useItemCounter > 0)
|
lock (stateLock)
|
||||||
{
|
{
|
||||||
useItemCounter--;
|
switch (state)
|
||||||
if (useItemCounter <= 0)
|
|
||||||
{
|
{
|
||||||
UseItemInHand();
|
case FishingState.WaitJoinGame:
|
||||||
|
break;
|
||||||
|
case FishingState.WaitingToCast:
|
||||||
|
if (AutoEat.Eating)
|
||||||
|
counter = (int)(Settings.AutoFishing_CastDelay * 10);
|
||||||
|
else if (--counter < 0)
|
||||||
|
state = FishingState.CastingRod;
|
||||||
|
break;
|
||||||
|
case FishingState.CastingRod:
|
||||||
|
UseFishRod();
|
||||||
|
counter = 0;
|
||||||
|
state = FishingState.WaitingFishingBobber;
|
||||||
|
break;
|
||||||
|
case FishingState.WaitingFishingBobber:
|
||||||
|
if (++counter > castTimeout)
|
||||||
|
{
|
||||||
|
if (castTimeout < 6000)
|
||||||
|
castTimeout *= 2; // Exponential backoff
|
||||||
|
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.cast_timeout", castTimeout / 10.0));
|
||||||
|
|
||||||
|
counter = (int)(Settings.AutoFishing_CastDelay * 10);
|
||||||
|
state = FishingState.WaitingToCast;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FishingState.WaitingFishToBite:
|
||||||
|
if (++counter > (int)(Settings.AutoFishing_FishingTimeout * 10))
|
||||||
|
{
|
||||||
|
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.fishing_timeout"));
|
||||||
|
|
||||||
|
counter = (int)(Settings.AutoFishing_CastDelay * 10);
|
||||||
|
state = FishingState.WaitingToCast;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FishingState.StartMove:
|
||||||
|
if (--counter < 0)
|
||||||
|
{
|
||||||
|
double[,]? locationList = Settings.AutoFishing_Location;
|
||||||
|
if (locationList != null)
|
||||||
|
{
|
||||||
|
if (GetTerrainEnabled())
|
||||||
|
{
|
||||||
|
UpdateLocation(locationList);
|
||||||
|
state = FishingState.WaitingMovement;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogToConsole(Translations.Get("extra.terrainandmovement_required"));
|
||||||
|
state = FishingState.WaitJoinGame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
counter = (int)(Settings.AutoFishing_CastDelay * 10);
|
||||||
|
state = FishingState.DurabilityCheck;
|
||||||
|
goto case FishingState.DurabilityCheck;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FishingState.WaitingMovement:
|
||||||
|
if (!ClientIsMoving())
|
||||||
|
{
|
||||||
|
LookAtLocation(nextYaw, nextPitch);
|
||||||
|
LogToConsole(Translations.Get("bot.autoFish.update_lookat", nextYaw, nextPitch));
|
||||||
|
|
||||||
|
state = FishingState.DurabilityCheck;
|
||||||
|
goto case FishingState.DurabilityCheck;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FishingState.DurabilityCheck:
|
||||||
|
if (DurabilityCheck())
|
||||||
|
{
|
||||||
|
counter = (int)(Settings.AutoFishing_CastDelay * 10);
|
||||||
|
state = FishingState.WaitingToCast;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FishingState.Stopping:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnEntitySpawn(Entity entity)
|
public override void OnEntitySpawn(Entity entity)
|
||||||
{
|
{
|
||||||
if (entity.Type == EntityType.FishingBobber)
|
if (entity.Type == EntityType.FishingBobber && entity.ObjectData == GetPlayerEntityID())
|
||||||
{
|
{
|
||||||
if (GetCurrentLocation().Distance(entity.Location) < 2 && !isFishing)
|
if (Settings.AutoFishing_LogFishingBobber)
|
||||||
|
LogToConsole(string.Format("FishingBobber spawn at {0}, distance = {1:0.00}", entity.Location, GetCurrentLocation().Distance(entity.Location)));
|
||||||
|
|
||||||
|
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.throw"));
|
||||||
|
lock (stateLock)
|
||||||
{
|
{
|
||||||
LogToConsoleTranslated("bot.autoFish.throw");
|
fishingBobber = entity;
|
||||||
fishingRod = entity;
|
|
||||||
LastPos = entity.Location;
|
LastPos = entity.Location;
|
||||||
isFishing = true;
|
isFishing = true;
|
||||||
|
|
||||||
|
castTimeout = 24;
|
||||||
|
counter = 0;
|
||||||
|
state = FishingState.WaitingFishToBite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnEntityDespawn(Entity entity)
|
public override void OnEntityDespawn(Entity entity)
|
||||||
{
|
{
|
||||||
if (entity.Type == EntityType.FishingBobber)
|
if (entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID)
|
||||||
{
|
{
|
||||||
if(entity.ID == fishingRod.ID)
|
if (Settings.AutoFishing_LogFishingBobber)
|
||||||
|
LogToConsole(string.Format("FishingBobber despawn at {0}", entity.Location));
|
||||||
|
|
||||||
|
if (isFishing)
|
||||||
{
|
{
|
||||||
isFishing = false;
|
isFishing = false;
|
||||||
|
|
||||||
if (Settings.AutoFishing_Antidespawn)
|
if (Settings.AutoFishing_Antidespawn)
|
||||||
{
|
{
|
||||||
useItemCounter = 5; // 500ms
|
LogToConsoleTranslated("bot.autoFish.despawn");
|
||||||
|
|
||||||
|
lock (stateLock)
|
||||||
|
{
|
||||||
|
counter = (int)(Settings.AutoFishing_CastDelay * 10);
|
||||||
|
state = FishingState.WaitingToCast;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -75,41 +251,55 @@ namespace MinecraftClient.ChatBots
|
||||||
|
|
||||||
public override void OnEntityMove(Entity entity)
|
public override void OnEntityMove(Entity entity)
|
||||||
{
|
{
|
||||||
if (isFishing)
|
if (isFishing && fishingBobber!.ID == entity.ID)
|
||||||
{
|
{
|
||||||
if (fishingRod.ID == entity.ID)
|
Location Pos = entity.Location;
|
||||||
|
double Dx = LastPos.X - Pos.X;
|
||||||
|
double Dy = LastPos.Y - Pos.Y;
|
||||||
|
double Dz = LastPos.Z - Pos.Z;
|
||||||
|
LastPos = Pos;
|
||||||
|
|
||||||
|
if (Settings.AutoFishing_LogFishingBobber)
|
||||||
|
LogToConsole(string.Format("FishingBobber {0} Dx={1:0.000000} Dy={2:0.000000} Dz={3:0.000000}", Pos, Dx, Math.Abs(Dy), Dz));
|
||||||
|
|
||||||
|
if (Math.Abs(Dx) < Math.Abs(Settings.AutoFishing_StationaryThreshold) &&
|
||||||
|
Math.Abs(Dz) < Math.Abs(Settings.AutoFishing_StationaryThreshold) &&
|
||||||
|
Math.Abs(Dy) > Math.Abs(Settings.AutoFishing_HookThreshold))
|
||||||
{
|
{
|
||||||
Location Pos = entity.Location;
|
// prevent triggering multiple time
|
||||||
Double Dx = LastPos.X - Pos.X;
|
if ((DateTime.Now - CaughtTime).TotalSeconds > 1)
|
||||||
Double Dy = LastPos.Y - Pos.Y;
|
|
||||||
Double Dz = LastPos.Z - Pos.Z;
|
|
||||||
LastPos = Pos;
|
|
||||||
// check if fishing hook is stationary
|
|
||||||
if (Dx == 0 && Dz == 0)
|
|
||||||
{
|
{
|
||||||
if (Math.Abs(Dy) > fishingHookThreshold)
|
isFishing = false;
|
||||||
{
|
CaughtTime = DateTime.Now;
|
||||||
// caught
|
OnCaughtFish();
|
||||||
// prevent triggering multiple time
|
|
||||||
if ((DateTime.Now - CaughtTime).TotalSeconds > 1)
|
|
||||||
{
|
|
||||||
OnCaughtFish();
|
|
||||||
CaughtTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fishingRod = entity;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void AfterGameJoined()
|
||||||
|
{
|
||||||
|
StartFishing();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnRespawn()
|
||||||
|
{
|
||||||
|
StartFishing();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnDeath()
|
||||||
|
{
|
||||||
|
StopFishing();
|
||||||
|
}
|
||||||
|
|
||||||
public override bool OnDisconnect(DisconnectReason reason, string message)
|
public override bool OnDisconnect(DisconnectReason reason, string message)
|
||||||
{
|
{
|
||||||
fishingRod = null;
|
StopFishing();
|
||||||
LastPos = new Location();
|
|
||||||
|
fishingBobber = null;
|
||||||
|
LastPos = Location.Zero;
|
||||||
CaughtTime = DateTime.Now;
|
CaughtTime = DateTime.Now;
|
||||||
isFishing = false;
|
|
||||||
useItemCounter = 0;
|
|
||||||
return base.OnDisconnect(reason, message);
|
return base.OnDisconnect(reason, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,43 +308,124 @@ namespace MinecraftClient.ChatBots
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void OnCaughtFish()
|
public void OnCaughtFish()
|
||||||
{
|
{
|
||||||
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.caught"));
|
++fishCount;
|
||||||
// retract fishing rod
|
if (Settings.AutoFishing_Location != null)
|
||||||
UseItemInHand();
|
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.caught_at",
|
||||||
if (inventoryEnabled)
|
fishingBobber!.Location.X, fishingBobber!.Location.Y, fishingBobber!.Location.Z, fishCount));
|
||||||
|
else
|
||||||
|
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.caught", fishCount));
|
||||||
|
|
||||||
|
lock (stateLock)
|
||||||
{
|
{
|
||||||
if (!hasFishingRod())
|
UseFishRod();
|
||||||
{
|
|
||||||
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.no_rod"));
|
counter = 0;
|
||||||
return;
|
state = FishingState.StartMove;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// thread-safe
|
|
||||||
useItemCounter = 8; // 800ms
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void UpdateLocation(double[,] locationList)
|
||||||
/// Check whether the player has a fishing rod in inventory
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>TRUE if the player has a fishing rod</returns>
|
|
||||||
public bool hasFishingRod()
|
|
||||||
{
|
{
|
||||||
if (!inventoryEnabled)
|
if (curLocationIdx >= locationList.GetLength(0))
|
||||||
return false;
|
|
||||||
int start = 36;
|
|
||||||
int end = 44;
|
|
||||||
Inventory.Container container = GetPlayerInventory();
|
|
||||||
|
|
||||||
foreach (KeyValuePair<int, Item> a in container.Items)
|
|
||||||
{
|
{
|
||||||
if (a.Key < start || a.Key > end)
|
curLocationIdx = Math.Max(0, locationList.GetLength(0) - 2);
|
||||||
continue;
|
moveDir = -1;
|
||||||
|
}
|
||||||
if (a.Value.Type == ItemType.FishingRod)
|
else if (curLocationIdx < 0)
|
||||||
return true;
|
{
|
||||||
|
curLocationIdx = Math.Min(locationList.GetLength(0) - 1, 1);
|
||||||
|
moveDir = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
int locationType = locationList.GetLength(1);
|
||||||
|
|
||||||
|
if (locationType == 2)
|
||||||
|
{
|
||||||
|
nextYaw = (float)locationList[curLocationIdx, 0];
|
||||||
|
nextPitch = (float)locationList[curLocationIdx, 1];
|
||||||
|
}
|
||||||
|
else if (locationType == 3)
|
||||||
|
{
|
||||||
|
nextYaw = GetYaw();
|
||||||
|
nextPitch = GetPitch();
|
||||||
|
}
|
||||||
|
else if (locationType == 5)
|
||||||
|
{
|
||||||
|
nextYaw = (float)locationList[curLocationIdx, 3];
|
||||||
|
nextPitch = (float)locationList[curLocationIdx, 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locationType == 3 || locationType == 5)
|
||||||
|
{
|
||||||
|
Location current = GetCurrentLocation();
|
||||||
|
Location goal = new(locationList[curLocationIdx, 0], locationList[curLocationIdx, 1], locationList[curLocationIdx, 2]);
|
||||||
|
|
||||||
|
bool isMoveSuccessed;
|
||||||
|
if (!Movement.CheckChunkLoading(GetWorld(), current, goal))
|
||||||
|
{
|
||||||
|
LogToConsole(Translations.Get("cmd.move.chunk_not_loaded", goal.X, goal.Y, goal.Z));
|
||||||
|
isMoveSuccessed = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isMoveSuccessed = MoveToLocation(goal, allowUnsafe: false, allowDirectTeleport: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isMoveSuccessed)
|
||||||
|
{
|
||||||
|
nextYaw = GetYaw();
|
||||||
|
nextPitch = GetPitch();
|
||||||
|
LogToConsole(Translations.Get("cmd.move.fail", goal));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogToConsole(Translations.Get("cmd.move.walk", goal, current));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curLocationIdx += moveDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DurabilityCheck()
|
||||||
|
{
|
||||||
|
if (!inventoryEnabled)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
bool useMainHand = Settings.AutoFishing_Mainhand;
|
||||||
|
Container container = GetPlayerInventory();
|
||||||
|
|
||||||
|
int itemSolt = useMainHand ? GetCurrentSlot() + 36 : 45;
|
||||||
|
|
||||||
|
if (container.Items.TryGetValue(itemSolt, out Item? handItem) &&
|
||||||
|
handItem.Type == ItemType.FishingRod && (64 - handItem.Damage) >= Settings.AutoFishing_DurabilityLimit)
|
||||||
|
{
|
||||||
|
isWaitingRod = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!isWaitingRod)
|
||||||
|
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.no_rod"));
|
||||||
|
|
||||||
|
if (Settings.AutoFishing_AutoRodSwitch)
|
||||||
|
{
|
||||||
|
foreach ((int slot, Item item) in container.Items)
|
||||||
|
{
|
||||||
|
if (item.Type == ItemType.FishingRod && (64 - item.Damage) >= Settings.AutoFishing_DurabilityLimit)
|
||||||
|
{
|
||||||
|
WindowAction(0, slot, WindowActionType.LeftClick);
|
||||||
|
WindowAction(0, itemSolt, WindowActionType.LeftClick);
|
||||||
|
WindowAction(0, slot, WindowActionType.LeftClick);
|
||||||
|
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.switch", slot, (64 - item.Damage)));
|
||||||
|
isWaitingRod = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isWaitingRod = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,7 @@ namespace MinecraftClient.Commands
|
||||||
|
|
||||||
Location goal = Movement.Move(handler.GetCurrentLocation(), direction);
|
Location goal = Movement.Move(handler.GetCurrentLocation(), direction);
|
||||||
|
|
||||||
ChunkColumn? chunkColumn = handler.GetWorld().GetChunkColumn(goal);
|
if (!Movement.CheckChunkLoading(handler.GetWorld(), handler.GetCurrentLocation(), goal))
|
||||||
if (chunkColumn == null || chunkColumn.FullyLoaded == false)
|
|
||||||
return Translations.Get("cmd.move.chunk_not_loaded", goal.X, goal.Y, goal.Z);
|
return Translations.Get("cmd.move.chunk_not_loaded", goal.X, goal.Y, goal.Z);
|
||||||
|
|
||||||
if (Movement.CanMove(handler.GetWorld(), handler.GetCurrentLocation(), direction))
|
if (Movement.CanMove(handler.GetWorld(), handler.GetCurrentLocation(), direction))
|
||||||
|
|
@ -98,8 +97,7 @@ namespace MinecraftClient.Commands
|
||||||
double z = args[2].StartsWith('~') ? current.Z + (args[2].Length > 1 ? double.Parse(args[2][1..]) : 0) : double.Parse(args[2]);
|
double z = args[2].StartsWith('~') ? current.Z + (args[2].Length > 1 ? double.Parse(args[2][1..]) : 0) : double.Parse(args[2]);
|
||||||
Location goal = new(x, y, z);
|
Location goal = new(x, y, z);
|
||||||
|
|
||||||
ChunkColumn? chunkColumn = handler.GetWorld().GetChunkColumn(goal);
|
if (!Movement.CheckChunkLoading(handler.GetWorld(), current, goal))
|
||||||
if (chunkColumn == null || chunkColumn.FullyLoaded == false)
|
|
||||||
return Translations.Get("cmd.move.chunk_not_loaded", x, y, z);
|
return Translations.Get("cmd.move.chunk_not_loaded", x, y, z);
|
||||||
|
|
||||||
if (takeRisk || Movement.PlayerFitsHere(handler.GetWorld(), goal))
|
if (takeRisk || Movement.PlayerFitsHere(handler.GetWorld(), goal))
|
||||||
|
|
|
||||||
|
|
@ -643,5 +643,25 @@ namespace MinecraftClient.Mapping
|
||||||
throw new ArgumentException("Unknown direction", "direction");
|
throw new ArgumentException("Unknown direction", "direction");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check that the chunks at both the start and destination locations have been loaded
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="world">Current world</param>
|
||||||
|
/// <param name="start">Start location</param>
|
||||||
|
/// <param name="dest">Destination location</param>
|
||||||
|
/// <returns>Is loading complete</returns>
|
||||||
|
public static bool CheckChunkLoading(World world, Location start, Location dest)
|
||||||
|
{
|
||||||
|
ChunkColumn? chunkColumn = world.GetChunkColumn(dest);
|
||||||
|
if (chunkColumn == null || chunkColumn.FullyLoaded == false)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
chunkColumn = world.GetChunkColumn(start);
|
||||||
|
if (chunkColumn == null || chunkColumn.FullyLoaded == false)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1277,6 +1277,15 @@ namespace MinecraftClient
|
||||||
return InvokeOnMainThread(() => handler.SendUseItem(0, this.sequenceId));
|
return InvokeOnMainThread(() => handler.SendUseItem(0, this.sequenceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use the item currently in the player's left hand
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>TRUE if the item was successfully used</returns>
|
||||||
|
public bool UseItemOnLeftHand()
|
||||||
|
{
|
||||||
|
return InvokeOnMainThread(() => handler.SendUseItem(1, this.sequenceId));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Try to merge a slot
|
/// Try to merge a slot
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,21 @@ interaction=Attack # Possible values: Interact, Attack (default)
|
||||||
# /!\ Make sure server rules allow automated farming before using this bot
|
# /!\ Make sure server rules allow automated farming before using this bot
|
||||||
enabled=false
|
enabled=false
|
||||||
antidespawn=false
|
antidespawn=false
|
||||||
|
main_hand=true # Use the main hand or the second hand to hold the rod.
|
||||||
|
auto_start=true # Whether to start fishing automatically after entering a world.
|
||||||
|
cast_delay=0.4 # How soon to re-cast after successful fishing.
|
||||||
|
fishing_delay=3.0 # How long after entering the game to start fishing (seconds).
|
||||||
|
fishing_timeout=300.0 # Fishing timeout (seconds). Timeout will trigger a re-cast.
|
||||||
|
durability_limit=2 # Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature.
|
||||||
|
auto_rod_switch=true # Switch to a new rod from inventory after the current rod is unavailable.
|
||||||
|
stationary_threshold=0.001 # Hooks moving in the X and Z axes below this threshold will be considered stationary.
|
||||||
|
hook_threshold=0.2 # A stationary hook moving on the Y-axis above this threshold will be considered to have caught a fish.
|
||||||
|
log_fishing_bobber=false # For debugging purposes, you can use this log to adjust the two thresholds mentioned above.
|
||||||
|
location= # Some plugins do not allow the player to fish in one place. This allows the player to change position/angle after each fish caught.
|
||||||
|
# Floating point numbers can be used for both coordinates and angles. Leave blank to disable this function.
|
||||||
|
# Change the angle only (recommended): location=yaw_1, pitch_1; yaw_2, pitch_2; ...; yaw_n, pitch_n
|
||||||
|
# Change position only: location=x1, y1, z1; x2, y2, z2; ...; xn, yn, zn
|
||||||
|
# Change both angle and position: location=x1, y1, z1, yaw_1, pitch_1; x2, y2, z2, yaw_2, pitch_2; ... ;xn, yn, zn, yaw_n, pitch_n
|
||||||
|
|
||||||
[AutoEat]
|
[AutoEat]
|
||||||
# Automatically eat food when your Hunger value is low
|
# Automatically eat food when your Hunger value is low
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ error.connection_timeout=§8A timeout occured while attempting to connect to thi
|
||||||
error.forge=§8Forge Login Handshake did not complete successfully
|
error.forge=§8Forge Login Handshake did not complete successfully
|
||||||
error.forge_encrypt=§8Forge StartEncryption Handshake did not complete successfully
|
error.forge_encrypt=§8Forge StartEncryption Handshake did not complete successfully
|
||||||
error.setting.str2int=Failed to convert '{0}' into an integer. Please check your settings.
|
error.setting.str2int=Failed to convert '{0}' into an integer. Please check your settings.
|
||||||
|
error.setting.str2locationList.convert_fail=Failed to convert '{0}' to a floating point number. Please check your settings.
|
||||||
|
error.setting.str2locationList.format_err=Wrong format, can't parse '{0}' into position data.. Please check your settings.
|
||||||
error.setting.argument_syntax={0}: Invalid syntax, expecting --argname=value or --section.argname=value
|
error.setting.argument_syntax={0}: Invalid syntax, expecting --argname=value or --section.argname=value
|
||||||
error.setting.unknown_section={0}: Unknown setting section '{1}'
|
error.setting.unknown_section={0}: Unknown setting section '{1}'
|
||||||
error.setting.unknown_or_invalid={0}: Unknown setting or invalid value
|
error.setting.unknown_or_invalid={0}: Unknown setting or invalid value
|
||||||
|
|
@ -447,9 +449,17 @@ bot.autoDrop.no_mode=Cannot read drop mode from config. Using include mode.
|
||||||
bot.autoDrop.no_inventory=Cannot find inventory {0}!
|
bot.autoDrop.no_inventory=Cannot find inventory {0}!
|
||||||
|
|
||||||
# AutoFish
|
# AutoFish
|
||||||
bot.autoFish.throw=Threw a fishing rod
|
bot.autoFish.no_inv_handle=Inventory handling is not enabled. Cannot check rod durability and switch rods.
|
||||||
bot.autoFish.caught=Caught a fish!
|
bot.autoFish.start=Fishing will start in {0:0.0} second(s).
|
||||||
bot.autoFish.no_rod=No Fishing Rod on hand. Maybe broken?
|
bot.autoFish.throw=Casting successfully.
|
||||||
|
bot.autoFish.caught=Caught a fish! (Count: {0})
|
||||||
|
bot.autoFish.caught_at=Caught a fish at ({0:0.0},{1:0.0},{2:0.0})! (Count: {3})
|
||||||
|
bot.autoFish.no_rod=Current fishing rod is not available. Maybe broken or low durability?
|
||||||
|
bot.autoFish.despawn=Fish floating despawn, will re-cast.
|
||||||
|
bot.autoFish.fishing_timeout=Fishing timeout, will soon re-cast.
|
||||||
|
bot.autoFish.cast_timeout=Casting timeout and will soon retry. (Timeout increased to {0:0.0} sec).
|
||||||
|
bot.autoFish.update_lookat=Update yaw = {0:0.00}, pitch = {1:0.00}.
|
||||||
|
bot.autoFish.switch=Switch to the rod in slot {0}, durability {1}/64.
|
||||||
|
|
||||||
# AutoRelog
|
# AutoRelog
|
||||||
bot.autoRelog.launch=Launching with {0} reconnection attempts
|
bot.autoRelog.launch=Launching with {0} reconnection attempts
|
||||||
|
|
|
||||||
|
|
@ -1012,6 +1012,16 @@ namespace MinecraftClient
|
||||||
Handler.UpdateLocation(Handler.GetCurrentLocation(), location);
|
Handler.UpdateLocation(Handler.GetCurrentLocation(), location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Look at the specified location
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="yaw">Yaw to look at</param>
|
||||||
|
/// <param name="pitch">Pitch to look at</param>
|
||||||
|
protected void LookAtLocation(float yaw, float pitch)
|
||||||
|
{
|
||||||
|
Handler.UpdateLocation(Handler.GetCurrentLocation(), yaw, pitch);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a Y-M-D h:m:s timestamp representing the current system date and time
|
/// Get a Y-M-D h:m:s timestamp representing the current system date and time
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -1113,6 +1123,15 @@ namespace MinecraftClient
|
||||||
return Handler.GetUserUuidStr();
|
return Handler.GetUserUuidStr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the EntityID of the current player
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>EntityID of the current player</returns>
|
||||||
|
protected int GetPlayerEntityID()
|
||||||
|
{
|
||||||
|
return Handler.GetPlayerEntityID();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return the list of currently online players
|
/// Return the list of currently online players
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -1253,6 +1272,15 @@ namespace MinecraftClient
|
||||||
return Handler.UseItemOnHand();
|
return Handler.UseItemOnHand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use item currently in the player's hand (active inventory bar slot)
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>TRUE if successful</returns>
|
||||||
|
protected bool UseItemInLeftHand()
|
||||||
|
{
|
||||||
|
return Handler.UseItemOnLeftHand();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check inventory handling enable status
|
/// Check inventory handling enable status
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,17 @@ namespace MinecraftClient
|
||||||
//Auto Fishing
|
//Auto Fishing
|
||||||
public static bool AutoFishing_Enabled = false;
|
public static bool AutoFishing_Enabled = false;
|
||||||
public static bool AutoFishing_Antidespawn = false;
|
public static bool AutoFishing_Antidespawn = false;
|
||||||
|
public static bool AutoFishing_Mainhand = true;
|
||||||
|
public static bool AutoFishing_AutoStart = true;
|
||||||
|
public static double AutoFishing_CastDelay = 0.4;
|
||||||
|
public static double AutoFishing_FishingDelay = 3.0;
|
||||||
|
public static double AutoFishing_FishingTimeout = 300.0;
|
||||||
|
public static double AutoFishing_DurabilityLimit = 2;
|
||||||
|
public static bool AutoFishing_AutoRodSwitch = true;
|
||||||
|
public static double AutoFishing_StationaryThreshold = 0.001;
|
||||||
|
public static double AutoFishing_HookThreshold = 0.2;
|
||||||
|
public static bool AutoFishing_LogFishingBobber = false;
|
||||||
|
public static double[,]? AutoFishing_Location = null;
|
||||||
|
|
||||||
//Auto Eating
|
//Auto Eating
|
||||||
public static bool AutoEat_Enabled = false;
|
public static bool AutoEat_Enabled = false;
|
||||||
|
|
@ -711,6 +722,17 @@ namespace MinecraftClient
|
||||||
{
|
{
|
||||||
case "enabled": AutoFishing_Enabled = str2bool(argValue); return true;
|
case "enabled": AutoFishing_Enabled = str2bool(argValue); return true;
|
||||||
case "antidespawn": AutoFishing_Antidespawn = str2bool(argValue); return true;
|
case "antidespawn": AutoFishing_Antidespawn = str2bool(argValue); return true;
|
||||||
|
case "main_hand": AutoFishing_Mainhand = str2bool(argValue); return true;
|
||||||
|
case "auto_start": AutoFishing_AutoStart = str2bool(argValue); return true;
|
||||||
|
case "cast_delay": AutoFishing_CastDelay = str2double(argValue); return true;
|
||||||
|
case "fishing_delay": AutoFishing_FishingDelay = str2double(argValue); return true;
|
||||||
|
case "fishing_timeout": AutoFishing_FishingTimeout = str2double(argValue); return true;
|
||||||
|
case "durability_limit": AutoFishing_DurabilityLimit = str2int(argValue); return true;
|
||||||
|
case "auto_rod_switch": AutoFishing_AutoRodSwitch = str2bool(argValue); return true;
|
||||||
|
case "stationary_threshold": AutoFishing_StationaryThreshold = str2double(argValue); return true;
|
||||||
|
case "hook_threshold": AutoFishing_HookThreshold = str2double(argValue); return true;
|
||||||
|
case "log_fishing_bobber": AutoFishing_LogFishingBobber = str2bool(argValue); return true;
|
||||||
|
case "location": AutoFishing_Location = str2locationList(argValue); return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -863,9 +885,24 @@ namespace MinecraftClient
|
||||||
/// <returns>Float number</returns>
|
/// <returns>Float number</returns>
|
||||||
public static float str2float(string str)
|
public static float str2float(string str)
|
||||||
{
|
{
|
||||||
float f;
|
if (float.TryParse(str.Trim(), out float num))
|
||||||
if (float.TryParse(str.Trim(), out f))
|
return num;
|
||||||
return f;
|
else
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2int", str));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert the specified string to a double number, defaulting to zero if invalid argument
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">String to parse as a float number</param>
|
||||||
|
/// <returns>Double number</returns>
|
||||||
|
public static double str2double(string str)
|
||||||
|
{
|
||||||
|
if (double.TryParse(str.Trim(), out double num))
|
||||||
|
return num;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2int", str));
|
ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2int", str));
|
||||||
|
|
@ -886,6 +923,46 @@ namespace MinecraftClient
|
||||||
return str == "true" || str == "1";
|
return str == "true" || str == "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert the specified string to a list of location, returning null if invalid argument
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">String to parse as a location list</param>
|
||||||
|
/// <returns>Location list (null or double[*,5] or double[*,3] or double[*,2])</returns>
|
||||||
|
public static double[,]? str2locationList(string str)
|
||||||
|
{
|
||||||
|
string[] locationStrList = str.Split(';', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
double[,]? res = null;
|
||||||
|
int codLen = 0;
|
||||||
|
for (int i = 0; i < locationStrList.Length; ++i)
|
||||||
|
{
|
||||||
|
string[] coordinates_str_list = locationStrList[i].Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||||
|
int curCodLen = coordinates_str_list.Length;
|
||||||
|
if ((curCodLen == 2 || curCodLen == 3 || curCodLen == 5) && (i == 0 || curCodLen == codLen))
|
||||||
|
{
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
res = new double[locationStrList.Length, curCodLen];
|
||||||
|
codLen = curCodLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < curCodLen; ++j)
|
||||||
|
{
|
||||||
|
if (!double.TryParse(coordinates_str_list[j], out res![i, j]))
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2locationList.convert_fail", coordinates_str_list[j]));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2locationList.format_err", locationStrList[i]));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static string ToLowerIfNeed(string str)
|
public static string ToLowerIfNeed(string str)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue