Physical Notes (/vault withdraw)
Converts digital balance into redeemable physical paper items. Each note carries an HMAC-SHA256 signature that makes duplication via NBT / external plugins infeasible.
Withdrawing Notes
vault.withdraw (default true).
Flow:
- Validates positive amount and withdraws from wallet with
TxType.NOTE_WITHDRAW - Generates a random 16-char hex
noteId(truncated UUID) - Builds an
ItemStackofMaterial.PAPERwith display name + i18n lore - Stores signed metadata and delivers the item to the player
Secure Storage — Dual Layer
Layer 1: PersistentDataContainer (1.14+)
If theNamespacedKey + PersistentDataContainer API is available at runtime, 6 keys are saved via reflection (PhysicalNoteService.java:89–132):
Signed payload:
noteId|cid|amount|issuerId|issuedAt
Layer 2: Legacy Lore HMAC (1.8 – 1.13)
If PDC does not exist, the signature written as the last lore line is validated:Dynamic i18n Label Extraction
For compatibility with 11 languages and pre-v2.1 legacy notes,extractFallback() detects prefixes such as “Nº serie”, “Importe”, “Moneda” using labelStarts() + hardcoded fallback lists (PhysicalNoteService.java:408–482):
Serial — Detects: Nº serie / Serial / S/N / Número de série / Seriennummer / Numer seryjny / Серийный номер / 序列号 / 序號 / क्रम संख्या
Amount — Detects: Importe / Amount / Valor / Montant / Betrag / Bedrag / Kwota / Сумма / 金额 / 金額 / राशि
Currency — Detects: Moneda / Currency / Moeda / Devise / Währung / Valuta / Waluta / Валюта / 货币 / 貨幣 / मुद्रा
Additionally parseAmountLoose() interprets decimal/thousand separators . and , based on position (last occurrence = decimal).
Redeeming Notes
Right-click while holding the note (air or block) triggersPlayerInteractEvent. Flow:
extractNoteData()validates PDC first, then lore fallback- Checks
redeemed_notes.ymltable for already-redeemed IDs - If new: deposits the amount →
TxType.NOTE_REDEEM→ marks as redeemed - Removes 1 unit from the stack (or
setItemInHand(null)if quantity = 1)
Anti-dupe Window + redeemed_notes
- redeemed_notes.yml stores the global list of redeemed IDs (persisted on disk)
ConcurrentHashMapin memory as a fast cache- If HMAC does not match or the ID was already used, returns
note.redeem.invalidornote.redeem.already_redeemed
HMAC Secret
Generated viaSecureRandom → 32 bytes and saved in .note-secret.dat (hidden with attrib +H on Windows). Without this file, no existing note can be validated.