From c086a21636d63522d14ba7b5b05e8f18e6ca0765 Mon Sep 17 00:00:00 2001 From: BeDePaY Date: Tue, 3 Feb 2026 11:42:06 +0300 Subject: [PATCH 1/2] Refactor BlockHandler to use ConcurrentHashMap for cooldown management and add clearCooldown method. Update ListenerClass to clear cooldowns on player quit and remove from toggle list. Improve code readability with consistent formatting and comments. --- .vscode/settings.json | 3 + .../espi/protectionstones/BlockHandler.java | 99 ++++++--- .../espi/protectionstones/FlagHandler.java | 54 +++-- .../espi/protectionstones/ListenerClass.java | 167 ++++++++++----- .../dev/espi/protectionstones/PSPlayer.java | 109 ++++++---- .../protectionstones/ProtectionStones.java | 192 ++++++++++++------ .../espi/protectionstones/utils/WGUtils.java | 144 +++++++++---- 7 files changed, 522 insertions(+), 246 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..7b016a89 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.compile.nullAnalysis.mode": "automatic" +} \ No newline at end of file diff --git a/src/main/java/dev/espi/protectionstones/BlockHandler.java b/src/main/java/dev/espi/protectionstones/BlockHandler.java index 891a7650..a10fac20 100644 --- a/src/main/java/dev/espi/protectionstones/BlockHandler.java +++ b/src/main/java/dev/espi/protectionstones/BlockHandler.java @@ -41,28 +41,45 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class BlockHandler { - private static HashMap lastProtectStonePlaced = new HashMap<>(); + // Use ConcurrentHashMap with UUID key to prevent memory leaks (Player objects + // are not garbage collected) + private static final ConcurrentHashMap lastProtectStonePlaced = new ConcurrentHashMap<>(); private static String checkCooldown(Player p) { - double currentTime = System.currentTimeMillis(); - if (lastProtectStonePlaced.containsKey(p)) { + long currentTime = System.currentTimeMillis(); + UUID playerId = p.getUniqueId(); + Long lastPlace = lastProtectStonePlaced.get(playerId); + + if (lastPlace != null) { double cooldown = ProtectionStones.getInstance().getConfigOptions().placingCooldown; // seconds - double lastPlace = lastProtectStonePlaced.get(p); // milliseconds if (lastPlace + cooldown * 1000 > currentTime) { // if cooldown has not been finished - return String.format("%.1f", cooldown - ((currentTime - lastPlace) / 1000)); + return String.format("%.1f", cooldown - ((currentTime - lastPlace) / 1000.0)); } - lastProtectStonePlaced.remove(p); } - lastProtectStonePlaced.put(p, currentTime); + lastProtectStonePlaced.put(playerId, currentTime); return null; } - private static boolean isFarEnoughFromOtherClaims(PSProtectBlock blockOptions, World w, LocalPlayer lp, double bx, double by, double bz) { - BlockVector3 min = WGUtils.getMinVector(bx, by, bz, blockOptions.distanceBetweenClaims, blockOptions.distanceBetweenClaims, blockOptions.distanceBetweenClaims); - BlockVector3 max = WGUtils.getMaxVector(bx, by, bz, blockOptions.distanceBetweenClaims, blockOptions.distanceBetweenClaims, blockOptions.distanceBetweenClaims); + /** + * Clears the cooldown cache for a player. Should be called when player quits. + * + * @param playerId the UUID of the player + */ + public static void clearCooldown(UUID playerId) { + lastProtectStonePlaced.remove(playerId); + } + + private static boolean isFarEnoughFromOtherClaims(PSProtectBlock blockOptions, World w, LocalPlayer lp, double bx, + double by, double bz) { + BlockVector3 min = WGUtils.getMinVector(bx, by, bz, blockOptions.distanceBetweenClaims, + blockOptions.distanceBetweenClaims, blockOptions.distanceBetweenClaims); + BlockVector3 max = WGUtils.getMaxVector(bx, by, bz, blockOptions.distanceBetweenClaims, + blockOptions.distanceBetweenClaims, blockOptions.distanceBetweenClaims); ProtectedRegion td = new ProtectedCuboidRegion("regionRadiusTest" + (long) (bx + by + bz), true, min, max); td.setPriority(blockOptions.priority); @@ -72,13 +89,16 @@ private static boolean isFarEnoughFromOtherClaims(PSProtectBlock blockOptions, W if (rgm.overlapsUnownedRegion(td, lp)) { for (ProtectedRegion rg : rgm.getApplicableRegions(td)) { // skip if the user is already an owner - if (rg.isOwner(lp)) continue; + if (rg.isOwner(lp)) + continue; if (ProtectionStones.isPSRegion(rg) && rg.getFlag(Flags.PASSTHROUGH) != StateFlag.State.ALLOW) { - // if it is a PS region, and "passthrough allow" is not set, then it is not far enough + // if it is a PS region, and "passthrough allow" is not set, then it is not far + // enough return false; } else if (rg.getPriority() >= td.getPriority()) { - // if the priorities are the same for plain WorldGuard regions, it is not far enough + // if the priorities are the same for plain WorldGuard regions, it is not far + // enough return false; } } @@ -93,15 +113,18 @@ public static void createPSRegion(BlockPlaceEvent e) { Block b = e.getBlock(); // check if the block is a protection stone - if (!ProtectionStones.isProtectBlockType(b)) return; + if (!ProtectionStones.isProtectBlockType(b)) + return; PSProtectBlock blockOptions = ProtectionStones.getBlockOptions(b); // check if the item was created by protection stones (stored in custom tag) // block must have restrictObtaining enabled for blocking place - if (blockOptions.restrictObtaining && !ProtectionStones.isProtectBlockItem(e.getItemInHand(), true)) return; + if (blockOptions.restrictObtaining && !ProtectionStones.isProtectBlockItem(e.getItemInHand(), true)) + return; // check if player has toggled off placement of protection stones - if (ProtectionStones.toggleList.contains(p.getUniqueId())) return; + if (ProtectionStones.toggleList.contains(p.getUniqueId())) + return; // check if player can place block in that area if (!WorldGuardPlugin.inst().createProtectionQuery().testBlockPlace(p, b.getLocation(), b.getType())) { @@ -112,7 +135,10 @@ public static void createPSRegion(BlockPlaceEvent e) { // check if it is in a WorldGuard region RegionManager rgm = WGUtils.getRegionManagerWithPlayer(p); - if (!blockOptions.allowPlacingInWild && rgm.getApplicableRegions(BlockVector3.at(b.getLocation().getX(), b.getLocation().getY(), b.getLocation().getZ())).size() == 0) { + if (!blockOptions.allowPlacingInWild && rgm + .getApplicableRegions( + BlockVector3.at(b.getLocation().getX(), b.getLocation().getY(), b.getLocation().getZ())) + .size() == 0) { PSL.msg(p, PSL.MUST_BE_PLACED_IN_EXISTING_REGION.msg()); e.setCancelled(true); return; @@ -155,7 +181,8 @@ public static boolean createPSRegion(Player p, Location l, PSProtectBlock blockO // check if in world blacklist or not in world whitelist boolean containsWorld = blockOptions.worlds.contains(p.getLocation().getWorld().getName()); - if ((containsWorld && blockOptions.worldListType.equalsIgnoreCase("blacklist")) || (!containsWorld && blockOptions.worldListType.equalsIgnoreCase("whitelist"))) { + if ((containsWorld && blockOptions.worldListType.equalsIgnoreCase("blacklist")) + || (!containsWorld && blockOptions.worldListType.equalsIgnoreCase("whitelist"))) { if (blockOptions.preventBlockPlaceInRestrictedWorld) { PSL.msg(p, PSL.WORLD_DENIED_CREATE.msg()); return false; @@ -167,21 +194,24 @@ public static boolean createPSRegion(Player p, Location l, PSProtectBlock blockO } // end of non-admin checks // check if player has enough money - if (ProtectionStones.getInstance().isVaultSupportEnabled() && blockOptions.costToPlace != 0 && !ProtectionStones.getInstance().getVaultEconomy().has(p, blockOptions.costToPlace)) { + if (ProtectionStones.getInstance().isVaultSupportEnabled() && blockOptions.costToPlace != 0 + && !ProtectionStones.getInstance().getVaultEconomy().has(p, blockOptions.costToPlace)) { PSL.msg(p, PSL.NOT_ENOUGH_MONEY.msg().replace("%price%", String.format("%.2f", blockOptions.costToPlace))); return false; } // debug message if (!ProtectionStones.getInstance().isVaultSupportEnabled() && blockOptions.costToPlace != 0) { - ProtectionStones.getPluginLogger().info("Vault is not enabled but there is a price set on the protection stone placement! It will not work!"); + ProtectionStones.getPluginLogger().info( + "Vault is not enabled but there is a price set on the protection stone placement! It will not work!"); } if (createActualRegion(p, l, blockOptions)) { // region creation successful // take money if (ProtectionStones.getInstance().isVaultSupportEnabled() && blockOptions.costToPlace != 0) { - EconomyResponse er = ProtectionStones.getInstance().getVaultEconomy().withdrawPlayer(p, blockOptions.costToPlace); + EconomyResponse er = ProtectionStones.getInstance().getVaultEconomy().withdrawPlayer(p, + blockOptions.costToPlace); if (!er.transactionSuccess()) { PSL.msg(p, er.errorMessage); return true; @@ -205,7 +235,8 @@ public static boolean createActualRegion(Player p, Location l, PSProtectBlock bl String id = WGUtils.createPSID(bx, by, bz); - // if the region's id already exists, possibly placing block where a region is hidden + // if the region's id already exists, possibly placing block where a region is + // hidden if (rm.hasRegion(id)) { PSL.msg(p, PSL.REGION_ALREADY_IN_LOCATION_IS_HIDDEN.msg()); return false; @@ -226,7 +257,8 @@ public static boolean createActualRegion(Player p, Location l, PSProtectBlock bl rm.addRegion(region); // added to the region manager, be careful in implementing checks // check if new region overlaps more powerful region - if (!blockOptions.allowOverlapUnownedRegions && !p.hasPermission("protectionstones.superowner") && WGUtils.overlapsStrongerRegion(p.getWorld(), region, lp)) { + if (!blockOptions.allowOverlapUnownedRegions && !p.hasPermission("protectionstones.superowner") + && WGUtils.overlapsStrongerRegion(p.getWorld(), region, lp)) { rm.removeRegion(id); PSL.msg(p, PSL.REGION_OVERLAP.msg()); return false; @@ -242,14 +274,16 @@ public static boolean createActualRegion(Player p, Location l, PSProtectBlock bl try { region.setFlags(flags); } catch (Exception e) { - ProtectionStones.getPluginLogger().severe(String.format("Region flags have failed to initialize for: %s (%s)", blockOptions.alias, blockOptions.type)); + ProtectionStones.getPluginLogger().severe(String.format( + "Region flags have failed to initialize for: %s (%s)", blockOptions.alias, blockOptions.type)); throw e; } FlagHandler.initCustomFlagsForPS(region, l, blockOptions); // check for player's number of adjacent region groups if (ProtectionStones.getInstance().getConfigOptions().regionsMustBeAdjacent) { - if (MiscUtil.getPermissionNumber(p, "protectionstones.adjacent.", 1) >= 0 && !p.hasPermission("protectionstones.admin")) { + if (MiscUtil.getPermissionNumber(p, "protectionstones.adjacent.", 1) >= 0 + && !p.hasPermission("protectionstones.admin")) { HashMap> adjGroups = WGUtils.getPlayerAdjacentRegionGroups(p, rm); int permNum = MiscUtil.getPermissionNumber(p, "protectionstones.adjacent.", 1); @@ -284,9 +318,11 @@ public static boolean createActualRegion(Player p, Location l, PSProtectBlock bl } // show merge menu - if (ProtectionStones.getInstance().getConfigOptions().allowMergingRegions && blockOptions.allowMerging && p.hasPermission("protectionstones.merge")) { + if (ProtectionStones.getInstance().getConfigOptions().allowMergingRegions && blockOptions.allowMerging + && p.hasPermission("protectionstones.merge")) { PSRegion r = PSRegion.fromWGRegion(p.getWorld(), region); - if (r != null) playerMergeTask(p, r); + if (r != null) + playerMergeTask(p, r); } return true; @@ -314,10 +350,12 @@ private static void playerMergeTask(Player p, PSRegion r) { PSRegion finalMergeTo = mergeTo; Bukkit.getScheduler().runTaskAsynchronously(ProtectionStones.getInstance(), () -> { try { - WGMerge.mergeRealRegions(p.getWorld(), r.getWGRegionManager(), finalMergeTo, Arrays.asList(finalMergeTo, r)); + WGMerge.mergeRealRegions(p.getWorld(), r.getWGRegionManager(), finalMergeTo, + Arrays.asList(finalMergeTo, r)); PSL.msg(p, PSL.MERGE_AUTO_MERGED.msg().replace("%region%", finalMergeTo.getId())); } catch (WGMerge.RegionHoleException e) { - PSL.msg(p, PSL.NO_REGION_HOLES.msg()); // TODO github issue #120, prevent holes even if showGUI is true + PSL.msg(p, PSL.NO_REGION_HOLES.msg()); // TODO github issue #120, prevent holes even if showGUI + // is true } catch (WGMerge.RegionCannotMergeWhileRentedException e) { // don't need to tell player that you can't merge } @@ -332,7 +370,8 @@ private static void playerMergeTask(Player p, PSRegion r) { p.sendMessage(ChatColor.WHITE + ""); // send empty line PSL.msg(p, PSL.MERGE_INTO.msg()); PSL.msg(p, PSL.MERGE_HEADER.msg().replace("%region%", r.getId())); - for (TextComponent t : tc) p.spigot().sendMessage(t); + for (TextComponent t : tc) + p.spigot().sendMessage(t); p.sendMessage(ChatColor.WHITE + ""); // send empty line } } diff --git a/src/main/java/dev/espi/protectionstones/FlagHandler.java b/src/main/java/dev/espi/protectionstones/FlagHandler.java index 28e46b20..eb4669e6 100644 --- a/src/main/java/dev/espi/protectionstones/FlagHandler.java +++ b/src/main/java/dev/espi/protectionstones/FlagHandler.java @@ -44,12 +44,15 @@ public class FlagHandler { // Custom WorldGuard Flags used by ProtectionStones // Added to blocks on BlockPlaceEvent Listener - // When adding flags, you may want to add them to the hidden_flags_from_info config option list + // When adding flags, you may want to add them to the hidden_flags_from_info + // config option list public static final Flag PS_HOME = new StringFlag("ps-home"); public static final Flag PS_BLOCK_MATERIAL = new StringFlag("ps-block-material"); public static final Flag PS_NAME = new StringFlag("ps-name"); - public static final Flag> PS_MERGED_REGIONS = new SetFlag<>("ps-merged-regions", new StringFlag("ps-merged-region")); - public static final Flag> PS_MERGED_REGIONS_TYPES = new SetFlag<>("ps-merged-regions-types", new StringFlag("ps-merged-region-type")); // each entry: "[psID] [type]" + public static final Flag> PS_MERGED_REGIONS = new SetFlag<>("ps-merged-regions", + new StringFlag("ps-merged-region")); + public static final Flag> PS_MERGED_REGIONS_TYPES = new SetFlag<>("ps-merged-regions-types", + new StringFlag("ps-merged-region-type")); // each entry: "[psID] [type]" public static final Flag PS_LANDLORD = new StringFlag("ps-landlord"); public static final Flag PS_TENANT = new StringFlag("ps-tenant"); @@ -57,9 +60,12 @@ public class FlagHandler { public static final Flag PS_PRICE = new DoubleFlag("ps-price"); public static final Flag PS_RENT_LAST_PAID = new DoubleFlag("ps-rent-last-paid"); public static final Flag PS_FOR_SALE = new BooleanFlag("ps-for-sale"); - public static final Flag> PS_RENT_SETTINGS = new SetFlag<>("ps-rent-settings", new StringFlag("ps-rent-setting")); // TODO - public static final Flag> PS_TAX_PAYMENTS_DUE = new SetFlag<>("ps-tax-payments-due", new StringFlag("ps-tax-payment")); - public static final Flag> PS_TAX_LAST_PAYMENT_ADDED = new SetFlag<>("ps-tax-last-payment-added", new StringFlag("ps-tax-last-payment-entry")); + public static final Flag> PS_RENT_SETTINGS = new SetFlag<>("ps-rent-settings", + new StringFlag("ps-rent-setting")); // TODO + public static final Flag> PS_TAX_PAYMENTS_DUE = new SetFlag<>("ps-tax-payments-due", + new StringFlag("ps-tax-payment")); + public static final Flag> PS_TAX_LAST_PAYMENT_ADDED = new SetFlag<>("ps-tax-last-payment-added", + new StringFlag("ps-tax-last-payment-entry")); public static final Flag PS_TAX_AUTOPAYER = new StringFlag("ps-tax-autopayer"); // called on initial start @@ -83,7 +89,8 @@ static void registerFlags() { registry.register(PS_TAX_LAST_PAYMENT_ADDED); registry.register(PS_TAX_AUTOPAYER); } catch (FlagConflictException e) { - Bukkit.getLogger().severe("Flag conflict found! The plugin will not work properly! Please contact the developers of the plugin."); + Bukkit.getLogger().severe( + "Flag conflict found! The plugin will not work properly! Please contact the developers of the plugin."); e.printStackTrace(); } @@ -123,12 +130,14 @@ static void initCustomFlagsForPS(ProtectedRegion region, Location l, PSProtectBl } public static List getPlayerPlaceholderFlags() { - return Arrays.asList("greeting", "greeting-title", "greeting-action", "farewell", "farewell-title", "farewell-action"); + return Arrays.asList("greeting", "greeting-title", "greeting-action", "farewell", "farewell-title", + "farewell-action"); } // Edit flags that require placeholders (variables) public static void initDefaultFlagPlaceholders(HashMap, Object> flags, Player p) { - for (Flag f : getPlayerPlaceholderFlags().stream().map(WGUtils.getFlagRegistry()::get).collect(Collectors.toList())) { + for (Flag f : getPlayerPlaceholderFlags().stream().map(WGUtils.getFlagRegistry()::get) + .collect(Collectors.toList())) { if (flags.get(f) != null) { String s = (String) flags.get(f); @@ -153,7 +162,8 @@ static void initDefaultFlagsForBlock(PSProtectBlock b) { String[] splGroups = spl[1].split(","); List groups = new ArrayList<>(); for (String g : splGroups) { - if (FLAG_GROUPS.contains(g)) groups.add(g); + if (FLAG_GROUPS.contains(g)) + groups.add(g); } b.allowedFlags.put(spl[2], groups); @@ -161,7 +171,8 @@ static void initDefaultFlagsForBlock(PSProtectBlock b) { b.allowedFlags.put(f, FLAG_GROUPS); } } catch (Exception e) { - ProtectionStones.getInstance().getLogger().warning("Skipping flag " + f + ". Did you configure the allowed_flags section correctly?"); + ProtectionStones.getInstance().getLogger() + .warning("Skipping flag " + f + ". Did you configure the allowed_flags section correctly?"); e.printStackTrace(); } } @@ -172,8 +183,7 @@ static void initDefaultFlagsForBlock(PSProtectBlock b) { // loop through default flags for (String flagraw : b.flags) { String[] split = flagraw.split(" "); - String settings = "", // flag settings (after flag name) - group = "", // -g group + String group = "", // -g group flagName = split[0]; boolean isEmpty = false; // whether or not it's the -e flag at beginning @@ -189,9 +199,14 @@ static void initDefaultFlagsForBlock(PSProtectBlock b) { startInd = 2; // ex. -e deny-message } - // get settings (after flag name) - for (int i = startInd; i < split.length; i++) settings += split[i] + " "; - settings = settings.trim(); + // get settings (after flag name) using StringBuilder for efficiency + StringBuilder settingsBuilder = new StringBuilder(); + for (int i = startInd; i < split.length; i++) { + if (settingsBuilder.length() > 0) + settingsBuilder.append(" "); + settingsBuilder.append(split[i]); + } + String settings = settingsBuilder.toString(); // if the setting is set to -e, change to empty flag if (settings.equals("-e")) { @@ -204,7 +219,9 @@ static void initDefaultFlagsForBlock(PSProtectBlock b) { // warn if flag setting has already been set if (b.regionFlags.containsKey(flag)) { - ProtectionStones.getPluginLogger().warning(String.format("Duplicate default flags found (only one flag setting can be applied for each flag)! Overwriting the previous value set for %s with \"%s\" ...", flagName, flagraw)); + ProtectionStones.getPluginLogger().warning(String.format( + "Duplicate default flags found (only one flag setting can be applied for each flag)! Overwriting the previous value set for %s with \"%s\" ...", + flagName, flagraw)); } // apply flag @@ -214,7 +231,8 @@ static void initDefaultFlagsForBlock(PSProtectBlock b) { RegionGroup rGroup = flag.getRegionGroupFlag().detectValue(group); if (rGroup == null) { - ProtectionStones.getPluginLogger().severe(String.format("Error parsing flag \"%s\", the group value is invalid!", flagraw)); + ProtectionStones.getPluginLogger().severe( + String.format("Error parsing flag \"%s\", the group value is invalid!", flagraw)); continue; } diff --git a/src/main/java/dev/espi/protectionstones/ListenerClass.java b/src/main/java/dev/espi/protectionstones/ListenerClass.java index 87175a00..49d63788 100644 --- a/src/main/java/dev/espi/protectionstones/ListenerClass.java +++ b/src/main/java/dev/espi/protectionstones/ListenerClass.java @@ -48,6 +48,7 @@ import org.bukkit.event.player.PlayerBucketEmptyEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.inventory.Inventory; @@ -67,7 +68,8 @@ public void onPlayerJoin(PlayerJoinEvent e) { UUIDCache.storeUUIDNamePair(p.getUniqueId(), p.getName()); // allow worldguard to resolve all UUIDs to names - Bukkit.getScheduler().runTaskAsynchronously(ProtectionStones.getInstance(), () -> UUIDCache.storeWGProfile(p.getUniqueId(), p.getName())); + Bukkit.getScheduler().runTaskAsynchronously(ProtectionStones.getInstance(), + () -> UUIDCache.storeWGProfile(p.getUniqueId(), p.getName())); // add recipes to player's recipe book p.discoverRecipes(RecipeUtil.getRecipeKeys()); @@ -80,7 +82,8 @@ public void onPlayerJoin(PlayerJoinEvent e) { } // tax join message - if (ProtectionStones.getInstance().getConfigOptions().taxEnabled && ProtectionStones.getInstance().getConfigOptions().taxMessageOnJoin) { + if (ProtectionStones.getInstance().getConfigOptions().taxEnabled + && ProtectionStones.getInstance().getConfigOptions().taxMessageOnJoin) { Bukkit.getScheduler().runTaskAsynchronously(ProtectionStones.getInstance(), () -> { int amount = 0; for (PSRegion psr : psp.getTaxEligibleRegions()) { @@ -96,7 +99,19 @@ public void onPlayerJoin(PlayerJoinEvent e) { } } - // specifically add WG passthrough bypass here, so other plugins can see the result + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerQuit(PlayerQuitEvent e) { + Player p = e.getPlayer(); + + // clear cooldown cache to prevent memory leaks + BlockHandler.clearCooldown(p.getUniqueId()); + + // remove from toggle list to prevent memory buildup over time + ProtectionStones.toggleList.remove(p.getUniqueId()); + } + + // specifically add WG passthrough bypass here, so other plugins can see the + // result @EventHandler(priority = EventPriority.LOWEST) public void onBlockPlaceLowPriority(PlaceBlockEvent event) { var cause = event.getCause().getRootCause(); @@ -111,17 +126,21 @@ public void onBlockPlaceLowPriority(PlaceBlockEvent event) { if (options != null && options.placingBypassesWGPassthrough) { // check if any regions here have the passthrough flag - // we can't query unfortunately, since null flags seem to equate to ALLOW, when we want it to be DENY - ApplicableRegionSet set = WGUtils.getRegionManagerWithWorld(event.getWorld()).getApplicableRegions(BukkitAdapter.asBlockVector(block.getLocation())); + // we can't query unfortunately, since null flags seem to equate to ALLOW, when + // we want it to be DENY + ApplicableRegionSet set = WGUtils.getRegionManagerWithWorld(event.getWorld()) + .getApplicableRegions(BukkitAdapter.asBlockVector(block.getLocation())); - // loop through regions, if any region does not have a passthrough value set, then don't allow + // loop through regions, if any region does not have a passthrough value set, + // then don't allow for (var region : set.getRegions()) { if (region.getFlag(Flags.PASSTHROUGH) == null) { return; } } - // if every region with an explicit passthrough value, then allow passthrough of protection block + // if every region with an explicit passthrough value, then allow passthrough of + // protection block event.setResult(Event.Result.ALLOW); } } @@ -133,8 +152,10 @@ public void onBlockPlace(BlockPlaceEvent e) { BlockHandler.createPSRegion(e); } - // returns the error message, or "" if the player has permission to break the region - // TODO: refactor and move this to PSRegion, so that /ps unclaim can use the same checks + // returns the error message, or "" if the player has permission to break the + // region + // TODO: refactor and move this to PSRegion, so that /ps unclaim can use the + // same checks private String checkPermissionToBreakProtection(Player p, PSRegion r) { // check for destroy permission if (!p.hasPermission("protectionstones.destroy")) { @@ -146,7 +167,8 @@ private String checkPermissionToBreakProtection(Player p, PSRegion r) { return PSL.NO_REGION_PERMISSION.msg(); } - // cannot break region being rented (prevents splitting merged regions, and breaking as tenant owner) + // cannot break region being rented (prevents splitting merged regions, and + // breaking as tenant owner) if (r.getRentStage() == PSRegion.RentStage.RENTING && !p.hasPermission("protectionstones.superowner")) { return PSL.RENT_CANNOT_BREAK_WHILE_RENTING.msg(); } @@ -167,10 +189,11 @@ private boolean playerBreakProtection(Player p, PSRegion r) { } // Call PSBreakEvent - PSBreakProtectBlockEvent event = new PSBreakProtectBlockEvent(r , p); + PSBreakProtectBlockEvent event = new PSBreakProtectBlockEvent(r, p); Bukkit.getPluginManager().callEvent(event); // don't give ps block to player if the event is cancelled - if (event.isCancelled()) return false; + if (event.isCancelled()) + return false; // return protection stone if no drop option is off if (blockOptions != null && !blockOptions.noDrop) { @@ -188,7 +211,9 @@ private boolean playerBreakProtection(Player p, PSRegion r) { // check if removing the region and firing region remove event blocked it if (!r.deleteRegion(true, p)) { - if (!ProtectionStones.getInstance().getConfigOptions().allowMergingHoles) { // side case if the removing creates a hole and those are prevented + if (!ProtectionStones.getInstance().getConfigOptions().allowMergingHoles) { // side case if the removing + // creates a hole and those are + // prevented PSL.msg(p, PSL.DELETE_REGION_PREVENTED_NO_HOLES.msg()); } return false; @@ -215,13 +240,15 @@ public void onPlayerInteract(PlayerInteractEvent e) { } // this will be the first event handler called in the chain - // thus we should cancel the event here if possible (so other plugins don't start acting upon it) + // thus we should cancel the event here if possible (so other plugins don't + // start acting upon it) @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onBlockBreakLowPriority(BlockBreakEvent e) { Player p = e.getPlayer(); Block pb = e.getBlock(); - if (!ProtectionStones.isProtectBlock(pb)) return; + if (!ProtectionStones.isProtectBlock(pb)) + return; // check if player has permission to break the protection PSRegion r = PSRegion.fromLocation(pb.getLocation()); @@ -242,15 +269,18 @@ public void onBlockBreak(BlockBreakEvent e) { PSProtectBlock blockOptions = ProtectionStones.getBlockOptions(pb); // check if block broken is protection stone type - if (blockOptions == null) return; + if (blockOptions == null) + return; // check if that is actually a protection stone block (owns a region) if (!ProtectionStones.isProtectBlock(pb)) { - // prevent silk touching of protection stone blocks (that aren't holding a region) + // prevent silk touching of protection stone blocks (that aren't holding a + // region) if (blockOptions.preventSilkTouch) { ItemStack left = p.getInventory().getItemInMainHand(); ItemStack right = p.getInventory().getItemInOffHand(); - if (!left.containsEnchantment(Enchantment.SILK_TOUCH) && !right.containsEnchantment(Enchantment.SILK_TOUCH)) { + if (!left.containsEnchantment(Enchantment.SILK_TOUCH) + && !right.containsEnchantment(Enchantment.SILK_TOUCH)) { return; } e.setDropItems(false); @@ -307,10 +337,13 @@ public void onPrepareItemCraft(PrepareItemCraftEvent e) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onCrafter(CrafterCraftEvent e) { - if (e.getBlock().getType() != Material.CRAFTER) return; - if (!(e.getBlock().getState() instanceof Container container)) return; + if (e.getBlock().getType() != Material.CRAFTER) + return; + if (!(e.getBlock().getState() instanceof Container container)) + return; for (ItemStack item : container.getInventory().getContents()) { - if (item == null) continue; + if (item == null) + continue; PSProtectBlock options = ProtectionStones.getBlockOptions(item); if (options != null && !options.allowUseInCrafting) { e.setCancelled(true); @@ -320,7 +353,8 @@ public void onCrafter(CrafterCraftEvent e) { } } - // -=-=-=- disable grindstone inventory to prevent infinite exp exploit with enchanted_effect option -=-=-=- + // -=-=-=- disable grindstone inventory to prevent infinite exp exploit with + // enchanted_effect option -=-=-=- // see https://github.com/espidev/ProtectionStones/issues/324 @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @@ -332,14 +366,14 @@ public void onInventoryClickEvent(InventoryClickEvent e) { } } - // -=-=-=- block changes to protection block related events -=-=-=- @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onPlayerBucketFill(PlayerBucketEmptyEvent e) { Block clicked = e.getBlockClicked(); BlockFace bf = e.getBlockFace(); - Block check = clicked.getWorld().getBlockAt(clicked.getX() + e.getBlockFace().getModX(), clicked.getY() + bf.getModY(), clicked.getZ() + e.getBlockFace().getModZ()); + Block check = clicked.getWorld().getBlockAt(clicked.getX() + e.getBlockFace().getModX(), + clicked.getY() + bf.getModY(), clicked.getZ() + e.getBlockFace().getModZ()); if (ProtectionStones.isProtectBlock(check)) { e.setCancelled(true); } @@ -372,7 +406,7 @@ public void onSpongeAbsorb(SpongeAbsorbEvent event) { event.setCancelled(true); } } - + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onBlockFade(BlockFadeEvent e) { if (ProtectionStones.isProtectBlock(e.getBlock())) { @@ -389,23 +423,31 @@ public void onBlockForm(BlockFormEvent e) { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onBlockDropItem(BlockDropItemEvent e) { - // unfortunately, the below fix does not really work because Spigot only triggers for the source block, despite - // what the documentation says: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockDropItemEvent.html - - // we want to replace protection blocks that have their protection block broken (ex. signs, banners) - // the block may not exist anymore, and so we have to recreate the isProtectBlock method here + // unfortunately, the below fix does not really work because Spigot only + // triggers for the source block, despite + // what the documentation says: + // https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockDropItemEvent.html + + // we want to replace protection blocks that have their protection block broken + // (ex. signs, banners) + // the block may not exist anymore, and so we have to recreate the + // isProtectBlock method here BlockState bs = e.getBlockState(); - if (!ProtectionStones.isProtectBlockType(bs.getType().toString())) return; + if (!ProtectionStones.isProtectBlockType(bs.getType().toString())) + return; RegionManager rgm = WGUtils.getRegionManagerWithWorld(bs.getWorld()); - if (rgm == null) return; + if (rgm == null) + return; // check if the block is a source block ProtectedRegion br = rgm.getRegion(WGUtils.createPSID(bs.getLocation())); - if (!ProtectionStones.isPSRegion(br) && PSMergedRegion.getMergedRegion(bs.getLocation()) == null) return; + if (!ProtectionStones.isPSRegion(br) && PSMergedRegion.getMergedRegion(bs.getLocation()) == null) + return; PSRegion r = PSRegion.fromLocation(bs.getLocation()); - if (r == null) return; + if (r == null) + return; // puts the block back r.unhide(); @@ -447,7 +489,8 @@ public void onEntityExplode(EntityExplodeEvent e) { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onEntityChangeBlock(EntityChangeBlockEvent e) { - if (!ProtectionStones.isProtectBlock(e.getBlock())) return; + if (!ProtectionStones.isProtectBlock(e.getBlock())) + return; // events like ender dragon block break, wither running into block break, etc. if (!blockExplodeUtil(e.getBlock().getWorld(), e.getBlock())) { @@ -501,24 +544,30 @@ private boolean blockExplodeUtil(World w, Block b) { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onPlayerTeleport(PlayerTeleportEvent event) { // we only want plugin triggered teleports, ignore natural teleportation - if (event.getCause() == TeleportCause.ENDER_PEARL || event.getCause() == TeleportCause.CHORUS_FRUIT) return; + if (event.getCause() == TeleportCause.ENDER_PEARL || event.getCause() == TeleportCause.CHORUS_FRUIT) + return; - if (event.getPlayer().hasPermission("protectionstones.tp.bypassprevent")) return; + if (event.getPlayer().hasPermission("protectionstones.tp.bypassprevent")) + return; WorldGuardPlugin wg = WorldGuardPlugin.inst(); RegionManager rgm = WGUtils.getRegionManagerWithWorld(event.getTo().getWorld()); BlockVector3 v = BlockVector3.at(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ()); if (rgm != null) { - // check if player can teleport into region (no region with preventTeleportIn = true) + // check if player can teleport into region (no region with preventTeleportIn = + // true) ApplicableRegionSet regions = rgm.getApplicableRegions(v); - if (regions.getRegions().isEmpty()) return; + if (regions.getRegions().isEmpty()) + return; boolean foundNoTeleport = false; for (ProtectedRegion r : regions) { String f = r.getFlag(FlagHandler.PS_BLOCK_MATERIAL); - if (f != null && ProtectionStones.getBlockOptions(f) != null && ProtectionStones.getBlockOptions(f).preventTeleportIn) + if (f != null && ProtectionStones.getBlockOptions(f) != null + && ProtectionStones.getBlockOptions(f).preventTeleportIn) foundNoTeleport = true; - if (r.getOwners().contains(wg.wrapPlayer(event.getPlayer()))) return; + if (r.getOwners().contains(wg.wrapPlayer(event.getPlayer()))) + return; } if (foundNoTeleport) { @@ -531,32 +580,38 @@ public void onPlayerTeleport(PlayerTeleportEvent event) { // -=-=-=- player defined events -=-=-=- private void execEvent(String action, CommandSender s, String player, PSRegion region) { - if (player == null) player = ""; + if (player == null) + player = ""; // split action_type: action String[] sp = action.split(": "); - if (sp.length == 0) return; + if (sp.length < 2) + return; // need at least action_type and action StringBuilder act = new StringBuilder(sp[1]); - for (int i = 2; i < sp.length; i++) act.append(": ").append(sp[i]); // add anything extra that has a colon + for (int i = 2; i < sp.length; i++) + act.append(": ").append(sp[i]); // add anything extra that has a colon act = new StringBuilder(act.toString() .replace("%player%", player) .replace("%world%", region.getWorld().getName()) - .replace("%region%", region.getName() == null ? region.getId() : region.getName() + " (" + region.getId() + ")") + .replace("%region%", + region.getName() == null ? region.getId() : region.getName() + " (" + region.getId() + ")") .replace("%block_x%", region.getProtectBlock().getX() + "") .replace("%block_y%", region.getProtectBlock().getY() + "") .replace("%block_z%", region.getProtectBlock().getZ() + "")); switch (sp[0]) { case "player_command": - if (s != null) Bukkit.getServer().dispatchCommand(s, act.toString()); + if (s != null) + Bukkit.getServer().dispatchCommand(s, act.toString()); break; case "console_command": Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), act.toString()); break; case "message": - if (s != null) s.sendMessage(ChatColor.translateAlternateColorCodes('&', act.toString())); + if (s != null) + s.sendMessage(ChatColor.translateAlternateColorCodes('&', act.toString())); break; case "global_message": for (Player p : Bukkit.getOnlinePlayers()) { @@ -572,10 +627,13 @@ private void execEvent(String action, CommandSender s, String player, PSRegion r @EventHandler public void onPSCreate(PSCreateEvent event) { - if (event.isCancelled()) return; - if (!event.getRegion().getTypeOptions().eventsEnabled) return; + if (event.isCancelled()) + return; + if (!event.getRegion().getTypeOptions().eventsEnabled) + return; - // run on next tick (after the region is created to allow for edits to the region) + // run on next tick (after the region is created to allow for edits to the + // region) Bukkit.getServer().getScheduler().runTask(ProtectionStones.getInstance(), () -> { // run custom commands (in config) for (String action : event.getRegion().getTypeOptions().regionCreateCommands) { @@ -586,9 +644,12 @@ public void onPSCreate(PSCreateEvent event) { @EventHandler public void onPSRemove(PSRemoveEvent event) { - if (event.isCancelled()) return; - if (event.getRegion().getTypeOptions() == null) return; - if (!event.getRegion().getTypeOptions().eventsEnabled) return; + if (event.isCancelled()) + return; + if (event.getRegion().getTypeOptions() == null) + return; + if (!event.getRegion().getTypeOptions().eventsEnabled) + return; // run custom commands (in config) for (String action : event.getRegion().getTypeOptions().regionDestroyCommands) { diff --git a/src/main/java/dev/espi/protectionstones/PSPlayer.java b/src/main/java/dev/espi/protectionstones/PSPlayer.java index b866da36..85c6af20 100644 --- a/src/main/java/dev/espi/protectionstones/PSPlayer.java +++ b/src/main/java/dev/espi/protectionstones/PSPlayer.java @@ -92,6 +92,7 @@ public PSPlayer(UUID uuid) { /** * Get the wrapped player's uuid. + * * @return the uuid */ @@ -107,7 +108,8 @@ public UUID getUuid() { */ public Player getPlayer() { - if (p == null) return Bukkit.getPlayer(uuid); + if (p == null) + return Bukkit.getPlayer(uuid); return p; } @@ -120,7 +122,8 @@ public Player getPlayer() { */ public OfflinePlayer getOfflinePlayer() { - if (p == null) return Bukkit.getOfflinePlayer(uuid); + if (p == null) + return Bukkit.getOfflinePlayer(uuid); return p; } @@ -137,7 +140,8 @@ public String getName() { */ public boolean hasAmount(double amount) { - if (!ProtectionStones.getInstance().isVaultSupportEnabled()) return false; + if (!ProtectionStones.getInstance().isVaultSupportEnabled()) + return false; return ProtectionStones.getInstance().getVaultEconomy().has(getOfflinePlayer(), amount); } @@ -149,7 +153,8 @@ public boolean hasAmount(double amount) { */ public double getBalance() { - if (!ProtectionStones.getInstance().isVaultSupportEnabled()) return 0; + if (!ProtectionStones.getInstance().isVaultSupportEnabled()) + return 0; return ProtectionStones.getInstance().getVaultEconomy().getBalance(getOfflinePlayer()); } @@ -162,7 +167,8 @@ public double getBalance() { */ public EconomyResponse depositBalance(double amount) { - if (ProtectionStones.getInstance().getVaultEconomy() == null) return null; + if (ProtectionStones.getInstance().getVaultEconomy() == null) + return null; return ProtectionStones.getInstance().getVaultEconomy().depositPlayer(getOfflinePlayer(), amount); } @@ -175,7 +181,8 @@ public EconomyResponse depositBalance(double amount) { */ public EconomyResponse withdrawBalance(double amount) { - if (ProtectionStones.getInstance().getVaultEconomy() == null) return null; + if (ProtectionStones.getInstance().getVaultEconomy() == null) + return null; return ProtectionStones.getInstance().getVaultEconomy().withdrawPlayer(getOfflinePlayer(), amount); } @@ -192,14 +199,20 @@ public void pay(PSPlayer payee, double amount) { payee.depositBalance(amount); } - static class CannotAccessOfflinePlayerPermissionsException extends RuntimeException {} + static class CannotAccessOfflinePlayerPermissionsException extends RuntimeException { + } /** - * Get a player's permission limits for each protection block (protectionstones.limit.alias.x) - * Protection blocks that aren't specified in the player's permissions will not be returned in the map. - * If LuckPerms support isn't enabled and the player is not online, then the method will throw a CannotAccessOfflinePlayerPermissionsException. + * Get a player's permission limits for each protection block + * (protectionstones.limit.alias.x) + * Protection blocks that aren't specified in the player's permissions will not + * be returned in the map. + * If LuckPerms support isn't enabled and the player is not online, then the + * method will throw a CannotAccessOfflinePlayerPermissionsException. * - * @return a hashmap containing a psprotectblock object to an integer, which is the number of protection regions of that type the player is allowed to place + * @return a hashmap containing a psprotectblock object to an integer, which is + * the number of protection regions of that type the player is allowed + * to place */ public HashMap getRegionLimits() { @@ -208,11 +221,14 @@ public HashMap getRegionLimits() { List permissions; if (getPlayer() != null) { - permissions = getPlayer().getEffectivePermissions().stream().map(PermissionAttachmentInfo::getPermission).collect(Collectors.toList()); + permissions = getPlayer().getEffectivePermissions().stream().map(PermissionAttachmentInfo::getPermission) + .collect(Collectors.toList()); } else if (getOfflinePlayer().getPlayer() != null) { - permissions = getOfflinePlayer().getPlayer().getEffectivePermissions().stream().map(PermissionAttachmentInfo::getPermission).collect(Collectors.toList()); + permissions = getOfflinePlayer().getPlayer().getEffectivePermissions().stream() + .map(PermissionAttachmentInfo::getPermission).collect(Collectors.toList()); } else if (ProtectionStones.getInstance().isLuckPermsSupportEnabled()) { - // use luckperms to obtain all of an offline player's permissions (vault and spigot api are unable to do this) + // use luckperms to obtain all of an offline player's permissions (vault and + // spigot api are unable to do this) try { permissions = MiscUtil.getLuckPermsUserPermissions(getUuid()); } catch (InterruptedException | ExecutionException e) { @@ -228,10 +244,14 @@ public HashMap getRegionLimits() { String[] spl = perm.split("\\."); if (spl.length == 4 && ProtectionStones.getProtectBlockFromAlias(spl[2]) != null) { - PSProtectBlock block = ProtectionStones.getProtectBlockFromAlias(spl[2]); - int limit = Integer.parseInt(spl[3]); - if (regionLimits.get(block) == null || regionLimits.get(block) < limit) { // only use max limit - regionLimits.put(block, limit); + try { + PSProtectBlock block = ProtectionStones.getProtectBlockFromAlias(spl[2]); + int limit = Integer.parseInt(spl[3]); + if (regionLimits.get(block) == null || regionLimits.get(block) < limit) { // only use max limit + regionLimits.put(block, limit); + } + } catch (NumberFormatException ignored) { + // skip invalid limit permissions like "protectionstones.limit.alias.*" } } } @@ -240,17 +260,21 @@ public HashMap getRegionLimits() { } /** - * Get a player's total protection limit from permission (protectionstones.limit.x) - * If there is no attached Player object to this PSPlayer, and LuckPerms is not enabled, this throws a CannotAccessOfflinePlayerPermissionsException. + * Get a player's total protection limit from permission + * (protectionstones.limit.x) + * If there is no attached Player object to this PSPlayer, and LuckPerms is not + * enabled, this throws a CannotAccessOfflinePlayerPermissionsException. * - * @return the number of protection regions the player can have, or -1 if there is no limit set. + * @return the number of protection regions the player can have, or -1 if there + * is no limit set. */ public int getGlobalRegionLimits() { if (getPlayer() != null) { return MiscUtil.getPermissionNumber(getPlayer(), "protectionstones.limit.", -1); } else if (ProtectionStones.getInstance().isLuckPermsSupportEnabled()) { - // use LuckPerms to obtain all of an offline player's permissions (vault and spigot api are unable to do this) + // use LuckPerms to obtain all of an offline player's permissions (vault and + // spigot api are unable to do this) try { List permissions = MiscUtil.getLuckPermsUserPermissions(getUuid()); return MiscUtil.getPermissionNumber(permissions, "protectionstones.limit.", -1); @@ -265,7 +289,8 @@ public int getGlobalRegionLimits() { /** * Get the list of regions that a player can pay money for taxes to. - * Note: this should be run asynchronously, as it can be very slow with large amounts of regions. + * Note: this should be run asynchronously, as it can be very slow with large + * amounts of regions. * * @return the regions that the player owes tax money to */ @@ -278,7 +303,8 @@ public List getTaxEligibleRegions() { for (ProtectedRegion r : rgm.getRegions().values()) { PSRegion psr = PSRegion.fromWGRegion(w, r); - if (psr != null && psr.isOwner(getUuid()) && psr.getTypeOptions() != null && psr.getTypeOptions().taxPeriod != -1) { + if (psr != null && psr.isOwner(getUuid()) && psr.getTypeOptions() != null + && psr.getTypeOptions().taxPeriod != -1) { ret.add(psr); } } @@ -287,17 +313,21 @@ public List getTaxEligibleRegions() { } /** - * Get the list of regions that a player owns, or is a member of. It is recommended to run this asynchronously + * Get the list of regions that a player owns, or is a member of. It is + * recommended to run this asynchronously * since the query can be slow. * * @param w world to search for regions in - * @param canBeMember whether or not to add regions where the player is a member, not owner - * @return list of regions that the player owns (or is a part of if canBeMember is true) + * @param canBeMember whether or not to add regions where the player is a + * member, not owner + * @return list of regions that the player owns (or is a part of if canBeMember + * is true) */ public List getPSRegions(World w, boolean canBeMember) { RegionManager rgm = WGUtils.getRegionManagerWithWorld(w); - if (rgm == null) return new ArrayList<>(); + if (rgm == null) + return new ArrayList<>(); return rgm.getRegions().values().stream() .filter(ProtectionStones::isPSRegion) @@ -307,24 +337,31 @@ public List getPSRegions(World w, boolean canBeMember) { } /** - * Get the list of regions that a player owns, or is a member of. It is recommended to run this asynchronously + * Get the list of regions that a player owns, or is a member of. It is + * recommended to run this asynchronously * since the query can be slow. * - * Note: Regions that the player owns that are named will be cross-world, otherwise this only searches in one world. + * Note: Regions that the player owns that are named will be cross-world, + * otherwise this only searches in one world. * * @param w world to search for regions in - * @param canBeMember whether or not to add regions where the player is a member, not owner - * @return list of regions that the player owns (or is a part of if canBeMember is true) + * @param canBeMember whether or not to add regions where the player is a + * member, not owner + * @return list of regions that the player owns (or is a part of if canBeMember + * is true) */ public List getPSRegionsCrossWorld(World w, boolean canBeMember) { List regions = getPSRegions(w, canBeMember); // set entry format: "worldName regionId" - Set regionIdAdded = regions.stream().map(r -> w.getName() + " " + r.getId()).collect(Collectors.toSet()); + Set regionIdAdded = regions.stream().map(r -> w.getName() + " " + r.getId()) + .collect(Collectors.toSet()); // obtain cross-world named worlds ProtectionStones.regionNameToID.forEach((rw, rs) -> { World world = Bukkit.getWorld(rw); + if (world == null) + return; // skip unloaded worlds RegionManager rm = WGUtils.getRegionManagerWithWorld(world); if (rm != null) { rs.values().forEach(rIds -> rIds.forEach(rId -> { @@ -346,9 +383,11 @@ public List getPSRegionsCrossWorld(World w, boolean canBeMember) { } /** - * Get the list of homes a player owns. It is recommended to run this asynchronously. + * Get the list of homes a player owns. It is recommended to run this + * asynchronously. * - * Note: Regions that the player owns that are named will be cross-world, otherwise this only searches in one world. + * Note: Regions that the player owns that are named will be cross-world, + * otherwise this only searches in one world. * * @param w world to search for regions in * @return list of regions that are the player's homes diff --git a/src/main/java/dev/espi/protectionstones/ProtectionStones.java b/src/main/java/dev/espi/protectionstones/ProtectionStones.java index bd1775f9..adf472ca 100644 --- a/src/main/java/dev/espi/protectionstones/ProtectionStones.java +++ b/src/main/java/dev/espi/protectionstones/ProtectionStones.java @@ -48,13 +48,14 @@ import java.io.File; import java.lang.reflect.Field; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; - /** - * The base class for the plugin. Some utilities are static, and others are instance methods, so they need to + * The base class for the plugin. Some utilities are static, and others are + * instance methods, so they need to * be accessed through getInstance(). */ @@ -76,7 +77,6 @@ public class ProtectionStones extends JavaPlugin { private PSConfig configOptions; static HashMap protectionStonesOptions = new HashMap<>(); - // ps alias to id cache // > static HashMap>> regionNameToID = new HashMap<>(); @@ -91,8 +91,8 @@ public class ProtectionStones extends JavaPlugin { private boolean placeholderAPISupportEnabled = false; - // ps toggle/on/off list - public static Set toggleList = new HashSet<>(); + // ps toggle/on/off list - thread-safe set + public static Set toggleList = ConcurrentHashMap.newKeySet(); /* ~~~~~~~~~~ Instance methods ~~~~~~~~~~~~ */ @@ -164,6 +164,7 @@ public boolean isDebug() { /** * Print a debug message (only prints if the plugin is in debug mode). + * * @param msg the message to print */ public void debug(String msg) { @@ -194,12 +195,12 @@ public void setConfigOptions(PSConfig conf) { public List getConfiguredBlocks() { List l = new ArrayList<>(); for (PSProtectBlock b : protectionStonesOptions.values()) { - if (!l.contains(b)) l.add(b); + if (!l.contains(b)) + l.add(b); } return l; } - /* ~~~~~~~~~~ Static methods ~~~~~~~~~~~~~~ */ /** @@ -227,18 +228,21 @@ public static PSEconomy getEconomy() { * Get the protection block config options for the block specified. * * @param block the block to get the block options of - * @return the config options for the protect block specified (null if not found) + * @return the config options for the protect block specified (null if not + * found) */ public static PSProtectBlock getBlockOptions(Block block) { - if (block == null) return null; + if (block == null) + return null; return getBlockOptions(BlockUtil.getProtectBlockType(block)); } /** * Get the protection block config options for the item specified. * - * If the options has restrict-obtaining enabled, and the item does not contain the required NBT tag, null will + * If the options has restrict-obtaining enabled, and the item does not contain + * the required NBT tag, null will * be returned. * * @param item the item to get the block options of @@ -246,16 +250,20 @@ public static PSProtectBlock getBlockOptions(Block block) { */ public static PSProtectBlock getBlockOptions(ItemStack item) { - if (!isProtectBlockItem(item)) return null; + if (!isProtectBlockItem(item)) + return null; return getBlockOptions(BlockUtil.getProtectBlockType(item)); } /** - * Gets the config options for the protection block type specified. It is recommended to use the block parameter overloaded + * Gets the config options for the protection block type specified. It is + * recommended to use the block parameter overloaded * method instead if possible, since it deals better with heads. * - * @param blockType the material type name (Bukkit) of the protect block to get the options for, or "PLAYER_HEAD name" for heads - * @return the config options for the protect block specified (null if not found) + * @param blockType the material type name (Bukkit) of the protect block to get + * the options for, or "PLAYER_HEAD name" for heads + * @return the config options for the protect block specified (null if not + * found) */ public static PSProtectBlock getBlockOptions(String blockType) { return protectionStonesOptions.get(blockType); @@ -266,10 +274,13 @@ public static boolean isProtectBlockType(Block b) { } /** - * Get whether or not a material is used as a protection block. It is recommended to use the block - * parameter overloaded method if possible since player heads have a different format. + * Get whether or not a material is used as a protection block. It is + * recommended to use the block + * parameter overloaded method if possible since player heads have a different + * format. * - * @param material material type to check (Bukkit material name), or "PLAYER_HEAD name" for heads + * @param material material type to check (Bukkit material name), or + * "PLAYER_HEAD name" for heads * @return whether or not that material is being used for a protection block */ public static boolean isProtectBlockType(String material) { @@ -277,41 +288,52 @@ public static boolean isProtectBlockType(String material) { } /** - * Check whether or not a given block is a protection block, and actually protects a region. + * Check whether or not a given block is a protection block, and actually + * protects a region. + * * @param b the block to look at - * @return whether or not the block is a protection block responsible for a region. + * @return whether or not the block is a protection block responsible for a + * region. */ public static boolean isProtectBlock(Block b) { - if (!isProtectBlockType(b)) return false; + if (!isProtectBlockType(b)) + return false; RegionManager rgm = WGUtils.getRegionManagerWithWorld(b.getWorld()); - if (rgm == null) return false; - return rgm.getRegion(WGUtils.createPSID(b.getLocation())) != null || PSMergedRegion.getMergedRegion(b.getLocation()) != null; + if (rgm == null) + return false; + return rgm.getRegion(WGUtils.createPSID(b.getLocation())) != null + || PSMergedRegion.getMergedRegion(b.getLocation()) != null; } /** - * Check if a WorldGuard {@link ProtectedRegion} is a ProtectionStones region, and is configured in the config. + * Check if a WorldGuard {@link ProtectedRegion} is a ProtectionStones region, + * and is configured in the config. * * @param r the region to check - * @return true if the WorldGuard region is a ProtectionStones region, and false if it isn't + * @return true if the WorldGuard region is a ProtectionStones region, and false + * if it isn't */ public static boolean isPSRegion(ProtectedRegion r) { return isPSRegionFormat(r) && getBlockOptions(r.getFlag(FlagHandler.PS_BLOCK_MATERIAL)) != null; } /** - * Check if a WorldGuard {@link ProtectedRegion} has the format of a ProtectionStones region, but is not necessarily configured + * Check if a WorldGuard {@link ProtectedRegion} has the format of a + * ProtectionStones region, but is not necessarily configured * in the config. * * @param r the region to check - * @return true if the WorldGuard region is a ProtectionStones region, and false if it isn't + * @return true if the WorldGuard region is a ProtectionStones region, and false + * if it isn't */ public static boolean isPSRegionFormat(ProtectedRegion r) { return r != null && r.getId().startsWith("ps") && r.getFlag(FlagHandler.PS_BLOCK_MATERIAL) != null; } /** - * Check if a ProtectionStones name is already used by a region globally (from /ps name) + * Check if a ProtectionStones name is already used by a region globally (from + * /ps name) * * @param name the name to search for * @return whether or not there is a region with this name @@ -322,14 +344,16 @@ public static boolean isPSNameAlreadyUsed(String name) { RegionManager rgm = WGUtils.getRegionManagerWithWorld(Bukkit.getWorld(worldUid)); List l = regionNameToID.get(worldUid).get(name); - if (l == null) continue; + if (l == null) + continue; for (int i = 0; i < l.size(); i++) { // remove outdated cache if (rgm.getRegion(l.get(i)) == null) { l.remove(i); i--; } } - if (!l.isEmpty()) return true; + if (!l.isEmpty()) + return true; } return false; } @@ -337,14 +361,17 @@ public static boolean isPSNameAlreadyUsed(String name) { /** * Get protection stone regions using an ID or alias. * - * @param w the world to search in (only if it is an id; aliases/names are global) + * @param w the world to search in (only if it is an id; aliases/names + * are global) * @param identifier id or alias of the region - * @return a list of psregions that match the id or alias; will be empty if no regions were found + * @return a list of psregions that match the id or alias; will be empty if no + * regions were found */ public static List getPSRegions(World w, String identifier) { RegionManager rgm = WGUtils.getRegionManagerWithWorld(w); - if (rgm == null) return new ArrayList<>(); + if (rgm == null) + return new ArrayList<>(); PSRegion r = PSRegion.fromWGRegion(w, rgm.getRegion(identifier)); if (r != null) { // return id based query @@ -357,7 +384,8 @@ public static List getPSRegions(World w, String identifier) { } /** - * Removes a protection stone region given its ID, and the region manager it is stored in + * Removes a protection stone region given its ID, and the region manager it is + * stored in * Note: Does not remove the PS block. * * @param w the world that the region is in @@ -366,13 +394,16 @@ public static List getPSRegions(World w, String identifier) { */ public static boolean removePSRegion(World w, String psID) { - PSRegion r = PSRegion.fromWGRegion(checkNotNull(w), checkNotNull(WGUtils.getRegionManagerWithWorld(w).getRegion(psID))); + PSRegion r = PSRegion.fromWGRegion(checkNotNull(w), + checkNotNull(WGUtils.getRegionManagerWithWorld(w).getRegion(psID))); return r != null && r.deleteRegion(false); } /** - * Removes a protection stone region given its ID, and the region manager it is stored in, with a player as its cause - * Note: Does not remove the PS block, and does not check if the player (cause) has permission to do this. + * Removes a protection stone region given its ID, and the region manager it is + * stored in, with a player as its cause + * Note: Does not remove the PS block, and does not check if the player (cause) + * has permission to do this. * * @param w the world that the region is in * @param psID the worldguard region ID of the region @@ -381,7 +412,8 @@ public static boolean removePSRegion(World w, String psID) { */ public static boolean removePSRegion(World w, String psID, Player cause) { - PSRegion r = PSRegion.fromWGRegion(checkNotNull(w), checkNotNull(WGUtils.getRegionManagerWithWorld(w).getRegion(psID))); + PSRegion r = PSRegion.fromWGRegion(checkNotNull(w), + checkNotNull(WGUtils.getRegionManagerWithWorld(w).getRegion(psID))); return r != null && r.deleteRegion(false, cause); } @@ -393,41 +425,54 @@ public static boolean removePSRegion(World w, String psID, Player cause) { */ public static PSProtectBlock getProtectBlockFromAlias(String name) { - if (name == null) return null; + if (name == null) + return null; for (PSProtectBlock cpb : ProtectionStones.protectionStonesOptions.values()) { - if (cpb.alias.equalsIgnoreCase(name) || cpb.type.equalsIgnoreCase(name)) return cpb; + if (cpb.alias.equalsIgnoreCase(name) || cpb.type.equalsIgnoreCase(name)) + return cpb; } return null; } /** - * Check if an item is a valid protection block, and if checkNBT is true, check if it was created by - * ProtectionStones. Be aware that blocks may have restrict-obtaining off, meaning that it is ignored whether or not + * Check if an item is a valid protection block, and if checkNBT is true, check + * if it was created by + * ProtectionStones. Be aware that blocks may have restrict-obtaining off, + * meaning that it is ignored whether or not * the item is created by ProtectionStones (in this case have checkNBT false). * * @param item the item to check - * @param checkNBT whether or not to check if the plugin signed off on the item (restrict-obtaining) - * @return whether or not the item is a valid protection block item, and was created by protection stones + * @param checkNBT whether or not to check if the plugin signed off on the item + * (restrict-obtaining) + * @return whether or not the item is a valid protection block item, and was + * created by protection stones */ public static boolean isProtectBlockItem(ItemStack item, boolean checkNBT) { - if (item == null) return false; + if (item == null) + return false; // check basic item - if (!ProtectionStones.isProtectBlockType(BlockUtil.getProtectBlockType(item))) return false; + if (!ProtectionStones.isProtectBlockType(BlockUtil.getProtectBlockType(item))) + return false; // check for player heads - if (!checkNBT) return true; // if not checking nbt, you only need to check type + if (!checkNBT) + return true; // if not checking nbt, you only need to check type boolean tag = false; - // otherwise, check if the item was created by protection stones (stored in custom tag) + // otherwise, check if the item was created by protection stones (stored in + // custom tag) if (item.getItemMeta() != null) { CustomItemTagContainer tagContainer = item.getItemMeta().getCustomTagContainer(); try { // check if tag byte is 1 - Byte isPSBlock = tagContainer.getCustomTag(new NamespacedKey(ProtectionStones.getInstance(), "isPSBlock"), ItemTagType.BYTE); + Byte isPSBlock = tagContainer + .getCustomTag(new NamespacedKey(ProtectionStones.getInstance(), "isPSBlock"), ItemTagType.BYTE); tag = isPSBlock != null && isPSBlock == 1; } catch (IllegalArgumentException es) { - try { // some nbt data may be using a string (legacy nbt from ps version 2.0.0 -> 2.0.6) - String isPSBlock = tagContainer.getCustomTag(new NamespacedKey(ProtectionStones.getInstance(), "isPSBlock"), ItemTagType.STRING); + try { // some nbt data may be using a string (legacy nbt from ps version 2.0.0 -> + // 2.0.6) + String isPSBlock = tagContainer.getCustomTag( + new NamespacedKey(ProtectionStones.getInstance(), "isPSBlock"), ItemTagType.STRING); tag = isPSBlock != null && isPSBlock.equals("true"); } catch (IllegalArgumentException ignored) { } @@ -438,18 +483,24 @@ public static boolean isProtectBlockItem(ItemStack item, boolean checkNBT) { } /** - * Check if an item is a valid protection block, and if the block type has restrict-obtaining on, check if it was - * created by ProtectionStones (custom NBT tag). Be aware that blocks may have restrict-obtaining - * off, meaning that it ignores whether or not the item is created by ProtectionStones. + * Check if an item is a valid protection block, and if the block type has + * restrict-obtaining on, check if it was + * created by ProtectionStones (custom NBT tag). Be aware that blocks may have + * restrict-obtaining + * off, meaning that it ignores whether or not the item is created by + * ProtectionStones. * - * @param item the item to check - * @return whether or not the item is a valid protection block item, and was created by protection stones + * @param item the item to check + * @return whether or not the item is a valid protection block item, and was + * created by protection stones */ public static boolean isProtectBlockItem(ItemStack item) { - if (item == null) return false; + if (item == null) + return false; PSProtectBlock b = ProtectionStones.getBlockOptions(BlockUtil.getProtectBlockType(item)); - if (b == null) return false; + if (b == null) + return false; return isProtectBlockItem(item, b.restrictObtaining); } @@ -457,7 +508,8 @@ public static boolean isProtectBlockItem(ItemStack item) { * Get a protection block item from a protect block config object. * * @param b the config options for the protection block - * @return the item with NBT and other metadata to signify that it was created by protection stones + * @return the item with NBT and other metadata to signify that it was created + * by protection stones */ public static ItemStack createProtectBlockItem(PSProtectBlock b) { @@ -486,7 +538,8 @@ public static ItemStack createProtectBlockItem(PSProtectBlock b) { im.setDisplayName(ChatColor.translateAlternateColorCodes('&', b.displayName)); } List lore = new ArrayList<>(); - for (String s : b.lore) lore.add(ChatColor.translateAlternateColorCodes('&', s)); + for (String s : b.lore) + lore.add(ChatColor.translateAlternateColorCodes('&', s)); im.setLore(lore); // hide enchant name (cannot call addUnsafeEnchantment here) @@ -517,7 +570,8 @@ public static void loadConfig(boolean isReload) { ArgHelp.initHelpMenu(); // load economy - if (ProtectionStones.getInstance().economy != null) ProtectionStones.getInstance().economy.stop(); + if (ProtectionStones.getInstance().economy != null) + ProtectionStones.getInstance().economy.stop(); ProtectionStones.getInstance().economy = new PSEconomy(); // add command to Bukkit (using reflection) @@ -568,15 +622,19 @@ public void onEnable() { // register event listeners getServer().getPluginManager().registerEvents(new ListenerClass(), this); - // check that WorldGuard and WorldEdit are enabled (WorldGuard will only be enabled if there's WorldEdit) - if (getServer().getPluginManager().getPlugin("WorldGuard") == null || !getServer().getPluginManager().getPlugin("WorldGuard").isEnabled()) { + // check that WorldGuard and WorldEdit are enabled (WorldGuard will only be + // enabled if there's WorldEdit) + if (getServer().getPluginManager().getPlugin("WorldGuard") == null + || !getServer().getPluginManager().getPlugin("WorldGuard").isEnabled()) { getLogger().severe("WorldGuard or WorldEdit not enabled! Disabling ProtectionStones..."); getServer().getPluginManager().disablePlugin(this); } // check if Vault is enabled (for economy support) - if (getServer().getPluginManager().getPlugin("Vault") != null && getServer().getPluginManager().getPlugin("Vault").isEnabled()) { - RegisteredServiceProvider econ = getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class); + if (getServer().getPluginManager().getPlugin("Vault") != null + && getServer().getPluginManager().getPlugin("Vault").isEnabled()) { + RegisteredServiceProvider econ = getServer().getServicesManager() + .getRegistration(net.milkbowl.vault.economy.Economy.class); if (econ == null) { getLogger().warning("No economy plugin found by Vault! There will be no economy support!"); vaultSupportEnabled = false; @@ -589,7 +647,8 @@ public void onEnable() { } // check for PlaceholderAPI - if (getServer().getPluginManager().getPlugin("PlaceholderAPI") != null && getServer().getPluginManager().getPlugin("PlaceholderAPI").isEnabled()) { + if (getServer().getPluginManager().getPlugin("PlaceholderAPI") != null + && getServer().getPluginManager().getPlugin("PlaceholderAPI").isEnabled()) { getLogger().info("PlaceholderAPI support enabled!"); placeholderAPISupportEnabled = true; new PSPlaceholderExpansion().register(); @@ -598,7 +657,8 @@ public void onEnable() { } // check for LuckPerms - if (getServer().getPluginManager().getPlugin("LuckPerms") != null && getServer().getPluginManager().getPlugin("LuckPerms").isEnabled()) { + if (getServer().getPluginManager().getPlugin("LuckPerms") != null + && getServer().getPluginManager().getPlugin("LuckPerms").isEnabled()) { try { luckPermsSupportEnabled = true; luckPerms = getServer().getServicesManager().load(LuckPerms.class); diff --git a/src/main/java/dev/espi/protectionstones/utils/WGUtils.java b/src/main/java/dev/espi/protectionstones/utils/WGUtils.java index 6631a959..701027ba 100644 --- a/src/main/java/dev/espi/protectionstones/utils/WGUtils.java +++ b/src/main/java/dev/espi/protectionstones/utils/WGUtils.java @@ -37,7 +37,8 @@ public class WGUtils { - // Integer.MAX_VALUE/MIN_VALUE causes strange issues with WG not detecting players in regions, + // Integer.MAX_VALUE/MIN_VALUE causes strange issues with WG not detecting + // players in regions, // so we use the 16 bit limit, which is more than enough. public static final int MAX_BUILD_HEIGHT = Short.MAX_VALUE; public static final int MIN_BUILD_HEIGHT = Short.MIN_VALUE; @@ -58,13 +59,15 @@ public static RegionManager getRegionManagerWithPlayer(Player p) { */ public static RegionManager getRegionManagerWithWorld(World w) { - if (w == null) return null; + if (w == null) + return null; return WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(w)); } /** * Get all region managers for all worlds. - * Use this instead of looping worlds manually because some worlds may not have a region manager. + * Use this instead of looping worlds manually because some worlds may not have + * a region manager. * * @return returns all region managers from all worlds */ @@ -73,7 +76,8 @@ public static HashMap getAllRegionManagers() { HashMap m = new HashMap<>(); for (World w : Bukkit.getWorlds()) { RegionManager rgm = getRegionManagerWithWorld(w); - if (rgm != null) m.put(w, rgm); + if (rgm != null) + m.put(w, rgm); } return m; } @@ -88,13 +92,20 @@ public static PSLocation parsePSRegionToLocation(String regionId) { /** * Find regions that are overlapping or adjacent to the region given. + * * @param r * @param rgm * @param w * @return the list of regions */ public static Set findOverlapOrAdjacentRegions(ProtectedRegion r, RegionManager rgm, World w) { - HashSet overlappingRegions = new HashSet<>(rgm.getApplicableRegions(r).getRegions()); // we need to ensure addAll is implemented + HashSet overlappingRegions = new HashSet<>(rgm.getApplicableRegions(r).getRegions()); // we + // need + // to + // ensure + // addAll + // is + // implemented // find adjacent regions (not overlapping, but bordering) for (var edgeRegion : getTransientEdgeRegions(w, r)) { @@ -102,11 +113,14 @@ public static Set findOverlapOrAdjacentRegions(ProtectedRegion } // HACK: WORKAROUND FOR BUG - // We have a major issue with detecting adjacent regions when the other regions are PSGroupRegion (the one adjacent + // We have a major issue with detecting adjacent regions when the other regions + // are PSGroupRegion (the one adjacent // to the current one), which is likely a WorldGuard bug? // - // If you create an adjacent region south or east of the region, it seems that it doesn't detect the adjacent edge - // overlap, you need to increase the edge by one more block for the north and west (2 blocks from region edge). + // If you create an adjacent region south or east of the region, it seems that + // it doesn't detect the adjacent edge + // overlap, you need to increase the edge by one more block for the north and + // west (2 blocks from region edge). for (var edgeRegion : getTransientEdgeRegionsHelper(w, r, true)) { for (var region : rgm.getApplicableRegions(edgeRegion).getRegions()) { PSRegion psr = PSRegion.fromWGRegion(w, region); @@ -121,13 +135,21 @@ public static Set findOverlapOrAdjacentRegions(ProtectedRegion /** * Find regions that are overlapping or adjacent to the region given. + * * @param r * @param regionsToCheck * @param w * @return the list of regions */ - public static Set findOverlapOrAdjacentRegions(ProtectedRegion r, List regionsToCheck, World w) { - HashSet overlappingRegions = new HashSet<>(r.getIntersectingRegions(regionsToCheck)); // we need to ensure addAll is implemented + public static Set findOverlapOrAdjacentRegions(ProtectedRegion r, + List regionsToCheck, World w) { + HashSet overlappingRegions = new HashSet<>(r.getIntersectingRegions(regionsToCheck)); // we + // need + // to + // ensure + // addAll + // is + // implemented // find adjacent regions (not overlapping, but bordering) for (var edgeRegion : getTransientEdgeRegions(w, r)) { @@ -135,11 +157,14 @@ public static Set findOverlapOrAdjacentRegions(ProtectedRegion } // HACK: WORKAROUND FOR BUG - // We have a major issue with detecting adjacent regions when the other regions are PSGroupRegion (the one adjacent + // We have a major issue with detecting adjacent regions when the other regions + // are PSGroupRegion (the one adjacent // to the current one), which is likely a WorldGuard bug? // - // If you create an adjacent region south or east of the region, it seems that it doesn't detect the adjacent edge - // overlap, you need to increase the edge by one more block for the north and west (2 blocks from region edge). + // If you create an adjacent region south or east of the region, it seems that + // it doesn't detect the adjacent edge + // overlap, you need to increase the edge by one more block for the north and + // west (2 blocks from region edge). for (var edgeRegion : getTransientEdgeRegionsHelper(w, r, true)) { for (var region : edgeRegion.getIntersectingRegions(regionsToCheck)) { PSRegion psr = PSRegion.fromWGRegion(w, region); @@ -153,7 +178,9 @@ public static Set findOverlapOrAdjacentRegions(ProtectedRegion } /** - * Find the list of regions that border `r` (adjacent to the edge), but do not include the corners. + * Find the list of regions that border `r` (adjacent to the edge), but do not + * include the corners. + * * @param r region * @return the list of regions */ @@ -161,16 +188,19 @@ public static List getTransientEdgeRegions(World w, ProtectedRe return getTransientEdgeRegionsHelper(w, r, false); } - private static List getTransientEdgeRegionsHelper(World w, ProtectedRegion r, boolean oneBlockAdjustHack) { + private static List getTransientEdgeRegionsHelper(World w, ProtectedRegion r, + boolean oneBlockAdjustHack) { ArrayList toReturn = new ArrayList<>(); PSRegion psr = PSRegion.fromWGRegion(w, r); - // note that PSGroupRegion is a subclass of PSStandardRegion, so we need PSGroupRegion check first + // note that PSGroupRegion is a subclass of PSStandardRegion, so we need + // PSGroupRegion check first if (r instanceof ProtectedPolygonalRegion && psr instanceof PSGroupRegion) { PSGroupRegion psgr = (PSGroupRegion) PSRegion.fromWGRegion(w, r); for (PSMergedRegion psmr : psgr.getMergedRegions()) { - var testRegion = getDefaultProtectedRegion(psmr.getTypeOptions(), WGUtils.parsePSRegionToLocation(psmr.getId())); + var testRegion = getDefaultProtectedRegion(psmr.getTypeOptions(), + WGUtils.parsePSRegionToLocation(psmr.getId())); toReturn.addAll(getTransientEdgeRegionsHelper(w, testRegion, oneBlockAdjustHack)); } } else if (r instanceof ProtectedCuboidRegion || (psr instanceof PSStandardRegion)) { @@ -179,18 +209,21 @@ private static List getTransientEdgeRegionsHelper(World w, Prot maxY = maxPoint.getY(), minZ = minPoint.getZ(), maxZ = maxPoint.getZ(); toReturn = new ArrayList<>( - Arrays.asList( - new ProtectedCuboidRegion(r.getId() + "-edge-0", true, BlockVector3.at(minX, minY - 1, minZ), BlockVector3.at(maxX, maxY + 1, maxZ)), - new ProtectedCuboidRegion(r.getId() + "-edge-1", true, BlockVector3.at(minX - 1, minY, minZ), BlockVector3.at(maxX + 1, maxY, maxZ)), - new ProtectedCuboidRegion(r.getId() + "-edge-2", true, BlockVector3.at(minX, minY, minZ - 1), BlockVector3.at(maxX, maxY, maxZ + 1)) - ) - ); + Arrays.asList( + new ProtectedCuboidRegion(r.getId() + "-edge-0", true, + BlockVector3.at(minX, minY - 1, minZ), BlockVector3.at(maxX, maxY + 1, maxZ)), + new ProtectedCuboidRegion(r.getId() + "-edge-1", true, + BlockVector3.at(minX - 1, minY, minZ), BlockVector3.at(maxX + 1, maxY, maxZ)), + new ProtectedCuboidRegion(r.getId() + "-edge-2", true, + BlockVector3.at(minX, minY, minZ - 1), BlockVector3.at(maxX, maxY, maxZ + 1)))); if (oneBlockAdjustHack) { // one block extra in the north - toReturn.add(new ProtectedCuboidRegion(r.getId() + "-edge-3", true, BlockVector3.at(minX, minY, minZ - 2), BlockVector3.at(maxX, maxY, maxZ))); + toReturn.add(new ProtectedCuboidRegion(r.getId() + "-edge-3", true, + BlockVector3.at(minX, minY, minZ - 2), BlockVector3.at(maxX, maxY, maxZ))); // one block extra in the west - toReturn.add(new ProtectedCuboidRegion(r.getId() + "-edge-4", true, BlockVector3.at(minX - 2, minY, minZ), BlockVector3.at(maxX, maxY, maxZ))); + toReturn.add(new ProtectedCuboidRegion(r.getId() + "-edge-4", true, + BlockVector3.at(minX - 2, minY, minZ), BlockVector3.at(maxX, maxY, maxZ))); } } @@ -205,7 +238,8 @@ public static boolean overlapsStrongerRegion(World w, ProtectedRegion r, LocalPl // loop through all regions to check for "none" option for (ProtectedRegion rg : rp.getRegions()) { - if (rg.getId().equals(r.getId())) continue; + if (rg.getId().equals(r.getId())) + continue; if (ProtectionStones.isPSRegion(rg)) { PSRegion psr = PSRegion.fromWGRegion(w, rg); @@ -220,8 +254,10 @@ public static boolean overlapsStrongerRegion(World w, ProtectedRegion r, LocalPl if (rgm.overlapsUnownedRegion(r, lp)) { // check if the lp is not owner of a intersecting region for (ProtectedRegion rg : rp) { // ignore itself it has already has been added to the rgm - if (rg.getId().equals(r.getId())) continue; - if (rg.isOwner(lp)) continue; + if (rg.getId().equals(r.getId())) + continue; + if (rg.isOwner(lp)) + continue; if (rg.getPriority() > r.getPriority()) { // if protection priority < overlap priority return true; @@ -232,7 +268,8 @@ public static boolean overlapsStrongerRegion(World w, ProtectedRegion r, LocalPl PSRegion pr = PSRegion.fromWGRegion(w, rg); // don't need to check for owner, since all of these are unowned regions. - if (pr.isMember(lp.getUniqueId()) && pr.getTypeOptions().allowOtherRegionsToOverlap.equals("member")) { + if (pr.isMember(lp.getUniqueId()) + && pr.getTypeOptions().allowOtherRegionsToOverlap.equals("member")) { // if members are allowed to overlap this region continue; } @@ -242,7 +279,8 @@ public static boolean overlapsStrongerRegion(World w, ProtectedRegion r, LocalPl } // otherwise, this region is not allowed to be overlapped return true; - } else if (rg.getPriority() >= r.getPriority()) { // if the priorities are the same for plain WorldGuard regions, prevent overlap + } else if (rg.getPriority() >= r.getPriority()) { // if the priorities are the same for plain WorldGuard + // regions, prevent overlap return true; } } @@ -255,6 +293,8 @@ public static String matchLocationToPSID(Location l) { BlockVector3 v = BlockVector3.at(l.getX(), l.getY(), l.getZ()); String currentPSID = ""; RegionManager rgm = WGUtils.getRegionManagerWithWorld(l.getWorld()); + if (rgm == null) + return currentPSID; // world not supported by WorldGuard List idList = rgm.getApplicableRegionsIDs(v); if (idList.size() == 1) { // if the location is only in one region if (ProtectionStones.isPSRegionFormat(rgm.getRegion(idList.get(0)))) { @@ -290,17 +330,20 @@ public static BlockVector3 getMinChunkVector(double bx, double by, double bz, lo --chunkRadius; // this becomes chunk offset from centre chunk, not radius long chunkX = (long) Math.floor(bx / 16); long chunkZ = (long) Math.floor(bz / 16); - return BlockVector3.at((chunkX - chunkRadius) * 16, (yRadius == -1) ? MIN_BUILD_HEIGHT : by - yRadius, (chunkZ - chunkRadius) * 16); + return BlockVector3.at((chunkX - chunkRadius) * 16, (yRadius == -1) ? MIN_BUILD_HEIGHT : by - yRadius, + (chunkZ - chunkRadius) * 16); } public static BlockVector3 getMaxChunkVector(double bx, double by, double bz, long chunkRadius, long yRadius) { --chunkRadius; // this becomes chunk offset from centre chunk, not radius long chunkX = (long) Math.floor(bx / 16); long chunkZ = (long) Math.floor(bz / 16); - return BlockVector3.at((chunkX + chunkRadius) * 16 + 15, (yRadius == -1) ? MAX_BUILD_HEIGHT : by + yRadius, (chunkZ + chunkRadius) * 16 + 15); + return BlockVector3.at((chunkX + chunkRadius) * 16 + 15, (yRadius == -1) ? MAX_BUILD_HEIGHT : by + yRadius, + (chunkZ + chunkRadius) * 16 + 15); } - // create PS ids without making the numbers have scientific notation (addressed with long) + // create PS ids without making the numbers have scientific notation (addressed + // with long) public static String createPSID(double bx, double by, double bz) { return "ps" + (long) bx + "x" + (long) by + "y" + (long) bz + "z"; } @@ -310,8 +353,10 @@ public static String createPSID(Location l) { } public static boolean hasNoAccess(ProtectedRegion region, Player p, LocalPlayer lp, boolean canBeMember) { - if (region == null) return true; - return !p.hasPermission("protectionstones.superowner") && !region.isOwner(lp) && (!canBeMember || !region.isMember(lp)); + if (region == null) + return true; + return !p.hasPermission("protectionstones.superowner") && !region.isOwner(lp) + && (!canBeMember || !region.isMember(lp)); } // get the overlapping sets of groups of regions a player owns @@ -323,19 +368,23 @@ public static HashMap> getPlayerAdjacentRegionGroups(P HashMap> groupToIDs = new HashMap<>(); for (PSRegion r : pRegions) { - Set overlapping = findOverlapOrAdjacentRegions(r.getWGRegion(), r.getWGRegionManager(), r.getWorld()); + Set overlapping = findOverlapOrAdjacentRegions(r.getWGRegion(), r.getWGRegionManager(), + r.getWorld()); // algorithm to find adjacent regions String adjacentGroup = idToGroup.get(r.getId()); for (ProtectedRegion pr : overlapping) { - if (ProtectionStones.isPSRegion(pr) && pr.isOwner(WorldGuardPlugin.inst().wrapPlayer(p)) && !pr.getId().equals(r.getId())) { + if (ProtectionStones.isPSRegion(pr) && pr.isOwner(WorldGuardPlugin.inst().wrapPlayer(p)) + && !pr.getId().equals(r.getId())) { if (adjacentGroup == null) { // if the region hasn't been found to overlap a region yet if (idToGroup.get(pr.getId()) == null) { // if the overlapped region isn't part of a group yet idToGroup.put(pr.getId(), r.getId()); idToGroup.put(r.getId(), r.getId()); - groupToIDs.put(r.getId(), new ArrayList<>(Arrays.asList(pr.getId(), r.getId()))); // create new group + groupToIDs.put(r.getId(), new ArrayList<>(Arrays.asList(pr.getId(), r.getId()))); // create + // new + // group } else { // if the overlapped region is part of a group String groupID = idToGroup.get(pr.getId()); idToGroup.put(r.getId(), groupID); @@ -348,7 +397,9 @@ public static HashMap> getPlayerAdjacentRegionGroups(P if (idToGroup.get(pr.getId()) == null) { // if the overlapped region isn't part of a group idToGroup.put(pr.getId(), adjacentGroup); groupToIDs.get(adjacentGroup).add(pr.getId()); - } else if (!idToGroup.get(pr.getId()).equals(adjacentGroup)) { // if the overlapped region is part of a group, merge the groups + } else if (!idToGroup.get(pr.getId()).equals(adjacentGroup)) { // if the overlapped region is + // part of a group, merge the + // groups String mergeGroupID = idToGroup.get(pr.getId()); for (String gid : groupToIDs.get(mergeGroupID)) { idToGroup.put(gid, adjacentGroup); @@ -384,8 +435,10 @@ public static List getPointsFromDecomposedRegion(PSRegion r) { assert r.getPoints().size() == 4; List xs = new ArrayList<>(), zs = new ArrayList<>(); for (BlockVector2 p : r.getPoints()) { - if (!xs.contains(p.getX())) xs.add(p.getX()); - if (!zs.contains(p.getZ())) zs.add(p.getZ()); + if (!xs.contains(p.getX())) + xs.add(p.getX()); + if (!zs.contains(p.getZ())) + zs.add(p.getZ()); } List points = new ArrayList<>(); @@ -416,21 +469,24 @@ public static boolean canMergeRegionTypes(PSProtectBlock current, PSRegion merge // set a flag on a region, only saving it to db if it is actually a new value // prevents unnecessary messages in the console from saves - public static > void setFlagIfNeeded(ProtectedRegion region, T flag, String value) { + public static > void setFlagIfNeeded( + ProtectedRegion region, T flag, String value) { String curValue = region.getFlag(flag); if ((curValue == null && value == null) || (curValue != null && !curValue.equals(value))) { region.setFlag(flag, value); } } - public static >, V> void setFlagIfNeeded(ProtectedRegion region, T flag, Set value) { + public static >, V> void setFlagIfNeeded( + ProtectedRegion region, T flag, Set value) { Set curValue = region.getFlag(flag); if (!checkCollectionEquality(curValue, value)) { region.setFlag(flag, value); } } - public static >, V> void setFlagIfNeeded(ProtectedRegion region, T flag, List value) { + public static >, V> void setFlagIfNeeded( + ProtectedRegion region, T flag, List value) { List curValue = region.getFlag(flag); if (!checkCollectionEquality(curValue, value)) { region.setFlag(flag, value); From d20c21aa651292870f0353c2f3cf505b89746b53 Mon Sep 17 00:00:00 2001 From: BeDePaY <75163645+DimaSergeew@users.noreply.github.com> Date: Tue, 3 Feb 2026 11:48:24 +0300 Subject: [PATCH 2/2] Delete .vscode directory --- .vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 7b016a89..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "java.compile.nullAnalysis.mode": "automatic" -} \ No newline at end of file