Update from Git Manager GUI
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -111,6 +111,26 @@ public class MySQLManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stellt sicher, dass die Verbindung offen ist.
|
||||
* Reconnect-Logik für den Fall, dass die MySQL-Verbindung abgelaufen ist
|
||||
* (z.B. nach wait_timeout, Netzwerk-Unterbrechung).
|
||||
* Wird vor jeder DB-Operation aufgerufen.
|
||||
*/
|
||||
private void ensureConnected() {
|
||||
try {
|
||||
if (connection == null || connection.isClosed() || !connection.isValid(2)) {
|
||||
connection = DriverManager.getConnection(
|
||||
"jdbc:mysql://" + host + ":" + port + "/" + database
|
||||
+ "?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&serverTimezone=UTC",
|
||||
user, password
|
||||
);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// BungeeCord-Erweiterung: Sanfte Schema-Migration
|
||||
// Fügt eine Spalte hinzu, falls sie noch nicht existiert.
|
||||
@@ -179,6 +199,7 @@ public class MySQLManager {
|
||||
* asc_transfers (CrossLink Transfers)
|
||||
*/
|
||||
public void setupTables() {
|
||||
ensureConnected();
|
||||
try (Statement st = connection.createStatement()) {
|
||||
|
||||
// ── Basis-Tabellen anlegen (falls nicht vorhanden) ────────────────────
|
||||
@@ -229,6 +250,17 @@ public class MySQLManager {
|
||||
// Jede Migration ist idempotent – mehrfaches Ausführen schadet nicht.
|
||||
// ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
// ── Mülltruchen-Tabellen ──────────────────────────────────────────────
|
||||
st.execute("CREATE TABLE IF NOT EXISTS asc_trash_chests (" +
|
||||
"uuid VARCHAR(36) PRIMARY KEY, world VARCHAR(32)," +
|
||||
"x INT, y INT, z INT, server VARCHAR(64) DEFAULT ''" +
|
||||
");");
|
||||
|
||||
st.execute("CREATE TABLE IF NOT EXISTS asc_trash_items (" +
|
||||
"uuid VARCHAR(36), item VARCHAR(64)," +
|
||||
"PRIMARY KEY(uuid, item)" +
|
||||
");");
|
||||
|
||||
// v1 → v2 (BungeeCord): server-Spalten
|
||||
tryAlterColumn(st, "asc_input_chests", "server", "VARCHAR(64) DEFAULT ''");
|
||||
tryAlterColumn(st, "asc_target_chests", "server", "VARCHAR(64) DEFAULT ''");
|
||||
@@ -265,6 +297,7 @@ public class MySQLManager {
|
||||
*/
|
||||
public void heartbeat(String serverName) {
|
||||
if (serverName == null || serverName.isEmpty()) return;
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"REPLACE INTO asc_servers (server_name, last_seen) VALUES (?, NOW());")) {
|
||||
ps.setString(1, serverName);
|
||||
@@ -280,6 +313,7 @@ public class MySQLManager {
|
||||
*/
|
||||
public void addTransfer(String uuid, String item, int amount,
|
||||
String targetWorld, String targetServer, String sourceServer) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"INSERT INTO asc_transfers (uuid, item, amount, target_world, target_server, source_server) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?);")) {
|
||||
@@ -348,6 +382,7 @@ public class MySQLManager {
|
||||
}
|
||||
|
||||
public void deleteTransfer(long id) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"DELETE FROM asc_transfers WHERE id=?;")) {
|
||||
ps.setLong(1, id);
|
||||
@@ -358,6 +393,7 @@ public class MySQLManager {
|
||||
}
|
||||
|
||||
public void updateTransferAmount(long id, int newAmount) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"UPDATE asc_transfers SET amount=? WHERE id=?;")) {
|
||||
ps.setInt(1, newAmount);
|
||||
@@ -371,6 +407,7 @@ public class MySQLManager {
|
||||
// --- Spieler ---
|
||||
|
||||
public void savePlayer(String uuid, String name) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"REPLACE INTO asc_players (uuid, name) VALUES (?, ?);")) {
|
||||
ps.setString(1, uuid);
|
||||
@@ -382,6 +419,7 @@ public class MySQLManager {
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getAllPlayers() {
|
||||
ensureConnected();
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
try (Statement st = connection.createStatement();
|
||||
ResultSet rs = st.executeQuery("SELECT uuid, name FROM asc_players;")) {
|
||||
@@ -410,6 +448,7 @@ public class MySQLManager {
|
||||
/** BungeeCord-Überladung mit serverName. */
|
||||
public void addInputChest(String uuid, String chestId, String world,
|
||||
int x, int y, int z, boolean isPublic, String serverName) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"REPLACE INTO asc_input_chests (uuid, chest_id, world, x, y, z, `public`, server) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?);")) {
|
||||
@@ -428,6 +467,7 @@ public class MySQLManager {
|
||||
}
|
||||
|
||||
public void removeInputChest(String uuid, String chestId) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"DELETE FROM asc_input_chests WHERE uuid=? AND chest_id=?;")) {
|
||||
ps.setString(1, uuid);
|
||||
@@ -439,6 +479,7 @@ public class MySQLManager {
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getInputChests(String uuid) {
|
||||
ensureConnected();
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"SELECT * FROM asc_input_chests WHERE uuid=?;")) {
|
||||
@@ -481,6 +522,7 @@ public class MySQLManager {
|
||||
/** 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) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"REPLACE INTO asc_target_chests (uuid, item, slot, world, x, y, z, `public`, server) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);")) {
|
||||
@@ -558,6 +600,7 @@ public class MySQLManager {
|
||||
|
||||
/** Gibt ALLE Zieltruhen für ein bestimmtes Item zurück, sortiert nach slot. */
|
||||
public List<Map<String, Object>> getTargetChestsForItem(String uuid, String item) {
|
||||
ensureConnected();
|
||||
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;")) {
|
||||
@@ -584,6 +627,7 @@ public class MySQLManager {
|
||||
}
|
||||
|
||||
public void removeTargetChest(String uuid, String item) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"DELETE FROM asc_target_chests WHERE uuid=? AND item=?;")) {
|
||||
ps.setString(1, uuid);
|
||||
@@ -596,6 +640,7 @@ public class MySQLManager {
|
||||
|
||||
/** Gibt alle Zieltruhen eines Spielers zurück, sortiert nach item + slot. */
|
||||
public List<Map<String, Object>> getTargetChests(String uuid) {
|
||||
ensureConnected();
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"SELECT * FROM asc_target_chests WHERE uuid=? ORDER BY item ASC, slot ASC;")) {
|
||||
@@ -645,6 +690,7 @@ public class MySQLManager {
|
||||
/** Vollständige Überladung mit explizitem slot. */
|
||||
public void setRestChest(String uuid, int slot, String world, int x, int y, int z,
|
||||
boolean isPublic, String serverName) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"REPLACE INTO asc_rest_chests (uuid, slot, world, x, y, z, `public`, server) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?);")) {
|
||||
@@ -696,6 +742,7 @@ public class MySQLManager {
|
||||
|
||||
/** Zählt wie viele Rest-Truhen ein Spieler hat. */
|
||||
public int countRestChests(String uuid) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"SELECT COUNT(*) FROM asc_rest_chests WHERE uuid=?;")) {
|
||||
ps.setString(1, uuid);
|
||||
@@ -709,6 +756,7 @@ public class MySQLManager {
|
||||
|
||||
/** Gibt ALLE Rest-Truhen eines Spielers zurück, sortiert nach slot. */
|
||||
public List<Map<String, Object>> getRestChests(String uuid) {
|
||||
ensureConnected();
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"SELECT * FROM asc_rest_chests WHERE uuid=? ORDER BY slot ASC;")) {
|
||||
@@ -740,6 +788,7 @@ public class MySQLManager {
|
||||
|
||||
/** Löscht eine spezifische Rest-Truhe anhand ihrer Location. */
|
||||
public void removeRestChestByLocation(String uuid, String world, int x, int y, int z) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"DELETE FROM asc_rest_chests WHERE uuid=? AND world=? AND x=? AND y=? AND z=?;")) {
|
||||
ps.setString(1, uuid);
|
||||
@@ -870,4 +919,198 @@ public class MySQLManager {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
// MÜLLTRUCHEN (asc_trash_chests + asc_trash_items)
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/** Legt eine Mülltruche an oder aktualisiert sie. */
|
||||
public void setTrashChest(String uuid, String world, int x, int y, int z, String server) {
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"INSERT INTO asc_trash_chests (uuid, world, x, y, z, server) VALUES (?,?,?,?,?,?) " +
|
||||
"ON DUPLICATE KEY UPDATE world=VALUES(world), x=VALUES(x), y=VALUES(y), z=VALUES(z), server=VALUES(server)")) {
|
||||
ps.setString(1, uuid);
|
||||
ps.setString(2, world);
|
||||
ps.setInt(3, x);
|
||||
ps.setInt(4, y);
|
||||
ps.setInt(5, z);
|
||||
ps.setString(6, server == null ? "" : server);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) { e.printStackTrace(); }
|
||||
}
|
||||
|
||||
/** Gibt die Mülltruche eines Spielers zurück (nur die des eigenen Servers). */
|
||||
public Map<String, Object> getTrashChest(String uuid, String serverName) {
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"SELECT * FROM asc_trash_chests WHERE uuid=? AND (server=? OR server='') LIMIT 1")) {
|
||||
ps.setString(1, uuid);
|
||||
ps.setString(2, serverName == null ? "" : serverName);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("world", rs.getString("world"));
|
||||
map.put("x", rs.getInt("x"));
|
||||
map.put("y", rs.getInt("y"));
|
||||
map.put("z", rs.getInt("z"));
|
||||
map.put("server", rs.getString("server"));
|
||||
return map;
|
||||
}
|
||||
} catch (SQLException e) { e.printStackTrace(); }
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Gibt ALLE Mülltruchen zurück (für sign-update oder cross-server). */
|
||||
public List<Map<String, Object>> getAllTrashChests() {
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
try (PreparedStatement ps = connection.prepareStatement("SELECT * FROM asc_trash_chests")) {
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("uuid", rs.getString("uuid"));
|
||||
map.put("world", rs.getString("world"));
|
||||
map.put("x", rs.getInt("x"));
|
||||
map.put("y", rs.getInt("y"));
|
||||
map.put("z", rs.getInt("z"));
|
||||
map.put("server", rs.getString("server"));
|
||||
list.add(map);
|
||||
}
|
||||
} catch (SQLException e) { e.printStackTrace(); }
|
||||
return list;
|
||||
}
|
||||
|
||||
/** Entfernt die Mülltruche eines Spielers. */
|
||||
public void removeTrashChest(String uuid) {
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"DELETE FROM asc_trash_chests WHERE uuid=?")) {
|
||||
ps.setString(1, uuid);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) { e.printStackTrace(); }
|
||||
}
|
||||
|
||||
/** Speichert die komplette Filter-Liste (ersetzt alte Einträge). */
|
||||
public void setTrashItems(String uuid, List<String> items) {
|
||||
try (PreparedStatement del = connection.prepareStatement(
|
||||
"DELETE FROM asc_trash_items WHERE uuid=?")) {
|
||||
del.setString(1, uuid);
|
||||
del.executeUpdate();
|
||||
} catch (SQLException e) { e.printStackTrace(); }
|
||||
if (items == null || items.isEmpty()) return;
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"INSERT IGNORE INTO asc_trash_items (uuid, item) VALUES (?,?)")) {
|
||||
for (String item : items) {
|
||||
ps.setString(1, uuid);
|
||||
ps.setString(2, item);
|
||||
ps.addBatch();
|
||||
}
|
||||
ps.executeBatch();
|
||||
} catch (SQLException e) { e.printStackTrace(); }
|
||||
}
|
||||
|
||||
/** Lädt die Filter-Liste eines Spielers. */
|
||||
public List<String> getTrashItems(String uuid) {
|
||||
List<String> list = new ArrayList<>();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"SELECT item FROM asc_trash_items WHERE uuid=?")) {
|
||||
ps.setString(1, uuid);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) list.add(rs.getString("item"));
|
||||
} catch (SQLException e) { e.printStackTrace(); }
|
||||
return list;
|
||||
}
|
||||
|
||||
/** Fügt ein einzelnes Item zur Filter-Liste hinzu. */
|
||||
public void addTrashItem(String uuid, String item) {
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"INSERT IGNORE INTO asc_trash_items (uuid, item) VALUES (?,?)")) {
|
||||
ps.setString(1, uuid);
|
||||
ps.setString(2, item);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) { e.printStackTrace(); }
|
||||
}
|
||||
|
||||
/** Entfernt ein einzelnes Item aus der Filter-Liste. */
|
||||
public void removeTrashItem(String uuid, String item) {
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"DELETE FROM asc_trash_items WHERE uuid=? AND item=?")) {
|
||||
ps.setString(1, uuid);
|
||||
ps.setString(2, item);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) { e.printStackTrace(); }
|
||||
}
|
||||
|
||||
/** Entfernt alle Filter-Items eines Spielers. */
|
||||
public void removeAllTrashItems(String uuid) {
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"DELETE FROM asc_trash_items WHERE uuid=?")) {
|
||||
ps.setString(1, uuid);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) { e.printStackTrace(); }
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// FIX: Einzelne UNION-Abfrage statt 3 separater Queries für isChestPublic()
|
||||
// Reduziert Main-Thread-Blockierung bei MySQL um ~66%.
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
/**
|
||||
* Prüft ob eine Truhen-Location in IRGENDEINER Tabelle als public markiert ist.
|
||||
* Kombiniert Input-, Target- und Rest-Tabelle in einer einzigen UNION-Abfrage.
|
||||
* @return true wenn mindestens eine Zeile `public=TRUE` enthält.
|
||||
*/
|
||||
public boolean isLocationPublic(String world, int x, int y, int z) {
|
||||
ensureConnected();
|
||||
// MySQL erfordert Klammern um einzelne SELECT-Statements wenn LIMIT verwendet wird
|
||||
String sql =
|
||||
"(SELECT 1 FROM asc_input_chests WHERE world=? AND x=? AND y=? AND z=? AND `public`=TRUE LIMIT 1) " +
|
||||
"UNION ALL " +
|
||||
"(SELECT 1 FROM asc_target_chests WHERE world=? AND x=? AND y=? AND z=? AND `public`=TRUE LIMIT 1) " +
|
||||
"UNION ALL " +
|
||||
"(SELECT 1 FROM asc_rest_chests WHERE world=? AND x=? AND y=? AND z=? AND `public`=TRUE LIMIT 1)";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int base = i * 4;
|
||||
ps.setString(base + 1, world);
|
||||
ps.setInt (base + 2, x);
|
||||
ps.setInt (base + 3, y);
|
||||
ps.setInt (base + 4, z);
|
||||
}
|
||||
ResultSet rs = ps.executeQuery();
|
||||
boolean result = rs.next();
|
||||
rs.close();
|
||||
return result;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// FIX: MySQL-Implementierung für findItemForChestLocation()
|
||||
// Wurde bisher übersprungen – Clean-Sign-Modus + MySQL war dadurch broken.
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
/**
|
||||
* Findet den Item-Typ (z.B. "IRON_ORE") der einer Truhen-Location als Zieltruhe
|
||||
* zugewiesen ist. Wird von findItemForChestLocation() im Clean-Sign-Modus benötigt.
|
||||
* @return Material-Name oder null wenn nicht gefunden.
|
||||
*/
|
||||
public String getItemForLocation(String uuid, String world, int x, int y, int z) {
|
||||
ensureConnected();
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"SELECT item FROM asc_target_chests WHERE uuid=? AND world=? AND x=? AND y=? AND z=? LIMIT 1;")) {
|
||||
ps.setString(1, uuid);
|
||||
ps.setString(2, world);
|
||||
ps.setInt(3, x);
|
||||
ps.setInt(4, y);
|
||||
ps.setInt(5, z);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
String item = rs.getString("item");
|
||||
rs.close();
|
||||
return item;
|
||||
}
|
||||
rs.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
512
src/main/java/com/viper/autosortchest/TrashChestManager.java
Normal file
512
src/main/java/com/viper/autosortchest/TrashChestManager.java
Normal file
@@ -0,0 +1,512 @@
|
||||
package com.viper.autosortchest;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Chest;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
import org.bukkit.profile.PlayerProfile;
|
||||
import org.bukkit.profile.PlayerTextures;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Verwaltet Mülltruhen für das AutoSortChest-Plugin.
|
||||
*
|
||||
* Speicherung:
|
||||
* MySQL (wenn aktiv):
|
||||
* asc_trash_chests : uuid, world, x, y, z, server
|
||||
* asc_trash_items : uuid, item
|
||||
* YAML (Fallback):
|
||||
* players.<uuid>.trash-chest.world / x / y / z
|
||||
* players.<uuid>.trash-items (StringList)
|
||||
*/
|
||||
public class TrashChestManager {
|
||||
|
||||
private static final String SKULL_TEXTURE =
|
||||
"http://textures.minecraft.net/texture/942e7fb9b8eae22d55e32b8222f38eca7b2c41948b15d769b716d80f9d113611";
|
||||
|
||||
/** Gibt den sprachabhängigen GUI-Titel zurück (Farbe aus chest-titles.trash). */
|
||||
private String getGuiTitle() {
|
||||
boolean isEn = "en".equalsIgnoreCase(plugin.getConfig().getString("language", "de"));
|
||||
String colorPrefix = getChestTitleColor();
|
||||
String label = isEn ? "Configure Trash Chest" : "Mülltruhe konfigurieren";
|
||||
return colorPrefix + ChatColor.BOLD + label;
|
||||
}
|
||||
|
||||
private final Main plugin;
|
||||
|
||||
/** UUID → Truhen-Location (In-Memory-Cache für diesen Server) */
|
||||
private final Map<UUID, Location> trashChestLocations = new HashMap<>();
|
||||
/** UUID → Filter-Liste */
|
||||
private final Map<UUID, List<String>> trashFilterLists = new HashMap<>();
|
||||
/** Location-Key → Besitzer-UUID */
|
||||
private final Map<String, UUID> locationToOwner = new HashMap<>();
|
||||
/** Spieler-UUID → Truhen-Besitzer-UUID (offene GUIs) */
|
||||
private final Map<UUID, UUID> openGuiOwners = new HashMap<>();
|
||||
|
||||
private BukkitTask autoTrashTask = null;
|
||||
|
||||
public TrashChestManager(Main plugin) {
|
||||
this.plugin = plugin;
|
||||
loadAllTrashChests();
|
||||
plugin.getServer().getPluginManager().registerEvents(new GuiListener(), plugin);
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
// LADEN
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private void loadAllTrashChests() {
|
||||
if (plugin.isMysqlEnabled() && plugin.getMysqlManager() != null) {
|
||||
loadMySQL();
|
||||
} else {
|
||||
loadYaml();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadMySQL() {
|
||||
MySQLManager db = plugin.getMysqlManager();
|
||||
String myServer = plugin.getServerName();
|
||||
for (Map<String, Object> row : db.getAllTrashChests()) {
|
||||
String srv = (String) row.getOrDefault("server", "");
|
||||
if (!srv.isEmpty() && !srv.equals(myServer)) continue;
|
||||
try {
|
||||
UUID uuid = UUID.fromString((String) row.get("uuid"));
|
||||
World world = Bukkit.getWorld((String) row.get("world"));
|
||||
if (world == null) continue;
|
||||
Location loc = new Location(world, (int) row.get("x"), (int) row.get("y"), (int) row.get("z"));
|
||||
trashChestLocations.put(uuid, loc);
|
||||
locationToOwner.put(locKey(loc), uuid);
|
||||
trashFilterLists.put(uuid, new ArrayList<>(db.getTrashItems(uuid.toString())));
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadYaml() {
|
||||
FileConfiguration data = plugin.getPlayerData();
|
||||
if (data == null || data.getConfigurationSection("players") == null) return;
|
||||
for (String uuidStr : data.getConfigurationSection("players").getKeys(false)) {
|
||||
try {
|
||||
UUID uuid = UUID.fromString(uuidStr);
|
||||
String base = "players." + uuidStr + ".trash-chest";
|
||||
if (!data.contains(base + ".world")) continue;
|
||||
World world = Bukkit.getWorld(data.getString(base + ".world"));
|
||||
if (world == null) continue;
|
||||
Location loc = new Location(world,
|
||||
data.getInt(base + ".x"), data.getInt(base + ".y"), data.getInt(base + ".z"));
|
||||
trashChestLocations.put(uuid, loc);
|
||||
locationToOwner.put(locKey(loc), uuid);
|
||||
trashFilterLists.put(uuid, new ArrayList<>(data.getStringList("players." + uuidStr + ".trash-items")));
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
// SPEICHERN
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private void saveTrashChest(UUID uuid) {
|
||||
if (plugin.isMysqlEnabled() && plugin.getMysqlManager() != null) {
|
||||
saveMySQL(uuid);
|
||||
} else {
|
||||
saveYaml(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMySQL(UUID uuid) {
|
||||
MySQLManager db = plugin.getMysqlManager();
|
||||
String uuidStr = uuid.toString();
|
||||
Location loc = trashChestLocations.get(uuid);
|
||||
if (loc != null && loc.getWorld() != null) {
|
||||
db.setTrashChest(uuidStr, loc.getWorld().getName(),
|
||||
loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(),
|
||||
plugin.getServerName());
|
||||
db.savePlayer(uuidStr, Bukkit.getOfflinePlayer(uuid).getName());
|
||||
} else {
|
||||
db.removeTrashChest(uuidStr);
|
||||
}
|
||||
db.setTrashItems(uuidStr, trashFilterLists.getOrDefault(uuid, new ArrayList<>()));
|
||||
}
|
||||
|
||||
private void saveYaml(UUID uuid) {
|
||||
FileConfiguration data = plugin.getPlayerData();
|
||||
String uuidStr = uuid.toString();
|
||||
Location loc = trashChestLocations.get(uuid);
|
||||
if (loc != null && loc.getWorld() != null) {
|
||||
String path = "players." + uuidStr + ".trash-chest";
|
||||
data.set(path + ".world", loc.getWorld().getName());
|
||||
data.set(path + ".x", loc.getBlockX());
|
||||
data.set(path + ".y", loc.getBlockY());
|
||||
data.set(path + ".z", loc.getBlockZ());
|
||||
} else {
|
||||
data.set("players." + uuidStr + ".trash-chest", null);
|
||||
}
|
||||
data.set("players." + uuidStr + ".trash-items",
|
||||
trashFilterLists.getOrDefault(uuid, new ArrayList<>()));
|
||||
plugin.savePlayerDataPublic();
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
// TRUHE VERWALTEN
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public void setTrashChestLocation(UUID uuid, Location loc) {
|
||||
Location old = trashChestLocations.get(uuid);
|
||||
if (old != null) locationToOwner.remove(locKey(old));
|
||||
trashChestLocations.put(uuid, loc);
|
||||
locationToOwner.put(locKey(loc), uuid);
|
||||
trashFilterLists.putIfAbsent(uuid, new ArrayList<>());
|
||||
saveTrashChest(uuid);
|
||||
}
|
||||
|
||||
public void removeTrashChest(UUID uuid) {
|
||||
Location loc = trashChestLocations.remove(uuid);
|
||||
if (loc != null) locationToOwner.remove(locKey(loc));
|
||||
trashFilterLists.remove(uuid);
|
||||
if (plugin.isMysqlEnabled() && plugin.getMysqlManager() != null) {
|
||||
plugin.getMysqlManager().removeTrashChest(uuid.toString());
|
||||
plugin.getMysqlManager().removeAllTrashItems(uuid.toString());
|
||||
} else {
|
||||
FileConfiguration data = plugin.getPlayerData();
|
||||
data.set("players." + uuid + ".trash-chest", null);
|
||||
data.set("players." + uuid + ".trash-items", null);
|
||||
plugin.savePlayerDataPublic();
|
||||
}
|
||||
}
|
||||
|
||||
public UUID getTrashChestOwner(Location loc) {
|
||||
return locationToOwner.get(locKey(loc));
|
||||
}
|
||||
|
||||
public Location getTrashChestLocation(UUID uuid) {
|
||||
return trashChestLocations.get(uuid);
|
||||
}
|
||||
|
||||
public Map<UUID, Location> getAllTrashChests() {
|
||||
return new HashMap<>(trashChestLocations);
|
||||
}
|
||||
|
||||
public boolean isTrashItem(UUID uuid, Material mat) {
|
||||
if (!trashChestLocations.containsKey(uuid)) return false;
|
||||
List<String> filter = trashFilterLists.getOrDefault(uuid, new ArrayList<>());
|
||||
// Leerer Filter = keine Items werden weitergeleitet (Mülltruhe deaktiviert)
|
||||
return !filter.isEmpty() && filter.contains(mat.name());
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
// ITEM-VERARBEITUNG
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public void processTrashChestInventory(UUID ownerUUID, Inventory inv) {
|
||||
List<String> filter = trashFilterLists.getOrDefault(ownerUUID, new ArrayList<>());
|
||||
// Leerer Filter = keine Items löschen (Mülltruhe deaktiviert bis Items konfiguriert sind)
|
||||
if (filter.isEmpty()) return;
|
||||
for (int i = 0; i < inv.getSize(); i++) {
|
||||
ItemStack item = inv.getItem(i);
|
||||
if (item == null || item.getType() == Material.AIR) continue;
|
||||
if (filter.contains(item.getType().name())) inv.setItem(i, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearTrashChest(UUID ownerUUID) {
|
||||
Location loc = trashChestLocations.get(ownerUUID);
|
||||
if (loc == null || loc.getWorld() == null) return;
|
||||
if (loc.getBlock().getState() instanceof Chest chest) chest.getInventory().clear();
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
// FILTER-LISTE
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public boolean addToFilter(UUID uuid, Material mat) {
|
||||
List<String> filter = trashFilterLists.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||
if (filter.contains(mat.name())) return false;
|
||||
filter.add(mat.name());
|
||||
if (plugin.isMysqlEnabled() && plugin.getMysqlManager() != null) {
|
||||
plugin.getMysqlManager().addTrashItem(uuid.toString(), mat.name());
|
||||
} else {
|
||||
saveTrashChest(uuid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean removeFromFilter(UUID uuid, Material mat) {
|
||||
List<String> filter = trashFilterLists.get(uuid);
|
||||
if (filter == null) return false;
|
||||
boolean removed = filter.remove(mat.name());
|
||||
if (removed) {
|
||||
if (plugin.isMysqlEnabled() && plugin.getMysqlManager() != null) {
|
||||
plugin.getMysqlManager().removeTrashItem(uuid.toString(), mat.name());
|
||||
} else {
|
||||
saveTrashChest(uuid);
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
public List<String> getFilter(UUID uuid) {
|
||||
return trashFilterLists.getOrDefault(uuid, new ArrayList<>());
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
// AUTO-CLEAR-TASK
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public void startAutoTrashTask() {
|
||||
stopAutoTrashTask();
|
||||
int intervalSeconds = plugin.getConfig().getInt("trash.auto_clear_interval_seconds", 0);
|
||||
if (intervalSeconds <= 0) return;
|
||||
long ticks = intervalSeconds * 20L;
|
||||
autoTrashTask = new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (Map.Entry<UUID, Location> entry : new HashMap<>(trashChestLocations).entrySet()) {
|
||||
Location loc = entry.getValue();
|
||||
if (loc == null || loc.getWorld() == null) continue;
|
||||
if (loc.getBlock().getState() instanceof Chest chest) {
|
||||
processTrashChestInventory(entry.getKey(), chest.getInventory());
|
||||
}
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(plugin, ticks, ticks);
|
||||
}
|
||||
|
||||
public void stopAutoTrashTask() {
|
||||
if (autoTrashTask != null) { autoTrashTask.cancel(); autoTrashTask = null; }
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
// KONFIGURATIONS-GUI
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public void openConfigGui(Player player, UUID ownerUUID) {
|
||||
openGuiOwners.put(player.getUniqueId(), ownerUUID);
|
||||
Inventory gui = Bukkit.createInventory(null, 54, getGuiTitle());
|
||||
|
||||
List<String> filter = trashFilterLists.getOrDefault(ownerUUID, new ArrayList<>());
|
||||
int displaySlot = 0;
|
||||
for (String matName : filter) {
|
||||
if (displaySlot >= 45) break;
|
||||
Material mat = Material.matchMaterial(matName);
|
||||
if (mat == null || mat == Material.AIR) continue;
|
||||
ItemStack display = new ItemStack(mat, 1);
|
||||
ItemMeta meta = display.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(getSignColor("trash", "line1") + formatMaterialName(matName));
|
||||
meta.setLore(Arrays.asList(getSignColor("trash", "line4") + "Rechtsklick: Entfernen"));
|
||||
display.setItemMeta(meta);
|
||||
}
|
||||
gui.setItem(displaySlot++, display);
|
||||
}
|
||||
|
||||
ItemStack filler = new ItemStack(Material.BLACK_STAINED_GLASS_PANE);
|
||||
ItemMeta fillerMeta = filler.getItemMeta();
|
||||
if (fillerMeta != null) { fillerMeta.setDisplayName(" "); filler.setItemMeta(fillerMeta); }
|
||||
for (int i = 45; i <= 53; i++) gui.setItem(i, filler.clone());
|
||||
|
||||
ItemStack modeInfo = new ItemStack(filter.isEmpty() ? Material.BARRIER : Material.WATER_BUCKET);
|
||||
ItemMeta modeMeta = modeInfo.getItemMeta();
|
||||
if (modeMeta != null) {
|
||||
if (filter.isEmpty()) {
|
||||
modeMeta.setDisplayName(getSignColor("trash", "line2") + "" + ChatColor.BOLD + "Modus: Deaktiviert");
|
||||
modeMeta.setLore(Arrays.asList(
|
||||
ChatColor.GRAY + "Kein Filter gesetzt –",
|
||||
ChatColor.GRAY + "Items werden NICHT gelöscht.",
|
||||
getSignColor("trash", "line1") + "Füge Items hinzu um die Mülltruhe",
|
||||
getSignColor("trash", "line1") + "zu aktivieren."));
|
||||
} else {
|
||||
modeMeta.setDisplayName(getSignColor("trash", "line4") + "" + ChatColor.BOLD + "Modus: Filter aktiv");
|
||||
modeMeta.setLore(Arrays.asList(
|
||||
ChatColor.GRAY + "Nur gefilterte Items",
|
||||
ChatColor.GRAY + "werden gelöscht."));
|
||||
}
|
||||
modeInfo.setItemMeta(modeMeta);
|
||||
}
|
||||
gui.setItem(48, modeInfo);
|
||||
|
||||
ItemStack addBtn = new ItemStack(Material.LIME_STAINED_GLASS_PANE);
|
||||
ItemMeta addMeta = addBtn.getItemMeta();
|
||||
if (addMeta != null) {
|
||||
addMeta.setDisplayName(getSignColor("trash", "line1") + "" + ChatColor.BOLD + "Item hinzufügen");
|
||||
addMeta.setLore(Arrays.asList(
|
||||
ChatColor.GRAY + "Item in die Hand nehmen",
|
||||
ChatColor.GRAY + "und diesen Button klicken."));
|
||||
addBtn.setItemMeta(addMeta);
|
||||
}
|
||||
gui.setItem(49, addBtn);
|
||||
gui.setItem(53, buildSkullButton());
|
||||
player.openInventory(gui);
|
||||
}
|
||||
|
||||
public UUID getGuiOwner(UUID playerUUID) {
|
||||
return openGuiOwners.get(playerUUID);
|
||||
}
|
||||
|
||||
private ItemStack buildSkullButton() {
|
||||
ItemStack skull;
|
||||
try {
|
||||
skull = new ItemStack(Material.PLAYER_HEAD);
|
||||
SkullMeta meta = (SkullMeta) skull.getItemMeta();
|
||||
if (meta != null) {
|
||||
PlayerProfile profile = Bukkit.createPlayerProfile(UUID.randomUUID(), "TrashBtn");
|
||||
PlayerTextures textures = profile.getTextures();
|
||||
textures.setSkin(new URL(SKULL_TEXTURE));
|
||||
profile.setTextures(textures);
|
||||
meta.setOwnerProfile(profile);
|
||||
meta.setDisplayName(getChestTitleColor() + "" + ChatColor.BOLD + "Mülltruhe leeren");
|
||||
meta.setLore(Arrays.asList(
|
||||
ChatColor.GRAY + "Klicken um alle Items",
|
||||
ChatColor.GRAY + "sofort zu löschen."));
|
||||
skull.setItemMeta(meta);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
skull = new ItemStack(Material.RED_DYE);
|
||||
ItemMeta meta = skull.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(getChestTitleColor() + "" + ChatColor.BOLD + "Mülltruhe leeren");
|
||||
meta.setLore(Arrays.asList(
|
||||
ChatColor.GRAY + "Klicken um alle Items",
|
||||
ChatColor.GRAY + "sofort zu löschen."));
|
||||
skull.setItemMeta(meta);
|
||||
}
|
||||
}
|
||||
return skull;
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
// GUI-LISTENER
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private class GuiListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
if (!(event.getWhoClicked() instanceof Player player)) return;
|
||||
if (!getGuiTitle().equals(event.getView().getTitle())) return;
|
||||
event.setCancelled(true);
|
||||
|
||||
int clickedSlot = event.getRawSlot();
|
||||
UUID ownerUUID = openGuiOwners.get(player.getUniqueId());
|
||||
if (ownerUUID == null) return;
|
||||
|
||||
if (clickedSlot == 53) {
|
||||
clearTrashChest(ownerUUID);
|
||||
player.sendMessage(getMessage("trash-cleared"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (clickedSlot == 49) {
|
||||
ItemStack inHand = player.getInventory().getItemInMainHand();
|
||||
if (inHand.getType() == Material.AIR) {
|
||||
player.sendMessage(getMessage("no-item-in-hand"));
|
||||
return;
|
||||
}
|
||||
Material mat = inHand.getType();
|
||||
if (addToFilter(ownerUUID, mat)) {
|
||||
player.sendMessage(getMessage("trash-item-added").replace("%item%", formatMaterialName(mat.name())));
|
||||
} else {
|
||||
player.sendMessage(getMessage("trash-item-already"));
|
||||
}
|
||||
openConfigGui(player, ownerUUID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (clickedSlot >= 45) return;
|
||||
|
||||
if (event.isRightClick()) {
|
||||
ItemStack clicked = event.getCurrentItem();
|
||||
if (clicked == null || clicked.getType() == Material.AIR) return;
|
||||
Material mat = clicked.getType();
|
||||
if (removeFromFilter(ownerUUID, mat)) {
|
||||
player.sendMessage(getMessage("trash-item-removed").replace("%item%", formatMaterialName(mat.name())));
|
||||
openConfigGui(player, ownerUUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClose(InventoryCloseEvent event) {
|
||||
if (event.getPlayer() instanceof Player player) {
|
||||
openGuiOwners.remove(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
// HILFSMETHODEN
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private String getMessage(String key) {
|
||||
String msg = plugin.getConfig().getString("messages." + key, "messages." + key + " fehlt");
|
||||
return org.bukkit.ChatColor.translateAlternateColorCodes('&', msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest eine Schildfarbe aus sign-colors.<type>.<line> in der Config.
|
||||
* Gibt übersetzten §-Code zurück (z.B. §6 für &6).
|
||||
*/
|
||||
private String getSignColor(String type, String line) {
|
||||
String raw = plugin.getConfig().getString("sign-colors." + type + "." + line, "&f");
|
||||
return ChatColor.translateAlternateColorCodes('&', raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest nur den Farb-Präfix aus chest-titles.trash (ohne Titeltext).
|
||||
* Gibt übersetzten §-Code zurück.
|
||||
*/
|
||||
private String getChestTitleColor() {
|
||||
boolean isEn = "en".equalsIgnoreCase(plugin.getConfig().getString("language", "de"));
|
||||
String lang = isEn ? "en" : "de";
|
||||
String full = plugin.getConfig().getString("chest-titles.trash." + lang,
|
||||
isEn ? "&4Trash Chest" : "&4Mülltruhe");
|
||||
// Nur die führenden &X / &l Codes extrahieren
|
||||
StringBuilder codes = new StringBuilder();
|
||||
for (int i = 0; i + 1 < full.length(); i++) {
|
||||
if (full.charAt(i) == '&' && "0123456789abcdefklmnor".indexOf(full.charAt(i + 1)) >= 0) {
|
||||
codes.append(full, i, i + 2);
|
||||
i++; // Zeichen überspringen
|
||||
} else {
|
||||
break; // erster Nicht-Code-Zeichenblock → abbrechen
|
||||
}
|
||||
}
|
||||
return ChatColor.translateAlternateColorCodes('&', codes.length() > 0 ? codes.toString() : "&4");
|
||||
}
|
||||
|
||||
private String locKey(Location loc) {
|
||||
return loc.getWorld().getName() + ":" + loc.getBlockX() + ":" + loc.getBlockY() + ":" + loc.getBlockZ();
|
||||
}
|
||||
|
||||
public static String formatMaterialName(String name) {
|
||||
if (name == null || name.isEmpty()) return "";
|
||||
String[] parts = name.split("_");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String part : parts) {
|
||||
if (sb.length() > 0) sb.append(' ');
|
||||
if (!part.isEmpty()) {
|
||||
sb.append(Character.toUpperCase(part.charAt(0)));
|
||||
if (part.length() > 1) sb.append(part.substring(1).toLowerCase());
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ public class UpdateChecker {
|
||||
|
||||
public void getVersion(final Consumer<String> consumer) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> {
|
||||
try (InputStream is = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + this.resourceId + "/~").openStream();
|
||||
try (InputStream is = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + this.resourceId).openStream();
|
||||
Scanner scann = new Scanner(is)) {
|
||||
if (scann.hasNext()) {
|
||||
consumer.accept(scann.next());
|
||||
|
||||
@@ -7,19 +7,24 @@
|
||||
#
|
||||
# ============================================================
|
||||
|
||||
# --- GRUNDLEGUNG ---
|
||||
# Version der Konfigurationsdatei. Nicht ändern, um Fehler zu vermeiden!
|
||||
# ============================================================
|
||||
# ALLGEMEIN
|
||||
# ============================================================
|
||||
|
||||
version: "2.2"
|
||||
|
||||
# Debug-Modus (true = Ausführliche Logs in der Server-Konsole, nur zum Entwickeln nutzen)
|
||||
# Version der Konfigurationsdatei – bitte nicht ändern!
|
||||
version: "2.3"
|
||||
|
||||
# Debug-Modus: true = Ausführliche Logs in der Konsole (nur zum Entwickeln)
|
||||
debug: false
|
||||
|
||||
# ---------------------------------------------------
|
||||
# DATENBANK (MySQL/MariaDB) - Optional
|
||||
# ---------------------------------------------------
|
||||
# Aktiviere MySQL/MariaDB Speicherung (true/false)
|
||||
# Sprache der Benutzeroberfläche
|
||||
# Mögliche Werte: 'de' (Deutsch) oder 'en' (Englisch)
|
||||
# Betrifft: /asc help, /asc info und die Truhen-Fenstertitel
|
||||
language: "de"
|
||||
|
||||
# ============================================================
|
||||
# DATENBANK (MySQL / MariaDB) – Optional
|
||||
# ============================================================
|
||||
|
||||
mysql:
|
||||
enabled: false
|
||||
@@ -29,103 +34,72 @@ mysql:
|
||||
user: "autosortchest"
|
||||
password: "autosortchest"
|
||||
|
||||
# Soll serverübergreifendes Sortieren (mit MySQL) erlaubt sein?
|
||||
server_crosslink: true
|
||||
|
||||
# ---------------------------------------------------
|
||||
# ============================================================
|
||||
# BUNGEE CORD / MULTI-SERVER SETUP
|
||||
# ---------------------------------------------------
|
||||
# ============================================================
|
||||
# Soll serverübergreifendes Sortieren (über MySQL) erlaubt sein?
|
||||
server_crosslink: false
|
||||
|
||||
# Eindeutiger Name dieses Servers im BungeeCord-Netzwerk.
|
||||
# WICHTIG: Jeder Server braucht einen anderen Namen!
|
||||
#
|
||||
# Beispiele:
|
||||
# server_name: "lobby"
|
||||
# server_name: "survival"
|
||||
# server_name: "creative"
|
||||
# Beispiele: lobby | survival | creative
|
||||
#
|
||||
# Leer lassen = Legacy-Modus (welt-basierte Erkennung, kein BungeeCord).
|
||||
# Fuer BungeeCord MUSS mysql.enabled: true gesetzt sein!
|
||||
# Für BungeeCord MUSS mysql.enabled: true gesetzt sein!
|
||||
#
|
||||
# Setup-Schritte:
|
||||
# Setup:
|
||||
# 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.
|
||||
# 4. Alle Server neu starten – Schema wird automatisch migriert.
|
||||
|
||||
server_name: ""
|
||||
|
||||
# ---------------------------------------------------
|
||||
# SPRACHE (Language)
|
||||
# ---------------------------------------------------
|
||||
# Mögliche Werte: 'de' für Deutsch oder 'en' für Englisch
|
||||
# Ändert den Text von /asc help und /asc info
|
||||
language: "de"
|
||||
|
||||
# ---------------------------------------------------
|
||||
# BLACKLIST FÜR WELTEN (Optional)
|
||||
# ---------------------------------------------------
|
||||
# ============================================================
|
||||
# WELTEN-BLACKLIST
|
||||
# ============================================================
|
||||
# Welten, in denen AutoSortChest NICHT funktioniert
|
||||
|
||||
world_blacklist:
|
||||
- "world_nether"
|
||||
- "world_the_end"
|
||||
|
||||
# ---------------------------------------------------
|
||||
# VISUELLE EFFEKTE (PARTIKEL & TÖNE)
|
||||
# ---------------------------------------------------
|
||||
|
||||
# Einstellungen für den Regenbogen-Effekt beim Sortieren
|
||||
effects:
|
||||
# Sollen Effekte angezeigt werden?
|
||||
enabled: false
|
||||
# Soll ein Ton gespielt werden, wenn Items ankommen?
|
||||
sound: false
|
||||
# Der Partikel-Typ.
|
||||
# 'DUST' ist zwingend für den bunten Regenbogen-Effekt im aktuellen Code.
|
||||
type: "DUST"
|
||||
|
||||
# ---------------------------------------------------
|
||||
# ============================================================
|
||||
# SORTIER-INTERVALL (Ticks)
|
||||
# ---------------------------------------------------
|
||||
# Wie oft soll sortiert werden? (1 Tick = 0,05s)
|
||||
#
|
||||
# Wähle hier je nach Server-Leistung:
|
||||
#
|
||||
# 1 = SEHR SCHNELL (Items verschwinden sofort)
|
||||
# WARNUNG: Kann bei vielen Truhen Lagg verursachen!
|
||||
#
|
||||
# 5 = SCHNELL (Sehr flüssig, gute Balance)
|
||||
# Empfohlen für schnelle Server.
|
||||
#
|
||||
# 10 = FLÜSSIG (0,5s Verzögerung)
|
||||
# Spart Ressourcen, fühlt sich noch schnell an.
|
||||
#
|
||||
# 20 = STANDARD (1 Sekunde)
|
||||
# Standard-Wert, minimale Last.
|
||||
#
|
||||
# 30+ = SPARSAM (>1,5 Sekunden)
|
||||
# Für sehr große Server mit schwacher Hardware.
|
||||
# ============================================================
|
||||
# Wie oft soll sortiert werden? (1 Tick = 0,05 Sekunden)
|
||||
#
|
||||
# 1 = Sehr schnell – Items verschwinden sofort
|
||||
# WARNUNG: Kann bei vielen Truhen Lag verursachen!
|
||||
# 5 = Schnell – Sehr flüssig, gute Balance
|
||||
# 10 = Flüssig – 0,5s Verzögerung, spart Ressourcen <- Empfohlen
|
||||
# 20 = Standard – 1 Sekunde, minimale Last
|
||||
# 30+ = Sparsam – Für große Server mit schwacher Hardware
|
||||
|
||||
sort_interval_ticks: 5
|
||||
sort_interval_ticks: 10
|
||||
|
||||
# ---------------------------------------------------
|
||||
# LIMITS FÜR SORTIERKISTEN (Optional)
|
||||
# ---------------------------------------------------
|
||||
# Maximale Anzahl an Sortierkisten pro Spielergruppe
|
||||
# Wie oft werden Rest-Truhen neu einsortiert? (in Ticks)
|
||||
rest_resort_interval_ticks: 400 # 400 Ticks = 20 Sekunden
|
||||
|
||||
# ============================================================
|
||||
# TRUHEN-LIMITS
|
||||
# ============================================================
|
||||
# Maximale Anzahl an Sortiertruhen pro Spielergruppe.
|
||||
# Sollen Limits aktiv sein? (true = ja, false = keine Beschränkung)
|
||||
|
||||
chest_limits:
|
||||
# Sollen Truhen-Limits aktiv sein? (true = ja, false = keine Beschraenkung)
|
||||
enabled: true
|
||||
|
||||
# Jede Gruppe hat eigene Limits fuer input, rest und target.
|
||||
# Spieler benoetigen die Permission: autosortchest.limit.<gruppe>
|
||||
enabled: false
|
||||
|
||||
# Spieler benötigen die Permission: autosortchest.limit.<gruppe>
|
||||
# WICHTIG: Spieler OHNE jegliche autosortchest.limit.*-Permission können bei
|
||||
# aktivierten Limits KEINE Truhen erstellen (Limit = 0).
|
||||
# Vergib autosortchest.limit.default an alle normalen Spieler (z.B. in LuckPerms).
|
||||
default:
|
||||
input: 1 # Eingangstruhen (Input)
|
||||
input: 1 # Eingangstruhen
|
||||
rest: 1 # Rest-Truhen (Fallback)
|
||||
target: 50 # Zieltruhen gesamt
|
||||
target_per_item: 1 # Wie viele Zieltruhen pro Item-Typ erlaubt sind
|
||||
target_per_item: 1 # Zieltruhen pro Item-Typ
|
||||
|
||||
vip:
|
||||
input: 2
|
||||
@@ -133,7 +107,7 @@ chest_limits:
|
||||
target: 100
|
||||
target_per_item: 3
|
||||
|
||||
# Weitere Gruppen:
|
||||
# Weitere Gruppen (auskommentiert):
|
||||
# supporter:
|
||||
# input: 3
|
||||
# rest: 2
|
||||
@@ -145,61 +119,197 @@ chest_limits:
|
||||
# target: 200
|
||||
# target_per_item: 10
|
||||
|
||||
# ---------------------------------------------------
|
||||
# SCHILDFARBEN (Farbcodes wie im Chat)
|
||||
# &c = Rot, &a = Grün, &e = Gelb, &6 = Gold, &f = Weiß, &0 = Schwarz
|
||||
# ---------------------------------------------------
|
||||
# ============================================================
|
||||
# MÜLLTRUHE
|
||||
# ============================================================
|
||||
# auto_clear_interval_seconds:
|
||||
# 0 = Deaktiviert (Truhe wird nur beim Schließen geleert)
|
||||
# 300 = Alle 5 Minuten
|
||||
# 3600 = Stündlich
|
||||
|
||||
trash:
|
||||
auto_clear_interval_seconds: 0
|
||||
|
||||
# ============================================================
|
||||
# VISUELLE EFFEKTE (Partikel & Töne)
|
||||
# ============================================================
|
||||
|
||||
effects:
|
||||
# Sollen Partikel-Effekte angezeigt werden?
|
||||
enabled: false
|
||||
# Soll ein Ton gespielt werden, wenn Items ankommen?
|
||||
sound: false
|
||||
# Partikel-Typ ('DUST' = bunter Regenbogen-Effekt)
|
||||
type: "DUST"
|
||||
|
||||
# ============================================================
|
||||
# SCHILD-STIL
|
||||
# ============================================================
|
||||
|
||||
sign-style:
|
||||
# Saubere Zieltruhen-Schilder (ohne Item-Namen auf dem Schild)
|
||||
# true = Clean-Modus aktiviert
|
||||
clean-target: false
|
||||
|
||||
# ============================================================
|
||||
# SCHILDFARBEN
|
||||
# ============================================================
|
||||
# Farbcodes: &0-&9, &a-&f | &l = Fett, &o = Kursiv, &r = Reset
|
||||
#
|
||||
# &0 Schwarz &1 Dunkelblau &2 Dunkelgrün &3 Cyan
|
||||
# &4 Dunkelrot &5 Lila &6 Gold &7 Grau
|
||||
# &8 Dunkelgrau &9 Blau &a Grün &b Aqua
|
||||
# &c Rot &d Pink &e Gelb &f Weiß
|
||||
|
||||
sign-colors:
|
||||
# Farben für die Eingangstruhe ([asc] / input)
|
||||
|
||||
# Eingangstruhe ([asc] / input)
|
||||
input:
|
||||
line1: "&6" # Zeile 1: [asc]
|
||||
line2: "&0" # Zeile 2: input
|
||||
line4: "&1" # Zeile 4: Spielername
|
||||
line1: "&6" # Zeile 1: [asc]
|
||||
line2: "&0" # Zeile 2: input
|
||||
line4: "&1" # Zeile 4: Spielername
|
||||
|
||||
# Farben für die Zieltruhe ([asc] / ziel)
|
||||
# Zieltruhe ([asc] / ziel)
|
||||
target:
|
||||
line1: "&6" # Zeile 1: [asc]
|
||||
line2: "&0" # Zeile 2: ziel
|
||||
line3: "&f" # Zeile 3: Item-Name
|
||||
line4: "&1" # Zeile 4: Spielername
|
||||
line1: "&6" # Zeile 1: [asc]
|
||||
line2: "&0" # Zeile 2: ziel
|
||||
line3: "&f" # Zeile 3: Item-Name
|
||||
line4: "&1" # Zeile 4: Spielername
|
||||
|
||||
# Farben für volle Truhen (Automatische Erkennung)
|
||||
# Volle Zieltruhe (automatische Erkennung)
|
||||
full:
|
||||
line1: "&c" # Zeile 1: [asc]
|
||||
line2: "&4" # Zeile 2: ziel / rest (Rot)
|
||||
line3: "&e" # Zeile 3: Item-Name (Gelb)
|
||||
line4: "&1" # Zeile 4: Spielername
|
||||
line1: "&c" # Zeile 1: [asc]
|
||||
line2: "&4" # Zeile 2: ziel / rest
|
||||
line3: "&e" # Zeile 3: Item-Name
|
||||
line4: "&1" # Zeile 4: Spielername
|
||||
|
||||
# Farben für die Rest-Truhe ([asc] / rest)
|
||||
# Rest-Truhe ([asc] / rest)
|
||||
rest:
|
||||
line1: "&6" # Zeile 1: [asc]
|
||||
line2: "&0" # Zeile 2: rest
|
||||
line3: "&f" # Zeile 3: (Leer)
|
||||
line4: "&1" # Zeile 4: Spielername
|
||||
line1: "&6" # Zeile 1: [asc]
|
||||
line2: "&0" # Zeile 2: rest
|
||||
line3: "&f" # Zeile 3: (leer)
|
||||
line4: "&1" # Zeile 4: Spielername
|
||||
|
||||
# ---------------------------------------------------
|
||||
# SYSTEM NACHRICHTEN (Spieler-Feedback)
|
||||
# Mülltruhe ([asc] / trash)
|
||||
trash:
|
||||
line1: "&6" # Zeile 1: [asc]
|
||||
line2: "&0" # Zeile 2: trash
|
||||
line4: "&1" # Zeile 4: Spielername
|
||||
|
||||
# ============================================================
|
||||
# CLEAN-MODUS SCHILDFARBEN (sign-style.clean-target: true)
|
||||
# ============================================================
|
||||
# Eigene Farben für den sauberen Schild-Stil.
|
||||
# Farbcodes: &0-&9, &a-&f | &l = Fett, &o = Kursiv, &r = Reset
|
||||
#
|
||||
# Layout im Clean-Modus (Zeile 4 = versteckter Typ-Marker, nicht konfigurierbar):
|
||||
#
|
||||
# input → Z1: Spielername | Z2: "Eingang/Input" | Z3: Öffentlich/Privat
|
||||
# target → Z1: Item-Name | Z2: Spielername | Z3: Öffentlich/Privat
|
||||
# full → Z1: Item-Name | Z2: Spielername | Z3: Öffentlich/Privat (volle Truhe)
|
||||
# rest → Z1: Spielername | Z2: "Rest" | Z3: Öffentlich/Privat
|
||||
# trash → Z1: Spielername | Z2: "Müll/Trash" | Z3: (leer)
|
||||
|
||||
sign-colors-clean:
|
||||
|
||||
# Eingangstruhe (Clean)
|
||||
input:
|
||||
line1: "&1" # Zeile 1: Spielername
|
||||
line2: "&0" # Zeile 2: "Eingang" / "Input"
|
||||
line3: "&a" # Zeile 3: Öffentlich / Privat
|
||||
|
||||
# Zieltruhe (Clean)
|
||||
target:
|
||||
line1: "&f" # Zeile 1: Item-Name
|
||||
line2: "&1" # Zeile 2: Spielername
|
||||
line3: "&a" # Zeile 3: Öffentlich / Privat
|
||||
|
||||
# Volle Zieltruhe (Clean)
|
||||
full:
|
||||
line1: "&c" # Zeile 1: Item-Name (volle Truhe) → Rot
|
||||
line2: "&1" # Zeile 2: Spielername
|
||||
line3: "&a" # Zeile 3: Öffentlich / Privat
|
||||
|
||||
# Rest-Truhe (Clean)
|
||||
rest:
|
||||
line1: "&1" # Zeile 1: Spielername
|
||||
line2: "&0" # Zeile 2: "Rest"
|
||||
line3: "&a" # Zeile 3: Öffentlich / Privat
|
||||
|
||||
# Mülltruhe (Clean)
|
||||
trash:
|
||||
line1: "&1" # Zeile 1: Spielername
|
||||
line2: "&0" # Zeile 2: "Müll" / "Trash"
|
||||
|
||||
# ============================================================
|
||||
# TRUHEN-FENSTERTITEL
|
||||
# ============================================================
|
||||
# Farbe & Text des Titels, wenn ein Spieler eine ASC-Truhe öffnet.
|
||||
# Farbcodes wie oben.
|
||||
#
|
||||
# Platzhalter für Zieltruhen:
|
||||
# %item% -> wird durch den Item-Namen ersetzt (z.B. "Iron Ore")
|
||||
# Beispiel: "&6%item%" zeigt "Iron Ore" in Gold
|
||||
|
||||
chest-titles:
|
||||
input:
|
||||
de: "&6Eingangstruhe"
|
||||
en: "&6Input Chest"
|
||||
target:
|
||||
de: "&6%item%"
|
||||
en: "&6%item%"
|
||||
rest:
|
||||
de: "&6Rest-Truhe"
|
||||
en: "&6Rest Chest"
|
||||
trash:
|
||||
de: "&4Mülltruhe"
|
||||
en: "&4Trash Chest"
|
||||
|
||||
# ============================================================
|
||||
# SYSTEM-NACHRICHTEN (Spieler-Feedback)
|
||||
# ============================================================
|
||||
# Platzhalter: %player%, %item%, %x%, %y%, %z%, %mode%
|
||||
# ---------------------------------------------------
|
||||
|
||||
messages:
|
||||
# --- FEHLERMELDUNGEN ---
|
||||
no-chest-near-sign: "&cKeine Truhe in der Nähe des Schildes!"
|
||||
no-item-in-hand: "&cDu musst ein Item in der Hand halten!"
|
||||
not-your-chest: "&cDiese Truhe gehört dir nicht!"
|
||||
|
||||
# --- Fehlermeldungen ---
|
||||
no-chest-near-sign: "&cKeine Truhe in der Nähe des Schildes!"
|
||||
no-item-in-hand: "&cDu musst ein Item in der Hand halten!"
|
||||
not-your-chest: "&cDiese Truhe gehört dir nicht!"
|
||||
target-chest-missing: "&cZieltruhe für %item% fehlt!"
|
||||
sign-break-denied: "&cDu musst Shift gedrückt halten, um dieses Schild oder die Truhe abzubauen!"
|
||||
no-permission: "&cDu hast keine Berechtigung für diesen Befehl!"
|
||||
sign-break-denied: "&cDu musst Shift gedrückt halten, um dieses Schild oder die Truhe abzubauen!"
|
||||
no-permission: "&cDu hast keine Berechtigung für diesen Befehl!"
|
||||
world-blacklisted: "&cIn dieser Welt kannst du keine AutoSortChest erstellen!"
|
||||
|
||||
# --- ERFOLGSMELDUNGEN ---
|
||||
input-chest-set: "&aEingangstruhe erfolgreich gesetzt!"
|
||||
target-chest-set: "&aZieltruhe erfolgreich für %item% eingerichtet!"
|
||||
rest-chest-set: "&aRest-Truhe (Fallback) erfolgreich gesetzt!"
|
||||
reload-success: "&aKonfiguration erfolgreich neu geladen!"
|
||||
# --- Limit-Fehlermeldungen ---
|
||||
# Platzhalter: %max% = erlaubtes Limit, %item% = Item-Name
|
||||
limit-input-reached: "&cDu hast das Limit deiner Eingangstruhen erreicht! &7(%max%)"
|
||||
limit-rest-reached: "&cDu hast das Limit deiner Rest-Truhen erreicht! &7(%max%)"
|
||||
limit-target-reached: "&cDu hast das Limit deiner Zieltruhen erreicht! &7(%max%)"
|
||||
limit-target-per-item: "&cDu hast das Limit für %item%-Truhen erreicht! &7(%max%)"
|
||||
limit-no-permission: "&cDu hast keine Berechtigung um Truhen zu erstellen!"
|
||||
|
||||
# --- HINWEIS MELDUNGEN ---
|
||||
# --- Erfolgsmeldungen ---
|
||||
input-chest-set: "&aEingangstruhe erfolgreich gesetzt!"
|
||||
target-chest-set: "&aZieltruhe erfolgreich für %item% eingerichtet!"
|
||||
rest-chest-set: "&aRest-Truhe (Fallback) erfolgreich gesetzt!"
|
||||
trash-chest-set: "&aMülltruhe erfolgreich eingerichtet!"
|
||||
trash-chest-hint: "&7Rechtsklicke das Schild um Items zu konfigurieren."
|
||||
reload-success: "&aKonfiguration erfolgreich neu geladen!"
|
||||
|
||||
# --- Mülltruhe GUI ---
|
||||
trash-cleared: "&a✔ Mülltruhe wurde geleert!"
|
||||
trash-item-added: "&a✔ &e%item% &azur Müll-Liste hinzugefügt."
|
||||
trash-item-already: "&eDiseses Item ist bereits in der Müll-Liste."
|
||||
trash-item-removed: "&c✖ &e%item% &caus der Müll-Liste entfernt."
|
||||
|
||||
# --- Mülltruhe Info (erscheint im Chat beim Öffnen der Truhe) ---
|
||||
# Platzhalter: %items% = kommagetrennte Item-Liste
|
||||
trash-info-empty: "&4Mülltruhe &8(Deaktiviert) &7– Rechtsklick Schild zum Konfigurieren"
|
||||
trash-info-filter: "&4Müll: &f%items% &8| Schild-Rechtsklick: Konfigurieren"
|
||||
|
||||
# --- Hinweise ---
|
||||
target-chest-full: "&cZieltruhe für %item% ist voll! Koordinaten: (%x%, %y%, %z%)"
|
||||
mode-changed: "&aModus gewechselt: &e%mode%"
|
||||
mode-public: "&aÖffentlich"
|
||||
mode-private: "&cPrivat"
|
||||
mode-changed: "&aModus gewechselt: &e%mode%"
|
||||
mode-public: "&aÖffentlich"
|
||||
mode-private: "&cPrivat"
|
||||
@@ -1,5 +1,5 @@
|
||||
name: AutoSortChest
|
||||
version: 2.3
|
||||
version: 2.5
|
||||
main: com.viper.autosortchest.Main
|
||||
api-version: 1.21
|
||||
authors: [M_Viper]
|
||||
@@ -10,6 +10,9 @@ commands:
|
||||
usage: /<command> [help|info|reload|import|export]
|
||||
aliases: [autosortchest]
|
||||
permissions:
|
||||
autosortchest.use:
|
||||
description: Erlaubt das Erstellen von AutoSortChest-Schildern (Eingang, Ziel, Rest, Muelltruhe)
|
||||
default: true
|
||||
autosortchest.reload:
|
||||
description: Erlaubt das Neuladen der Konfiguration mit /asc reload
|
||||
default: op
|
||||
@@ -20,14 +23,20 @@ permissions:
|
||||
description: Erlaubt den Export von MySQL nach players.yml mit /asc export
|
||||
default: op
|
||||
autosortchest.bypass:
|
||||
description: Erlaubt das Abbauen von ASC-Schildern ohne Shift-Taste und unabhängig vom Besitzer
|
||||
description: Erlaubt das Abbauen von ASC-Schildern ohne Shift-Taste und unabhaengig vom Besitzer
|
||||
default: op
|
||||
autosortchest.limit.bypass:
|
||||
description: Umgeht alle Truhen-Limits (input, rest, target) – OPs haben dies automatisch
|
||||
default: op
|
||||
autosortchest.admin:
|
||||
description: Erlaubt OPs/Admins Zugriff auf fremde AutoSortChest-Truhen (Öffnen, Entnehmen, Abbauen)
|
||||
description: Erlaubt OPs/Admins Zugriff auf fremde AutoSortChest-Truhen
|
||||
default: op
|
||||
autosortchest.limit.<gruppe>:
|
||||
description: >
|
||||
Limits fuer eine benutzerdefinierte Gruppe aus der config.yml.
|
||||
Ersetze <gruppe> durch den Gruppennamen (z.B. autosortchest.limit.vip).
|
||||
Die Gruppen und ihre Limits werden ausschliesslich in der config.yml definiert.
|
||||
WICHTIG: Spieler ohne jegliche autosortchest.limit.*-Permission koennen bei
|
||||
aktivierten Limits (chest_limits.enabled: true) KEINE Truhen erstellen.
|
||||
Vergib autosortchest.limit.default fuer den Standard-Rang.
|
||||
default: false
|
||||
Reference in New Issue
Block a user