Update from Git Manager GUI

This commit is contained in:
2026-02-23 13:06:31 +01:00
parent 4a475cae54
commit 2877eb0372
4 changed files with 1304 additions and 484 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,40 @@ public class MySQLManager {
} }
} }
/**
* Migriert den PRIMARY KEY von (uuid, item) auf (uuid, item, slot) falls nötig.
* Nutzt DATABASE() um nur die aktuelle DB zu prüfen (kein Cross-DB-Problem).
*/
private void migrateTargetChestPrimaryKey(Statement st) {
try {
// Slot-Spalte zuerst sicherstellen (muss vor PK-Änderung existieren)
// tryAlterColumn wurde bereits aufgerufen, hier nochmal sicher prüfen
if (!columnExists("asc_target_chests", "slot")) {
st.execute("ALTER TABLE asc_target_chests ADD COLUMN slot INT NOT NULL DEFAULT 0;");
}
// Prüfen ob slot bereits im PRIMARY KEY ist
try (PreparedStatement ps = connection.prepareStatement(
"SELECT COUNT(*) FROM information_schema.KEY_COLUMN_USAGE " +
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'asc_target_chests' " +
"AND CONSTRAINT_NAME = 'PRIMARY' AND COLUMN_NAME = 'slot';")) {
ResultSet rs = ps.executeQuery();
if (rs.next() && rs.getInt(1) > 0) {
rs.close();
return; // Bereits migriert
}
rs.close();
}
// Alten PK droppen und neuen mit slot anlegen
st.execute("ALTER TABLE asc_target_chests DROP PRIMARY KEY, ADD PRIMARY KEY(uuid, item, slot);");
} catch (SQLException e) {
if (!e.getMessage().toLowerCase().contains("primary")) {
e.printStackTrace();
}
}
}
public void disconnect() { public void disconnect() {
try { try {
if (connection != null && !connection.isClosed()) connection.close(); if (connection != null && !connection.isClosed()) connection.close();
@@ -44,70 +78,238 @@ public class MySQLManager {
} }
} }
// ═══════════════════════════════════════════════════════════════════
// BungeeCord-Erweiterung: Sanfte Schema-Migration
// Fügt eine Spalte hinzu, falls sie noch nicht existiert.
// ═══════════════════════════════════════════════════════════════════
/**
* Fügt eine Spalte hinzu, falls sie noch nicht existiert.
* Nutzt DatabaseMetaData statt IF NOT EXISTS (kompatibel mit allen MySQL/MariaDB-Versionen).
*/
private void tryAlterColumn(Statement st, String table, String column, String definition) {
try {
ResultSet rs = st.getConnection().getMetaData().getColumns(null, null, table, column);
if (!rs.next()) {
st.execute("ALTER TABLE " + table + " ADD COLUMN " + column + " " + definition + ";");
}
rs.close();
} catch (SQLException e) {
if (!e.getMessage().toLowerCase().contains("duplicate column")) {
e.printStackTrace();
}
}
}
/**
* Prüft ob eine Spalte in einer Tabelle existiert.
*/
private boolean columnExists(String table, String column) {
try {
ResultSet rs = connection.getMetaData().getColumns(null, null, table, column);
boolean exists = rs.next();
rs.close();
return exists;
} catch (SQLException e) {
return false;
}
}
/**
* Prüft ob ein Index/Constraint auf einer Tabelle existiert.
*/
private boolean indexExists(String table, String indexName) {
try (PreparedStatement ps = connection.prepareStatement(
"SELECT COUNT(*) FROM information_schema.STATISTICS " +
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND INDEX_NAME = ?;")) {
ps.setString(1, table);
ps.setString(2, indexName);
ResultSet rs = ps.executeQuery();
boolean exists = rs.next() && rs.getInt(1) > 0;
rs.close();
return exists;
} catch (SQLException e) {
return false;
}
}
/**
* Erstellt alle Tabellen und führt automatisch alle Schema-Migrationen durch.
* Bestehende Datenbanken älterer Versionen werden vollständig und sicher migriert:
*
* Alte Version → Neue Spalten:
* asc_input_chests : +server
* asc_rest_chests : +server
* asc_target_chests : +slot, +server, PRIMARY KEY (uuid,item) → (uuid,item,slot)
* asc_transfers : +target_server, +source_server
* Neue Tabellen (falls fehlend):
* asc_servers (BungeeCord Heartbeat)
* asc_transfers (CrossLink Transfers)
*/
public void setupTables() { public void setupTables() {
try (Statement st = connection.createStatement()) { try (Statement st = connection.createStatement()) {
st.execute("CREATE TABLE IF NOT EXISTS asc_players (uuid VARCHAR(36) PRIMARY KEY, name VARCHAR(32));");
st.execute("CREATE TABLE IF NOT EXISTS asc_input_chests (uuid VARCHAR(36), chest_id VARCHAR(36), world VARCHAR(32), x INT, y INT, z INT, `public` BOOLEAN DEFAULT FALSE, PRIMARY KEY(uuid, chest_id));"); // ── Basis-Tabellen anlegen (falls nicht vorhanden) ────────────────────
st.execute("CREATE TABLE IF NOT EXISTS asc_target_chests (uuid VARCHAR(36), item VARCHAR(64), world VARCHAR(32), x INT, y INT, z INT, `public` BOOLEAN DEFAULT FALSE, PRIMARY KEY(uuid, item));"); st.execute("CREATE TABLE IF NOT EXISTS asc_players (" +
st.execute("CREATE TABLE IF NOT EXISTS asc_rest_chests (uuid VARCHAR(36), world VARCHAR(32), x INT, y INT, z INT, `public` BOOLEAN DEFAULT FALSE, PRIMARY KEY(uuid));"); "uuid VARCHAR(36) PRIMARY KEY, name VARCHAR(32)" +
");");
st.execute("CREATE TABLE IF NOT EXISTS asc_input_chests (" +
"uuid VARCHAR(36), chest_id VARCHAR(36), world VARCHAR(32)," +
"x INT, y INT, z INT, `public` BOOLEAN DEFAULT FALSE," +
"PRIMARY KEY(uuid, chest_id)" +
");");
// asc_target_chests: neue Installs bekommen slot direkt im Schema.
// Alte Installs werden weiter unten per Migration angepasst.
st.execute("CREATE TABLE IF NOT EXISTS asc_target_chests (" +
"uuid VARCHAR(36), item VARCHAR(64), slot INT NOT NULL DEFAULT 0," +
"world VARCHAR(32), x INT, y INT, z INT, `public` BOOLEAN DEFAULT FALSE," +
"PRIMARY KEY(uuid, item, slot)" +
");");
st.execute("CREATE TABLE IF NOT EXISTS asc_rest_chests (" +
"uuid VARCHAR(36), world VARCHAR(32)," +
"x INT, y INT, z INT, `public` BOOLEAN DEFAULT FALSE," +
"PRIMARY KEY(uuid)" +
");");
// ── asc_transfers: immer beim Start sicherstellen ─────────────────────
// (früher lazy führte dazu dass target_server/source_server fehlten)
st.execute("CREATE TABLE IF NOT EXISTS asc_transfers (" +
"id BIGINT AUTO_INCREMENT PRIMARY KEY," +
"uuid VARCHAR(36) NOT NULL," +
"item VARCHAR(64) NOT NULL," +
"amount INT NOT NULL," +
"target_world VARCHAR(32) NOT NULL," +
"created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" +
");");
// ── asc_servers: BungeeCord Heartbeat ────────────────────────────────
st.execute("CREATE TABLE IF NOT EXISTS asc_servers (" +
"server_name VARCHAR(64) PRIMARY KEY," +
"last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
");");
// ══════════════════════════════════════════════════════════════════════
// SCHEMA-MIGRATIONEN (für bestehende Datenbanken älterer Versionen)
// Jede Migration ist idempotent mehrfaches Ausführen schadet nicht.
// ══════════════════════════════════════════════════════════════════════
// v1 → v2 (BungeeCord): server-Spalten
tryAlterColumn(st, "asc_input_chests", "server", "VARCHAR(64) DEFAULT ''");
tryAlterColumn(st, "asc_target_chests", "server", "VARCHAR(64) DEFAULT ''");
tryAlterColumn(st, "asc_rest_chests", "server", "VARCHAR(64) DEFAULT ''");
// v1 → v2 (BungeeCord): Transfer-Routing-Spalten
tryAlterColumn(st, "asc_transfers", "target_server", "VARCHAR(64) DEFAULT ''");
tryAlterColumn(st, "asc_transfers", "source_server", "VARCHAR(64) DEFAULT ''");
// v2 → v3 (Multi-Target): slot-Spalte + PRIMARY KEY Migration
migrateTargetChestPrimaryKey(st);
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
/** /**
* Transfer-Tabelle für serverübergreifende Sortierung. * setupTransferTable() bleibt für Rückwärtskompatibilität erhalten,
* delegiert aber nur noch an setupTables() da alles konsolidiert wurde.
*/ */
public void setupTransferTable() { public void setupTransferTable() {
try (Statement st = connection.createStatement()) { // Transfers werden jetzt immer in setupTables() angelegt.
st.execute( // Diese Methode existiert nur noch damit bestehende Aufrufe nicht brechen.
"CREATE TABLE IF NOT EXISTS asc_transfers (" +
" id BIGINT AUTO_INCREMENT PRIMARY KEY," +
" uuid VARCHAR(36) NOT NULL," +
" item VARCHAR(64) NOT NULL," +
" amount INT NOT NULL," +
" target_world VARCHAR(32) NOT NULL," +
" created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" +
");"
);
} catch (SQLException e) {
e.printStackTrace();
}
} }
public void addTransfer(String uuid, String item, int amount, String targetWorld) { // ── BungeeCord NEU: Heartbeat ─────────────────────────────────────────────
/**
* Schreibt / aktualisiert den Heartbeat-Eintrag für diesen Server.
* Sollte alle 30 Sekunden asynchron aufgerufen werden.
*/
public void heartbeat(String serverName) {
if (serverName == null || serverName.isEmpty()) return;
try (PreparedStatement ps = connection.prepareStatement( try (PreparedStatement ps = connection.prepareStatement(
"INSERT INTO asc_transfers (uuid, item, amount, target_world) VALUES (?, ?, ?, ?);")) { "REPLACE INTO asc_servers (server_name, last_seen) VALUES (?, NOW());")) {
ps.setString(1, uuid); ps.setString(1, serverName);
ps.setString(2, item);
ps.setInt(3, amount);
ps.setString(4, targetWorld);
ps.executeUpdate(); ps.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
public List<Map<String, Object>> getPendingTransfers(String uuid) { // ── BungeeCord NEU: Transfer mit Server-Routing ───────────────────────────
List<Map<String, Object>> list = new ArrayList<>(); /**
* Legt einen Transfer-Eintrag mit explizitem Ziel-Server an.
*/
public void addTransfer(String uuid, String item, int amount,
String targetWorld, String targetServer, String sourceServer) {
try (PreparedStatement ps = connection.prepareStatement( try (PreparedStatement ps = connection.prepareStatement(
"SELECT id, item, amount, target_world FROM asc_transfers WHERE uuid=? ORDER BY created_at ASC;")) { "INSERT INTO asc_transfers (uuid, item, amount, target_world, target_server, source_server) " +
"VALUES (?, ?, ?, ?, ?, ?);")) {
ps.setString(1, uuid); ps.setString(1, uuid);
ps.setString(2, item);
ps.setInt(3, amount);
ps.setString(4, targetWorld);
ps.setString(5, targetServer != null ? targetServer : "");
ps.setString(6, sourceServer != null ? sourceServer : "");
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* Legacy-Überladung (ohne Server-Routing) Rückwärtskompatibilität.
*/
public void addTransfer(String uuid, String item, int amount, String targetWorld) {
addTransfer(uuid, item, amount, targetWorld, "", "");
}
/**
* Gibt ausstehende Transfers zurück, gefiltert nach serverName.
* serverName leer/null → alle Transfers (Legacy).
* serverName gesetzt → nur Transfers mit target_server == serverName ODER target_server == '' (Legacy).
*/
public List<Map<String, Object>> getPendingTransfers(String uuid, String serverName) {
List<Map<String, Object>> list = new ArrayList<>();
try {
PreparedStatement ps;
if (serverName == null || serverName.isEmpty()) {
ps = connection.prepareStatement(
"SELECT id, item, amount, target_world, target_server, source_server " +
"FROM asc_transfers WHERE uuid=? ORDER BY created_at ASC;");
ps.setString(1, uuid);
} else {
ps = connection.prepareStatement(
"SELECT id, item, amount, target_world, target_server, source_server " +
"FROM asc_transfers WHERE uuid=? AND (target_server=? OR target_server='') " +
"ORDER BY created_at ASC;");
ps.setString(1, uuid);
ps.setString(2, serverName);
}
ResultSet rs = ps.executeQuery(); ResultSet rs = ps.executeQuery();
while (rs.next()) { while (rs.next()) {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("id", rs.getLong("id")); map.put("id", rs.getLong("id"));
map.put("item", rs.getString("item")); map.put("item", rs.getString("item"));
map.put("amount", rs.getInt("amount")); map.put("amount", rs.getInt("amount"));
map.put("target_world", rs.getString("target_world")); map.put("target_world", rs.getString("target_world"));
map.put("target_server", rs.getString("target_server"));
map.put("source_server", rs.getString("source_server"));
list.add(map); list.add(map);
} }
ps.close();
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); e.printStackTrace();
} }
return list; return list;
} }
/** Legacy-Überladung ohne serverName. */
public List<Map<String, Object>> getPendingTransfers(String uuid) {
return getPendingTransfers(uuid, "");
}
public void deleteTransfer(long id) { public void deleteTransfer(long id) {
try (PreparedStatement ps = connection.prepareStatement( try (PreparedStatement ps = connection.prepareStatement(
"DELETE FROM asc_transfers WHERE id=?;")) { "DELETE FROM asc_transfers WHERE id=?;")) {
@@ -142,12 +344,6 @@ public class MySQLManager {
} }
} }
/**
* Gibt alle Spieler aus der asc_players-Tabelle zurück.
* Wird für den Export (MySQL → YAML) benötigt.
*
* @return Liste mit uuid und name je Spieler
*/
public List<Map<String, Object>> getAllPlayers() { public List<Map<String, Object>> getAllPlayers() {
List<Map<String, Object>> list = new ArrayList<>(); List<Map<String, Object>> list = new ArrayList<>();
try (Statement st = connection.createStatement(); try (Statement st = connection.createStatement();
@@ -171,8 +367,15 @@ public class MySQLManager {
} }
public void addInputChest(String uuid, String chestId, String world, int x, int y, int z, boolean isPublic) { public void addInputChest(String uuid, String chestId, String world, int x, int y, int z, boolean isPublic) {
addInputChest(uuid, chestId, world, x, y, z, isPublic, "");
}
/** BungeeCord-Überladung mit serverName. */
public void addInputChest(String uuid, String chestId, String world,
int x, int y, int z, boolean isPublic, String serverName) {
try (PreparedStatement ps = connection.prepareStatement( try (PreparedStatement ps = connection.prepareStatement(
"REPLACE INTO asc_input_chests (uuid, chest_id, world, x, y, z, `public`) VALUES (?, ?, ?, ?, ?, ?, ?);")) { "REPLACE INTO asc_input_chests (uuid, chest_id, world, x, y, z, `public`, server) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?);")) {
ps.setString(1, uuid); ps.setString(1, uuid);
ps.setString(2, chestId); ps.setString(2, chestId);
ps.setString(3, world); ps.setString(3, world);
@@ -180,6 +383,7 @@ public class MySQLManager {
ps.setInt(5, y); ps.setInt(5, y);
ps.setInt(6, z); ps.setInt(6, z);
ps.setBoolean(7, isPublic); ps.setBoolean(7, isPublic);
ps.setString(8, serverName != null ? serverName : "");
ps.executeUpdate(); ps.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); e.printStackTrace();
@@ -206,11 +410,13 @@ public class MySQLManager {
while (rs.next()) { while (rs.next()) {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("chest_id", rs.getString("chest_id")); map.put("chest_id", rs.getString("chest_id"));
map.put("world", rs.getString("world")); map.put("world", rs.getString("world"));
map.put("x", rs.getInt("x")); map.put("x", rs.getInt("x"));
map.put("y", rs.getInt("y")); map.put("y", rs.getInt("y"));
map.put("z", rs.getInt("z")); map.put("z", rs.getInt("z"));
map.put("public", rs.getBoolean("public")); map.put("public", rs.getBoolean("public"));
try { map.put("server", rs.getString("server")); }
catch (SQLException ignored) { map.put("server", ""); }
list.add(map); list.add(map);
} }
} catch (SQLException e) { } catch (SQLException e) {
@@ -226,40 +432,118 @@ public class MySQLManager {
} }
public void setTargetChest(String uuid, String item, String world, int x, int y, int z, boolean isPublic) { public void setTargetChest(String uuid, String item, String world, int x, int y, int z, boolean isPublic) {
setTargetChest(uuid, item, world, x, y, z, isPublic, "");
}
/** BungeeCord-Überladung mit serverName. Slot=0 (Standard, erster Platz). */
public void setTargetChest(String uuid, String item, String world,
int x, int y, int z, boolean isPublic, String serverName) {
setTargetChest(uuid, item, 0, world, x, y, z, isPublic, serverName);
}
/** Vollständige Überladung mit slot für Multi-Target-Support. */
public void setTargetChest(String uuid, String item, int slot, String world,
int x, int y, int z, boolean isPublic, String serverName) {
try (PreparedStatement ps = connection.prepareStatement( try (PreparedStatement ps = connection.prepareStatement(
"REPLACE INTO asc_target_chests (uuid, item, world, x, y, z, `public`) VALUES (?, ?, ?, ?, ?, ?, ?);")) { "REPLACE INTO asc_target_chests (uuid, item, slot, world, x, y, z, `public`, server) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);")) {
ps.setString(1, uuid); ps.setString(1, uuid);
ps.setString(2, item); ps.setString(2, item);
ps.setString(3, world); ps.setInt(3, slot);
ps.setInt(4, x); ps.setString(4, world);
ps.setInt(5, y); ps.setInt(5, x);
ps.setInt(6, z); ps.setInt(6, y);
ps.setBoolean(7, isPublic); ps.setInt(7, z);
ps.setBoolean(8, isPublic);
ps.setString(9, serverName != null ? serverName : "");
ps.executeUpdate(); ps.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
public Map<String, Object> getTargetChest(String uuid, String item) { /** Löscht eine spezifische Slot-Zieltruhe und verschiebt höhere Slots nach unten. */
public void removeTargetChestSlot(String uuid, String item, int slot) {
try (PreparedStatement ps = connection.prepareStatement( try (PreparedStatement ps = connection.prepareStatement(
"SELECT * FROM asc_target_chests WHERE uuid=? AND item=?;")) { "DELETE FROM asc_target_chests WHERE uuid=? AND item=? AND slot=?;")) {
ps.setString(1, uuid);
ps.setString(2, item);
ps.setInt(3, slot);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
// Slots nach dem gelöschten nach unten schieben
try (PreparedStatement ps = connection.prepareStatement(
"UPDATE asc_target_chests SET slot=slot-1 WHERE uuid=? AND item=? AND slot>?;")) {
ps.setString(1, uuid);
ps.setString(2, item);
ps.setInt(3, slot);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
/** Gibt den nächsten freien Slot für ein Item zurück. */
public int getNextTargetSlot(String uuid, String item) {
try (PreparedStatement ps = connection.prepareStatement(
"SELECT COALESCE(MAX(slot)+1, 0) AS next_slot FROM asc_target_chests WHERE uuid=? AND item=?;")) {
ps.setString(1, uuid); ps.setString(1, uuid);
ps.setString(2, item); ps.setString(2, item);
ResultSet rs = ps.executeQuery(); ResultSet rs = ps.executeQuery();
if (rs.next()) { if (rs.next()) return rs.getInt("next_slot");
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
/** Zählt wie viele Zieltruhen für ein bestimmtes Item registriert sind. */
public int countTargetChestsForItem(String uuid, String item) {
try (PreparedStatement ps = connection.prepareStatement(
"SELECT COUNT(*) FROM asc_target_chests WHERE uuid=? AND item=?;")) {
ps.setString(1, uuid);
ps.setString(2, item);
ResultSet rs = ps.executeQuery();
if (rs.next()) return rs.getInt(1);
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
/** Gibt die erste (slot=0) Zieltruhe für ein Item zurück (Legacy-Kompatibilität). */
public Map<String, Object> getTargetChest(String uuid, String item) {
List<Map<String, Object>> all = getTargetChestsForItem(uuid, item);
return all.isEmpty() ? null : all.get(0);
}
/** Gibt ALLE Zieltruhen für ein bestimmtes Item zurück, sortiert nach slot. */
public List<Map<String, Object>> getTargetChestsForItem(String uuid, String item) {
List<Map<String, Object>> list = new ArrayList<>();
try (PreparedStatement ps = connection.prepareStatement(
"SELECT * FROM asc_target_chests WHERE uuid=? AND item=? ORDER BY slot ASC;")) {
ps.setString(1, uuid);
ps.setString(2, item);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("world", rs.getString("world")); map.put("item", rs.getString("item"));
map.put("x", rs.getInt("x")); map.put("slot", rs.getInt("slot"));
map.put("y", rs.getInt("y")); map.put("world", rs.getString("world"));
map.put("z", rs.getInt("z")); map.put("x", rs.getInt("x"));
map.put("y", rs.getInt("y"));
map.put("z", rs.getInt("z"));
map.put("public", rs.getBoolean("public")); map.put("public", rs.getBoolean("public"));
return map; try { map.put("server", rs.getString("server")); }
catch (SQLException ignored) { map.put("server", ""); }
list.add(map);
} }
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); e.printStackTrace();
} }
return null; return list;
} }
public void removeTargetChest(String uuid, String item) { public void removeTargetChest(String uuid, String item) {
@@ -273,20 +557,24 @@ public class MySQLManager {
} }
} }
/** Gibt alle Zieltruhen eines Spielers zurück, sortiert nach item + slot. */
public List<Map<String, Object>> getTargetChests(String uuid) { public List<Map<String, Object>> getTargetChests(String uuid) {
List<Map<String, Object>> list = new ArrayList<>(); List<Map<String, Object>> list = new ArrayList<>();
try (PreparedStatement ps = connection.prepareStatement( try (PreparedStatement ps = connection.prepareStatement(
"SELECT * FROM asc_target_chests WHERE uuid=?;")) { "SELECT * FROM asc_target_chests WHERE uuid=? ORDER BY item ASC, slot ASC;")) {
ps.setString(1, uuid); ps.setString(1, uuid);
ResultSet rs = ps.executeQuery(); ResultSet rs = ps.executeQuery();
while (rs.next()) { while (rs.next()) {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("item", rs.getString("item")); map.put("item", rs.getString("item"));
map.put("world", rs.getString("world")); map.put("slot", rs.getInt("slot"));
map.put("x", rs.getInt("x")); map.put("world", rs.getString("world"));
map.put("y", rs.getInt("y")); map.put("x", rs.getInt("x"));
map.put("z", rs.getInt("z")); map.put("y", rs.getInt("y"));
map.put("z", rs.getInt("z"));
map.put("public", rs.getBoolean("public")); map.put("public", rs.getBoolean("public"));
try { map.put("server", rs.getString("server")); }
catch (SQLException ignored) { map.put("server", ""); }
list.add(map); list.add(map);
} }
} catch (SQLException e) { } catch (SQLException e) {
@@ -302,14 +590,22 @@ public class MySQLManager {
} }
public void setRestChest(String uuid, String world, int x, int y, int z, boolean isPublic) { public void setRestChest(String uuid, String world, int x, int y, int z, boolean isPublic) {
setRestChest(uuid, world, x, y, z, isPublic, "");
}
/** BungeeCord-Überladung mit serverName. */
public void setRestChest(String uuid, String world, int x, int y, int z,
boolean isPublic, String serverName) {
try (PreparedStatement ps = connection.prepareStatement( try (PreparedStatement ps = connection.prepareStatement(
"REPLACE INTO asc_rest_chests (uuid, world, x, y, z, `public`) VALUES (?, ?, ?, ?, ?, ?);")) { "REPLACE INTO asc_rest_chests (uuid, world, x, y, z, `public`, server) " +
"VALUES (?, ?, ?, ?, ?, ?, ?);")) {
ps.setString(1, uuid); ps.setString(1, uuid);
ps.setString(2, world); ps.setString(2, world);
ps.setInt(3, x); ps.setInt(3, x);
ps.setInt(4, y); ps.setInt(4, y);
ps.setInt(5, z); ps.setInt(5, z);
ps.setBoolean(6, isPublic); ps.setBoolean(6, isPublic);
ps.setString(7, serverName != null ? serverName : "");
ps.executeUpdate(); ps.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); e.printStackTrace();
@@ -323,11 +619,13 @@ public class MySQLManager {
ResultSet rs = ps.executeQuery(); ResultSet rs = ps.executeQuery();
if (rs.next()) { if (rs.next()) {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("world", rs.getString("world")); map.put("world", rs.getString("world"));
map.put("x", rs.getInt("x")); map.put("x", rs.getInt("x"));
map.put("y", rs.getInt("y")); map.put("y", rs.getInt("y"));
map.put("z", rs.getInt("z")); map.put("z", rs.getInt("z"));
map.put("public", rs.getBoolean("public")); map.put("public", rs.getBoolean("public"));
try { map.put("server", rs.getString("server")); }
catch (SQLException ignored) { map.put("server", ""); }
return map; return map;
} }
} catch (SQLException e) { } catch (SQLException e) {
@@ -346,7 +644,7 @@ public class MySQLManager {
} }
} }
// --- Hilfsmethoden für serverCrosslink --- // --- Hilfsmethoden für serverCrosslink (unverändert) ---
public List<Map<String, Object>> getAllInputChests() { public List<Map<String, Object>> getAllInputChests() {
List<Map<String, Object>> list = new ArrayList<>(); List<Map<String, Object>> list = new ArrayList<>();
@@ -355,12 +653,14 @@ public class MySQLManager {
while (rs.next()) { while (rs.next()) {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("chest_id", rs.getString("chest_id")); map.put("chest_id", rs.getString("chest_id"));
map.put("uuid", rs.getString("uuid")); map.put("uuid", rs.getString("uuid"));
map.put("world", rs.getString("world")); map.put("world", rs.getString("world"));
map.put("x", rs.getInt("x")); map.put("x", rs.getInt("x"));
map.put("y", rs.getInt("y")); map.put("y", rs.getInt("y"));
map.put("z", rs.getInt("z")); map.put("z", rs.getInt("z"));
map.put("public", rs.getBoolean("public")); map.put("public", rs.getBoolean("public"));
try { map.put("server", rs.getString("server")); }
catch (SQLException ignored) { map.put("server", ""); }
list.add(map); list.add(map);
} }
} catch (SQLException e) { } catch (SQLException e) {
@@ -375,13 +675,15 @@ public class MySQLManager {
ResultSet rs = ps.executeQuery(); ResultSet rs = ps.executeQuery();
while (rs.next()) { while (rs.next()) {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("item", rs.getString("item")); map.put("item", rs.getString("item"));
map.put("uuid", rs.getString("uuid")); map.put("uuid", rs.getString("uuid"));
map.put("world", rs.getString("world")); map.put("world", rs.getString("world"));
map.put("x", rs.getInt("x")); map.put("x", rs.getInt("x"));
map.put("y", rs.getInt("y")); map.put("y", rs.getInt("y"));
map.put("z", rs.getInt("z")); map.put("z", rs.getInt("z"));
map.put("public", rs.getBoolean("public")); map.put("public", rs.getBoolean("public"));
try { map.put("server", rs.getString("server")); }
catch (SQLException ignored) { map.put("server", ""); }
list.add(map); list.add(map);
} }
} catch (SQLException e) { } catch (SQLException e) {
@@ -396,12 +698,14 @@ public class MySQLManager {
ResultSet rs = ps.executeQuery(); ResultSet rs = ps.executeQuery();
if (rs.next()) { if (rs.next()) {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("uuid", rs.getString("uuid")); map.put("uuid", rs.getString("uuid"));
map.put("world", rs.getString("world")); map.put("world", rs.getString("world"));
map.put("x", rs.getInt("x")); map.put("x", rs.getInt("x"));
map.put("y", rs.getInt("y")); map.put("y", rs.getInt("y"));
map.put("z", rs.getInt("z")); map.put("z", rs.getInt("z"));
map.put("public", rs.getBoolean("public")); map.put("public", rs.getBoolean("public"));
try { map.put("server", rs.getString("server")); }
catch (SQLException ignored) { map.put("server", ""); }
return map; return map;
} }
} catch (SQLException e) { } catch (SQLException e) {

View File

@@ -32,6 +32,28 @@ mysql:
# Soll serverübergreifendes Sortieren (mit MySQL) erlaubt sein? # Soll serverübergreifendes Sortieren (mit MySQL) erlaubt sein?
server_crosslink: true server_crosslink: true
# ---------------------------------------------------
# BUNGEE CORD / MULTI-SERVER SETUP
# ---------------------------------------------------
# Eindeutiger Name dieses Servers im BungeeCord-Netzwerk.
# WICHTIG: Jeder Server braucht einen anderen Namen!
#
# Beispiele:
# server_name: "lobby"
# server_name: "survival"
# server_name: "creative"
#
# Leer lassen = Legacy-Modus (welt-basierte Erkennung, kein BungeeCord).
# Fuer BungeeCord MUSS mysql.enabled: true gesetzt sein!
#
# Setup-Schritte:
# 1. Gleiche MySQL-Datenbank auf allen Servern eintragen.
# 2. Auf jedem Server einen einzigartigen server_name setzen.
# 3. mysql.enabled: true und server_crosslink: true setzen.
# 4. Alle Server neu starten - Schema wird automatisch migriert.
server_name: ""
# --------------------------------------------------- # ---------------------------------------------------
# SPRACHE (Language) # SPRACHE (Language)
# --------------------------------------------------- # ---------------------------------------------------
@@ -84,7 +106,6 @@ effects:
# 30+ = SPARSAM (>1,5 Sekunden) # 30+ = SPARSAM (>1,5 Sekunden)
# Für sehr große Server mit schwacher Hardware. # Für sehr große Server mit schwacher Hardware.
# #
sort_interval_ticks: 5
sort_interval_ticks: 5 sort_interval_ticks: 5
@@ -94,11 +115,35 @@ sort_interval_ticks: 5
# Maximale Anzahl an Sortierkisten pro Spielergruppe # Maximale Anzahl an Sortierkisten pro Spielergruppe
chest_limits: chest_limits:
default: 50 # Sollen Truhen-Limits aktiv sein? (true = ja, false = keine Beschraenkung)
vip: 100 enabled: true
# Beispiel für weitere Gruppen:
# supporter: 150 # Jede Gruppe hat eigene Limits fuer input, rest und target.
# admin: 200 # Spieler benoetigen die Permission: autosortchest.limit.<gruppe>
default:
input: 1 # Eingangstruhen (Input)
rest: 1 # Rest-Truhen (Fallback)
target: 50 # Zieltruhen gesamt
target_per_item: 1 # Wie viele Zieltruhen pro Item-Typ erlaubt sind
vip:
input: 2
rest: 2
target: 100
target_per_item: 3
# Weitere Gruppen:
# supporter:
# input: 3
# rest: 2
# target: 150
# target_per_item: 5
# admin:
# input: 5
# rest: 3
# target: 200
# target_per_item: 10
# --------------------------------------------------- # ---------------------------------------------------
# SCHILDFARBEN (Farbcodes wie im Chat) # SCHILDFARBEN (Farbcodes wie im Chat)
@@ -158,4 +203,3 @@ messages:
mode-changed: "&aModus gewechselt: &e%mode%" mode-changed: "&aModus gewechselt: &e%mode%"
mode-public: "&aÖffentlich" mode-public: "&aÖffentlich"
mode-private: "&cPrivat" mode-private: "&cPrivat"

View File

@@ -1,5 +1,5 @@
name: AutoSortChest name: AutoSortChest
version: 2.0 version: 2.2
main: com.viper.autosortchest.Main main: com.viper.autosortchest.Main
api-version: 1.21 api-version: 1.21
authors: [M_Viper] authors: [M_Viper]