Upload folder via GUI - src
This commit is contained in:
@@ -548,10 +548,7 @@ public class StatusAPI extends Plugin implements Runnable {
|
||||
playerMap.put("deaths", ps.deaths);
|
||||
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;
|
||||
double playerBalance = playerBalances.getOrDefault(ps.uuid, 0.0);
|
||||
Map<String, Object> economy = new LinkedHashMap<>();
|
||||
economy.put("balance", playerBalance);
|
||||
economy.put("total_earned", ps.totalEarned);
|
||||
@@ -575,12 +572,7 @@ public class StatusAPI extends Plugin implements Runnable {
|
||||
// Kein Cache – UUID und Balance kommen direkt aus der DB
|
||||
if ("GET".equalsIgnoreCase(method) && "/economy/player".equalsIgnoreCase(pathOnly)) {
|
||||
Map<String, String> qp = parseQueryParams(path);
|
||||
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 auflösen aus Query-Param
|
||||
UUID ecoUuid = null;
|
||||
String ecoName = null;
|
||||
String uuidParam = qp.get("uuid");
|
||||
@@ -588,16 +580,13 @@ public class StatusAPI extends Plugin implements Runnable {
|
||||
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);
|
||||
// Balance aus playerBalances Map lesen (befüllt von StatusAPIBridge via NexEco)
|
||||
double directBalance = playerBalances.getOrDefault(ecoUuid, 0.0);
|
||||
Map<String, Object> payload = new LinkedHashMap<>();
|
||||
payload.put("success", true);
|
||||
payload.put("uuid", ecoUuid.toString());
|
||||
@@ -610,35 +599,26 @@ public class StatusAPI extends Plugin implements Runnable {
|
||||
}
|
||||
|
||||
// POST /economy/update
|
||||
// Kein Cache – Balance wird direkt in die DB geschrieben
|
||||
// Empfängt Balance-Updates von StatusAPIBridge (Vault/NexEco → HTTP)
|
||||
// Schreibt NUR in playerBalances für Tablist/Scoreboard – KEINE DB-Schreiboperationen
|
||||
if ("POST".equalsIgnoreCase(method) && "/economy/update".equalsIgnoreCase(pathOnly)) {
|
||||
String body = readBody(in, headers);
|
||||
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;
|
||||
}
|
||||
// Balance direkt in DB schreiben
|
||||
// Balance NUR in playerBalances Map speichern (für Tablist/Scoreboard)
|
||||
// Die echte DB-Verwaltung macht ausschließlich NexEco
|
||||
String balStr = extractJsonString(body, "balance");
|
||||
if (balStr != null && !balStr.isEmpty()) {
|
||||
try {
|
||||
double newBal = Double.parseDouble(balStr);
|
||||
ecoModUpd.getManager().setBalance(ecoUpdUuid, newBal);
|
||||
// Auch in der Tablist-Map speichern
|
||||
playerBalances.put(ecoUpdUuid, newBal);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
@@ -939,10 +919,7 @@ public class StatusAPI extends Plugin implements Runnable {
|
||||
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;
|
||||
double statusBalance = playerBalances.getOrDefault(p.getUniqueId(), 0.0);
|
||||
Map<String, Object> eco = new LinkedHashMap<>();
|
||||
eco.put("balance", statusBalance);
|
||||
eco.put("total_earned", ps.totalEarned);
|
||||
|
||||
@@ -1,134 +1,19 @@
|
||||
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
|
||||
* /ecoadmin – wird NICHT mehr auf BungeeCord registriert.
|
||||
* NexEco /eco auf dem Spigot-Server übernimmt Admin-Befehle.
|
||||
*/
|
||||
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;
|
||||
super("ecoadmin_disabled_nexeco", "economy.admin");
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
public void execute(CommandSender sender, String[] args) {}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,16 @@ import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Verwaltet die MySQL-Verbindung (HikariCP) und die Tabelle bc_accounts.
|
||||
* Wird vom EconomyModule gehalten und an den EconomyManager weitergegeben.
|
||||
*
|
||||
* Fixes:
|
||||
* - balance-Spalte als DOUBLE(30,2) statt VARCHAR → kompatibel mit NexEco & SurvivalPlus
|
||||
* - atomare Transaktion für withdraw+deposit → kein Geldverlust bei Absturz
|
||||
* - FOR UPDATE Lock → kein Race-Condition-Bug bei gleichzeitigen Überweisungen
|
||||
*/
|
||||
public class EconomyDatabase {
|
||||
|
||||
private static final String TABLE = "bc_accounts";
|
||||
private static final String TABLE = "bc_accounts";
|
||||
private static final String TABLE_NAMES = "bc_player_names";
|
||||
|
||||
private final Logger log;
|
||||
private HikariDataSource dataSource;
|
||||
@@ -24,11 +29,10 @@ public class EconomyDatabase {
|
||||
this.log = plugin.getLogger();
|
||||
|
||||
HikariConfig cfg = new HikariConfig();
|
||||
// HikariCP Startup-Logs unterdrücken
|
||||
java.util.logging.Logger.getLogger("com.zaxxer.hikari").setLevel(java.util.logging.Level.WARNING);
|
||||
java.util.logging.Logger.getLogger("com.zaxxer.hikari.HikariDataSource").setLevel(java.util.logging.Level.WARNING);
|
||||
cfg.setJdbcUrl("jdbc:mysql://" + host + ":" + port + "/" + database
|
||||
+ "?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&useUnicode=true");
|
||||
+ "?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&useUnicode=true"
|
||||
+ "&allowPublicKeyRetrieval=true");
|
||||
cfg.setUsername(user);
|
||||
cfg.setPassword(password);
|
||||
cfg.setMaximumPoolSize(5);
|
||||
@@ -45,35 +49,42 @@ public class EconomyDatabase {
|
||||
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();
|
||||
// ── bc_accounts: balance als DOUBLE – kompatibel mit NexEco & SurvivalPlus ──
|
||||
try (Connection con = dataSource.getConnection(); Statement st = con.createStatement()) {
|
||||
st.executeUpdate(
|
||||
"CREATE TABLE IF NOT EXISTS `" + TABLE + "` (" +
|
||||
" `player_name` VARCHAR(36) NOT NULL," +
|
||||
" `balance` DOUBLE(30,2) NOT NULL DEFAULT 0.00," +
|
||||
" PRIMARY KEY (`player_name`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
|
||||
);
|
||||
// Falls Tabelle existiert aber balance noch VARCHAR ist → konvertieren
|
||||
st.executeUpdate(
|
||||
"ALTER TABLE `" + TABLE + "` " +
|
||||
"MODIFY COLUMN `balance` DOUBLE(30,2) NOT NULL DEFAULT 0.00"
|
||||
);
|
||||
} catch (SQLException e) {
|
||||
log.severe("[Economy] Tabellen-Setup (bc_accounts) fehlgeschlagen: " + e.getMessage());
|
||||
// ALTER schlägt fehl wenn Typ bereits korrekt ist – kein Problem
|
||||
if (!e.getMessage().contains("Duplicate") && !e.getMessage().contains("doesn't exist")) {
|
||||
log.warning("[Economy] Tabellen-Setup bc_accounts: " + 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();
|
||||
// ── bc_player_names ────────────────────────────────────────────────────
|
||||
try (Connection con = dataSource.getConnection(); Statement st = con.createStatement()) {
|
||||
st.executeUpdate(
|
||||
"CREATE TABLE IF NOT EXISTS `" + TABLE_NAMES + "` (" +
|
||||
" `uuid` VARCHAR(36) NOT NULL PRIMARY KEY," +
|
||||
" `name` VARCHAR(16) NOT NULL," +
|
||||
" `updated` BIGINT NOT NULL" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
|
||||
);
|
||||
if (StatusAPI.DEBUG) log.info("[Economy] MySQL verbunden – Tabellen bereit.");
|
||||
} catch (SQLException e) {
|
||||
log.severe("[Economy] Tabellen-Setup (bc_player_names) fehlgeschlagen: " + e.getMessage());
|
||||
log.severe("[Economy] Tabellen-Setup bc_player_names fehlgeschlagen: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,89 +93,22 @@ public class EconomyDatabase {
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (dataSource != null && !dataSource.isClosed()) {
|
||||
dataSource.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);
|
||||
}
|
||||
// ── Kontostand ────────────────────────────────────────────────────────────
|
||||
|
||||
/** 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` = ?")) {
|
||||
"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"));
|
||||
}
|
||||
if (rs.next()) return rs.getDouble("balance");
|
||||
}
|
||||
} catch (SQLException | NumberFormatException e) {
|
||||
} catch (SQLException e) {
|
||||
log.warning("[Economy] Load fehlgeschlagen für " + uuid + ": " + e.getMessage());
|
||||
}
|
||||
return -1;
|
||||
@@ -175,13 +119,114 @@ public class EconomyDatabase {
|
||||
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`)")) {
|
||||
"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.setDouble(2, balance);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
log.warning("[Economy] Save fehlgeschlagen für " + uuid + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomare Überweisung von → to.
|
||||
* Nutzt eine SQL-Transaktion mit FOR UPDATE Lock – race-condition-sicher.
|
||||
* Gibt false zurück wenn Sender nicht genug Guthaben hat.
|
||||
*/
|
||||
public boolean transfer(UUID from, UUID to, double amount, double startBalance) {
|
||||
if (!isConnected()) return false;
|
||||
Connection con = null;
|
||||
try {
|
||||
con = dataSource.getConnection();
|
||||
con.setAutoCommit(false);
|
||||
|
||||
// Sender sperren und Balance lesen
|
||||
double fromBalance;
|
||||
try (PreparedStatement ps = con.prepareStatement(
|
||||
"SELECT `balance` FROM `" + TABLE + "` WHERE `player_name` = ? FOR UPDATE")) {
|
||||
ps.setString(1, from.toString());
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
fromBalance = rs.next() ? rs.getDouble("balance") : startBalance;
|
||||
}
|
||||
}
|
||||
|
||||
if (fromBalance < amount) { con.rollback(); return false; }
|
||||
|
||||
// Sender abziehen
|
||||
try (PreparedStatement ps = con.prepareStatement(
|
||||
"INSERT INTO `" + TABLE + "` (`player_name`, `balance`) VALUES (?, ?) " +
|
||||
"ON DUPLICATE KEY UPDATE `balance` = VALUES(`balance`)")) {
|
||||
ps.setString(1, from.toString());
|
||||
ps.setDouble(2, fromBalance - amount);
|
||||
ps.executeUpdate();
|
||||
}
|
||||
|
||||
// Empfänger gutschreiben (Konto anlegen falls nötig)
|
||||
try (PreparedStatement ps = con.prepareStatement(
|
||||
"INSERT INTO `" + TABLE + "` (`player_name`, `balance`) VALUES (?, ?) " +
|
||||
"ON DUPLICATE KEY UPDATE `balance` = `balance` + ?")) {
|
||||
ps.setString(1, to.toString());
|
||||
ps.setDouble(2, startBalance + amount);
|
||||
ps.setDouble(3, amount);
|
||||
ps.executeUpdate();
|
||||
}
|
||||
|
||||
con.commit();
|
||||
return true;
|
||||
|
||||
} catch (SQLException e) {
|
||||
log.warning("[Economy] Transfer fehlgeschlagen: " + e.getMessage());
|
||||
try { if (con != null) con.rollback(); } catch (SQLException ex) { /* ignore */ }
|
||||
return false;
|
||||
} finally {
|
||||
try { if (con != null) con.close(); } catch (SQLException ex) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
// ── Name-Lookup ───────────────────────────────────────────────────────────
|
||||
|
||||
public void saveNameMapping(UUID uuid, String name) {
|
||||
if (!isConnected()) return;
|
||||
try (Connection con = dataSource.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement(
|
||||
"INSERT INTO `" + TABLE_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());
|
||||
}
|
||||
}
|
||||
|
||||
public UUID findUUIDByNameOwn(String name) {
|
||||
if (!isConnected()) return null;
|
||||
try (Connection con = dataSource.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement(
|
||||
"SELECT `uuid` FROM `" + TABLE_NAMES + "` WHERE `name` = ? LIMIT 1")) {
|
||||
ps.setString(1, name);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) return UUID.fromString(rs.getString("uuid"));
|
||||
}
|
||||
} catch (SQLException | IllegalArgumentException e) { /* ignorieren */ }
|
||||
return null;
|
||||
}
|
||||
|
||||
public 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 s = rs.getString("player_uuid");
|
||||
if (s != null && !s.isEmpty()) return UUID.fromString(s);
|
||||
}
|
||||
}
|
||||
} catch (SQLException | IllegalArgumentException e) { /* CMI nicht vorhanden – kein Problem */ }
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,37 @@
|
||||
package net.viper.status.modules.economy;
|
||||
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
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;
|
||||
import net.viper.status.StatusAPI;
|
||||
|
||||
/**
|
||||
* EconomyListener – nur noch Aufräumen der playerBalances Map.
|
||||
*
|
||||
* Das Befüllen der Map geschieht ausschließlich durch die StatusAPIBridge
|
||||
* (Spigot) die über Vault/NexEco den Kontostand per HTTP an die StatusAPI sendet.
|
||||
*/
|
||||
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;
|
||||
// EconomyManager wird nicht mehr benötigt
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
});
|
||||
// Wird von StatusAPIBridge befüllt – nichts zu tun beim Login
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onDisconnect(PlayerDisconnectEvent event) {
|
||||
// Beim Logout aus der Map entfernen
|
||||
StatusAPI.playerBalances.remove(event.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
public void cancelTasks() {
|
||||
// Kein periodischer Task mehr nötig
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,110 +1,31 @@
|
||||
package net.viper.status.modules.economy;
|
||||
|
||||
import net.viper.status.StatusAPI;
|
||||
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.).
|
||||
* EconomyManager – Stub, nicht mehr aktiv.
|
||||
* Economy wird ausschließlich über NexEco (Spigot) verwaltet.
|
||||
*/
|
||||
public class EconomyManager {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final EconomyDatabase db;
|
||||
private final double startBalance;
|
||||
public EconomyManager(Plugin plugin, EconomyDatabase db, 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) {}
|
||||
|
||||
public void saveNameMapping(UUID uuid, String name) {
|
||||
db.saveNameMapping(uuid, name);
|
||||
}
|
||||
public UUID resolveUUID(String name) { return null; }
|
||||
|
||||
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);
|
||||
}
|
||||
public double getBalance(UUID uuid) { return 0.0; }
|
||||
|
||||
/** 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);
|
||||
StatusAPI.debugLog(plugin, "[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 void setBalance(UUID uuid, double amount) {}
|
||||
|
||||
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 boolean deposit(UUID uuid, double amount) { return false; }
|
||||
|
||||
public void setBalance(UUID uuid, double amount) {
|
||||
db.save(uuid, Math.max(0.0, amount));
|
||||
}
|
||||
public boolean withdraw(UUID uuid, double amount) { return false; }
|
||||
|
||||
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 transfer(UUID from, UUID to, double amount) { return false; }
|
||||
|
||||
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 false; }
|
||||
|
||||
public boolean hasAccount(UUID uuid) {
|
||||
return db.load(uuid) >= 0;
|
||||
}
|
||||
public double getStartBalance() { return 0.0; }
|
||||
}
|
||||
|
||||
@@ -1,95 +1,32 @@
|
||||
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).
|
||||
* EconomyModule – DEAKTIVIERT.
|
||||
*
|
||||
* 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
|
||||
* Die Economy wird ausschließlich über NexEco (Spigot) verwaltet.
|
||||
* Die StatusAPIBridge (Spigot-Plugin) liest den Kontostand über Vault/NexEco
|
||||
* und pushed ihn per HTTP an die StatusAPI → playerBalances Map.
|
||||
*
|
||||
* 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.
|
||||
* Damit gibt es nur EINE Datenquelle für Kontostände: NexEco / money_accounts.
|
||||
* Das alte EconomyModule schrieb in bc_accounts – das führte zu doppelten,
|
||||
* inkonsistenten Kontoständen.
|
||||
*/
|
||||
public class EconomyModule implements Module {
|
||||
|
||||
private EconomyDatabase database;
|
||||
private EconomyManager manager;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "EconomyModule";
|
||||
}
|
||||
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));
|
||||
|
||||
StatusAPI.debugLog(plugin, "[Economy] EconomyModule aktiviert (start-balance: " + startBal + ").");
|
||||
plugin.getLogger().info("[Economy] EconomyModule ist deaktiviert – NexEco ist zuständig.");
|
||||
plugin.getLogger().info("[Economy] Kontostände kommen via StatusAPIBridge (Vault → NexEco → HTTP).");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable(Plugin plugin) {
|
||||
if (database != null) {
|
||||
database.close();
|
||||
StatusAPI.debugLog(plugin, "[Economy] MySQL-Verbindung geschlossen.");
|
||||
}
|
||||
}
|
||||
public void onDisable(Plugin plugin) {}
|
||||
|
||||
/** 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; }
|
||||
}
|
||||
public EconomyManager getManager() { return null; }
|
||||
}
|
||||
|
||||
@@ -1,102 +1,20 @@
|
||||
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).
|
||||
* /pay – wird NICHT mehr auf BungeeCord registriert.
|
||||
* NexEco auf dem Spigot-Server übernimmt /pay direkt.
|
||||
* Diese Klasse existiert nur noch für Kompilier-Kompatibilität.
|
||||
*/
|
||||
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;
|
||||
super("pay_disabled_nexeco", null);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
public void execute(CommandSender sender, String[] args) {}
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ public class ScoreboardModule implements Module, Listener {
|
||||
String newsStr = buildNewsTicker(nOff);
|
||||
// Finde welche Zeilennummer(n) %news% enthält und sende nur diese
|
||||
java.util.Map<Integer, java.util.List<String>> lineMap =
|
||||
isAdmin ? adminLineMap : playerLineMap;
|
||||
isAdmin ? adminLineMap : isSupporter ? supporterLineMap : playerLineMap;
|
||||
for (java.util.Map.Entry<Integer, java.util.List<String>> entry : lineMap.entrySet()) {
|
||||
boolean hasNews = false;
|
||||
for (String v : entry.getValue()) {
|
||||
|
||||
@@ -16,16 +16,7 @@ commands:
|
||||
usage: /scoreboard [hide|show|player|admin]
|
||||
aliases: [sb, togglesb]
|
||||
|
||||
# ── Economy ───────────────────────────────────────────────
|
||||
pay:
|
||||
description: Überweise Geld an einen Spieler (auch offline)
|
||||
usage: /pay <Spieler> <Betrag>
|
||||
|
||||
ecoadmin:
|
||||
description: Admin-Verwaltung des Economy-Systems
|
||||
usage: /ecoadmin <give|take|set|check> <Spieler> [Betrag]
|
||||
permission: economy.admin
|
||||
aliases: [ecomod, moneyadmin]
|
||||
# /pay und /ecoadmin werden von NexEco (Spigot) verwaltet
|
||||
|
||||
# ── VanishModule ──────────────────────────────────────────
|
||||
vanish:
|
||||
|
||||
@@ -29,7 +29,7 @@ scoreboard.rainbow.mode=wave
|
||||
scoreboard.rainbow.speed=10
|
||||
# Farben: Hex (#RRGGBB oder &#RRGGBB) oder Minecraft-Codes (&0-&f) – kommagetrennt
|
||||
# Leer lassen = voller HSB-Regenbogen
|
||||
scoreboard.rainbow.colors=#FF0000,#FF6600,#FFFF00,#00FF00,#00FFFF,#0000FF,#FF00FF
|
||||
scoreboard.rainbow.colors=&f,&b
|
||||
|
||||
scoreboard.admin_permission=statusapi.scoreboard.admin
|
||||
scoreboard.supporter_permission=statusapi.scoreboard.supporter
|
||||
@@ -55,7 +55,7 @@ scoreboard.money_decimal_separator=,
|
||||
# scoreboard.separator=%gradient:#FF0000:#0000FF:────────────────────% (Farbig)
|
||||
# scoreboard.separator= (Leer/unsichtbar)
|
||||
# ===================================================
|
||||
scoreboard.separator=&8&m--------------------
|
||||
scoreboard.separator=&8&m-----------------------
|
||||
|
||||
# ===================================================
|
||||
# NEWS-TICKER – erscheint als %news% Placeholder
|
||||
@@ -97,8 +97,24 @@ scoreboard.lines.13=%news%
|
||||
scoreboard.lines.14=%line%
|
||||
scoreboard.lines.15=&7%compass%
|
||||
|
||||
# TicketSystem-Zeilen (auskommentiert – nach Bedarf einbinden):
|
||||
# scoreboard.lines.X=&7Tickets: &e%ticket_my_open%
|
||||
# ===================================================
|
||||
# SUPPORTER-ZEILEN
|
||||
# ===================================================
|
||||
scoreboard.supporter_lines.1=%line%
|
||||
scoreboard.supporter_lines.2=%gradient:&b:&f:&b:&l> Player Info:%
|
||||
scoreboard.supporter_lines.3=&7%rank% &f%player%
|
||||
scoreboard.supporter_lines.4=&7Ping: &f%ping%ms &8| &7%server%
|
||||
scoreboard.supporter_lines.5=
|
||||
scoreboard.supporter_lines.6=%gradient:&b:&f:&b:&l> Ticket:%
|
||||
scoreboard.supporter_lines.7=&7Offen: &c%ticket_open%
|
||||
scoreboard.supporter_lines.8=&7In Bearbeitung: &e%ticket_claimed%
|
||||
scoreboard.supporter_lines.9=
|
||||
scoreboard.supporter_lines.10=%gradient:&b:&f:&b:&l> Server Info:%
|
||||
scoreboard.supporter_lines.11=&7Online: &f%online% &8/ &7%maxplayers%
|
||||
scoreboard.supporter_lines.12=
|
||||
scoreboard.supporter_lines.13=
|
||||
scoreboard.supporter_lines.14=%line%
|
||||
scoreboard.supporter_lines.15=&7%compass%
|
||||
|
||||
# ===================================================
|
||||
# ADMIN-ZEILEN
|
||||
@@ -113,30 +129,12 @@ scoreboard.admin_lines.6=
|
||||
scoreboard.admin_lines.7=%gradient:&b:&f:&b:&l> Server Info:%
|
||||
scoreboard.admin_lines.8=&f%server% &8| &7RAM: &e%ram%
|
||||
scoreboard.admin_lines.8.2=&7Proxy: &f%uptime%
|
||||
scoreboard.admin_lines.9=
|
||||
scoreboard.admin_lines.10=&7TPS: &a%tps%
|
||||
scoreboard.admin_lines.11=
|
||||
scoreboard.admin_lines.12=%gradient:&b:&f:&b:&l> Tickets:%
|
||||
scoreboard.admin_lines.13=&7Offen: &c%ticket_open% &8| &7Aktiv: &e%ticket_claimed%
|
||||
scoreboard.admin_lines.14=&7Bewertung: &a%ticket_rating_good%&8/&c%ticket_rating_bad% &7(&f%ticket_rating_pct%&7%&7)
|
||||
scoreboard.admin_lines.15=%line%
|
||||
scoreboard.admin_lines.15.2=&7%compass%
|
||||
|
||||
# ===================================================
|
||||
# SUPPORTER-ZEILEN
|
||||
# ===================================================
|
||||
scoreboard.supporter_lines.1=%line%
|
||||
scoreboard.supporter_lines.2=%gradient:&6:&f:&6:&l> Support Panel:%
|
||||
scoreboard.supporter_lines.3=&7%rank% &f%player%
|
||||
scoreboard.supporter_lines.4=&7Ping: &f%ping%ms &8| &7%server%
|
||||
scoreboard.supporter_lines.5=
|
||||
scoreboard.supporter_lines.6=%gradient:&6:&f:&6:&l> Tickets:%
|
||||
scoreboard.supporter_lines.7=&7Offen: &c%ticket_open%
|
||||
scoreboard.supporter_lines.8=&7Meine Tickets: &e%ticket_my_open%
|
||||
scoreboard.supporter_lines.9=
|
||||
scoreboard.supporter_lines.10=%gradient:&6:&f:&6:&l> Server Info:%
|
||||
scoreboard.supporter_lines.11=&7Online: &f%online% &8/ &7%maxplayers%
|
||||
scoreboard.supporter_lines.12=&7Zeit: &f%time%
|
||||
scoreboard.supporter_lines.13=
|
||||
scoreboard.supporter_lines.14=%line%
|
||||
scoreboard.supporter_lines.15=&7%compass%
|
||||
scoreboard.admin_lines.9=&7TPS: &a%tps%
|
||||
scoreboard.admin_lines.10=
|
||||
scoreboard.admin_lines.11=%gradient:&b:&f:&b:&l> Ticket:%
|
||||
scoreboard.admin_lines.12=&7Tickets Offen: &c%ticket_open%
|
||||
scoreboard.admin_lines.12.2=&7Tickets In Bearbeitung: &e%ticket_claimed%
|
||||
scoreboard.admin_lines.13=&7Spieler: %online% &8/ &7%maxplayers%
|
||||
scoreboard.admin_lines.14=%line%
|
||||
scoreboard.admin_lines.15=&7%compass%
|
||||
scoreboard.admin_lines.15.2=&7Pos: X:&f%x% &7Y:&f%y% &7Z:&f%z%
|
||||
|
||||
Reference in New Issue
Block a user