From b9751e40e9c5328d6dd186bc07beb2bf2c852a27 Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Thu, 5 Feb 2026 21:09:06 +0100 Subject: [PATCH] feat: Add filter registration option for random teleport locations --- .../RandomTeleportLocationFilter.java | 47 +++++++++++++++++++ .../randomteleport/RandomTeleportService.java | 26 ++++++++++ .../RandomTeleportSafeLocationService.java | 19 +++++++- .../RandomTeleportServiceImpl.java | 40 ++++++++++++++++ 4 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportLocationFilter.java diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportLocationFilter.java b/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportLocationFilter.java new file mode 100644 index 000000000..d0df98a91 --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportLocationFilter.java @@ -0,0 +1,47 @@ +package com.eternalcode.core.feature.randomteleport; + +import org.bukkit.Location; + +/** + * Filter for validating random teleport locations. + *

+ * Allows external plugins to add custom validation logic for random teleport locations. + * For example, a plot plugin could implement this to prevent teleporting into claimed plots. + *

+ * Example usage: + *

{@code
+ * public class PlotFilter implements RandomTeleportLocationFilter {
+ *     @Override
+ *     public boolean isValid(Location location) {
+ *         // Check if location is inside a claimed plot
+ *         return !plotAPI.isPlotClaimed(location);
+ *     }
+ *
+ *     @Override
+ *     public String getFilterName() {
+ *         return "PlotFilter";
+ *     }
+ * }
+ *
+ * // Register the filter
+ * EternalCoreApi api = EternalCoreApiProvider.provide();
+ * api.getRandomTeleportService().registerFilter(new PlotFilter());
+ * }
+ */ +public interface RandomTeleportLocationFilter { + + /** + * Checks if the given location is valid for random teleportation. + * + * @param location The location to validate + * @return true if the location is valid, false otherwise + */ + boolean isValid(Location location); + + /** + * Gets the name of this filter for debugging and logging purposes. + * + * @return The filter name + */ + String getFilterName(); +} diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportService.java b/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportService.java index e577cc392..5551f1c71 100644 --- a/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportService.java +++ b/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportService.java @@ -99,4 +99,30 @@ public interface RandomTeleportService { */ CompletableFuture getSafeRandomLocationInWorldBorder(World world, int attemptCount); + /** + * Registers a custom location filter that will be used to validate random teleport locations. + *

+ * Filters are checked after the built-in safety checks (unsafe blocks, world border, etc.). + * All registered filters must return true for a location to be considered valid. + * + * @param filter The filter to register + * @throws IllegalArgumentException if a filter with the same name is already registered + */ + void registerFilter(RandomTeleportLocationFilter filter); + + /** + * Unregisters a previously registered location filter. + * + * @param filterName The name of the filter to unregister + * @return true if the filter was found and removed, false otherwise + */ + boolean unregisterFilter(String filterName); + + /** + * Gets all currently registered location filters. + * + * @return An unmodifiable collection of registered filters + */ + Collection getRegisteredFilters(); + } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSafeLocationService.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSafeLocationService.java index bbb3be754..8521477a4 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSafeLocationService.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSafeLocationService.java @@ -25,15 +25,18 @@ class RandomTeleportSafeLocationService { private final RandomTeleportSettings randomTeleportSettings; private final LocationsConfiguration locationsConfiguration; + private final RandomTeleportServiceImpl randomTeleportService; private final Random random = new Random(); @Inject RandomTeleportSafeLocationService( RandomTeleportSettings randomTeleportSettings, - LocationsConfiguration locationsConfiguration + LocationsConfiguration locationsConfiguration, + RandomTeleportServiceImpl randomTeleportService ) { this.randomTeleportSettings = randomTeleportSettings; this.locationsConfiguration = locationsConfiguration; + this.randomTeleportService = randomTeleportService; } public CompletableFuture getSafeRandomLocation(World world, RandomTeleportRadius radius, int attemptCount) { @@ -103,9 +106,21 @@ private boolean isSafeLocation(Chunk chunk, Location location) { return false; } - return switch (world.getEnvironment()) { + boolean environmentValid = switch (world.getEnvironment()) { case NORMAL, THE_END, CUSTOM -> true; case NETHER -> location.getY() <= NETHER_MAX_HEIGHT; }; + + if (!environmentValid) { + return false; + } + + for (RandomTeleportLocationFilter filter : this.randomTeleportService.getRegisteredFiltersInternal()) { + if (!filter.isValid(location)) { + return false; + } + } + + return true; } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportServiceImpl.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportServiceImpl.java index bb358a6b3..23d6663ff 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportServiceImpl.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportServiceImpl.java @@ -9,10 +9,12 @@ import com.eternalcode.core.injector.annotations.component.Service; import io.papermc.lib.PaperLib; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import org.bukkit.Location; import org.bukkit.World; @@ -25,6 +27,7 @@ class RandomTeleportServiceImpl implements RandomTeleportService { private final RandomTeleportSettings randomTeleportSettings; private final RandomTeleportSafeLocationService safeLocationService; private final EventCaller eventCaller; + private final Map registeredFilters; @Inject RandomTeleportServiceImpl( @@ -34,6 +37,7 @@ class RandomTeleportServiceImpl implements RandomTeleportService { this.randomTeleportSettings = randomTeleportSettings; this.safeLocationService = safeLocationService; this.eventCaller = eventCaller; + this.registeredFilters = new ConcurrentHashMap<>(); } @Override @@ -150,4 +154,40 @@ private RandomTeleportRadius getWorldBorderRadius(World world) { int borderRadius = (int) (worldBorder.getSize() / 2); return RandomTeleportRadius.of(-borderRadius, borderRadius, -borderRadius, borderRadius); } + + @Override + public void registerFilter(RandomTeleportLocationFilter filter) { + if (filter == null) { + throw new IllegalArgumentException("Filter cannot be null"); + } + + String filterName = filter.getFilterName(); + if (filterName == null || filterName.isEmpty()) { + throw new IllegalArgumentException("Filter name cannot be null or empty"); + } + + if (this.registeredFilters.containsKey(filterName)) { + throw new IllegalArgumentException("Filter with name '" + filterName + "' is already registered"); + } + + this.registeredFilters.put(filterName, filter); + } + + @Override + public boolean unregisterFilter(String filterName) { + if (filterName == null || filterName.isEmpty()) { + return false; + } + + return this.registeredFilters.remove(filterName) != null; + } + + @Override + public Collection getRegisteredFilters() { + return Collections.unmodifiableCollection(this.registeredFilters.values()); + } + + Collection getRegisteredFiltersInternal() { + return this.registeredFilters.values(); + } }