From 18e5722790b6d8c0ce34747394dd5f5e4aea9bb5 Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Thu, 12 Dec 2024 03:06:21 +0100 Subject: [PATCH] GH-874 Expand Random Teleport configuration. (#874) * Add more flexible configuration for Random Teleport. * Complete configurations with missing unsafe blocks and air blocks * Fix issues reported by @coderabbitai * Update eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportRadiusRepresenterImpl.java Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Resolve issues reported in review, create `RandomTeleportRadiusRepresenterImpl#of` methods. * Update eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportRadiusRepresenterImpl.java Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportServiceImpl.java Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSettingsImpl.java Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Add import. * Remvoe buttons, add height range * Fix comments * Add teleportation counting. * Update eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportTaskService.java Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSafeLocationService.java Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Move description. * Rename some classes make API more usable. * Teleport players without waiting with the `/rtp (admin command). * Add migrations, rename configs * Fix migration --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Rollczi --- .../com/eternalcode/core/delay/Delay.java | 14 +- .../eternalcode/core/delay/DelaySettings.java | 1 + .../randomteleport/RandomTeleportRadius.java | 17 ++ ...tResult.java => RandomTeleportResult.java} | 2 +- .../randomteleport/RandomTeleportService.java | 14 +- .../SimpleRandomTeleportRadius.java | 12 ++ .../configuration/ConfigurationManager.java | 6 + .../implementation/PluginConfiguration.java | 56 +---- .../configuration/migration/Migration.java | 7 + .../migration/MigrationController.java | 38 ++++ .../migration/MigrationService.java | 37 ++++ .../randomteleport/RandomTeleportCommand.java | 54 +++-- .../RandomTeleportHeightRange.java | 30 +++ .../RandomTeleportPermissionConstant.java | 8 + .../RandomTeleportPlaceholders.java | 16 ++ .../RandomTeleportRadiusConfig.java | 42 ++++ .../RandomTeleportResolveWorldUtil.java | 24 +++ .../RandomTeleportSafeLocationService.java | 111 ++++++++++ .../RandomTeleportServiceImpl.java | 163 +++----------- .../RandomTeleportSettings.java | 25 ++- .../RandomTeleportSettingsImpl.java | 202 ++++++++++++++++++ .../RandomTeleportTaskService.java | 78 +++++++ .../randomteleport/RandomTeleportType.java | 2 +- .../implementation/ENTranslation.java | 7 +- .../implementation/PLTranslation.java | 7 +- .../eternalcode/core/util/ReflectUtil.java | 24 +++ 26 files changed, 770 insertions(+), 227 deletions(-) create mode 100644 eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportRadius.java rename eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/{TeleportResult.java => RandomTeleportResult.java} (53%) create mode 100644 eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/SimpleRandomTeleportRadius.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/Migration.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/MigrationController.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/MigrationService.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportHeightRange.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportPermissionConstant.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportPlaceholders.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportRadiusConfig.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportResolveWorldUtil.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSafeLocationService.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSettingsImpl.java create mode 100644 eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportTaskService.java diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/delay/Delay.java b/eternalcore-api/src/main/java/com/eternalcode/core/delay/Delay.java index fc666dd96..9bc19e46b 100644 --- a/eternalcore-api/src/main/java/com/eternalcode/core/delay/Delay.java +++ b/eternalcore-api/src/main/java/com/eternalcode/core/delay/Delay.java @@ -5,18 +5,24 @@ import java.time.Duration; import java.time.Instant; +import java.util.function.Supplier; public class Delay { private final Cache delays; - private final DelaySettings delaySettings; + private final Supplier delaySettings; + @Deprecated public Delay(DelaySettings delaySettings) { - this.delaySettings = delaySettings; + this((Supplier) () -> delaySettings.delay()); + } + + public Delay(Supplier delayProvider) { + this.delaySettings = delayProvider; this.delays = CacheBuilder.newBuilder() - .expireAfterWrite(delaySettings.delay()) + .expireAfterWrite(delayProvider.get()) .build(); } @@ -25,7 +31,7 @@ public void markDelay(T key, Duration delay) { } public void markDelay(T key) { - this.markDelay(key, this.delaySettings.delay()); + this.markDelay(key, this.delaySettings.get()); } public void unmarkDelay(T key) { diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/delay/DelaySettings.java b/eternalcore-api/src/main/java/com/eternalcode/core/delay/DelaySettings.java index 6e82f552b..95c2241f6 100644 --- a/eternalcore-api/src/main/java/com/eternalcode/core/delay/DelaySettings.java +++ b/eternalcore-api/src/main/java/com/eternalcode/core/delay/DelaySettings.java @@ -2,6 +2,7 @@ import java.time.Duration; +@Deprecated public interface DelaySettings { Duration delay(); diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportRadius.java b/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportRadius.java new file mode 100644 index 000000000..0961f1155 --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportRadius.java @@ -0,0 +1,17 @@ +package com.eternalcode.core.feature.randomteleport; + +public interface RandomTeleportRadius { + int maxZ(); + int minZ(); + int maxX(); + int minX(); + + static RandomTeleportRadius of(int minX, int maxX, int minZ, int maxZ) { + return new SimpleRandomTeleportRadius(minX, maxX, minZ, maxZ); + } + + static RandomTeleportRadius of(int radius) { + return new SimpleRandomTeleportRadius(-radius, radius, -radius, radius); + } + +} diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/TeleportResult.java b/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportResult.java similarity index 53% rename from eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/TeleportResult.java rename to eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportResult.java index 3e37bb18f..30459857f 100644 --- a/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/TeleportResult.java +++ b/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportResult.java @@ -2,5 +2,5 @@ import org.bukkit.Location; -public record TeleportResult(boolean success, Location location) { +public record RandomTeleportResult(boolean success, Location location) { } 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 a22083652..79b13bc5a 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 @@ -14,7 +14,7 @@ public interface RandomTeleportService { * @param player The player to teleport. * @return A CompletableFuture containing the TeleportResult indicating the success or failure of the teleportation. */ - CompletableFuture teleport(Player player); + CompletableFuture teleport(Player player); /** * Asynchronously teleports the specified player to a random location within the specified world. @@ -23,7 +23,7 @@ public interface RandomTeleportService { * @param world The world to which the player should be teleported. * @return A CompletableFuture containing the TeleportResult indicating the success or failure of the teleportation. */ - CompletableFuture teleport(Player player, World world); + CompletableFuture teleport(Player player, World world); /** * Asynchronously retrieves a safe random location within the specified world. @@ -45,6 +45,16 @@ public interface RandomTeleportService { */ CompletableFuture getSafeRandomLocation(World world, int radius, int attemptCount); + /** + * Asynchronously retrieves a safe random location within the specified world, using radius. + * + * @param world The world in which to find a random location. + * @param radius The radius around the player to search for a safe location. + * @param attemptCount The number of attempts to find a safe location. + * @return A CompletableFuture containing the random Location that is deemed safe. + */ + CompletableFuture getSafeRandomLocation(World world, RandomTeleportRadius radius, int attemptCount); + /** * Asynchronously retrieves a safe random location within the border in specified world. * diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/SimpleRandomTeleportRadius.java b/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/SimpleRandomTeleportRadius.java new file mode 100644 index 000000000..ac8f4dab9 --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/feature/randomteleport/SimpleRandomTeleportRadius.java @@ -0,0 +1,12 @@ +package com.eternalcode.core.feature.randomteleport; + +record SimpleRandomTeleportRadius(int minX, int maxX, int minZ, int maxZ) implements RandomTeleportRadius { + public SimpleRandomTeleportRadius { + if (minX > maxX) { + throw new IllegalArgumentException("minX cannot be greater than maxX"); + } + if (minZ > maxZ) { + throw new IllegalArgumentException("minZ cannot be greater than maxZ"); + } + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/ConfigurationManager.java b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/ConfigurationManager.java index e0fd77dc4..ae974a502 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/ConfigurationManager.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/ConfigurationManager.java @@ -14,6 +14,7 @@ import com.eternalcode.multification.notice.resolver.NoticeResolverRegistry; import java.io.File; import java.time.Duration; +import java.util.Collections; import java.util.HashSet; import java.util.Set; import net.dzikoysk.cdn.Cdn; @@ -77,4 +78,9 @@ public void reload() { this.load(config); } } + + public Set getConfigs() { + return Collections.unmodifiableSet(this.configs); + } + } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/implementation/PluginConfiguration.java b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/implementation/PluginConfiguration.java index ca3f0103b..11cf299f2 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/implementation/PluginConfiguration.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/implementation/PluginConfiguration.java @@ -7,9 +7,8 @@ import com.eternalcode.core.feature.automessage.AutoMessageSettings; import com.eternalcode.core.feature.chat.ChatSettings; import com.eternalcode.core.feature.jail.JailSettings; -import com.eternalcode.core.feature.randomteleport.RandomTeleportSettings; -import com.eternalcode.core.feature.randomteleport.RandomTeleportType; import com.eternalcode.core.feature.helpop.HelpOpSettings; +import com.eternalcode.core.feature.randomteleport.RandomTeleportSettingsImpl; import com.eternalcode.core.feature.spawn.SpawnSettings; import com.eternalcode.core.injector.annotations.component.ConfigurationFile; import com.eternalcode.core.feature.teleportrequest.TeleportRequestSettings; @@ -125,58 +124,7 @@ public Duration teleportationTimeToSpawn() { } @Description({ "", "# Random Teleport Section" }) - public RandomTeleport randomTeleport = new RandomTeleport(); - - @Contextual - public static class RandomTeleport implements RandomTeleportSettings, DelaySettings { - @Description({ - "# Type of random teleportation,", - "# WORLD_BORDER_RADIUS - radius based on the world-border size.", - "# STATIC_RADIUS - radius based on the manually value." - }) - public RandomTeleportType randomTeleportType = RandomTeleportType.WORLD_BORDER_RADIUS; - - @Description({ - "# Radius of random teleportation, this uses for starting point spawn via /setworldspawn.", - "# If you want to use a static radius, set the type to STATIC_RADIUS and set the radius here.", - "# If you using WORLD_BORDER_RADIUS, this value will be ignored." - }) - public int randomTeleportRadius = 1000; - - @Description("# Teleport to a specific world, if left empty it will teleport to the player's current world") - public String randomTeleportWorld = "world"; - - @Description("# Number of attempts to teleport to a random location") - public int randomTeleportAttempts = 10; - - @Override - public int randomTeleportRadius() { - return this.randomTeleportRadius; - } - - @Override - public RandomTeleportType randomTeleportType() { - return this.randomTeleportType; - } - - @Override - public String randomTeleportWorld() { - return this.randomTeleportWorld; - } - - @Override - public int randomTeleportAttempts() { - return this.randomTeleportAttempts; - } - - @Description("# Delay to request next random teleportation") - public Duration randomTeleportDelay = Duration.ofSeconds(60); - - @Override - public Duration delay() { - return this.randomTeleportDelay; - } - } + public RandomTeleportSettingsImpl randomTeleport = new RandomTeleportSettingsImpl(); @Description({ " ", "# Homes Section" }) public Homes homes = new Homes(); diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/Migration.java b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/Migration.java new file mode 100644 index 000000000..29f44b8f6 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/Migration.java @@ -0,0 +1,7 @@ +package com.eternalcode.core.configuration.migration; + +public interface Migration { + + boolean migrate(); + +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/MigrationController.java b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/MigrationController.java new file mode 100644 index 000000000..67dbe8f3e --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/MigrationController.java @@ -0,0 +1,38 @@ +package com.eternalcode.core.configuration.migration; + +import com.eternalcode.core.configuration.ConfigurationManager; +import com.eternalcode.core.configuration.ReloadableConfig; +import com.eternalcode.core.injector.annotations.Inject; +import com.eternalcode.core.injector.annotations.component.Controller; +import com.eternalcode.core.publish.Subscribe; +import com.eternalcode.core.publish.Subscriber; +import com.eternalcode.core.publish.event.EternalInitializeEvent; +import java.util.logging.Logger; + +@Controller +class MigrationController implements Subscriber { + + private final MigrationService migrationService; + private final ConfigurationManager configurationManager; + private final Logger logger; + + @Inject + MigrationController(MigrationService migrationService, ConfigurationManager configurationManager, Logger logger) { + this.migrationService = migrationService; + this.configurationManager = configurationManager; + this.logger = logger; + } + + @Subscribe + void onMigration(EternalInitializeEvent event) { + for (ReloadableConfig config : configurationManager.getConfigs()) { + boolean wasMigrated = migrationService.migrate(config); + + if (wasMigrated) { + configurationManager.save(config); + logger.info("Configuration " + config.getClass().getSimpleName() + " was migrated and saved."); + } + } + } + +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/MigrationService.java b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/MigrationService.java new file mode 100644 index 000000000..42878c6ad --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migration/MigrationService.java @@ -0,0 +1,37 @@ +package com.eternalcode.core.configuration.migration; + +import com.eternalcode.core.configuration.ReloadableConfig; +import com.eternalcode.core.injector.annotations.component.Service; +import com.eternalcode.core.util.ReflectUtil; +import java.lang.reflect.Field; +import net.dzikoysk.cdn.entity.Contextual; + +@Service +class MigrationService { + + public boolean migrate(T config) { + return reflectMigrate(config); + } + + private boolean reflectMigrate(T config) { + boolean isMigrated = false; + + for (Field declaredField : ReflectUtil.getAllSuperFields(config.getClass())) { + Class fieldType = declaredField.getType(); + + if (Migration.class.isAssignableFrom(fieldType)) { + Migration migration = ReflectUtil.getFieldValue(declaredField, config); + boolean wasMigrationSuccessful = migration.migrate(); + isMigrated |= wasMigrationSuccessful; + } + + if (fieldType.isAnnotationPresent(Contextual.class)) { + Object fieldValue = ReflectUtil.getFieldValue(declaredField, config); + isMigrated |= reflectMigrate(fieldValue); + } + } + + return isMigrated; + } + +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportCommand.java index 63be8bfe9..cf4b16e8a 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportCommand.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportCommand.java @@ -1,11 +1,15 @@ package com.eternalcode.core.feature.randomteleport; +import static com.eternalcode.core.feature.randomteleport.RandomTeleportPermissionConstant.RTP_BYPASS_PERMISSION; +import static com.eternalcode.core.feature.randomteleport.RandomTeleportPermissionConstant.RTP_COMMAND_OTHER; +import static com.eternalcode.core.feature.randomteleport.RandomTeleportPermissionConstant.RTP_COMMAND_SELF; +import static com.eternalcode.core.feature.randomteleport.RandomTeleportPlaceholders.PLACEHOLDERS; + import com.eternalcode.annotations.scan.command.DescriptionDocs; import com.eternalcode.core.configuration.implementation.PluginConfiguration; import com.eternalcode.core.delay.Delay; import com.eternalcode.core.injector.annotations.Inject; import com.eternalcode.core.notice.NoticeService; -import com.eternalcode.core.placeholder.Placeholders; import com.eternalcode.core.util.DurationUtil; import com.eternalcode.core.viewer.Viewer; import dev.rollczi.litecommands.annotations.argument.Arg; @@ -15,40 +19,34 @@ import dev.rollczi.litecommands.annotations.permission.Permission; import java.time.Duration; import java.util.UUID; +import java.util.function.Supplier; import org.bukkit.entity.Player; @Command(name = "rtp", aliases = "randomteleport") class RandomTeleportCommand { - private static final Placeholders PLACEHOLDERS = Placeholders.builder() - .with("{PLAYER}", Player::getName) - .with("{WORLD}", player -> player.getWorld().getName()) - .with("{X}", player -> String.valueOf(player.getLocation().getBlockX())) - .with("{Y}", player -> String.valueOf(player.getLocation().getBlockY())) - .with("{Z}", player -> String.valueOf(player.getLocation().getBlockZ())) - .build(); - - private static final String RTP_BYPASS_PERMISSION = "eternalcore.rtp.bypass"; - private final NoticeService noticeService; private final RandomTeleportService randomTeleportService; + private final RandomTeleportTaskService randomTeleportTaskService; private final PluginConfiguration config; - private final Delay delay; + private final Delay cooldown; @Inject RandomTeleportCommand( NoticeService noticeService, RandomTeleportService randomTeleportService, + RandomTeleportTaskService randomTeleportTaskService, PluginConfiguration config ) { this.noticeService = noticeService; this.randomTeleportService = randomTeleportService; + this.randomTeleportTaskService = randomTeleportTaskService; this.config = config; - this.delay = new Delay<>(this.config.randomTeleport); + this.cooldown = new Delay<>((Supplier) () -> this.config.randomTeleport.cooldown()); } @Execute - @Permission("eternalcore.rtp") + @Permission(RTP_COMMAND_SELF) @DescriptionDocs(description = "Teleportation of the sender to a random location, if you want bypass cooldown use eternalcore.rtp.bypass permission") void executeSelf(@Context Player player) { UUID uuid = player.getUniqueId(); @@ -62,7 +60,7 @@ void executeSelf(@Context Player player) { .player(player.getUniqueId()) .send(); - this.randomTeleportService.teleport(player) + this.randomTeleportTaskService.createTeleport(player) .whenCompleteAsync((result, error) -> { if (error != null || !result.success()) { this.handleTeleportFailure(player); @@ -72,11 +70,11 @@ void executeSelf(@Context Player player) { this.handleTeleportSuccess(player); }); - this.delay.markDelay(uuid, this.config.randomTeleport.delay()); + this.cooldown.markDelay(uuid, this.config.randomTeleport.delay()); } @Execute - @Permission("eternalcore.rtp.other") + @Permission(RTP_COMMAND_OTHER) @DescriptionDocs(description = "Teleportation of a player to a random location.", arguments = "") void executeOther(@Context Viewer sender, @Arg Player player) { UUID uuid = player.getUniqueId(); @@ -97,11 +95,18 @@ void executeOther(@Context Viewer sender, @Arg Player player) { return; } - this.handleTeleportSuccess(player); this.handleAdminTeleport(sender, player); }); - this.delay.markDelay(uuid, this.config.randomTeleport.delay()); + this.cooldown.markDelay(uuid, this.config.randomTeleport.delay()); + } + + private void handleTeleportSuccess(Player player) { + this.noticeService.player( + player.getUniqueId(), + translation -> translation.randomTeleport().teleportedToRandomLocation(), + RandomTeleportPlaceholders.PLACEHOLDERS.toFormatter(player) + ); } private void handleTeleportFailure(Player player) { @@ -111,13 +116,6 @@ private void handleTeleportFailure(Player player) { .send(); } - private void handleTeleportSuccess(Player player) { - this.noticeService.player( - player.getUniqueId(), - translation -> translation.randomTeleport().teleportedToRandomLocation(), - PLACEHOLDERS.toFormatter(player)); - } - private void handleAdminTeleport(Viewer sender, Player player) { this.noticeService.viewer( sender, @@ -132,8 +130,8 @@ private boolean hasRandomTeleportDelay(Player player) { return false; } - if (this.delay.hasDelay(uniqueId)) { - Duration time = this.delay.getDurationToExpire(uniqueId); + if (this.cooldown.hasDelay(uniqueId)) { + Duration time = this.cooldown.getDurationToExpire(uniqueId); this.noticeService.create() .notice(translation -> translation.randomTeleport().randomTeleportDelay()) diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportHeightRange.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportHeightRange.java new file mode 100644 index 000000000..0d01ea85e --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportHeightRange.java @@ -0,0 +1,30 @@ +package com.eternalcode.core.feature.randomteleport; + +import net.dzikoysk.cdn.entity.Contextual; + +@Contextual +class RandomTeleportHeightRange { + + int minY; + int maxY; + + RandomTeleportHeightRange(int minY, int maxY) { + this.minY = minY; + this.maxY = maxY; + } + + RandomTeleportHeightRange() { + } + + static RandomTeleportHeightRange of(int minY, int maxY) { + return new RandomTeleportHeightRange(minY, maxY); + } + + int getMinY() { + return this.minY; + } + + int getMaxY() { + return this.maxY; + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportPermissionConstant.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportPermissionConstant.java new file mode 100644 index 000000000..d448db9ae --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportPermissionConstant.java @@ -0,0 +1,8 @@ +package com.eternalcode.core.feature.randomteleport; + +final class RandomTeleportPermissionConstant { + + static final String RTP_BYPASS_PERMISSION = "eternalcore.rtp.bypass"; + static final String RTP_COMMAND_SELF = "eternalcore.rtp"; + static final String RTP_COMMAND_OTHER = "eternalcore.rtp.other"; +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportPlaceholders.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportPlaceholders.java new file mode 100644 index 000000000..7d3301828 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportPlaceholders.java @@ -0,0 +1,16 @@ +package com.eternalcode.core.feature.randomteleport; + +import com.eternalcode.core.placeholder.Placeholders; +import org.bukkit.entity.Player; + +final class RandomTeleportPlaceholders { + + static final Placeholders PLACEHOLDERS = Placeholders.builder() + .with("{PLAYER}", Player::getName) + .with("{WORLD}", player -> player.getWorld().getName()) + .with("{X}", player -> String.valueOf(player.getLocation().getBlockX())) + .with("{Y}", player -> String.valueOf(player.getLocation().getBlockY())) + .with("{Z}", player -> String.valueOf(player.getLocation().getBlockZ())) + .build(); + +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportRadiusConfig.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportRadiusConfig.java new file mode 100644 index 000000000..965433926 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportRadiusConfig.java @@ -0,0 +1,42 @@ +package com.eternalcode.core.feature.randomteleport; + +import net.dzikoysk.cdn.entity.Contextual; + +@Contextual +class RandomTeleportRadiusConfig implements RandomTeleportRadius { + + int minX; + int maxX; + int minZ; + int maxZ; + + RandomTeleportRadiusConfig(int minX, int maxX, int minZ, int maxZ) { + this.minX = minX; + this.maxX = maxX; + this.minZ = minZ; + this.maxZ = maxZ; + } + + RandomTeleportRadiusConfig() { + } + + @Override + public int minX() { + return this.minX; + } + + @Override + public int maxX() { + return this.maxX; + } + + @Override + public int minZ() { + return this.minZ; + } + + @Override + public int maxZ() { + return this.maxZ; + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportResolveWorldUtil.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportResolveWorldUtil.java new file mode 100644 index 000000000..2f5564c5b --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportResolveWorldUtil.java @@ -0,0 +1,24 @@ +package com.eternalcode.core.feature.randomteleport; + +import static org.bukkit.Bukkit.getWorld; + +import org.bukkit.World; +import org.bukkit.entity.Player; + +final class RandomTeleportResolveWorldUtil { + + static World resolveWorld(Player player, RandomTeleportSettings settings) { + World world = player.getWorld(); + + if (!settings.world().isBlank()) { + world = getWorld(settings.world()); + + if (world == null) { + throw new IllegalStateException( + "World " + settings.world() + " does not exist!"); + } + } + + return world; + } +} 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 new file mode 100644 index 000000000..bbb3be754 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSafeLocationService.java @@ -0,0 +1,111 @@ +package com.eternalcode.core.feature.randomteleport; + +import com.eternalcode.commons.bukkit.position.PositionAdapter; +import com.eternalcode.core.configuration.implementation.LocationsConfiguration; +import com.eternalcode.core.injector.annotations.Inject; +import com.eternalcode.core.injector.annotations.component.Service; +import io.papermc.lib.PaperLib; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.WorldBorder; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; + +import java.util.Random; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +@Service +class RandomTeleportSafeLocationService { + + private static final int DEFAULT_NETHER_HEIGHT = 125; + private static final int NETHER_MAX_HEIGHT = 127; + + private final RandomTeleportSettings randomTeleportSettings; + private final LocationsConfiguration locationsConfiguration; + private final Random random = new Random(); + + @Inject + RandomTeleportSafeLocationService( + RandomTeleportSettings randomTeleportSettings, + LocationsConfiguration locationsConfiguration + ) { + this.randomTeleportSettings = randomTeleportSettings; + this.locationsConfiguration = locationsConfiguration; + } + + public CompletableFuture getSafeRandomLocation(World world, RandomTeleportRadius radius, int attemptCount) { + if (attemptCount < 0) { + return CompletableFuture.failedFuture(new RuntimeException("Cannot find safe location")); + } + + boolean noneWorld = this.locationsConfiguration.spawn.isNoneWorld(); + Location spawnLocation = !noneWorld + ? PositionAdapter.convert(this.locationsConfiguration.spawn) + : world.getSpawnLocation(); + + int spawnX = spawnLocation.getBlockX(); + int spawnZ = spawnLocation.getBlockZ(); + + int randomX = spawnX + this.random.nextInt(radius.maxX() - radius.minX() + 1) + radius.minX(); + int randomZ = spawnZ + this.random.nextInt(radius.maxZ() - radius.minZ() + 1) + radius.minZ(); + + return PaperLib.getChunkAtAsync(new Location(world, randomX, 100, randomZ)).thenCompose(chunk -> { + int randomY = chunk.getWorld().getHighestBlockYAt(randomX, randomZ); + + if (world.getEnvironment() == World.Environment.NETHER) { + randomY = this.random.nextInt(DEFAULT_NETHER_HEIGHT); + } + + RandomTeleportHeightRange heightRange = this.randomTeleportSettings.heightRange(); + int minHeight = heightRange.getMinY(); + int maxHeight = heightRange.getMaxY() - 1; + randomY = Math.min(Math.max(randomY, minHeight), maxHeight); + + Location generatedLocation = new Location(world, randomX, randomY, randomZ).add(0.5, 1, 0.5); + + if (this.isSafeLocation(chunk, generatedLocation)) { + return CompletableFuture.completedFuture(generatedLocation); + } + + return this.getSafeRandomLocation(world, radius, attemptCount - 1); + }); + } + + private boolean isSafeLocation(Chunk chunk, Location location) { + if (location.getWorld() == null) { + return false; + } + + World world = chunk.getWorld(); + Block block = world.getBlockAt(location); + Block blockAbove = block.getRelative(BlockFace.UP); + Block blockFloor = block.getRelative(BlockFace.DOWN); + + if (this.randomTeleportSettings.unsafeBlocks().contains(blockFloor.getType())) { + return false; + } + + Set airBlocks = this.randomTeleportSettings.airBlocks(); + if (!airBlocks.contains(block.getType()) || !airBlocks.contains(blockAbove.getType())) { + return false; + } + + if (!blockFloor.getType().isSolid()) { + return false; + } + + WorldBorder worldBorder = world.getWorldBorder(); + + if (!worldBorder.isInside(location)) { + return false; + } + + return switch (world.getEnvironment()) { + case NORMAL, THE_END, CUSTOM -> true; + case NETHER -> location.getY() <= NETHER_MAX_HEIGHT; + }; + } +} 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 9c633cea8..8f2260f19 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 @@ -1,94 +1,53 @@ package com.eternalcode.core.feature.randomteleport; -import com.eternalcode.core.configuration.implementation.LocationsConfiguration; +import static com.eternalcode.core.feature.randomteleport.RandomTeleportResolveWorldUtil.resolveWorld; + import com.eternalcode.core.event.EventCaller; import com.eternalcode.core.feature.randomteleport.event.PreRandomTeleportEvent; import com.eternalcode.core.feature.randomteleport.event.RandomTeleportEvent; import com.eternalcode.core.injector.annotations.Inject; import com.eternalcode.core.injector.annotations.component.Service; -import com.eternalcode.commons.bukkit.position.PositionAdapter; import io.papermc.lib.PaperLib; -import org.bukkit.Chunk; +import java.util.concurrent.CompletableFuture; import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Server; import org.bukkit.World; import org.bukkit.WorldBorder; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; -import java.util.EnumSet; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.CompletableFuture; - @Service class RandomTeleportServiceImpl implements RandomTeleportService { - private static final Set UNSAFE_BLOCKS = EnumSet.of( - Material.LAVA, - Material.WATER, - Material.CACTUS, - Material.FIRE, - Material.COBWEB, - Material.SWEET_BERRY_BUSH, - Material.MAGMA_BLOCK - ); - - private static final Set AIR_BLOCKS = EnumSet.of( - Material.AIR, - Material.CAVE_AIR, - Material.VOID_AIR, - Material.GRASS, - Material.TALL_GRASS, - Material.VINE - ); - - private static final int DEFAULT_NETHER_HEIGHT = 125; - private static final int NETHER_MAX_HEIGHT = 127; - private final RandomTeleportSettings randomTeleportSettings; - private final LocationsConfiguration locationsConfiguration; + private final RandomTeleportSafeLocationService safeLocationService; private final EventCaller eventCaller; - private final Server server; - private final Random random = new Random(); @Inject - RandomTeleportServiceImpl(RandomTeleportSettings randomTeleportSettings, LocationsConfiguration locationsConfiguration, EventCaller eventCaller, Server server) { + RandomTeleportServiceImpl( + RandomTeleportSettings randomTeleportSettings, + RandomTeleportSafeLocationService safeLocationService, EventCaller eventCaller + ) { this.randomTeleportSettings = randomTeleportSettings; - this.locationsConfiguration = locationsConfiguration; + this.safeLocationService = safeLocationService; this.eventCaller = eventCaller; - this.server = server; } @Override - public CompletableFuture teleport(Player player) { - World world = player.getWorld(); - - if (!this.randomTeleportSettings.randomTeleportWorld().isBlank()) { - world = this.server.getWorld(this.randomTeleportSettings.randomTeleportWorld()); - - if (world == null) { - throw new IllegalStateException("World " + this.randomTeleportSettings.randomTeleportWorld() + " is not exists!"); - } - } - - + public CompletableFuture teleport(Player player) { + World world = resolveWorld(player, randomTeleportSettings); return this.teleport(player, world); } @Override - public CompletableFuture teleport(Player player, World world) { + public CompletableFuture teleport(Player player, World world) { PreRandomTeleportEvent preRandomTeleportEvent = this.eventCaller.callEvent(new PreRandomTeleportEvent(player)); if (preRandomTeleportEvent.isCancelled()) { - return CompletableFuture.completedFuture(new TeleportResult(false, player.getLocation())); + return CompletableFuture.completedFuture(new RandomTeleportResult(false, player.getLocation())); } - return this.getSafeRandomLocation(world, this.randomTeleportSettings.randomTeleportAttempts()) + return this.getSafeRandomLocation(world, this.randomTeleportSettings.teleportAttempts()) .thenCompose(location -> PaperLib.teleportAsync(player, location).thenApply(success -> { - TeleportResult teleportResult = new TeleportResult(success, location); + RandomTeleportResult teleportResult = new RandomTeleportResult(success, location); RandomTeleportEvent event = new RandomTeleportEvent(player, location); this.eventCaller.callEvent(event); @@ -99,92 +58,36 @@ public CompletableFuture teleport(Player player, World world) { @Override public CompletableFuture getSafeRandomLocation(World world, int attemptCount) { - RandomTeleportType type = this.randomTeleportSettings.randomTeleportType(); - int radius = this.randomTeleportSettings.randomTeleportRadius(); + RandomTeleportRadius radius = switch (this.randomTeleportSettings.radiusType()) { + case STATIC_RADIUS -> this.randomTeleportSettings.radius(); + case WORLD_BORDER_RADIUS -> this.getWorldBorderRadius(world); + }; - return this.getSafeRandomLocation(world, type, radius, attemptCount); + return this.safeLocationService.getSafeRandomLocation(world, radius, attemptCount); } @Override public CompletableFuture getSafeRandomLocation(World world, int radius, int attemptCount) { - return this.getSafeRandomLocation(world, RandomTeleportType.STATIC_RADIUS, radius, attemptCount); + return this.safeLocationService.getSafeRandomLocation(world, RandomTeleportRadius.of(radius), attemptCount); } @Override - public CompletableFuture getSafeRandomLocationInWorldBorder(World world, int attemptCount) { - return this.getSafeRandomLocation(world, RandomTeleportType.WORLD_BORDER_RADIUS, 0, attemptCount); + public CompletableFuture getSafeRandomLocation( + World world, + RandomTeleportRadius radius, + int attemptCount + ) { + return this.safeLocationService.getSafeRandomLocation(world, radius, attemptCount); } - private CompletableFuture getSafeRandomLocation(World world, RandomTeleportType type, int radius, int attemptCount) { - if (attemptCount < 0) { - return CompletableFuture.failedFuture(new RuntimeException("Cannot find safe location")); - } - - if (type == RandomTeleportType.WORLD_BORDER_RADIUS) { - WorldBorder worldBorder = world.getWorldBorder(); - radius = (int) worldBorder.getSize() / 2; - } - - boolean noneWorld = this.locationsConfiguration.spawn.isNoneWorld(); - Location spawnLocation = !noneWorld - ? PositionAdapter.convert(this.locationsConfiguration.spawn) - : world.getSpawnLocation(); - - int spawnX = spawnLocation.getBlockX(); - int spawnZ = spawnLocation.getBlockZ(); - - int randomX = spawnX + this.random.nextInt(-radius, radius); - int randomZ = spawnZ + this.random.nextInt(-radius, radius); - - return PaperLib.getChunkAtAsync(new Location(world, randomX, 100, randomZ)).thenCompose(chunk -> { - int randomY = chunk.getWorld().getHighestBlockYAt(randomX, randomZ); - - if (world.getEnvironment() == World.Environment.NETHER) { - randomY = this.random.nextInt(DEFAULT_NETHER_HEIGHT); - } - - Location generatedLocation = new Location(world, randomX, randomY, randomZ).add(0.5, 1, 0.5); - - if (this.isSafeLocation(chunk, generatedLocation)) { - return CompletableFuture.completedFuture(generatedLocation); - } - - return this.getSafeRandomLocation(world, attemptCount - 1); - }); + @Override + public CompletableFuture getSafeRandomLocationInWorldBorder(World world, int attemptCount) { + return this.getSafeRandomLocation(world, this.getWorldBorderRadius(world), attemptCount); } - private boolean isSafeLocation(Chunk chunk, Location location) { - if (location == null || location.getWorld() == null) { - return false; - } - - World world = chunk.getWorld(); - Block block = world.getBlockAt(location); - Block blockAbove = block.getRelative(BlockFace.UP); - Block blockFloor = block.getRelative(BlockFace.DOWN); - - if (UNSAFE_BLOCKS.contains(blockFloor.getType())) { - return false; - } - - if (!AIR_BLOCKS.contains(block.getType()) || !AIR_BLOCKS.contains(blockAbove.getType())) { - return false; - } - - if (!blockFloor.getType().isSolid()) { - return false; - } - + private RandomTeleportRadius getWorldBorderRadius(World world) { WorldBorder worldBorder = world.getWorldBorder(); - - if (!worldBorder.isInside(location)) { - return false; - } - - return switch (world.getEnvironment()) { - case NORMAL, THE_END -> true; - case NETHER -> location.getY() <= NETHER_MAX_HEIGHT; - default -> false; - }; + int borderRadius = (int) (worldBorder.getSize() / 2); + return RandomTeleportRadius.of(-borderRadius, borderRadius, -borderRadius, borderRadius); } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSettings.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSettings.java index cd02c2be4..ff5e36bf7 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSettings.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSettings.java @@ -1,12 +1,27 @@ package com.eternalcode.core.feature.randomteleport; -public interface RandomTeleportSettings { +import java.time.Duration; +import java.util.Set; +import org.bukkit.Material; - int randomTeleportRadius(); +interface RandomTeleportSettings { - RandomTeleportType randomTeleportType(); + RandomTeleportRadius radius(); - String randomTeleportWorld(); + RandomTeleportType radiusType(); + + String world(); + + int teleportAttempts(); + + Set unsafeBlocks(); + + Set airBlocks(); + + RandomTeleportHeightRange heightRange(); + + Duration delay(); + + Duration cooldown(); - int randomTeleportAttempts(); } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSettingsImpl.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSettingsImpl.java new file mode 100644 index 000000000..df4e10eae --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportSettingsImpl.java @@ -0,0 +1,202 @@ +package com.eternalcode.core.feature.randomteleport; + +import com.eternalcode.core.configuration.migration.Migration; +import java.time.Duration; +import java.util.EnumSet; +import java.util.Set; +import net.dzikoysk.cdn.entity.Contextual; +import net.dzikoysk.cdn.entity.Description; +import org.bukkit.Material; + +@Contextual +public class RandomTeleportSettingsImpl implements RandomTeleportSettings, Migration { + + @Description("# Delay to wait for the random teleportation") + public Duration delay = Duration.ofSeconds(5); + + @Description("# Cooldown for random teleportation") + public Duration cooldown = Duration.ofSeconds(60); + @Deprecated public Duration randomTeleportDelay = null; + + @Description({ + "# Type of radius for random teleportation", + "# WORLD_BORDER_RADIUS - radius based on the world-border size.", + "# STATIC_RADIUS - static radius based on the configuration.", + }) + public RandomTeleportType radiusType = RandomTeleportType.WORLD_BORDER_RADIUS; + @Deprecated public RandomTeleportType randomTeleportType = null; + + @Description({ + "# Radius of random teleportation, this uses for starting point spawn via /setworldspawn.", + "# If you want to use a static radius, set the type to STATIC_RADIUS and set the radius here.", + "# If you using WORLD_BORDER_RADIUS, this value will be ignored." + }) + public RandomTeleportRadiusConfig radius = new RandomTeleportRadiusConfig(5000, 5000, 5000, 5000); + @Deprecated public Integer randomTeleportRadius = null; + + @Description("# Teleport to a specific world, if left empty it will teleport to the player's current world") + public String world = "world"; + @Deprecated public String randomTeleportWorld = null; + + @Description("# Number of attempts to find a safe location for random teleportation") + public int teleportAttempts = 10; + @Deprecated public Integer randomTeleportAttempts = null; + + @Description({ + "# Unsafe blocks for random teleportation", + "# These blocks are considered unsafe for players to be teleported onto.", + "# The list includes blocks that can cause damage, suffocation, or other", + "# undesirable effects. Ensure that the list is comprehensive to avoid", + "# teleporting players to hazardous locations." + }) + public Set unsafeBlocks = EnumSet.of( + Material.LAVA, + Material.WATER, + Material.CACTUS, + Material.FIRE, + Material.COBWEB, + Material.SWEET_BERRY_BUSH, + Material.MAGMA_BLOCK, + Material.BEDROCK, + Material.TNT, + Material.SEAGRASS, + Material.TALL_SEAGRASS, + Material.BUBBLE_COLUMN, + Material.POWDER_SNOW, + Material.WITHER_ROSE + ); + + @Description({ + "# Air blocks for random teleportation", + "# These blocks are considered safe for players to be teleported into.", + "# The list includes blocks that do not cause damage or impede movement.", + "# Ensure that the list is comprehensive to avoid teleporting players", + "# into solid or hazardous blocks." + }) + public Set airBlocks = EnumSet.of( + Material.AIR, + Material.CAVE_AIR, + Material.GRASS, + Material.TALL_GRASS, + Material.VINE, + Material.STRUCTURE_VOID, + Material.DEAD_BUSH, + Material.DANDELION, + Material.POPPY, + Material.BLUE_ORCHID, + Material.ALLIUM, + Material.AZURE_BLUET, + Material.RED_TULIP, + Material.ORANGE_TULIP, + Material.WHITE_TULIP, + Material.PINK_TULIP, + Material.OXEYE_DAISY, + Material.CORNFLOWER, + Material.LILY_OF_THE_VALLEY, + Material.SUNFLOWER, + Material.LILAC, + Material.ROSE_BUSH, + Material.PEONY, + Material.LARGE_FERN, + Material.LEGACY_LONG_GRASS, + Material.LEGACY_DEAD_BUSH, + Material.RAIL, + Material.POWERED_RAIL, + Material.DETECTOR_RAIL, + Material.ACTIVATOR_RAIL, + Material.REDSTONE_WIRE, + Material.WALL_TORCH, + Material.COMPARATOR, + Material.REPEATER, + Material.LEVER, + Material.STRING, + Material.SNOW + ); + + @Description({ + "# Height range for random teleportation", + "# - Minimum: -64 (1.18+) or 0 (older versions)", + "# - Maximum: 320 (1.18+) or 256 (older versions)", + "# - Default range: 60-160 blocks", + "# Note: Values are automatically capped to world height limits" + }) + public RandomTeleportHeightRange heightRange = RandomTeleportHeightRange.of(60, 160); + + @Override + public Duration delay() { + return this.delay; + } + + @Override + public RandomTeleportRadius radius() { + return this.radius; + } + + @Override + public RandomTeleportType radiusType() { + return this.radiusType; + } + + @Override + public String world() { + return this.world; + } + + @Override + public int teleportAttempts() { + return this.teleportAttempts; + } + + @Override + public Set unsafeBlocks() { + return unsafeBlocks; + } + + @Override + public Set airBlocks() { + return airBlocks; + } + + @Override + public RandomTeleportHeightRange heightRange() { + return this.heightRange; + } + + @Override + public Duration cooldown() { + return this.cooldown; + } + + @Override + public boolean migrate() { + boolean migrated = false; + if (randomTeleportDelay != null) { + cooldown = randomTeleportDelay; + randomTeleportDelay = null; + migrated = true; + } + if (randomTeleportType != null) { + radiusType = randomTeleportType; + randomTeleportType = null; + migrated = true; + } + if (randomTeleportRadius != null) { + radius = new RandomTeleportRadiusConfig(-randomTeleportRadius, randomTeleportRadius, -randomTeleportRadius, randomTeleportRadius); + randomTeleportRadius = null; + migrated = true; + } + if (randomTeleportWorld != null) { + world = randomTeleportWorld; + randomTeleportWorld = null; + migrated = true; + } + if (randomTeleportAttempts != null) { + teleportAttempts = randomTeleportAttempts; + randomTeleportAttempts = null; + migrated = true; + } + return migrated; + } + +} + diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportTaskService.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportTaskService.java new file mode 100644 index 000000000..366e6c6e6 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportTaskService.java @@ -0,0 +1,78 @@ +package com.eternalcode.core.feature.randomteleport; + +import static com.eternalcode.core.feature.randomteleport.RandomTeleportPermissionConstant.RTP_BYPASS_PERMISSION; +import static com.eternalcode.core.feature.randomteleport.RandomTeleportResolveWorldUtil.resolveWorld; + +import com.eternalcode.commons.bukkit.position.Position; +import com.eternalcode.commons.bukkit.position.PositionAdapter; +import com.eternalcode.core.feature.teleport.Teleport; +import com.eternalcode.core.feature.teleport.TeleportResult; +import com.eternalcode.core.feature.teleport.TeleportTaskService; +import com.eternalcode.core.injector.annotations.Inject; +import com.eternalcode.core.injector.annotations.component.Service; +import com.eternalcode.core.notice.NoticeService; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; + +@Service +class RandomTeleportTaskService { + + private final NoticeService noticeService; + private final RandomTeleportSettings randomTeleportSettings; + private final TeleportTaskService teleportTaskService; + private final RandomTeleportSafeLocationService randomTeleportSafeLocationService; + private final RandomTeleportService randomTeleportService; + + @Inject + RandomTeleportTaskService( + NoticeService noticeService, + RandomTeleportSettings randomTeleportSettings, + TeleportTaskService teleportTaskService, + RandomTeleportSafeLocationService randomTeleportSafeLocationService, RandomTeleportService randomTeleportService + ) { + this.noticeService = noticeService; + this.randomTeleportSettings = randomTeleportSettings; + this.teleportTaskService = teleportTaskService; + this.randomTeleportSafeLocationService = randomTeleportSafeLocationService; + this.randomTeleportService = randomTeleportService; + } + + CompletableFuture createTeleport(Player player) { + World world = resolveWorld(player, randomTeleportSettings); + RandomTeleportRadius radius = this.randomTeleportSettings.radius(); + return this.randomTeleportSafeLocationService.getSafeRandomLocation( + world, + radius, + this.randomTeleportSettings.teleportAttempts() + ).thenCompose(location -> this.createTeleport(player, location)); + } + + private CompletableFuture createTeleport(Player player, Location location) { + if (player.hasPermission(RTP_BYPASS_PERMISSION)) { + return this.randomTeleportService.teleport(player); + } + + if (this.teleportTaskService.isInTeleport(player.getUniqueId())) { + this.noticeService.create() + .notice(translation -> translation.teleport().teleportTaskAlreadyExist()) + .player(player.getUniqueId()) + .send(); + + return CompletableFuture.completedFuture(new RandomTeleportResult(false, player.getLocation())); + } + + Location playerLocation = player.getLocation(); + Position playerPosition = PositionAdapter.convert(playerLocation); + Position destination = PositionAdapter.convert(location); + + Duration time = this.randomTeleportSettings.delay(); + Teleport teleport = + this.teleportTaskService.createTeleport(player.getUniqueId(), playerPosition, destination, time); + + return teleport.getResult() + .thenApply(result -> new RandomTeleportResult(result == TeleportResult.SUCCESS, location)); + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportType.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportType.java index f7dac9fbc..e3a8b422b 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportType.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportType.java @@ -1,6 +1,6 @@ package com.eternalcode.core.feature.randomteleport; -public enum RandomTeleportType { +enum RandomTeleportType { WORLD_BORDER_RADIUS, STATIC_RADIUS, diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/ENTranslation.java b/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/ENTranslation.java index 232a8c6e9..0abcc395c 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/ENTranslation.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/ENTranslation.java @@ -241,7 +241,12 @@ public static class ENTeleportSection implements TeleportSection { @Getter @Contextual public static class ENRandomTeleportSection implements RandomTeleportSection { - public Notice randomTeleportStarted = Notice.chat("Teleportation to a random location has started!"); + public Notice randomTeleportStarted = Notice.builder() + .chat("Teleportation to a random location has started!") + .title("Random teleport") + .subtitle("Searching, please wait...") + .build(); + public Notice randomTeleportFailed = Notice.chat("A safe location could not be found, please try again!"); diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/PLTranslation.java b/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/PLTranslation.java index d62768ae5..d8fa66581 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/PLTranslation.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/PLTranslation.java @@ -242,7 +242,12 @@ public static class PLTeleportSection implements TeleportSection { @Contextual public static class PLRandomTeleportSection implements RandomTeleportSection { @Description(" ") - public Notice randomTeleportStarted = Notice.chat("Rozpoczynanie procesu losowania lokalizacji..."); + public Notice randomTeleportStarted = Notice.builder() + .chat("Rozpoczynanie procesu losowania lokalizacji...") + .title("Losowy teleport") + .subtitle("Wyszukiwanie lokalizacji, proszę czekać...") + .build(); + public Notice randomTeleportFailed = Notice.chat("Nie udało się znaleźć bezpiecznej lokalizacji, spróbuj ponownie!"); public Notice teleportedToRandomLocation = Notice.chat("Zostałeś przeteleportowany na losową lokalizację!"); diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/util/ReflectUtil.java b/eternalcore-core/src/main/java/com/eternalcode/core/util/ReflectUtil.java index cf3e98b69..add5d4817 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/util/ReflectUtil.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/util/ReflectUtil.java @@ -4,7 +4,9 @@ import com.google.common.reflect.ClassPath; import java.io.IOException; +import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -70,4 +72,26 @@ public static T unsafeCast(Object object) { return (T) object; } + public static List getAllSuperFields(Class aClass) { + List fields = new ArrayList<>(); + Class currentClass = aClass; + + while (currentClass != null && currentClass != Object.class) { + Collections.addAll(fields, currentClass.getDeclaredFields()); + + currentClass = currentClass.getSuperclass(); + } + + return fields; + } + + public static T getFieldValue(Field declaredField, Object object) { + try { + declaredField.setAccessible(true); + return unsafeCast(declaredField.get(object)); + } + catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } }