HideoutiaPlugin
A single Paper plugin that runs an entire server economy and government: monthly Prime Minister elections with config-defined parties, sign shops, government-owned land (sold and leased through WorldGuard regions), four PM-settable taxes feeding a government ledger, gameplay income, and a mail system tying it together.
This is a server-admin guide. For the architecture and design rationale see
ImplementationPlan.md and DesignDoc.txt.
Requirements
- Paper 26.1.2 (
api-version: 26.1) β Spigot/Bukkit are not supported. - Java 25 runtime.
- Vault plus an economy provider plugin (e.g. EssentialsX) β Vault holds player balances; this plugin only routes money through it.
- WorldGuard 7.x, built for the server's Minecraft version β required for land regions.
Both Vault and WorldGuard are hard dependencies (depend in plugin.yml); the plugin will not
enable without them.
- DiscordSRV (optional,
softdepend) β only needed for the Discord PM-role sync (see Discord integration). Everything else works identically without it. - LuckPerms (optional,
softdepend) β only needed for the LuckPerms PM-group sync (see LuckPerms integration). Everything else works identically without it.
Install
- Drop
hideoutia-plugin-<version>.jarintoplugins/alongside Vault, an economy plugin, and WorldGuard. - Start the server once to generate
plugins/HideoutiaPlugin/config.ymland the SQLite database (data.db). - Edit
config.ymlto taste and run/hideoutia admin reload(no restart needed for most settings β see Config reload below).
Upgrading from DansGovernance? The data folder moved from
plugins/DansGovernance/toplugins/HideoutiaPlugin/. Rename the folder before first start to keep your existing SQLite database (andconfig.yml); otherwise the plugin generates a fresh, empty one.
Storage is SQLite, created in the plugin data folder; sqlite-jdbc is shaded and relocated, so
it never clashes with other plugins.
Configuration overview
All money values in config.yml are in normal currency units (e.g. 12.50); the plugin
converts to integer cents internally. Tax percents convert to basis points.
Displayed amounts are prefixed with economy.currencySymbol / startingBalance (default $) and grouped US-style
($1,234,567.89) regardless of the server's JVM locale; only the symbol is configurable. Negatives
read as -$123.45. Tight sign lines drop the .00 on whole amounts ($1,234).
| Section | Purpose |
|---|---|
timezone |
Timezone for all election calendar math (1st-of-month, power transfer). |
database.file |
SQLite filename inside the plugin data folder. |
economy.currencySymbol |
Symbol prefixed to all displayed money (default $). |
tax.* |
Default tax percents β seed only for a fresh DB; PM-set values win afterwards. |
parties.* |
Joinable parties: key is the join id, displayName is the label. |
election.pm.* |
PM election start day, vote duration, power-transfer day, optional test cadence. |
election.pm.snap.* |
Snap-election petition: triggerPercent of the playerbase (default 10%) and minPlayerbase (default 50) below which the public petition is disabled. |
election.party.* |
Party-leadership trigger threshold, candidacy window, vote duration. |
income.* |
Gameplay payout rates per category with an all fallback (seed only for a fresh DB; PM-set rates win afterwards), the anti-exploit/behaviour toggles excludePlayerPlaced, excludeSpawners, showActionbar (these stay config-driven and hot-reload), income.moneyDrop.enabled (pay income as collectible money items so auto-farms earn; read at startup), and income.boost.* for the random income-boost periods (in-memory, read at startup). |
shop.* |
Shop sign tag and the maximum per-item price (fat-finger guard). |
land.* |
Region prefix, max plot volume, max pending requests per player, plot sign tag, default lease period, and auto-approval (autoApprove: instant sale of modest, non-overlapping claims at a per-block price). |
mail.* |
Join notification toggle, subject/body length limits. |
welfare.* |
Daily claim toggle/amount, wealth threshold, and the one-time new-player start check (toggle/amount). These only seed a fresh DB; the PM owns them thereafter via /hideoutia welfare set. |
crime.murder.* |
Auto murder-charge toggle and the separate first-/repeat-offence fine and prisonTime (a human duration like 10m). Read at startup. |
prison.confinementRadius |
How far (blocks) a prisoner may stray from their cell before being teleported back. Read at startup. |
election.pm.testCadenceSeconds > 0 overrides the real calendar so a full cycle runs in minutes
on a dev server (clamped to a 60s floor).
Config reload
/hideoutia admin reload re-reads config.yml into the live config-derived snapshots: the currency
symbol, the income behaviour toggles (player-placed blocks, spawner mobs, actionbar), shop sign tag/price cap,
mail length limits and join notification, plot sign tag, plot volume/pending caps, default lease
period, the snap-election trigger percent, and the party list (new parties added, renamed
parties updated).
Does not hot-reload (needs a restart, or applies only to the next cycle):
database.fileβ the connection is opened once at enable.- In-flight election timings β a live election keeps the schedule it was created with; new cadences/durations apply to the next cycle.
tax.*defaults,welfare.*figures, andincome.<category>.*rates β these only seed a fresh DB; the PM-set tax rates, welfare figures, and income rates in the database are authoritative and are never overwritten. (The income behaviour toggles above are not rates and do hot-reload.)- Removed parties are never deleted (members/history would be lost); they just stop being joinable.
crime.*andprison.*β the murder penalties and confinement radius are read once at enable; change them with a restart (in-flight sentences keep their already-scheduled wall-clock deadlines).
Commands and permissions
Every module lives behind one root command: /hideoutia <module> β¦. Running /hideoutia (or
/hideoutia help) lists the modules you may use, each with a one-line description; /hideoutia help <module> prints that module's subcommands with usage and a summary. Tab completion suggests module
names at the first argument and delegates later arguments to the module β subcommand names, fixed
option sets (e.g. tax types, party keys), and online player names where an argument is a player.
Both help and completion are permission-filtered, so you only ever see what you can use.
| Command | What it does | Permission (default) |
|---|---|---|
/hideoutia gov balance | ledger [page] | history [page] |
View the treasury balance, ledger, and the Prime Minister term history (newest first) | hideoutia.gov.view (all) |
/hideoutia gov pay <player> <amount> |
PM spends from the treasury (may go negative) | PM, or hideoutia.admin (op) |
/hideoutia tax show |
Show the four tax rates | hideoutia.tax.view (all) |
/hideoutia tax set <type> <pct> |
PM sets a tax rate | PM, or hideoutia.admin (op) |
/hideoutia tax levy <pct> [confirm] |
PM imposes a one-time flat % tax on every balance into the treasury (previews first; confirm to execute) |
PM, or hideoutia.admin (op) |
/hideoutia tax levy curve <base%> <pivot> <exponent> [max%] [confirm] |
PM imposes a one-time progressive levy: bigger balances pay a higher rate (previews first; confirm to execute) |
PM, or hideoutia.admin (op) |
/hideoutia income list [category] |
Show gameplay income rates (all, or one category) | hideoutia.income.view (all) |
/hideoutia income set <category> <key|all> <amount> (and remove <category> <key>) |
PM adds/updates or removes an income rate | PM, or hideoutia.admin (op) |
/hideoutia income boost |
Show the current income-boost status (active multiplier/time left, or that boosts are random) | hideoutia.income.view (all) |
/hideoutia welfare claim | status |
Claim the daily means-tested welfare check, or view your status | hideoutia.welfare.use (all) |
/hideoutia welfare set <check|threshold|start-check|enabled|start-check-enabled> <amount|on|off> |
PM sets a welfare figure | PM, or hideoutia.admin (op) |
/hideoutia mail list | read | send | archive | delete |
Player mail | hideoutia.mail.use (all) |
/hideoutia mail gov all <subject> | <body> |
PM broadcast to all players | PM, or hideoutia.admin (op) |
/hideoutia party list [page] | join | leave | info | trigger-election | candidate |
Party membership and leadership | hideoutia.party.use (all) |
/hideoutia election status | candidates [page] |
View the PM election (status shows any snap-petition progress) | hideoutia.election.view (all) |
/hideoutia election snap |
PM/admin: call a snap PM election now | hideoutia.election.view (PM or hideoutia.admin) |
/hideoutia election demand |
Petition for a snap PM election | hideoutia.election.view (all) |
/hideoutia election close [party] |
Force-resolve the live PM (or a party's) election now | hideoutia.admin (op) |
/hideoutia vote <candidate> |
Vote in the PM election | hideoutia.vote.use (all) |
/hideoutia citizen [player] |
View a civic profile: party, taxes paid, votes, plots owned | hideoutia.citizen.use (all) |
/hideoutia company create | info | list | topup | shares | buy | give | confirm | sell |
Found and fund a company, view it, and trade its shares | hideoutia.company.use (all) |
/hideoutia company job post|close|list | apply | applications | accept | reject | fire | quit |
The employment lifecycle (post/apply/hire/fire; quit your own job) | hideoutia.company.use (all; exec-gated in-game) |
/hideoutia company dividend | exec leave | motion appoint|remove|dissolve|list|vote |
Set the dividend, step down, and run share-weighted shareholder motions | hideoutia.company.use (all; exec-gated in-game) |
/hideoutia company shop transfer|reclaim | land transfer|reclaim |
Move a shop or plot into or out of a company | hideoutia.company.use (all; exec-gated in-game) |
/hideoutia contract list [page] | info <id> | mine [page] | accept <id> |
View government contracts and accept open ones | hideoutia.contract.view (all) |
/hideoutia contract post <amount> <title> | <description> (and payout | retender | cancel <id>) |
PM posts (optional brief after |)/pays out/retenders/cancels a paid contract |
PM, or hideoutia.admin (op) |
/hideoutia prison record [player] [page] | list [page] | cells [page] |
View a criminal record, who is imprisoned, and the cells | hideoutia.prison.view (all) |
/hideoutia prison bail [player] |
Pay a prisoner's bail (or your own) to free them early; charged from your balance | hideoutia.prison.view (all) |
/hideoutia prison charge <player> <fine> [duration|indefinite] [bail <amount>] [reason] | expunge <id> | release <player> | cell add [name] | remove <id> |
PM fines/jails a player (timed or indefinite, optional bail price), expunges a conviction, releases a prisoner early, or manages cells | PM, or hideoutia.admin (op) |
/hideoutia law list [page] | view <id> |
Read the published laws | hideoutia.law.view (all) |
/hideoutia law add <text> | edit <id> <text> | remove <id> |
PM publishes, rewords, or repeals a law | PM, or hideoutia.admin (op) |
/hideoutia discord status |
Check your DiscordSRV link and (if PM) PM-role status β see Discord integration | hideoutia.discord.view (all) |
/hideoutia plot request | info | sell | lease | unlist | subdivide | dissolve | delete | evict | show |
Player land management (delete returns your plot to wilderness; show outlines your current plot) |
hideoutia.plot.request (all) |
/hideoutia plot trust <player> | untrust <player> | trusted |
Grant/revoke/list build rights on the plot you're standing in | data-driven (owner or current tenant) |
/hideoutia plot tp <id> | return | approve | reject | reclaim |
PM land administration (tp/return inspect a request in-world) |
hideoutia.plot.admin (op) |
/hideoutia admin reload | status | setpm <player> |
Admin tooling | hideoutia.admin (op) |
The root command checks each module's base permission above before dispatching; sub-actions
(e.g. PM-only pay) are gated inside the module as before.
Shop creation is gated by hideoutia.shop.create (default all); hideoutia.shop.admin
(default op) can break/remove any shop and create [Gov Shop] government shops (chestless,
infinite-stock stores trading with the treasury). hideoutia.plot.admin (default op) also manages
government plot signs.
PM authorisation is data-driven (gov_state.pm_uuid); hideoutia.admin is the override path
for every PM-gated action.
The four taxes
Each is independently settable by the PM with /hideoutia tax set <type> <percent>; every taxed
transaction routes its cut to the government ledger.
- income β skimmed from gameplay earnings before the net is deposited.
- sales β skimmed from shop trades; the seller receives the price minus this tax. A government shop (
[Gov Shop]) charges no sales tax when a player buys from the state (the treasury is the receiver and doesn't tax itself), but still taxes a player who sells to the state. Its treasury debit on a buy may run the government balance negative β a deliberate PM economy lever. - land-sale β skimmed from player-to-player plot sales (not sales from the government).
- land-lease β skimmed from each recurring lease payment; the owner gets the net.
One-time levy
Beyond the four ongoing taxes the PM can impose a one-time flat levy:
/hideoutia tax levy <percent> [confirm]. It is ad-hoc, not a stored rate β it sweeps a percentage
of every citizen's current balance into the treasury once, and is never re-applied on reload. It
hits all citizens on the seen_player roster (online or offline) at the same rate, skips any
balance of zero or less (and any debit that floors to zero), and never pushes a balance negative.
Because it is irreversible and playerbase-wide it is a two-step confirm: the bare form previews
("this will take <pct>% from <N> citizens β about <$total>"), and adding confirm executes. The
confirm carries no server-side state β you simply re-run with the same percent. Each player is debited
individually; a failed withdrawal for one player neither aborts the sweep nor inflates the treasury
credit (only what is actually collected is credited, once, with ledger reason tax:levy).
Progressive levy. The same sweep can instead taper by wealth via a power curve:
/hideoutia tax levy curve <base%> <pivot> <exponent> [max%] [confirm]. Each player's effective rate is
effectiveRate = min(max%, base% Γ (balance / pivot) ^ exponent)
so at the pivot balance the rate is exactly base%, smaller balances pay less and larger ones pay
more. The exponent tunes the steepness: below 1 is gentle (log-like), 1 is linear in the ratio,
above 1 is steep (it is bounded to 0β10, and a negative exponent β which would be regressive β is
rejected). pivot is a money amount and must be greater than 0 (it is a divisor); base% and max%
are 0β100. max% is optional and defaults to 100% (the hard ceiling β no one is ever taxed above
100%); confirm is optional. For example levy curve 10 10000 0.5 50 taxes ~3.2% at $1k, 10% at the
$10k pivot, ~31.6% at $100k, and is capped at 50% for the very wealthy. Everything else β the scope
(all citizens, skip β€0 balances and zero debits, never go negative), the previewβconfirm flow, and
the single tax:levy treasury credit of only what was withdrawn β is identical to the flat levy; the
preview additionally echoes the curve params and the effective rate at the smallest, median, and
largest balance on the roster.
Income rates
Gameplay earns money from four fixed sources tied to game listeners β mining, fishing,
mobs, and farming. Each source has a set of specific entries (an uppercase Material or
EntityType name, e.g. DIAMOND_ORE or BLAZE, paying a fixed amount) plus an optional all
fallback paid for anything not listed. A payout resolves the specific entry first, then all, then
nothing.
The rate entries are seeded from income.* on a fresh database, after which the PM (or a
hideoutia.admin holder) owns them via /hideoutia income set <category> <key|all> <amount> and
/hideoutia income remove <category> <key> β the stored values live in the income_rate table, are
authoritative, and survive reloads and restarts, exactly like the PM-set tax rates and welfare
figures. /hideoutia income list [category] shows the current rates to anyone. Removing a specific
entry lets it fall back to all; removing all makes unlisted keys in that category pay nothing.
The four categories are fixed (a new one would need a new game listener), so the PM manages entries within them, not new categories. A key is free-form (not validated against the Bukkit registry) so the PM can pre-seed a rate for an item that doesn't exist yet; an unknown key simply never matches.
The three behaviour toggles β income.excludePlayerPlaced (mined player-placed blocks pay
nothing), income.excludeSpawners (spawner-spawned mobs pay nothing), and income.showActionbar
(flash a +$x banked actionbar when money is collected) β are not rates: they stay in config.yml
and hot-reload with /hideoutia admin reload.
Money drops
Income is paid as collectible money items dropped into the world, not credited straight to your
balance β and the money is always an additional reward: every normal drop (the ore, the fish, the
mob loot, the crop's produce and seeds) still drops exactly as in vanilla, with a money item added on
top. This is also what lets auto-farms earn: a water/piston farm breaks crops with no player event,
so the old per-break credit paid them nothing, but the plugin detects the auto-break and mints money at
the crop (see the harvest-detection note below for exactly which auto-breaks count). Set
income.moneyDrop.enabled: false (read at startup, restart to change) to revert to the old
instant-to-balance behaviour.
- What drops. A money item is a glowing gold piece whose value lives in its item data, not its stack count (so two different-valued drops never merge into one ambiguous stack). Its tier shows the rough worth at a glance: under $10 a gold nugget, under $1000 a gold ingot, otherwise a gold block. The income boost is applied when the item is minted, so a 3x boost triples the drop's value.
- How each source pays. Mining mints a money item at the broken ore (the ore still drops); fishing mints at the angler (the catch is kept); a mob death mints at the corpse β including kills with no player killer, so grinders pay out (whoever banks the drop is taxed). Farming mints a money item at a harvested mature crop (wheat, carrots, potatoes, beetroot, nether wart) worth that crop's farming rate β the crop's own produce and seeds still drop normally, so money is a side reward, not a replacement.
- Banking and tax. Money banks the instant it enters a player inventory β picked up off the ground, or pulled out of a chest/hopper (shift-click, drag, hotbar-swap, etc.). On banking, the income tax is split off to the government (attributed to the collector) and the net is deposited; the drop itself carries the gross, so uncollected money owes the treasury nothing. Money picked up by a hopper or chest stays an item inside the container β it sits there until a player takes it out.
- Bundling. Money items near each other are merged into one stack worth their sum (re-tiered), which keeps a busy farm from spawning an unbounded number of item entities.
- Money is minted on the harvest, never from items. Money is minted on the crop-break itself, not by converting any item entity, so breaking a chest/hopper full of wheat, a dispenser ejecting it, a villager trade or composter overflow can never mint money β there is nothing in the plugin that turns an item into money. A player harvest mints when the player breaks a mature crop. An auto-farm harvest is detected by the break mechanism β water/lava washing a crop away and pistons pushing or pulling a crop are covered, minting at the crop as it breaks. Other auto-breaks (villager farmers, trampling, explosions) are not detected and so simply don't earn.
Income boosts
Every so often the server runs a random income boost: for a fixed window, all gameplay income
is multiplied (default 3x for 30 minutes), announced to everyone at start and end. The next boost
starts a random gap after the previous one ends (default 2β4 hours). The multiplier is applied to
the gross before income tax, so a player's net and the government's tax cut both scale together.
Boosts are configured under income.boost.* (enabled, multiplier, durationMinutes,
minGapHours, maxGapHours), read at startup like the broadcast intervals.
Boosts are ephemeral / in-memory only β there is no database table. A boost in progress is lost on
restart and the next one is freshly scheduled when the plugin enables. /hideoutia income boost shows
the current status to anyone (active multiplier and time remaining, or that boosts occur at random).
Welfare
Welfare is paid from the government budget. The five figures are seeded from welfare.* on a fresh
database, after which the PM (or a hideoutia.admin holder) owns them via
/hideoutia welfare set <check|threshold|start-check|enabled|start-check-enabled> <amount|on|off> β
the stored values are authoritative and survive reloads and restarts, exactly like the PM-set tax
rates. The whole feature can be switched off (/hideoutia welfare set enabled off, or
welfare.enabled: false on a fresh DB); the command then reports that welfare is disabled. Money
amounts are in major units and may be zero (a zero amount is floored, never negative).
- Daily claim (
/hideoutia welfare claim) payscheckAmountbut only while the player's balance is strictly belowwealthThreshold(a balance exactly at the threshold is over it and is refused). The claim resets at local midnight intimezoneβ it's once per calendar day, not a rolling 24h window, so it's predictable for players ("come back tomorrow"). The trade-off is that a claim at 23:59 and another at 00:01 are both allowed; that's accepted./hideoutia welfare statusshows the amount, the threshold, and whether you can claim right now. - New-player start check is a one-time payment of
startCheck.amountto a player on their first ever join, with a welcome letter naming the amount. It's gated on the seen-player roster insert actually creating a row, so backfilled and returning players never receive it β only genuinely new players do. On an existing server every current player is backfilled intoseen_playerat startup, so nobody is retroactively paid. The same rule means a player whose first ever join happened while the start check was disabled won't receive it after a re-enable. Negative amounts in config are floored to zero (a negative check would reverse the money flow).
Both payments use the same deposit-then-debit flow as /hideoutia gov pay: the player is credited first and
the government ledger is debited only on success (reasons welfare:claim and welfare:start-check),
so a failed deposit never leaves a phantom debit. Two deliberate choices:
- Not income-taxed. Welfare deposits the full amount; routing it through income tax would just have the government taxing itself.
- The ledger may go negative. Welfare still pays even if it drives the government balance below
zero, consistent with
/hideoutia gov pay.
Government contracts
The PM can post paid work for players: /hideoutia contract post <amount> <title> | <description>
(the description after a | is optional and shows in /hideoutia contract info). The budgeted
amount is debited from the treasury immediately on posting (ledger reason contract:post) β it
is escrowed by that debit, so a contract can't promise money the treasury later refuses to honour.
The post is announced once to everyone online.
The lifecycle is OPEN β ACCEPTED β PAID, with CANCELLED as the other terminal state and a
return path ACCEPTED β OPEN:
- Accept (
/hideoutia contract accept <id>, any player) takes an open contract and assigns you as its contractor. You can't accept a contract you posted yourself. - Payout (
/hideoutia contract payout <id>, PM) deposits the budgeted amount to the contractor and marks the contractPAID. Because the money was already debited at post, payout makes no further treasury movement β a post β accept β payout lifecycle nets the treasury exactly the budget and the contractor exactly the budget (money is conserved). Like/hideoutia gov pay, it deposits first and only marksPAIDon success, so a failed deposit leaves the contractACCEPTEDand retryable. - Retender (
/hideoutia contract retender <id>, PM) returns an accepted contract to tender, clearing its contractor. No money moves β the amount stays escrowed. - Cancel (
/hideoutia contract cancel <id>, PM) ends an open or accepted contract and credits the escrowed amount back to the treasury (reasoncontract:cancel-refund). The contractor, if any, is paid nothing and is notified by mail.
Anyone can browse with /hideoutia contract list (open + active) and /hideoutia contract info <id>; /hideoutia contract mine lists the contracts you've accepted. As with welfare, payouts are
not income-taxed (government expenditure) and the treasury may go negative. All the
poster/contractor notifications are sent as government mail.
Justice and the prison
A criminal record tracks convictions; a conviction carries a fine and, optionally, prison
time. Anyone can read a record (/hideoutia prison record [player]), see who's currently jailed
(/hideoutia prison list), and list the cells (/hideoutia prison cells); a one-line summary also
appears on the citizen profile. Two sources create convictions:
- Automated murder charges. When one player directly kills another (a
PlayerDeathEventwhose killer is a player and not the victim), the killer is charged automatically. Murder uses separate first- and repeat-offence penalties β an explicit fine and prison time for each (set inconfig.ymlundercrime.murder), not a reduction percentage β chosen by how many murder convictions the killer already has. Disable it withcrime.murder.enabled: false. - Manual charges.
/hideoutia prison charge <player> <fine> [duration|indefinite] [bail <amount>] [reason](PM orhideoutia.admin) fines a player and, if a duration is given (30s/10m/2h/1d), jails them. Passindefinite(orperm/life) instead of a duration for an indefinite sentence with no auto-release: the prisoner stays jailed until the PM ends it manually with/hideoutia prison release <player>(keeps the conviction on record) or/hideoutia prison expunge <id>. Only manual charges can be indefinite; automated murder charges are always timed. An optionalbail <amount>pair (after the duration, before the reason) sets an early-release price (see Bail below);bail 0or omitting it means not bailable. A token that isn't a valid duration or keyword is treated as the start of the reason, so/hideoutia prison charge bob 100 litteringis a fine-only charge.
Fines take the full amount. The whole fine is always withdrawn (the server economy permits
debt) and credited to the treasury (ledger reason prison:fine); the conviction records how much
was actually collected. If the withdrawal somehow fails, the player is still convicted/imprisoned
and the shortfall is reported β there is no separate debt to chase.
Cells and confinement. Custodial sentences need a cell. The PM adds one at their feet with
/hideoutia prison cell add [name] and removes a free one with /hideoutia prison cell remove <id>
(an occupied cell is refused). If no cell is free, a charge that carries prison time is rejected
β nothing is fined or recorded. Occupancy lives solely on the cell (one prisoner, one cell for the
whole stay). A prisoner is teleported into their cell and kept there: stray more than
prison.confinementRadius blocks (or change world) and you're teleported back, and your outward
teleports (/spawn, ender pearls, β¦) are cancelled β except the plugin's own release/imprison
teleports.
Confiscation and welfare. On their first imprisonment a prisoner's full inventory (hotbar, main, armor, and offhand β not the ender chest or XP) is taken and held, and returned in full on release. The items are stored server-side, so nothing drops if they die in the cell β they respawn straight back inside it. Re-offending while already jailed doesn't re-confiscate (their hands are already empty), and a prisoner can't starve: hunger loss is cancelled while they're serving. Being jailed or released while offline is reconciled on their next login (items taken/returned then), since the stored items are the source of truth for what's still pending.
Wall-clock sentences. The countdown runs in real time from conviction, online or not, and
auto-release fires at the deadline (via the same restart-safe DeadlineEngine the leases and
elections use, so time served during downtime is caught up on the next boot). A player who logs in
mid-sentence is re-teleported to their cell. Re-offending while jailed stacks: the release is
extended to the latest deadline and the prisoner keeps their one cell. An indefinite sentence has
no deadline at all and dominates any timed sentence in the stack β the prisoner is never
auto-released while one is active. Release teleports to the main world's spawn.
Served and expunged. A served sentence stays on the record with status SERVED. The PM (or
hideoutia.admin) can /hideoutia prison expunge <id> to delete a conviction outright; if it was
the player's last active conviction, expunging it also releases them. /hideoutia prison release <player> ends a sentence early without touching the record.
Bail. A charge can carry an optional bail price that lets a prisoner buy their way out early.
Anyone can pay with /hideoutia prison bail <player> (or a bare /hideoutia prison bail to pay
your own); it is charged from the payer's balance and credited to the treasury (ledger reason
prison:bail). Unlike a fine, bail requires sufficient funds β if the payer can't cover the full
amount nothing is taken and the prisoner stays jailed (no partial, no debt). Bail is available only
when every active conviction carries a price, so a single non-bailable charge (an indefinite, an
automated murder, or one posted without a bail price) blocks it; when several priced charges are
stacked, the total is their sum. Paying bail ends the sentence exactly like a release β active
convictions marked SERVED, cell freed, inventory restored, teleported to spawn β and the charges
stay on the record.
Laws
The government publishes laws β numbered text statements anyone can read. Browse them with
/hideoutia law list (paginated) and read one with /hideoutia law view <id>. Each law is a single
statement (an auto-assigned id plus the text), with no separate title.
The PM (or hideoutia.admin) writes them: /hideoutia law add <text> publishes a new law and
reports its id, /hideoutia law edit <id> <text> rewords an existing one (keeping its id and
original publish date), and /hideoutia law remove <id> repeals it. Blank text is rejected, and a
statement is trimmed of surrounding whitespace. The list starts empty.
Companies
Players can form companies β joint-stock entities that hold their own money, own shops and land, employ players, pay salaries and dividends, and are run by share-weighted shareholder votes. A company is identified by an autoincrement id and a unique name; its money lives in an append-only company ledger (Vault can't key a non-player balance), separate from any player's wallet.
Founding and shares (fixed-pool treasury). /hideoutia company create <name> <shares> <startingBalance> <public|private> funds the company from your wallet, gives all the shares to
you, and makes you its sole executive. The share count is fixed at creation and never changes β it
is the maximum that will ever exist. Every share is either held by a player or sits in the company
treasury (the unsold pool, derived as share count β Ξ£ player holdings); the founder starts
holding the lot, so the treasury begins empty. Share value is (ledger balance + owned land value) / share count, floored to whole cents β shops add no intrinsic value, land does (valued at the same
per-block rate land auto-approval uses). Buying (/hideoutia company buy) takes shares out of the
treasury at the current price (cash into the ledger, treasury shrinks), so you can only buy what the
company actually holds β a buy beyond the treasury is refused, and per-share value is unchanged. Selling
(/hideoutia company sell) returns shares to the treasury at that price (cash out of the ledger),
where they become buyable again β this is how a founder "floats" shares for others to buy. A sale that
would overdraw the ledger is refused; selling your entire stake is allowed (it just leaves the company
with no outstanding shares until someone buys back in). /hideoutia company give <name> <player> <shares> gifts shares between players for free and asks you to /hideoutia company confirm first;
/hideoutia company topup adds cash to a company (anyone may). info/shares show the outstanding and
treasury (available-to-buy) counts.
Employment. An executive posts jobs (/hideoutia company job post <name> <title> <dailySalary> [descβ¦]); a player applies (/hideoutia company apply <jobId>), an executive accepts or rejects
(accept/reject <appId>), and either side can end it (/hideoutia company quit, or an exec's
fire <name> <player>). A player holds at most one job. While employed, your gameplay income is
split 50/50 with your employer before tax β each half is taxed at the income rate, your net lands
in your wallet and the company's net in its ledger (so a company is taxed at player rates).
Daily run. Once a day each company pays salaries (in seniority order β if it can't make a wage it
auto-terminates that employee), then pays a dividend: a configurable percentage
(/hideoutia company dividend <name> <percent>) of the day's profit (balance β yesterday's baseline), split pro-rata over outstanding (player-held) shares and taxed at the dividend rate β
treasury shares earn nothing, so the whole pool reaches player holders; rounding dust stays in the
company, and a company with no outstanding shares pays no dividend. If a company's value can't cover
even 1Β’ per share it enters a 24-hour insolvency grace period and dissolves if it hasn't recovered.
Governance. Executives open motions to appoint or remove an executive, or to dissolve the
company (/hideoutia company motion appoint|remove|dissolve β¦); shareholders vote with their current
weight (/hideoutia company motion vote <id> yes|no). A motion passes the instant one side crosses
>50% of the outstanding (player-held) shares β treasury shares are non-voting and excluded from
the denominator β otherwise at a 24-hour deadline (a tie, including 0-0 when every share sits in the
treasury, rejects β the status quo wins). The tally always re-weights to current holdings, so selling
or gifting shares mid-vote changes the outcome. When the last executive leaves (voluntarily or by
motion) the largest shareholder is auto-promoted, so a company with shareholders is never leaderless;
if every share has been floated to the treasury there is no one to promote and the company is simply
left without that auto-promotion.
Assets. An executive moves a shop or plot into a company with /hideoutia company shop transfer
/ land transfer and back out to a player with shop reclaim / land reclaim β no money changes
hands (the share value simply reflects the new holdings). A company plot has no WorldGuard owner;
instead its members β its executives and its employees β are added as WG members, kept in sync
on every appointment/hire and every departure (a player who holds both roles keeps build rights until
they hold neither). Company shops have no separate access lock, so restock access follows the land:
a company shop sitting on company land is restockable by whoever can build there, i.e. its members. On
dissolution a company's shops become government shops, its land returns to the government, its remaining
cash sweeps to the treasury, open motions are voided, and employees are let go.
Executive-only actions are gated in-game by the share-vote/executive model, not by a permission β
hideoutia.company.use (default all) is the only node a player needs; hideoutia.company.admin
(default op) is the override path.
Discord integration
Optional, via the DiscordSRV plugin. When DiscordSRV is installed and a PM role id is
configured, a Discord role automatically tracks whoever is the in-game Prime Minister: it's granted
when someone takes office and stripped when they leave. The feature is dormant (and the rest of
the plugin unaffected) whenever DiscordSRV is absent or discord.pmRoleId is unset β startup logs
one line saying which.
Setup:
- Install DiscordSRV and connect its bot as per its own docs. This plugin reuses DiscordSRV's bot and account linking β it bundles no Discord bot of its own.
- Give the DiscordSRV bot the Manage Roles permission, and drag the PM role below the bot's top role in Server Settings β Roles. Discord forbids a bot from managing any role at or above its own highest role, so the sync will silently fail to apply otherwise.
- Set
discord.pmRoleIdinconfig.ymlto the PM role's id (right-click the role in Discord with Developer Mode on β Copy Role ID) and restart.
Players link their account with DiscordSRV's own command, /discord link (this plugin doesn't
reimplement linking). Once linked, becoming PM grants the Discord role automatically. A player can
self-check with /hideoutia discord status: it reports whether their account is linked and, if
they're the PM, that the role should be applied. Only linked players can receive the role β an
unlinked PM simply doesn't get it until they link, at which point it's granted immediately.
On startup (and whenever DiscordSRV's bot reconnects) the plugin reconciles the role to the current PM β handy if the PM changed while the server was down.
LuckPerms integration
Optional, via the LuckPerms plugin. When LuckPerms is installed and luckperms.pmGroup is set,
that LuckPerms group automatically tracks whoever is the in-game Prime Minister: the player is added
to the group when they take office and removed when they leave. This is fully automatic β there's no
command and no permission to grant. The feature is dormant (and the rest of the plugin
unaffected) whenever LuckPerms is absent or luckperms.pmGroup is empty β startup logs one line
saying which.
Setup:
- Install LuckPerms as per its own docs.
- Create the group in LuckPerms (e.g.
/lp creategroup pm) and attach whatever permissions or prefix the PM should have. The group must already exist for membership to have any effect. - Set
luckperms.pmGroupinconfig.ymlto that group's name (defaultpm) and restart.
The sync uses the player's own account, so no linking step is needed, and it works for offline players β an outgoing PM who is offline still loses the group, and an incoming PM who is offline still gains it. On startup the plugin reconciles group membership to the current PM (granting it to them and stripping it from anyone else who still has it), so drift from a PM change during downtime self-corrects.
Build trust
An owner can let other players build on a plot without selling it. /hideoutia plot trust <player> adds the
player to the plot's WorldGuard member domain and records the grant; /hideoutia plot untrust <player>
revokes it; /hideoutia plot trusted lists who is trusted and who granted each.
Authorisation is data-driven, not a permission β hideoutia.plot.request gates requests, not
trust:
- The owner may trust/untrust on their plot (including sub-plots they own) at any time.
- While a plot is leased, the current tenant may also trust/untrust β it's their home for
the term. Each grant records who made it (
granted_by), which drives the lifecycle rules below. - Government plots (no owner) can't have trust granted β the PM/admins build via WorldGuard bypass and don't need it.
- You can't trust the owner, the sitting tenant (already a member), or someone already trusted, and you can't untrust someone who isn't trusted.
Trust is scoped to a single plot id. Because a sub-plot's region sits at a higher WorldGuard priority than its parent, trusting someone on the parent does not grant them build inside a leased or owned sub-plot β the sub-region stays authoritative within its bounds.
Lifecycle (kept consistent so build rights never outlive the relationship that granted them):
- Sale / ownership transfer clears all trust on the plot β the new owner starts fresh.
- Lease end / eviction (missed payment or owner-evict) drops only the tenant-granted trust; the owner's own grants persist. (The departing tenant's own membership is removed by the existing lease-end path.)
- Reclaim (PM) returns the plot to the government and clears all trust.
- Dissolving a sub-plot clears that sub-plot's trust rows (the region is deleted anyway; this
just avoids leaving rows behind β a
plot_idforeign key withON DELETE CASCADEis the backstop).
Election timeline
Prime Minister (monthly, calendar-driven):
- On
startDayOfMonththe ballot opens (VOTING_OPEN); candidates are auto-populated from each party's current leader. Voting runs forvoteDurationSeconds(default 72h). - The ballot closes and the winner is tallied (
VOTING_CLOSED). Tie-break: the incumbent if they stood, otherwise the lowest UUID. - Power transfers at
max(powerTransferDayOfMonth, vote close), settinggov_state.pm_uuid.
Snap PM election (off-cycle): candidates and voteDurationSeconds are the same as a scheduled
election, but power transfers as soon as the ballot closes β there is no wait for the
powerTransferDayOfMonth anchor. Two entry points:
/hideoutia election snapβ the PM (or ahideoutia.adminholder) calls one immediately./hideoutia election demandβ any player petitions. The petition is enabled only once the playerbase reachesminPlayerbase; on reachingceil(triggerPercent% Γ playerbase)distinct demands a snap election starts. Demands don't expire, but they are cleared whenever any PM election starts (scheduled, snap, or PM-called) so a stale petition can't immediately retrigger.
The monthly schedule runs independently. If a scheduled start fires while a PM election is still in progress (e.g. a snap is mid-ballot), it is skipped rather than opening a second election; the next month fires as usual.
Closing an election early (admin): /hideoutia election close force-resolves the live PM
election now instead of waiting for its scheduled vote-close / power-transfer deadlines, and
/hideoutia election close <party> does the same for a party's leadership election. It fast-forwards
to the result by firing that election's own still-pending transitions immediately β the same
handlers the deadlines would have run β so it tallies, installs the winner (or voids if there are no
eligible candidates), and mails the normal result. Because it reuses the real transitions, a closed
snap election records its term exactly as the normal snap close would (a SNAP_ELECTION, not a
scheduled one), and the original future-dated deadlines simply no-op when they later arrive.
The "playerbase" is every player who has ever joined, tracked in seen_player (a join listener
plus a one-off startup backfill). The backfill seeds from Bukkit's offline-player cache, which is
lossy for players who joined before this feature shipped; the roster self-heals as they rejoin.
Party leadership (member-triggered): leadership-trigger requests accumulate; at
triggerThresholdPercent of members (default 20%) an election is created with a
candidacyWindowSeconds window (any member may /hideoutia party candidate), then a voteDurationSeconds
ballot of all members; the leader is set on close. Joins and new triggers for that party are
blocked while its election is live.
Between elections, leadership is kept filled automatically: the first member to join a leaderless party becomes its leader, and when the sitting leader leaves (or switches parties) the earliest-joined remaining member is promoted (tie-broken by lowest UUID), or the leader is cleared if the party is now empty. Promoted leaders are mailed. A leader leaving mid-election is allowed; the election result overwrites the interim leader when it completes.
All horizons are absolute epoch deadlines persisted in the DB. On startup the deadline engine fast-forwards anything past-due (e.g. after downtime), then ticks every 60 seconds.
Known limitations
- WorldGuard build β the WorldGuard jar on the server must support the running Minecraft
version. WorldGuard API usage is isolated in
WorldGuardBridge; verify region creation on a real server. - Anti-exploit block tags are not persisted across restarts β player-placed blocks lose their "no payout" tag on restart (an accepted minor exploit window).
- Orphan cleanup is lazy β a shop whose linked container has been removed is deleted the next time someone clicks its sign, not scanned for at startup. A plot sign broken by its owner/PM clears its own link.
/hideoutia plot tpreturn locations are not persisted β the location stashed by/hideoutia plot tpfor/hideoutia plot returnlives in memory only; a restart loses any pending return (the reviewer just walks back). Plot outlines (/hideoutia plot tp,/hideoutia plot show) likewise stop on restart.- Prison confinement is best-effort β confinement is a
PlayerMoveEventsnap-back keyed on a block-position change (and an outward-teleport cancel), not a true region lock; a fast vehicle or a plugin teleport that bypassesPlayerTeleportEventcould briefly leave the bubble before the next move event pulls the prisoner back. The cell is a single point with a radius, not a built room β the PM must build the walls. There is no income/build restriction while jailed beyond the location lock.
Manual dev-server verification checklist
Logic is covered by JUnit; these need a running server with Vault + WorldGuard:
- WorldGuard region lifecycle: request β approve β buy β sell β reclaim, and subdivide/dissolve.
- Vault round-trips against a real economy plugin (buy/sell, gov pay, tax skims).
- Shop end-to-end: create, buy, sell, protection (break/hopper/explosion/piston), including
[Gov Shop]creation/trade in-world. - An accelerated full election cycle (
testCadenceSeconds), including a mid-election restart. - Lease payment + eviction on insufficient funds.
/hideoutia plot tp <id>teleports to the request and outlines it;/hideoutia plot returnand approve/reject send the reviewer back and clear the outline;/hideoutia plot showoutlines an owner's current plot./hideoutia admin reload(config change takes effect, incl. currency symbol),status, andsetpm.- Prison end-to-end: add a cell, kill a player (auto murder charge + jail + cell teleport), confirm
the confinement snap-back and the cancelled
/spawn, wait out the wall-clock release (and a mid-sentence restart), then a stacked second charge, anexpunge, and an earlyrelease. - Company end-to-end:
/hideoutia company createβtopupβ a second playerbuys shares β an execjob postthen the playerapplys and the execaccepts β trigger acompany_dailyrun (or wait one out) and confirm salary + dividend mail and the share value moving β open a motion and have shareholdersvote(verify the >50% early close and the tie-rejects deadline) βcompany shop transferandcompany land transferan asset in, checking the executives gain WG build rights and areclaimremoves them β pass amotion dissolve(or let insolvency fire) and verify the company's shops become government shops, its land returns to the government, and its cash sweeps to the treasury.