From af59d5beb51a03a620cf7215207e416f469a3487 Mon Sep 17 00:00:00 2001 From: Xelara Networks Date: Sat, 20 Jun 2026 12:00:25 -0400 Subject: [PATCH] first commit --- README.md | 0 pom.xml | 59 ++ .../com/bitnix/dirttaxes/DirtTaxesPlugin.java | 630 ++++++++++++++++++ .../com/bitnix/dirttaxes/TaxJoinListener.java | 19 + src/main/resources/config.yml | 132 ++++ src/main/resources/plugin.yml | 29 + target/DirtTaxes.jar | Bin 0 -> 14517 bytes .../bitnix/dirttaxes/DirtTaxesPlugin.class | Bin 0 -> 19560 bytes .../bitnix/dirttaxes/TaxJoinListener.class | Bin 0 -> 939 bytes target/classes/config.yml | 132 ++++ target/classes/plugin.yml | 29 + target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 2 + .../compile/default-compile/inputFiles.lst | 2 + 14 files changed, 1039 insertions(+) create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/com/bitnix/dirttaxes/DirtTaxesPlugin.java create mode 100644 src/main/java/com/bitnix/dirttaxes/TaxJoinListener.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/plugin.yml create mode 100644 target/DirtTaxes.jar create mode 100644 target/classes/com/bitnix/dirttaxes/DirtTaxesPlugin.class create mode 100644 target/classes/com/bitnix/dirttaxes/TaxJoinListener.class create mode 100644 target/classes/config.yml create mode 100644 target/classes/plugin.yml create mode 100644 target/maven-archiver/pom.properties create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7f0f19f --- /dev/null +++ b/pom.xml @@ -0,0 +1,59 @@ + + 4.0.0 + + com.bitnix + DirtTaxes + 1.0.0 + jar + + DirtTaxes + Offline balance tax plugin for Paper 1.21.x + + + 21 + UTF-8 + 21 + + + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + jitpack.io + https://jitpack.io + + + + + + io.papermc.paper + paper-api + 1.21.1-R0.1-SNAPSHOT + provided + + + + com.github.MilkBowl + VaultAPI + 1.7 + provided + + + + + DirtTaxes + + + maven-compiler-plugin + 3.13.0 + + 21 + + + + + diff --git a/src/main/java/com/bitnix/dirttaxes/DirtTaxesPlugin.java b/src/main/java/com/bitnix/dirttaxes/DirtTaxesPlugin.java new file mode 100644 index 0000000..f03b4c6 --- /dev/null +++ b/src/main/java/com/bitnix/dirttaxes/DirtTaxesPlugin.java @@ -0,0 +1,630 @@ +package com.bitnix.dirttaxes; + +import net.milkbowl.vault.economy.Economy; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.TabExecutor; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitTask; + +import java.io.File; +import java.io.IOException; +import java.text.DecimalFormat; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import java.util.UUID; + +public class DirtTaxesPlugin extends JavaPlugin implements TabExecutor { + + private Economy economy; + private BukkitTask taxTask; + private File pendingFile; + private FileConfiguration pendingConfig; + private final Random random = new Random(); + private final DecimalFormat moneyFormat = new DecimalFormat("0.00"); + + @Override + public void onEnable() { + saveDefaultConfig(); + createDataFiles(); + + if (!setupEconomy()) { + getLogger().severe("Vault economy provider not found. Disabling DirtTaxes."); + getServer().getPluginManager().disablePlugin(this); + return; + } + + PluginCommand command = getCommand("dirttaxes"); + if (command != null) { + command.setExecutor(this); + command.setTabCompleter(this); + } + + scheduleTaxTask(); + registerJoinListener(); + + if (getConfig().getBoolean("tax.startup-tax-check", false)) { + long delayTicks = Math.max(20L, getConfig().getLong("tax.startup-tax-delay-seconds", 30L) * 20L); + Bukkit.getScheduler().runTaskLater(this, this::runOfflineTaxCycle, delayTicks); + } + + getLogger().info("DirtTaxes enabled."); + } + + @Override + public void onDisable() { + if (taxTask != null) { + taxTask.cancel(); + taxTask = null; + } + + Bukkit.getScheduler().cancelTasks(this); + savePendingMessages(); + } + + private void createDataFiles() { + if (!getDataFolder().exists()) { + getDataFolder().mkdirs(); + } + + pendingFile = new File(getDataFolder(), "pending_messages.yml"); + if (!pendingFile.exists()) { + try { + pendingFile.createNewFile(); + } catch (IOException e) { + getLogger().severe("Could not create pending_messages.yml"); + e.printStackTrace(); + } + } + + pendingConfig = YamlConfiguration.loadConfiguration(pendingFile); + } + + private boolean setupEconomy() { + RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Economy.class); + if (rsp == null) { + return false; + } + economy = rsp.getProvider(); + return economy != null; + } + + private void scheduleTaxTask() { + if (taxTask != null) { + taxTask.cancel(); + taxTask = null; + } + + long intervalMinutes = Math.max(1L, getConfig().getLong("tax.interval-minutes", 1440L)); + long intervalTicks = intervalMinutes * 60L * 20L; + + taxTask = Bukkit.getScheduler().runTaskTimer(this, this::runOfflineTaxCycle, intervalTicks, intervalTicks); + } + + private void registerJoinListener() { + getServer().getPluginManager().registerEvents(new TaxJoinListener(this), this); + } + + public void handleJoin(Player player) { + String path = "players." + player.getUniqueId(); + if (!pendingConfig.contains(path)) { + return; + } + + List messages = pendingConfig.getStringList(path + ".messages"); + for (String message : messages) { + player.sendMessage(color(message)); + } + + pendingConfig.set(path, null); + savePendingMessages(); + } + + private void runOfflineTaxCycle() { + if (!getConfig().getBoolean("tax.enabled", true)) { + return; + } + + long requiredOfflineMinutes = Math.max(0L, getConfig().getLong("tax.offline-minutes-required", 1440L)); + long requiredOfflineMillis = requiredOfflineMinutes * 60L * 1000L; + + for (OfflinePlayer offlinePlayer : Bukkit.getOfflinePlayers()) { + if (offlinePlayer == null) { + continue; + } + + if (offlinePlayer.isOnline()) { + continue; + } + + if (offlinePlayer.getName() == null) { + continue; + } + + long lastPlayed = offlinePlayer.getLastPlayed(); + if (lastPlayed <= 0L) { + continue; + } + + long offlineFor = Instant.now().toEpochMilli() - lastPlayed; + if (offlineFor < requiredOfflineMillis) { + continue; + } + + taxSinglePlayer(offlinePlayer, true); + } + + if (getConfig().getBoolean("storage.save-pending-messages", true)) { + savePendingMessages(); + } + } + + private boolean taxSinglePlayer(OfflinePlayer offlinePlayer, boolean automatic) { + if (offlinePlayer == null || offlinePlayer.getName() == null) { + return false; + } + + if (getConfig().getBoolean("tax.permission-exempt", true) && offlinePlayer.isOnline()) { + Player player = offlinePlayer.getPlayer(); + if (player != null && player.hasPermission("dirttaxes.exempt")) { + return false; + } + } + + if (getConfig().getBoolean("tax.op-exempt", true) && offlinePlayer.isOp()) { + return false; + } + + double balance = economy.getBalance(offlinePlayer); + double oldBalance = balance; + double newBalance = balance; + double amountTaken = 0.0; + + double maxDebt = getConfig().getDouble("tax.max-debt", -1000000.0); + if (maxDebt > 0) { + maxDebt = 0; + } + + if (balance > 0) { + double ratePercent = Math.max(0.0, getConfig().getDouble("tax.rate-percent", 5.0)); + double minimumTax = Math.max(0.0, getConfig().getDouble("tax.minimum-tax", 1.0)); + amountTaken = Math.max(minimumTax, balance * (ratePercent / 100.0)); + + if (amountTaken > balance) { + amountTaken = balance; + } + + if (getConfig().getBoolean("tax.round-to-cents", true)) { + amountTaken = roundMoney(amountTaken); + } + + if (amountTaken <= 0.0) { + return false; + } + + economy.withdrawPlayer(offlinePlayer, amountTaken); + newBalance = economy.getBalance(offlinePlayer); + + if (sameMoney(oldBalance, newBalance)) { + return false; + } + + amountTaken = Math.abs(oldBalance - newBalance); + } else if (getConfig().getBoolean("debt.enabled", true)) { + double interestPercent = Math.max(0.0, getConfig().getDouble("debt.negative-interest-percent", 2.5)); + double flatDebt = Math.max(0.0, getConfig().getDouble("debt.flat-debt-per-cycle", 500.0)); + + double debtIncrease = Math.abs(balance) * (interestPercent / 100.0) + flatDebt; + if (getConfig().getBoolean("tax.round-to-cents", true)) { + debtIncrease = roundMoney(debtIncrease); + } + + if (debtIncrease <= 0.0) { + return false; + } + + double targetBalance = balance - debtIncrease; + if (targetBalance < maxDebt) { + targetBalance = maxDebt; + } + + amountTaken = Math.abs(targetBalance - balance); + if (amountTaken <= 0.0) { + return false; + } + + boolean usedCommand = getConfig().getBoolean("debt.use-command-for-debt", true); + + if (usedCommand) { + runDebtCommand(offlinePlayer, amountTaken, balance, targetBalance); + newBalance = economy.getBalance(offlinePlayer); + + if (sameMoney(oldBalance, newBalance)) { + if (getConfig().getBoolean("debt.warn-if-command-fails", true)) { + String fail = prefixed("messages.debt-command-failed").replace("%player%", offlinePlayer.getName()); + Bukkit.getConsoleSender().sendMessage(fail); + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.hasPermission("dirttaxes.notify")) { + online.sendMessage(fail); + } + } + } + return false; + } + + amountTaken = Math.abs(oldBalance - newBalance); + } else { + economy.withdrawPlayer(offlinePlayer, amountTaken); + newBalance = economy.getBalance(offlinePlayer); + + if (sameMoney(oldBalance, newBalance)) { + return false; + } + + amountTaken = Math.abs(oldBalance - newBalance); + } + } else { + return false; + } + + if (getConfig().getBoolean("announcement.enabled", true)) { + announceTax(offlinePlayer, amountTaken, newBalance); + } + + if (automatic && getConfig().getBoolean("announcement.store-offline-message", true)) { + storeJoinMessages(offlinePlayer.getUniqueId(), amountTaken, newBalance); + } + + if (newBalance < 0 && getConfig().getBoolean("debt.announce-debt", true)) { + String debtMsg = getConfig().getString("debt.debt-message", "&4IRS &8- &c%player% is now drowning in debt. Balance: $%new_balance%"); + debtMsg = debtMsg.replace("%player%", offlinePlayer.getName()) + .replace("%new_balance%", formatMoney(newBalance)); + broadcastToStaffAndConsole(color(debtMsg)); + } + + return true; + } + + private void runDebtCommand(OfflinePlayer offlinePlayer, double amountTaken, double oldBalance, double targetBalance) { + String raw = getConfig().getString("debt.command", "cmi money set %player% %new_balance%"); + if (raw == null || raw.isBlank()) { + return; + } + + String command = raw + .replace("%player%", offlinePlayer.getName()) + .replace("%amount%", formatMoney(amountTaken)) + .replace("%old_balance%", formatMoney(oldBalance)) + .replace("%new_balance%", formatMoney(targetBalance)); + + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command); + } + + private void announceTax(OfflinePlayer offlinePlayer, double amountTaken, double newBalance) { + String message = getConfig().getString("announcement.staff-message", + "&6IRS &8- &eTook &c$%amount% &efrom &b%player%&e. New balance: &a$%new_balance%"); + + message = message.replace("%amount%", formatMoney(amountTaken)) + .replace("%player%", offlinePlayer.getName()) + .replace("%new_balance%", formatMoney(newBalance)); + + broadcastToStaffAndConsole(color(message)); + + if (getConfig().getBoolean("fun.enabled", true) + && getConfig().getBoolean("fun.include-funny-line-in-staff-alerts", true) + && getConfig().getBoolean("fun.random-funny-lines", true)) { + String funny = getRandomFunnyLine(); + if (funny != null && !funny.isBlank()) { + broadcastToStaffAndConsole(color(funny)); + } + } + } + + private void broadcastToStaffAndConsole(String message) { + if (getConfig().getBoolean("announcement.notify-online-staff", true)) { + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission("dirttaxes.notify")) { + player.sendMessage(message); + } + } + } + + if (getConfig().getBoolean("announcement.log-to-console", true)) { + Bukkit.getConsoleSender().sendMessage(message); + } + } + + private void storeJoinMessages(UUID uuid, double amountTaken, double newBalance) { + String path = "players." + uuid + ".messages"; + List messages = pendingConfig.getStringList(path); + + String joinMessage = getConfig().getString("announcement.join-message", + "&6IRS &8- &eTook &c$%amount% &efrom your account while you were away. New balance: &a$%new_balance%"); + joinMessage = joinMessage.replace("%amount%", formatMoney(amountTaken)) + .replace("%new_balance%", formatMoney(newBalance)); + + messages.add(joinMessage); + + if (getConfig().getBoolean("fun.enabled", true) + && getConfig().getBoolean("fun.send-funny-line-to-player", true) + && getConfig().getBoolean("fun.random-funny-lines", true)) { + String funny = getRandomFunnyLine(); + if (funny != null && !funny.isBlank()) { + messages.add(funny); + } + } + + pendingConfig.set(path, messages); + } + + private String getRandomFunnyLine() { + List lines = getConfig().getStringList("fun.funny-lines"); + if (lines.isEmpty()) { + return null; + } + return lines.get(random.nextInt(lines.size())); + } + + private void savePendingMessages() { + if (pendingConfig == null || pendingFile == null) { + return; + } + + try { + pendingConfig.save(pendingFile); + } catch (IOException e) { + getLogger().severe("Failed to save pending_messages.yml"); + e.printStackTrace(); + } + } + + private double roundMoney(double amount) { + return Math.round(amount * 100.0D) / 100.0D; + } + + private boolean sameMoney(double a, double b) { + return Math.abs(a - b) < 0.009D; + } + + private String formatMoney(double amount) { + return moneyFormat.format(amount); + } + + private String color(String text) { + return ChatColor.translateAlternateColorCodes('&', text); + } + + private String msg(String path) { + return color(getConfig().getString(path, path)); + } + + private String prefixed(String path) { + return msg("messages.prefix") + msg(path); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (args.length == 0) { + if (!sender.hasPermission("dirttaxes.use")) { + sender.sendMessage(prefixed("messages.no-permission")); + return true; + } + + String info = msg("messages.info") + .replace("%rate%", String.valueOf(getConfig().getDouble("tax.rate-percent", 5.0))) + .replace("%interval%", String.valueOf(getConfig().getLong("tax.interval-minutes", 1440L))) + .replace("%maxdebt%", formatMoney(getConfig().getDouble("tax.max-debt", -1000000.0))); + sender.sendMessage(msg("messages.prefix") + info); + sender.sendMessage(msg("messages.prefix") + msg("messages.usage")); + return true; + } + + String sub = args[0].toLowerCase(Locale.ROOT); + + switch (sub) { + case "reload" -> { + if (!sender.hasPermission("dirttaxes.reload")) { + sender.sendMessage(prefixed("messages.no-permission")); + return true; + } + + reloadConfig(); + scheduleTaxTask(); + sender.sendMessage(prefixed("messages.reloaded")); + return true; + } + + case "info" -> { + if (!sender.hasPermission("dirttaxes.use")) { + sender.sendMessage(prefixed("messages.no-permission")); + return true; + } + + String info = msg("messages.info") + .replace("%rate%", String.valueOf(getConfig().getDouble("tax.rate-percent", 5.0))) + .replace("%interval%", String.valueOf(getConfig().getLong("tax.interval-minutes", 1440L))) + .replace("%maxdebt%", formatMoney(getConfig().getDouble("tax.max-debt", -1000000.0))); + sender.sendMessage(msg("messages.prefix") + info); + return true; + } + + case "setrate" -> { + if (!sender.hasPermission("dirttaxes.setrate")) { + sender.sendMessage(prefixed("messages.no-permission")); + return true; + } + + if (args.length < 2) { + sender.sendMessage(prefixed("messages.usage")); + return true; + } + + Double value = parseDouble(args[1]); + if (value == null) { + sender.sendMessage(prefixed("messages.invalid-number")); + return true; + } + + if (value < 0) { + sender.sendMessage(prefixed("messages.rate-must-be-nonnegative")); + return true; + } + + getConfig().set("tax.rate-percent", value); + saveConfig(); + sender.sendMessage(prefixed("messages.set-rate-success").replace("%rate%", String.valueOf(value))); + return true; + } + + case "setmaxdebt" -> { + if (!sender.hasPermission("dirttaxes.setmaxdebt")) { + sender.sendMessage(prefixed("messages.no-permission")); + return true; + } + + if (args.length < 2) { + sender.sendMessage(prefixed("messages.usage")); + return true; + } + + Double value = parseDouble(args[1]); + if (value == null) { + sender.sendMessage(prefixed("messages.invalid-number")); + return true; + } + + if (value > 0) { + sender.sendMessage(prefixed("messages.max-debt-must-be-negative")); + return true; + } + + getConfig().set("tax.max-debt", value); + saveConfig(); + sender.sendMessage(prefixed("messages.set-max-debt-success").replace("%maxdebt%", formatMoney(value))); + return true; + } + + case "setinterval" -> { + if (!sender.hasPermission("dirttaxes.setinterval")) { + sender.sendMessage(prefixed("messages.no-permission")); + return true; + } + + if (args.length < 2) { + sender.sendMessage(prefixed("messages.usage")); + return true; + } + + Long value = parseLong(args[1]); + if (value == null) { + sender.sendMessage(prefixed("messages.invalid-number")); + return true; + } + + if (value <= 0) { + sender.sendMessage(prefixed("messages.interval-must-be-positive")); + return true; + } + + getConfig().set("tax.interval-minutes", value); + saveConfig(); + scheduleTaxTask(); + sender.sendMessage(prefixed("messages.set-interval-success").replace("%interval%", String.valueOf(value))); + return true; + } + + case "forcetax" -> { + if (!sender.hasPermission("dirttaxes.forcetax")) { + sender.sendMessage(prefixed("messages.no-permission")); + return true; + } + + if (args.length >= 2) { + OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]); + if (target == null || target.getName() == null) { + sender.sendMessage(prefixed("messages.player-not-found")); + return true; + } + + taxSinglePlayer(target, true); + sender.sendMessage(prefixed("messages.force-tax-player-success").replace("%player%", target.getName())); + return true; + } + + runOfflineTaxCycle(); + sender.sendMessage(prefixed("messages.force-tax-all-success")); + return true; + } + + default -> { + sender.sendMessage(prefixed("messages.usage")); + return true; + } + } + } + + private Double parseDouble(String input) { + try { + return Double.parseDouble(input); + } catch (NumberFormatException ignored) { + return null; + } + } + + private Long parseLong(String input) { + try { + return Long.parseLong(input); + } catch (NumberFormatException ignored) { + return null; + } + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (args.length == 1) { + List subs = new ArrayList<>(); + subs.add("reload"); + subs.add("info"); + subs.add("setrate"); + subs.add("setmaxdebt"); + subs.add("setinterval"); + subs.add("forcetax"); + return partial(args[0], subs); + } + + if (args.length == 2 && args[0].equalsIgnoreCase("forcetax")) { + List names = new ArrayList<>(); + for (OfflinePlayer player : Bukkit.getOfflinePlayers()) { + if (player.getName() != null) { + names.add(player.getName()); + } + } + return partial(args[1], names); + } + + return Collections.emptyList(); + } + + private List partial(String input, List options) { + String lower = input.toLowerCase(Locale.ROOT); + List matches = new ArrayList<>(); + for (String option : options) { + if (option.toLowerCase(Locale.ROOT).startsWith(lower)) { + matches.add(option); + } + } + return matches; + } +} diff --git a/src/main/java/com/bitnix/dirttaxes/TaxJoinListener.java b/src/main/java/com/bitnix/dirttaxes/TaxJoinListener.java new file mode 100644 index 0000000..2360510 --- /dev/null +++ b/src/main/java/com/bitnix/dirttaxes/TaxJoinListener.java @@ -0,0 +1,19 @@ +package com.bitnix.dirttaxes; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class TaxJoinListener implements Listener { + + private final DirtTaxesPlugin plugin; + + public TaxJoinListener(DirtTaxesPlugin plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + plugin.handleJoin(event.getPlayer()); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..d400008 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,132 @@ +tax: + enabled: true + + # How often offline players are taxed, in minutes. + # 1440 = once per day + interval-minutes: 1440 + + # Percentage of current balance removed each tax cycle. + # 5.0 = 5% + rate-percent: 5.0 + + # Minimum tax amount if balance is above 0 + minimum-tax: 1.0 + + # Maximum debt allowed. Once a player reaches this negative value, + # no more debt is added. + max-debt: -1000000.0 + + # Players offline at least this many minutes before they can be taxed + offline-minutes-required: 1440 + + # If true, tax all offline players on plugin startup after delay + startup-tax-check: false + + # Delay in seconds before startup tax check runs + startup-tax-delay-seconds: 30 + + # If true, players with exempt permission bypass taxes + permission-exempt: true + + # If true, OP players bypass taxes + op-exempt: true + + # If true, tax amount is rounded to 2 decimals + round-to-cents: true + +announcement: + enabled: true + + # Broadcast to online players with dirttaxes.notify + notify-online-staff: true + + # Also send to console + log-to-console: true + + # Message for staff/console announcements + staff-message: "&6IRS &8- &eTook &c$%amount% &efrom &b%player%&e. New balance: &a$%new_balance%" + + # Optional message sent to the player next time they join + store-offline-message: true + join-message: "&6IRS &8- &eTook &c$%amount% &efrom your account while you were away. New balance: &a$%new_balance%" + +debt: + enabled: true + + # If player balance is 0 or below, add this flat debt per cycle + flat-debt-per-cycle: 500.0 + + # Funny extra interest percent applied to negative balances + # Example: -1000 with 2.5% becomes -1025 before flat debt + negative-interest-percent: 2.5 + + # IMPORTANT: + # When true, negative/debt taxation uses a configurable console command + # instead of Vault withdrawPlayer(). + # + # Use this for economies like CMI where Vault may not push balances below 0. + use-command-for-debt: true + + # Console command used to apply debt. + # Placeholders: + # %player% = player name + # %amount% = amount being added as debt this cycle + # %old_balance% = previous balance + # %new_balance% = expected new balance after tax + # + # Examples you can try on your server: + # command: "cmi money take %player% %amount%" + # command: "money take %player% %amount%" + # command: "cmi money set %player% %new_balance%" + command: "cmi money set %player% %new_balance%" + + # If true, announce/log if command mode fails to actually change balance + warn-if-command-fails: true + + # Broadcast when someone falls deeper into debt + announce-debt: true + debt-message: "&4IRS &8- &c%player% is now drowning in debt. Balance: $%new_balance%" + +fun: + enabled: true + + # Send a random funny IRS line to the player on join if they were taxed offline + send-funny-line-to-player: true + + # Also include funny line in staff alerts + include-funny-line-in-staff-alerts: true + + random-funny-lines: true + funny-lines: + - "&7[IRS] Asset seizure successful." + - "&7[IRS] Another loyal citizen has contributed." + - "&7[IRS] Freedom isn’t free." + - "&7[IRS] Thank you for your involuntary donation." + - "&7[IRS] The tax goblin smiles." + - "&7[IRS] Audit complete. Wallet lighter." + - "&7[IRS] Your wallet has been selected for routine maintenance." + - "&7[IRS] We noticed you looked too comfortable." + - "&7[IRS] This concludes your surprise patriotism fee." + - "&7[IRS] You were fined for excessive wealth retention." + +storage: + # Save pending join notifications to disk every tax cycle + save-pending-messages: true + +messages: + prefix: "&6[DirtTaxes] &r" + no-permission: "&cYou do not have permission." + reloaded: "&aDirtTaxes reloaded." + usage: "&e/dirttaxes reload|info|setrate |setmaxdebt |setinterval |forcetax [player]" + info: "&eTax rate: &f%rate%% &8| &eInterval: &f%interval%m &8| &eMax debt: &f$%maxdebt%" + set-rate-success: "&aTax rate set to &f%rate%%" + set-max-debt-success: "&aMax debt set to &f$%maxdebt%" + set-interval-success: "&aTax interval set to &f%interval% minutes" + force-tax-all-success: "&aForced tax cycle for all offline players." + force-tax-player-success: "&aForced tax for &f%player%" + player-not-found: "&cPlayer not found." + invalid-number: "&cInvalid number." + max-debt-must-be-negative: "&cMax debt must be 0 or less." + interval-must-be-positive: "&cInterval must be greater than 0." + rate-must-be-nonnegative: "&cTax rate must be 0 or greater." + debt-command-failed: "&cDebt command did not appear to change %player%'s balance." diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..13e26d8 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,29 @@ +name: DirtTaxes +version: 1.0.0 +main: com.bitnix.dirttaxes.DirtTaxesPlugin +api-version: '1.21' +depend: [Vault] + +commands: + dirttaxes: + description: Main DirtTaxes command + usage: /dirttaxes + aliases: [taxes, irs] + +permissions: + dirttaxes.use: + default: op + dirttaxes.reload: + default: op + dirttaxes.setrate: + default: op + dirttaxes.setmaxdebt: + default: op + dirttaxes.setinterval: + default: op + dirttaxes.forcetax: + default: op + dirttaxes.exempt: + default: false + dirttaxes.notify: + default: op diff --git a/target/DirtTaxes.jar b/target/DirtTaxes.jar new file mode 100644 index 0000000000000000000000000000000000000000..dd5093eaac6a84953d3f8bbb36c71570dd631161 GIT binary patch literal 14517 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 literal 0 HcmV?d00001 diff --git a/target/classes/config.yml b/target/classes/config.yml new file mode 100644 index 0000000..d400008 --- /dev/null +++ b/target/classes/config.yml @@ -0,0 +1,132 @@ +tax: + enabled: true + + # How often offline players are taxed, in minutes. + # 1440 = once per day + interval-minutes: 1440 + + # Percentage of current balance removed each tax cycle. + # 5.0 = 5% + rate-percent: 5.0 + + # Minimum tax amount if balance is above 0 + minimum-tax: 1.0 + + # Maximum debt allowed. Once a player reaches this negative value, + # no more debt is added. + max-debt: -1000000.0 + + # Players offline at least this many minutes before they can be taxed + offline-minutes-required: 1440 + + # If true, tax all offline players on plugin startup after delay + startup-tax-check: false + + # Delay in seconds before startup tax check runs + startup-tax-delay-seconds: 30 + + # If true, players with exempt permission bypass taxes + permission-exempt: true + + # If true, OP players bypass taxes + op-exempt: true + + # If true, tax amount is rounded to 2 decimals + round-to-cents: true + +announcement: + enabled: true + + # Broadcast to online players with dirttaxes.notify + notify-online-staff: true + + # Also send to console + log-to-console: true + + # Message for staff/console announcements + staff-message: "&6IRS &8- &eTook &c$%amount% &efrom &b%player%&e. New balance: &a$%new_balance%" + + # Optional message sent to the player next time they join + store-offline-message: true + join-message: "&6IRS &8- &eTook &c$%amount% &efrom your account while you were away. New balance: &a$%new_balance%" + +debt: + enabled: true + + # If player balance is 0 or below, add this flat debt per cycle + flat-debt-per-cycle: 500.0 + + # Funny extra interest percent applied to negative balances + # Example: -1000 with 2.5% becomes -1025 before flat debt + negative-interest-percent: 2.5 + + # IMPORTANT: + # When true, negative/debt taxation uses a configurable console command + # instead of Vault withdrawPlayer(). + # + # Use this for economies like CMI where Vault may not push balances below 0. + use-command-for-debt: true + + # Console command used to apply debt. + # Placeholders: + # %player% = player name + # %amount% = amount being added as debt this cycle + # %old_balance% = previous balance + # %new_balance% = expected new balance after tax + # + # Examples you can try on your server: + # command: "cmi money take %player% %amount%" + # command: "money take %player% %amount%" + # command: "cmi money set %player% %new_balance%" + command: "cmi money set %player% %new_balance%" + + # If true, announce/log if command mode fails to actually change balance + warn-if-command-fails: true + + # Broadcast when someone falls deeper into debt + announce-debt: true + debt-message: "&4IRS &8- &c%player% is now drowning in debt. Balance: $%new_balance%" + +fun: + enabled: true + + # Send a random funny IRS line to the player on join if they were taxed offline + send-funny-line-to-player: true + + # Also include funny line in staff alerts + include-funny-line-in-staff-alerts: true + + random-funny-lines: true + funny-lines: + - "&7[IRS] Asset seizure successful." + - "&7[IRS] Another loyal citizen has contributed." + - "&7[IRS] Freedom isn’t free." + - "&7[IRS] Thank you for your involuntary donation." + - "&7[IRS] The tax goblin smiles." + - "&7[IRS] Audit complete. Wallet lighter." + - "&7[IRS] Your wallet has been selected for routine maintenance." + - "&7[IRS] We noticed you looked too comfortable." + - "&7[IRS] This concludes your surprise patriotism fee." + - "&7[IRS] You were fined for excessive wealth retention." + +storage: + # Save pending join notifications to disk every tax cycle + save-pending-messages: true + +messages: + prefix: "&6[DirtTaxes] &r" + no-permission: "&cYou do not have permission." + reloaded: "&aDirtTaxes reloaded." + usage: "&e/dirttaxes reload|info|setrate |setmaxdebt |setinterval |forcetax [player]" + info: "&eTax rate: &f%rate%% &8| &eInterval: &f%interval%m &8| &eMax debt: &f$%maxdebt%" + set-rate-success: "&aTax rate set to &f%rate%%" + set-max-debt-success: "&aMax debt set to &f$%maxdebt%" + set-interval-success: "&aTax interval set to &f%interval% minutes" + force-tax-all-success: "&aForced tax cycle for all offline players." + force-tax-player-success: "&aForced tax for &f%player%" + player-not-found: "&cPlayer not found." + invalid-number: "&cInvalid number." + max-debt-must-be-negative: "&cMax debt must be 0 or less." + interval-must-be-positive: "&cInterval must be greater than 0." + rate-must-be-nonnegative: "&cTax rate must be 0 or greater." + debt-command-failed: "&cDebt command did not appear to change %player%'s balance." diff --git a/target/classes/plugin.yml b/target/classes/plugin.yml new file mode 100644 index 0000000..13e26d8 --- /dev/null +++ b/target/classes/plugin.yml @@ -0,0 +1,29 @@ +name: DirtTaxes +version: 1.0.0 +main: com.bitnix.dirttaxes.DirtTaxesPlugin +api-version: '1.21' +depend: [Vault] + +commands: + dirttaxes: + description: Main DirtTaxes command + usage: /dirttaxes + aliases: [taxes, irs] + +permissions: + dirttaxes.use: + default: op + dirttaxes.reload: + default: op + dirttaxes.setrate: + default: op + dirttaxes.setmaxdebt: + default: op + dirttaxes.setinterval: + default: op + dirttaxes.forcetax: + default: op + dirttaxes.exempt: + default: false + dirttaxes.notify: + default: op diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties new file mode 100644 index 0000000..05fe83b --- /dev/null +++ b/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Sat Jun 20 11:12:19 EDT 2026 +artifactId=DirtTaxes +groupId=com.bitnix +version=1.0.0 diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..6ba8c26 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,2 @@ +com/bitnix/dirttaxes/TaxJoinListener.class +com/bitnix/dirttaxes/DirtTaxesPlugin.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..e184c55 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,2 @@ +/home/bitnix/Desktop/DirtTaxes/src/main/java/com/bitnix/dirttaxes/DirtTaxesPlugin.java +/home/bitnix/Desktop/DirtTaxes/src/main/java/com/bitnix/dirttaxes/TaxJoinListener.java