Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor and enhance pathfinder functionality #80

Merged
merged 41 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4901fd6
Renamed PathingRuleSet to PathfinderConfiguration
Metaphoriker Jun 18, 2024
597cb5e
Renamed PathfinderStrategy to PathFilter
Metaphoriker Jun 18, 2024
3e17a5a
Made it possible to give the Pathfinder multiple filters
Metaphoriker Jun 18, 2024
4705fe9
Introducing a new bukkit independent event system
Metaphoriker Jun 19, 2024
8830501
Merged NMS modules from v1_18 upwards
Metaphoriker Jun 19, 2024
344eeda
Renamed filters to something more fitting
Metaphoriker Jun 20, 2024
2e49b33
Now not every filter has to validate the node
Metaphoriker Jun 20, 2024
a96fa7e
Overworked pre-existing filters
Metaphoriker Jun 20, 2024
f04f25b
Added annotation
Metaphoriker Jun 20, 2024
ff62a03
Renamed strategy directories to filter
Metaphoriker Jun 21, 2024
7a377ac
Fix classpath for Paper
olijeffers0n Jun 21, 2024
9fac85e
Fix typo
olijeffers0n Jun 21, 2024
ae7b021
Merge trunk into v3.0
Metaphoriker Jun 21, 2024
5187df8
Merge remote-tracking branch 'origin/v3.0' into v3.0
olijeffers0n Jun 21, 2024
477da29
chore: misc changes
olijeffers0n Jun 21, 2024
e41ade5
Merge trunk
Metaphoriker Jun 21, 2024
26dbd17
Now using the typo fixed method
Metaphoriker Jun 24, 2024
67f9d79
Merge trunk
Metaphoriker Jun 25, 2024
b03b719
Once again made AStarPathfinder Java 8 compatible
Metaphoriker Jun 25, 2024
b9d16c3
Further elaborated on PathFilter javadocs
Metaphoriker Jun 25, 2024
3ac84ac
Some changes regarding filters
Metaphoriker Jun 25, 2024
8efd0e4
Refactored AStarPathfinder to use FibonacciHeap for improved performa…
Metaphoriker Jun 25, 2024
fe22c16
Now ErrorLogger also logs the stacktrace
Metaphoriker Jun 25, 2024
33c84f9
Introduced @Depending annotation for PathFilter dependencies with det…
Metaphoriker Jun 25, 2024
0658011
Added a new javadocs tag
Metaphoriker Jun 25, 2024
315d1e1
Added internal cache for filters to avoid double execution
Metaphoriker Jun 25, 2024
b1ba9b5
Refactor FilterDependencyValidator for better readability
Metaphoriker Jun 25, 2024
f90f851
Replaced methods with @Getter
Metaphoriker Jun 25, 2024
ce6bea9
Added a comment to the example
Metaphoriker Jun 26, 2024
957b256
Now logs the stacktrace when on async
Metaphoriker Jun 26, 2024
08d78e2
Now the ErrorLogger logs the model version of pathetic being used ins…
Metaphoriker Jun 27, 2024
93375de
Made ExpiringHashMap a ConcurrentHashMap which fixes a concurrent mod…
Metaphoriker Jun 30, 2024
9de0849
Merge trunk
Metaphoriker Jul 2, 2024
d32ce21
Made the heuristic consistent and added fcost comparison for a more a…
Metaphoriker Jul 2, 2024
db5e2af
Now using the fCost in the pathfinder
Metaphoriker Jul 2, 2024
909006a
The example shows now the distance of the pathfinding
Metaphoriker Jul 2, 2024
1faf61b
new codestyle
Metaphoriker Jul 31, 2024
9f7c55e
Revert "new codestyle"
Metaphoriker Jul 31, 2024
f974e5d
Now dependency-filters will be dynamically generated
Metaphoriker Aug 4, 2024
a0892d6
Now recursively checks through all dependencies, also through the dep…
Metaphoriker Aug 4, 2024
0bfaed1
Corrected comment in the example
Metaphoriker Aug 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class PathExample extends JavaPlugin {
private void goFindSomePath(PathPosition start, PathPosition end) {
Pathfinder pathfinder = PatheticMapper.newPathfinder();
pathfinder
.findPath(start, end, new DirectPathfinderStrategy())
.findPath(start, end, List.of(new PassablePathFilter()))
.thenAccept(
pathfinderResult ->
pathfinderResult
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.patheloper.api.event;

import com.google.common.eventbus.EventBus;
import lombok.experimental.UtilityClass;

@UtilityClass
public class EventPublisher {

private static final EventBus eventBus = new EventBus();

public static void raiseEvent(PathingEvent pathingEvent) {
eventBus.post(pathingEvent);
}

public static void registerListener(Object listener) {
eventBus.register(listener);
}

public static void unregisterListener(Object listener) {
eventBus.unregister(listener);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,4 @@
package org.patheloper.api.event;

import lombok.NonNull;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;

/** Represents all pathing events */
public class PathingEvent extends Event {

private static final HandlerList HANDLERS_LIST = new HandlerList();

public static HandlerList getHandlerList() {
return HANDLERS_LIST;
}

@NonNull
@Override
public HandlerList getHandlers() {
return HANDLERS_LIST;
}
}
public interface PathingEvent {}
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package org.patheloper.api.event;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import org.patheloper.api.pathing.result.PathfinderResult;

/**
* An event called when a pathfinder finishes pathing. Therefor, the result does not matter. Means
* that the event is called even if the pathing fails.
*/
@Getter
@AllArgsConstructor
public class PathingFinishedEvent extends PathingEvent {
public class PathingFinishedEvent implements PathingEvent {

@NonNull private final PathfinderResult pathfinderResult;

public @NonNull PathfinderResult getPathfinderResult() {
return this.pathfinderResult;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,17 @@
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.bukkit.event.Cancellable;
import org.patheloper.api.pathing.strategy.PathfinderStrategy;
import org.patheloper.api.pathing.filter.PathFilter;
import org.patheloper.api.wrapper.PathPosition;

import java.util.List;

/** An event called when a Pathfinder starts pathing. */
@Getter
@RequiredArgsConstructor
public class PathingStartFindEvent extends PathingEvent implements Cancellable {

@Getter private final PathPosition start;
@Getter private final PathPosition target;
@NonNull @Getter private final PathfinderStrategy pathfinderStrategy;
private boolean cancelled = false;

@Override
public boolean isCancelled() {
return this.cancelled;
}
public class PathingStartFindEvent implements PathingEvent {

@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
@NonNull private final PathPosition start;
@NonNull private final PathPosition target;
@NonNull private final List<PathFilter> filters;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* This package contains classes related to the event system of pathetic.
*
* <p>The event system is built around the Guava EventBus. It provides a way to define, raise, and
* handle custom events within the application. The main class in this package is the
* EventPublisher, which provides static methods to raise events and register/unregister listeners.
*
* <p>To create a new event, you would extend the PathingEvent class. To handle an event, you would
* create a method in your listener class that is annotated with @Subscribe and takes a single
* argument of your event type.
*
* <p>For example:
*
* <pre>
* // Define a new event
* public class CustomEvent extends PathingEvent {
* private final String message;
*
* public CustomEvent(String message) {
* this.message = message;
* }
*
* public String getMessage() {
* return message;
* }
* }
*
* // Define a listener for the new event
* public class CustomEventListener {
*
* @Subscribe
* public void onCustomEvent(CustomEvent event) {
* System.out.println("Received custom event with message: " + event.getMessage());
* }
* }
*
* // Register the listener and raise the event
* EventPublisher.registerListener(new CustomEventListener());
* EventPublisher.raiseEvent(new CustomEvent("Hello, world!"));
* </pre>
*/
package org.patheloper.api.event;
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
package org.patheloper.api.pathing;

import java.util.List;
import java.util.concurrent.CompletionStage;
import lombok.NonNull;
import org.patheloper.api.pathing.result.PathfinderResult;
import org.patheloper.api.pathing.strategy.PathfinderStrategy;
import org.patheloper.api.pathing.filter.PathFilter;
import org.patheloper.api.wrapper.PathPosition;

import javax.annotation.Nullable;

/**
* A Pathfinder is a class that can find a path between two positions while following a given set of
* rules.
*/
public interface Pathfinder {

/**
* Tries to find a Path between the two {@link PathPosition}'s provided with the given strategy.
* Tries to find a Path between the two {@link PathPosition}'s provided with the given filters.
*
* @param strategy The {@link PathfinderStrategy} to use
* @param filters A list of {@link PathFilter}'s to apply to the pathfinding process.
* @return An {@link CompletionStage} that will contain a {@link PathfinderResult}.
*/
@NonNull
CompletionStage<PathfinderResult> findPath(
@NonNull PathPosition start,
@NonNull PathPosition target,
@NonNull PathfinderStrategy strategy);
@NonNull PathPosition start, @NonNull PathPosition target, @Nullable List<@NonNull PathFilter> filters);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class HeuristicWeights {
* 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;
double manhattanWeight;

/**
* The weight applied to the Octile distance component of the heuristic. A higher weight allows
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
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.
* Defines a set of configurable parameters that govern the behavior of the A* pathfinding
* algorithm. By adjusting these parameters, 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 {
public class PathfinderConfiguration {

/**
* The maximum number of iterations allowed for the pathfinding algorithm. This acts as a
Expand Down Expand Up @@ -57,7 +57,7 @@ public class PathingRuleSet {
boolean allowingFailFast;

/**
* If pathfinding fails, this setting determines whether the algorithm should fall back to the
* If pathfinding fails, this parameter determines whether the algorithm should fall back to the
* last successfully calculated path. This can help maintain progress, but might use an outdated
* path.
*/
Expand Down Expand Up @@ -85,39 +85,40 @@ public class PathingRuleSet {
@Builder.Default HeuristicWeights heuristicWeights = HeuristicWeights.NATURAL_PATH_WEIGHTS;

/**
* @return A new {@link PathingRuleSet} with default values but async.
* @return A new {@link PathfinderConfiguration} with default parameters but async.
*/
public static PathingRuleSet createAsyncRuleSet() {
public static PathfinderConfiguration createAsyncConfiguration() {
return builder().async(true).build();
}

/**
* @return A new {@link PathingRuleSet} with default values.
* @return A new {@link PathfinderConfiguration} with default parameters.
*/
public static PathingRuleSet createRuleSet() {
public static PathfinderConfiguration createConfiguration() {
return builder().build();
}

/**
* Creates a deep copy of the given {@link PathingRuleSet}.
* Creates a deep copy of the given {@link PathfinderConfiguration}.
*
* <p>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.
* <p>This method constructs a new instance of {@link PathfinderConfiguration} 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.
* @param pathfinderConfiguration The {@link PathfinderConfiguration} to copy.
* @return A new {@link PathfinderConfiguration} instance with the same values as the input.
*/
public static PathingRuleSet deepCopy(PathingRuleSet pathingRuleSet) {
public static PathfinderConfiguration deepCopy(PathfinderConfiguration pathfinderConfiguration) {
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)
.maxIterations(pathfinderConfiguration.maxIterations)
.maxLength(pathfinderConfiguration.maxLength)
.async(pathfinderConfiguration.async)
.allowingDiagonal(pathfinderConfiguration.allowingDiagonal)
.allowingFailFast(pathfinderConfiguration.allowingFailFast)
.allowingFallback(pathfinderConfiguration.allowingFallback)
.loadingChunks(pathfinderConfiguration.loadingChunks)
.counterCheck(pathfinderConfiguration.counterCheck)
.heuristicWeights(pathfinderConfiguration.heuristicWeights)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.patheloper.api.pathing.filter;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
* Annotation to specify dependencies between {@link PathFilter} implementations.
*
* <p>This annotation should be applied to a {@link PathFilter} to indicate that it depends on one
* or more other filters. When a filter is annotated with {@code @Depending}, all specified
* dependencies are dynamically created and validated for the annotated filter to be considered
* valid.
*
* <p>Usage Example:
*
* <pre>{@code
* @Depending({PassablePathFilter.class})
* public class SolidGroundPathFilter implements PathFilter {
* @Override
* public boolean filter(PathValidationContext context) {
* // Filtering logic here
* return true;
* }
* }
* }</pre>
*
* <p>In the example above, the {@code SolidGroundPathFilter} depends on the {@code
* PassablePathFilter}. This means that {@code PassablePathFilter} will be dynamically created and
* validated whenever {@code SolidGroundPathFilter} is used.
*
* <p>Dependencies are generated and validated at runtime, regardless of their presence in the
* filter chain. If a dependent filter cannot be instantiated, an {@link IllegalStateException} will
* be thrown.
*
* @see PathFilter
* @see PathValidationContext
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Depending {

/**
* Specifies the dependent filters required for validation.
*
* @return An array of {@link PathFilter} classes that the annotated filter depends on.
*/
Class<? extends PathFilter>[] value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.patheloper.api.pathing.filter;

import lombok.NonNull;
import org.patheloper.api.pathing.Pathfinder;

import java.util.List;

/**
* A PathFilter is a functional interface that allows customization of the pathfinding process
* within the {@link Pathfinder}. It provides a mechanism to influence the selection of paths during
* the pathfinding algorithm's execution.
*
* <p>In essence, a PathFilter acts as a whitelist for blocks during the pathfinding process. It
* evaluates each potential block (or node) on the path and determines whether it is valid or not
* based on the implemented filter logic. Only the blocks that pass the filter are considered valid
* and included in the final path.
*/
@FunctionalInterface
public interface PathFilter {

/**
* Evaluates the given {@link PathValidationContext} to determine if the path is valid. This
* method is used during the pathfinding process to filter out unwanted paths.
*
* @param pathValidationContext The context providing the information necessary to evaluate
* @return true if the path is valid, false otherwise
*/
boolean filter(@NonNull PathValidationContext pathValidationContext);

/**
* Cleans up the resources used during the pathfinding process. This method is guaranteed to
* always be called after pathfinding and should be overridden to ensure proper disposal of
* resources. Users can rely on the fact that this method will be invoked post pathfinding to do
* necessary clean-ups.
*/
default void cleanup() {}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package org.patheloper.api.pathing.strategy;
package org.patheloper.api.pathing.filter;

import lombok.Value;
import org.patheloper.api.snapshot.SnapshotManager;
import org.patheloper.api.wrapper.PathPosition;

/** A parameter object for the {@link PathfinderStrategy#isValid} method. */
/** A parameter object for the {@link PathFilter#filter} method. */
@Value
public class PathValidationContext {

Expand Down
Loading
Loading