From 7e6db74161f4fd14e78492bb380bef1e6c7c7228 Mon Sep 17 00:00:00 2001 From: LlmDl Date: Thu, 21 Dec 2023 12:12:24 -0600 Subject: [PATCH 1/9] - Bump adventure-platform to 4.3.2 for 1.20.4 compatibility. --- Towny/pom.xml | 4 ++-- Towny/src/main/resources/ChangeLog.txt | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Towny/pom.xml b/Towny/pom.xml index a6316a314c..87de49e117 100644 --- a/Towny/pom.xml +++ b/Towny/pom.xml @@ -13,7 +13,7 @@ towny jar - 0.100.0.11 + 0.100.0.12 @@ -167,7 +167,7 @@ net.kyori adventure-platform-bukkit - 4.3.1 + 4.3.2 net.kyori diff --git a/Towny/src/main/resources/ChangeLog.txt b/Towny/src/main/resources/ChangeLog.txt index fc35a08749..ff405ea8f8 100644 --- a/Towny/src/main/resources/ChangeLog.txt +++ b/Towny/src/main/resources/ChangeLog.txt @@ -9285,4 +9285,6 @@ v0.92.0.11: - This setting is only used when nation_proximity_to_capital_city is above 0. - Leave this setting at 0.0 in order to allow nations to chain towns together to go as wide as they like. 0.100.0.11: - - Fix config migrator file formatting, courtesy of Warrior with PR #7128. \ No newline at end of file + - Fix config migrator file formatting, courtesy of Warrior with PR #7128. +0.100.0.12: + - Bump adventure-platform to 4.3.2 for 1.20.4 compatibility. \ No newline at end of file From 335443f5e9ecd623a2277a2cddbc6a93ec7a1a6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 17:31:24 -0600 Subject: [PATCH 2/9] Bump org.apache.maven.plugins:maven-surefire-plugin from 3.2.2 to 3.2.3 (#7124) Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.2.2 to 3.2.3. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.2.2...surefire-3.2.3) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Towny/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Towny/pom.xml b/Towny/pom.xml index 87de49e117..920f6ab708 100644 --- a/Towny/pom.xml +++ b/Towny/pom.xml @@ -472,7 +472,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.2 + 3.2.3 ${skipTests} From a279271f65e244906ab34e3abbd7c7ae152cb175 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 17:32:22 -0600 Subject: [PATCH 3/9] Bump com.github.seeseemelk:MockBukkit-v1.20 from 3.56.0 to 3.58.0 (#7119) Bumps [com.github.seeseemelk:MockBukkit-v1.20](https://github.com/MockBukkit/MockBukkit) from 3.56.0 to 3.58.0. - [Commits](https://github.com/MockBukkit/MockBukkit/compare/v3.56.0...v3.58.0) --- updated-dependencies: - dependency-name: com.github.seeseemelk:MockBukkit-v1.20 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Towny/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Towny/pom.xml b/Towny/pom.xml index 920f6ab708..6fb6d2c70c 100644 --- a/Towny/pom.xml +++ b/Towny/pom.xml @@ -256,7 +256,7 @@ com.github.seeseemelk MockBukkit-v1.20 - 3.56.0 + 3.58.0 test From 6773c7cf5e2b216052ecef8674d3970e9a8bdc82 Mon Sep 17 00:00:00 2001 From: LlmDl Date: Thu, 21 Dec 2023 17:33:49 -0600 Subject: [PATCH 4/9] Make Towny more resiliant to errors which come from having no Economy present on the server. (#7127) * Make Towny more resiliant to errors which come from having no Economy present on the server. Alter ConfirmationTransaction to no longer take in an Account in the constructor, instead it takes in a TownyObject, which will resolve an account. This prevents errors when non-Economy-enabled servers attempt to create a Confirmation with a cost set on it. Make TownyEconomyHandler#getFormattedBalance able to handle returning a formatted balance when the Economy is not active. Add prettyMoney method to BaseCommand, use it throughout the command classes to make things look cleaner. * Swap TownyObject for EconomyHandler --- .../bukkit/towny/TownyEconomyHandler.java | 2 + .../bukkit/towny/command/BaseCommand.java | 5 ++ .../bukkit/towny/command/NationCommand.java | 18 +++--- .../bukkit/towny/command/PlotCommand.java | 44 +++++++------ .../bukkit/towny/command/ResidentCommand.java | 2 +- .../bukkit/towny/command/TownCommand.java | 61 +++++++++---------- .../bukkit/towny/command/TownyCommand.java | 42 ++++++------- .../confirmations/ConfirmationHandler.java | 4 +- .../ConfirmationTransaction.java | 50 +++++++++++++-- .../bukkit/towny/utils/TownRuinUtil.java | 13 ++-- 10 files changed, 138 insertions(+), 103 deletions(-) diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/TownyEconomyHandler.java b/Towny/src/main/java/com/palmergames/bukkit/towny/TownyEconomyHandler.java index 438870324d..5956c3ab8c 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/TownyEconomyHandler.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/TownyEconomyHandler.java @@ -285,6 +285,8 @@ public static boolean setBalance(String accountName, double amount, World world) * @return string containing the formatted balance */ public static String getFormattedBalance(double balance) { + if (!isActive()) + return String.valueOf(balance); String formattedBalance = economy.getFormattedBalance(balance); if (formattedBalance != null) { diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/BaseCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/BaseCommand.java index 393b05d30c..b46d0c15d0 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/BaseCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/BaseCommand.java @@ -1,6 +1,7 @@ package com.palmergames.bukkit.towny.command; import com.palmergames.bukkit.towny.TownyAPI; +import com.palmergames.bukkit.towny.TownyEconomyHandler; import com.palmergames.bukkit.towny.TownyUniverse; import com.palmergames.bukkit.towny.exceptions.NoPermissionException; import com.palmergames.bukkit.towny.exceptions.ResidentNPCException; @@ -390,4 +391,8 @@ public static void catchNPCResident(Resident resident) throws ResidentNPCExcepti if (resident.isNPC()) throw new ResidentNPCException(); } + + public static String prettyMoney(double cost) { + return TownyEconomyHandler.getFormattedBalance(cost); + } } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java index 94b7a37309..59d61a0f62 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java @@ -945,8 +945,9 @@ public static void newNation(CommandSender sender, String name, Town capitalTown // If it isn't free to make a nation, send a confirmation. if (!noCharge && TownyEconomyHandler.isActive()) { // Test if they can pay. - if (!capitalTown.getAccount().canPayFromHoldings(TownySettings.getNewNationPrice())) - throw new TownyException(Translatable.of("msg_no_funds_new_nation2", TownySettings.getNewNationPrice())); + double cost = TownySettings.getNewNationPrice(); + if (!capitalTown.getAccount().canPayFromHoldings(cost)) + throw new TownyException(Translatable.of("msg_no_funds_new_nation2", cost)); final String finalName = filteredName; Confirmation.runOnAccept(() -> { @@ -959,9 +960,9 @@ public static void newNation(CommandSender sender, String name, Town capitalTown TownyMessaging.sendGlobalMessage(Translatable.of("msg_new_nation", sender.getName(), StringMgmt.remUnderscore(finalName))); }) - .setCost(new ConfirmationTransaction(TownySettings::getNewNationPrice, capitalTown.getAccount(), "New Nation Cost", - Translatable.of("msg_no_funds_new_nation2", TownySettings.getNewNationPrice()))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(TownySettings.getNewNationPrice()))) + .setCost(new ConfirmationTransaction(TownySettings::getNewNationPrice, capitalTown, "New Nation Cost", + Translatable.of("msg_no_funds_new_nation2", cost))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(sender); // Or, it is free, so just make the nation. @@ -1973,11 +1974,12 @@ private static void nationSetMapColor(CommandSender sender, Nation nation, Strin if (!TownySettings.getNationColorsMap().containsKey(color)) throw new TownyException(Translatable.of("msg_err_invalid_nation_map_color", TownySettings.getNationColorsMap().keySet().toString())); - if (TownySettings.getNationSetMapColourCost() > 0) + double cost = TownySettings.getNationSetMapColourCost(); + if (cost > 0) Confirmation .runOnAccept(() -> setNationMapColor(nation, color, admin, sender)) - .setTitle(Translatable.of("msg_confirm_purchase", TownySettings.getNationSetMapColourCost())) - .setCost(new ConfirmationTransaction(()-> TownySettings.getNationSetMapColourCost(), nation.getAccount(), "Cost of setting nation map color.")) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) + .setCost(new ConfirmationTransaction(() -> cost, nation, "Cost of setting nation map color.")) .sendTo(sender); else setNationMapColor(nation, color, admin, sender); diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/PlotCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/PlotCommand.java index a67ce9d134..c040373a89 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/PlotCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/PlotCommand.java @@ -588,12 +588,12 @@ private void tryPlotSetType(Player player, Resident resident, TownBlock townBloc // Test if we can pay first to throw an exception. if (cost > 0 && TownyEconomyHandler.isActive() && !resident.getAccount().canPayFromHoldings(cost)) - throw new TownyException(Translatable.of("msg_err_cannot_afford_plot_set_type_cost", townBlockType, TownyEconomyHandler.getFormattedBalance(cost))); + throw new TownyException(Translatable.of("msg_err_cannot_afford_plot_set_type_cost", townBlockType, prettyMoney(cost))); // Handle payment via a confirmation to avoid suprise costs. if (cost > 0 && TownyEconomyHandler.isActive()) { Confirmation.runOnAccept(() -> { - TownyMessaging.sendMsg(resident, Translatable.of("msg_plot_set_cost", TownyEconomyHandler.getFormattedBalance(cost), townBlockType)); + TownyMessaging.sendMsg(resident, Translatable.of("msg_plot_set_cost", prettyMoney(cost), townBlockType)); try { townBlock.setType(townBlockType, resident); @@ -604,9 +604,9 @@ private void tryPlotSetType(Player player, Resident resident, TownBlock townBloc BukkitTools.fireEvent(new PlayerChangePlotTypeEvent(townBlockType, oldType, townBlock, player)); TownyMessaging.sendMsg(player, Translatable.of("msg_plot_set_type", townBlockType)); }) - .setCost(new ConfirmationTransaction(() -> cost, resident.getAccount(), String.format("Plot set to %s", townBlockType), - Translatable.of("msg_err_cannot_afford_plot_set_type_cost", townBlockType, TownyEconomyHandler.getFormattedBalance(cost)))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(cost))) + .setCost(new ConfirmationTransaction(() -> cost, resident, String.format("Plot set to %s", townBlockType), + Translatable.of("msg_err_cannot_afford_plot_set_type_cost", townBlockType, prettyMoney(cost)))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(BukkitTools.getPlayerExact(resident.getName())); // No cost or economy so no confirmation. @@ -648,20 +648,20 @@ public void parsePlotSetOutpost(Player player, Resident resident, TownBlock town // Throws a TownyException with message if outpost should not be set. OutpostUtil.OutpostTests(town, resident, townyWorld, key, resident.isAdmin(), true); - if (TownyEconomyHandler.isActive() && TownySettings.getOutpostCost() > 0) { + if (TownySettings.getOutpostCost() > 0) { // Create a confirmation for setting outpost. Confirmation.runOnAccept(() -> { // Set the outpost spawn and display feedback. town.addOutpostSpawn(player.getLocation()); - TownyMessaging.sendMsg(player, Translatable.of("msg_plot_set_cost", TownyEconomyHandler.getFormattedBalance(TownySettings.getOutpostCost()), Translatable.of("outpost"))); + TownyMessaging.sendMsg(player, Translatable.of("msg_plot_set_cost", prettyMoney(TownySettings.getOutpostCost()), Translatable.of("outpost"))); }) - .setCost(new ConfirmationTransaction(() -> TownySettings.getOutpostCost(), town.getAccount(), "PlotSetOutpost", Translatable.of("msg_err_cannot_afford_to_set_outpost"))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(TownySettings.getOutpostCost()))) + .setCost(new ConfirmationTransaction(() -> TownySettings.getOutpostCost(), town, "PlotSetOutpost", Translatable.of("msg_err_cannot_afford_to_set_outpost"))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(TownySettings.getOutpostCost()))) .sendTo(player); } else { // Set the outpost spawn and display feedback with no cost confirmation. town.addOutpostSpawn(player.getLocation()); - TownyMessaging.sendMsg(player, Translatable.of("msg_plot_set_cost", TownyEconomyHandler.getFormattedBalance(TownySettings.getOutpostCost()), Translatable.of("outpost"))); + TownyMessaging.sendMsg(player, Translatable.of("msg_plot_set_cost", prettyMoney(TownySettings.getOutpostCost()), Translatable.of("outpost"))); } } @@ -994,7 +994,7 @@ public void setPlotForSale(Resident resident, WorldCoord worldCoord, double forS if (forSale != -1) { Translatable message = TownyEconomyHandler.isActive() - ? Translatable.of("msg_plot_for_sale_amount", resident.getName(), worldCoord.toString(), TownyEconomyHandler.getFormattedBalance(townBlock.getPlotPrice())) + ? Translatable.of("msg_plot_for_sale_amount", resident.getName(), worldCoord.toString(), prettyMoney(townBlock.getPlotPrice())) : Translatable.of("msg_plot_for_sale", resident.getName(), worldCoord.toString()); TownyMessaging.sendPrefixedTownMessage(townBlock.getTownOrNull(), message); @@ -1342,7 +1342,7 @@ public void parsePlotGroupForSale(String[] split, Resident resident, TownBlock t group.save(); Translatable message = TownyEconomyHandler.isActive() - ? Translatable.of("msg_player_put_group_up_for_sale_amount", player.getName(), group.getName(), TownyEconomyHandler.getFormattedBalance(group.getPrice())) + ? Translatable.of("msg_player_put_group_up_for_sale_amount", player.getName(), group.getName(), prettyMoney(group.getPrice())) : Translatable.of("msg_player_put_group_up_for_sale", player.getName(), group.getName()); TownyMessaging.sendPrefixedTownMessage(town, message); @@ -1576,7 +1576,7 @@ public void parsePlotGroupSetTownBlockType(String[] split, Resident resident, To double cost = type.getCost() * plotGroupTownBlocks.size(); // Test if we can pay first to throw an exception. if (cost > 0 && TownyEconomyHandler.isActive() && !resident.getAccount().canPayFromHoldings(cost)) - throw new TownyException(Translatable.of("msg_err_cannot_afford_plot_set_type_cost", type, TownyEconomyHandler.getFormattedBalance(cost))); + throw new TownyException(Translatable.of("msg_err_cannot_afford_plot_set_type_cost", type, prettyMoney(cost))); // Handle payment via a confirmation to avoid suprise costs. if (cost > 0 && TownyEconomyHandler.isActive()) { @@ -1584,7 +1584,7 @@ public void parsePlotGroupSetTownBlockType(String[] split, Resident resident, To if (townBlock.getPlotObjectGroup() == null) return; - TownyMessaging.sendMsg(resident, Translatable.of("msg_plot_set_cost", TownyEconomyHandler.getFormattedBalance(cost), type)); + TownyMessaging.sendMsg(resident, Translatable.of("msg_plot_set_cost", prettyMoney(cost), type)); for (TownBlock tb : townBlock.getPlotObjectGroup().getTownBlocks()) { try { @@ -1597,11 +1597,9 @@ public void parsePlotGroupSetTownBlockType(String[] split, Resident resident, To } TownyMessaging.sendMsg(player, Translatable.of("msg_set_group_type_to_x", type)); }) - .setCost(new ConfirmationTransaction(() -> type.getCost() * plotGroupTownBlocks.size(), - resident.getAccount(), - String.format("Plot group (" + plotGroupTownBlocks.size() + ") set to %s", type), - Translatable.of("msg_err_cannot_afford_plot_set_type_cost", type, TownyEconomyHandler.getFormattedBalance(cost)))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(cost))) + .setCost(new ConfirmationTransaction(() -> cost, resident, String.format("Plot group (%s) set to %s", plotGroupTownBlocks.size(), type), + Translatable.of("msg_err_cannot_afford_plot_set_type_cost", type, prettyMoney(cost)))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(BukkitTools.getPlayerExact(resident.getName())); // No cost or economy so no confirmation. } else { @@ -1837,7 +1835,7 @@ private void continuePlotClaimProcess(List selection, Resident resid PlotGroup group = tb.getPlotObjectGroup(); if (TownyEconomyHandler.isActive() && (!resident.getAccount().canPayFromHoldings(group.getPrice()))) - throw new TownyException(Translatable.of("msg_no_funds_claim_plot_group", group.getTownBlocks().size(), TownyEconomyHandler.getFormattedBalance(group.getPrice()))); + throw new TownyException(Translatable.of("msg_no_funds_claim_plot_group", group.getTownBlocks().size(), prettyMoney(group.getPrice()))); // Add the confirmation for claiming a plot group. Confirmation.runOnAccept(() -> { @@ -1849,7 +1847,7 @@ private void continuePlotClaimProcess(List selection, Resident resid // Execute the plot claim. new PlotClaim(Towny.getPlugin(), player, resident, coords, true, false, true).start(); }) - .setTitle(Translatable.of("msg_plot_group_claim_confirmation", group.getTownBlocks().size()).append(" ").append(TownyEconomyHandler.getFormattedBalance(group.getPrice())).append(". ").append(Translatable.of("are_you_sure_you_want_to_continue"))) + .setTitle(Translatable.of("msg_plot_group_claim_confirmation", group.getTownBlocks().size()).append(" ").append(prettyMoney(group.getPrice())).append(". ").append(Translatable.of("are_you_sure_you_want_to_continue"))) .sendTo(player); return; @@ -1877,7 +1875,7 @@ private void continuePlotClaimProcess(List selection, Resident resid throw new TownyException(Translatable.of("msg_max_plot_own", maxPlots)); if (TownyEconomyHandler.isActive() && (!resident.getAccount().canPayFromHoldings(cost))) - throw new TownyException(Translatable.of("msg_no_funds_claim_plot", TownyEconomyHandler.getFormattedBalance(cost))); + throw new TownyException(Translatable.of("msg_no_funds_claim_plot", prettyMoney(cost))); if (cost != 0) { final List finalSelection = selection; @@ -1885,7 +1883,7 @@ private void continuePlotClaimProcess(List selection, Resident resid // Start the claim task new PlotClaim(plugin, player, resident, finalSelection, true, false, false).start(); }) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(cost))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(player); } else { // Start the claim task diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/ResidentCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/ResidentCommand.java index 88e191a5ee..ffad445da4 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/ResidentCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/ResidentCommand.java @@ -389,7 +389,7 @@ private void parseResidentJail(Player player, String[] split) throws TownyExcept TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_unable_to_pay_bail")); } }) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(cost))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(player); } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownCommand.java index 1f1ec4efbb..e92976ff3a 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownCommand.java @@ -1042,7 +1042,7 @@ private void townPlots(CommandSender sender, String[] args) throws TownyExceptio String plotTypeLine = translator.of("msg_town_plots_type_line", type.getFormattedName(), residentOwned, typeCache.getNumTownBlocks(type, CacheType.FORSALE), typeCache.getNumTownBlocks(type, CacheType.ALL)); if (TownyEconomyHandler.isActive()) - plotTypeLine += translator.of("msg_town_plots_type_line_revenue", TownyEconomyHandler.getFormattedBalance(residentOwned * type.getTax(town))); + plotTypeLine += translator.of("msg_town_plots_type_line_revenue", prettyMoney(residentOwned * type.getTax(town))); out.add(plotTypeLine); } out.add(Translatable.of("msg_town_plots_revenue_disclaimer").forLocale(player)); @@ -1175,7 +1175,7 @@ public void listTowns(CommandSender sender, String[] split) throws TownyExceptio Translatable spawnCost = Translatable.of("msg_spawn_cost_free"); if (TownyEconomyHandler.isActive()) - spawnCost = Translatable.of("msg_spawn_cost", TownyEconomyHandler.getFormattedBalance(town.getSpawnCost())); + spawnCost = Translatable.of("msg_spawn_cost", prettyMoney(town.getSpawnCost())); townName = townName.hoverEvent(HoverEvent.showText(Translatable.of("msg_click_spawn", town).append("\n").append(spawnCost).locale(sender).component())); output.add(Pair.pair(town.getUUID(), townName)); @@ -1419,7 +1419,7 @@ private static void townToggleNeutral(CommandSender sender, boolean admin, Town // If they setting neutral status on send a message confirming they paid something, if they did. if (peacefulState && TownyEconomyHandler.isActive() && cost > 0) { town.getAccount().withdraw(cost, "Peaceful Town Cost"); - TownyMessaging.sendMsg(sender, Translatable.of("msg_you_paid", TownyEconomyHandler.getFormattedBalance(cost))); + TownyMessaging.sendMsg(sender, Translatable.of("msg_you_paid", prettyMoney(cost))); } // Set the toggle setting. @@ -2258,12 +2258,12 @@ public static void townSetName(CommandSender sender, String[] split, Town town) if(TownyEconomyHandler.isActive() && TownySettings.getTownRenameCost() > 0) { if (!town.getAccount().canPayFromHoldings(TownySettings.getTownRenameCost())) - throw new TownyException(Translatable.of("msg_err_no_money", TownyEconomyHandler.getFormattedBalance(TownySettings.getTownRenameCost()))); + throw new TownyException(Translatable.of("msg_err_no_money", prettyMoney(TownySettings.getTownRenameCost()))); final Town finalTown = town; final String finalName = name; Confirmation.runOnAccept(() -> townRename(sender, finalTown, finalName)) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(TownySettings.getTownRenameCost()))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(TownySettings.getTownRenameCost()))) .sendTo(sender); } else { townRename(sender, town, name); @@ -2413,8 +2413,8 @@ public static void townSetMapColor(CommandSender sender, String[] split, Town to if (TownySettings.getTownSetMapColourCost() > 0) Confirmation .runOnAccept(()-> setTownMapColor(town, color)) - .setTitle(Translatable.of("msg_confirm_purchase", TownySettings.getTownSetMapColourCost())) - .setCost(new ConfirmationTransaction(()-> TownySettings.getTownSetMapColourCost(), town.getAccount(), "Cost of setting town map color.")) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(TownySettings.getTownSetMapColourCost()))) + .setCost(new ConfirmationTransaction(TownySettings::getTownSetMapColourCost, town, "Cost of setting town map color.")) .sendTo(sender); else setTownMapColor(town, color); @@ -2435,7 +2435,7 @@ public static void townSetTaxPercent(CommandSender sender, String[] split, Town town.setMaxPercentTaxAmount(Double.parseDouble(split[1])); - TownyMessaging.sendPrefixedTownMessage(town, Translatable.of("msg_town_set_tax_max_percent_amount", sender.getName(), TownyEconomyHandler.getFormattedBalance(town.getMaxPercentTaxAmount()))); + TownyMessaging.sendPrefixedTownMessage(town, Translatable.of("msg_town_set_tax_max_percent_amount", sender.getName(), prettyMoney(town.getMaxPercentTaxAmount()))); } private static void parseTownBaltop(Player player, Town town) throws TownyException { @@ -2447,7 +2447,7 @@ private static void parseTownBaltop(Player player, Town town) throws TownyExcept int i = 0; for (Resident res : residents) - sb.append(Translatable.of("msg_baltop_book_format", ++i, res.getName(), TownyEconomyHandler.getFormattedBalance(res.getAccount().getCachedBalance())).forLocale(player) + "\n"); + sb.append(Translatable.of("msg_baltop_book_format", ++i, res.getName(), prettyMoney(res.getAccount().getCachedBalance())).forLocale(player) + "\n"); ItemStack book = BookFactory.makeBook("Town Baltop", town.getName(), sb.toString()); plugin.getScheduler().run(player, () -> player.openBook(book)); @@ -2472,7 +2472,7 @@ else if (TownySettings.isBonusBlocksPerTownLevel() && TownySettings.getMaxBonusB if (split.length == 0 || !split[0].equalsIgnoreCase("bonus")) { TownyMessaging.sendMessage(sender, ChatTools.formatTitle("/town buy")); String line = Colors.Yellow + "[Purchased Bonus] " + Colors.Green + "Cost: " + Colors.LightGreen + "%s" + Colors.Gray + " | " + Colors.Green + "Max: " + Colors.LightGreen + "%d"; - TownyMessaging.sendMessage(sender, String.format(line, TownyEconomyHandler.getFormattedBalance(town.getBonusBlockCost()), TownySettings.getMaxPurchasedBlocks(town))); + TownyMessaging.sendMessage(sender, String.format(line, prettyMoney(town.getBonusBlockCost()), TownySettings.getMaxPurchasedBlocks(town))); if (TownySettings.getPurchasedBonusBlocksIncreaseValue() != 1.0) TownyMessaging.sendMessage(sender, Colors.Green + "Cost Increase per TownBlock: " + Colors.LightGreen + "+" + new DecimalFormat("##.##%").format(TownySettings.getPurchasedBonusBlocksIncreaseValue()-1)); TownyMessaging.sendMessage(sender, ChatTools.formatCommand("", "/town buy", "bonus [n]", "")); @@ -2514,16 +2514,16 @@ public static void townBuyBonusTownBlocks(Town town, int inputN, CommandSender s double cost = town.getBonusBlockCostN(n); // Test if the town can pay and throw economy exception if not. if (!town.getAccount().canPayFromHoldings(cost)) - throw new TownyException(Translatable.of("msg_no_funds_to_buy", n, Translatable.of("bonus_townblocks"), TownyEconomyHandler.getFormattedBalance(cost))); + throw new TownyException(Translatable.of("msg_no_funds_to_buy", n, Translatable.of("bonus_townblocks"), prettyMoney(cost))); Confirmation.runOnAccept(() -> { town.addPurchasedBlocks(n); - TownyMessaging.sendMsg(sender, Translatable.of("msg_buy", n, Translatable.of("bonus_townblocks"), TownyEconomyHandler.getFormattedBalance(cost))); + TownyMessaging.sendMsg(sender, Translatable.of("msg_buy", n, Translatable.of("bonus_townblocks"), prettyMoney(cost))); town.save(); }) - .setCost(new ConfirmationTransaction(() -> cost, town.getAccount(), String.format("Town Buy Bonus (%d)", n), - Translatable.of("msg_no_funds_to_buy", n, Translatable.of("bonus_townblocks"), TownyEconomyHandler.getFormattedBalance(cost)))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(cost))) + .setCost(new ConfirmationTransaction(() -> cost, town, String.format("Town Buy Bonus (%d)", n), + Translatable.of("msg_no_funds_to_buy", n, Translatable.of("bonus_townblocks"), prettyMoney(cost)))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(sender); } @@ -2587,8 +2587,9 @@ public static void newTown(Player player, String name, Resident resident, boolea } // Test if the resident can afford the town. - if (!resident.getAccount().canPayFromHoldings(TownySettings.getNewTownPrice())) - throw new TownyException(Translatable.of("msg_no_funds_new_town2", (resident.getName().equals(player.getName()) ? Translatable.of("msg_you") : resident.getName()), TownySettings.getNewTownPrice())); + double cost = TownySettings.getNewTownPrice(); + if (!resident.getAccount().canPayFromHoldings(cost)) + throw new TownyException(Translatable.of("msg_no_funds_new_town2", (resident.getName().equals(player.getName()) ? Translatable.of("msg_you") : resident.getName()), cost)); // Send a confirmation before taking their money and throwing the PreNewTownEvent. final String finalName = name; @@ -2603,9 +2604,9 @@ public static void newTown(Player player, String name, Resident resident, boolea } }) .setCancellableEvent(new PreNewTownEvent(player, name, spawnLocation)) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(TownySettings.getNewTownPrice()))) - .setCost(new ConfirmationTransaction(TownySettings::getNewTownPrice, resident.getAccount(), "New Town Cost", - Translatable.of("msg_no_funds_new_town2", (resident.getName().equals(player.getName()) ? Translatable.of("msg_you") : resident.getName()), TownySettings.getNewTownPrice()))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) + .setCost(new ConfirmationTransaction(() -> cost, resident, "New Town Cost", + Translatable.of("msg_no_funds_new_town2", (resident.getName().equals(player.getName()) ? Translatable.of("msg_you") : resident.getName()), prettyMoney(cost)))) .sendTo(player); } @@ -2751,7 +2752,7 @@ public static void townRename(CommandSender sender, Town town, String newName) { double renameCost = TownySettings.getTownRenameCost(); if (TownyEconomyHandler.isActive() && renameCost > 0 && !town.getAccount().withdraw(renameCost, String.format("Town renamed to: %s", newName))) { - TownyMessaging.sendErrorMsg(sender, Translatable.of("msg_err_no_money", TownyEconomyHandler.getFormattedBalance(renameCost))); + TownyMessaging.sendErrorMsg(sender, Translatable.of("msg_err_no_money", prettyMoney(renameCost))); return; } @@ -3637,7 +3638,7 @@ else if (finalSelection.size() == 1) if (!town.getAccount().canPayFromHoldings(blockCost)) { double missingAmount = blockCost - town.getAccount().getHoldingBalance(); - throw new TownyException(Translatable.of("msg_no_funds_claim2", finalSelection.size(), TownyEconomyHandler.getFormattedBalance(blockCost), TownyEconomyHandler.getFormattedBalance(missingAmount), new DecimalFormat("#").format(missingAmount))); + throw new TownyException(Translatable.of("msg_no_funds_claim2", finalSelection.size(), prettyMoney(blockCost), prettyMoney(missingAmount), new DecimalFormat("#").format(missingAmount))); } town.getAccount().withdraw(blockCost, String.format("Town Claim (%d) by %s", finalSelection.size(), player.getName())); @@ -3721,19 +3722,19 @@ public static void parseTownUnclaimCommand(Player player, String[] split) throws if (TownyEconomyHandler.isActive() && TownySettings.getClaimRefundPrice() < 0) { double cost = Math.abs(TownySettings.getClaimRefundPrice() * selection.size()); if (!town.getAccount().canPayFromHoldings(cost)) { - TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_your_town_cannot_afford_unclaim", TownyEconomyHandler.getFormattedBalance(cost))); + TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_your_town_cannot_afford_unclaim", prettyMoney(cost))); return; } List finalSelection = selection; Confirmation.runOnAccept(()-> { if (!town.getAccount().canPayFromHoldings(cost)) { - TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_your_town_cannot_afford_unclaim", TownyEconomyHandler.getFormattedBalance(cost))); + TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_your_town_cannot_afford_unclaim", prettyMoney(cost))); return; } // Set the area to unclaim plugin.getScheduler().runAsync(new TownClaim(plugin, player, town, finalSelection, false, false, false)); }) - .setTitle(Translatable.of("confirmation_unclaiming_costs", TownyEconomyHandler.getFormattedBalance(cost))) + .setTitle(Translatable.of("confirmation_unclaiming_costs", prettyMoney(cost))) .sendTo(player); return; } @@ -3751,7 +3752,7 @@ private static void parseTownUnclaimAllCommand(Player player, Town town, Residen if (TownyEconomyHandler.isActive() && TownySettings.getClaimRefundPrice() < 0) { int numTownBlocks = town.getTownBlocks().size() - (town.hasHomeBlock() ? 1 : 0); - String formattedCost = TownyEconomyHandler.getFormattedBalance(Math.abs(TownySettings.getClaimRefundPrice() * numTownBlocks)); + String formattedCost = prettyMoney(Math.abs(TownySettings.getClaimRefundPrice() * numTownBlocks)); // Unclaiming will cost the player money because of a negative refund price. Have them confirm the cost. Confirmation .runOnAcceptAsync(new TownClaim(plugin, player, town, null, false, false, false)) @@ -3825,11 +3826,11 @@ private void parseTownTakeoverClaimCommand(Player player) throws TownyException throw new TownyException(Translatable.of("msg_err_another_plugin_cancelled_takeover")); double cost = TownySettings.getTakeoverClaimPrice(); - String costSlug = !TownyEconomyHandler.isActive() || cost <= 0 ? Translatable.of("msg_spawn_cost_free").forLocale(player) : TownyEconomyHandler.getFormattedBalance(cost); + String costSlug = !TownyEconomyHandler.isActive() || cost <= 0 ? Translatable.of("msg_spawn_cost_free").forLocale(player) : prettyMoney(cost); String townName = wc.getTownOrNull().getName(); Confirmation.runOnAccept(() -> Bukkit.getScheduler().runTask(plugin, new TownClaim(plugin, player, town, Arrays.asList(wc), false, true, false))) .setTitle(Translatable.of("confirmation_you_are_about_to_take_over_a_claim", townName, costSlug)) - .setCost(new ConfirmationTransaction(() -> cost, town.getAccount(), "Takeover Claim (" + wc.toString() + ") from " + townName + ".")) + .setCost(new ConfirmationTransaction(() -> cost, town, "Takeover Claim (" + wc.toString() + ") from " + townName + ".")) .sendTo(player); } @@ -3972,10 +3973,6 @@ private static double[] getMergeCosts(Town remainingTown, Town succumbingTown, b return mergeCost; } - private static String prettyMoney(double cost) { - return TownyEconomyHandler.getFormattedBalance(cost); - } - private static void sendTownMergeRequest(CommandSender sender, Town remainingTown, Town succumbingTown, double cost) { TownyMessaging.sendMsg(sender, Translatable.of("msg_town_merge_request_sent", succumbingTown.getName())); TownyMessaging.sendMsg(succumbingTown.getMayor(), Translatable.of("msg_town_merge_request_received", remainingTown.getName(), sender.getName(), remainingTown.getName())); diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyCommand.java index fe6bd9fff1..75d7f6932f 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyCommand.java @@ -412,10 +412,10 @@ public List getTownyPrices(Town town, Translator translator) { nation = town.getNationOrNull(); output.add(ChatTools.formatTitle(translator.of("towny_prices_title"))); - output.add(translator.of("towny_prices_town_nation", getMoney(TownySettings.getNewTownPrice()), getMoney(TownySettings.getNewNationPrice()))); - output.add(translator.of("towny_prices_reclaim", getMoney(TownySettings.getEcoPriceReclaimTown()))); + output.add(translator.of("towny_prices_town_nation", prettyMoney(TownySettings.getNewTownPrice()), prettyMoney(TownySettings.getNewNationPrice()))); + output.add(translator.of("towny_prices_reclaim", prettyMoney(TownySettings.getEcoPriceReclaimTown()))); if (town != null) { - output.add(translator.of("towny_prices_upkeep", getMoney(TownySettings.getTownUpkeepCost(town)), getMoney(TownySettings.getNationUpkeepCost(nation)))); + output.add(translator.of("towny_prices_upkeep", prettyMoney(TownySettings.getTownUpkeepCost(town)), prettyMoney(TownySettings.getNationUpkeepCost(nation)))); output.add(translator.of("towny_prices_upkeep_based_on", (TownySettings.isUpkeepByPlot() ? translator.of("towny_prices_upkeep_num_plots") : translator.of("towny_prices_upkeep_town_level")))); String upkeepformula; if (TownySettings.isNationUpkeepPerPlot()) @@ -426,36 +426,36 @@ else if (TownySettings.isNationUpkeepPerTown()) upkeepformula = translator.of("towny_prices_upkeep_nation_level"); output.add(translator.of("towny_prices_nation_upkeep_based_on", upkeepformula)); if (town.isOverClaimed() && TownySettings.getUpkeepPenalty() > 0) - output.add(translator.of("towny_prices_overclaimed_upkeep", getMoney(TownySettings.getTownPenaltyUpkeepCost(town)))); + output.add(translator.of("towny_prices_overclaimed_upkeep", prettyMoney(TownySettings.getTownPenaltyUpkeepCost(town)))); if (TownySettings.getUpkeepPenalty() > 0 ) output.add(translator.of("towny_prices_overclaimed_based_on", (TownySettings.isUpkeepPenaltyByPlot() ? translator.of("towny_prices_overclaimed_num_plots") : translator.of("towny_prices_overclaimed_flat_cost")), TownySettings.getUpkeepPenalty())); - output.add(translator.of("towny_prices_town_merge", getMoney(TownySettings.getBaseCostForTownMerge()), getMoney(town.getTownBlockCost()/2))); - output.add(translator.of("towny_prices_claiming_townblock", getMoney(town.getTownBlockCost()) + + output.add(translator.of("towny_prices_town_merge", prettyMoney(TownySettings.getBaseCostForTownMerge()), prettyMoney(town.getTownBlockCost()/2))); + output.add(translator.of("towny_prices_claiming_townblock", prettyMoney(town.getTownBlockCost()) + (Double.valueOf(TownySettings.getClaimPriceIncreaseValue()).equals(1.0) ? "" : translator.of("towny_prices_claiming_townblock_increase", new DecimalFormat("##.##%").format(TownySettings.getClaimPriceIncreaseValue()-1))))); - output.add(translator.of("towny_prices_claiming_outposts", getMoney(TownySettings.getOutpostCost()))); + output.add(translator.of("towny_prices_claiming_outposts", prettyMoney(TownySettings.getOutpostCost()))); } if (town == null) - output.add(translator.of("towny_prices_upkeep", getMoney(TownySettings.getTownUpkeep()), getMoney(TownySettings.getNationUpkeep()))); + output.add(translator.of("towny_prices_upkeep", prettyMoney(TownySettings.getTownUpkeep()), prettyMoney(TownySettings.getNationUpkeep()))); if (town != null) { output.add(translator.of("towny_prices_townname", town.getFormattedName())); - output.add(translator.of("towny_prices_price_plot", getMoney(town.getPlotPrice()),getMoney(TownySettings.getOutpostCost()))); - output.add(translator.of("towny_prices_price_shop", getMoney(town.getCommercialPlotPrice()), getMoney(town.getEmbassyPlotPrice()))); + output.add(translator.of("towny_prices_price_plot", prettyMoney(town.getPlotPrice()),prettyMoney(TownySettings.getOutpostCost()))); + output.add(translator.of("towny_prices_price_shop", prettyMoney(town.getCommercialPlotPrice()), prettyMoney(town.getEmbassyPlotPrice()))); - output.add(translator.of("towny_prices_taxes_plot", (town.isTaxPercentage()? town.getTaxes() + "%" : getMoney(town.getTaxes())), getMoney(town.getPlotTax()))); - output.add(translator.of("towny_prices_taxes_shop", getMoney(town.getCommercialPlotTax()), getMoney(town.getEmbassyPlotTax()))); - output.add(translator.of("towny_prices_town_neutral_tax", getMoney(TownySettings.getTownNeutralityCost(town)))); + output.add(translator.of("towny_prices_taxes_plot", (town.isTaxPercentage()? town.getTaxes() + "%" : prettyMoney(town.getTaxes())), prettyMoney(town.getPlotTax()))); + output.add(translator.of("towny_prices_taxes_shop", prettyMoney(town.getCommercialPlotTax()), prettyMoney(town.getEmbassyPlotTax()))); + output.add(translator.of("towny_prices_town_neutral_tax", prettyMoney(TownySettings.getTownNeutralityCost(town)))); output.add(translator.of("towny_prices_plots")); List townBlockTypes = new ArrayList<>(TownBlockTypeHandler.getTypes().values()); for (int i = 0; i < townBlockTypes.size(); i++) { if (i == townBlockTypes.size() - 1) - output.add(translator.of("towny_prices_type_single", townBlockTypes.get(i).getFormattedName(), getMoney(townBlockTypes.get(i).getCost()))); + output.add(translator.of("towny_prices_type_single", townBlockTypes.get(i).getFormattedName(), prettyMoney(townBlockTypes.get(i).getCost()))); else { output.add(translator.of("towny_prices_type_double", - townBlockTypes.get(i).getFormattedName(), getMoney(townBlockTypes.get(i).getCost()), - townBlockTypes.get(i+1).getFormattedName(), getMoney(townBlockTypes.get(i+1).getCost()) + townBlockTypes.get(i).getFormattedName(), prettyMoney(townBlockTypes.get(i).getCost()), + townBlockTypes.get(i+1).getFormattedName(), prettyMoney(townBlockTypes.get(i+1).getCost()) )); i++; @@ -464,16 +464,12 @@ else if (TownySettings.isNationUpkeepPerTown()) if (nation != null) { output.add(translator.of("towny_prices_nationname", nation.getFormattedName())); - output.add(translator.of("towny_prices_nation_tax", (nation.isTaxPercentage() ? nation.getTaxes() + "%" : getMoney(nation.getTaxes())), getMoney(TownySettings.getNationNeutralityCost(nation)))); + output.add(translator.of("towny_prices_nation_tax", (nation.isTaxPercentage() ? nation.getTaxes() + "%" : prettyMoney(nation.getTaxes())), prettyMoney(TownySettings.getNationNeutralityCost(nation)))); } } return output; } - - private String getMoney(double cost) { - return TownyEconomyHandler.getFormattedBalance(cost); - } - + public List getTopBankBalance(final List governments) { final int maxListing = TownySettings.getTownyTopSize(); final List output = new ArrayList<>(); @@ -488,7 +484,7 @@ public List getTopBankBalance(final List governments) { if (maxListing != -1 && index > maxListing) { break; } - output.add(String.format(Colors.LightGray + "%-20s " + Colors.Gold + "|" + Colors.Blue + " %s", gov.getFormattedName(), getMoney(gov.getAccount().getCachedBalance()))); + output.add(String.format(Colors.LightGray + "%-20s " + Colors.Gold + "|" + Colors.Blue + " %s", gov.getFormattedName(), prettyMoney(gov.getAccount().getCachedBalance()))); } return output; } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationHandler.java b/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationHandler.java index f93c5b4c85..25ddd466c1 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationHandler.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationHandler.java @@ -141,9 +141,9 @@ public static void acceptConfirmation(CommandSender sender) { // Determine the cost, done in this phase in case the cost could be manipulated before confirming. transaction.supplyCost(); double cost = transaction.getCost(); + Account payee = transaction.getPayee(); // Can they pay the cost? - if (cost > 0) { - Account payee = transaction.getPayee(); + if (cost > 0 && payee != null) { if (!payee.canPayFromHoldings(cost)) { TownyMessaging.sendErrorMsg(sender, transaction.getInsufficientFundsMessage()); TownyMessaging.sendErrorMsg(sender, Translatable.of("msg_err_you_need_x_to_pay", cost)); diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationTransaction.java b/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationTransaction.java index 490f9b8f43..da0b6927ee 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationTransaction.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationTransaction.java @@ -2,7 +2,10 @@ import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; + import com.palmergames.bukkit.towny.TownyEconomyHandler; +import com.palmergames.bukkit.towny.object.EconomyHandler; import com.palmergames.bukkit.towny.object.Translatable; import com.palmergames.bukkit.towny.object.economy.Account; @@ -17,15 +20,15 @@ public class ConfirmationTransaction { * A transaction which must succeed for a Confirmation to complete. * * @param costSupplier cost of the transaction. - * @param payee Account which will have to pay. + * @param townyObject EconomyHandler which will have to pay, this will be a Resident, Town or Nation. * @param loggedMessage The message logged in the money.csv file. * @param insufficientFundsMessage Transatable which will display the cannot pay message. */ - public ConfirmationTransaction(Supplier costSupplier, Account payee, String loggedMessage, Translatable insufficientFundsMessage) { + public ConfirmationTransaction(Supplier costSupplier, EconomyHandler townyObject, String loggedMessage, Translatable insufficientFundsMessage) { this.costSupplier = costSupplier; - this.payee = payee; this.loggedMessage = loggedMessage; this.insufficientFundsMessage = insufficientFundsMessage; + this.payee = !TownyEconomyHandler.isActive() ? null : townyObject.getAccount(); } /** @@ -33,14 +36,14 @@ public ConfirmationTransaction(Supplier costSupplier, Account payee, Str * Uses the default no money error message. * * @param costSupplier cost of the transaction. - * @param payee Account which will have to pay. + * @param townyObject EconomyHandler which will have to pay, this will be a Resident, Town or Nation. * @param loggedMessage The message logged in the money.csv file. */ - public ConfirmationTransaction(Supplier costSupplier, Account payee, String loggedMessage) { + public ConfirmationTransaction(Supplier costSupplier, EconomyHandler townyObject, String loggedMessage) { this.costSupplier = costSupplier; - this.payee = payee; this.loggedMessage = loggedMessage; this.insufficientFundsMessage = null; + this.payee = !TownyEconomyHandler.isActive() ? null : townyObject.getAccount(); } public void supplyCost() { @@ -51,6 +54,7 @@ public double getCost() { return cost; } + @Nullable public Account getPayee() { return payee; } @@ -62,4 +66,38 @@ public String getLoggedMessage() { public Translatable getInsufficientFundsMessage() { return insufficientFundsMessage != null ? insufficientFundsMessage : Translatable.of("msg_err_no_money", TownyEconomyHandler.getFormattedBalance(getCost())); } + + /** + * A transaction which must succeed for a Confirmation to complete. + * + * @deprecated since 0.100.0.10 use {@link #ConfirmationTransaction(Supplier, EconomyHandler, String, Translatable)} instead. + * @param costSupplier cost of the transaction. + * @param payee Account which will have to pay. + * @param loggedMessage The message logged in the money.csv file. + * @param insufficientFundsMessage Transatable which will display the cannot pay message. + */ + @Deprecated + public ConfirmationTransaction(Supplier costSupplier, Account payee, String loggedMessage, Translatable insufficientFundsMessage) { + this.costSupplier = costSupplier; + this.payee = payee; + this.loggedMessage = loggedMessage; + this.insufficientFundsMessage = insufficientFundsMessage; + } + + /** + * A transaction which must succeed for a Confirmation to complete. + * Uses the default no money error message. + * + * @deprecated since 0.100.0.10 use {@link #ConfirmationTransaction(Supplier, EconomyHandler, String)} instead. + * @param costSupplier cost of the transaction. + * @param payee Account which will have to pay. + * @param loggedMessage The message logged in the money.csv file. + */ + @Deprecated + public ConfirmationTransaction(Supplier costSupplier, Account payee, String loggedMessage) { + this.costSupplier = costSupplier; + this.payee = payee; + this.loggedMessage = loggedMessage; + this.insufficientFundsMessage = null; + } } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/utils/TownRuinUtil.java b/Towny/src/main/java/com/palmergames/bukkit/towny/utils/TownRuinUtil.java index e2fb73ebf1..506d7a9a19 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/utils/TownRuinUtil.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/utils/TownRuinUtil.java @@ -153,14 +153,11 @@ public static void processRuinedTownReclaimRequest(Player player) { if (TownySettings.getTownRuinsMinDurationHours() - getTimeSinceRuining(town) > 0) throw new TownyException(Translatable.of("msg_err_cannot_reclaim_town_yet", TownySettings.getTownRuinsMinDurationHours() - getTimeSinceRuining(town))); - if (TownyEconomyHandler.isActive() && townReclaimCost > 0) { - Confirmation.runOnAccept(() -> reclaimTown(resident, town)) - .setCost(new ConfirmationTransaction(() -> townReclaimCost, resident.getAccount(), "Cost of town reclaim.", Translatable.of("msg_insuf_funds"))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(townReclaimCost))) - .sendTo(player); - } else { - reclaimTown(resident, town); - } + //Ask them to confirm they want to reclaim the town. + Confirmation.runOnAccept(() -> reclaimTown(resident, town)) + .setCost(new ConfirmationTransaction(() -> townReclaimCost, resident, "Cost of town reclaim.", Translatable.of("msg_insuf_funds"))) + .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(townReclaimCost))) + .sendTo(player); } catch (TownyException e) { TownyMessaging.sendErrorMsg(player, e.getMessage(player)); } From 8d3a2f49e2436fe73a2cf56140d639899b893c47 Mon Sep 17 00:00:00 2001 From: LlmDl Date: Thu, 21 Dec 2023 17:34:40 -0600 Subject: [PATCH 5/9] New translations en-us.yml (German) (#7130) --- Towny/src/main/resources/lang/de-DE.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Towny/src/main/resources/lang/de-DE.yml b/Towny/src/main/resources/lang/de-DE.yml index 3ded6ba674..4a6895cb67 100644 --- a/Towny/src/main/resources/lang/de-DE.yml +++ b/Towny/src/main/resources/lang/de-DE.yml @@ -103,13 +103,13 @@ town_list_help_5: "Listet Städte nach beanspruchtem Land mit der angegebenen Se town_list_help_6: "Listet die Städte, mit den meisten Bewohnern online mit der angegebenen Seite auf." town_set_help_0: "Set your town's board." town_set_help_1: "Set your town's homeblock to where you're standing." -town_set_help_2: "Sets the spawn point in your homeblock or an outpost spawnpoint when stood in an outpost." +town_set_help_2: "Setzt deinen Stadt spawn Punkt, oder Outpost spawn punkt, solange du dich in einem Outpost befindest." town_set_help_3: "See /town set perm ? for help." -town_set_help_4: "Set your town's tax amount." +town_set_help_4: "Setze deine Stadt Steuer fest." town_set_help_5: "Set the special amounts for per-plot taxes." -town_set_help_6: "Set the default prices for buying plots." +town_set_help_6: "Setzte die Grundgebühren für Plots fest." town_set_help_7: "Set the cost to use /t spawn." -town_set_help_8: "Set your town's name." +town_set_help_8: "Lege deinen Stadt-Namen fest." town_set_help_9: "Set your town's tag." town_set_help_10: "Set the title or surname of your resident." town_set_help_11: "Set the max amount of money a percentage tax can take from a resident." From 98c99f0e105c636f6ec03cf58f203ad88246c38f Mon Sep 17 00:00:00 2001 From: LlmDl Date: Thu, 21 Dec 2023 17:35:12 -0600 Subject: [PATCH 6/9] - Bump org.apache.maven.plugins:maven-surefire-plugin from 3.1.2 to 3.2.3. - Bump com.github.seeseemelk:MockBukkit-v1.20 from 3.56.0 to 3.58.0. - Make Towny more resiliant to errors which come from having no Economy present on the server. --- Towny/pom.xml | 2 +- Towny/src/main/resources/ChangeLog.txt | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Towny/pom.xml b/Towny/pom.xml index 6fb6d2c70c..3d9f30d467 100644 --- a/Towny/pom.xml +++ b/Towny/pom.xml @@ -13,7 +13,7 @@ towny jar - 0.100.0.12 + 0.100.0.13 diff --git a/Towny/src/main/resources/ChangeLog.txt b/Towny/src/main/resources/ChangeLog.txt index ff405ea8f8..d0bc984a19 100644 --- a/Towny/src/main/resources/ChangeLog.txt +++ b/Towny/src/main/resources/ChangeLog.txt @@ -9287,4 +9287,8 @@ v0.92.0.11: 0.100.0.11: - Fix config migrator file formatting, courtesy of Warrior with PR #7128. 0.100.0.12: - - Bump adventure-platform to 4.3.2 for 1.20.4 compatibility. \ No newline at end of file + - Bump adventure-platform to 4.3.2 for 1.20.4 compatibility. +0.100.0.13: + - Bump org.apache.maven.plugins:maven-surefire-plugin from 3.1.2 to 3.2.3. + - Bump com.github.seeseemelk:MockBukkit-v1.20 from 3.56.0 to 3.58.0. + - Make Towny more resiliant to errors which come from having no Economy present on the server. \ No newline at end of file From f046154b00fa0c2fdeb0d1942f6d60d9416bd232 Mon Sep 17 00:00:00 2001 From: LlmDl Date: Thu, 21 Dec 2023 17:35:52 -0600 Subject: [PATCH 7/9] Add the ability for nations to sanction towns, preventing them from joining the nation via invite or join. (#7129) * Add the ability for nations to sanction towns, preventing them from joining the nation via invite or join. Closes #6544. * Forgot a lang string. * Add admin command * Can't forget about the name blacklist! * Fix lang strings, add ability to /n sanctiontown list [nation], add events. --- .../bukkit/towny/command/HelpMenu.java | 11 +++ .../bukkit/towny/command/NationCommand.java | 98 ++++++++++++++++++- .../towny/command/TownyAdminCommand.java | 28 ++++-- .../bukkit/towny/db/SQLSchema.java | 1 + .../bukkit/towny/db/TownyFlatFileSource.java | 8 ++ .../bukkit/towny/db/TownySQLSource.java | 7 +- .../nation/NationSanctionTownAddEvent.java | 38 +++++++ .../nation/NationSanctionTownRemoveEvent.java | 38 +++++++ .../bukkit/towny/object/Nation.java | 32 ++++++ .../towny/permissions/PermissionNodes.java | 2 + .../bukkit/util/NameValidation.java | 2 +- Towny/src/main/resources/lang/en-US.yml | 27 ++++- Towny/src/main/resources/plugin.yml | 2 + 13 files changed, 282 insertions(+), 12 deletions(-) create mode 100644 Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownAddEvent.java create mode 100644 Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownRemoveEvent.java diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/HelpMenu.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/HelpMenu.java index 128c0c6ddd..34f710ef39 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/HelpMenu.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/HelpMenu.java @@ -937,6 +937,17 @@ protected MenuBuilder load() { } }, + NATION_SANCTIONTOWN { + @Override + protected MenuBuilder load() { + return new MenuBuilder("nation sanctiontown") + .add("add [town]", Translatable.of("nation_sanction_help_1")) + .add("remove [town]", Translatable.of("nation_sanction_help_2")) + .add("list", Translatable.of("nation_sanction_help_3")) + .add("list [nation]", Translatable.of("nation_sanction_help_4")); + } + }, + ALLIES_STRING { @Override protected MenuBuilder load() { diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java index 59d61a0f62..aee7927a0f 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java @@ -17,6 +17,8 @@ import com.palmergames.bukkit.towny.event.NationPreRemoveEnemyEvent; import com.palmergames.bukkit.towny.event.nation.NationRankAddEvent; import com.palmergames.bukkit.towny.event.nation.NationRankRemoveEvent; +import com.palmergames.bukkit.towny.event.nation.NationSanctionTownAddEvent; +import com.palmergames.bukkit.towny.event.nation.NationSanctionTownRemoveEvent; import com.palmergames.bukkit.towny.event.nation.NationSetSpawnEvent; import com.palmergames.bukkit.towny.event.nation.NationTownLeaveEvent; import com.palmergames.bukkit.towny.event.NationRemoveEnemyEvent; @@ -125,6 +127,7 @@ public class NationCommand extends BaseCommand implements CommandExecutor { "enemylist", "ally", "spawn", + "sanctiontown", "king", "leader", "bankhistory", @@ -235,6 +238,18 @@ else if (args.length == 3) if (args.length == 3) { return Collections.singletonList("-ignore"); } + case "sanctiontown": + if (args.length == 2) + return NameUtil.filterByStart(Arrays.asList("add", "remove", "list"), args[1]); + if (args.length == 3 && args[1].equalsIgnoreCase("add") || args[1].equalsIgnoreCase("remove")) + return NameUtil.filterByStart(TownyUniverse.getInstance().getTowns() + .stream() + .filter(t -> !nation.hasTown(t)) + .map(Town::getName) + .collect(Collectors.toList()), args[2]); + if (args.length == 3 && args[1].equalsIgnoreCase("list")) + return getTownyStartingWith(args[2], "n"); + break; case "add": return getTownyStartingWith(args[args.length - 1], "t"); case "kick": @@ -528,6 +543,9 @@ public void parseNationCommand(final Player player, String[] split) throws Towny checkPermOrThrow(player, PermissionNodes.TOWNY_COMMAND_NATION_KICK.getNode()); nationKick(player, StringMgmt.remFirstArg(split)); break; + case "sanctiontown": + nationSanctionTown(player, null, StringMgmt.remFirstArg(split)); + break; case "set": /* Permission test is internal*/ nationSet(player, StringMgmt.remFirstArg(split), false, null); @@ -666,7 +684,11 @@ private void parseNationJoin(Player player, String[] args) { // Check if town is town is free to join. if (!nation.isOpen()) throw new TownyException(Translatable.of("msg_err_nation_not_open", nation.getFormattedName())); - + + // Check if the town is sanctioned and not allowed to join. + if (nation.hasSanctionedTown(town)) + throw new TownyException(Translatable.of("msg_err_cannot_join_nation_sanctioned_town", nation.getName())); + if (!testTownHasEnoughResidents(town)) throw new TownyException(Translatable.of("msg_err_not_enough_residents_join_nation", town.getName())); @@ -1180,6 +1202,13 @@ public void nationAdd(Player player, String[] names) throws TownyException { continue; } + if (nation.hasSanctionedTown(town)) { + // Town is sanctioned and cannot join. + removeinvites.add(townname); + TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_cannot_add_sanctioned_town", townname)); + continue; + } + if (!testNationMaxResidents(nation, town)) { // Town has too many residents to join the nation removeinvites.add(townname); @@ -1398,6 +1427,73 @@ public static void nationKick(CommandSender sender, Nation nation, List ki TownyMessaging.sendErrorMsg(sender, Translatable.of("msg_invalid_name")); } + public static void nationSanctionTown(CommandSender sender, Nation nation, String[] args) throws TownyException { + if (args.length == 0 || args[0].equals("?")) { + HelpMenu.NATION_SANCTIONTOWN.send(sender); + return; + } + + if (nation == null && sender instanceof Player player) + nation = getNationFromPlayerOrThrow(player); + + if (nation == null) + throw new TownyException(Translatable.of("msg_err_no_nation_cannot_do")); + + if (args[0].toLowerCase(Locale.ROOT).equals("list")) { + if (args.length == 2) + nation = getNationOrThrow(args[1]); + nationSanctionTownList(sender, nation); + return; + } + + if (args.length != 2) { + HelpMenu.NATION_SANCTIONTOWN.send(sender); + return; + } + checkPermOrThrow(sender, PermissionNodes.TOWNY_COMMAND_NATION_SANCTIONTOWN.getNode()); + Town town = getTownOrThrow(args[1]); + switch(args[0].toLowerCase(Locale.ROOT)) { + case "add" -> nationSanctionTownAdd(sender, nation, town); + case "remove" -> nationSactionTownRemove(sender, nation, town); + default -> HelpMenu.NATION_SANCTIONTOWN.send(sender); + } + } + + private static void nationSanctionTownList(CommandSender sender, Nation nation) { + if (nation.getSanctionedTowns().isEmpty()) { + TownyMessaging.sendMsg(sender, Translatable.of("msg_err_nation_has_no_sanctioned_towns")); + return; + } + Translator translator = Translator.locale(sender); + TownyMessaging.sendMessage(sender, ChatTools.formatTitle(nation.getName() + " " + translator.of("title_nation_sanctioned_towns"))); + TownyMessaging.sendMessage(sender, TownyFormatter.getFormattedTownyObjects(translator.of("title_nation_sanctioned_towns"), new ArrayList<>(nation.getSanctionedTowns()))); + } + + private static void nationSanctionTownAdd(CommandSender sender, Nation nation, Town town) throws TownyException { + if (nation.hasTown(town)) + throw new TownyException(Translatable.of("msg_err_nation_cannot_sanction_own_town")); + + if (nation.hasSanctionedTown(town)) + throw new TownyException(Translatable.of("msg_err_nation_town_already_sanctioned")); + + BukkitTools.ifCancelledThenThrow(new NationSanctionTownAddEvent(nation, town)); + + nation.addSanctionedTown(town); + nation.save(); + TownyMessaging.sendMsg(sender, Translatable.of("msg_err_nation_town_sanctioned", town.getName())); + } + + private static void nationSactionTownRemove(CommandSender sender, Nation nation, Town town) throws TownyException { + if (!nation.hasSanctionedTown(town)) + throw new TownyException(Translatable.of("msg_err_nation_town_isnt_sanctioned")); + + BukkitTools.ifCancelledThenThrow(new NationSanctionTownRemoveEvent(nation, town)); + + nation.removeSanctionedTown(town); + nation.save(); + TownyMessaging.sendMsg(sender, Translatable.of("msg_err_nation_town_unsanctioned", town.getName())); + } + private void nationAlly(Player player, String[] split) throws TownyException { if (split.length == 0) { HelpMenu.ALLIES_STRING.send(player); diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyAdminCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyAdminCommand.java index 5172e148ff..f2614b2077 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyAdminCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyAdminCommand.java @@ -172,6 +172,7 @@ public class TownyAdminCommand extends BaseCommand implements CommandExecutor { "rename", "delete", "toggle", + "sanctiontown", "set", "meta", "deposit", @@ -508,6 +509,9 @@ else if (args.length > 3 && TownyCommandAddonAPI.hasCommand(CommandType.TOWNYADM if (args.length == 2) { return filterByStartOrGetTownyStartingWith(Collections.singletonList("new"), args[1], "+n"); } else if (args.length > 2 && !args[1].equalsIgnoreCase("new")) { + Nation nation = TownyUniverse.getInstance().getNation(args[1]); + if (nation == null) + return Collections.emptyList(); switch (args[2].toLowerCase(Locale.ROOT)) { case "add": if (args.length == 4) @@ -518,14 +522,8 @@ else if (args.length > 3 && TownyCommandAddonAPI.hasCommand(CommandType.TOWNYADM else if (args.length == 5) return NameUtil.filterByStart(BaseCommand.setOnOffCompletes, args[4]); case "set": { - Nation nation = TownyUniverse.getInstance().getNation(args[1]); - if (nation != null) { - if (args.length == 4) { - return NameUtil.filterByStart(adminNationSetTabCompletes, args[3]); - } - } - else { - return Collections.emptyList(); + if (args.length == 4) { + return NameUtil.filterByStart(adminNationSetTabCompletes, args[3]); } } case "meta": @@ -546,6 +544,16 @@ else if (args.length == 5) return getTownyStartingWith(args[4], "r"); else if (args.length == 6) return NameUtil.filterByStart(TownyPerms.getNationRanks(), args[5]); + case "sanctiontown": + if (args.length == 4) + return NameUtil.filterByStart(Arrays.asList("add","remove","list"), args[3]); + if (args.length == 5 && args[3].equalsIgnoreCase("add") || args[4].equalsIgnoreCase("remove")) + return NameUtil.filterByStart(TownyUniverse.getInstance().getTowns() + .stream() + .filter(t -> !nation.hasTown(t)) + .map(Town::getName) + .collect(Collectors.toList()), args[4]); + break; case "enemy": case "ally": if (args.length == 4) @@ -1633,6 +1641,10 @@ public void parseAdminNationCommand(CommandSender sender, String[] split) throws checkPermOrThrow(sender, PermissionNodes.TOWNY_COMMAND_TOWNYADMIN_NATION_KICK.getNode()); NationCommand.nationKick(sender, nation, TownyAPI.getInstance().getTowns(StringMgmt.remArgs(split, 2))); break; + case "sanctiontown": + checkPermOrThrow(sender, PermissionNodes.TOWNY_COMMAND_TOWNYADMIN_NATION_SANCTIONTOWN.getNode()); + NationCommand.nationSanctionTown(sender, nation, StringMgmt.remArgs(split, 2)); + break; case "delete": checkPermOrThrow(sender, PermissionNodes.TOWNY_COMMAND_TOWNYADMIN_NATION_DELETE.getNode()); Confirmation.runOnAccept(() -> { diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/db/SQLSchema.java b/Towny/src/main/java/com/palmergames/bukkit/towny/db/SQLSchema.java index db381c9024..7f59bfadd2 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/db/SQLSchema.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/db/SQLSchema.java @@ -269,6 +269,7 @@ private static List getNationColumns(){ columns.add("`isOpen` bool NOT NULL DEFAULT '1'"); columns.add("`metadata` text DEFAULT NULL"); columns.add("`conqueredTax` float NOT NULL"); + columns.add("`sanctionedTowns` mediumtext DEFAULT NULL"); return columns; } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownyFlatFileSource.java b/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownyFlatFileSource.java index 7c95e759c7..db38f3f0a5 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownyFlatFileSource.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownyFlatFileSource.java @@ -1218,6 +1218,11 @@ public boolean loadNation(Nation nation) { if (line != null && !line.isEmpty()) nation.setConqueredTax(Double.parseDouble(line)); + line = keys.get("sanctionedTowns"); + if (line != null) { + nation.loadSanctionedTowns(line.split("#")); + } + } catch (Exception e) { plugin.getLogger().log(Level.WARNING, Translation.of("flatfile_err_reading_nation_file_at_line", nation.getName(), line, nation.getName()), e); return false; @@ -2136,6 +2141,9 @@ public boolean saveNation(Nation nation) { list.add("metadata=" + serializeMetadata(nation)); list.add("conqueredTax=" + nation.getConqueredTax()); + + // SanctionedTowns + list.add("sanctionedTowns=" + StringMgmt.join(nation.getSanctionedTownsForSaving(), "#")); /* * Make sure we only save in async */ diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownySQLSource.java b/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownySQLSource.java index 5c54d58866..9c3e0cd008 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownySQLSource.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownySQLSource.java @@ -1387,6 +1387,11 @@ private boolean loadNation(ResultSet rs) { } catch (SQLException ignored) { } + line = rs.getString("sanctionedTowns"); + if (line != null) { + nation.loadSanctionedTowns(line.split("#")); + } + return true; } catch (SQLException e) { TownyMessaging.sendErrorMsg("SQL: Load Nation " + name + " SQL Error - " + e.getMessage()); @@ -2349,7 +2354,7 @@ public synchronized boolean saveNation(Nation nation) { nat_hm.put("metadata", ""); nat_hm.put("conqueredTax", nation.getConqueredTax()); - + nat_hm.put("sanctionedTowns", StringMgmt.join(nation.getSanctionedTownsForSaving(), "#")); updateDB("NATIONS", nat_hm, Collections.singletonList("name")); } catch (Exception e) { diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownAddEvent.java b/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownAddEvent.java new file mode 100644 index 0000000000..34f31113b0 --- /dev/null +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownAddEvent.java @@ -0,0 +1,38 @@ +package com.palmergames.bukkit.towny.event.nation; + +import com.palmergames.bukkit.towny.event.CancellableTownyEvent; +import com.palmergames.bukkit.towny.object.Nation; +import com.palmergames.bukkit.towny.object.Town; + +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class NationSanctionTownAddEvent extends CancellableTownyEvent { + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final Nation nation; + private final Town town; + + public NationSanctionTownAddEvent(Nation nation, Town town) { + this.nation = nation; + this.town = town; + } + + public Nation getNation() { + return nation; + } + + public Town getTown() { + return town; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } +} diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownRemoveEvent.java b/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownRemoveEvent.java new file mode 100644 index 0000000000..75517e988d --- /dev/null +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownRemoveEvent.java @@ -0,0 +1,38 @@ +package com.palmergames.bukkit.towny.event.nation; + +import com.palmergames.bukkit.towny.event.CancellableTownyEvent; +import com.palmergames.bukkit.towny.object.Nation; +import com.palmergames.bukkit.towny.object.Town; + +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class NationSanctionTownRemoveEvent extends CancellableTownyEvent { + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final Nation nation; + private final Town town; + + public NationSanctionTownRemoveEvent(Nation nation, Town town) { + this.nation = nation; + this.town = town; + } + + public Nation getNation() { + return nation; + } + + public Town getTown() { + return town; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } +} diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java b/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java index c57c5718e7..0f647454e2 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java @@ -34,6 +34,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.UUID; import java.util.stream.Collectors; public class Nation extends Government { @@ -41,6 +42,7 @@ public class Nation extends Government { private static final String ECONOMY_ACCOUNT_PREFIX = TownySettings.getNationAccountPrefix(); private final List towns = new ArrayList<>(); + private final List sanctionedTowns = new ArrayList<>(); private List allies = new ArrayList<>(); private List enemies = new ArrayList<>(); private Town capital; @@ -698,4 +700,34 @@ public int getLevel(int populationSize) { public void playerBroadCastMessageToNation(Player player, String message) { TownyMessaging.sendPrefixedNationMessage(this, Translatable.of("town_say_format", player.getName(), TownyComponents.stripClickTags(message))); } + + + public List getSanctionedTowns() { + return sanctionedTowns; + } + + public boolean hasSanctionedTown(Town town) { + return sanctionedTowns.contains(town); + } + + public void addSanctionedTown(Town town) { + if (!sanctionedTowns.contains(town)) + sanctionedTowns.add(town); + } + + public void removeSanctionedTown(Town town) { + sanctionedTowns.remove(town); + } + + public List getSanctionedTownsForSaving() { + return sanctionedTowns.stream().map(t -> t.getUUID().toString()).collect(Collectors.toList()); + } + + public void loadSanctionedTowns(String[] tokens) { + for (String stringUUID : tokens) { + UUID uuid = UUID.fromString(stringUUID); + if (uuid != null && TownyAPI.getInstance().getTown(uuid) != null) + sanctionedTowns.add(TownyAPI.getInstance().getTown(uuid)); + } + } } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/permissions/PermissionNodes.java b/Towny/src/main/java/com/palmergames/bukkit/towny/permissions/PermissionNodes.java index f81f85110e..6c5a59dafe 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/permissions/PermissionNodes.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/permissions/PermissionNodes.java @@ -90,6 +90,7 @@ public enum PermissionNodes { TOWNY_COMMAND_NATION_BANKHISTORY("towny.command.nation.bankhistory"), TOWNY_COMMAND_NATION_BALTOP("towny.command.nation.baltop"), TOWNY_COMMAND_NATION_KICK("towny.command.nation.kick"), + TOWNY_COMMAND_NATION_SANCTIONTOWN("towny.command.nation.sanctiontown"), /* * Town command permissions @@ -338,6 +339,7 @@ public enum PermissionNodes { TOWNY_COMMAND_TOWNYADMIN_NATION_RECHECK("towny.command.townyadmin.nation.recheck"), TOWNY_COMMAND_TOWNYADMIN_NATION_RENAME("towny.command.townyadmin.nation.rename"), TOWNY_COMMAND_TOWNYADMIN_NATION_MERGE("towny.command.townyadmin.nation.merge"), + TOWNY_COMMAND_TOWNYADMIN_NATION_SANCTIONTOWN("towny.command.townyadmin.nation.sanctiontown"), TOWNY_COMMAND_TOWNYADMIN_NATION_SET("towny.command.townyadmin.nation.set"), TOWNY_COMMAND_TOWNYADMIN_NATION_SETFOUNDINGDATE("towny.command.townyadmin.nation.set.foundingdate"), TOWNY_COMMAND_TOWNYADMIN_NATION_TOGGLE("towny.command.townyadmin.nation.toggle"), diff --git a/Towny/src/main/java/com/palmergames/bukkit/util/NameValidation.java b/Towny/src/main/java/com/palmergames/bukkit/util/NameValidation.java index 6bcc8d0395..8e90a68361 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/util/NameValidation.java +++ b/Towny/src/main/java/com/palmergames/bukkit/util/NameValidation.java @@ -29,7 +29,7 @@ public class NameValidation { "outlawlist","deposit","outlaw","outpost","ranklist","rank","reclaim","reslist","say","set","toggle","join", "invite","buy","mayor","bankhistory","enemy","ally","townlist","allylist","enemylist","king","merge","jail", "plotgrouplist","trust","purge","leader","baltop","all","help", "spawn", "takeoverclaim", "ban", "unjail", - "trusttown","forsale","fs","notforsale","nfs","buytown")); + "trusttown","forsale","fs","notforsale","nfs","buytown","sanctiontown")); } /** diff --git a/Towny/src/main/resources/lang/en-US.yml b/Towny/src/main/resources/lang/en-US.yml index ebb3a4c368..c9b567b105 100644 --- a/Towny/src/main/resources/lang/en-US.yml +++ b/Towny/src/main/resources/lang/en-US.yml @@ -206,6 +206,11 @@ nation_toggle_help_1: "Toggles public status, allowing non-residents to use /n s nation_toggle_help_2: "Toggles open status, allowing towns to join without an invite." nation_toggle_help_3: "Toggles taxpercent, making towns pay a percentage instead of fixed rate." +nation_sanction_help_1: "Adds a town to the SanctionedTown list." +nation_sanction_help_2: "Removes a town from the SanctionedTown list." +nation_sanction_help_3: "Lists your nation's Sanctioned Towns." +nation_sanction_help_4: "Lists the given nation's Sanctioned Towns." + king_help_1: 'Nation Leader Help' king_help_2: 'Set your alliance.' king_help_3: 'Set your enemies.' @@ -2371,4 +2376,24 @@ msg_err_resident_is_npc: "You cannot do this to an NPC resident." # Message shown when a resident doesn't own any land and the /res plotlist command is used. msg_err_resident_doesnt_own_any_land: "The resident does not own any land personally." -msg_unnamed: "Unnamed" \ No newline at end of file +msg_unnamed: "Unnamed" + +msg_err_nation_cannot_sanction_own_town: "You cannot sanction a town that is a part of your nation." + +msg_err_nation_town_isnt_sanctioned: "Your nation is not sanctioning that town." + +msg_err_nation_town_already_sanctioned: "Your nation already sanctions that town." + +msg_err_nation_town_sanctioned: "Your nation has now sanctioned %s, they will not be allowed to join your nation." + +msg_err_nation_town_unsanctioned: "Your nation is no longer sanctioning %s." + +msg_err_nation_has_no_sanctioned_towns: "The nation has no sanctioned towns." + +title_nation_sanctioned_towns: 'Sanctioned Towns' + +msg_err_cannot_add_sanctioned_town: "Your nation cannot add %s, your nation has sanctioned them." + +msg_err_cannot_join_nation_sanctioned_town: "Your town cannot join %s, this nation has sanctioned your town." + +msg_err_no_nation_cannot_do: "You cannot perform this action because no nation was able to be determined." \ No newline at end of file diff --git a/Towny/src/main/resources/plugin.yml b/Towny/src/main/resources/plugin.yml index 640db5bc98..4986906125 100644 --- a/Towny/src/main/resources/plugin.yml +++ b/Towny/src/main/resources/plugin.yml @@ -211,6 +211,7 @@ permissions: towny.command.nation.othernation: true towny.command.nation.withdraw: true towny.command.nation.rank.*: true + towny.command.nation.sanctiontown: true towny.command.nation.say: true towny.command.nation.set.*: true towny.command.nation.spawn: true @@ -746,6 +747,7 @@ permissions: towny.command.townyadmin.nation.rank: true towny.command.townyadmin.nation.recheck: true towny.command.townyadmin.nation.rename: true + towny.command.townyadmin.nation.sanctiontown: true towny.command.townyadmin.nation.set: true towny.command.townyadmin.nation.set.*: true towny.command.townyadmin.nation.toggle: true From f320915b2237345d8402801872607fc1192aaadd Mon Sep 17 00:00:00 2001 From: LlmDl Date: Thu, 21 Dec 2023 17:38:26 -0600 Subject: [PATCH 8/9] - Add the ability for nations to sanction towns, preventing them from joining the nation via invite or join. - Using /n sanctiontown ... kings can add/remove and list their sanctioned towns. - Closes #6544. - New Command: /nation sanctiontown - add [townname] - Adds a town to the sanctioned town list. - remove [townname] - Removes a town from the sanctioned town list. - list - Lists your nation's sanctioned towns. - list [nationname] - Lists the sanctioned towns of other nations. - New Command: /ta nation [nationname] sanctiontown - add [townname] - Adds a town to the sanctioned town list. - remove [townname] - Removes a town from the sanctioned town list. - list - Lists the nation's sanctioned towns. - New Permission Node: towny.command.nation.sanctiontown - Allows adding/removing sanctioned towns, a child node of towny.command.nation.*. - No townyperms.yml change is required. - New Permission Node: towny.command.townyadmin.nation.sanctiontown - Allows admins to add/remove sanctioned towns from a given nation, a child node of towny.command.townyadmin.nation.*. - No townyperms.yml change is required. - API: Added NationSanctionTownAddEvent - A cancellable event which is fired before a town becomes sanctioned. - API: Added NationSanctionTownRemoveEvent - A cancellable event which is fired before a town becomes unsanctioned. --- Towny/src/main/resources/ChangeLog.txt | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Towny/src/main/resources/ChangeLog.txt b/Towny/src/main/resources/ChangeLog.txt index d0bc984a19..d55cc54e95 100644 --- a/Towny/src/main/resources/ChangeLog.txt +++ b/Towny/src/main/resources/ChangeLog.txt @@ -9291,4 +9291,26 @@ v0.92.0.11: 0.100.0.13: - Bump org.apache.maven.plugins:maven-surefire-plugin from 3.1.2 to 3.2.3. - Bump com.github.seeseemelk:MockBukkit-v1.20 from 3.56.0 to 3.58.0. - - Make Towny more resiliant to errors which come from having no Economy present on the server. \ No newline at end of file + - Make Towny more resiliant to errors which come from having no Economy present on the server. + - Add the ability for nations to sanction towns, preventing them from joining the nation via invite or join. + - Using /n sanctiontown ... kings can add/remove and list their sanctioned towns. + - Closes #6544. + - New Command: /nation sanctiontown + - add [townname] - Adds a town to the sanctioned town list. + - remove [townname] - Removes a town from the sanctioned town list. + - list - Lists your nation's sanctioned towns. + - list [nationname] - Lists the sanctioned towns of other nations. + - New Command: /ta nation [nationname] sanctiontown + - add [townname] - Adds a town to the sanctioned town list. + - remove [townname] - Removes a town from the sanctioned town list. + - list - Lists the nation's sanctioned towns. + - New Permission Node: towny.command.nation.sanctiontown + - Allows adding/removing sanctioned towns, a child node of towny.command.nation.*. + - No townyperms.yml change is required. + - New Permission Node: towny.command.townyadmin.nation.sanctiontown + - Allows admins to add/remove sanctioned towns from a given nation, a child node of towny.command.townyadmin.nation.*. + - No townyperms.yml change is required. + - API: Added NationSanctionTownAddEvent + - A cancellable event which is fired before a town becomes sanctioned. + - API: Added NationSanctionTownRemoveEvent + - A cancellable event which is fired before a town becomes unsanctioned. \ No newline at end of file From e82ee03feb593aba501fadb4cd42765e14cdfd5f Mon Sep 17 00:00:00 2001 From: LlmDl Date: Thu, 21 Dec 2023 19:42:00 -0600 Subject: [PATCH 9/9] - Fix potential IllegalArgumentException by loading sanctioned towns in a safer way. --- Towny/pom.xml | 2 +- .../com/palmergames/bukkit/towny/object/Nation.java | 10 +++++++--- Towny/src/main/resources/ChangeLog.txt | 4 +++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Towny/pom.xml b/Towny/pom.xml index 3d9f30d467..220d5e400a 100644 --- a/Towny/pom.xml +++ b/Towny/pom.xml @@ -13,7 +13,7 @@ towny jar - 0.100.0.13 + 0.100.0.14 diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java b/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java index 0f647454e2..cdaf705950 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java @@ -725,9 +725,13 @@ public List getSanctionedTownsForSaving() { public void loadSanctionedTowns(String[] tokens) { for (String stringUUID : tokens) { - UUID uuid = UUID.fromString(stringUUID); - if (uuid != null && TownyAPI.getInstance().getTown(uuid) != null) - sanctionedTowns.add(TownyAPI.getInstance().getTown(uuid)); + try { + Town town = TownyAPI.getInstance().getTown(UUID.fromString(stringUUID)); + if (town != null) + sanctionedTowns.add(town); + } catch (IllegalArgumentException ignored) { + continue; + } } } } diff --git a/Towny/src/main/resources/ChangeLog.txt b/Towny/src/main/resources/ChangeLog.txt index d55cc54e95..b708dc797c 100644 --- a/Towny/src/main/resources/ChangeLog.txt +++ b/Towny/src/main/resources/ChangeLog.txt @@ -9313,4 +9313,6 @@ v0.92.0.11: - API: Added NationSanctionTownAddEvent - A cancellable event which is fired before a town becomes sanctioned. - API: Added NationSanctionTownRemoveEvent - - A cancellable event which is fired before a town becomes unsanctioned. \ No newline at end of file + - A cancellable event which is fired before a town becomes unsanctioned. +0.100.0.14: + - Fix potential IllegalArgumentException by loading sanctioned towns in a safer way. \ No newline at end of file