示例#1
0
        /// <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)
        {
            Queue<Location> result = null;

            AutoTimeout.Perform(() =>
            {
                HashSet<Location> ClosedSet = new HashSet<Location>(); // The set of locations already evaluated.
                HashSet<Location> OpenSet = new HashSet<Location>(new[] { start });  // 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)
                        List<Location> total_path = new List<Location>(new[] { current });
                        while (Came_From.ContainsKey(current))
                        {
                            current = Came_From[current];
                            total_path.Add(current);
                        }
                        total_path.Reverse();
                        result = new Queue<Location>(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)
                    }
                }
            }, TimeSpan.FromSeconds(5));

            return result;
        }
示例#2
0
 /// <summary>
 /// Get block at the specified location
 /// </summary>
 /// <param name="location">Location to retrieve block from</param>
 /// <returns>Block at specified location or Air if the location is not loaded</returns>
 public Block GetBlock(Location location)
 {
     ChunkColumn column = GetChunkColumn(location);
     if (column != null)
     {
         Chunk chunk = column.GetChunk(location);
         if (chunk != null)
             return chunk.GetBlock(location);
     }
     return new Block(Material.Air);
 }
示例#3
0
 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;
                 case "get": return handler.GetCurrentLocation().ToString();
                 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.";
 }
示例#4
0
 /// <summary>
 /// Set block at the specified location
 /// </summary>
 /// <param name="location">Location to set block to</param>
 /// <param name="block">Block to set</param>
 public void SetBlock(Location location, Block block)
 {
     ChunkColumn column = this[location.ChunkX, location.ChunkZ];
     if (column != null)
     {
         Chunk chunk = column[location.ChunkY];
         if (chunk == null)
             column[location.ChunkY] = chunk = new Chunk();
         chunk[location.ChunkBlockX, location.ChunkBlockY, location.ChunkBlockZ] = block;
     }
 }
示例#5
0
 /* ========= 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();
 }
示例#6
0
 /// <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));
 }
示例#7
0
 /* ========= 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");
     }
 }
示例#8
0
 /// <summary>
 /// Look at the specified location
 /// </summary>
 /// <param name="location">Location to look at</param>
 protected void LookAtLocation(Mapping.Location location)
 {
     Handler.UpdateLocation(Handler.GetCurrentLocation(), location);
 }
示例#9
0
 /// <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));
 }
示例#10
0
 public bool SendLocationUpdate(Location location, bool onGround)
 {
     return false; //Currently not implemented
 }
 /// <summary>
 /// Move to the specified location
 /// </summary>
 /// <param name="location">Location to reach</param>
 /// <param name="allowUnsafe">Allow possible but unsafe locations thay may hurt the player: lava, cactus...</param>
 /// <param name="allowSmallTeleport">Allow non-vanilla small teleport instead of computing path, but may cause invalid moves and/or trigger anti-cheat plugins</param>
 /// <returns>True if a path has been found</returns>
 protected bool MoveToLocation(Mapping.Location location, bool allowUnsafe = false, bool allowSmallTeleport = false)
 {
     return(Handler.MoveTo(location, allowUnsafe, allowSmallTeleport));
 }
示例#12
0
 /* ========= 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;
 }
示例#13
0
 /// <summary>
 /// Get chunk column at the specified location
 /// </summary>
 /// <param name="location">Location to retrieve chunk column</param>
 /// <returns>The chunk column</returns>
 public ChunkColumn GetChunkColumn(Location location)
 {
     return this[location.ChunkX, location.ChunkZ];
 }
示例#14
0
 /// <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;
 }
示例#15
0
 /// <summary>
 /// Called when the server sends a new player location,
 /// or if a ChatBot whishes to update the player's location.
 /// </summary>
 /// <param name="location">The new location</param>
 /// <param name="relative">If true, the location is relative to the current location</param>
 public void UpdateLocation(Location location)
 {
     UpdateLocation(location, false);
 }
示例#16
0
 /// <summary>
 /// Called when the server sends a new player location,
 /// or if a ChatBot whishes to update the player's location.
 /// </summary>
 /// <param name="location">The new location</param>
 /// <param name="relative">If true, the location is relative to the current location</param>
 public void UpdateLocation(Location location, bool relative)
 {
     lock (locationLock)
     {
         if (relative)
         {
             this.location += location;
         }
         else this.location = location;
         locationReceived = true;
     }
 }
示例#17
0
        /// <summary>
        /// Called ~10 times per second by the protocol handler
        /// </summary>
        public void OnUpdate()
        {
            foreach (var bot in bots.ToArray())
            {
                try
                {
                    bot.Update();
                    bot.ProcessQueuedText();
                }
                catch (Exception e)
                {
                    if (!(e is ThreadAbortException))
                    {
                        ConsoleIO.WriteLineFormatted("§8Update: Got error from " + bot.ToString() + ": " + e.ToString());
                    }
                    else throw; //ThreadAbortException should not be caught
                }
            }

            if (Settings.TerrainAndMovements && locationReceived)
            {
                lock (locationLock)
                {
                    for (int i = 0; i < 2; i++) //Needs to run at 20 tps; MCC runs at 10 tps
                    {
                        if (steps != null && steps.Count > 0)
                            location = steps.Dequeue();
                        else if (path != null && path.Count > 0)
                            steps = Movement.Move2Steps(location, path.Dequeue());
                        else location = Movement.HandleGravity(world, location);
                        handler.SendLocationUpdate(location, Movement.IsOnGround(world, location));
                    }
                }
            }
        }
示例#18
0
 /// <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;
     }
 }
示例#19
0
 /// <summary>
 /// Get chunk at the specified location
 /// </summary>
 /// <param name="location">Location, a modulo will be applied</param>
 /// <returns>The chunk, or null if not loaded</returns>
 public Chunk GetChunk(Location location)
 {
     return this[location.ChunkY];
 }
示例#20
0
        /// <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());
        }
示例#21
0
 /// <summary>
 /// Get block at the specified location
 /// </summary>
 /// <param name="location">Location, a modulo will be applied</param>
 /// <returns>The block</returns>
 public Block GetBlock(Location location)
 {
     return this[location.ChunkBlockX, location.ChunkBlockY, location.ChunkBlockZ];
 }
示例#22
0
 /// <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();
 }
示例#23
0
 /// <summary>
 /// Send a location update to the server
 /// </summary>
 /// <param name="location">The new location of the player</param>
 /// <param name="onGround">True if the player is on the ground</param>
 /// <returns>True if the location update was successfully sent</returns>
 public bool SendLocationUpdate(Location location, bool onGround)
 {
     if (Settings.TerrainAndMovements)
     {
         try
         {
             SendPacket(protocolversion >= MC19Version ? 0x0C : 0x04, concatBytes(
                 getDouble(location.X), getDouble(location.Y), getDouble(location.Z),
                 new byte[] { onGround ? (byte)1 : (byte)0 }));
             return true;
         }
         catch (SocketException) { return false; }
     }
     else return false;
 }
示例#24
0
 /// <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>
 /// 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>
 protected bool MoveToLocation(Mapping.Location location, bool allowUnsafe = false)
 {
     return(Handler.MoveTo(location, allowUnsafe));
 }
示例#26
0
        /// <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 });
        }