From 68a4a670eec8b9359469f36dad1e66b713e03020 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Sun, 25 Jan 2026 20:28:55 +0100 Subject: [PATCH] Update from Git Manager GUI --- src/main/java/viper/ButtonControl.java | 433 ++++++++++++-------- src/main/java/viper/ButtonListener.java | 367 +++++++++-------- src/main/java/viper/ButtonTabCompleter.java | 47 +++ src/main/java/viper/DataManager.java | 121 ++++-- src/main/resources/lang.yml | 50 ++- src/main/resources/plugin.yml | 18 +- 6 files changed, 633 insertions(+), 403 deletions(-) create mode 100644 src/main/java/viper/ButtonTabCompleter.java diff --git a/src/main/java/viper/ButtonControl.java b/src/main/java/viper/ButtonControl.java index a9f3b21..127c576 100644 --- a/src/main/java/viper/ButtonControl.java +++ b/src/main/java/viper/ButtonControl.java @@ -20,6 +20,8 @@ import org.bukkit.Note.Tone; import org.bukkit.event.Listener; import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerJoinEvent; +import java.util.ArrayList; +import java.util.List; import java.util.List; import java.util.UUID; @@ -33,56 +35,98 @@ public class ButtonControl extends JavaPlugin { @Override public void onEnable() { + // Initialisierung der Manager configManager = new ConfigManager(this); dataManager = new DataManager(this); - // Spigot Update Checker starten - new UpdateChecker(this, 127702).getVersion(version -> { - String currentVersion = this.getDescription().getVersion(); - String normalizedLatest = version.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim(); - String normalizedCurrent = currentVersion.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim(); + // Spigot Update Checker beim Serverstart ausführen + new UpdateChecker(this, 127702).getVersion(version -> { + String currentVersion = this.getDescription().getVersion(); + String normalizedLatest = version.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim(); + String normalizedCurrent = currentVersion.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim(); - if (isNewerVersion(normalizedLatest, normalizedCurrent)) { - getLogger().info("Neue Version verfügbar: " + version); - getLogger().info("Download: https://www.spigotmc.org/resources/buttoncontrol.127702/"); - Bukkit.getScheduler().runTask(this, () -> { - Bukkit.getOnlinePlayers().stream() - .filter(p -> p.hasPermission("buttoncontrol.update")) - .forEach(p -> { - p.sendMessage("§6[ButtonControl] §eEine neue Version ist verfügbar: §f" + version); - p.sendMessage("§6[ButtonControl] §eDownload: §fhttps://www.spigotmc.org/resources/buttoncontrol.127702/"); - }); - }); - } else { - getLogger().info("Keine neue Version verfügbar."); - } - }); + if (isNewerVersion(normalizedLatest, normalizedCurrent)) { + // Konsole bleibt sachlich + getLogger().info("Update verfügbar: v" + version); - // Listener für Spieler-Joins - getServer().getPluginManager().registerEvents(new Listener() { - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - Player player = event.getPlayer(); - if (!player.hasPermission("buttoncontrol.update")) return; - new UpdateChecker(ButtonControl.this, 127702).getVersion(version -> { - String currentVersion = getDescription().getVersion(); - String normalizedLatest = version.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim(); - String normalizedCurrent = currentVersion.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim(); + // Schicke die stylische Nachricht an Admins + Bukkit.getScheduler().runTask(this, () -> { + Bukkit.getOnlinePlayers().stream() + .filter(p -> p.hasPermission("buttoncontrol.update")) + .forEach(p -> { + p.sendMessage(""); // Leerzeile für Abstand + p.sendMessage("§8§m-----------------------------------------"); + p.sendMessage(" §6§lButtonControl §7- §e§lUpdate verfügbar!"); + p.sendMessage(""); + p.sendMessage(" §7Aktuelle Version: §c" + currentVersion); + p.sendMessage(" §7Neue Version: §a" + version); + p.sendMessage(""); + p.sendMessage(" §eDownload hier:"); + p.sendMessage(" §bhttps://www.spigotmc.org/resources/127702/"); + p.sendMessage("§8§m-----------------------------------------"); + p.sendMessage(""); + }); + }); + } else { + getLogger().info("ButtonControl ist auf dem neuesten Stand (v" + currentVersion + ")."); + } + }); - if (isNewerVersion(normalizedLatest, normalizedCurrent)) { - player.sendMessage("§6[ButtonControl] §eEine neue Version ist verfügbar: §f" + version); - player.sendMessage("§6[ButtonControl] §eDownload: §fhttps://www.spigotmc.org/resources/buttoncontrol.127702/"); - } - }); - } - }, this); + // Listener für Spieler-Joins (Update-Benachrichtigung beim Betreten des Servers) + getServer().getPluginManager().registerEvents(new org.bukkit.event.Listener() { + @org.bukkit.event.EventHandler + public void onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent event) { + Player player = event.getPlayer(); + if (!player.hasPermission("buttoncontrol.update")) return; + + new UpdateChecker(ButtonControl.this, 127702).getVersion(version -> { + String currentVersion = getDescription().getVersion(); + String normalizedLatest = version.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim(); + String normalizedCurrent = currentVersion.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim(); - updateConfigWithDefaults(); + if (isNewerVersion(normalizedLatest, normalizedCurrent)) { + // Stylische Box für den Spieler beim Joinen + player.sendMessage(""); + player.sendMessage("§8§m-----------------------------------------"); + player.sendMessage(" §6§lButtonControl §7- §e§lUpdate verfügbar!"); + player.sendMessage(""); + player.sendMessage(" §7Aktuelle Version: §c" + currentVersion); + player.sendMessage(" §7Neue Version: §a" + version); + player.sendMessage(""); + player.sendMessage(" §eDownload hier:"); + player.sendMessage(" §bhttps://www.spigotmc.org/resources/127702/"); + player.sendMessage("§8§m-----------------------------------------"); + player.sendMessage(""); + } + }); + } + }, this); + + // Konfiguration und Spielmechaniken laden + updateConfigWithDefaults(); + + // --- COMMANDS & TAB-COMPLETER --- + // Registriert den Executor (Befehlsverarbeitung) und den TabCompleter (Vorschläge) + if (getCommand("bc") != null) { + getCommand("bc").setExecutor(this); // 'this' setzt voraus, dass ButtonControl 'onCommand' enthält + getCommand("bc").setTabCompleter(new ButtonTabCompleter()); + } + + // Event-Listener registrieren getServer().getPluginManager().registerEvents(new ButtonListener(this, configManager, dataManager), this); + + // Rezepte und bStats/Metrics registerRecipes(); - getServer().getScheduler().runTaskTimer(this, this::checkDaylightSensors, 0L, 20L * 10); - getServer().getScheduler().runTaskTimer(this, this::checkMotionSensors, 0L, 10L); MetricsHandler.startMetrics(this); + + // --- AUTOMATISIERUNG-TIMER --- + // Daylight Sensoren: Prüfung alle 200 Ticks (10 Sekunden) + getServer().getScheduler().runTaskTimer(this, this::checkDaylightSensors, 0L, 20L * 10); + + // Bewegungsmelder (Motion Sensors): Prüfung alle 10 Ticks (0.5 Sekunden) für flüssige Erkennung + getServer().getScheduler().runTaskTimer(this, this::checkMotionSensors, 0L, 10L); + + getLogger().info("ButtonControl v" + getDescription().getVersion() + " wurde erfolgreich aktiviert!"); } private boolean isNewerVersion(String latest, String current) { @@ -123,58 +167,83 @@ public class ButtonControl extends JavaPlugin { } private void registerRecipes() { - ItemStack controlButton = new ItemStack(Material.STONE_BUTTON); - ItemMeta buttonMeta = controlButton.getItemMeta(); - buttonMeta.setDisplayName("§6Steuer-Button"); - controlButton.setItemMeta(buttonMeta); + // --- 1. Dynamische Steuer-Buttons für JEDE Holz/Stein-Art --- + for (Material mat : Material.values()) { + if (mat.name().endsWith("_BUTTON")) { + // Wir erstellen für jeden Button-Typ ein eigenes Ergebnis + ItemStack controlButton = new ItemStack(mat); + ItemMeta buttonMeta = controlButton.getItemMeta(); + if (buttonMeta != null) { + buttonMeta.setDisplayName("§6Steuer-Button"); + List lore = new ArrayList<>(); + lore.add("§7Ein universeller Controller für"); + lore.add("§7Türen, Lampen und mehr."); + buttonMeta.setLore(lore); + controlButton.setItemMeta(buttonMeta); + } - NamespacedKey buttonKey = new NamespacedKey(this, "control_button"); - ShapedRecipe buttonRecipe = new ShapedRecipe(buttonKey, controlButton); - buttonRecipe.shape("123", "456", "789"); - buttonRecipe.setIngredient('2', Material.STONE_BUTTON); - buttonRecipe.setIngredient('5', Material.STONE_BUTTON); - buttonRecipe.setIngredient('8', Material.STONE_BUTTON); - Bukkit.addRecipe(buttonRecipe); + // Wir brauchen für jedes Material einen eindeutigen Key (z.B. control_button_oak_button) + NamespacedKey key = new NamespacedKey(this, "control_" + mat.name().toLowerCase()); + if (Bukkit.getRecipe(key) != null) Bukkit.removeRecipe(key); - ItemStack controlDaylight = new ItemStack(Material.DAYLIGHT_DETECTOR); - ItemMeta daylightMeta = controlDaylight.getItemMeta(); + ShapedRecipe recipe = new ShapedRecipe(key, controlButton); + recipe.shape("123", "456", "789"); + // Das Material muss an allen drei Stellen gleich sein (z.B. 3x Eiche) + recipe.setIngredient('2', mat); + recipe.setIngredient('5', mat); + recipe.setIngredient('8', mat); + Bukkit.addRecipe(recipe); + } + } + + // --- 2. Steuer-Tageslichtsensor Rezept --- + ItemStack controlDaylight = new ItemStack(Material.DAYLIGHT_DETECTOR); + ItemMeta daylightMeta = controlDaylight.getItemMeta(); + if (daylightMeta != null) { daylightMeta.setDisplayName("§6Steuer-Tageslichtsensor"); controlDaylight.setItemMeta(daylightMeta); + } + NamespacedKey daylightKey = new NamespacedKey(this, "control_daylight"); + if (Bukkit.getRecipe(daylightKey) != null) Bukkit.removeRecipe(daylightKey); + ShapedRecipe daylightRecipe = new ShapedRecipe(daylightKey, controlDaylight); + daylightRecipe.shape("123", "456", "789"); + daylightRecipe.setIngredient('2', Material.DAYLIGHT_DETECTOR); + daylightRecipe.setIngredient('5', Material.DAYLIGHT_DETECTOR); + daylightRecipe.setIngredient('8', Material.DAYLIGHT_DETECTOR); + Bukkit.addRecipe(daylightRecipe); - NamespacedKey daylightKey = new NamespacedKey(this, "control_daylight"); - ShapedRecipe daylightRecipe = new ShapedRecipe(daylightKey, controlDaylight); - daylightRecipe.shape("123", "456", "789"); - daylightRecipe.setIngredient('2', Material.DAYLIGHT_DETECTOR); - daylightRecipe.setIngredient('5', Material.DAYLIGHT_DETECTOR); - daylightRecipe.setIngredient('8', Material.DAYLIGHT_DETECTOR); - Bukkit.addRecipe(daylightRecipe); - - ItemStack controlNoteBlock = new ItemStack(Material.NOTE_BLOCK); - ItemMeta noteBlockMeta = controlNoteBlock.getItemMeta(); + // --- 3. Steuer-Notenblock Rezept --- + ItemStack controlNoteBlock = new ItemStack(Material.NOTE_BLOCK); + ItemMeta noteBlockMeta = controlNoteBlock.getItemMeta(); + if (noteBlockMeta != null) { noteBlockMeta.setDisplayName("§6Steuer-Notenblock"); controlNoteBlock.setItemMeta(noteBlockMeta); + } + NamespacedKey noteBlockKey = new NamespacedKey(this, "control_noteblock"); + if (Bukkit.getRecipe(noteBlockKey) != null) Bukkit.removeRecipe(noteBlockKey); + ShapedRecipe noteBlockRecipe = new ShapedRecipe(noteBlockKey, controlNoteBlock); + noteBlockRecipe.shape("123", "456", "789"); + noteBlockRecipe.setIngredient('2', Material.NOTE_BLOCK); + noteBlockRecipe.setIngredient('5', Material.NOTE_BLOCK); + noteBlockRecipe.setIngredient('8', Material.NOTE_BLOCK); + Bukkit.addRecipe(noteBlockRecipe); - NamespacedKey noteBlockKey = new NamespacedKey(this, "control_noteblock"); - ShapedRecipe noteBlockRecipe = new ShapedRecipe(noteBlockKey, controlNoteBlock); - noteBlockRecipe.shape("123", "456", "789"); - noteBlockRecipe.setIngredient('2', Material.NOTE_BLOCK); - noteBlockRecipe.setIngredient('5', Material.NOTE_BLOCK); - noteBlockRecipe.setIngredient('8', Material.NOTE_BLOCK); - Bukkit.addRecipe(noteBlockRecipe); - - ItemStack controlMotion = new ItemStack(Material.TRIPWIRE_HOOK); - ItemMeta motionMeta = controlMotion.getItemMeta(); + // --- 4. Steuer-Bewegungsmelder Rezept --- + ItemStack controlMotion = new ItemStack(Material.TRIPWIRE_HOOK); + ItemMeta motionMeta = controlMotion.getItemMeta(); + if (motionMeta != null) { motionMeta.setDisplayName("§6Steuer-Bewegungsmelder"); controlMotion.setItemMeta(motionMeta); - - NamespacedKey motionKey = new NamespacedKey(this, "control_motion"); - ShapedRecipe motionRecipe = new ShapedRecipe(motionKey, controlMotion); - motionRecipe.shape("123", "456", "789"); - motionRecipe.setIngredient('2', Material.TRIPWIRE_HOOK); - motionRecipe.setIngredient('5', Material.TRIPWIRE_HOOK); - motionRecipe.setIngredient('8', Material.TRIPWIRE_HOOK); - Bukkit.addRecipe(motionRecipe); } + NamespacedKey motionKey = new NamespacedKey(this, "control_motion"); + if (Bukkit.getRecipe(motionKey) != null) Bukkit.removeRecipe(motionKey); + ShapedRecipe motionRecipe = new ShapedRecipe(motionKey, controlMotion); + motionRecipe.shape("123", "456", "789"); + motionRecipe.setIngredient('2', Material.TRIPWIRE_HOOK); + motionRecipe.setIngredient('5', Material.TRIPWIRE_HOOK); + motionRecipe.setIngredient('8', Material.TRIPWIRE_HOOK); + Bukkit.addRecipe(motionRecipe); +} public void checkDaylightSensors() { List allControllers = dataManager.getAllPlacedControllers(); @@ -182,16 +251,8 @@ public class ButtonControl extends JavaPlugin { String buttonId = dataManager.getButtonIdForPlacedController(controllerLoc); if (buttonId == null) continue; - String[] parts = controllerLoc.split(","); - if (parts.length != 4) continue; - - World world = getServer().getWorld(parts[0]); - if (world == null) continue; - - Location loc = new Location(world, - Integer.parseInt(parts[1]), - Integer.parseInt(parts[2]), - Integer.parseInt(parts[3])); + Location loc = parseLocation(controllerLoc); + if (loc == null) continue; Block block = loc.getBlock(); if (block.getType() != Material.DAYLIGHT_DETECTOR) continue; @@ -203,19 +264,10 @@ public class ButtonControl extends JavaPlugin { if (connectedBlocks == null) continue; for (String targetLocStr : connectedBlocks) { - String[] targetParts = targetLocStr.split(","); - if (targetParts.length != 4) continue; - - World targetWorld = getServer().getWorld(targetParts[0]); - if (targetWorld == null) continue; - - Location targetLoc = new Location(targetWorld, - Integer.parseInt(targetParts[1]), - Integer.parseInt(targetParts[2]), - Integer.parseInt(targetParts[3])); + Location targetLoc = parseLocation(targetLocStr); + if (targetLoc == null) continue; Block targetBlock = targetLoc.getBlock(); - if (targetBlock.getType() == Material.REDSTONE_LAMP) { Lightable lamp = (Lightable) targetBlock.getBlockData(); lamp.setLit(!isDay); @@ -229,16 +281,8 @@ public class ButtonControl extends JavaPlugin { long now = System.currentTimeMillis(); List allControllers = dataManager.getAllPlacedControllers(); for (String controllerLoc : allControllers) { - String[] parts = controllerLoc.split(","); - if (parts.length != 4) continue; - - World world = getServer().getWorld(parts[0]); - if (world == null) continue; - - Location loc = new Location(world, - Integer.parseInt(parts[1]), - Integer.parseInt(parts[2]), - Integer.parseInt(parts[3])); + Location loc = parseLocation(controllerLoc); + if (loc == null) continue; Block block = loc.getBlock(); if (block.getType() != Material.TRIPWIRE_HOOK) continue; @@ -246,15 +290,15 @@ public class ButtonControl extends JavaPlugin { String buttonId = dataManager.getButtonIdForPlacedController(controllerLoc); if (buttonId == null) continue; - // Individuelle Einstellungen für diesen Bewegungsmelder double radius = dataManager.getMotionSensorRadius(controllerLoc); if (radius == -1) radius = configManager.getConfig().getDouble("motion-detection-radius", 5.0); + long delay = dataManager.getMotionSensorDelay(controllerLoc); if (delay == -1) delay = configManager.getConfig().getLong("motion-close-delay-ms", 5000L); - boolean detected = !world.getNearbyEntities(loc, radius, radius, radius, e -> e instanceof Player).isEmpty(); - + boolean detected = !loc.getWorld().getNearbyEntities(loc, radius, radius, radius, e -> e instanceof Player).isEmpty(); List connectedBlocks = dataManager.getConnectedBlocks(buttonId); + if (connectedBlocks == null || connectedBlocks.isEmpty()) continue; if (detected) { @@ -272,19 +316,10 @@ public class ButtonControl extends JavaPlugin { private void setOpenables(List connectedBlocks, boolean open) { for (String targetLocStr : connectedBlocks) { - String[] targetParts = targetLocStr.split(","); - if (targetParts.length != 4) continue; - - World targetWorld = getServer().getWorld(targetParts[0]); - if (targetWorld == null) continue; - - Location targetLoc = new Location(targetWorld, - Integer.parseInt(targetParts[1]), - Integer.parseInt(targetParts[2]), - Integer.parseInt(targetParts[3])); + Location targetLoc = parseLocation(targetLocStr); + if (targetLoc == null) continue; Block targetBlock = targetLoc.getBlock(); - if (targetBlock.getBlockData() instanceof org.bukkit.block.data.Openable) { org.bukkit.block.data.Openable openable = (org.bukkit.block.data.Openable) targetBlock.getBlockData(); openable.setOpen(open); @@ -293,6 +328,18 @@ public class ButtonControl extends JavaPlugin { } } + private Location parseLocation(String locStr) { + String[] parts = locStr.split(","); + if (parts.length != 4) return null; + World world = getServer().getWorld(parts[0]); + if (world == null) return null; + try { + return new Location(world, Integer.parseInt(parts[1]), Integer.parseInt(parts[2]), Integer.parseInt(parts[3])); + } catch (NumberFormatException e) { + return null; + } + } + public void playDoorbellSound(Location loc, String instrument) { Block block = loc.getBlock(); if (block.getType() != Material.NOTE_BLOCK) return; @@ -321,58 +368,116 @@ public class ButtonControl extends JavaPlugin { @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - if (command.getName().equalsIgnoreCase("bc")) { - if (args.length == 0) { - sender.sendMessage("§6[ButtonControl] §7Verwende: /bc "); + if (!command.getName().equalsIgnoreCase("bc")) return false; + + if (args.length == 0) { + sender.sendMessage("§6[ButtonControl] §7Verwende: /bc "); + return true; + } + + // --- INFO BEFEHL --- + if (args[0].equalsIgnoreCase("info")) { + sender.sendMessage("§6§lButtonControl §7- v" + getDescription().getVersion()); + sender.sendMessage("§eAuthor: §fM_Viper"); + sender.sendMessage("§eFeatures: §fTüren, Lampen, Notenblöcke, Sensoren"); + return true; + } + + // --- RELOAD BEFEHL --- + if (args[0].equalsIgnoreCase("reload")) { + if (!sender.hasPermission("buttoncontrol.reload")) { + sender.sendMessage(configManager.getMessage("keine-berechtigung")); return true; } + configManager.reloadConfig(); + updateConfigWithDefaults(); + dataManager.reloadData(); + sender.sendMessage(configManager.getMessage("konfiguration-neugeladen")); + return true; + } - if (args[0].equalsIgnoreCase("info")) { - sender.sendMessage("§6[ButtonControl] §7Informationen zum Plugin:"); - sender.sendMessage("§eVersion: §f" + getDescription().getVersion()); - sender.sendMessage("§eErsteller: §fM_Viper"); - sender.sendMessage("§ePlugin: §fButtonControl"); - sender.sendMessage("§eGetestet für Minecraft: §f1.21.5 - 1.21.8"); - sender.sendMessage("§eWeitere Infos: §fTüren, Lampen & Notenblöcke mit Buttons oder Tageslichtsensoren steuern"); + // --- NOTE BEFEHL --- + if (args[0].equalsIgnoreCase("note") && sender instanceof Player) { + Player player = (Player) sender; + if (args.length < 2) { + player.sendMessage("§6[ButtonControl] §7Verwende: /bc note "); return true; } - - if (args[0].equalsIgnoreCase("reload")) { - if (!sender.hasPermission("buttoncontrol.reload")) { - sender.sendMessage(configManager.getMessage("keine-berechtigung")); - return true; - } - configManager.reloadConfig(); - updateConfigWithDefaults(); - dataManager.reloadData(); - sender.sendMessage(configManager.getMessage("konfiguration-reloaded")); - return true; + try { + org.bukkit.Instrument.valueOf(args[1].toUpperCase()); + dataManager.setPlayerInstrument(player.getUniqueId(), args[1].toUpperCase()); + player.sendMessage(String.format(configManager.getMessage("instrument-gesetzt"), args[1].toUpperCase())); + } catch (Exception e) { + player.sendMessage(configManager.getMessage("ungueltiges-instrument")); } + return true; + } - if (args[0].equalsIgnoreCase("note") && sender instanceof Player) { - Player player = (Player) sender; - if (!player.hasPermission("buttoncontrol.note")) { - player.sendMessage(configManager.getMessage("keine-berechtigung")); - return true; - } - if (args.length < 2) { - sender.sendMessage("§6[ButtonControl] §7Verwende: /bc note "); - sender.sendMessage("§7Verfügbare Instrumente: PIANO, BASS_DRUM, SNARE, STICKS, BASS_GUITAR, FLUTE, BELL, GUITAR, CHIME, XYLOPHONE, etc."); + // --- TRUST / PUBLIC / PRIVATE SYSTEM --- + if (sender instanceof Player) { + Player player = (Player) sender; + if (args[0].equalsIgnoreCase("trust") || args[0].equalsIgnoreCase("untrust") || + args[0].equalsIgnoreCase("public") || args[0].equalsIgnoreCase("private")) { + + Block target = player.getTargetBlockExact(5); + + // Erkennt nun Stein- und alle Holzbuttons sowie Sensoren + if (target == null || (!target.getType().name().endsWith("_BUTTON") && + target.getType() != Material.DAYLIGHT_DETECTOR && + target.getType() != Material.TRIPWIRE_HOOK)) { + + player.sendMessage(configManager.getMessage("kein-controller-im-blick")); return true; } - String instrument = args[1].toUpperCase(); - try { - org.bukkit.Instrument.valueOf(instrument); - dataManager.setPlayerInstrument(player.getUniqueId(), instrument); - sender.sendMessage(String.format(configManager.getMessage("instrument-gesetzt"), instrument)); - } catch (IllegalArgumentException e) { - sender.sendMessage(configManager.getMessage("ungueltiges-instrument")); + String targetLoc = target.getWorld().getName() + "," + target.getX() + "," + target.getY() + "," + target.getZ(); + String buttonId = dataManager.getButtonIdForLocation(targetLoc); + + if (buttonId == null) { + player.sendMessage(configManager.getMessage("keine-bloecke-verbunden")); + return true; + } + + if (!dataManager.isOwner(buttonId, player.getUniqueId())) { + player.sendMessage(configManager.getMessage("nur-besitzer-abbauen")); + return true; + } + + // Spieler hinzufügen + if (args[0].equalsIgnoreCase("trust")) { + if (args.length < 2) { + player.sendMessage("§6[ButtonControl] §7Verwende: /bc trust "); + return true; + } + Player targetPlayer = Bukkit.getPlayer(args[1]); + if (targetPlayer == null) { + player.sendMessage(configManager.getMessage("spieler-nicht-gefunden")); + return true; + } + dataManager.addTrustedPlayer(buttonId, targetPlayer.getUniqueId()); + player.sendMessage(String.format(configManager.getMessage("trust-hinzugefuegt"), targetPlayer.getName())); + } + // Spieler entfernen + else if (args[0].equalsIgnoreCase("untrust")) { + if (args.length < 2) { + player.sendMessage("§6[ButtonControl] §7Verwende: /bc untrust "); + return true; + } + UUID targetUUID = Bukkit.getOfflinePlayer(args[1]).getUniqueId(); + dataManager.removeTrustedPlayer(buttonId, targetUUID); + player.sendMessage(String.format(configManager.getMessage("trust-entfernt"), args[1])); + } + // Status umschalten (Public) oder erzwingen (Private) + else if (args[0].equalsIgnoreCase("public") || args[0].equalsIgnoreCase("private")) { + boolean newState = args[0].equalsIgnoreCase("public") ? !dataManager.isPublic(buttonId) : false; + dataManager.setPublic(buttonId, newState); + String statusColor = newState ? "§aÖffentlich" : "§cPrivat"; + player.sendMessage(String.format(configManager.getMessage("status-geandert"), statusColor)); } return true; } } - return false; + return true; } public ConfigManager getConfigManager() { diff --git a/src/main/java/viper/ButtonListener.java b/src/main/java/viper/ButtonListener.java index 2487dd4..9c2bb1c 100644 --- a/src/main/java/viper/ButtonListener.java +++ b/src/main/java/viper/ButtonListener.java @@ -1,10 +1,12 @@ package viper; +import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.data.Openable; import org.bukkit.block.data.Lightable; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; @@ -31,106 +33,44 @@ public class ButtonListener implements Listener { @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { - String playerUUID = event.getPlayer().getUniqueId().toString(); + Player player = event.getPlayer(); + UUID playerUUID = player.getUniqueId(); ItemStack item = event.getItem(); Block block = event.getClickedBlock(); - // Block wird gesteuert (Button, Tageslichtsensor oder Bewegungsmelder) + // 1. Logik für bereits platzierte Controller (Benutzung) if (event.getAction() == Action.RIGHT_CLICK_BLOCK && block != null) { String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ(); - String buttonId = dataManager.getButtonIdForPlacedController(playerUUID, blockLocation); + String buttonId = dataManager.getButtonIdForLocation(blockLocation); if (buttonId != null) { - event.setCancelled(true); - // Bewegungsmelder: GUI öffnen - if (block.getType() == Material.TRIPWIRE_HOOK) { - new MotionSensorGUI(plugin, event.getPlayer(), blockLocation, buttonId).open(); + if (!dataManager.canAccess(buttonId, playerUUID)) { + player.sendMessage(configManager.getMessage("keine-berechtigung-controller")); + event.setCancelled(true); return; } - // Button oder Tageslichtsensor: Normale Steuerung - if (block.getType() == Material.STONE_BUTTON || block.getType() == Material.DAYLIGHT_DETECTOR) { - List connectedBlocks = dataManager.getConnectedBlocks(playerUUID, buttonId); + + if (block.getType() == Material.TRIPWIRE_HOOK) { + event.setCancelled(true); + new MotionSensorGUI(plugin, player, blockLocation, buttonId).open(); + return; + } + + if (isButton(block.getType()) || block.getType() == Material.DAYLIGHT_DETECTOR) { + List connectedBlocks = dataManager.getConnectedBlocks(buttonId); + if (connectedBlocks != null && !connectedBlocks.isEmpty()) { - boolean anyDoorOpened = false; - boolean anyDoorClosed = false; - boolean anyGateOpened = false; - boolean anyGateClosed = false; - boolean anyTrapOpened = false; - boolean anyTrapClosed = false; - boolean anyLampOn = false; - boolean anyLampOff = false; - boolean anyNoteBlockPlayed = false; - boolean anyBellPlayed = false; - - for (String loc : connectedBlocks) { - String[] parts = loc.split(","); - Location location = new Location(plugin.getServer().getWorld(parts[0]), - Integer.parseInt(parts[1]), - Integer.parseInt(parts[2]), - Integer.parseInt(parts[3])); - Block targetBlock = location.getBlock(); - - if (isDoor(targetBlock.getType()) || isGate(targetBlock.getType()) || isTrapdoor(targetBlock.getType())) { - if (targetBlock.getBlockData() instanceof Openable) { - Openable openable = (Openable) targetBlock.getBlockData(); - boolean wasOpen = openable.isOpen(); - openable.setOpen(!wasOpen); - targetBlock.setBlockData(openable); - - if (isDoor(targetBlock.getType())) { - if (!wasOpen) anyDoorOpened = true; else anyDoorClosed = true; - } else if (isGate(targetBlock.getType())) { - if (!wasOpen) anyGateOpened = true; else anyGateClosed = true; - } else if (isTrapdoor(targetBlock.getType())) { - if (!wasOpen) anyTrapOpened = true; else anyTrapClosed = true; - } - } - } else if (targetBlock.getType() == Material.REDSTONE_LAMP) { - Lightable lamp = (Lightable) targetBlock.getBlockData(); - boolean wasLit = lamp.isLit(); - lamp.setLit(!wasLit); - targetBlock.setBlockData(lamp); - if (!wasLit) anyLampOn = true; else anyLampOff = true; - } else if (targetBlock.getType() == Material.NOTE_BLOCK) { - String instrument = dataManager.getPlayerInstrument(event.getPlayer().getUniqueId()); - if (instrument == null) { - instrument = configManager.getConfig().getString("default-note", "PIANO"); - } - plugin.playDoorbellSound(location, instrument); - anyNoteBlockPlayed = true; - } else if (targetBlock.getType() == Material.BELL) { - targetBlock.getWorld().playSound(location, org.bukkit.Sound.BLOCK_BELL_USE, 3.0f, 1.0f); - anyBellPlayed = true; - } - } - - if (anyDoorOpened) event.getPlayer().sendMessage(configManager.getMessage("tueren-geoeffnet")); - if (anyDoorClosed) event.getPlayer().sendMessage(configManager.getMessage("tueren-geschlossen")); - if (anyGateOpened) event.getPlayer().sendMessage(configManager.getMessage("gates-geoeffnet")); - if (anyGateClosed) event.getPlayer().sendMessage(configManager.getMessage("gates-geschlossen")); - if (anyTrapOpened) event.getPlayer().sendMessage(configManager.getMessage("fallturen-geoeffnet")); - if (anyTrapClosed) event.getPlayer().sendMessage(configManager.getMessage("fallturen-geschlossen")); - if (anyLampOn) event.getPlayer().sendMessage(configManager.getMessage("lampen-eingeschaltet")); - if (anyLampOff) event.getPlayer().sendMessage(configManager.getMessage("lampen-ausgeschaltet")); - if (anyNoteBlockPlayed) event.getPlayer().sendMessage(configManager.getMessage("notenblock-ausgeloest")); - if (anyBellPlayed) event.getPlayer().sendMessage(configManager.getMessage("glocke-gelaeutet")); + toggleConnectedBlocks(player, playerUUID, connectedBlocks); } else { - event.getPlayer().sendMessage(configManager.getMessage("keine-bloecke-verbunden")); + player.sendMessage(configManager.getMessage("keine-bloecke-verbunden")); } } return; } } - // Verbindung herstellen - if (item == null || (!item.getType().equals(Material.STONE_BUTTON) && - !item.getType().equals(Material.DAYLIGHT_DETECTOR) && - !item.getType().equals(Material.NOTE_BLOCK) && - !item.getType().equals(Material.TRIPWIRE_HOOK))) { - return; - } - - if (!item.hasItemMeta() || !item.getItemMeta().getDisplayName().contains("§6Steuer-")) { + // 2. Logik für das Verbinden von neuen Blöcken (Item in der Hand) + if (item == null || !item.hasItemMeta() || !item.getItemMeta().getDisplayName().contains("§6Steuer-")) { return; } @@ -138,139 +78,218 @@ public class ButtonListener implements Listener { return; } - if (isDoor(block.getType()) || isGate(block.getType()) || isTrapdoor(block.getType()) || - block.getType() == Material.REDSTONE_LAMP || - block.getType() == Material.NOTE_BLOCK || - block.getType() == Material.BELL) { - + if (isInteractableTarget(block.getType())) { event.setCancelled(true); - String buttonId = item.getItemMeta().hasLore() ? item.getItemMeta().getLore().get(0) : UUID.randomUUID().toString(); - List connectedBlocks = dataManager.getConnectedBlocks(playerUUID, buttonId); + ItemMeta meta = item.getItemMeta(); + String buttonId = null; + + // ID aus der Lore extrahieren (wir suchen nach dem Präfix §8ID: ) + if (meta != null && meta.hasLore() && !meta.getLore().isEmpty()) { + String firstLine = meta.getLore().get(0); + if (firstLine.startsWith("§8ID: ")) { + buttonId = firstLine.replace("§8ID: ", ""); + } + } + + // Falls keine ID existiert (neues Item), generieren und SOFORT speichern + if (buttonId == null) { + buttonId = UUID.randomUUID().toString(); + updateButtonLore(item, buttonId); + } + + List connectedBlocks = dataManager.getConnectedBlocks(buttonId); if (connectedBlocks == null) { connectedBlocks = new ArrayList<>(); } - int maxDoors = configManager.getMaxDoors(); - int maxGates = configManager.getMaxDoors(); - int maxTraps = configManager.getMaxDoors(); - int maxLamps = configManager.getMaxLamps(); - int maxNoteBlocks = configManager.getMaxNoteBlocks(); - int maxBells = configManager.getMaxBells(); - - int doorCount = (int) connectedBlocks.stream().filter(loc -> isDoor(getMaterialFromLocation(loc))).count(); - int gateCount = (int) connectedBlocks.stream().filter(loc -> isGate(getMaterialFromLocation(loc))).count(); - int trapCount = (int) connectedBlocks.stream().filter(loc -> isTrapdoor(getMaterialFromLocation(loc))).count(); - int lampCount = (int) connectedBlocks.stream().filter(loc -> getMaterialFromLocation(loc) == Material.REDSTONE_LAMP).count(); - int noteBlockCount = (int) connectedBlocks.stream().filter(loc -> getMaterialFromLocation(loc) == Material.NOTE_BLOCK).count(); - int bellCount = (int) connectedBlocks.stream().filter(loc -> getMaterialFromLocation(loc) == Material.BELL).count(); - - if (isDoor(block.getType()) && doorCount >= maxDoors) { - event.getPlayer().sendMessage(configManager.getMessage("max-tueren-erreicht")); - return; - } - if (isGate(block.getType()) && gateCount >= maxGates) { - event.getPlayer().sendMessage(configManager.getMessage("max-gates-erreicht")); - return; - } - if (isTrapdoor(block.getType()) && trapCount >= maxTraps) { - event.getPlayer().sendMessage(configManager.getMessage("max-fallturen-erreicht")); - return; - } - if (block.getType() == Material.REDSTONE_LAMP && lampCount >= maxLamps) { - event.getPlayer().sendMessage(configManager.getMessage("max-lampen-erreicht")); - return; - } - if (block.getType() == Material.NOTE_BLOCK && noteBlockCount >= maxNoteBlocks) { - event.getPlayer().sendMessage(configManager.getMessage("max-notenbloecke-erreicht")); - return; - } - if (block.getType() == Material.BELL && bellCount >= maxBells) { - event.getPlayer().sendMessage(configManager.getMessage("max-glocken-erreicht")); - return; - } - String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ(); - if (!connectedBlocks.contains(blockLocation)) { + if (connectedBlocks.contains(blockLocation)) { + player.sendMessage(configManager.getMessage("block-bereits-verbunden")); + return; + } + + if (checkLimits(player, block.getType(), connectedBlocks)) { connectedBlocks.add(blockLocation); - dataManager.setConnectedBlocks(playerUUID, buttonId, connectedBlocks); - updateButtonLore(item, buttonId); - event.getPlayer().sendMessage(configManager.getMessage("block-verbunden")); - } else { - event.getPlayer().sendMessage(configManager.getMessage("block-bereits-verbunden")); + dataManager.setConnectedBlocks(playerUUID.toString(), buttonId, connectedBlocks); + player.sendMessage(configManager.getMessage("block-verbunden")); } } } + private void toggleConnectedBlocks(Player player, UUID playerUUID, List connectedBlocks) { + boolean anyDoorOpened = false, anyDoorClosed = false; + boolean anyGateOpened = false, anyGateClosed = false; + boolean anyTrapOpened = false, anyTrapClosed = false; + boolean anyLampOn = false, anyLampOff = false; + boolean anyNoteBlockPlayed = false; + boolean anyBellPlayed = false; + + for (String locStr : connectedBlocks) { + Location location = parseLocation(locStr); + if (location == null) continue; + Block targetBlock = location.getBlock(); + + if (isDoor(targetBlock.getType()) || isGate(targetBlock.getType()) || isTrapdoor(targetBlock.getType())) { + if (targetBlock.getBlockData() instanceof Openable) { + Openable openable = (Openable) targetBlock.getBlockData(); + boolean wasOpen = openable.isOpen(); + openable.setOpen(!wasOpen); + targetBlock.setBlockData(openable); + + if (isDoor(targetBlock.getType())) { + if (!wasOpen) anyDoorOpened = true; else anyDoorClosed = true; + } else if (isGate(targetBlock.getType())) { + if (!wasOpen) anyGateOpened = true; else anyGateClosed = true; + } else if (isTrapdoor(targetBlock.getType())) { + if (!wasOpen) anyTrapOpened = true; else anyTrapClosed = true; + } + } + } + else if (targetBlock.getType() == Material.REDSTONE_LAMP) { + Lightable lamp = (Lightable) targetBlock.getBlockData(); + boolean wasLit = lamp.isLit(); + lamp.setLit(!wasLit); + targetBlock.setBlockData(lamp); + if (!wasLit) anyLampOn = true; else anyLampOff = true; + } + else if (targetBlock.getType() == Material.NOTE_BLOCK) { + String instrument = dataManager.getPlayerInstrument(playerUUID); + if (instrument == null) { + instrument = configManager.getConfig().getString("default-note", "PIANO"); + } + plugin.playDoorbellSound(location, instrument); + anyNoteBlockPlayed = true; + } + else if (targetBlock.getType() == Material.BELL) { + targetBlock.getWorld().playSound(location, org.bukkit.Sound.BLOCK_BELL_USE, 3.0f, 1.0f); + anyBellPlayed = true; + } + } + + if (anyDoorOpened) player.sendMessage(configManager.getMessage("tueren-geoeffnet")); + if (anyDoorClosed) player.sendMessage(configManager.getMessage("tueren-geschlossen")); + if (anyGateOpened) player.sendMessage(configManager.getMessage("gates-geoeffnet")); + if (anyGateClosed) player.sendMessage(configManager.getMessage("gates-geschlossen")); + if (anyTrapOpened) player.sendMessage(configManager.getMessage("fallturen-geoeffnet")); + if (anyTrapClosed) player.sendMessage(configManager.getMessage("fallturen-geschlossen")); + if (anyLampOn) player.sendMessage(configManager.getMessage("lampen-eingeschaltet")); + if (anyLampOff) player.sendMessage(configManager.getMessage("lampen-ausgeschaltet")); + if (anyNoteBlockPlayed) player.sendMessage(configManager.getMessage("notenblock-ausgeloest")); + if (anyBellPlayed) player.sendMessage(configManager.getMessage("glocke-gelaeutet")); + } + + private boolean checkLimits(Player player, Material type, List connected) { + if (isDoor(type)) { + if (connected.stream().filter(l -> isDoor(getMaterialFromLocation(l))).count() >= configManager.getMaxDoors()) { + player.sendMessage(configManager.getMessage("max-tueren-erreicht")); + return false; + } + } else if (isGate(type)) { + if (connected.stream().filter(l -> isGate(getMaterialFromLocation(l))).count() >= configManager.getMaxGates()) { + player.sendMessage(configManager.getMessage("max-gates-erreicht")); + return false; + } + } else if (isTrapdoor(type)) { + if (connected.stream().filter(l -> isTrapdoor(getMaterialFromLocation(l))).count() >= configManager.getMaxTrapdoors()) { + player.sendMessage(configManager.getMessage("max-fallturen-erreicht")); + return false; + } + } else if (type == Material.REDSTONE_LAMP) { + if (connected.stream().filter(l -> getMaterialFromLocation(l) == Material.REDSTONE_LAMP).count() >= configManager.getMaxLamps()) { + player.sendMessage(configManager.getMessage("max-lampen-erreicht")); + return false; + } + } else if (type == Material.NOTE_BLOCK) { + if (connected.stream().filter(l -> getMaterialFromLocation(l) == Material.NOTE_BLOCK).count() >= configManager.getMaxNoteBlocks()) { + player.sendMessage(configManager.getMessage("max-notenbloecke-erreicht")); + return false; + } + } else if (type == Material.BELL) { + if (connected.stream().filter(l -> getMaterialFromLocation(l) == Material.BELL).count() >= configManager.getMaxBells()) { + player.sendMessage(configManager.getMessage("max-glocken-erreicht")); + return false; + } + } + return true; + } + @EventHandler public void onBlockPlace(BlockPlaceEvent event) { - String playerUUID = event.getPlayer().getUniqueId().toString(); ItemStack item = event.getItemInHand(); + if (item == null || !item.hasItemMeta() || !item.getItemMeta().getDisplayName().contains("§6Steuer-")) { + return; + } + Block block = event.getBlockPlaced(); + ItemMeta meta = item.getItemMeta(); + String buttonId = null; - if (item == null || (!item.getType().equals(Material.STONE_BUTTON) && - !item.getType().equals(Material.DAYLIGHT_DETECTOR) && - !item.getType().equals(Material.NOTE_BLOCK) && - !item.getType().equals(Material.TRIPWIRE_HOOK))) { - return; + if (meta != null && meta.hasLore() && !meta.getLore().isEmpty()) { + String firstLine = meta.getLore().get(0); + if (firstLine.startsWith("§8ID: ")) { + buttonId = firstLine.replace("§8ID: ", ""); + } } - if (!item.hasItemMeta() || !item.getItemMeta().getDisplayName().contains("§6Steuer-")) { - return; + if (buttonId == null) { + buttonId = UUID.randomUUID().toString(); + updateButtonLore(item, buttonId); } - - String buttonId = item.getItemMeta().hasLore() ? item.getItemMeta().getLore().get(0) : UUID.randomUUID().toString(); + String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ(); - dataManager.addPlacedController(playerUUID, blockLocation, buttonId); + dataManager.registerController(blockLocation, event.getPlayer().getUniqueId(), buttonId); event.getPlayer().sendMessage(configManager.getMessage("controller-platziert")); } @EventHandler public void onBlockBreak(BlockBreakEvent event) { - String playerUUID = event.getPlayer().getUniqueId().toString(); Block block = event.getBlock(); String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ(); - String buttonId = dataManager.getButtonIdForPlacedController(playerUUID, blockLocation); + String buttonId = dataManager.getButtonIdForLocation(blockLocation); if (buttonId != null) { - dataManager.removePlacedController(playerUUID, blockLocation); - dataManager.setConnectedBlocks(playerUUID, buttonId, null); - dataManager.removeMotionSensorSettings(blockLocation); // Entferne Bewegungsmelder-Einstellungen + if (!dataManager.isOwner(buttonId, event.getPlayer().getUniqueId())) { + event.getPlayer().sendMessage(configManager.getMessage("nur-besitzer-abbauen")); + event.setCancelled(true); + return; + } + + dataManager.removeController(blockLocation); event.getPlayer().sendMessage(configManager.getMessage("controller-entfernt")); } } - private boolean isDoor(Material material) { - return material.toString().endsWith("_DOOR"); - } - - private boolean isGate(Material material) { - return material.toString().endsWith("_FENCE_GATE"); - } - - private boolean isTrapdoor(Material material) { - return material.toString().endsWith("_TRAPDOOR"); + private boolean isButton(Material m) { return m.name().endsWith("_BUTTON"); } + private boolean isDoor(Material m) { return m.name().endsWith("_DOOR"); } + private boolean isGate(Material m) { return m.name().endsWith("_FENCE_GATE"); } + private boolean isTrapdoor(Material m) { return m.name().endsWith("_TRAPDOOR"); } + + private boolean isInteractableTarget(Material m) { + return isDoor(m) || isGate(m) || isTrapdoor(m) || m == Material.REDSTONE_LAMP || m == Material.NOTE_BLOCK || m == Material.BELL; } private Material getMaterialFromLocation(String locString) { - String[] parts = locString.split(","); - if (parts.length != 4) return null; - Location loc = new Location(plugin.getServer().getWorld(parts[0]), - Integer.parseInt(parts[1]), - Integer.parseInt(parts[2]), - Integer.parseInt(parts[3])); - return loc.getBlock().getType(); + Location l = parseLocation(locString); + return l != null ? l.getBlock().getType() : Material.AIR; + } + + private Location parseLocation(String s) { + String[] p = s.split(","); + if (p.length != 4) return null; + try { + return new Location(Bukkit.getWorld(p[0]), Integer.parseInt(p[1]), Integer.parseInt(p[2]), Integer.parseInt(p[3])); + } catch (Exception e) { return null; } } private void updateButtonLore(ItemStack item, String buttonId) { ItemMeta meta = item.getItemMeta(); if (meta != null) { - List lore = meta.hasLore() ? meta.getLore() : new ArrayList<>(); - if (!lore.contains(buttonId)) { - lore.add(buttonId); - meta.setLore(lore); - item.setItemMeta(meta); - } + List lore = new ArrayList<>(); + lore.add("§8ID: " + buttonId); + lore.add("§7Ein universeller Controller für"); + meta.setLore(lore); + item.setItemMeta(meta); } } } \ No newline at end of file diff --git a/src/main/java/viper/ButtonTabCompleter.java b/src/main/java/viper/ButtonTabCompleter.java new file mode 100644 index 0000000..4aaa79d --- /dev/null +++ b/src/main/java/viper/ButtonTabCompleter.java @@ -0,0 +1,47 @@ +package viper; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class ButtonTabCompleter implements TabCompleter { + + private final List commands = Arrays.asList("info", "reload", "note", "trust", "untrust", "public", "private"); + private final List instruments = Arrays.asList( + "PIANO", "BASS_DRUM", "SNARE_DRUM", "STICKS", "BASS_GUITAR", + "FLUTE", "BELL", "CHIME", "GUITAR", "XYLOPHONE", + "IRON_XYLOPHONE", "COW_BELL", "DIDGERIDOO", "BIT", "BANJO", "PLING" + ); + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (!(sender instanceof Player)) return Collections.emptyList(); + + List completions = new ArrayList<>(); + + // Erste Ebene: /bc + if (args.length == 1) { + StringUtil.copyPartialMatches(args[0], commands, completions); + } + + // Zweite Ebene: /bc note + else if (args.length == 2 && args[0].equalsIgnoreCase("note")) { + StringUtil.copyPartialMatches(args[1], instruments, completions); + } + + // Zweite Ebene: /bc trust/untrust (Spielernamen vorschlagen) + else if (args.length == 2 && (args[0].equalsIgnoreCase("trust") || args[0].equalsIgnoreCase("untrust"))) { + return null; // 'null' lässt Bukkit automatisch alle Online-Spieler vorschlagen + } + + Collections.sort(completions); + return completions; + } +} \ No newline at end of file diff --git a/src/main/java/viper/DataManager.java b/src/main/java/viper/DataManager.java index 09d6058..528d99a 100644 --- a/src/main/java/viper/DataManager.java +++ b/src/main/java/viper/DataManager.java @@ -7,7 +7,6 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Set; import java.util.UUID; public class DataManager { @@ -32,69 +31,106 @@ public class DataManager { data = YamlConfiguration.loadConfiguration(dataFile); } - public List getConnectedBlocks(String playerUUID, String buttonId) { - return data.getStringList("players." + playerUUID + ".buttons." + buttonId); + public String getButtonIdForLocation(String location) { + return getButtonIdForPlacedController(location); } + public boolean canAccess(String buttonId, UUID playerUUID) { + if (isPublic(buttonId)) return true; + if (isOwner(buttonId, playerUUID)) return true; + List trusted = data.getStringList("trust." + buttonId); + return trusted.contains(playerUUID.toString()); + } + + public boolean isOwner(String buttonId, UUID playerUUID) { + // Wir prüfen direkt im globalen Pfad der Buttons + return data.contains("players." + playerUUID.toString() + ".buttons." + buttonId); + } + + public void registerController(String location, UUID ownerUUID, String buttonId) { + addPlacedController(ownerUUID.toString(), location, buttonId); + } + + public void removeController(String location) { + if (data.getConfigurationSection("players") == null) return; + for (String playerUUID : data.getConfigurationSection("players").getKeys(false)) { + String path = "players." + playerUUID + ".placed-controllers." + location; + if (data.contains(path)) { + data.set(path, null); + } + } + removeMotionSensorSettings(location); + saveData(); + } + + public void addTrustedPlayer(String buttonId, UUID targetUUID) { + List trusted = data.getStringList("trust." + buttonId); + if (!trusted.contains(targetUUID.toString())) { + trusted.add(targetUUID.toString()); + data.set("trust." + buttonId, trusted); + saveData(); + } + } + + public void removeTrustedPlayer(String buttonId, UUID targetUUID) { + List trusted = data.getStringList("trust." + buttonId); + trusted.remove(targetUUID.toString()); + data.set("trust." + buttonId, trusted); + saveData(); + } + + public void setPublic(String buttonId, boolean isPublic) { + data.set("public-status." + buttonId, isPublic); + saveData(); + } + + public boolean isPublic(String buttonId) { + return data.getBoolean("public-status." + buttonId, false); + } + + // Speichert die Blöcke für eine ID unter einem spezifischen Spieler public void setConnectedBlocks(String playerUUID, String buttonId, List blocks) { data.set("players." + playerUUID + ".buttons." + buttonId, blocks); saveData(); } public void addPlacedController(String playerUUID, String location, String buttonId) { + // Verhindert doppelte Punkte im Pfad, falls die Location Punkte enthält data.set("players." + playerUUID + ".placed-controllers." + location, buttonId); saveData(); } - public String getButtonIdForPlacedController(String playerUUID, String location) { - return data.getString("players." + playerUUID + ".placed-controllers." + location); - } - - public void removePlacedController(String playerUUID, String location) { - data.set("players." + playerUUID + ".placed-controllers." + location, null); - saveData(); - } - - public List getAllPlacedControllers(String playerUUID) { - if (data.getConfigurationSection("players." + playerUUID + ".placed-controllers") == null) { - return new ArrayList<>(); - } - Set keys = data.getConfigurationSection("players." + playerUUID + ".placed-controllers").getKeys(false); - return new ArrayList<>(keys); - } - - public List getAllPlacedControllers() { - List allControllers = new ArrayList<>(); - if (data.getConfigurationSection("players") == null) { - return allControllers; - } - Set players = data.getConfigurationSection("players").getKeys(false); - for (String playerUUID : players) { - allControllers.addAll(getAllPlacedControllers(playerUUID)); - } - return allControllers; - } - public String getButtonIdForPlacedController(String location) { if (data.getConfigurationSection("players") == null) return null; - Set players = data.getConfigurationSection("players").getKeys(false); - for (String playerUUID : players) { - String buttonId = getButtonIdForPlacedController(playerUUID, location); + for (String playerUUID : data.getConfigurationSection("players").getKeys(false)) { + String buttonId = data.getString("players." + playerUUID + ".placed-controllers." + location); if (buttonId != null) return buttonId; } return null; } + // VERBESSERT: Sucht gezielt nach den Blöcken für diese ID public List getConnectedBlocks(String buttonId) { - if (data.getConfigurationSection("players") == null) return null; - Set players = data.getConfigurationSection("players").getKeys(false); - for (String playerUUID : players) { - List connected = getConnectedBlocks(playerUUID, buttonId); - if (connected != null && !connected.isEmpty()) { - return connected; + if (data.getConfigurationSection("players") == null) return new ArrayList<>(); + + for (String playerUUID : data.getConfigurationSection("players").getKeys(false)) { + String path = "players." + playerUUID + ".buttons." + buttonId; + if (data.contains(path)) { + return data.getStringList(path); } } - return null; + return new ArrayList<>(); // Niemals null zurückgeben, um Fehler im Listener zu vermeiden + } + + public List getAllPlacedControllers() { + List allControllers = new ArrayList<>(); + if (data.getConfigurationSection("players") == null) return allControllers; + for (String playerUUID : data.getConfigurationSection("players").getKeys(false)) { + if (data.getConfigurationSection("players." + playerUUID + ".placed-controllers") != null) { + allControllers.addAll(data.getConfigurationSection("players." + playerUUID + ".placed-controllers").getKeys(false)); + } + } + return allControllers; } public void setPlayerInstrument(UUID playerUUID, String instrument) { @@ -106,7 +142,6 @@ public class DataManager { return data.getString("players." + playerUUID.toString() + ".instrument"); } - // Bewegungsmelder-Einstellungen public void setMotionSensorRadius(String location, double radius) { data.set("motion-sensors." + location + ".radius", radius); saveData(); diff --git a/src/main/resources/lang.yml b/src/main/resources/lang.yml index 282c5dd..e9509b4 100644 --- a/src/main/resources/lang.yml +++ b/src/main/resources/lang.yml @@ -1,5 +1,9 @@ +# lang.yml - ButtonControl Nachrichten + +# --- Bestehende Nachrichten (Türen/Tore/Falltüren) --- tueren-geoeffnet: "§aTüren wurden geöffnet." tueren-geschlossen: "§cTüren wurden geschlossen." +max-tueren-erreicht: "§cMaximale Anzahl an Türen erreicht." gates-geoeffnet: "§aZauntore wurden geöffnet." gates-geschlossen: "§cZauntore wurden geschlossen." @@ -9,31 +13,45 @@ fallturen-geoeffnet: "§aFalltüren wurden geöffnet." fallturen-geschlossen: "§cFalltüren wurden geschlossen." max-fallturen-erreicht: "§cMaximale Anzahl an Falltüren erreicht." +# --- Lampen & Glocken --- lampen-eingeschaltet: "§aLampen wurden eingeschaltet." lampen-ausgeschaltet: "§cLampen wurden ausgeschaltet." - -bloecke-umgeschaltet: "§eBlöcke wurden umgeschaltet." -keine-bloecke-verbunden: "§cKeine Blöcke sind verbunden." - -max-tueren-erreicht: "§cMaximale Anzahl an Türen erreicht." max-lampen-erreicht: "§cMaximale Anzahl an Lampen erreicht." -max-notenbloecke-erreicht: "§cMaximale Anzahl an Notenblöcken erreicht." - -max-glocken-erreicht: "§cMaximale Anzahl an Glocken erreicht." glocke-gelaeutet: "§aGlocke wurde geläutet." +max-glocken-erreicht: "§cMaximale Anzahl an Glocken erreicht." -block-verbunden: "§aBlock verbunden." -block-bereits-verbunden: "§cBlock ist bereits verbunden." -controller-platziert: "§aController platziert." -controller-entfernt: "§cController entfernt." - +# --- Notenblöcke & Instrumente --- notenblock-ausgeloest: "§aNotenblock-Klingel wurde ausgelöst." instrument-gesetzt: "§aDein Notenblock-Instrument wurde auf %s gesetzt." ungueltiges-instrument: "§cUngültiges Instrument! Verwende: /bc note " +max-notenbloecke-erreicht: "§cMaximale Anzahl an Notenblöcken erreicht." -konfiguration-neugeladen: "§aKonfiguration und Daten erfolgreich neu geladen!" -keine-berechtigung: "§cDu hast keine Berechtigung für diesen Befehl!" - +# --- Kolben (Erweiterung) --- kolben-ausgefahren: "§6[ButtonControl] §7Kolben wurden ausgefahren." kolben-eingefahren: "§6[ButtonControl] §7Kolben wurden eingefahren." max-kolben-erreicht: "§6[ButtonControl] §7Maximale Anzahl an Kolben erreicht." + +# --- Controller & Verbindung --- +block-verbunden: "§aBlock verbunden." +block-bereits-verbunden: "§cBlock ist bereits verbunden." +keine-bloecke-verbunden: "§cKeine Blöcke sind verbunden." +bloecke-umgeschaltet: "§eBlöcke wurden umgeschaltet." +controller-platziert: "§aController platziert." +controller-entfernt: "§cController entfernt." + +# --- Trust- & Sicherheitssystem --- +keine-berechtigung: "§cDu hast keine Berechtigung für diesen Befehl!" +keine-berechtigung-controller: "§cDu hast keine Berechtigung, diesen Controller zu benutzen!" +nur-besitzer-abbauen: "§cNur der Besitzer darf diesen Controller verwalten oder abbauen!" +spieler-nicht-gefunden: "§cDieser Spieler wurde nicht gefunden." + +# %s wird durch "§aÖffentlich" oder "§cPrivat" ersetzt +status-geandert: "§6[ButtonControl] §7Der Controller ist nun %s§7." + +# Trust Nachrichten +trust-hinzugefuegt: "§aDu hast %s Vertrauen für diesen Controller gegeben." +trust-entfernt: "§cDu hast %s das Vertrauen für diesen Controller entzogen." +kein-controller-im-blick: "§cDu musst einen Controller (Button, Haken oder Sensor) direkt ansehen!" + +# --- System --- +konfiguration-neugeladen: "§aKonfiguration und Daten erfolgreich neu geladen!" \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a07a37b..e5b2323 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,14 +1,14 @@ name: ButtonControl -version: 1.4 +version: 1.5 main: viper.ButtonControl -api-version: 1.18 -author: viper -description: Ein Plugin, um Türen, Redstone-Lampen und Notenblöcke mit einem Button oder Tageslichtsensor zu steuern. +api-version: 1.21 +author: M_Viper +description: Ein Plugin, um Türen, Redstone-Lampen und Notenblöcke mit einem Button, Tageslichtsensor oder Bewegungsmelder zu steuern. commands: bc: - description: Steuert das ButtonControl Plugin - usage: / [info|reload|note ] + description: Hauptbefehl für ButtonControl + usage: /bc aliases: [buttoncontrol] permissions: @@ -17,4 +17,10 @@ permissions: default: op buttoncontrol.note: description: Erlaubt das Ändern des Notenblock-Instruments + default: true + buttoncontrol.update: + description: Erlaubt das Empfangen von Update-Benachrichtigungen + default: op + buttoncontrol.trust: + description: Erlaubt das Verwalten von Vertrauen und Status (Public/Private) default: true \ No newline at end of file