Skip to content

🔌 API Documentation

Essentials Plus provides a comprehensive Economy API that allows other plugins and mods to interact with the player balance system. This guide will help you integrate the API into your own projects.

📦 Installation

Maven

Add the Essentials Plus JAR as a system dependency to your pom.xml:

xml

<dependencies>
    <dependency>
        <groupId>de.fof1092</groupId>
        <artifactId>essentialsplus</artifactId>
        <version>1.15.13</version>
        <scope>system</scope>
        <systemPath>${project.basedir}/libs/essentialsplus-1.15.13.jar</systemPath>
    </dependency>
</dependencies>

TIP

Place the Essentials Plus JAR file in a libs folder in your project root, or adjust the systemPath to point to your JAR location.

Gradle

Add the JAR as a file dependency to your build.gradle:

gradle
dependencies {
    compileOnly files('libs/essentialsplus-1.15.13.jar')
}

TIP

Place the Essentials Plus JAR file in a libs folder in your project root, or adjust the path accordingly.

Important

Essentials Plus is not open source and not available via public repositories. You must:

  1. Obtain the JAR file from the official source
  2. Include it as a soft dependency in your project
  3. Use system scope (Maven) or compileOnly (Gradle) to ensure it's not bundled
  4. Ensure Essentials Plus is installed on the server separately

💰 Economy API

The Economy API provides asynchronous access to player balances with full event support and transaction logging.

Features

  • Fully Asynchronous - All operations return CompletableFuture
  • Event System - Cancellable BalanceChangeEvent before every transaction
  • Thread-Safe - Dedicated thread pool for economy operations
  • Transaction Logging - Detailed logging for auditing and debugging
  • Configurable Limits - Min/max balance validation
  • UUID & Name Support - Query by player UUID or username

Quick Start

java
import de.fof1092.essentialsplus.economy.EconomyAPI;

import java.util.UUID;

public class MyPlugin {

    public void rewardPlayer(UUID playerUUID, double amount) {
        // Add money to player's balance
        EconomyAPI.increaseBalance(playerUUID, amount, "Quest Reward")
                .thenAccept(newBalance -> {
                    System.out.println("New balance: " + EconomyAPI.formatCurrency(newBalance));
                })
                .exceptionally(ex -> {
                    System.err.println("Failed to add balance: " + ex.getMessage());
                    return null;
                });
    }
}

📚 API Reference

Balance Queries

Get Balance

java
// By UUID
CompletableFuture<Double> getBalance(UUID playerUUID)

// By username
CompletableFuture<Double> getBalance(String playerName)

Example:

java
EconomyAPI.getBalance(playerUUID)
    .thenAccept(balance -> {
        System.out.println("Player has: " + balance);
    });

Check Sufficient Balance

java
// By UUID
CompletableFuture<Boolean> hasBalance(UUID playerUUID, double amount)

// By username
CompletableFuture<Boolean> hasBalance(String playerName, double amount)

Example:

java
EconomyAPI.hasBalance(playerUUID, 1000.0)
    .thenAccept(hasEnough -> {
        if (hasEnough) {
            System.out.println("Player can afford this item!");
        }
    });

Balance Modifications

Set Balance

Sets the player's balance to an exact amount.

java
// By UUID
CompletableFuture<Double> setBalance(UUID playerUUID, double newBalance, String reason)

// By username
CompletableFuture<Double> setBalance(String playerName, double newBalance, String reason)

Example:

java
EconomyAPI.setBalance(playerUUID, 5000.0, "Admin reset")
    .thenAccept(newBalance -> {
        System.out.println("Balance set to: " + newBalance);
    })
    .exceptionally(ex -> {
        System.err.println("Error: " + ex.getMessage());
        return null;
    });

Increase Balance

Adds a positive amount to the player's balance.

java
// By UUID
CompletableFuture<Double> increaseBalance(UUID playerUUID, double amount, String reason)

// By username
CompletableFuture<Double> increaseBalance(String playerName, double amount, String reason)

Example:

java
EconomyAPI.increaseBalance(playerUUID, 250.0, "Daily reward")
    .thenAccept(newBalance -> {
        player.sendMessage("You received 250$! New balance: " + newBalance);
    });

TIP

The amount must be positive. Use changeBalance() for negative amounts.

Decrease Balance

Subtracts a positive amount from the player's balance.

java
// By UUID
CompletableFuture<Double> decreaseBalance(UUID playerUUID, double amount, String reason)

// By username
CompletableFuture<Double> decreaseBalance(String playerName, double amount, String reason)

Example:

java
EconomyAPI.decreaseBalance(playerUUID, 100.0, "Item purchase")
    .thenAccept(newBalance -> {
        player.sendMessage("Purchase successful! Remaining: " + newBalance);
    })
    .exceptionally(ex -> {
        player.sendMessage("Purchase failed: " + ex.getMessage());
        return null;
    });

Change Balance

Changes the balance by a positive or negative amount.

java
// By UUID
CompletableFuture<Double> changeBalance(UUID playerUUID, double changeAmount, String reason)

// By username
CompletableFuture<Double> changeBalance(String playerName, double changeAmount, String reason)

Example:

java
// Add money
EconomyAPI.changeBalance(playerUUID, 500.0, "Bonus");

// Remove money
EconomyAPI.changeBalance(playerUUID, -500.0, "Tax");

Transfers

Transfer Balance

Transfer money from one player to another.

java
// By UUID
CompletableFuture<Void> transferBalance(UUID fromUUID, UUID toUUID, double amount, String reason)

// By username
CompletableFuture<Void> transferBalance(String fromName, String toName, double amount, String reason)

Example:

java
EconomyAPI.transferBalance(senderUUID, receiverUUID, 100.0, "Payment")
    .thenRun(() -> {
        sender.sendMessage("Transfer successful!");
        receiver.sendMessage("You received 100$!");
    })
    .exceptionally(ex -> {
        sender.sendMessage("Transfer failed: " + ex.getMessage());
        return null;
    });

Leaderboard

Get Top Balances

Retrieve a sorted list of the richest players.

java
CompletableFuture<LinkedHashMap<UUID, Double>> getTopBalances(int minRank, int maxRank)

Example:

java
// Get top 10 players
EconomyAPI.getTopBalances(1, 10)
    .thenAccept(topBalances -> {
        int rank = 1;
        for (Map.Entry<UUID, Double> entry : topBalances.entrySet()) {
            System.out.println(rank + ". " + entry.getKey() + ": " + entry.getValue());
            rank++;
        }
    });

Utility Methods

Format Currency

java
String formatCurrency(double amount)

Example:

java
String formatted = EconomyAPI.formatCurrency(1234.56);
// Output: "1234.56$"

Get Currency Information

java
String getCurrencySymbol()          // Returns: "$"

String getCurrencyNameSingular()    // Returns: "Dollar"

String getCurrencyNamePlural()      // Returns: "Dollars"

boolean isEnabled()                 // Returns: true if economy is enabled

🎯 Practical Examples

Shop System

java
public class ShopManager {

    public void buyItem(Player player, String itemName, double price) {
        UUID playerUUID = player.getUuid();

        // Check if player has enough money
        EconomyAPI.hasBalance(playerUUID, price).thenAccept(hasEnough -> {
            if (!hasEnough) {
                player.sendMessage("You don't have enough money!");
                return;
            }

            // Deduct money and give item
            EconomyAPI.decreaseBalance(playerUUID, price, "Bought " + itemName)
                    .thenAccept(newBalance -> {
                        giveItemToPlayer(player, itemName);
                        player.sendMessage("Purchased " + itemName + " for " +
                                EconomyAPI.formatCurrency(price));
                        player.sendMessage("New balance: " +
                                EconomyAPI.formatCurrency(newBalance));
                    })
                    .exceptionally(ex -> {
                        player.sendMessage("Purchase failed: " + ex.getMessage());
                        return null;
                    });
        });
    }

    public void sellItem(Player player, String itemName, double price) {
        UUID playerUUID = player.getUuid();

        if (!hasItemInInventory(player, itemName)) {
            player.sendMessage("You don't have this item!");
            return;
        }

        removeItemFromPlayer(player, itemName);

        EconomyAPI.increaseBalance(playerUUID, price, "Sold " + itemName)
                .thenAccept(newBalance -> {
                    player.sendMessage("Sold " + itemName + " for " +
                            EconomyAPI.formatCurrency(price));
                    player.sendMessage("New balance: " +
                            EconomyAPI.formatCurrency(newBalance));
                });
    }

    private void giveItemToPlayer(Player player, String itemName) {
        // Implementation
    }

    private void removeItemFromPlayer(Player player, String itemName) {
        // Implementation
    }

    private boolean hasItemInInventory(Player player, String itemName) {
        // Implementation
        return false;
    }
}

Quest System with Rewards

java
public class QuestManager {

    public void completeQuest(Player player, Quest quest) {
        UUID playerUUID = player.getUuid();
        double reward = quest.getReward();

        EconomyAPI.increaseBalance(playerUUID, reward, "Quest: " + quest.getName())
                .thenAccept(newBalance -> {
                    player.sendMessage("Quest completed!");
                    player.sendMessage("Reward: " + EconomyAPI.formatCurrency(reward));
                    player.sendMessage("Total balance: " +
                            EconomyAPI.formatCurrency(newBalance));

                    // Unlock next quest, save progress, etc.
                    unlockNextQuest(player, quest);
                })
                .exceptionally(ex -> {
                    // This should rarely happen, but handle it gracefully
                    player.sendMessage("Error processing quest reward. Please contact an admin.");
                    System.err.println("Quest reward error: " + ex.getMessage());
                    return null;
                });
    }

    private void unlockNextQuest(Player player, Quest quest) {
        // Implementation
    }
}

Player-to-Player Trading

java
public class TradeManager {

    public void executeTrade(Player player1, Player player2,
                             double amount1, double amount2) {
        UUID uuid1 = player1.getUuid();
        UUID uuid2 = player2.getUuid();

        // Check both players have enough money
        CompletableFuture<Boolean> check1 = EconomyAPI.hasBalance(uuid1, amount1);
        CompletableFuture<Boolean> check2 = EconomyAPI.hasBalance(uuid2, amount2);

        CompletableFuture.allOf(check1, check2).thenRun(() -> {
            if (!check1.join() || !check2.join()) {
                player1.sendMessage("Trade cancelled: Insufficient funds");
                player2.sendMessage("Trade cancelled: Insufficient funds");
                return;
            }

            // Execute both transfers
            CompletableFuture<Void> transfer1 =
                    EconomyAPI.transferBalance(uuid1, uuid2, amount1, "Trade");
            CompletableFuture<Void> transfer2 =
                    EconomyAPI.transferBalance(uuid2, uuid1, amount2, "Trade");

            CompletableFuture.allOf(transfer1, transfer2).thenRun(() -> {
                player1.sendMessage("Trade completed!");
                player2.sendMessage("Trade completed!");
            }).exceptionally(ex -> {
                player1.sendMessage("Trade failed: " + ex.getMessage());
                player2.sendMessage("Trade failed: " + ex.getMessage());
                return null;
            });
        });
    }
}

Leaderboard Display

java
public class LeaderboardCommand {

    public void showTopPlayers(Player player) {
        EconomyAPI.getTopBalances(1, 10).thenAccept(topBalances -> {
            player.sendMessage("=== Top 10 Richest Players ===");

            int rank = 1;
            for (Map.Entry<UUID, Double> entry : topBalances.entrySet()) {
                String playerName = getPlayerName(entry.getKey());
                String balance = EconomyAPI.formatCurrency(entry.getValue());

                player.sendMessage(rank + ". " + playerName + " - " + balance);
                rank++;
            }
        });
    }

    private String getPlayerName(UUID uuid) {
        // Get player name from UserManager or cache
        return "Player";
    }
}

🎪 Event System

The Economy API fires cancellable events before every balance change, allowing you to implement custom logic like taxes, limits, or logging.

Balance Change Event

java
import de.fof1092.essentialsplus.economy.BalanceChangeEvent;
import com.hypixel.hytale.server.core.HytaleServer;

public class EconomyEventListener {

    public void register() {
        HytaleServer.get().getEventBus().registerGlobal(
                BalanceChangeEvent.class,
                this::onBalanceChange
        );
    }

    private void onBalanceChange(BalanceChangeEvent event) {
        // Event information
        UUID playerUUID = event.getPlayerUUID();
        String playerName = event.getPlayerName();
        double oldBalance = event.getOldBalance();
        double newBalance = event.getNewBalance();
        double change = event.getChangeAmount();
        BalanceChangeEvent.ChangeType type = event.getChangeType();
        String reason = event.getReason();

        // Example: Log all transactions
        System.out.println(playerName + " balance changed from " +
                oldBalance + " to " + newBalance + " (" + reason + ")");

        // Example: Cancel if player would exceed limit
        if (newBalance > 1000000.0) {
            event.setCancelled(true);
            // Notify player
        }
    }
}

Change Types

The ChangeType enum indicates the type of balance operation:

  • SET - Balance was set to a specific value
  • INCREASE - Balance was increased
  • DECREASE - Balance was decreased
  • TRANSFER_SEND - Balance was transferred to another player (sender)
  • TRANSFER_RECEIVE - Balance was transferred from another player (receiver)
  • CHANGE - Balance was changed by an unknown amount

Tax System Example

java
public class TaxSystem {

    private static final double TAX_RATE = 0.10; // 10%
    private static final double TAX_THRESHOLD = 1000.0;

    public void register() {
        HytaleServer.get().getEventBus().registerGlobal(
                BalanceChangeEvent.class,
                this::applyTax
        );
    }

    private void applyTax(BalanceChangeEvent event) {
        // Only tax on income (increases)
        if (event.getChangeType() != BalanceChangeEvent.ChangeType.INCREASE) {
            return;
        }

        // Only tax large amounts
        if (event.getChangeAmount() < TAX_THRESHOLD) {
            return;
        }

        // Calculate tax
        double tax = event.getChangeAmount() * TAX_RATE;
        double netIncome = event.getChangeAmount() - tax;

        // Cancel original transaction
        event.setCancelled(true);

        // Apply net income instead
        EconomyAPI.increaseBalance(
                event.getPlayerUUID(),
                netIncome,
                event.getReason() + " (after " + (int) (TAX_RATE * 100) + "% tax)"
        ).thenAccept(newBalance -> {
            // Optional: Notify player about tax
            Player player = getPlayer(event.getPlayerUUID());
            if (player != null) {
                player.sendMessage("Tax deducted: " +
                        EconomyAPI.formatCurrency(tax));
            }
        });
    }

    private Player getPlayer(UUID uuid) {
        // Get player from server
        return null;
    }
}

Transaction Logger Example

java
public class TransactionLogger {

    private final List<Transaction> transactionHistory = new ArrayList<>();

    public void register() {
        HytaleServer.get().getEventBus().registerGlobal(
                BalanceChangeEvent.class,
                this::logTransaction
        );
    }

    private void logTransaction(BalanceChangeEvent event) {
        // Don't log cancelled transactions
        if (event.isCancelled()) {
            return;
        }

        Transaction transaction = new Transaction(
                event.getPlayerUUID(),
                event.getPlayerName(),
                event.getOldBalance(),
                event.getNewBalance(),
                event.getChangeType(),
                event.getReason(),
                System.currentTimeMillis()
        );

        transactionHistory.add(transaction);

        // Optional: Save to database
        saveToDatabase(transaction);
    }

    public List<Transaction> getPlayerTransactions(UUID playerUUID) {
        return transactionHistory.stream()
                .filter(t -> t.playerUUID.equals(playerUUID))
                .collect(Collectors.toList());
    }

    private void saveToDatabase(Transaction transaction) {
        // Implementation
    }

    private static class Transaction {
        final UUID playerUUID;
        final String playerName;
        final double oldBalance;
        final double newBalance;
        final BalanceChangeEvent.ChangeType type;
        final String reason;
        final long timestamp;

        Transaction(UUID playerUUID, String playerName, double oldBalance,
                    double newBalance, BalanceChangeEvent.ChangeType type,
                    String reason, long timestamp) {
            this.playerUUID = playerUUID;
            this.playerName = playerName;
            this.oldBalance = oldBalance;
            this.newBalance = newBalance;
            this.type = type;
            this.reason = reason;
            this.timestamp = timestamp;
        }
    }
}

⚙️ Configuration

The Economy system can be configured in the config.json file:

json
{
  "economy": {
    "enabled": true,
    "startBalance": 100.0,
    "minBalance": 0.0,
    "maxBalance": 100000000.0,
    "transactionLogging": true,
    "currencySymbol": "$",
    "currencyNameSingular": "Dollar",
    "currencyNamePlural": "Dollars"
  }
}

Configuration Options

OptionTypeDefaultDescription
enabledbooleantrueEnable/disable the economy system
startBalancedouble100.0Initial balance for new players
minBalancedouble0.0Minimum allowed balance
maxBalancedouble100000000.0Maximum allowed balance
transactionLoggingbooleantrueLog all transactions to console
currencySymbolstring"$"Currency symbol
currencyNameSingularstring"Dollar"Singular currency name
currencyNamePluralstring"Dollars"Plural currency name

🔒 Best Practices

1. Always Handle Exceptions

java
// ❌ Bad
EconomyAPI.increaseBalance(playerUUID, 100.0, "Reward");

// ✅ Good
EconomyAPI.increaseBalance(playerUUID, 100.0, "Reward")
    .exceptionally(ex -> {
        System.err.println("Failed to reward player: " + ex.getMessage());
        return null;
    });

2. Use Meaningful Reasons

java
// ❌ Bad
EconomyAPI.decreaseBalance(playerUUID, 50.0, null);

// ✅ Good
EconomyAPI.decreaseBalance(playerUUID, 50.0, "Shop: Bought Diamond Sword");

3. Check Balance Before Transactions

java
// ✅ Good practice
EconomyAPI.hasBalance(playerUUID, cost)
    .thenAccept(hasEnough -> {
        if (hasEnough) {
            EconomyAPI.decreaseBalance(playerUUID, cost, "Purchase")
                .thenAccept(newBalance -> {
                    // Give item
                });
        } else {
            player.sendMessage("Insufficient funds!");
        }
    });

4. Use UUID Instead of Username

java
// ❌ Slower, name might change
EconomyAPI.getBalance("PlayerName");

// ✅ Faster, always correct
EconomyAPI.getBalance(playerUUID);

5. Chain Operations Properly

java
// ✅ Good - Sequential operations
EconomyAPI.getBalance(playerUUID)
    .thenCompose(balance -> {
        if (balance >= 100.0) {
            return EconomyAPI.decreaseBalance(playerUUID, 100.0, "Upgrade");
        } else {
            return CompletableFuture.failedFuture(
                new Exception("Not enough money")
            );
        }
    })
    .thenAccept(newBalance -> {
        player.sendMessage("Upgrade purchased!");
    })
    .exceptionally(ex -> {
        player.sendMessage("Purchase failed: " + ex.getMessage());
        return null;
    });

❗ Error Handling

Exception Hierarchy

All economy exceptions inherit from EconomyException:

EconomyException (base)
├── PlayerNotFoundException
├── InsufficientBalanceException
├── BalanceLimitExceededException
└── BalanceChangeCancelledException

Specific Exceptions

PlayerNotFoundException

Thrown when a player is not found in the system.

Properties:

  • getPlayerUUID() - UUID of the missing player (can be null)
  • getPlayerName() - Name of the missing player (can be null)

Example:

java
EconomyAPI.getBalance(playerUUID)
    .exceptionally(ex -> {
        Throwable cause = ex.getCause();
        if (cause instanceof PlayerNotFoundException) {
            PlayerNotFoundException pnf = (PlayerNotFoundException) cause;
            System.err.println("Player not found: " + pnf.getPlayerUUID());
            // Maybe create the player account?
        }
        return 0.0;
    });

InsufficientBalanceException

Thrown when a player doesn't have enough balance for an operation.

Properties:

  • getPlayerUUID() - UUID of the player
  • getCurrentBalance() - Current balance
  • getRequiredBalance() - Required balance
  • getDeficit() - How much money is missing

Example:

java
EconomyAPI.decreaseBalance(playerUUID, 1000.0, "Purchase")
    .exceptionally(ex -> {
        Throwable cause = ex.getCause();
        if (cause instanceof InsufficientBalanceException) {
            InsufficientBalanceException ibe = (InsufficientBalanceException) cause;
            player.sendMessage("You need " +
                EconomyAPI.formatCurrency(ibe.getDeficit()) + " more!");
            player.sendMessage("Current: " +
                EconomyAPI.formatCurrency(ibe.getCurrentBalance()));
        }
        return null;
    });

BalanceLimitExceededException

Thrown when a balance operation would exceed configured limits (min/max).

Properties:

  • getPlayerUUID() - UUID of the player
  • getAttemptedBalance() - The balance that was attempted
  • getLimitValue() - The limit that was exceeded
  • getLimitType() - MINIMUM or MAXIMUM
  • isMinimumExceeded() - Returns true if minimum was exceeded
  • isMaximumExceeded() - Returns true if maximum was exceeded

Example:

java
EconomyAPI.setBalance(playerUUID, 999999999.0, "Test")
    .exceptionally(ex -> {
        Throwable cause = ex.getCause();
        if (cause instanceof BalanceLimitExceededException) {
            BalanceLimitExceededException ble = (BalanceLimitExceededException) cause;
            if (ble.isMaximumExceeded()) {
                player.sendMessage("Balance cannot exceed " +
                    EconomyAPI.formatCurrency(ble.getLimitValue()));
            } else {
                player.sendMessage("Balance cannot go below " +
                    EconomyAPI.formatCurrency(ble.getLimitValue()));
            }
        }
        return null;
    });

BalanceChangeCancelledException

Thrown when a balance change operation is cancelled by an event listener.

Properties:

  • getPlayerUUID() - UUID of the player
  • getOldBalance() - Balance before the attempted change
  • getNewBalance() - Balance that was attempted

Example:

java
EconomyAPI.increaseBalance(playerUUID, 10000.0, "Lottery Win")
    .exceptionally(ex -> {
        Throwable cause = ex.getCause();
        if (cause instanceof BalanceChangeCancelledException) {
            BalanceChangeCancelledException bcc = (BalanceChangeCancelledException) cause;
            player.sendMessage("Transaction was blocked by server anti-cheat!");
            System.err.println("Suspicious transaction detected for " +
                bcc.getPlayerUUID());
        }
        return null;
    });

Advanced Exception Handling

Handle Multiple Exception Types

java
EconomyAPI.decreaseBalance(playerUUID, cost, "Item Purchase")
    .thenAccept(newBalance -> {
        giveItemToPlayer(player);
        player.sendMessage("Purchase successful! New balance: " +
            EconomyAPI.formatCurrency(newBalance));
    })
    .exceptionally(ex -> {
        Throwable cause = ex.getCause();
        
        if (cause instanceof PlayerNotFoundException) {
            player.sendMessage("Account not found! Please rejoin the server.");
        } else if (cause instanceof InsufficientBalanceException) {
            InsufficientBalanceException ibe = (InsufficientBalanceException) cause;
            player.sendMessage("You need " +
                EconomyAPI.formatCurrency(ibe.getDeficit()) + " more!");
        } else if (cause instanceof BalanceLimitExceededException) {
            player.sendMessage("This transaction would exceed balance limits!");
        } else if (cause instanceof BalanceChangeCancelledException) {
            player.sendMessage("Transaction was blocked by the server!");
        } else {
            player.sendMessage("An error occurred: " + cause.getMessage());
            cause.printStackTrace();
        }

        return null;
    });

Type-Safe Exception Handling

java
public class ShopManager {

    public void buyItem(Player player, String itemName, double price) {
        UUID playerUUID = player.getUuid();

        EconomyAPI.decreaseBalance(playerUUID, price, "Shop: " + itemName)
                .thenAccept(newBalance -> {
                    giveItemToPlayer(player, itemName);
                    player.sendMessage("Purchased " + itemName);
                })
                .exceptionally(ex -> {
                    handlePurchaseError(player, ex.getCause(), price);
                    return null;
                });
    }

    private void handlePurchaseError(Player player, Throwable cause, double price) {
        if (cause instanceof InsufficientBalanceException ibe) {
            player.sendMessage("Not enough money!");
            player.sendMessage("You have: " +
                    EconomyAPI.formatCurrency(ibe.getCurrentBalance()));
            player.sendMessage("You need: " +
                    EconomyAPI.formatCurrency(price));
            player.sendMessage("Missing: " +
                    EconomyAPI.formatCurrency(ibe.getDeficit()));
        } else if (cause instanceof PlayerNotFoundException) {
            player.sendMessage("Your account was not found!");
            player.sendMessage("Please contact an administrator.");
        } else {
            player.sendMessage("Purchase failed: " + cause.getMessage());
        }
    }

    private void giveItemToPlayer(Player player, String itemName) {
        // Implementation
    }
}

Exception-Based Flow Control

java
public class LotterySystem {

    public void buyTicket(Player player, double ticketCost) {
        UUID playerUUID = player.getUuid();

        // Try to purchase ticket
        EconomyAPI.decreaseBalance(playerUUID, ticketCost, "Lottery Ticket")
                .thenCompose(newBalance -> {
                    // Success! Generate ticket
                    String ticketNumber = generateTicketNumber();
                    player.sendMessage("Ticket purchased: " + ticketNumber);
                    player.sendMessage("Remaining balance: " +
                            EconomyAPI.formatCurrency(newBalance));
                    return CompletableFuture.completedFuture(ticketNumber);
                })
                .exceptionally(ex -> {
                    Throwable cause = ex.getCause();

                    if (cause instanceof InsufficientBalanceException ibe) {

                        // Offer alternative: cheaper ticket
                        double cheaperTicket = ticketCost / 2;
                        if (ibe.getCurrentBalance() >= cheaperTicket) {
                            player.sendMessage("You can't afford a full ticket (" +
                                    EconomyAPI.formatCurrency(ticketCost) + ")");
                            player.sendMessage("But you can buy a half-ticket for " +
                                    EconomyAPI.formatCurrency(cheaperTicket));
                        } else {
                            player.sendMessage("Insufficient funds! You need " +
                                    EconomyAPI.formatCurrency(ibe.getDeficit()) + " more.");
                        }
                    } else {
                        player.sendMessage("Could not purchase ticket: " +
                                cause.getMessage());
                    }

                    return null;
                });
    }

    private String generateTicketNumber() {
        return "TICKET-" + System.currentTimeMillis();
    }
}

Common Exceptions

IllegalArgumentException

Thrown when:

  • Amount is negative for increaseBalance or decreaseBalance
  • Invalid rank range for getTopBalances

Example:

java
try {
    EconomyAPI.increaseBalance(playerUUID, -100.0, "Test"); // Throws!
} catch (IllegalArgumentException e) {
    System.err.println("Invalid amount: " + e.getMessage());
}

Handling API Not Initialized

java
try {
    EconomyAPI.getBalance(playerUUID)
        .thenAccept(balance -> {
            // ...
        });
} catch (IllegalStateException e) {
    System.err.println("Economy API not initialized! Is Essentials Plus installed?");
}

🐛 Troubleshooting

Economy API not available

Problem: IllegalStateException: EconomyAPI has not been initialized yet!

Solution:

  • Ensure Essentials Plus is installed on the server
  • Make sure your plugin loads after Essentials Plus
  • Check if economy is enabled in config.json

Transactions not working

Problem: Balance changes don't persist or throw exceptions

Solution:

  • Check server logs for error messages
  • Verify economy.enabled is true in config
  • Ensure player exists in the system (joined at least once)
  • Check min/max balance limits

Events not firing

Problem: Your event listener is not being called

Solution:

java
// Make sure you register the listener
HytaleServer.get()
    .getEventBus()
    .registerGlobal(
        BalanceChangeEvent.class, 
        this::onBalanceChange
    );

Performance issues

Problem: Economy operations are slow

Solution:

  • Use UUID instead of player name for lookups
  • Batch operations when possible
  • Avoid synchronous .join() calls on the main thread
  • Check if transaction logging is causing issues (can be disabled)

📝 Version Compatibility

Essentials Plus VersionAPI VersionHytale Version
1.11.0+1.0Latest

💬 Support

Need help? Found a bug? Have suggestions?

📄 License

Essentials Plus and its API are provided as-is. Please refer to the main plugin license for usage terms.