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);