Upload folder via GUI - src

This commit is contained in:
Git Manager GUI
2026-05-05 21:03:26 +02:00
parent d686fee958
commit 365ed04af4
9 changed files with 761 additions and 26 deletions

View File

@@ -5,6 +5,7 @@ import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;
import net.viper.status.module.ModuleManager;
import net.viper.status.modules.economy.EconomyModule;
import net.viper.status.modules.antibot.AntiBotModule;
import net.viper.status.modules.network.NetworkInfoModule;
import net.viper.status.modules.AutoMessage.AutoMessageModule;
@@ -89,6 +90,7 @@ public class StatusAPI extends Plugin implements Runnable {
moduleManager.registerModule(new AutoMessageModule());
moduleManager.registerModule(new CustomCommandModule());
moduleManager.registerModule(new ServerSwitcherModule());
moduleManager.registerModule(new EconomyModule());
try {
Class<?> forumBridge = Class.forName("net.viper.status.modules.forum.ForumBridgeModule");
@@ -506,8 +508,13 @@ public class StatusAPI extends Plugin implements Runnable {
playerMap.put("playtime", ps.getPlaytimeWithCurrentSession());
playerMap.put("joins", ps.joins);
playerMap.put("online", ProxyServer.getInstance().getPlayer(ps.uuid) != null);
// Balance direkt aus MySQL (serverübergreifend)
EconomyModule ecoModPlayer = (EconomyModule) moduleManager.getModule("EconomyModule");
double playerBalance = (ecoModPlayer != null && ecoModPlayer.getManager() != null)
? ecoModPlayer.getManager().getBalance(ps.uuid)
: ps.balance;
Map<String, Object> economy = new LinkedHashMap<>();
economy.put("balance", ps.balance);
economy.put("balance", playerBalance);
economy.put("total_earned", ps.totalEarned);
economy.put("total_spent", ps.totalSpent);
economy.put("transactions_count", ps.transactionsCount);
@@ -526,49 +533,92 @@ public class StatusAPI extends Plugin implements Runnable {
}
// GET /economy/player?uuid=... oder ?name=...
// Kein Cache UUID und Balance kommen direkt aus der DB
if ("GET".equalsIgnoreCase(method) && "/economy/player".equalsIgnoreCase(pathOnly)) {
Map<String, String> qp = parseQueryParams(path);
StatsModule statsMod = (StatsModule) moduleManager.getModule("StatsModule");
PlayerStats ps = resolvePlayer(qp.get("uuid"), qp.get("name"), statsMod);
if (ps == null) {
EconomyModule ecoModGet = (EconomyModule) moduleManager.getModule("EconomyModule");
if (ecoModGet == null || ecoModGet.getManager() == null) {
sendHttpResponse(out, "{\"success\":false,\"error\":\"economy_module_unavailable\"}", 503);
return;
}
// UUID auflösen: erst Query-Param, dann Name über EconomyManager (DB-Lookup)
UUID ecoUuid = null;
String ecoName = null;
String uuidParam = qp.get("uuid");
String nameParam = qp.get("name");
if (uuidParam != null && !uuidParam.isEmpty()) {
try { ecoUuid = UUID.fromString(uuidParam.trim()); } catch (IllegalArgumentException ignored) {}
}
if (ecoUuid == null && nameParam != null && !nameParam.isEmpty()) {
ecoUuid = ecoModGet.getManager().resolveUUID(nameParam.trim());
ecoName = nameParam.trim();
}
if (ecoUuid == null) {
sendHttpResponse(out, "{\"success\":false,\"error\":\"player_not_found\"}", 404);
return;
}
if (ecoName == null) ecoName = uuidParam;
double directBalance = ecoModGet.getManager().getBalance(ecoUuid);
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("success", true);
payload.put("uuid", ps.uuid.toString());
payload.put("name", ps.name);
payload.put("uuid", ecoUuid.toString());
payload.put("name", ecoName);
Map<String, Object> economy = new LinkedHashMap<>();
economy.put("balance", ps.balance);
economy.put("total_earned", ps.totalEarned);
economy.put("total_spent", ps.totalSpent);
economy.put("transactions_count", ps.transactionsCount);
economy.put("balance", directBalance);
payload.put("economy", economy);
sendHttpResponse(out, buildJsonString(payload), 200);
return;
}
// POST /economy/update
// Kein Cache Balance wird direkt in die DB geschrieben
if ("POST".equalsIgnoreCase(method) && "/economy/update".equalsIgnoreCase(pathOnly)) {
String body = readBody(in, headers);
StatsModule statsModEco = (StatsModule) moduleManager.getModule("StatsModule");
PlayerStats psEco = resolvePlayer(extractJsonString(body, "uuid"), extractJsonString(body, "name"), statsModEco);
if (psEco == null) {
EconomyModule ecoModUpd = (EconomyModule) moduleManager.getModule("EconomyModule");
if (ecoModUpd == null || ecoModUpd.getManager() == null) {
sendHttpResponse(out, "{\"success\":false,\"error\":\"economy_module_unavailable\"}", 503);
return;
}
// UUID auflösen
UUID ecoUpdUuid = null;
String uuidBody = extractJsonString(body, "uuid");
String nameBody = extractJsonString(body, "name");
if (uuidBody != null && !uuidBody.isEmpty()) {
try { ecoUpdUuid = UUID.fromString(uuidBody.trim()); } catch (IllegalArgumentException ignored) {}
}
if (ecoUpdUuid == null && nameBody != null && !nameBody.isEmpty()) {
ecoUpdUuid = ecoModUpd.getManager().resolveUUID(nameBody.trim());
}
if (ecoUpdUuid == null) {
sendHttpResponse(out, "{\"success\":false,\"error\":\"player_not_found\"}", 404);
return;
}
String balStr = extractJsonString(body, "balance");
String earnStr = extractJsonString(body, "total_earned");
if (earnStr == null) earnStr = extractJsonString(body, "totalEarned");
String spentStr = extractJsonString(body, "total_spent");
if (spentStr == null) spentStr = extractJsonString(body, "totalSpent");
String txStr = extractJsonString(body, "transactions_count");
if (txStr == null) txStr = extractJsonString(body, "transactionsCount");
synchronized (psEco) {
try { if (balStr != null && !balStr.isEmpty()) psEco.balance = Double.parseDouble(balStr); } catch (Exception ignored) {}
try { if (earnStr != null && !earnStr.isEmpty()) psEco.totalEarned = Double.parseDouble(earnStr); } catch (Exception ignored) {}
try { if (spentStr != null && !spentStr.isEmpty()) psEco.totalSpent = Double.parseDouble(spentStr); } catch (Exception ignored) {}
try { if (txStr != null && !txStr.isEmpty()) psEco.transactionsCount = Integer.parseInt(txStr); } catch (Exception ignored) {}
// Balance direkt in DB schreiben
String balStr = extractJsonString(body, "balance");
if (balStr != null && !balStr.isEmpty()) {
try {
double newBal = Double.parseDouble(balStr);
ecoModUpd.getManager().setBalance(ecoUpdUuid, newBal);
} catch (NumberFormatException ignored) {}
}
// Stats-Felder (total_earned etc.) im Cache aktualisieren, falls vorhanden
StatsModule statsModEco = (StatsModule) moduleManager.getModule("StatsModule");
if (statsModEco != null) {
PlayerStats psEco = statsModEco.getManager().getIfPresent(ecoUpdUuid);
if (psEco != null) {
String earnStr = extractJsonString(body, "total_earned");
if (earnStr == null) earnStr = extractJsonString(body, "totalEarned");
String spentStr = extractJsonString(body, "total_spent");
if (spentStr == null) spentStr = extractJsonString(body, "totalSpent");
String txStr = extractJsonString(body, "transactions_count");
if (txStr == null) txStr = extractJsonString(body, "transactionsCount");
synchronized (psEco) {
try { if (balStr != null && !balStr.isEmpty()) psEco.balance = Double.parseDouble(balStr); } catch (Exception ignored) {}
try { if (earnStr != null && !earnStr.isEmpty()) psEco.totalEarned = Double.parseDouble(earnStr); } catch (Exception ignored) {}
try { if (spentStr != null && !spentStr.isEmpty()) psEco.totalSpent = Double.parseDouble(spentStr); } catch (Exception ignored) {}
try { if (txStr != null && !txStr.isEmpty()) psEco.transactionsCount = Integer.parseInt(txStr); } catch (Exception ignored) {}
}
}
}
sendHttpResponse(out, "{\"success\":true}", 200);
return;
@@ -690,8 +740,13 @@ public class StatusAPI extends Plugin implements Runnable {
playerInfo.put("joins", ps.joins);
playerInfo.put("first_seen", ps.firstSeen);
playerInfo.put("last_seen", ps.lastSeen);
// Balance direkt aus MySQL (serverübergreifend)
EconomyModule ecoModStatus = (EconomyModule) moduleManager.getModule("EconomyModule");
double statusBalance = (ecoModStatus != null && ecoModStatus.getManager() != null)
? ecoModStatus.getManager().getBalance(p.getUniqueId())
: ps.balance;
Map<String, Object> eco = new LinkedHashMap<>();
eco.put("balance", ps.balance);
eco.put("balance", statusBalance);
eco.put("total_earned", ps.totalEarned);
eco.put("total_spent", ps.totalSpent);
eco.put("transactions_count", ps.transactionsCount);

View File

@@ -0,0 +1,134 @@
package net.viper.status.modules.economy;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.Plugin;
import java.util.UUID;
/**
* /ecoadmin <give|take|set|check> <Spieler> [Betrag]
* Admin-Verwaltung des Economy-Systems (auch für Offline-Spieler).
*
* Berechtigung: economy.admin
*/
public class EcoAdminCommand extends Command {
private final Plugin plugin;
private final EconomyManager manager;
public EcoAdminCommand(Plugin plugin, EconomyManager manager) {
super("ecoadmin", "economy.admin", "ecomod", "moneyadmin");
this.plugin = plugin;
this.manager = manager;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (!sender.hasPermission("economy.admin")) {
sender.sendMessage(new TextComponent(c("&cKeine Berechtigung.")));
return;
}
if (args.length < 2) {
sendHelp(sender);
return;
}
String action = args[0].toLowerCase();
String targetName = args[1];
plugin.getProxy().getScheduler().runAsync(plugin, () -> {
UUID targetUUID = manager.resolveUUID(targetName);
if (targetUUID == null) {
sender.sendMessage(new TextComponent(c("&cSpieler &e" + targetName + " &cnicht gefunden.")));
return;
}
switch (action) {
case "check": {
double bal = manager.getBalance(targetUUID);
sender.sendMessage(new TextComponent(c("&7Kontostand von &e" + targetName
+ "&7: &a" + formatAmount(bal) + " $")));
break;
}
case "give": {
if (args.length < 3) { sendHelp(sender); return; }
double amount = parseAmount(args[2]);
if (amount <= 0) { sender.sendMessage(new TextComponent(c("&cUngültiger Betrag."))); return; }
manager.deposit(targetUUID, amount);
double newBal = manager.getBalance(targetUUID);
sender.sendMessage(new TextComponent(c("&a+ " + formatAmount(amount)
+ " $ &7an &e" + targetName + " &7→ Neues Guthaben: &a" + formatAmount(newBal) + " $")));
notifyPlayer(targetUUID, c("&7Admin hat dir &a+" + formatAmount(amount)
+ " $ &7gegeben. Guthaben: &a" + formatAmount(newBal) + " $"));
break;
}
case "take": {
if (args.length < 3) { sendHelp(sender); return; }
double amount = parseAmount(args[2]);
if (amount <= 0) { sender.sendMessage(new TextComponent(c("&cUngültiger Betrag."))); return; }
boolean ok = manager.withdraw(targetUUID, amount);
if (!ok) {
sender.sendMessage(new TextComponent(c("&cNicht genug Guthaben bei &e" + targetName + "&c.")));
return;
}
double newBal = manager.getBalance(targetUUID);
sender.sendMessage(new TextComponent(c("&c- " + formatAmount(amount)
+ " $ &7von &e" + targetName + " &7→ Neues Guthaben: &a" + formatAmount(newBal) + " $")));
notifyPlayer(targetUUID, c("&7Admin hat dir &c-" + formatAmount(amount)
+ " $ &7abgezogen. Guthaben: &a" + formatAmount(newBal) + " $"));
break;
}
case "set": {
if (args.length < 3) { sendHelp(sender); return; }
double amount = parseAmount(args[2]);
if (amount < 0) { sender.sendMessage(new TextComponent(c("&cBetrag darf nicht negativ sein."))); return; }
manager.setBalance(targetUUID, amount);
sender.sendMessage(new TextComponent(c("&7Guthaben von &e" + targetName
+ " &7gesetzt auf &a" + formatAmount(amount) + " $")));
notifyPlayer(targetUUID, c("&7Dein Guthaben wurde auf &a" + formatAmount(amount) + " $ &7gesetzt."));
break;
}
default:
sendHelp(sender);
}
});
}
private void notifyPlayer(UUID uuid, String message) {
ProxiedPlayer target = plugin.getProxy().getPlayer(uuid);
if (target != null) {
target.sendMessage(new TextComponent(message));
}
}
private void sendHelp(CommandSender sender) {
sender.sendMessage(new TextComponent(c("&e/ecoadmin check <Spieler>")));
sender.sendMessage(new TextComponent(c("&e/ecoadmin give <Spieler> <Betrag>")));
sender.sendMessage(new TextComponent(c("&e/ecoadmin take <Spieler> <Betrag>")));
sender.sendMessage(new TextComponent(c("&e/ecoadmin set <Spieler> <Betrag>")));
}
private static double parseAmount(String s) {
try { return Double.parseDouble(s.replace(",", ".")); }
catch (NumberFormatException e) { return -1; }
}
private static String formatAmount(double amount) {
return String.format("%,.2f", amount);
}
private static String c(String msg) {
return ChatColor.translateAlternateColorCodes('&', msg);
}
}

View File

@@ -0,0 +1,183 @@
package net.viper.status.modules.economy;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import net.md_5.bungee.api.plugin.Plugin;
import java.sql.*;
import java.util.UUID;
import java.util.logging.Logger;
/**
* Verwaltet die MySQL-Verbindung (HikariCP) und die Tabelle bc_accounts.
* Wird vom EconomyModule gehalten und an den EconomyManager weitergegeben.
*/
public class EconomyDatabase {
private static final String TABLE = "bc_accounts";
private final Logger log;
private HikariDataSource dataSource;
public EconomyDatabase(Plugin plugin, String host, int port, String database, String user, String password) {
this.log = plugin.getLogger();
HikariConfig cfg = new HikariConfig();
cfg.setJdbcUrl("jdbc:mysql://" + host + ":" + port + "/" + database
+ "?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&useUnicode=true");
cfg.setUsername(user);
cfg.setPassword(password);
cfg.setMaximumPoolSize(5);
cfg.setMinimumIdle(1);
cfg.setConnectionTimeout(10_000);
cfg.setIdleTimeout(600_000);
cfg.setMaxLifetime(1_800_000);
cfg.setPoolName("StatusAPI-Economy");
cfg.addDataSourceProperty("cachePrepStmts", "true");
cfg.addDataSourceProperty("prepStmtCacheSize", "250");
cfg.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
try {
dataSource = new HikariDataSource(cfg);
} catch (Exception e) {
log.severe("[Economy] MySQL-Verbindung fehlgeschlagen: " + e.getMessage());
log.severe("[Economy] Bitte Zugangsdaten in verify.properties prüfen!");
return;
}
// Tabelle anlegen falls nicht vorhanden (kompatibel mit SurvivalPlus)
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(
"CREATE TABLE IF NOT EXISTS `" + TABLE + "` (" +
" `player_name` VARCHAR(36) NOT NULL," +
" `balance` VARCHAR(255) NOT NULL DEFAULT '0'," +
" PRIMARY KEY (`player_name`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")) {
ps.executeUpdate();
} catch (SQLException e) {
log.severe("[Economy] Tabellen-Setup (bc_accounts) fehlgeschlagen: " + e.getMessage());
}
// bc_player_names Tabelle anlegen
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(
"CREATE TABLE IF NOT EXISTS `bc_player_names` (" +
" `uuid` VARCHAR(36) NOT NULL PRIMARY KEY," +
" `name` VARCHAR(16) NOT NULL," +
" `updated` BIGINT NOT NULL" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")) {
ps.executeUpdate();
log.info("[Economy] MySQL verbunden Tabellen bereit.");
} catch (SQLException e) {
log.severe("[Economy] Tabellen-Setup (bc_player_names) fehlgeschlagen: " + e.getMessage());
}
}
public boolean isConnected() {
return dataSource != null && !dataSource.isClosed();
}
public void close() {
if (dataSource != null && !dataSource.isClosed()) {
dataSource.close();
}
}
/**
* Sucht UUID in CMI_users nutzt eine separate DB-Verbindung falls
* economy.cmi.database konfiguriert ist, sonst dieselbe DB.
*/
public java.util.UUID findUUIDByName(String name) {
if (!isConnected()) return null;
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(
"SELECT `player_uuid` FROM `CMI_users` WHERE `username` = ? LIMIT 1")) {
ps.setString(1, name);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
String uuidStr = rs.getString("player_uuid");
if (uuidStr != null && !uuidStr.isEmpty()) {
return java.util.UUID.fromString(uuidStr);
}
}
}
} catch (SQLException | IllegalArgumentException e) {
// CMI_users nicht in dieser DB kein Problem, bc_player_names ist der Primär-Lookup
}
return null;
}
/** Speichert den Spielernamen in einer eigenen Lookup-Tabelle. */
public void saveNameMapping(java.util.UUID uuid, String name) {
if (!isConnected()) return;
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(
"INSERT INTO `bc_player_names` (`uuid`, `name`, `updated`) VALUES (?, ?, ?) " +
"ON DUPLICATE KEY UPDATE `name` = VALUES(`name`), `updated` = VALUES(`updated`)")) {
ps.setString(1, uuid.toString());
ps.setString(2, name);
ps.setLong(3, System.currentTimeMillis());
ps.executeUpdate();
} catch (SQLException e) {
log.warning("[Economy] Name-Mapping fehlgeschlagen: " + e.getMessage());
}
}
/** UUID-Lookup über bc_player_names (eigene Tabelle). */
public java.util.UUID findUUIDByNameOwn(String name) {
if (!isConnected()) return null;
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(
"SELECT `uuid` FROM `bc_player_names` WHERE `name` = ? LIMIT 1")) {
ps.setString(1, name);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return java.util.UUID.fromString(rs.getString("uuid"));
}
}
} catch (SQLException | IllegalArgumentException e) {
// Tabelle noch nicht vorhanden
}
return null;
}
/** Kombinierter UUID-Lookup: erst eigene Tabelle, dann CMI_users. */
public java.util.UUID resolveUUID(String name) {
java.util.UUID uuid = findUUIDByNameOwn(name);
if (uuid != null) return uuid;
return findUUIDByName(name);
}
/** Lädt den Kontostand direkt aus der DB. Gibt -1 zurück wenn kein Eintrag. */
public double load(UUID uuid) {
if (!isConnected()) return -1;
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(
"SELECT `balance` FROM `" + TABLE + "` WHERE `player_name` = ?")) {
ps.setString(1, uuid.toString());
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return Double.parseDouble(rs.getString("balance"));
}
}
} catch (SQLException | NumberFormatException e) {
log.warning("[Economy] Load fehlgeschlagen für " + uuid + ": " + e.getMessage());
}
return -1;
}
/** Schreibt einen Kontostand in die DB (INSERT oder UPDATE). */
public void save(UUID uuid, double balance) {
if (!isConnected()) return;
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(
"INSERT INTO `" + TABLE + "` (`player_name`, `balance`) VALUES (?, ?) " +
"ON DUPLICATE KEY UPDATE `balance` = VALUES(`balance`)")) {
ps.setString(1, uuid.toString());
ps.setString(2, String.valueOf(balance));
ps.executeUpdate();
} catch (SQLException e) {
log.warning("[Economy] Save fehlgeschlagen für " + uuid + ": " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,33 @@
package net.viper.status.modules.economy;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.event.EventHandler;
public class EconomyListener implements Listener {
private final Plugin plugin;
private final EconomyManager manager;
public EconomyListener(Plugin plugin, EconomyManager manager) {
this.plugin = plugin;
this.manager = manager;
}
/**
* Beim ersten Login: Konto anlegen falls noch nicht vorhanden.
* Kein Caching alle weiteren Zugriffe gehen direkt in die DB.
*/
@EventHandler
public void onLogin(PostLoginEvent event) {
ProxiedPlayer player = event.getPlayer();
plugin.getProxy().getScheduler().runAsync(plugin, () -> {
// Namen für Offline-Lookup speichern
manager.saveNameMapping(player.getUniqueId(), player.getName());
// Konto anlegen falls neu
manager.getBalance(player.getUniqueId());
});
}
}

View File

@@ -0,0 +1,109 @@
package net.viper.status.modules.economy;
import net.md_5.bungee.api.plugin.Plugin;
import java.util.UUID;
/**
* Kein Cache jeder Zugriff geht direkt in die Datenbank.
* Damit ist der Kontostand immer aktuell, egal von welchem Server
* er zuletzt geändert wurde (SurvivalPlus, CMI, etc.).
*/
public class EconomyManager {
private final Plugin plugin;
private final EconomyDatabase db;
private final double startBalance;
public EconomyManager(Plugin plugin, EconomyDatabase db, double startBalance) {
this.plugin = plugin;
this.db = db;
this.startBalance = startBalance;
}
public void saveNameMapping(UUID uuid, String name) {
db.saveNameMapping(uuid, name);
}
public UUID resolveUUID(String name) {
// 1. Online-Spieler auf dem Proxy (case-insensitive)
for (net.md_5.bungee.api.connection.ProxiedPlayer p : plugin.getProxy().getPlayers()) {
if (p.getName().equalsIgnoreCase(name)) return p.getUniqueId();
}
// 2. Eigene bc_player_names Tabelle
UUID uuid = db.findUUIDByNameOwn(name);
if (uuid != null) return uuid;
// 3. CMI_users Fallback
uuid = db.findUUIDByName(name);
if (uuid != null) return uuid;
// 4. Mojang API als letzter Ausweg
return lookupMojang(name);
}
/** UUID via Mojang API holen (nur wenn alle lokalen Lookups fehlschlagen). */
private UUID lookupMojang(String name) {
try {
java.net.URL url = new java.net.URL("https://api.mojang.com/users/profiles/minecraft/" + name);
java.net.HttpURLConnection con = (java.net.HttpURLConnection) url.openConnection();
con.setConnectTimeout(3000);
con.setReadTimeout(3000);
con.setRequestMethod("GET");
if (con.getResponseCode() != 200) return null;
java.io.BufferedReader br = new java.io.BufferedReader(
new java.io.InputStreamReader(con.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) sb.append(line);
br.close();
String json = sb.toString();
int idIdx = json.indexOf("\"id\":\"");
if (idIdx < 0) return null;
String raw = json.substring(idIdx + 6, idIdx + 38);
String formatted = raw.replaceFirst(
"(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5");
UUID uuid = UUID.fromString(formatted);
// Für künftige Lookups speichern
db.saveNameMapping(uuid, name);
plugin.getLogger().info("[Economy] Mojang-Lookup: " + name + "" + uuid);
return uuid;
} catch (Exception e) {
plugin.getLogger().warning("[Economy] Mojang-Lookup fehlgeschlagen für " + name + ": " + e.getMessage());
return null;
}
}
public double getBalance(UUID uuid) {
double bal = db.load(uuid);
if (bal < 0) {
// Neuer Spieler Startkonto anlegen
db.save(uuid, startBalance);
return startBalance;
}
return bal;
}
public void setBalance(UUID uuid, double amount) {
db.save(uuid, Math.max(0.0, amount));
}
public boolean deposit(UUID uuid, double amount) {
if (amount <= 0) return false;
double current = db.load(uuid);
if (current < 0) current = startBalance;
db.save(uuid, current + amount);
return true;
}
public boolean withdraw(UUID uuid, double amount) {
if (amount <= 0) return false;
double current = db.load(uuid);
if (current < 0) current = 0;
if (current < amount) return false;
db.save(uuid, current - amount);
return true;
}
public boolean hasAccount(UUID uuid) {
return db.load(uuid) >= 0;
}
}

View File

@@ -0,0 +1,95 @@
package net.viper.status.modules.economy;
import net.md_5.bungee.api.plugin.Plugin;
import net.viper.status.StatusAPI;
import net.viper.status.module.Module;
import java.util.Properties;
/**
* EconomyModule serverübergreifendes Geldkonto über MySQL (bc_accounts).
*
* Konfiguration in verify.properties:
* economy.mysql.host=localhost
* economy.mysql.port=3306
* economy.mysql.database=survivalplus
* economy.mysql.username=root
* economy.mysql.password=
* economy.start-balance=500.0
*
* Das Modul registriert sich im Modul-System der StatusAPI und hält
* EconomyDatabase + EconomyManager, die von SurvivalPlus und allen
* anderen Servern über die gemeinsame bc_accounts-Tabelle genutzt werden.
*/
public class EconomyModule implements Module {
private EconomyDatabase database;
private EconomyManager manager;
@Override
public String getName() {
return "EconomyModule";
}
@Override
public void onEnable(Plugin plugin) {
// Konfiguration aus verify.properties laden
Properties props = ((StatusAPI) plugin).getVerifyProperties();
String host = getProp(props, "economy.mysql.host", "localhost");
int port = getInt (props, "economy.mysql.port", 3306);
String database = getProp(props, "economy.mysql.database", "survivalplus");
String user = getProp(props, "economy.mysql.username", "root");
String password = getProp(props, "economy.mysql.password", "");
double startBal = getDbl (props, "economy.start-balance", 500.0);
this.database = new EconomyDatabase(plugin, host, port, database, user, password);
if (!this.database.isConnected()) {
plugin.getLogger().severe("[Economy] Modul wird NICHT aktiviert keine DB-Verbindung.");
return;
}
this.manager = new EconomyManager(plugin, this.database, startBal);
// Listener registrieren (Login → load, Disconnect → save)
plugin.getProxy().getPluginManager().registerListener(plugin, new EconomyListener(plugin, manager));
plugin.getProxy().getPluginManager().registerCommand(plugin, new PayCommand(plugin, manager));
plugin.getProxy().getPluginManager().registerCommand(plugin, new EcoAdminCommand(plugin, manager));
plugin.getLogger().info("[Economy] EconomyModule aktiviert (start-balance: " + startBal + ").");
}
@Override
public void onDisable(Plugin plugin) {
if (database != null) {
database.close();
plugin.getLogger().info("[Economy] MySQL-Verbindung geschlossen.");
}
}
/** Gibt den EconomyManager zurück (für andere Module oder HTTP-Endpoints). */
public EconomyManager getManager() {
return manager;
}
// ----------------------------------------------------------------
// Hilfsmethoden
// ----------------------------------------------------------------
private static String getProp(Properties p, String key, String def) {
if (p == null) return def;
String v = p.getProperty(key, def);
return (v == null || v.trim().isEmpty()) ? def : v.trim();
}
private static int getInt(Properties p, String key, int def) {
try { return Integer.parseInt(getProp(p, key, String.valueOf(def))); }
catch (NumberFormatException e) { return def; }
}
private static double getDbl(Properties p, String key, double def) {
try { return Double.parseDouble(getProp(p, key, String.valueOf(def))); }
catch (NumberFormatException e) { return def; }
}
}

View File

@@ -0,0 +1,102 @@
package net.viper.status.modules.economy;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.Plugin;
import java.util.UUID;
/**
* /pay <Spieler> <Betrag>
* Überweist Geld an einen anderen Spieler (auch offline).
*/
public class PayCommand extends Command {
private final Plugin plugin;
private final EconomyManager manager;
public PayCommand(Plugin plugin, EconomyManager manager) {
super("pay", null);
this.plugin = plugin;
this.manager = manager;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (!(sender instanceof ProxiedPlayer)) {
sender.sendMessage(new TextComponent(c("&cNur Spieler können diesen Befehl nutzen.")));
return;
}
if (args.length < 2) {
sender.sendMessage(new TextComponent(c("&eVerwendung: &f/pay <Spieler> <Betrag>")));
return;
}
ProxiedPlayer payer = (ProxiedPlayer) sender;
String targetName = args[0];
double amount;
try {
amount = Double.parseDouble(args[1].replace(",", "."));
} catch (NumberFormatException e) {
sender.sendMessage(new TextComponent(c("&cUngültiger Betrag.")));
return;
}
if (amount <= 0) {
sender.sendMessage(new TextComponent(c("&cDer Betrag muss größer als 0 sein.")));
return;
}
if (targetName.equalsIgnoreCase(payer.getName())) {
sender.sendMessage(new TextComponent(c("&cDu kannst dir nicht selbst Geld überweisen.")));
return;
}
plugin.getProxy().getScheduler().runAsync(plugin, () -> {
UUID targetUUID = manager.resolveUUID(targetName);
if (targetUUID == null) {
payer.sendMessage(new TextComponent(c("&cSpieler &e" + targetName + " &cnicht gefunden.")));
return;
}
double payerBalance = manager.getBalance(payer.getUniqueId());
if (payerBalance < amount) {
payer.sendMessage(new TextComponent(c("&cNicht genug Guthaben. Dein Kontostand: &e"
+ formatAmount(payerBalance) + " $")));
return;
}
manager.withdraw(payer.getUniqueId(), amount);
manager.deposit(targetUUID, amount);
payer.sendMessage(new TextComponent(c("&aErfolgreich &e" + formatAmount(amount)
+ " $ &aan &e" + targetName + " &aüberwiesen.")));
payer.sendMessage(new TextComponent(c("&7Dein neues Guthaben: &e"
+ formatAmount(manager.getBalance(payer.getUniqueId())) + " $")));
// Online-Benachrichtigung an Empfänger
ProxiedPlayer target = plugin.getProxy().getPlayer(targetUUID);
if (target != null) {
target.sendMessage(new TextComponent(c("&a" + payer.getName()
+ " hat dir &e" + formatAmount(amount) + " $ &aüberwiesen.")));
target.sendMessage(new TextComponent(c("&7Dein neues Guthaben: &e"
+ formatAmount(manager.getBalance(targetUUID)) + " $")));
}
});
}
private static String formatAmount(double amount) {
return String.format("%,.2f", amount);
}
private static String c(String msg) {
return ChatColor.translateAlternateColorCodes('&', msg);
}
}