Custom Storage Providers for EzEconomy
EzEconomy supports pluggable storage backends for all economy, bank, and currency operations. You can implement your own provider to use a custom database, cloud service, or any data source.
Overview
A storage provider is any class that implements the StorageProvider interface from the EzEconomy API. This interface defines all required methods for player balances, banks, currencies, and (optionally) transactions.
- Location:
com.skyblockexp.ezeconomy.api.storage.StorageProvider - Purpose: Abstracts all persistent data operations for the plugin
- Use Cases:
- Integrate with a custom SQL/NoSQL database
- Use a remote API or cloud service
- Add advanced caching or sharding
Implementation Steps
-
Implement the Interface
public class MyProvider implements StorageProvider { // Implement all required methods } -
Register Your Provider
Register your provider in your plugin’s
onLoadmethod, before EzEconomy finishes loading:import com.skyblockexp.ezeconomy.api.storage.StorageProvider; import com.skyblockexp.ezeconomy.EzEconomy; public void onLoad() { StorageProvider customProvider = new MyProvider(...); EzEconomy.registerStorageProvider(customProvider); } -
Required Methods
Your provider must implement all methods for:
- Player balances (get, set, deposit, withdraw)
- Bank operations (create, delete, balance, members)
- Currency management
- (Optional) Transaction history
See the Javadoc for
StorageProviderfor method signatures and expected behaviors. -
Legacy Compatibility
For single-currency servers, legacy overloads are provided. You must implement these for full compatibility with older plugins.
Guidelines
- Atomicity: Ensure all balance changes are atomic and thread-safe.
- Performance: Use async IO and caching where possible. Avoid blocking the main server thread.
- Validation: Validate all DTOs (data transfer objects) before writing to storage.
- Error Handling: Throw meaningful exceptions for impossible or failed operations.
- Migration: If replacing the default provider, migrate data before switching in production.
Example: Minimal Provider
public class ExampleProvider implements StorageProvider {
// Implement all required methods...
}
Notes
- Only one custom provider can be registered at a time.
- Register your provider before EzEconomy finishes loading.
- See the EzEconomy source for built-in provider examples (YML, SQLite, MySQL, MongoDB).
StorageProvider Interface Reference
This section documents all methods of the StorageProvider interface. Implement all required methods for a fully functional provider.
Initialization & Lifecycle
void init()
Initialize the storage provider. ThrowStorageInitExceptionon failure.void load()
Load data or establish connections. ThrowStorageLoadExceptionon failure.void save()
Persist any in-memory data. ThrowStorageSaveExceptionon failure.void shutdown()
Clean up and close resources.
Player Balances
double getBalance(UUID uuid, String currency)
Get a player’s balance for a currency.void setBalance(UUID uuid, String currency, double amount)
Set a player’s balance for a currency.boolean tryWithdraw(UUID uuid, String currency, double amount)
Attempt to withdraw from a player’s balance. Return false if insufficient funds.void deposit(UUID uuid, String currency, double amount)
Deposit to a player’s balance.Map<UUID, Double> getAllBalances(String currency)
Get all player balances for a currency.Set<String> cleanupOrphanedPlayers()
Remove balances for unknown players. Default: no-op.
Transactions
void logTransaction(Transaction transaction)
Log a transaction for a player and currency.List<Transaction> getTransactions(UUID uuid, String currency)
Get transaction history for a player and currency.
Transfers
TransferResult transfer(UUID fromUuid, UUID toUuid, String currency, double amount)
Transfer funds between players (default: debit/credit same amount).TransferResult transfer(UUID fromUuid, UUID toUuid, String currency, double debitAmount, double creditAmount)
Transfer custom debit/credit amounts between players.
Bank Operations
boolean createBank(String name, UUID owner)
Create a new bank.boolean deleteBank(String name)
Delete a bank.boolean bankExists(String name)
Check if a bank exists.double getBankBalance(String name, String currency)
Get a bank’s balance for a currency.void setBankBalance(String name, String currency, double amount)
Set a bank’s balance for a currency.boolean tryWithdrawBank(String name, String currency, double amount)
Attempt to withdraw from a bank.void depositBank(String name, String currency, double amount)
Deposit to a bank.Set<String> getBanks()
Get all bank names.boolean isBankOwner(String name, UUID uuid)
Check if a UUID is the owner of a bank.boolean isBankMember(String name, UUID uuid)
Check if a UUID is a member of a bank.boolean addBankMember(String name, UUID uuid)
Add a member to a bank.boolean removeBankMember(String name, UUID uuid)
Remove a member from a bank.Set<UUID> getBankMembers(String name)
Get all member UUIDs of a bank.
Cross-Server Support (v3.1.0+)
These default methods support cross-server payment notifications. Override them if your backend stores player data or pending messages. As of v3.1.1, resolvePlayerByName() and persistPlayerInfo() are fully implemented in the MySQL storage provider and are used by PayCommand and PaymentExecutor for cross-server player resolution.
UUID resolvePlayerByName(String name)
Look up a player UUID by name from the shared database. Used byPayCommandwhen the recipient is not found in Bukkit’s local player cache. Default: returns null. MySQL implementation queries theplayerstable.void persistPlayerInfo(UUID uuid, String name, String displayName)
Store a player’s UUID, name, and display name. Called on join and during cross-server lookups. Default: no-op. MySQL implementation upserts into theplayerstable.void insertPendingNotification(UUID targetUuid, String message)
Store a notification for an offline player. Default: no-op.List<String> pollPendingNotifications(UUID targetUuid)
Retrieve and delete pending notifications for a player. Default: returns empty list.void cleanupOldNotifications(long olderThanMs)
Remove notifications older than the given threshold. Default: no-op.
Connection & Status
boolean isConnected()
Return true if the provider is connected to its backend (default: false).
Legacy Overloads (Single-Currency)
All legacy methods use the default currency “dollar”. Implement for compatibility with older plugins:
double getBalance(UUID uuid)void setBalance(UUID uuid, double amount)boolean tryWithdraw(UUID uuid, double amount)void deposit(UUID uuid, double amount)Map<UUID, Double> getAllBalances()double getBankBalance(String name)void setBankBalance(String name, double amount)boolean tryWithdrawBank(String name, double amount)void depositBank(String name, double amount)
Thread Safety & Atomicity
- All balance and transfer operations must be atomic and thread-safe.
- Use locking or transactions as appropriate for your backend.
Exceptions
- Throw the appropriate exception (
StorageInitException,StorageLoadException,StorageSaveException) for lifecycle failures. - Throw meaningful runtime exceptions for impossible or failed operations.
See Also: