diff --git a/MinecraftClient/Commands/Move.cs b/MinecraftClient/Commands/Move.cs index 8922b88e..1b4f85ee 100644 --- a/MinecraftClient/Commands/Move.cs +++ b/MinecraftClient/Commands/Move.cs @@ -82,7 +82,8 @@ namespace MinecraftClient.Commands { if (handler.MoveTo(goal, allowUnsafe: takeRisk)) return Translations.Get("cmd.move.moving", args[0]); - else return takeRisk ? Translations.Get("cmd.move.dir_fail") : Translations.Get("cmd.move.suggestforce"); + else + return takeRisk ? Translations.Get("cmd.move.dir_fail") : Translations.Get("cmd.move.suggestforce"); } else return Translations.Get("cmd.move.dir_fail"); } @@ -90,7 +91,7 @@ namespace MinecraftClient.Commands { try { - Location current = handler.GetCurrentLocation(), currentCenter = new Location(current).ConvertToCenter(); + Location current = handler.GetCurrentLocation(), currentCenter = current.ToCenter(); double x = args[0].StartsWith('~') ? current.X + (args[0].Length > 1 ? double.Parse(args[0][1..]) : 0) : double.Parse(args[0]); double y = args[1].StartsWith('~') ? current.Y + (args[1].Length > 1 ? double.Parse(args[1][1..]) : 0) : double.Parse(args[1]); @@ -103,7 +104,7 @@ namespace MinecraftClient.Commands if (takeRisk || Movement.PlayerFitsHere(handler.GetWorld(), goal)) { - if (current.DistanceSquared(goal) <= 1.5) + if (current.ToFloor() == goal.ToFloor()) handler.MoveTo(goal, allowDirectTeleport: true); else if (!handler.MoveTo(goal, allowUnsafe: takeRisk)) return takeRisk ? Translations.Get("cmd.move.fail", goal) : Translations.Get("cmd.move.suggestforce", goal); diff --git a/MinecraftClient/Mapping/Entity.cs b/MinecraftClient/Mapping/Entity.cs index 82f6a2a9..f2ad04bb 100644 --- a/MinecraftClient/Mapping/Entity.cs +++ b/MinecraftClient/Mapping/Entity.cs @@ -141,7 +141,7 @@ namespace MinecraftClient.Mapping /// Entity location /// Player uuid /// Player name - public Entity(int ID, EntityType type, Location location, Guid uuid, string? name) + public Entity(int ID, EntityType type, Location location, Guid uuid, string? name, byte yaw, byte pitch) { this.ID = ID; this.Type = type; @@ -151,6 +151,8 @@ namespace MinecraftClient.Mapping this.Health = 1.0f; this.Equipment = new Dictionary(); this.Item = new Item(ItemType.Air, 0, null); + this.Yaw = yaw * (1 / 256) * 360; // to angle in 360 degree + this.Pitch = pitch * (1 / 256) * 360; } } } diff --git a/MinecraftClient/Mapping/Location.cs b/MinecraftClient/Mapping/Location.cs index d7972b43..edc8404f 100644 --- a/MinecraftClient/Mapping/Location.cs +++ b/MinecraftClient/Mapping/Location.cs @@ -76,24 +76,19 @@ namespace MinecraftClient.Mapping /// /// Round coordinates /// - /// itself - public Location ConvertToFloor() + /// New location + public Location ToFloor() { - this.X = Math.Floor(this.X); - this.Y = Math.Floor(this.Y); - this.Z = Math.Floor(this.Z); - return this; + return new Location(Math.Floor(this.X), Math.Floor(this.Y), Math.Floor(this.Z)); } /// /// Get the center coordinates /// - /// itself - public Location ConvertToCenter() + /// New location + public Location ToCenter() { - this.X = Math.Floor(this.X) + 0.5; - this.Z = Math.Floor(this.Z) + 0.5; - return this; + return new Location(Math.Floor(this.X) + 0.5, this.Y, Math.Floor(this.Z) + 0.5); } /// diff --git a/MinecraftClient/Mapping/Movement.cs b/MinecraftClient/Mapping/Movement.cs index 5cde8624..8389cdd7 100644 --- a/MinecraftClient/Mapping/Movement.cs +++ b/MinecraftClient/Mapping/Movement.cs @@ -49,14 +49,18 @@ namespace MinecraftClient.Mapping /// Location the player is currently at /// Allow possible but unsafe locations /// A list of new locations the player can move to - public static IEnumerable GetAvailableMoves(World world, Location location, bool allowUnsafe = false) + public static IEnumerable GetAvailableMoves(World world, Location originLocation, bool allowUnsafe = false) { - List availableMoves = new List(); + Location location = originLocation.ToCenter(); + List availableMoves = new(); if (IsOnGround(world, location) || IsSwimming(world, location)) { foreach (Direction dir in Enum.GetValues(typeof(Direction))) - if (CanMove(world, location, dir) && (allowUnsafe || IsSafe(world, Move(location, dir)))) - availableMoves.Add(Move(location, dir)); + { + Location dest = Move(location, dir); + if (CanMove(world, location, dir) && (allowUnsafe || IsSafe(world, dest))) + availableMoves.Add(dest); + } } else { @@ -98,7 +102,8 @@ namespace MinecraftClient.Mapping Y += motionY; if (Y < goal.Y) return new Queue(new[] { goal }); - else return new Queue(new[] { new Location(start.X, Y, start.Z) }); + else + return new Queue(new[] { new Location(start.X, Y, start.Z) }); } else { @@ -110,12 +115,13 @@ namespace MinecraftClient.Mapping if (totalStepsDouble >= 1) { - Queue movementSteps = new Queue(); + Queue movementSteps = new(); for (int i = 1; i <= totalSteps; i++) movementSteps.Enqueue(start + step * i); return movementSteps; } - else return new Queue(new[] { goal }); + else + return new Queue(new[] { goal }); } } @@ -168,9 +174,8 @@ namespace MinecraftClient.Mapping throw new ArgumentException("minOffset must be lower or equal to maxOffset", "minOffset"); // Round start coordinates for easier calculation - Location startCenter = new Location(start).ConvertToCenter(); - Location startLower = new Location(start).ConvertToFloor(); - Location goalLower = new Location(goal).ConvertToFloor(); + Location startLower = start.ToFloor(); + Location goalLower = goal.ToFloor(); // We always use distance squared so our limits must also be squared. minOffset *= minOffset; @@ -206,7 +211,7 @@ namespace MinecraftClient.Mapping // Return if goal found and no maxOffset was given OR current node is between minOffset and maxOffset if ((current.Location == goalLower && maxOffset <= 0) || (maxOffset > 0 && current.H_score >= minOffset && current.H_score <= maxOffset)) { - return ReconstructPath(CameFrom, current.Location, startCenter, goal); + return ReconstructPath(CameFrom, current.Location, start, goal); } // Discover neighbored blocks @@ -235,7 +240,7 @@ namespace MinecraftClient.Mapping //// Goal could not be reached. Set the path to the closest location if close enough if (current != null && (maxOffset == int.MaxValue || openSet.MinH_ScoreNode.H_score <= maxOffset)) - return ReconstructPath(CameFrom, openSet.MinH_ScoreNode.Location, startCenter, goal); + return ReconstructPath(CameFrom, openSet.MinH_ScoreNode.Location, start, goal); else return null; } @@ -246,31 +251,35 @@ namespace MinecraftClient.Mapping /// The collection of Locations that leads back to the start /// Endpoint of our later walk /// the path that leads to current from the start position - private static Queue ReconstructPath(Dictionary Came_From, Location current, Location startCenter, Location end) + private static Queue ReconstructPath(Dictionary Came_From, Location current, Location start, Location end) { - // Add 0.5 to walk over the middle of a block and avoid collisions - Location center = new(0.5, 0, 0.5); - + int midPathCnt = 0; List total_path = new(); // Move from the center of the block to the final position - if (current != end && current.DistanceSquared(end) <= 1.5) + if (current != end && current == end.ToFloor()) total_path.Add(end); // Generate intermediate paths - total_path.Add(current + center); + total_path.Add(current.ToCenter()); while (Came_From.ContainsKey(current)) { + ++midPathCnt; current = Came_From[current]; - total_path.Add(current + center); + total_path.Add(current.ToCenter()); } - // Move to the center of the block first - if (current != startCenter && current.DistanceSquared(startCenter) <= 1.5) - total_path.Add(startCenter); + if (midPathCnt <= 2 && start.DistanceSquared(end) < 2.0) + return new Queue(new Location[] { end }); + else + { + // Move to the center of the block first + if (current != start && current == start.ToFloor()) + total_path.Add(start.ToCenter()); - total_path.Reverse(); - return new Queue(total_path); + total_path.Reverse(); + return new Queue(total_path); + } } /// @@ -567,7 +576,7 @@ namespace MinecraftClient.Mapping return PlayerFitsHere(world, Move(location, Direction.North)) && PlayerFitsHere(world, Move(location, Direction.West)) && PlayerFitsHere(world, Move(location, direction)); default: - throw new ArgumentException("Unknown direction", "direction"); + throw new ArgumentException("Unknown direction", nameof(direction)); } } diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 62ca784a..3ef7fbf1 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -2791,12 +2791,16 @@ namespace MinecraftClient /// /// Called when a player spawns or enters the client's render distance /// - public void OnSpawnPlayer(int entityID, Guid uuid, Location location, byte Yaw, byte Pitch) + public void OnSpawnPlayer(int entityID, Guid uuid, Location location, byte yaw, byte pitch) { - string? playerName = null; - if (onlinePlayers.ContainsKey(uuid)) - playerName = onlinePlayers[uuid].Name; - Entity playerEntity = new(entityID, EntityType.Player, location, uuid, playerName); + Entity playerEntity; + if (onlinePlayers.TryGetValue(uuid, out PlayerInfo? player)) + { + playerEntity = new(entityID, EntityType.Player, location, uuid, player.Name, yaw, pitch); + player.entity = playerEntity; + } + else + playerEntity = new(entityID, EntityType.Player, location, uuid, null, yaw, pitch); OnSpawnEntity(playerEntity); } diff --git a/MinecraftClient/Protocol/PlayerInfo.cs b/MinecraftClient/Protocol/PlayerInfo.cs index 73d96018..b16376ce 100644 --- a/MinecraftClient/Protocol/PlayerInfo.cs +++ b/MinecraftClient/Protocol/PlayerInfo.cs @@ -23,6 +23,12 @@ namespace MinecraftClient.Protocol public string? DisplayName; + // Entity info + + public Mapping.Entity? entity; + + // For message signature + private readonly PublicKey? PublicKey; private readonly DateTime? KeyExpiresAt;