Building an Economy System
A complete economy system with wallets, transactions, and balance tracking.
An economy system lets players earn, spend, and trade currency. This is essential for survival servers, shops, ranks, and mini-games. We use SuperDB to persistently store wallet data so balances survive server restarts.
How It Works
The economy system stores each player's balance, transaction history, and metadata in a SuperDB database. When a player joins, we check if they have a wallet - if not, we create one with a starting balance. When money is added or removed, we record it in their transaction history for auditing and analysis.
Setup
First, we initialize the database and set constants:
import { SuperDB } from "@minecraft/server";
import { world } from "@minecraft/server";
const economyDB = new SuperDB({
name: "economy",
immediateWrite: true
});
const STARTING_BALANCE = 100;
Why immediateWrite: true? Money is critical data. We want it saved immediately so if the server crashes, players don't lose their balance. Without this, data might only save periodically.
Create Player Wallet
function createWallet(playerId) {
economyDB.set(playerId, {
balance: STARTING_BALANCE,
transactions: [],
lastUpdated: Date.now()
});
}
// On player join
world.afterEvents.playerJoin.subscribe((event) => {
const playerId = event.player.nameTag;
if (!economyDB.has(playerId)) {
createWallet(playerId);
event.player.sendMessage(`$${STARTING_BALANCE} added to your account!`);
}
});
Add/Remove Money
function addMoney(playerId, amount, reason = "donation") {
const wallet = economyDB.get(playerId);
if (!wallet) return false;
wallet.balance += amount;
wallet.transactions.push({
type: "deposit",
amount,
reason,
timestamp: Date.now()
});
wallet.lastUpdated = Date.now();
economyDB.set(playerId, wallet);
return true;
}
function removeMoney(playerId, amount, reason = "purchase") {
const wallet = economyDB.get(playerId);
if (!wallet || wallet.balance < amount) return false;
wallet.balance -= amount;
wallet.transactions.push({
type: "withdrawal",
amount,
reason,
timestamp: Date.now()
});
wallet.lastUpdated = Date.now();
economyDB.set(playerId, wallet);
return true;
}
function getBalance(playerId) {
const wallet = economyDB.get(playerId);
return wallet ? wallet.balance : 0;
}
Chat Commands for Economy
world.beforeEvents.chatSend.subscribe((event) => {
const message = event.message;
const player = event.sender;
const playerId = player.nameTag;
if (message === "!balance") {
event.cancel = true;
const balance = getBalance(playerId);
player.sendMessage(`Your balance: $${balance}`);
}
if (message.startsWith("!pay ")) {
event.cancel = true;
const parts = message.split(" ");
const targetName = parts[1];
const amount = parseInt(parts[2]);
if (!targetName || !amount || amount <= 0) {
player.sendMessage("Usage: !pay <player> <amount>");
return;
}
if (getBalance(playerId) < amount) {
player.sendMessage("Not enough money!");
return;
}
removeMoney(playerId, amount, `payment to ${targetName}`);
addMoney(targetName, amount, `payment from ${playerId}`);
player.sendMessage(`Sent $${amount} to ${targetName}`);
const target = world.getAllPlayers().find(p => p.nameTag === targetName);
if (target) {
target.sendMessage(`Received $${amount} from ${playerId}`);
}
}
});
Leaderboard
function getTopPlayers(limit = 10) {
const players = [];
economyDB.forEach((key, wallet) => {
players.push({ name: key, balance: wallet.balance });
});
return players
.sort((a, b) => b.balance - a.balance)
.slice(0, limit);
}
// Chat command for leaderboard
world.beforeEvents.chatSend.subscribe((event) => {
if (event.message === "!top") {
event.cancel = true;
const player = event.sender;
const top = getTopPlayers(5);
player.sendMessage("=== Top 5 Richest ===");
top.forEach((p, i) => {
player.sendMessage(`${i + 1}. ${p.name}: $${p.balance}`);
});
}
});