Upload folder via GUI - BCEconomy_pl

This commit is contained in:
Git Manager GUI
2026-05-07 22:02:37 +02:00
parent 417947c396
commit f29bb25435
6 changed files with 667 additions and 0 deletions

92
BCEconomy_pl/pom.xml Normal file
View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.viper</groupId>
<artifactId>BCEconomy</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>vault-repo</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- Vault -->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<scope>provided</scope>
</dependency>
<!-- HikariCP (wird ins JAR gepackt) -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.1.0</version>
<scope>compile</scope>
</dependency>
<!-- MySQL Connector (wird ins JAR gepackt) -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Alle Dependencies ins JAR shaden -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.2</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>net.viper.bceconomy.libs.hikari</shadedPattern>
</relocation>
<relocation>
<pattern>com.mysql</pattern>
<shadedPattern>net.viper.bceconomy.libs.mysql</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,204 @@
package net.viper.bceconomy;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.*;
import java.util.UUID;
import java.util.logging.Logger;
/**
* Verwaltet die MySQL-Verbindung zur bc_accounts Tabelle.
* Kein Cache jeder Zugriff ist ein direkter DB-Query.
* Kompatibel mit der Tabelle die StatusAPI / SurvivalPlus anlegt.
*/
public class BCDatabase {
private static final String TABLE = "bc_accounts";
private static final String TABLE_NAMES = "bc_player_names";
private final Logger log;
private HikariDataSource dataSource;
public BCDatabase(Logger log, String host, int port, String database,
String user, String password) {
this.log = log;
HikariConfig cfg = new HikariConfig();
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("BCEconomy");
cfg.addDataSourceProperty("cachePrepStmts", "true");
cfg.addDataSourceProperty("prepStmtCacheSize", "250");
cfg.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
try {
dataSource = new HikariDataSource(cfg);
} catch (Exception e) {
log.severe("[BCEconomy] MySQL-Verbindung fehlgeschlagen: " + e.getMessage());
return;
}
createTables();
log.info("[BCEconomy] MySQL verbunden Tabellen bereit.");
}
private void createTables() {
// bc_accounts kompatibel mit SurvivalPlus-Struktur (id + player_name + balance)
// Wir legen sie nur an wenn sie noch nicht existieren.
String createAccounts =
"CREATE TABLE IF NOT EXISTS `" + TABLE + "` (" +
" `id` INT(10) NOT NULL AUTO_INCREMENT," +
" `player_name` VARCHAR(50) NOT NULL," +
" `balance` DOUBLE(30,2) NOT NULL DEFAULT 0.00," +
" PRIMARY KEY (`id`)," +
" UNIQUE KEY `player_name` (`player_name`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
String createNames =
"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;";
try (Connection con = dataSource.getConnection()) {
try (PreparedStatement ps = con.prepareStatement(createAccounts)) {
ps.executeUpdate();
}
try (PreparedStatement ps = con.prepareStatement(createNames)) {
ps.executeUpdate();
}
} catch (SQLException e) {
log.severe("[BCEconomy] Tabellen-Setup fehlgeschlagen: " + e.getMessage());
}
}
public boolean isConnected() {
return dataSource != null && !dataSource.isClosed();
}
public void close() {
if (isConnected()) dataSource.close();
}
// ────────────────────────────────────────────────
// Balance direkt aus DB (kein Cache)
// ────────────────────────────────────────────────
/**
* Liest Balance direkt aus der DB.
* @return Balance oder -1 wenn kein Konto vorhanden.
*/
public double loadBalance(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("[BCEconomy] loadBalance fehlgeschlagen für " + uuid + ": " + e.getMessage());
}
return -1;
}
/**
* Schreibt Balance direkt in die DB (INSERT oder UPDATE).
*/
public void saveBalance(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("[BCEconomy] saveBalance fehlgeschlagen für " + uuid + ": " + e.getMessage());
}
}
/**
* Legt ein neues Konto mit Startguthaben an, falls noch keines existiert.
*/
public void createAccountIfAbsent(UUID uuid, double startBalance) {
if (!isConnected()) return;
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement(
"INSERT IGNORE INTO `" + TABLE + "` (`player_name`, `balance`) VALUES (?, ?)")) {
ps.setString(1, uuid.toString());
ps.setDouble(2, startBalance);
ps.executeUpdate();
} catch (SQLException e) {
log.warning("[BCEconomy] createAccount fehlgeschlagen für " + uuid + ": " + e.getMessage());
}
}
public boolean hasAccount(UUID uuid) {
return loadBalance(uuid) >= 0;
}
// ────────────────────────────────────────────────
// Name-Mapping (für Offline-Spieler 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("[BCEconomy] saveNameMapping fehlgeschlagen: " + e.getMessage());
}
}
public UUID resolveUUIDByName(String name) {
if (!isConnected()) return null;
// 1. Eigene bc_player_names Tabelle
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
}
// 2. CMI_users Fallback
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 raw = rs.getString("player_uuid");
if (raw != null && !raw.isEmpty()) return UUID.fromString(raw);
}
}
} catch (SQLException | IllegalArgumentException e) {
// CMI nicht installiert kein Problem
}
return null;
}
}

View File

@@ -0,0 +1,97 @@
package net.viper.bceconomy;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
/**
* BCEconomy Spigot-Plugin für Lobby, Citybuild usw.
*
* Registriert sich als Vault-Economy-Provider und liest/schreibt
* den Kontostand direkt aus der gemeinsamen MySQL-Tabelle bc_accounts.
* Kein Cache immer aktuell, serverübergreifend korrekt.
*
* Voraussetzung: Vault muss auf dem Server installiert sein.
* SurvivalPlus ist NICHT nötig dieses Plugin ersetzt es für Lobby/Citybuild.
*/
public class BCEconomyPlugin extends JavaPlugin implements Listener {
private BCDatabase database;
private BCEconomyProvider provider;
@Override
public void onEnable() {
// Config laden / erzeugen
saveDefaultConfig();
String host = getConfig().getString ("mysql.host", "localhost");
int port = getConfig().getInt ("mysql.port", 3306);
String database = getConfig().getString ("mysql.database", "Survival");
String user = getConfig().getString ("mysql.username", "root");
String password = getConfig().getString ("mysql.password", "");
String currName = getConfig().getString("economy.currency-name", "Dollar");
String currPlural= getConfig().getString("economy.currency-name-plural", "Dollar");
double startBal = getConfig().getDouble("economy.start-balance", 500.0);
int decimals = getConfig().getInt ("economy.decimals", 2);
// DB verbinden
this.database = new BCDatabase(getLogger(), host, port, database, user, password);
if (!this.database.isConnected()) {
getLogger().severe("[BCEconomy] Keine DB-Verbindung Plugin wird deaktiviert.");
Bukkit.getPluginManager().disablePlugin(this);
return;
}
// Vault prüfen
if (Bukkit.getPluginManager().getPlugin("Vault") == null) {
getLogger().severe("[BCEconomy] Vault nicht gefunden Plugin wird deaktiviert.");
Bukkit.getPluginManager().disablePlugin(this);
return;
}
// Economy-Provider registrieren
this.provider = new BCEconomyProvider(this.database, currName, currPlural, startBal, decimals);
getServer().getServicesManager().register(
Economy.class, this.provider, this, ServicePriority.Highest);
// Login-Listener: Konto anlegen + Name-Mapping speichern
Bukkit.getPluginManager().registerEvents(this, this);
getLogger().info("[BCEconomy] Aktiviert Vault-Economy verbunden mit " +
database + "@" + host + ":" + port);
}
@Override
public void onDisable() {
if (database != null) {
database.close();
getLogger().info("[BCEconomy] MySQL-Verbindung geschlossen.");
}
}
/**
* Bei jedem Login: Konto anlegen (falls neu) und Name speichern.
* Alles async kein Blockieren des Main-Threads.
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onLogin(PlayerLoginEvent event) {
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) return;
Player player = event.getPlayer();
Bukkit.getScheduler().runTaskAsynchronously(this, () -> {
database.saveNameMapping(player.getUniqueId(), player.getName());
database.createAccountIfAbsent(player.getUniqueId(),
getConfig().getDouble("economy.start-balance", 500.0));
});
}
public BCDatabase getDatabase() { return database; }
public BCEconomyProvider getProvider() { return provider; }
}

View File

@@ -0,0 +1,245 @@
package net.viper.bceconomy;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.economy.EconomyResponse;
import org.bukkit.OfflinePlayer;
import java.util.List;
import java.util.UUID;
/**
* Vault Economy-Implementierung.
* Jeder Aufruf geht direkt in die DB kein Cache.
*/
public class BCEconomyProvider implements Economy {
private final BCDatabase db;
private final String currencyName;
private final String currencyNamePlural;
private final double startBalance;
private final int decimals;
public BCEconomyProvider(BCDatabase db, String currencyName,
String currencyNamePlural,
double startBalance, int decimals) {
this.db = db;
this.currencyName = currencyName;
this.currencyNamePlural = currencyNamePlural;
this.startBalance = startBalance;
this.decimals = decimals;
}
// ────────────────────────────────────────────────
// Meta
// ────────────────────────────────────────────────
@Override public boolean isEnabled() { return db.isConnected(); }
@Override public String getName() { return "BCEconomy"; }
@Override public boolean hasBankSupport() { return false; }
@Override public int fractionalDigits() { return decimals; }
@Override public String format(double amount) {
return String.format("%,." + decimals + "f " + currencyName, amount);
}
@Override public String currencyNamePlural() { return currencyNamePlural; }
@Override public String currencyNameSingular() { return currencyName; }
// ────────────────────────────────────────────────
// Account
// ────────────────────────────────────────────────
@Override
public boolean hasAccount(OfflinePlayer player) {
return db.hasAccount(player.getUniqueId());
}
@Override
public boolean hasAccount(OfflinePlayer player, String world) {
return hasAccount(player);
}
@Override
public boolean createPlayerAccount(OfflinePlayer player) {
db.createAccountIfAbsent(player.getUniqueId(), startBalance);
if (player.getName() != null)
db.saveNameMapping(player.getUniqueId(), player.getName());
return true;
}
@Override
public boolean createPlayerAccount(OfflinePlayer player, String world) {
return createPlayerAccount(player);
}
// ────────────────────────────────────────────────
// Balance direkt aus DB
// ────────────────────────────────────────────────
@Override
public double getBalance(OfflinePlayer player) {
double bal = db.loadBalance(player.getUniqueId());
if (bal < 0) {
// Neuer Spieler Konto anlegen
db.createAccountIfAbsent(player.getUniqueId(), startBalance);
if (player.getName() != null)
db.saveNameMapping(player.getUniqueId(), player.getName());
return startBalance;
}
return bal;
}
@Override
public double getBalance(OfflinePlayer player, String world) {
return getBalance(player);
}
@Override
public boolean has(OfflinePlayer player, double amount) {
return getBalance(player) >= amount;
}
@Override
public boolean has(OfflinePlayer player, String world, double amount) {
return has(player, amount);
}
// ────────────────────────────────────────────────
// Deposit / Withdraw direkt in DB
// ────────────────────────────────────────────────
@Override
public EconomyResponse depositPlayer(OfflinePlayer player, double amount) {
if (amount < 0)
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.FAILURE,
"Betrag darf nicht negativ sein.");
double current = db.loadBalance(player.getUniqueId());
if (current < 0) {
db.createAccountIfAbsent(player.getUniqueId(), startBalance);
current = startBalance;
}
double newBal = current + amount;
db.saveBalance(player.getUniqueId(), newBal);
return new EconomyResponse(amount, newBal, EconomyResponse.ResponseType.SUCCESS, null);
}
@Override
public EconomyResponse depositPlayer(OfflinePlayer player, String world, double amount) {
return depositPlayer(player, amount);
}
@Override
public EconomyResponse withdrawPlayer(OfflinePlayer player, double amount) {
if (amount < 0)
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.FAILURE,
"Betrag darf nicht negativ sein.");
double current = db.loadBalance(player.getUniqueId());
if (current < 0) current = 0;
if (current < amount)
return new EconomyResponse(amount, current, EconomyResponse.ResponseType.FAILURE,
"Nicht genug Guthaben.");
double newBal = current - amount;
db.saveBalance(player.getUniqueId(), newBal);
return new EconomyResponse(amount, newBal, EconomyResponse.ResponseType.SUCCESS, null);
}
@Override
public EconomyResponse withdrawPlayer(OfflinePlayer player, String world, double amount) {
return withdrawPlayer(player, amount);
}
// ────────────────────────────────────────────────
// Deprecated String-Methoden (Vault-Kompatibilität)
// ────────────────────────────────────────────────
@Override @Deprecated
public boolean hasAccount(String playerName) {
UUID uuid = db.resolveUUIDByName(playerName);
return uuid != null && db.hasAccount(uuid);
}
@Override @Deprecated
public boolean hasAccount(String playerName, String world) { return hasAccount(playerName); }
@Override @Deprecated
public double getBalance(String playerName) {
UUID uuid = db.resolveUUIDByName(playerName);
if (uuid == null) return 0;
double bal = db.loadBalance(uuid);
return bal < 0 ? startBalance : bal;
}
@Override @Deprecated
public double getBalance(String playerName, String world) { return getBalance(playerName); }
@Override @Deprecated
public boolean has(String playerName, double amount) { return getBalance(playerName) >= amount; }
@Override @Deprecated
public boolean has(String playerName, String world, double amount) { return has(playerName, amount); }
@Override @Deprecated
public EconomyResponse depositPlayer(String playerName, double amount) {
UUID uuid = db.resolveUUIDByName(playerName);
if (uuid == null)
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.FAILURE, "Spieler nicht gefunden.");
double current = db.loadBalance(uuid);
if (current < 0) current = 0;
double newBal = current + amount;
db.saveBalance(uuid, newBal);
return new EconomyResponse(amount, newBal, EconomyResponse.ResponseType.SUCCESS, null);
}
@Override @Deprecated
public EconomyResponse depositPlayer(String playerName, String world, double amount) {
return depositPlayer(playerName, amount);
}
@Override @Deprecated
public EconomyResponse withdrawPlayer(String playerName, double amount) {
UUID uuid = db.resolveUUIDByName(playerName);
if (uuid == null)
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.FAILURE, "Spieler nicht gefunden.");
double current = db.loadBalance(uuid);
if (current < 0) current = 0;
if (current < amount)
return new EconomyResponse(amount, current, EconomyResponse.ResponseType.FAILURE, "Nicht genug Guthaben.");
double newBal = current - amount;
db.saveBalance(uuid, newBal);
return new EconomyResponse(amount, newBal, EconomyResponse.ResponseType.SUCCESS, null);
}
@Override @Deprecated
public EconomyResponse withdrawPlayer(String playerName, String world, double amount) {
return withdrawPlayer(playerName, amount);
}
@Override @Deprecated
public boolean createPlayerAccount(String playerName) { return false; }
@Override @Deprecated
public boolean createPlayerAccount(String playerName, String world) { return false; }
// ────────────────────────────────────────────────
// Bank (nicht unterstützt)
// ────────────────────────────────────────────────
@Override public EconomyResponse createBank(String name, String player) { return notSupported(); }
@Override public EconomyResponse createBank(String name, OfflinePlayer p) { return notSupported(); }
@Override public EconomyResponse deleteBank(String name) { return notSupported(); }
@Override public EconomyResponse bankBalance(String name) { return notSupported(); }
@Override public EconomyResponse bankHas(String name, double amount) { return notSupported(); }
@Override public EconomyResponse bankWithdraw(String name, double amount) { return notSupported(); }
@Override public EconomyResponse bankDeposit(String name, double amount) { return notSupported(); }
@Override public EconomyResponse isBankOwner(String name, String p) { return notSupported(); }
@Override public EconomyResponse isBankOwner(String name, OfflinePlayer p) { return notSupported(); }
@Override public EconomyResponse isBankMember(String name, String p) { return notSupported(); }
@Override public EconomyResponse isBankMember(String name, OfflinePlayer p) { return notSupported(); }
@Override public List<String> getBanks() { return List.of(); }
private EconomyResponse notSupported() {
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.NOT_IMPLEMENTED,
"Banken werden nicht unterstützt.");
}
}

View File

@@ -0,0 +1,17 @@
# BCEconomy Konfiguration
# Dieselben Zugangsdaten wie in StatusAPI (verify.properties: economy.mysql.*)
mysql:
host: localhost
port: 3306
database: Survival
username: root
password: ""
economy:
currency-name: Dollar
currency-name-plural: Dollar
currency-symbol: "$"
start-balance: 500.0
# Wie viele Dezimalstellen angezeigt werden (2 = xx.xx)
decimals: 2

View File

@@ -0,0 +1,12 @@
name: BCEconomy
main: net.viper.bceconomy.BCEconomyPlugin
version: 1.0.0
api-version: 1.20
author: M_Viper
description: Serverübergreifendes Economy-System via MySQL (bc_accounts)
depend:
- Vault
softdepend:
- CMI