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 d1a030448..fbe178d74 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 @@ -65,6 +65,7 @@ public T load(T config) { .withSerdesPack(serdesPack) .withBindFile(file) .saveDefaults() + .withRemoveOrphans(true) .load(true) .migrate(Migrations.ALL); // Remember: migration should be launched after the #load method. diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0003_Move_tprp_to_dedicated_section.java b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0003_Move_tprp_to_dedicated_section.java index 2d7977078..f9dc7870a 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0003_Move_tprp_to_dedicated_section.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0003_Move_tprp_to_dedicated_section.java @@ -9,10 +9,11 @@ class Migration_0003_Move_tprp_to_dedicated_section extends NamedMigration { Migration_0003_Move_tprp_to_dedicated_section() { super( "Move tprp to dedicated config section", - move("teleport", "teleportToRandomPlayer"), - move( - "teleportToRandomPlayer.includeOpPlayersInRandomTeleport", - "teleportToRandomPlayer.teleportToOpPlayers") + move("teleport.includeOpPlayersInRandomTeleport", "teleportToRandomPlayer.teleportToOpPlayers"), + + // for translation files + move("teleport.randomPlayerNotFound", "teleportToRandomPlayer.randomPlayerNotFound"), + move("teleport.teleportedToRandomPlayer", "teleportToRandomPlayer.teleportedToRandomPlayer") ); } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/TeleportRandomPlayerService.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/TeleportRandomPlayerService.java index 0a32fd8ce..7ebab55f4 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/TeleportRandomPlayerService.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/TeleportRandomPlayerService.java @@ -4,12 +4,15 @@ import com.eternalcode.core.injector.annotations.component.Service; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; -import java.time.Instant; -import java.util.Comparator; -import java.util.UUID; import java.util.concurrent.TimeUnit; import org.bukkit.Server; import org.bukkit.entity.Player; + +import java.time.Instant; +import java.util.Comparator; +import java.util.UUID; +import java.util.List; +import java.util.stream.Collectors; import org.jetbrains.annotations.Nullable; @Service @@ -34,21 +37,83 @@ public TeleportRandomPlayerService( @Nullable public Player findLeastRecentlyTeleportedPlayer(Player sender) { UUID senderId = sender.getUniqueId(); - return this.server.getOnlinePlayers().stream() + + List validTargets = this.server.getOnlinePlayers().stream() .filter(target -> !target.equals(sender)) .filter(target -> this.randomPlayerSettings.teleportToOpPlayers() || !target.isOp()) + .collect(Collectors.toList()); + + if (validTargets.isEmpty()) { + return null; + } + + return validTargets.stream() .min(Comparator.comparing(target -> this.getTeleportationHistory(target, senderId))) .orElse(null); } + @Nullable + public Player findLeastRecentlyTeleportedPlayerByY(Player sender, int minY, int maxY) { + if (minY > maxY) { + throw new IllegalArgumentException("MinY cannot be greater than maxY"); + } + + UUID senderId = sender.getUniqueId(); + + List validTargets = this.server.getOnlinePlayers().stream() + .filter(target -> !target.equals(sender)) + .filter(target -> this.randomPlayerSettings.teleportToOpPlayers() || !target.isOp()) + .filter(target -> this.isPlayerInYRange(target, minY, maxY)) + .collect(Collectors.toList()); + + if (validTargets.isEmpty()) { + return null; + } + + return validTargets.stream() + .min(Comparator.comparing(target -> this.getTeleportationHistory(target, senderId))) + .orElse(null); + } + + private boolean isPlayerInYRange(Player player, int minY, int maxY) { + if (player == null) { + return false; + } + else { + player.getLocation(); + } + + double playerY = player.getLocation().getY(); + return playerY >= minY && playerY <= maxY; + } + private Instant getTeleportationHistory(Player target, UUID senderId) { - return this.teleportationHistory.get(new HistoryKey(senderId, target.getUniqueId()), key -> Instant.EPOCH); + if (target == null) { + return Instant.EPOCH; + } + + return this.teleportationHistory.get( + new HistoryKey(senderId, target.getUniqueId()), + key -> Instant.EPOCH + ); } public void updateTeleportationHistory(Player sender, Player target) { - this.teleportationHistory.put(new HistoryKey(sender.getUniqueId(), target.getUniqueId()), Instant.now()); + if (sender == null || target == null) { + return; + } + + this.teleportationHistory.put( + new HistoryKey(sender.getUniqueId(), target.getUniqueId()), + Instant.now() + ); } private record HistoryKey(UUID sender, UUID target) { + public HistoryKey { + if (sender == null || target == null) { + throw new IllegalArgumentException("Sender and target UUIDs cannot be null"); + } + } } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/TeleportToRandomPlayerCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/TeleportToRandomPlayerCommand.java index 252faf478..d3c75803f 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/TeleportToRandomPlayerCommand.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/TeleportToRandomPlayerCommand.java @@ -3,6 +3,7 @@ import com.eternalcode.annotations.scan.command.DescriptionDocs; import com.eternalcode.core.injector.annotations.Inject; import com.eternalcode.core.notice.NoticeService; +import dev.rollczi.litecommands.annotations.argument.Arg; import dev.rollczi.litecommands.annotations.command.Command; import dev.rollczi.litecommands.annotations.context.Context; import dev.rollczi.litecommands.annotations.execute.Execute; @@ -31,30 +32,58 @@ public TeleportToRandomPlayerCommand( void execute(@Context Player player) { Player targetPlayer = this.randomPlayerService.findLeastRecentlyTeleportedPlayer(player); - if (targetPlayer != null && targetPlayer.equals(player)) { + if (targetPlayer == null || !targetPlayer.isOnline()) { this.noticeService.create() .player(player.getUniqueId()) - .notice(translation -> translation.teleport().randomPlayerNotFound()) + .notice(translation -> translation.teleportToRandomPlayer().randomPlayerNotFound()) .send(); return; } - if (targetPlayer == null) { + this.randomPlayerService.updateTeleportationHistory(player, targetPlayer); + PaperLib.teleportAsync(player, targetPlayer.getLocation()); + + this.noticeService.create() + .player(player.getUniqueId()) + .notice(translation -> translation.teleportToRandomPlayer().teleportedToRandomPlayer()) + .placeholder("{PLAYER}", targetPlayer.getName()) + .send(); + } + + /** + * Teleports the player to a random player within a specific Y-level range. + * Useful for finding players in caves, spotting potential x-rayers, + * or quickly locating players in general. + */ + @Execute + @DescriptionDocs(description = "Teleport to a player who is within specified Y range and hasn't been teleported to recently") + void executeWithYRange(@Context Player player, @Arg int minY, @Arg int maxY) { + if (minY > maxY) { this.noticeService.create() .player(player.getUniqueId()) - .notice(translation -> translation.teleport().randomPlayerNotFound()) + .notice(translation -> translation.teleportToRandomPlayer().randomPlayerInRangeNotFound()) .send(); return; } - this.randomPlayerService.updateTeleportationHistory(player, targetPlayer); + Player targetPlayer = this.randomPlayerService.findLeastRecentlyTeleportedPlayerByY(player, minY, maxY); + if (targetPlayer == null || !targetPlayer.isOnline()) { + this.noticeService.create() + .player(player.getUniqueId()) + .notice(translation -> translation.teleportToRandomPlayer().randomPlayerInRangeNotFound()) + .send(); + return; + } + + this.randomPlayerService.updateTeleportationHistory(player, targetPlayer); PaperLib.teleportAsync(player, targetPlayer.getLocation()); this.noticeService.create() .player(player.getUniqueId()) - .notice(translation -> translation.teleport().teleportedToRandomPlayer()) + .notice(translation -> translation.teleportToRandomPlayer().teleportedToRandomPlayerInRange()) .placeholder("{PLAYER}", targetPlayer.getName()) + .placeholder("{Y}", String.valueOf((int) targetPlayer.getLocation().getY())) .send(); } -} +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/messages/ENTeleportToRandomPlayerMessages.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/messages/ENTeleportToRandomPlayerMessages.java new file mode 100644 index 000000000..83a2aaeb5 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/messages/ENTeleportToRandomPlayerMessages.java @@ -0,0 +1,27 @@ +package com.eternalcode.core.feature.teleportrandomplayer.messages; + +import com.eternalcode.multification.notice.Notice; +import eu.okaeri.configs.OkaeriConfig; +import eu.okaeri.configs.annotation.Comment; +import lombok.Getter; +import lombok.experimental.Accessors; + +@Getter +@Accessors(fluent = true) +public class ENTeleportToRandomPlayerMessages extends OkaeriConfig implements TeleportToRandomPlayerMessages { + + public Notice randomPlayerNotFound = + Notice.chat("No player found to teleport!"); + + @Comment("{PLAYER} - The name of the player you have been teleported to") + public Notice teleportedToRandomPlayer = + Notice.chat("Teleported to random player {PLAYER}!"); + + public Notice randomPlayerInRangeNotFound = + Notice.chat("No player found in range to teleport!"); + + @Comment("{PLAYER} - The name of the player you have been teleported to within range") + public Notice teleportedToRandomPlayerInRange = + Notice.chat("Teleported to a random player in range: {PLAYER}!"); + +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/messages/PLTeleportToRandomPlayerMessages.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/messages/PLTeleportToRandomPlayerMessages.java new file mode 100644 index 000000000..ec32b5c20 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/messages/PLTeleportToRandomPlayerMessages.java @@ -0,0 +1,26 @@ +package com.eternalcode.core.feature.teleportrandomplayer.messages; + +import com.eternalcode.multification.notice.Notice; +import eu.okaeri.configs.OkaeriConfig; +import eu.okaeri.configs.annotation.Comment; +import lombok.Getter; +import lombok.experimental.Accessors; + +@Getter +@Accessors(fluent = true) +public class PLTeleportToRandomPlayerMessages extends OkaeriConfig implements TeleportToRandomPlayerMessages { + + public Notice randomPlayerNotFound = + Notice.chat("Nie można odnaleźć gracza do teleportacji!"); + + @Comment("{PLAYER} - Nazwa gracza, do którego zostałeś teleportowany") + public Notice teleportedToRandomPlayer = + Notice.chat("Zostałeś losowo teleportowany do {PLAYER}!"); + + public Notice randomPlayerInRangeNotFound = + Notice.chat("Nie można odnaleźć gracza w zasięgu do teleportacji!"); + + @Comment("{PLAYER} - Nazwa gracza, do którego zostałeś teleportowany w zasięgu") + public Notice teleportedToRandomPlayerInRange = + Notice.chat("Zostałeś losowo teleportowany do gracza w zasięgu: {PLAYER}!"); +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/messages/TeleportToRandomPlayerMessages.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/messages/TeleportToRandomPlayerMessages.java new file mode 100644 index 000000000..d30fab73e --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/teleportrandomplayer/messages/TeleportToRandomPlayerMessages.java @@ -0,0 +1,10 @@ +package com.eternalcode.core.feature.teleportrandomplayer.messages; + +import com.eternalcode.multification.notice.Notice; + +public interface TeleportToRandomPlayerMessages { + Notice randomPlayerNotFound(); + Notice teleportedToRandomPlayer(); + Notice randomPlayerInRangeNotFound(); + Notice teleportedToRandomPlayerInRange(); +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/translation/Translation.java b/eternalcore-core/src/main/java/com/eternalcode/core/translation/Translation.java index 9144f3e30..4e8911216 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/translation/Translation.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/translation/Translation.java @@ -20,6 +20,7 @@ import com.eternalcode.core.feature.signeditor.messages.SignEditorMessages; import com.eternalcode.core.feature.spawn.messages.SpawnMessages; import com.eternalcode.core.feature.sudo.messages.SudoMessages; +import com.eternalcode.core.feature.teleportrandomplayer.messages.TeleportToRandomPlayerMessages; import com.eternalcode.core.feature.teleportrequest.messages.TeleportRequestMessages; import com.eternalcode.core.feature.time.messages.TimeAndWeatherMessages; import com.eternalcode.core.feature.vanish.messages.VanishMessages; @@ -59,10 +60,6 @@ interface TeleportSection { Notice teleportedToLastLocation(); Notice teleportedSpecifiedPlayerLastLocation(); Notice lastLocationNoExist(); - - // teleport to random player command - Notice randomPlayerNotFound(); - Notice teleportedToRandomPlayer(); } interface ChatSection { @@ -212,6 +209,8 @@ interface ContainerSection { SudoMessages sudo(); // Teleport Section TeleportSection teleport(); + // teleport to random player section. + TeleportToRandomPlayerMessages teleportToRandomPlayer(); // Random Teleport Section RandomTeleportMessages randomTeleport(); // Chat Section 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 e212638a7..3a4465272 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 @@ -20,6 +20,7 @@ import com.eternalcode.core.feature.signeditor.messages.ENSignEditorMessages; import com.eternalcode.core.feature.spawn.messages.ENSpawnMessages; import com.eternalcode.core.feature.sudo.messages.ENSudoMessages; +import com.eternalcode.core.feature.teleportrandomplayer.messages.ENTeleportToRandomPlayerMessages; import com.eternalcode.core.feature.teleportrequest.messages.ENTeleportRequestMessages; import com.eternalcode.core.feature.time.messages.ENTimeAndWeatherMessages; import com.eternalcode.core.feature.vanish.messages.ENVanishMessages; @@ -209,13 +210,14 @@ public static class ENTeleportSection extends OkaeriConfig implements TeleportSe public Notice teleportedSpecifiedPlayerLastLocation = Notice.chat("Teleported {PLAYER} to the last location!"); @Comment(" ") public Notice lastLocationNoExist = Notice.chat("Last location is not exist!"); - - @Comment(" ") - public Notice randomPlayerNotFound = Notice.chat("No player found to teleport!"); - @Comment({" ", "# {PLAYER} - The player you were teleported"}) - public Notice teleportedToRandomPlayer = Notice.chat("Teleported to random player {PLAYER}!"); } + @Comment({ + " ", + "# This section is responsible for the messages of the /tprp command", + }) + public ENTeleportToRandomPlayerMessages teleportToRandomPlayer = new ENTeleportToRandomPlayerMessages(); + @Comment({ " ", "# This section is responsible for messages related to random teleport", 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 e280d773c..ab2ac10cf 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 @@ -20,6 +20,7 @@ import com.eternalcode.core.feature.signeditor.messages.PLSignEditorMessages; import com.eternalcode.core.feature.spawn.messages.PLSpawnMessages; import com.eternalcode.core.feature.sudo.messages.PLSudoMessages; +import com.eternalcode.core.feature.teleportrandomplayer.messages.PLTeleportToRandomPlayerMessages; import com.eternalcode.core.feature.teleportrequest.messages.PLTeleportRequestMessages; import com.eternalcode.core.feature.time.messages.PLTimeAndWeatherMessages; import com.eternalcode.core.feature.vanish.messages.PLVanishMessages; @@ -210,13 +211,14 @@ public static class PLTeleportSection extends OkaeriConfig implements TeleportSe public Notice teleportedSpecifiedPlayerLastLocation = Notice.chat("Przeteleportowano gracza {PLAYER} do ostatniej lokalizacji!"); @Comment(" ") public Notice lastLocationNoExist = Notice.chat("Nie ma zapisanej ostatniej lokalizacji!"); - - @Comment(" ") - public Notice randomPlayerNotFound = Notice.chat("Nie można odnaleźć gracza do teleportacji!"); - @Comment({" ", "# {PLAYER} - Gracz do którego cię teleportowano"}) - public Notice teleportedToRandomPlayer = Notice.chat("Zostałeś losowo teleportowany do {PLAYER}!"); } + @Comment({ + " ", + "# Ta sekcja odpowiada za wiadomości komendy /tprp" + }) + public PLTeleportToRandomPlayerMessages teleportToRandomPlayer = new PLTeleportToRandomPlayerMessages(); + @Comment({ " ", "# Ta sekcja odpowiada za edycję komunikatów losowej teleportacji",