From 429de47a13115436411e2561c53daf0bfc4ef8e1 Mon Sep 17 00:00:00 2001 From: Dianelito <95977637+Dianelito@users.noreply.github.com> Date: Sun, 15 Feb 2026 20:10:56 +0000 Subject: [PATCH 1/2] Add support for minimessages and other formats (like hex colors) --- README.md | 2 +- pom.xml | 29 +- .../espi/protectionstones/ListenerClass.java | 168 ++- .../espi/protectionstones/PSGroupRegion.java | 69 +- .../java/dev/espi/protectionstones/PSL.java | 1207 ++++++++++------- .../dev/espi/protectionstones/PSRegion.java | 199 ++- .../protectionstones/ProtectionStones.java | 221 ++- .../flags/FarewellFlagHandler.java | 24 +- .../flags/GreetingFlagHandler.java | 19 +- src/main/resources/block2.toml | 300 ++++ src/main/resources/plugin.yml | 3 +- 11 files changed, 1557 insertions(+), 684 deletions(-) create mode 100644 src/main/resources/block2.toml diff --git a/README.md b/README.md index c914c4a1..df541ba6 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ View the Spigot page (with FAQ and install instructions) [here](https://www.spig Check the [wiki](https://github.com/espidev/ProtectionStones/wiki) for plugin reference information. ### Dependencies -* ProtectionStones 2.10.6 +* ProtectionStones 2.10.7 * Spigot 1.21.6+ * WorldGuard 7.0.9+ * WorldEdit 7.2.6+ diff --git a/pom.xml b/pom.xml index 3c7a417c..d2dcbde8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 dev.espi protectionstones - 2.10.6 + 2.10.7 ProtectionStones A grief prevention plugin for Spigot Minecraft servers. https://github.com/espidev/ProtectionStones @@ -89,8 +89,8 @@ 3.1 - 16 - 16 + 21 + 21 ${project.build.sourceEncoding} @@ -107,13 +107,18 @@ org.apache.maven.plugins maven-shade-plugin - 3.3.0 + 3.5.0 - true + false + false org.bstats - dev.espi.protectionstones + dev.espi.protectionstones.bstats + + + net.kyori + dev.espi.protectionstones.lib.kyori @@ -201,7 +206,7 @@ org.bstats bstats-bukkit - 3.0.2 + 3.1.0 compile @@ -272,5 +277,15 @@ json-simple 1.1.1 + + net.kyori + adventure-text-minimessage + 4.17.0 + + + net.kyori + adventure-platform-bukkit + 4.3.3 + diff --git a/src/main/java/dev/espi/protectionstones/ListenerClass.java b/src/main/java/dev/espi/protectionstones/ListenerClass.java index 87175a00..d4488d3d 100644 --- a/src/main/java/dev/espi/protectionstones/ListenerClass.java +++ b/src/main/java/dev/espi/protectionstones/ListenerClass.java @@ -44,6 +44,8 @@ import org.bukkit.event.block.*; import org.bukkit.event.entity.EntityChangeBlockEvent; import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.entity.EntityType; import org.bukkit.event.inventory.*; import org.bukkit.event.player.PlayerBucketEmptyEvent; import org.bukkit.event.player.PlayerInteractEvent; @@ -67,7 +69,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 +83,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 +100,8 @@ public void onPlayerJoin(PlayerJoinEvent e) { } } - // specifically add WG passthrough bypass here, so other plugins can see the result + // 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 +116,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 +142,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 +157,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 +179,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 +201,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 +230,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 +259,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 +327,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 +343,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 +356,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 +396,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 +413,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 +479,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())) { @@ -456,6 +489,17 @@ public void onEntityChangeBlock(EntityChangeBlockEvent e) { } } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onProjectileHit(ProjectileHitEvent e) { + if (e.getHitBlock() != null && ProtectionStones.isProtectBlock(e.getHitBlock())) { + EntityType type = e.getEntity().getType(); + if (type == EntityType.WIND_CHARGE || type == EntityType.BREEZE_WIND_CHARGE) { + e.setCancelled(true); + e.getEntity().remove(); + } + } + } + private void explodeUtil(List blockList, World w) { // loop through exploded blocks for (int i = 0; i < blockList.size(); i++) { @@ -501,24 +545,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 +581,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 == 0) + return; 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 +628,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 +645,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/PSGroupRegion.java b/src/main/java/dev/espi/protectionstones/PSGroupRegion.java index 2d5bc3df..51f5e35b 100644 --- a/src/main/java/dev/espi/protectionstones/PSGroupRegion.java +++ b/src/main/java/dev/espi/protectionstones/PSGroupRegion.java @@ -19,6 +19,7 @@ import com.sk89q.worldguard.protection.regions.ProtectedRegion; import dev.espi.protectionstones.utils.MiscUtil; import dev.espi.protectionstones.utils.Objs; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.entity.Player; @@ -28,8 +29,10 @@ import java.util.stream.Collectors; /** - * Represents a region that exists but is a group of merged {@link PSStandardRegion}s. - * Contains multiple {@link PSMergedRegion} representing the individual merged regions (which don't technically exist in WorldGuard). + * Represents a region that exists but is a group of merged + * {@link PSStandardRegion}s. + * Contains multiple {@link PSMergedRegion} representing the individual merged + * regions (which don't technically exist in WorldGuard). */ public class PSGroupRegion extends PSStandardRegion { @@ -66,12 +69,14 @@ public void updateTaxPayments() { long currentTime = System.currentTimeMillis(); List payments = Objs.replaceNull(getTaxPaymentsDue(), new ArrayList<>()); - List lastAdded = Objs.replaceNull(getRegionLastTaxPaymentAddedEntries(), new ArrayList<>()); + List lastAdded = Objs.replaceNull(getRegionLastTaxPaymentAddedEntries(), + new ArrayList<>()); // loop over merged regions for (PSMergedRegion r : getMergedRegions()) { // taxes disabled - if (getTypeOptions().taxPeriod == -1) continue; + if (getTypeOptions().taxPeriod == -1) + continue; boolean found = false; for (LastRegionTaxPaymentEntry last : lastAdded) { @@ -79,8 +84,11 @@ public void updateTaxPayments() { if (last.getRegionId().equals(r.getId())) { found = true; // if it's time to pay - if (last.getLastPaymentAdded() + Duration.ofSeconds(r.getTypeOptions().taxPeriod).toMillis() < currentTime) { - payments.add(new TaxPayment(currentTime + Duration.ofSeconds(r.getTypeOptions().taxPaymentTime).toMillis(), r.getTaxRate(), r.getId())); + if (last.getLastPaymentAdded() + + Duration.ofSeconds(r.getTypeOptions().taxPeriod).toMillis() < currentTime) { + payments.add(new TaxPayment( + currentTime + Duration.ofSeconds(r.getTypeOptions().taxPaymentTime).toMillis(), + r.getTaxRate(), r.getId())); last.setLastPaymentAdded(currentTime); } break; @@ -88,7 +96,9 @@ public void updateTaxPayments() { } if (!found) { - payments.add(new TaxPayment(currentTime + Duration.ofSeconds(r.getTypeOptions().taxPaymentTime).toMillis(), r.getTaxRate(), r.getId())); + payments.add( + new TaxPayment(currentTime + Duration.ofSeconds(r.getTypeOptions().taxPaymentTime).toMillis(), + r.getTaxRate(), r.getId())); lastAdded.add(new LastRegionTaxPaymentEntry(r.getId(), currentTime)); } } @@ -98,13 +108,15 @@ public void updateTaxPayments() { @Override public boolean hide() { - for (PSMergedRegion r : getMergedRegions()) r.hide(); + for (PSMergedRegion r : getMergedRegions()) + r.hide(); return true; } @Override public boolean unhide() { - for (PSMergedRegion r : getMergedRegions()) r.unhide(); + for (PSMergedRegion r : getMergedRegions()) + r.unhide(); return true; } @@ -125,18 +137,22 @@ public boolean deleteRegion(boolean deleteBlock, Player cause) { /** * Get the merged region whose ID is the same as the group region ID. + * * @return the root region */ public PSMergedRegion getRootRegion() { for (PSMergedRegion r : getMergedRegions()) { - if (r.getId().equals(getId())) return r; + if (r.getId().equals(getId())) + return r; } return null; } /** * Check if this region contains a specific merged region - * @param id the psID that would've been generated if the merged region was a standard region + * + * @param id the psID that would've been generated if the merged region was a + * standard region * @return whether or not the id is a merged region */ public boolean hasMergedRegion(String id) { @@ -145,7 +161,9 @@ public boolean hasMergedRegion(String id) { /** * Removes the merged region's information from the object. - * Note: This DOES NOT remove the actual PSMergedRegion object, you have to call deleteRegion() on that as well. + * Note: This DOES NOT remove the actual PSMergedRegion object, you have to call + * deleteRegion() on that as well. + * * @param id the id of the merged region */ public void removeMergedRegionInfo(String id) { @@ -166,14 +184,17 @@ public void removeMergedRegionInfo(String id) { if (getWGRegion().getFlag(FlagHandler.PS_TAX_LAST_PAYMENT_ADDED) != null) { String entry = ""; for (String e : getWGRegion().getFlag(FlagHandler.PS_TAX_LAST_PAYMENT_ADDED)) { - if (e.startsWith(id)) entry = e; + if (e.startsWith(id)) + entry = e; } getWGRegion().getFlag(FlagHandler.PS_TAX_LAST_PAYMENT_ADDED).remove(entry); } } /** - * Get the list of {@link PSMergedRegion} objects of the regions that were merged into this region. + * Get the list of {@link PSMergedRegion} objects of the regions that were + * merged into this region. + * * @return the list of regions merged into this region */ public List getMergedRegions() { @@ -183,8 +204,11 @@ public List getMergedRegions() { } /** - * Get the list of {@link PSMergedRegion} objects of the regions that were merged into this region. - * Note: This is unsafe as it includes {@link PSMergedRegion}s that are of types not configured in the config. + * Get the list of {@link PSMergedRegion} objects of the regions that were + * merged into this region. + * Note: This is unsafe as it includes {@link PSMergedRegion}s that are of types + * not configured in the config. + * * @return the list of regions merged into this region */ public List getMergedRegionsUnsafe() { @@ -196,4 +220,17 @@ public List getMergedRegionsUnsafe() { } return l; } + + public PSMergedRegion getMergedRegion(Location l) { + if (l == null) + return null; + com.sk89q.worldedit.math.BlockVector3 v = com.sk89q.worldedit.math.BlockVector3.at(l.getX(), l.getY(), + l.getZ()); + for (PSMergedRegion r : getMergedRegions()) { + if (r.getWGRegion().contains(v)) { + return r; + } + } + return null; + } } diff --git a/src/main/java/dev/espi/protectionstones/PSL.java b/src/main/java/dev/espi/protectionstones/PSL.java index b64c3248..2f700a6c 100644 --- a/src/main/java/dev/espi/protectionstones/PSL.java +++ b/src/main/java/dev/espi/protectionstones/PSL.java @@ -22,515 +22,766 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import net.kyori.adventure.text.minimessage.MiniMessage; + import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public enum PSL { - // messages.yml - - COOLDOWN("cooldown", ChatColor.GOLD + "Warning: " + ChatColor.GRAY + "Please wait for %time% seconds before placing again!"), - NO_SUCH_COMMAND("no_such_command", ChatColor.RED + "No such command. please type /ps help for more info"), - NO_ACCESS("no_access", ChatColor.RED + "You are not allowed to do that here."), - NO_ROOM_IN_INVENTORY("no_room_in_inventory", ChatColor.RED + "You don't have enough room in your inventory."), - NO_ROOM_DROPPING_ON_FLOOR("no_room_dropping_on_floor", ChatColor.RED + "You don't have enough room in your inventory. Dropping item on floor."), - INVALID_BLOCK("invalid_block", ChatColor.RED + "Invalid protection block."), - NOT_ENOUGH_MONEY("not_enough_money", ChatColor.RED + "You don't have enough money! The price is %price%."), - PAID_MONEY("paid_money", ChatColor.AQUA + "You've paid $%price%."), - INVALID_WORLD("invalid_world", ChatColor.RED + "Invalid world."), - MUST_BE_PLAYER("must_be_player", ChatColor.RED + "You must be a player to execute this command."), - GO_BACK_PAGE("go_back_page", "Go back a page."), - GO_NEXT_PAGE("go_next_page", "Go to next page."), - PAGE_DOES_NOT_EXIST("page_does_not_exist", ChatColor.RED + "Page does not exist."), - - HELP("help", ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " PS Help " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "=====\n" + ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps help"), - HELP_NEXT("help_next", ChatColor.GRAY + "Do /ps help %page% to go to the next page!"), - - COMMAND_REQUIRES_PLAYER_NAME("command_requires_player_name", ChatColor.RED + "This command requires a player name."), - - NO_PERMISSION_TOGGLE("no_permission_toggle", ChatColor.RED + "You don't have permission to use the toggle command."), - NO_PERMISSION_CREATE("no_permission_create", ChatColor.RED + "You don't have permission to place a protection block."), - NO_PERMISSION_CREATE_SPECIFIC("no_permission_create_specific", ChatColor.RED + "You don't have permission to place this protection block type."), - NO_PERMISSION_DESTROY("no_permission_destroy", ChatColor.RED + "You don't have permission to destroy a protection block."), - NO_PERMISSION_MEMBERS("no_permission_members", "&cYou don't have permission to use member commands."), - NO_PERMISSION_OWNERS("no_permission_owners", "&cYou don't have permission to use owner commands."), - NO_PERMISSION_ADMIN("no_permission_admin", ChatColor.RED + "You do not have permission to use that command."), - NO_PERMISSION_COUNT("no_permission_count", ChatColor.RED + "You do not have permission to use that command."), - NO_PERMISSION_COUNT_OTHERS("no_permission_count_others", ChatColor.RED + "You do not have permission to use that command."), - NO_PERMISSION_FLAGS("no_permission_flags", "&cYou do not have permission to use flag commands."), - NO_PERMISSION_PER_FLAG("no_permission_per_flag", ChatColor.RED + "You do not have permission to use that flag."), - NO_PERMISSION_RENT("no_permission_rent", ChatColor.RED + "You do not have permission for renting."), - NO_PERMISSION_TAX("no_permission_tax", ChatColor.RED + "You do not have permission to use the tax command."), - NO_PERMISSION_BUYSELL("no_permission_buysell", ChatColor.RED + "You do not have permission to buy and sell regions."), - NO_PERMISSION_UNHIDE("no_permission_unhide", ChatColor.RED + "You do not have permission to unhide protection blocks."), - NO_PERMISSION_HIDE("no_permission_hide", ChatColor.RED + "You do not have permission to hide protection blocks."), - NO_PERMISSION_INFO("no_permission_info", ChatColor.RED + "You do not have permission to use the region info command."), - NO_PERMISSION_PRIORITY("no_permission_priority", ChatColor.RED + "You do not have permission to use the priority command."), - NO_PERMISSION_REGION("no_permission_region", ChatColor.RED + "You do not have permission to use region commands."), - NO_PERMISSION_TP("no_permission_tp", ChatColor.RED + "You do not have permission to teleport to other players' protection blocks."), - NO_PERMISSION_HOME("no_permission_home", ChatColor.RED + "You do not have permission to teleport to your protection blocks."), - NO_PERMISSION_UNCLAIM("no_permission_unclaim", ChatColor.RED + "You do not have permission to use the unclaim command."), - NO_PERMISSION_UNCLAIM_REMOTE("no_permission_unclaim_remote", ChatColor.RED + "You do not have permission to use the unclaim remote command."), - NO_PERMISSION_VIEW("no_permission_view", ChatColor.RED + "You do not have permission to use the view command."), - NO_PERMISSION_GIVE("no_permission_give", ChatColor.RED + "You do not have permission to use the give command."), - NO_PERMISSION_GET("no_permission_get", ChatColor.RED + "You do not have permission to use the get command."), - NO_PERMISSION_SETHOME("no_permission_sethome", ChatColor.RED + "You do not have permission to use the sethome command."), - NO_PERMISSION_LIST("no_permission_list", ChatColor.RED + "You do not have permission to use the list command."), - NO_PERMISSION_LIST_OTHERS("no_permission_list_others", ChatColor.RED + "You do not have permission to use the list command for others."), - NO_PERMISSION_NAME("no_permission_name", ChatColor.RED + "You do not have permission to use the name command."), - NO_PERMISSION_SETPARENT("no_permission_setparent", ChatColor.RED + "You do not have permission to use the setparent command."), - NO_PERMISSION_SETPARENT_OTHERS("no_permission_setparent_others", ChatColor.RED + "You do not have permission to inherit from regions you don't own."), - NO_PERMISSION_MERGE("no_permission_merge", ChatColor.RED + "You do not have permission to use /ps merge."), - - ADDED_TO_REGION("psregion.added_to_region", ChatColor.AQUA + "%player%" + ChatColor.GRAY + " has been added to this region."), - ADDED_TO_REGION_SPECIFIC("psregion.added_to_region_specific", ChatColor.AQUA + "%player%" + ChatColor.GRAY + " has been added to region %region%."), - REMOVED_FROM_REGION("psregion.removed_from_region", ChatColor.AQUA + "%player%" + ChatColor.GRAY + " has been removed from region."), - REMOVED_FROM_REGION_SPECIFIC("psregion.removed_from_region_specific", ChatColor.AQUA + "%player%" + ChatColor.GRAY + " has been removed from region %region%."), - NOT_IN_REGION("psregion.not_in_region", ChatColor.RED + "You are not in a protection stones region!"), - PLAYER_NOT_FOUND("psregion.player_not_found", ChatColor.RED + "Player not found."), - NOT_PS_REGION("psregion.not_ps_region", ChatColor.RED + "Not a protection stones region."), - REGION_DOES_NOT_EXIST("psregion.region_does_not_exist", ChatColor.RED + "Region does not exist."), - NO_REGIONS_OWNED("psregion.no_regions_owned", ChatColor.RED + "You don't own any protected regions in this world!"), - NO_REGION_PERMISSION("psregion.no_region_permission", ChatColor.RED + "You do not have permission to do this in this region."), - PROTECTED("psregion.protected", ChatColor.AQUA + "This area is now protected."), - NO_LONGER_PROTECTED("psregion.no_longer_protected", ChatColor.YELLOW + "This area is no longer protected."), - CANT_PROTECT_THAT("psregion.cant_protect_that", ChatColor.RED + "You can't protect that area."), - REACHED_REGION_LIMIT("psregion.reached_region_limit", ChatColor.RED + "You can not have any more protected regions (%limit%)."), - REACHED_PER_BLOCK_REGION_LIMIT("psregion.reached_per_block_region_limit", ChatColor.RED + "You can not have any more regions of this type (%limit%)."), - WORLD_DENIED_CREATE("psregion.world_denied_create", ChatColor.RED + "You can not create protections in this world."), - REGION_OVERLAP("psregion.region_overlap", ChatColor.RED + "You can not place a protection block here as it overlaps another region."), - REGION_TOO_CLOSE("psregion.region_too_close", ChatColor.RED + "Your protection block must be a minimum of %num% blocks from the edge of other regions!"), - REGION_CANT_TELEPORT("psregion.cant_teleport", ChatColor.RED + "Your teleportation was blocked by a protection region!"), - SPECIFY_ID_INSTEAD_OF_ALIAS("psregion.specify_id_instead_of_alias", ChatColor.GRAY + "There were multiple regions found with this name! Please use an ID instead.\n Regions with this name: " + ChatColor.AQUA + "%regions%"), - REGION_NOT_ADJACENT("psregion.region_not_adjacent", ChatColor.RED + "You've passed the limit of non-adjacent regions! Try putting your protection block closer to other regions you already own."), - REGION_NOT_OVERLAPPING("psregion.not_overlapping", ChatColor.RED + "These regions don't overlap each other!"), - MULTI_REGION_DOES_NOT_EXIST("psregion.multi_region_does_not_exist", "One of these regions don't exist!"), - NO_REGION_HOLES("psregion.no_region_holes", ChatColor.RED + "Unprotected area detected inside region! This is not allowed!"), - DELETE_REGION_PREVENTED_NO_HOLES("psregion.delete_region_prevented", ChatColor.GRAY + "The region could not be removed, possibly because it creates a hole in the existing region."), - NOT_OWNER("psregion.not_owner", ChatColor.RED + "You are not an owner of this region!"), - CANNOT_MERGE_RENTED_REGION("psregion.cannot_merge_rented_region", ChatColor.RED + "Cannot merge regions because region %region% is in the process of being rented out!"), - NO_PERMISSION_REGION_TYPE("psregion.no_permission_region_type", ChatColor.RED + "You do not have permission to have this region type."), - REGION_HIDDEN("psregion.hidden", ChatColor.GRAY + "The protection block is now hidden."), - MUST_BE_PLACED_IN_EXISTING_REGION("psregion.must_be_placed_in_existing_region", ChatColor.RED + "This must be placed inside of an existing region!"), - REGION_ALREADY_IN_LOCATION_IS_HIDDEN("psregion.already_in_location_is_hidden", ChatColor.RED + "A region already exists in this location (is the protection block hidden?)"), - CANNOT_REMOVE_YOURSELF_LAST_OWNER("psregion.cannot_remove_yourself_last_owner", ChatColor.RED + "You cannot remove yourself as you are the last owner."), - CANNOT_REMOVE_YOURSELF_FROM_ALL_REGIONS("psregion.cannot_remove_yourself_all_regions", ChatColor.RED + "You cannot remove yourself from all of your regions at once, for safety reasons."), - - // ps toggle - TOGGLE_HELP("toggle.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps toggle|on|off"), - TOGGLE_HELP_DESC("toggle.help_desc", "Use this command to turn on or off placement of protection blocks."), - TOGGLE_ON("toggle.toggle_on", ChatColor.AQUA + "Protection block placement turned on."), - TOGGLE_OFF("toggle.toggle_off", ChatColor.AQUA + "Protection block placement turned off."), - - // ps count - COUNT_HELP("count.count_help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps count [player (optional)]"), - COUNT_HELP_DESC("count.count_help_desc", "Count the number of regions you own or another player."), - PERSONAL_REGION_COUNT("count.personal_region_count", ChatColor.GRAY + "Your region count in this world: " + ChatColor.AQUA + "%num%"), - PERSONAL_REGION_COUNT_MERGED("count.personal_region_count_merged", ChatColor.GRAY + "- Including each merged region: " + ChatColor.AQUA + "%num%"), - OTHER_REGION_COUNT("count.other_region_count", ChatColor.GRAY + "%player%'s region count in this world: " + ChatColor.AQUA + "%num%"), - OTHER_REGION_COUNT_MERGED("count.other_region_count_merged", ChatColor.GRAY + "- Including each merged region: " + ChatColor.AQUA + "%num%"), - - // ps flag - FLAG_HELP("flag.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps flag [flagname] [value|null|default]"), - FLAG_HELP_DESC("flag.help_desc", "Use this command to set a flag in your protected region."), - FLAG_SET("flag.flag_set", ChatColor.AQUA + "%flag%" + ChatColor.GRAY + " flag has been set."), - FLAG_NOT_SET("flag.flag_not_set", ChatColor.AQUA + "%flag%" + ChatColor.GRAY + " flag has " + ChatColor.RED + "not" + ChatColor.GRAY + " been set. Check your values again."), - FLAG_PREVENT_EXPLOIT("flag.flag_prevent_exploit", ChatColor.RED + "This has been disabled to prevent exploits."), - FLAG_PREVENT_EXPLOIT_HOVER("flag.flag_prevent_exploit_hover", ChatColor.RED + "Disabled for security reasons."), - FLAG_GUI_HEADER("flag.gui_header", ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " Flags (click to change) " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), - FLAG_GUI_HOVER_SET("flag.gui_hover_set", ChatColor.AQUA + "Click to set."), - FLAG_GUI_HOVER_SET_TEXT("flag.gui_hover_set_text", ChatColor.AQUA + "Click to change." + ChatColor.WHITE + "\nCurrent value:\n%value%"), - FLAG_GUI_HOVER_CHANGE_GROUP("flag.hover_change_group", "Click to set this flag to apply to only %group%."), - FLAG_GUI_HOVER_CHANGE_GROUP_NULL("flag.hover_change_group_null", ChatColor.RED + "You must set this flag to a value before changing the group."), - - // ps rent - RENT_HELP("rent.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps rent"), - RENT_HELP_DESC("rent.help_desc", "Use this command to manage rents (buying and selling)."), - RENT_HELP_HEADER("rent.help_header", ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " Rent Help " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), - RENT_ALREADY_RENTING("rent.already_renting", ChatColor.RED + "The region is already being rented out! You must stop leasing the region first."), - RENT_NOT_RENTED("rent.not_rented", ChatColor.RED + "This region is not being rented."), - RENT_LEASE_SUCCESS("rent.lease_success", ChatColor.AQUA + "Region leasing terms set:\n" + ChatColor.AQUA + "Price: " + ChatColor.GRAY + "%price%\n" + ChatColor.AQUA + "Payment Term: " + ChatColor.GRAY + "%period%"), - RENT_STOPPED("rent.stopped", ChatColor.AQUA + "Leasing stopped."), - RENT_EVICTED("rent.evicted", ChatColor.GRAY + "Evicted tenant %tenant%."), - RENT_NOT_RENTING("rent.not_renting", ChatColor.RED + "This region is not being rented out to tenants."), - RENT_PAID_LANDLORD("rent.paid_landlord", ChatColor.AQUA + "%tenant%" + ChatColor.GRAY + " has paid " + ChatColor.AQUA + "$%price%" + ChatColor.GRAY + " for renting out " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + "."), - RENT_PAID_TENANT("rent.paid_tenant", ChatColor.GRAY + "Paid " + ChatColor.AQUA + "$%price%" + ChatColor.GRAY + " to " + ChatColor.AQUA + "%landlord%" + ChatColor.GRAY + " for region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + "."), - RENT_RENTING_LANDLORD("rent.renting_landlord", ChatColor.AQUA + "%player%" + ChatColor.GRAY + " is now renting out region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + "."), - RENT_RENTING_TENANT("rent.renting_tenant", ChatColor.GRAY + "You are now renting out region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " for " + ChatColor.AQUA + "%price%" + ChatColor.GRAY + " per " + ChatColor.AQUA + "%period%" + ChatColor.GRAY + "."), - RENT_NOT_TENANT("rent.not_tenant", ChatColor.RED + "You are not the tenant of this region!"), - RENT_TENANT_STOPPED_LANDLORD("rent.tenant_stopped_landlord", ChatColor.AQUA + "%player%" + ChatColor.GRAY + " has stopped renting out region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + ". It is now available for others to rent."), - RENT_TENANT_STOPPED_TENANT("rent.tenant_stopped_tenant", ChatColor.AQUA + "You have stopped renting out region %region%."), - RENT_BEING_SOLD("rent.being_sold", ChatColor.RED + "The region is being sold! Do /ps sell stop first."), - RENT_EVICT_NO_MONEY_TENANT("rent.evict_no_money_tenant", ChatColor.GRAY + "You have been " + ChatColor.RED + "evicted" + ChatColor.GRAY + " from region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " because you do not have enough money (%price%) to pay for rent."), - RENT_EVICT_NO_MONEY_LANDLORD("rent.evict_no_money_landlord", ChatColor.AQUA + "%tenant%" + ChatColor.GRAY + " has been " + ChatColor.RED + "evicted" + ChatColor.GRAY + " from region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " because they are unable to afford rent."), - RENT_CANNOT_RENT_OWN_REGION("rent.cannot_rent_own_region", ChatColor.RED + "You cannot rent your own region!"), - RENT_REACHED_LIMIT("rent.reached_limit", ChatColor.RED + "You've reached the limit of regions you are allowed to rent!"), - RENT_PRICE_TOO_LOW("rent.price_too_low", ChatColor.RED + "The rent price is too low (must be larger than %price%)."), - RENT_PRICE_TOO_HIGH("rent.price_too_high", ChatColor.RED + "The rent price is too high (must be lower than %price%)."), - RENT_PERIOD_TOO_SHORT("rent.period_too_short", ChatColor.RED + "The rent period is too short (must be longer than %period% seconds)."), - RENT_PERIOD_TOO_LONG("rent.period_too_long", ChatColor.RED + "The rent period is too long (must be shorter than %period% seconds)."), - RENT_PERIOD_INVALID("rent.period_invalid", ChatColor.RED + "Invalid period format! Example: 24h for once a day."), - RENT_CANNOT_BREAK_WHILE_RENTING("rent.cannot_break_while_renting", ChatColor.RED + "You cannot break the region when it is being rented out."), - - // ps tax - TAX_HELP("tax.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps tax"), - TAX_HELP_DESC("tax.help_desc", "Use this command to manage and pay taxes."), - TAX_HELP_HEADER("tax.help_header", ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " Taxes Help " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), - TAX_DISABLED_REGION("tax.disabled_region", ChatColor.RED + "Taxes are disabled for this region."), - TAX_SET_AS_AUTOPAYER("tax.set_as_autopayer", ChatColor.GRAY + "Taxes for region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " will now be automatically paid by you."), - TAX_SET_NO_AUTOPAYER("tax.set_no_autopayer", ChatColor.GRAY + "Taxes for region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " now have to be manually paid for."), - TAX_PAID("tax.paid", ChatColor.GRAY + "Paid " + ChatColor.AQUA + "$%amount%" + ChatColor.GRAY + " in taxes for region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + "."), - TAX_INFO_HEADER("tax.info_header", ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " Tax Info (click for more info) " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), - TAX_JOIN_MSG_PENDING_PAYMENTS("tax.join_msg_pending_payments", ChatColor.GRAY + "You have " + ChatColor.AQUA + "$%money%" + ChatColor.GRAY + " in tax payments due on your regions!\nView them with /ps tax info."), - TAX_PLAYER_REGION_INFO("tax.player_region_info", ChatColor.GRAY + "> " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " - " + ChatColor.DARK_AQUA + "$%money% due"), - TAX_PLAYER_REGION_INFO_AUTOPAYER("tax.player_region_info_autopayer", ChatColor.GRAY + "> " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " - " + ChatColor.DARK_AQUA + "$%money% due" + ChatColor.GRAY + " (you autopay)"), - TAX_CLICK_TO_SHOW_MORE_INFO("tax.click_to_show_more_info", "Click to show more information."), - TAX_REGION_INFO_HEADER("tax.region_info_header", ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " %region% Tax Info " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), - TAX_REGION_INFO("tax.region_info", ChatColor.BLUE + "Tax Rate: " + ChatColor.GRAY + "$%taxrate% (sum of all merged regions)" + "\n" - + ChatColor.BLUE + "Time between tax cycles: " + ChatColor.GRAY + "%taxperiod%" + "\n" - + ChatColor.BLUE + "Time to pay taxes after cycle: " + ChatColor.GRAY + "%taxpaymentperiod%" + "\n" - + ChatColor.BLUE + "Tax Autopayer: " + ChatColor.GRAY + "%taxautopayer%" + "\n" - + ChatColor.BLUE + "Taxes Owed: " + ChatColor.GRAY + "$%taxowed%"), - TAX_NEXT("tax.next_page", ChatColor.GRAY + "Do /ps tax info -p %page% to go to the next page!"), - - // ps buy - BUY_HELP("buy.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps buy"), - BUY_HELP_DESC("buy.help_desc", "Buy the region you are currently in."), - BUY_NOT_FOR_SALE("buy.not_for_sale", ChatColor.RED + "This region is not for sale."), - BUY_STOP_SELL("buy.stop_sell", ChatColor.GRAY + "The region is now not for sale."), - BUY_SOLD_BUYER("buy.sold_buyer", ChatColor.GRAY + "Bought region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " for " + ChatColor.AQUA + "$%price%" + ChatColor.GRAY + " from " + ChatColor.AQUA + "%player%" + ChatColor.GRAY + "."), - BUY_SOLD_SELLER("buy.sold_seller", ChatColor.GRAY + "Sold region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " for " + ChatColor.AQUA + "$%price%" + ChatColor.GRAY + " to " + ChatColor.AQUA + "%player%" + ChatColor.GRAY + "."), - - // ps sell - SELL_HELP("sell.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps sell [price|stop]"), - SELL_HELP_DESC("sell.help_desc", "Sell the region you are currently in."), - SELL_RENTED_OUT("sell.rented_out", ChatColor.RED + "The region is being rented out! You must stop renting it out to sell."), - SELL_FOR_SALE("sell.for_sale", ChatColor.GRAY + "The region is now for sale for " + ChatColor.AQUA + "$%price%" + ChatColor.GRAY + "."), - - // ps hide/unhide - VISIBILITY_HIDE_HELP("visibility.hide_help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps hide"), - VISIBILITY_HIDE_HELP_DESC("visibility.hide_help_desc", "Use this command to hide or unhide your protection block."), - VISIBILITY_UNHIDE_HELP("visibility.unhide_help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps unhide"), - VISIBILITY_UNHIDE_HELP_DESC("visibility.unhide_help_desc", "Use this command to hide or unhide your protection block."), - ALREADY_NOT_HIDDEN("visibility.already_not_hidden", ChatColor.GRAY + "The protection stone doesn't appear hidden..."), - ALREADY_HIDDEN("visibility.already_hidden", ChatColor.GRAY + "The protection stone appears to already be hidden..."), - - // ps info - INFO_HELP("info.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps info members|owners|flags"), - INFO_HELP_DESC("info.help_desc", "Use this command inside a ps region to see more information about it."), - INFO_HEADER("info.header", ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " PS Info " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), - INFO_TYPE2("info.type2", "&9Type: &7%type%", "%type%"), - INFO_MAY_BE_MERGED("info.may_be_merged", "(may be merged with other types)"), - INFO_MERGED2("info.merged2", ChatColor.BLUE + "Merged regions: " + ChatColor.GRAY + "%merged%", "%merged%"), - INFO_MEMBERS2("info.members2", "&9Members: &7%members%", "%members%"), - INFO_NO_MEMBERS("info.no_members", ChatColor.RED + "(no members)"), - INFO_OWNERS2("info.owners2", "&9Owners: &7%owners%", "%owners%"), - INFO_NO_OWNERS("info.no_owners", ChatColor.RED + "(no owners)"), - INFO_FLAGS2("info.flags2", "&9Flags: &7%flags%", "%flags%"), - INFO_NO_FLAGS("info.no_flags", "(none)"), - INFO_REGION2("info.region2", "&9Region: &b%region%", "%region%"), - INFO_PRIORITY2("info.priority2", "&9Priority: &b%priority%", "%priority%"), - INFO_PARENT2("info.parent2", "&9Parent: &b%parentregion%", "%parentregion%"), - INFO_BOUNDS_XYZ("info.bounds_xyz", "&9Bounds: &b(%minx%,%miny%,%minz%) -> (%maxx%,%maxy%,%maxz%)", - "%minx%", "%miny%", "%minz%", "%maxx%", "%maxy%", "%maxz%" - ), - INFO_BOUNDS_XZ("info.bounds_xz", "&9Bounds: &b(%minx%, %minz%) -> (%maxx%, %maxz%)", - "%minx%", "%minz%", "%maxx%", "%maxz%" - ), - INFO_SELLER2("info.seller2", "&9Seller: &7%seller%", "%seller%"), - INFO_PRICE2("info.price2", "&9Price: &7%price%", "%price%"), - INFO_TENANT2("info.tenant2", "&9Tenant: &7%tenant%", "%tenant%"), - INFO_LANDLORD2("info.landlord2", "&9Landlord: &7%landlord%", "%landlord%"), - INFO_RENT2("info.rent2", "&9Rent: &7%rent%", "%rent%"), - INFO_AVAILABLE_FOR_SALE("info.available_for_sale", ChatColor.AQUA + "Region available for sale!"), - INFO_AVAILABLE_FOR_RENT("info.available_for_rent", ChatColor.AQUA + "Region available for rent!"), - - // ps priority - PRIORITY_HELP("priority.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps priority [number|null]"), - PRIORITY_HELP_DESC("priority.help_desc", "Use this command to set your region's priority."), - PRIORITY_INFO("priority.info", ChatColor.GRAY + "Priority: %priority%"), - PRIORITY_SET("priority.set", ChatColor.YELLOW + "Priority has been set."), - PRIORITY_ERROR("priority.error", ChatColor.RED + "Error parsing input, check it again?"), - - // ps region - REGION_HELP("region.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps region [list|remove|disown] [playername]"), - REGION_HELP_DESC("region.help_desc", "Use this command to find information or edit other players' (or your own) protected regions."), - REGION_NOT_FOUND_FOR_PLAYER("region.not_found_for_player", ChatColor.GRAY + "No regions found for %player% in this world."), - REGION_LIST("region.list", ChatColor.GRAY + "%player%'s regions in this world: " + ChatColor.AQUA + "%regions%"), - REGION_REMOVE("region.remove", ChatColor.YELLOW + "%player%'s regions have been removed in this world, and they have been removed from regions they co-owned."), - REGION_DISOWN("region.disown", ChatColor.YELLOW + "%player% has been removed as owner from all regions on this world."), - REGION_ERROR_SEARCH("region.error_search", ChatColor.RED + "Error while searching for %player%'s regions. Please make sure you have entered the correct name."), - - // ps tp - TP_HELP("tp.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps tp [id/player] [num (optional)]"), - TP_HELP_DESC("tp.help_desc", "Teleports you to one of a given player's regions."), - NUMBER_ABOVE_ZERO("tp.number_above_zero", ChatColor.RED + "Please enter a number above 0."), - TP_VALID_NUMBER("tp.valid_number", ChatColor.RED + "Please enter a valid number."), - ONLY_HAS_REGIONS("tp.only_has_regions", ChatColor.RED + "%player% only has %num% protected regions in this world!"), - TPING("tp.tping", ChatColor.GREEN + "Teleporting..."), - TP_ERROR_NAME("tp.error_name", ChatColor.RED + "Error in teleporting to protected region! (parsing WG region name error)"), - TP_ERROR_TP("tp.error_tp", ChatColor.RED + "Error in finding the region to teleport to!"), - TP_IN_SECONDS("tp.in_seconds", ChatColor.GRAY + "Teleporting in " + ChatColor.AQUA + "%seconds%" + ChatColor.GRAY + " seconds."), - TP_CANCELLED_MOVED("tp.cancelled_moved", ChatColor.RED + "Teleport cancelled. You moved!"), - - // ps home - HOME_HELP("home.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps home [name/id]"), - HOME_HELP_DESC("home.help_desc", "Teleports you to one of your protected regions."), - HOME_HEADER("home.header", ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " Homes (click to teleport) " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), - HOME_CLICK_TO_TP("home.click_to_tp", "Click to teleport!"), - HOME_NEXT("home.next_page", ChatColor.GRAY + "Do /ps home -p %page% to go to the next page!"), - - // ps unclaim - UNCLAIM_HELP("unclaim.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps unclaim"), - UNCLAIM_HELP_DESC("unclaim.help_desc", "Use this command to pickup a placed protection stone and remove the region."), - UNCLAIM_HEADER("unclaim.header",ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " Unclaim (click to unclaim) " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), - - // ps view - VIEW_HELP("view.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps view"), - VIEW_HELP_DESC("view.help_desc", "Use this command to view the borders of a protected region."), - VIEW_COOLDOWN("view.cooldown", ChatColor.RED + "Please wait a while before using /ps view again."), - VIEW_GENERATING("view.generating", ChatColor.GRAY + "Generating border..."), - VIEW_GENERATE_DONE("view.generate_done", ChatColor.GREEN + "Done! The border will disappear after 30 seconds!"), - VIEW_REMOVING("view.removing", ChatColor.AQUA + "Removing border...\n" + ChatColor.GREEN + "If you still see ghost blocks, relog!"), - - // ps admin - ADMIN_HELP("admin.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps admin"), - ADMIN_HELP_DESC("admin.help_desc", "Do /ps admin help for more information."), - ADMIN_CLEANUP_HEADER("admin.cleanup_header", ChatColor.YELLOW + "Cleanup %arg% %days% days\n================"), - ADMIN_CLEANUP_FOOTER("admin.cleanup_footer", ChatColor.YELLOW + "================\nCompleted %arg% cleanup."), - ADMIN_HIDE_TOGGLED("admin.hide_toggled", ChatColor.YELLOW + "All protection stones have been %message% in this world."), - ADMIN_LAST_LOGON("admin.last_logon", ChatColor.YELLOW + "%player% last played %days% days ago."), - ADMIN_IS_BANNED("admin.is_banned", ChatColor.YELLOW + "%player% is banned."), - ADMIN_ERROR_PARSING("admin.error_parsing", ChatColor.RED + "Error parsing days, are you sure it is a number?"), - ADMIN_CONSOLE_WORLD("admin.console_world", ChatColor.RED + "Please specify the world as the last parameter."), - ADMIN_LASTLOGONS_HEADER("admin.lastlogons_header", ChatColor.YELLOW + "%days% Days Plus:\n================"), - ADMIN_LASTLOGONS_LINE("admin.lastlogons_line", ChatColor.YELLOW + "%player% %time% days"), - ADMIN_LASTLOGONS_FOOTER("admin.lastlogons_footer", ChatColor.YELLOW + "================\n%count% Total Players Shown\n%checked% Total Players Checked"), - - // ps reload - RELOAD_HELP("reload.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps reload"), - RELOAD_HELP_DESC("reload.help_desc", "Reload settings from the config."), - RELOAD_START("reload.start", ChatColor.AQUA + "Reloading config..."), - RELOAD_COMPLETE("reload.complete", ChatColor.AQUA + "Completed config reload!"), - - // ps add/remove - ADDREMOVE_HELP("addremove.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps add|remove [playername]"), - ADDREMOVE_HELP_DESC("addremove.help_desc", "Use this command to add or remove a member of your protected region."), - ADDREMOVE_OWNER_HELP("addremove.owner_help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps addowner|removeowner [playername]"), - ADDREMOVE_OWNER_HELP_DESC("addremove.owner_help_desc", "Use this command to add or remove an owner of your protected region."), - ADDREMOVE_PLAYER_REACHED_LIMIT("addremove.player_reached_limit", ChatColor.RED + "This player has reached their region limit."), - ADDREMOVE_PLAYER_NEEDS_TO_BE_ONLINE("addremove.player_needs_to_be_online", ChatColor.RED + "The player needs to be online to add them."), - - // ps get - GET_HELP("get.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps get [block]"), - GET_HELP_DESC("get.help_desc", "Use this command to get or purchase a protection block."), - GET_GOTTEN("get.gotten", ChatColor.AQUA + "Added protection block to inventory!"), - GET_NO_PERMISSION_BLOCK("get.no_permission_block", ChatColor.RED + "You don't have permission to get this block."), - GET_HEADER("get.header", ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " Protect Blocks (click to get) " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), - GET_GUI_BLOCK("get.gui_block", ChatColor.GRAY + "> " + ChatColor.AQUA + "%alias% " + ChatColor.GRAY + "- %description% (" + ChatColor.WHITE + "$%price%" + ChatColor.GRAY + ")"), - GET_GUI_HOVER("get.gui_hover", "Click to buy a %alias%!"), - - // ps give - GIVE_HELP("give.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps give [block] [player] [amount (optional)]"), - GIVE_HELP_DESC("give.help_desc", "Use this command to give a player a protection block."), - GIVE_GIVEN("give.given", ChatColor.GRAY + "Gave " + ChatColor.AQUA + "%block%" + ChatColor.GRAY + " to " + ChatColor.AQUA + "%player%" + ChatColor.GRAY + "."), - GIVE_NO_INVENTORY_ROOM("give.no_inventory_room", ChatColor.RED + "The player does not have enough inventory room."), - - // ps sethome - SETHOME_HELP("sethome.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps sethome"), - SETHOME_HELP_DESC("sethome.help_desc", "Use this command to set the home of a region to where you are right now."), - SETHOME_SET("sethome.set", ChatColor.GRAY + "The home for " + ChatColor.AQUA + "%psid%" + ChatColor.GRAY + " has been set to your location."), - - // ps list - LIST_HELP("list.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps list [player (optional)]"), - LIST_HELP_DESC("list.help_desc", "Use this command to list the regions you, or another player owns."), - LIST_HEADER("list.header", ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " %player%'s Regions " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), - LIST_OWNER("list.owner", ChatColor.GRAY + "Owner of:"), - LIST_MEMBER("list.member", ChatColor.GRAY + "Member of:"), - LIST_NO_REGIONS("list.no_regions", ChatColor.GRAY + "You currently do not own and are not a member of any regions."), - LIST_NO_REGIONS_PLAYER("list.no_regions_player", ChatColor.AQUA + "%player% " + ChatColor.GRAY + "does not own and is not a member of any regions."), - - // ps name - NAME_HELP("name.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps name [name|none]"), - NAME_HELP_DESC("name.help_desc", "Use this command to give a nickname to your region, to make identifying your region easier."), - NAME_REMOVED("name.removed", ChatColor.GRAY + "Removed the name for %id%."), - NAME_SET_NAME("name.set_name", ChatColor.GRAY + "Set the name of %id% to " + ChatColor.AQUA + "%name%" + ChatColor.GRAY + "."), - NAME_TAKEN("name.taken", ChatColor.GRAY + "The region name " + ChatColor.AQUA + "%name%" + ChatColor.GRAY + " has already been taken! Try another one."), - - // ps setparent - SETPARENT_HELP("setparent.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps setparent [region|none]"), - SETPARENT_HELP_DESC("setparent.help_desc", "Use this command to allow this region to inherit properties from another region (owners, members, flags, etc.)."), - SETPARENT_SUCCESS("setparent.success", ChatColor.GRAY + "Successfully set the parent of " + ChatColor.AQUA + "%id%" + ChatColor.GRAY + " to " + ChatColor.AQUA + "%parent%" + ChatColor.GRAY + "."), - SETPARENT_SUCCESS_REMOVE("setparent.success_remove", ChatColor.GRAY + "Successfully removed the parent of " + ChatColor.AQUA + "%id%" + ChatColor.GRAY + "."), - SETPARENT_CIRCULAR_INHERITANCE("setparent.circular_inheritance", ChatColor.RED + "Detected circular inheritance (the parent already inherits from this region?). Parent not set."), - - // ps merge - MERGE_HELP("merge.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps merge"), - MERGE_HELP_DESC("merge.help_desc", "Use this command to merge the region you are in with other overlapping regions."), - MERGE_DISABLED("merge.disabled", "Merging regions is disabled in the config!"), - MERGE_MERGED("merge.merged", ChatColor.AQUA + "Regions were successfully merged!"), - MERGE_HEADER("merge.header", ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " Merge %region% (click to merge) " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), - MERGE_WARNING("merge.warning", ChatColor.GRAY + "Note: This will delete all of the settings for the current region!"), - MERGE_NOT_ALLOWED("merge.not_allowed", ChatColor.RED + "You are not allowed to merge this protection region type."), - MERGE_INTO("merge.into", ChatColor.AQUA + "This region overlaps other regions you can merge into!"), - MERGE_NO_REGIONS("merge.no_region", ChatColor.GRAY + "There are no overlapping regions to merge into."), - MERGE_CLICK_TO_MERGE("merge.click_to_merge", "Click to merge with %region%!"), - MERGE_AUTO_MERGED("merge.auto_merged", ChatColor.GRAY + "Region automatically merged with " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + "."), - - ; - - private final String path; - private final String defaultMessage; - - private final String[] placeholders; - private final int placeholdersCount; - private String message; - private boolean isEmpty; - - private static final File conf = new File(ProtectionStones.getInstance().getDataFolder(), "messages.yml"); - - PSL(String path, String defaultMessage, String... placeholders) { - this.path = path; - this.defaultMessage = defaultMessage; - - this.placeholders = placeholders; - this.placeholdersCount = placeholders.length; - this.message = defaultMessage; - this.isEmpty = message.isEmpty(); - } - - public String msg() { - return message; - } - - public boolean isEmpty() { - return isEmpty; - } - - @Nullable - public String format(final Object... args) { - if (isEmpty) { - return null; + // messages.yml + + COOLDOWN("cooldown", + ChatColor.GOLD + "Warning: " + ChatColor.GRAY + + "Please wait for %time% seconds before placing again!"), + NO_SUCH_COMMAND("no_such_command", ChatColor.RED + "No such command. please type /ps help for more info"), + NO_ACCESS("no_access", ChatColor.RED + "You are not allowed to do that here."), + NO_ROOM_IN_INVENTORY("no_room_in_inventory", ChatColor.RED + "You don't have enough room in your inventory."), + NO_ROOM_DROPPING_ON_FLOOR("no_room_dropping_on_floor", + ChatColor.RED + "You don't have enough room in your inventory. Dropping item on floor."), + INVALID_BLOCK("invalid_block", ChatColor.RED + "Invalid protection block."), + NOT_ENOUGH_MONEY("not_enough_money", ChatColor.RED + "You don't have enough money! The price is %price%."), + PAID_MONEY("paid_money", ChatColor.AQUA + "You've paid $%price%."), + INVALID_WORLD("invalid_world", ChatColor.RED + "Invalid world."), + MUST_BE_PLAYER("must_be_player", ChatColor.RED + "You must be a player to execute this command."), + GO_BACK_PAGE("go_back_page", "Go back a page."), + GO_NEXT_PAGE("go_next_page", "Go to next page."), + PAGE_DOES_NOT_EXIST("page_does_not_exist", ChatColor.RED + "Page does not exist."), + + HELP("help", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " PS Help " + + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "=====\n" + ChatColor.AQUA + + "> " + ChatColor.GRAY + + "/ps help"), + HELP_NEXT("help_next", ChatColor.GRAY + "Do /ps help %page% to go to the next page!"), + + COMMAND_REQUIRES_PLAYER_NAME("command_requires_player_name", + ChatColor.RED + "This command requires a player name."), + + NO_PERMISSION_TOGGLE("no_permission_toggle", + ChatColor.RED + "You don't have permission to use the toggle command."), + NO_PERMISSION_CREATE("no_permission_create", + ChatColor.RED + "You don't have permission to place a protection block."), + NO_PERMISSION_CREATE_SPECIFIC("no_permission_create_specific", + ChatColor.RED + "You don't have permission to place this protection block type."), + NO_PERMISSION_DESTROY("no_permission_destroy", + ChatColor.RED + "You don't have permission to destroy a protection block."), + NO_PERMISSION_MEMBERS("no_permission_members", "&cYou don't have permission to use member commands."), + NO_PERMISSION_OWNERS("no_permission_owners", "&cYou don't have permission to use owner commands."), + NO_PERMISSION_ADMIN("no_permission_admin", ChatColor.RED + "You do not have permission to use that command."), + NO_PERMISSION_COUNT("no_permission_count", ChatColor.RED + "You do not have permission to use that command."), + NO_PERMISSION_COUNT_OTHERS("no_permission_count_others", + ChatColor.RED + "You do not have permission to use that command."), + NO_PERMISSION_FLAGS("no_permission_flags", "&cYou do not have permission to use flag commands."), + NO_PERMISSION_PER_FLAG("no_permission_per_flag", + ChatColor.RED + "You do not have permission to use that flag."), + NO_PERMISSION_RENT("no_permission_rent", ChatColor.RED + "You do not have permission for renting."), + NO_PERMISSION_TAX("no_permission_tax", ChatColor.RED + "You do not have permission to use the tax command."), + NO_PERMISSION_BUYSELL("no_permission_buysell", + ChatColor.RED + "You do not have permission to buy and sell regions."), + NO_PERMISSION_UNHIDE("no_permission_unhide", + ChatColor.RED + "You do not have permission to unhide protection blocks."), + NO_PERMISSION_HIDE("no_permission_hide", + ChatColor.RED + "You do not have permission to hide protection blocks."), + NO_PERMISSION_INFO("no_permission_info", + ChatColor.RED + "You do not have permission to use the region info command."), + NO_PERMISSION_PRIORITY("no_permission_priority", + ChatColor.RED + "You do not have permission to use the priority command."), + NO_PERMISSION_REGION("no_permission_region", + ChatColor.RED + "You do not have permission to use region commands."), + NO_PERMISSION_TP("no_permission_tp", + ChatColor.RED + "You do not have permission to teleport to other players' protection blocks."), + NO_PERMISSION_HOME("no_permission_home", + ChatColor.RED + "You do not have permission to teleport to your protection blocks."), + NO_PERMISSION_UNCLAIM("no_permission_unclaim", + ChatColor.RED + "You do not have permission to use the unclaim command."), + NO_PERMISSION_UNCLAIM_REMOTE("no_permission_unclaim_remote", + ChatColor.RED + "You do not have permission to use the unclaim remote command."), + NO_PERMISSION_VIEW("no_permission_view", ChatColor.RED + "You do not have permission to use the view command."), + NO_PERMISSION_GIVE("no_permission_give", ChatColor.RED + "You do not have permission to use the give command."), + NO_PERMISSION_GET("no_permission_get", ChatColor.RED + "You do not have permission to use the get command."), + NO_PERMISSION_SETHOME("no_permission_sethome", + ChatColor.RED + "You do not have permission to use the sethome command."), + NO_PERMISSION_LIST("no_permission_list", ChatColor.RED + "You do not have permission to use the list command."), + NO_PERMISSION_LIST_OTHERS("no_permission_list_others", + ChatColor.RED + "You do not have permission to use the list command for others."), + NO_PERMISSION_NAME("no_permission_name", ChatColor.RED + "You do not have permission to use the name command."), + NO_PERMISSION_SETPARENT("no_permission_setparent", + ChatColor.RED + "You do not have permission to use the setparent command."), + NO_PERMISSION_SETPARENT_OTHERS("no_permission_setparent_others", + ChatColor.RED + "You do not have permission to inherit from regions you don't own."), + NO_PERMISSION_MERGE("no_permission_merge", ChatColor.RED + "You do not have permission to use /ps merge."), + + ADDED_TO_REGION("psregion.added_to_region", + ChatColor.AQUA + "%player%" + ChatColor.GRAY + " has been added to this region."), + ADDED_TO_REGION_SPECIFIC("psregion.added_to_region_specific", + ChatColor.AQUA + "%player%" + ChatColor.GRAY + " has been added to region %region%."), + REMOVED_FROM_REGION("psregion.removed_from_region", + ChatColor.AQUA + "%player%" + ChatColor.GRAY + " has been removed from region."), + REMOVED_FROM_REGION_SPECIFIC("psregion.removed_from_region_specific", + ChatColor.AQUA + "%player%" + ChatColor.GRAY + " has been removed from region %region%."), + NOT_IN_REGION("psregion.not_in_region", ChatColor.RED + "You are not in a protection stones region!"), + PLAYER_NOT_FOUND("psregion.player_not_found", ChatColor.RED + "Player not found."), + NOT_PS_REGION("psregion.not_ps_region", ChatColor.RED + "Not a protection stones region."), + REGION_DOES_NOT_EXIST("psregion.region_does_not_exist", ChatColor.RED + "Region does not exist."), + NO_REGIONS_OWNED("psregion.no_regions_owned", + ChatColor.RED + "You don't own any protected regions in this world!"), + NO_REGION_PERMISSION("psregion.no_region_permission", + ChatColor.RED + "You do not have permission to do this in this region."), + PROTECTED("psregion.protected", ChatColor.AQUA + "This area is now protected."), + NO_LONGER_PROTECTED("psregion.no_longer_protected", ChatColor.YELLOW + "This area is no longer protected."), + CANT_PROTECT_THAT("psregion.cant_protect_that", ChatColor.RED + "You can't protect that area."), + REACHED_REGION_LIMIT("psregion.reached_region_limit", + ChatColor.RED + "You can not have any more protected regions (%limit%)."), + REACHED_PER_BLOCK_REGION_LIMIT("psregion.reached_per_block_region_limit", + ChatColor.RED + "You can not have any more regions of this type (%limit%)."), + WORLD_DENIED_CREATE("psregion.world_denied_create", + ChatColor.RED + "You can not create protections in this world."), + REGION_OVERLAP("psregion.region_overlap", + ChatColor.RED + "You can not place a protection block here as it overlaps another region."), + REGION_TOO_CLOSE("psregion.region_too_close", + ChatColor.RED + "Your protection block must be a minimum of %num% blocks from the edge of other regions!"), + REGION_CANT_TELEPORT("psregion.cant_teleport", + ChatColor.RED + "Your teleportation was blocked by a protection region!"), + SPECIFY_ID_INSTEAD_OF_ALIAS("psregion.specify_id_instead_of_alias", ChatColor.GRAY + + "There were multiple regions found with this name! Please use an ID instead.\n Regions with this name: " + + ChatColor.AQUA + "%regions%"), + REGION_NOT_ADJACENT("psregion.region_not_adjacent", ChatColor.RED + + "You've passed the limit of non-adjacent regions! Try putting your protection block closer to other regions you already own."), + REGION_NOT_OVERLAPPING("psregion.not_overlapping", ChatColor.RED + "These regions don't overlap each other!"), + MULTI_REGION_DOES_NOT_EXIST("psregion.multi_region_does_not_exist", "One of these regions don't exist!"), + NO_REGION_HOLES("psregion.no_region_holes", + ChatColor.RED + "Unprotected area detected inside region! This is not allowed!"), + DELETE_REGION_PREVENTED_NO_HOLES("psregion.delete_region_prevented", + ChatColor.GRAY + + "The region could not be removed, possibly because it creates a hole in the existing region."), + NOT_OWNER("psregion.not_owner", ChatColor.RED + "You are not an owner of this region!"), + CANNOT_MERGE_RENTED_REGION("psregion.cannot_merge_rented_region", + ChatColor.RED + "Cannot merge regions because region %region% is in the process of being rented out!"), + NO_PERMISSION_REGION_TYPE("psregion.no_permission_region_type", + ChatColor.RED + "You do not have permission to have this region type."), + REGION_HIDDEN("psregion.hidden", ChatColor.GRAY + "The protection block is now hidden."), + MUST_BE_PLACED_IN_EXISTING_REGION("psregion.must_be_placed_in_existing_region", + ChatColor.RED + "This must be placed inside of an existing region!"), + REGION_ALREADY_IN_LOCATION_IS_HIDDEN("psregion.already_in_location_is_hidden", + ChatColor.RED + "A region already exists in this location (is the protection block hidden?)"), + CANNOT_REMOVE_YOURSELF_LAST_OWNER("psregion.cannot_remove_yourself_last_owner", + ChatColor.RED + "You cannot remove yourself as you are the last owner."), + CANNOT_REMOVE_YOURSELF_FROM_ALL_REGIONS("psregion.cannot_remove_yourself_all_regions", + ChatColor.RED + "You cannot remove yourself from all of your regions at once, for safety reasons."), + + // ps toggle + TOGGLE_HELP("toggle.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps toggle|on|off"), + TOGGLE_HELP_DESC("toggle.help_desc", "Use this command to turn on or off placement of protection blocks."), + TOGGLE_ON("toggle.toggle_on", ChatColor.AQUA + "Protection block placement turned on."), + TOGGLE_OFF("toggle.toggle_off", ChatColor.AQUA + "Protection block placement turned off."), + + // ps count + COUNT_HELP("count.count_help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps count [player (optional)]"), + COUNT_HELP_DESC("count.count_help_desc", "Count the number of regions you own or another player."), + PERSONAL_REGION_COUNT("count.personal_region_count", + ChatColor.GRAY + "Your region count in this world: " + ChatColor.AQUA + "%num%"), + PERSONAL_REGION_COUNT_MERGED("count.personal_region_count_merged", + ChatColor.GRAY + "- Including each merged region: " + ChatColor.AQUA + "%num%"), + OTHER_REGION_COUNT("count.other_region_count", + ChatColor.GRAY + "%player%'s region count in this world: " + ChatColor.AQUA + "%num%"), + OTHER_REGION_COUNT_MERGED("count.other_region_count_merged", + ChatColor.GRAY + "- Including each merged region: " + ChatColor.AQUA + "%num%"), + + // ps flag + FLAG_HELP("flag.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps flag [flagname] [value|null|default]"), + FLAG_HELP_DESC("flag.help_desc", "Use this command to set a flag in your protected region."), + FLAG_SET("flag.flag_set", ChatColor.AQUA + "%flag%" + ChatColor.GRAY + " flag has been set."), + FLAG_NOT_SET("flag.flag_not_set", + ChatColor.AQUA + "%flag%" + ChatColor.GRAY + " flag has " + ChatColor.RED + "not" + + ChatColor.GRAY + + " been set. Check your values again."), + FLAG_PREVENT_EXPLOIT("flag.flag_prevent_exploit", + ChatColor.RED + "This has been disabled to prevent exploits."), + FLAG_PREVENT_EXPLOIT_HOVER("flag.flag_prevent_exploit_hover", ChatColor.RED + "Disabled for security reasons."), + FLAG_GUI_HEADER("flag.gui_header", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + + " Flags (click to change) " + + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), + FLAG_GUI_HOVER_SET("flag.gui_hover_set", ChatColor.AQUA + "Click to set."), + FLAG_GUI_HOVER_SET_TEXT("flag.gui_hover_set_text", + ChatColor.AQUA + "Click to change." + ChatColor.WHITE + "\nCurrent value:\n%value%"), + FLAG_GUI_HOVER_CHANGE_GROUP("flag.hover_change_group", "Click to set this flag to apply to only %group%."), + FLAG_GUI_HOVER_CHANGE_GROUP_NULL("flag.hover_change_group_null", + ChatColor.RED + "You must set this flag to a value before changing the group."), + + // ps rent + RENT_HELP("rent.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps rent"), + RENT_HELP_DESC("rent.help_desc", "Use this command to manage rents (buying and selling)."), + RENT_HELP_HEADER("rent.help_header", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " Rent Help " + + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), + RENT_ALREADY_RENTING("rent.already_renting", + ChatColor.RED + "The region is already being rented out! You must stop leasing the region first."), + RENT_NOT_RENTED("rent.not_rented", ChatColor.RED + "This region is not being rented."), + RENT_LEASE_SUCCESS("rent.lease_success", + ChatColor.AQUA + "Region leasing terms set:\n" + ChatColor.AQUA + "Price: " + ChatColor.GRAY + + "%price%\n" + + ChatColor.AQUA + "Payment Term: " + ChatColor.GRAY + "%period%"), + RENT_STOPPED("rent.stopped", ChatColor.AQUA + "Leasing stopped."), + RENT_EVICTED("rent.evicted", ChatColor.GRAY + "Evicted tenant %tenant%."), + RENT_NOT_RENTING("rent.not_renting", ChatColor.RED + "This region is not being rented out to tenants."), + RENT_PAID_LANDLORD("rent.paid_landlord", + ChatColor.AQUA + "%tenant%" + ChatColor.GRAY + " has paid " + ChatColor.AQUA + "$%price%" + + ChatColor.GRAY + + " for renting out " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + "."), + RENT_PAID_TENANT("rent.paid_tenant", + ChatColor.GRAY + "Paid " + ChatColor.AQUA + "$%price%" + ChatColor.GRAY + " to " + + ChatColor.AQUA + + "%landlord%" + ChatColor.GRAY + " for region " + ChatColor.AQUA + "%region%" + + ChatColor.GRAY + + "."), + RENT_RENTING_LANDLORD("rent.renting_landlord", + ChatColor.AQUA + "%player%" + ChatColor.GRAY + " is now renting out region " + ChatColor.AQUA + + "%region%" + + ChatColor.GRAY + "."), + RENT_RENTING_TENANT("rent.renting_tenant", + ChatColor.GRAY + "You are now renting out region " + ChatColor.AQUA + "%region%" + + ChatColor.GRAY + " for " + + ChatColor.AQUA + "%price%" + ChatColor.GRAY + " per " + ChatColor.AQUA + + "%period%" + + ChatColor.GRAY + "."), + RENT_NOT_TENANT("rent.not_tenant", ChatColor.RED + "You are not the tenant of this region!"), + RENT_TENANT_STOPPED_LANDLORD("rent.tenant_stopped_landlord", + ChatColor.AQUA + "%player%" + ChatColor.GRAY + " has stopped renting out region " + + ChatColor.AQUA + + "%region%" + ChatColor.GRAY + ". It is now available for others to rent."), + RENT_TENANT_STOPPED_TENANT("rent.tenant_stopped_tenant", + ChatColor.AQUA + "You have stopped renting out region %region%."), + RENT_BEING_SOLD("rent.being_sold", ChatColor.RED + "The region is being sold! Do /ps sell stop first."), + RENT_EVICT_NO_MONEY_TENANT("rent.evict_no_money_tenant", + ChatColor.GRAY + "You have been " + ChatColor.RED + "evicted" + ChatColor.GRAY + " from region " + + ChatColor.AQUA + "%region%" + ChatColor.GRAY + + " because you do not have enough money (%price%) to pay for rent."), + RENT_EVICT_NO_MONEY_LANDLORD("rent.evict_no_money_landlord", + ChatColor.AQUA + "%tenant%" + ChatColor.GRAY + " has been " + ChatColor.RED + "evicted" + + ChatColor.GRAY + + " from region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + + " because they are unable to afford rent."), + RENT_CANNOT_RENT_OWN_REGION("rent.cannot_rent_own_region", ChatColor.RED + "You cannot rent your own region!"), + RENT_REACHED_LIMIT("rent.reached_limit", + ChatColor.RED + "You've reached the limit of regions you are allowed to rent!"), + RENT_PRICE_TOO_LOW("rent.price_too_low", + ChatColor.RED + "The rent price is too low (must be larger than %price%)."), + RENT_PRICE_TOO_HIGH("rent.price_too_high", + ChatColor.RED + "The rent price is too high (must be lower than %price%)."), + RENT_PERIOD_TOO_SHORT("rent.period_too_short", + ChatColor.RED + "The rent period is too short (must be longer than %period% seconds)."), + RENT_PERIOD_TOO_LONG("rent.period_too_long", + ChatColor.RED + "The rent period is too long (must be shorter than %period% seconds)."), + RENT_PERIOD_INVALID("rent.period_invalid", + ChatColor.RED + "Invalid period format! Example: 24h for once a day."), + RENT_CANNOT_BREAK_WHILE_RENTING("rent.cannot_break_while_renting", + ChatColor.RED + "You cannot break the region when it is being rented out."), + + // ps tax + TAX_HELP("tax.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps tax"), + TAX_HELP_DESC("tax.help_desc", "Use this command to manage and pay taxes."), + TAX_HELP_HEADER("tax.help_header", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " Taxes Help " + + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), + TAX_DISABLED_REGION("tax.disabled_region", ChatColor.RED + "Taxes are disabled for this region."), + TAX_SET_AS_AUTOPAYER("tax.set_as_autopayer", + ChatColor.GRAY + "Taxes for region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + + " will now be automatically paid by you."), + TAX_SET_NO_AUTOPAYER("tax.set_no_autopayer", + ChatColor.GRAY + "Taxes for region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + + " now have to be manually paid for."), + TAX_PAID("tax.paid", + ChatColor.GRAY + "Paid " + ChatColor.AQUA + "$%amount%" + ChatColor.GRAY + + " in taxes for region " + + ChatColor.AQUA + "%region%" + ChatColor.GRAY + "."), + TAX_INFO_HEADER("tax.info_header", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + + " Tax Info (click for more info) " + ChatColor.DARK_GRAY + + ChatColor.STRIKETHROUGH + "====="), + TAX_JOIN_MSG_PENDING_PAYMENTS("tax.join_msg_pending_payments", + ChatColor.GRAY + "You have " + ChatColor.AQUA + "$%money%" + ChatColor.GRAY + + " in tax payments due on your regions!\nView them with /ps tax info."), + TAX_PLAYER_REGION_INFO("tax.player_region_info", + ChatColor.GRAY + "> " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " - " + + ChatColor.DARK_AQUA + + "$%money% due"), + TAX_PLAYER_REGION_INFO_AUTOPAYER("tax.player_region_info_autopayer", + ChatColor.GRAY + "> " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " - " + + ChatColor.DARK_AQUA + + "$%money% due" + ChatColor.GRAY + " (you autopay)"), + TAX_CLICK_TO_SHOW_MORE_INFO("tax.click_to_show_more_info", "Click to show more information."), + TAX_REGION_INFO_HEADER("tax.region_info_header", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + + " %region% Tax Info " + + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), + TAX_REGION_INFO("tax.region_info", + ChatColor.BLUE + "Tax Rate: " + ChatColor.GRAY + "$%taxrate% (sum of all merged regions)" + "\n" + + ChatColor.BLUE + "Time between tax cycles: " + ChatColor.GRAY + "%taxperiod%" + + "\n" + + ChatColor.BLUE + "Time to pay taxes after cycle: " + ChatColor.GRAY + + "%taxpaymentperiod%" + "\n" + + ChatColor.BLUE + "Tax Autopayer: " + ChatColor.GRAY + "%taxautopayer%" + "\n" + + ChatColor.BLUE + "Taxes Owed: " + ChatColor.GRAY + "$%taxowed%"), + TAX_NEXT("tax.next_page", ChatColor.GRAY + "Do /ps tax info -p %page% to go to the next page!"), + + // ps buy + BUY_HELP("buy.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps buy"), + BUY_HELP_DESC("buy.help_desc", "Buy the region you are currently in."), + BUY_NOT_FOR_SALE("buy.not_for_sale", ChatColor.RED + "This region is not for sale."), + BUY_STOP_SELL("buy.stop_sell", ChatColor.GRAY + "The region is now not for sale."), + BUY_SOLD_BUYER("buy.sold_buyer", + ChatColor.GRAY + "Bought region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " for " + + ChatColor.AQUA + + "$%price%" + ChatColor.GRAY + " from " + ChatColor.AQUA + "%player%" + + ChatColor.GRAY + "."), + BUY_SOLD_SELLER("buy.sold_seller", + ChatColor.GRAY + "Sold region " + ChatColor.AQUA + "%region%" + ChatColor.GRAY + " for " + + ChatColor.AQUA + + "$%price%" + ChatColor.GRAY + " to " + ChatColor.AQUA + "%player%" + + ChatColor.GRAY + "."), + + // ps sell + SELL_HELP("sell.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps sell [price|stop]"), + SELL_HELP_DESC("sell.help_desc", "Sell the region you are currently in."), + SELL_RENTED_OUT("sell.rented_out", + ChatColor.RED + "The region is being rented out! You must stop renting it out to sell."), + SELL_FOR_SALE("sell.for_sale", + ChatColor.GRAY + "The region is now for sale for " + ChatColor.AQUA + "$%price%" + + ChatColor.GRAY + "."), + + // ps hide/unhide + VISIBILITY_HIDE_HELP("visibility.hide_help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps hide"), + VISIBILITY_HIDE_HELP_DESC("visibility.hide_help_desc", + "Use this command to hide or unhide your protection block."), + VISIBILITY_UNHIDE_HELP("visibility.unhide_help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps unhide"), + VISIBILITY_UNHIDE_HELP_DESC("visibility.unhide_help_desc", + "Use this command to hide or unhide your protection block."), + ALREADY_NOT_HIDDEN("visibility.already_not_hidden", + ChatColor.GRAY + "The protection stone doesn't appear hidden..."), + ALREADY_HIDDEN("visibility.already_hidden", + ChatColor.GRAY + "The protection stone appears to already be hidden..."), + + // ps info + INFO_HELP("info.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps info members|owners|flags"), + INFO_HELP_DESC("info.help_desc", "Use this command inside a ps region to see more information about it."), + INFO_HEADER("info.header", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " PS Info " + + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), + INFO_TYPE2("info.type2", "&9Type: &7%type%", "%type%"), + INFO_MAY_BE_MERGED("info.may_be_merged", "(may be merged with other types)"), + INFO_MERGED2("info.merged2", ChatColor.BLUE + "Merged regions: " + ChatColor.GRAY + "%merged%", "%merged%"), + INFO_MEMBERS2("info.members2", "&9Members: &7%members%", "%members%"), + INFO_NO_MEMBERS("info.no_members", ChatColor.RED + "(no members)"), + INFO_OWNERS2("info.owners2", "&9Owners: &7%owners%", "%owners%"), + INFO_NO_OWNERS("info.no_owners", ChatColor.RED + "(no owners)"), + INFO_FLAGS2("info.flags2", "&9Flags: &7%flags%", "%flags%"), + INFO_NO_FLAGS("info.no_flags", "(none)"), + INFO_REGION2("info.region2", "&9Region: &b%region%", "%region%"), + INFO_PRIORITY2("info.priority2", "&9Priority: &b%priority%", "%priority%"), + INFO_PARENT2("info.parent2", "&9Parent: &b%parentregion%", "%parentregion%"), + INFO_BOUNDS_XYZ("info.bounds_xyz", "&9Bounds: &b(%minx%,%miny%,%minz%) -> (%maxx%,%maxy%,%maxz%)", + "%minx%", "%miny%", "%minz%", "%maxx%", "%maxy%", "%maxz%"), + INFO_BOUNDS_XZ("info.bounds_xz", "&9Bounds: &b(%minx%, %minz%) -> (%maxx%, %maxz%)", + "%minx%", "%minz%", "%maxx%", "%maxz%"), + INFO_SELLER2("info.seller2", "&9Seller: &7%seller%", "%seller%"), + INFO_PRICE2("info.price2", "&9Price: &7%price%", "%price%"), + INFO_TENANT2("info.tenant2", "&9Tenant: &7%tenant%", "%tenant%"), + INFO_LANDLORD2("info.landlord2", "&9Landlord: &7%landlord%", "%landlord%"), + INFO_RENT2("info.rent2", "&9Rent: &7%rent%", "%rent%"), + INFO_AVAILABLE_FOR_SALE("info.available_for_sale", ChatColor.AQUA + "Region available for sale!"), + INFO_AVAILABLE_FOR_RENT("info.available_for_rent", ChatColor.AQUA + "Region available for rent!"), + + // ps priority + PRIORITY_HELP("priority.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps priority [number|null]"), + PRIORITY_HELP_DESC("priority.help_desc", "Use this command to set your region's priority."), + PRIORITY_INFO("priority.info", ChatColor.GRAY + "Priority: %priority%"), + PRIORITY_SET("priority.set", ChatColor.YELLOW + "Priority has been set."), + PRIORITY_ERROR("priority.error", ChatColor.RED + "Error parsing input, check it again?"), + + // ps region + REGION_HELP("region.help", + ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps region [list|remove|disown] [playername]"), + REGION_HELP_DESC("region.help_desc", + "Use this command to find information or edit other players' (or your own) protected regions."), + REGION_NOT_FOUND_FOR_PLAYER("region.not_found_for_player", + ChatColor.GRAY + "No regions found for %player% in this world."), + REGION_LIST("region.list", + ChatColor.GRAY + "%player%'s regions in this world: " + ChatColor.AQUA + "%regions%"), + REGION_REMOVE("region.remove", ChatColor.YELLOW + + "%player%'s regions have been removed in this world, and they have been removed from regions they co-owned."), + REGION_DISOWN("region.disown", + ChatColor.YELLOW + "%player% has been removed as owner from all regions on this world."), + REGION_ERROR_SEARCH("region.error_search", ChatColor.RED + + "Error while searching for %player%'s regions. Please make sure you have entered the correct name."), + + // ps tp + TP_HELP("tp.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps tp [id/player] [num (optional)]"), + TP_HELP_DESC("tp.help_desc", "Teleports you to one of a given player's regions."), + NUMBER_ABOVE_ZERO("tp.number_above_zero", ChatColor.RED + "Please enter a number above 0."), + TP_VALID_NUMBER("tp.valid_number", ChatColor.RED + "Please enter a valid number."), + ONLY_HAS_REGIONS("tp.only_has_regions", + ChatColor.RED + "%player% only has %num% protected regions in this world!"), + TPING("tp.tping", ChatColor.GREEN + "Teleporting..."), + TP_ERROR_NAME("tp.error_name", + ChatColor.RED + "Error in teleporting to protected region! (parsing WG region name error)"), + TP_ERROR_TP("tp.error_tp", ChatColor.RED + "Error in finding the region to teleport to!"), + TP_IN_SECONDS("tp.in_seconds", + ChatColor.GRAY + "Teleporting in " + ChatColor.AQUA + "%seconds%" + ChatColor.GRAY + + " seconds."), + TP_CANCELLED_MOVED("tp.cancelled_moved", ChatColor.RED + "Teleport cancelled. You moved!"), + + // ps home + HOME_HELP("home.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps home [name/id]"), + HOME_HELP_DESC("home.help_desc", "Teleports you to one of your protected regions."), + HOME_HEADER("home.header", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + + " Homes (click to teleport) " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + + "====="), + HOME_CLICK_TO_TP("home.click_to_tp", "Click to teleport!"), + HOME_NEXT("home.next_page", ChatColor.GRAY + "Do /ps home -p %page% to go to the next page!"), + + // ps unclaim + UNCLAIM_HELP("unclaim.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps unclaim"), + UNCLAIM_HELP_DESC("unclaim.help_desc", + "Use this command to pickup a placed protection stone and remove the region."), + UNCLAIM_HEADER("unclaim.header", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + + " Unclaim (click to unclaim) " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + + "====="), + + // ps view + VIEW_HELP("view.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps view"), + VIEW_HELP_DESC("view.help_desc", "Use this command to view the borders of a protected region."), + VIEW_COOLDOWN("view.cooldown", ChatColor.RED + "Please wait a while before using /ps view again."), + VIEW_GENERATING("view.generating", ChatColor.GRAY + "Generating border..."), + VIEW_GENERATE_DONE("view.generate_done", ChatColor.GREEN + "Done! The border will disappear after 30 seconds!"), + VIEW_REMOVING("view.removing", + ChatColor.AQUA + "Removing border...\n" + ChatColor.GREEN + + "If you still see ghost blocks, relog!"), + + // ps admin + ADMIN_HELP("admin.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps admin"), + ADMIN_HELP_DESC("admin.help_desc", "Do /ps admin help for more information."), + ADMIN_CLEANUP_HEADER("admin.cleanup_header", ChatColor.YELLOW + "Cleanup %arg% %days% days\n================"), + ADMIN_CLEANUP_FOOTER("admin.cleanup_footer", ChatColor.YELLOW + "================\nCompleted %arg% cleanup."), + ADMIN_HIDE_TOGGLED("admin.hide_toggled", + ChatColor.YELLOW + "All protection stones have been %message% in this world."), + ADMIN_LAST_LOGON("admin.last_logon", ChatColor.YELLOW + "%player% last played %days% days ago."), + ADMIN_IS_BANNED("admin.is_banned", ChatColor.YELLOW + "%player% is banned."), + ADMIN_ERROR_PARSING("admin.error_parsing", ChatColor.RED + "Error parsing days, are you sure it is a number?"), + ADMIN_CONSOLE_WORLD("admin.console_world", ChatColor.RED + "Please specify the world as the last parameter."), + ADMIN_LASTLOGONS_HEADER("admin.lastlogons_header", ChatColor.YELLOW + "%days% Days Plus:\n================"), + ADMIN_LASTLOGONS_LINE("admin.lastlogons_line", ChatColor.YELLOW + "%player% %time% days"), + ADMIN_LASTLOGONS_FOOTER("admin.lastlogons_footer", + ChatColor.YELLOW + "================\n%count% Total Players Shown\n%checked% Total Players Checked"), + + // ps reload + RELOAD_HELP("reload.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps reload"), + RELOAD_HELP_DESC("reload.help_desc", "Reload settings from the config."), + RELOAD_START("reload.start", ChatColor.AQUA + "Reloading config..."), + RELOAD_COMPLETE("reload.complete", ChatColor.AQUA + "Completed config reload!"), + + // ps add/remove + ADDREMOVE_HELP("addremove.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps add|remove [playername]"), + ADDREMOVE_HELP_DESC("addremove.help_desc", + "Use this command to add or remove a member of your protected region."), + ADDREMOVE_OWNER_HELP("addremove.owner_help", + ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps addowner|removeowner [playername]"), + ADDREMOVE_OWNER_HELP_DESC("addremove.owner_help_desc", + "Use this command to add or remove an owner of your protected region."), + ADDREMOVE_PLAYER_REACHED_LIMIT("addremove.player_reached_limit", + ChatColor.RED + "This player has reached their region limit."), + ADDREMOVE_PLAYER_NEEDS_TO_BE_ONLINE("addremove.player_needs_to_be_online", + ChatColor.RED + "The player needs to be online to add them."), + + // ps get + GET_HELP("get.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps get [block]"), + GET_HELP_DESC("get.help_desc", "Use this command to get or purchase a protection block."), + GET_GOTTEN("get.gotten", ChatColor.AQUA + "Added protection block to inventory!"), + GET_NO_PERMISSION_BLOCK("get.no_permission_block", + ChatColor.RED + "You don't have permission to get this block."), + GET_HEADER("get.header", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + + " Protect Blocks (click to get) " + ChatColor.DARK_GRAY + + ChatColor.STRIKETHROUGH + "====="), + GET_GUI_BLOCK("get.gui_block", + ChatColor.GRAY + "> " + ChatColor.AQUA + "%alias% " + ChatColor.GRAY + "- %description% (" + + ChatColor.WHITE + + "$%price%" + ChatColor.GRAY + ")"), + GET_GUI_HOVER("get.gui_hover", "Click to buy a %alias%!"), + + // ps give + GIVE_HELP("give.help", + ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps give [block] [player] [amount (optional)]"), + GIVE_HELP_DESC("give.help_desc", "Use this command to give a player a protection block."), + GIVE_GIVEN("give.given", + ChatColor.GRAY + "Gave " + ChatColor.AQUA + "%block%" + ChatColor.GRAY + " to " + ChatColor.AQUA + + "%player%" + ChatColor.GRAY + "."), + GIVE_NO_INVENTORY_ROOM("give.no_inventory_room", + ChatColor.RED + "The player does not have enough inventory room."), + + // ps sethome + SETHOME_HELP("sethome.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps sethome"), + SETHOME_HELP_DESC("sethome.help_desc", + "Use this command to set the home of a region to where you are right now."), + SETHOME_SET("sethome.set", + ChatColor.GRAY + "The home for " + ChatColor.AQUA + "%psid%" + ChatColor.GRAY + + " has been set to your location."), + + // ps list + LIST_HELP("list.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps list [player (optional)]"), + LIST_HELP_DESC("list.help_desc", "Use this command to list the regions you, or another player owns."), + LIST_HEADER("list.header", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + + " %player%'s Regions " + + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "====="), + LIST_OWNER("list.owner", ChatColor.GRAY + "Owner of:"), + LIST_MEMBER("list.member", ChatColor.GRAY + "Member of:"), + LIST_NO_REGIONS("list.no_regions", + ChatColor.GRAY + "You currently do not own and are not a member of any regions."), + LIST_NO_REGIONS_PLAYER("list.no_regions_player", + ChatColor.AQUA + "%player% " + ChatColor.GRAY + + "does not own and is not a member of any regions."), + + // ps name + NAME_HELP("name.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps name [name|none]"), + NAME_HELP_DESC("name.help_desc", + "Use this command to give a nickname to your region, to make identifying your region easier."), + NAME_REMOVED("name.removed", ChatColor.GRAY + "Removed the name for %id%."), + NAME_SET_NAME("name.set_name", + ChatColor.GRAY + "Set the name of %id% to " + ChatColor.AQUA + "%name%" + ChatColor.GRAY + "."), + NAME_TAKEN("name.taken", + ChatColor.GRAY + "The region name " + ChatColor.AQUA + "%name%" + ChatColor.GRAY + + " has already been taken! Try another one."), + + // ps setparent + SETPARENT_HELP("setparent.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps setparent [region|none]"), + SETPARENT_HELP_DESC("setparent.help_desc", + "Use this command to allow this region to inherit properties from another region (owners, members, flags, etc.)."), + SETPARENT_SUCCESS("setparent.success", + ChatColor.GRAY + "Successfully set the parent of " + ChatColor.AQUA + "%id%" + ChatColor.GRAY + + " to " + + ChatColor.AQUA + "%parent%" + ChatColor.GRAY + "."), + SETPARENT_SUCCESS_REMOVE("setparent.success_remove", + ChatColor.GRAY + "Successfully removed the parent of " + ChatColor.AQUA + "%id%" + + ChatColor.GRAY + "."), + SETPARENT_CIRCULAR_INHERITANCE("setparent.circular_inheritance", + ChatColor.RED + + "Detected circular inheritance (the parent already inherits from this region?). Parent not set."), + + // ps merge + MERGE_HELP("merge.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps merge"), + MERGE_HELP_DESC("merge.help_desc", + "Use this command to merge the region you are in with other overlapping regions."), + MERGE_DISABLED("merge.disabled", "Merging regions is disabled in the config!"), + MERGE_MERGED("merge.merged", ChatColor.AQUA + "Regions were successfully merged!"), + MERGE_HEADER("merge.header", + ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + + " Merge %region% (click to merge) " + ChatColor.DARK_GRAY + + ChatColor.STRIKETHROUGH + "====="), + MERGE_WARNING("merge.warning", + ChatColor.GRAY + "Note: This will delete all of the settings for the current region!"), + MERGE_NOT_ALLOWED("merge.not_allowed", + ChatColor.RED + "You are not allowed to merge this protection region type."), + MERGE_INTO("merge.into", ChatColor.AQUA + "This region overlaps other regions you can merge into!"), + MERGE_NO_REGIONS("merge.no_region", ChatColor.GRAY + "There are no overlapping regions to merge into."), + MERGE_CLICK_TO_MERGE("merge.click_to_merge", "Click to merge with %region%!"), + MERGE_AUTO_MERGED("merge.auto_merged", + ChatColor.GRAY + "Region automatically merged with " + ChatColor.AQUA + "%region%" + + ChatColor.GRAY + "."), + + ; + + private final String path; + private final String defaultMessage; + + private final String[] placeholders; + private final int placeholdersCount; + private String message; + private boolean isEmpty; + + private static final File conf = new File(ProtectionStones.getInstance().getDataFolder(), "messages.yml"); + + PSL(String path, String defaultMessage, String... placeholders) { + this.path = path; + this.defaultMessage = defaultMessage; + + this.placeholders = placeholders; + this.placeholdersCount = placeholders.length; + this.message = defaultMessage; + this.isEmpty = message.isEmpty(); } - if (this.placeholdersCount == 0) { - return this.message; + public String msg() { + return message; } - if (this.placeholdersCount != args.length) { - throw new IllegalArgumentException("Expected " + this.placeholdersCount + " arguments but got " + args.length); + public boolean isEmpty() { + return isEmpty; } - return StringUtils.replaceEach( - this.message, - this.placeholders, - Arrays.stream(args).filter(Objects::nonNull).map(Object::toString).toArray(String[]::new) - ); - } + @Nullable + public String format(final Object... args) { + if (isEmpty) { + return null; + } - public boolean send(@NotNull final CommandSender receiver, @NotNull final Object... args) { - final String msg = this.format(args); + if (this.placeholdersCount == 0) { + return this.message; + } + + if (this.placeholdersCount != args.length) { + throw new IllegalArgumentException( + "Expected " + this.placeholdersCount + " arguments but got " + args.length); + } - if (msg != null) { - receiver.sendMessage(msg); + return StringUtils.replaceEach( + this.message, + this.placeholders, + Arrays.stream(args).filter(Objects::nonNull).map(Object::toString) + .toArray(String[]::new)); } - return true; - } + public boolean send(@NotNull final CommandSender receiver, @NotNull final Object... args) { + final String msg = this.format(args); - public void append(@NotNull final StringBuilder builder, @NotNull final Object... args) { - final String msg = this.format(args); + if (msg != null) { + receiver.sendMessage(msg); + } - if (msg != null) { - builder.append(msg); + return true; } - } - // Sends a message to a commandsender if the string is not empty + public void append(@NotNull final StringBuilder builder, @NotNull final Object... args) { + final String msg = this.format(args); - public static boolean msg(CommandSender p, String str) { - if (str != null && !str.isEmpty() && p != null) { - p.sendMessage(str); - } - return true; - } - - public static boolean msg(PSPlayer p, String str) { - return msg(p.getPlayer(), str); - } - - public static void loadConfig() { - YamlConfiguration yml = new YamlConfiguration(); - - // check if messages.yml exists - if (!conf.exists()) { - try { - conf.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } + if (msg != null) { + builder.append(msg); + } } - // load config - try { - yml.load(conf); // can throw error - for (PSL psl : PSL.values()) { - - // fix message if need be - if (yml.getString(psl.path) == null) { // if msg not found in config - yml.set(psl.path, applyConfigColours(psl.defaultMessage)); - } else { - // perform message upgrades - messageUpgrades(psl, yml); + // Sends a message to a commandsender if the string is not empty + + public static boolean msg(CommandSender p, String str) { + if (str != null && !str.isEmpty() && p != null) { + String mm = legacyToMiniMessage(str); + ProtectionStones.getAdventure().sender(p) + .sendMessage(MiniMessage.miniMessage().deserialize(mm)); } + return true; + } - // load message - psl.message = applyInGameColours(yml.getString(psl.path)); - psl.isEmpty = psl.message.isEmpty(); - } - try { - yml.save(conf); - } catch (IOException e) { - e.printStackTrace(); - } - } catch (Exception e) { // prevent bad messages.yml file from resetting the file - e.printStackTrace(); + public static boolean msg(PSPlayer p, String str) { + return msg(p.getPlayer(), str); } - } - - // message upgrades over time - private static void messageUpgrades(PSL psl, YamlConfiguration yml) { - String value = yml.getString(psl.path); - assert(value != null); - - // psl upgrade conversions - if (psl == PSL.REACHED_REGION_LIMIT && value.equals("&cYou can not create any more protected regions.")) { - yml.set(psl.path, psl.defaultMessage); - } else if (psl == PSL.REACHED_PER_BLOCK_REGION_LIMIT && value.equals("&cYou can not create any more regions of this type.")) { - yml.set(psl.path, psl.defaultMessage); - } else if (value.contains("§")) { - yml.set(psl.path, applyConfigColours(value)); + + public static void loadConfig() { + YamlConfiguration yml = new YamlConfiguration(); + + // check if messages.yml exists + if (!conf.exists()) { + try { + conf.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // load config + try { + yml.load(conf); // can throw error + for (PSL psl : PSL.values()) { + + // fix message if need be + if (yml.getString(psl.path) == null) { // if msg not found in config + yml.set(psl.path, applyConfigColours(psl.defaultMessage)); + } else { + // perform message upgrades + messageUpgrades(psl, yml); + } + + // load message + psl.message = applyInGameColours(yml.getString(psl.path)); + psl.isEmpty = psl.message.isEmpty(); + } + try { + yml.save(conf); + } catch (IOException e) { + e.printStackTrace(); + } + } catch (Exception e) { // prevent bad messages.yml file from resetting the file + e.printStackTrace(); + } } - } - // match all {abc format for hex - private static final Pattern hexPattern = Pattern.compile("(?"); + return msg.replace('&', '§'); } - return msg.replace('&', '§'); - } + public static String legacyToMiniMessage(String str) { + // Handle null + if (str == null) + return ""; + + // Convert &#RRGGBB to <#RRGGBB> + str = str.replaceAll("(?i)[&§]#([a-f0-9]{6})", "<#$1>"); + + // Convert &x&r&r&g&g&b&b to <#rrggbb> (BungeeCord format) - uncommon but good + // to support + // This regex is complex, usually easier to just handle the standard formats. + // Let's stick to the standard conversions for now as regex for &x is verbose. + + // Replace legacy characters + return str + .replace("&0", "").replace("§0", "") + .replace("&1", "").replace("§1", "") + .replace("&2", "").replace("§2", "") + .replace("&3", "").replace("§3", "") + .replace("&4", "").replace("§4", "") + .replace("&5", "").replace("§5", "") + .replace("&6", "").replace("§6", "") + .replace("&7", "").replace("§7", "") + .replace("&8", "").replace("§8", "") + .replace("&9", "").replace("§9", "") + .replace("&a", "").replace("§a", "") + .replace("&b", "").replace("§b", "") + .replace("&c", "").replace("§c", "") + .replace("&d", "").replace("§d", "") + .replace("&e", "").replace("§e", "") + .replace("&f", "").replace("§f", "") + .replace("&k", "").replace("§k", "") + .replace("&l", "").replace("§l", "") + .replace("&m", "").replace("§m", "") + .replace("&n", "").replace("§n", "") + .replace("&o", "").replace("§o", "") + .replace("&r", "").replace("§r", ""); + } - private static String applyConfigColours(String msg) { - return msg.replace('§', '&'); - } + private static String applyConfigColours(String msg) { + return msg.replace('§', '&'); + } } diff --git a/src/main/java/dev/espi/protectionstones/PSRegion.java b/src/main/java/dev/espi/protectionstones/PSRegion.java index 2c2e285a..27de6a67 100644 --- a/src/main/java/dev/espi/protectionstones/PSRegion.java +++ b/src/main/java/dev/espi/protectionstones/PSRegion.java @@ -49,11 +49,14 @@ public abstract class PSRegion { // ~~~~~~~~~~~~~~~~~ static ~~~~~~~~~~~~~~~~~ /** - * Get the protection stone region that the location is in, or the closest one if there are overlapping regions. - * Returns either {@link PSGroupRegion}, {@link PSStandardRegion} or {@link PSMergedRegion}. + * Get the protection stone region that the location is in, or the closest one + * if there are overlapping regions. + * Returns either {@link PSGroupRegion}, {@link PSStandardRegion} or + * {@link PSMergedRegion}. * * @param l the location - * @return the {@link PSRegion} object if the location is in a region, or null if the location is not in a region + * @return the {@link PSRegion} object if the location is in a region, or null + * if the location is not in a region */ public static PSRegion fromLocation(Location l) { PSRegion r = fromLocationUnsafe(l); @@ -61,21 +64,27 @@ public static PSRegion fromLocation(Location l) { } /** - * Get the protection stone region that the location is in, or the closest one if there are overlapping regions. - * May return a region with an unconfigured block type (getTypeOptions returns null). - * Returns either {@link PSGroupRegion}, {@link PSStandardRegion} or {@link PSMergedRegion}. + * Get the protection stone region that the location is in, or the closest one + * if there are overlapping regions. + * May return a region with an unconfigured block type (getTypeOptions returns + * null). + * Returns either {@link PSGroupRegion}, {@link PSStandardRegion} or + * {@link PSMergedRegion}. * * @param l the location - * @return the {@link PSRegion} object if the location is in a region, or null if the location is not in a region + * @return the {@link PSRegion} object if the location is in a region, or null + * if the location is not in a region */ public static PSRegion fromLocationUnsafe(Location l) { checkNotNull(checkNotNull(l).getWorld()); RegionManager rgm = WGUtils.getRegionManagerWithWorld(l.getWorld()); - if (rgm == null) return null; + if (rgm == null) + return null; // check exact location first for merged region block PSMergedRegion pr = PSMergedRegion.getMergedRegion(l); - if (pr != null) return pr; + if (pr != null) + return pr; return fromLocationGroupUnsafe(l); } @@ -85,7 +94,8 @@ public static PSRegion fromLocationUnsafe(Location l) { * Returns either {@link PSGroupRegion} or {@link PSStandardRegion}. * * @param l the location - * @return the {@link PSRegion} object if the location is in a region, or null if the location is not in a region + * @return the {@link PSRegion} object if the location is in a region, or null + * if the location is not in a region */ public static PSRegion fromLocationGroup(Location l) { PSRegion r = fromLocationGroupUnsafe(l); @@ -94,16 +104,19 @@ public static PSRegion fromLocationGroup(Location l) { /** * Get the protection stone parent region that the location is in. - * May return a region with an unconfigured block type (getTypeOptions returns null). + * May return a region with an unconfigured block type (getTypeOptions returns + * null). * Returns either {@link PSGroupRegion} or {@link PSStandardRegion}. * * @param l the location - * @return the {@link PSRegion} object if the location is in a region, or null if the location is not in a region + * @return the {@link PSRegion} object if the location is in a region, or null + * if the location is not in a region */ public static PSRegion fromLocationGroupUnsafe(Location l) { checkNotNull(checkNotNull(l).getWorld()); RegionManager rgm = WGUtils.getRegionManagerWithWorld(l.getWorld()); - if (rgm == null) return null; + if (rgm == null) + return null; // check if location is in a region String psID = WGUtils.matchLocationToPSID(l); @@ -112,7 +125,9 @@ public static PSRegion fromLocationGroupUnsafe(Location l) { if (r == null) { return null; } else if (r.getFlag(FlagHandler.PS_MERGED_REGIONS) != null) { - return new PSGroupRegion(r, rgm, l.getWorld()); + PSGroupRegion gr = new PSGroupRegion(r, rgm, l.getWorld()); + PSRegion mr = gr.getMergedRegion(l); + return mr != null ? mr : gr; } else { return new PSStandardRegion(r, rgm, l.getWorld()); } @@ -120,14 +135,17 @@ public static PSRegion fromLocationGroupUnsafe(Location l) { /** * Get the protection stone region with the world and region. - * It returns a WGRegion with a null type if the block type isn't configured in the config. + * It returns a WGRegion with a null type if the block type isn't configured in + * the config. * * @param w the world * @param r the WorldGuard region - * @return the {@link PSRegion} based on the parameters, or null if the region given is not a protectionstones region + * @return the {@link PSRegion} based on the parameters, or null if the region + * given is not a protectionstones region */ public static PSRegion fromWGRegion(World w, ProtectedRegion r) { - if (!ProtectionStones.isPSRegionFormat(r)) return null; + if (!ProtectionStones.isPSRegionFormat(r)) + return null; if (r.getFlag(FlagHandler.PS_MERGED_REGIONS) != null) { return new PSGroupRegion(r, WGUtils.getRegionManagerWithWorld(checkNotNull(w)), w); } else { @@ -136,7 +154,8 @@ public static PSRegion fromWGRegion(World w, ProtectedRegion r) { } /** - * Get the protection stones regions that have the given name as their set nickname (/ps name) + * Get the protection stones regions that have the given name as their set + * nickname (/ps name) * * @param w the world to look for regions in * @param name the nickname of the region @@ -145,12 +164,14 @@ public static PSRegion fromWGRegion(World w, ProtectedRegion r) { public static List fromName(World w, String name) { RegionManager rgm = WGUtils.getRegionManagerWithWorld(w); - if (rgm == null) return new ArrayList<>(); + if (rgm == null) + return new ArrayList<>(); List l = new ArrayList<>(); List rIds = ProtectionStones.regionNameToID.get(w.getUID()).get(name); - if (rIds == null) return l; + if (rIds == null) + return l; for (int i = 0; i < rIds.size(); i++) { String id = rIds.get(i); @@ -165,7 +186,8 @@ public static List fromName(World w, String name) { } /** - * Get the protection stones regions that have the given name as their set nickname (/ps name), from all worlds. + * Get the protection stones regions that have the given name as their set + * nickname (/ps name), from all worlds. * * @param name the nickname of the regions * @return the map of worlds, to the regions that have the name @@ -195,13 +217,16 @@ public String getID() { } /** - * Get the WorldGuard ID of the region. Note that this is not guaranteed to be unique between worlds. + * Get the WorldGuard ID of the region. Note that this is not guaranteed to be + * unique between worlds. + * * @return the id of the region */ public abstract String getId(); /** * Get the name (nickname) of the region from /ps name. + * * @return the name of the region, or null if the region does not have a name */ @@ -209,6 +234,7 @@ public String getID() { /** * Set the name of the region (from /ps name). + * * @param name new name, or null to remove the name */ @@ -216,14 +242,18 @@ public String getID() { /** * Set the parent of this region. + * * @param r the region to be the parent, or null for no parent - * @throws ProtectedRegion.CircularInheritanceException thrown when the parent already inherits from the child + * @throws ProtectedRegion.CircularInheritanceException thrown when the parent + * already inherits from + * the child */ public abstract void setParent(PSRegion r) throws ProtectedRegion.CircularInheritanceException; /** * Get the parent of this region, if there is one. + * * @return the parent of the region, or null if there isn't one */ @@ -231,12 +261,14 @@ public String getID() { /** * Get the location of the set home the region has (for /ps tp). + * * @return the location of the home, or null if the ps_home flag is not set. */ public abstract Location getHome(); /** * Set the home of the region (internally changes the flag). + * * @param blockX block x location * @param blockY block y location * @param blockZ block z location @@ -245,11 +277,12 @@ public String getID() { /** * Set the home of the region (internally changes the flag). + * * @param blockX block x location * @param blockY block y location * @param blockZ block z location - * @param yaw location yaw - * @param pitch location pitch + * @param yaw location yaw + * @param pitch location pitch */ public abstract void setHome(double blockX, double blockY, double blockZ, float yaw, float pitch); @@ -275,6 +308,7 @@ public enum RentStage { /** * Sells the region to a player at the price listed. + * * @param player player to transfer the region to */ public abstract void sell(UUID player); @@ -286,36 +320,42 @@ public enum RentStage { /** * Get the landlord of the region. + * * @return returns the UUID of the landlord, or null if there is none. */ public abstract UUID getLandlord(); /** * Set the landlord of the region. + * * @param landlord uuid of landlord, or null to remove */ public abstract void setLandlord(UUID landlord); /** * Get the tenant of the region. + * * @return returns the UUID of the tenant, or null if there is none. */ public abstract UUID getTenant(); /** * Set the tenant of the region + * * @param tenant uuid of tenant, or null to remove */ public abstract void setTenant(UUID tenant); /** * Get the rent period of the region + * * @return returns the rent duration, or null if there is none */ public abstract String getRentPeriod(); /** * Set the rent period of the region + * * @param s the duration between rent payments (d h m s), or null to remove */ public abstract void setRentPeriod(String s); @@ -324,7 +364,8 @@ public enum RentStage { * Get the price of the region * This applies to either the rent or the full purchase of a region. * - * @return the price of the region during rent payments, or null if there is no rent + * @return the price of the region during rent payments, or null if there is no + * rent */ public abstract Double getPrice(); @@ -338,12 +379,14 @@ public enum RentStage { /** * Set the unix timestamp of when rent was last paid. + * * @param timestamp the unix timestamp of when rent was last paid, or null */ public abstract void setRentLastPaid(Long timestamp); /** * Get the unix timestamp of when rent was last paid. + * * @return the unix timestamp of when rent was last paid, or null if not renting */ public abstract Long getRentLastPaid(); @@ -379,7 +422,8 @@ public enum RentStage { public static class TaxPayment implements Comparable { long whenPaymentIsDue; double amount; - String regionId; // the region that caused the payment (especially applicable for group regions); also used since hashsets don't have duplicates + String regionId; // the region that caused the payment (especially applicable for group regions); + // also used since hashsets don't have duplicates public TaxPayment(long whenPaymentIsDue, double amount, String regionId) { this.whenPaymentIsDue = whenPaymentIsDue; @@ -388,13 +432,16 @@ public TaxPayment(long whenPaymentIsDue, double amount, String regionId) { } /** - * Convert a flag entry to a tax payment object. The flag entry is in the form "timestamp amount regionId". + * Convert a flag entry to a tax payment object. The flag entry is in the form + * "timestamp amount regionId". + * * @param s the flag value * @return the tax payment object, or null if the string was invalid */ public static TaxPayment fromString(String s) { String[] arr = s.split(" "); - if (arr.length < 3) return null; + if (arr.length < 3) + return null; try { return new TaxPayment(Long.parseLong(arr[0]), Double.parseDouble(arr[1]), arr[2]); } catch (NumberFormatException e) { @@ -404,6 +451,7 @@ public static TaxPayment fromString(String s) { /** * Converts the tax payment object into its flag representation. + * * @return the flag representation of this object */ public String toFlagEntry() { @@ -450,13 +498,17 @@ public LastRegionTaxPaymentEntry(String regionId, long lastPaymentAdded) { } /** - * Convert a flag entry to a last region tax payment entry object. The flag entry is in the form "regionId timestamp". + * Convert a flag entry to a last region tax payment entry object. The flag + * entry is in the form "regionId timestamp". + * * @param s the flag value - * @return the last region tax payment entry object, or null if the string was invalid + * @return the last region tax payment entry object, or null if the string was + * invalid */ public static LastRegionTaxPaymentEntry fromString(String s) { String[] arr = s.split(" "); - if (arr.length < 2) return null; + if (arr.length < 2) + return null; try { return new LastRegionTaxPaymentEntry(arr[0], Long.parseLong(arr[1])); } catch (NumberFormatException e) { @@ -465,7 +517,9 @@ public static LastRegionTaxPaymentEntry fromString(String s) { } /** - * Converts the last region tax payment entry object into its flag representation. + * Converts the last region tax payment entry object into its flag + * representation. + * * @return the flag representation of this object */ public String toFlagEntry() { @@ -491,6 +545,7 @@ public void setLastPaymentAdded(long lastPaymentAdded) { /** * Get the tax rate for this region type. + * * @return the tax rate */ public double getTaxRate() { @@ -500,66 +555,83 @@ public double getTaxRate() { /** * Get the formatted period(s) between tax payments for this region type. * If you simply wanted the number of seconds, use getTypeOptions().taxPeriod - * @return the duration between tax payments, or multiple if there are several different ones + * + * @return the duration between tax payments, or multiple if there are several + * different ones */ public abstract String getTaxPeriod(); /** * Get the formatted period(s) allowed for the payment of tax. - * If you simply wanted the number of seconds, use getTypeOptions().taxPaymentTime - * @return the duration of time allowed to pay a tax, or multiple if there are several different ones + * If you simply wanted the number of seconds, use + * getTypeOptions().taxPaymentTime + * + * @return the duration of time allowed to pay a tax, or multiple if there are + * several different ones */ public abstract String getTaxPaymentPeriod(); /** * Get the list of tax payments that are due. + * * @return the list of tax payments outstanding */ public abstract List getTaxPaymentsDue(); /** * Save the list of tax payments due onto the region. + * * @param taxPayments the full list of tax payments that are due */ public abstract void setTaxPaymentsDue(List taxPayments); /** - * Get the list of timestamps of the last time regions and sub regions have added to the tax payments list. - * @return the list of the last time regions and sub regions have added to the tax payment list + * Get the list of timestamps of the last time regions and sub regions have + * added to the tax payments list. + * + * @return the list of the last time regions and sub regions have added to the + * tax payment list */ public abstract List getRegionLastTaxPaymentAddedEntries(); /** - * Save the list of timestamps of the last time regions and sub regions have added to the tax payments list on to the base region. + * Save the list of timestamps of the last time regions and sub regions have + * added to the tax payments list on to the base region. + * * @param entries the full list of {@link LastRegionTaxPaymentEntry} entries. */ public abstract void setRegionLastTaxPaymentAddedEntries(List entries); /** * Get the player that is set to autopay the tax amount. + * * @return the player that is set as the autopayer, or null if no player is set */ public abstract UUID getTaxAutopayer(); /** * Set a player to auto-pay taxes for this region. + * * @param player the player to use to auto-pay taxes */ public abstract void setTaxAutopayer(UUID player); /** * Pay outstanding taxes. - * It will only withdraw the amount required to pay the taxes, and will take up to the amount + * It will only withdraw the amount required to pay the taxes, and will take up + * to the amount * specified if the outstanding payments are larger. * - * @param p the player to take money from + * @param p the player to take money from * @param amount the amount to take * @return the {@link EconomyResponse} returned by Vault */ public abstract EconomyResponse payTax(PSPlayer p, double amount); /** - * Check if any tax payments are now late (exceeded tax payment time shown in config). + * Check if any tax payments are now late (exceeded tax payment time shown in + * config). + * * @return whether or not any tax payments are now late */ public abstract boolean isTaxPaymentLate(); @@ -573,6 +645,7 @@ public double getTaxRate() { /** * Must be run sync (calls Bukkit API) + * * @return whether or not the protection block is hidden (/ps hide) */ public boolean isHidden() { @@ -581,6 +654,7 @@ public boolean isHidden() { /** * Hides the protection block, if it is not hidden. + * * @return whether or not the block was hidden */ public boolean hide() { @@ -594,6 +668,7 @@ public boolean hide() { /** * Unhides the protection block, if it is hidden. + * * @return whether or not the block was unhidden */ public boolean unhide() { @@ -616,12 +691,15 @@ public boolean unhide() { * Toggle whether or not the protection block is hidden. */ public void toggleHide() { - if (!hide()) unhide(); + if (!hide()) + unhide(); } /** - * This method returns the block that is supposed to contain the protection block. - * Warning: If the protection stone is hidden, this will give the block that took its place! + * This method returns the block that is supposed to contain the protection + * block. + * Warning: If the protection stone is hidden, this will give the block that + * took its place! * * @return returns the block that may contain the protection stone */ @@ -634,17 +712,20 @@ public void toggleHide() { public abstract PSProtectBlock getTypeOptions(); /** - * @return returns the protect block type (may include custom player heads PLAYER_HEAD:playername) that the region is + * @return returns the protect block type (may include custom player heads + * PLAYER_HEAD:playername) that the region is */ public abstract String getType(); /** * Change the type of the protection region. + * * @param type the type of protection region to switch to */ public void setType(PSProtectBlock type) { if (!isHidden()) { - Material set = Material.matchMaterial(type.type) == null ? Material.PLAYER_HEAD : Material.matchMaterial(type.type); + Material set = Material.matchMaterial(type.type) == null ? Material.PLAYER_HEAD + : Material.matchMaterial(type.type); getProtectBlock().setType(set); if (type.type.startsWith("PLAYER_HEAD") && type.type.split(":").length > 1) { BlockUtil.setHeadType(type.type, getProtectBlock()); @@ -654,6 +735,7 @@ public void setType(PSProtectBlock type) { /** * Get whether or not a player is an owner of this region. + * * @param uuid the player's uuid * @return whether or not the player is a member */ @@ -662,6 +744,7 @@ public void setType(PSProtectBlock type) { /** * Get whether or not a player is a member of this region. + * * @param uuid the player's uuid * @return whether or not the player is a member */ @@ -680,25 +763,30 @@ public void setType(PSProtectBlock type) { /** * Add an owner to the region. + * * @param uuid the uuid of the player to add */ public abstract void addOwner(UUID uuid); /** * Add a member to the region. + * * @param uuid the uuid of the player to add */ public abstract void addMember(UUID uuid); /** * Remove an owner of the region, and deal with side-effects. - * Examples of side-effects: removing player as landlord, removing player as auto taxpayer + * Examples of side-effects: removing player as landlord, removing player as + * auto taxpayer + * * @param uuid the uuid of the player to remove */ public abstract void removeOwner(UUID uuid); /** * Remove a member of the region, and deal with side-effects + * * @param uuid the uuid of the player to remove */ public abstract void removeMember(UUID uuid); @@ -709,7 +797,8 @@ public void setType(PSProtectBlock type) { public abstract List getPoints(); /** - * Get a list of regions that the current region can merge into, taking into account a player's permissions. + * Get a list of regions that the current region can merge into, taking into + * account a player's permissions. * * @param p the player to compare permissions with * @return the list of regions that the current region can merge into @@ -719,7 +808,8 @@ public void setType(PSProtectBlock type) { /** * Deletes the region forever. Can be cancelled by event cancellation. * - * @param deleteBlock whether or not to also set the protection block to air (if not hidden) + * @param deleteBlock whether or not to also set the protection block to air (if + * not hidden) * @return whether or not the region was able to be successfully removed */ public abstract boolean deleteRegion(boolean deleteBlock); @@ -727,7 +817,8 @@ public void setType(PSProtectBlock type) { /** * Deletes the region forever. Can be cancelled by event cancellation. * - * @param deleteBlock whether or not to also set the protection block to air (if not hidden) + * @param deleteBlock whether or not to also set the protection block to air (if + * not hidden) * @param cause the player that caused the region to break * @return whether or not the region was able to be successfully removed */ @@ -747,8 +838,10 @@ public RegionManager getWGRegionManager() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; PSRegion psRegion = (PSRegion) o; return Objects.equals(getId(), psRegion.getId()) && Objects.equals(getWorld(), psRegion.getWorld()); } diff --git a/src/main/java/dev/espi/protectionstones/ProtectionStones.java b/src/main/java/dev/espi/protectionstones/ProtectionStones.java index bd1775f9..3d997c7b 100644 --- a/src/main/java/dev/espi/protectionstones/ProtectionStones.java +++ b/src/main/java/dev/espi/protectionstones/ProtectionStones.java @@ -52,9 +52,9 @@ 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 +76,6 @@ public class ProtectionStones extends JavaPlugin { private PSConfig configOptions; static HashMap protectionStonesOptions = new HashMap<>(); - // ps alias to id cache // > static HashMap>> regionNameToID = new HashMap<>(); @@ -94,6 +93,15 @@ public class ProtectionStones extends JavaPlugin { // ps toggle/on/off list public static Set toggleList = new HashSet<>(); + private static net.kyori.adventure.platform.bukkit.BukkitAudiences adventure; + + public static net.kyori.adventure.platform.bukkit.BukkitAudiences getAdventure() { + if (adventure == null) { + throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!"); + } + return adventure; + } + /* ~~~~~~~~~~ Instance methods ~~~~~~~~~~~~ */ /** @@ -164,6 +172,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 +203,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 +236,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 +258,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 +282,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 +296,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 +352,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 +369,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 +392,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 +402,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 +420,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 +433,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 +491,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 +516,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) { @@ -482,11 +542,21 @@ public static ItemStack createProtectBlockItem(PSProtectBlock b) { } // add display name and lore + net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage + .miniMessage(); + net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer lcs = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer + .builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build(); // Standard hex format + if (!b.displayName.equals("")) { - im.setDisplayName(ChatColor.translateAlternateColorCodes('&', b.displayName)); + // Convert legacy to mm first if needed, or straight mm + String parsed = lcs.serialize(mm.deserialize(PSL.legacyToMiniMessage(b.displayName))); + im.setDisplayName(parsed); } List lore = new ArrayList<>(); - for (String s : b.lore) lore.add(ChatColor.translateAlternateColorCodes('&', s)); + for (String s : b.lore) { + String parsed = lcs.serialize(mm.deserialize(PSL.legacyToMiniMessage(s))); + lore.add(parsed); + } im.setLore(lore); // hide enchant name (cannot call addUnsafeEnchantment here) @@ -517,7 +587,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) @@ -549,6 +620,7 @@ public void onLoad() { @Override public void onEnable() { + adventure = net.kyori.adventure.platform.bukkit.BukkitAudiences.create(this); FlagHandler.registerHandlers(); // register custom WG flag handlers TomlFormat.instance(); @@ -560,7 +632,11 @@ public void onEnable() { // metrics (bStats) // https://bstats.org/plugin/bukkit/ProtectionStones/4071 - new Metrics(this, 4071); + try { + new Metrics(this, 4071); + } catch (Throwable t) { + getLogger().warning("bStats metrics disabled due to error: " + t.getMessage()); + } // load command arguments PSCommand.addDefaultArguments(); @@ -568,15 +644,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 +669,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 +679,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); @@ -661,4 +743,11 @@ public void onEnable() { getLogger().info(ChatColor.WHITE + "ProtectionStones has successfully started!"); } + @Override + public void onDisable() { + if (adventure != null) { + adventure.close(); + adventure = null; + } + } } \ No newline at end of file diff --git a/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java b/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java index 86deb3c8..013fa13c 100644 --- a/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java +++ b/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java @@ -36,6 +36,7 @@ // farewell-action flag public class FarewellFlagHandler extends FlagValueChangeHandler { public static final FarewellFlagHandler.Factory FACTORY = new FarewellFlagHandler.Factory(); + public static class Factory extends Handler.Factory { @Override public FarewellFlagHandler create(Session session) { @@ -53,26 +54,39 @@ protected void onInitialValue(LocalPlayer localPlayer, ApplicableRegionSet appli } @Override - protected boolean onSetValue(LocalPlayer localPlayer, Location from, Location to, ApplicableRegionSet toSet, String currentValue, String lastValue, MoveType moveType) { + protected boolean onSetValue(LocalPlayer localPlayer, Location from, Location to, ApplicableRegionSet toSet, + String currentValue, String lastValue, MoveType moveType) { Player p = Bukkit.getPlayer(localPlayer.getUniqueId()); - // the greeting action flag for the other region should show instead if it is set + // the greeting action flag for the other region should show instead if it is + // set for (ProtectedRegion r : toSet.getRegions()) { if (r.getFlag(FlagHandler.GREET_ACTION) != null) { return true; } } if (p != null && lastValue != null && !lastValue.equals(currentValue)) { - p.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(ChatColor.translateAlternateColorCodes('&', lastValue))); + net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage + .miniMessage(); + net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer lcs = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer + .builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build(); + String legacy = lcs.serialize(mm.deserialize(dev.espi.protectionstones.PSL.legacyToMiniMessage(lastValue))); + p.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(legacy)); } return true; } @Override - protected boolean onAbsentValue(LocalPlayer localPlayer, Location location, Location location1, ApplicableRegionSet applicableRegionSet, String lastValue, MoveType moveType) { + protected boolean onAbsentValue(LocalPlayer localPlayer, Location location, Location location1, + ApplicableRegionSet applicableRegionSet, String lastValue, MoveType moveType) { Player p = Bukkit.getPlayer(localPlayer.getUniqueId()); if (p != null && lastValue != null) { - p.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(ChatColor.translateAlternateColorCodes('&', lastValue))); + net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage + .miniMessage(); + net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer lcs = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer + .builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build(); + String legacy = lcs.serialize(mm.deserialize(dev.espi.protectionstones.PSL.legacyToMiniMessage(lastValue))); + p.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(legacy)); } return true; } diff --git a/src/main/java/dev/espi/protectionstones/flags/GreetingFlagHandler.java b/src/main/java/dev/espi/protectionstones/flags/GreetingFlagHandler.java index 5462592e..bc35f9a0 100644 --- a/src/main/java/dev/espi/protectionstones/flags/GreetingFlagHandler.java +++ b/src/main/java/dev/espi/protectionstones/flags/GreetingFlagHandler.java @@ -32,6 +32,7 @@ public class GreetingFlagHandler extends FlagValueChangeHandler { public static final Factory FACTORY = new Factory(); + public static class Factory extends Handler.Factory { @Override public GreetingFlagHandler create(Session session) { @@ -49,15 +50,25 @@ protected void onInitialValue(LocalPlayer localPlayer, ApplicableRegionSet appli } @Override - protected boolean onSetValue(LocalPlayer localPlayer, Location location, Location location1, ApplicableRegionSet applicableRegionSet, String currentValue, String lastValue, MoveType moveType) { - if (currentValue != null && !currentValue.equals(lastValue) && Bukkit.getPlayer(localPlayer.getUniqueId()) != null) { - Bukkit.getPlayer(localPlayer.getUniqueId()).spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(ChatColor.translateAlternateColorCodes('&', currentValue))); + protected boolean onSetValue(LocalPlayer localPlayer, Location location, Location location1, + ApplicableRegionSet applicableRegionSet, String currentValue, String lastValue, MoveType moveType) { + if (currentValue != null && !currentValue.equals(lastValue) + && Bukkit.getPlayer(localPlayer.getUniqueId()) != null) { + net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage + .miniMessage(); + net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer lcs = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer + .builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build(); + String legacy = lcs + .serialize(mm.deserialize(dev.espi.protectionstones.PSL.legacyToMiniMessage(currentValue))); + Bukkit.getPlayer(localPlayer.getUniqueId()).spigot().sendMessage(ChatMessageType.ACTION_BAR, + new TextComponent(legacy)); } return true; } @Override - protected boolean onAbsentValue(LocalPlayer localPlayer, Location location, Location location1, ApplicableRegionSet applicableRegionSet, String lastValue, MoveType moveType) { + protected boolean onAbsentValue(LocalPlayer localPlayer, Location location, Location location1, + ApplicableRegionSet applicableRegionSet, String lastValue, MoveType moveType) { return true; } } diff --git a/src/main/resources/block2.toml b/src/main/resources/block2.toml new file mode 100644 index 00000000..7a6320f5 --- /dev/null +++ b/src/main/resources/block2.toml @@ -0,0 +1,300 @@ +# Define your protection block below +# Use block type from here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html +# -------------------------------------------------------------------------------------------------- +# If you want to use player heads, you can use "PLAYER_HEAD:player_name" (ex. "PLAYER_HEAD:Notch") +# To use custom player heads, you need the base24 value of the head. On minecraft-heads.com, you will find this value in the Other section under "Value:". +# To use UUIDs for player heads, go to https://sessionserver.mojang.com/session/minecraft/profile/PUT-UUID-HERE and copy the value from the "value" field not including quotes. +# When you have the value, you can set the type to "PLAYER_HEAD:value" +type = "DIAMOND_ORE" + +# Another way to refer to the protection stone +# Can be used for /ps give and /ps get +# Must be one word (no spaces) +alias = "24" + +# Description of the protection block type +# Shows up in /ps get menu +description = "24 block radius protection zone." + +# Whether or not to restrict obtaining of the protection stone to only /ps get and /ps give and custom crafting recipes. +# Other ways to obtain this block (ex. mining) will not work as a protection stone. +# Useful to allow the protection block to only be obtained from a shop or command. +# Set to "false" if you want to allow players to obtain a protection stone naturally +restrict_obtaining = true + +# Enable or disable the use of this protection stone in specific worlds +# "blacklist" mode prevents this protect block from being used in the worlds in "worlds" +# "whitelist" mode allows this protect block to only be used in the worlds in "worlds" +# Can be overridden with protectionstones.admin permission (including OP)! +world_list_type = "blacklist" +worlds = [ + "exampleworld1", + "exampleworld2" +] + +# Whether or not to actually restrict the protection stone from being placed when the world is restricted (in blacklist/whitelist) +# The block will place normally, without PS behaviour. +prevent_block_place_in_restricted_world = true + +# Whether or not to allow the block to be placed in the wild. +# If set to false, the protection block can only be placed in existing regions. +allow_placing_in_wild = true + +# Whether or not to allow the block to bypass the WorldGuard passthrough flag. +# This allows the protection block to be placed even if WorldGuard prevents block placing in the wild. +placing_bypasses_wg_passthrough = true + +[region] + # Minimum distance between claims (that aren't owned by the same owner), measured from the protection block to the edge of another region + # You will probably have to change this between blocks, since the region sizes will be different + # Set to -1 for no minimum, but will still check for overlapping regions + distance_between_claims = -1 + + # Protection radius of block (radius of 24 -> 129 x 129 region) + # Set y_radius to -1 if you want it to protect for all y levels. + # y_radius must be -1 if you are allowing the region to be merged ("allow_merging" option) + x_radius = 24 + y_radius = -1 + z_radius = 24 + + # Enables "chunk snapping mode", where the region boundaries will be determined by the chunk the block is in, and the + # chunk_radius (how many chunks away from the center chunk). + # Allows players to not have to worry about the exact placement of their block, and removes the issue of messy overlapping + # regions (as they all conform to chunk boundaries). + # Set to -1 to disable, and any number larger than or equal to 1 to enable. + # Note: If enabled, x_radius and z_radius will be ignored! + chunk_radius = -1 + + # How many blocks to offset the default location of /ps home from the protection block + home_x_offset = 0.0 + home_y_offset = 1.0 + home_z_offset = 0.0 + + # Specify the default flags to be set when a new protected region is created. + # Can use -g [group] before the flag to set group flags (ex. -g members pvp deny). + # Can use PlaceholderAPI placeholders in string flags (ex. greeting, farewell). + flags = [ + "pvp deny", + "tnt deny", + "greeting Entering %player%'s protected area", + "farewell Leaving &b&l%player%'s &f&lprotected area", + "greeting-action Entering %player%'s protected area", + "farewell-action Leaving &b&l%player%'s &f&lprotected area", + "creeper-explosion deny", + "wither-damage deny", + "ghast-fireball deny", + ] + + # List all the flags that can be set by region owners. + # If you want to whitelist the groups that can be set as well, use -g (ex. "-g all,members pvp" restricts it to no group flag, and members group) + # "-g all pvp" - Prevents players setting the group to nonmembers, and being invulnerable to attacks. + allowed_flags = [ + "-g all pvp", + "greeting", + "greeting-title", + "greeting-action", + "farewell", + "farewell-title", + "farewell-action", + "mob-spawning", + "creeper-explosion", + "wither-damage", + "ghast-fireball", + ] + + # Which flags to hide from /ps info + hidden_flags_from_info = [ + "ps-name", + "ps-merged-regions", + "ps-merged-regions-types", + "ps-block-material", + "ps-price", + "ps-landlord", + "ps-tenant", + "ps-rent-period", + "ps-rent-last-paid", + "ps-for-sale", + "ps-rent-settings", + "ps-tax-payments-due", + "ps-tax-last-payment-added", + "ps-tax-autopayer" + ] + + # Default priority type for this block type protection stone + priority = 0 + + # Whether or not to allow creation of regions that overlap other regions you don't own + # This is dangerous, so think about this carefully if you set it to true. + allow_overlap_unowned_regions = false + + # Whether or not to allow players to create other regions that overlap this region. + # "owner" - only allow owners to overlap this (default) + # "member" - allow members and owners to overlap this region type. (useful for city plots) + # "all" - allow all players to overlap this region type. + # "none" - no players, not even the owners of the region can overlap it + # allow_overlap_unowned_regions does not need to be true for this to work. + allow_other_regions_to_overlap = "owner" + + # Whether or not to allow this regions created with this block to merge with other regions + # allow_merging_regions must be set to true in config.toml + allow_merging = true + + # Allowed types of regions to merge into (referred to by alias) + # Be sure to add the alias of this current region type to allow merging with it ex. ["24"] + # Add "all" if you want to allow this region to merge into any region + allowed_merging_into_types = [ + "all" + ] + +[block_data] + # Name given to protection block when obtained with /ps give or /ps get + # Also affects custom crafted items (see custom_recipe) + # Leave as '' for no name + display_name = "&a&m<---&r&b 24 Radius Protection Block &r&a&m--->" + + # Lore given to protection block when obtained with /ps give or /ps get + # Also affects custom crafted items (see custom_recipe) + # Leave as [] for no lore + lore = [ + "&It's beautiful, isn't it?", + ] + + # Whether the item should have an "glow/enchant" effect look when in a player's inventory. + enchanted_effect = false + + # Add price when using /ps get + # Must have compatible economy plugin (requires Vault, ie. Essentials) + # Must be a decimal (ex. not 10, but 10.0) + price = 0.0 + + # Whether or not to allow crafting this item using a custom recipe + # Useful to allow crafting the item when restrict_obtaining is set to true + allow_craft_with_custom_recipe = false + # Specify the custom crafting recipe below + # You must fill the item spots with names from here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # You can also use other protection stone items as ingredients in the recipe, in the format PROTECTION_STONES:alias + # Make sure that you set allow_use_in_crafting for that block to true, or else you can't use it in crafting + # If you want air, you can just leave the spot as "" + custom_recipe = [ + ["", "STONE", ""], + ["STONE", "EMERALD", "STONE"], + ["", "STONE", ""] + ] + # Amount of the protection item to give when crafted + recipe_amount = 1 + + # The custom model data of the block item, useful for resource packs. Set to -1 to disable. + custom_model_data = -1 + +[economy] + # Taxes must be enabled in config.toml first (tax_enabled) + # The amount to tax the region per tax cycle. + # Must be a decimal (ex. not 10, but 10.0) + tax_amount = 0.0 + + # The amount of seconds between tax cycles. Set to -1 to disable taxes. + tax_period = -1 + + # Amount of time to pay taxes in seconds after tax cycle before there is punishment. + tax_payment_time = 82400 + + # Automatically set the player that created the region as the taxpayer. + start_with_tax_autopay = true + + # What role tenants should be added as (for rents). It can either be "owner" or "member". + tenant_rent_role = "member" + + # Should the landlords of rented out regions still be an owner while it is rented out? + landlord_still_owner = false + +[behaviour] + # Hide protection stone right away when placed? + auto_hide = false + + # Whether or not to automatically merge into other regions when placed if there is only one overlapping and allow_merging is true + auto_merge = false + + # Disable returning the block when removed/unclaimed? + no_drop = false + + # Prevents piston pushing of the block. Recommended to keep as true. + prevent_piston_push = true + + # Prevents the block from being destroyed when exploded. + # Recommended to keep true to prevent players from exploiting more protection stones with /ps unhide (when the block is destroyed) + prevent_explode = true + + # Destroys the protection stone region when block is exploded. Can be useful for PVP/Factions servers. + # prevent_explode must be false for this to work. + destroy_region_when_explode = false + + # Silk Touch: if true, ore-blocks that are also configured by ProtectionStones will disallow Silk Touch drops + # This was the old behaviour to prevent natural obtaining of the protection stone. + # Recommended to keep false if "Restrict Obtaining" (the new way) is true + prevent_silk_touch = false + + # Set cost for when a protection block is placed (separate from /ps get cost) + cost_to_place = 0.0 + + # Allow protect block item to be smelt in furnaces + allow_smelt_item = false + + # Allows the protection block to be used in crafting recipes + # You may want it set to false to prevent players decomposing its elements + allow_use_in_crafting = false + +[player] + # Whether or not to allow breaking the protection block with a shift-right click + # Useful if the protection block is unbreakable (bedrock, command block), etc. + allow_shift_right_break = false + + # Whether or not to prevent teleporting into a protected region if the player doesn't own it (except with ender pearl and chorus fruit) + # Does not prevent entry, use the flag "entry deny" for preventing entry. + # Bypass with protectionstones.tp.bypassprevent + prevent_teleport_in = false + + # Can't move for x seconds before teleporting with /ps home or /ps tp. Can be disabled with 0. + # Option to teleport only if player stands still. + # Can override with permission protectionstones.tp.bypasswait + no_moving_when_tp_waiting = true + tp_waiting_seconds = 0 + + # Whether or not to prevent obtaining this block through /ps get. + # Ignored with protectionstones.admin + prevent_ps_get = false + + # Whether or not to prevent this region type from showing up in /ps home, and allowing teleport. + # Note: admins can still use /ps tp to this region type + prevent_ps_home = false + + # Extra permission required to place this specific protection block (you still need protectionstones.create) + # Also applies to /ps get (you still need protectionstones.get) + # '' for no extra permission + permission = '' + +[event] + + # Events section + # ~~~~~~~~~~~~~~ + # For each line on events, it is the format 'type: action' + # The following are accepted types: + # player_command - Execute command by player that caused event (won't execute if not applicable) + # console_command - Execute command by console + # message - Send message to player or console if applicable (colour support with &) + # global_message - Send message to all players and console (colour support with &) + # console_message - Send message to console (colour support with &) + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # Whether or not to enable event tracking (API events will still be enabled) + enable = false + + # Execute commands when a region is created (ex. player place protection block) + # Variables: %player%, %world%, %region%, %block_x%, %block_y%, %block_z% + on_region_create = [ + 'global_message: &l%player% created the region %region%!', + ] + + # Execute commands when a region is destroyed (ex. when player destroy protection block) + # Variables: %player%, %world%, %region%, %block_x%, %block_y%, %block_z% + on_region_destroy = [ + 'console_command: say %player% has destroyed region %region%!', + ] diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index d29b6003..66ec20d6 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,8 @@ name: ProtectionStones version: ${version} description: ${description} -authors: [EspiDev] +authors: [EspiDev 2018 - 2026, jerzean +2026, Dianelito] +contribuitors: https://github.com/espidev/ProtectionStones/graphs/contributors depend: [WorldGuard, WorldEdit] softdepend: [Vault, PlaceholderAPI, LuckPerms] From 3dbb05eaa90bad011dedf4f233f2593757ed269c Mon Sep 17 00:00:00 2001 From: Dianelito <95977637+Dianelito@users.noreply.github.com> Date: Sun, 15 Feb 2026 20:50:31 +0000 Subject: [PATCH 2/2] fix that the minimessages in the chat weren't working --- .../espi/protectionstones/FlagHandler.java | 55 +++++++--- .../protectionstones/ProtectionStones.java | 5 +- .../flags/FarewellFlagHandler.java | 22 ++-- .../flags/GreetingFlagHandler.java | 16 +-- .../flags/PSMessageFlagHandler.java | 100 ++++++++++++++++++ 5 files changed, 156 insertions(+), 42 deletions(-) create mode 100644 src/main/java/dev/espi/protectionstones/flags/PSMessageFlagHandler.java diff --git a/src/main/java/dev/espi/protectionstones/FlagHandler.java b/src/main/java/dev/espi/protectionstones/FlagHandler.java index 28e46b20..12539779 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(); } @@ -100,6 +107,18 @@ static void registerHandlers() { SessionManager sessionManager = WorldGuard.getInstance().getPlatform().getSessionManager(); sessionManager.registerHandler(GreetingFlagHandler.FACTORY, ExitFlag.FACTORY); sessionManager.registerHandler(FarewellFlagHandler.FACTORY, ExitFlag.FACTORY); + + // Register handlers for standard WorldGuard flags to support MiniMessage + try { + sessionManager.registerHandler(new dev.espi.protectionstones.flags.PSMessageFlagHandler.Factory( + (Flag) dev.espi.protectionstones.utils.WGUtils.getFlagRegistry().get("greeting"), false, + false), ExitFlag.FACTORY); + sessionManager.registerHandler(new dev.espi.protectionstones.flags.PSMessageFlagHandler.Factory( + (Flag) dev.espi.protectionstones.utils.WGUtils.getFlagRegistry().get("farewell"), true, + false), ExitFlag.FACTORY); + } catch (Exception ignored) { + } + } // adds flag permissions for ALL registered WorldGuard flags @@ -123,12 +142,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 +174,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 +183,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(); } } @@ -190,7 +213,8 @@ static void initDefaultFlagsForBlock(PSProtectBlock b) { } // get settings (after flag name) - for (int i = startInd; i < split.length; i++) settings += split[i] + " "; + for (int i = startInd; i < split.length; i++) + settings += split[i] + " "; settings = settings.trim(); // if the setting is set to -e, change to empty flag @@ -204,7 +228,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 +240,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/ProtectionStones.java b/src/main/java/dev/espi/protectionstones/ProtectionStones.java index 3d997c7b..d2ecebee 100644 --- a/src/main/java/dev/espi/protectionstones/ProtectionStones.java +++ b/src/main/java/dev/espi/protectionstones/ProtectionStones.java @@ -565,7 +565,8 @@ public static ItemStack createProtectBlockItem(PSProtectBlock b) { } // add identifier for protection stone created items - im.getCustomTagContainer().setCustomTag(new NamespacedKey(plugin, "isPSBlock"), ItemTagType.BYTE, (byte) 1); + im.getPersistentDataContainer().set(new NamespacedKey(plugin, "isPSBlock"), + org.bukkit.persistence.PersistentDataType.BYTE, (byte) 1); is.setItemMeta(im); @@ -635,7 +636,7 @@ public void onEnable() { try { new Metrics(this, 4071); } catch (Throwable t) { - getLogger().warning("bStats metrics disabled due to error: " + t.getMessage()); + getLogger().log(java.util.logging.Level.WARNING, "bStats metrics disabled due to error", t); } // load command arguments diff --git a/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java b/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java index 013fa13c..f98038ad 100644 --- a/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java +++ b/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java @@ -27,10 +27,8 @@ import dev.espi.protectionstones.FlagHandler; import dev.espi.protectionstones.PSRegion; import dev.espi.protectionstones.ProtectionStones; -import net.md_5.bungee.api.ChatMessageType; -import net.md_5.bungee.api.chat.TextComponent; +import dev.espi.protectionstones.ProtectionStones; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.entity.Player; // farewell-action flag @@ -66,12 +64,9 @@ protected boolean onSetValue(LocalPlayer localPlayer, Location from, Location to } } if (p != null && lastValue != null && !lastValue.equals(currentValue)) { - net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage - .miniMessage(); - net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer lcs = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer - .builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build(); - String legacy = lcs.serialize(mm.deserialize(dev.espi.protectionstones.PSL.legacyToMiniMessage(lastValue))); - p.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(legacy)); + ProtectionStones.getAdventure().player(p) + .sendActionBar(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage() + .deserialize(dev.espi.protectionstones.PSL.legacyToMiniMessage(lastValue))); } return true; } @@ -81,12 +76,9 @@ protected boolean onAbsentValue(LocalPlayer localPlayer, Location location, Loca ApplicableRegionSet applicableRegionSet, String lastValue, MoveType moveType) { Player p = Bukkit.getPlayer(localPlayer.getUniqueId()); if (p != null && lastValue != null) { - net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage - .miniMessage(); - net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer lcs = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer - .builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build(); - String legacy = lcs.serialize(mm.deserialize(dev.espi.protectionstones.PSL.legacyToMiniMessage(lastValue))); - p.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(legacy)); + ProtectionStones.getAdventure().player(p) + .sendActionBar(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage() + .deserialize(dev.espi.protectionstones.PSL.legacyToMiniMessage(lastValue))); } return true; } diff --git a/src/main/java/dev/espi/protectionstones/flags/GreetingFlagHandler.java b/src/main/java/dev/espi/protectionstones/flags/GreetingFlagHandler.java index bc35f9a0..1c889567 100644 --- a/src/main/java/dev/espi/protectionstones/flags/GreetingFlagHandler.java +++ b/src/main/java/dev/espi/protectionstones/flags/GreetingFlagHandler.java @@ -23,10 +23,9 @@ import com.sk89q.worldguard.session.handler.FlagValueChangeHandler; import com.sk89q.worldguard.session.handler.Handler; import dev.espi.protectionstones.FlagHandler; -import net.md_5.bungee.api.ChatMessageType; -import net.md_5.bungee.api.chat.TextComponent; +import dev.espi.protectionstones.ProtectionStones; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; // greeting-action flag public class GreetingFlagHandler extends FlagValueChangeHandler { @@ -54,14 +53,9 @@ protected boolean onSetValue(LocalPlayer localPlayer, Location location, Locatio ApplicableRegionSet applicableRegionSet, String currentValue, String lastValue, MoveType moveType) { if (currentValue != null && !currentValue.equals(lastValue) && Bukkit.getPlayer(localPlayer.getUniqueId()) != null) { - net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage - .miniMessage(); - net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer lcs = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer - .builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build(); - String legacy = lcs - .serialize(mm.deserialize(dev.espi.protectionstones.PSL.legacyToMiniMessage(currentValue))); - Bukkit.getPlayer(localPlayer.getUniqueId()).spigot().sendMessage(ChatMessageType.ACTION_BAR, - new TextComponent(legacy)); + ProtectionStones.getAdventure().player(Bukkit.getPlayer(localPlayer.getUniqueId())) + .sendActionBar(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage() + .deserialize(dev.espi.protectionstones.PSL.legacyToMiniMessage(currentValue))); } return true; } diff --git a/src/main/java/dev/espi/protectionstones/flags/PSMessageFlagHandler.java b/src/main/java/dev/espi/protectionstones/flags/PSMessageFlagHandler.java new file mode 100644 index 00000000..826cf950 --- /dev/null +++ b/src/main/java/dev/espi/protectionstones/flags/PSMessageFlagHandler.java @@ -0,0 +1,100 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package dev.espi.protectionstones.flags; + +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.session.MoveType; +import com.sk89q.worldguard.session.Session; +import com.sk89q.worldguard.session.handler.FlagValueChangeHandler; +import com.sk89q.worldguard.session.handler.Handler; +import dev.espi.protectionstones.PSL; +import dev.espi.protectionstones.ProtectionStones; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class PSMessageFlagHandler extends FlagValueChangeHandler { + + private final boolean isFarewell; + private final boolean isActionBar; + + public static class Factory extends Handler.Factory { + private final Flag flag; + private final boolean isFarewell; + private final boolean isActionBar; + + public Factory(Flag flag, boolean isFarewell, boolean isActionBar) { + this.flag = flag; + this.isFarewell = isFarewell; + this.isActionBar = isActionBar; + } + + @Override + public PSMessageFlagHandler create(Session session) { + return new PSMessageFlagHandler(session, flag, isFarewell, isActionBar); + } + } + + public PSMessageFlagHandler(Session session, Flag flag, boolean isFarewell, boolean isActionBar) { + super(session, flag); + this.isFarewell = isFarewell; + this.isActionBar = isActionBar; + } + + @Override + protected void onInitialValue(LocalPlayer localPlayer, ApplicableRegionSet applicableRegionSet, String s) { + } + + private void sendMessage(LocalPlayer localPlayer, String message) { + if (message == null || message.isEmpty()) + return; + Player p = Bukkit.getPlayer(localPlayer.getUniqueId()); + if (p == null) + return; + + var component = MiniMessage.miniMessage().deserialize(PSL.legacyToMiniMessage(message)); + var audience = ProtectionStones.getAdventure().player(p); + + if (isActionBar) { + audience.sendActionBar(component); + } else { + audience.sendMessage(component); + } + } + + @Override + protected boolean onSetValue(LocalPlayer localPlayer, Location from, Location to, ApplicableRegionSet toSet, + String currentValue, String lastValue, MoveType moveType) { + if (!isFarewell && currentValue != null && !currentValue.equals(lastValue)) { + sendMessage(localPlayer, currentValue); + } else if (isFarewell && lastValue != null && !lastValue.equals(currentValue)) { + sendMessage(localPlayer, lastValue); + } + return true; + } + + @Override + protected boolean onAbsentValue(LocalPlayer localPlayer, Location from, Location to, ApplicableRegionSet toSet, + String lastValue, MoveType moveType) { + if (isFarewell && lastValue != null) { + sendMessage(localPlayer, lastValue); + } + return true; + } +}