diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs
index 961a9527..b767cdc2 100644
--- a/MinecraftClient/ChatBots/AutoFishing.cs
+++ b/MinecraftClient/ChatBots/AutoFishing.cs
@@ -13,61 +13,237 @@ namespace MinecraftClient.ChatBots
///
class AutoFishing : ChatBot
{
- private Entity fishingRod;
- private Double fishingHookThreshold = 0.2;
- private Location LastPos = new Location();
- private DateTime CaughtTime = DateTime.Now;
+ private int fishCount = 0;
private bool inventoryEnabled;
- private bool isFishing = false;
- private int useItemCounter = 0;
+ private int castTimeout = 12;
+
+ 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()
{
if (!GetEntityHandlingEnabled())
{
LogToConsoleTranslated("extra.entity_required");
- LogToConsoleTranslated("general.bot_unload");
- UnloadBot();
+ state = FishingState.WaitJoinGame;
}
inventoryEnabled = GetInventoryEnabled();
+ if (!inventoryEnabled)
+ LogToConsoleTranslated("bot.autoFish.no_inv_handle");
+ }
+
+ ///
+ /// Update settings when reloaded
+ ///
+ 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()
{
- if (useItemCounter > 0)
+ lock (stateLock)
{
- useItemCounter--;
- if (useItemCounter <= 0)
+ switch (state)
{
- 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)
{
- 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");
- fishingRod = entity;
+ fishingBobber = entity;
LastPos = entity.Location;
isFishing = true;
+
+ castTimeout = 24;
+ counter = 0;
+ state = FishingState.WaitingFishToBite;
}
}
}
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;
+
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)
{
- 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;
- Double Dx = LastPos.X - Pos.X;
- 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)
+ // prevent triggering multiple time
+ if ((DateTime.Now - CaughtTime).TotalSeconds > 1)
{
- if (Math.Abs(Dy) > fishingHookThreshold)
- {
- // caught
- // prevent triggering multiple time
- if ((DateTime.Now - CaughtTime).TotalSeconds > 1)
- {
- OnCaughtFish();
- CaughtTime = DateTime.Now;
- }
- }
+ isFishing = false;
+ CaughtTime = DateTime.Now;
+ OnCaughtFish();
}
- 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)
{
- fishingRod = null;
- LastPos = new Location();
+ StopFishing();
+
+ fishingBobber = null;
+ LastPos = Location.Zero;
CaughtTime = DateTime.Now;
- isFishing = false;
- useItemCounter = 0;
+
return base.OnDisconnect(reason, message);
}
@@ -118,43 +308,124 @@ namespace MinecraftClient.ChatBots
///
public void OnCaughtFish()
{
- LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.caught"));
- // retract fishing rod
- UseItemInHand();
- if (inventoryEnabled)
+ ++fishCount;
+ if (Settings.AutoFishing_Location != null)
+ LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.caught_at",
+ fishingBobber!.Location.X, fishingBobber!.Location.Y, fishingBobber!.Location.Z, fishCount));
+ else
+ LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.caught", fishCount));
+
+ lock (stateLock)
{
- if (!hasFishingRod())
- {
- LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.no_rod"));
- return;
- }
+ UseFishRod();
+
+ counter = 0;
+ state = FishingState.StartMove;
}
- // thread-safe
- useItemCounter = 8; // 800ms
}
- ///
- /// Check whether the player has a fishing rod in inventory
- ///
- /// TRUE if the player has a fishing rod
- public bool hasFishingRod()
+ private void UpdateLocation(double[,] locationList)
{
- if (!inventoryEnabled)
- return false;
- int start = 36;
- int end = 44;
- Inventory.Container container = GetPlayerInventory();
-
- foreach (KeyValuePair a in container.Items)
+ if (curLocationIdx >= locationList.GetLength(0))
{
- if (a.Key < start || a.Key > end)
- continue;
-
- if (a.Value.Type == ItemType.FishingRod)
- return true;
+ curLocationIdx = Math.Max(0, locationList.GetLength(0) - 2);
+ moveDir = -1;
+ }
+ else if (curLocationIdx < 0)
+ {
+ 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;
+ }
}
}
}
diff --git a/MinecraftClient/Commands/Move.cs b/MinecraftClient/Commands/Move.cs
index 1b4f85ee..da58e76b 100644
--- a/MinecraftClient/Commands/Move.cs
+++ b/MinecraftClient/Commands/Move.cs
@@ -74,8 +74,7 @@ namespace MinecraftClient.Commands
Location goal = Movement.Move(handler.GetCurrentLocation(), direction);
- ChunkColumn? chunkColumn = handler.GetWorld().GetChunkColumn(goal);
- if (chunkColumn == null || chunkColumn.FullyLoaded == false)
+ if (!Movement.CheckChunkLoading(handler.GetWorld(), handler.GetCurrentLocation(), goal))
return Translations.Get("cmd.move.chunk_not_loaded", goal.X, goal.Y, goal.Z);
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]);
Location goal = new(x, y, z);
- ChunkColumn? chunkColumn = handler.GetWorld().GetChunkColumn(goal);
- if (chunkColumn == null || chunkColumn.FullyLoaded == false)
+ if (!Movement.CheckChunkLoading(handler.GetWorld(), current, goal))
return Translations.Get("cmd.move.chunk_not_loaded", x, y, z);
if (takeRisk || Movement.PlayerFitsHere(handler.GetWorld(), goal))
diff --git a/MinecraftClient/Mapping/Movement.cs b/MinecraftClient/Mapping/Movement.cs
index 8389cdd7..21811c8d 100644
--- a/MinecraftClient/Mapping/Movement.cs
+++ b/MinecraftClient/Mapping/Movement.cs
@@ -643,5 +643,25 @@ namespace MinecraftClient.Mapping
throw new ArgumentException("Unknown direction", "direction");
}
}
+
+ ///
+ /// Check that the chunks at both the start and destination locations have been loaded
+ ///
+ /// Current world
+ /// Start location
+ /// Destination location
+ /// Is loading complete
+ 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;
+ }
}
}
diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs
index 3ef7fbf1..8ba106fa 100644
--- a/MinecraftClient/McClient.cs
+++ b/MinecraftClient/McClient.cs
@@ -1277,6 +1277,15 @@ namespace MinecraftClient
return InvokeOnMainThread(() => handler.SendUseItem(0, this.sequenceId));
}
+ ///
+ /// Use the item currently in the player's left hand
+ ///
+ /// TRUE if the item was successfully used
+ public bool UseItemOnLeftHand()
+ {
+ return InvokeOnMainThread(() => handler.SendUseItem(1, this.sequenceId));
+ }
+
///
/// Try to merge a slot
///
diff --git a/MinecraftClient/Resources/config/MinecraftClient.ini b/MinecraftClient/Resources/config/MinecraftClient.ini
index 332c00be..634004f8 100644
--- a/MinecraftClient/Resources/config/MinecraftClient.ini
+++ b/MinecraftClient/Resources/config/MinecraftClient.ini
@@ -207,6 +207,21 @@ interaction=Attack # Possible values: Interact, Attack (default)
# /!\ Make sure server rules allow automated farming before using this bot
enabled=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]
# Automatically eat food when your Hunger value is low
diff --git a/MinecraftClient/Resources/lang/en.ini b/MinecraftClient/Resources/lang/en.ini
index 1d0cc4ad..637c40bd 100644
--- a/MinecraftClient/Resources/lang/en.ini
+++ b/MinecraftClient/Resources/lang/en.ini
@@ -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_encrypt=§8Forge StartEncryption Handshake did not complete successfully
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.unknown_section={0}: Unknown setting section '{1}'
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}!
# AutoFish
-bot.autoFish.throw=Threw a fishing rod
-bot.autoFish.caught=Caught a fish!
-bot.autoFish.no_rod=No Fishing Rod on hand. Maybe broken?
+bot.autoFish.no_inv_handle=Inventory handling is not enabled. Cannot check rod durability and switch rods.
+bot.autoFish.start=Fishing will start in {0:0.0} second(s).
+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
bot.autoRelog.launch=Launching with {0} reconnection attempts
diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs
index 4fc59e1e..4d4ac0ac 100644
--- a/MinecraftClient/Scripting/ChatBot.cs
+++ b/MinecraftClient/Scripting/ChatBot.cs
@@ -1012,6 +1012,16 @@ namespace MinecraftClient
Handler.UpdateLocation(Handler.GetCurrentLocation(), location);
}
+ ///
+ /// Look at the specified location
+ ///
+ /// Yaw to look at
+ /// Pitch to look at
+ protected void LookAtLocation(float yaw, float pitch)
+ {
+ Handler.UpdateLocation(Handler.GetCurrentLocation(), yaw, pitch);
+ }
+
///
/// Get a Y-M-D h:m:s timestamp representing the current system date and time
///
@@ -1113,6 +1123,15 @@ namespace MinecraftClient
return Handler.GetUserUuidStr();
}
+ ///
+ /// Return the EntityID of the current player
+ ///
+ /// EntityID of the current player
+ protected int GetPlayerEntityID()
+ {
+ return Handler.GetPlayerEntityID();
+ }
+
///
/// Return the list of currently online players
///
@@ -1253,6 +1272,15 @@ namespace MinecraftClient
return Handler.UseItemOnHand();
}
+ ///
+ /// Use item currently in the player's hand (active inventory bar slot)
+ ///
+ /// TRUE if successful
+ protected bool UseItemInLeftHand()
+ {
+ return Handler.UseItemOnLeftHand();
+ }
+
///
/// Check inventory handling enable status
///
diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs
index db493953..254f7c31 100644
--- a/MinecraftClient/Settings.cs
+++ b/MinecraftClient/Settings.cs
@@ -203,6 +203,17 @@ namespace MinecraftClient
//Auto Fishing
public static bool AutoFishing_Enabled = 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
public static bool AutoEat_Enabled = false;
@@ -711,6 +722,17 @@ namespace MinecraftClient
{
case "enabled": AutoFishing_Enabled = 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;
@@ -863,9 +885,24 @@ namespace MinecraftClient
/// Float number
public static float str2float(string str)
{
- float f;
- if (float.TryParse(str.Trim(), out f))
- return f;
+ if (float.TryParse(str.Trim(), out float num))
+ return num;
+ else
+ {
+ ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2int", str));
+ return 0;
+ }
+ }
+
+ ///
+ /// Convert the specified string to a double number, defaulting to zero if invalid argument
+ ///
+ /// String to parse as a float number
+ /// Double number
+ public static double str2double(string str)
+ {
+ if (double.TryParse(str.Trim(), out double num))
+ return num;
else
{
ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2int", str));
@@ -886,6 +923,46 @@ namespace MinecraftClient
return str == "true" || str == "1";
}
+ ///
+ /// Convert the specified string to a list of location, returning null if invalid argument
+ ///
+ /// String to parse as a location list
+ /// Location list (null or double[*,5] or double[*,3] or double[*,2])
+ 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)]
public static string ToLowerIfNeed(string str)
{