Skip to content

GH-1075 Add /tprp for caves. Move tprp feature to own section. #1077

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

Merged
merged 5 commits into from
Aug 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public <T extends OkaeriConfig & EternalConfigurationFile> 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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -34,21 +37,83 @@ public TeleportRandomPlayerService(
@Nullable
public Player findLeastRecentlyTeleportedPlayer(Player sender) {
UUID senderId = sender.getUniqueId();
return this.server.getOnlinePlayers().stream()

List<Player> 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<Player> 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");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
}
}
Original file line number Diff line number Diff line change
@@ -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("<red>✘ <dark_red>No player found to teleport!");

@Comment("{PLAYER} - The name of the player you have been teleported to")
public Notice teleportedToRandomPlayer =
Notice.chat("<green>► <white>Teleported to random player <green>{PLAYER}<white>!");

public Notice randomPlayerInRangeNotFound =
Notice.chat("<red>✘ <dark_red>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("<green>► <white>Teleported to a random player in range: <green>{PLAYER}<white>!");

}
Original file line number Diff line number Diff line change
@@ -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("<red>✘ <dark_red>Nie można odnaleźć gracza do teleportacji!");

@Comment("{PLAYER} - Nazwa gracza, do którego zostałeś teleportowany")
public Notice teleportedToRandomPlayer =
Notice.chat("<green>► <white>Zostałeś losowo teleportowany do <green>{PLAYER}<white>!");

public Notice randomPlayerInRangeNotFound =
Notice.chat("<red>✘ <dark_red>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("<green>► <white>Zostałeś losowo teleportowany do gracza w zasięgu: <green>{PLAYER}<white>!");
}
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -59,10 +60,6 @@ interface TeleportSection {
Notice teleportedToLastLocation();
Notice teleportedSpecifiedPlayerLastLocation();
Notice lastLocationNoExist();

// teleport to random player command
Notice randomPlayerNotFound();
Notice teleportedToRandomPlayer();
}

interface ChatSection {
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -209,13 +210,14 @@ public static class ENTeleportSection extends OkaeriConfig implements TeleportSe
public Notice teleportedSpecifiedPlayerLastLocation = Notice.chat("<green>► <white>Teleported <green>{PLAYER} <white>to the last location!");
@Comment(" ")
public Notice lastLocationNoExist = Notice.chat("<red>✘ <dark_red>Last location is not exist!");

@Comment(" ")
public Notice randomPlayerNotFound = Notice.chat("<red>✘ <dark_red>No player found to teleport!");
@Comment({" ", "# {PLAYER} - The player you were teleported"})
public Notice teleportedToRandomPlayer = Notice.chat("<green>► <white>Teleported to random player <green>{PLAYER}<white>!");
}

@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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -210,13 +211,14 @@ public static class PLTeleportSection extends OkaeriConfig implements TeleportSe
public Notice teleportedSpecifiedPlayerLastLocation = Notice.chat("<green>► <white>Przeteleportowano gracza <green>{PLAYER} <white>do ostatniej lokalizacji!");
@Comment(" ")
public Notice lastLocationNoExist = Notice.chat("<red>✘ <dark_red>Nie ma zapisanej ostatniej lokalizacji!");

@Comment(" ")
public Notice randomPlayerNotFound = Notice.chat("<red>✘ <dark_red>Nie można odnaleźć gracza do teleportacji!");
@Comment({" ", "# {PLAYER} - Gracz do którego cię teleportowano"})
public Notice teleportedToRandomPlayer = Notice.chat("<green>► <white>Zostałeś losowo teleportowany do <green>{PLAYER}<white>!");
}

@Comment({
" ",
"# Ta sekcja odpowiada za wiadomości komendy /tprp"
})
public PLTeleportToRandomPlayerMessages teleportToRandomPlayer = new PLTeleportToRandomPlayerMessages();

@Comment({
" ",
"# Ta sekcja odpowiada za edycję komunikatów losowej teleportacji",
Expand Down