mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Add simple movements with /move command
- Determine if we can move to the specified direction - Add moving ability to the specified direction - Add /move command for triggering moves - Add move decomp. into steps (more natural) - Add pathfinding routines (still WIP) - SO YES YOU CAN NOW WALK USING MCC!!!
This commit is contained in:
parent
00131de08b
commit
b0c8f82697
9 changed files with 475 additions and 17 deletions
59
MinecraftClient/Commands/Move.cs
Normal file
59
MinecraftClient/Commands/Move.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using MinecraftClient.Mapping;
|
||||||
|
|
||||||
|
namespace MinecraftClient.Commands
|
||||||
|
{
|
||||||
|
public class Move : Command
|
||||||
|
{
|
||||||
|
public override string CMDName { get { return "move"; } }
|
||||||
|
public override string CMDDesc { get { return "move <up|down|east|west|north|south|x y z>: walk or start walking."; } }
|
||||||
|
|
||||||
|
public override string Run(McTcpClient handler, string command)
|
||||||
|
{
|
||||||
|
if (Settings.TerrainAndMovements)
|
||||||
|
{
|
||||||
|
string[] args = getArgs(command);
|
||||||
|
if (args.Length == 1)
|
||||||
|
{
|
||||||
|
string dirStr = getArg(command).Trim().ToLower();
|
||||||
|
Direction direction;
|
||||||
|
switch (dirStr)
|
||||||
|
{
|
||||||
|
case "up": direction = Direction.Up; break;
|
||||||
|
case "down": direction = Direction.Down; break;
|
||||||
|
case "east": direction = Direction.East; break;
|
||||||
|
case "west": direction = Direction.West; break;
|
||||||
|
case "north": direction = Direction.North; break;
|
||||||
|
case "south": direction = Direction.South; break;
|
||||||
|
default: return "Unknown direction '" + dirStr + "'.";
|
||||||
|
}
|
||||||
|
if (Movement.CanMove(handler.GetWorld(), handler.GetCurrentLocation(), direction))
|
||||||
|
{
|
||||||
|
handler.MoveTo(Movement.Move(handler.GetCurrentLocation(), direction));
|
||||||
|
return "Moving " + dirStr + '.';
|
||||||
|
}
|
||||||
|
else return "Cannot move in that direction.";
|
||||||
|
}
|
||||||
|
else if (args.Length == 3)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int x = int.Parse(args[0]);
|
||||||
|
int y = int.Parse(args[1]);
|
||||||
|
int z = int.Parse(args[2]);
|
||||||
|
Location goal = new Location(x, y, z);
|
||||||
|
if (handler.MoveTo(goal))
|
||||||
|
return "Walking to " + goal;
|
||||||
|
return "Failed to compute path to " + goal;
|
||||||
|
}
|
||||||
|
catch (FormatException) { return CMDDesc; }
|
||||||
|
}
|
||||||
|
else return CMDDesc;
|
||||||
|
}
|
||||||
|
else return "Please enable terrainandmovements in config to use this command.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
MinecraftClient/Mapping/Direction.cs
Normal file
21
MinecraftClient/Mapping/Direction.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MinecraftClient.Mapping
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a unit movement in the world
|
||||||
|
/// </summary>
|
||||||
|
/// <see href="http://minecraft.gamepedia.com/Coordinates"/>
|
||||||
|
public enum Direction
|
||||||
|
{
|
||||||
|
South = 0,
|
||||||
|
West = 1,
|
||||||
|
North = 2,
|
||||||
|
East = 3,
|
||||||
|
Up = 4,
|
||||||
|
Down = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -127,6 +127,28 @@ namespace MinecraftClient.Mapping
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a squared distance to the specified location
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">Other location for computing distance</param>
|
||||||
|
/// <returns>Distance to the specified location, without using a square root</returns>
|
||||||
|
public double DistanceSquared(Location location)
|
||||||
|
{
|
||||||
|
return ((X - location.X) * (X - location.X))
|
||||||
|
+ ((Y - location.Y) * (Y - location.Y))
|
||||||
|
+ ((Z - location.Z) * (Z - location.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get exact distance to the specified location
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">Other location for computing distance</param>
|
||||||
|
/// <returns>Distance to the specified location, with square root so lower performances</returns>
|
||||||
|
public double Distance(Location location)
|
||||||
|
{
|
||||||
|
return Math.Sqrt(DistanceSquared(location));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compare two locations. Locations are equals if the integer part of their coordinates are equals.
|
/// Compare two locations. Locations are equals if the integer part of their coordinates are equals.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -156,7 +178,7 @@ namespace MinecraftClient.Mapping
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>Location representation as ulong</returns>
|
/// <returns>Location representation as ulong</returns>
|
||||||
|
|
||||||
public ulong GetLongRepresentation()
|
public ulong GetLong()
|
||||||
{
|
{
|
||||||
return ((((ulong)X) & 0x3FFFFFF) << 38) | ((((ulong)Y) & 0xFFF) << 26) | (((ulong)Z) & 0x3FFFFFF);
|
return ((((ulong)X) & 0x3FFFFFF) << 38) | ((((ulong)Y) & 0xFFF) << 26) | (((ulong)Z) & 0x3FFFFFF);
|
||||||
}
|
}
|
||||||
|
|
@ -166,7 +188,7 @@ namespace MinecraftClient.Mapping
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Location represented by the ulong</returns>
|
/// <returns>Location represented by the ulong</returns>
|
||||||
|
|
||||||
public static Location FromLongRepresentation(ulong location)
|
public static Location FromLong(ulong location)
|
||||||
{
|
{
|
||||||
int x = (int)(location >> 38);
|
int x = (int)(location >> 38);
|
||||||
int y = (int)((location >> 26) & 0xFFF);
|
int y = (int)((location >> 26) & 0xFFF);
|
||||||
|
|
@ -186,7 +208,7 @@ namespace MinecraftClient.Mapping
|
||||||
/// <param name="loc1">First location to compare</param>
|
/// <param name="loc1">First location to compare</param>
|
||||||
/// <param name="loc2">Second location to compare</param>
|
/// <param name="loc2">Second location to compare</param>
|
||||||
/// <returns>TRUE if the locations are equals</returns>
|
/// <returns>TRUE if the locations are equals</returns>
|
||||||
public static bool operator == (Location loc1, Location loc2)
|
public static bool operator ==(Location loc1, Location loc2)
|
||||||
{
|
{
|
||||||
if (loc1 == null && loc2 == null)
|
if (loc1 == null && loc2 == null)
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -201,7 +223,7 @@ namespace MinecraftClient.Mapping
|
||||||
/// <param name="loc1">First location to compare</param>
|
/// <param name="loc1">First location to compare</param>
|
||||||
/// <param name="loc2">Second location to compare</param>
|
/// <param name="loc2">Second location to compare</param>
|
||||||
/// <returns>TRUE if the locations are equals</returns>
|
/// <returns>TRUE if the locations are equals</returns>
|
||||||
public static bool operator != (Location loc1, Location loc2)
|
public static bool operator !=(Location loc1, Location loc2)
|
||||||
{
|
{
|
||||||
if (loc1 == null && loc2 == null)
|
if (loc1 == null && loc2 == null)
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -219,7 +241,7 @@ namespace MinecraftClient.Mapping
|
||||||
/// <param name="loc1">First location to sum</param>
|
/// <param name="loc1">First location to sum</param>
|
||||||
/// <param name="loc2">Second location to sum</param>
|
/// <param name="loc2">Second location to sum</param>
|
||||||
/// <returns>Sum of the two locations</returns>
|
/// <returns>Sum of the two locations</returns>
|
||||||
public static Location operator + (Location loc1, Location loc2)
|
public static Location operator +(Location loc1, Location loc2)
|
||||||
{
|
{
|
||||||
return new Location
|
return new Location
|
||||||
(
|
(
|
||||||
|
|
@ -229,6 +251,57 @@ namespace MinecraftClient.Mapping
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Substract a location to another
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="NullReferenceException">
|
||||||
|
/// Thrown if one of the provided location is null
|
||||||
|
/// </exception>
|
||||||
|
/// <param name="loc1">First location</param>
|
||||||
|
/// <param name="loc2">Location to substract to the first one</param>
|
||||||
|
/// <returns>Sum of the two locations</returns>
|
||||||
|
public static Location operator -(Location loc1, Location loc2)
|
||||||
|
{
|
||||||
|
return new Location
|
||||||
|
(
|
||||||
|
loc1.X - loc2.X,
|
||||||
|
loc1.Y - loc2.Y,
|
||||||
|
loc1.Z - loc2.Z
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Multiply a location by a scalar value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loc">Location to multiply</param>
|
||||||
|
/// <param name="val">Scalar value</param>
|
||||||
|
/// <returns>Product of the location and the scalar value</returns>
|
||||||
|
public static Location operator *(Location loc, double val)
|
||||||
|
{
|
||||||
|
return new Location
|
||||||
|
(
|
||||||
|
loc.X * val,
|
||||||
|
loc.Y * val,
|
||||||
|
loc.Z * val
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Divide a location by a scalar value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loc">Location to divide</param>
|
||||||
|
/// <param name="val">Scalar value</param>
|
||||||
|
/// <returns>Result of the division</returns>
|
||||||
|
public static Location operator /(Location loc, double val)
|
||||||
|
{
|
||||||
|
return new Location
|
||||||
|
(
|
||||||
|
loc.X / val,
|
||||||
|
loc.Y / val,
|
||||||
|
loc.Z / val
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DO NOT USE. Defined to comply with C# requirements requiring a GetHashCode() when overriding Equals() or ==
|
/// DO NOT USE. Defined to comply with C# requirements requiring a GetHashCode() when overriding Equals() or ==
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -342,5 +342,24 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the provided material is a liquid a player can swim into
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="m">Material to test</param>
|
||||||
|
/// <returns>True if the material is a liquid</returns>
|
||||||
|
public static bool IsLiquid(this Material m)
|
||||||
|
{
|
||||||
|
switch (m)
|
||||||
|
{
|
||||||
|
case Material.Water:
|
||||||
|
case Material.StationaryWater:
|
||||||
|
case Material.Lava:
|
||||||
|
case Material.StationaryLava:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
264
MinecraftClient/Mapping/Movement.cs
Normal file
264
MinecraftClient/Mapping/Movement.cs
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MinecraftClient.Mapping
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Allows moving through a Minecraft world
|
||||||
|
/// </summary>
|
||||||
|
public static class Movement
|
||||||
|
{
|
||||||
|
/* ========= PATHFINDING METHODS ========= */
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle movements due to gravity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="world">World the player is currently located in</param>
|
||||||
|
/// <param name="location">Location the player is currently at</param>
|
||||||
|
/// <returns>Updated location after applying gravity</returns>
|
||||||
|
public static Location HandleGravity(World world, Location location)
|
||||||
|
{
|
||||||
|
Location onFoots = new Location(location.X, Math.Floor(location.Y), location.Z);
|
||||||
|
Location belowFoots = Move(location, Direction.Down);
|
||||||
|
if (!IsOnGround(world, location) && !IsSwimming(world, location))
|
||||||
|
location = Move2Steps(location, belowFoots).Dequeue();
|
||||||
|
else if (!(world.GetBlock(onFoots).Type.IsSolid()))
|
||||||
|
location = Move2Steps(location, onFoots).Dequeue();
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return a list of possible moves for the player
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="world">World the player is currently located in</param>
|
||||||
|
/// <param name="location">Location the player is currently at</param>
|
||||||
|
/// <param name="allowUnsafe">Allow possible but unsafe locations</param>
|
||||||
|
/// <returns>A list of new locations the player can move to</returns>
|
||||||
|
public static IEnumerable<Location> GetAvailableMoves(World world, Location location, bool allowUnsafe = false)
|
||||||
|
{
|
||||||
|
List<Location> availableMoves = new List<Location>();
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (Direction dir in new []{ Direction.East, Direction.West, Direction.North, Direction.South })
|
||||||
|
if (CanMove(world, location, dir) && IsOnGround(world, Move(location, dir)) && (allowUnsafe || IsSafe(world, Move(location, dir))))
|
||||||
|
availableMoves.Add(Move(location, dir));
|
||||||
|
availableMoves.Add(Move(location, Direction.Down));
|
||||||
|
}
|
||||||
|
return availableMoves;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decompose a single move from a block to another into several steps
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Allows moving by little steps instead or directly moving between blocks,
|
||||||
|
/// which would be rejected by anti-cheat plugins anyway.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="start">Start location</param>
|
||||||
|
/// <param name="goal">Destination location</param>
|
||||||
|
/// <param name="stepsByBlock">Amount of steps by block</param>
|
||||||
|
/// <returns>A list of locations corresponding to the requested steps</returns>
|
||||||
|
public static Queue<Location> Move2Steps(Location start, Location goal, int stepsByBlock = 8)
|
||||||
|
{
|
||||||
|
if (stepsByBlock <= 0)
|
||||||
|
stepsByBlock = 1;
|
||||||
|
|
||||||
|
double totalStepsDouble = start.Distance(goal) * stepsByBlock;
|
||||||
|
int totalSteps = (int)Math.Ceiling(totalStepsDouble);
|
||||||
|
Location step = (goal - start) / totalSteps;
|
||||||
|
|
||||||
|
if (totalStepsDouble >= 1)
|
||||||
|
{
|
||||||
|
Queue<Location> movementSteps = new Queue<Location>();
|
||||||
|
for (int i = 1; i <= totalSteps; i++)
|
||||||
|
movementSteps.Enqueue(start + step * i);
|
||||||
|
return movementSteps;
|
||||||
|
}
|
||||||
|
else return new Queue<Location>(new[] { goal });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate a path from the start location to the destination location
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Based on the A* pathfinding algorithm described on Wikipedia
|
||||||
|
/// </remarks>
|
||||||
|
/// <see href="https://en.wikipedia.org/wiki/A*_search_algorithm#Pseudocode"/>
|
||||||
|
/// <param name="start">Start location</param>
|
||||||
|
/// <param name="goal">Destination location</param>
|
||||||
|
/// <param name="allowUnsafe">Allow possible but unsafe locations</param>
|
||||||
|
/// <returns>A list of locations, or null if calculation failed</returns>
|
||||||
|
public static Queue<Location> CalculatePath(World world, Location start, Location goal, bool allowUnsafe = false)
|
||||||
|
{
|
||||||
|
HashSet<Location> ClosedSet = new HashSet<Location>(); // The set of locations already evaluated.
|
||||||
|
HashSet<Location> OpenSet = new HashSet<Location>(); // The set of tentative nodes to be evaluated, initially containing the start node
|
||||||
|
Dictionary<Location, Location> Came_From = new Dictionary<Location, Location>(); // The map of navigated nodes.
|
||||||
|
|
||||||
|
Dictionary<Location, int> g_score = new Dictionary<Location, int>(); //:= map with default value of Infinity
|
||||||
|
g_score[start] = 0; // Cost from start along best known path.
|
||||||
|
// Estimated total cost from start to goal through y.
|
||||||
|
Dictionary<Location, int> f_score = new Dictionary<Location, int>(); //:= map with default value of Infinity
|
||||||
|
f_score[start] = (int)start.DistanceSquared(goal); //heuristic_cost_estimate(start, goal)
|
||||||
|
|
||||||
|
while (OpenSet.Count > 0)
|
||||||
|
{
|
||||||
|
Location current = //the node in OpenSet having the lowest f_score[] value
|
||||||
|
OpenSet.Select(location => f_score.ContainsKey(location)
|
||||||
|
? new KeyValuePair<Location, int>(location, f_score[location])
|
||||||
|
: new KeyValuePair<Location, int>(location, int.MaxValue))
|
||||||
|
.OrderBy(pair => pair.Value).First().Key;
|
||||||
|
if (current == goal)
|
||||||
|
{ //reconstruct_path(Came_From, goal)
|
||||||
|
Queue<Location> total_path = new Queue<Location>(new Location[] { current });
|
||||||
|
while (Came_From.ContainsKey(current))
|
||||||
|
{
|
||||||
|
current = Came_From[current];
|
||||||
|
total_path.Enqueue(current);
|
||||||
|
}
|
||||||
|
return total_path;
|
||||||
|
}
|
||||||
|
OpenSet.Remove(current);
|
||||||
|
ClosedSet.Add(current);
|
||||||
|
foreach (Location neighbor in GetAvailableMoves(world, current, allowUnsafe))
|
||||||
|
{
|
||||||
|
if (ClosedSet.Contains(neighbor))
|
||||||
|
continue; // Ignore the neighbor which is already evaluated.
|
||||||
|
int tentative_g_score = g_score[current] + (int)current.DistanceSquared(neighbor); //dist_between(current,neighbor) // length of this path.
|
||||||
|
if (!OpenSet.Contains(neighbor)) // Discover a new node
|
||||||
|
OpenSet.Add(neighbor);
|
||||||
|
else if (tentative_g_score >= g_score[neighbor])
|
||||||
|
continue; // This is not a better path.
|
||||||
|
|
||||||
|
// This path is the best until now. Record it!
|
||||||
|
Came_From[neighbor] = current;
|
||||||
|
g_score[neighbor] = tentative_g_score;
|
||||||
|
f_score[neighbor] = g_score[neighbor] + (int)neighbor.DistanceSquared(goal); //heuristic_cost_estimate(neighbor, goal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========= LOCATION PROPERTIES ========= */
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the specified location is on the ground
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="world">World for performing check</param>
|
||||||
|
/// <param name="location">Location to check</param>
|
||||||
|
/// <returns>True if the specified location is on the ground</returns>
|
||||||
|
public static bool IsOnGround(World world, Location location)
|
||||||
|
{
|
||||||
|
return world.GetBlock(Move(location, Direction.Down)).Type.IsSolid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the specified location implies swimming
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="world">World for performing check</param>
|
||||||
|
/// <param name="location">Location to check</param>
|
||||||
|
/// <returns>True if the specified location implies swimming</returns>
|
||||||
|
public static bool IsSwimming(World world, Location location)
|
||||||
|
{
|
||||||
|
return world.GetBlock(location).Type.IsLiquid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the specified location is safe
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="world">World for performing check</param>
|
||||||
|
/// <param name="location">Location to check</param>
|
||||||
|
/// <returns>True if the destination location won't directly harm the player</returns>
|
||||||
|
public static bool IsSafe(World world, Location location)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
//No block that can harm the player
|
||||||
|
!world.GetBlock(location).Type.CanHarmPlayers()
|
||||||
|
&& !world.GetBlock(Move(location, Direction.Up)).Type.CanHarmPlayers()
|
||||||
|
&& !world.GetBlock(Move(location, Direction.Down)).Type.CanHarmPlayers()
|
||||||
|
|
||||||
|
//No fall from a too high place
|
||||||
|
&& (world.GetBlock(Move(location, Direction.Down)).Type.IsSolid()
|
||||||
|
|| world.GetBlock(Move(location, Direction.Down, 2)).Type.IsSolid()
|
||||||
|
|| world.GetBlock(Move(location, Direction.Down, 3)).Type.IsSolid())
|
||||||
|
|
||||||
|
//Not an underwater location
|
||||||
|
&& !(world.GetBlock(Move(location, Direction.Up)).Type.IsLiquid());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========= SIMPLE MOVEMENTS ========= */
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the player can move in the specified direction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="world">World the player is currently located in</param>
|
||||||
|
/// <param name="location">Location the player is currently at</param>
|
||||||
|
/// <param name="direction">Direction the player is moving to</param>
|
||||||
|
/// <returns>True if the player can move in the specified direction</returns>
|
||||||
|
public static bool CanMove(World world, Location location, Direction direction)
|
||||||
|
{
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case Direction.Down:
|
||||||
|
return !IsOnGround(world, location);
|
||||||
|
case Direction.Up:
|
||||||
|
return (IsOnGround(world, location) || IsSwimming(world, location))
|
||||||
|
&& !world.GetBlock(Move(Move(location, Direction.Up), Direction.Up)).Type.IsSolid();
|
||||||
|
case Direction.East:
|
||||||
|
case Direction.West:
|
||||||
|
case Direction.South:
|
||||||
|
case Direction.North:
|
||||||
|
return !world.GetBlock(Move(location, direction)).Type.IsSolid()
|
||||||
|
&& !world.GetBlock(Move(Move(location, direction), Direction.Up)).Type.IsSolid();
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("Unknown direction", "direction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get an updated location for moving in the specified direction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">Current location</param>
|
||||||
|
/// <param name="direction">Direction to move to</param>
|
||||||
|
/// <param name="length">Distance, in blocks</param>
|
||||||
|
/// <returns>Updated location</returns>
|
||||||
|
public static Location Move(Location location, Direction direction, int length = 1)
|
||||||
|
{
|
||||||
|
return location + Move(direction) * length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a location delta for moving in the specified direction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="direction">Direction to move to</param>
|
||||||
|
/// <returns>A location delta for moving in that direction</returns>
|
||||||
|
public static Location Move(Direction direction)
|
||||||
|
{
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case Direction.Down:
|
||||||
|
return new Location(0, -1, 0);
|
||||||
|
case Direction.Up:
|
||||||
|
return new Location(0, 1, 0);
|
||||||
|
case Direction.East:
|
||||||
|
return new Location(1, 0, 0);
|
||||||
|
case Direction.West:
|
||||||
|
return new Location(-1, 0, 0);
|
||||||
|
case Direction.South:
|
||||||
|
return new Location(0, 0, 1);
|
||||||
|
case Direction.North:
|
||||||
|
return new Location(0, 0, -1);
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("Unknown direction", "direction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,6 +33,8 @@ namespace MinecraftClient
|
||||||
|
|
||||||
private object locationLock = new object();
|
private object locationLock = new object();
|
||||||
private World world = new World();
|
private World world = new World();
|
||||||
|
private Queue<Location> steps;
|
||||||
|
private Queue<Location> path;
|
||||||
private Location location;
|
private Location location;
|
||||||
|
|
||||||
private string host;
|
private string host;
|
||||||
|
|
@ -369,6 +371,23 @@ namespace MinecraftClient
|
||||||
UpdateLocation(location, false);
|
UpdateLocation(location, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Move to the specified location
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">Location to reach</param>
|
||||||
|
/// <param name="allowUnsafe">Allow possible but unsafe locations</param>
|
||||||
|
/// <returns>True if a path has been found</returns>
|
||||||
|
public bool MoveTo(Location location, bool allowUnsafe = false)
|
||||||
|
{
|
||||||
|
lock (locationLock)
|
||||||
|
{
|
||||||
|
if (Movement.GetAvailableMoves(world, this.location, allowUnsafe).Contains(location))
|
||||||
|
path = new Queue<Location>(new[] { location });
|
||||||
|
else path = Movement.CalculatePath(world, this.location, location, allowUnsafe);
|
||||||
|
return path != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Received some text from the server
|
/// Received some text from the server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -453,15 +472,15 @@ namespace MinecraftClient
|
||||||
{
|
{
|
||||||
lock (locationLock)
|
lock (locationLock)
|
||||||
{
|
{
|
||||||
Location onFoots = new Location(location.X, Math.Floor(location.Y), location.Z);
|
for (int i = 0; i < 2; i++) //Needs to run at 20 tps; MCC runs at 10 tps
|
||||||
Location belowFoots = location + new Location(0, -1, 0);
|
{
|
||||||
Block blockOnFoots = world.GetBlock(onFoots);
|
if (steps != null && steps.Count > 0)
|
||||||
Block blockBelowFoots = world.GetBlock(belowFoots);
|
location = steps.Dequeue();
|
||||||
handler.SendLocationUpdate(location, blockBelowFoots.Type.IsSolid());
|
else if (path != null && path.Count > 0)
|
||||||
if (!blockBelowFoots.Type.IsSolid())
|
steps = Movement.Move2Steps(location, path.Dequeue());
|
||||||
location = belowFoots;
|
else location = Movement.HandleGravity(world, location);
|
||||||
else if (!blockOnFoots.Type.IsSolid())
|
handler.SendLocationUpdate(location, Movement.IsOnGround(world, location));
|
||||||
location = onFoots;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@
|
||||||
<Compile Include="ChatBot.cs" />
|
<Compile Include="ChatBot.cs" />
|
||||||
<Compile Include="Command.cs" />
|
<Compile Include="Command.cs" />
|
||||||
<Compile Include="Commands\Connect.cs" />
|
<Compile Include="Commands\Connect.cs" />
|
||||||
|
<Compile Include="Commands\Move.cs" />
|
||||||
<Compile Include="Commands\Exit.cs" />
|
<Compile Include="Commands\Exit.cs" />
|
||||||
<Compile Include="Commands\Log.cs" />
|
<Compile Include="Commands\Log.cs" />
|
||||||
<Compile Include="Commands\Reco.cs" />
|
<Compile Include="Commands\Reco.cs" />
|
||||||
|
|
@ -117,7 +118,9 @@
|
||||||
<Compile Include="Mapping\Block.cs" />
|
<Compile Include="Mapping\Block.cs" />
|
||||||
<Compile Include="Mapping\Chunk.cs" />
|
<Compile Include="Mapping\Chunk.cs" />
|
||||||
<Compile Include="Mapping\ChunkColumn.cs" />
|
<Compile Include="Mapping\ChunkColumn.cs" />
|
||||||
|
<Compile Include="Mapping\Direction.cs" />
|
||||||
<Compile Include="Mapping\Material.cs" />
|
<Compile Include="Mapping\Material.cs" />
|
||||||
|
<Compile Include="Mapping\Movement.cs" />
|
||||||
<Compile Include="Mapping\World.cs" />
|
<Compile Include="Mapping\World.cs" />
|
||||||
<Compile Include="Protocol\Handlers\Forge\FMLHandshakeClientState.cs" />
|
<Compile Include="Protocol\Handlers\Forge\FMLHandshakeClientState.cs" />
|
||||||
<Compile Include="Protocol\Handlers\Forge\FMLHandshakeDiscriminator.cs" />
|
<Compile Include="Protocol\Handlers\Forge\FMLHandshakeDiscriminator.cs" />
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
break;
|
break;
|
||||||
case 0x23: //Block Change
|
case 0x23: //Block Change
|
||||||
if (Settings.TerrainAndMovements)
|
if (Settings.TerrainAndMovements)
|
||||||
handler.GetWorld().SetBlock(Location.FromLongRepresentation(readNextULong(packetData)), new Block((ushort)readNextVarInt(packetData)));
|
handler.GetWorld().SetBlock(Location.FromLong(readNextULong(packetData)), new Block((ushort)readNextVarInt(packetData)));
|
||||||
break;
|
break;
|
||||||
case 0x26: //Map Chunk Bulk
|
case 0x26: //Map Chunk Bulk
|
||||||
if (Settings.TerrainAndMovements)
|
if (Settings.TerrainAndMovements)
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ namespace MinecraftClient
|
||||||
case "scriptcache": CacheScripts = str2bool(argValue); break;
|
case "scriptcache": CacheScripts = str2bool(argValue); break;
|
||||||
case "showsystemmessages": DisplaySystemMessages = str2bool(argValue); break;
|
case "showsystemmessages": DisplaySystemMessages = str2bool(argValue); break;
|
||||||
case "showxpbarmessages": DisplayXPBarMessages = str2bool(argValue); break;
|
case "showxpbarmessages": DisplayXPBarMessages = str2bool(argValue); break;
|
||||||
case "handleterrainandmovements": TerrainAndMovements = str2bool(argValue); break;
|
case "terrainandmovements": TerrainAndMovements = str2bool(argValue); break;
|
||||||
|
|
||||||
case "botowners":
|
case "botowners":
|
||||||
Bots_Owners.Clear();
|
Bots_Owners.Clear();
|
||||||
|
|
@ -409,7 +409,7 @@ namespace MinecraftClient
|
||||||
+ "chatbotlogfile= #leave empty for no logfile\r\n"
|
+ "chatbotlogfile= #leave empty for no logfile\r\n"
|
||||||
+ "showsystemmessages=true #system messages for server ops\r\n"
|
+ "showsystemmessages=true #system messages for server ops\r\n"
|
||||||
+ "showxpbarmessages=true #messages displayed above xp bar\r\n"
|
+ "showxpbarmessages=true #messages displayed above xp bar\r\n"
|
||||||
+ "handleterrainandmovements=false #requires quite more ram\r\n"
|
+ "terrainandmovements=false #uses more ram, cpu, bandwidth\r\n"
|
||||||
+ "accountlist=accounts.txt\r\n"
|
+ "accountlist=accounts.txt\r\n"
|
||||||
+ "serverlist=servers.txt\r\n"
|
+ "serverlist=servers.txt\r\n"
|
||||||
+ "playerheadicon=true\r\n"
|
+ "playerheadicon=true\r\n"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue