diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bc48d245..4f4a675a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,6 @@ name: Build Pathetic on: push: branches: - - stage - production pull_request: types: diff --git a/README.md b/README.md index 80bf42bb..7833f441 100644 --- a/README.md +++ b/README.md @@ -41,13 +41,16 @@ public class PathExample extends JavaPlugin { @Override public void onEnable() { - PatheticMapper.initialize(this); goFindSomePath(randomLocation(), randomLocation()); } - private void goFindSomePath(PathPosition start, PathPosition end) { + @Override + public void onDisable() { + PatheticMapper.shutdown(); + } + private void goFindSomePath(PathPosition start, PathPosition end) { Pathfinder pathfinder = PatheticMapper.newPathfinder(); pathfinder .findPath(start, end, new DirectPathfinderStrategy()) diff --git a/pathetic-api/pom.xml b/pathetic-api/pom.xml index 77f43491..44217df8 100644 --- a/pathetic-api/pom.xml +++ b/pathetic-api/pom.xml @@ -7,11 +7,11 @@ pathetic-main org.patheloper - 2.3 + 2.4 pathetic-api - 2.3 + 2.4 8 diff --git a/pathetic-api/src/main/java/org/patheloper/api/pathing/configuration/HeuristicWeights.java b/pathetic-api/src/main/java/org/patheloper/api/pathing/configuration/HeuristicWeights.java new file mode 100644 index 00000000..b4f84084 --- /dev/null +++ b/pathetic-api/src/main/java/org/patheloper/api/pathing/configuration/HeuristicWeights.java @@ -0,0 +1,58 @@ +package org.patheloper.api.pathing.configuration; + +import lombok.Value; + +/** + * Represents a set of weights used to calculate a heuristic for the A* pathfinding algorithm. These + * weights influence the prioritization of different path characteristics during the search. + * + *

This class defines weights for the following distance metrics: + * + *

+ */ +@Value(staticConstructor = "create") +public class HeuristicWeights { + + /** + * Provides a set of default heuristic weights that may be suitable for natural pathfinding. These + * values can be adjusted for specific scenarios. + */ + public static final HeuristicWeights NATURAL_PATH_WEIGHTS = create(0.3, 0.15, 0.6, 0.3); + + /** + * Provides a set of weights strongly prioritizing the shortest direct path, even if diagonally. + */ + public static final HeuristicWeights DIRECT_PATH_WEIGHTS = create(0.6, 0.3, 0.0, 0.1); + + /** + * The weight applied to the Manhattan distance component of the heuristic. A higher weight + * favours paths with a greater emphasis on direct, axis-aligned movement. + */ + double manhattenWeight; + + /** + * The weight applied to the Octile distance component of the heuristic. A higher weight allows + * diagonal movement, enabling more flexible paths in 3D environments. + */ + double octileWeight; + + /** + * The weight applied to the perpendicular distance component of the heuristic. Increased weight + * discourages deviations from the straight line between the start and target, resulting in + * smoother paths. + */ + double perpendicularWeight; + + /** + * The weight applied to the height difference (elevation change) component of the heuristic. A + * higher weight gives more consideration to vertical distance, important for terrains with + * varying verticality. + */ + double heightWeight; +} diff --git a/pathetic-api/src/main/java/org/patheloper/api/pathing/configuration/PathingRuleSet.java b/pathetic-api/src/main/java/org/patheloper/api/pathing/configuration/PathingRuleSet.java new file mode 100644 index 00000000..d2a90db4 --- /dev/null +++ b/pathetic-api/src/main/java/org/patheloper/api/pathing/configuration/PathingRuleSet.java @@ -0,0 +1,123 @@ +package org.patheloper.api.pathing.configuration; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Value; +import lombok.With; + +/** + * Defines a set of configurable rules that govern the behavior of the A* pathfinding algorithm. By + * adjusting these settings, you can fine-tune the pathfinding process to suit the specific needs of + * your Minecraft environment. + */ +@With +@Value +@Getter +@Builder(toBuilder = true, access = AccessLevel.PRIVATE) +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public class PathingRuleSet { + + /** + * The maximum number of iterations allowed for the pathfinding algorithm. This acts as a + * safeguard to prevent infinite loops in complex scenarios. + * + * @default 5000 + */ + @Builder.Default int maxIterations = 5000; + + /** + * The maximum permissible length of a calculated path (in blocks). Use this to constrain long + * searches that could impact performance. A value of 0 indicates no limit. + */ + int maxLength; + + /** + * Determines whether pathfinding calculations should be executed asynchronously in a separate + * thread. This can improve responsiveness in the main thread, but may introduce synchronization + * complexities. + */ + boolean async; + + /** + * Controls whether the pathfinding algorithm can take diagonal steps. Enabling this allows for + * more flexible and potentially shorter paths but might require a slightly more refined + * heuristic. + * + * @default true + */ + @Builder.Default boolean allowingDiagonal = true; + + /** + * If set to true, the pathfinding process will terminate immediately if no path is found between + * the start and target. This can be helpful for quick validation but prevents fallback + * strategies. + */ + boolean allowingFailFast; + + /** + * If pathfinding fails, this setting determines whether the algorithm should fall back to the + * last successfully calculated path. This can help maintain progress, but might use an outdated + * path. + */ + boolean allowingFallback; + + /** + * Controls whether chunks should be loaded or generated as needed during the pathfinding process. + * This is essential for exploring uncharted areas, but may impact performance. + */ + boolean loadingChunks; + + /** + * If pathfinding fails, determines whether to run a reverse pathfinding check (from target to + * start) to verify the result. This is a computationally expensive fallback but can help identify + * some failure cases. + */ + boolean counterCheck; + + /** + * The set of weights used to calculate heuristics within the A* algorithm. These influence the + * pathfinding priority for distance, elevation changes, smoothness, and diagonal movement. + * + * @default HeuristicWeights.NATURAL_PATH_WEIGHTS + */ + @Builder.Default HeuristicWeights heuristicWeights = HeuristicWeights.NATURAL_PATH_WEIGHTS; + + /** + * @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) + .heuristicWeights(pathingRuleSet.heuristicWeights) + .build(); + } +} diff --git a/pathetic-api/src/main/java/org/patheloper/api/pathing/result/Path.java b/pathetic-api/src/main/java/org/patheloper/api/pathing/result/Path.java index 2c9732c8..670654e6 100644 --- a/pathetic-api/src/main/java/org/patheloper/api/pathing/result/Path.java +++ b/pathetic-api/src/main/java/org/patheloper/api/pathing/result/Path.java @@ -4,7 +4,9 @@ import org.patheloper.api.util.ParameterizedSupplier; import org.patheloper.api.wrapper.PathPosition; -public interface Path { +import java.util.function.Consumer; + +public interface Path extends Iterable { /** * The length of the Path compiled from the number of positions @@ -83,7 +85,11 @@ public interface Path { @NonNull PathPosition getEnd(); - /** Returns the path from the Pathfinder as a {@link Iterable} full of {@link PathPosition} */ + /** + * Returns the path from the Pathfinder as a {@link Iterable} full of {@link PathPosition} + * @deprecated Will be removed in future versions {@link #forEach(Consumer)} + */ @NonNull + @Deprecated Iterable 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: + * + *

+ */ + 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 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 +