From 4aa6c1c99f9b84d9d5cf04ed2d90ccfc089752a3 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sun, 2 Oct 2022 13:49:36 +0800 Subject: [PATCH] Upgrade GetLookingBlock --- MinecraftClient/ChatBots/AutoFishing.cs | 4 +- MinecraftClient/Commands/Dig.cs | 53 +++++---- MinecraftClient/Commands/Look.cs | 16 ++- MinecraftClient/Mapping/Block.cs | 2 + MinecraftClient/Mapping/Location.cs | 13 +-- MinecraftClient/Mapping/RaycastHelper.cs | 135 ++++++++++++++++++++++ MinecraftClient/Mapping/World.cs | 33 +----- MinecraftClient/McClient.cs | 4 +- MinecraftClient/Resources/lang/en.ini | 6 +- MinecraftClient/Resources/lang/zh-CHS.ini | 10 +- MinecraftClient/Scripting/ChatBot.cs | 11 ++ 11 files changed, 213 insertions(+), 74 deletions(-) create mode 100644 MinecraftClient/Mapping/RaycastHelper.cs 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 ///