Skip to content

Treasury's Economy API

Lachlan edited this page Jul 26, 2023 · 27 revisions

This page is incomplete and invalid as of Treasury v2.0.0.


⚠️ This page has not been updated for Treasury v2 ⚠️

Whilst we're working on updating all the documentation to the new API, please contact us if you have any questions.


Description

Treasury's main feature is its Economy API. The Economy API facilitates the interaction between consumer plugins to economy providers. Instead of a plugin needing to provide individual support for each economy provider out there, they can simply do it all through Treasury, which acts as a middleman between an economy provider plugin and whichever consumer plugin desires to utilize it.

Many Minecraft servers have adopted virtual economies over the years, necessitating the use of three main types of plugins to achieve it:

  1. An economy provider, such as TheNewEconomy, EssentialsX or Polyconomy. These store all of the player data for the economy (i.e. player's balances), and offers commands such as /pay and /baltop for simple balance management on an individual basis.
  2. An economy consumer plugin, such as JobsReborn, Shopkeepers or ChestShop. These create transactions (i.e. withdrawals and deposits) for players.
  3. An economy API, such as Treasury, Reserve or Vault. These sit in between the provider and consumer plugins to maintain very easy indirect integration between them.

The API is the result of multiple developers reflecting upon Vault's popular yet flawed API, then building a new API from scratch to solve all of the issues. Feel free to read more about the comparison with Vault's API here.

πŸ“œ Note: Treasury is not an economy provider or consumer. It is intentionally designed not to fulfill any of these tasks, since it is a library plugin.

πŸ”— Link: Click here to view Treasury's javadocs, which explain how all of the internals work.

Fundamental Systems

Treasury's Economy API is made up of several components which interlink to facilitate the economy system for servers.

Accounts

An Account is an object which holds balances in any amount of currencies (currencies are described below).

All accounts are bound by an identifier, which is simply any text (String), e.g. MiningCompanyBank or ValleyTown.

There are two types of accounts, and two sub-systems of the Accounts system. Read about it below:

Type: Player Accounts

Player accounts are owned by an individual player, which is assumed to have full control over all of their balances. These accounts are bound to the owning player's unique ID (UUID) of their Minecraft account.

Example usage:

  • Player Notch joins the server for the first time. "Welcome!"
  • Economy service providing plugin, SampleEconomy:
    • creates a new account for Notch.
    • adds the default balance of $50 Dollars to Notch.
  • Economy consumer plugin DailyReward gives a join reward to Notch of 1 Token.
  • Player jeb_ gifts the player Notch an amount of $12.34 Dollars.
  • Player Notch purchases tools from the server shop provided by the economy consumer plugin ChestShop using all of their balance in Dollars. They still have 1 Token.

Type: Non-Player Accounts

Non-player accounts are different to player accounts in that they are not bound to any player. Instead, they can hold a list of 'account members' (this sub-system is described below), each of which has their own 'account permissions' (this sub-system is also described below).

Our Non-Player Accounts contrast against Vault's Bank Accounts, as the latter requires an owning player and has no permissions functionality. We believe our system is better since there are many cases where an account is desired which is not bound to a specific player, such as a town/faction balance.

Sub-System: Account Members

Account Members are players who have at least one Account Permission (described below) with an associated account. For example, a village could have a non-player account named VillageBank, with Notch and jeb_ as account members.

πŸ“œ Note: By design, as a default, Treasury limits Player Accounts to only having one member, being the Player associated with each account. This can be easily overriden by the Provider, although it is not recommended. We advise consumer plugins to avoid modifying account members on Player Accounts.

Sub-System: Account Permissions

Account Members can have one or more Account Permissions. These signal what each account member is allowed to do with the account. For example, an account member may have the BALANCE permission (to view its balance) and the DEPOSIT permission, but not the WITHDRAW permission.

Account permissions are handled with TriState values, being TRUE, FALSE or UNSPECIFIED.

πŸ“œ Note: By design, as a default, Treasury limits Player Accounts to having the Player associated with each PlayerAccount to hold all available Account Permissions. This can be easily overriden by the Provider, although it is not recommended. We advise consumer plugins to avoid modifying account permissions on Player Accounts.

⚠ Warning: Treasury does not care if a transaction occurs with a user who does not have adequate Account Permissions to do so. The economy provider is expected to handle the permissions side of things, Treasury just sets the standard of which permissions exist, how to retrieve them, etc. Such is the case because Treasury is designed to possess minimal interference in its APIs, which gives more control to the plugins utilizing them.

Currencies

Another one of Treasury's signature features is multi-currency support through our Currency interface.

Even economy consumers can attempt to register new currencies! Of course, this is all in the control by the economy provider in use, such requests can be outright rejected, it's all up to the economy provider. πŸ˜‰

Each currency has its own:

  • identifier (e.g. dollar).
  • symbol (e.g. $)
  • decimal character (e.g. .)
  • singular display name (e.g. Dollar)
  • plural display name (e.g. Dollars)
  • starting balance (e.g. $50.00)
    • Treasury allows starting balances to differ per-player, whilst still also allowing a 'global' starting balance for each currency to be retrieved. (e.g. Notch's starting balance is $100.00)

An economy provider is also expected to make a single currency a primary one - e.g. Dollars. This acts as the 'main' currency which is used most commonly.

Treasury also facilitates the conversion between currencies, e.g. from Dollars to Tokens. This is handled by the economy provider, although it is common to see currencies being assigned a 'relative value multiplier' to represent how much each currency is worth. For example, Tokens may be worth 12x more than Dollars, so it requires $12 to convert to 1 Token.

Lastly, Treasury makes it simple for any plugin to get a formatted amount of a currency. For example, a balance of 10.239014 in the currency dollar can be translated to $10.24 or 10.24 Dollars. This can even be parsed from the formatted value, back into the amount itself! Lots of customization potential! πŸ˜ƒ

Subscriptions

Gone are the days where your server freezes whilst your economy provider makes a query to the database. Treasury has made achieving concurrency throughout the Economy API extremely easy, through the EconomySubscriber interface.

This interface is very simple: economy consumers call a method such as retrieveBalance, and await a response from the economy provider. This is usually achieved within milliseconds, depending on the speed of the economy provider's database. The economy provider will respond with a success (accompanied with the value requested), or a failure (accompanied with an EconomyException, which describes whichever error that occurred). Most of the common errors are specified in the EconomyFailureReason enum, which drop right into the exception mentioned.

What this achieves, is that you can ask the economy provider to do something, without having to expect it to happen instantly. Most of the time it doesn't need to happen instantly, but other APIs such as Vault force it to, lagging the server unnecessarily.

The EconomySubscriber was designed with the intention to be very easy for lesser skilled developers to adopt. If you are comfortable with doing so, converting it to a CompletableFuture and using that will make your code cleaner (and you can use additional logic thanks to the class). This is achieved using the EconomySubscriber#asFuture method. In addition, where any subscriptions are required to happen instantly (like in Vault), you can utilize the CompletableFuture conversion to stall the thread until the request is complete (not recommended of course).

Example (EconomySubscriber), setting a balance from a command:

public void setBalance(
        @NotNull CommandSender sender,
        @NotNull UUID target,
        @NotNull BigDecimal balance,
        @NotNull Currency currency
) {
    final EconomyProvider economy = //Obtain provider

    // Create initiator object: check if CommandSender is a player or not
    final EconomyTransactionInitiator<?> initiator;
    if(sender instanceof Player) {
        initiator = EconomyTransactionInitiator.createInitiator(Type.PLAYER, ((Player sender).getUniqueId());
    } else {
        initiator = EconomyTransactionInitiator.SERVER;
    }

    economy.retrievePlayerAccount(target, new EconomySubscriber<>() {
        @Override
        public void succeed(@NotNull PlayerAccount account) {
            account.setBalance(balance, initiator, currency, new EconomySubscriber<>() {
                @Override
                public void succeed(@NotNull BigDecimal newBalance) {
                    sender.sendMessage(String.format("Set balance to %s.", newBalance));
                }

                @Override
                public void fail(@NotNull EconomyException exception) {
                    sender.sendMessage("Something went wrong!");
                }
            });
        }

        @Override
        public void fail(@NotNull EconomyException exception) {
            sender.sendMessage("Something went wrong!");
        }
    });
}

Example (CompletableFuture), setting a balance from a command:

public void setBalance(
    @NotNull CommandSender issuer,
    @NotNull UUID target,
    @NotNull BigDecimal balance,
    @NotNull Currency currency
) {
    final EconomyProvider economy = // Obtain provider

    // Create initiator object: check if CommandSender is a player or not
    final EconomyTransactionInitiator<?> initiator;
    if(sender instanceof Player) {
        initiator = EconomyTransactionInitiator.createInitiator(Type.PLAYER, ((Player sender).getUniqueId());
    } else {
        initiator = EconomyTransactionInitiator.SERVER;
    }

    // Then we need to obtain the account.
    EconomySubscriber.asFuture(subscriber -> economy.retrievePlayerAccount(target, subscriber))

    // Then we set the balance.
    .thenCompose(account -> EconomySubscriber.asFuture(subscriber -> account.setBalance(balance, initiator, currency, subscriber)))

    // And then we can use the final value however we like.
    .whenComplete((newBalance, exception) -> {
        if (exception != null) {
            ender.sendMessage("Something went wrong!");
        } else {
            sender.sendMessage(String.format("Set balance to %s.", newBalance));
        }
    });
}

Transactions

Transactions are objects which contain data about a deposit or withdrawal into an account.

Each transaction contains the following data:

  • Currency ID: what currency was used
  • Initiator: who/what initiated the transaction (player, plugin or server)
  • Timestamp (now by default): when the transaction occurred
  • Type: whether the transaction was a deposit or withdrawal
  • Reason (optional): why the transaction occurred
  • Amount: how much money was involved in the transaction
  • Importance: how important the transaction was. Example:
    • HIGH: payments with /pay
    • MEDIUM: transactions in the server store
    • LOW: payments from quests & jobs

Treasury also allows for an account's transaction history to be retrieved from the economy provider.

Economy API Version

To enhance compatibility between API changes, Treasury makes it very easy for economy consumers to see which Economy API Version an economy provider is using.

The versions are stored in the EconomyAPIVersion enum, formatted as v[MAJOR].[MINOR] (notice the exclusion of [PATCH], which is only used for Treasury's plugin version).

Optional Features

Negative Balances

Economy providers can choose whether they wish to store balances of accounts which are negative (below-zero). Without negative balance support, an economy provider should deny any account withdrawals that will result in a negative balance.

Bukkit Events

Treasury offers a selection of Events for the Bukkit platform that allow economy consumers to listen to them and intercept transactions if they wish to. This is a feature which is optional for economy providers to support. Treasury does not fire these events, the economy providers do.

  • AccountEvent: Not meant to be fired or listened to, simply a class for other account-based events to extend from. Implements Cancellable.
  • AccountTransactionEvent: Fired when any account attempts to make a transaction. Implements Cancellable.
  • NonPlayerAccountTransactionEvent: Fired when a non-player account attempts to make a transaction. Implements Cancellable.
  • PlayerAccountTransactionEvent: Fired when a player account attempts to make a transaction. Implements Cancellable.

Plugin-Side Utilities

The Treasury plugin offers a few utilities to aid in managing the economy. These being:

  1. Simple migration between economy providers, using /treasury economy migrate;
  2. View generic information about the primary economy provider on the server, using /treasury economy info.

πŸ“œ View more info about Treasury's commands here.

More utility features may be added upon request. The current list is not final. πŸ™‚

How to use Individual Methods

All of Treasury's API methods are documented in the javadocs.

Register your EconomyProvider

If your plugin is meant to provide the economy service to the server, then you will need to use the Treasury Services API so that other plugins know where to access your economy providing class.

Clone this wiki locally