Update from Git Manager GUI

This commit is contained in:
2026-03-04 11:41:13 +01:00
parent 673ed06998
commit 4d72b64866
6 changed files with 3162 additions and 719 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}
}

View 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.&lt;type&gt;.&lt;line&gt; 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();
}
}

View File

@@ -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());

View File

@@ -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,60 +119,196 @@ 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
# 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
# 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)
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)
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 ---
# --- 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!"
world-blacklisted: "&cIn dieser Welt kannst du keine AutoSortChest erstellen!"
# --- ERFOLGSMELDUNGEN ---
# --- 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!"
# --- 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!"
# --- HINWEIS MELDUNGEN ---
# --- 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"

View File

@@ -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