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();
+ }
}