COWMODSКаталог модов

MultiBank

Economy plugin with personal and shared group accounts, roles, a GUI, atomic transactions, an optional Vault bridge, and a developer API.

Автор: roguiii4630обновлён 1 окт. 2026 г.
Экономика

Описание

MultiBank

A secure, feature-rich economy plugin for Paper/Purpur 26.3 servers (2.0.0 runs on 26.2, 1.x on 1.21.x).

MultiBank replaces EssentialsX Economy and acts as your server's main economy system, with GUI support, multi-account management, a Vault bridge for legacy plugins, and a clean developer API.

Features

  • Multi-account system: Personal + Group/Joint accounts with roles (OWNER, MANAGER, MEMBER, VIEWER)
  • GUI interface: Full-featured inventory GUI via /mb — pay players, manage accounts, view history, accept invites, view top balances
  • Vault bridge: Legacy plugins (shops, auction houses, etc.) work seamlessly via Vault Economy provider
  • Developer API: Exposed via Bukkit ServicesManager with async CompletableFuture methods
  • Custom Events: BalanceChangeEvent, TransferEvent, AccountCreateEvent, PrimaryAccountChangeEvent, AccountFrozenEvent
  • Atomic transactions: No duplication, no double-spend, no race conditions — all DB operations are transactional
  • SQLite/MySQL: Configurable storage backend with HikariCP connection pooling
  • Localization: English (en_US) + Portuguese (pt_PT), easily extensible
  • Rate limiting: Configurable per-player operation throttling
  • Long cents storage: Balances stored as long cents internally — no floating-point precision loss

Banking modules

Four optional modules, on by default, each switchable in config.yml without losing its data.

  • Savings accounts (/poupanca): interest paid on the lowest balance held during the period, so money parked a moment before the tick earns nothing.
  • Term deposits (/poupanca prazo): capital locked in escrow for a fixed term and out of the player's balance, at several times the open savings rate. The accruing interest stays hidden until it matures — and cancelling early returns exactly what went in, to the cent.
  • Loans (/emprestimo): borrow from the State, or from other players through an offer board where capital is escrowed at offer time and acceptance is a database compare-and-swap. Defaulting hands the balance to garnishment, which collects it from the borrower's future income.
  • Certificates (/certificados): savings and treasury certificates modelled on the Portuguese instruments. Subscribing lends money to the State, which puts it to work.
  • The State (/estado): a public treasury balance sheet and the community projects it funds. Delivering a project destroys the money — the economy's one deliberate sink.

No module creates currency. Every credit has a matching debit: interest is paid out of the treasury, loans move treasury or escrow money, and if the State cannot afford a payment it is not made rather than minted. /mb admin audit reconciles the escrow accounts against the contracts they back and reports any drift.

Installation

  1. Download MultiBank-2.1.0+mc26.3.jar
  2. Place it in your server's plugins/ folder
  3. (Optional) Place Vault.jar in plugins/ for legacy plugin support
  4. Start the server — config.yml and lang/ files will be generated
  5. Edit plugins/MultiBank/config.yml to customize currency, database, pay limits, etc.
  6. Use /mb admin reload to apply config changes without restart

Commands

Player Commands

CommandAliasesDescription
/mb—Opens the main GUI
/money [player]/bal, /saldo, /balanceCheck balance (primary account)
/pay <player> <amount> [fromAccount]/pagarPay another player
/pay confirm—Confirm a pending large payment
/pay cancel—Cancel a pending payment
/topbal [page]/top, /baltop, /topmoneyView richest accounts

Account Management (/mb account)

CommandDescription
/mb account listList all your accounts
/mb account info <accountId>View account details
/mb account create <name>Create a group/joint account
/mb account rename <accountId> <name>Rename an account
/mb account setprimary <accountId>Set your primary account
/mb account invite <accountId> <player>Invite a player to your group account
/mb account accept [inviteId]Accept an invite (latest if no ID)
/mb account kick <accountId> <player>Remove a member from your group account
/mb account role <accountId> <player> <OWNER|MANAGER|MEMBER|VIEWER>Change a member's role
/mb account leave <accountId>Leave a group account (cannot leave if owner)

Savings and term deposits (/poupanca, /savings)

CommandDescription
/poupancaSavings and term deposit GUI
/poupanca abrir [name]Open a savings account
/poupanca depositar <amount> [savingsId]Move money into savings
/poupanca levantar <amount> [savingsId]Take money out
/poupanca infoYour accounts, interest base and time to the next payment
/poupanca taxaThe current rate and how it is calculated
/poupanca prazo tiposTerm deposits on offer
/poupanca prazo abrir <type> <amount>Lock capital away for a fixed term
/poupanca prazoYour locked positions
/poupanca prazo cancelar <id>Cancel or collect — capital always returned in full

Interest is paid on the lowest balance held during the period, so capital that was not at risk for the whole period earns nothing. A term deposit's accrued interest is hidden until it matures.

Loans (/emprestimo, /loan)

CommandDescription
/emprestimoCredit dashboard
/emprestimo simular <amount> <periods>Quote a State loan before committing
/emprestimo pedir <amount> <periods>Borrow from the State
/emprestimo oferecer <amount> <rate%> <periods>Offer a loan to other players (escrows the capital)
/emprestimo mercadoBrowse open offers
/emprestimo aceitar <offerId>Take an offer
/emprestimo cancelar <offerId>Withdraw your own offer
/emprestimo pagar <loanId> [amount]Repay; no amount settles the balance
/emprestimo meusLoans you owe and loans you granted
/emprestimo creditoYour score, record and credit line

Instalments are collected automatically. Missing too many defaults the loan, and the balance is then collected as a share of the borrower's incoming money until it is cleared.

Certificates (/certificados, /certs)

CommandDescription
/certificadosCertificate GUI
/certificados seriesSeries on offer, with rates and terms
/certificados subscrever <series> <amount>Lend the amount to the State
/certificados resgatar <certId>Redeem
/certificados meusYour holdings

The State (/estado, /state)

CommandDescription
/estadoTreasury and projects GUI
/estado infoBalance, obligations, what is lent out, solvency
/estado projetosCommunity projects and their progress
/estado propor <goal> <name…>Propose a project
/estado votar <projectId>Vote for a proposal
/estado financiar <projectId> <amount>Contribute

Admin Commands (/mb admin)

CommandDescriptionPermission
/mb admin give <player|accountId> <amount> [reason]Give moneymultibank.admin.give
/mb admin take <player|accountId> <amount> [reason]Take moneymultibank.admin.take
/mb admin set <player|accountId> <amount> [reason]Set balancemultibank.admin.set
/mb admin freeze <player|accountId> <on|off>Freeze/unfreeze accountmultibank.admin.freeze
/mb admin reloadReload config & lang filesmultibank.admin.reload
/mb admin treasury infoTreasury balance sheetmultibank.admin.treasury
/mb admin treasury grant <amount> [reason]Seed the treasury (mints; recorded as such)multibank.admin.treasury
/mb admin treasury burn <amount> [reason]Destroy treasury moneymultibank.admin.treasury
/mb admin project open <goal> <name…>Open a project for contributionsmultibank.admin.project
/mb admin project complete <id>Mark delivered — spends and destroys the moneymultibank.admin.project
/mb admin project cancel <id>Cancel and refund every contributormultibank.admin.project
/mb admin auditReconcile escrow against contracts; report driftmultibank.admin.audit

Console and RCON replies for these are written to the server log: the lookups answer from a database thread, by which time RCON has already closed out the request.

The treasury starts empty. Until it holds money it cannot lend or pay interest — either seed it with /mb admin treasury grant, or let it fill from certificate subscriptions and loan fees.

OP Override Commands (/mb op)

These commands are restricted to server OPs (isOp()) and console only — no permission nodes, pure OP check. They always target the player's personal account, regardless of their current primary account setting.

CommandDescriptionExample
/mb op set <player> <amount>Hard-set a player's personal balance/multibank op set username 4000
/mb op give <player> <amount>Give money to a player's personal account/mb op give username 250
/mb op take <player> <amount>Take money from a player's personal account/mb op take username 100

Rules:

  • Amount supports decimals (e.g., 4000.50), converted to cents internally
  • Works for offline players (UUID lookup via Bukkit.getOfflinePlayer)
  • All operations are atomic (single DB transaction)
  • Every action is logged to console and transaction history with reason "OP override"
  • Take behavior when balance would go negative is configurable:
    • clamp-negative-to-zero: true (default) — takes only what the player has, balance becomes 0
    • clamp-negative-to-zero: false — denies the operation entirely
    • allow-negative-after-take: true — allows negative balance (overrides clamp)

Examples:

/multibank op set Steve 4000       → Sets Steve's personal balance to $4,000.00
/mb op give Alex 250.50            → Gives Alex $250.50 into personal account
/mb op take Notch 100              → Takes $100.00 from Notch's personal account

Permissions

PermissionDescriptionDefault
multibank.useUse /mb GUI and basic commandsAll players
multibank.balanceCheck balancesAll players
multibank.payUse /payAll players
multibank.topView top balancesAll players
multibank.account.createCreate group accountsAll players
multibank.adminAccess admin subcommandsOP only
multibank.admin.give/mb admin giveOP only
multibank.admin.take/mb admin takeOP only
multibank.admin.set/mb admin setOP only
multibank.admin.freeze/mb admin freezeOP only
multibank.admin.reload/mb admin reloadOP only
multibank.savingsSavings accounts and term depositsAll players
multibank.loanLoan commands and GUIAll players
multibank.loan.borrowTake a loan or accept an offerAll players
multibank.loan.lendPost a lending offerAll players
multibank.certCertificatesAll players
multibank.stateView the treasury and projectsAll players
multibank.project.voteVote on proposed projectsAll players
multibank.project.proposePropose a projectAll players
multibank.admin.treasury/mb admin treasuryOP only
multibank.admin.project/mb admin projectOP only
multibank.admin.audit/mb admin auditOP only

Nodes are registered in code at enable time. Paper ignores the permissions: block of paper-plugin.yml, and an unregistered node silently defaults to OP — which is what had made multibank.account.create staff-only despite the config implying otherwise.

Roles (Group Accounts)

RoleDepositWithdrawTransferInviteKickSet RolesRenameSet PrimaryView
OWNER
MANAGER
MEMBER
VIEWER

Vault Bridge

MultiBank automatically registers as a Vault Economy provider if Vault is present on the server. This means any plugin that uses Vault's Economy API (shops, auction houses, job plugins, etc.) will transparently use MultiBank balances.

How it works:

  • Economy.getBalance(player) → reads the player's personal account balance
  • Economy.depositPlayer(player, amount) → deposits into personal account (atomic)
  • Economy.withdrawPlayer(player, amount) → withdraws from personal account (atomic)
  • Economy.has(player, amount) → checks personal account balance
  • Economy.createPlayerAccount(player) → auto-creates personal account with starting balance
  • Bank operations → return NOT_IMPLEMENTED (use MultiBank's group accounts instead)

Startup logs:

  • "Vault bridge enabled — MultiBank is now the economy provider." — success
  • "Vault not found; bridge disabled." — Vault not on server (this is fine)

Configuration: set vault.enabled: false in config.yml to disable the bridge.

Thread safety: All Vault operations use timeout-guarded async DB calls. The bridge is safe to call from any thread.

Developer API

Package

pt.henrique.multibank.api.MultiBankApi

Events are in:

pt.henrique.multibank.api.event.*

Getting the API

import pt.henrique.multibank.api.MultiBankApi;

// In your plugin's onEnable():
MultiBankApi api = getServer().getServicesManager().load(MultiBankApi.class);
if(api ==null){

        getLogger().

        warning("MultiBank not found! Economy features disabled.");
    return;
            }

API Methods

All methods return CompletableFuture for async execution. Chain with .thenAccept() / .thenCompose() for non-blocking use.

Account Resolution

CompletableFuture<Account> getPersonalAccount(UUID playerId)
CompletableFuture<Optional<Account>> getAccountById(String accountId)
CompletableFuture<List<Account>> listAccounts(UUID playerId)
CompletableFuture<Account> getPrimaryAccount(UUID playerId)
CompletableFuture<Void> setPrimaryAccount(UUID playerId, String accountId)

Balance Operations

CompletableFuture<Long> getBalanceCents(String accountId)
CompletableFuture<TransactionResult> deposit(String accountId, long cents, String reason, UUID actor)
CompletableFuture<TransactionResult> withdraw(String accountId, long cents, String reason, UUID actor)
CompletableFuture<TransactionResult> transfer(String fromAccountId, String toAccountId, long cents, String reason, UUID actor)

Group Management

CompletableFuture<String> createGroupAccount(UUID owner, String name)
CompletableFuture<Void> inviteMember(String accountId, UUID inviter, UUID target)
CompletableFuture<Void> setMemberRole(String accountId, UUID actor, UUID target, Role role)

Security

boolean isFrozen(String accountId)

Example Usage

// Get a player's primary balance
api.getPrimaryAccount(playerUuid).thenAccept(account -> {
    long cents = account.getBalanceCents();
    getLogger().info("Balance: " + (cents / 100.0));
});

// Deposit money
api.deposit(accountId, 5000L, "Shop purchase refund", actorUuid)
    .thenAccept(result -> {
        if (result.isSuccess()) {
            getLogger().info("Deposit OK, txId=" + result.transaction().txId());
        }
    });

// Transfer between accounts (atomic)
api.transfer(fromId, toId, 10000L, "Trade deal", actorUuid)
    .thenAccept(result -> {
        if (result.isSuccess()) {
            // Transfer complete — both balances updated atomically
        } else {
            // result.status() tells you why: INSUFFICIENT_FUNDS, ACCOUNT_FROZEN, etc.
        }
    });

// Create a group account
api.createGroupAccount(ownerUuid, "My Faction Bank").thenAccept(accountId -> {
    getLogger().info("Created group account: " + accountId);
});

// Check if an account is frozen
boolean frozen = api.isFrozen(accountId);

Custom Events

All events are in pt.henrique.multibank.api.event and are fired on the main server thread.

EventCancellableFields
BalanceChangeEventaccountId, oldBalance, newBalance, amount, actor, reason
TransferEventfromAccountId, toAccountId, amount, actor, reason
AccountCreateEventaccountId, type, owner, name
PrimaryAccountChangeEventplayerId, oldAccountId, newAccountId
AccountFrozenEventaccountId, frozen, actor
import pt.henrique.multibank.api.event.BalanceChangeEvent;

@EventHandler
public void onBalanceChange(BalanceChangeEvent event) {
  getLogger().info("Account " + event.getAccountId()
          + " balance changed by " + event.getAmount()
          + " cents. Reason: " + event.getReason());
}

Adding MultiBank as a Dependency

In your plugin's build.gradle.kts:

repositories {
    // Add the repo where MultiBank is hosted, or use a local jar
}

dependencies {
    compileOnly(files("libs/MultiBank-1.0.0.jar"))
}

In your paper-plugin.yml:

dependencies:
  server:
    MultiBank:
      load: BEFORE
      required: true  # or false if optional
      join-classpath: true

Configuration

See plugins/MultiBank/config.yml for all options:

SectionKey options
languageen_US, pt_PT (or any custom lang file)
starting-balanceStarting money for new accounts (default: 100.00)
currency.*Symbol, position, decimals, separators, name
database.*sqlite (default) or mysql with host/port/credentials
pay.*Enable/disable, min/max amounts, confirmation threshold
accounts.*Overdraft, invite expiry, max group accounts
admin.op-override.*Enable/disable OP override, allow console, negative balance rules
vault.enabledEnable/disable Vault bridge
top.*Mode (personal/all/primary), page size
security.*Rate limits, GUI debounce

Database Schema

TablePurpose
accountsAccount ID, type, owner UUID, name, frozen flag, created timestamp
balancesAccount ID → balance in cents (long)
account_membersAccount ID + member UUID + role
primary_accountsPlayer UUID → their primary account ID
invitesInvite ID, account, target, inviter, expiry
transactionsFull audit log: txId, timestamp, type, from/to accounts, amount, actor, reason, success
schema_versionMigration bookkeeping; upgrades are additive and idempotent
savings_statePer-account accrual watermark, low-water balance and interest earned
interest_accrualsOne row per (account, period) credited — the idempotency key for savings
loan_offersPeer-to-peer offers and their escrow state
loansContracts: principal, outstanding, arrears, rate, term, schedule position, status
loan_chargesOne row per (loan, period) charged — the idempotency key for instalments
garnishmentsDefaulted balances being collected from a debtor's income
credit_profilesRepayment record and score
certificatesSubscribed certificates and term deposits, with their custody
cert_accrualsOne row per (certificate, period) accrued
projects, project_contributions, project_votesCommunity projects, contributions and votes

Security Notes

  • Atomic transactions: All economy operations (deposit, withdraw, transfer, set) use database transactions — no partial updates possible
  • No double-spend: Transfer operations debit and credit both accounts in a single atomic transaction
  • SQLite WAL mode: Enabled for concurrent read/write safety with busy timeout
  • Rate limiting: Configurable per-player operation throttle (default: 30/min) prevents abuse
  • GUI debouncing: Click debounce (default: 250ms) prevents double-click exploits
  • Frozen accounts: Cannot send or receive any funds until unfrozen by admin
  • UUID-based identity: All internal operations use UUID — never trusts player names
  • Long cents storage: Balances stored as long cents — no floating-point precision issues
  • Transaction audit log: Every operation generates a unique txId UUID and is logged with actor, reason, and timestamp
  • Non-negative enforcement: Balances cannot go negative unless allow-overdraft is enabled
  • Vault timeout safety: Vault bridge operations use 10-second timeout to prevent server hangs
  • In-database arithmetic: every balance change is computed inside the SQL statement under the row's write lock. A read-modify-write in Java lets two threads observe the same balance and both write — the lost-update duplication this plugin was patched for
  • Idempotent accrual: interest and instalments claim their period through a primary key, so a scheduler misfire, a bulk catch-up or a manual re-run pays each period exactly once
  • Monotonic time: accrual watermarks advance by whole periods from their previous value, never to the wall clock; a clock moved backwards yields zero periods, and catch-up is clamped
  • Escrow before commitment: a lending offer debits the lender when it is posted, so it can never be backed by money since spent, and one balance cannot back two offers
  • Compare-and-swap contracts: accepting an offer, redeeming a certificate and completing a project all flip a row from an expected state; concurrent attempts produce one winner
  • Anti-arbitrage validation: a configuration where an interest-bearing product out-earns the cost of borrowing disables that product at startup rather than being accepted, and holding debt blocks investing — which also closes the 0%-loan-from-a-friend variant
  • Ledger reconciliation: /mb admin audit checks each escrow account against the contracts it backs and reports drift, which is the footprint any duplication bug leaves behind

Building from Source

Two targets from one source tree:

git clone <repo-url>
cd MultiBank

./gradlew build                 # 26.3 (default) — runs the test suite
./gradlew -Pmc=262 build        # 26.2
./gradlew -Pmc=1211 build       # 1.21.11

Output: build/libs/MultiBank-<version>+mc26.3.jar (or +mc26.2, +mc1.21.11).

Every target emits Java 21 bytecode. The 26.x APIs are compiled to class file 69 and can only be read by JDK 25, hence the JDK 25 toolchain; put one in /opt/jdk-25 or adjust org.gradle.java.installations.paths.

The tests exercise the banking logic against a real SQLite database, including a randomised 400-operation run that asserts the money supply never changes.

Requirements

  • Paper or Purpur 26.3 (2.0.0 for 26.2, 1.x for 1.21.x)
  • Java 25
  • (Optional) VaultUnlocked for legacy plugin economy support

License

GPL-3.0 — see LICENSE.

Последние версии

Все версии →
release

2.1.0+mc26.3

2.1.0+mc26.3
1 окт. 2026 г.195Minecraft: 26.3paper, purpur
Скачать
Что нового
## 2.1.0 Requires Minecraft 26.3 and Java 25. On 26.2 use 2.0.0; on 1.21.x use the 1.x release. ### Added - **Banking modules**, on by default and each switchable in `config.yml` without losing its data: - **Savings accounts**: interest on the lowest balance held during the period, so money parked a moment before the tick earns nothing. - **Term deposits**: capital locked in escrow for a fixed term at several times the savings rate. Interest stays hidden until it matures, and cancelling early returns exactly what went in. - **Loans** from the State or from other players. Player offers escrow the capital when they are made, and acceptance is a database compare-and-swap, so two players cannot take the same offer. Instalments are counted in Minecraft days of the borrower's own playtime. A loan can be paid down early with no further interest. A default hands the balance to garnishment, which takes a share of the borrower's future income. - **Certificates**: savings and treasury certificates modelled on the Portuguese instruments. - **The State**: a public treasury and the community projects players vote to fund. Delivering a project destroys the money, which is the economy's one deliberate sink. - **No module creates money.** Interest comes out of the treasury and is not paid if the treasury cannot afford it. Investment rates must stay below the cost of credit or the module turns itself off at start-up, and a player in debt cannot invest. `/mb admin audit` reconciles the escrow accounts against the contracts behind them. - A GUI for every module, with a shared amount picker in the same style as the Market's. ### Fixed - Faction accounts never switched on. ServerFactions enables after MultiBank because it uses MultiBank's economy, and the hook checked for it during MultiBank's own start-up, when it is never there yet. The hook is wired unconditionally now and links on first use. - GUI payments and the Vault bridge check the source account's transfer permission inside the same database transaction that moves the money, and reject amounts that are zero or smaller than one cent. ### Changed - Built against Paper 26.3 on Java 25 and tested on Paper 26.3 build 140, which Paper still marks as beta: there was no stable 26.3 build when this was released. `api-version` is now `26.3`. - The plugin now reports version 2.1.0. The 2.0.0 jar reported `1.0.0`. - The database schema migrates on first start. Balances and history are kept; 99 tests run against a real SQLite file, including a random run of 400 operations with the total money supply checked after every step.
release

2.0.0

2.0.0
6 авг. 2026 г.184Minecraft: 26.2paper, purpur
Скачать
Что нового
## 2.0.0 Build for Minecraft 26.2 on Paper and Purpur, compiled against paper-api 26.2 with Java 25. The build for 1.21.x stays available as version 1.0.0.
release

1.0.0

1.0.0
11 июл. 2026 г.84Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11paper, purpur
Скачать