getPositions();
}
diff --git a/pathetic-api/src/main/java/org/patheloper/api/pathing/rules/PathingRuleSet.java b/pathetic-api/src/main/java/org/patheloper/api/pathing/rules/PathingRuleSet.java
deleted file mode 100644
index 82a5940b..00000000
--- a/pathetic-api/src/main/java/org/patheloper/api/pathing/rules/PathingRuleSet.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package org.patheloper.api.pathing.rules;
-
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import lombok.Value;
-import lombok.With;
-
-/**
- * Configuration options for pathfinding.
- *
- * This class defines a set of rules that guide the behavior of the pathfinding process.
- *
- *
- `maxIterations`: The maximum number of iterations allowed during pathfinding. Set this to
- * prevent infinite loops.
- *
- *
- `maxLength`: The maximum length of the path. Avoid setting this too high as it can cause
- * performance issues.
- *
- *
- `async`: Whether to run pathfinding asynchronously or not.
- *
- *
- `allowingDiagonal`: Whether to allow diagonal movement when pathfinding.
- *
- *
- `allowingFailFast`: Whether to fail fast if the target is unreachable from the start.
- *
- *
- `allowingFallback`: If pathfinding fails, whether to fall back to the previously found path.
- *
- *
- `loadingChunks`: Whether to load or generate chunks during pathfinding.
- *
- *
- `counterCheck`: Whether to run a counter check on the path if it's not found to validate the
- * result. Note: `counterCheck` is a fallback mechanism that reevaluates the entire path from end to
- * beginning.
- */
-@With
-@Value
-@Getter
-@Builder(toBuilder = true, access = AccessLevel.PRIVATE)
-@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
-public class PathingRuleSet {
-
- @Builder.Default int maxIterations = 5000; // to avoid freewheeling
- int maxLength;
- boolean async;
- @Builder.Default boolean allowingDiagonal = true;
- boolean allowingFailFast;
- boolean allowingFallback;
- boolean loadingChunks;
- boolean counterCheck;
-
- /**
- * @return A new {@link PathingRuleSet} with default values but async.
- */
- public static PathingRuleSet createAsyncRuleSet() {
- return builder().async(true).build();
- }
-
- /**
- * @return A new {@link PathingRuleSet} with default values.
- */
- public static PathingRuleSet createRuleSet() {
- return builder().build();
- }
-
- /**
- * Creates a deep copy of the given {@link PathingRuleSet}.
- *
- *
This method constructs a new instance of {@link PathingRuleSet} with the same values
- * as the input. It ensures a deep copy by copying the values of primitive and boolean fields directly.
- *
- * @param pathingRuleSet The {@link PathingRuleSet} to copy.
- * @return A new {@link PathingRuleSet} instance with the same values as the input.
- */
- public static PathingRuleSet deepCopy(PathingRuleSet pathingRuleSet) {
- return builder()
- .maxIterations(pathingRuleSet.maxIterations)
- .maxLength(pathingRuleSet.maxLength)
- .async(pathingRuleSet.async)
- .allowingDiagonal(pathingRuleSet.allowingDiagonal)
- .allowingFailFast(pathingRuleSet.allowingFailFast)
- .allowingFallback(pathingRuleSet.allowingFallback)
- .loadingChunks(pathingRuleSet.loadingChunks)
- .counterCheck(pathingRuleSet.counterCheck)
- .build();
- }
-}
diff --git a/pathetic-api/src/main/java/org/patheloper/api/pathing/strategy/strategies/BoatStrategy.java b/pathetic-api/src/main/java/org/patheloper/api/pathing/strategy/strategies/BoatStrategy.java
new file mode 100644
index 00000000..7a87d497
--- /dev/null
+++ b/pathetic-api/src/main/java/org/patheloper/api/pathing/strategy/strategies/BoatStrategy.java
@@ -0,0 +1,23 @@
+package org.patheloper.api.pathing.strategy.strategies;
+
+import org.bukkit.Material;
+import org.patheloper.api.pathing.strategy.PathValidationContext;
+import org.patheloper.api.pathing.strategy.PathfinderStrategy;
+import org.patheloper.api.snapshot.SnapshotManager;
+import org.patheloper.api.wrapper.PathPosition;
+
+public class BoatStrategy implements PathfinderStrategy {
+
+ @Override
+ public boolean isValid(PathValidationContext pathValidationContext) {
+ SnapshotManager snapshotManager = pathValidationContext.getSnapshotManager();
+ PathPosition pathPosition = pathValidationContext.getPosition();
+
+ return snapshotManager
+ .getBlock(pathPosition.subtract(0, 1, 0))
+ .getBlockInformation()
+ .getMaterial()
+ == Material.WATER
+ && snapshotManager.getBlock(pathPosition).isPassable();
+ }
+}
diff --git a/pathetic-api/src/main/java/org/patheloper/api/pathing/strategy/strategies/JumpablePathfinderStrategy.java b/pathetic-api/src/main/java/org/patheloper/api/pathing/strategy/strategies/JumpablePathfinderStrategy.java
index 29c743c9..c7a21f78 100644
--- a/pathetic-api/src/main/java/org/patheloper/api/pathing/strategy/strategies/JumpablePathfinderStrategy.java
+++ b/pathetic-api/src/main/java/org/patheloper/api/pathing/strategy/strategies/JumpablePathfinderStrategy.java
@@ -48,8 +48,21 @@ public boolean isValid(@NonNull PathValidationContext pathValidationContext) {
return true;
}
- if ((position.getBlockY() - lastValidPosition.getBlockY()) > jumpHeight) return false;
+ int heightDiff = position.getBlockY() - lastValidPosition.getBlockY();
+ if (isJumpingDown(position, lastValidPosition)) {
+ heightDiff *= -1;
+ }
+
+ if (heightDiff > jumpHeight) {
+ return false;
+ }
return startBlock.isPassable() && position.distance(lastValidPosition) <= maxJumpDistance;
}
+
+ private boolean isJumpingDown(PathPosition position, PathPosition lastValidPosition) {
+ return position.getBlockX() == lastValidPosition.getBlockX()
+ && position.getBlockZ() == lastValidPosition.getBlockZ()
+ && position.getBlockY() < lastValidPosition.getBlockY();
+ }
}
diff --git a/pathetic-api/src/main/java/org/patheloper/api/pathing/strategy/strategies/PlayerWalkableStrategy.java b/pathetic-api/src/main/java/org/patheloper/api/pathing/strategy/strategies/PlayerWalkableStrategy.java
new file mode 100644
index 00000000..3cdbf830
--- /dev/null
+++ b/pathetic-api/src/main/java/org/patheloper/api/pathing/strategy/strategies/PlayerWalkableStrategy.java
@@ -0,0 +1,27 @@
+package org.patheloper.api.pathing.strategy.strategies;
+
+import org.bukkit.Material;
+import org.patheloper.api.pathing.strategy.PathValidationContext;
+import org.patheloper.api.snapshot.SnapshotManager;
+import org.patheloper.api.wrapper.PathPosition;
+
+public class PlayerWalkableStrategy extends WalkablePathfinderStrategy {
+
+ @Override
+ public boolean isValid(PathValidationContext pathValidationContext) {
+ PathPosition pathPosition = pathValidationContext.getPosition();
+ SnapshotManager snapshotManager = pathValidationContext.getSnapshotManager();
+
+ if (snapshotManager.getBlock(pathPosition).isPassable()
+ && canStandOn(snapshotManager.getBlock(pathPosition.subtract(0, 1, 0)), snapshotManager)) {
+ return true;
+ }
+
+ if (snapshotManager.getBlock(pathPosition.subtract(0, 1, 0)).getBlockInformation().getMaterial()
+ == Material.WATER
+ && snapshotManager.getBlock(pathPosition).isPassable()) return true;
+
+ return snapshotManager.getBlock(pathPosition).getBlockInformation().getMaterial()
+ == Material.WATER;
+ }
+}
diff --git a/pathetic-api/src/main/java/org/patheloper/api/snapshot/NMSInterface.java b/pathetic-api/src/main/java/org/patheloper/api/snapshot/NMSInterface.java
index d94d53fb..b7390fab 100644
--- a/pathetic-api/src/main/java/org/patheloper/api/snapshot/NMSInterface.java
+++ b/pathetic-api/src/main/java/org/patheloper/api/snapshot/NMSInterface.java
@@ -2,6 +2,7 @@
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
/** This is for internal purpose only and is used to receive a ChunkSnapshot version-independent. */
public interface NMSInterface {
@@ -15,4 +16,7 @@ public interface NMSInterface {
* @return The {@link ChunkSnapshot} of the chunk at the given coordinates
*/
ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ);
+
+ /** Get the block state from a chunk snapshot at the given coordinates */
+ BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z);
}
diff --git a/pathetic-api/src/main/java/org/patheloper/api/wrapper/PathEnvironment.java b/pathetic-api/src/main/java/org/patheloper/api/wrapper/PathEnvironment.java
index b7a9b238..dc280ec0 100644
--- a/pathetic-api/src/main/java/org/patheloper/api/wrapper/PathEnvironment.java
+++ b/pathetic-api/src/main/java/org/patheloper/api/wrapper/PathEnvironment.java
@@ -1,7 +1,6 @@
package org.patheloper.api.wrapper;
import java.util.UUID;
-import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
@@ -10,13 +9,31 @@
@Value
@Getter
@ToString
-@EqualsAndHashCode
@RequiredArgsConstructor
/** Represents the pathing environment */
public class PathEnvironment {
UUID uuid;
String name;
- @EqualsAndHashCode.Exclude Integer minHeight;
- @EqualsAndHashCode.Exclude Integer maxHeight;
+ Integer minHeight;
+ Integer maxHeight;
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (!(o instanceof PathEnvironment)) return false;
+
+ PathEnvironment other = (PathEnvironment) o;
+ if (this.name.length() != other.name.length()) return false; // early exit
+
+ return this.name.equals(other.name);
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 59;
+ int result = 1;
+ result = result * PRIME + this.name.hashCode();
+ return result;
+ }
}
diff --git a/pathetic-api/src/main/java/org/patheloper/api/wrapper/PathPosition.java b/pathetic-api/src/main/java/org/patheloper/api/wrapper/PathPosition.java
index b01c0022..9b8bb503 100644
--- a/pathetic-api/src/main/java/org/patheloper/api/wrapper/PathPosition.java
+++ b/pathetic-api/src/main/java/org/patheloper/api/wrapper/PathPosition.java
@@ -1,14 +1,13 @@
package org.patheloper.api.wrapper;
+import java.util.Objects;
import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import org.patheloper.api.util.NumberUtils;
@AllArgsConstructor
-@EqualsAndHashCode
@Getter
@ToString
public class PathPosition implements Cloneable {
@@ -252,4 +251,20 @@ public PathPosition clone() {
clone.z = this.z;
return clone;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PathPosition that = (PathPosition) o;
+ return x == that.x
+ && y == that.y
+ && z == that.z
+ && Objects.equals(pathEnvironment, that.pathEnvironment);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(pathEnvironment, x, y, z);
+ }
}
diff --git a/pathetic-example/pom.xml b/pathetic-example/pom.xml
index ff9577e0..744e31cd 100644
--- a/pathetic-example/pom.xml
+++ b/pathetic-example/pom.xml
@@ -7,7 +7,7 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
pathetic-example
@@ -35,7 +35,7 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.5.1
+ 3.5.2
package
@@ -73,7 +73,7 @@
org.patheloper
pathetic-mapping
- 2.3
+ 2.4
diff --git a/pathetic-example/src/main/java/org/patheloper/example/PatheticPlugin.java b/pathetic-example/src/main/java/org/patheloper/example/PatheticPlugin.java
index 8e07eb82..a4e1f9e2 100644
--- a/pathetic-example/src/main/java/org/patheloper/example/PatheticPlugin.java
+++ b/pathetic-example/src/main/java/org/patheloper/example/PatheticPlugin.java
@@ -2,7 +2,7 @@
import org.bukkit.plugin.java.JavaPlugin;
import org.patheloper.api.pathing.Pathfinder;
-import org.patheloper.api.pathing.rules.PathingRuleSet;
+import org.patheloper.api.pathing.configuration.PathingRuleSet;
import org.patheloper.example.command.PatheticCommand;
import org.patheloper.mapping.PatheticMapper;
@@ -25,4 +25,10 @@ public void onEnable() {
getCommand("pathetic").setExecutor(new PatheticCommand(reusablePathfinder));
}
+
+ @Override
+ public void onDisable() {
+ PatheticMapper
+ .shutdown(); // This is very important to clear any resources Pathetic still holds on
+ }
}
diff --git a/pathetic-mapping/pom.xml b/pathetic-mapping/pom.xml
index cc06907e..65abf6ae 100644
--- a/pathetic-mapping/pom.xml
+++ b/pathetic-mapping/pom.xml
@@ -5,12 +5,12 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
4.0.0
pathetic-mapping
- 2.3
+ 2.4
8
@@ -22,7 +22,7 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.5.1
+ 3.5.2
package
@@ -39,7 +39,7 @@
org.patheloper
pathetic-model
- 2.3
+ 2.4
compile
diff --git a/pathetic-mapping/src/main/java/org/patheloper/mapping/PatheticMapper.java b/pathetic-mapping/src/main/java/org/patheloper/mapping/PatheticMapper.java
index 85f36966..56e10392 100644
--- a/pathetic-mapping/src/main/java/org/patheloper/mapping/PatheticMapper.java
+++ b/pathetic-mapping/src/main/java/org/patheloper/mapping/PatheticMapper.java
@@ -5,7 +5,7 @@
import org.bukkit.plugin.java.JavaPlugin;
import org.patheloper.Pathetic;
import org.patheloper.api.pathing.Pathfinder;
-import org.patheloper.api.pathing.rules.PathingRuleSet;
+import org.patheloper.api.pathing.configuration.PathingRuleSet;
import org.patheloper.model.pathing.pathfinder.AStarPathfinder;
import org.patheloper.util.ErrorLogger;
@@ -14,16 +14,24 @@
public class PatheticMapper {
/**
- * Initializes the Lib. If the lib is not initialized yet but is used anyways, this will cause
- * many things to break.
- *
+ * @apiNote If Pathetic is not initialized yet but is used anyways, this will cause many things to
+ * break.
* @param javaPlugin the JavaPlugin which initializes the lib
- * @throws IllegalStateException If an attempt is made to initialize more than 1 time
+ * @throws IllegalStateException If an attempt is made to initialize more than once
*/
public void initialize(JavaPlugin javaPlugin) {
Pathetic.initialize(javaPlugin);
}
+ /**
+ * Signals Pathetic to initiate its shutdown process, releasing resources and finalizing
+ * operations. This method should be called when Pathetic is no longer needed or the plugin is
+ * being disabled.
+ */
+ public void shutdown() {
+ Pathetic.shutdown();
+ }
+
/**
* Instantiates a new pathfinder object.
*
@@ -44,6 +52,6 @@ public void initialize(JavaPlugin javaPlugin) {
public @NonNull Pathfinder newPathfinder(PathingRuleSet pathingRuleSet) {
if (Pathetic.isInitialized()) return new AStarPathfinder(pathingRuleSet);
- throw ErrorLogger.logFatalError("Pathetic is not initialized");
+ throw ErrorLogger.logFatalError("Pathetic is not initialized yet.");
}
}
diff --git a/pathetic-model/pom.xml b/pathetic-model/pom.xml
index 621eb4f7..080bb39c 100644
--- a/pathetic-model/pom.xml
+++ b/pathetic-model/pom.xml
@@ -7,11 +7,11 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
pathetic-model
- 2.3
+ 2.4
8
@@ -30,7 +30,7 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.5.1
+ 3.5.2
@@ -78,7 +78,7 @@
org.apache.logging.log4j
log4j-core
- 2.22.1
+ 2.23.0
provided
@@ -90,13 +90,13 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
compile
org.patheloper
pathetic-nms
- 2.3
+ 2.4
compile
diff --git a/pathetic-model/src/main/java/org/patheloper/Pathetic.java b/pathetic-model/src/main/java/org/patheloper/Pathetic.java
index 801e9716..ed2f93e0 100644
--- a/pathetic-model/src/main/java/org/patheloper/Pathetic.java
+++ b/pathetic-model/src/main/java/org/patheloper/Pathetic.java
@@ -2,7 +2,9 @@
import java.io.IOException;
import java.io.InputStream;
+import java.util.HashSet;
import java.util.Properties;
+import java.util.Set;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
@@ -16,6 +18,8 @@ public class Pathetic {
private static final String PROPERTIES_FILE = "pathetic.properties";
+ private static final Set SHUTDOWN_LISTENERS = new HashSet<>();
+
private static JavaPlugin instance;
@Getter private static String modelVersion;
@@ -33,16 +37,21 @@ public static void initialize(JavaPlugin javaPlugin) {
loadModelVersion();
- if (BukkitVersionUtil.getVersion().isUnder(13, 0))
+ if (BukkitVersionUtil.getVersion().isUnder(16, 0)
+ || BukkitVersionUtil.getVersion().isEqual(BukkitVersionUtil.Version.of(16, 0)))
javaPlugin
.getLogger()
.warning(
- "pathetic is currently running in a version older than 1.13. "
- + "Some functionalities might not be accessible, such as accessing the BlockState of certain blocks.");
+ "pathetic is currently running in a version older than or equal to 1.16. "
+ + "Some functionalities might not be accessible, such as accessing the BlockState of blocks.");
javaPlugin.getLogger().info("pathetic successfully initialized");
}
+ public static void shutdown() {
+ SHUTDOWN_LISTENERS.forEach(Runnable::run);
+ }
+
public static boolean isInitialized() {
return instance != null;
}
@@ -51,6 +60,10 @@ public static JavaPlugin getPluginInstance() {
return instance;
}
+ public static void addShutdownListener(Runnable listener) {
+ SHUTDOWN_LISTENERS.add(listener);
+ }
+
private static void loadModelVersion() {
try (InputStream inputStream =
Pathetic.class.getClassLoader().getResourceAsStream(PROPERTIES_FILE)) {
diff --git a/pathetic-model/src/main/java/org/patheloper/model/pathing/Node.java b/pathetic-model/src/main/java/org/patheloper/model/pathing/Node.java
index fe8e0d2c..28dc1cff 100644
--- a/pathetic-model/src/main/java/org/patheloper/model/pathing/Node.java
+++ b/pathetic-model/src/main/java/org/patheloper/model/pathing/Node.java
@@ -2,35 +2,29 @@
import lombok.EqualsAndHashCode;
import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import org.patheloper.api.pathing.configuration.HeuristicWeights;
import org.patheloper.api.wrapper.PathPosition;
import org.patheloper.api.wrapper.PathVector;
import org.patheloper.util.ComputingCache;
@Getter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+@RequiredArgsConstructor
public class Node implements Comparable {
- private static final double MANHATTAN_WEIGHT = 0.6;
- private static final double OCTILE_WEIGHT = 0.3;
- private static final double PERPENDICULAR_WEIGHT = 0.1;
-
- private final Integer depth;
@EqualsAndHashCode.Include private final PathPosition position;
- private final PathPosition target;
private final PathPosition start;
- private final ComputingCache heuristic = new ComputingCache<>(this::heuristic);
- private Node parent;
+ private final PathPosition target;
+ private final HeuristicWeights heuristicWeights;
+ private final Integer depth;
- public Node(PathPosition position, PathPosition start, PathPosition target, Integer depth) {
- this.position = position;
- this.target = target;
- this.start = start;
- this.depth = depth;
- }
+ private final ComputingCache fCostCache = new ComputingCache<>(this::calculateFCost);
+ private final ComputingCache gCostCache = new ComputingCache<>(this::calculateGCost);
+ private final ComputingCache heuristic = new ComputingCache<>(this::heuristic);
- public void setParent(Node parent) {
- this.parent = parent;
- }
+ @Setter private Node parent;
public boolean isTarget() {
return this.position.getBlockX() == target.getBlockX()
@@ -38,14 +32,47 @@ public boolean isTarget() {
&& this.position.getBlockZ() == target.getBlockZ();
}
+ /**
+ * Calculates the estimated total cost of the path from the start node to the goal node, passing
+ * through this node.
+ *
+ * @return the estimated total cost (represented by the F-Score)
+ */
+ private double getFCost() {
+ return fCostCache.get();
+ }
+
+ /**
+ * The accumulated cost (also known as G-Score) from the starting node to the current node. This
+ * value represents the actual (known) cost of traversing the path to the current node. It is
+ * typically calculated by summing the movement costs from the start node to the current node.
+ */
+ private double getGCost() {
+ return gCostCache.get();
+ }
+
+ private double calculateFCost() {
+ return getGCost() + heuristic.get();
+ }
+
+ private double calculateGCost() {
+ if (parent == null) {
+ return 0;
+ }
+ return parent.getGCost() + position.distance(parent.position);
+ }
+
private double heuristic() {
double manhattanDistance = this.position.manhattanDistance(target);
double octileDistance = this.position.octileDistance(target);
double perpendicularDistance = calculatePerpendicularDistance();
+ double heightFactor =
+ Math.abs(this.position.getBlockY() - target.getBlockY()); // Consider height differences
- return manhattanDistance * MANHATTAN_WEIGHT
- + octileDistance * OCTILE_WEIGHT
- + perpendicularDistance * PERPENDICULAR_WEIGHT;
+ return (manhattanDistance * heuristicWeights.getManhattenWeight())
+ + (octileDistance * heuristicWeights.getOctileWeight())
+ + (perpendicularDistance * heuristicWeights.getPerpendicularWeight())
+ + (heightFactor * heuristicWeights.getHeightWeight());
}
private double calculatePerpendicularDistance() {
diff --git a/pathetic-model/src/main/java/org/patheloper/model/pathing/pathfinder/AStarPathfinder.java b/pathetic-model/src/main/java/org/patheloper/model/pathing/pathfinder/AStarPathfinder.java
index a48ac466..022fd40c 100644
--- a/pathetic-model/src/main/java/org/patheloper/model/pathing/pathfinder/AStarPathfinder.java
+++ b/pathetic-model/src/main/java/org/patheloper/model/pathing/pathfinder/AStarPathfinder.java
@@ -1,10 +1,22 @@
package org.patheloper.model.pathing.pathfinder;
+import com.google.common.hash.BloomFilter;
+import com.google.common.hash.Funnels;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.PriorityQueue;
+import java.util.Set;
import lombok.NonNull;
+import org.patheloper.api.pathing.configuration.PathingRuleSet;
import org.patheloper.api.pathing.result.Path;
import org.patheloper.api.pathing.result.PathState;
import org.patheloper.api.pathing.result.PathfinderResult;
-import org.patheloper.api.pathing.rules.PathingRuleSet;
import org.patheloper.api.pathing.strategy.PathValidationContext;
import org.patheloper.api.pathing.strategy.PathfinderStrategy;
import org.patheloper.api.wrapper.PathPosition;
@@ -14,20 +26,52 @@
import org.patheloper.model.pathing.result.PathImpl;
import org.patheloper.model.pathing.result.PathfinderResultImpl;
import org.patheloper.util.ErrorLogger;
+import org.patheloper.util.ExpiringHashMap;
+import org.patheloper.util.Tuple3;
import org.patheloper.util.WatchdogUtil;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.PriorityQueue;
-import java.util.Set;
-
/** A pathfinder that uses the A* algorithm. */
public class AStarPathfinder extends AbstractPathfinder {
+ /**
+ * Defines the size of a single cell in the pathfinding grid. A smaller value creates a more
+ * granular grid, allowing for more precise pathfinding but potentially increasing memory usage.
+ */
+ private static final int DEFAULT_GRID_CELL_SIZE = 12;
+
+ /**
+ * Determines the size of the Bloom filter used within each GridRegionData object. A larger size
+ * reduces the chance of false positives (incorrectly reporting a position as examined) but
+ * increases memory consumption.
+ */
+ private static final int DEFAULT_BLOOM_FILTER_SIZE = 1000;
+
+ /**
+ * Sets the false positive probability (FPP) for the Bloom filters. A lower FPP means a smaller
+ * chance of incorrectly reporting a position as examined, but it also requires a larger Bloom
+ * filter size.
+ */
+ private static final double DEFAULT_FPP = 0.01; // 1% false positive probability
+
+ /**
+ * Employs a grid-based optimization strategy for efficient pathfinding in the Minecraft world.
+ * This involves dividing the world into smaller cells to improve performance. Key reasons for
+ * adopting gridding include:
+ *
+ *
+ * - **Spatial Partitioning:** Divides the world into smaller, manageable regions, enabling
+ * faster retrieval of examined positions and obstacle information.
+ *
- **Locality Exploitation:** Encourages the A* algorithm to prioritize exploration of
+ * neighboring positions, as adjacent nodes often fall within the same or neighboring grid
+ * cells.
+ *
- **Memory Management:** Facilitates efficient memory usage in conjunction with the
+ * `ExpiringHashMap`. Grid regions that are no longer actively needed for pathfinding are
+ * automatically removed, optimizing memory allocation.
+ *
+ */
+ private final Map, ExpiringHashMap.Entry> gridMap =
+ new ExpiringHashMap<>();
+
public AStarPathfinder(PathingRuleSet pathingRuleSet) {
super(pathingRuleSet);
}
@@ -35,7 +79,9 @@ public AStarPathfinder(PathingRuleSet pathingRuleSet) {
@Override
protected PathfinderResult resolvePath(
PathPosition start, PathPosition target, PathfinderStrategy strategy) {
- Node startNode = new Node(start.floor(), start.floor(), target.floor(), 0);
+ Node startNode =
+ new Node(
+ start.floor(), start.floor(), target.floor(), pathingRuleSet.getHeuristicWeights(), 0);
PriorityQueue nodeQueue = new PriorityQueue<>(Collections.singleton(startNode));
Set examinedPositions = new HashSet<>();
@@ -187,7 +233,7 @@ private boolean isNodeValid(
Set examinedPositions,
PathfinderStrategy strategy,
boolean allowingDiagonal) {
- if (isNodeInvalid(newNode, nodeQueue, examinedPositions, strategy)) return false;
+ if (isNodeInvalid(newNode, nodeQueue, strategy)) return false;
/*
* So at this point there is nothing wrong with the node itself, We can move to it technically.
@@ -252,9 +298,8 @@ private boolean isReachable(Node from, Node to, PathfinderStrategy strategy) {
* Return whether the height difference between the given nodes is passable. If the nodes have no
* height difference, this will always return true.
*/
- private boolean isHeightDifferencePassable(
- Node from, Node to, PathVector vector1, boolean hasYDifference) {
- if (!hasYDifference) return true;
+ private boolean isHeightDifferencePassable(Node from, Node to, PathVector vector1, boolean hasHeightDifference) {
+ if(!hasHeightDifference) return true;
int yDifference = from.getPosition().getBlockY() - to.getPosition().getBlockY();
Node neighbour3 = createNeighbourNode(from, vector1.add(new PathVector(0, yDifference, 0)));
@@ -323,27 +368,51 @@ private Node createNeighbourNode(Node currentNode, PathVector offset) {
currentNode.getPosition().add(offset),
currentNode.getStart(),
currentNode.getTarget(),
+ pathingRuleSet.getHeuristicWeights(),
currentNode.getDepth() + 1);
newNode.setParent(currentNode);
return newNode;
}
/**
- * @return whether the given node is invalid or not
+ * Checks if a node is valid for inclusion in a path. This is where the majority of the
+ * pathfinding logic and world interaction happens.
*/
private boolean isNodeInvalid(
- Node node,
- Collection nodeQueue,
- Set examinedPositions,
- PathfinderStrategy strategy) {
- return examinedPositions.contains(node.getPosition())
+ Node node, Collection nodeQueue, PathfinderStrategy strategy) {
+
+ int gridX = node.getPosition().getBlockX() / DEFAULT_GRID_CELL_SIZE;
+ int gridY = node.getPosition().getBlockY() / DEFAULT_GRID_CELL_SIZE;
+ int gridZ = node.getPosition().getBlockZ() / DEFAULT_GRID_CELL_SIZE;
+
+ GridRegionData regionData =
+ gridMap
+ .computeIfAbsent(
+ new Tuple3<>(gridX, gridY, gridZ),
+ k -> new ExpiringHashMap.Entry<>(new GridRegionData()))
+ .getValue();
+
+ regionData.regionalExaminedPositions.add(node.getPosition());
+
+ // Bloom filter for a quick membership test
+ if (regionData.bloomFilter.mightContain(pathPositionToBloomFilterKey(node.getPosition()))) {
+ // If potentially in the set, check the definitive HashSet
+ if (regionData.regionalExaminedPositions.contains(node.getPosition())) {
+ return true; // Node is already examined, so it's invalid
+ }
+ }
+
+ return !isWithinWorldBounds(node.getPosition())
|| nodeQueue.contains(node)
- || !isWithinWorldBounds(node.getPosition())
|| !strategy.isValid(
new PathValidationContext(
node.getPosition(), node.getParent().getPosition(), snapshotManager));
}
+ private String pathPositionToBloomFilterKey(PathPosition position) {
+ return position.getBlockX() + "," + position.getBlockY() + "," + position.getBlockZ();
+ }
+
/** Traces the path from the given node by retracing the steps from the node's parent. */
private List tracePathFromNode(Node endNode) {
List path = new ArrayList<>();
@@ -357,4 +426,18 @@ private List tracePathFromNode(Node endNode) {
Collections.reverse(path); // make it the right order
return path;
}
+
+ private class GridRegionData {
+ private final BloomFilter bloomFilter;
+ private final Set regionalExaminedPositions;
+
+ public GridRegionData() {
+ bloomFilter =
+ BloomFilter.create(
+ Funnels.stringFunnel(Charset.defaultCharset()),
+ DEFAULT_BLOOM_FILTER_SIZE,
+ DEFAULT_FPP);
+ regionalExaminedPositions = new HashSet<>();
+ }
+ }
}
diff --git a/pathetic-model/src/main/java/org/patheloper/model/pathing/pathfinder/AbstractPathfinder.java b/pathetic-model/src/main/java/org/patheloper/model/pathing/pathfinder/AbstractPathfinder.java
index 747920fb..4f100f96 100644
--- a/pathetic-model/src/main/java/org/patheloper/model/pathing/pathfinder/AbstractPathfinder.java
+++ b/pathetic-model/src/main/java/org/patheloper/model/pathing/pathfinder/AbstractPathfinder.java
@@ -1,6 +1,5 @@
package org.patheloper.model.pathing.pathfinder;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
@@ -8,10 +7,8 @@
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
-import java.util.concurrent.Executor;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.logging.Logger;
import lombok.NonNull;
import org.bukkit.event.Cancellable;
@@ -20,9 +17,9 @@
import org.patheloper.api.event.PathingFinishedEvent;
import org.patheloper.api.event.PathingStartFindEvent;
import org.patheloper.api.pathing.Pathfinder;
+import org.patheloper.api.pathing.configuration.PathingRuleSet;
import org.patheloper.api.pathing.result.PathState;
import org.patheloper.api.pathing.result.PathfinderResult;
-import org.patheloper.api.pathing.rules.PathingRuleSet;
import org.patheloper.api.pathing.strategy.PathfinderStrategy;
import org.patheloper.api.snapshot.SnapshotManager;
import org.patheloper.api.wrapper.PathBlock;
@@ -44,15 +41,11 @@ abstract class AbstractPathfinder implements Pathfinder {
private static final SnapshotManager LOADING_SNAPSHOT_MANAGER =
new FailingSnapshotManager.RequestingSnapshotManager();
- private static final Executor PATHING_EXECUTOR =
- new ThreadPoolExecutor(
- Runtime.getRuntime().availableProcessors() / 4,
- Runtime.getRuntime().availableProcessors(),
- 250L,
- TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<>(10000),
- new ThreadFactoryBuilder().setDaemon(true).setNameFormat("Pathfinder Task-%d").build(),
- new ThreadPoolExecutor.AbortPolicy());
+ private static final ExecutorService PATHING_EXECUTOR = Executors.newWorkStealingPool();
+
+ static {
+ Pathetic.addShutdownListener(PATHING_EXECUTOR::shutdown);
+ }
protected final PathingRuleSet pathingRuleSet;
diff --git a/pathetic-model/src/main/java/org/patheloper/model/pathing/result/PathImpl.java b/pathetic-model/src/main/java/org/patheloper/model/pathing/result/PathImpl.java
index dde43398..caaa52a1 100644
--- a/pathetic-model/src/main/java/org/patheloper/model/pathing/result/PathImpl.java
+++ b/pathetic-model/src/main/java/org/patheloper/model/pathing/result/PathImpl.java
@@ -3,9 +3,11 @@
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.Getter;
@@ -35,6 +37,16 @@ public PathImpl(
this.length = Iterables.size(positions);
}
+ @Override
+ public @NonNull Iterator iterator() {
+ return positions.iterator();
+ }
+
+ @Override
+ public void forEach(Consumer super PathPosition> action) {
+ positions.forEach(action);
+ }
+
@Override
public Path interpolate(double resolution) {
List enlargedPositions = new ArrayList<>();
diff --git a/pathetic-model/src/main/java/org/patheloper/model/snapshot/FailingSnapshotManager.java b/pathetic-model/src/main/java/org/patheloper/model/snapshot/FailingSnapshotManager.java
index 5263f138..51936bd1 100644
--- a/pathetic-model/src/main/java/org/patheloper/model/snapshot/FailingSnapshotManager.java
+++ b/pathetic-model/src/main/java/org/patheloper/model/snapshot/FailingSnapshotManager.java
@@ -68,7 +68,7 @@ private static Optional fetchBlock(PathPosition position) {
Material material =
ChunkUtils.getMaterial(chunkSnapshotOptional.get(), x, position.getBlockY(), z);
BlockState blockState =
- ChunkUtils.getBlockState(chunkSnapshotOptional.get(), x, position.getBlockY(), z);
+ NMS_UTILS.getNmsInterface().getBlockState(chunkSnapshotOptional.get(), x, position.getBlockY(), z);
return Optional.of(new PathBlock(position, new BlockInformation(material, blockState)));
}
@@ -159,7 +159,7 @@ private static PathBlock ensureBlock(PathPosition pathPosition) {
Material material = ChunkUtils.getMaterial(chunkSnapshot, x, pathPosition.getBlockY(), z);
BlockState blockState =
- ChunkUtils.getBlockState(chunkSnapshot, x, pathPosition.getBlockY(), z);
+ NMS_UTILS.getNmsInterface().getBlockState(chunkSnapshot, x, pathPosition.getBlockY(), z);
return new PathBlock(pathPosition, new BlockInformation(material, blockState));
}
diff --git a/pathetic-model/src/main/java/org/patheloper/model/snapshot/world/WorldDomain.java b/pathetic-model/src/main/java/org/patheloper/model/snapshot/world/WorldDomain.java
index 840eb429..66bd140a 100644
--- a/pathetic-model/src/main/java/org/patheloper/model/snapshot/world/WorldDomain.java
+++ b/pathetic-model/src/main/java/org/patheloper/model/snapshot/world/WorldDomain.java
@@ -1,29 +1,30 @@
package org.patheloper.model.snapshot.world;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
+import java.util.Map;
import java.util.Optional;
-import java.util.concurrent.TimeUnit;
import org.bukkit.ChunkSnapshot;
+import org.patheloper.util.ExpiringHashMap;
public class WorldDomain {
- private final Cache chunkSnapshotCache =
- CacheBuilder.newBuilder().maximumSize(100000).expireAfterAccess(5, TimeUnit.MINUTES).build();
+ private final Map> chunkSnapshotMap =
+ new ExpiringHashMap<>();
public Optional getSnapshot(long key) {
- return Optional.ofNullable(chunkSnapshotCache.getIfPresent(key));
+ ExpiringHashMap.Entry entry = chunkSnapshotMap.get(key);
+ if (entry == null) return Optional.empty();
+ return Optional.ofNullable(entry.getValue());
}
public void addSnapshot(final long key, final ChunkSnapshot snapshot) {
- chunkSnapshotCache.put(key, snapshot);
+ chunkSnapshotMap.put(key, new ExpiringHashMap.Entry<>(snapshot));
}
public void removeSnapshot(final long key) {
- chunkSnapshotCache.invalidate(key);
+ chunkSnapshotMap.remove(key);
}
public boolean containsSnapshot(final long key) {
- return chunkSnapshotCache.getIfPresent(key) != null;
+ return chunkSnapshotMap.get(key) != null;
}
}
diff --git a/pathetic-model/src/main/java/org/patheloper/util/ChunkUtils.java b/pathetic-model/src/main/java/org/patheloper/util/ChunkUtils.java
index db42e86a..0766f3f2 100644
--- a/pathetic-model/src/main/java/org/patheloper/util/ChunkUtils.java
+++ b/pathetic-model/src/main/java/org/patheloper/util/ChunkUtils.java
@@ -1,12 +1,12 @@
package org.patheloper.util;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Material;
-import org.bukkit.block.BlockState;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
@UtilityClass
public class ChunkUtils {
@@ -46,11 +46,4 @@ public Material getMaterial(ChunkSnapshot snapshot, int x, int y, int z) {
return snapshot.getBlockType(x, y, z);
}
- /** Get the block state from a chunk snapshot at the given coordinates */
- public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
- if (BukkitVersionUtil.getVersion().isUnder(13, 0))
- return null; // This is not supported in 1.12 and below
-
- return snapshot.getBlockData(x, y, z).createBlockState();
- }
}
diff --git a/pathetic-model/src/main/java/org/patheloper/util/ExpiringHashMap.java b/pathetic-model/src/main/java/org/patheloper/util/ExpiringHashMap.java
new file mode 100644
index 00000000..80d5fa45
--- /dev/null
+++ b/pathetic-model/src/main/java/org/patheloper/util/ExpiringHashMap.java
@@ -0,0 +1,57 @@
+package org.patheloper.util;
+
+import java.util.HashMap;
+import lombok.Getter;
+
+public class ExpiringHashMap extends HashMap> {
+
+ private static final long EXPIRATION_TIME = 5 * 60 * 1000;
+
+ @Override
+ public Entry put(K key, Entry value) {
+ return super.put(key, value);
+ }
+
+ @Override
+ public Entry get(Object key) {
+ Entry entry = super.get(key);
+ if (entry != null) {
+ if (entry.isExpired()) {
+ super.remove(key);
+ return null;
+ } else {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ Entry entry = super.get(key);
+ if (entry != null) {
+ if (entry.isExpired()) {
+ super.remove(key);
+ return false;
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static class Entry {
+
+ @Getter private final V value;
+ private final long expirationTime;
+
+ public Entry(V value) {
+ this.value = value;
+ this.expirationTime = System.currentTimeMillis() + EXPIRATION_TIME;
+ }
+
+ public boolean isExpired() {
+ return System.currentTimeMillis() > expirationTime;
+ }
+ }
+}
diff --git a/pathetic-model/src/main/java/org/patheloper/util/Tuple3.java b/pathetic-model/src/main/java/org/patheloper/util/Tuple3.java
new file mode 100644
index 00000000..c39ec18e
--- /dev/null
+++ b/pathetic-model/src/main/java/org/patheloper/util/Tuple3.java
@@ -0,0 +1,30 @@
+package org.patheloper.util;
+
+import java.util.Objects;
+
+public class Tuple3 {
+ public final T x;
+ public final T y;
+ public final T z;
+
+ public Tuple3(T x, T y, T z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Tuple3> tuple3 = (Tuple3>) o;
+ return Objects.equals(x, tuple3.x)
+ && Objects.equals(y, tuple3.y)
+ && Objects.equals(z, tuple3.z);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(x, y, z);
+ }
+}
diff --git a/pathetic-model/src/main/java/org/patheloper/util/WatchdogUtil.java b/pathetic-model/src/main/java/org/patheloper/util/WatchdogUtil.java
index 64d75c3f..cd9bd3ff 100644
--- a/pathetic-model/src/main/java/org/patheloper/util/WatchdogUtil.java
+++ b/pathetic-model/src/main/java/org/patheloper/util/WatchdogUtil.java
@@ -16,7 +16,7 @@ public class WatchdogUtil {
watchdogClazz = Class.forName("org.spigotmc.WatchdogThread");
tickMethod = watchdogClazz.getDeclaredMethod("tick");
} catch (ClassNotFoundException | NoSuchMethodException e) {
- e.printStackTrace();
+ throw ErrorLogger.logFatalError(e.getMessage(), e);
}
}
diff --git a/pathetic-nms/pom.xml b/pathetic-nms/pom.xml
index 2b3a3080..144e9a9d 100644
--- a/pathetic-nms/pom.xml
+++ b/pathetic-nms/pom.xml
@@ -6,12 +6,12 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
4.0.0
pathetic-nms
- 2.3
+ 2.4
8
@@ -23,86 +23,93 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
org.patheloper
v1_20_R3
- 2.3
+ 2.4
compile
org.patheloper
v1_20_R2
- 2.3
+ 2.4
compile
org.patheloper
v1_20_R1
- 2.3
+ 2.4
compile
org.patheloper
v1_19_R3
- 2.3
+ 2.4
compile
org.patheloper
v1_19_R2
- 2.3
+ 2.4
+ compile
+
+
+
+ org.patheloper
+ v1_18_R2
+ 2.4
compile
org.patheloper
v1_18
- 2.3
+ 2.4
compile
org.patheloper
v1_17
- 2.3
+ 2.4
compile
org.patheloper
v1_16
- 2.3
+ 2.4
compile
org.patheloper
v1_15
- 2.3
+ 2.4
compile
org.patheloper
v1_12
- 2.3
+ 2.4
compile
org.patheloper
v1_8
- 2.3
+ 2.4
compile
-
\ No newline at end of file
+
diff --git a/pathetic-nms/src/main/java/org/patheloper/nms/NMSUtils.java b/pathetic-nms/src/main/java/org/patheloper/nms/NMSUtils.java
index f83d3087..7c04f94a 100644
--- a/pathetic-nms/src/main/java/org/patheloper/nms/NMSUtils.java
+++ b/pathetic-nms/src/main/java/org/patheloper/nms/NMSUtils.java
@@ -6,6 +6,7 @@
import org.patheloper.nms.v1_16.OneSixteenNMSInterface;
import org.patheloper.nms.v1_17.OneSeventeenNMSInterface;
import org.patheloper.nms.v1_18.OneEighteenNMSInterface;
+import org.patheloper.nms.v1_18_R2.OneEighteenTwoNMSInterface;
import org.patheloper.nms.v1_19_R2.OneNineteenTwoNMSInterface;
import org.patheloper.nms.v1_19_R3.OneNineteenThreeNMSInterface;
import org.patheloper.nms.v1_20_R1.OneTwentyOneNMSInterface;
@@ -42,6 +43,10 @@ public NMSUtils(int major, int minor) {
}
throw new IllegalArgumentException("Unsupported version: " + major + "." + minor);
case 18:
+ if (minor == 2) {
+ nmsInterface = new OneEighteenTwoNMSInterface();
+ break;
+ }
nmsInterface = new OneEighteenNMSInterface();
break;
case 17:
diff --git a/pathetic-nms/v1_12/pom.xml b/pathetic-nms/v1_12/pom.xml
index 2f09cd37..2dd7d0ab 100644
--- a/pathetic-nms/v1_12/pom.xml
+++ b/pathetic-nms/v1_12/pom.xml
@@ -5,7 +5,7 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
../../pom.xml
4.0.0
@@ -36,7 +36,7 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
diff --git a/pathetic-nms/v1_12/src/main/java/org/patheloper/nms/v1_12/OneTwelveNMSInterface.java b/pathetic-nms/v1_12/src/main/java/org/patheloper/nms/v1_12/OneTwelveNMSInterface.java
index 0d6abbea..d1fcc7ee 100644
--- a/pathetic-nms/v1_12/src/main/java/org/patheloper/nms/v1_12/OneTwelveNMSInterface.java
+++ b/pathetic-nms/v1_12/src/main/java/org/patheloper/nms/v1_12/OneTwelveNMSInterface.java
@@ -4,6 +4,7 @@
import net.minecraft.server.v1_12_R1.WorldServer;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.v1_12_R1.CraftWorld;
import org.patheloper.api.snapshot.NMSInterface;
@@ -21,4 +22,9 @@ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
return null;
}
}
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ return null;
+ }
}
diff --git a/pathetic-nms/v1_15/pom.xml b/pathetic-nms/v1_15/pom.xml
index e75d9bca..cb5a5457 100644
--- a/pathetic-nms/v1_15/pom.xml
+++ b/pathetic-nms/v1_15/pom.xml
@@ -5,7 +5,7 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
../../pom.xml
4.0.0
@@ -36,7 +36,7 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
diff --git a/pathetic-nms/v1_15/src/main/java/org/patheloper/nms/v1_15/OneFifteenNMSInterface.java b/pathetic-nms/v1_15/src/main/java/org/patheloper/nms/v1_15/OneFifteenNMSInterface.java
index c0a72ad0..d08aa7dc 100644
--- a/pathetic-nms/v1_15/src/main/java/org/patheloper/nms/v1_15/OneFifteenNMSInterface.java
+++ b/pathetic-nms/v1_15/src/main/java/org/patheloper/nms/v1_15/OneFifteenNMSInterface.java
@@ -4,6 +4,7 @@
import net.minecraft.server.v1_15_R1.WorldServer;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.v1_15_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
import org.patheloper.api.snapshot.NMSInterface;
@@ -24,4 +25,9 @@ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
return null;
}
}
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ return null;
+ }
}
diff --git a/pathetic-nms/v1_16/pom.xml b/pathetic-nms/v1_16/pom.xml
index 31260b48..2b0bc648 100644
--- a/pathetic-nms/v1_16/pom.xml
+++ b/pathetic-nms/v1_16/pom.xml
@@ -5,7 +5,7 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
../../pom.xml
4.0.0
@@ -36,7 +36,7 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
diff --git a/pathetic-nms/v1_16/src/main/java/org/patheloper/nms/v1_16/OneSixteenNMSInterface.java b/pathetic-nms/v1_16/src/main/java/org/patheloper/nms/v1_16/OneSixteenNMSInterface.java
index 3323abd6..f0875f19 100644
--- a/pathetic-nms/v1_16/src/main/java/org/patheloper/nms/v1_16/OneSixteenNMSInterface.java
+++ b/pathetic-nms/v1_16/src/main/java/org/patheloper/nms/v1_16/OneSixteenNMSInterface.java
@@ -4,6 +4,7 @@
import net.minecraft.server.v1_16_R3.WorldServer;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.v1_16_R3.CraftChunk;
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
import org.patheloper.api.snapshot.NMSInterface;
@@ -25,4 +26,9 @@ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
return null;
}
}
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ return null;
+ }
}
diff --git a/pathetic-nms/v1_17/pom.xml b/pathetic-nms/v1_17/pom.xml
index 9eea2dcc..6e0868b9 100644
--- a/pathetic-nms/v1_17/pom.xml
+++ b/pathetic-nms/v1_17/pom.xml
@@ -5,7 +5,7 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
../../pom.xml
4.0.0
@@ -36,7 +36,7 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
diff --git a/pathetic-nms/v1_17/src/main/java/org/patheloper/nms/v1_17/OneSeventeenNMSInterface.java b/pathetic-nms/v1_17/src/main/java/org/patheloper/nms/v1_17/OneSeventeenNMSInterface.java
index 68e5f72f..64775aa0 100644
--- a/pathetic-nms/v1_17/src/main/java/org/patheloper/nms/v1_17/OneSeventeenNMSInterface.java
+++ b/pathetic-nms/v1_17/src/main/java/org/patheloper/nms/v1_17/OneSeventeenNMSInterface.java
@@ -1,11 +1,16 @@
package org.patheloper.nms.v1_17;
import net.minecraft.server.level.WorldServer;
+import net.minecraft.world.level.block.state.IBlockData;
import net.minecraft.world.level.chunk.ChunkStatus;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_17_R1.block.CraftBlockStates;
+import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
import org.patheloper.api.snapshot.NMSInterface;
public class OneSeventeenNMSInterface implements NMSInterface {
@@ -25,4 +30,11 @@ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
return null;
}
}
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ BlockData data = snapshot.getBlockData(x, y, z);
+ IBlockData state = ((CraftBlockData) data).getState();
+ return CraftBlockStates.getBlockState(state, null);
+ }
}
diff --git a/pathetic-nms/v1_18/pom.xml b/pathetic-nms/v1_18/pom.xml
index 47ce2c81..411c755d 100644
--- a/pathetic-nms/v1_18/pom.xml
+++ b/pathetic-nms/v1_18/pom.xml
@@ -5,7 +5,7 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
../../pom.xml
4.0.0
@@ -36,7 +36,7 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
diff --git a/pathetic-nms/v1_18/src/main/java/org/patheloper/nms/v1_18/OneEighteenNMSInterface.java b/pathetic-nms/v1_18/src/main/java/org/patheloper/nms/v1_18/OneEighteenNMSInterface.java
index 7c9e2c9c..d113acd0 100644
--- a/pathetic-nms/v1_18/src/main/java/org/patheloper/nms/v1_18/OneEighteenNMSInterface.java
+++ b/pathetic-nms/v1_18/src/main/java/org/patheloper/nms/v1_18/OneEighteenNMSInterface.java
@@ -7,8 +7,12 @@
import net.minecraft.world.level.chunk.DataPaletteBlock;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_18_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_18_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_18_R1.block.CraftBlockStates;
+import org.bukkit.craftbukkit.v1_18_R1.block.data.CraftBlockData;
import org.patheloper.api.snapshot.NMSInterface;
public class OneEighteenNMSInterface implements NMSInterface {
@@ -48,4 +52,11 @@ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
return null;
}
}
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ BlockData data = snapshot.getBlockData(x, y, z);
+ IBlockData state = ((CraftBlockData) data).getState();
+ return CraftBlockStates.getBlockState(state, null);
+ }
}
diff --git a/pathetic-nms/v1_18_R2/pom.xml b/pathetic-nms/v1_18_R2/pom.xml
new file mode 100644
index 00000000..4ce847da
--- /dev/null
+++ b/pathetic-nms/v1_18_R2/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ org.patheloper
+ pathetic-main
+ 2.4
+ ../../pom.xml
+
+ 4.0.0
+
+ v1_18_R2
+
+
+ 16
+ 16
+ UTF-8
+
+
+
+
+ codemc-repo
+ https://repo.codemc.org/repository/nms/
+
+
+
+
+
+ org.spigotmc
+ spigot
+ 1.18.2-R0.1-SNAPSHOT
+ provided
+
+
+
+ org.patheloper
+ pathetic-api
+ 2.4
+ provided
+
+
+
diff --git a/pathetic-nms/v1_18_R2/src/main/java/org/patheloper/nms/v1_18_R2/OneEighteenTwoNMSInterface.java b/pathetic-nms/v1_18_R2/src/main/java/org/patheloper/nms/v1_18_R2/OneEighteenTwoNMSInterface.java
new file mode 100644
index 00000000..cf6fd26b
--- /dev/null
+++ b/pathetic-nms/v1_18_R2/src/main/java/org/patheloper/nms/v1_18_R2/OneEighteenTwoNMSInterface.java
@@ -0,0 +1,62 @@
+package org.patheloper.nms.v1_18_R2;
+
+import java.lang.reflect.Field;
+import net.minecraft.server.level.WorldServer;
+import net.minecraft.world.level.block.state.IBlockData;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import net.minecraft.world.level.chunk.DataPaletteBlock;
+import org.bukkit.ChunkSnapshot;
+import org.bukkit.World;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
+import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_18_R2.block.CraftBlockStates;
+import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
+import org.patheloper.api.snapshot.NMSInterface;
+
+public class OneEighteenTwoNMSInterface implements NMSInterface {
+
+ private static final Field blockIDField;
+
+ static {
+ try {
+ blockIDField = CraftChunk.class.getDeclaredField("emptyBlockIDs");
+ blockIDField.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
+ try {
+
+ WorldServer server = ((CraftWorld) world).getHandle();
+ CraftChunk newCraftChunk = new CraftChunk(server, chunkX, chunkZ);
+
+ server.k().a(chunkX, chunkZ, ChunkStatus.o, true);
+ DataPaletteBlock dataDataPaletteBlock =
+ (DataPaletteBlock) blockIDField.get(newCraftChunk);
+
+ dataDataPaletteBlock.b();
+ dataDataPaletteBlock.a();
+ ChunkSnapshot chunkSnapshot = newCraftChunk.getChunkSnapshot();
+ dataDataPaletteBlock.b();
+
+ return chunkSnapshot;
+
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ BlockData data = snapshot.getBlockData(x, y, z);
+ IBlockData state = ((CraftBlockData) data).getState();
+ return CraftBlockStates.getBlockState(state, null);
+ }
+}
diff --git a/pathetic-nms/v1_19_R2/pom.xml b/pathetic-nms/v1_19_R2/pom.xml
index f0b6f4fe..af0d6f88 100644
--- a/pathetic-nms/v1_19_R2/pom.xml
+++ b/pathetic-nms/v1_19_R2/pom.xml
@@ -5,7 +5,7 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
../../pom.xml
4.0.0
@@ -36,7 +36,7 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
diff --git a/pathetic-nms/v1_19_R2/src/main/java/org/patheloper/nms/v1_19_R2/OneNineteenTwoNMSInterface.java b/pathetic-nms/v1_19_R2/src/main/java/org/patheloper/nms/v1_19_R2/OneNineteenTwoNMSInterface.java
index ebc95f5b..32685a82 100644
--- a/pathetic-nms/v1_19_R2/src/main/java/org/patheloper/nms/v1_19_R2/OneNineteenTwoNMSInterface.java
+++ b/pathetic-nms/v1_19_R2/src/main/java/org/patheloper/nms/v1_19_R2/OneNineteenTwoNMSInterface.java
@@ -7,8 +7,12 @@
import net.minecraft.world.level.chunk.DataPaletteBlock;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_19_R2.CraftChunk;
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R2.block.CraftBlockStates;
+import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
import org.patheloper.api.snapshot.NMSInterface;
public class OneNineteenTwoNMSInterface implements NMSInterface {
@@ -48,4 +52,11 @@ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
return null;
}
}
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ BlockData data = snapshot.getBlockData(x, y, z);
+ IBlockData state = ((CraftBlockData) data).getState();
+ return CraftBlockStates.getBlockState(state, null);
+ }
}
diff --git a/pathetic-nms/v1_19_R3/pom.xml b/pathetic-nms/v1_19_R3/pom.xml
index 44874ca1..cbe4569d 100644
--- a/pathetic-nms/v1_19_R3/pom.xml
+++ b/pathetic-nms/v1_19_R3/pom.xml
@@ -5,7 +5,7 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
../../pom.xml
4.0.0
@@ -36,7 +36,7 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
diff --git a/pathetic-nms/v1_19_R3/src/main/java/org/patheloper/nms/v1_19_R3/OneNineteenThreeNMSInterface.java b/pathetic-nms/v1_19_R3/src/main/java/org/patheloper/nms/v1_19_R3/OneNineteenThreeNMSInterface.java
index c785109a..3f20669e 100644
--- a/pathetic-nms/v1_19_R3/src/main/java/org/patheloper/nms/v1_19_R3/OneNineteenThreeNMSInterface.java
+++ b/pathetic-nms/v1_19_R3/src/main/java/org/patheloper/nms/v1_19_R3/OneNineteenThreeNMSInterface.java
@@ -7,8 +7,12 @@
import net.minecraft.world.level.chunk.DataPaletteBlock;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_19_R3.CraftChunk;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlockStates;
+import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
import org.patheloper.api.snapshot.NMSInterface;
public class OneNineteenThreeNMSInterface implements NMSInterface {
@@ -48,4 +52,11 @@ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
return null;
}
}
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ BlockData data = snapshot.getBlockData(x, y, z);
+ IBlockData state = ((CraftBlockData) data).getState();
+ return CraftBlockStates.getBlockState(state, null);
+ }
}
diff --git a/pathetic-nms/v1_20_R1/pom.xml b/pathetic-nms/v1_20_R1/pom.xml
index 6bf7f1fa..d5c6a380 100644
--- a/pathetic-nms/v1_20_R1/pom.xml
+++ b/pathetic-nms/v1_20_R1/pom.xml
@@ -5,7 +5,7 @@
org.patheloper
pathetic-main
- 2.3
+ 2.4
../../pom.xml
4.0.0
@@ -36,7 +36,7 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
diff --git a/pathetic-nms/v1_20_R1/src/main/java/org/patheloper/nms/v1_20_R1/OneTwentyOneNMSInterface.java b/pathetic-nms/v1_20_R1/src/main/java/org/patheloper/nms/v1_20_R1/OneTwentyOneNMSInterface.java
index abb6773f..4fd29077 100644
--- a/pathetic-nms/v1_20_R1/src/main/java/org/patheloper/nms/v1_20_R1/OneTwentyOneNMSInterface.java
+++ b/pathetic-nms/v1_20_R1/src/main/java/org/patheloper/nms/v1_20_R1/OneTwentyOneNMSInterface.java
@@ -7,6 +7,7 @@
import net.minecraft.world.level.chunk.DataPaletteBlock;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.v1_20_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.patheloper.api.snapshot.NMSInterface;
@@ -48,4 +49,9 @@ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
return null;
}
}
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ return snapshot.getBlockData(x, y, z).createBlockState();
+ }
}
diff --git a/pathetic-nms/v1_20_R2/pom.xml b/pathetic-nms/v1_20_R2/pom.xml
index a3138f71..92a11fb4 100644
--- a/pathetic-nms/v1_20_R2/pom.xml
+++ b/pathetic-nms/v1_20_R2/pom.xml
@@ -5,7 +5,7 @@
org.patheloper
pathetic-main
- 2.3
+ 2.4
../../pom.xml
4.0.0
@@ -36,7 +36,7 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
diff --git a/pathetic-nms/v1_20_R2/src/main/java/org/patheloper/nms/v1_20_R2/OneTwentyTwoNMSInterface.java b/pathetic-nms/v1_20_R2/src/main/java/org/patheloper/nms/v1_20_R2/OneTwentyTwoNMSInterface.java
index 0e6e82f0..e5520fb9 100644
--- a/pathetic-nms/v1_20_R2/src/main/java/org/patheloper/nms/v1_20_R2/OneTwentyTwoNMSInterface.java
+++ b/pathetic-nms/v1_20_R2/src/main/java/org/patheloper/nms/v1_20_R2/OneTwentyTwoNMSInterface.java
@@ -6,6 +6,7 @@
import net.minecraft.world.level.chunk.DataPaletteBlock;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.v1_20_R2.CraftChunk;
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
import org.patheloper.api.snapshot.NMSInterface;
@@ -49,4 +50,9 @@ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
return null;
}
}
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ return snapshot.getBlockData(x, y, z).createBlockState();
+ }
}
diff --git a/pathetic-nms/v1_20_R3/pom.xml b/pathetic-nms/v1_20_R3/pom.xml
index 4959c256..860f21da 100644
--- a/pathetic-nms/v1_20_R3/pom.xml
+++ b/pathetic-nms/v1_20_R3/pom.xml
@@ -5,7 +5,7 @@
org.patheloper
pathetic-main
- 2.3
+ 2.4
../../pom.xml
4.0.0
@@ -36,7 +36,7 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
diff --git a/pathetic-nms/v1_20_R3/src/main/java/org/patheloper/nms/v1_20_R3/OneTwentyThreeNMSInterface.java b/pathetic-nms/v1_20_R3/src/main/java/org/patheloper/nms/v1_20_R3/OneTwentyThreeNMSInterface.java
index 40a99497..0d9e04f4 100644
--- a/pathetic-nms/v1_20_R3/src/main/java/org/patheloper/nms/v1_20_R3/OneTwentyThreeNMSInterface.java
+++ b/pathetic-nms/v1_20_R3/src/main/java/org/patheloper/nms/v1_20_R3/OneTwentyThreeNMSInterface.java
@@ -1,17 +1,17 @@
package org.patheloper.nms.v1_20_R3;
+import java.lang.reflect.Field;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.level.block.state.IBlockData;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.DataPaletteBlock;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.v1_20_R3.CraftChunk;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.patheloper.api.snapshot.NMSInterface;
-import java.lang.reflect.Field;
-
public final class OneTwentyThreeNMSInterface implements NMSInterface {
private static final Field blockIDField;
@@ -49,4 +49,9 @@ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
return null;
}
}
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ return snapshot.getBlockData(x, y, z).createBlockState();
+ }
}
diff --git a/pathetic-nms/v1_8/pom.xml b/pathetic-nms/v1_8/pom.xml
index b2805093..06be3db7 100644
--- a/pathetic-nms/v1_8/pom.xml
+++ b/pathetic-nms/v1_8/pom.xml
@@ -5,7 +5,7 @@
pathetic-main
org.patheloper
- 2.3
+ 2.4
../../pom.xml
4.0.0
@@ -36,7 +36,7 @@
org.patheloper
pathetic-api
- 2.3
+ 2.4
provided
diff --git a/pathetic-nms/v1_8/src/main/java/org/patheloper/nms/v1_8/OneEightNMSInterface.java b/pathetic-nms/v1_8/src/main/java/org/patheloper/nms/v1_8/OneEightNMSInterface.java
index 806d2ec2..46abfd70 100644
--- a/pathetic-nms/v1_8/src/main/java/org/patheloper/nms/v1_8/OneEightNMSInterface.java
+++ b/pathetic-nms/v1_8/src/main/java/org/patheloper/nms/v1_8/OneEightNMSInterface.java
@@ -4,6 +4,7 @@
import net.minecraft.server.v1_8_R3.WorldServer;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
+import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
import org.patheloper.api.snapshot.NMSInterface;
@@ -21,4 +22,9 @@ public ChunkSnapshot getSnapshot(World world, int chunkX, int chunkZ) {
return null;
}
}
+
+ @Override
+ public BlockState getBlockState(ChunkSnapshot snapshot, int x, int y, int z) {
+ return null;
+ }
}
diff --git a/pom.xml b/pom.xml
index 23cd5a54..916f9175 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
org.patheloper
pathetic-main
pom
- 2.3
+ 2.4
17
@@ -24,6 +24,7 @@
pathetic-nms/v1_20_R1
pathetic-nms/v1_19_R3
pathetic-nms/v1_19_R2
+ pathetic-nms/v1_18_R2
pathetic-nms/v1_18
pathetic-nms/v1_17
pathetic-nms/v1_16
@@ -94,10 +95,15 @@
a
Experimental:
+
+ default
+ a
+ Default:
+
-
\ No newline at end of file
+