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

Both Vault and WorldGuard are hard dependencies (depend in plugin.yml); the plugin will not enable without them.

Install

  1. Drop hideoutia-plugin-<version>.jar into plugins/ alongside Vault, an economy plugin, and WorldGuard.
  2. Start the server once to generate plugins/HideoutiaPlugin/config.yml and the SQLite database (data.db).
  3. Edit config.yml to 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/ to plugins/HideoutiaPlugin/. Rename the folder before first start to keep your existing SQLite database (and config.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):

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.

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.

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).

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:

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:

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:

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:

  1. 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.
  2. 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.
  3. Set discord.pmRoleId in config.yml to 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:

  1. Install LuckPerms as per its own docs.
  2. 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.
  3. Set luckperms.pmGroup in config.yml to that group's name (default pm) 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:

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):

Election timeline

Prime Minister (monthly, calendar-driven):

  1. On startDayOfMonth the ballot opens (VOTING_OPEN); candidates are auto-populated from each party's current leader. Voting runs for voteDurationSeconds (default 72h).
  2. The ballot closes and the winner is tallied (VOTING_CLOSED). Tie-break: the incumbent if they stood, otherwise the lowest UUID.
  3. Power transfers at max(powerTransferDayOfMonth, vote close), setting gov_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:

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

Manual dev-server verification checklist

Logic is covered by JUnit; these need a running server with Vault + WorldGuard: