From eb7bd6cc95abcc7f71a388414d7f25c75e6e963a Mon Sep 17 00:00:00 2001 From: akastijn Date: Tue, 12 May 2026 20:06:47 +0200 Subject: [PATCH] Added random plot command by Jakob Stranz --- build.gradle.kts | 4 + settings.gradle.kts | 3 +- .../com/alttd/playerutils/PlayerUtils.java | 17 +- .../playerutils_subcommands/RandomPlot.java | 181 ++++++++++++++++++ .../com/alttd/playerutils/config/Config.java | 52 ++++- .../alttd/playerutils/config/Messages.java | 24 ++- 6 files changed, 272 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/alttd/playerutils/commands/playerutils_subcommands/RandomPlot.java diff --git a/build.gradle.kts b/build.gradle.kts index b59db9b..8d48eb5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -47,4 +47,8 @@ dependencies { compileOnly("org.projectlombok:lombok:1.18.38") annotationProcessor("org.projectlombok:lombok:1.18.38") + + implementation(platform("com.intellectualsites.bom:bom-newest:1.56")) + compileOnly("com.intellectualsites.plotsquared:plotsquared-core") + compileOnly("com.intellectualsites.plotsquared:plotsquared-bukkit") { isTransitive = false } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 3a4ceae..7ef543d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,8 +14,9 @@ dependencyResolutionManagement { password = nexusPass } } - maven("https://repo.destro.xyz/snapshots") maven("https://jitpack.io") + // PlotSquared + maven("https://repo.papermc.io/repository/maven-public/") } repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) } diff --git a/src/main/java/com/alttd/playerutils/PlayerUtils.java b/src/main/java/com/alttd/playerutils/PlayerUtils.java index f75ef7d..3ad68e9 100644 --- a/src/main/java/com/alttd/playerutils/PlayerUtils.java +++ b/src/main/java/com/alttd/playerutils/PlayerUtils.java @@ -2,29 +2,42 @@ package com.alttd.playerutils; import com.alttd.playerutils.commands.PlayerUtilsCommand; import com.alttd.playerutils.commands.playerutils_subcommands.GhastSpeed; +import com.alttd.playerutils.commands.playerutils_subcommands.RandomPlot; import com.alttd.playerutils.commands.playerutils_subcommands.RotateBlock; import com.alttd.playerutils.config.Config; import com.alttd.playerutils.config.KeyStorage; import com.alttd.playerutils.config.Messages; import com.alttd.playerutils.event_listeners.*; +import lombok.extern.slf4j.Slf4j; import org.bukkit.Bukkit; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import java.util.concurrent.TimeUnit; +@Slf4j public final class PlayerUtils extends JavaPlugin { private PlayerUtilsCommand playerUtilsCommand; @Override public void onEnable() { - registerCommands(); - registerEvents(); reloadConfigs(); + registerCommands(); + registerRandomPlot(); + registerEvents(); registerSchedulers(); } + private void registerRandomPlot() { + if (!getServer().getPluginManager().isPluginEnabled("PlotSquared")) { + log.warn("PlotSquared not found — random plot command will not be registered."); + return; + } + playerUtilsCommand.addSubCommand(new RandomPlot(this)); + log.info("PlotSquared found - registered random plot command."); + } + @Override public void onDisable() { KeyStorage.STORAGE.save(); diff --git a/src/main/java/com/alttd/playerutils/commands/playerutils_subcommands/RandomPlot.java b/src/main/java/com/alttd/playerutils/commands/playerutils_subcommands/RandomPlot.java new file mode 100644 index 0000000..f75442f --- /dev/null +++ b/src/main/java/com/alttd/playerutils/commands/playerutils_subcommands/RandomPlot.java @@ -0,0 +1,181 @@ +package com.alttd.playerutils.commands.playerutils_subcommands; + +import com.alttd.playerutils.PlayerUtils; +import com.alttd.playerutils.commands.SubCommand; +import com.alttd.playerutils.config.Config; +import com.alttd.playerutils.config.Messages; +import com.plotsquared.bukkit.player.BukkitPlayer; +import com.plotsquared.bukkit.util.BukkitUtil; +import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.events.TeleportCause; +import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.plot.PlotArea; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.title.Title; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.Nullable; + +import java.time.Duration; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; + +@Slf4j +@RequiredArgsConstructor +public class RandomPlot extends SubCommand { + private static final String PERMISSION = "playerutils.randomplot"; + private final PlayerUtils plugin; + private final MiniMessage miniMessage = MiniMessage.miniMessage(); + + @Override + public boolean onCommand(CommandSender commandSender, String[] args) { + if (!(commandSender instanceof Player player)) { + commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY); + return true; + } + + String worldName = player.getWorld().getName(); + if (!Config.RANDOM_PLOT.ALLOWED_WORLDS.contains(worldName)) { + player.sendRichMessage(Messages.RANDOM_PLOT.WORLD_NOT_ALLOWED); + return true; + } + + if (!player.hasPermission(PERMISSION)) { + player.sendRichMessage(Messages.GENERIC.NO_PERMISSION, Placeholder.parsed("permission", PERMISSION)); + return true; + } + + if (!Bukkit.getPluginManager().isPluginEnabled("PlotSquared")) { + player.sendRichMessage(Messages.RANDOM_PLOT.PLOT_SQUARED_NOT_ENABLED); + return true; + } + + List plots = collectPlots(worldName); + if (plots.isEmpty()) { + player.sendRichMessage(Messages.RANDOM_PLOT.NO_PLOTS_FOUND); + return true; + } + + Plot target = plots.get(ThreadLocalRandom.current().nextInt(plots.size())); + player.sendRichMessage(Messages.RANDOM_PLOT.TELEPORT_START); + startCountdown(player, target); + return true; + } + + // ── PlotSquared helpers ─────────────────────────────────────────────────── + + private List collectPlots(String worldName) { + try { + return PlotSquared.get().getPlotAreaManager().getPlotAreasSet(worldName) + .stream() + .map(PlotArea::getPlots) + .flatMap(Collection::stream) + .toList(); + } catch (Exception e) { + log.error("Failed to retrieve plots from PlotSquared for world '{}'", worldName, e); + } + return List.of(); + } + + private void teleportToPlot(Player player, Plot plot) { + try { + BukkitPlayer bPlayer = BukkitUtil.adapt(player); + plot.teleportPlayer(bPlayer, TeleportCause.PLUGIN, success -> { + if (Boolean.TRUE.equals(success)) { + player.sendRichMessage(Messages.RANDOM_PLOT.TELEPORT_SUCCESS, + Placeholder.parsed("plot_id", plot.getId().toSeparatedString(";")), + Placeholder.parsed("plot_owner", getOwner(plot))); + } + }); + } catch (Exception e) { + log.error("Failed to teleport {} to plot {}", player.getName(), plot.getId(), e); + } + } + + private String getOwner(Plot plot) { + String ownerName; + UUID owner = plot.getOwner(); + if (owner == null) { + ownerName = "Unknown"; + } else { + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(owner); + ownerName = offlinePlayer.getName(); + if (ownerName == null) { + ownerName = "Unknown"; + } + } + return ownerName; + } + + // ── Countdown ───────────────────────────────────────────────────────────── + + private void startCountdown(Player player, Plot plot) { + final Location origin = player.getLocation().clone(); + final int[] secondsLeft = {Config.RANDOM_PLOT.COUNTDOWN_SECONDS}; + + new BukkitRunnable() { + @Override + public void run() { + if (!player.isOnline()) { + cancel(); + return; + } + + // Movement check — cancel if player moved more than 0.5 blocks on any axis + Location current = player.getLocation(); + if (Math.abs(current.getX() - origin.getX()) > 0.5 + || Math.abs(current.getY() - origin.getY()) > 0.5 + || Math.abs(current.getZ() - origin.getZ()) > 0.5) { + cancel(); + player.sendRichMessage(Messages.RANDOM_PLOT.TELEPORT_CANCELLED); + return; + } + + if (secondsLeft[0] <= 0) { + cancel(); + teleportToPlot(player, plot); + return; + } + + // Show countdown title for current second + String rawTitle = Config.RANDOM_PLOT.COUNTDOWN_TITLES + .getOrDefault(secondsLeft[0], String.valueOf(secondsLeft[0])); + Component title = miniMessage.deserialize(rawTitle); + Component subtitle = miniMessage.deserialize(Config.RANDOM_PLOT.COUNTDOWN_SUBTITLE); + + player.showTitle(Title.title( + title, + subtitle, + Title.Times.times(Duration.ZERO, Duration.ofMillis(1200), Duration.ofMillis(200)) + )); + + secondsLeft[0]--; + } + }.runTaskTimer(plugin, 0L, 20L); + } + + @Override + public String getName() { + return "randomplot"; + } + + @Override + public List getTabComplete(CommandSender commandSender, String[] args) { + return List.of(); + } + + @Override + public String getHelpMessage() { + return Messages.HELP.RANDOM_PLOT; + } +} diff --git a/src/main/java/com/alttd/playerutils/config/Config.java b/src/main/java/com/alttd/playerutils/config/Config.java index 05db98f..6feba30 100644 --- a/src/main/java/com/alttd/playerutils/config/Config.java +++ b/src/main/java/com/alttd/playerutils/config/Config.java @@ -5,18 +5,20 @@ import org.bukkit.configuration.ConfigurationSection; import java.io.File; import java.util.HashMap; +import java.util.List; import java.util.Set; -@Slf4j public class Config extends AbstractConfig{ +@Slf4j +public class Config extends AbstractConfig { static Config config; Config() { super( new File(File.separator - + "mnt" + File.separator - + "configs" + File.separator - + "PlayerUtils"), + + "mnt" + File.separator + + "configs" + File.separator + + "PlayerUtils"), "config.yml"); } @@ -92,4 +94,46 @@ import java.util.Set; WAYPOINT_TRANSMIT_RANGE = config.getDouble(prefix, "waypoint_transmit_range", WAYPOINT_TRANSMIT_RANGE); } } + + public static class RANDOM_PLOT { + private static final String prefix = "random-plot."; + + // Command + public static List ALLOWED_WORLDS = List.of("plotworld"); + + // Permissions + public static int COUNTDOWN_SECONDS = 3; + public static HashMap COUNTDOWN_TITLES = new HashMap<>(); + public static String COUNTDOWN_SUBTITLE = "Preparing teleportation"; + + @SuppressWarnings("unused") + private static void load() { + ALLOWED_WORLDS = config.getStringList(prefix, "allowed-worlds", ALLOWED_WORLDS); + + COUNTDOWN_SECONDS = config.getInt(prefix + "countdown.", "seconds", COUNTDOWN_SECONDS); + COUNTDOWN_SUBTITLE = config.getString(prefix + "countdown.", "subtitle", COUNTDOWN_SUBTITLE); + + // Countdown titles — integer keys map to display strings + COUNTDOWN_TITLES.clear(); + ConfigurationSection titlesSection = + config.getConfigurationSection("random-plot.countdown.titles"); + if (titlesSection != null) { + for (String key : titlesSection.getKeys(false)) { + try { + COUNTDOWN_TITLES.put(Integer.parseInt(key), + titlesSection.getString(key, key)); + } catch (NumberFormatException ignored) { + log.warn("Invalid countdown title key: {}", key); + } + } + } else { + config.yaml.addDefault("random-plot.countdown.titles.3", "Go!"); + config.yaml.addDefault("random-plot.countdown.titles.2", "2"); + config.yaml.addDefault("random-plot.countdown.titles.1", "1"); + COUNTDOWN_TITLES.put(3, "2"); + COUNTDOWN_TITLES.put(2, "1"); + COUNTDOWN_TITLES.put(1, "Go!"); + } + } + } } diff --git a/src/main/java/com/alttd/playerutils/config/Messages.java b/src/main/java/com/alttd/playerutils/config/Messages.java index c4ae211..62ee8e1 100644 --- a/src/main/java/com/alttd/playerutils/config/Messages.java +++ b/src/main/java/com/alttd/playerutils/config/Messages.java @@ -1,7 +1,5 @@ package com.alttd.playerutils.config; -import org.jetbrains.annotations.NotNull; - import java.io.File; import java.util.List; @@ -35,6 +33,7 @@ public class Messages extends AbstractConfig { public static String KEY = "Receive a key that you are owed: /pu key"; public static String GHAST_SPEED = "Set the speed of a ghast: /pu ghastspeed "; public static String RECOUNT_ARMOR_STANDS = "Recount armor stands in current chunk: /pu recount"; + public static String RANDOM_PLOT = "Get a random plot: /pu randomplot"; @SuppressWarnings("unused") private static void load() { @@ -197,4 +196,25 @@ public class Messages extends AbstractConfig { SUCCESS = config.getString(prefix, "success", SUCCESS); } } + + public static class RANDOM_PLOT { + private static final String prefix = "random-plot."; + + public static String WORLD_NOT_ALLOWED = "You must be in an allowed world to use this command."; + public static String PLOT_SQUARED_NOT_ENABLED = "PlotSquared is not available on this server."; + public static String TELEPORT_START = "Starting teleport countdown..."; + public static String TELEPORT_SUCCESS = "You have arrived at by random plot!"; + public static String TELEPORT_CANCELLED = "Teleport cancelled! You moved."; + public static String NO_PLOTS_FOUND = "No plots found in this world."; + + @SuppressWarnings("unused") + private static void load() { + WORLD_NOT_ALLOWED = config.getString(prefix, "world-not-allowed", WORLD_NOT_ALLOWED); + PLOT_SQUARED_NOT_ENABLED = config.getString(prefix, "plotsquared-not-enabled", PLOT_SQUARED_NOT_ENABLED); + TELEPORT_START = config.getString(prefix, "teleport-start", TELEPORT_START); + TELEPORT_SUCCESS = config.getString(prefix, "teleport-success", TELEPORT_SUCCESS); + TELEPORT_CANCELLED = config.getString(prefix, "teleport-cancelled", TELEPORT_CANCELLED); + NO_PLOTS_FOUND = config.getString(prefix, "no-plots-found", NO_PLOTS_FOUND); + } + } }