From 1ed27e2ccee455be00f6e9324fd5b1fa9757c858 Mon Sep 17 00:00:00 2001 From: Tomhmagic <80466923+tomhmagic@users.noreply.github.com> Date: Wed, 6 Sep 2023 11:29:55 +0100 Subject: [PATCH] Completed savings feature added message caching for when messages are sent to offline players --- .../com/tanukicraft/townypay/TownyPay.java | 79 +++++-------------- .../townypay/commands/NationPayAddon.java | 3 +- .../townypay/commands/ResidentPayAddon.java | 15 ++-- .../commands/ResidentSavingsAddon.java | 11 +-- .../townypay/commands/TownPayAddon.java | 3 +- .../townypay/functions/PayFunctions.java | 16 +++- .../townypay/functions/SavingsFunctions.java | 53 ++++--------- .../listeners/PlayerJoinEventListener.java | 28 +++++++ .../listeners/TownyNewDayEventListener.java | 20 +++++ .../metadata/KingPayMetaDataController.java | 2 +- .../metadata/MayorPayMetaDataController.java | 4 +- .../townypay/metadata/MetaDataInit.java | 57 +++++++++++++ .../settings/MessageCacheSettings.java | 30 +++++++ .../townypay/util/CustomConfig.java | 45 +++++++++++ .../townypay/util/GeneralUtil.java | 30 ++++--- .../townypay/util/MessageCacheUtil.java | 67 ++++++++++++++++ .../townypay/util/MessageUtil.java | 30 ++++++- src/main/resources/config.yml | 34 +++++--- src/main/resources/lang/en-US.yml | 9 ++- 19 files changed, 391 insertions(+), 145 deletions(-) create mode 100644 src/main/java/com/tanukicraft/townypay/listeners/PlayerJoinEventListener.java create mode 100644 src/main/java/com/tanukicraft/townypay/metadata/MetaDataInit.java create mode 100644 src/main/java/com/tanukicraft/townypay/settings/MessageCacheSettings.java create mode 100644 src/main/java/com/tanukicraft/townypay/util/CustomConfig.java create mode 100644 src/main/java/com/tanukicraft/townypay/util/MessageCacheUtil.java diff --git a/src/main/java/com/tanukicraft/townypay/TownyPay.java b/src/main/java/com/tanukicraft/townypay/TownyPay.java index e00db0b..ac22147 100644 --- a/src/main/java/com/tanukicraft/townypay/TownyPay.java +++ b/src/main/java/com/tanukicraft/townypay/TownyPay.java @@ -2,34 +2,35 @@ import com.palmergames.bukkit.towny.TownyAPI; import com.palmergames.bukkit.towny.TownyCommandAddonAPI; -import com.palmergames.bukkit.towny.exceptions.KeyAlreadyRegisteredException; import com.palmergames.bukkit.towny.exceptions.TownyException; import com.palmergames.bukkit.towny.exceptions.initialization.TownyInitException; -import com.palmergames.bukkit.towny.object.Translatable; import com.palmergames.bukkit.towny.object.TranslationLoader; -import com.palmergames.bukkit.towny.object.metadata.BooleanDataField; -import com.palmergames.bukkit.towny.object.metadata.IntegerDataField; import com.palmergames.bukkit.util.Colors; import com.palmergames.bukkit.util.Version; import com.tanukicraft.townypay.commands.*; +import com.tanukicraft.townypay.listeners.PlayerJoinEventListener; import com.tanukicraft.townypay.listeners.TownyNewDayEventListener; import com.tanukicraft.townypay.settings.NationSettings; import com.tanukicraft.townypay.settings.TownSettings; +import com.tanukicraft.townypay.metadata.MetaDataInit; +import com.tanukicraft.townypay.util.CustomConfig; import org.bukkit.*; +import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; +import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Objects; public final class TownyPay extends JavaPlugin { - private File customConfigFile; - private FileConfiguration customConfig; - private static Version requiredTownyVersion = Version.fromString("0.99.5.0"); + private static final Version requiredTownyVersion = Version.fromString("0.99.5.0"); private static TownyPay plugin; @@ -41,10 +42,11 @@ public static TownyPay getPlugin() { } + // Called when the plugin first loads. @Override public void onLoad() { - loadCustomDataField(); + MetaDataInit.loadCustomDataField(); } @Override public void onEnable() { @@ -66,8 +68,9 @@ public boolean loadAll() { printArt(); townyVersionCheck(); loadConfig(); + createCustomConfig(); //Load languages - loadLocalization(false); + loadLocalization(); //Load commands and listeners registerCommands(); @@ -91,7 +94,7 @@ public void createPluginFolder(){ if(!f.exists()) f.mkdir(); } - private void loadLocalization(boolean reload) throws TownyException { + private void loadLocalization() throws TownyException { try { Plugin plugin = getPlugin(); Path langFolderPath = Paths.get(plugin.getDataFolder().getPath()).resolve("lang"); @@ -101,9 +104,6 @@ private void loadLocalization(boolean reload) throws TownyException { } catch (TownyInitException e) { throw new TownyException("Locale files failed to load! Disabling!"); } - if (reload) { - info(Translatable.of("msg_reloaded_lang").defaultLocale()); - } } private void printArt() { Bukkit.getConsoleSender().sendMessage(""); @@ -118,6 +118,7 @@ private void printArt() { private void registerListeners() { PluginManager pm = Bukkit.getServer().getPluginManager(); pm.registerEvents(new TownyNewDayEventListener(), this); + pm.registerEvents(new PlayerJoinEventListener(), this); } private void registerCommands() { @@ -147,9 +148,14 @@ private void loadConfig(){ createPluginFolder(); this.saveDefaultConfig(); } + private void createCustomConfig() { + CustomConfig.setup("MessageCache", "yml"); + CustomConfig.get().options().copyDefaults(true); + CustomConfig.save(); + } private String getTownyVersion() { - return Bukkit.getPluginManager().getPlugin("Towny").getDescription().getVersion(); + return Objects.requireNonNull(Bukkit.getPluginManager().getPlugin("Towny")).getDescription().getVersion(); } private void townyVersionCheck() throws TownyException{ if (!(Version.fromString(getTownyVersion()).compareTo(requiredTownyVersion) >= 0)) @@ -163,51 +169,6 @@ public static void info(String message) { } - // Parts of a datafield - - // Use those parts to create a new data field to store an integer - private static IntegerDataField TownyPayBudgetField = new IntegerDataField("TownyPayBudget", 0, "Budget"); - private static IntegerDataField TownyPaySpendField = new IntegerDataField("TownyPaySpend", 0, "Spend"); - private static IntegerDataField TownyPaySetPayField = new IntegerDataField("TownyPaySetPay", 0, "Pay"); - private static BooleanDataField TownyPayToggleField = new BooleanDataField("TownyPayToggle", true, "Pay Toggle"); - private static IntegerDataField TownyPaySavingsField = new IntegerDataField("TownyPaySavings", 0, "Savings"); - private static IntegerDataField TownyPayHoldingsField = new IntegerDataField("TownyPayHoldings", 0, "Holdings"); - - private void loadCustomDataField(){ - // (Optional) Try to globally register the data field. - // Globally registering data fields allow them to be modified in-game by administrators. - try { - TownyAPI.getInstance().registerCustomDataField(TownyPayBudgetField); - } catch (KeyAlreadyRegisteredException e) { - getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again - } - try { - TownyAPI.getInstance().registerCustomDataField(TownyPaySpendField); - } catch (KeyAlreadyRegisteredException e) { - getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again - } - try { - TownyAPI.getInstance().registerCustomDataField(TownyPaySetPayField); - } catch (KeyAlreadyRegisteredException e) { - getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again - } - try { - TownyAPI.getInstance().registerCustomDataField(TownyPayToggleField); - } catch (KeyAlreadyRegisteredException e) { - getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again - } - try { - TownyAPI.getInstance().registerCustomDataField(TownyPaySavingsField); - } catch (KeyAlreadyRegisteredException e) { - getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again - } - try { - TownyAPI.getInstance().registerCustomDataField(TownyPayHoldingsField); - } catch (KeyAlreadyRegisteredException e) { - getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again - } - getLogger().info("Custom data fields successfully registered!"); - } } diff --git a/src/main/java/com/tanukicraft/townypay/commands/NationPayAddon.java b/src/main/java/com/tanukicraft/townypay/commands/NationPayAddon.java index e8ef0a6..b67504c 100644 --- a/src/main/java/com/tanukicraft/townypay/commands/NationPayAddon.java +++ b/src/main/java/com/tanukicraft/townypay/commands/NationPayAddon.java @@ -93,8 +93,7 @@ public boolean onCommand(CommandSender commandSender, Command command, String s, //remove the tax from the player targetRes.getAccount().withdraw(calcTax, String.valueOf(Translatable.of("townypay.nation.BudgetPaymentTaxReason"))); //message the player - Player targetPlayer = targetRes.getPlayer(); - MessageUtil.sendPlayerMsg(targetPlayer,Translatable.of("townypay.general.PaymentReceived",senderNation, pay, tax)); + MessageUtil.sendResidentMsg(targetRes,Translatable.of("townypay.general.PaymentReceived",senderNation, pay, tax)); //message the sender MessageUtil.sendMsg(commandSender,Translatable.of("townypay.general.PaymentSend", targetRes, pay)); diff --git a/src/main/java/com/tanukicraft/townypay/commands/ResidentPayAddon.java b/src/main/java/com/tanukicraft/townypay/commands/ResidentPayAddon.java index c6a906a..5512641 100644 --- a/src/main/java/com/tanukicraft/townypay/commands/ResidentPayAddon.java +++ b/src/main/java/com/tanukicraft/townypay/commands/ResidentPayAddon.java @@ -17,17 +17,20 @@ import java.util.Collections; import java.util.List; +import java.util.Objects; + public class ResidentPayAddon extends BaseCommand implements CommandExecutor, TabCompleter { @Override public boolean onCommand(CommandSender commandSender, Command command, String s, String[] strings){ Resident res = TownyAPI.getInstance().getResident((Player) commandSender); + assert res != null; if (res.hasPermissionNode("townypay.command.resident.pay")) { //check for perm String target = null; try { - target = TownyAPI.getInstance().getResident(strings[0]).toString(); - } catch (Exception e) { + target = Objects.requireNonNull(TownyAPI.getInstance().getResident(strings[0])).toString(); + } catch (Exception ignored) { } if(strings.length == 2){ //check player and value was given if (target == null) { //if no valid player was given @@ -55,7 +58,9 @@ public boolean onCommand(CommandSender commandSender, Command command, String s, return false; } else { //pay given player + assert targetRes != null; Town targetTown = GeneralUtil.getTown(targetRes); + assert senderRes != null; Town senderTown = GeneralUtil.getTown(senderRes); Nation targetNation = GeneralUtil.getNation(targetRes); Nation senderNation = GeneralUtil.getNation(senderRes); @@ -67,7 +72,7 @@ public boolean onCommand(CommandSender commandSender, Command command, String s, } } - double calcTax = (pay / 100) * tax; + double calcTax = ((double) pay / 100) * tax; //send money senderRes.getAccount().payTo(pay, targetRes.getAccount(), String.valueOf(Translatable.of("townypay.Resident.PaymentReason"))); @@ -75,8 +80,8 @@ public boolean onCommand(CommandSender commandSender, Command command, String s, //remove the tax from the player targetRes.getAccount().withdraw(calcTax, String.valueOf(Translatable.of("townypay.Resident.PaymentTaxReason"))); //message the player - Player targetPlayer = targetRes.getPlayer(); - MessageUtil.sendPlayerMsg(targetPlayer,Translatable.of("townypay.general.PaymentReceived",senderRes, pay, tax)); + MessageUtil.sendResidentMsg(targetRes,Translatable.of("townypay.general.PaymentReceived",senderRes, pay, tax)); + //message the sender MessageUtil.sendMsg(commandSender,Translatable.of("townypay.general.PaymentSend", targetRes, pay)); diff --git a/src/main/java/com/tanukicraft/townypay/commands/ResidentSavingsAddon.java b/src/main/java/com/tanukicraft/townypay/commands/ResidentSavingsAddon.java index 705c7c4..b555de7 100644 --- a/src/main/java/com/tanukicraft/townypay/commands/ResidentSavingsAddon.java +++ b/src/main/java/com/tanukicraft/townypay/commands/ResidentSavingsAddon.java @@ -23,10 +23,6 @@ public class ResidentSavingsAddon extends BaseCommand implements CommandExecutor @Override public boolean onCommand(CommandSender commandSender, Command command, String s, String[] strings) { Resident res = TownyAPI.getInstance().getResident((Player) commandSender); - commandSender.sendMessage("DEBUG: length = " + strings.length); - commandSender.sendMessage("DEBUG: arg0 = " + strings[0]); - //commandSender.sendMessage("DEBUG: arg1 = " + strings[1]); - commandSender.sendMessage("DEBUG: s???? = " + s); assert res != null; if (res.hasPermissionNode("townypay.command.resident.savings")) { @@ -36,12 +32,10 @@ public boolean onCommand(CommandSender commandSender, Command command, String s, switch (arg) { case "info": sendSavingsInfo(commandSender, res); - commandSender.sendMessage("DEBUG: info section"); break; case "deposit": if (!GeneralUtil.isNotInteger(strings[1])) { amount = Integer.parseInt(strings[1]); - commandSender.sendMessage("DEBUG: deposit section"); savingsDeposit(commandSender, res, amount); } else { MessageUtil.sendErrorMsg(commandSender, Translatable.of("townypay.general.ValueError")); @@ -50,7 +44,6 @@ public boolean onCommand(CommandSender commandSender, Command command, String s, case "withdraw": if (!GeneralUtil.isNotInteger(strings[1])) { amount = Integer.parseInt(strings[1]); - commandSender.sendMessage("DEBUG: withdraw section"); savingsWithdraw(commandSender, res, amount); } else { MessageUtil.sendErrorMsg(commandSender, Translatable.of("townypay.general.ValueError")); @@ -89,6 +82,7 @@ private void savingsDeposit(CommandSender sender, Resident resident, int amount) } else { int newHoldings = currentHoldings + amount; SavingsMetaDataController.setHoldingsData(resident,newHoldings); + resident.getAccount().withdraw(amount, String.valueOf(Translatable.of("townypay.Savings.Deposit"))); MessageUtil.sendMsg(sender, Translatable.of("townypay.Resident.Savings.Deposit", amount, newHoldings)); } } @@ -103,7 +97,8 @@ private void savingsWithdraw(CommandSender sender, Resident resident, int amount int newSavings = currentSavings - amount; SavingsMetaDataController.setSavingsData(resident,newSavings); - resident.getAccount().withdraw(amount, String.valueOf(Translatable.of("townypay.Resident.Savings.Withdraw"))); + resident.getAccount().deposit(amount, String.valueOf(Translatable.of("townypay.Savings.Withdraw"))); + MessageUtil.sendMsg(sender, Translatable.of("townypay.Resident.Savings.Withdraw", amount, newSavings)); } } diff --git a/src/main/java/com/tanukicraft/townypay/commands/TownPayAddon.java b/src/main/java/com/tanukicraft/townypay/commands/TownPayAddon.java index 8a13bc6..3bad33d 100644 --- a/src/main/java/com/tanukicraft/townypay/commands/TownPayAddon.java +++ b/src/main/java/com/tanukicraft/townypay/commands/TownPayAddon.java @@ -90,8 +90,7 @@ public boolean onCommand(CommandSender commandSender, Command command, String s, //remove the tax from the player targetRes.getAccount().withdraw(calcTax, String.valueOf(Translatable.of("townypay.town.BudgetPaymentTaxReason"))); //message the player - Player targetPlayer = targetRes.getPlayer(); - MessageUtil.sendPlayerMsg(targetPlayer,Translatable.of("townypay.general.PaymentReceived",senderTown, pay, tax)); + MessageUtil.sendResidentMsg(targetRes,Translatable.of("townypay.general.PaymentReceived",senderTown, pay, tax)); //message the sender MessageUtil.sendMsg(commandSender,Translatable.of("townypay.general.PaymentSend", targetRes, pay)); diff --git a/src/main/java/com/tanukicraft/townypay/functions/PayFunctions.java b/src/main/java/com/tanukicraft/townypay/functions/PayFunctions.java index 71833ae..c0bd758 100644 --- a/src/main/java/com/tanukicraft/townypay/functions/PayFunctions.java +++ b/src/main/java/com/tanukicraft/townypay/functions/PayFunctions.java @@ -28,7 +28,7 @@ public static void payMayors() { MessageUtil.logStatus(Translatable.of("townypay.status.log.mayorpay.start")); for (Town town : towns) { - Boolean enabled = true; + boolean enabled = true; if (MayorPayMetaDataController.hasToggleData(town)) { enabled = MayorPayMetaDataController.getToggleData(town); @@ -41,7 +41,8 @@ public static void payMayors() { double bal = town.getAccount().getHoldingBalance(); double profit = GeneralUtil.calcProfit(bal, upkeep, townProfitCalc); if (TownSettings.canSetPay()) { - if (!MayorPayMetaDataController.hasPayData(town)) { + String stringCheck = String.valueOf(MayorPayMetaDataController.getPayData(town)); + if (!MayorPayMetaDataController.hasPayData(town) || !GeneralUtil.isNotInteger(stringCheck)) { MayorPayMetaDataController.setPayData(town, TownSettings.getPayValue()); } mayorPay = MayorPayMetaDataController.getPayData(town); @@ -66,6 +67,9 @@ public static void payMayors() { assert player != null; player.withdraw(taxFloor, String.valueOf(Translatable.of("townypay.town.PayTaxReason"))); + //Message Mayor + MessageUtil.sendResidentMsg(town.getMayor(),Translatable.of("townypay.town.PayMsg", payFloor, taxFloor)); + MessageUtil.logStatus(Translatable.of("townypay.status.log.mayorpay", town, payFloor, player.getName(), taxFloor)); } else { MessageUtil.logStatus(Translatable.of("townypay.status.log.nobalance.town", town)); @@ -91,7 +95,7 @@ public static void payKings() { MessageUtil.logStatus(Translatable.of("townypay.status.log.kingpay.start")); for (Nation nation : nations) { - Boolean enabled = true; + boolean enabled = true; if (KingPayMetaDataController.hasToggleData(nation)) { enabled = KingPayMetaDataController.getToggleData(nation); @@ -103,7 +107,8 @@ public static void payKings() { double profit = GeneralUtil.calcProfit(bal, upkeep, nationProfitCalc); if (enabled) { if (TownSettings.canSetPay()) { - if (!KingPayMetaDataController.hasPayData(nation)) { + String stringCheck = String.valueOf(KingPayMetaDataController.getPayData(nation)); + if (!KingPayMetaDataController.hasPayData(nation) || !GeneralUtil.isNotInteger(stringCheck)) { KingPayMetaDataController.setPayData(nation, TownSettings.getPayValue()); } kingPay = KingPayMetaDataController.getPayData(nation); @@ -129,6 +134,9 @@ public static void payKings() { assert player != null; player.withdraw(taxFloor, String.valueOf(Translatable.of("townypay.nation.PayTaxReason"))); + //Message Mayor + MessageUtil.sendResidentMsg(nation.getKing(),Translatable.of("townypay.nation.PayMsg", payFloor, taxFloor)); + MessageUtil.logStatus(Translatable.of("townypay.status.log.kingpay", nation, payFloor, player.getName(), taxFloor)); } else { MessageUtil.logStatus(Translatable.of("townypay.status.log.nobalance.nation", nation)); diff --git a/src/main/java/com/tanukicraft/townypay/functions/SavingsFunctions.java b/src/main/java/com/tanukicraft/townypay/functions/SavingsFunctions.java index 03ed635..0cba427 100644 --- a/src/main/java/com/tanukicraft/townypay/functions/SavingsFunctions.java +++ b/src/main/java/com/tanukicraft/townypay/functions/SavingsFunctions.java @@ -6,10 +6,9 @@ import com.palmergames.bukkit.towny.object.Translatable; import com.tanukicraft.townypay.metadata.SavingsMetaDataController; import com.tanukicraft.townypay.settings.SavingsSettings; +import com.tanukicraft.townypay.util.GeneralUtil; import com.tanukicraft.townypay.util.MessageUtil; -import java.time.LocalDate; -import java.time.ZoneId; import java.util.List; import java.util.Random; @@ -25,25 +24,22 @@ public class SavingsFunctions { public static void paySavings(){ - boolean doVariableModifer = false; + boolean doVariableModifier = variableModifier > 0.0; - if (variableModifier > 0.0) { - doVariableModifer = true; - } - - if (doVariableModifer) { - minimalInterest = calcVariableModifier(minimalInterest, variableModifier); - lowInterest = calcVariableModifier(lowInterest, variableModifier); - moderateInterest = calcVariableModifier(moderateInterest, variableModifier); - highInterest = calcVariableModifier(highInterest, variableModifier); - maximumInterest = calcVariableModifier(maximumInterest, variableModifier); - baseInterest = calcVariableModifier(baseInterest, variableModifier); + if (doVariableModifier) { + minimalInterest = calcVariableModifier(minimalInterest); + lowInterest = calcVariableModifier(lowInterest); + moderateInterest = calcVariableModifier(moderateInterest); + highInterest = calcVariableModifier(highInterest); + maximumInterest = calcVariableModifier(maximumInterest); + baseInterest = calcVariableModifier(baseInterest); } List residents = TownyAPI.getInstance().getResidents(); + MessageUtil.logStatus(Translatable.of("townypay.status.log.Savings.start")); for (Resident resident: residents){ double interest = baseInterest; - if (doVariableModifer) { + if (doVariableModifier) { if (resident.hasTown()) { double balance; int residentCount; @@ -57,13 +53,12 @@ public static void paySavings(){ interest = getThresholdInterest(averageBalance,minimalInterest,lowInterest,moderateInterest,highInterest,maximumInterest); } } - if (isWithinLastOnline(resident)) { - MessageUtil.logStatus(Translatable.of("townypay.status.log.Savings.start")); + if (GeneralUtil.isWithinLastOnline(resident,SavingsSettings.getLastOnline())) { addInterest(resident, interest); moveHoldings(resident); - MessageUtil.logStatus(Translatable.of("townypay.status.log.Savings.end")); } } + MessageUtil.logStatus(Translatable.of("townypay.status.log.Savings.end")); } private static void addInterest(Resident resident, double interest){ @@ -92,9 +87,9 @@ private static void moveHoldings(Resident resident){ } } - private static double calcVariableModifier(double baseInterest, double modifier){ - double minValue = (baseInterest - modifier); - double maxValue = (baseInterest + modifier); + private static double calcVariableModifier(double baseInterest){ + double minValue = (baseInterest - SavingsFunctions.variableModifier); + double maxValue = (baseInterest + SavingsFunctions.variableModifier); // Create a random number generator Random random = new Random(); @@ -132,20 +127,4 @@ private static double getThresholdInterest(double averageBalance, double minimal return baseInterest; } } - private static boolean isWithinLastOnline(Resident resident) { - long timestamp = resident.getLastOnline(); - int daysToSubtract = SavingsSettings.getLastOnline(); - - // Calculate the date that is daysToSubtract before the current date - LocalDate currentDate = LocalDate.now(); - LocalDate targetDate = currentDate.minusDays(daysToSubtract); - - // Convert the target date to a timestamp - long targetTimestamp = targetDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli(); - - // Check if the given timestamp is within the specified range - return timestamp >= targetTimestamp; - } - - } diff --git a/src/main/java/com/tanukicraft/townypay/listeners/PlayerJoinEventListener.java b/src/main/java/com/tanukicraft/townypay/listeners/PlayerJoinEventListener.java new file mode 100644 index 0000000..66f7201 --- /dev/null +++ b/src/main/java/com/tanukicraft/townypay/listeners/PlayerJoinEventListener.java @@ -0,0 +1,28 @@ +package com.tanukicraft.townypay.listeners; + +import com.palmergames.bukkit.towny.TownyAPI; +import com.palmergames.bukkit.towny.object.Resident; +import com.palmergames.bukkit.towny.object.Translatable; +import com.tanukicraft.townypay.util.MessageCacheUtil; +import com.tanukicraft.townypay.util.MessageUtil; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class PlayerJoinEventListener implements Listener { + + @EventHandler + + public void onJoin(PlayerJoinEvent event){ + Player player = event.getPlayer(); + Resident resident = TownyAPI.getInstance().getResident(player); + + assert resident != null; + if (MessageCacheUtil.hasMsgCache(resident)){ + MessageUtil.sendMsg(player, Translatable.of("townypay.general.OfflineMsg")); + + MessageCacheUtil.sendMsgCache(resident); + } + } +} diff --git a/src/main/java/com/tanukicraft/townypay/listeners/TownyNewDayEventListener.java b/src/main/java/com/tanukicraft/townypay/listeners/TownyNewDayEventListener.java index 1aa0dd4..0f8d09e 100644 --- a/src/main/java/com/tanukicraft/townypay/listeners/TownyNewDayEventListener.java +++ b/src/main/java/com/tanukicraft/townypay/listeners/TownyNewDayEventListener.java @@ -2,16 +2,22 @@ import com.palmergames.bukkit.towny.TownyAPI; import com.palmergames.bukkit.towny.event.NewDayEvent; +import com.palmergames.bukkit.towny.object.Resident; import com.palmergames.bukkit.towny.object.Translatable; import com.tanukicraft.townypay.functions.BudgetFunctions; import com.tanukicraft.townypay.functions.PayFunctions; import com.tanukicraft.townypay.functions.SavingsFunctions; +import com.tanukicraft.townypay.settings.MessageCacheSettings; +import com.tanukicraft.townypay.util.GeneralUtil; +import com.tanukicraft.townypay.util.MessageCacheUtil; import com.tanukicraft.townypay.util.MessageUtil; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import com.tanukicraft.townypay.settings.TownSettings; import com.tanukicraft.townypay.settings.NationSettings; +import java.util.List; + public class TownyNewDayEventListener implements Listener { @EventHandler @@ -44,5 +50,19 @@ public static void onNewDay(NewDayEvent event) { } SavingsFunctions.paySavings(); + + if (MessageCacheSettings.getClearCache() != -1){ + checkResidentCacheMsg(); + } + } + + private static void checkResidentCacheMsg(){ + List residents = TownyAPI.getInstance().getResidents(); + int lastOnlineDays = MessageCacheSettings.getClearCache(); + for (Resident resident : residents){ + if(GeneralUtil.isWithinLastOnline(resident, lastOnlineDays)){ + MessageCacheUtil.clearMsgCache(resident); + } + } } } diff --git a/src/main/java/com/tanukicraft/townypay/metadata/KingPayMetaDataController.java b/src/main/java/com/tanukicraft/townypay/metadata/KingPayMetaDataController.java index 28fc720..f30aceb 100644 --- a/src/main/java/com/tanukicraft/townypay/metadata/KingPayMetaDataController.java +++ b/src/main/java/com/tanukicraft/townypay/metadata/KingPayMetaDataController.java @@ -7,7 +7,7 @@ public class KingPayMetaDataController { private static IntegerDataField payIDF = new IntegerDataField("TownyPaySetPay", 0); - private static BooleanDataField toggleBDF = new BooleanDataField("TownyPaySetPay", true); + private static BooleanDataField toggleBDF = new BooleanDataField("TownyPayToggle", true); public static boolean hasPayData(Nation nation){ return MetaDataUtil.hasMeta(nation,payIDF); diff --git a/src/main/java/com/tanukicraft/townypay/metadata/MayorPayMetaDataController.java b/src/main/java/com/tanukicraft/townypay/metadata/MayorPayMetaDataController.java index 39f3c31..1858283 100644 --- a/src/main/java/com/tanukicraft/townypay/metadata/MayorPayMetaDataController.java +++ b/src/main/java/com/tanukicraft/townypay/metadata/MayorPayMetaDataController.java @@ -6,8 +6,8 @@ import com.palmergames.bukkit.towny.utils.MetaDataUtil; public class MayorPayMetaDataController { - private static IntegerDataField payIDF = new IntegerDataField("TownyPaySetPay", 0); - private static BooleanDataField toggleBDF = new BooleanDataField("TownyPaySetPay", true); + private static IntegerDataField payIDF = new IntegerDataField("TownyPaySet", 0); + private static BooleanDataField toggleBDF = new BooleanDataField("TownyPayToggle", true); public static boolean hasPayData(Town town){ diff --git a/src/main/java/com/tanukicraft/townypay/metadata/MetaDataInit.java b/src/main/java/com/tanukicraft/townypay/metadata/MetaDataInit.java new file mode 100644 index 0000000..0fa27ab --- /dev/null +++ b/src/main/java/com/tanukicraft/townypay/metadata/MetaDataInit.java @@ -0,0 +1,57 @@ +package com.tanukicraft.townypay.metadata; + +import com.palmergames.bukkit.towny.TownyAPI; +import com.palmergames.bukkit.towny.exceptions.KeyAlreadyRegisteredException; +import com.palmergames.bukkit.towny.object.metadata.BooleanDataField; +import com.palmergames.bukkit.towny.object.metadata.IntegerDataField; +import com.palmergames.bukkit.towny.object.metadata.StringDataField; +import static org.bukkit.Bukkit.getLogger; + +public class MetaDataInit { + // Parts of a datafield + + // Use those parts to create a new data field to store an integer + private static final IntegerDataField TownyPayBudgetField = new IntegerDataField("TownyPayBudget", 0, "Budget"); + private static final IntegerDataField TownyPaySpendField = new IntegerDataField("TownyPaySpend", 0, "Spend"); + private static final IntegerDataField TownyPaySetField = new IntegerDataField("TownyPaySet", 0, "Pay"); + private static final BooleanDataField TownyPayToggleField = new BooleanDataField("TownyPayToggle", true, "Pay Toggle"); + private static final IntegerDataField TownyPaySavingsField = new IntegerDataField("TownyPaySavings", 0, "Savings"); + private static final IntegerDataField TownyPayHoldingsField = new IntegerDataField("TownyPayHoldings", 0, "Holdings"); + + public static void loadCustomDataField(){ + // (Optional) Try to globally register the data field. + // Globally registering data fields allow them to be modified in-game by administrators. + try { + TownyAPI.getInstance().registerCustomDataField(TownyPayBudgetField); + } catch (KeyAlreadyRegisteredException e) { + getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again + } + try { + TownyAPI.getInstance().registerCustomDataField(TownyPaySpendField); + } catch (KeyAlreadyRegisteredException e) { + getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again + } + try { + TownyAPI.getInstance().registerCustomDataField(TownyPaySetField); + } catch (KeyAlreadyRegisteredException e) { + getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again + } + try { + TownyAPI.getInstance().registerCustomDataField(TownyPayToggleField); + } catch (KeyAlreadyRegisteredException e) { + getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again + } + try { + TownyAPI.getInstance().registerCustomDataField(TownyPaySavingsField); + } catch (KeyAlreadyRegisteredException e) { + getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again + } + try { + TownyAPI.getInstance().registerCustomDataField(TownyPayHoldingsField); + } catch (KeyAlreadyRegisteredException e) { + getLogger().warning(e.getMessage()); // A flag with the same key name already exists try again + } + + getLogger().info("Custom data fields successfully registered!"); + } +} diff --git a/src/main/java/com/tanukicraft/townypay/settings/MessageCacheSettings.java b/src/main/java/com/tanukicraft/townypay/settings/MessageCacheSettings.java new file mode 100644 index 0000000..ee2f926 --- /dev/null +++ b/src/main/java/com/tanukicraft/townypay/settings/MessageCacheSettings.java @@ -0,0 +1,30 @@ +package com.tanukicraft.townypay.settings; + +import com.palmergames.bukkit.towny.object.Translatable; +import com.tanukicraft.townypay.TownyPay; +import com.tanukicraft.townypay.util.MessageUtil; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.Objects; + +public class MessageCacheSettings { + private static final FileConfiguration config = TownyPay.getPlugin(TownyPay.class).getConfig(); + + public static int getClearCache(){ + return config.getInt("MessageCacheSettings.ClearCache"); + } + public static int getMaxCache(){ + return config.getInt("MessageCacheSettings.MaxCache"); + } + public static String getExtraMSG(){ + String extraMSG = config.getString("MessageCacheSettings.ExtraMSG"); + + if (Objects.equals(extraMSG, "FIRST") || Objects.equals(extraMSG, "LAST") || Objects.equals(extraMSG, "BLOCK")){ + return extraMSG; + } else { + MessageUtil.logError(Translatable.of("townypay.ConfigError.MessageCacheSettings.ExtraMSG", extraMSG)); + return null; + } + } +} + diff --git a/src/main/java/com/tanukicraft/townypay/util/CustomConfig.java b/src/main/java/com/tanukicraft/townypay/util/CustomConfig.java new file mode 100644 index 0000000..a965c64 --- /dev/null +++ b/src/main/java/com/tanukicraft/townypay/util/CustomConfig.java @@ -0,0 +1,45 @@ +package com.tanukicraft.townypay.util; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; + +public class CustomConfig { + + private static File file; + private static FileConfiguration customFile; + + //Finds or generates the custom config file + public static void setup(String fileName, String exstention){ + file = new File(Bukkit.getServer().getPluginManager().getPlugin("TownyPay").getDataFolder(), fileName + "." + exstention); + + if (!file.exists()){ + try{ + file.createNewFile(); + }catch (IOException e){ + //owww + } + } + customFile = YamlConfiguration.loadConfiguration(file); + } + + public static FileConfiguration get(){ + return customFile; + } + + public static void save(){ + try{ + customFile.save(file); + }catch (IOException e){ + System.out.println("Couldn't save file"); + } + } + + public static void reload(){ + customFile = YamlConfiguration.loadConfiguration(file); + } + +} \ No newline at end of file diff --git a/src/main/java/com/tanukicraft/townypay/util/GeneralUtil.java b/src/main/java/com/tanukicraft/townypay/util/GeneralUtil.java index 1333a76..b20bd17 100644 --- a/src/main/java/com/tanukicraft/townypay/util/GeneralUtil.java +++ b/src/main/java/com/tanukicraft/townypay/util/GeneralUtil.java @@ -1,11 +1,13 @@ package com.tanukicraft.townypay.util; -import com.palmergames.bukkit.towny.TownyUniverse; import com.palmergames.bukkit.towny.exceptions.TownyException; import com.palmergames.bukkit.towny.object.Nation; import com.palmergames.bukkit.towny.object.Resident; import com.palmergames.bukkit.towny.object.Town; +import java.time.LocalDate; +import java.time.ZoneId; + public class GeneralUtil { public static boolean isNotInteger(String input) { try { @@ -44,24 +46,30 @@ public static Nation getNation(Resident resident){ public static boolean isSameTown(Town firstTown, Town secondTown){ if (firstTown == null | secondTown == null){ return false; - } else if (firstTown == secondTown){ - return true; - } else { - return false; - } + } else return firstTown == secondTown; } public static boolean isSameNation(Nation firstNation, Nation secondNation){ if (firstNation == null | secondNation == null){ return false; - } else if (firstNation == secondNation){ - return true; - } else { - return false; - } + } else return firstNation == secondNation; } public static double calcProfit (Double balance, Double upkeep, Double profitCac){ return balance - ((upkeep / 100) * profitCac); } + + public static boolean isWithinLastOnline(Resident resident, Integer days) { + long timestamp = resident.getLastOnline(); + + // Calculate the date that is daysToSubtract before the current date + LocalDate currentDate = LocalDate.now(); + LocalDate targetDate = currentDate.minusDays(days); + + // Convert the target date to a timestamp + long targetTimestamp = targetDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli(); + + // Check if the given timestamp is within the specified range + return timestamp >= targetTimestamp; + } } diff --git a/src/main/java/com/tanukicraft/townypay/util/MessageCacheUtil.java b/src/main/java/com/tanukicraft/townypay/util/MessageCacheUtil.java new file mode 100644 index 0000000..b8c41e6 --- /dev/null +++ b/src/main/java/com/tanukicraft/townypay/util/MessageCacheUtil.java @@ -0,0 +1,67 @@ +package com.tanukicraft.townypay.util; + +import com.palmergames.bukkit.towny.TownyMessaging; +import com.palmergames.bukkit.towny.object.Resident; +import com.tanukicraft.townypay.settings.MessageCacheSettings; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class MessageCacheUtil { + + + public static void cacheMsg(Resident resident, String message) { + List cache = new ArrayList<>(); + String resID = String.valueOf(resident.getUUID()); + + boolean blockMsg = false; + if (hasMsgCache(resident)) { + cache = CustomConfig.get().getStringList(resID); + if (MessageCacheSettings.getMaxCache() != -1) { + if (cache.size() == MessageCacheSettings.getMaxCache()) { + switch (Objects.requireNonNull(MessageCacheSettings.getExtraMSG())) { + case "FIRST": + cache.remove(0); + break; + case "LAST": + cache.remove(cache.size() - 1); + break; + case "BLOCK": + blockMsg = true; + break; + case "default": + break; + } + } + } + } + if (!blockMsg) { + cache.add(message); + CustomConfig.get().set(resID,cache); + CustomConfig.save(); + } + } + public static void sendMsgCache(Resident resident){ + String resID = String.valueOf(resident.getUUID()); + List msgCache = CustomConfig.get().getStringList(resID); + + for (String msg : msgCache){ + TownyMessaging.sendMsg(resident.getPlayer(), msg); + } + + clearMsgCache(resident); + } + public static void clearMsgCache(Resident resident){ + String resID = String.valueOf(resident.getUUID()); + CustomConfig.get().set(resID,null); + CustomConfig.save(); + } + + public static boolean hasMsgCache(Resident resident){ + String resID = String.valueOf(resident.getUUID()); + List cache = CustomConfig.get().getStringList(resID); + return !cache.isEmpty(); + } + +} diff --git a/src/main/java/com/tanukicraft/townypay/util/MessageUtil.java b/src/main/java/com/tanukicraft/townypay/util/MessageUtil.java index bfa5b13..1551575 100644 --- a/src/main/java/com/tanukicraft/townypay/util/MessageUtil.java +++ b/src/main/java/com/tanukicraft/townypay/util/MessageUtil.java @@ -9,6 +9,9 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + import static org.bukkit.Bukkit.getServer; public class MessageUtil { @@ -33,10 +36,29 @@ public static void sendMsg(CommandSender sender, Translatable message) { } } - public static void sendPlayerMsg(Player player, Translatable message) { - if (player != null) { - Resident res = TownyAPI.getInstance().getResident(player); - TownyMessaging.sendMsg(res, message); + public static void sendResidentMsg(Resident resident, Translatable message) { + assert resident != null; + if (resident.isOnline()) { + TownyMessaging.sendMsg(resident, message); + } else { + // Get the current date and time + LocalDateTime currentDateTime = LocalDateTime.now(); + + // Define a custom date and time format + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + // Format the date as a string + String formattedDate = currentDateTime.format(formatter); + + MessageCacheUtil.cacheMsg(resident, "[" + formattedDate + "] " + message); } } + + + public static void playerDebug(Player player, String message){ + player.sendMessage(ChatColor.YELLOW + "DEBUG: " + message); + } + public static void consoleDebug(String message){ + getServer().getConsoleSender().sendMessage(ChatColor.YELLOW + "DEBUG: " + message); + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 253b9bd..1126eb0 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -114,9 +114,9 @@ ResidentSettings: #--------------------------------------------# SavingsSettings: #Base interest used for all nomads and if TownBalanceInterest is disabled - BaseInterest: 0.1 + BaseInterest: 0.5 #Modifer to work out a variable from Min=BaseInterest-VariableModifier, and Max=BaseInterest+VariableModifier. Set to 0 to disable - VariableModifier: 0.1 + VariableModifier: 0.5 #Will check if the resident has been online within this many days to give savings interest. Prevents dead accounts receiving interest LastOnline: 5 @@ -126,9 +126,9 @@ SavingsSettings: Minimum: 100 #Maximum amount of savings allowed. Set to -1 to disable. Doesn't apply to interest payments, once the maximum has reached, interest will still continue. #This is used when transferring to holdings to ensure Savings+Holdings is doesn't go over maximum - Maximum: 10000 + Maximum: 50000 #Full cap on how much money will earn interest. Set to -1 to disable. - Cap: 50000 + Cap: 100000 Holdings: #Minimum amount to be able to transfer to holdings Minimum: 1 @@ -148,8 +148,24 @@ SavingsSettings: Maximum: 500000 #Percent interest rates for each threshold, if town does not meet threshold then BaseInterest will be used Rates: - Minimal: 0.2 - Low: 0.3 - Moderate: 0.5 - High: 0.75 - Maximum: 1.0 \ No newline at end of file + Minimal: 0.5 + Low: 0.75 + Moderate: 1.0 + High: 1.5 + Maximum: 2.0 + +#--------------------------------------------# +# Message Cache Settings # +#--------------------------------------------# +#Messages sent to offline players are cached to be sent when they log back in +MessageCacheSettings: + #Choose how long they players to be inactive before clearing cached messages, set to -1 to disable. + ClearCache: 7 + #Set max message cache, set to -1 to disable. + MaxCache: 5 + #If MaxCache is enabled, choose what method for dealing with extra messages. + #Acceptable methods: + #FIRST - Remove first message cached + #LAST - Remove last message cached + #BLOCK - Block the next messages after cache is full + ExtraMSG: 'FIRST' \ No newline at end of file diff --git a/src/main/resources/lang/en-US.yml b/src/main/resources/lang/en-US.yml index 2450649..95bf6c5 100644 --- a/src/main/resources/lang/en-US.yml +++ b/src/main/resources/lang/en-US.yml @@ -56,11 +56,13 @@ townypay.general.NotEnoughMoney: 'You do not have enough money!' townypay.general.OverBudget: 'You cannot pay more than the budget!' townypay.general.PaymentReceived: 'You received a payment from %1$s. Value: %2$s. Tax Collected: %3$s' townypay.general.PaymentSend: 'You sent a payment to %1$s. Value: %2$s' +townypay.general.OfflineMsg: 'You received the following messages while you were offline' ### Town townypay.town.PayReason: 'Mayor Pay' townypay.town.PayTaxReason: 'Mayor Pay Tax' townypay.town.PayToggled: 'Mayor Pay has been set to %s' +townypay.town.PayMsg: 'You received Mayor Pay. Value: %1$s. Tax Collected: %2$s' townypay.town.BudgetPaymentReason: '%1$s sent a Town Payment sent to %2$s' townypay.town.BudgetPaymentTaxReason: 'Tax of Town Payment from %s' townypay.town.PaySet: 'Mayor Pay has been set to %s' @@ -72,6 +74,7 @@ townypay.town.CommandFail.toggle: 'Invalid command, correct usage - /town toggle townypay.nation.PayReason: 'King Pay' townypay.nation.PayTaxReason: 'King Pay Tax' townypay.nation.PayToggled: 'King Pay has been set to %s' +townypay.nation.PayMsg: 'You received King Pay. Value: %1$s. Tax Collected: %2$s' townypay.nation.BudgetPaymentReason: 'Sent a Nation Payment sent to %s' townypay.nation.BudgetPaymentTaxReason: 'Tax of Nation Payment from %s' townypay.nation.PaySet: 'King Pay has been set to %s' @@ -88,9 +91,12 @@ townypay.Resident.Savings.Info: 'Current Savings: %s | In Holdings: %s' townypay.Resident.Savings.Deposit: 'Transferred %s into Holdings. New total: %s' townypay.Resident.Savings.Withdraw: 'Withdrew %s from Savings. New total: %s' townypay.Savings.CommandFail.Withdraw.toomuch: 'You cannot withdraw more than the total of Savings' +townypay.Savings.CommandFail.Withdraw.zero: 'You cannot withdraw 0 or less!' ### Savings townypay.Savings.InterestPayment: 'Interest Payment of %s' +townypay.Savings.Withdraw: 'Withdraw from Savings' +townypay.Savings.Deposit: 'Deposit into Holdings' townypay.Savings.CommandFail.total: 'You cannot have more than %s in Savings and Holdings combined' townypay.Savings.CommandFail.holdingsmax: 'You cannot send more than %s into Holdings' townypay.Savings.CommandFail.holdingsmin: 'You have to deposit at least %s into holdings' @@ -124,4 +130,5 @@ townypay.status.log.nation.paymentsent: 'Nation payment sent from %1$s to %2$s f townypay.status.log.Savings.InterestPayment: 'Interest payment for %s of %s from a savings balance of %s' townypay.status.log.Savings.start: 'Paying savings interest to all residents' townypay.status.log.Savings.end: 'Savings interest complete' -townypay.status.log.Savings.HoldingsTransfer: 'Moved %s of holdings for %s, new balance %s' \ No newline at end of file +townypay.status.log.Savings.HoldingsTransfer: 'Moved %s of holdings for %s, new balance %s' +townypay.ConfigError.MessageCacheSettings.ExtraMSG: 'Incorrect method in config, expected FIRST/LAST/BLOCK. Got %s' \ No newline at end of file