diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs
index 61cf5be1..38001e6c 100644
--- a/MinecraftClient/ChatBots/AutoFishing.cs
+++ b/MinecraftClient/ChatBots/AutoFishing.cs
@@ -203,7 +203,7 @@ namespace MinecraftClient.ChatBots
public override void OnEntityDespawn(Entity entity)
{
- if (entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID)
+ if (entity != null && entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID)
{
if (Settings.AutoFishing_LogFishingBobber)
LogToConsole(string.Format("FishingBobber despawn at {0}", entity.Location));
@@ -228,7 +228,7 @@ namespace MinecraftClient.ChatBots
public override void OnEntityMove(Entity entity)
{
- if (isFishing && fishingBobber!.ID == entity.ID)
+ if (isFishing && entity != null && fishingBobber!.ID == entity.ID)
{
Location Pos = entity.Location;
double Dx = LastPos.X - Pos.X;
diff --git a/MinecraftClient/Commands/Dig.cs b/MinecraftClient/Commands/Dig.cs
index eadf13db..6f297720 100644
--- a/MinecraftClient/Commands/Dig.cs
+++ b/MinecraftClient/Commands/Dig.cs
@@ -17,28 +17,41 @@ namespace MinecraftClient.Commands
if (!handler.GetTerrainEnabled())
return Translations.Get("extra.terrainandmovement_required");
- if (hasArg(command))
+ string[] args = getArgs(command);
+ if (args.Length == 0)
{
- string[] args = getArgs(command);
- if (args.Length == 3)
- {
- try
- {
- Location current = handler.GetCurrentLocation();
- Location blockToBreak = Location.Parse(current, args[0], args[1], args[2]);
- if (blockToBreak.DistanceSquared(current.EyesLocation()) > 25)
- return Translations.Get("cmd.dig.too_far");
- if (handler.GetWorld().GetBlock(blockToBreak).Type == Material.Air)
- return Translations.Get("cmd.dig.no_block");
- if (handler.DigBlock(blockToBreak))
- return Translations.Get("cmd.dig.dig", blockToBreak.X, blockToBreak.Y, blockToBreak.Z);
- else return "cmd.dig.fail";
- }
- catch (FormatException) { return GetCmdDescTranslated(); }
- }
- else return GetCmdDescTranslated();
+ (bool hasBlock, Location blockLoc, Block block) = RaycastHelper.RaycastBlock(handler, 4.5, false);
+ if (!hasBlock)
+ return Translations.Get("cmd.dig.too_far");
+ else if (block.Type == Material.Air)
+ return Translations.Get("cmd.dig.no_block");
+ else if (handler.DigBlock(blockLoc, lookAtBlock: false))
+ return Translations.Get("cmd.dig.dig", blockLoc.X, blockLoc.Y, blockLoc.Z, block.Type);
+ else
+ return Translations.Get("cmd.dig.fail");
+ }
+ else if (args.Length == 3)
+ {
+ try
+ {
+ Location current = handler.GetCurrentLocation();
+ Location blockToBreak = Location.Parse(current, args[0], args[1], args[2]);
+ if (blockToBreak.DistanceSquared(current.EyesLocation()) > 25)
+ return Translations.Get("cmd.dig.too_far");
+ Block block = handler.GetWorld().GetBlock(blockToBreak);
+ if (block.Type == Material.Air)
+ return Translations.Get("cmd.dig.no_block");
+ else if (handler.DigBlock(blockToBreak))
+ return Translations.Get("cmd.dig.dig", blockToBreak.X, blockToBreak.Y, blockToBreak.Z, block.Type);
+ else
+ return Translations.Get("cmd.dig.fail");
+ }
+ catch (FormatException) { return GetCmdDescTranslated(); }
+ }
+ else
+ {
+ return GetCmdDescTranslated();
}
- else return GetCmdDescTranslated();
}
}
}
diff --git a/MinecraftClient/Commands/Look.cs b/MinecraftClient/Commands/Look.cs
index 68b1cab4..433f53a7 100644
--- a/MinecraftClient/Commands/Look.cs
+++ b/MinecraftClient/Commands/Look.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net.Http.Headers;
using System.Text;
using MinecraftClient.Mapping;
@@ -17,7 +18,20 @@ namespace MinecraftClient.Commands
if (handler.GetTerrainEnabled())
{
string[] args = getArgs(command);
- if (args.Length == 1)
+ if (args.Length == 0)
+ {
+ const double maxDistance = 8.0;
+ (bool hasBlock, Location target, Block block) = RaycastHelper.RaycastBlock(handler, maxDistance, false);
+ if (!hasBlock)
+ return Translations.Get("cmd.look.noinspection", maxDistance);
+ else
+ {
+ Location current = handler.GetCurrentLocation(), target_center = target.ToCenter();
+ return Translations.Get("cmd.look.inspection", block.Type, target.X, target.Y, target.Z,
+ current.Distance(target_center), current.EyesLocation().Distance(target_center));
+ }
+ }
+ else if (args.Length == 1)
{
string dirStr = getArg(command).Trim().ToLower();
Direction direction;
diff --git a/MinecraftClient/Mapping/Block.cs b/MinecraftClient/Mapping/Block.cs
index f7f1315b..5aa1f382 100644
--- a/MinecraftClient/Mapping/Block.cs
+++ b/MinecraftClient/Mapping/Block.cs
@@ -12,6 +12,8 @@ namespace MinecraftClient.Mapping
///
public struct Block
{
+ public static readonly Block Air = new(0);
+
///
/// Get or set global block ID to Material mapping
/// The global Palette is a concept introduced with Minecraft 1.13
diff --git a/MinecraftClient/Mapping/Location.cs b/MinecraftClient/Mapping/Location.cs
index 339ec562..5852e6c6 100644
--- a/MinecraftClient/Mapping/Location.cs
+++ b/MinecraftClient/Mapping/Location.cs
@@ -11,6 +11,8 @@ namespace MinecraftClient.Mapping
///
public struct Location
{
+ public static readonly Location Zero = new(0, 0, 0);
+
///
/// The X Coordinate
///
@@ -26,17 +28,6 @@ namespace MinecraftClient.Mapping
///
public double Z;
- ///
- /// Get location with zeroed coordinates
- ///
- public static Location Zero
- {
- get
- {
- return new Location(0, 0, 0);
- }
- }
-
///
/// Create a new location
///
diff --git a/MinecraftClient/Mapping/RaycastHelper.cs b/MinecraftClient/Mapping/RaycastHelper.cs
new file mode 100644
index 00000000..764d55e1
--- /dev/null
+++ b/MinecraftClient/Mapping/RaycastHelper.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using MinecraftClient.Protocol;
+
+namespace MinecraftClient.Mapping
+{
+ public static class RaycastHelper
+ {
+ public static Tuple RaycastBlock(McClient handler, double maxDistance, bool includeFluids)
+ {
+ return RaycastBlock(handler.GetWorld(), handler.GetCurrentLocation(), handler.GetYaw(), handler.GetPitch(), maxDistance, includeFluids);
+ }
+
+ public static Tuple RaycastBlock(World world, Location playerLocation, float yaw, float pitch, double maxDistance, bool includeFluids)
+ {
+ Location camera = playerLocation.EyesLocation();
+ Location rotation = MathHelper.GetRotationVector(yaw, pitch);
+ Location end = camera + (rotation * maxDistance);
+ return Raycast(world, camera, end, includeFluids);
+ }
+
+ public static Tuple RaycastEntity(McClient handler, double maxDistance)
+ {
+ throw new NotImplementedException();
+ }
+
+ private static Block CheckRaycastResult(World world, Location location, bool includeFluids)
+ {
+ Block block = world.GetBlock(location);
+
+ if (!includeFluids && MaterialExtensions.IsLiquid(block.Type))
+ return Block.Air;
+ else
+ return block;
+ }
+
+ public static Tuple Raycast(World world, Location start, Location end, bool includeFluids)
+ {
+ if (start == end)
+ return new(false, Location.Zero, Block.Air);
+
+ double start_x = MathHelper.Lerp(-1.0E-7, start.X, end.X);
+ double start_y = MathHelper.Lerp(-1.0E-7, start.Y, end.Y);
+ double start_z = MathHelper.Lerp(-1.0E-7, start.Z, end.Z);
+ double end_x = MathHelper.Lerp(-1.0E-7, end.X, start.X);
+ double end_y = MathHelper.Lerp(-1.0E-7, end.Y, start.Y);
+ double end_z = MathHelper.Lerp(-1.0E-7, end.Z, start.Z);
+
+ Location res_location = new(Math.Floor(start_x), Math.Floor(start_y), Math.Floor(start_z));
+ Block block = CheckRaycastResult(world, res_location, includeFluids);
+ if (block.Type != Material.Air)
+ return new(true, res_location, block);
+
+ double dx = end_x - start_x;
+ double dy = end_y - start_y;
+ double dz = end_z - start_z;
+ int dx_sign = Math.Sign(dx);
+ int dy_sign = Math.Sign(dy);
+ int dz_sign = Math.Sign(dz);
+ double x_step = dx_sign == 0 ? double.MaxValue : (double)dx_sign / dx;
+ double y_step = dy_sign == 0 ? double.MaxValue : (double)dy_sign / dy;
+ double z_step = dz_sign == 0 ? double.MaxValue : (double)dz_sign / dz;
+ double x_frac = x_step * (dx_sign > 0 ? 1.0 - MathHelper.FractionalPart(start_x) : MathHelper.FractionalPart(start_x));
+ double y_frac = y_step * (dy_sign > 0 ? 1.0 - MathHelper.FractionalPart(start_y) : MathHelper.FractionalPart(start_y));
+ double z_frac = z_step * (dz_sign > 0 ? 1.0 - MathHelper.FractionalPart(start_z) : MathHelper.FractionalPart(start_z));
+
+ while (x_frac <= 1.0 || y_frac <= 1.0 || z_frac <= 1.0)
+ {
+ if (x_frac < y_frac)
+ {
+ if (x_frac < z_frac)
+ {
+ res_location.X += dx_sign;
+ x_frac += x_step;
+ }
+ else
+ {
+ res_location.Z += dz_sign;
+ z_frac += z_step;
+ }
+ }
+ else if (y_frac < z_frac)
+ {
+ res_location.Y += dy_sign;
+ y_frac += y_step;
+ }
+ else
+ {
+ res_location.Z += dz_sign;
+ z_frac += z_step;
+ }
+
+ block = CheckRaycastResult(world, res_location, includeFluids);
+ if (block.Type != Material.Air)
+ return new(true, res_location, block);
+ }
+
+ return new(false, Location.Zero, Block.Air);
+ }
+ }
+
+ public static class MathHelper
+ {
+ public static Location GetRotationVector(float yaw, float pitch)
+ {
+ float yaw_rad = -yaw * (MathF.PI / 180);
+ (float yaw_sin, float yaw_cos) = MathF.SinCos(yaw_rad);
+
+ float pitch_rad = pitch * (MathF.PI / 180);
+ (float pitch_sin, float pitch_cos) = MathF.SinCos(pitch_rad);
+
+ return new(yaw_sin * pitch_cos, -pitch_sin, yaw_cos * pitch_cos);
+ }
+
+ public static double Lerp(double delta, double start, double end)
+ {
+ return start + delta * (end - start);
+ }
+
+ public static long Lfloor(double value)
+ {
+ long l = (long)value;
+ return value < (double)l ? l - 1L : l;
+ }
+
+ public static double FractionalPart(double value)
+ {
+ return value - Lfloor(value);
+ }
+ }
+}
diff --git a/MinecraftClient/Mapping/World.cs b/MinecraftClient/Mapping/World.cs
index 47a768ad..ffe231ab 100644
--- a/MinecraftClient/Mapping/World.cs
+++ b/MinecraftClient/Mapping/World.cs
@@ -144,7 +144,7 @@ namespace MinecraftClient.Mapping
if (chunk != null)
return chunk.GetBlock(location);
}
- return new Block(0); //Air
+ return Block.Air;
}
///
@@ -218,36 +218,5 @@ namespace MinecraftClient.Mapping
chunkCnt = 0;
chunkLoadNotCompleted = 0;
}
-
- ///
- /// Get the location of block of the entity is looking
- ///
- /// Location of the entity
- /// Yaw of the entity
- /// Pitch of the entity
- /// Location of the block or empty Location if no block was found
- public Location GetLookingBlockLocation(Location location, double yaw, double pitch)
- {
- double rotX = (Math.PI / 180) * yaw;
- double rotY = (Math.PI / 180) * pitch;
- double x = -Math.Cos(rotY) * Math.Sin(rotX);
- double y = -Math.Sin(rotY);
- double z = Math.Cos(rotY) * Math.Cos(rotX);
- Location vector = new Location(x, y, z);
- for (int i = 0; i < 5; i++)
- {
- Location newVector = vector * i;
- Location blockLocation = location.EyesLocation() + new Location(newVector.X, newVector.Y, newVector.Z);
- blockLocation.X = Math.Floor(blockLocation.X);
- blockLocation.Y = Math.Floor(blockLocation.Y);
- blockLocation.Z = Math.Floor(blockLocation.Z);
- Block b = GetBlock(blockLocation);
- if (b.Type != Material.Air)
- {
- return blockLocation;
- }
- }
- return new Location();
- }
}
}
diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs
index 89809d3f..73213fbf 100644
--- a/MinecraftClient/McClient.cs
+++ b/MinecraftClient/McClient.cs
@@ -2902,9 +2902,9 @@ namespace MinecraftClient
{
foreach (int a in Entities)
{
- if (entities.ContainsKey(a))
+ if (entities.TryGetValue(a, out Entity? entity))
{
- DispatchBotEvent(bot => bot.OnEntityDespawn(entities[a]));
+ DispatchBotEvent(bot => bot.OnEntityDespawn(entity));
entities.Remove(a);
}
}
diff --git a/MinecraftClient/Resources/lang/en.ini b/MinecraftClient/Resources/lang/en.ini
index 1a05a119..1a89dc37 100644
--- a/MinecraftClient/Resources/lang/en.ini
+++ b/MinecraftClient/Resources/lang/en.ini
@@ -255,7 +255,7 @@ cmd.debug.state_off=Debug messages are now OFF
cmd.dig.desc=Attempt to break a block
cmd.dig.too_far=You are too far away from this block.
cmd.dig.no_block=No block at this location (Air)
-cmd.dig.dig=Attempting to dig block at {0} {1} {2}
+cmd.dig.dig=Attempting to dig block at {0} {1} {2} ({3})
cmd.dig.fail=Failed to start digging block.
# Entitycmd
@@ -368,8 +368,10 @@ cmd.log.desc=log some text to the console.
# Look
cmd.look.desc=look at direction or coordinates.
cmd.look.unknown=Unknown direction '{0}'
-cmd.look.at=Looking at YAW: {0} PITCH: {1}
+cmd.look.at=Looking at yaw: {0} pitch: {1}
cmd.look.block=Looking at {0}
+cmd.look.inspection=The block currently intersecting the line of sight is {0} ({1:0}, {2:0}, {3:0}), distance is {4:0.0}({5:0.0}).
+cmd.look.noinspection=There is no block within {0} meters that intersects with the line of sight.
# Move
cmd.move.desc=walk or start walking.
diff --git a/MinecraftClient/Resources/lang/zh-CHS.ini b/MinecraftClient/Resources/lang/zh-CHS.ini
index 2bfa0285..9f4cef10 100644
--- a/MinecraftClient/Resources/lang/zh-CHS.ini
+++ b/MinecraftClient/Resources/lang/zh-CHS.ini
@@ -234,7 +234,7 @@ cmd.debug.state_off=调试消息现在已关闭
cmd.dig.desc=试图破坏一个方块
cmd.dig.too_far=你离这个方块太远了。
cmd.dig.no_block=这个地方没有方块 (空气)
-cmd.dig.dig=尝试挖掘位于{0} {1} {2}的方块
+cmd.dig.dig=尝试挖掘位于({0}, {1}, {2})的方块({3})。
cmd.dig.fail=无法开始挖掘方块。
# Entitycmd
@@ -311,9 +311,11 @@ cmd.log.desc=将文本记录到控制台。
# Look
cmd.look.desc=查看方向或坐标。
-cmd.look.unknown=未知方向 '{0}'
-cmd.look.at=正在看向偏航角:{0} 俯仰角:{1}
-cmd.look.block=正在看向{0}
+cmd.look.unknown=未知方向 '{0}'。
+cmd.look.at=当前视角 偏航角:{0} 俯仰角:{1}。
+cmd.look.block=正看向位于 {0} 的方块。
+cmd.look.inspection=与视线相交的第一个方块是 {0} ({1:0}, {2:0}, {3:0}),距离玩家 {4:0.0}({5:0.0})。
+cmd.look.noinspection=在 {0} 米内没有任何方块与视线相交。
# Move
cmd.move.desc=移动或开始移动。
diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs
index 80d0da3f..6671ad26 100644
--- a/MinecraftClient/Scripting/ChatBot.cs
+++ b/MinecraftClient/Scripting/ChatBot.cs
@@ -1033,6 +1033,17 @@ namespace MinecraftClient
Handler.UpdateLocation(Handler.GetCurrentLocation(), yaw, pitch);
}
+ ///
+ /// Find the block on the line of sight.
+ ///
+ /// Maximum distance from sight
+ /// Whether to detect fluid
+ /// Position of the block
+ protected Tuple GetLookingBlock(double maxDistance = 4.5, bool includeFluids = false)
+ {
+ return RaycastHelper.RaycastBlock(Handler, maxDistance, includeFluids);
+ }
+
///
/// Get a Y-M-D h:m:s timestamp representing the current system date and time
///