package com.viper.autosortchest; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.Chest; import org.bukkit.block.Sign; import org.bukkit.block.data.type.WallSign; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.ChatColor; import org.bukkit.Material; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.*; public class Main extends JavaPlugin implements Listener, CommandExecutor { private File playerDataFile; private FileConfiguration playerData; private FileConfiguration config; private final Map> fullChestMessageTracker = new HashMap<>(); private static final long MESSAGE_COOLDOWN = 5 * 60 * 1000; // 5 Minuten in Millisekunden private static final String CONFIG_VERSION = "1.3"; @Override public void onEnable() { // Lade Standard-Konfiguration saveDefaultConfig(); config = getConfig(); // Lade players.yml playerDataFile = new File(getDataFolder(), "players.yml"); if (!playerDataFile.exists()) { saveResource("players.yml", false); } loadPlayerData(); // Registriere Events getServer().getPluginManager().registerEvents(this, this); // Registriere Befehle this.getCommand("asc").setExecutor(this); // Starte periodischen Task new BukkitRunnable() { @Override public void run() { checkInputChests(); } }.runTaskTimer(this, 20L, 20L); // Alle 1 Sekunde (20 Ticks) // Starte Task zum Bereinigen des Message-Trackers new BukkitRunnable() { @Override public void run() { cleanMessageTracker(); } }.runTaskTimer(this, 20L * 60, 20L * 60); // Alle Minute // Aktualisiere Konfiguration und Schilder updateConfig(); updateExistingSigns(); getLogger().info("AutoSortChest Plugin aktiviert! Version: " + getDescription().getVersion()); } @Override public void onDisable() { savePlayerData(); getLogger().info("AutoSortChest Plugin deaktiviert!"); } private void savePlayerData() { if (playerData == null || playerDataFile == null) { getLogger().warning("Kann players.yml nicht speichern: playerData oder playerDataFile ist null"); return; } try { playerData.save(playerDataFile); getLogger().info("players.yml erfolgreich gespeichert"); } catch (IOException e) { getLogger().warning("Fehler beim Speichern der players.yml: " + e.getMessage()); } } private void loadPlayerData() { if (playerDataFile == null) { playerDataFile = new File(getDataFolder(), "players.yml"); } try { if (!playerDataFile.exists()) { saveResource("players.yml", false); getLogger().info("Neue players.yml erstellt"); } playerData = YamlConfiguration.loadConfiguration(playerDataFile); if (playerData.getConfigurationSection("players") == null) { getLogger().warning("Abschnitt 'players' in players.yml fehlt. Initialisiere leer."); playerData.createSection("players"); savePlayerData(); } else { int playerCount = playerData.getConfigurationSection("players").getKeys(false).size(); getLogger().info("players.yml geladen mit " + playerCount + " Spieler-Einträgen"); } } catch (Exception e) { getLogger().warning("Fehler beim Laden von players.yml: " + e.getMessage()); playerData = new YamlConfiguration(); playerData.createSection("players"); savePlayerData(); } } private void updateConfig() { File configFile = new File(getDataFolder(), "config.yml"); InputStream defaultConfigStream = getResource("config.yml"); FileConfiguration defaultConfig; // Lade Standardkonfiguration if (defaultConfigStream == null) { getLogger().warning("Standard-config.yml nicht gefunden in Plugin-Ressourcen! Verwende Fallback-Werte."); defaultConfig = new YamlConfiguration(); } else { try { defaultConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(defaultConfigStream)); getLogger().info("Standard-config.yml erfolgreich geladen."); } catch (Exception e) { getLogger().warning("Fehler beim Laden von Standard-config.yml: " + e.getMessage()); defaultConfig = new YamlConfiguration(); } } // Prüfe Konfigurationsversion String currentVersion = config.getString("version", "1.0"); boolean versionUpdated = false; if (!currentVersion.equals(CONFIG_VERSION)) { getLogger().info("Aktualisiere config.yml von Version " + currentVersion + " auf " + CONFIG_VERSION); config.set("version", CONFIG_VERSION); versionUpdated = true; } // Setze Standardwerte für fehlende Schlüssel if (!config.contains("debug")) { config.set("debug", defaultConfig.getBoolean("debug", false)); getLogger().info("Setze Standardwert: debug = " + config.getBoolean("debug")); } // Prüfe und setze sign-colors.input if (!config.contains("sign-colors.input")) { config.createSection("sign-colors.input"); config.set("sign-colors.input.line1", defaultConfig.getString("sign-colors.input.line1", "&6")); config.set("sign-colors.input.line2", defaultConfig.getString("sign-colors.input.line2", "&0")); config.set("sign-colors.input.line4", defaultConfig.getString("sign-colors.input.line4", "&f")); getLogger().info("Setze Standardwerte für sign-colors.input"); } else { if (!config.contains("sign-colors.input.line1")) { config.set("sign-colors.input.line1", defaultConfig.getString("sign-colors.input.line1", "&6")); getLogger().info("Setze Standardwert: sign-colors.input.line1 = " + config.getString("sign-colors.input.line1")); } if (!config.contains("sign-colors.input.line2")) { config.set("sign-colors.input.line2", defaultConfig.getString("sign-colors.input.line2", "&0")); getLogger().info("Setze Standardwert: sign-colors.input.line2 = " + config.getString("sign-colors.input.line2")); } if (!config.contains("sign-colors.input.line4")) { config.set("sign-colors.input.line4", defaultConfig.getString("sign-colors.input.line4", "&f")); getLogger().info("Setze Standardwert: sign-colors.input.line4 = " + config.getString("sign-colors.input.line4")); } } // Prüfe und setze sign-colors.target if (!config.contains("sign-colors.target")) { config.createSection("sign-colors.target"); config.set("sign-colors.target.line1", defaultConfig.getString("sign-colors.target.line1", "&6")); config.set("sign-colors.target.line2", defaultConfig.getString("sign-colors.target.line2", "&0")); config.set("sign-colors.target.line3", defaultConfig.getString("sign-colors.target.line3", "&f")); config.set("sign-colors.target.line4", defaultConfig.getString("sign-colors.target.line4", "&f")); getLogger().info("Setze Standardwerte für sign-colors.target"); } else { if (!config.contains("sign-colors.target.line1")) { config.set("sign-colors.target.line1", defaultConfig.getString("sign-colors.target.line1", "&6")); getLogger().info("Setze Standardwert: sign-colors.target.line1 = " + config.getString("sign-colors.target.line1")); } if (!config.contains("sign-colors.target.line2")) { config.set("sign-colors.target.line2", defaultConfig.getString("sign-colors.target.line2", "&0")); getLogger().info("Setze Standardwert: sign-colors.target.line2 = " + config.getString("sign-colors.target.line2")); } if (!config.contains("sign-colors.target.line3")) { config.set("sign-colors.target.line3", defaultConfig.getString("sign-colors.target.line3", "&f")); getLogger().info("Setze Standardwert: sign-colors.target.line3 = " + config.getString("sign-colors.target.line3")); } if (!config.contains("sign-colors.target.line4")) { config.set("sign-colors.target.line4", defaultConfig.getString("sign-colors.target.line4", "&f")); getLogger().info("Setze Standardwert: sign-colors.target.line4 = " + config.getString("sign-colors.target.line4")); } } // Prüfe und setze sign-colors.full if (!config.contains("sign-colors.full")) { config.createSection("sign-colors.full"); config.set("sign-colors.full.line1", defaultConfig.getString("sign-colors.full.line1", "&c")); config.set("sign-colors.full.line2", defaultConfig.getString("sign-colors.full.line2", "&4")); config.set("sign-colors.full.line3", defaultConfig.getString("sign-colors.full.line3", "&e")); config.set("sign-colors.full.line4", defaultConfig.getString("sign-colors.full.line4", "&e")); getLogger().info("Setze Standardwerte für sign-colors.full"); } else { if (!config.contains("sign-colors.full.line1")) { config.set("sign-colors.full.line1", defaultConfig.getString("sign-colors.full.line1", "&c")); getLogger().info("Setze Standardwert: sign-colors.full.line1 = " + config.getString("sign-colors.full.line1")); } if (!config.contains("sign-colors.full.line2")) { config.set("sign-colors.full.line2", defaultConfig.getString("sign-colors.full.line2", "&4")); getLogger().info("Setze Standardwert: sign-colors.full.line2 = " + config.getString("sign-colors.full.line2")); } if (!config.contains("sign-colors.full.line3")) { config.set("sign-colors.full.line3", defaultConfig.getString("sign-colors.full.line3", "&e")); getLogger().info("Setze Standardwert: sign-colors.full.line3 = " + config.getString("sign-colors.full.line3")); } if (!config.contains("sign-colors.full.line4")) { config.set("sign-colors.full.line4", defaultConfig.getString("sign-colors.full.line4", "&e")); getLogger().info("Setze Standardwert: sign-colors.full.line4 = " + config.getString("sign-colors.full.line4")); } } // Prüfe und setze messages if (!config.contains("messages.no-chest-near-sign")) { config.set("messages.no-chest-near-sign", defaultConfig.getString("messages.no-chest-near-sign", "&cKeine Truhe in der Nähe des Schildes!")); getLogger().info("Setze Standardwert: messages.no-chest-near-sign"); } if (!config.contains("messages.no-item-in-hand")) { config.set("messages.no-item-in-hand", defaultConfig.getString("messages.no-item-in-hand", "&cDu musst ein Item in der Hand halten!")); getLogger().info("Setze Standardwert: messages.no-item-in-hand"); } if (!config.contains("messages.not-your-chest")) { config.set("messages.not-your-chest", defaultConfig.getString("messages.not-your-chest", "&cDiese Truhe gehört dir nicht!")); getLogger().info("Setze Standardwert: messages.not-your-chest"); } if (!config.contains("messages.input-chest-set")) { config.set("messages.input-chest-set", defaultConfig.getString("messages.input-chest-set", "&aEingangstruhe erfolgreich gesetzt!")); getLogger().info("Setze Standardwert: messages.input-chest-set"); } if (!config.contains("messages.target-chest-set")) { config.set("messages.target-chest-set", defaultConfig.getString("messages.target-chest-set", "&aZieltruhe für %item% erfolgreich gesetzt!")); getLogger().info("Setze Standardwert: messages.target-chest-set"); } if (!config.contains("messages.target-chest-missing")) { config.set("messages.target-chest-missing", defaultConfig.getString("messages.target-chest-missing", "&cZieltruhe für %item% fehlt!")); getLogger().info("Setze Standardwert: messages.target-chest-missing"); } String targetChestFull = config.getString("messages.target-chest-full", ""); String defaultTargetChestFull = defaultConfig.getString("messages.target-chest-full", "&cZieltruhe für %item% ist voll! Koordinaten: (%x%, %y%, %z%)"); if (!config.contains("messages.target-chest-full") || !targetChestFull.equals(defaultTargetChestFull)) { config.set("messages.target-chest-full", defaultTargetChestFull); getLogger().info("Setze oder aktualisiere Standardwert: messages.target-chest-full = " + defaultTargetChestFull); } // --- NEUE NACHRICHTEN FÜR ÖFFENTLICHEN MODUS --- if (!config.contains("messages.mode-changed")) { config.set("messages.mode-changed", defaultConfig.getString("messages.mode-changed", "&aModus gewechselt: &e%mode%")); getLogger().info("Setze Standardwert: messages.mode-changed"); } if (!config.contains("messages.mode-public")) { config.set("messages.mode-public", defaultConfig.getString("messages.mode-public", "&aÖffentlich")); getLogger().info("Setze Standardwert: messages.mode-public"); } if (!config.contains("messages.mode-private")) { config.set("messages.mode-private", defaultConfig.getString("messages.mode-private", "&cPrivat")); getLogger().info("Setze Standardwert: messages.mode-private"); } // ---------------------------------------------- if (!config.contains("messages.help")) { String helpMessage = "&6&l=== AutoSortChest Hilfe ===\n" + "&eEingangstruhe erstellen:\n" + "&f1. Platziere ein Schild an einer Truhe.\n" + "&f2. Schreibe:\n" + " &7[asc]\n" + " &7input\n" + "&fDein Name wird automatisch in Zeile 4 eingetragen.\n" + "&eZieltruhe erstellen:\n" + "&f1. Platziere ein Schild an einer Truhe.\n" + "&f2. Schreibe:\n" + " &7[asc]\n" + " &7ziel\n" + "&f3. Rechtsklicke mit einem Item in der Hand.\n" + "&eBefehle:\n" + "&f- &b/asc help &f- Zeigt diese Hilfe.\n" + "&f- &b/asc info &f- Zeigt Plugin-Informationen.\n" + "&f- &b/asc reload &f- Lädt die Konfiguration neu (OP).\n" + "&6&l===================="; config.set("messages.help", defaultConfig.getString("messages.help", helpMessage)); getLogger().info("Setze Standardwert: messages.help"); } if (!config.contains("messages.info")) { String infoMessage = "&6&l=== AutoSortChest Info ===\n" + "&ePlugin: &fAutoSortChest\n" + "&eVersion: &f%version%\n" + "&eKonfigurationsversion: &f%config_version%\n" + "&eErsteller: &f%author%\n" + "&eBeschreibung: &fAutomatisches Sortieren von Items in Truhen.\n" + "&6&l===================="; config.set("messages.info", defaultConfig.getString("messages.info", infoMessage)); getLogger().info("Setze Standardwert: messages.info"); } if (!config.contains("messages.no-permission")) { config.set("messages.no-permission", defaultConfig.getString("messages.no-permission", "&cDu hast keine Berechtigung für diesen Befehl!")); getLogger().info("Setze Standardwert: messages.no-permission"); } if (!config.contains("messages.reload-success")) { config.set("messages.reload-success", defaultConfig.getString("messages.reload-success", "&aKonfiguration erfolgreich neu geladen!")); getLogger().info("Setze Standardwert: messages.reload-success"); } if (!config.contains("messages.sign-break-denied")) { config.set("messages.sign-break-denied", defaultConfig.getString("messages.sign-break-denied", "&cDu musst Shift gedrückt halten, um dieses Schild oder die Truhe abzubauen!")); getLogger().info("Setze Standardwert: messages.sign-break-denied"); } // Speichere die aktualisierte Konfiguration try { config.save(configFile); if (configFile.exists() && configFile.length() > 0) { getLogger().info("config.yml erfolgreich aktualisiert und gespeichert auf Version " + CONFIG_VERSION); } else { getLogger().warning("config.yml wurde nicht korrekt gespeichert (Datei existiert nicht oder ist leer)"); } } catch (IOException e) { getLogger().warning("Fehler beim Speichern der config.yml: " + e.getMessage()); } } private void updateExistingSigns() { if (playerData == null) { getLogger().warning("playerData ist null. Kann bestehende Schilder nicht aktualisieren."); return; } if (playerData.getConfigurationSection("players") == null) { getLogger().warning("Abschnitt 'players' in players.yml fehlt. Keine Schilder zum Aktualisieren."); return; } for (String uuidString : playerData.getConfigurationSection("players").getKeys(false)) { UUID playerUUID; try { playerUUID = UUID.fromString(uuidString); } catch (IllegalArgumentException e) { getLogger().warning("Ungültige UUID in players.yml: " + uuidString); continue; } String path = "players." + uuidString; // Eingangstruhe String inputPath = path + ".input-chest"; if (playerData.contains(inputPath)) { String worldName = playerData.getString(inputPath + ".world"); World world = getServer().getWorld(worldName); if (world == null) { getLogger().warning("Welt " + worldName + " für Eingangstruhe von Spieler UUID " + uuidString + " nicht gefunden"); continue; } int x = playerData.getInt(inputPath + ".x"); int y = playerData.getInt(inputPath + ".y"); int z = playerData.getInt(inputPath + ".z"); Location chestLocation = new Location(world, x, y, z); Block chestBlock = chestLocation.getBlock(); if (!(chestBlock.getState() instanceof Chest)) { getLogger().warning("Eingangstruhe bei " + chestLocation + " ist keine Truhe"); continue; } for (Block face : new Block[] { chestBlock.getRelative(1, 0, 0), chestBlock.getRelative(-1, 0, 0), chestBlock.getRelative(0, 0, 1), chestBlock.getRelative(0, 0, -1) }) { if (face.getState() instanceof Sign sign && isSignAttachedToChest(face, chestBlock)) { String[] lines = sign.getLines(); String line0 = ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")); String line1 = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); String line3 = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : "")); if (line0.equalsIgnoreCase("[asc]") && line1.equalsIgnoreCase("input")) { sign.setLine(0, getSignColor("input", "line1") + "[asc]"); sign.setLine(1, getSignColor("input", "line2") + "input"); sign.setLine(3, getSignColor("input", "line4") + line3); sign.update(); if (isDebug()) { getLogger().fine("Eingangsschild bei " + face.getLocation() + " für Spieler UUID " + uuidString + " aktualisiert"); } } } } } // Zieltruhen String targetPath = path + ".target-chests"; if (playerData.contains(targetPath)) { for (String itemType : playerData.getConfigurationSection(targetPath).getKeys(false)) { String targetChestPath = targetPath + "." + itemType; String worldName = playerData.getString(targetChestPath + ".world"); World world = getServer().getWorld(worldName); if (world == null) { getLogger().warning("Welt " + worldName + " für Zieltruhe von Item " + itemType + " nicht gefunden"); continue; } int x = playerData.getInt(targetChestPath + ".x"); int y = playerData.getInt(targetChestPath + ".y"); int z = playerData.getInt(targetChestPath + ".z"); Location chestLocation = new Location(world, x, y, z); Block chestBlock = chestLocation.getBlock(); if (!(chestBlock.getState() instanceof Chest)) { getLogger().warning("Zieltruhe für Item " + itemType + " bei " + chestLocation + " ist keine Truhe"); continue; } Chest chest = (Chest) chestBlock.getState(); Inventory inventory = chest.getInventory(); boolean isFull = isInventoryFull(inventory); for (Block face : new Block[] { chestBlock.getRelative(1, 0, 0), chestBlock.getRelative(-1, 0, 0), chestBlock.getRelative(0, 0, 1), chestBlock.getRelative(0, 0, -1) }) { if (face.getState() instanceof Sign sign && isSignAttachedToChest(face, chestBlock)) { String[] lines = sign.getLines(); String line0 = ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")); String line1 = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); String line2 = ChatColor.stripColor((String) (lines[2] != null ? lines[2] : "")); String line3 = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : "")); if (line0.equalsIgnoreCase("[asc]") && line1.equalsIgnoreCase("ziel")) { String colorType = isFull ? "full" : "target"; sign.setLine(0, getSignColor(colorType, "line1") + "[asc]"); sign.setLine(1, getSignColor(colorType, "line2") + "ziel"); sign.setLine(2, getSignColor(colorType, "line3") + line2); sign.setLine(3, getSignColor(colorType, "line4") + line3); sign.update(); if (isDebug()) { getLogger().fine("Zieltruhe-Schild für Item " + itemType + " bei " + face.getLocation() + " aktualisiert (voll: " + isFull + ")"); } } } } } } } } private boolean isInventoryFull(Inventory inventory) { for (ItemStack item : inventory.getContents()) { if (item == null || item.getAmount() < item.getMaxStackSize()) { return false; } } return true; } private boolean isChestPublic(Sign sign) { String line3 = sign.getLine(3); // MIT Farbcodes! String line3Clean = ChatColor.stripColor(line3); return line3Clean.toLowerCase().contains("[public]"); } private boolean isDebug() { return config != null && config.getBoolean("debug", false); } private String getMessage(String key) { if (config == null) { return ChatColor.RED + "Fehlende Konfiguration: " + key; } String message = config.getString("messages." + key, "Fehlende Nachricht: " + key); return ChatColor.translateAlternateColorCodes('&', message); } private String getSignColor(String type, String line) { if (config == null) { return "&f"; } String color = config.getString("sign-colors." + type + "." + line, "&f"); return ChatColor.translateAlternateColorCodes('&', color); } private boolean isSignAttachedToChest(Block signBlock, Block chestBlock) { if (!(signBlock.getBlockData() instanceof WallSign)) { if (isDebug()) getLogger().fine("Schild bei " + signBlock.getLocation() + " ist kein Wandschild"); return false; } WallSign wallSign = (WallSign) signBlock.getBlockData(); Block attachedBlock = signBlock.getRelative(wallSign.getFacing().getOppositeFace()); boolean attached = attachedBlock.equals(chestBlock); if (!attached && isDebug()) { getLogger().fine("Schild bei " + signBlock.getLocation() + " ist nicht an Truhe bei " + chestBlock.getLocation() + " angebracht"); } return attached; } private void setInputChestLocation(UUID playerUUID, Location location) { String path = "players." + playerUUID + ".input-chest"; playerData.set(path + ".world", location.getWorld().getName()); playerData.set(path + ".x", location.getBlockX()); playerData.set(path + ".y", location.getBlockY()); playerData.set(path + ".z", location.getBlockZ()); savePlayerData(); } private void setTargetChestLocation(UUID playerUUID, Location location, Material itemType) { String path = "players." + playerUUID + ".target-chests." + itemType.name(); playerData.set(path + ".world", location.getWorld().getName()); playerData.set(path + ".x", location.getBlockX()); playerData.set(path + ".y", location.getBlockY()); playerData.set(path + ".z", location.getBlockZ()); savePlayerData(); getLogger().info("Zieltruhe für " + getServer().getPlayer(playerUUID).getName() + " (" + itemType.name() + ") gesetzt bei " + location); } private Location getTargetChestLocation(UUID playerUUID, Material itemType) { String path = "players." + playerUUID + ".target-chests." + itemType.name(); if (!playerData.contains(path)) { if (isDebug()) getLogger().fine("Keine Zieltruhe für Item " + itemType.name() + " für Spieler UUID " + playerUUID); return null; } String worldName = playerData.getString(path + ".world"); World world = getServer().getWorld(worldName); if (world == null) { getLogger().warning("Welt " + worldName + " für Zieltruhe von Item " + itemType.name() + " nicht gefunden"); return null; } int x = playerData.getInt(path + ".x"); int y = playerData.getInt(path + ".y"); int z = playerData.getInt(path + ".z"); return new Location(world, x, y, z); } private void cleanMessageTracker() { long currentTime = System.currentTimeMillis(); fullChestMessageTracker.entrySet().removeIf(entry -> { Map messages = entry.getValue(); messages.entrySet().removeIf(msg -> currentTime - msg.getValue() > MESSAGE_COOLDOWN); return messages.isEmpty(); }); } private boolean canSendFullChestMessage(UUID playerUUID, Material material) { Map playerMessages = fullChestMessageTracker.computeIfAbsent(playerUUID, k -> new HashMap<>()); Long lastMessageTime = playerMessages.get(material); long currentTime = System.currentTimeMillis(); if (lastMessageTime == null || currentTime - lastMessageTime > MESSAGE_COOLDOWN) { playerMessages.put(material, currentTime); return true; } return false; } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (!command.getName().equalsIgnoreCase("asc")) return false; if (!(sender instanceof Player)) { sender.sendMessage(ChatColor.RED + "Dieser Befehl ist nur für Spieler!"); return true; } Player player = (Player) sender; if (args.length == 0 || args[0].equalsIgnoreCase("help")) { String helpMessage = getMessage("help"); player.sendMessage(helpMessage.split("\n")); return true; } if (args[0].equalsIgnoreCase("info")) { String infoMessage = getMessage("info") .replace("%version%", getDescription().getVersion()) .replace("%config_version%", config != null ? config.getString("version", CONFIG_VERSION) : CONFIG_VERSION) .replace("%author%", String.join(", ", getDescription().getAuthors())); player.sendMessage(infoMessage.split("\n")); return true; } if (args[0].equalsIgnoreCase("reload")) { if (!player.hasPermission("autosortchest.reload")) { player.sendMessage(getMessage("no-permission")); return true; } reloadConfig(); config = getConfig(); loadPlayerData(); updateConfig(); updateExistingSigns(); player.sendMessage(getMessage("reload-success")); getLogger().info("Konfiguration und Spielerdaten neu geladen durch " + player.getName()); return true; } String helpMessage = getMessage("help"); player.sendMessage(helpMessage.split("\n")); return true; } @EventHandler public void onSignChange(SignChangeEvent event) { Player player = event.getPlayer(); UUID playerUUID = player.getUniqueId(); Block signBlock = event.getBlock(); String[] lines = event.getLines(); if (lines.length >= 2 && lines[0].equalsIgnoreCase("[asc]") && lines[1].equalsIgnoreCase("input")) { Block chestBlock = null; if (signBlock.getBlockData() instanceof WallSign wallSign) { Block attachedBlock = signBlock.getRelative(wallSign.getFacing().getOppositeFace()); if (attachedBlock.getState() instanceof Chest) { chestBlock = attachedBlock; } } if (chestBlock == null) { player.sendMessage(getMessage("no-chest-near-sign")); getLogger().warning("Keine Truhe an Schild bei " + signBlock.getLocation() + " für Spieler " + player.getName()); return; } event.setLine(0, getSignColor("input", "line1") + "[asc]"); event.setLine(1, getSignColor("input", "line2") + "input"); event.setLine(3, getSignColor("input", "line4") + player.getName()); setInputChestLocation(playerUUID, chestBlock.getLocation()); player.sendMessage(getMessage("input-chest-set")); getLogger().info("Eingangstruhe für " + player.getName() + " gesetzt bei " + chestBlock.getLocation()); } } @EventHandler public void onPlayerInteract(org.bukkit.event.player.PlayerInteractEvent event) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; Player player = event.getPlayer(); UUID playerUUID = player.getUniqueId(); Block clickedBlock = event.getClickedBlock(); ItemStack itemInHand = event.getItem(); if (clickedBlock == null) return; // Schild-Interaktion if (clickedBlock.getState() instanceof Sign sign) { String[] lines = sign.getLines(); if (lines.length >= 2 && ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")).equalsIgnoreCase("[asc]")) { // Truhe finden Block chestBlock = null; if (sign.getBlockData() instanceof WallSign wallSign) { Block attachedBlock = sign.getBlock().getRelative(wallSign.getFacing().getOppositeFace()); if (attachedBlock.getState() instanceof Chest) { chestBlock = attachedBlock; } } if (chestBlock == null) { player.sendMessage(getMessage("no-chest-near-sign")); event.setCancelled(true); return; } String line1Clean = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); // --- LOGIK FÜR ZIELTRUHEN (ZIEL) --- if (line1Clean.equalsIgnoreCase("ziel")) { String line3Raw = lines[3] != null ? lines[3] : ""; String line3Clean = ChatColor.stripColor(line3Raw); String pureOwnerName = line3Clean.replace("[Public]", "").replace("[public]", "").trim(); boolean isOwner = pureOwnerName.equalsIgnoreCase(player.getName()); // 1. FALL: MODUS WECHSELN (Shift + Klick + Leere Hand) if (event.getAction() == Action.RIGHT_CLICK_BLOCK && player.isSneaking() && (itemInHand == null || itemInHand.getType() == Material.AIR)) { if (isOwner) { boolean isPublic = isChestPublic(sign); String newModeText; String newLine4; if (isPublic) { // Wechsel zu Privat newModeText = getMessage("mode-private"); newLine4 = getSignColor("target", "line4") + pureOwnerName; } else { // Wechsel zu Öffentlich newModeText = getMessage("mode-public"); newLine4 = getSignColor("target", "line4") + pureOwnerName + " " + ChatColor.RESET + "[Public]"; } sign.setLine(3, newLine4); sign.update(); player.sendMessage(getMessage("mode-changed").replace("%mode%", newModeText)); event.setCancelled(true); return; } else { player.sendMessage(getMessage("not-your-chest")); event.setCancelled(true); return; } } // 2. FALL: ITEM ZUWEISEN (Klick + Item in Hand) String line2Clean = ChatColor.stripColor((String) (lines[2] != null ? lines[2] : "")); if (line2Clean.isEmpty()) { if (itemInHand == null || itemInHand.getType() == Material.AIR) { player.sendMessage(getMessage("no-item-in-hand")); event.setCancelled(true); return; } // Wenn das Schild noch KEINEN Owner hat, darf jeder es "übernehmen" if (!pureOwnerName.isEmpty() && !pureOwnerName.equalsIgnoreCase("Unknown") && !isOwner) { player.sendMessage(getMessage("not-your-chest")); event.setCancelled(true); return; } Chest chest = (Chest) chestBlock.getState(); boolean isFull = isInventoryFull(chest.getInventory()); String colorType = isFull ? "full" : "target"; sign.setLine(0, getSignColor(colorType, "line1") + "[asc]"); sign.setLine(1, getSignColor(colorType, "line2") + "ziel"); sign.setLine(2, getSignColor(colorType, "line3") + itemInHand.getType().name()); // Wenn noch kein Name da war, setze ihn String finalLine4 = line3Raw; if (pureOwnerName.isEmpty() || pureOwnerName.equalsIgnoreCase("Unknown")) { finalLine4 = getSignColor("target", "line4") + player.getName(); } sign.setLine(3, finalLine4); sign.update(); setTargetChestLocation(player.getUniqueId(), chestBlock.getLocation(), itemInHand.getType()); player.sendMessage(getMessage("target-chest-set").replace("%item%", itemInHand.getType().name())); event.setCancelled(true); return; } // Zugriffsschutz für konfigurierte Truhen // Wenn nicht Owner und NICHT öffentlich -> Zugriff verweigern if (!isOwner && !isChestPublic(sign)) { player.sendMessage(getMessage("not-your-chest")); event.setCancelled(true); return; } // Wenn Owner oder Öffentlich -> Truhe öffnen lassen (Event nicht canceln) return; } // --- LOGIK FÜR EINGANGSTRUHEN (INPUT) --- if (line1Clean.equalsIgnoreCase("input")) { String line3Raw = lines[3] != null ? lines[3] : ""; String line3Clean = ChatColor.stripColor(line3Raw); String pureOwnerName = line3Clean.replace("[Public]", "").replace("[public]", "").trim(); boolean isOwner = pureOwnerName.equalsIgnoreCase(player.getName()); // 1. FALL: MODUS WECHSELN (Shift + Klick + Leere Hand) if (player.isSneaking() && (itemInHand == null || itemInHand.getType() == Material.AIR)) { if (isOwner) { boolean isPublic = isChestPublic(sign); String newModeText; String newLine4; if (isPublic) { newModeText = getMessage("mode-private"); newLine4 = getSignColor("input", "line4") + pureOwnerName; } else { newModeText = getMessage("mode-public"); newLine4 = getSignColor("input", "line4") + pureOwnerName + " " + ChatColor.RESET + "[Public]"; } sign.setLine(3, newLine4); sign.update(); player.sendMessage(getMessage("mode-changed").replace("%mode%", newModeText)); event.setCancelled(true); return; } else { player.sendMessage(getMessage("not-your-chest")); event.setCancelled(true); return; } } // Zugriffsschutz if (!isOwner && !isChestPublic(sign)) { player.sendMessage(getMessage("not-your-chest")); event.setCancelled(true); return; } } } return; } // Truhe-Interaktion (wenn man direkt auf die Truhe klickt und nicht auf das Schild) if (clickedBlock.getState() instanceof Chest) { Block chestBlock = clickedBlock; Block signBlock = null; // Suche Schild for (Block face : new Block[] { chestBlock.getRelative(1, 0, 0), chestBlock.getRelative(-1, 0, 0), chestBlock.getRelative(0, 0, 1), chestBlock.getRelative(0, 0, -1) }) { if (face.getState() instanceof Sign sign && isSignAttachedToChest(face, chestBlock)) { String[] lines = sign.getLines(); if (lines.length >= 2 && ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")).equalsIgnoreCase("[asc]")) { signBlock = face; break; } } } if (signBlock != null) { Sign sign = (Sign) signBlock.getState(); String[] lines = sign.getLines(); String line1Clean = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); // NUR FÜR ZIELTRUHEN if (line1Clean.equalsIgnoreCase("ziel")) { String line3Raw = lines[3] != null ? lines[3] : ""; String line3Clean = ChatColor.stripColor(line3Raw); String pureOwnerName = line3Clean.replace("[public]", "").replace("[Public]", "").trim(); boolean isOwner = pureOwnerName.equalsIgnoreCase(player.getName()); // 1. FALL: MODUS WECHSELN if (player.isSneaking() && (itemInHand == null || itemInHand.getType() == Material.AIR)) { if (isOwner) { boolean isPublic = isChestPublic(sign); String newModeText; String newLine4; String baseName = getSignColor("target", "line4") + pureOwnerName; if (isPublic) { newModeText = getMessage("mode-private"); newLine4 = baseName; } else { newModeText = getMessage("mode-public"); newLine4 = baseName + " " + ChatColor.RESET + "[Public]"; } sign.setLine(3, newLine4); sign.update(); player.sendMessage(getMessage("mode-changed").replace("%mode%", newModeText)); event.setCancelled(true); return; } else { player.sendMessage(getMessage("not-your-chest")); event.setCancelled(true); return; } } // 2. FALL: ITEM ZUWEISEN String line2Clean = ChatColor.stripColor((String) (lines[2] != null ? lines[2] : "")); if (line2Clean.isEmpty()) { if (itemInHand == null || itemInHand.getType() == Material.AIR) { player.sendMessage(getMessage("no-item-in-hand")); event.setCancelled(true); return; } if (!pureOwnerName.isEmpty() && !pureOwnerName.equalsIgnoreCase("Unknown") && !isOwner) { player.sendMessage(getMessage("not-your-chest")); event.setCancelled(true); return; } Chest chest = (Chest) chestBlock.getState(); boolean isFull = isInventoryFull(chest.getInventory()); String colorType = isFull ? "full" : "target"; sign.setLine(0, getSignColor(colorType, "line1") + "[asc]"); sign.setLine(1, getSignColor(colorType, "line2") + "ziel"); sign.setLine(2, getSignColor(colorType, "line3") + itemInHand.getType().name()); String finalLine4 = line3Raw; if (pureOwnerName.isEmpty() || pureOwnerName.equalsIgnoreCase("Unknown")) { finalLine4 = getSignColor("target", "line4") + player.getName(); } sign.setLine(3, finalLine4); sign.update(); setTargetChestLocation(player.getUniqueId(), chestBlock.getLocation(), itemInHand.getType()); player.sendMessage(getMessage("target-chest-set").replace("%item%", itemInHand.getType().name())); event.setCancelled(true); return; } // Wenn nicht Owner und nicht öffentlich -> Zugriff verweigern if (!isOwner && !isChestPublic(sign)) { player.sendMessage(getMessage("not-your-chest")); event.setCancelled(true); return; } } // FALL: EINGANGSTRUHE if (line1Clean.equalsIgnoreCase("input")) { String line3Raw = lines[3] != null ? lines[3] : ""; String line3Clean = ChatColor.stripColor(line3Raw); String pureOwnerName = line3Clean.replace("[public]", "").replace("[Public]", "").trim(); boolean isOwner = pureOwnerName.equalsIgnoreCase(player.getName()); // Moduswechsel if (player.isSneaking() && (itemInHand == null || itemInHand.getType() == Material.AIR)) { if (isOwner) { boolean isPublic = isChestPublic(sign); String newModeText; String newLine4; String baseName = getSignColor("input", "line4") + pureOwnerName; if (isPublic) { newModeText = getMessage("mode-private"); newLine4 = baseName; } else { newModeText = getMessage("mode-public"); newLine4 = baseName + " " + ChatColor.RESET + "[Public]"; } sign.setLine(3, newLine4); sign.update(); player.sendMessage(getMessage("mode-changed").replace("%mode%", newModeText)); event.setCancelled(true); return; } else { player.sendMessage(getMessage("not-your-chest")); event.setCancelled(true); return; } } if (!isOwner && !isChestPublic(sign)) { player.sendMessage(getMessage("not-your-chest")); event.setCancelled(true); return; } } } } } @EventHandler(priority = EventPriority.HIGHEST) public void onBlockBreak(BlockBreakEvent event) { Block block = event.getBlock(); Player player = event.getPlayer(); Block signBlock = null; String signOwner = ""; boolean isAscChest = false; if (isDebug()) { getLogger().fine("BlockBreakEvent ausgelöst bei " + block.getLocation() + " durch Spieler " + player.getName() + ", GameMode: " + player.getGameMode() + ", Sneaking: " + player.isSneaking() + ", OP: " + player.isOp() + ", Berechtigung autosortchest.bypass: " + player.hasPermission("autosortchest.bypass")); } // Fall 1: Schild wird abgebaut if (block.getState() instanceof Sign sign) { String[] lines = sign.getLines(); if (lines.length >= 2 && ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")).equalsIgnoreCase("[asc]") && (ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("input") || ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("ziel"))) { signBlock = block; signOwner = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : "")); if (isDebug()) { getLogger().fine("Schild erkannt bei " + block.getLocation() + ", Besitzer: " + signOwner); } } } // Fall 2: Truhe wird abgebaut if (block.getState() instanceof Chest chestBlock) { for (Block face : new Block[] { block.getRelative(1, 0, 0), block.getRelative(-1, 0, 0), block.getRelative(0, 0, 1), block.getRelative(0, 0, -1) }) { if (face.getState() instanceof Sign sign && isSignAttachedToChest(face, block)) { String[] lines = sign.getLines(); if (lines.length >= 2 && ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")).equalsIgnoreCase("[asc]") && (ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("input") || ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("ziel"))) { signBlock = face; signOwner = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : "")); isAscChest = true; if (isDebug()) { getLogger().fine("Truhe mit [asc]-Schild erkannt bei " + block.getLocation() + ", Schild bei " + face.getLocation() + ", Besitzer: " + signOwner); } break; } } } } // Kein [asc]-Schild oder keine Truhe mit [asc]-Schild if (signBlock == null) { if (isDebug()) { getLogger().fine("Kein [asc]-Schild oder Truhe mit [asc]-Schild bei " + block.getLocation()); } return; } // Prüfe Besitzer boolean isOwner = signOwner.isEmpty() || signOwner.equalsIgnoreCase(player.getName()); if (!isOwner) { player.sendMessage(getMessage("not-your-chest")); event.setCancelled(true); getLogger().warning("Spieler " + player.getName() + " versuchte, " + (isAscChest ? "Truhe" : "Schild") + " bei " + block.getLocation() + " abzubauen, das nicht ihm gehört (Besitzer: " + signOwner + ")"); return; } // Prüfe Shift-Taste (Berechtigungen ignorieren) if (!player.isSneaking()) { player.sendMessage(getMessage("sign-break-denied")); event.setCancelled(true); if (isDebug()) { getLogger().fine("Spieler " + player.getName() + " versuchte, " + (isAscChest ? "Truhe" : "Schild") + " bei " + block.getLocation() + " ohne Shift abzubauen"); } return; } if (isDebug()) { getLogger().fine("Spieler " + player.getName() + " hat " + (isAscChest ? "Truhe" : "Schild") + " bei " + block.getLocation() + " erfolgreich abgebaut"); } } @EventHandler public void onInventoryClose(InventoryCloseEvent event) { if (!(event.getInventory().getHolder() instanceof Chest chest)) return; Player player = (Player) event.getPlayer(); Block chestBlock = chest.getBlock(); Block signBlock = null; // Suche nach einem an die Truhe angehängten Schild for (Block face : new Block[] { chestBlock.getRelative(1, 0, 0), chestBlock.getRelative(-1, 0, 0), chestBlock.getRelative(0, 0, 1), chestBlock.getRelative(0, 0, -1) }) { if (face.getState() instanceof Sign sign && isSignAttachedToChest(face, chestBlock)) { String[] lines = sign.getLines(); if (lines.length >= 2 && ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")).equalsIgnoreCase("[asc]") && ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("ziel")) { signBlock = face; break; } } } if (signBlock == null) { if (isDebug()) getLogger().fine("Keine Zieltruhe-Schild an Truhe bei " + chestBlock.getLocation()); return; } Sign sign = (Sign) signBlock.getState(); String[] lines = sign.getLines(); String signOwner = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : "")); if (!signOwner.equalsIgnoreCase(player.getName())) { if (isDebug()) getLogger().fine("Schild bei " + signBlock.getLocation() + " gehört nicht Spieler " + player.getName() + " (Besitzer: " + signOwner + ")"); return; } boolean isFull = isInventoryFull(chest.getInventory()); String colorType = isFull ? "full" : "target"; String currentLine0 = ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")); String currentLine1 = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); String currentLine2 = ChatColor.stripColor((String) (lines[2] != null ? lines[2] : "")); String currentLine3 = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : "")); if (currentLine0.equalsIgnoreCase("[asc]") && currentLine1.equalsIgnoreCase("ziel")) { sign.setLine(0, getSignColor(colorType, "line1") + "[asc]"); sign.setLine(1, getSignColor(colorType, "line2") + "ziel"); sign.setLine(2, getSignColor(colorType, "line3") + currentLine2); sign.setLine(3, getSignColor(colorType, "line4") + currentLine3); sign.update(); if (isDebug()) { getLogger().fine("Zieltruhe-Schild bei " + signBlock.getLocation() + " aktualisiert nach Inventar-Schließung (voll: " + isFull + ")"); } } } private void distributeItems(Player player, Inventory sourceInventory) { UUID playerUUID = player.getUniqueId(); Block chestBlock = ((Chest) sourceInventory.getHolder()).getBlock(); // Prüfe, ob die Truhe leer ist boolean hasItems = false; for (ItemStack item : sourceInventory.getContents()) { if (item != null && item.getType() != Material.AIR) { hasItems = true; break; } } if (!hasItems) { if (isDebug()) getLogger().fine("Eingangstruhe bei " + chestBlock.getLocation() + " ist leer"); return; } for (int slot = 0; slot < sourceInventory.getSize(); slot++) { ItemStack item = sourceInventory.getItem(slot); if (item == null || item.getType() == Material.AIR) continue; Location targetChestLocation = getTargetChestLocation(playerUUID, item.getType()); if (targetChestLocation == null) { if (isDebug()) getLogger().fine("Keine Zieltruhe für Item " + item.getType().name() + " für Spieler " + player.getName()); continue; } if (!(targetChestLocation.getBlock().getState() instanceof Chest)) { if (canSendFullChestMessage(playerUUID, item.getType())) { player.sendMessage(getMessage("target-chest-missing").replace("%item%", item.getType().name())); } playerData.set("players." + playerUUID + ".target-chests." + item.getType().name(), null); savePlayerData(); getLogger().warning("Zieltruhe für " + item.getType().name() + " fehlt bei " + targetChestLocation); continue; } Chest targetChest = (Chest) targetChestLocation.getBlock().getState(); Inventory targetInventory = targetChest.getInventory(); boolean isValidTarget = false; String signOwner = "Unbekannt"; Block signBlock = null; for (Block face : new Block[] { targetChest.getBlock().getRelative(1, 0, 0), targetChest.getBlock().getRelative(-1, 0, 0), targetChest.getBlock().getRelative(0, 0, 1), targetChest.getBlock().getRelative(0, 0, -1) }) { if (face.getState() instanceof Sign sign && isSignAttachedToChest(face, targetChest.getBlock())) { String[] lines = sign.getLines(); String line0 = ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")); String line1 = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); String line3 = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : "")); if (isDebug()) { getLogger().fine("Prüfe Zieltruhe-Schild bei " + face.getLocation() + ": Zeile 1='" + line0 + "', Zeile 2='" + line1 + "', Zeile 4='" + line3 + "' für Spieler " + player.getName()); } if (line0.equalsIgnoreCase("[asc]") && line1.equalsIgnoreCase("ziel")) { signOwner = line3.isEmpty() ? "Unbekannt" : line3; if (line3.equalsIgnoreCase(player.getName())) { isValidTarget = true; signBlock = face; if (isDebug()) { getLogger().fine("Gültiges Zieltruhe-Schild gefunden bei " + face.getLocation() + " für Spieler " + player.getName()); } break; } } else if (isDebug()) { getLogger().fine("Zieltruhe-Schild bei " + face.getLocation() + " hat ungültige Zeilen: [asc]=" + line0 + ", ziel=" + line1); } } } if (!isValidTarget) { if (canSendFullChestMessage(playerUUID, item.getType())) { player.sendMessage(getMessage("not-your-chest")); } getLogger().warning("Zieltruhe bei " + targetChestLocation + " gehört nicht Spieler " + player.getName() + " (Besitzer: " + signOwner + ")"); continue; } ItemStack itemToTransfer = item.clone(); Map leftover = targetInventory.addItem(itemToTransfer); boolean isFull = isInventoryFull(targetInventory); if (signBlock != null) { Sign sign = (Sign) signBlock.getState(); String[] lines = sign.getLines(); String line0 = ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")); String line1 = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); String line2 = ChatColor.stripColor((String) (lines[2] != null ? lines[2] : "")); String line3 = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : "")); if (line0.equalsIgnoreCase("[asc]") && line1.equalsIgnoreCase("ziel")) { String colorType = isFull ? "full" : "target"; sign.setLine(0, getSignColor(colorType, "line1") + "[asc]"); sign.setLine(1, getSignColor(colorType, "line2") + "ziel"); sign.setLine(2, getSignColor(colorType, "line3") + line2); sign.setLine(3, getSignColor(colorType, "line4") + line3); sign.update(); if (isDebug()) { getLogger().fine("Zieltruhe-Schild bei " + signBlock.getLocation() + " aktualisiert (voll: " + isFull + ")"); } } } if (leftover.isEmpty()) { sourceInventory.setItem(slot, null); } else { if (canSendFullChestMessage(playerUUID, item.getType())) { String message = getMessage("target-chest-full") .replace("%item%", item.getType().name()) .replace("%x%", String.valueOf(targetChestLocation.getBlockX())) .replace("%y%", String.valueOf(targetChestLocation.getBlockY())) .replace("%z%", String.valueOf(targetChestLocation.getBlockZ())); player.sendMessage(message); } for (ItemStack leftoverItem : leftover.values()) { if (leftoverItem != null && leftoverItem.getType() == item.getType()) { item.setAmount(leftoverItem.getAmount()); sourceInventory.setItem(slot, item); break; } } } } } private void checkInputChests() { if (playerData == null) { getLogger().warning("playerData ist null. Kann Eingangstruhe nicht prüfen."); return; } if (playerData.getConfigurationSection("players") == null) { getLogger().warning("Abschnitt 'players' in players.yml fehlt. Keine Eingangstruhe zu prüfen."); return; } for (String uuidString : playerData.getConfigurationSection("players").getKeys(false)) { UUID ownerUUID; try { ownerUUID = UUID.fromString(uuidString); } catch (IllegalArgumentException e) { getLogger().warning("Ungültige UUID in players.yml: " + uuidString); continue; } String path = "players." + uuidString + ".input-chest"; if (!playerData.contains(path)) continue; String worldName = playerData.getString(path + ".world"); World world = getServer().getWorld(worldName); if (world == null) { getLogger().warning("Welt " + worldName + " für Eingangstruhe von UUID " + uuidString + " nicht gefunden"); continue; } int x = playerData.getInt(path + ".x"); int y = playerData.getInt(path + ".y"); int z = playerData.getInt(path + ".z"); Location location = new Location(world, x, y, z); if (!(location.getBlock().getState() instanceof Chest)) { if (isDebug()) { getLogger().fine("Eingangstruhe bei " + location + " existiert nicht"); } continue; } Chest chest = (Chest) location.getBlock().getState(); Block inputSignBlock = null; boolean isPublic = false; String ownerName = "Unknown"; // Schild suchen und Status prüfen for (Block face : new Block[] { chest.getBlock().getRelative(1, 0, 0), chest.getBlock().getRelative(-1, 0, 0), chest.getBlock().getRelative(0, 0, 1), chest.getBlock().getRelative(0, 0, -1) }) { if (face.getState() instanceof Sign sign && isSignAttachedToChest(face, chest.getBlock())) { String[] lines = sign.getLines(); String line0 = ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")); String line1 = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); if (line0.equalsIgnoreCase("[asc]") && line1.equalsIgnoreCase("input")) { inputSignBlock = face; String line3Raw = lines[3] != null ? lines[3] : ""; String line3Clean = ChatColor.stripColor(line3Raw); isPublic = line3Clean.toLowerCase().endsWith("[public]"); ownerName = line3Clean.replace(" [Public]", "").replace(" [public]", "").trim(); break; } } } if (inputSignBlock == null) { if (isDebug()) getLogger().fine("Kein Eingangsschild an Truhe bei " + location); continue; } // Wenn das Inventar leer ist, brauchen wir nichts tun boolean hasItems = false; for (ItemStack item : chest.getInventory().getContents()) { if (item != null && item.getType() != Material.AIR) { hasItems = true; break; } } if (!hasItems) continue; // Jetzt kommt der entscheidende Teil für die Multiplayer-Funktionalität: // Wenn PRIVATE: Nur sortieren, wenn der Owner ONLINE ist. // Wenn ÖFFENTLICH: Immer sortieren, aber wir brauchen einen "fiktiven" Player für die Fehlermeldungen // oder wir senden Meldungen an alle Online-Spieler, die in der Nähe sind? // Der Einfachheit halber: Wenn Öffentlich, sortieren wir stumm oder senden Meldungen an den Owner wenn er online ist. Player ownerPlayer = getServer().getPlayer(ownerUUID); if (!isPublic) { // Privat: Nur wenn Owner online if (ownerPlayer == null || !ownerPlayer.isOnline()) continue; } else { // Öffentlich: Wenn Owner offline, können wir keine "Truhe voll" Nachrichten an den Owner senden. // Wir sortieren trotzdem. // Wir setzen ownerPlayer auf null, damit distributeItems weiß, dass niemand Besitzer ist (für Messages). } // Wir rufen distributeItems auf. // WICHTIG: distributeItems nutzt `player.getUniqueId()` um die Zieltruhen zu finden. // Das funktioniert auch, wenn ownerPlayer null ist, solange wir die UUID übergeben. // Wir müssen aber aufpassen, dass `distributeItems` nicht crasht, wenn player null ist. distributeItemsForOwner(ownerUUID, ownerPlayer, chest.getInventory()); } } // 4. KORRIGIERTE distributeItemsForOwner Methode: private void distributeItemsForOwner(UUID ownerUUID, Player ownerPlayer, Inventory sourceInventory) { boolean hasItems = false; for (ItemStack item : sourceInventory.getContents()) { if (item != null && item.getType() != Material.AIR) { hasItems = true; break; } } if (!hasItems) return; // Owner-Name ermitteln String ownerName = "Unknown"; if (ownerPlayer != null) { ownerName = ownerPlayer.getName(); } else { // Offline-Namen aus PlayerData holen wenn möglich org.bukkit.OfflinePlayer offlinePlayer = getServer().getOfflinePlayer(ownerUUID); if (offlinePlayer.hasPlayedBefore()) { ownerName = offlinePlayer.getName(); } } for (int slot = 0; slot < sourceInventory.getSize(); slot++) { ItemStack item = sourceInventory.getItem(slot); if (item == null || item.getType() == Material.AIR) continue; Location targetChestLocation = getTargetChestLocation(ownerUUID, item.getType()); if (targetChestLocation == null) continue; if (!(targetChestLocation.getBlock().getState() instanceof Chest)) { if (ownerPlayer != null && canSendFullChestMessage(ownerUUID, item.getType())) { ownerPlayer.sendMessage(getMessage("target-chest-missing").replace("%item%", item.getType().name())); } playerData.set("players." + ownerUUID + ".target-chests." + item.getType().name(), null); savePlayerData(); continue; } Chest targetChest = (Chest) targetChestLocation.getBlock().getState(); Inventory targetInventory = targetChest.getInventory(); boolean isValidTarget = false; Block signBlock = null; for (Block face : new Block[] { targetChest.getBlock().getRelative(1, 0, 0), targetChest.getBlock().getRelative(-1, 0, 0), targetChest.getBlock().getRelative(0, 0, 1), targetChest.getBlock().getRelative(0, 0, -1) }) { if (face.getState() instanceof Sign sign && isSignAttachedToChest(face, targetChest.getBlock())) { String[] lines = sign.getLines(); String line0 = ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")); String line1 = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); String line3Clean = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : "")); String signOwnerName = line3Clean.replace("[Public]", "").replace("[public]", "").trim(); if (line0.equalsIgnoreCase("[asc]") && line1.equalsIgnoreCase("ziel")) { if (signOwnerName.equalsIgnoreCase(ownerName) || signOwnerName.equalsIgnoreCase("Unknown")) { isValidTarget = true; signBlock = face; break; } } } } if (!isValidTarget) continue; ItemStack itemToTransfer = item.clone(); Map leftover = targetInventory.addItem(itemToTransfer); boolean isFull = isInventoryFull(targetInventory); if (signBlock != null) { Sign sign = (Sign) signBlock.getState(); String[] lines = sign.getLines(); String line0 = ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")); String line1 = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); String line2 = ChatColor.stripColor((String) (lines[2] != null ? lines[2] : "")); String line3Raw = lines[3] != null ? lines[3] : ""; if (line0.equalsIgnoreCase("[asc]") && line1.equalsIgnoreCase("ziel")) { String colorType = isFull ? "full" : "target"; sign.setLine(0, getSignColor(colorType, "line1") + "[asc]"); sign.setLine(1, getSignColor(colorType, "line2") + "ziel"); sign.setLine(2, getSignColor(colorType, "line3") + line2); sign.setLine(3, line3Raw); sign.update(); } } if (leftover.isEmpty()) { sourceInventory.setItem(slot, null); } else { if (ownerPlayer != null && canSendFullChestMessage(ownerUUID, item.getType())) { String message = getMessage("target-chest-full") .replace("%item%", item.getType().name()) .replace("%x%", String.valueOf(targetChestLocation.getBlockX())) .replace("%y%", String.valueOf(targetChestLocation.getBlockY())) .replace("%z%", String.valueOf(targetChestLocation.getBlockZ())); ownerPlayer.sendMessage(message); } for (ItemStack leftoverItem : leftover.values()) { if (leftoverItem != null && leftoverItem.getType() == item.getType()) { item.setAmount(leftoverItem.getAmount()); sourceInventory.setItem(slot, item); break; } } } } } }