diff --git a/src/main/java/net/viper/status/modules/economy/EconomyDatabase.java b/src/main/java/net/viper/status/modules/economy/EconomyDatabase.java deleted file mode 100644 index 6ce2ada..0000000 --- a/src/main/java/net/viper/status/modules/economy/EconomyDatabase.java +++ /dev/null @@ -1,232 +0,0 @@ -package net.viper.status.modules.economy; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import net.viper.status.StatusAPI; -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. - * - * 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_NAMES = "bc_player_names"; - - 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(); - java.util.logging.Logger.getLogger("com.zaxxer.hikari").setLevel(java.util.logging.Level.WARNING); - cfg.setJdbcUrl("jdbc:mysql://" + host + ":" + port + "/" + database - + "?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&useUnicode=true" - + "&allowPublicKeyRetrieval=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()); - return; - } - - // ── 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) { - // 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 ──────────────────────────────────────────────────── - 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()); - } - } - - public boolean isConnected() { - return dataSource != null && !dataSource.isClosed(); - } - - public void close() { - if (dataSource != null && !dataSource.isClosed()) dataSource.close(); - } - - // ── 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` = ?")) { - ps.setString(1, uuid.toString()); - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) return rs.getDouble("balance"); - } - } catch (SQLException 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.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; - } -}