mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Support for using relative coordinates in /move
This commit is contained in:
parent
8f6b962607
commit
0eb8d9998c
4 changed files with 58 additions and 27 deletions
|
|
@ -90,21 +90,27 @@ namespace MinecraftClient.Commands
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int x = int.Parse(args[0]);
|
Location current = handler.GetCurrentLocation(), currentCenter = new Location(current).ConvertToCenter();
|
||||||
int y = int.Parse(args[1]);
|
|
||||||
int z = int.Parse(args[2]);
|
double x = args[0].StartsWith('~') ? current.X + (args[0].Length > 1 ? double.Parse(args[0][1..]) : 0) : double.Parse(args[0]);
|
||||||
Location goal = new Location(x, y, z);
|
double y = args[1].StartsWith('~') ? current.Y + (args[1].Length > 1 ? double.Parse(args[1][1..]) : 0) : double.Parse(args[1]);
|
||||||
|
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);
|
ChunkColumn? chunkColumn = handler.GetWorld().GetChunkColumn(goal);
|
||||||
if (chunkColumn == null || chunkColumn.FullyLoaded == false)
|
if (chunkColumn == null || chunkColumn.FullyLoaded == false)
|
||||||
return Translations.Get("cmd.move.chunk_not_loaded");
|
return Translations.Get("cmd.move.chunk_not_loaded");
|
||||||
|
|
||||||
Location current = handler.GetCurrentLocation();
|
if (takeRisk || Movement.PlayerFitsHere(handler.GetWorld(), goal))
|
||||||
handler.MoveTo(current.ToCenter(), allowDirectTeleport: true);
|
{
|
||||||
|
if (current.DistanceSquared(goal) <= 1.5)
|
||||||
if (handler.MoveTo(goal, allowUnsafe: takeRisk))
|
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);
|
||||||
return Translations.Get("cmd.move.walk", goal, current);
|
return Translations.Get("cmd.move.walk", goal, current);
|
||||||
else return takeRisk ? Translations.Get("cmd.move.fail", goal) : Translations.Get("cmd.move.suggestforce", goal);
|
}
|
||||||
|
else
|
||||||
|
return Translations.Get("cmd.move.suggestforce", goal);
|
||||||
}
|
}
|
||||||
catch (FormatException) { return GetCmdDescTranslated(); }
|
catch (FormatException) { return GetCmdDescTranslated(); }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,16 @@ namespace MinecraftClient.Mapping
|
||||||
Z = z;
|
Z = z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new location
|
||||||
|
/// </summary>
|
||||||
|
public Location(Location loc)
|
||||||
|
{
|
||||||
|
X = loc.X;
|
||||||
|
Y = loc.Y;
|
||||||
|
Z = loc.Z;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new location
|
/// Create a new location
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -67,7 +77,7 @@ namespace MinecraftClient.Mapping
|
||||||
/// Round coordinates
|
/// Round coordinates
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>itself</returns>
|
/// <returns>itself</returns>
|
||||||
public Location ToFloor()
|
public Location ConvertToFloor()
|
||||||
{
|
{
|
||||||
this.X = Math.Floor(this.X);
|
this.X = Math.Floor(this.X);
|
||||||
this.Y = Math.Floor(this.Y);
|
this.Y = Math.Floor(this.Y);
|
||||||
|
|
@ -79,7 +89,7 @@ namespace MinecraftClient.Mapping
|
||||||
/// Get the center coordinates
|
/// Get the center coordinates
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>itself</returns>
|
/// <returns>itself</returns>
|
||||||
public Location ToCenter()
|
public Location ConvertToCenter()
|
||||||
{
|
{
|
||||||
this.X = Math.Floor(this.X) + 0.5;
|
this.X = Math.Floor(this.X) + 0.5;
|
||||||
this.Z = Math.Floor(this.Z) + 0.5;
|
this.Z = Math.Floor(this.Z) + 0.5;
|
||||||
|
|
|
||||||
|
|
@ -168,8 +168,9 @@ namespace MinecraftClient.Mapping
|
||||||
throw new ArgumentException("minOffset must be lower or equal to maxOffset", "minOffset");
|
throw new ArgumentException("minOffset must be lower or equal to maxOffset", "minOffset");
|
||||||
|
|
||||||
// Round start coordinates for easier calculation
|
// Round start coordinates for easier calculation
|
||||||
start.ToFloor();
|
Location startCenter = new Location(start).ConvertToCenter();
|
||||||
goal.ToFloor();
|
Location startLower = new Location(start).ConvertToFloor();
|
||||||
|
Location goalLower = new Location(goal).ConvertToFloor();
|
||||||
|
|
||||||
// We always use distance squared so our limits must also be squared.
|
// We always use distance squared so our limits must also be squared.
|
||||||
minOffset *= minOffset;
|
minOffset *= minOffset;
|
||||||
|
|
@ -187,8 +188,8 @@ namespace MinecraftClient.Mapping
|
||||||
Dictionary<Location, int> gScoreDict = new Dictionary<Location, int>();
|
Dictionary<Location, int> gScoreDict = new Dictionary<Location, int>();
|
||||||
|
|
||||||
// Set start values for variables
|
// Set start values for variables
|
||||||
openSet.Insert(0, (int)start.DistanceSquared(goal), start);
|
openSet.Insert(0, (int)startLower.DistanceSquared(goalLower), startLower);
|
||||||
gScoreDict[start] = 0;
|
gScoreDict[startLower] = 0;
|
||||||
BinaryHeap.Node current = null;
|
BinaryHeap.Node current = null;
|
||||||
|
|
||||||
///---///
|
///---///
|
||||||
|
|
@ -203,9 +204,9 @@ namespace MinecraftClient.Mapping
|
||||||
current = openSet.GetRootLocation();
|
current = openSet.GetRootLocation();
|
||||||
|
|
||||||
// Return if goal found and no maxOffset was given OR current node is between minOffset and maxOffset
|
// Return if goal found and no maxOffset was given OR current node is between minOffset and maxOffset
|
||||||
if ((current.Location == goal && maxOffset <= 0) || (maxOffset > 0 && current.H_score >= minOffset && current.H_score <= maxOffset))
|
if ((current.Location == goalLower && maxOffset <= 0) || (maxOffset > 0 && current.H_score >= minOffset && current.H_score <= maxOffset))
|
||||||
{
|
{
|
||||||
return ReconstructPath(CameFrom, current.Location);
|
return ReconstructPath(CameFrom, current.Location, startCenter, goal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discover neighbored blocks
|
// Discover neighbored blocks
|
||||||
|
|
@ -227,14 +228,14 @@ namespace MinecraftClient.Mapping
|
||||||
|
|
||||||
// If this location is not already included in the Binary Heap: save it
|
// If this location is not already included in the Binary Heap: save it
|
||||||
if (!openSet.ContainsLocation(neighbor))
|
if (!openSet.ContainsLocation(neighbor))
|
||||||
openSet.Insert(tentativeGScore, (int)neighbor.DistanceSquared(goal), neighbor);
|
openSet.Insert(tentativeGScore, (int)neighbor.DistanceSquared(goalLower), neighbor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//// Goal could not be reached. Set the path to the closest location if close enough
|
//// 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))
|
if (current != null && (maxOffset == int.MaxValue || openSet.MinH_ScoreNode.H_score <= maxOffset))
|
||||||
return ReconstructPath(CameFrom, openSet.MinH_ScoreNode.Location);
|
return ReconstructPath(CameFrom, openSet.MinH_ScoreNode.Location, startCenter, goal);
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -245,15 +246,29 @@ namespace MinecraftClient.Mapping
|
||||||
/// <param name="Came_From">The collection of Locations that leads back to the start</param>
|
/// <param name="Came_From">The collection of Locations that leads back to the start</param>
|
||||||
/// <param name="current">Endpoint of our later walk</param>
|
/// <param name="current">Endpoint of our later walk</param>
|
||||||
/// <returns>the path that leads to current from the start position</returns>
|
/// <returns>the path that leads to current from the start position</returns>
|
||||||
private static Queue<Location> ReconstructPath(Dictionary<Location, Location> Came_From, Location current)
|
private static Queue<Location> ReconstructPath(Dictionary<Location, Location> Came_From, Location current, Location startCenter, Location end)
|
||||||
{
|
{
|
||||||
// Add 0.5 to walk over the middle of a block and avoid collisions
|
// Add 0.5 to walk over the middle of a block and avoid collisions
|
||||||
List<Location> total_path = new List<Location>(new[] { current + new Location(0.5, 0, 0.5) });
|
Location center = new(0.5, 0, 0.5);
|
||||||
|
|
||||||
|
List<Location> total_path = new();
|
||||||
|
|
||||||
|
// Move from the center of the block to the final position
|
||||||
|
if (current != end && current.DistanceSquared(end) <= 1.5)
|
||||||
|
total_path.Add(end);
|
||||||
|
|
||||||
|
// Generate intermediate paths
|
||||||
|
total_path.Add(current + center);
|
||||||
while (Came_From.ContainsKey(current))
|
while (Came_From.ContainsKey(current))
|
||||||
{
|
{
|
||||||
current = Came_From[current];
|
current = Came_From[current];
|
||||||
total_path.Add(current + new Location(0.5, 0, 0.5));
|
total_path.Add(current + center);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Move to the center of the block first
|
||||||
|
if (current != startCenter && current.DistanceSquared(startCenter) <= 1.5)
|
||||||
|
total_path.Add(startCenter);
|
||||||
|
|
||||||
total_path.Reverse();
|
total_path.Reverse();
|
||||||
return new Queue<Location>(total_path);
|
return new Queue<Location>(total_path);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1102,7 +1102,7 @@ namespace MinecraftClient
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Move to the specified location
|
/// Move to the specified location
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="location">Location to reach</param>
|
/// <param name="goal">Location to reach</param>
|
||||||
/// <param name="allowUnsafe">Allow possible but unsafe locations thay may hurt the player: lava, cactus...</param>
|
/// <param name="allowUnsafe">Allow possible but unsafe locations thay may hurt the player: lava, cactus...</param>
|
||||||
/// <param name="allowDirectTeleport">Allow non-vanilla direct teleport instead of computing path, but may cause invalid moves and/or trigger anti-cheat plugins</param>
|
/// <param name="allowDirectTeleport">Allow non-vanilla direct teleport instead of computing path, but may cause invalid moves and/or trigger anti-cheat plugins</param>
|
||||||
/// <param name="maxOffset">If no valid path can be found, also allow locations within specified distance of destination</param>
|
/// <param name="maxOffset">If no valid path can be found, also allow locations within specified distance of destination</param>
|
||||||
|
|
@ -1110,21 +1110,21 @@ namespace MinecraftClient
|
||||||
/// <param name="timeout">How long to wait until the path is evaluated (default: 5 seconds)</param>
|
/// <param name="timeout">How long to wait until the path is evaluated (default: 5 seconds)</param>
|
||||||
/// <remarks>When location is unreachable, computation will reach timeout, then optionally fallback to a close location within maxOffset</remarks>
|
/// <remarks>When location is unreachable, computation will reach timeout, then optionally fallback to a close location within maxOffset</remarks>
|
||||||
/// <returns>True if a path has been found</returns>
|
/// <returns>True if a path has been found</returns>
|
||||||
public bool MoveTo(Location location, bool allowUnsafe = false, bool allowDirectTeleport = false, int maxOffset = 0, int minOffset = 0, TimeSpan? timeout = null)
|
public bool MoveTo(Location goal, bool allowUnsafe = false, bool allowDirectTeleport = false, int maxOffset = 0, int minOffset = 0, TimeSpan? timeout = null)
|
||||||
{
|
{
|
||||||
lock (locationLock)
|
lock (locationLock)
|
||||||
{
|
{
|
||||||
if (allowDirectTeleport)
|
if (allowDirectTeleport)
|
||||||
{
|
{
|
||||||
// 1-step path to the desired location without checking anything
|
// 1-step path to the desired location without checking anything
|
||||||
UpdateLocation(location, location); // Update yaw and pitch to look at next step
|
UpdateLocation(goal, goal); // Update yaw and pitch to look at next step
|
||||||
handler.SendLocationUpdate(location, Movement.IsOnGround(world, location), _yaw, _pitch);
|
handler.SendLocationUpdate(goal, Movement.IsOnGround(world, goal), _yaw, _pitch);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Calculate path through pathfinding. Path contains a list of 1-block movement that will be divided into steps
|
// Calculate path through pathfinding. Path contains a list of 1-block movement that will be divided into steps
|
||||||
path = Movement.CalculatePath(world, this.location, location, allowUnsafe, maxOffset, minOffset, timeout ?? TimeSpan.FromSeconds(5));
|
path = Movement.CalculatePath(world, this.location, goal, allowUnsafe, maxOffset, minOffset, timeout ?? TimeSpan.FromSeconds(5));
|
||||||
return path != null;
|
return path != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue