From 6370ef6bd2cb7bac79ccef49c8f3fc0d93d886d3 Mon Sep 17 00:00:00 2001 From: Jerzean Date: Fri, 11 Apr 2025 19:45:26 -0400 Subject: [PATCH 1/9] fix issues with /ps flag updated /ps admin to use textcomponent (now has clickable commands) --- .../commands/ArgAdminHelp.java | 208 ++++++++++++++++-- .../protectionstones/commands/ArgFlag.java | 26 ++- .../event/PSBreakProtectBlockEvent.java | 2 - src/main/resources/plugin.yml | 2 +- 4 files changed, 200 insertions(+), 38 deletions(-) diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java index c224df2b..30eb04d1 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java @@ -16,7 +16,8 @@ package dev.espi.protectionstones.commands; import dev.espi.protectionstones.ProtectionStones; -import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.ChatColor; @@ -24,32 +25,191 @@ public class ArgAdminHelp { - private static void send(CommandSender p, String text, String info) { - TextComponent tc = new TextComponent(text); - tc.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(info).create())); - p.spigot().sendMessage(tc); + // Provided helper method to generate a dot string. + private static String getDots(int num) { + StringBuilder str = new StringBuilder(" " + ChatColor.DARK_GRAY); + for (int i = 0; i < num; i++) { + str.append("."); + } + return str.toString(); + } + + /** + * Sends a help message to the sender with a right-aligned clickable tag. + * This version allows you to manually supply the exact number of dots. + * + * @param p The CommandSender. + * @param text The left-side command text (short form). + * @param info The detailed info for hover (contains full command usage). + * @param clickCommand The command to run or suggest (should be unformatted). + * @param run If true the tag will execute the command; if false it will suggest it. + * @param manualDots The manual number of dots (filler) to add before the tag. + */ + private static void send(CommandSender p, String text, String info, String clickCommand, boolean run, int manualDots) { + // Create the main text component from legacy text. + BaseComponent[] mainComponents = TextComponent.fromLegacyText(text); + TextComponent mainText = new TextComponent(""); + for (BaseComponent component : mainComponents) { + mainText.addExtra(component); + } + + // Determine the tag label. + String tagLabel = run ? "[Run]" : "[Prep]"; + + // Use the provided manual dot count. + String dotsString = getDots(manualDots); + + // Create a dots component. + BaseComponent[] dotsComponents = TextComponent.fromLegacyText(dotsString); + TextComponent dotsText = new TextComponent(""); + for (BaseComponent component : dotsComponents) { + dotsText.addExtra(component); + } + + // Create the clickable tag component. + BaseComponent[] tagComponents = TextComponent.fromLegacyText(tagLabel); + TextComponent clickableComponent = new TextComponent(""); + for (BaseComponent component : tagComponents) { + clickableComponent.addExtra(component); + } + + // Create the hover event from the info text. + BaseComponent[] hoverComponents = TextComponent.fromLegacyText(info); + HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponents); + + // Set the same hover event on all parts. + mainText.setHoverEvent(hoverEvent); + dotsText.setHoverEvent(hoverEvent); + clickableComponent.setHoverEvent(hoverEvent); + + // Setup the click event on the [Run/Prep] tag. + if (run) { + clickableComponent.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, ChatColor.stripColor(clickCommand))); + } else { + clickableComponent.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, ChatColor.stripColor(clickCommand))); + } + + // Append the dots and clickable tag to the main text. + mainText.addExtra(dotsText); + mainText.addExtra(clickableComponent); + + // Send the assembled message. + p.spigot().sendMessage(mainText); } static boolean argumentAdminHelp(CommandSender p, String[] args) { - String bc = "/" + ProtectionStones.getInstance().getConfigOptions().base_command; - - p.sendMessage(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET + " PS Admin Help " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "=====\n" + ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps admin help"); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin version", "Show the version number of the plugin."); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin hide", "Hide all of the protection stone blocks in the world you are in."); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin unhide", "Unhide all of the protection stone blocks in the world you are in."); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin cleanup remove [days] [-t typealias (optional)] [world (console)]", "Remove inactive players that haven't joined within the last [days] days from protected regions in the world you are in (or specified). Then, remove any regions with no owners left."); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin cleanup disown [days] [-t typealias (optional)] [world (console)]", "Remove inactive players that haven't joined within the last [days] days from protected regions in the world you are in (or specified)."); - send(p, ArgAdmin.getFlagHelp(), "Set a flag for all protection stone regions in a world."); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin lastlogon [player]", "Get the last time a player logged on."); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin lastlogons", "List all of the last logons of each player."); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin stats [player (optional)]", "Show some statistics of the plugin."); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin recreate", "Recreate all PS regions using radius set in config."); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin debug", "Toggles debug mode."); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin settaxautopayers", "Add a tax autopayer for every region on the server that does not have one."); - send(p, ArgAdmin.getForceMergeHelp(), "Merge overlapping PS regions together if they have the same owners, members and flags."); - send(p, ArgAdmin.getChangeBlockHelp(), "Change all of the PS blocks and regions in a world to a different block. Both blocks must be configured in config."); - send(p, ArgAdmin.getChangeRegionTypeHelp(), "Change the internal type of all PS regions of a certain type. Useful for error correction."); - send(p, ChatColor.AQUA + "> " + ChatColor.GRAY + bc + " admin fixregions", "Use this command to recalculate block types for PS regions in a world."); + String baseCommand = ProtectionStones.getInstance().getConfigOptions().base_command; + String bc = "/" + baseCommand; + String tx = ChatColor.AQUA + "> " + ChatColor.GRAY + bc; + + // Display header as before. + p.sendMessage(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "===============" + + ChatColor.RESET + " PS Admin Help " + + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "===============\n"); + + // Now you can manually supply each dot count. + send(p, + tx + " admin version", + "Show the version number of the plugin.\n\n" + bc + " admin version", + baseCommand + " admin version", + true, 61); + + send(p, + tx + " admin hide", + "Hide all of the protection stone blocks in the world you are in.\n\n" + bc + " admin hide", + bc + " admin hide", + true, 70); + + send(p, + tx + " admin unhide", + "Unhide all of the protection stone blocks in the world you are in.\n\n" + bc + " admin unhide", + bc + " admin unhide", + true, 64); + + send(p, + tx + " admin cleanup remove", + "Remove inactive players that haven't joined within the last [days] days from protected regions in the world you are in (or specified). Then, remove any regions with no owners left.\n\n" + + bc + " admin cleanup remove [days] [-t typealias (optional)] [world (console)]", + bc + " admin cleanup remove", + false, 39); + + send(p, + tx + " admin cleanup disown", + "Remove inactive players that haven't joined within the last [days] days from protected regions in the world you are in (or specified).\n\n" + + bc + " admin cleanup disown", + bc + " admin cleanup disown", + false, 41); + + send(p, + tx + " admin flag", + "Set a flag for all protection stone regions in a world.\n\n" + + bc + " admin flag [world] [flagname] [value|null|default]", + bc + " admin flag [world] [flagname] [value|null|default]", + false, 69); + + send(p, + tx + " admin lastlogon", + "Get the last time a player logged on.\n\n" + bc + " admin lastlogon [player]", + bc + " admin lastlogon", + false, 56); + + send(p, + tx + " admin lastlogons", + "List all of the last logons of each player.\n\n" + bc + " admin lastlogons", + bc + " admin lastlogons", + true, 55); + + send(p, + tx + " admin stats", + "Show some statistics of the plugin.\n\n" + bc + " admin stats [player (optional)]", + bc + " admin stats", + true, 68); + + send(p, + tx + " admin recreate", + "Recreate all PS regions using radius set in config.\n\n" + bc + " admin recreate", + bc + " admin recreate", + true, 58); + + send(p, + tx + " admin debug", + "Toggle debug mode.\n\n" + bc + " admin debug", + bc + " admin debug", + false, 64); + + send(p, + tx + " admin settaxautopayers", + "Add a tax autopayer for every region on the server that does not have one.\n\n" + bc + " admin settaxautopayers", + bc + " admin settaxautopayers", + false, 34); + + send(p, + tx + " admin forcemerge", + "Merge overlapping PS regions together if they have the same owners, members and flags.\n\n" + + bc + " admin forcemerge [world]", + bc + " admin forcemerge [world]", + true, 52); + + send(p, + tx + " admin changeblock", + "Change all of the PS blocks and regions in a world to a different block. Both blocks must be configured in config.\n\n" + + bc + " admin changeblock [world] [oldtypealias] [newtypealias]", + bc + " admin changeblock [world] [oldtypealias] [newtypealias]", + false, 49); + + send(p, + tx + " admin changeregiontype", + "Change the internal type of all PS regions of a certain type. Useful for error correction.\n\n" + + bc + " admin changeregiontype [world] [oldtype] [newtype]", + bc + " admin changeregiontype [world] [oldtype] [newtype]", + false, 34); + + send(p, + tx + " admin fixregions", + "Use this command to recalculate block types for PS regions in a world.\n\n" + bc + " admin fixregions", + bc + " admin fixregions", + true, 56); + p.sendMessage(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "============================================="); return true; } diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgFlag.java b/src/main/java/dev/espi/protectionstones/commands/ArgFlag.java index 829bdad2..70dc62ca 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgFlag.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgFlag.java @@ -138,14 +138,6 @@ private boolean openFlagGUI(Player p, PSRegion r, int page) { deny.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " deny")); } - // HACK: Prevent pvp flag value from being changed to none/null, if it is set to a value with the group flag set to all - if (flag.equalsIgnoreCase("pvp") && isGroupValueAll) { - if (fValue == StateFlag.State.DENY) { - deny.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.FLAG_PREVENT_EXPLOIT_HOVER.msg()).create())); - } else if (fValue == StateFlag.State.ALLOW) { - allow.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.FLAG_PREVENT_EXPLOIT_HOVER.msg()).create())); - } - } flagLine.addExtra(allow); flagLine.addExtra(" "); @@ -193,15 +185,27 @@ private boolean openFlagGUI(Player p, PSRegion r, int page) { // set hover and click task for flag group BaseComponent[] hover; - if (fValue == null) { + // HACK: Prevent pvp flag value from being changed to none/null + // Special handling for "pvp" flag with "all" group, disabling interaction. + if (flag.equalsIgnoreCase("pvp") && groupfValue.equalsIgnoreCase("all")) { + hover = new ComponentBuilder(PSL.FLAG_PREVENT_EXPLOIT_HOVER.msg()).create(); + // Remove click action to fully disable changing this group. + groupChange.setClickEvent(null); + } else if (fValue == null) { hover = new ComponentBuilder(PSL.FLAG_GUI_HOVER_CHANGE_GROUP_NULL.msg()).create(); } else { hover = new ComponentBuilder(PSL.FLAG_GUI_HOVER_CHANGE_GROUP.msg().replace("%group%", nextGroup)).create(); } - if (!nextGroup.equals(groupfValue)) { // only display hover message if the group is not the same + + // Always set hover if the flag is pvp and group is "all" + if (flag.equalsIgnoreCase("pvp") && groupfValue.equalsIgnoreCase("all")) { + hover = new ComponentBuilder(PSL.FLAG_PREVENT_EXPLOIT_HOVER.msg()).create(); + groupChange.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hover)); + groupChange.setClickEvent(null); // Disable click event explicitly + } else if (!nextGroup.equals(groupfValue)) { groupChange.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hover)); + groupChange.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + "-g " + nextGroup + " " + page + ":" + flag + " " + fValue)); } - groupChange.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + "-g " + nextGroup + " " + page + ":" + flag + " " + fValue)); flagLine.addExtra(groupChange); // send message diff --git a/src/main/java/dev/espi/protectionstones/event/PSBreakProtectBlockEvent.java b/src/main/java/dev/espi/protectionstones/event/PSBreakProtectBlockEvent.java index 289a0fff..04455cda 100644 --- a/src/main/java/dev/espi/protectionstones/event/PSBreakProtectBlockEvent.java +++ b/src/main/java/dev/espi/protectionstones/event/PSBreakProtectBlockEvent.java @@ -6,7 +6,6 @@ import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; import java.util.Objects; @@ -65,7 +64,6 @@ public void setCancelled(boolean cancel) { isCancelled = cancel; } - @NotNull @Override public HandlerList getHandlers() { return HANDLERS; diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index c5ad41a3..14c4232b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ name: ProtectionStones version: ${version} description: ${description} -authors: [EspiDev] +authors: [EspiDev, Jerzean] depend: [WorldGuard, WorldEdit] softdepend: [Vault, PlaceholderAPI, LuckPerms] From 1cc0b4fea093fc2900a67e564b33fe80d95719d2 Mon Sep 17 00:00:00 2001 From: Jerzean Date: Sat, 12 Apr 2025 21:54:29 -0400 Subject: [PATCH 2/9] fixes --- README.md | 2 +- .../commands/ArgAdminHelp.java | 97 +++++-------------- .../protectionstones/commands/ArgFlag.java | 4 +- 3 files changed, 26 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 595aa439..fe38dba2 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.5 +* ProtectionStones 2.10.6 * Spigot 1.20.6+ * WorldGuard 7.0.9+ * WorldEdit 7.2.6+ diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java index 30eb04d1..0f5a0a00 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java @@ -25,27 +25,7 @@ public class ArgAdminHelp { - // Provided helper method to generate a dot string. - private static String getDots(int num) { - StringBuilder str = new StringBuilder(" " + ChatColor.DARK_GRAY); - for (int i = 0; i < num; i++) { - str.append("."); - } - return str.toString(); - } - - /** - * Sends a help message to the sender with a right-aligned clickable tag. - * This version allows you to manually supply the exact number of dots. - * - * @param p The CommandSender. - * @param text The left-side command text (short form). - * @param info The detailed info for hover (contains full command usage). - * @param clickCommand The command to run or suggest (should be unformatted). - * @param run If true the tag will execute the command; if false it will suggest it. - * @param manualDots The manual number of dots (filler) to add before the tag. - */ - private static void send(CommandSender p, String text, String info, String clickCommand, boolean run, int manualDots) { + private static void send(CommandSender p, String text, String info, String clickCommand, boolean run) { // Create the main text component from legacy text. BaseComponent[] mainComponents = TextComponent.fromLegacyText(text); TextComponent mainText = new TextComponent(""); @@ -53,46 +33,16 @@ private static void send(CommandSender p, String text, String info, String click mainText.addExtra(component); } - // Determine the tag label. - String tagLabel = run ? "[Run]" : "[Prep]"; - - // Use the provided manual dot count. - String dotsString = getDots(manualDots); - - // Create a dots component. - BaseComponent[] dotsComponents = TextComponent.fromLegacyText(dotsString); - TextComponent dotsText = new TextComponent(""); - for (BaseComponent component : dotsComponents) { - dotsText.addExtra(component); - } - - // Create the clickable tag component. - BaseComponent[] tagComponents = TextComponent.fromLegacyText(tagLabel); - TextComponent clickableComponent = new TextComponent(""); - for (BaseComponent component : tagComponents) { - clickableComponent.addExtra(component); - } - - // Create the hover event from the info text. + // Create the hover event from the info text, add click event after BaseComponent[] hoverComponents = TextComponent.fromLegacyText(info); - HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponents); - - // Set the same hover event on all parts. - mainText.setHoverEvent(hoverEvent); - dotsText.setHoverEvent(hoverEvent); - clickableComponent.setHoverEvent(hoverEvent); - - // Setup the click event on the [Run/Prep] tag. + mainText.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponents)); + //toggle for running on mouse click, currently disabled if (run) { - clickableComponent.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, ChatColor.stripColor(clickCommand))); + mainText.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, ChatColor.stripColor(clickCommand))); } else { - clickableComponent.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, ChatColor.stripColor(clickCommand))); + mainText.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, ChatColor.stripColor(clickCommand))); } - // Append the dots and clickable tag to the main text. - mainText.addExtra(dotsText); - mainText.addExtra(clickableComponent); - // Send the assembled message. p.spigot().sendMessage(mainText); } @@ -102,113 +52,112 @@ static boolean argumentAdminHelp(CommandSender p, String[] args) { String bc = "/" + baseCommand; String tx = ChatColor.AQUA + "> " + ChatColor.GRAY + bc; - // Display header as before. p.sendMessage(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "===============" + ChatColor.RESET + " PS Admin Help " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "===============\n"); - // Now you can manually supply each dot count. send(p, tx + " admin version", "Show the version number of the plugin.\n\n" + bc + " admin version", baseCommand + " admin version", - true, 61); + false); send(p, tx + " admin hide", "Hide all of the protection stone blocks in the world you are in.\n\n" + bc + " admin hide", bc + " admin hide", - true, 70); + false); send(p, tx + " admin unhide", "Unhide all of the protection stone blocks in the world you are in.\n\n" + bc + " admin unhide", bc + " admin unhide", - true, 64); + false); send(p, tx + " admin cleanup remove", "Remove inactive players that haven't joined within the last [days] days from protected regions in the world you are in (or specified). Then, remove any regions with no owners left.\n\n" + bc + " admin cleanup remove [days] [-t typealias (optional)] [world (console)]", bc + " admin cleanup remove", - false, 39); + false); send(p, tx + " admin cleanup disown", "Remove inactive players that haven't joined within the last [days] days from protected regions in the world you are in (or specified).\n\n" + bc + " admin cleanup disown", bc + " admin cleanup disown", - false, 41); + false); send(p, tx + " admin flag", "Set a flag for all protection stone regions in a world.\n\n" + bc + " admin flag [world] [flagname] [value|null|default]", bc + " admin flag [world] [flagname] [value|null|default]", - false, 69); + false); send(p, tx + " admin lastlogon", "Get the last time a player logged on.\n\n" + bc + " admin lastlogon [player]", bc + " admin lastlogon", - false, 56); + false); send(p, tx + " admin lastlogons", "List all of the last logons of each player.\n\n" + bc + " admin lastlogons", bc + " admin lastlogons", - true, 55); + false); send(p, tx + " admin stats", "Show some statistics of the plugin.\n\n" + bc + " admin stats [player (optional)]", bc + " admin stats", - true, 68); + false); send(p, tx + " admin recreate", "Recreate all PS regions using radius set in config.\n\n" + bc + " admin recreate", bc + " admin recreate", - true, 58); + false); send(p, tx + " admin debug", "Toggle debug mode.\n\n" + bc + " admin debug", bc + " admin debug", - false, 64); + false); send(p, tx + " admin settaxautopayers", "Add a tax autopayer for every region on the server that does not have one.\n\n" + bc + " admin settaxautopayers", bc + " admin settaxautopayers", - false, 34); + false); send(p, tx + " admin forcemerge", "Merge overlapping PS regions together if they have the same owners, members and flags.\n\n" + bc + " admin forcemerge [world]", bc + " admin forcemerge [world]", - true, 52); + false); send(p, tx + " admin changeblock", "Change all of the PS blocks and regions in a world to a different block. Both blocks must be configured in config.\n\n" + bc + " admin changeblock [world] [oldtypealias] [newtypealias]", bc + " admin changeblock [world] [oldtypealias] [newtypealias]", - false, 49); + false); send(p, tx + " admin changeregiontype", "Change the internal type of all PS regions of a certain type. Useful for error correction.\n\n" + bc + " admin changeregiontype [world] [oldtype] [newtype]", bc + " admin changeregiontype [world] [oldtype] [newtype]", - false, 34); + false); send(p, tx + " admin fixregions", "Use this command to recalculate block types for PS regions in a world.\n\n" + bc + " admin fixregions", bc + " admin fixregions", - true, 56); + false); + //add footer since it was missing p.sendMessage(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "============================================="); return true; diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgFlag.java b/src/main/java/dev/espi/protectionstones/commands/ArgFlag.java index 70dc62ca..486108d9 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgFlag.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgFlag.java @@ -118,8 +118,8 @@ private boolean openFlagGUI(Player p, PSRegion r, int page) { } // add line based on flag type + boolean isGroupValueAll = groupfValue.equalsIgnoreCase("all") || groupfValue.isEmpty();; if (f instanceof StateFlag) { // allow/deny - boolean isGroupValueAll = groupfValue.equalsIgnoreCase("all") || groupfValue.isEmpty(); TextComponent allow = new TextComponent((fValue == StateFlag.State.ALLOW ? ChatColor.WHITE : ChatColor.DARK_GRAY) + "Allow"), deny = new TextComponent((fValue == StateFlag.State.DENY ? ChatColor.WHITE : ChatColor.DARK_GRAY) + "Deny"); @@ -187,7 +187,7 @@ private boolean openFlagGUI(Player p, PSRegion r, int page) { BaseComponent[] hover; // HACK: Prevent pvp flag value from being changed to none/null // Special handling for "pvp" flag with "all" group, disabling interaction. - if (flag.equalsIgnoreCase("pvp") && groupfValue.equalsIgnoreCase("all")) { + if (flag.equalsIgnoreCase("pvp") && isGroupValueAll) { hover = new ComponentBuilder(PSL.FLAG_PREVENT_EXPLOIT_HOVER.msg()).create(); // Remove click action to fully disable changing this group. groupChange.setClickEvent(null); From 9312c381e454ddde378798ef5357d9a839f982f8 Mon Sep 17 00:00:00 2001 From: Jerzean Date: Sat, 12 Apr 2025 21:58:45 -0400 Subject: [PATCH 3/9] Need to check this, before merging my intellij was being stupid and not working before with it --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 37940957..ad5e3411 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 dev.espi protectionstones - 2.10.5 + 2.10.6 ProtectionStones A grief prevention plugin for Spigot Minecraft servers. https://github.com/espidev/ProtectionStones @@ -207,13 +207,13 @@ org.spigotmc spigot-api - 1.20.6-R0.1-SNAPSHOT + 1.21.4-R0.1-SNAPSHOT provided com.sk89q.worldguard worldguard-bukkit - 7.0.9-SNAPSHOT + 7.1.0-SNAPSHOT provided @@ -231,7 +231,7 @@ com.sk89q.worldedit worldedit-bukkit - 7.2.6-SNAPSHOT + 7.4.0-SNAPSHOT provided From 0e5414a5a1dda94bc488e6a5f93a0cdec45b32ed Mon Sep 17 00:00:00 2001 From: Jerzean Date: Sat, 12 Apr 2025 21:59:44 -0400 Subject: [PATCH 4/9] Need to check this, before merging my intellij was being stupid and not working before with it --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ad5e3411..39ee6ede 100644 --- a/pom.xml +++ b/pom.xml @@ -207,7 +207,7 @@ org.spigotmc spigot-api - 1.21.4-R0.1-SNAPSHOT + 1.21.5-R0.1-SNAPSHOT provided From bb51bdc641e192f7e28fd8cb233eb4a9a1886c86 Mon Sep 17 00:00:00 2001 From: Jerzean Date: Wed, 16 Apr 2025 22:15:35 -0400 Subject: [PATCH 5/9] revert spigot version --- pom.xml | 6 +++--- .../dev/espi/protectionstones/commands/ArgAdminHelp.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 39ee6ede..e8b232eb 100644 --- a/pom.xml +++ b/pom.xml @@ -207,13 +207,13 @@ org.spigotmc spigot-api - 1.21.5-R0.1-SNAPSHOT + 1.20.6-R0.1-SNAPSHOT provided com.sk89q.worldguard worldguard-bukkit - 7.1.0-SNAPSHOT + 7.0.9-SNAPSHOT provided @@ -231,7 +231,7 @@ com.sk89q.worldedit worldedit-bukkit - 7.4.0-SNAPSHOT + 7.2.6-SNAPSHOT provided diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java index 0f5a0a00..344acd3b 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java @@ -36,7 +36,7 @@ private static void send(CommandSender p, String text, String info, String click // Create the hover event from the info text, add click event after BaseComponent[] hoverComponents = TextComponent.fromLegacyText(info); mainText.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponents)); - //toggle for running on mouse click, currently disabled + //toggle for running on mouse click if (run) { mainText.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, ChatColor.stripColor(clickCommand))); } else { @@ -56,6 +56,7 @@ static boolean argumentAdminHelp(CommandSender p, String[] args) { ChatColor.RESET + " PS Admin Help " + ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "===============\n"); + // The run parameter is currently unused by all messages send(p, tx + " admin version", "Show the version number of the plugin.\n\n" + bc + " admin version", @@ -157,7 +158,6 @@ static boolean argumentAdminHelp(CommandSender p, String[] args) { "Use this command to recalculate block types for PS regions in a world.\n\n" + bc + " admin fixregions", bc + " admin fixregions", false); - //add footer since it was missing p.sendMessage(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "============================================="); return true; From 55fe14cf680124572bd6eb177934ffe0dbf2a5e7 Mon Sep 17 00:00:00 2001 From: Jerzean Date: Wed, 17 Sep 2025 19:38:44 -0400 Subject: [PATCH 6/9] Spigot version bump for autocrafter support --- pom.xml | 4 +- .../espi/protectionstones/ListenerClass.java | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e8b232eb..b3df6501 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 @@ -207,7 +207,7 @@ org.spigotmc spigot-api - 1.20.6-R0.1-SNAPSHOT + 1.21.3-R0.1-SNAPSHOT provided diff --git a/src/main/java/dev/espi/protectionstones/ListenerClass.java b/src/main/java/dev/espi/protectionstones/ListenerClass.java index 783de998..bf4024c8 100644 --- a/src/main/java/dev/espi/protectionstones/ListenerClass.java +++ b/src/main/java/dev/espi/protectionstones/ListenerClass.java @@ -53,7 +53,10 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.CraftingRecipe; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.ShapelessRecipe; import java.util.List; @@ -220,6 +223,7 @@ public void onPlayerInteract(PlayerInteractEvent e) { // 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) { + ProtectionStones.getInstance().debug("ListenerClass.java, onBlockBreakLowPriority"); Player p = e.getPlayer(); Block pb = e.getBlock(); @@ -228,6 +232,9 @@ public void onBlockBreakLowPriority(BlockBreakEvent e) { // check if player has permission to break the protection PSRegion r = PSRegion.fromLocation(pb.getLocation()); if (r != null) { + ProtectionStones.getInstance().debug("Player:"+ p.getName()+", Holding:"+ p.getPlayer().getInventory().getItemInMainHand().getType().name()+", Enchants:" +p.getPlayer().getInventory().getItemInMainHand().getEnchantments()); + + String error = checkPermissionToBreakProtection(p, r); if (!error.isEmpty()) { PSL.msg(p, error); @@ -238,6 +245,7 @@ public void onBlockBreakLowPriority(BlockBreakEvent e) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onBlockBreak(BlockBreakEvent e) { + ProtectionStones.getInstance().debug("ListenerClass.java, onBlockBreak"); Player p = e.getPlayer(); Block pb = e.getBlock(); @@ -275,6 +283,7 @@ public void onBlockBreak(BlockBreakEvent e) { @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onFurnaceSmelt(FurnaceSmeltEvent e) { + ProtectionStones.getInstance().debug("ListenerClass.java, onFurnaceSmelt"); // prevent protect block item to be smelt PSProtectBlock options = ProtectionStones.getBlockOptions(e.getSource()); if (options != null && !options.allowSmeltItem) { @@ -284,6 +293,7 @@ public void onFurnaceSmelt(FurnaceSmeltEvent e) { @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onFurnaceBurnItem(FurnaceBurnEvent e) { + ProtectionStones.getInstance().debug("ListenerClass.java, onFurnaceBurnItem"); // prevent protect block item to be smelt Furnace f = (Furnace) e.getBlock().getState(); if (f.getInventory().getSmelting() != null) { @@ -299,6 +309,7 @@ public void onFurnaceBurnItem(FurnaceBurnEvent e) { @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onPrepareItemCraft(PrepareItemCraftEvent e) { + ProtectionStones.getInstance().debug("ListenerClass.java, onPrepareItemCraft"); for (ItemStack s : e.getInventory().getMatrix()) { PSProtectBlock options = ProtectionStones.getBlockOptions(s); if (options != null && !options.allowUseInCrafting) { @@ -307,12 +318,49 @@ public void onPrepareItemCraft(PrepareItemCraftEvent e) { } } + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onCrafterCraft(CrafterCraftEvent e) { + ProtectionStones.getInstance().debug("ListenerClass.java, onCrafterCraft"); + + CraftingRecipe recipe = e.getRecipe(); + if (recipe == null) return; + + // Shaped recipes (grid-based) + if (recipe instanceof ShapedRecipe shaped) { + for (ItemStack ingredient : shaped.getIngredientMap().values()) { + if (ingredient != null) { + PSProtectBlock options = ProtectionStones.getBlockOptions(ingredient); + if (options != null && !options.allowUseInCrafting) { + e.setResult(new ItemStack(Material.AIR)); + e.setCancelled(true); + return; + } + } + } + } + + // Shapeless recipes (unordered list) + if (recipe instanceof ShapelessRecipe shapeless) { + for (ItemStack ingredient : shapeless.getIngredientList()) { + if (ingredient != null) { + PSProtectBlock options = ProtectionStones.getBlockOptions(ingredient); + if (options != null && !options.allowUseInCrafting) { + e.setResult(new ItemStack(Material.AIR)); + e.setCancelled(true); + return; + } + } + } + } + } + // -=-=-=- 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) public void onInventoryClickEvent(InventoryClickEvent e) { + ProtectionStones.getInstance().debug("ListenerClass.java, onInventoryClickEvent"); if (e.getInventory().getType() == InventoryType.GRINDSTONE) { if (ProtectionStones.isProtectBlockItem(e.getCurrentItem())) { e.setCancelled(true); @@ -325,6 +373,7 @@ public void onInventoryClickEvent(InventoryClickEvent e) { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onPlayerBucketFill(PlayerBucketEmptyEvent e) { + ProtectionStones.getInstance().debug("ListenerClass.java, onPlayerBucketFill"); 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()); @@ -335,6 +384,7 @@ public void onPlayerBucketFill(PlayerBucketEmptyEvent e) { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onBlockIgnite(BlockIgniteEvent e) { + ProtectionStones.getInstance().debug("ListenerClass.java, onBlockIgnite"); if (ProtectionStones.isProtectBlock(e.getBlock())) { e.setCancelled(true); } @@ -425,16 +475,19 @@ private void pistonUtil(List pushedBlocks, BlockPistonEvent e) { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onBlockExplode(BlockExplodeEvent e) { + ProtectionStones.getInstance().debug("ListenerClass.java, onBlockExplode"); explodeUtil(e.blockList(), e.getBlock().getLocation().getWorld()); } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onEntityExplode(EntityExplodeEvent e) { + ProtectionStones.getInstance().debug("ListenerClass.java, onEntityExplode"); explodeUtil(e.blockList(), e.getLocation().getWorld()); } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onEntityChangeBlock(EntityChangeBlockEvent e) { + ProtectionStones.getInstance().debug("ListenerClass.java, onEntityChangeBlock"); if (!ProtectionStones.isProtectBlock(e.getBlock())) return; // events like ender dragon block break, wither running into block break, etc. From 2d64a8de6c8205e90846c4e5e70cd71c3b58aa32 Mon Sep 17 00:00:00 2001 From: Jerzean Date: Fri, 19 Sep 2025 02:27:54 -0400 Subject: [PATCH 7/9] Spigot version bump for crafter support (Not compat with sub 1.21 yet), Initial Kyori updates, Full # and Hex code support( couple visual bugs but working otherwise) Spigot and Paper adaptor for chat events (No more using "p.sendmessage", USE "PSL.msg" Need to add all hardcoded msgs to the Messages.yml --- pom.xml | 34 +- .../espi/protectionstones/BlockHandler.java | 35 +- .../espi/protectionstones/ListenerClass.java | 81 +- .../dev/espi/protectionstones/PSEconomy.java | 87 +- .../java/dev/espi/protectionstones/PSL.java | 807 +++++++++--------- .../protectionstones/PSStandardRegion.java | 2 +- .../protectionstones/ProtectionStones.java | 25 +- .../commands/ArgAddRemove.java | 37 +- .../protectionstones/commands/ArgAdmin.java | 80 +- .../commands/ArgAdminChangeType.java | 5 +- .../commands/ArgAdminChangeblock.java | 10 +- .../commands/ArgAdminCleanup.java | 9 +- .../commands/ArgAdminForceMerge.java | 4 +- .../commands/ArgAdminHelp.java | 180 +--- .../commands/ArgAdminHide.java | 2 +- .../commands/ArgAdminLastlogon.java | 30 +- .../commands/ArgAdminRecreate.java | 6 +- .../commands/ArgAdminSetTaxAutopayers.java | 22 +- .../protectionstones/commands/ArgBuySell.java | 29 +- .../protectionstones/commands/ArgCount.java | 20 +- .../protectionstones/commands/ArgFlag.java | 275 +++--- .../protectionstones/commands/ArgGet.java | 65 +- .../protectionstones/commands/ArgGive.java | 15 +- .../protectionstones/commands/ArgHelp.java | 57 +- .../protectionstones/commands/ArgHome.java | 65 +- .../protectionstones/commands/ArgInfo.java | 12 +- .../protectionstones/commands/ArgList.java | 4 +- .../protectionstones/commands/ArgMerge.java | 63 +- .../protectionstones/commands/ArgName.java | 16 +- .../commands/ArgPriority.java | 2 +- .../protectionstones/commands/ArgRegion.java | 15 +- .../protectionstones/commands/ArgRent.java | 72 +- .../protectionstones/commands/ArgSethome.java | 2 +- .../commands/ArgSetparent.java | 13 +- .../protectionstones/commands/ArgTax.java | 179 ++-- .../protectionstones/commands/ArgToggle.java | 14 +- .../espi/protectionstones/commands/ArgTp.java | 22 +- .../protectionstones/commands/ArgUnclaim.java | 22 +- .../flags/FarewellFlagHandler.java | 4 +- .../espi/protectionstones/utils/ChatUtil.java | 63 +- .../protectionstones/utils/LimitUtil.java | 21 +- .../espi/protectionstones/utils/TextGUI.java | 103 ++- 42 files changed, 1473 insertions(+), 1136 deletions(-) diff --git a/pom.xml b/pom.xml index b3df6501..3e0ec5cf 100644 --- a/pom.xml +++ b/pom.xml @@ -213,7 +213,7 @@ com.sk89q.worldguard worldguard-bukkit - 7.0.9-SNAPSHOT + 7.0.14-SNAPSHOT provided @@ -231,7 +231,7 @@ com.sk89q.worldedit worldedit-bukkit - 7.2.6-SNAPSHOT + 7.3.16-SNAPSHOT provided @@ -240,6 +240,36 @@ + + net.kyori + adventure-api + 4.17.0 + compile + + + net.kyori + adventure-platform-bukkit + 4.3.2 + compile + + + net.kyori + adventure-text-serializer-plain + 4.17.0 + compile + + + net.kyori + adventure-text-serializer-bungeecord + 4.4.1 + compile + + + net.kyori + adventure-text-minimessage + 4.17.0 + compile + me.clip placeholderapi diff --git a/src/main/java/dev/espi/protectionstones/BlockHandler.java b/src/main/java/dev/espi/protectionstones/BlockHandler.java index 891a7650..c3d37003 100644 --- a/src/main/java/dev/espi/protectionstones/BlockHandler.java +++ b/src/main/java/dev/espi/protectionstones/BlockHandler.java @@ -30,6 +30,7 @@ import dev.espi.protectionstones.utils.MiscUtil; import dev.espi.protectionstones.utils.WGMerge; import dev.espi.protectionstones.utils.WGUtils; +import net.kyori.adventure.text.Component; import net.md_5.bungee.api.chat.TextComponent; import net.milkbowl.vault.economy.EconomyResponse; import org.bukkit.*; @@ -37,10 +38,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.block.BlockPlaceEvent; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; +import java.util.*; public class BlockHandler { private static HashMap lastProtectStonePlaced = new HashMap<>(); @@ -140,7 +138,7 @@ public static boolean createPSRegion(Player p, Location l, PSProtectBlock blockO if (ProtectionStones.getInstance().getConfigOptions().placingCooldown != -1) { String time = checkCooldown(p); if (time != null) { - PSL.msg(p, PSL.COOLDOWN.msg().replace("%time%", time)); + PSL.msg(p, PSL.COOLDOWN.replace("%time%", time)); return false; } } @@ -168,7 +166,7 @@ public static boolean createPSRegion(Player p, Location l, PSProtectBlock blockO // check if player has enough money if (ProtectionStones.getInstance().isVaultSupportEnabled() && blockOptions.costToPlace != 0 && !ProtectionStones.getInstance().getVaultEconomy().has(p, blockOptions.costToPlace)) { - PSL.msg(p, PSL.NOT_ENOUGH_MONEY.msg().replace("%price%", String.format("%.2f", blockOptions.costToPlace))); + PSL.msg(p, PSL.NOT_ENOUGH_MONEY.replace("%price%", String.format("%.2f", blockOptions.costToPlace))); return false; } @@ -183,10 +181,10 @@ public static boolean createPSRegion(Player p, Location l, PSProtectBlock blockO if (ProtectionStones.getInstance().isVaultSupportEnabled() && blockOptions.costToPlace != 0) { EconomyResponse er = ProtectionStones.getInstance().getVaultEconomy().withdrawPlayer(p, blockOptions.costToPlace); if (!er.transactionSuccess()) { - PSL.msg(p, er.errorMessage); + PSL.msg(p, Component.text(er.errorMessage)); return true; } - PSL.msg(p, PSL.PAID_MONEY.msg().replace("%price%", String.format("%.2f", blockOptions.costToPlace))); + PSL.msg(p, PSL.PAID_MONEY.replace("%price%", String.format("%.2f", blockOptions.costToPlace))); } return true; @@ -214,7 +212,7 @@ public static boolean createActualRegion(Player p, Location l, PSProtectBlock bl // check for minimum distance between claims by using fake region if (blockOptions.distanceBetweenClaims != -1 && !p.hasPermission("protectionstones.superowner")) { if (!isFarEnoughFromOtherClaims(blockOptions, p.getWorld(), lp, bx, by, bz)) { - PSL.msg(p, PSL.REGION_TOO_CLOSE.msg().replace("%num%", "" + blockOptions.distanceBetweenClaims)); + PSL.msg(p, PSL.REGION_TOO_CLOSE.replace("%num%", "" + blockOptions.distanceBetweenClaims)); return false; } } @@ -315,7 +313,7 @@ private static void playerMergeTask(Player p, PSRegion r) { Bukkit.getScheduler().runTaskAsynchronously(ProtectionStones.getInstance(), () -> { try { WGMerge.mergeRealRegions(p.getWorld(), r.getWGRegionManager(), finalMergeTo, Arrays.asList(finalMergeTo, r)); - PSL.msg(p, PSL.MERGE_AUTO_MERGED.msg().replace("%region%", finalMergeTo.getId())); + PSL.msg(p, PSL.MERGE_AUTO_MERGED.replace("%region%", finalMergeTo.getId())); } catch (WGMerge.RegionHoleException e) { PSL.msg(p, PSL.NO_REGION_HOLES.msg()); // TODO github issue #120, prevent holes even if showGUI is true } catch (WGMerge.RegionCannotMergeWhileRentedException e) { @@ -327,14 +325,21 @@ private static void playerMergeTask(Player p, PSRegion r) { // show merge gui if (showGUI) { - List tc = ArgMerge.getGUI(p, r); + List tc = ArgMerge.getGUI(p, r); if (!tc.isEmpty()) { // if there are regions you can merge into - p.sendMessage(ChatColor.WHITE + ""); // send empty line + PSL.msg(p, Component.empty()); PSL.msg(p, PSL.MERGE_INTO.msg()); - PSL.msg(p, PSL.MERGE_HEADER.msg().replace("%region%", r.getId())); - for (TextComponent t : tc) p.spigot().sendMessage(t); - p.sendMessage(ChatColor.WHITE + ""); // send empty line + PSL.msg(p, PSL.MERGE_HEADER.replaceAll(Map.of("%region%", r.getId()))); + + // GUI entries + for (Component t : tc) { + PSL.msg(p, t); + } + + // empty line again + PSL.msg(p, Component.empty()); } } + } } diff --git a/src/main/java/dev/espi/protectionstones/ListenerClass.java b/src/main/java/dev/espi/protectionstones/ListenerClass.java index bf4024c8..aba96aaa 100644 --- a/src/main/java/dev/espi/protectionstones/ListenerClass.java +++ b/src/main/java/dev/espi/protectionstones/ListenerClass.java @@ -29,6 +29,8 @@ import dev.espi.protectionstones.utils.RecipeUtil; import dev.espi.protectionstones.utils.UUIDCache; import dev.espi.protectionstones.utils.WGUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -37,6 +39,7 @@ import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.Furnace; +import org.bukkit.block.data.type.Crafter; import org.bukkit.command.CommandSender; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; @@ -53,10 +56,7 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; -import org.bukkit.inventory.CraftingRecipe; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.ShapedRecipe; -import org.bukkit.inventory.ShapelessRecipe; +import org.bukkit.inventory.*; import java.util.List; @@ -95,7 +95,7 @@ public void onPlayerJoin(PlayerJoinEvent e) { } if (amount != 0) { - PSL.msg(psp, PSL.TAX_JOIN_MSG_PENDING_PAYMENTS.msg().replace("%money%", "" + amount)); + PSL.msg(psp.getPlayer(), PSL.TAX_JOIN_MSG_PENDING_PAYMENTS.replace("%money%", "" + amount)); } }); } @@ -140,7 +140,7 @@ public void onBlockPlace(BlockPlaceEvent 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 - private String checkPermissionToBreakProtection(Player p, PSRegion r) { + private Component checkPermissionToBreakProtection(Player p, PSRegion r) { // check for destroy permission if (!p.hasPermission("protectionstones.destroy")) { return PSL.NO_PERMISSION_DESTROY.msg(); @@ -156,7 +156,7 @@ private String checkPermissionToBreakProtection(Player p, PSRegion r) { return PSL.RENT_CANNOT_BREAK_WHILE_RENTING.msg(); } - return ""; + return Component.empty(); } // helper method for breaking protection blocks @@ -165,8 +165,8 @@ private boolean playerBreakProtection(Player p, PSRegion r) { PSProtectBlock blockOptions = r.getTypeOptions(); // check if player has permission to break the protection - String error = checkPermissionToBreakProtection(p, r); - if (!error.isEmpty()) { + Component error = checkPermissionToBreakProtection(p, r); + if (!error.contains(Component.empty())) { PSL.msg(p, error); return false; } @@ -235,8 +235,8 @@ public void onBlockBreakLowPriority(BlockBreakEvent e) { ProtectionStones.getInstance().debug("Player:"+ p.getName()+", Holding:"+ p.getPlayer().getInventory().getItemInMainHand().getType().name()+", Enchants:" +p.getPlayer().getInventory().getItemInMainHand().getEnchantments()); - String error = checkPermissionToBreakProtection(p, r); - if (!error.isEmpty()) { + Component error = checkPermissionToBreakProtection(p, r); + if (!error.contains(Component.empty())) { PSL.msg(p, error); e.setCancelled(true); } @@ -318,37 +318,42 @@ public void onPrepareItemCraft(PrepareItemCraftEvent e) { } } - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onCrafterCraft(CrafterCraftEvent e) { - ProtectionStones.getInstance().debug("ListenerClass.java, onCrafterCraft"); - - CraftingRecipe recipe = e.getRecipe(); - if (recipe == null) return; - - // Shaped recipes (grid-based) - if (recipe instanceof ShapedRecipe shaped) { - for (ItemStack ingredient : shaped.getIngredientMap().values()) { - if (ingredient != null) { - PSProtectBlock options = ProtectionStones.getBlockOptions(ingredient); - if (options != null && !options.allowUseInCrafting) { - e.setResult(new ItemStack(Material.AIR)); - e.setCancelled(true); - return; - } - } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onCrafterClick(InventoryClickEvent e) { + if (!(e.getInventory().getHolder() instanceof Crafter)) return; + + ItemStack item = e.getCursor(); // item being placed + if (item != null && ProtectionStones.getBlockOptions(item) != null) { + e.setCancelled(true); + e.getWhoClicked().sendMessage(ChatColor.RED + "You cannot insert ProtectionStone blocks into a Crafter."); + } + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onCrafterHopperMove(InventoryMoveItemEvent e) { + if (!(e.getDestination().getHolder() instanceof Crafter)) return; + + ItemStack item = e.getItem(); + if (item != null) { + PSProtectBlock options = ProtectionStones.getBlockOptions(item); + if (options != null && !options.allowUseInCrafting) { + e.setCancelled(true); } } + } - // Shapeless recipes (unordered list) - if (recipe instanceof ShapelessRecipe shapeless) { - for (ItemStack ingredient : shapeless.getIngredientList()) { - if (ingredient != null) { - PSProtectBlock options = ProtectionStones.getBlockOptions(ingredient); - if (options != null && !options.allowUseInCrafting) { - e.setResult(new ItemStack(Material.AIR)); - e.setCancelled(true); - return; + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onCrafterDrag(InventoryDragEvent e) { + if (!(e.getInventory().getHolder() instanceof Crafter)) return; + + for (ItemStack item : e.getNewItems().values()) { + if (item != null) { + PSProtectBlock options = ProtectionStones.getBlockOptions(item); + if (options != null && !options.allowUseInCrafting) { + e.setCancelled(true); + if (e.getWhoClicked() instanceof Player player) { + PSL.msg(player, Component.text("You cannot use ProtectionStones blocks in a Crafter.", NamedTextColor.RED)); } + return; } } } diff --git a/src/main/java/dev/espi/protectionstones/PSEconomy.java b/src/main/java/dev/espi/protectionstones/PSEconomy.java index f776e00f..ce8f9f2b 100644 --- a/src/main/java/dev/espi/protectionstones/PSEconomy.java +++ b/src/main/java/dev/espi/protectionstones/PSEconomy.java @@ -23,12 +23,14 @@ import net.milkbowl.vault.economy.EconomyResponse; import org.bukkit.Bukkit; import org.bukkit.World; +import org.bukkit.entity.Player; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; @@ -136,9 +138,13 @@ public static void processTaxes(PSRegion r) { EconomyResponse res = r.payTax(psp, psp.getBalance()); if (psp.getPlayer() != null && res.amount != 0) { - PSL.msg(psp.getPlayer(), PSL.TAX_PAID.msg() - .replace("%amount%", String.format("%.2f", res.amount)) - .replace("%region%", r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")")); + PSL.msg(psp.getPlayer(), + PSL.TAX_PAID.replaceAll(Map.of( + "%amount%", String.format("%.2f", res.amount), + "%region%", r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")" + )) + ); + } } @@ -157,49 +163,80 @@ public static void processTaxes(PSRegion r) { * @param r the region to perform the rent payment */ public static void doRentPayment(PSRegion r) { - PSPlayer tenant = PSPlayer.fromPlayer(Bukkit.getOfflinePlayer(r.getTenant())); + PSPlayer tenant = PSPlayer.fromPlayer(Bukkit.getOfflinePlayer(r.getTenant())); PSPlayer landlord = PSPlayer.fromPlayer(Bukkit.getOfflinePlayer(r.getLandlord())); + final String regionName = (r.getName() != null ? r.getName() : r.getId()); + final String priceStr = String.format("%.2f", r.getPrice()); + // not enough money for rent if (!tenant.hasAmount(r.getPrice())) { - if (tenant.getOfflinePlayer().isOnline()) { - PSL.msg(Bukkit.getPlayer(r.getTenant()), PSL.RENT_EVICT_NO_MONEY_TENANT.msg() - .replace("%region%", r.getName() != null ? r.getName() : r.getId()) - .replace("%price%", String.format("%.2f", r.getPrice()))); + Player tenantOnline = Bukkit.getPlayer(r.getTenant()); + if (tenantOnline != null) { + PSL.msg( + tenantOnline, + PSL.RENT_EVICT_NO_MONEY_TENANT.replaceAll(Map.of( + "%region%", regionName, + "%price%", priceStr + )) + ); } - if (landlord.getOfflinePlayer().isOnline()) { - PSL.msg(Bukkit.getPlayer(r.getLandlord()), PSL.RENT_EVICT_NO_MONEY_LANDLORD.msg() - .replace("%region%", r.getName() != null ? r.getName() : r.getId()) - .replace("%tenant%", tenant.getName())); + + Player landlordOnline = Bukkit.getPlayer(r.getLandlord()); + if (landlordOnline != null) { + PSL.msg( + landlordOnline, + PSL.RENT_EVICT_NO_MONEY_LANDLORD.replaceAll(Map.of( + "%region%", regionName, + "%tenant%", tenant.getName() + )) + ); } + r.removeRenting(); return; } // send payment messages - if (tenant.getOfflinePlayer().isOnline()) { - PSL.msg(Bukkit.getPlayer(r.getTenant()), PSL.RENT_PAID_TENANT.msg() - .replace("%price%", String.format("%.2f", r.getPrice())) - .replace("%landlord%", landlord.getName()) - .replace("%region%", r.getName() != null ? r.getName() : r.getId())); + Player tenantOnline = Bukkit.getPlayer(r.getTenant()); + if (tenantOnline != null) { + PSL.msg( + tenantOnline, + PSL.RENT_PAID_TENANT.replaceAll(Map.of( + "%price%", priceStr, + "%landlord%", landlord.getName(), + "%region%", regionName + )) + ); } - if (landlord.getOfflinePlayer().isOnline()) { - PSL.msg(Bukkit.getPlayer(r.getLandlord()), PSL.RENT_PAID_LANDLORD.msg() - .replace("%price%", String.format("%.2f", r.getPrice())) - .replace("%tenant%", tenant.getName()) - .replace("%region%", r.getName() != null ? r.getName() : r.getId())); + + Player landlordOnline = Bukkit.getPlayer(r.getLandlord()); + if (landlordOnline != null) { + PSL.msg( + landlordOnline, + PSL.RENT_PAID_LANDLORD.replaceAll(Map.of( + "%price%", priceStr, + "%tenant%", tenant.getName(), + "%region%", regionName + )) + ); } - // update money must be run in main thread - Bukkit.getScheduler().runTask(ProtectionStones.getInstance(), () -> tenant.pay(landlord, r.getPrice())); + // update balances (main thread) + Bukkit.getScheduler().runTask( + ProtectionStones.getInstance(), + () -> tenant.pay(landlord, r.getPrice()) + ); + r.setRentLastPaid(Instant.now().getEpochSecond()); - try { // must save region to persist last paid + try { r.getWGRegionManager().saveChanges(); } catch (StorageException e) { e.printStackTrace(); } } + /** * Get list of rented regions. * diff --git a/src/main/java/dev/espi/protectionstones/PSL.java b/src/main/java/dev/espi/protectionstones/PSL.java index b64c3248..825d16fa 100644 --- a/src/main/java/dev/espi/protectionstones/PSL.java +++ b/src/main/java/dev/espi/protectionstones/PSL.java @@ -1,4 +1,4 @@ -/* +package dev.espi.protectionstones;/* * 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 @@ -13,18 +13,21 @@ * along with this program. If not, see . */ -package dev.espi.protectionstones; - -import org.apache.commons.lang3.StringUtils; -import org.bukkit.ChatColor; +import dev.espi.protectionstones.utils.ChatUtil; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.util.Arrays; +import java.util.Map; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -32,505 +35,517 @@ 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."), + // --- General --- + COOLDOWN("cooldown", "Warning: Please wait for %time% seconds before placing again!", "%time%"), + NO_SUCH_COMMAND("no_such_command", "No such command. please type /ps help for more info"), + NO_ACCESS("no_access", "You are not allowed to do that here."), + NO_ROOM_IN_INVENTORY("no_room_in_inventory", "You don't have enough room in your inventory."), + NO_ROOM_DROPPING_ON_FLOOR("no_room_dropping_on_floor", "You don't have enough room in your inventory. Dropping item on floor."), + INVALID_BLOCK("invalid_block", "Invalid protection block."), + NOT_ENOUGH_MONEY("not_enough_money", "You don't have enough money! The price is %price%.", "%price%"), + PAID_MONEY("paid_money", "You've paid $%price%.", "%price%"), + INVALID_WORLD("invalid_world", "Invalid world."), + MUST_BE_PLAYER("must_be_player", "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!"), + PAGE_DOES_NOT_EXIST("page_does_not_exist", "Page does not exist."), + + HELP("help", "===== PS Help =====\n> /ps help"), + HELP_NEXT("help_next", "Do /ps help %page% to go to the next page!", "%page%"), + + COMMAND_REQUIRES_PLAYER_NAME("command_requires_player_name", "This command requires a player name."), + + NO_PERMISSION_TOGGLE("no_permission_toggle", "You don't have permission to use the toggle command."), + NO_PERMISSION_CREATE("no_permission_create", "You don't have permission to place a protection block."), + NO_PERMISSION_CREATE_SPECIFIC("no_permission_create_specific", "You don't have permission to place this protection block type."), + NO_PERMISSION_DESTROY("no_permission_destroy", "You don't have permission to destroy a protection block."), + NO_PERMISSION_MEMBERS("no_permission_members", "You don't have permission to use member commands."), + NO_PERMISSION_OWNERS("no_permission_owners", "You don't have permission to use owner commands."), + NO_PERMISSION_ADMIN("no_permission_admin", "You do not have permission to use that command."), + NO_PERMISSION_COUNT("no_permission_count", "You do not have permission to use that command."), + NO_PERMISSION_COUNT_OTHERS("no_permission_count_others", "You do not have permission to use that command."), + NO_PERMISSION_FLAGS("no_permission_flags", "You do not have permission to use flag commands."), + NO_PERMISSION_PER_FLAG("no_permission_per_flag", "You do not have permission to use that flag."), + NO_PERMISSION_RENT("no_permission_rent", "You do not have permission for renting."), + NO_PERMISSION_TAX("no_permission_tax", "You do not have permission to use the tax command."), + NO_PERMISSION_BUYSELL("no_permission_buysell", "You do not have permission to buy and sell regions."), + NO_PERMISSION_UNHIDE("no_permission_unhide", "You do not have permission to unhide protection blocks."), + NO_PERMISSION_HIDE("no_permission_hide", "You do not have permission to hide protection blocks."), + NO_PERMISSION_INFO("no_permission_info", "You do not have permission to use the region info command."), + NO_PERMISSION_PRIORITY("no_permission_priority", "You do not have permission to use the priority command."), + NO_PERMISSION_REGION("no_permission_region", "You do not have permission to use region commands."), + NO_PERMISSION_TP("no_permission_tp", "You do not have permission to teleport to other players' protection blocks."), + NO_PERMISSION_HOME("no_permission_home", "You do not have permission to teleport to your protection blocks."), + NO_PERMISSION_UNCLAIM("no_permission_unclaim", "You do not have permission to use the unclaim command."), + NO_PERMISSION_UNCLAIM_REMOTE("no_permission_unclaim_remote", "You do not have permission to use the unclaim remote command."), + NO_PERMISSION_VIEW("no_permission_view", "You do not have permission to use the view command."), + NO_PERMISSION_GIVE("no_permission_give", "You do not have permission to use the give command."), + NO_PERMISSION_GET("no_permission_get", "You do not have permission to use the get command."), + NO_PERMISSION_SETHOME("no_permission_sethome", "You do not have permission to use the sethome command."), + NO_PERMISSION_LIST("no_permission_list", "You do not have permission to use the list command."), + NO_PERMISSION_LIST_OTHERS("no_permission_list_others", "You do not have permission to use the list command for others."), + NO_PERMISSION_NAME("no_permission_name", "You do not have permission to use the name command."), + NO_PERMISSION_SETPARENT("no_permission_setparent", "You do not have permission to use the setparent command."), + NO_PERMISSION_SETPARENT_OTHERS("no_permission_setparent_others", "You do not have permission to inherit from regions you don't own."), + NO_PERMISSION_MERGE("no_permission_merge", "You do not have permission to use /ps merge."), + + // --- Region --- + ADDED_TO_REGION("psregion.added_to_region", "%player% has been added to this region.", "%player%"), + ADDED_TO_REGION_SPECIFIC("psregion.added_to_region_specific", "%player% has been added to region %region%.", "%player%", "%region%"), + REMOVED_FROM_REGION("psregion.removed_from_region", "%player% has been removed from region.", "%player%"), + REMOVED_FROM_REGION_SPECIFIC("psregion.removed_from_region_specific", "%player% has been removed from region %region%.", "%player%", "%region%"), + NOT_IN_REGION("psregion.not_in_region", "You are not in a protection stones region!"), + PLAYER_NOT_FOUND("psregion.player_not_found", "Player not found."), + NOT_PS_REGION("psregion.not_ps_region", "Not a protection stones region."), + REGION_DOES_NOT_EXIST("psregion.region_does_not_exist", "Region does not exist."), + NO_REGIONS_OWNED("psregion.no_regions_owned", "You don't own any protected regions in this world!"), + NO_REGION_PERMISSION("psregion.no_region_permission", "You do not have permission to do this in this region."), + PROTECTED("psregion.protected", "This area is now protected."), + NO_LONGER_PROTECTED("psregion.no_longer_protected", "This area is no longer protected."), + CANT_PROTECT_THAT("psregion.cant_protect_that", "You can't protect that area."), + REACHED_REGION_LIMIT("psregion.reached_region_limit", "You can not have any more protected regions (%limit%).", "%limit%"), + REACHED_PER_BLOCK_REGION_LIMIT("psregion.reached_per_block_region_limit", "You can not have any more regions of this type (%limit%).", "%limit%"), + WORLD_DENIED_CREATE("psregion.world_denied_create", "You can not create protections in this world."), + REGION_OVERLAP("psregion.region_overlap", "You can not place a protection block here as it overlaps another region."), + REGION_TOO_CLOSE("psregion.region_too_close", "Your protection block must be a minimum of %num% blocks from the edge of other regions!", "%num%"), + REGION_CANT_TELEPORT("psregion.cant_teleport", "Your teleportation was blocked by a protection region!"), + SPECIFY_ID_INSTEAD_OF_ALIAS("psregion.specify_id_instead_of_alias", "There were multiple regions found with this name! Please use an ID instead.\n Regions with this name: %regions%", "%regions%"), + REGION_NOT_ADJACENT("psregion.region_not_adjacent", "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", "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"), + NO_REGION_HOLES("psregion.no_region_holes", "Unprotected area detected inside region! This is not allowed!"), + DELETE_REGION_PREVENTED_NO_HOLES("psregion.delete_region_prevented", "The region could not be removed, possibly because it creates a hole in the existing region."), + NOT_OWNER("psregion.not_owner", "You are not an owner of this region!"), + CANNOT_MERGE_RENTED_REGION("psregion.cannot_merge_rented_region", "Cannot merge regions because region %region% is in the process of being rented out!", "%region%"), + NO_PERMISSION_REGION_TYPE("psregion.no_permission_region_type", "You do not have permission to have this region type."), + REGION_HIDDEN("psregion.hidden", "The protection block is now hidden."), + MUST_BE_PLACED_IN_EXISTING_REGION("psregion.must_be_placed_in_existing_region", "This must be placed inside of an existing region!"), + REGION_ALREADY_IN_LOCATION_IS_HIDDEN("psregion.already_in_location_is_hidden", "A region already exists in this location (is the protection block hidden?)"), + CANNOT_REMOVE_YOURSELF_LAST_OWNER("psregion.cannot_remove_yourself_last_owner", "You cannot remove yourself as you are the last owner."), + CANNOT_REMOVE_YOURSELF_FROM_ALL_REGIONS("psregion.cannot_remove_yourself_all_regions", "You cannot remove yourself from all of your regions at once, for safety reasons."), + + // --- Toggle --- + TOGGLE_HELP("toggle.help", "> /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."), + TOGGLE_ON("toggle.toggle_on", "Protection block placement turned on."), + TOGGLE_OFF("toggle.toggle_off", "Protection block placement turned off."), - // ps count - COUNT_HELP("count.count_help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps count [player (optional)]"), + // --- Count --- + COUNT_HELP("count.count_help", "> /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%"), + PERSONAL_REGION_COUNT("count.personal_region_count", "Your region count in this world: %num%", "%num%"), + PERSONAL_REGION_COUNT_MERGED("count.personal_region_count_merged", "- Including each merged region: %num%", "%num%"), + OTHER_REGION_COUNT("count.other_region_count", "%player%'s region count in this world: %num%", "%player%", "%num%"), + OTHER_REGION_COUNT_MERGED("count.other_region_count_merged", "- Including each merged region: %num%", "%num%"), - // ps flag - FLAG_HELP("flag.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps flag [flagname] [value|null|default]"), + // --- Flag --- + FLAG_HELP("flag.help", "> /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"), + FLAG_SET("flag.flag_set", "%flag% flag has been set.", "%flag%"), + FLAG_NOT_SET("flag.flag_not_set", "%flag% flag has not been set. Check your values again.", "%flag%"), + FLAG_PREVENT_EXPLOIT("flag.flag_prevent_exploit", "This has been disabled to prevent exploits."), + FLAG_PREVENT_EXPLOIT_HOVER("flag.flag_prevent_exploit_hover", "Disabled for security reasons."), + FLAG_GUI_HEADER("flag.gui_header", "===== Flags (click to change) ====="), + FLAG_GUI_HOVER_SET("flag.gui_hover_set", "Click to set."), + FLAG_GUI_HOVER_SET_TEXT("flag.gui_hover_set_text", "Click to change.\nCurrent value:\n%value%", "%value%"), + FLAG_GUI_HOVER_CHANGE_GROUP("flag.hover_change_group", "Click to set this flag to apply to only %group%.", "%group%"), + FLAG_GUI_HOVER_CHANGE_GROUP_NULL("flag.hover_change_group_null", "You must set this flag to a value before changing the group."), + + // --- Rent --- + RENT_HELP("rent.help", "> /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"), + RENT_HELP_HEADER("rent.help_header", "===== Rent Help ====="), + RENT_ALREADY_RENTING("rent.already_renting", "The region is already being rented out! You must stop leasing the region first."), + RENT_NOT_RENTED("rent.not_rented", "This region is not being rented."), + RENT_LEASE_SUCCESS("rent.lease_success", "Region leasing terms set:\nPrice: %price%\nPayment Term: %period%", "%price%", "%period%"), + RENT_STOPPED("rent.stopped", "Leasing stopped."), + RENT_EVICTED("rent.evicted", "Evicted tenant %tenant%.", "%tenant%"), + RENT_NOT_RENTING("rent.not_renting", "This region is not being rented out to tenants."), + RENT_PAID_LANDLORD("rent.paid_landlord", "%tenant% has paid $%price% for renting out %region%.", "%tenant%", "%price%", "%region%"), + RENT_PAID_TENANT("rent.paid_tenant", "Paid $%price% to %landlord% for region %region%.", "%price%", "%landlord%", "%region%"), + RENT_RENTING_LANDLORD("rent.renting_landlord", "%player% is now renting out region %region%.", "%player%", "%region%"), + RENT_RENTING_TENANT("rent.renting_tenant", "You are now renting out region %region% for %price% per %period%.", "%region%", "%price%", "%period%"), + RENT_NOT_TENANT("rent.not_tenant", "You are not the tenant of this region!"), + RENT_TENANT_STOPPED_LANDLORD("rent.tenant_stopped_landlord", "%player% has stopped renting out region %region%. It is now available for others to rent.", "%player%", "%region%"), + RENT_TENANT_STOPPED_TENANT("rent.tenant_stopped_tenant", "You have stopped renting out region %region%.", "%region%"), + RENT_BEING_SOLD("rent.being_sold", "The region is being sold! Do /ps sell stop first."), + RENT_EVICT_NO_MONEY_TENANT("rent.evict_no_money_tenant", "You have been evicted from region %region% because you do not have enough money (%price%) to pay for rent.", "%region%", "%price%"), + RENT_EVICT_NO_MONEY_LANDLORD("rent.evict_no_money_landlord", "%tenant% has been evicted from region %region% because they are unable to afford rent.", "%tenant%", "%region%"), + RENT_CANNOT_RENT_OWN_REGION("rent.cannot_rent_own_region", "You cannot rent your own region!"), + RENT_REACHED_LIMIT("rent.reached_limit", "You've reached the limit of regions you are allowed to rent!"), + RENT_PRICE_TOO_LOW("rent.price_too_low", "The rent price is too low (must be larger than %price%).", "%price%"), + RENT_PRICE_TOO_HIGH("rent.price_too_high", "The rent price is too high (must be lower than %price%).", "%price%"), + RENT_PERIOD_TOO_SHORT("rent.period_too_short", "The rent period is too short (must be longer than %period% seconds).", "%period%"), + RENT_PERIOD_TOO_LONG("rent.period_too_long", "The rent period is too long (must be shorter than %period% seconds).", "%period%"), + RENT_PERIOD_INVALID("rent.period_invalid", "Invalid period format! Example: 24h for once a day."), + RENT_CANNOT_BREAK_WHILE_RENTING("rent.cannot_break_while_renting", "You cannot break the region when it is being rented out."), + + // --- Tax --- + TAX_HELP("tax.help", "> /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_HELP_HEADER("tax.help_header", "===== Taxes Help ====="), + TAX_DISABLED_REGION("tax.disabled_region", "Taxes are disabled for this region."), + TAX_SET_AS_AUTOPAYER("tax.set_as_autopayer", "Taxes for region %region% will now be automatically paid by you.", "%region%"), + TAX_SET_NO_AUTOPAYER("tax.set_no_autopayer", "Taxes for region %region% now have to be manually paid for.", "%region%"), + TAX_PAID("tax.paid", "Paid $%amount% in taxes for region %region%.", "%amount%", "%region%"), + TAX_INFO_HEADER("tax.info_header", "===== Tax Info (click for more info) ====="), + TAX_JOIN_MSG_PENDING_PAYMENTS("tax.join_msg_pending_payments", "You have $%money% in tax payments due on your regions!\nView them with /ps tax info.", "%money%"), + TAX_PLAYER_REGION_INFO("tax.player_region_info", "> %region% - $%money% due", "%region%", "%money%"), + TAX_PLAYER_REGION_INFO_AUTOPAYER("tax.player_region_info_autopayer", "> %region% - $%money% due (you autopay)", "%region%", "%money%"), 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"), + TAX_REGION_INFO_HEADER("tax.region_info_header", "===== %region% Tax Info =====", "%region%"), + TAX_REGION_INFO("tax.region_info", "Tax Rate: $%taxrate% (sum of all merged regions)\n" + + "Time between tax cycles: %taxperiod%\n" + + "Time to pay taxes after cycle: %taxpaymentperiod%\n" + + "Tax Autopayer: %taxautopayer%\n" + + "Taxes Owed: $%taxowed%", "%taxrate%", "%taxperiod%", "%taxpaymentperiod%", "%taxautopayer%", "%taxowed%"), + TAX_NEXT("tax.next_page", "Do /ps tax info -p %page% to go to the next page!", "%page%"), + + // --- Buy --- + BUY_HELP("buy.help", "> /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 + "."), + BUY_NOT_FOR_SALE("buy.not_for_sale", "This region is not for sale."), + BUY_STOP_SELL("buy.stop_sell", "The region is now not for sale."), + BUY_SOLD_BUYER("buy.sold_buyer", "Bought region %region% for $%price% from %player%.", "%region%", "%price%", "%player%"), + BUY_SOLD_SELLER("buy.sold_seller", "Sold region %region% for $%price% to %player%.", "%region%", "%price%", "%player%"), - // ps sell - SELL_HELP("sell.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps sell [price|stop]"), + // --- Sell --- + SELL_HELP("sell.help", "> /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 + "."), + SELL_RENTED_OUT("sell.rented_out", "The region is being rented out! You must stop renting it out to sell."), + SELL_FOR_SALE("sell.for_sale", "The region is now for sale for $%price%.", "%price%"), - // ps hide/unhide - VISIBILITY_HIDE_HELP("visibility.hide_help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps hide"), + // --- Hide/Unhide --- + VISIBILITY_HIDE_HELP("visibility.hide_help", "> /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("visibility.unhide_help", "> /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..."), + ALREADY_NOT_HIDDEN("visibility.already_not_hidden", "The protection stone doesn't appear hidden..."), + ALREADY_HIDDEN("visibility.already_hidden", "The protection stone appears to already be hidden..."), - // ps info - INFO_HELP("info.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps info members|owners|flags"), + // --- Info --- + INFO_HELP("info.help", "> /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_HEADER("info.header", "===== PS Info ====="), + INFO_TYPE2("info.type2", "Type: %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_MERGED2("info.merged2", "Merged regions: %merged%", "%merged%"), + INFO_MEMBERS2("info.members2", "Members: %members%", "%members%"), + INFO_NO_MEMBERS("info.no_members", "(no members)"), + INFO_OWNERS2("info.owners2", "Owners: %owners%", "%owners%"), + INFO_NO_OWNERS("info.no_owners", "(no owners)"), + INFO_FLAGS2("info.flags2", "Flags: %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]"), + INFO_REGION2("info.region2", "Region: %region%", "%region%"), + INFO_PRIORITY2("info.priority2", "Priority: %priority%", "%priority%"), + INFO_PARENT2("info.parent2", "Parent: %parentregion%", "%parentregion%"), + INFO_BOUNDS_XYZ("info.bounds_xyz", "Bounds: (%minx%,%miny%,%minz%) -> (%maxx%,%maxy%,%maxz%)", + "%minx%", "%miny%", "%minz%", "%maxx%", "%maxy%", "%maxz%"), + INFO_BOUNDS_XZ("info.bounds_xz", "Bounds: (%minx%, %minz%) -> (%maxx%, %maxz%)", + "%minx%", "%minz%", "%maxx%", "%maxz%"), + INFO_SELLER2("info.seller2", "Seller: %seller%", "%seller%"), + INFO_PRICE2("info.price2", "Price: %price%", "%price%"), + INFO_TENANT2("info.tenant2", "Tenant: %tenant%", "%tenant%"), + INFO_LANDLORD2("info.landlord2", "Landlord: %landlord%", "%landlord%"), + INFO_RENT2("info.rent2", "Rent: %rent%", "%rent%"), + INFO_AVAILABLE_FOR_SALE("info.available_for_sale", "Region available for sale!"), + INFO_AVAILABLE_FOR_RENT("info.available_for_rent", "Region available for rent!"), + + // --- Priority --- + PRIORITY_HELP("priority.help", "> /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?"), + PRIORITY_INFO("priority.info", "Priority: %priority%", "%priority%"), + PRIORITY_SET("priority.set", "Priority has been set."), + PRIORITY_ERROR("priority.error", "Error parsing input, check it again?"), - // ps region - REGION_HELP("region.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps region [list|remove|disown] [playername]"), + // --- Region (admin over players) --- + REGION_HELP("region.help", "> /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)]"), + REGION_NOT_FOUND_FOR_PLAYER("region.not_found_for_player", "No regions found for %player% in this world.", "%player%"), + REGION_LIST("region.list", "%player%'s regions in this world: %regions%", "%player%", "%regions%"), + REGION_REMOVE("region.remove", "%player%'s regions have been removed in this world, and they have been removed from regions they co-owned.", "%player%"), + REGION_DISOWN("region.disown", "%player% has been removed as owner from all regions on this world.", "%player%"), + REGION_ERROR_SEARCH("region.error_search", "Error while searching for %player%'s regions. Please make sure you have entered the correct name.", "%player%"), + + // --- TP --- + TP_HELP("tp.help", "> /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]"), + NUMBER_ABOVE_ZERO("tp.number_above_zero", "Please enter a number above 0."), + TP_VALID_NUMBER("tp.valid_number", "Please enter a valid number."), + ONLY_HAS_REGIONS("tp.only_has_regions", "%player% only has %num% protected regions in this world!", "%player%", "%num%"), + TPING("tp.tping", "Teleporting..."), + TP_ERROR_NAME("tp.error_name", "Error in teleporting to protected region! (parsing WG region name error)"), + TP_ERROR_TP("tp.error_tp", "Error in finding the region to teleport to!"), + TP_IN_SECONDS("tp.in_seconds", "Teleporting in %seconds% seconds.", "%seconds%"), + TP_CANCELLED_MOVED("tp.cancelled_moved", "Teleport cancelled. You moved!"), + + // --- Home --- + HOME_HELP("home.help", "> /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_HEADER("home.header", "===== Homes (click to teleport) ====="), 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!"), + HOME_NEXT("home.next_page", "Do /ps home -p %page% to go to the next page!", "%page%"), - // ps unclaim - UNCLAIM_HELP("unclaim.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps unclaim"), + // --- Unclaim --- + UNCLAIM_HELP("unclaim.help", "> /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 + "====="), + UNCLAIM_HEADER("unclaim.header", "===== Unclaim (click to unclaim) ====="), - // ps view - VIEW_HELP("view.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps view"), + // --- View --- + VIEW_HELP("view.help", "> /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!"), + VIEW_COOLDOWN("view.cooldown", "Please wait a while before using /ps view again."), + VIEW_GENERATING("view.generating", "Generating border..."), + VIEW_GENERATE_DONE("view.generate_done", "Done! The border will disappear after 30 seconds!"), + VIEW_REMOVING("view.removing", "Removing border...\nIf you still see ghost blocks, relog!"), - // ps admin - ADMIN_HELP("admin.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps admin"), + // --- Admin --- + ADMIN_HELP("admin.help", "> /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"), + ADMIN_CLEANUP_HEADER("admin.cleanup_header", "Cleanup %arg% %days% days\n================", "%arg%", "%days%"), + ADMIN_CLEANUP_FOOTER("admin.cleanup_footer", "================\nCompleted %arg% cleanup.", "%arg%"), + ADMIN_HIDE_TOGGLED("admin.hide_toggled", "All protection stones have been %message% in this world.", "%message%"), + ADMIN_LAST_LOGON("admin.last_logon", "%player% last played %days% days ago.", "%player%", "%days%"), + ADMIN_IS_BANNED("admin.is_banned", "%player% is banned.", "%player%"), + ADMIN_ERROR_PARSING("admin.error_parsing", "Error parsing days, are you sure it is a number?"), + ADMIN_CONSOLE_WORLD("admin.console_world", "Please specify the world as the last parameter."), + ADMIN_LASTLOGONS_HEADER("admin.lastlogons_header", "%days% Days Plus:\n================", "%days%"), + ADMIN_LASTLOGONS_LINE("admin.lastlogons_line", "%player% %time% days", "%player%", "%time%"), + ADMIN_LASTLOGONS_FOOTER("admin.lastlogons_footer", "================\n%count% Total Players Shown\n%checked% Total Players Checked", "%count%", "%checked%"), + + // --- Reload --- + RELOAD_HELP("reload.help", "> /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!"), + RELOAD_START("reload.start", "Reloading config..."), + RELOAD_COMPLETE("reload.complete", "Completed config reload!"), - // ps add/remove - ADDREMOVE_HELP("addremove.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps add|remove [playername]"), + // --- Add/Remove --- + ADDREMOVE_HELP("addremove.help", "> /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("addremove.owner_help", "> /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."), + ADDREMOVE_PLAYER_REACHED_LIMIT("addremove.player_reached_limit", "This player has reached their region limit."), + ADDREMOVE_PLAYER_NEEDS_TO_BE_ONLINE("addremove.player_needs_to_be_online", "The player needs to be online to add them."), - // ps get - GET_HELP("get.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps get [block]"), + // --- Get --- + GET_HELP("get.help", "> /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)]"), + GET_GOTTEN("get.gotten", "Added protection block to inventory!"), + GET_NO_PERMISSION_BLOCK("get.no_permission_block", "You don't have permission to get this block."), + GET_HEADER("get.header", "===== Protect Blocks (click to get) ====="), + GET_GUI_BLOCK("get.gui_block", "> %alias% - %description% ($%price%)", "%alias%", "%description%", "%price%"), + GET_GUI_HOVER("get.gui_hover", "Click to buy a %alias%!", "%alias%"), + + // --- Give --- + GIVE_HELP("give.help", "> /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."), + GIVE_GIVEN("give.given", "Gave %block% to %player%.", "%block%", "%player%"), + GIVE_NO_INVENTORY_ROOM("give.no_inventory_room", "The player does not have enough inventory room."), - // ps sethome - SETHOME_HELP("sethome.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps sethome"), + // --- Sethome --- + SETHOME_HELP("sethome.help", "> /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."), + SETHOME_SET("sethome.set", "The home for %psid% has been set to your location.", "%psid%"), - // ps list - LIST_HELP("list.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps list [player (optional)]"), + // --- List --- + LIST_HELP("list.help", "> /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]"), + LIST_HEADER("list.header", "===== %player%'s Regions =====", "%player%"), + LIST_OWNER("list.owner", "Owner of:"), + LIST_MEMBER("list.member", "Member of:"), + LIST_NO_REGIONS("list.no_regions", "You currently do not own and are not a member of any regions."), + LIST_NO_REGIONS_PLAYER("list.no_regions_player", "%player% does not own and is not a member of any regions.", "%player%"), + + // --- Name --- + NAME_HELP("name.help", "> /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."), + NAME_REMOVED("name.removed", "Removed the name for %id%.", "%id%"), + NAME_SET_NAME("name.set_name", "Set the name of %id% to %name%.", "%id%", "%name%"), + NAME_TAKEN("name.taken", "The region name %name% has already been taken! Try another one.", "%name%"), - // ps setparent - SETPARENT_HELP("setparent.help", ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps setparent [region|none]"), + // --- Setparent --- + SETPARENT_HELP("setparent.help", "> /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."), + SETPARENT_SUCCESS("setparent.success", "Successfully set the parent of %id% to %parent%.", "%id%", "%parent%"), + SETPARENT_SUCCESS_REMOVE("setparent.success_remove", "Successfully removed the parent of %id%.", "%id%"), + SETPARENT_CIRCULAR_INHERITANCE("setparent.circular_inheritance", "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 --- + MERGE_HELP("merge.help", "> /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 + "."), - + MERGE_MERGED("merge.merged", "Regions were successfully merged!"), + MERGE_HEADER("merge.header", "===== Merge %region% (click to merge) =====", "%region%"), + MERGE_WARNING("merge.warning", "Note: This will delete all of the settings for the current region!"), + MERGE_NOT_ALLOWED("merge.not_allowed", "You are not allowed to merge this protection region type."), + MERGE_INTO("merge.into", "This region overlaps other regions you can merge into!"), + MERGE_NO_REGIONS("merge.no_region", "There are no overlapping regions to merge into."), + MERGE_CLICK_TO_MERGE("merge.click_to_merge", "Click to merge with %region%!", "%region%"), + MERGE_AUTO_MERGED("merge.auto_merged", "Region automatically merged with %region%.", "%region%"), ; + // ===== FIELDS ===== private final String path; private final String defaultMessage; - private final String[] placeholders; private final int placeholdersCount; - private String message; + + private Component message; private boolean isEmpty; - private static final File conf = new File(ProtectionStones.getInstance().getDataFolder(), "messages.yml"); + // NOTE: if your enum constants are declared ABOVE these fields (usual for enums), + // do NOT reference them in the constructor. That's why we call MiniMessage.miniMessage() inline there. + private static final File conf = + new File(ProtectionStones.getInstance().getDataFolder(), "messages.yml"); + private static final MiniMessage MINI = MiniMessage.miniMessage(); + private static final LegacyComponentSerializer LEGACY = LegacyComponentSerializer.legacyAmpersand(); + + // &#RRGGBB -> <#RRGGBB> + private static final Pattern HEX_HASH = Pattern.compile("(? &x (so legacy ampersand parser can handle them) + private static final Pattern SECTION = Pattern.compile("§([0-9a-frk-orA-FRK-OR])"); 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(); + this.message = MiniMessage.miniMessage().deserialize(defaultMessage); + this.isEmpty = defaultMessage.isEmpty(); } - public String msg() { - return message; - } + public String path() { return path; } + public String def() { return defaultMessage; } + public String[] placeholders() { return placeholders; } - public boolean isEmpty() { - return isEmpty; - } + public Component msg() { return message; } + public boolean isEmpty() { return isEmpty; } @Nullable - public String format(final Object... args) { - if (isEmpty) { - return null; - } - - if (this.placeholdersCount == 0) { - return this.message; - } + public Component format(final Object... args) { + if (isEmpty) return null; + if (this.placeholdersCount == 0) return this.message; if (this.placeholdersCount != args.length) { throw new IllegalArgumentException("Expected " + this.placeholdersCount + " arguments but got " + args.length); } - return StringUtils.replaceEach( - this.message, - this.placeholders, - Arrays.stream(args).filter(Objects::nonNull).map(Object::toString).toArray(String[]::new) - ); + Component formatted = this.message; + for (int i = 0; i < placeholdersCount; i++) { + final String ph = this.placeholders[i]; // effectively final + final String rep = Objects.toString(args[i], ""); // effectively final + formatted = formatted.replaceText(b -> b.matchLiteral(ph).replacement(rep)); + } + return formatted; } - public boolean send(@NotNull final CommandSender receiver, @NotNull final Object... args) { - final String msg = this.format(args); + // Convenience: replace a single placeholder in THIS message and return a new Component + public Component replace(final String placeholder, final String value) { + if (isEmpty) return null; + final String ph = Objects.toString(placeholder, ""); + final String rep = Objects.toString(value, ""); + return this.message.replaceText(b -> b.matchLiteral(ph).replacement(rep)); + } - if (msg != null) { - receiver.sendMessage(msg); + // Convenience: replace many placeholders in THIS message (map keys are the literals like "%price%") + public Component replaceAll(final Map replacements) { + if (isEmpty) return null; + Component out = this.message; + for (Map.Entry e : replacements.entrySet()) { + final String ph = Objects.toString(e.getKey(), ""); + final String rep = Objects.toString(e.getValue(), ""); + out = out.replaceText(b -> b.matchLiteral(ph).replacement(rep)); } + return out; + } + public boolean send(@NotNull final CommandSender receiver, @NotNull final Object... args) { + final Component c = this.format(args); + if (c != null && receiver != null) { + ProtectionStones.getInstance().audiences().sender(receiver).sendMessage(c); + } return true; } public void append(@NotNull final StringBuilder builder, @NotNull final Object... args) { - final String msg = this.format(args); - - if (msg != null) { - builder.append(msg); + final Component c = this.format(args); + if (c != null) { + builder.append(MINI.serialize(c)); } } - // 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) { - p.sendMessage(str); - } - return true; + // Static helpers + public static boolean msg(CommandSender p, Component comp) { + return ChatUtil.send(p, comp); } - - public static boolean msg(PSPlayer p, String str) { - return msg(p.getPlayer(), str); + // actionbar msgs + public static boolean action(Player p, String comp) { + return ChatUtil.sendActionBar(p, comp); } + // ====== CONFIG LOAD / AUTO-MIGRATE ====== public static void loadConfig() { - YamlConfiguration yml = new YamlConfiguration(); + final YamlConfiguration yml = new YamlConfiguration(); - // check if messages.yml exists if (!conf.exists()) { - try { - conf.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } + try { conf.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } - // load config try { - yml.load(conf); // can throw error - for (PSL psl : PSL.values()) { + yml.load(conf); + boolean updated = false; - // fix message if need be - if (yml.getString(psl.path) == null) { // if msg not found in config - yml.set(psl.path, applyConfigColours(psl.defaultMessage)); + for (PSL psl : PSL.values()) { + String raw = yml.getString(psl.path); + + if (raw == null) { + // first time: write default MiniMessage + yml.set(psl.path, psl.defaultMessage); + psl.message = MINI.deserialize(psl.defaultMessage); + raw = psl.defaultMessage; + updated = true; } else { - // perform message upgrades - messageUpgrades(psl, yml); + // migrate legacy to MiniMessage if needed + String upgraded = upgradeToMini(raw); + if (!Objects.equals(upgraded, raw)) { + yml.set(psl.path, upgraded); + raw = upgraded; + updated = true; + } + psl.message = MINI.deserialize(raw); } - // load message - psl.message = applyInGameColours(yml.getString(psl.path)); - psl.isEmpty = psl.message.isEmpty(); + psl.isEmpty = raw.isEmpty(); } - try { - yml.save(conf); - } catch (IOException e) { - e.printStackTrace(); - } - } catch (Exception e) { // prevent bad messages.yml file from resetting the file - e.printStackTrace(); - } - } - // 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)); + if (updated) yml.save(conf); + } catch (Exception e) { + // don't wipe the file on error + e.printStackTrace(); } } - // match all {abc format for hex - private static final Pattern hexPattern = Pattern.compile("(?' + Matcher hex = HEX_HASH.matcher(s); + if (hex.find()) { + s = hex.replaceAll("<#$1>"); } - return msg.replace('&', '§'); - } + // 3) If any &-style legacy codes remain, convert to Components and serialize to MiniMessage + if (s.indexOf('&') >= 0) { + Component comp = LEGACY.deserialize(s); + s = MINI.serialize(comp); + } - private static String applyConfigColours(String msg) { - return msg.replace('§', '&'); + return s; } } diff --git a/src/main/java/dev/espi/protectionstones/PSStandardRegion.java b/src/main/java/dev/espi/protectionstones/PSStandardRegion.java index 243b1a40..8d0adcff 100644 --- a/src/main/java/dev/espi/protectionstones/PSStandardRegion.java +++ b/src/main/java/dev/espi/protectionstones/PSStandardRegion.java @@ -452,7 +452,7 @@ public void removeOwner(UUID uuid) { if (getTenant() != null) { PSPlayer tenant = PSPlayer.fromUUID(getTenant()); if (tenant.getOfflinePlayer().isOnline()) { - PSL.msg(Bukkit.getPlayer(getTenant()), PSL.RENT_TENANT_STOPPED_TENANT.msg() + PSL.msg(Bukkit.getPlayer(getTenant()), PSL.RENT_TENANT_STOPPED_TENANT .replace("%region%", getName() != null ? getName() : getId())); } } diff --git a/src/main/java/dev/espi/protectionstones/ProtectionStones.java b/src/main/java/dev/espi/protectionstones/ProtectionStones.java index bd1775f9..81b71e42 100644 --- a/src/main/java/dev/espi/protectionstones/ProtectionStones.java +++ b/src/main/java/dev/espi/protectionstones/ProtectionStones.java @@ -28,6 +28,7 @@ import dev.espi.protectionstones.utils.upgrade.LegacyUpgrade; import dev.espi.protectionstones.utils.UUIDCache; import dev.espi.protectionstones.utils.WGUtils; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.milkbowl.vault.economy.Economy; import org.bstats.bukkit.Metrics; import org.bukkit.*; @@ -44,6 +45,7 @@ import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; import net.luckperms.api.LuckPerms; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.lang.reflect.Field; @@ -63,7 +65,7 @@ public class ProtectionStones extends JavaPlugin { public static final int CONFIG_VERSION = 16; private boolean debug = false; - + private static ProtectionStones instance; public static File configLocation, blockDataFolder; public static CommentedFileConfig config; @@ -76,6 +78,8 @@ public class ProtectionStones extends JavaPlugin { private PSConfig configOptions; static HashMap protectionStonesOptions = new HashMap<>(); + private BukkitAudiences adventure; + // ps alias to id cache // > @@ -547,8 +551,25 @@ public void onLoad() { FlagHandler.registerFlags(); } + public @NotNull BukkitAudiences audiences() { + if (adventure == null) { + throw new IllegalStateException("Tried to access Adventure audiences while plugin is disabled!"); + } + return adventure; + } + + @Override + + public void onDisable() { + if (this.adventure != null) { + this.adventure.close(); + this.adventure = null; + } + } + @Override public void onEnable() { + this.adventure = BukkitAudiences.create(this); FlagHandler.registerHandlers(); // register custom WG flag handlers TomlFormat.instance(); @@ -576,7 +597,7 @@ public void onEnable() { // 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); + RegisteredServiceProvider econ = getServer().getServicesManager().getRegistration(Economy.class); if (econ == null) { getLogger().warning("No economy plugin found by Vault! There will be no economy support!"); vaultSupportEnabled = false; diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAddRemove.java b/src/main/java/dev/espi/protectionstones/commands/ArgAddRemove.java index b6f167e7..6b4ab777 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAddRemove.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAddRemove.java @@ -20,6 +20,7 @@ import dev.espi.protectionstones.utils.LimitUtil; import dev.espi.protectionstones.utils.UUIDCache; import dev.espi.protectionstones.utils.WGUtils; +import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -121,28 +122,38 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap UUIDCache.storeWGProfile(addPlayerUuid, addPlayerName)); + // add to WorldGuard profile cache asynchronously + Bukkit.getScheduler().runTaskAsynchronously( + ProtectionStones.getInstance(), + () -> UUIDCache.storeWGProfile(addPlayerUuid, addPlayerName) + ); } else if ((operationType.equals("remove") && r.isMember(addPlayerUuid)) || (operationType.equals("removeowner") && r.isOwner(addPlayerUuid))) { if (flags.containsKey("-a")) { - PSL.msg(p, PSL.REMOVED_FROM_REGION_SPECIFIC.msg() - .replace("%player%", addPlayerName) - .replace("%region%", r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")")); + PSL.msg(p, PSL.REMOVED_FROM_REGION_SPECIFIC.replaceAll(Map.of( + "%player%", addPlayerName, + "%region%", (r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")") + ))); } else { - PSL.msg(p, PSL.REMOVED_FROM_REGION.msg().replace("%player%", addPlayerName)); + PSL.msg(p, PSL.REMOVED_FROM_REGION.replaceAll(Map.of( + "%player%", addPlayerName + ))); } } + switch (operationType) { case "add" -> r.addMember(addPlayerUuid); case "remove" -> r.removeMember(addPlayerUuid); @@ -218,7 +229,7 @@ public boolean determinePlayerSurpassedLimit(Player commandSender, List { if (r instanceof PSGroupRegion) { return ((PSGroupRegion) r).getMergedRegions().stream(); @@ -227,11 +238,11 @@ public boolean determinePlayerSurpassedLimit(Player commandSender, List " + ChatColor.GRAY + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + - " admin cleanup [remove|preview] [-t typealias (optional)] [days] [world (optional)]"; + // --- Cleanup --- + public static Component getCleanupHelp() { + return Component.text("> ", NamedTextColor.AQUA) + .append(Component.text("/", NamedTextColor.GRAY)) + .append(Component.text(ProtectionStones.getInstance().getConfigOptions().base_command + " admin cleanup [remove|preview] [-t typealias (optional)] [days] [world (optional)]", NamedTextColor.GRAY)); } - public static String getFlagHelp() { - return ChatColor.AQUA + "> " + ChatColor.GRAY + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + - " admin flag [world] [flagname] [value|null|default]"; + // --- Flag --- + public static Component getFlagHelp() { + return Component.text("> ", NamedTextColor.AQUA) + .append(Component.text("/", NamedTextColor.GRAY)) + .append(Component.text(ProtectionStones.getInstance().getConfigOptions().base_command + " admin flag [world] [flagname] [value|null|default]", NamedTextColor.GRAY)); } - public static String getChangeBlockHelp() { - return ChatColor.AQUA + "> " + ChatColor.GRAY + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + - " admin changeblock [world] [oldtypealias] [newtypealias]"; + // --- Change Block --- + public static Component getChangeBlockHelp() { + return Component.text("> ", NamedTextColor.AQUA) + .append(Component.text("/", NamedTextColor.GRAY)) + .append(Component.text(ProtectionStones.getInstance().getConfigOptions().base_command + " admin changeblock [world] [oldtypealias] [newtypealias]", NamedTextColor.GRAY)); } - public static String getChangeRegionTypeHelp() { - return ChatColor.AQUA + "> " + ChatColor.GRAY + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + - " admin changeregiontype [world] [oldtype] [newtype]"; + // --- Change Region Type --- + public static Component getChangeRegionTypeHelp() { + return Component.text("> ", NamedTextColor.AQUA) + .append(Component.text("/", NamedTextColor.GRAY)) + .append(Component.text(ProtectionStones.getInstance().getConfigOptions().base_command + " admin changeregiontype [world] [oldtype] [newtype]", NamedTextColor.GRAY)); } - public static String getForceMergeHelp() { - return ChatColor.AQUA + "> " + ChatColor.GRAY + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + - " admin forcemerge [world]"; + // --- Force Merge --- + public static Component getForceMergeHelp() { + return Component.text("> ", NamedTextColor.AQUA) + .append(Component.text("/", NamedTextColor.GRAY)) + .append(Component.text(ProtectionStones.getInstance().getConfigOptions().base_command + " admin forcemerge [world]", NamedTextColor.GRAY)); } @Override @@ -93,53 +104,74 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap tabComplete(CommandSender sender, String alias, String[] args) { if (args.length == 2) { diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminChangeType.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminChangeType.java index 04981138..cc5bdc83 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminChangeType.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminChangeType.java @@ -22,6 +22,7 @@ import dev.espi.protectionstones.FlagHandler; import dev.espi.protectionstones.PSL; import dev.espi.protectionstones.utils.WGUtils; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; @@ -45,7 +46,9 @@ static boolean argumentAdminChangeType(CommandSender p, String[] args) { RegionManager rgm = WGUtils.getRegionManagerWithWorld(w); if (rgm == null) { - return PSL.msg(p, ChatColor.GRAY + "The world does not have WorldGuard configured!"); + return PSL.msg(p, MiniMessage.miniMessage().deserialize( + "The world does not have WorldGuard configured!" + )); } String fromType = args[3], toType = args[4]; diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminChangeblock.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminChangeblock.java index 64ae5245..c487d025 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminChangeblock.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminChangeblock.java @@ -19,6 +19,8 @@ import com.sk89q.worldguard.protection.regions.ProtectedRegion; import dev.espi.protectionstones.*; import dev.espi.protectionstones.utils.WGUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; @@ -37,11 +39,13 @@ static boolean argumentAdminChangeblock(CommandSender p, String[] args) { String world = args[2], fromBlockAlias = args[3], toBlockAlias = args[4]; if (ProtectionStones.getProtectBlockFromAlias(fromBlockAlias) == null) { - PSL.msg(p, ChatColor.GRAY + "The type to change from is not a registered protection block!"); + PSL.msg(p, Component.text("The type to change from is not a registered protection block!", NamedTextColor.GRAY)); + return true; } if (ProtectionStones.getProtectBlockFromAlias(toBlockAlias) == null) { - PSL.msg(p, ChatColor.GRAY + "The type to change to is not a registered protection block!"); + PSL.msg(p, Component.text("The type to change to is not a registered protection block!", NamedTextColor.GRAY)); + return true; } @@ -62,7 +66,7 @@ static boolean argumentAdminChangeblock(CommandSender p, String[] args) { } RegionManager rgm = WGUtils.getRegionManagerWithWorld(w); if (rgm == null) { - return PSL.msg(p, ChatColor.GRAY + "The world does not have WorldGuard configured!"); + return PSL.msg(p, Component.text("The world does not have WorldGuard configured!", NamedTextColor.GRAY)); } for (ProtectedRegion r : rgm.getRegions().values()) { if (ProtectionStones.isPSRegion(r)) { diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminCleanup.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminCleanup.java index 4736246a..65e8ca8d 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminCleanup.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminCleanup.java @@ -94,9 +94,10 @@ static boolean argumentAdminCleanup(CommandSender p, String[] preParseArgs) { Bukkit.getScheduler().runTaskAsynchronously(ProtectionStones.getInstance(), () -> { int days = (args.size() > 0) ? Integer.parseInt(args.get(0)) : 30; // 30 days is default if days aren't specified - PSL.msg(p, PSL.ADMIN_CLEANUP_HEADER.msg() - .replace("%arg%", cleanupOperation) - .replace("%days%", "" + days)); + PSL.msg(p, PSL.ADMIN_CLEANUP_HEADER.replaceAll(Map.of( + "%arg%", cleanupOperation, + "%days%", String.valueOf(days) + ))); HashSet activePlayers = new HashSet<>(); @@ -149,7 +150,7 @@ static private void regionLoop(Iterator deleteRegionsIterator, Command Bukkit.getScheduler().runTaskLater(ProtectionStones.getInstance(), () -> processRegion(deleteRegionsIterator, p, isRemoveOperation), 1); } else { // finished region iteration - PSL.msg(p, PSL.ADMIN_CLEANUP_FOOTER.msg() + PSL.msg(p, PSL.ADMIN_CLEANUP_FOOTER .replace("%arg%", isRemoveOperation ? "remove" : "preview")); // flush and close preview file diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminForceMerge.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminForceMerge.java index 92e7f3e6..ba1e8ca9 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminForceMerge.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminForceMerge.java @@ -25,6 +25,8 @@ import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.utils.WGMerge; import dev.espi.protectionstones.utils.WGUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; @@ -87,7 +89,7 @@ public static boolean argumentAdminForceMerge(CommandSender p, String[] args) { Set options = new HashSet<>(); for (int i = 3; i < args.length; i++) { if (!flags.contains(args[i])) { - PSL.msg(p, ChatColor.RED + "Invalid option."); + PSL.msg(p, Component.text("Invalid option.", NamedTextColor.RED)); return true; } else { options.add(args[i]); diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java index 344acd3b..a28b1548 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminHelp.java @@ -16,149 +16,57 @@ package dev.espi.protectionstones.commands; import dev.espi.protectionstones.ProtectionStones; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.TextComponent; -import org.bukkit.ChatColor; +import dev.espi.protectionstones.PSL; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import org.bukkit.command.CommandSender; public class ArgAdminHelp { - private static void send(CommandSender p, String text, String info, String clickCommand, boolean run) { - // Create the main text component from legacy text. - BaseComponent[] mainComponents = TextComponent.fromLegacyText(text); - TextComponent mainText = new TextComponent(""); - for (BaseComponent component : mainComponents) { - mainText.addExtra(component); - } + private static void send(CommandSender sender, String command, String description, boolean run) { + Component line = Component.text("> ", NamedTextColor.AQUA) + .append(Component.text(command, NamedTextColor.GRAY)) + .hoverEvent(HoverEvent.showText(Component.text(description, NamedTextColor.WHITE))) + .clickEvent(run + ? ClickEvent.runCommand(command) + : ClickEvent.suggestCommand(command)); - // Create the hover event from the info text, add click event after - BaseComponent[] hoverComponents = TextComponent.fromLegacyText(info); - mainText.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponents)); - //toggle for running on mouse click - if (run) { - mainText.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, ChatColor.stripColor(clickCommand))); - } else { - mainText.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, ChatColor.stripColor(clickCommand))); - } - - // Send the assembled message. - p.spigot().sendMessage(mainText); + PSL.msg(sender, line); } - static boolean argumentAdminHelp(CommandSender p, String[] args) { - String baseCommand = ProtectionStones.getInstance().getConfigOptions().base_command; - String bc = "/" + baseCommand; - String tx = ChatColor.AQUA + "> " + ChatColor.GRAY + bc; - - p.sendMessage(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "===============" + - ChatColor.RESET + " PS Admin Help " + - ChatColor.DARK_GRAY + ChatColor.STRIKETHROUGH + "===============\n"); - - // The run parameter is currently unused by all messages - send(p, - tx + " admin version", - "Show the version number of the plugin.\n\n" + bc + " admin version", - baseCommand + " admin version", - false); - - send(p, - tx + " admin hide", - "Hide all of the protection stone blocks in the world you are in.\n\n" + bc + " admin hide", - bc + " admin hide", - false); - - send(p, - tx + " admin unhide", - "Unhide all of the protection stone blocks in the world you are in.\n\n" + bc + " admin unhide", - bc + " admin unhide", - false); - - send(p, - tx + " admin cleanup remove", - "Remove inactive players that haven't joined within the last [days] days from protected regions in the world you are in (or specified). Then, remove any regions with no owners left.\n\n" + - bc + " admin cleanup remove [days] [-t typealias (optional)] [world (console)]", - bc + " admin cleanup remove", - false); - - send(p, - tx + " admin cleanup disown", - "Remove inactive players that haven't joined within the last [days] days from protected regions in the world you are in (or specified).\n\n" + - bc + " admin cleanup disown", - bc + " admin cleanup disown", - false); - - send(p, - tx + " admin flag", - "Set a flag for all protection stone regions in a world.\n\n" + - bc + " admin flag [world] [flagname] [value|null|default]", - bc + " admin flag [world] [flagname] [value|null|default]", - false); - - send(p, - tx + " admin lastlogon", - "Get the last time a player logged on.\n\n" + bc + " admin lastlogon [player]", - bc + " admin lastlogon", - false); - - send(p, - tx + " admin lastlogons", - "List all of the last logons of each player.\n\n" + bc + " admin lastlogons", - bc + " admin lastlogons", - false); - - send(p, - tx + " admin stats", - "Show some statistics of the plugin.\n\n" + bc + " admin stats [player (optional)]", - bc + " admin stats", - false); - - send(p, - tx + " admin recreate", - "Recreate all PS regions using radius set in config.\n\n" + bc + " admin recreate", - bc + " admin recreate", - false); - - send(p, - tx + " admin debug", - "Toggle debug mode.\n\n" + bc + " admin debug", - bc + " admin debug", - false); - - send(p, - tx + " admin settaxautopayers", - "Add a tax autopayer for every region on the server that does not have one.\n\n" + bc + " admin settaxautopayers", - bc + " admin settaxautopayers", - false); - - send(p, - tx + " admin forcemerge", - "Merge overlapping PS regions together if they have the same owners, members and flags.\n\n" + - bc + " admin forcemerge [world]", - bc + " admin forcemerge [world]", - false); - - send(p, - tx + " admin changeblock", - "Change all of the PS blocks and regions in a world to a different block. Both blocks must be configured in config.\n\n" + - bc + " admin changeblock [world] [oldtypealias] [newtypealias]", - bc + " admin changeblock [world] [oldtypealias] [newtypealias]", - false); - - send(p, - tx + " admin changeregiontype", - "Change the internal type of all PS regions of a certain type. Useful for error correction.\n\n" + - bc + " admin changeregiontype [world] [oldtype] [newtype]", - bc + " admin changeregiontype [world] [oldtype] [newtype]", - false); - - send(p, - tx + " admin fixregions", - "Use this command to recalculate block types for PS regions in a world.\n\n" + bc + " admin fixregions", - bc + " admin fixregions", - false); - p.sendMessage(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "============================================="); + static boolean argumentAdminHelp(CommandSender sender, String[] args) { + String baseCommand = "/" + ProtectionStones.getInstance().getConfigOptions().base_command; + + // Header + PSL.msg(sender, Component.empty().append(Component.text("===============", NamedTextColor.DARK_GRAY, TextDecoration.STRIKETHROUGH)) + .append(Component.space()) + .append(Component.text("PS Admin Help", NamedTextColor.AQUA).decoration(TextDecoration.STRIKETHROUGH, false)) + .append(Component.space()) + .append(Component.text("===============", NamedTextColor.DARK_GRAY, TextDecoration.STRIKETHROUGH))); + + // Entries + send(sender, baseCommand + " admin version", "Show the version number of the plugin.", false); + send(sender, baseCommand + " admin hide", "Hide all protection stone blocks in the current world.", false); + send(sender, baseCommand + " admin unhide", "Unhide all protection stone blocks in the current world.", false); + send(sender, baseCommand + " admin cleanup remove", "Remove inactive players, then remove empty regions.", false); + send(sender, baseCommand + " admin cleanup disown", "Remove inactive players from regions only.", false); + send(sender, baseCommand + " admin flag [world] [flagname] [value|null|default]", "Set a flag for all PS regions in a world.", false); + send(sender, baseCommand + " admin lastlogon [player]", "Get the last time a player logged on.", false); + send(sender, baseCommand + " admin lastlogons", "List last logons of all players.", false); + send(sender, baseCommand + " admin stats [player?]", "Show plugin statistics.", false); + send(sender, baseCommand + " admin recreate", "Recreate all PS regions using the configured radius.", false); + send(sender, baseCommand + " admin debug", "Toggle debug mode.", false); + send(sender, baseCommand + " admin settaxautopayers", "Assign a tax autopayer to all regions without one.", false); + send(sender, baseCommand + " admin forcemerge [world]", "Merge overlapping PS regions if owners/members/flags match.", false); + send(sender, baseCommand + " admin changeblock [world] [oldtypealias] [newtypealias]", "Change all PS blocks/regions in a world to a different block.", false); + send(sender, baseCommand + " admin changeregiontype [world] [oldtype] [newtype]", "Change the type of all PS regions of a certain type.", false); + send(sender, baseCommand + " admin fixregions", "Recalculate block types for PS regions in a world.", false); + + // Footer + PSL.msg(sender, Component.text("=============================================", NamedTextColor.DARK_GRAY, TextDecoration.STRIKETHROUGH)); return true; } diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminHide.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminHide.java index 30d1515e..2e7d8042 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminHide.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminHide.java @@ -62,7 +62,7 @@ static boolean argumentAdminHide(CommandSender p, String[] args) { } String hMessage = args[1].equalsIgnoreCase("unhide") ? "unhidden" : "hidden"; - PSL.msg(p, PSL.ADMIN_HIDE_TOGGLED.msg() + PSL.msg(p, PSL.ADMIN_HIDE_TOGGLED .replace("%message%", hMessage)); }); diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminLastlogon.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminLastlogon.java index e91aa61d..c6f7a075 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminLastlogon.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminLastlogon.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.Comparator; +import java.util.Map; class ArgAdminLastlogon { // /ps admin lastlogon @@ -34,7 +35,7 @@ public int compare(OfflinePlayer o1, OfflinePlayer o2) { } static boolean argumentAdminLastLogon(CommandSender p, String[] args) { if (args.length < 3) { - p.sendMessage(PSL.COMMAND_REQUIRES_PLAYER_NAME.msg()); + PSL.msg(p, PSL.COMMAND_REQUIRES_PLAYER_NAME.msg()); return true; } OfflinePlayer op = Bukkit.getOfflinePlayer(args[2]); @@ -42,15 +43,16 @@ static boolean argumentAdminLastLogon(CommandSender p, String[] args) { String playerName = args[2]; long lastPlayed = (System.currentTimeMillis() - op.getLastPlayed()) / 86400000L; - PSL.msg(p, PSL.ADMIN_LAST_LOGON.msg() - .replace("%player%", playerName) - .replace("%days%", "" +lastPlayed)); + PSL.msg(p, PSL.ADMIN_LAST_LOGON.replaceAll(Map.of( + "%player%", playerName, + "%days%", String.valueOf(lastPlayed) + ))); if (op.isBanned()) { - PSL.msg(p, PSL.ADMIN_IS_BANNED.msg() - .replace("%player%", playerName)); + PSL.msg(p, PSL.ADMIN_IS_BANNED.replace("%player%", playerName)); } + return true; } @@ -67,7 +69,7 @@ static boolean argumentAdminLastLogons(CommandSender p, String[] args) { } OfflinePlayer[] offlinePlayerList = Bukkit.getServer().getOfflinePlayers().clone(); int playerCounter = 0; - PSL.msg(p, PSL.ADMIN_LASTLOGONS_HEADER.msg() + PSL.msg(p, PSL.ADMIN_LASTLOGONS_HEADER .replace("%days%", "" + days)); Arrays.sort(offlinePlayerList, new PlayerComparator()); @@ -75,15 +77,17 @@ static boolean argumentAdminLastLogons(CommandSender p, String[] args) { long lastPlayed = (System.currentTimeMillis() - offlinePlayer.getLastPlayed()) / 86400000L; if (lastPlayed >= days) { playerCounter++; - PSL.msg(p, PSL.ADMIN_LASTLOGONS_LINE.msg() - .replace("%player%", offlinePlayer.getName()) - .replace("%time%", "" + lastPlayed)); + PSL.msg(p, PSL.ADMIN_LASTLOGONS_LINE.replaceAll(Map.of( + "%player%", offlinePlayer.getName(), + "%time%", String.valueOf(lastPlayed) + ))); } } - PSL.msg(p, PSL.ADMIN_LASTLOGONS_FOOTER.msg() - .replace("%count%", "" + playerCounter) - .replace("%checked%", "" + offlinePlayerList.length)); + PSL.msg(p, PSL.ADMIN_LASTLOGONS_FOOTER.replaceAll(Map.of( + "%count%", String.valueOf(playerCounter), + "%checked%", String.valueOf(offlinePlayerList.length) + ))); return true; } diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminRecreate.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminRecreate.java index 469bff5d..824a6490 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminRecreate.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminRecreate.java @@ -20,6 +20,8 @@ import com.sk89q.worldguard.protection.regions.ProtectedRegion; import dev.espi.protectionstones.*; import dev.espi.protectionstones.utils.WGUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; @@ -31,7 +33,7 @@ class ArgAdminRecreate { static boolean argumentAdminRecreate(CommandSender s, String[] args) { - s.sendMessage(ChatColor.YELLOW + "Recreating..."); + PSL.msg(s, Component.text("Recreating...", NamedTextColor.YELLOW)); HashMap m = WGUtils.getAllRegionManagers(); for (World w : m.keySet()) { @@ -62,7 +64,7 @@ static boolean argumentAdminRecreate(CommandSender s, String[] args) { } } - s.sendMessage(ChatColor.YELLOW + "Done."); + PSL.msg(s, Component.text("Done.", NamedTextColor.YELLOW)); return true; } } diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgAdminSetTaxAutopayers.java b/src/main/java/dev/espi/protectionstones/commands/ArgAdminSetTaxAutopayers.java index 592e300e..4ef055d4 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgAdminSetTaxAutopayers.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgAdminSetTaxAutopayers.java @@ -22,6 +22,8 @@ import dev.espi.protectionstones.PSRegion; import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.utils.WGUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -29,10 +31,15 @@ class ArgAdminSetTaxAutopayers { static boolean argumentAdminSetTaxAutopayers(CommandSender s, String[] args) { if (!ProtectionStones.getInstance().getConfigOptions().taxEnabled) { - return PSL.msg(s, ChatColor.RED + "Taxes are disabled! Enable it in the config."); + return PSL.msg(s, Component.text("Taxes are disabled! Enable it in the config.", NamedTextColor.RED)); } - PSL.msg(s, ChatColor.GRAY + "Scanning through regions, and setting tax autopayers for regions that don't have one..."); + PSL.msg(s, + Component.text( + "Scanning through regions, and setting tax autopayers for regions that don't have one...", + NamedTextColor.GRAY + ) + ); Bukkit.getScheduler().runTaskAsynchronously(ProtectionStones.getInstance(), () -> { WGUtils.getAllRegionManagers().forEach((w, rgm) -> { @@ -41,14 +48,21 @@ static boolean argumentAdminSetTaxAutopayers(CommandSender s, String[] args) { if (psr != null && psr.getTypeOptions() != null && psr.getTypeOptions().taxPeriod != -1 && psr.getTaxAutopayer() == null) { if (psr.getOwners().size() >= 1) { - PSL.msg(s, ChatColor.GRAY + "Configured tax autopayer to be " + psr.getOwners().get(0).toString() + " for region " + psr.getId()); + PSL.msg(s, + Component.text("Configured tax autopayer to be ", NamedTextColor.GRAY) + .append(Component.text(psr.getOwners().get(0).toString(), NamedTextColor.AQUA)) + .append(Component.text(" for region ", NamedTextColor.GRAY)) + .append(Component.text(psr.getId(), NamedTextColor.AQUA)) + ); + psr.setTaxAutopayer(psr.getOwners().get(0)); } } } }); - PSL.msg(s, ChatColor.GREEN + "Complete!"); + PSL.msg(s, Component.text("Complete!", NamedTextColor.GREEN)); + }); return true; diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgBuySell.java b/src/main/java/dev/espi/protectionstones/commands/ArgBuySell.java index 8a4fef13..930a54f6 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgBuySell.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgBuySell.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Map; public class ArgBuySell implements PSCommandArg { @Override @@ -81,21 +82,27 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap b.matchLiteral("%limit%") + .replacement(String.valueOf(PSPlayer.fromPlayer(p).getGlobalRegionLimits())))); if (!PSPlayer.fromPlayer(p).hasAmount(r.getPrice())) - return PSL.msg(p, PSL.NOT_ENOUGH_MONEY.msg().replace("%price%", new DecimalFormat("#.##").format(r.getPrice()))); + return PSL.msg(p, PSL.NOT_ENOUGH_MONEY.replace("%price%", new DecimalFormat("#.##").format(r.getPrice()))); - PSL.msg(p, PSL.BUY_SOLD_BUYER.msg() - .replace("%region%", r.getName() == null ? r.getId() : r.getName()) - .replace("%price%", String.format("%.2f", r.getPrice())) - .replace("%player%", UUIDCache.getNameFromUUID(r.getLandlord()))); + PSL.msg(p, PSL.BUY_SOLD_BUYER.replaceAll(Map.of( + "%region%", (r.getName() == null ? r.getId() : r.getName()), + "%price%", String.format("%.2f", r.getPrice()), + "%player%", UUIDCache.getNameFromUUID(r.getLandlord()) + ))); if (Bukkit.getPlayer(r.getLandlord()) != null) { - PSL.msg(Bukkit.getPlayer(r.getLandlord()), PSL.BUY_SOLD_SELLER.msg() - .replace("%region%", r.getName() == null ? r.getId() : r.getName()) - .replace("%price%", String.format("%.2f", r.getPrice())) - .replace("%player%", p.getName())); + PSL.msg( + Bukkit.getPlayer(r.getLandlord()), + PSL.BUY_SOLD_SELLER.replaceAll(Map.of( + "%region%", (r.getName() == null ? r.getId() : r.getName()), + "%price%", String.format("%.2f", r.getPrice()), + "%player%", p.getName() + )) + ); } r.sell(p.getUniqueId()); @@ -118,7 +125,7 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap allowedFlags = new ArrayList<>(r.getTypeOptions().allowedFlags.keySet()); + final List allowedFlags = new ArrayList<>(r.getTypeOptions().allowedFlags.keySet()); - // ensure the page is valid and in range - if (page < 0 || (page * GUI_SIZE) > allowedFlags.size()) { - PSL.msg(p, PSL.PAGE_DOES_NOT_EXIST.msg()); - return true; - } + // ensure the page is valid and in range + if (page < 0 || (page * GUI_SIZE) > allowedFlags.size()) { + PSL.msg(p, PSL.PAGE_DOES_NOT_EXIST.msg()); + return true; + } - // add blank space if gui not long enough - for (int i = 0; i < (GUI_SIZE * page + GUI_SIZE) - (Math.min(allowedFlags.size(), GUI_SIZE * page + GUI_SIZE) - GUI_SIZE * page); i++) { - PSL.msg(p, ChatColor.WHITE + ""); - } + // add blank space if gui not long enough + final int rowsStart = GUI_SIZE * page; + final int rowsEnd = Math.min(allowedFlags.size(), rowsStart + GUI_SIZE); + final int blanks = (rowsStart + GUI_SIZE) - (rowsEnd - rowsStart); + for (int i = 0; i < blanks; i++) { + PSL.msg(p, Component.text(" ")); + } - PSL.msg(p, PSL.FLAG_GUI_HEADER.msg()); + PSL.msg(p, PSL.FLAG_GUI_HEADER.msg()); - // send actual flags - for (int i = GUI_SIZE * page; i < Math.min(allowedFlags.size(), GUI_SIZE * page + GUI_SIZE); i++) { - if (i >= allowedFlags.size()) { - PSL.msg(p, ChatColor.WHITE + ""); - } else { - String flag = allowedFlags.get(i); - List currentFlagGroups = r.getTypeOptions().allowedFlags.get(flag); - TextComponent flagLine = new TextComponent(); + // send actual flags + for (int i = rowsStart; i < Math.min(allowedFlags.size(), rowsStart + GUI_SIZE); i++) { + if (i >= allowedFlags.size()) { + PSL.msg(p, Component.text(" ")); + continue; + } + + final String flagKey = allowedFlags.get(i); + final List currentFlagGroups = r.getTypeOptions().allowedFlags.get(flagKey); // calculate flag command - String suggestedCommand = "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " flag "; + final String suggestedCommand = "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " flag "; // match flag - Flag f = Flags.fuzzyMatchFlag(WGUtils.getFlagRegistry(), flag); + final Flag f = Flags.fuzzyMatchFlag(WGUtils.getFlagRegistry(), flagKey); if (f == null) continue; + Object fValue = r.getWGRegion().getFlag(f); - // check current flag's set group + // sanitize § -> & in String flag values to avoid "illegal characters" kicks + if (fValue instanceof String) { + fValue = ((String) fValue).replace("§", "&"); + } + + // current region group for this flag String groupfValue = "all"; if (f.getRegionGroupFlag() != null && r.getWGRegion().getFlag(f.getRegionGroupFlag()) != null) { groupfValue = r.getWGRegion().getFlag(f.getRegionGroupFlag()).toString() .toLowerCase().replace("_", ""); } - // add flag group if there is one set for the flag (for use in click commands) - String flagGroup = ""; - if (f.getRegionGroupFlag() != null && r.getWGRegion().getFlag(f.getRegionGroupFlag()) != null) { - flagGroup = "-g " + groupfValue + " "; - } + // if a group is set, include it in click commands + final String flagGroupArg = (f.getRegionGroupFlag() != null && r.getWGRegion().getFlag(f.getRegionGroupFlag()) != null) + ? "-g " + groupfValue + " " + : ""; - // replace § with & to prevent "illegal characters in chat" disconnection - if (fValue instanceof String) { - fValue = ((String) fValue).replace("§", "&"); - } - - // add line based on flag type - boolean isGroupValueAll = groupfValue.equalsIgnoreCase("all") || groupfValue.isEmpty();; - if (f instanceof StateFlag) { // allow/deny - - TextComponent allow = new TextComponent((fValue == StateFlag.State.ALLOW ? ChatColor.WHITE : ChatColor.DARK_GRAY) + "Allow"), - deny = new TextComponent((fValue == StateFlag.State.DENY ? ChatColor.WHITE : ChatColor.DARK_GRAY) + "Deny"); + // build the line + Component flagLine = Component.empty(); - allow.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.FLAG_GUI_HOVER_SET.msg()).create())); - deny.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.FLAG_GUI_HOVER_SET.msg()).create())); + final boolean isGroupValueAll = groupfValue.equalsIgnoreCase("all") || groupfValue.isEmpty(); - if (fValue == StateFlag.State.ALLOW) { - allow.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " none")); - deny.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " deny")); - } else if (fValue == StateFlag.State.DENY) { - allow.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " allow")); - deny.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " none")); + if (f instanceof StateFlag) { + // allow/deny widgets + final boolean isAllow = fValue == StateFlag.State.ALLOW; + final boolean isDeny = fValue == StateFlag.State.DENY; + + Component allow = Component.text("Allow", isAllow ? NamedTextColor.WHITE : NamedTextColor.DARK_GRAY); + Component deny = Component.text("Deny", isDeny ? NamedTextColor.WHITE : NamedTextColor.DARK_GRAY); + + allow = allow.hoverEvent(HoverEvent.showText(PSL.FLAG_GUI_HOVER_SET.msg())); + deny = deny.hoverEvent(HoverEvent.showText(PSL.FLAG_GUI_HOVER_SET.msg())); + + if (isAllow) { + allow = allow.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " none")); + deny = deny.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " deny")); + } else if (isDeny) { + allow = allow.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " allow")); + deny = deny.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " none")); } else { - allow.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " allow")); - deny.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " deny")); + allow = allow.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " allow")); + deny = deny.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " deny")); } - - flagLine.addExtra(allow); - flagLine.addExtra(" "); - flagLine.addExtra(deny); - flagLine.addExtra(getDots(5)); - } else if (f instanceof BooleanFlag) { // true/false - TextComponent allow = new TextComponent((fValue == Boolean.TRUE ? ChatColor.WHITE : ChatColor.DARK_GRAY) + "True"), - deny = new TextComponent((fValue == Boolean.FALSE ? ChatColor.WHITE : ChatColor.DARK_GRAY) + "False"); - - allow.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.FLAG_GUI_HOVER_SET.msg()).create())); - deny.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.FLAG_GUI_HOVER_SET.msg()).create())); - if (fValue == Boolean.TRUE) { - allow.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " none")); - deny.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " false")); - } else if (fValue == Boolean.FALSE) { - allow.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " true")); - deny.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " none")); + flagLine = flagLine.append(allow) + .append(space()) + .append(deny) + .append(dots(5)); + } else if (f instanceof BooleanFlag) { + // true/false widgets + final boolean isTrue = fValue == Boolean.TRUE; + final boolean isFalse = fValue == Boolean.FALSE; + + Component t = Component.text("True", isTrue ? NamedTextColor.WHITE : NamedTextColor.DARK_GRAY); + Component fC = Component.text("False", isFalse ? NamedTextColor.WHITE : NamedTextColor.DARK_GRAY); + + t = t.hoverEvent(HoverEvent.showText(PSL.FLAG_GUI_HOVER_SET.msg())); + fC = fC.hoverEvent(HoverEvent.showText(PSL.FLAG_GUI_HOVER_SET.msg())); + + if (isTrue) { + t = t.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " none")); + fC = fC.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " false")); + } else if (isFalse) { + t = t.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " true")); + fC = fC.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " none")); } else { - allow.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " true")); - deny.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + flagGroup + page + ":" + flag + " false")); + t = t.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " true")); + fC = fC.clickEvent(ClickEvent.runCommand(suggestedCommand + flagGroupArg + page + ":" + flagKey + " false")); } - flagLine.addExtra(allow); - flagLine.addExtra(" "); - flagLine.addExtra(deny); - flagLine.addExtra(getDots(5)); - } else { // text - TextComponent edit = new TextComponent(ChatColor.DARK_GRAY + "Edit"); - edit.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.FLAG_GUI_HOVER_SET_TEXT.msg() - .replace("%value%", fValue == null ? "none" : fValue.toString())).create())); - edit.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, suggestedCommand + flagGroup + flag + " ")); - flagLine.addExtra(edit); - flagLine.addExtra(getDots(22)); + flagLine = flagLine.append(t) + .append(space()) + .append(fC) + .append(dots(5)); + } else { + // text flag -> edit widget + final String currentVal = (fValue == null ? "none" : fValue.toString()); + Component edit = Component.text("Edit", NamedTextColor.DARK_GRAY) + .hoverEvent(HoverEvent.showText( + PSL.FLAG_GUI_HOVER_SET_TEXT.replace("%value%", currentVal) + )) + .clickEvent(ClickEvent.suggestCommand(suggestedCommand + flagGroupArg + flagKey + " ")); + flagLine = flagLine.append(edit) + .append(dots(22)); } - // put group it applies to - TextComponent groupChange = new TextComponent(ChatColor.DARK_GRAY + " [ " + ChatColor.WHITE + groupfValue + ChatColor.DARK_GRAY + " ]"); + // group switcher [ group ] + Component groupChange = Component.text(" [ ", NamedTextColor.DARK_GRAY) + .append(Component.text(groupfValue, NamedTextColor.WHITE)) + .append(Component.text(" ]", NamedTextColor.DARK_GRAY)); - String nextGroup; - if (currentFlagGroups.contains(groupfValue)) { // if the current flag group is an allowed flag group + // figure out next group + final String nextGroup; + if (currentFlagGroups.contains(groupfValue)) { nextGroup = currentFlagGroups.get((currentFlagGroups.indexOf(groupfValue) + 1) % currentFlagGroups.size()); - } else { // otherwise, just take the first allowed flag group + } else { nextGroup = currentFlagGroups.get(0); } - // set hover and click task for flag group - BaseComponent[] hover; - // HACK: Prevent pvp flag value from being changed to none/null - // Special handling for "pvp" flag with "all" group, disabling interaction. - if (flag.equalsIgnoreCase("pvp") && isGroupValueAll) { - hover = new ComponentBuilder(PSL.FLAG_PREVENT_EXPLOIT_HOVER.msg()).create(); - // Remove click action to fully disable changing this group. - groupChange.setClickEvent(null); - } else if (fValue == null) { - hover = new ComponentBuilder(PSL.FLAG_GUI_HOVER_CHANGE_GROUP_NULL.msg()).create(); + // hover/click for group change + // special-case pvp+all prevention + if (flagKey.equalsIgnoreCase("pvp") && isGroupValueAll) { + groupChange = groupChange + .hoverEvent(HoverEvent.showText(PSL.FLAG_PREVENT_EXPLOIT_HOVER.msg())); + // no click event (disabled on purpose) } else { - hover = new ComponentBuilder(PSL.FLAG_GUI_HOVER_CHANGE_GROUP.msg().replace("%group%", nextGroup)).create(); + if (fValue == null) { + groupChange = groupChange.hoverEvent(HoverEvent.showText(PSL.FLAG_GUI_HOVER_CHANGE_GROUP_NULL.msg())); + } else { + groupChange = groupChange + .hoverEvent(HoverEvent.showText(PSL.FLAG_GUI_HOVER_CHANGE_GROUP.replace("%group%", nextGroup))) + .clickEvent(ClickEvent.runCommand(suggestedCommand + "-g " + nextGroup + " " + page + ":" + flagKey + " " + (fValue == null ? "none" : fValue))); + } } - // Always set hover if the flag is pvp and group is "all" - if (flag.equalsIgnoreCase("pvp") && groupfValue.equalsIgnoreCase("all")) { - hover = new ComponentBuilder(PSL.FLAG_PREVENT_EXPLOIT_HOVER.msg()).create(); - groupChange.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hover)); - groupChange.setClickEvent(null); // Disable click event explicitly - } else if (!nextGroup.equals(groupfValue)) { - groupChange.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hover)); - groupChange.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand + "-g " + nextGroup + " " + page + ":" + flag + " " + fValue)); - } + // append group and trailing dots + flag name + flagLine = flagLine.append(groupChange); - flagLine.addExtra(groupChange); - // send message - flagLine.addExtra(getDots(40 - REGION_GROUP_KERNING_LENGTHS[FLAG_GROUPS.indexOf(groupfValue)]) + ChatColor.AQUA + " " + flag); + // keep your kerning/dots layout + final int kerning = 40 - REGION_GROUP_KERNING_LENGTHS[FLAG_GROUPS.indexOf(groupfValue)]; + flagLine = flagLine.append(dots(Math.max(0, kerning))) + .append(space()) + .append(Component.text(flagKey, NamedTextColor.AQUA)); - p.spigot().sendMessage(flagLine); + // send + ProtectionStones.getInstance().audiences().player(p).sendMessage(flagLine); } + + return true; } - // create footer - TextComponent backPage = new TextComponent(ChatColor.AQUA + " <<"), nextPage = new TextComponent(ChatColor.AQUA + ">> "); - backPage.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.GO_BACK_PAGE.msg()).create())); - nextPage.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.GO_NEXT_PAGE.msg()).create())); - backPage.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " flag " + (page - 1))); - nextPage.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " flag " + (page + 1))); - - TextComponent footer = new TextComponent(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET); - // add back page button if the page isn't 0 - if (page != 0) footer.addExtra(backPage); - // add page number - footer.addExtra(new TextComponent(ChatColor.WHITE + " " + (page + 1) + " ")); - // add forward page button if the page isn't last - if (page * GUI_SIZE + GUI_SIZE < r.getTypeOptions().allowedFlags.size()) footer.addExtra(nextPage); - footer.addExtra(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "====="); - - p.spigot().sendMessage(footer); - return true; + // helpers + private static Component dots(final int n) { + return (n <= 0) ? Component.empty() : Component.text(".".repeat(n), NamedTextColor.DARK_GRAY); + } + private static Component space() { + return Component.text(" "); } @Override @@ -382,7 +397,7 @@ static void setFlag(PSRegion r, CommandSender p, String flagName, String value, region.setFlag(flag.getRegionGroupFlag(), null); } - PSL.msg(p, PSL.FLAG_SET.msg().replace("%flag%", flagName)); + PSL.msg(p, PSL.FLAG_SET.replace("%flag%", flagName)); } else if (value.equalsIgnoreCase("null") || value.equalsIgnoreCase("none")) { // null flag (remove) @@ -400,7 +415,7 @@ static void setFlag(PSRegion r, CommandSender p, String flagName, String value, region.setFlag(flag.getRegionGroupFlag(), null); } - PSL.msg(p, PSL.FLAG_SET.msg().replace("%flag%", flagName)); + PSL.msg(p, PSL.FLAG_SET.replace("%flag%", flagName)); } else { // custom set flag using WG internal FlagContext fc = FlagContext.create().setInput(value).build(); @@ -408,12 +423,12 @@ static void setFlag(PSRegion r, CommandSender p, String flagName, String value, if (!groupValue.equals("") && flag.getRegionGroupFlag() != null) { region.setFlag(flag.getRegionGroupFlag(), flag.getRegionGroupFlag().detectValue(groupValue)); } - PSL.msg(p, PSL.FLAG_SET.msg().replace("%flag%", flagName)); + PSL.msg(p, PSL.FLAG_SET.replace("%flag%", flagName)); } } catch (InvalidFlagFormat invalidFlagFormat) { //invalidFlagFormat.printStackTrace(); - PSL.msg(p, PSL.FLAG_NOT_SET.msg().replace("%flag%", flagName)); + PSL.msg(p, PSL.FLAG_NOT_SET.replace("%flag%", flagName)); } } diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgGet.java b/src/main/java/dev/espi/protectionstones/commands/ArgGet.java index c66a2832..9fdfc65b 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgGet.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgGet.java @@ -19,10 +19,9 @@ import dev.espi.protectionstones.PSProtectBlock; import dev.espi.protectionstones.PSL; import dev.espi.protectionstones.ProtectionStones; -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.TextComponent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; import net.milkbowl.vault.economy.EconomyResponse; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; @@ -55,32 +54,46 @@ public HashMap getRegisteredFlags() { } private boolean openGetGUI(Player p) { + // Send the header PSL.msg(p, PSL.GET_HEADER.msg()); + for (PSProtectBlock b : ProtectionStones.getInstance().getConfiguredBlocks()) { - if ((!b.permission.equals("") && !p.hasPermission(b.permission)) || (b.preventPsGet && !p.hasPermission("protectionstones.admin"))) { + if ((!b.permission.equals("") && !p.hasPermission(b.permission)) + || (b.preventPsGet && !p.hasPermission("protectionstones.admin"))) { continue; // no permission } String price = new DecimalFormat("#.##").format(b.price); - TextComponent tc = new TextComponent(PSL.GET_GUI_BLOCK.msg() - .replace("%alias%", b.alias) - .replace("%price%", price) - .replace("%description%", b.description) - .replace("%xradius%", ""+b.xRadius) - .replace("%yradius%", ""+b.yRadius) - .replace("%zradius%", ""+b.zRadius)); - - tc.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.GET_GUI_HOVER.msg() - .replace("%alias%", b.alias) - .replace("%price%", price) - .replace("%description%", b.description) - .replace("%xradius%", ""+b.xRadius) - .replace("%yradius%", ""+b.yRadius) - .replace("%zradius%", ""+b.zRadius)).create())); - tc.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " get " + b.alias)); - - p.spigot().sendMessage(tc); + // Build the line using placeholders + Component line = PSL.GET_GUI_BLOCK.replaceAll(Map.of( + "%alias%", b.alias, + "%price%", price, + "%description%", b.description, + "%xradius%", String.valueOf(b.xRadius), + "%yradius%", String.valueOf(b.yRadius), + "%zradius%", String.valueOf(b.zRadius) + )); + + // Build hover text + Component hover = PSL.GET_GUI_HOVER.replaceAll(Map.of( + "%alias%", b.alias, + "%price%", price, + "%description%", b.description, + "%xradius%", String.valueOf(b.xRadius), + "%yradius%", String.valueOf(b.yRadius), + "%zradius%", String.valueOf(b.zRadius) + )); + + // Add hover + click events + Component clickable = line + .hoverEvent(HoverEvent.showText(hover)) + .clickEvent(ClickEvent.runCommand( + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " get " + b.alias + )); + + // Send Adventure component to player + PSL.msg(p, clickable); } return true; } @@ -113,7 +126,7 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap helpMenu = new ArrayList<>(); public static void initHelpMenu() { - String base = "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " "; + final String base = "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " "; helpMenu.clear(); + helpMenu.add(new HelpEntry(sendWithPerm(PSL.INFO_HELP.msg(), PSL.INFO_HELP_DESC.msg(), base + "info"), "protectionstones.info")); helpMenu.add(new HelpEntry(sendWithPerm(PSL.ADDREMOVE_HELP.msg(), PSL.ADDREMOVE_HELP_DESC.msg(), base), "protectionstones.members")); helpMenu.add(new HelpEntry(sendWithPerm(PSL.ADDREMOVE_OWNER_HELP.msg(), PSL.ADDREMOVE_OWNER_HELP_DESC.msg(), base), "protectionstones.owners")); @@ -78,6 +76,7 @@ public static void initHelpMenu() { helpMenu.add(new HelpEntry(sendWithPerm(PSL.REGION_HELP.msg(), PSL.REGION_HELP_DESC.msg(), base + "region"), "protectionstones.region")); helpMenu.add(new HelpEntry(sendWithPerm(PSL.ADMIN_HELP.msg(), PSL.ADMIN_HELP_DESC.msg(), base + "admin"), "protectionstones.admin")); helpMenu.add(new HelpEntry(sendWithPerm(PSL.RELOAD_HELP.msg(), PSL.RELOAD_HELP_DESC.msg(), base + "reload"), "protectionstones.admin")); + } @Override @@ -109,25 +108,37 @@ public boolean executeArgument(CommandSender p, String[] args, HashMap entries = new ArrayList<>(); + // Build visible entries based on permissions, skip “blank” (plain-text empty) lines + final PlainTextComponentSerializer plain = PlainTextComponentSerializer.plainText(); + List entries = new ArrayList<>(); for (HelpEntry he : helpMenu) { - // ignore blank lines - if (he.msg.getText().equals("")) { - continue; - } + Component line = he.msg; + if (line == null) continue; + + // ignore blank lines (no visible text) + if (plain.serialize(line).isBlank()) continue; + // check player permissions for (String perm : he.permission) { if (p.hasPermission(perm)) { - entries.add(he.msg); + entries.add(line); break; } } } - TextGUI.displayGUI(p, PSL.HELP.msg(), "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " help %page%", page, GUI_SIZE, entries, false); + TextGUI.displayGUI( + p, + PSL.HELP.msg(), + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " help %page%", + page, + GUI_SIZE, + entries, + false + ); if (page >= 0 && page * GUI_SIZE + GUI_SIZE < entries.size()) { - PSL.msg(p, PSL.HELP_NEXT.msg().replace("%page%", page + 2 + "")); + PSL.msg(p, PSL.HELP_NEXT.replace("%page%", String.valueOf(page + 2))); } return true; @@ -138,10 +149,10 @@ public List tabComplete(CommandSender sender, String alias, String[] arg return null; } - private static TextComponent sendWithPerm(String msg, String desc, String cmd) { - TextComponent m = new TextComponent(msg); - m.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, cmd)); - m.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(desc).create())); - return m; + /** Adventure version: clickable + hoverable line. */ + private static Component sendWithPerm(Component title, Component description, String cmd) { + return title + .clickEvent(ClickEvent.suggestCommand(cmd)) + .hoverEvent(HoverEvent.showText(description)); } } diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgHome.java b/src/main/java/dev/espi/protectionstones/commands/ArgHome.java index 44871a13..626acfa9 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgHome.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgHome.java @@ -19,10 +19,10 @@ import dev.espi.protectionstones.utils.ChatUtil; import dev.espi.protectionstones.utils.MiscUtil; import dev.espi.protectionstones.utils.TextGUI; -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.TextComponent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -91,30 +91,49 @@ public List tabComplete(CommandSender sender, String alias, String[] arg } private static final int GUI_SIZE = 17; + private static final MiniMessage MM = MiniMessage.miniMessage(); private void openHomeGUI(PSPlayer psp, List homes, int page) { - List entries = new ArrayList<>(); + final String base = ProtectionStones.getInstance().getConfigOptions().base_command; + + List entries = new ArrayList<>(); for (PSRegion r : homes) { - String msg; - if (r.getName() == null) { - msg = ChatColor.GRAY + "> " + ChatColor.AQUA + r.getId(); - } else { - msg = ChatColor.GRAY + "> " + ChatColor.AQUA + r.getName() + " (" + r.getId() + ")"; - } - TextComponent tc = new TextComponent(msg); - tc.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.HOME_CLICK_TO_TP.msg()).create())); - if (r.getName() == null) { - tc.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " home " + r.getId())); - } else { - tc.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " home " + r.getName())); - } - entries.add(tc); + final boolean hasName = r.getName() != null && !r.getName().isEmpty(); + // Build the visible label with MiniMessage so colors/hex work + final String label = hasName + ? "> " + r.getName() + " (" + r.getId() + ")" + : "> " + r.getId() + ""; + + Component line = MM.deserialize(label) + .hoverEvent(HoverEvent.showText(PSL.HOME_CLICK_TO_TP.msg())) + .clickEvent(ClickEvent.runCommand( + "/" + base + " home " + (hasName ? r.getName() : r.getId()) + )); + + entries.add(line); } - TextGUI.displayGUI(psp.getPlayer(), PSL.HOME_HEADER.msg(), "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " home -p %page%", page, GUI_SIZE, entries, true); - - if (page * GUI_SIZE + GUI_SIZE < entries.size()) - PSL.msg(psp, PSL.HOME_NEXT.msg().replace("%page%", page + 2 + "")); + // Header is already a Component from PSL + Component header = PSL.HOME_HEADER.msg(); + + // If your TextGUI has been updated to Adventure: + TextGUI.displayGUI( + psp.getPlayer(), + header, + "/" + base + " home -p %page%", + page, + GUI_SIZE, + entries, + true + ); + + // Show "next page" helper if there are more entries beyond this page + if ((page + 1) * GUI_SIZE < entries.size()) { + Component nextMsg = PSL.HOME_NEXT.replaceAll(Map.of( + "%page%", String.valueOf(page + 2) + )); + PSL.msg(psp.getPlayer(), nextMsg); + } } @Override diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgInfo.java b/src/main/java/dev/espi/protectionstones/commands/ArgInfo.java index bdcca788..3a3d26f7 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgInfo.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgInfo.java @@ -24,6 +24,9 @@ import dev.espi.protectionstones.*; import dev.espi.protectionstones.utils.UUIDCache; import dev.espi.protectionstones.utils.WGUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -65,7 +68,11 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap regions, UUID pUUID, boolea if (isCurrentPlayer) { PSL.msg(s, PSL.LIST_NO_REGIONS.msg()); } else { - PSL.msg(s, PSL.LIST_NO_REGIONS_PLAYER.msg().replace("%player%", UUIDCache.getNameFromUUID(pUUID))); + PSL.msg(s, PSL.LIST_NO_REGIONS_PLAYER.replace("%player%", UUIDCache.getNameFromUUID(pUUID))); } return; } - PSL.msg(s, PSL.LIST_HEADER.msg().replace("%player%", UUIDCache.getNameFromUUID(pUUID))); + PSL.msg(s, PSL.LIST_HEADER.replace("%player%", UUIDCache.getNameFromUUID(pUUID))); if (!ownerOf.isEmpty()) { PSL.msg(s, PSL.LIST_OWNER.msg()); diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgMerge.java b/src/main/java/dev/espi/protectionstones/commands/ArgMerge.java index 35a79fc7..6f3205b2 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgMerge.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgMerge.java @@ -22,10 +22,10 @@ import dev.espi.protectionstones.*; import dev.espi.protectionstones.utils.WGMerge; import dev.espi.protectionstones.utils.WGUtils; -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.TextComponent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -55,16 +55,35 @@ public HashMap getRegisteredFlags() { return null; } - public static List getGUI(Player p, PSRegion r) { + public static List getGUI(Player p, PSRegion r) { return r.getMergeableRegions(p).stream() .map(psr -> { - TextComponent tc = new TextComponent(ChatColor.AQUA + "> " + ChatColor.WHITE + psr.getId()); - if (psr.getName() != null) tc.addExtra(" (" + psr.getName() + ")"); // name - tc.addExtra(" (" + psr.getTypeOptions().alias + ")"); // region type + // Base label: > id + Component base = Component.text() + .append(Component.text("> ").color(NamedTextColor.AQUA)) + .append(Component.text(psr.getId(), NamedTextColor.WHITE)) + .build(); + + // Optional name + if (psr.getName() != null) { + base = base.append(Component.text(" (" + psr.getName() + ")", NamedTextColor.GRAY)); + } + + // Region type + base = base.append(Component.text(" (" + psr.getTypeOptions().alias + ")", NamedTextColor.GRAY)); + + // Add click + hover events + String cmd = "/" + ProtectionStones.getInstance().getConfigOptions().base_command + + " merge " + r.getId() + " " + psr.getId(); + + base = base.clickEvent(ClickEvent.runCommand(cmd)); - tc.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " merge " + r.getId() + " " + psr.getId())); - tc.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.MERGE_CLICK_TO_MERGE.msg().replace("%region%", psr.getId())).create())); - return tc; + // Hover: use your PSL message (converted to Component already) + base = base.hoverEvent(HoverEvent.showText( + PSL.MERGE_CLICK_TO_MERGE.replaceAll(Map.of("%region%", psr.getId())) + )); + + return base; }) .collect(Collectors.toList()); } @@ -85,7 +104,11 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap components = getGUI(p, r); + List components = getGUI(p, r); if (components.isEmpty()) { PSL.msg(p, PSL.MERGE_NO_REGIONS.msg()); } else { - p.sendMessage(ChatColor.WHITE + ""); // send empty line - PSL.msg(p, PSL.MERGE_HEADER.msg().replace("%region%", r.getId())); + PSL.msg(p, Component.empty()); + PSL.msg(p, PSL.MERGE_HEADER.replaceAll(Map.of("%region%", r.getId()))); PSL.msg(p, PSL.MERGE_WARNING.msg()); - for (TextComponent tc : components) p.spigot().sendMessage(tc); - p.sendMessage(ChatColor.WHITE + ""); // send empty line + for (Component tc : components) { + PSL.msg(p, tc); + } + + // send empty line again + PSL.msg(p, Component.empty()); } } else if (args.length == 3) { // /ps merge [region] [root] @@ -136,7 +163,7 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap " + ChatColor.GRAY + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + - " rent lease [price] [period]"; + public static Component getLeaseHelp() { + return Component.text("> ", NamedTextColor.AQUA) + .append(Component.text("/" + ProtectionStones.getInstance().getConfigOptions().base_command + " rent lease [price] [period]", NamedTextColor.GRAY)); } - public static String getStopLeaseHelp() { - return ChatColor.AQUA + "> " + ChatColor.GRAY + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + - " rent stoplease"; + public static Component getStopLeaseHelp() { + return Component.text("> ", NamedTextColor.AQUA) + .append(Component.text("/" + ProtectionStones.getInstance().getConfigOptions().base_command + " rent stoplease", NamedTextColor.GRAY)); } - public static String getRentHelp() { - return ChatColor.AQUA + "> " + ChatColor.GRAY + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + - " rent rent"; + public static Component getRentHelp() { + return Component.text("> ", NamedTextColor.AQUA) + .append(Component.text("/" + ProtectionStones.getInstance().getConfigOptions().base_command + " rent rent", NamedTextColor.GRAY)); } - public static String getStopRentingHelp() { - return ChatColor.AQUA + "> " + ChatColor.GRAY + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + - " rent stoprenting"; + public static Component getStopRentingHelp() { + return Component.text("> ", NamedTextColor.AQUA) + .append(Component.text("/" + ProtectionStones.getInstance().getConfigOptions().base_command + " rent stoprenting", NamedTextColor.GRAY)); } @Override @@ -129,27 +131,30 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap ProtectionStones.getInstance().getConfigOptions().maxRentPrice) // if rent price is too high - return PSL.msg(p, PSL.RENT_PRICE_TOO_HIGH.msg().replace("%price%", ""+ProtectionStones.getInstance().getConfigOptions().maxRentPrice)); + return PSL.msg(p, PSL.RENT_PRICE_TOO_HIGH.replace("%price%", ""+ProtectionStones.getInstance().getConfigOptions().maxRentPrice)); String period = String.join(" ", Arrays.asList(args).subList(3, args.length)); try { Duration d = MiscUtil.parseRentPeriod(period); if (ProtectionStones.getInstance().getConfigOptions().minRentPeriod != -1 && d.getSeconds() < ProtectionStones.getInstance().getConfigOptions().minRentPeriod) { - return PSL.msg(p, PSL.RENT_PERIOD_TOO_SHORT.msg().replace("%period%", ""+ProtectionStones.getInstance().getConfigOptions().minRentPeriod)); + return PSL.msg(p, PSL.RENT_PERIOD_TOO_SHORT.replace("%period%", ""+ProtectionStones.getInstance().getConfigOptions().minRentPeriod)); } if (ProtectionStones.getInstance().getConfigOptions().maxRentPeriod != -1 && d.getSeconds() > ProtectionStones.getInstance().getConfigOptions().maxRentPeriod) { - return PSL.msg(p, PSL.RENT_PERIOD_TOO_LONG.msg().replace("%period%", ""+ProtectionStones.getInstance().getConfigOptions().maxRentPeriod)); + return PSL.msg(p, PSL.RENT_PERIOD_TOO_LONG.replace("%period%", ""+ProtectionStones.getInstance().getConfigOptions().maxRentPeriod)); } } catch (NumberFormatException e) { return PSL.msg(p, PSL.RENT_PERIOD_INVALID.msg()); } r.setRentable(p.getUniqueId(), period, price); - return PSL.msg(p, PSL.RENT_LEASE_SUCCESS.msg().replace("%price%", args[2]).replace("%period%", period)); + return PSL.msg(p, PSL.RENT_LEASE_SUCCESS.replaceAll(Map.of( + "%price%", args[2], + "%period%", period + ))); case "stoplease": if (r.getRentStage() == PSRegion.RentStage.NOT_RENTING) @@ -170,10 +175,10 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap. - */ - package dev.espi.protectionstones.commands; import dev.espi.protectionstones.*; import dev.espi.protectionstones.utils.MiscUtil; import dev.espi.protectionstones.utils.TextGUI; import dev.espi.protectionstones.utils.UUIDCache; -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.TextComponent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; import net.milkbowl.vault.economy.EconomyResponse; import org.apache.commons.lang3.math.NumberUtils; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.util.StringUtil; import java.util.*; +import java.util.stream.Collectors; public class ArgTax implements PSCommandArg { - static final String INFO_HELP = ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps tax info [region (optional)]", // maybe put in /ps info - PAY_HELP = ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps tax pay [amount] [region (optional)]", - AUTOPAY_HELP = ChatColor.AQUA + "> " + ChatColor.GRAY + "/ps tax autopay [region (optional)]"; + private static final Component INFO_HELP = + Component.text("> /ps tax info [region (optional)]", NamedTextColor.AQUA); + private static final Component PAY_HELP = + Component.text("> /ps tax pay [amount] [region (optional)]", NamedTextColor.AQUA); + private static final Component AUTOPAY_HELP = + Component.text("> /ps tax autopay [region (optional)]", NamedTextColor.AQUA); @Override public List getNames() { @@ -74,18 +62,18 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap flags, PSPlayer p) { if (args.length == 2) { // /ps tax info Bukkit.getScheduler().runTaskAsynchronously(ProtectionStones.getInstance(), () -> { - int pageNum = (flags.get("-p") == null || !MiscUtil.isValidInteger(flags.get("-p")) ? 0 : Integer.parseInt(flags.get("-p"))-1); + int pageNum = (flags.get("-p") == null || !MiscUtil.isValidInteger(flags.get("-p")) + ? 0 + : Integer.parseInt(flags.get("-p")) - 1); - List entries = new ArrayList<>(); + List entries = new ArrayList<>(); for (PSRegion r : p.getTaxEligibleRegions()) { - double amountDue = 0; - for (PSRegion.TaxPayment tp : r.getTaxPaymentsDue()) { - amountDue += tp.getAmount(); - } - - TextComponent component; - if (r.getTaxAutopayer() != null & r.getTaxAutopayer() == p.getUuid()) { - component = new TextComponent(PSL.TAX_PLAYER_REGION_INFO_AUTOPAYER.msg() - .replace("%region%", (r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")")) - .replace("%money%", String.format("%.2f", amountDue))); - } else { - component = new TextComponent(PSL.TAX_PLAYER_REGION_INFO.msg() - .replace("%region%", (r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")")) - .replace("%money%", String.format("%.2f", amountDue))); - } - component.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " tax info " + r.getId())); - component.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.TAX_CLICK_TO_SHOW_MORE_INFO.msg()).create())); - entries.add(component); + double amountDue = r.getTaxPaymentsDue().stream() + .mapToDouble(PSRegion.TaxPayment::getAmount).sum(); + + Component base = (r.getTaxAutopayer() != null && r.getTaxAutopayer().equals(p.getUuid())) + ? PSL.TAX_PLAYER_REGION_INFO_AUTOPAYER.replaceAll(Map.of( + "%region%", (r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")"), + "%money%", String.format("%.2f", amountDue) + )) + : PSL.TAX_PLAYER_REGION_INFO.replaceAll(Map.of( + "%region%", (r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")"), + "%money%", String.format("%.2f", amountDue) + )); + + Component clickable = base + .clickEvent(ClickEvent.runCommand("/" + ProtectionStones.getInstance().getConfigOptions().base_command + " tax info " + r.getId())) + .hoverEvent(HoverEvent.showText(PSL.TAX_CLICK_TO_SHOW_MORE_INFO.msg())); + + entries.add(clickable); } - TextGUI.displayGUI(p.getPlayer(), PSL.TAX_INFO_HEADER.msg(), "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " tax info -p %page%", pageNum, GUI_SIZE, entries, true); - - if (pageNum * GUI_SIZE + GUI_SIZE < entries.size()) - PSL.msg(p, PSL.TAX_NEXT.msg().replace("%page%", pageNum + 2 + "")); + TextGUI.displayGUI( + p.getPlayer(), + PSL.TAX_INFO_HEADER.msg(), + "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " tax info -p %page%", + pageNum, + GUI_SIZE, + entries, + true + ); + + if (pageNum * GUI_SIZE + GUI_SIZE < entries.size()) { + PSL.msg(p.getPlayer(), PSL.TAX_NEXT.replaceAll(Map.of("%page%", String.valueOf(pageNum + 2)))); + } }); } else if (args.length == 3) { // /ps tax info [region] List list = ProtectionStones.getPSRegions(p.getPlayer().getWorld(), args[2]); if (list.isEmpty()) { - return PSL.msg(p, PSL.REGION_DOES_NOT_EXIST.msg()); + return PSL.msg(p.getPlayer(), PSL.REGION_DOES_NOT_EXIST.msg()); } PSRegion r = list.get(0); - double taxesOwed = 0; - for (PSRegion.TaxPayment tp : r.getTaxPaymentsDue()) { - taxesOwed += tp.getAmount(); - } - - PSL.msg(p, PSL.TAX_REGION_INFO_HEADER.msg().replace("%region%", r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")")); - PSL.msg(p, PSL.TAX_REGION_INFO.msg() - .replace("%taxrate%", String.format("%.2f", r.getTaxRate())) - .replace("%taxperiod%", r.getTaxPeriod()) - .replace("%taxpaymentperiod%", r.getTaxPaymentPeriod()) - .replace("%taxautopayer%", r.getTaxAutopayer() == null ? "none" : UUIDCache.getNameFromUUID(r.getTaxAutopayer())) - .replace("%taxowed%", String.format("%.2f", taxesOwed))); + double taxesOwed = r.getTaxPaymentsDue().stream().mapToDouble(PSRegion.TaxPayment::getAmount).sum(); + + PSL.msg(p.getPlayer(), PSL.TAX_REGION_INFO_HEADER.replaceAll(Map.of( + "%region%", r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")" + ))); + PSL.msg(p.getPlayer(), PSL.TAX_REGION_INFO.replaceAll(Map.of( + "%taxrate%", String.format("%.2f", r.getTaxRate()), + "%taxperiod%", r.getTaxPeriod(), + "%taxpaymentperiod%", r.getTaxPaymentPeriod(), + "%taxautopayer%", r.getTaxAutopayer() == null ? "none" : UUIDCache.getNameFromUUID(r.getTaxAutopayer()), + "%taxowed%", String.format("%.2f", taxesOwed) + ))); } else { - PSL.msg(p, INFO_HELP); + runHelp(p.getPlayer()); } return true; } public boolean taxPay(String[] args, PSPlayer p) { if (args.length != 3 && args.length != 4) - return PSL.msg(p, PAY_HELP); - // the amount to pay must be a number + return PSL.msg(p.getPlayer(), PAY_HELP); + if (!NumberUtils.isNumber(args[2])) - return PSL.msg(p, PAY_HELP); + return PSL.msg(p.getPlayer(), PAY_HELP); PSRegion r = resolveRegion(args.length == 4 ? args[3] : null, p); if (r == null) return true; - // player must be owner to pay for taxes if (!r.isOwner(p.getUuid())) - return PSL.msg(p, PSL.NOT_OWNER.msg()); + return PSL.msg(p.getPlayer(), PSL.NOT_OWNER.msg()); double payment = Double.parseDouble(args[2]); - // must be higher than or equal to zero if (payment <= 0) - return PSL.msg(p, PAY_HELP); - // player must have this amount of money + return PSL.msg(p.getPlayer(), PAY_HELP); + if (!p.hasAmount(payment)) - return PSL.msg(p, PSL.NOT_ENOUGH_MONEY.msg().replace("%price%", String.format("%.2f", payment))); + return PSL.msg(p.getPlayer(), PSL.NOT_ENOUGH_MONEY.replaceAll(Map.of("%price%", String.format("%.2f", payment)))); - // pay tax amount EconomyResponse res = r.payTax(p, payment); - PSL.msg(p, PSL.TAX_PAID.msg() - .replace("%amount%", String.format("%.2f", res.amount)) - .replace("%region%", r.getName() == null ? r.getId() : r.getName() + "(" + r.getId() + ")")); + PSL.msg(p.getPlayer(), PSL.TAX_PAID.replaceAll(Map.of( + "%amount%", String.format("%.2f", res.amount), + "%region%", (r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")") + ))); return true; } public boolean taxAutoPay(String[] args, PSPlayer p) { if (args.length != 2 && args.length != 3) - return PSL.msg(p, AUTOPAY_HELP); + return PSL.msg(p.getPlayer(), AUTOPAY_HELP); PSRegion r = resolveRegion(args.length == 3 ? args[2] : null, p); if (r == null) return true; - // player must be the owner of the region if (!r.isOwner(p.getUuid())) - return PSL.msg(p, PSL.NOT_OWNER.msg()); + return PSL.msg(p.getPlayer(), PSL.NOT_OWNER.msg()); - if (r.getTaxAutopayer() != null && r.getTaxAutopayer().equals(p.getUuid())) { // if removing the the tax autopayer + if (r.getTaxAutopayer() != null && r.getTaxAutopayer().equals(p.getUuid())) { r.setTaxAutopayer(null); - PSL.msg(p, PSL.TAX_SET_NO_AUTOPAYER.msg().replace("%region%", r.getName() == null ? r.getId() : r.getName() + "(" + r.getId() + ")")); - } else { // if the player is setting themselves as the tax autopayer + PSL.msg(p.getPlayer(), PSL.TAX_SET_NO_AUTOPAYER.replaceAll(Map.of( + "%region%", (r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")") + ))); + } else { r.setTaxAutopayer(p.getUuid()); - PSL.msg(p, PSL.TAX_SET_AS_AUTOPAYER.msg().replace("%region%", r.getName() == null ? r.getId() : r.getName() + "(" + r.getId() + ")")); + PSL.msg(p.getPlayer() , PSL.TAX_SET_AS_AUTOPAYER.replaceAll(Map.of( + "%region%", (r.getName() == null ? r.getId() : r.getName() + " (" + r.getId() + ")") + ))); } return true; } public PSRegion resolveRegion(String region, PSPlayer p) { PSRegion r; - if (region == null) { // region the player is standing in + if (region == null) { r = PSRegion.fromLocationGroup(p.getPlayer().getLocation()); if (r == null) { - PSL.msg(p, PSL.NOT_IN_REGION.msg()); + PSL.msg(p.getPlayer(), PSL.NOT_IN_REGION.msg()); return null; } - // if taxes are disabled for this region if (r.getTypeOptions() == null || r.getTypeOptions().taxPeriod == -1) { - PSL.msg(p, PSL.TAX_DISABLED_REGION.msg()); + PSL.msg(p.getPlayer(), PSL.TAX_DISABLED_REGION.msg()); return null; } - } else { // region query + } else { List list = ProtectionStones.getPSRegions(p.getPlayer().getWorld(), region); if (list.isEmpty()) { - PSL.msg(p, PSL.REGION_DOES_NOT_EXIST.msg()); + PSL.msg(p.getPlayer(), PSL.REGION_DOES_NOT_EXIST.msg()); return null; } else { r = list.get(0); diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgToggle.java b/src/main/java/dev/espi/protectionstones/commands/ArgToggle.java index b32f7610..d96e3709 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgToggle.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgToggle.java @@ -50,9 +50,9 @@ public boolean executeArgument(CommandSender s, String[] args, HashMap regions.size()) { - PSL.msg(p, PSL.ONLY_HAS_REGIONS.msg() - .replace("%player%", tpName) - .replace("%num%", "" + regions.size())); + PSL.msg(p.getPlayer(), PSL.ONLY_HAS_REGIONS.replaceAll(Map.of( + "%player%", tpName, + "%num%", String.valueOf(regions.size()) + ))); return; } @@ -129,7 +131,11 @@ public List tabComplete(CommandSender sender, String alias, String[] arg static void teleportPlayer(Player p, PSRegion r) { if (r.getTypeOptions() == null) { - PSL.msg(p, ChatColor.RED + "This region is problematic, and the block type (" + r.getType() + ") is not configured. Please contact an administrator."); + PSL.msg(p, + Component.text("This region is problematic, and the block type (", NamedTextColor.RED) + .append(Component.text(r.getType(), NamedTextColor.AQUA)) + .append(Component.text(") is not configured. Please contact an administrator.", NamedTextColor.RED)) + ); Bukkit.getLogger().info(ChatColor.RED + "This region is problematic, and the block type (" + r.getType() + ") is not configured."); return; } @@ -141,7 +147,7 @@ static void teleportPlayer(Player p, PSRegion r) { Bukkit.getScheduler().runTask(ProtectionStones.getInstance(), () -> p.teleport(r.getHome())); // run on main thread, not async } else if (!r.getTypeOptions().noMovingWhenTeleportWaiting) { // teleport delay, but doesn't care about moving - p.sendMessage(PSL.TP_IN_SECONDS.msg().replace("%seconds%", "" + r.getTypeOptions().tpWaitingSeconds)); + PSL.msg(p, PSL.TP_IN_SECONDS.replace("%seconds%", String.valueOf(r.getTypeOptions().tpWaitingSeconds))); Bukkit.getScheduler().runTaskLater(ProtectionStones.getInstance(), () -> { PSL.msg(p, PSL.TPING.msg()); @@ -149,7 +155,7 @@ static void teleportPlayer(Player p, PSRegion r) { }, 20 * r.getTypeOptions().tpWaitingSeconds); } else {// delay and not allowed to move - PSL.msg(p, PSL.TP_IN_SECONDS.msg().replace("%seconds%", "" + r.getTypeOptions().tpWaitingSeconds)); + PSL.msg(p, PSL.TP_IN_SECONDS.replace("%seconds%", "" + r.getTypeOptions().tpWaitingSeconds)); Location l = p.getLocation().clone(); UUID uuid = p.getUniqueId(); diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgUnclaim.java b/src/main/java/dev/espi/protectionstones/commands/ArgUnclaim.java index cbc633f6..eab607fe 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgUnclaim.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgUnclaim.java @@ -17,10 +17,12 @@ import dev.espi.protectionstones.*; import dev.espi.protectionstones.utils.TextGUI; -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.TextComponent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentBuilder; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -31,10 +33,12 @@ import java.util.HashMap; import java.util.List; + public class ArgUnclaim implements PSCommandArg { // /ps unclaim + private MiniMessage Mini; @Override public List getNames() { return Collections.singletonList("unclaim"); @@ -132,7 +136,7 @@ private int tryParseInt(String arg) { } private void displayPSRegions(CommandSender s, List regions, int page) { - List entries = new ArrayList<>(); + List entries = new ArrayList<>(); for (PSRegion rs : regions) { String msg; if (rs.getName() == null) { @@ -140,9 +144,11 @@ private void displayPSRegions(CommandSender s, List regions, int page) } else { msg = ChatColor.GRAY + "> " + ChatColor.AQUA + rs.getName() + " (" + rs.getId() + ")"; } - TextComponent tc = new TextComponent(ChatColor.AQUA + " [-] " + msg); - tc.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("Click to unclaim " + rs.getId()).create())); - tc.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " unclaim " + rs.getId())); + Component tc = Component.text(" [-] ", NamedTextColor.AQUA) + .append(Mini.deserialize(msg)) + .hoverEvent(HoverEvent.showText(Mini.deserialize("Click to unclaim " + rs.getId() + ""))) + .clickEvent(ClickEvent.suggestCommand("/" + ProtectionStones.getInstance().getConfigOptions().base_command + " unclaim " + rs.getId())); + entries.add(tc); } TextGUI.displayGUI(s, PSL.UNCLAIM_HEADER.msg(), "/" + ProtectionStones.getInstance().getConfigOptions().base_command + " unclaim list %page%", page, 17, entries, true); diff --git a/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java b/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java index 86deb3c8..be5f1f83 100644 --- a/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java +++ b/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java @@ -25,8 +25,10 @@ import com.sk89q.worldguard.session.handler.FlagValueChangeHandler; import com.sk89q.worldguard.session.handler.Handler; import dev.espi.protectionstones.FlagHandler; +import dev.espi.protectionstones.PSL; import dev.espi.protectionstones.PSRegion; import dev.espi.protectionstones.ProtectionStones; +import dev.espi.protectionstones.utils.ChatUtil; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; @@ -63,7 +65,7 @@ protected boolean onSetValue(LocalPlayer localPlayer, Location from, Location to } } if (p != null && lastValue != null && !lastValue.equals(currentValue)) { - p.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(ChatColor.translateAlternateColorCodes('&', lastValue))); + PSL.action(p, lastValue); } return true; } diff --git a/src/main/java/dev/espi/protectionstones/utils/ChatUtil.java b/src/main/java/dev/espi/protectionstones/utils/ChatUtil.java index e4dbfcd3..4157f3a0 100644 --- a/src/main/java/dev/espi/protectionstones/utils/ChatUtil.java +++ b/src/main/java/dev/espi/protectionstones/utils/ChatUtil.java @@ -17,6 +17,17 @@ import dev.espi.protectionstones.PSL; import dev.espi.protectionstones.PSRegion; + +import dev.espi.protectionstones.ProtectionStones; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import java.util.List; @@ -29,6 +40,56 @@ public static void displayDuplicateRegionAliases(Player p, List r) { rep.append(String.format(", %s (%s)", r.get(i).getId(), r.get(i).getWorld().getName())); } - PSL.msg(p, PSL.SPECIFY_ID_INSTEAD_OF_ALIAS.msg().replace("%regions%", rep.toString())); + PSL.msg(p, PSL.SPECIFY_ID_INSTEAD_OF_ALIAS.replace("%regions%", rep.toString())); + } + + //Convert between Paper Kyori and Spigot safe Chat events + // legacy serializer for plain fallback + private static final LegacyComponentSerializer LEGACY_SERIALIZER = + LegacyComponentSerializer.legacySection(); + + private static final LegacyComponentSerializer AMPERSAND_SERIALIZER = + LegacyComponentSerializer.legacyAmpersand(); + + // full serializer for hover/click + private static final BungeeComponentSerializer BUNGEE_SERIALIZER = + BungeeComponentSerializer.get(); + + /** Convert Adventure → Spigot (BaseComponent[]) preserving events */ + public static BaseComponent[] toSpigotDeep(Component comp) { + return BUNGEE_SERIALIZER.serialize(comp); + } + + /** Convert Adventure → plain ChatColor string (no events) */ + public static String toChatColorString(Component comp) { + return LEGACY_SERIALIZER.serialize(comp); + } + + /** Universal send method */ + public static boolean send(CommandSender p, Component comp) { + if (p == null || comp == null) return false; + + if (Bukkit.getServer().getName().toLowerCase().contains("paper")) { + ProtectionStones.getInstance().audiences().sender(p).sendMessage(comp); + } else if (p instanceof Player player) { + player.spigot().sendMessage(toSpigotDeep(comp)); // keep hover/click + } else { + p.sendMessage(toChatColorString(comp)); // console fallback + } + return true; + } + public static boolean sendActionBar(Player p, String message) { + if (Bukkit.getServer().getName().toLowerCase().contains("paper")) { + // Paper: Adventure API native + Component comp = AMPERSAND_SERIALIZER.deserialize(message); + ProtectionStones.getInstance().audiences().player(p).sendActionBar(comp); + } else { + // Spigot: Use Bungee API fallback + p.spigot().sendMessage( + ChatMessageType.ACTION_BAR, + new TextComponent(ChatColor.translateAlternateColorCodes('&', message)) + ); + } + return false; } } diff --git a/src/main/java/dev/espi/protectionstones/utils/LimitUtil.java b/src/main/java/dev/espi/protectionstones/utils/LimitUtil.java index 6747dd2e..67233e36 100644 --- a/src/main/java/dev/espi/protectionstones/utils/LimitUtil.java +++ b/src/main/java/dev/espi/protectionstones/utils/LimitUtil.java @@ -19,6 +19,7 @@ import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import dev.espi.protectionstones.*; +import net.kyori.adventure.text.Component; import org.bukkit.World; import org.bukkit.entity.Player; @@ -28,7 +29,7 @@ public class LimitUtil { // warning: group regions should be split into merged regions first - public static String checkAddOwner(PSPlayer psp, List blocksAdded) { + public static Component checkAddOwner(PSPlayer psp, List blocksAdded) { HashMap regionLimits = psp.getRegionLimits(); int maxPS = psp.getGlobalRegionLimits(); @@ -68,19 +69,17 @@ public static String checkAddOwner(PSPlayer psp, List blocksAdde return PSL.ADDREMOVE_PLAYER_REACHED_LIMIT.msg(); } } - return ""; + return Component.empty(); } public static boolean check(Player p, PSProtectBlock b) { if (!p.hasPermission("protectionstones.admin")) { - // check if player has limit on protection stones - String msg = LimitUtil.hasPlayerPassedRegionLimit(PSPlayer.fromPlayer(p), b); - if (!msg.isEmpty()) { - PSL.msg(p, msg); + Component msg = LimitUtil.hasPlayerPassedRegionLimit(PSPlayer.fromPlayer(p), b); + if (msg != null) { + PSL.msg(p, msg); // Component overload you already have return false; } } - return true; } @@ -148,7 +147,7 @@ private static HashMap getOwnedRegionTypeCounts(PSPlaye return counts; } - private static String hasPlayerPassedRegionLimit(PSPlayer psp, PSProtectBlock b) { + private static Component hasPlayerPassedRegionLimit(PSPlayer psp, PSProtectBlock b) { HashMap regionLimits = psp.getRegionLimits(); int maxPS = psp.getGlobalRegionLimits(); @@ -168,16 +167,16 @@ private static String hasPlayerPassedRegionLimit(PSPlayer psp, PSProtectBlock b) // check if player has passed region limit ProtectionStones.getInstance().debug(String.format("The player will have %d regions in total. Their limit is %d.", total, maxPS)); if (total >= maxPS && maxPS != -1) { - return PSL.REACHED_REGION_LIMIT.msg().replace("%limit%", ""+maxPS); + return PSL.REACHED_REGION_LIMIT.replace("%limit%", ""+maxPS); } // check if player has passed per block limit ProtectionStones.getInstance().debug(String.format("Of type %s: player will have %d regions - Player's limit is %d regions.", b.alias, bFound, regionLimits.get(b) == null ? -1 : regionLimits.get(b))); if (regionLimits.get(b) != null && bFound >= regionLimits.get(b)) { - return PSL.REACHED_PER_BLOCK_REGION_LIMIT.msg().replace("%limit%", ""+regionLimits.get(b)); + return PSL.REACHED_PER_BLOCK_REGION_LIMIT.replace("%limit%", ""+regionLimits.get(b)); } } - return ""; + return Component.empty(); } } diff --git a/src/main/java/dev/espi/protectionstones/utils/TextGUI.java b/src/main/java/dev/espi/protectionstones/utils/TextGUI.java index 6dba7141..c6a051ce 100644 --- a/src/main/java/dev/espi/protectionstones/utils/TextGUI.java +++ b/src/main/java/dev/espi/protectionstones/utils/TextGUI.java @@ -16,50 +16,93 @@ package dev.espi.protectionstones.utils; import dev.espi.protectionstones.PSL; -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.HoverEvent; +import dev.espi.protectionstones.ProtectionStones; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.md_5.bungee.api.chat.TextComponent; -import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import java.util.ArrayList; import java.util.List; +import static org.enginehub.piston.impl.LogManagerCompat.getLogger; + public class TextGUI { + private static final MiniMessage MM = MiniMessage.miniMessage(); + private static final PlainTextComponentSerializer PLAIN = PlainTextComponentSerializer.plainText(); // page starts at zero, but displays start at one // pageCommand will be replacing %page% - public static void displayGUI(CommandSender s, String header, String pageCommand, int currentPage, int guiSize, List lines, boolean sendBlankLines) { - int currentLine = currentPage * guiSize; - if (currentPage < 0 || currentLine > lines.size()) { - return; - } + public static void displayGUI( + CommandSender s, + Component header, + String pageCommand, + int currentPage, + int guiSize, + List lines, + boolean sendBlankLines + ){ + final int start = currentPage * guiSize; + final int end = Math.min(start + guiSize, lines.size()); + + if (currentPage < 0 || start > lines.size()) return; + // header PSL.msg(s, header); - for (int i = currentPage*guiSize; i < Math.min((currentPage+1) * guiSize, lines.size()); i++) { - if (sendBlankLines || !lines.get(i).equals(new TextComponent(""))) - s.spigot().sendMessage(lines.get(i)); + // page body + for (int i = start; i < end; i++) { + Component line = lines.get(i); + if (sendBlankLines || !isBlank(line)) { + ProtectionStones.getInstance().audiences().sender(s).sendMessage(line); + } + } + + // footer with paging + final boolean hasPrev = currentPage != 0; + final boolean hasNext = (currentPage + 1) * guiSize < lines.size(); + + if (lines.size() >= guiSize) { + Component footer = Component.empty().append(Component.text("=====", NamedTextColor.DARK_GRAY) + .decoration(TextDecoration.STRIKETHROUGH, true)); + + if (hasPrev) { + Component back = Component.text(" <<", NamedTextColor.AQUA) + .decoration(TextDecoration.STRIKETHROUGH, false) // turn off strikethrough + .hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(PSL.GO_BACK_PAGE.msg())) + .clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand(pageCommand.replace("%page%", String.valueOf(currentPage)))); + footer = footer.append(back); + } + + footer = footer.appendSpace() + .decoration(TextDecoration.STRIKETHROUGH, false) + .append(Component.text(currentPage + 1, NamedTextColor.WHITE)) + .appendSpace(); + + if (hasNext) { + Component next = Component.text(">> ", NamedTextColor.AQUA) + .decoration(TextDecoration.STRIKETHROUGH, false) + .hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(PSL.GO_NEXT_PAGE.msg())) + .clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand(pageCommand.replace("%page%", String.valueOf(currentPage + 2)))); + footer = footer.append(next); + } + + footer = footer.append(Component.text("=====", NamedTextColor.DARK_GRAY) + .decoration(TextDecoration.STRIKETHROUGH, true)); + + PSL.msg(s, footer); + getLogger().info(GsonComponentSerializer.gson().serialize(footer)); } - // footer page buttons - TextComponent backPage = new TextComponent(ChatColor.AQUA + " <<"), nextPage = new TextComponent(ChatColor.AQUA + ">> "); - backPage.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.GO_BACK_PAGE.msg()).create())); - nextPage.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(PSL.GO_NEXT_PAGE.msg()).create())); - backPage.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, pageCommand.replace("%page%", ""+currentPage))); - nextPage.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, pageCommand.replace("%page%", currentPage+2+""))); - - TextComponent footer = new TextComponent(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "=====" + ChatColor.RESET); - // add back page button if the page isn't 0 - if (currentPage != 0) footer.addExtra(backPage); - // add page number - footer.addExtra(new TextComponent(ChatColor.WHITE + " " + (currentPage + 1) + " ")); - // add forward page button if the page isn't last - if (currentPage * guiSize + guiSize < lines.size()) footer.addExtra(nextPage); - footer.addExtra(ChatColor.DARK_GRAY + "" + ChatColor.STRIKETHROUGH + "====="); - - // display footer only if there are more than one page of entries - if (lines.size() >= guiSize) s.spigot().sendMessage(footer); } + private static boolean isBlank(Component c) { + return PLAIN.serialize(c).isBlank(); + } } From c1972e8f690cb44e2cd4da0eb88c881f4073b47a Mon Sep 17 00:00:00 2001 From: Jerzean Date: Fri, 19 Sep 2025 02:34:33 -0400 Subject: [PATCH 8/9] Spigot version bump for crafter support (Not compat with sub 1.21 yet), Initial Kyori updates, Full # and Hex code support( couple visual bugs but working otherwise) Spigot and Paper adaptor for chat events (No more using "p.sendmessage", USE "PSL.msg" Need to add all hardcoded msgs to the Messages.yml --- .../dev/espi/protectionstones/flags/FarewellFlagHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java b/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java index be5f1f83..516dc2b4 100644 --- a/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java +++ b/src/main/java/dev/espi/protectionstones/flags/FarewellFlagHandler.java @@ -74,7 +74,7 @@ protected boolean onSetValue(LocalPlayer localPlayer, Location from, Location to 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))); + PSL.action(p, lastValue); } return true; } From ba373ccd6ded61ac4c6c6e39eb7daac11744c7cb Mon Sep 17 00:00:00 2001 From: Jerzean Date: Sat, 20 Sep 2025 01:09:10 -0400 Subject: [PATCH 9/9] Spigot version bump for crafter support (Not compat with sub 1.21) Initial Kyori updates, Full # and Hex code support( couple visual bugs but working otherwise) Spigot and Paper adaptor for chat events (No more using "p.sendmessage", USE "PSL.msg" Need to add all hardcoded msgs to the Messages.yml --- .../espi/protectionstones/ListenerClass.java | 47 ++++--------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/src/main/java/dev/espi/protectionstones/ListenerClass.java b/src/main/java/dev/espi/protectionstones/ListenerClass.java index aba96aaa..9c33db3a 100644 --- a/src/main/java/dev/espi/protectionstones/ListenerClass.java +++ b/src/main/java/dev/espi/protectionstones/ListenerClass.java @@ -35,10 +35,7 @@ import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.block.Furnace; +import org.bukkit.block.*; import org.bukkit.block.data.type.Crafter; import org.bukkit.command.CommandSender; import org.bukkit.enchantments.Enchantment; @@ -319,42 +316,18 @@ public void onPrepareItemCraft(PrepareItemCraftEvent e) { } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onCrafterClick(InventoryClickEvent e) { - if (!(e.getInventory().getHolder() instanceof Crafter)) return; - - ItemStack item = e.getCursor(); // item being placed - if (item != null && ProtectionStones.getBlockOptions(item) != null) { - e.setCancelled(true); - e.getWhoClicked().sendMessage(ChatColor.RED + "You cannot insert ProtectionStone blocks into a Crafter."); - } - } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onCrafterHopperMove(InventoryMoveItemEvent e) { - if (!(e.getDestination().getHolder() instanceof Crafter)) return; - - ItemStack item = e.getItem(); - if (item != null) { + public void onCrafter(CrafterCraftEvent e) { + Block block = e.getBlock(); + BlockState state = block.getState(); + if (block.getType() != Material.CRAFTER) return; + if (!(state instanceof Container container)) return; + Inventory inv = container.getInventory(); + for (ItemStack item : inv.getContents()) { + if (item == null) continue; PSProtectBlock options = ProtectionStones.getBlockOptions(item); if (options != null && !options.allowUseInCrafting) { e.setCancelled(true); - } - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onCrafterDrag(InventoryDragEvent e) { - if (!(e.getInventory().getHolder() instanceof Crafter)) return; - - for (ItemStack item : e.getNewItems().values()) { - if (item != null) { - PSProtectBlock options = ProtectionStones.getBlockOptions(item); - if (options != null && !options.allowUseInCrafting) { - e.setCancelled(true); - if (e.getWhoClicked() instanceof Player player) { - PSL.msg(player, Component.text("You cannot use ProtectionStones blocks in a Crafter.", NamedTextColor.RED)); - } - return; - } + e.setResult(new ItemStack(Material.AIR)); } } }