From 290ad0ef1e994c8e480998eec05e5228f9abe56d Mon Sep 17 00:00:00 2001 From: NotAlexNoyle Date: Fri, 27 Jun 2025 15:31:41 -0400 Subject: [PATCH] Use luckperms for permissions --- build.gradle.kts | 25 ++-- src/main/java/spawnog/SpawnOG.java | 29 +++-- .../spawnog/commands/SetSpawnCommand.java | 118 ++++++++---------- .../java/spawnog/commands/SpawnCommand.java | 74 ++++++----- .../java/spawnog/listener/SpawnListener.java | 65 ++++++---- 5 files changed, 169 insertions(+), 142 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 18892cc..763cb06 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,10 @@ plugins { - id("java") - id("java-library") - id("com.diffplug.spotless") version "7.0.4" - id("com.gradleup.shadow") version "8.3.6" - eclipse + id("java") // Tell gradle this is a java project. + id("java-library") // Import helper for source-based libraries. + id("com.diffplug.spotless") version "7.0.4" // Import auto-formatter. + id("com.gradleup.shadow") version "8.3.6" // Import shadow API. + eclipse // Import eclipse plugin for IDE integration. + id("io.freefair.lombok") version "8.13.1" // Automatic lombok support. } java { sourceCompatibility = JavaVersion.VERSION_17 } @@ -25,22 +26,24 @@ repositories { mavenCentral() gradlePluginPortal() maven { url = uri("https://repo.purpurmc.org/snapshots") } + maven { + url = uri("https://repo.codemc.io/repository/maven-public/") // Import the CodeMC Maven Repository. + } } dependencies { compileOnly("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT") - compileOnly("org.projectlombok:lombok:1.18.26") - annotationProcessor("org.projectlombok:lombok:1.18.26") + compileOnly("net.luckperms:api:5.4") // Import LuckPerms API. } -tasks.withType().configureEach { +tasks.withType().configureEach { // Ensure reproducible .jars isPreserveFileTimestamps = false isReproducibleFileOrder = true } tasks.shadowJar { - exclude("io.github.miniplaceholders.*") - archiveClassifier.set("") + exclude("io.github.miniplaceholders.*") // Exclude the MiniPlaceholders package from being shadowed. + archiveClassifier.set("") // Use empty string instead of null. minimize() } @@ -53,7 +56,7 @@ tasks.jar { archiveClassifier.set("part") } tasks.withType().configureEach { options.compilerArgs.add("-parameters") - options.compilerArgs.add("-Xlint:deprecation") + options.compilerArgs.add("-Xlint:deprecation") // Triggers deprecation warning messages. options.encoding = "UTF-8" options.isFork = true } diff --git a/src/main/java/spawnog/SpawnOG.java b/src/main/java/spawnog/SpawnOG.java index 0598cd8..1e16260 100644 --- a/src/main/java/spawnog/SpawnOG.java +++ b/src/main/java/spawnog/SpawnOG.java @@ -2,8 +2,8 @@ package spawnog; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.PluginCommand; +import net.luckperms.api.LuckPerms; +import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; import spawnog.commands.SetSpawnCommand; import spawnog.commands.SpawnCommand; @@ -15,30 +15,33 @@ public final class SpawnOG extends JavaPlugin { @Getter private static SpawnOG instance; + @Getter + private LuckPerms luckPerms; + @Override public void onEnable() { instance = this; saveDefaultConfig(); - registerCommand("spawn", new SpawnCommand(), "spawnog.teleport"); - registerCommand("setspawn", new SetSpawnCommand(), "spawnog.admin"); + RegisteredServiceProvider rsp = + getServer().getServicesManager().getRegistration(LuckPerms.class); + if (rsp != null) luckPerms = rsp.getProvider(); + + register("spawn", new SpawnCommand(), "essentials.spawn"); + register("setspawn", new SetSpawnCommand(), "essentials.setspawn"); getServer().getPluginManager().registerEvents(new SpawnListener(), this); } - @Override - public void onDisable() { - log.info("Plugin disabled."); - } - - private void registerCommand(String name, CommandExecutor exec, String perm) { - PluginCommand cmd = getCommand(name); + private void register(String name, Object exec, String perm) { + var cmd = getCommand(name); if (cmd == null) { - getLogger().severe("Command '" + name + "' is missing from plugin.yml!"); + getLogger().severe("Command '" + name + "' missing from plugin.yml!"); getServer().getPluginManager().disablePlugin(this); return; } cmd.setPermission(perm); - cmd.setExecutor(exec); + if (exec instanceof org.bukkit.command.CommandExecutor e) cmd.setExecutor(e); + if (exec instanceof org.bukkit.command.TabCompleter t) cmd.setTabCompleter(t); } } diff --git a/src/main/java/spawnog/commands/SetSpawnCommand.java b/src/main/java/spawnog/commands/SetSpawnCommand.java index d5d9f69..35b79e1 100644 --- a/src/main/java/spawnog/commands/SetSpawnCommand.java +++ b/src/main/java/spawnog/commands/SetSpawnCommand.java @@ -1,102 +1,92 @@ package spawnog.commands; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; +import java.util.*; import javax.management.openmbean.InvalidOpenTypeException; import org.bukkit.Location; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabCompleter; +import org.bukkit.command.*; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import spawnog.SpawnOG; public class SetSpawnCommand implements CommandExecutor, TabCompleter { - private final JavaPlugin plugin = SpawnOG.getInstance(); - private final FileConfiguration config = this.plugin.getConfig(); - private static String locationPath = "general.location"; + private final FileConfiguration cfg = SpawnOG.getInstance().getConfig(); + private static final Set FLAGS = Set.of("normalize-view", "normalize-position", "unset"); @Override public boolean onCommand( - @NotNull CommandSender sender, - @NotNull Command command, - @NotNull String commandLabel, - @NotNull String[] args) { - // only allow commands from players - if (!(sender instanceof Player)) return false; - Player player = (Player) sender; - List argsList = Arrays.asList(args); + @NotNull CommandSender sender, @NotNull Command cmd, @NotNull String lbl, @NotNull String[] args) { - // permission check - if (!player.hasPermission(command.getPermission())) { - player.sendMessage( - this.config.getString("locale.missingPermission", "You are lacking the required permissions.")); + if (!(sender instanceof Player p)) return false; + if (!p.hasPermission(cmd.getPermission())) { + p.sendMessage(cfg.getString("locale.missingPermission", "You are lacking the required permissions.")); return true; } - // if unset - if (argsList.contains("unset")) { - this.config.set(locationPath, null); - this.plugin.saveConfig(); - player.sendMessage(this.config.getString("locale.spawnSet", "Spawn location updated.")); + String group = null; + var flags = new HashSet(); + + for (String a : args) { + if (FLAGS.contains(a.toLowerCase())) flags.add(a.toLowerCase()); + else if (group == null) group = a.toLowerCase(); + } + + String path = "spawns." + (group == null ? "global" : group) + ".location"; + + if (flags.contains("unset")) { + cfg.set(path, null); + SpawnOG.getInstance().saveConfig(); + p.sendMessage(cfg.getString("locale.spawnSet", "Spawn location updated.")); return true; } - // get location - Location location = player.getLocation(); + Location l = p.getLocation(); - // normalize view if necessary - if (argsList.contains("normalize-view")) { - location.setYaw((float) this.getClosestDegree(location.getYaw(), new double[] {-180, -90, 0, 90, 180})); - location.setPitch((float) this.getClosestDegree(location.getPitch(), new double[] {-180, -90, 0, 90, 180})); + if (flags.contains("normalize-view")) { + l.setYaw((float) closest(l.getYaw())); + l.setPitch((float) closest(l.getPitch())); + } + if (flags.contains("normalize-position")) { + l.setX(l.getX() > l.getBlockX() ? l.getBlockX() + .5 : l.getBlockX() - .5); + l.setZ(l.getZ() > l.getBlockZ() ? l.getBlockZ() + .5 : l.getBlockZ() - .5); + l.setY(l.getBlockY()); } - // normalize position if necessary - if (argsList.contains("normalize-position")) { - location.setX(location.getX() > location.blockX() ? location.blockX() + .5 : location.blockX() - .5); - location.setZ(location.getZ() > location.blockZ() ? location.blockZ() + .5 : location.blockZ() - .5); - location.setY(location.blockY()); // no normalizing needed - } - - // store location - this.config.set(locationPath, location); - this.plugin.saveConfig(); - player.sendMessage(this.config.getString("locale.spawnSet", "Spawn location updated.")); + cfg.set(path, l); + SpawnOG.getInstance().saveConfig(); + p.sendMessage(cfg.getString("locale.spawnSet", "Spawn location updated.")); return true; } @Override public @Nullable List onTabComplete( - @NotNull CommandSender sender, - @NotNull Command command, - @NotNull String commandLabel, - @NotNull String[] args) { - List completes = new ArrayList<>(); - List argsList = Arrays.asList(args); + @NotNull CommandSender sender, @NotNull Command cmd, @NotNull String lbl, @NotNull String[] args) { - if (argsList.contains("unset")) return completes; + var current = Arrays.asList(args); + var out = new ArrayList(); - completes.add("normalize-view"); - completes.add("normalize-position"); - completes.add("unset"); + if (args.length <= 1) { + out.addAll( + cfg.getConfigurationSection("spawns") == null + ? List.of() + : cfg.getConfigurationSection("spawns").getKeys(false)); + } + out.addAll(FLAGS); - return completes.stream().filter(c -> !argsList.contains(c)).toList(); + return out.stream() + .filter(s -> !current.contains(s)) + .filter(s -> s.startsWith(args[args.length - 1].toLowerCase())) + .toList(); } - private double getClosestDegree(double degree, double[] possibleDegrees) { - if (possibleDegrees.length == 0) - throw new InvalidOpenTypeException("You need to specify at least one possible degree"); - - return Arrays.stream(possibleDegrees) + private double closest(double d) { + double[] arr = {-180, -90, 0, 90, 180}; + if (arr.length == 0) throw new InvalidOpenTypeException(); + return Arrays.stream(arr) .boxed() - .min(Comparator.comparing(d -> Math.abs(d - degree))) - .orElse(possibleDegrees[0]); + .min(Comparator.comparing(a -> Math.abs(a - d))) + .orElse(arr[0]); } } diff --git a/src/main/java/spawnog/commands/SpawnCommand.java b/src/main/java/spawnog/commands/SpawnCommand.java index f6fee16..a6b5fe7 100644 --- a/src/main/java/spawnog/commands/SpawnCommand.java +++ b/src/main/java/spawnog/commands/SpawnCommand.java @@ -1,14 +1,12 @@ package spawnog.commands; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.model.user.User; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabCompleter; +import org.bukkit.command.*; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -16,42 +14,42 @@ import org.jetbrains.annotations.Nullable; import spawnog.SpawnOG; public class SpawnCommand implements CommandExecutor, TabCompleter { - private final FileConfiguration config = SpawnOG.getInstance().getConfig(); - private static final String LOCATION_PATH = "general.location"; + + private final SpawnOG plugin = SpawnOG.getInstance(); + private final FileConfiguration cfg = plugin.getConfig(); @Override public boolean onCommand( - @NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - Location base = config.getLocation(LOCATION_PATH); - if (base == null) { - sender.sendMessage(config.getString("locale.spawnNotSet", "Server spawn not set.")); - return true; - } - Location dest = base.clone(); - dest.setYaw(0f); - dest.setPitch(0f); + @NotNull CommandSender sender, @NotNull Command cmd, @NotNull String lbl, @NotNull String[] args) { if (args.length == 0) { - if (!(sender instanceof Player)) return false; - Player player = (Player) sender; - if (!player.hasPermission(command.getPermission())) { - player.sendMessage( - config.getString("locale.missingPermission", "You are lacking the required permissions.")); + if (!(sender instanceof Player p)) return false; + if (!p.hasPermission(cmd.getPermission())) { + p.sendMessage(cfg.getString("locale.missingPermission", "You are lacking the required permissions.")); return true; } - player.teleportAsync(dest); + Location dest = resolve(p); + if (dest == null) { + sender.sendMessage(cfg.getString("locale.spawnNotSet", "Server spawn not set.")); + return true; + } + p.teleportAsync(dest); return true; } - if (!sender.hasPermission("spawn.others")) { - sender.sendMessage( - config.getString("locale.missingPermission", "You are lacking the required permissions.")); + if (!sender.hasPermission("essentials.spawn.others")) { + sender.sendMessage(cfg.getString("locale.missingPermission", "You are lacking the required permissions.")); return true; } Player target = Bukkit.getPlayerExact(args[0]); if (target == null) { - sender.sendMessage(config.getString("locale.playerNotFound", "Player not found.")); + sender.sendMessage(cfg.getString("locale.playerNotFound", "Player not found.")); + return true; + } + Location dest = resolve(target); + if (dest == null) { + sender.sendMessage(cfg.getString("locale.spawnNotSet", "Server spawn not set.")); return true; } target.teleportAsync(dest); @@ -60,13 +58,29 @@ public class SpawnCommand implements CommandExecutor, TabCompleter { @Override public @Nullable List onTabComplete( - @NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (args.length == 1 && sender.hasPermission("spawn.others")) { + @NotNull CommandSender sender, @NotNull Command cmd, @NotNull String lbl, @NotNull String[] args) { + + if (args.length == 1 && sender.hasPermission("essentials.spawn.others")) { return Bukkit.getOnlinePlayers().stream() .map(Player::getName) .filter(n -> n.toLowerCase().startsWith(args[0].toLowerCase())) .collect(Collectors.toList()); } - return new ArrayList<>(); + return List.of(); + } + + private Location resolve(Player p) { + String group = null; + LuckPerms lp = plugin.getLuckPerms(); + if (lp != null) { + User u = lp.getUserManager().getUser(p.getUniqueId()); + if (u != null) group = u.getPrimaryGroup(); + } + if (group != null) { + Location g = cfg.getLocation("spawns." + group.toLowerCase() + ".location"); + if (g != null) return g.clone(); + } + Location def = cfg.getLocation("spawns.global.location"); + return def == null ? null : def.clone(); } } diff --git a/src/main/java/spawnog/listener/SpawnListener.java b/src/main/java/spawnog/listener/SpawnListener.java index 7a238ef..ff6949b 100644 --- a/src/main/java/spawnog/listener/SpawnListener.java +++ b/src/main/java/spawnog/listener/SpawnListener.java @@ -2,40 +2,57 @@ package spawnog.listener; import org.bukkit.Location; import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; +import org.bukkit.event.*; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerRespawnEvent; import spawnog.SpawnOG; public class SpawnListener implements Listener { - private Location spawn() { - return SpawnOG.getInstance().getConfig().getLocation("general.location"); + + private final SpawnOG plugin = SpawnOG.getInstance(); + + @EventHandler + public void onJoin(PlayerJoinEvent e) { + Player p = e.getPlayer(); + boolean first = !p.hasPlayedBefore(); + boolean force = plugin.getConfig().getBoolean("spawn-on-join", false); + + if (!first && !force) return; + + Location dest; + if (first) { + String nb = + plugin.getConfig().getString("newbies.spawnpoint", "newbie").toLowerCase(); + dest = plugin.getConfig().getLocation("spawns." + nb + ".location"); + if (dest == null) dest = plugin.getConfig().getLocation("spawns.global.location"); + } else { + dest = plugin.getConfig().getLocation("spawns.global.location"); + } + if (dest != null) p.teleportAsync(dest.clone()); } @EventHandler - public void onJoin(PlayerJoinEvent event) { - if (!event.getPlayer().hasPlayedBefore()) { - Location s = spawn(); - if (s != null) { - Location loc = s.clone(); - loc.setYaw(0f); - loc.setPitch(0f); - event.getPlayer().teleportAsync(loc); + public void onRespawn(PlayerRespawnEvent e) { + Player p = e.getPlayer(); + boolean atHome = plugin.getConfig().getBoolean("respawn-at-home", true); + + if (atHome && (p.getBedSpawnLocation() != null)) return; + + if (atHome && plugin.getServer().getPluginManager().isPluginEnabled("Essentials")) { + try { + Object ess = plugin.getServer().getPluginManager().getPlugin("Essentials"); + Object user = ess.getClass().getMethod("getUser", Player.class).invoke(ess, p); + Location home = (Location) + user.getClass().getMethod("getHome", String.class).invoke(user, "home"); + if (home != null) { + e.setRespawnLocation(home.clone()); + return; + } + } catch (Throwable ignored) { } } - } - @EventHandler - public void onRespawn(PlayerRespawnEvent event) { - Player p = event.getPlayer(); - if (p.getBedSpawnLocation() != null) return; - Location s = spawn(); - if (s != null) { - Location loc = s.clone(); - loc.setYaw(0f); - loc.setPitch(0f); - event.setRespawnLocation(loc); - } + Location def = plugin.getConfig().getLocation("spawns.global.location"); + if (def != null) e.setRespawnLocation(def.clone()); } }