diff --git a/src/main/java/com/viper/autosortchest/Main.java b/src/main/java/com/viper/autosortchest/Main.java index a54577b..3f899cf 100644 --- a/src/main/java/com/viper/autosortchest/Main.java +++ b/src/main/java/com/viper/autosortchest/Main.java @@ -38,7 +38,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { 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.2"; + private static final String CONFIG_VERSION = "1.3"; @Override public void onEnable() { @@ -127,7 +127,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { } } - private void updateConfig() { + private void updateConfig() { File configFile = new File(getDataFolder(), "config.yml"); InputStream defaultConfigStream = getResource("config.yml"); FileConfiguration defaultConfig; @@ -268,6 +268,22 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { 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" + @@ -314,16 +330,11 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { getLogger().info("Setze Standardwert: messages.sign-break-denied"); } - // Logge die Konfiguration vor dem Speichern - getLogger().info("Konfiguration vor dem Speichern: " + config.saveToString()); - // 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); - FileConfiguration savedConfig = YamlConfiguration.loadConfiguration(configFile); - getLogger().info("Gespeicherte config.yml: " + savedConfig.saveToString()); } else { getLogger().warning("config.yml wurde nicht korrekt gespeichert (Datei existiert nicht oder ist leer)"); } @@ -462,6 +473,12 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { 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); } @@ -634,7 +651,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { } } - @EventHandler + @EventHandler public void onPlayerInteract(org.bukkit.event.player.PlayerInteractEvent event) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; @@ -648,7 +665,9 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { // 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]") && ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("ziel")) { + 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()); @@ -659,50 +678,151 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { if (chestBlock == null) { player.sendMessage(getMessage("no-chest-near-sign")); - getLogger().warning("Keine Truhe an Schild bei " + sign.getLocation() + " für Spieler " + player.getName()); event.setCancelled(true); return; } - String signOwner = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : "")); - if (!signOwner.isEmpty() && !signOwner.equalsIgnoreCase(player.getName())) { - player.sendMessage(getMessage("not-your-chest")); - getLogger().warning("Schild bei " + sign.getLocation() + " gehört nicht Spieler " + player.getName() + " (Besitzer: " + signOwner + ")"); - event.setCancelled(true); - return; - } + String line1Clean = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); - String line2 = ChatColor.stripColor((String) (lines[2] != null ? lines[2] : "")); - if (itemInHand == null || itemInHand.getType() == Material.AIR) { - if (line2.isEmpty()) { - player.sendMessage(getMessage("no-item-in-hand")); - event.setCancelled(true); + // --- 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; + } } - // Wenn line2 nicht leer ist, keine Aktion erforderlich (Schild nicht verändern, Event nicht abbrechen) - 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; } - Chest chest = (Chest) chestBlock.getState(); - boolean isFull = isInventoryFull(chest.getInventory()); - String colorType = isFull ? "full" : "target"; + // --- 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()); - sign.setLine(0, getSignColor(colorType, "line1") + "[asc]"); - sign.setLine(1, getSignColor(colorType, "line2") + "ziel"); - sign.setLine(2, getSignColor(colorType, "line3") + itemInHand.getType().name()); - sign.setLine(3, getSignColor(colorType, "line4") + player.getName()); - sign.update(); - setTargetChestLocation(playerUUID, chestBlock.getLocation(), itemInHand.getType()); - player.sendMessage(getMessage("target-chest-set").replace("%item%", itemInHand.getType().name())); - event.setCancelled(true); - return; + // 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 + // 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), @@ -711,54 +831,145 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { }) { 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")) { + if (lines.length >= 2 && ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")).equalsIgnoreCase("[asc]")) { signBlock = face; break; } } } - if (signBlock == null) { - if (isDebug()) getLogger().fine("Kein [asc]-Zielschild an Truhe bei " + chestBlock.getLocation()); - return; - } + if (signBlock != null) { + Sign sign = (Sign) signBlock.getState(); + String[] lines = sign.getLines(); + String line1Clean = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")); - Sign sign = (Sign) signBlock.getState(); - String[] lines = sign.getLines(); - String signOwner = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : "")); - if (!signOwner.isEmpty() && !signOwner.equalsIgnoreCase(player.getName())) { - player.sendMessage(getMessage("not-your-chest")); - getLogger().warning("Schild bei " + sign.getLocation() + " gehört nicht Spieler " + player.getName() + " (Besitzer: " + signOwner + ")"); - event.setCancelled(true); - return; - } + // 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()); - String line2 = ChatColor.stripColor((String) (lines[2] != null ? lines[2] : "")); - if (itemInHand == null || itemInHand.getType() == Material.AIR) { - if (line2.isEmpty()) { - player.sendMessage(getMessage("no-item-in-hand")); - event.setCancelled(true); + // 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; + } } - // Wenn line2 nicht leer ist, Truhe normal öffnen lassen (Event nicht abbrechen) - 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()); - sign.setLine(3, getSignColor(colorType, "line4") + player.getName()); - sign.update(); - setTargetChestLocation(playerUUID, chestBlock.getLocation(), itemInHand.getType()); - player.sendMessage(getMessage("target-chest-set").replace("%item%", itemInHand.getType().name())); - event.setCancelled(true); } } - @EventHandler(priority = EventPriority.HIGHEST) + @EventHandler(priority = EventPriority.HIGHEST) public void onBlockBreak(BlockBreakEvent event) { Block block = event.getBlock(); Player player = event.getPlayer(); @@ -1024,35 +1235,32 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { } } - private void checkInputChests() { + private void checkInputChests() { if (playerData == null) { - getLogger().warning("playerData ist null. Kann Eingangstruchen nicht prüfen."); + 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 Eingangstruchen zu prüfen."); + getLogger().warning("Abschnitt 'players' in players.yml fehlt. Keine Eingangstruhe zu prüfen."); return; } for (String uuidString : playerData.getConfigurationSection("players").getKeys(false)) { - UUID playerUUID; + UUID ownerUUID; try { - playerUUID = UUID.fromString(uuidString); + ownerUUID = UUID.fromString(uuidString); } catch (IllegalArgumentException e) { getLogger().warning("Ungültige UUID in players.yml: " + uuidString); continue; } - Player player = getServer().getPlayer(playerUUID); - if (player == null || !player.isOnline()) 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 Spieler " + player.getName() + " nicht gefunden"); + getLogger().warning("Welt " + worldName + " für Eingangstruhe von UUID " + uuidString + " nicht gefunden"); continue; } @@ -1063,13 +1271,17 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { if (!(location.getBlock().getState() instanceof Chest)) { if (isDebug()) { - getLogger().fine("Eingangstruhe für Spieler " + player.getName() + " bei " + location + " existiert nicht"); + getLogger().fine("Eingangstruhe bei " + location + " existiert nicht"); } continue; } Chest chest = (Chest) location.getBlock().getState(); - boolean isValidInput = false; + 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), @@ -1080,34 +1292,172 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor { 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 Eingangsschild bei " + face.getLocation() + ": Zeile 1='" + line0 + "', Zeile 2='" + line1 + "', Zeile 4='" + line3 + "' für Spieler " + player.getName()); - } + if (line0.equalsIgnoreCase("[asc]") && line1.equalsIgnoreCase("input")) { - if (line3.equalsIgnoreCase(player.getName())) { - isValidInput = true; - if (isDebug()) { - getLogger().fine("Gültiges Eingangsschild gefunden bei " + face.getLocation() + " für Spieler " + player.getName()); - } - break; - } else if (isDebug()) { - getLogger().fine("Eingangsschild bei " + face.getLocation() + " gehört nicht Spieler " + player.getName() + " (Besitzer: " + line3 + ")"); - } - } else if (isDebug()) { - getLogger().fine("Eingangsschild bei " + face.getLocation() + " hat ungültige Zeilen: [asc]=" + line0 + ", input=" + line1); + 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 (!isValidInput) { - if (isDebug()) { - getLogger().fine("Truhe bei " + location + " ist keine gültige Eingangstruhe für Spieler " + player.getName()); - } + if (inputSignBlock == null) { + if (isDebug()) getLogger().fine("Kein Eingangsschild an Truhe bei " + location); continue; } - distributeItems(player, chest.getInventory()); + // 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; + } + } + } + } + } } \ No newline at end of file