Nexal Taxes
A tax system for Minecraft servers: land taxes on GriefPrevention claims, wealth taxes with progressive brackets, a government treasury, debt with optional interest, evasion detection on money transfers, and cron-scheduled collections.
Features
- Land Ownership Tax - Tax per claimed block via GriefPrevention, with free blocks before taxing starts
- Wealth Tax - Tax on the balance above a threshold, with progressive brackets
- Government Treasury - Every collected tax goes into a central account that admins can deposit into and withdraw from
- Tax Debt - Unpaid taxes become debt (or are dropped, in "collect" mode). Debt can carry daily interest, is paid with
/tax pay, and can be deducted automatically at the next collection when the player has money left - Max Debt Actions - Warn the player, run commands, and optionally block money transfers once a player's debt reaches the limit
- Tax Evasion Detection - Watches transfer commands such as
/payand alerts admins about large transfers and rapid series of transfers - Cron Scheduling - Collections on any cron schedule, in a configurable timezone
- Offline Collection - Taxes are also collected from offline players
- Tax Exemptions - Exempt players by command, or by the
nexaltaxes.exemptpermission (also honoured while the player is offline) - Tax History GUI - Players browse their payment history
- Admin Panel -
/tax adminopens a hub with the settings screen, a dashboard (treasury, debt, top taxpayers, evasion alerts), forced collection, export and import - PlaceholderAPI Support - 13 placeholders for tax stats and treasury information
- Dual Storage - JSON or MySQL/MariaDB, with export and import to migrate between them
Technologies
- Java 17 with Paper API 1.20.1, Spigot compatible
- nexal-core for storage (JSON and SQL) and the help menu
- Utilizer for config, messages, tasks, and GUIs
- ACF (Aikar Command Framework) for commands
- cron4j for scheduling
- Vault for economy integration
- GriefPrevention for land claim tracking (optional)
- PlaceholderAPI for placeholders (optional)
Commands
Player Commands
| Command | Permission | Description |
|---|---|---|
/tax or /tax help [page] | nexaltaxes.use | Show the help menu |
/tax info | nexaltaxes.use | Show your estimated taxes, balance, debt and the next collection |
/tax history | nexaltaxes.history | Open your tax history GUI |
/tax pay [amount] | nexaltaxes.pay | Pay off your tax debt (all of it, or the amount given) |
/taxes is an alias of /tax.
Admin Commands
| Command | Permission | Description |
|---|---|---|
/tax admin | nexaltaxes.admin | Open the admin panel (the console gets the admin help) |
/tax admin help [page] | nexaltaxes.admin | Show the admin commands |
/tax admin alerts | nexaltaxes.admin | Open the dashboard: treasury, debt, top taxpayers and evasion alerts |
/tax admin exempt \<player> | nexaltaxes.admin | Toggle a player's tax exemption |
/tax admin treasury | nexaltaxes.admin | Show the treasury balance, total collected, last and next collection |
/tax admin treasury withdraw \<amount> | nexaltaxes.admin | Withdraw from the treasury to your balance |
/tax admin treasury deposit \<amount> | nexaltaxes.admin | Deposit from your balance into the treasury |
/tax admin collect | nexaltaxes.admin | Collect taxes from everyone now |
/tax admin reload | nexaltaxes.admin | Reload config.yml and messages.yml |
/tax admin export | nexaltaxes.admin | Export all data to plugins/NexalTaxes/export/ |
/tax admin import | nexaltaxes.admin | Import the files in plugins/NexalTaxes/export/ |
Admin Panel
/tax admin opens a hub with:
- Configuration - The settings screen, grouped into Collection (schedule, timezone, offline collection, treasury), Taxes (land and wealth rates), Debt (mode, max debt, interest, automatic deduction, max debt actions) and Evasion Detection (alerts and thresholds). Changes are saved to
config.ymlat once. Wealth brackets are shown there but edited inconfig.yml. - Dashboard - Treasury, debt overview, statistics, the top five taxpayers and the unresolved evasion alerts. Click an alert to resolve it.
- Treasury - Balance, total collected, last and next collection.
- Collect Now, Export Data and Import Data.
Permissions
| Permission | Description | Default |
|---|---|---|
nexaltaxes.use | View tax info and status | true |
nexaltaxes.history | View personal tax history | true |
nexaltaxes.pay | Pay off tax debt | true |
nexaltaxes.exempt | Exempt from all taxes | false |
nexaltaxes.admin | Access to admin commands and the admin panel | op |
nexaltaxes.alerts | Receive evasion alerts | op |
Permissions cannot be read for offline players, so for collections from offline players the plugin uses whether the player had nexaltaxes.exempt when they last left the server. A permission granted while the player is offline takes effect once they have joined and left again.
How a Collection Works
At each scheduled collection (or /tax admin collect), for every player who is not exempt:
- Interest - With
debt-interestabove 0, existing debt grows by that daily percentage for the time since the player's previous collection. - Taxes - Land tax (claimed blocks above the free blocks, times the cost per block; for offline players, the claimed blocks counted when they last joined or left) and wealth tax (by bracket) are taken from the balance. If the balance does not cover them, what is missing becomes debt in
debtmode, or is dropped incollectmode. - Older debt - With
auto-deducton, debt from earlier collections is paid from whatever balance is left. It is on by default. - Max debt - When the debt grew and has reached
max-debt, the max debt actions run.
Everything collected goes into the treasury (when enabled) and appears in the player's history.
Evasion Detection
The commands in transfer-commands (by default /pay and /epay, with or without a namespace such as /essentials:pay) are watched. A transfer counts once the sender's balance has actually dropped, so refused transfers are ignored, and it works with any economy plugin whose pay command takes \<player> \<amount>.
- A single transfer of at least
large-transfer-alertraises a Large Transfer alert. rapid-transfer-thresholdtransfers withinrapid-transfer-windowseconds raise a Rapid Transfers alert.
Alerts are logged, stored, shown on the dashboard and, with alert-admins, sent to online players with nexaltaxes.alerts.
Placeholders
Requires PlaceholderAPI. The identifier is nexaltaxes.
| Placeholder | Description |
|---|---|
%nexaltaxes_debt% | Player's current tax debt |
%nexaltaxes_total_paid% | Player's lifetime taxes paid |
%nexaltaxes_has_debt% | Whether player has debt (true/false) |
%nexaltaxes_is_exempt% | Whether player is exempted by an admin (true/false) |
%nexaltaxes_land_blocks% | Player's claimed land blocks |
%nexaltaxes_estimated_land_tax% | Estimated land tax for next collection |
%nexaltaxes_estimated_wealth_tax% | Estimated wealth tax for next collection |
%nexaltaxes_estimated_total_tax% | Total estimated tax |
%nexaltaxes_treasury_balance% | Government treasury balance |
%nexaltaxes_treasury_total% | Total taxes ever collected |
%nexaltaxes_next_collection% | Next collection date/time |
%nexaltaxes_total_taxpayers% | Total registered taxpayers |
%nexaltaxes_unresolved_alerts% | Number of unresolved evasion alerts |
Configuration
config.yml
# Storage configuration
storage:
type: json # json or sql
sql:
host: localhost
port: 3306
database: minecraft
username: root
password: ""
# Timezone the collection schedule runs in and times are shown in
timezone: "UTC"
# Cron expression for tax collection, in the timezone above
# Examples:
# "0 0 * * *" - Daily at midnight
# "0 */6 * * *" - Every 6 hours
# "0 0 * * 0" - Weekly on Sunday
collection-schedule: "0 0 * * *"
# Treasury settings
treasury:
enabled: true
starting-balance: 0.0
# Debt handling
debt-handling:
mode: debt # "debt" or "collect"
max-debt: 100000.0
debt-interest: 0.0 # daily %, 0 = off
auto-deduct: true # pay older debt from what is left at each collection
max-debt-actions:
warn: true
block-economy: false # block the transfer-commands for players at max debt
commands: [] # placeholders: %player%, %debt%, %uuid%
# How long history is kept; 0 keeps everything (the default)
retention:
tax-records-days: 0
resolved-alerts-days: 0 # unresolved alerts are always kept
# Collect from offline players
collect-offline: true
# Commands that send money as /\<command> \<player> \<amount>
transfer-commands:
- pay
- epay
# Tax settings
taxes:
land:
enabled: true
cost-per-block: 0.01
grace-blocks: 100
wealth:
enabled: true
threshold: 10000.0
brackets:
- max: 50000
rate: 1.0
- max: 100000
rate: 2.0
- max: -1
rate: 3.0
# Evasion detection (watches transfer-commands)
evasion-detection:
enabled: true
alert-admins: true
rapid-transfer-threshold: 5
rapid-transfer-window: 60
large-transfer-alert: 50000.0
messages.yml
Every message is customizable in MiniMessage format (gradients, hex colors), with optional sounds and titles. The colors are listed at the top of the file for find-and-replace. Messages added in an update are used from the plugin's defaults until you add them to your file. time-unknown, time-never and unknown-player set the texts shown when the next collection is not scheduled, before the first collection, and for a player whose name is not known.
Database Schema
With storage.type: sql these tables are created. With JSON the same collections are kept in plugins/NexalTaxes/data.json.
tax_payers
| Column | Type | Description |
|---|---|---|
| id | VARCHAR(36) | Player UUID (Primary Key) |
| name | VARCHAR(16) | Player name |
| totalPaid | DOUBLE | Lifetime taxes paid |
| debt | DOUBLE | Current unpaid debt |
| landBlocks | INT | Cached land block count |
| lastCollection | BIGINT | Last collection timestamp |
| exempt | BOOLEAN | Exempted by an admin |
| permissionExempt | BOOLEAN | Had nexaltaxes.exempt when last online |
tax_records
| Column | Type | Description |
|---|---|---|
| id | VARCHAR(36) | Record UUID (Primary Key) |
| player_id | VARCHAR(36) | Player UUID |
| tax_type | VARCHAR(20) | LAND / WEALTH / DEBT_PAYMENT |
| amount | DOUBLE | Amount paid |
| timestamp | BIGINT | Payment timestamp |
| description | VARCHAR(255) | Transaction details |
evasion_alerts
| Column | Type | Description |
|---|---|---|
| id | VARCHAR(36) | Alert UUID (Primary Key) |
| player_id | VARCHAR(36) | Suspect player UUID |
| player_name | VARCHAR(16) | Player name |
| alert_type | VARCHAR(30) | RAPID_TRANSFER / LARGE_TRANSFER |
| details | TEXT | Alert details |
| timestamp | BIGINT | Detection timestamp |
| resolved | BOOLEAN | Admin reviewed status |
| resolved_by | VARCHAR(36) | Admin who resolved |
treasury
| Column | Type | Description |
|---|---|---|
| id | VARCHAR(255) | Always treasury (Primary Key) |
| balance | DOUBLE | Current balance |
| total_collected | DOUBLE | Total ever deposited |
| last_collection_time | BIGINT | When the last collection ran |
Installation
- Place the plugin JAR in your
pluginsfolder - Ensure Vault and an economy plugin are installed
- (Optional) Install GriefPrevention for land taxes
- (Optional) Install PlaceholderAPI for placeholders
- Start/restart the server
- Configure the plugin with
/tax admin, or editconfig.ymlandmessages.yml - Use
/tax admin reloadto apply file changes (they are also picked up automatically)
Data Migration
To switch from JSON to SQL storage:
- Export current data:
/tax admin export - Change
storage.typetosqlin config.yml - Configure SQL connection settings
- Restart the server
- Import data:
/tax admin import(this also restores the treasury)
Retention
Tax records and resolved evasion alerts are kept forever by default. Set retention.tax-records-days or retention.resolved-alerts-days to remove older entries; they are removed at startup and after each collection. Unresolved alerts are never removed.