Skip to main content

Numerical Storage

Named numeric balances per player with levels, interest, a bank menu and placeholders.

Freev0.15Economy & crafting
RequiresTypeWriter0.9GUI (GuiAndDialogs)PlaceholderAPIoptionalVaultoptionalProfilesoptional

Overview​

Numerical Storage lets you create named numeric stores for each player: a bank account, a mana pool, a token wallet, a reserve of any resource. You describe a store once in a definition, and the extension saves one balance and one level per player.

A store can have levels, each with a maximum capacity. Players deposit into and withdraw from it through an inventory menu built with the GUI extension. The money can come from your economy plugin through Vault, or from any placeholder and console command you choose. Interest can be added on a schedule, and stores can be reset, edited or opened with commands.

Balances are saved in a TypeWriter artifact and written asynchronously, so a deposit never blocks the server thread. The balances, levels and capacity are exposed as PlaceholderAPI placeholders for scoreboards, holograms and menus.

Features​

  • Several stores — create as many definitions as you need, each with its own name, prefix, levels and menu.
  • Levels and capacity — every level has a maximum balance; players upgrade to raise it.
  • Level requirements — unlock a level with placeholder or fact comparisons, and optionally take a cost.
  • Interest — a cron schedule, a global rate, a rate per level and a rate per permission, with a cap per cycle.
  • Vault or commands — deposits and withdrawals go through Vault, or through a placeholder plus console commands.
  • Transfers — move a balance between two stores atomically.
  • Menu buttons — fixed amount, percentage, all, custom amount (dialog), sub-menus, upgrade and close.
  • Safe persistence — asynchronous saves, one lock per artifact and an automatic migration of old files.
  • Profile mode — optional per-profile balances with MMOProfiles.
  • Placeholders and commands — read values anywhere and edit them from the console.

Installation​

  1. Install the GUI extension: Numerical Storage needs it for its menus. The GUI extension is published under the name GuiAndDialogs.
  2. Install PlaceholderAPI if you want to use the %typewriter_ns_...% placeholders.
  3. Optional: install Vault (the extension is built against VaultUnlocked) and an economy plugin, to use the VAULT transaction mode.
  4. Optional: install MMOProfiles to use profileMode.
  5. Put the Numerical Storage JAR in plugins/Typewriter/extensions/ and restart the server.

The custom amount window uses Paper dialogs, so it needs a Paper version that supports them (Minecraft 1.21.6 or later).

Back up before updating

Since version 0.9, new artifacts use a technical identifier and a JSON schema version 2. The first time an old file is read, it is copied to the new path, saved under backups/numericalstorage/ and removed. Keep a copy of the TypeWriter assets/ folder before you update.

Getting started​

Create a bank paid with your Vault economy:

  1. In the TypeWriter web panel, open a manifest page and add a Player Numerical Storage artifact. Leave artifactId empty: TypeWriter generates it.
  2. Add a Numerical Storage Definition. Set displayName (for example Bank), prefix and link the artifact in artifact.
  3. In levels, add the first level with a limit of 1000, then a second one with 5000. Fill depositMessage and levelUpMessage if you want custom text.
  4. In transaction, set transactionMode to VAULT.
  5. In menu.transactionButtons, add a button of type DEPOSIT with amountType FIXED, an amount of 100, and the item to show. Add a second one of type WITHDRAW.
  6. Add a Open Numerical Storage Menu action. Link the definition.
  7. In its layoutPool, add a simple layout with the id main and set mainLayoutId to main. In the layout, add an item with buttonPrefix set to numericalstorage_button: and buttonType set to DEPOSIT_FIXED:100. Do the same with WITHDRAW_FIXED:100, UPGRADE_LEVEL and NAV_CLOSE.
  8. Link the action to an NPC dialogue or a command event, publish, and open the bank in game. You can also run /typewriter ns open <definition>.
One open-menu entry per definition

After each transaction the menu is reopened to show the new balance. The extension looks for an Open Numerical Storage Menu entry linked to the same definition. Without one, the menu is simply closed.

How it works​

The menu is a normal GUI layout. The Open Menu action opens it with the title and number of rows of the definition (menu.title, menu.rows, 1 to 6), and your layoutPool decides where each item goes. The extension turns an item into a working button when the item has buttonPrefix set to numericalstorage_button: and a buttonType from this list:

buttonTypeWhat the button does
DEPOSIT_FIXEDDeposit a fixed amount. Add the amount after a colon, for example DEPOSIT_FIXED:100.
DEPOSIT_PERCENTDeposit a percentage of the source balance, for example DEPOSIT_PERCENT:50.
DEPOSIT_CUSTOMClose the menu and ask for an amount in a dialog.
DEPOSIT_ALLDeposit the source balance, up to the remaining capacity. This needs an amountPlaceholder: in VAULT mode, the button tries to deposit the whole remaining capacity and fails if the economy balance is lower.
DEPOSIT_SUBMENUOpen a sub-menu of deposit buttons. The sub-menu id follows the colon.
WITHDRAW_FIXED, WITHDRAW_PERCENT, WITHDRAW_CUSTOM, WITHDRAW_ALL, WITHDRAW_SUBMENUSame as above, for withdrawals. Percent is taken from the stored balance.
TRANSFER_FIXED, TRANSFER_PERCENT, TRANSFER_ALLMove balance to another definition. The button comes from a transactionButtons entry of type TRANSFER with a transferTarget.
SUB_MENUOpen the sub-menu whose id follows the colon, for example SUB_MENU:deposit_options.
UPGRADE_LEVELUpgrade to the next level, or show the max level item.
NAV_CLOSEClose the menu.

When buttonType has an amount (DEPOSIT_FIXED:100), the extension picks the entry of menu.transactionButtons with the same type, amount type and amount, and uses its template as the item. Without an amount, it uses the first matching entry. When nothing matches, a plain dye item with the default label is shown.

Sub-menus are defined in menu.subMenus, as a map from an id to a title, a number of rows, transaction buttons placed in slots, a back button and a close button.

A transfer button needs the transferTarget of the other definition (its entry id), and the player must have room in the target store.

Commands​

All commands are sub-commands of /typewriter (alias /tw). <definition> is a definition entry. The optional [target] selects another player; without it the command applies to the sender.

CommandPermissionDescription
/typewriter ns open <definition> [target]typewriter.ns.openOpen the menu of the storage, through its Open Menu entry.
/typewriter ns add <definition> <amount> [target]typewriter.ns.addAdd to the balance. The capacity is not checked.
/typewriter ns remove <definition> <amount> [target]typewriter.ns.removeRemove from the balance, down to 0.
/typewriter ns level <definition> <level> [target]typewriter.ns.levelSet the level and reset the balance to 0.
/typewriter ns reset <definition> [target]typewriter.ns.resetReset the level to 1 and the balance to 0.

The commands /typewriter numstorage tx ..., upgrade and back_main are used internally by the menu buttons. They do nothing for a player without an open menu.

With profileMode on, add, remove, level and reset change the balance and level of the target's profile, like the menu does.

Command replies​

The replies of add, remove, level, reset and open, and the error replies below, are the fields of adminMessages on the definition. They are MiniMessage. Each one can use <player>, <storage> (the id of the definition) and <prefix>.

FieldExtra placeholderDefault
resetMessagenoneNumerical storage reset for <player>.
levelSetMessage<level>Set numerical storage level to <level> for <player>.
addMessage<amount>Added <amount> to <player>.
removeMessage<amount>Removed <amount> from <player>.
noMenuMessagenoneThe message shown when the storage has no Open Menu entry.
invalidAmountMessage<amount> (the value typed)Invalid amount '<amount>'. Use a positive number. Shown by add and remove when the amount is not a positive number.
noArtifactMessagenoneThe message shown by add, remove, level and reset when no artifact is linked to the definition.
noActiveProfileMessagenone<player> has no active profile. Shown by add, remove, level and reset, and to a player who clicks a menu action, when profileMode is on and the player has no active MMOProfiles profile. Nothing is written.

The defaults start with the Typewriter prefix. Three texts belong to no definition, so they are snippets in snippets.yml instead: numericalstorage.command.player_only (the menu button commands run by a non-player), and the placeholder texts numericalstorage.placeholder.interest_disabled (Disabled), numericalstorage.placeholder.interest_error (Error) and numericalstorage.placeholder.capacity_unlimited (the infinity sign). The interest_cooldown time units are the snippets numericalstorage.duration.days, hours, minutes, seconds (each a template with {value}, {value}j by default) and numericalstorage.duration.separator.

Permissions​

PermissionGrants
typewriter.ns.open/typewriter ns open.
typewriter.ns.add/typewriter ns add.
typewriter.ns.remove/typewriter ns remove.
typewriter.ns.level/typewriter ns level.
typewriter.ns.reset/typewriter ns reset.
Permissions in interestRatesGive a player a specific interest rate. The first permission of the list that the player has is used.

Placeholders​

Replace <id> with the entry id of the definition. They need PlaceholderAPI and are read from a cache, so they are safe to use in scoreboards. With profileMode on, balance, level, capacity and interest follow the profile the player is playing, like the menu does.

PlaceholderReturns
%typewriter_ns_balance_<id>%The current balance.
%typewriter_ns_level_<id>%The current level, starting at 1.
%typewriter_ns_capacity_<id>%The capacity of the current level, or the infinity sign when no level is defined.
%typewriter_ns_interest_<id>%The interest rate that applies to the player, in percent.
%typewriter_ns_interest_cooldown_<id>%The time until the next scheduled interest cycle, or Disabled.
%typewriter_ns_name_<id>%The displayName of the definition.
%typewriter_ns_prefix_<id>%The prefix of the definition.

Entries​

4 entries, generated from the extension source.

Actions2
Artifacts1
Manifests1

Troubleshooting​

The menu opens empty or buttons do nothing. Check that each item has buttonPrefix set to numericalstorage_button: exactly and a valid buttonType. If the prefix is wrong, the item stays a normal item with no action. Also check that mainLayoutId matches the id of a layout in layoutPool.

The menu closes after each deposit. The menu is reopened through an Open Numerical Storage Menu entry linked to the same definition. Add one, even if you open the menu with another entry.

"Cannot add ... Would exceed limit". The storage is at the capacity of the player's level (levels[].limit). Upgrade the level or raise the limit. With no level defined, there is no limit.

A deposit in INTERNAL mode fails. The player's source balance is read with amountPlaceholder and must return a plain number. The addCommand runs from the console before the deposit and must succeed. Use {player} and {amount} in the command.

"Economy system is not available." transactionMode is VAULT but Vault or an economy provider is missing. Install both, then restart.

Interest is not paid. Interest is applied when a player joins, for the cron cycles that passed since their last payment (at most 100 cycles). interestEnabled must be on and interestCron filled in. The first join only records the starting time. A balance at the capacity of its level earns nothing.

The upgrade asks for the capacity as its cost. When the next level has no criteria, the cost of an upgrade is the limit of the current level, taken from the stored balance. Add criteria to the next level to define a different requirement.

Placeholders show the balance of another profile. With profileMode on, the placeholders and the upgrade button read the balance and level of the profile the player has selected. Without MMOProfiles they read the player's own balance. With MMOProfiles and no active profile (profile selection screen, after /mmoprofiles quit), they show 0, because the player's UUID is the id of their Main profile and its balance must not be shown or credited. Writes are refused with noActiveProfileMessage, and the interest of a profile is settled when it is selected.

A placeholder is empty right after a restart. The storage is loaded in the background at join, so the first read can come before the load ends; the next read shows the stored value.

The placeholder is empty. The id in the placeholder must be the entry id of the definition, not its name. Run /papi parse me %typewriter_ns_balance_<id>% to test.

  • GUI — the menu engine used for the layouts and buttons.
  • MMOProfiles — optional, used by profileMode.