From 63acbc99d5d12ce2be4c5556cd1bd042dfdaeb36 Mon Sep 17 00:00:00 2001 From: NotAlexNoyle Date: Wed, 13 May 2026 08:10:01 -0400 Subject: [PATCH] Add region whitelist flag --- .../arena/spleef/ArenaSpleef.java | 32 ++++ .../arena/spleef/SpleefConfig.java | 40 +++++ .../arena/spleef/SpleefExecutor.java | 30 ++++ .../arena/spleef/SpleefFlags.java | 61 ++++++++ .../arena/spleef/SpleefMessages.java | 9 ++ .../arena/spleef/WorldGuardSupport.java | 29 ++++ .../arena/spleef/arena/SpleefArena.java | 11 ++ .../hook/GameModeInventoriesListener.java | 139 ++++++++++++++++++ src/main/resources/config.yml | 4 + src/main/resources/plugin.yml | 2 +- 10 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/battleplugins/arena/spleef/SpleefFlags.java create mode 100644 src/main/java/org/battleplugins/arena/spleef/hook/GameModeInventoriesListener.java diff --git a/src/main/java/org/battleplugins/arena/spleef/ArenaSpleef.java b/src/main/java/org/battleplugins/arena/spleef/ArenaSpleef.java index 9c4225d..cbd6150 100644 --- a/src/main/java/org/battleplugins/arena/spleef/ArenaSpleef.java +++ b/src/main/java/org/battleplugins/arena/spleef/ArenaSpleef.java @@ -10,6 +10,7 @@ import org.battleplugins.arena.spleef.action.GiveShovelAction; import org.battleplugins.arena.spleef.action.PasteLayersAction; import org.battleplugins.arena.spleef.api.SpleefApiListener; import org.battleplugins.arena.spleef.arena.SpleefArena; +import org.battleplugins.arena.spleef.hook.GameModeInventoriesListener; import org.battleplugins.arena.spleef.hook.PlayerBountiesListener; import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; @@ -34,9 +35,26 @@ public class ArenaSpleef extends JavaPlugin { private static ArenaSpleef instance; private SpleefConfig config; + private GameModeInventoriesListener gameModeInventoriesListener; private final NamespacedKey spleefItemKey = new NamespacedKey(this, "spleef_item"); + @Override + public void onLoad() { + + if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) { + + SpleefFlags.register(this); + + } else { + + this.getSLF4JLogger().warn( + "WorldGuard not present at load time; the 'allow-spleef' region flag will not be registered."); + + } + + } + @Override public void onEnable() { @@ -72,6 +90,14 @@ public class ArenaSpleef extends JavaPlugin { this.getServer().getPluginManager().registerEvents(new SpleefApiListener(), this); + this.gameModeInventoriesListener = GameModeInventoriesListener.register(this); + if (this.gameModeInventoriesListener != null) { + + this.getSLF4JLogger() + .info("Hooked into GameModeInventories-OG; Spleef will preserve gamemode-specific inventories."); + + } + if (PlayerBountiesListener.register(this)) { this.getSLF4JLogger().info( @@ -84,6 +110,12 @@ public class ArenaSpleef extends JavaPlugin { @Override public void onDisable() { + if (this.gameModeInventoriesListener != null) { + + this.gameModeInventoriesListener.releaseAll(); + + } + Bukkit.getScheduler().cancelTasks(this); } diff --git a/src/main/java/org/battleplugins/arena/spleef/SpleefConfig.java b/src/main/java/org/battleplugins/arena/spleef/SpleefConfig.java index ce30701..202ea0f 100644 --- a/src/main/java/org/battleplugins/arena/spleef/SpleefConfig.java +++ b/src/main/java/org/battleplugins/arena/spleef/SpleefConfig.java @@ -4,6 +4,8 @@ import org.battleplugins.arena.config.ArenaOption; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; +import java.util.List; +import java.util.Locale; import java.util.Map; public class SpleefConfig { @@ -14,6 +16,9 @@ public class SpleefConfig { @ArenaOption(name = "shovels", description = "The shovels for this spleef game.", required = true) private Map shovels; + @ArenaOption(name = "world-whitelist", description = "Worlds where Spleef arenas may be created and run. Empty list = all worlds allowed.") + private List worldWhitelist; + public boolean shouldProjectilesBreakBlocks() { return this.projectilesBreakBlocks; @@ -34,4 +39,39 @@ public class SpleefConfig { } + public boolean isWorldAllowed(String worldName) { + + if (this.worldWhitelist == null || this.worldWhitelist.isEmpty()) { + + return true; + + } + + if (worldName == null) { + + return false; + + } + + String target = worldName.toLowerCase(Locale.ROOT); + for (String allowed : this.worldWhitelist) { + + if (allowed != null && allowed.toLowerCase(Locale.ROOT).equals(target)) { + + return true; + + } + + } + + return false; + + } + + public List getWorldWhitelist() { + + return this.worldWhitelist == null ? List.of() : List.copyOf(this.worldWhitelist); + + } + } diff --git a/src/main/java/org/battleplugins/arena/spleef/SpleefExecutor.java b/src/main/java/org/battleplugins/arena/spleef/SpleefExecutor.java index d2f91dc..79e7f3b 100644 --- a/src/main/java/org/battleplugins/arena/spleef/SpleefExecutor.java +++ b/src/main/java/org/battleplugins/arena/spleef/SpleefExecutor.java @@ -18,6 +18,36 @@ public class SpleefExecutor extends ArenaCommandExecutor { } + @Override + public void create(Player player) { + + SpleefConfig config = ArenaSpleef.getInstance().getMainConfig(); + String worldName = player.getWorld().getName(); + if (config != null && !config.isWorldAllowed(worldName)) { + + SpleefMessages.CREATE_WORLD_NOT_WHITELISTED.send(player, worldName); + return; + + } + + if (!WorldGuardSupport.isEnabled()) { + + SpleefMessages.CREATE_WG_MISSING.send(player); + return; + + } + + if (!WorldGuardSupport.isSpleefAllowedAt(player)) { + + SpleefMessages.CREATE_WG_FLAG_REQUIRED.send(player); + return; + + } + + super.create(player); + + } + @ArenaCommand(commands = "layer", subCommands = "add", description = "Adds a layer to a spleef arena.", permissionNode = "layer.add") public void layer(Player player, CompetitionMap map) { diff --git a/src/main/java/org/battleplugins/arena/spleef/SpleefFlags.java b/src/main/java/org/battleplugins/arena/spleef/SpleefFlags.java new file mode 100644 index 0000000..57c7986 --- /dev/null +++ b/src/main/java/org/battleplugins/arena/spleef/SpleefFlags.java @@ -0,0 +1,61 @@ +package org.battleplugins.arena.spleef; + +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.flags.registry.FlagConflictException; +import com.sk89q.worldguard.protection.flags.registry.FlagRegistry; +import org.bukkit.plugin.Plugin; + +public final class SpleefFlags { + + public static final String ALLOW_SPLEEF_FLAG_NAME = "allow-spleef"; + + private static StateFlag allowSpleefFlag; + + private SpleefFlags() { + + } + + public static void register(Plugin plugin) { + + FlagRegistry registry = WorldGuard.getInstance().getFlagRegistry(); + try { + + StateFlag flag = new StateFlag(ALLOW_SPLEEF_FLAG_NAME, false); + registry.register(flag); + allowSpleefFlag = flag; + + } catch (FlagConflictException conflict) { + + Flag existing = registry.get(ALLOW_SPLEEF_FLAG_NAME); + if (existing instanceof StateFlag stateFlag) { + + allowSpleefFlag = stateFlag; + plugin.getSLF4JLogger().info("Reusing existing WorldGuard flag '{}'.", ALLOW_SPLEEF_FLAG_NAME); + + } else { + + plugin.getSLF4JLogger().error( + "WorldGuard flag '{}' is already registered with an incompatible type; spleef will not enforce it.", + ALLOW_SPLEEF_FLAG_NAME); + + } + + } catch (IllegalStateException late) { + + plugin.getSLF4JLogger().error( + "Failed to register WorldGuard flag '{}' — registry already locked. Ensure Spleef-OG loads before WorldGuard enables.", + ALLOW_SPLEEF_FLAG_NAME, late); + + } + + } + + public static StateFlag getAllowSpleefFlag() { + + return allowSpleefFlag; + + } + +} diff --git a/src/main/java/org/battleplugins/arena/spleef/SpleefMessages.java b/src/main/java/org/battleplugins/arena/spleef/SpleefMessages.java index ad19f79..b649627 100644 --- a/src/main/java/org/battleplugins/arena/spleef/SpleefMessages.java +++ b/src/main/java/org/battleplugins/arena/spleef/SpleefMessages.java @@ -34,4 +34,13 @@ public final class SpleefMessages { public static final Message DEATH_REGION_SET = Messages.success("spleef-death-region-set", "Death region set successfully!"); + public static final Message CREATE_WORLD_NOT_WHITELISTED = Messages.error("spleef-create-world-not-whitelisted", + "Spleef arenas cannot be created in this world. World {} is not in the Spleef world whitelist."); + public static final Message CREATE_WG_FLAG_REQUIRED = Messages.error("spleef-create-wg-flag-required", + "You must stand inside a WorldGuard region that has the allow-spleef flag set to allow to create a Spleef arena here."); + public static final Message CREATE_WG_MISSING = Messages.error("spleef-create-wg-missing", + "WorldGuard is not loaded; Spleef arena creation is disabled."); + public static final Message WORLD_BLOCKED_AT_RUNTIME = Messages.error("spleef-world-blocked", + "Spleef matches are not permitted in world {}."); + } diff --git a/src/main/java/org/battleplugins/arena/spleef/WorldGuardSupport.java b/src/main/java/org/battleplugins/arena/spleef/WorldGuardSupport.java index cf34a59..128eac1 100644 --- a/src/main/java/org/battleplugins/arena/spleef/WorldGuardSupport.java +++ b/src/main/java/org/battleplugins/arena/spleef/WorldGuardSupport.java @@ -2,11 +2,15 @@ package org.battleplugins.arena.spleef; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.RegionManager; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.entity.Player; public final class WorldGuardSupport { @@ -36,4 +40,29 @@ public final class WorldGuardSupport { } + public static boolean isSpleefAllowedAt(Player player) { + + StateFlag flag = SpleefFlags.getAllowSpleefFlag(); + if (flag == null) { + + return false; + + } + + LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(player); + Location location = player.getLocation(); + RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer() + .get(BukkitAdapter.adapt(location.getWorld())); + if (regionManager == null) { + + return false; + + } + + BlockVector3 position = BlockVector3.at(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + ApplicableRegionSet regions = regionManager.getApplicableRegions(position); + return regions.testState(localPlayer, flag); + + } + } diff --git a/src/main/java/org/battleplugins/arena/spleef/arena/SpleefArena.java b/src/main/java/org/battleplugins/arena/spleef/arena/SpleefArena.java index be57711..4c4092f 100644 --- a/src/main/java/org/battleplugins/arena/spleef/arena/SpleefArena.java +++ b/src/main/java/org/battleplugins/arena/spleef/arena/SpleefArena.java @@ -84,6 +84,17 @@ public class SpleefArena extends Arena { if (event.getCompetition() instanceof SpleefCompetition spleefCompetition) { + org.bukkit.World mapWorld = spleefCompetition.getMap().getWorld(); + String worldName = mapWorld == null ? null : mapWorld.getName(); + if (!ArenaSpleef.getInstance().getMainConfig().isWorldAllowed(worldName)) { + + ArenaSpleef.getInstance().getSLF4JLogger().warn( + "Refusing to start Spleef competition '{}' — world '{}' is not in the Spleef world whitelist.", + spleefCompetition.getMap().getName(), worldName); + return; + + } + spleefCompetition.beginLayerDecay(); } diff --git a/src/main/java/org/battleplugins/arena/spleef/hook/GameModeInventoriesListener.java b/src/main/java/org/battleplugins/arena/spleef/hook/GameModeInventoriesListener.java new file mode 100644 index 0000000..9f7d717 --- /dev/null +++ b/src/main/java/org/battleplugins/arena/spleef/hook/GameModeInventoriesListener.java @@ -0,0 +1,139 @@ +package org.battleplugins.arena.spleef.hook; + +import org.battleplugins.arena.event.player.ArenaJoinEvent; +import org.battleplugins.arena.event.player.ArenaLeaveEvent; +import org.battleplugins.arena.event.player.ArenaSpectateEvent; +import org.battleplugins.arena.spleef.api.SpleefAPI; +import org.battleplugins.arena.spleef.arena.SpleefCompetition; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.plugin.Plugin; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public final class GameModeInventoriesListener implements Listener { + + public static final String PLUGIN_NAME = "GameModeInventories-OG"; + + private static final String USE_PERMISSION = "gamemodeinventories.use"; + + private final Plugin owner; + private final Map attachments = new HashMap<>(); + + private GameModeInventoriesListener(Plugin owner) { + + this.owner = owner; + + } + + public static GameModeInventoriesListener register(Plugin owner) { + + final Plugin gameModeInventories = Bukkit.getPluginManager().getPlugin(PLUGIN_NAME); + if (gameModeInventories == null || !gameModeInventories.isEnabled()) { + + return null; + + } + + final GameModeInventoriesListener listener = new GameModeInventoriesListener(owner); + Bukkit.getPluginManager().registerEvents(listener, owner); + return listener; + + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onArenaJoin(ArenaJoinEvent event) { + + if (event.getArenaPlayer().getCompetition() instanceof SpleefCompetition) { + + this.suspend(event.getPlayer()); + + } + + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onArenaSpectate(ArenaSpectateEvent event) { + + if (event.getArenaPlayer().getCompetition() instanceof SpleefCompetition) { + + this.suspend(event.getPlayer()); + + } + + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onArenaLeave(ArenaLeaveEvent event) { + + if (!(event.getArenaPlayer().getCompetition() instanceof SpleefCompetition)) { + + return; + + } + + final Player player = event.getPlayer(); + Bukkit.getScheduler().runTask(this.owner, () -> { + + if (!SpleefAPI.isInSpleef(player)) { + + this.release(player); + + } + + }); + + } + + public void releaseAll() { + + for (PermissionAttachment attachment : this.attachments.values()) { + + try { + + attachment.remove(); + + } catch (IllegalArgumentException ignored) { + + } + + } + + this.attachments.clear(); + + } + + private void suspend(Player player) { + + // Keep GMI from saving arena-cleared contents over real inventories. + this.attachments.computeIfAbsent(player.getUniqueId(), + key -> player.addAttachment(this.owner, USE_PERMISSION, false)); + + } + + private void release(Player player) { + + final PermissionAttachment attachment = this.attachments.remove(player.getUniqueId()); + if (attachment == null) { + + return; + + } + + try { + + attachment.remove(); + + } catch (IllegalArgumentException ignored) { + + } + + } + +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 34185cf..450a6b5 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -10,6 +10,10 @@ config-version: 2.0 # The config version, do not change! # Whether projectiles (i.e. snowballs) should break blocks or not. projectiles-break-blocks: false +# Worlds where Spleef arenas may be created and where matches may run. +# Leave empty to allow every world. Names are matched case-insensitively. +world-whitelist: [] + # The shovels to use for Spleef. shovels: classic: diamond_shovel{display-name=Spleef Shovel} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index f22abcf..766b167 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -6,4 +6,4 @@ author: BattlePlugins website: https://battleplugins.org api-version: ${apiVersion} depend: [BattleArena, WorldGuard] -softdepend: [HorseTp-OG, PetTeleport-OG, PlayerBounties-OG, WorldEdit] +softdepend: [GameModeInventories-OG, HorseTp-OG, PetTeleport-OG, PlayerBounties-OG, WorldEdit]