-
Notifications
You must be signed in to change notification settings - Fork 14
Treasury's Economy API
This page is incomplete and invalid as of Treasury v2.0.0.
Whilst we're working on updating all the documentation to the new API, please contact us if you have any questions.
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:
- 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. - An economy consumer plugin, such as JobsReborn, Shopkeepers or ChestShop. These create transactions (i.e. withdrawals and deposits) for players.
- 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.
Treasury's Economy API is made up of several components which interlink to facilitate the economy system for servers.
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:
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
toNotch
.
- creates a new account for
- Economy consumer plugin
DailyReward
gives a join reward toNotch
of1 Token
. - Player
jeb_
gifts the playerNotch
an amount of$12.34 Dollars
. - Player
Notch
purchases tools from the server shop provided by the economy consumer pluginChestShop
using all of their balance inDollars
. They still have1 Token
.
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.
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.
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.
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
)
- Treasury allows starting balances to differ per-player, whilst still also allowing a 'global' starting balance for each currency to be retrieved. (e.g.
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! π
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 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.
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).
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.
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
.
The Treasury plugin offers a few utilities to aid in managing the economy. These being:
- Simple migration between economy providers, using
/treasury economy migrate
; - 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. π
All of Treasury's API methods are documented in the javadocs.
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.