diff --git a/Spigot-API-Patches/0150-Mob-Pathfinding-API.patch b/Spigot-API-Patches/0150-Mob-Pathfinding-API.patch new file mode 100644 index 0000000000..63bdf8baba --- /dev/null +++ b/Spigot-API-Patches/0150-Mob-Pathfinding-API.patch @@ -0,0 +1,208 @@ +From 89a06baab04317f850f553f044a6fba5e35388ad Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 9 Sep 2018 12:39:06 -0400 +Subject: [PATCH] Mob Pathfinding API + +Adds an API to allow plugins to instruct a Mob to Pathfind to a Location or Entity + +This does not do anything to stop other AI rules from changing the location, so +it is still up to the plugin to control that or override after another goal changed +the location. + +You can use EntityPathfindEvent to cancel new pathfinds from overriding your current. + +diff --git a/src/main/java/com/destroystokyo/paper/entity/Pathfinder.java b/src/main/java/com/destroystokyo/paper/entity/Pathfinder.java +new file mode 100644 +index 000000000..d6953b390 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/entity/Pathfinder.java +@@ -0,0 +1,167 @@ ++package com.destroystokyo.paper.entity; ++ ++import org.bukkit.Location; ++import org.bukkit.entity.LivingEntity; ++import org.bukkit.entity.Mob; ++ ++import javax.annotation.Nonnull; ++import javax.annotation.Nullable; ++import java.util.List; ++ ++/** ++ * Handles pathfinding operations for an Entity ++ */ ++public interface Pathfinder { ++ ++ /** ++ * ++ * @return The entity that is controlled by this pathfinder ++ */ ++ Mob getEntity(); ++ ++ /** ++ * Instructs the Entity to stop trying to navigate to its current desired location ++ */ ++ void stopPathfinding(); ++ ++ /** ++ * If the entity is currently trying to navigate to a destination, this will return true ++ * @return true if the entity is navigating to a destination ++ */ ++ boolean hasPath(); ++ ++ /** ++ * @return The location the entity is trying to navigate to, or null if there is no destination ++ */ ++ @Nullable PathResult getCurrentPath(); ++ ++ /** ++ * Calculates a destination for the Entity to navigate to, but does not set it ++ * as the current target. Useful for calculating what would happen before setting it. ++ * @param loc Location to navigate to ++ * @return The closest Location the Entity can get to for this navigation, or null if no path could be calculated ++ */ ++ @Nullable PathResult findPath(Location loc); ++ ++ /** ++ * Calculates a destination for the Entity to navigate to to reach the target entity, ++ * but does not set it as the current target. ++ * Useful for calculating what would happen before setting it. ++ * ++ * The behavior of this PathResult is subject to the games pathfinding rules, and may ++ * result in the pathfinding automatically updating to follow the target Entity. ++ * ++ * However, this behavior is not guaranteed, and is subject to the games behavior. ++ * ++ * @param target the Entity to navigate to ++ * @return The closest Location the Entity can get to for this navigation, or null if no path could be calculated ++ */ ++ @Nullable PathResult findPath(LivingEntity target); ++ ++ /** ++ * Calculates a destination for the Entity to navigate to, and sets it with default speed ++ * as the current target. ++ * @param loc Location to navigate to ++ * @return If the pathfinding was successfully started ++ */ ++ default boolean moveTo(@Nonnull Location loc) { ++ return moveTo(loc, 1); ++ } ++ ++ /** ++ * Calculates a destination for the Entity to navigate to, with desired speed ++ * as the current target. ++ * @param loc Location to navigate to ++ * @param speed Speed multiplier to navigate at, where 1 is 'normal' ++ * @return If the pathfinding was successfully started ++ */ ++ default boolean moveTo(@Nonnull Location loc, double speed) { ++ PathResult path = findPath(loc); ++ return path != null && moveTo(path, speed); ++ } ++ ++ /** ++ * Calculates a destination for the Entity to navigate to to reach the target entity, ++ * and sets it with default speed. ++ * ++ * The behavior of this PathResult is subject to the games pathfinding rules, and may ++ * result in the pathfinding automatically updating to follow the target Entity. ++ * ++ * However, this behavior is not guaranteed, and is subject to the games behavior. ++ * ++ * @param target the Entity to navigate to ++ * @return If the pathfinding was successfully started ++ */ ++ default boolean moveTo(@Nonnull LivingEntity target) { ++ return moveTo(target, 1); ++ } ++ ++ /** ++ * Calculates a destination for the Entity to navigate to to reach the target entity, ++ * and sets it with specified speed. ++ * ++ * The behavior of this PathResult is subject to the games pathfinding rules, and may ++ * result in the pathfinding automatically updating to follow the target Entity. ++ * ++ * However, this behavior is not guaranteed, and is subject to the games behavior. ++ * ++ * @param target the Entity to navigate to ++ * @param speed Speed multiplier to navigate at, where 1 is 'normal' ++ * @return If the pathfinding was successfully started ++ */ ++ default boolean moveTo(@Nonnull LivingEntity target, double speed) { ++ PathResult path = findPath(target); ++ return path != null && moveTo(path, speed); ++ } ++ ++ /** ++ * Takes the result of a previous pathfinding calculation and sets it ++ * as the active pathfinding with default speed. ++ * ++ * @param path The Path to start following ++ * @return If the pathfinding was successfully started ++ */ ++ default boolean moveTo(@Nonnull PathResult path) { ++ return moveTo(path, 1); ++ } ++ ++ /** ++ * Takes the result of a previous pathfinding calculation and sets it ++ * as the active pathfinding, ++ * ++ * @param path The Path to start following ++ * @param speed Speed multiplier to navigate at, where 1 is 'normal' ++ * @return If the pathfinding was successfully started ++ */ ++ boolean moveTo(@Nonnull PathResult path, double speed); ++ ++ /** ++ * Represents the result of a pathfinding calculation ++ */ ++ interface PathResult { ++ ++ /** ++ * All currently calculated points to follow along the path to reach the destination location ++ * ++ * Will return points the entity has already moved past, see {@link #getNextPointIndex()} ++ * @return List of points ++ */ ++ List getPoints(); ++ ++ /** ++ * @return Returns the index of the current point along the points returned in {@link #getPoints()} the entity ++ * is trying to reach, or null if we are done with this pathfinding. ++ */ ++ int getNextPointIndex(); ++ ++ /** ++ * @return The next location in the path points the entity is trying to reach, or null if there is no next point ++ */ ++ @Nullable Location getNextPoint(); ++ ++ /** ++ * @return The closest point the path can get to the target location ++ */ ++ @Nullable Location getFinalPoint(); ++ } ++} +diff --git a/src/main/java/org/bukkit/entity/Mob.java b/src/main/java/org/bukkit/entity/Mob.java +index d029d34ea..48eddcd30 100644 +--- a/src/main/java/org/bukkit/entity/Mob.java ++++ b/src/main/java/org/bukkit/entity/Mob.java +@@ -7,6 +7,14 @@ import org.bukkit.loot.Lootable; + */ + public interface Mob extends LivingEntity, Lootable { + ++ // Paper start ++ /** ++ * Enables access to control the pathing of an Entity ++ * @return Pathfinding Manager for this entity ++ */ ++ com.destroystokyo.paper.entity.Pathfinder getPathfinder(); ++ // Paper end ++ + /** + * Instructs this Mob to set the specified LivingEntity as its target. + *

+-- +2.18.0 + diff --git a/Spigot-Server-Patches/0365-Mob-Pathfinding-API.patch b/Spigot-Server-Patches/0365-Mob-Pathfinding-API.patch new file mode 100644 index 0000000000..bb57a5e84b --- /dev/null +++ b/Spigot-Server-Patches/0365-Mob-Pathfinding-API.patch @@ -0,0 +1,249 @@ +From 0547a2ffa21722f1ab5d985fb7fdbc610787e4b4 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 9 Sep 2018 13:30:00 -0400 +Subject: [PATCH] Mob Pathfinding API + +Implements Pathfinding API for mobs + +diff --git a/src/main/java/com/destroystokyo/paper/entity/PaperPathfinder.java b/src/main/java/com/destroystokyo/paper/entity/PaperPathfinder.java +new file mode 100644 +index 0000000000..ed3d86ddd3 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/entity/PaperPathfinder.java +@@ -0,0 +1,113 @@ ++package com.destroystokyo.paper.entity; ++ ++import net.minecraft.server.EntityInsentient; ++import net.minecraft.server.PathEntity; ++import net.minecraft.server.PathPoint; ++import org.apache.commons.lang.Validate; ++import org.bukkit.Location; ++import org.bukkit.craftbukkit.entity.CraftLivingEntity; ++import org.bukkit.entity.LivingEntity; ++import org.bukkit.entity.Mob; ++ ++import javax.annotation.Nonnull; ++import javax.annotation.Nullable; ++import java.util.ArrayList; ++import java.util.List; ++ ++public class PaperPathfinder implements com.destroystokyo.paper.entity.Pathfinder { ++ ++ private final EntityInsentient entity; ++ ++ public PaperPathfinder(EntityInsentient entity) { ++ this.entity = entity; ++ } ++ ++ @Override ++ public Mob getEntity() { ++ return entity.getBukkitMob(); ++ } ++ ++ @Override ++ public void stopPathfinding() { ++ entity.getNavigation().stopPathfinding(); ++ } ++ ++ @Override ++ public boolean hasPath() { ++ return entity.getNavigation().getPathEntity() != null; ++ } ++ ++ @Nullable ++ @Override ++ public PathResult getCurrentPath() { ++ PathEntity path = entity.getNavigation().getPathEntity(); ++ return path != null ? new PaperPathResult(path) : null; ++ } ++ ++ @Nullable ++ @Override ++ public PathResult findPath(Location loc) { ++ Validate.notNull(loc, "Location can not be null"); ++ PathEntity path = entity.getNavigation().calculateDestination(loc.getX(), loc.getY(), loc.getZ()); ++ return path != null ? new PaperPathResult(path) : null; ++ } ++ ++ @Nullable ++ @Override ++ public PathResult findPath(LivingEntity target) { ++ Validate.notNull(target, "Target can not be null"); ++ PathEntity path = entity.getNavigation().calculateDestination(((CraftLivingEntity) target).getHandle()); ++ return path != null ? new PaperPathResult(path) : null; ++ } ++ ++ @Override ++ public boolean moveTo(@Nonnull PathResult path, double speed) { ++ Validate.notNull(path, "PathResult can not be null"); ++ PathEntity pathEntity = ((PaperPathResult) path).path; ++ return entity.getNavigation().setDestination(pathEntity, speed); ++ } ++ ++ public class PaperPathResult implements com.destroystokyo.paper.entity.PaperPathfinder.PathResult { ++ ++ private final PathEntity path; ++ PaperPathResult(PathEntity path) { ++ this.path = path; ++ } ++ ++ @Nullable ++ @Override ++ public Location getFinalPoint() { ++ PathPoint point = path.getFinalPoint(); ++ return point != null ? toLoc(point) : null; ++ } ++ ++ @Override ++ public List getPoints() { ++ int pathCount = path.getPathCount(); ++ List points = new ArrayList<>(); ++ PathPoint[] pathPoints = path.getPoints(); ++ for (int i = 0; i < pathCount; i++) { ++ points.add(toLoc(pathPoints[i])); ++ } ++ return points; ++ } ++ ++ @Override ++ public int getNextPointIndex() { ++ return path.getNextIndex(); ++ } ++ ++ @Nullable ++ @Override ++ public Location getNextPoint() { ++ if (!path.hasNext()) { ++ return null; ++ } ++ return toLoc(path.getPoints()[path.getNextIndex()]); ++ } ++ } ++ ++ private Location toLoc(PathPoint point) { ++ return new Location(entity.world.getWorld(), point.getX(), point.getY(), point.getZ()); ++ } ++} +diff --git a/src/main/java/net/minecraft/server/NavigationAbstract.java b/src/main/java/net/minecraft/server/NavigationAbstract.java +index 452da80f11..4023253f42 100644 +--- a/src/main/java/net/minecraft/server/NavigationAbstract.java ++++ b/src/main/java/net/minecraft/server/NavigationAbstract.java +@@ -63,7 +63,7 @@ public abstract class NavigationAbstract { + } + + @Nullable +- public final PathEntity a(double d0, double d1, double d2) { ++ public final PathEntity calculateDestination(double d0, double d1, double d2) { return a(d0, d1, d2); } @Nullable public final PathEntity a(double d0, double d1, double d2) { // Paper - OBFHELPER + return this.b(new BlockPosition(d0, d1, d2)); + } + +@@ -89,7 +89,7 @@ public abstract class NavigationAbstract { + } + + @Nullable +- public PathEntity a(Entity entity) { ++ public PathEntity calculateDestination(Entity entity) { return a(entity); } @Nullable public PathEntity a(Entity entity) { // Paper - OBFHELPER + if (!this.b()) { + return null; + } else { +@@ -138,6 +138,7 @@ public abstract class NavigationAbstract { + private int pathfindFailures = 0; + // Paper end + ++ public boolean setDestination(@Nullable PathEntity pathentity, double speed) { return a(pathentity, speed); } // Paper - OBFHELPER + public boolean a(@Nullable PathEntity pathentity, double d0) { + if (pathentity == null) { + this.c = null; +@@ -159,8 +160,7 @@ public abstract class NavigationAbstract { + } + } + } +- +- @Nullable ++ @Nullable public PathEntity getPathEntity() { return m(); } @Nullable // Paper - OBFHELPER + public PathEntity m() { + return this.c; + } +@@ -261,6 +261,7 @@ public abstract class NavigationAbstract { + return this.c == null || this.c.b(); + } + ++ public void stopPathfinding() { q(); } // Paper - OBFHELPER + public void q() { + this.pathfindFailures = 0; this.lastFailure = 0; // Paper - Pathfinding optimizations + this.c = null; +diff --git a/src/main/java/net/minecraft/server/PathEntity.java b/src/main/java/net/minecraft/server/PathEntity.java +index 5ffcda6d56..dae08cbbf8 100644 +--- a/src/main/java/net/minecraft/server/PathEntity.java ++++ b/src/main/java/net/minecraft/server/PathEntity.java +@@ -3,12 +3,13 @@ package net.minecraft.server; + import javax.annotation.Nullable; + + public class PathEntity { +- private final PathPoint[] a; ++ private final PathPoint[] a; public PathPoint[] getPoints() { return a; } // Paper - OBFHELPER + private PathPoint[] b = new PathPoint[0]; + private PathPoint[] c = new PathPoint[0]; + private PathPoint d; +- private int e; +- private int f; ++ private int e; public int getNextIndex() { return e; } // Paper - OBFHELPER ++ private int f; public int getPathCount() { return f; } // Paper - OBFHELPER ++ public boolean hasNext() { return getNextIndex() < getPathCount(); } // Paper + + public PathEntity(PathPoint[] apathpoint) { + this.a = apathpoint; +@@ -24,7 +25,7 @@ public class PathEntity { + } + + @Nullable +- public PathPoint c() { ++ public PathPoint getFinalPoint() { return c(); } @Nullable public PathPoint c() { // Paper - OBFHELPER + return this.f > 0 ? this.a[this.f - 1] : null; + } + +@@ -63,7 +64,7 @@ public class PathEntity { + return this.a(entity, this.e); + } + +- public Vec3D f() { ++ public Vec3D getNext() { return f(); } public Vec3D f() { // Paper - OBFHELPER + PathPoint pathpoint = this.a[this.e]; + return new Vec3D((double)pathpoint.a, (double)pathpoint.b, (double)pathpoint.c); + } +diff --git a/src/main/java/net/minecraft/server/PathPoint.java b/src/main/java/net/minecraft/server/PathPoint.java +index 497f472233..7fd1eff0b5 100644 +--- a/src/main/java/net/minecraft/server/PathPoint.java ++++ b/src/main/java/net/minecraft/server/PathPoint.java +@@ -1,9 +1,9 @@ + package net.minecraft.server; + + public class PathPoint { +- public final int a; +- public final int b; +- public final int c; ++ public final int a; public final int getX() { return a; } // Paper - OBFHELPER ++ public final int b; public final int getY() { return b; } // Paper - OBFHELPER ++ public final int c; public final int getZ() { return c; } // Paper - OBFHELPER + private final int n; + public int d = -1; + public float e; +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +index 5bf1cd06fa..53c2d154ed 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +@@ -12,8 +12,11 @@ import org.bukkit.loot.LootTable; + public abstract class CraftMob extends CraftLivingEntity implements Mob { + public CraftMob(CraftServer server, EntityInsentient entity) { + super(server, entity); ++ paperPathfinder = new com.destroystokyo.paper.entity.PaperPathfinder(entity); // Paper + } + ++ private final com.destroystokyo.paper.entity.PaperPathfinder paperPathfinder; // Paper ++ @Override public com.destroystokyo.paper.entity.Pathfinder getPathfinder() { return paperPathfinder; } // Paper + @Override + public void setTarget(LivingEntity target) { + EntityInsentient entity = getHandle(); +-- +2.18.0 +