Skip to content

Quickstart: Economy Consumer

Lachlan edited this page Jun 25, 2023 · 9 revisions

This page is complete and valid as of Treasury v2.0.0.


Just to make sure you're at the right guide...

Integrating your economy consumer plugin with Treasury?

An economy provider is a plugin which facilitates the virtual economy of a server. All other plugins interacting with the existing virtual economy set up by the provider plugin(s) installed are considered economy consumers instead.

I'm still not sure... help!

For instance, plugins like Polyconomy, TheNewEconomy, and iConomy would not be considered consumers, rather, providers, as they facilitate the virtual economy of a server. They store each player's balances in their own database and control the whole virtual economy. Some economy providers choose to also contain features like economy consumer plugins; it's possible to be both at the same time.

Instead, plugins like JobsReborn, ChestShop, and DailyRewards would be considered consumers, as they don't facilitate the economy, they just utilise an economy which is already provided for them. They don't hold player's balances and other data, only requesting modifications to it whenever needed (such as withdrawing $50 when a player buys an iron ingot at a shop).

If you're still confused, contact us and we'll let you know. :)


If you've kept reading, looks like your plugin is an economy consumer plugin, which will need to access the existing economy provider and issue it with requests to view and manage the account data it maintains. For instance, if you've got a Shop plugin, you'll almost certainly need to deposit and withdraw money from players' balances.

Before we continue, make sure you've already imported the Treasury API into your project, explained at the top of the Quickstart main page.

The remainder of this guide assumes you're developing for the Bukkit platform; instructions are similar for other platforms too.

There are a few things you may want to add in your code. Firstly, check if Treasury is enabled on the server prior to accessing its APIs:

// global variable you can use throughout your project so that all code using the Treasury APIs can check this first.
// (recommendation: make it private and use getter and setter methods)
boolean useTreasury = false;

// in your onEnable method, you can add something like this:
if(Bukkit.getPluginManager().isPluginEnabled("Treasury")) {
    useTreasury = true;
} else {
    getLogger().warning("Treasury is not installed; disabling its integration.");
}

Secondly, create a method which accesses the primary economy provider loaded on the server:

// Retrieves the primary economy provider registered through Treasury.
// If no registration is found, a runtime exception is thrown.
// 'why create a method for this rather than just locating and storing the instance once?' -> the preferred economy provider can change over time if the server owner decides, best to keep it safe and just retrieve the preferred economy provider whenever we need to access it.
public EconomyProvider getPrimaryEcoProvider() {

    // Try to find an economy provider service registration.
    final Optional<Service<EconomyProvider>> serviceOpt = ServiceRegistry.INSTANCE.serviceFor(EconomyProvider.class);

    // Require an economy provider to be registered as a service before continuing.
    if(serviceOpt.isEmpty()) {
        throw RuntimeException("Expected an Economy Provider to be registered through Treasury; found none");
    }

    // Get the service instance from the optional, then get the economy provider from the service object.
    return serviceOpt.get().get();
}

Lastly, let's learn how to access a player account and manipulate their balance:

// handles logic when players join the server
@EventHandler
public void onJoin(final PlayerJoinEvent event) {
    final Player player = event.getPlayer();
  
    // if the player has joined for the first time, award their newbie bonus.
    if(!player.hasPlayedBefore()) {
        // *** newbie bonus deposit is handled asynchronously by method below ***
        awardNewbieBonus(player);
    }
}

// awards newbie bonuses to players.
// specifically, this bonus is $100 in whichever the primary currency the economy provider has set.
// only meant to be applied to players who haven't joined the server previous to their current session.
// *** deposit is handled asynchronously ***
private void awardNewbieBonus(final Player player) {

    // let's make sure Treasury is installed
    if(!useTreasury) {
        // server admin has already been warned Treasury integration is disabled, so just return silently here
        return;
    }

    // let's get the economy provider instance. conveniently we have the method from earlier...
    final EconomyProvider ecoProvider = getPrimaryEcoProvider();

    // establish some other data for later
    final BigDecimal amount = new BigDecimal(100); // the amount of the deposit.
    final Cause cause = Cause.plugin(new NamespacedKey("YourPluginNameHere", "NewbieBonus")); // Treasury wants to log the cause of the transaction we're going to make.
    final Currency currency = ecoProvider.getPrimaryCurrency(); // retrieve the primary currency set by the provider. if you want to allow a custom currency then you can also find a currency by name.

    // time to deal with economy player account data!
    // this is the hardest part due to the use of concurrent programming.
    ecoProvider
        .accountAccessor() // grab the account accessor instance so we can get the player's account.
        .player() // we're looking to access a player account, so go for a PlayerAccountAccessor.
        .withUniqueId(player.getUniqueId()) // specify which player account we're looking for - it'll be under the player's UUID.
        .get() // ask the economy provider for the player account; it'll create the account if it doesn't exist yet. this returns a CompletableFuture, not the account just yet.

        // once we've got the account, deposit the balance and merge that resulting CompletableFuture back into this one using thenCompose.
        .thenCompose(account -> account.depositBalance(amount, cause, currency))
        
        // once the balance has been deposited, let's send them a message so they know their account has been updated
        .thenRun(() -> player.sendMessage(ChatColor.AQUA + "Welcome to the server! You have been awarded $" + amount + " for your first join."))

        // let's handle any exceptions that come up.
        .exceptionally(ex -> {
            player.sendMessage(ChatColor.RED + "Unable to deposit your welcome gift, please inform an admin.");
            getLogger().severe("Unable to award " + player.getName() + " with their newbie gift; see stack trace below");
            ex.printStackTrace();
        });
    
}

That looks like a lot, right? Treasury is verbose by nature since it requires you to utilise it in a concurrent manner, but this is far better for users' servers to run.