Update from Git Manager GUI
This commit is contained in:
@@ -17,129 +17,108 @@ import org.bukkit.inventory.meta.ItemMeta;
|
|||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.bukkit.Note;
|
import org.bukkit.Note;
|
||||||
import org.bukkit.Note.Tone;
|
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.ArrayList;
|
||||||
import java.util.UUID;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class ButtonControl extends JavaPlugin {
|
public class ButtonControl extends JavaPlugin {
|
||||||
private ConfigManager configManager;
|
private ConfigManager configManager;
|
||||||
private DataManager dataManager;
|
private DataManager dataManager;
|
||||||
private Map<String, Long> lastMotionDetections = new HashMap<>();
|
|
||||||
|
// Bewegungsmelder-State
|
||||||
|
private final Map<String, Long> lastMotionDetections = new HashMap<>();
|
||||||
|
private final Set<String> activeSensors = new HashSet<>();
|
||||||
|
|
||||||
|
// Zeitgesteuerte Automation – verhindert mehrfaches Auslösen pro Zustandswechsel
|
||||||
|
private final Map<String, Boolean> timedControllerLastState = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
// Initialisierung der Manager
|
|
||||||
configManager = new ConfigManager(this);
|
configManager = new ConfigManager(this);
|
||||||
dataManager = new DataManager(this);
|
dataManager = new DataManager(this);
|
||||||
|
|
||||||
// Spigot Update Checker beim Serverstart ausführen
|
// Update-Checker beim Start
|
||||||
new UpdateChecker(this, 127702).getVersion(version -> {
|
new UpdateChecker(this, 127702).getVersion(version -> {
|
||||||
String currentVersion = this.getDescription().getVersion();
|
String current = getDescription().getVersion();
|
||||||
String normalizedLatest = version.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim();
|
if (isNewerVersion(strip(version), strip(current))) {
|
||||||
String normalizedCurrent = currentVersion.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim();
|
|
||||||
|
|
||||||
if (isNewerVersion(normalizedLatest, normalizedCurrent)) {
|
|
||||||
// Konsole bleibt sachlich
|
|
||||||
getLogger().info("Update verfügbar: v" + version);
|
getLogger().info("Update verfügbar: v" + version);
|
||||||
|
Bukkit.getScheduler().runTask(this, () ->
|
||||||
// Schicke die stylische Nachricht an Admins
|
|
||||||
Bukkit.getScheduler().runTask(this, () -> {
|
|
||||||
Bukkit.getOnlinePlayers().stream()
|
Bukkit.getOnlinePlayers().stream()
|
||||||
.filter(p -> p.hasPermission("buttoncontrol.update"))
|
.filter(p -> p.hasPermission("buttoncontrol.update"))
|
||||||
.forEach(p -> {
|
.forEach(p -> sendUpdateMessage(p, current, version)));
|
||||||
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 {
|
} else {
|
||||||
getLogger().info("ButtonControl ist auf dem neuesten Stand (v" + currentVersion + ").");
|
getLogger().info("ButtonControl ist auf dem neuesten Stand (v" + current + ").");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listener für Spieler-Joins (Update-Benachrichtigung beim Betreten des Servers)
|
// Update beim Joinen
|
||||||
getServer().getPluginManager().registerEvents(new org.bukkit.event.Listener() {
|
getServer().getPluginManager().registerEvents(new org.bukkit.event.Listener() {
|
||||||
@org.bukkit.event.EventHandler
|
@org.bukkit.event.EventHandler
|
||||||
public void onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent event) {
|
public void onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent event) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
if (!player.hasPermission("buttoncontrol.update")) return;
|
if (!player.hasPermission("buttoncontrol.update")) return;
|
||||||
|
|
||||||
new UpdateChecker(ButtonControl.this, 127702).getVersion(version -> {
|
new UpdateChecker(ButtonControl.this, 127702).getVersion(version -> {
|
||||||
String currentVersion = getDescription().getVersion();
|
String current = getDescription().getVersion();
|
||||||
String normalizedLatest = version.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim();
|
if (isNewerVersion(strip(version), strip(current)))
|
||||||
String normalizedCurrent = currentVersion.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim();
|
sendUpdateMessage(player, current, version);
|
||||||
|
|
||||||
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);
|
}, this);
|
||||||
|
|
||||||
// Konfiguration und Spielmechaniken laden
|
|
||||||
updateConfigWithDefaults();
|
|
||||||
|
|
||||||
// --- COMMANDS & TAB-COMPLETER ---
|
|
||||||
// Registriert den Executor (Befehlsverarbeitung) und den TabCompleter (Vorschläge)
|
|
||||||
if (getCommand("bc") != null) {
|
if (getCommand("bc") != null) {
|
||||||
getCommand("bc").setExecutor(this); // 'this' setzt voraus, dass ButtonControl 'onCommand' enthält
|
getCommand("bc").setExecutor(this);
|
||||||
getCommand("bc").setTabCompleter(new ButtonTabCompleter());
|
getCommand("bc").setTabCompleter(new ButtonTabCompleter());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event-Listener registrieren
|
getServer().getPluginManager().registerEvents(
|
||||||
getServer().getPluginManager().registerEvents(new ButtonListener(this, configManager, dataManager), this);
|
new ButtonListener(this, configManager, dataManager), this);
|
||||||
|
|
||||||
// Rezepte und bStats/Metrics
|
|
||||||
registerRecipes();
|
registerRecipes();
|
||||||
MetricsHandler.startMetrics(this);
|
MetricsHandler.startMetrics(this);
|
||||||
|
|
||||||
// --- AUTOMATISIERUNG-TIMER ---
|
|
||||||
// Daylight Sensoren: Prüfung alle 200 Ticks (10 Sekunden)
|
|
||||||
getServer().getScheduler().runTaskTimer(this, this::checkDaylightSensors, 0L, 20L * 10);
|
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);
|
getServer().getScheduler().runTaskTimer(this, this::checkMotionSensors, 0L, 10L);
|
||||||
|
getServer().getScheduler().runTaskTimer(this, this::checkTimedControllers, 0L, 20L * 5);
|
||||||
|
|
||||||
getLogger().info("ButtonControl v" + getDescription().getVersion() + " wurde erfolgreich aktiviert!");
|
getLogger().info("ButtonControl v" + getDescription().getVersion() + " wurde erfolgreich aktiviert!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Update-Hilfe
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
private String strip(String v) {
|
||||||
|
return v.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendUpdateMessage(Player player, String current, String latest) {
|
||||||
|
player.sendMessage("");
|
||||||
|
player.sendMessage("§8§m-----------------------------------------");
|
||||||
|
player.sendMessage(" §6§lButtonControl §7- §e§lUpdate verfügbar!");
|
||||||
|
player.sendMessage("");
|
||||||
|
player.sendMessage(" §7Aktuelle Version: §c" + current);
|
||||||
|
player.sendMessage(" §7Neue Version: §a" + latest);
|
||||||
|
player.sendMessage("");
|
||||||
|
player.sendMessage(" §eDownload: §bhttps://www.spigotmc.org/resources/127702/");
|
||||||
|
player.sendMessage("§8§m-----------------------------------------");
|
||||||
|
player.sendMessage("");
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isNewerVersion(String latest, String current) {
|
private boolean isNewerVersion(String latest, String current) {
|
||||||
try {
|
try {
|
||||||
String[] latestParts = latest.split("\\.");
|
String[] lp = latest.split("\\.");
|
||||||
String[] currentParts = current.split("\\.");
|
String[] cp = current.split("\\.");
|
||||||
int length = Math.max(latestParts.length, currentParts.length);
|
int len = Math.max(lp.length, cp.length);
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
for (int i = 0; i < length; i++) {
|
int l = i < lp.length ? Integer.parseInt(lp[i]) : 0;
|
||||||
int latestPart = (i < latestParts.length) ? Integer.parseInt(latestParts[i]) : 0;
|
int c = i < cp.length ? Integer.parseInt(cp[i]) : 0;
|
||||||
int currentPart = (i < currentParts.length) ? Integer.parseInt(currentParts[i]) : 0;
|
if (l > c) return true;
|
||||||
if (latestPart > currentPart) return true;
|
if (l < c) return false;
|
||||||
if (latestPart < currentPart) return false;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
@@ -147,262 +126,267 @@ public class ButtonControl extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateConfigWithDefaults() {
|
// -----------------------------------------------------------------------
|
||||||
if (!configManager.getConfig().contains("default-note")) {
|
// Rezepte
|
||||||
configManager.getConfig().set("default-note", "PIANO");
|
// -----------------------------------------------------------------------
|
||||||
}
|
|
||||||
if (!configManager.getConfig().contains("double-note-enabled")) {
|
|
||||||
configManager.getConfig().set("double-note-enabled", true);
|
|
||||||
}
|
|
||||||
if (!configManager.getConfig().contains("double-note-delay-ms")) {
|
|
||||||
configManager.getConfig().set("double-note-delay-ms", 1000);
|
|
||||||
}
|
|
||||||
if (!configManager.getConfig().contains("motion-detection-radius")) {
|
|
||||||
configManager.getConfig().set("motion-detection-radius", 5.0);
|
|
||||||
}
|
|
||||||
if (!configManager.getConfig().contains("motion-close-delay-ms")) {
|
|
||||||
configManager.getConfig().set("motion-close-delay-ms", 5000);
|
|
||||||
}
|
|
||||||
configManager.saveConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerRecipes() {
|
private void registerRecipes() {
|
||||||
// --- 1. Dynamische Steuer-Buttons für JEDE Holz/Stein-Art ---
|
// Alle Holz/Stein-Buttons
|
||||||
for (Material mat : Material.values()) {
|
for (Material mat : Material.values()) {
|
||||||
if (mat.name().endsWith("_BUTTON")) {
|
if (!mat.name().endsWith("_BUTTON")) continue;
|
||||||
// Wir erstellen für jeden Button-Typ ein eigenes Ergebnis
|
registerColumnRecipe(
|
||||||
ItemStack controlButton = new ItemStack(mat);
|
"control_" + mat.name().toLowerCase(), mat,
|
||||||
ItemMeta buttonMeta = controlButton.getItemMeta();
|
"§6Steuer-" + friendlyName(mat, "_BUTTON"),
|
||||||
if (buttonMeta != null) {
|
Arrays.asList("§7Ein universeller Controller.",
|
||||||
buttonMeta.setDisplayName("§6Steuer-Button");
|
"§7Verbindet Türen, Lampen und mehr.")
|
||||||
List<String> lore = new ArrayList<>();
|
);
|
||||||
lore.add("§7Ein universeller Controller für");
|
|
||||||
lore.add("§7Türen, Lampen und mehr.");
|
|
||||||
buttonMeta.setLore(lore);
|
|
||||||
controlButton.setItemMeta(buttonMeta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wir brauchen für jedes Material einen eindeutigen Key (z.B. control_button_oak_button)
|
// Tageslichtsensor
|
||||||
NamespacedKey key = new NamespacedKey(this, "control_" + mat.name().toLowerCase());
|
registerColumnRecipe("control_daylight", Material.DAYLIGHT_DETECTOR,
|
||||||
if (Bukkit.getRecipe(key) != null) Bukkit.removeRecipe(key);
|
"§6Steuer-Tageslichtsensor",
|
||||||
|
Arrays.asList("§7Öffnet/schließt nach Tageszeit."));
|
||||||
|
|
||||||
ShapedRecipe recipe = new ShapedRecipe(key, controlButton);
|
// Notenblock
|
||||||
recipe.shape("123", "456", "789");
|
registerColumnRecipe("control_noteblock", Material.NOTE_BLOCK,
|
||||||
// Das Material muss an allen drei Stellen gleich sein (z.B. 3x Eiche)
|
"§6Steuer-Notenblock",
|
||||||
recipe.setIngredient('2', mat);
|
Arrays.asList("§7Spielt einen Klingelton ab."));
|
||||||
recipe.setIngredient('5', mat);
|
|
||||||
recipe.setIngredient('8', mat);
|
// Bewegungsmelder
|
||||||
|
registerColumnRecipe("control_motion", Material.TRIPWIRE_HOOK,
|
||||||
|
"§6Steuer-Bewegungsmelder",
|
||||||
|
Arrays.asList("§7Erkennt Spieler und Mobs in der Nähe."));
|
||||||
|
|
||||||
|
// NEU: Schild-Controller
|
||||||
|
registerColumnRecipe("control_sign", Material.OAK_SIGN,
|
||||||
|
"§6Steuer-Schild",
|
||||||
|
Arrays.asList("§7Wandmontierbarer Controller.",
|
||||||
|
"§7Funktioniert wie ein Button."));
|
||||||
|
|
||||||
|
// NEU: Teppich-Sensoren (alle 16 Farben) – NUR Spieler
|
||||||
|
for (Material mat : Material.values()) {
|
||||||
|
if (!mat.name().endsWith("_CARPET")) continue;
|
||||||
|
registerColumnRecipe(
|
||||||
|
"control_carpet_" + mat.name().toLowerCase(), mat,
|
||||||
|
"§6Steuer-Teppich §8(" + friendlyName(mat, "_CARPET") + "§8)",
|
||||||
|
Arrays.asList("§7Erkennt NUR Spieler (keine Mobs).",
|
||||||
|
"§7Bodenbasierter Bewegungsmelder.")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerColumnRecipe(String keyName, Material mat, String displayName, List<String> lore) {
|
||||||
|
ItemStack item = new ItemStack(mat);
|
||||||
|
ItemMeta meta = item.getItemMeta();
|
||||||
|
if (meta != null) {
|
||||||
|
meta.setDisplayName(displayName);
|
||||||
|
meta.setLore(new ArrayList<>(lore));
|
||||||
|
item.setItemMeta(meta);
|
||||||
|
}
|
||||||
|
NamespacedKey key = new NamespacedKey(this, keyName);
|
||||||
|
if (Bukkit.getRecipe(key) != null) Bukkit.removeRecipe(key);
|
||||||
|
ShapedRecipe recipe = new ShapedRecipe(key, item);
|
||||||
|
recipe.shape(" X ", " X ", " X ");
|
||||||
|
recipe.setIngredient('X', mat);
|
||||||
Bukkit.addRecipe(recipe);
|
Bukkit.addRecipe(recipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** IRON_DOOR → "§7Iron Door" | OAK_BUTTON → "§7Oak Button" */
|
||||||
|
String friendlyName(Material mat, String stripSuffix) {
|
||||||
|
String[] parts = mat.name().replace(stripSuffix, "").split("_");
|
||||||
|
StringBuilder sb = new StringBuilder("§7");
|
||||||
|
for (String p : parts) {
|
||||||
|
if (sb.length() > 2) sb.append(" ");
|
||||||
|
sb.append(Character.toUpperCase(p.charAt(0)))
|
||||||
|
.append(p.substring(1).toLowerCase());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 2. Steuer-Tageslichtsensor Rezept ---
|
// -----------------------------------------------------------------------
|
||||||
ItemStack controlDaylight = new ItemStack(Material.DAYLIGHT_DETECTOR);
|
// Tageslichtsensor
|
||||||
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);
|
|
||||||
|
|
||||||
// --- 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);
|
|
||||||
|
|
||||||
// --- 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");
|
|
||||||
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() {
|
public void checkDaylightSensors() {
|
||||||
List<String> allControllers = dataManager.getAllPlacedControllers();
|
for (String loc : dataManager.getAllPlacedControllers()) {
|
||||||
for (String controllerLoc : allControllers) {
|
String buttonId = dataManager.getButtonIdForPlacedController(loc);
|
||||||
|
if (buttonId == null) continue;
|
||||||
|
Location location = parseLocation(loc);
|
||||||
|
if (location == null) continue;
|
||||||
|
if (location.getBlock().getType() != Material.DAYLIGHT_DETECTOR) continue;
|
||||||
|
|
||||||
|
long time = location.getWorld().getTime();
|
||||||
|
boolean isDay = time >= 0 && time < 13000;
|
||||||
|
List<String> connected = dataManager.getConnectedBlocks(buttonId);
|
||||||
|
if (connected == null) continue;
|
||||||
|
|
||||||
|
for (String ts : connected) {
|
||||||
|
Location tl = parseLocation(ts);
|
||||||
|
if (tl == null) continue;
|
||||||
|
Block tb = tl.getBlock();
|
||||||
|
if (tb.getType() == Material.REDSTONE_LAMP) {
|
||||||
|
Lightable lamp = (Lightable) tb.getBlockData();
|
||||||
|
lamp.setLit(!isDay);
|
||||||
|
tb.setBlockData(lamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Zeitgesteuerte Automation (NEU)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft alle 5 Sekunden ob ein Zeitplan (open-time / close-time) für einen Controller
|
||||||
|
* aktiv ist und öffnet/schließt die verbundenen Blöcke bei Wechsel.
|
||||||
|
*
|
||||||
|
* Ingame-Zeit: 0 = Sonnenaufgang, 6000 = Mittag, 13000 = Sonnenuntergang, 18000 = Mitternacht
|
||||||
|
* Anzeige: ticksToTime() wandelt in "HH:MM" um (Tag beginnt um 06:00).
|
||||||
|
*/
|
||||||
|
public void checkTimedControllers() {
|
||||||
|
for (String controllerLoc : dataManager.getAllPlacedControllers()) {
|
||||||
String buttonId = dataManager.getButtonIdForPlacedController(controllerLoc);
|
String buttonId = dataManager.getButtonIdForPlacedController(controllerLoc);
|
||||||
if (buttonId == null) continue;
|
if (buttonId == null) continue;
|
||||||
|
|
||||||
|
long openTime = dataManager.getScheduleOpenTime(buttonId);
|
||||||
|
long closeTime = dataManager.getScheduleCloseTime(buttonId);
|
||||||
|
if (openTime < 0 || closeTime < 0) continue;
|
||||||
|
|
||||||
Location loc = parseLocation(controllerLoc);
|
Location loc = parseLocation(controllerLoc);
|
||||||
if (loc == null) continue;
|
if (loc == null) continue;
|
||||||
|
|
||||||
Block block = loc.getBlock();
|
long worldTime = loc.getWorld().getTime() % 24000;
|
||||||
if (block.getType() != Material.DAYLIGHT_DETECTOR) continue;
|
boolean shouldBeOpen;
|
||||||
|
|
||||||
long time = loc.getWorld().getTime();
|
if (openTime <= closeTime) {
|
||||||
boolean isDay = time >= 0 && time < 13000;
|
// Normales Intervall: z.B. öffnen 6000, schließen 18000
|
||||||
|
shouldBeOpen = worldTime >= openTime && worldTime < closeTime;
|
||||||
|
} else {
|
||||||
|
// Über Mitternacht: z.B. öffnen 20000, schließen 4000
|
||||||
|
shouldBeOpen = worldTime >= openTime || worldTime < closeTime;
|
||||||
|
}
|
||||||
|
|
||||||
List<String> connectedBlocks = dataManager.getConnectedBlocks(buttonId);
|
Boolean lastState = timedControllerLastState.get(controllerLoc);
|
||||||
if (connectedBlocks == null) continue;
|
if (lastState != null && lastState == shouldBeOpen) continue;
|
||||||
|
|
||||||
for (String targetLocStr : connectedBlocks) {
|
timedControllerLastState.put(controllerLoc, shouldBeOpen);
|
||||||
Location targetLoc = parseLocation(targetLocStr);
|
List<String> connected = dataManager.getConnectedBlocks(buttonId);
|
||||||
if (targetLoc == null) continue;
|
if (connected != null && !connected.isEmpty()) {
|
||||||
|
setOpenables(connected, shouldBeOpen);
|
||||||
Block targetBlock = targetLoc.getBlock();
|
|
||||||
if (targetBlock.getType() == Material.REDSTONE_LAMP) {
|
|
||||||
Lightable lamp = (Lightable) targetBlock.getBlockData();
|
|
||||||
lamp.setLit(!isDay);
|
|
||||||
targetBlock.setBlockData(lamp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Bewegungsmelder
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
public void checkMotionSensors() {
|
public void checkMotionSensors() {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
List<String> allControllers = dataManager.getAllPlacedControllers();
|
|
||||||
for (String controllerLoc : allControllers) {
|
for (String controllerLoc : dataManager.getAllPlacedControllers()) {
|
||||||
Location loc = parseLocation(controllerLoc);
|
Location loc = parseLocation(controllerLoc);
|
||||||
if (loc == null) continue;
|
if (loc == null) continue;
|
||||||
|
Material bType = loc.getBlock().getType();
|
||||||
|
|
||||||
Block block = loc.getBlock();
|
boolean isTripwire = bType == Material.TRIPWIRE_HOOK;
|
||||||
if (block.getType() != Material.TRIPWIRE_HOOK) continue;
|
boolean isCarpet = bType.name().endsWith("_CARPET");
|
||||||
|
if (!isTripwire && !isCarpet) continue;
|
||||||
|
|
||||||
String buttonId = dataManager.getButtonIdForPlacedController(controllerLoc);
|
String buttonId = dataManager.getButtonIdForPlacedController(controllerLoc);
|
||||||
if (buttonId == null) continue;
|
if (buttonId == null) continue;
|
||||||
|
|
||||||
double radius = dataManager.getMotionSensorRadius(controllerLoc);
|
double radius = dataManager.getMotionSensorRadius(controllerLoc);
|
||||||
if (radius == -1) radius = configManager.getConfig().getDouble("motion-detection-radius", 5.0);
|
if (radius == -1) radius = configManager.getConfig().getDouble("motion-detection-radius", 5.0);
|
||||||
|
|
||||||
long delay = dataManager.getMotionSensorDelay(controllerLoc);
|
long delay = dataManager.getMotionSensorDelay(controllerLoc);
|
||||||
if (delay == -1) delay = configManager.getConfig().getLong("motion-close-delay-ms", 5000L);
|
if (delay == -1) delay = configManager.getConfig().getLong("motion-close-delay-ms", 5000L);
|
||||||
|
|
||||||
boolean detected = !loc.getWorld().getNearbyEntities(loc, radius, radius, radius, e -> e instanceof Player).isEmpty();
|
final double r = radius;
|
||||||
List<String> connectedBlocks = dataManager.getConnectedBlocks(buttonId);
|
boolean detected;
|
||||||
|
if (isCarpet) {
|
||||||
|
// NEU: Teppich erkennt NUR Spieler
|
||||||
|
detected = !loc.getWorld()
|
||||||
|
.getNearbyEntities(loc, r, r, r, e -> e instanceof Player).isEmpty();
|
||||||
|
} else {
|
||||||
|
// Tripwire: alle lebenden Entitäten
|
||||||
|
detected = !loc.getWorld()
|
||||||
|
.getNearbyEntities(loc, r, r, r,
|
||||||
|
e -> e instanceof org.bukkit.entity.LivingEntity).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
if (connectedBlocks == null || connectedBlocks.isEmpty()) continue;
|
List<String> connected = dataManager.getConnectedBlocks(buttonId);
|
||||||
|
if (connected == null || connected.isEmpty()) continue;
|
||||||
|
|
||||||
if (detected) {
|
if (detected) {
|
||||||
setOpenables(connectedBlocks, true);
|
if (!activeSensors.contains(controllerLoc)) {
|
||||||
|
setOpenables(connected, true);
|
||||||
|
activeSensors.add(controllerLoc);
|
||||||
|
}
|
||||||
lastMotionDetections.put(controllerLoc, now);
|
lastMotionDetections.put(controllerLoc, now);
|
||||||
} else {
|
} else {
|
||||||
Long last = lastMotionDetections.get(controllerLoc);
|
Long last = lastMotionDetections.get(controllerLoc);
|
||||||
if (last != null && now - last >= delay) {
|
if (last != null && now - last >= delay) {
|
||||||
setOpenables(connectedBlocks, false);
|
setOpenables(connected, false);
|
||||||
lastMotionDetections.remove(controllerLoc);
|
lastMotionDetections.remove(controllerLoc);
|
||||||
|
activeSensors.remove(controllerLoc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setOpenables(List<String> connectedBlocks, boolean open) {
|
void setOpenables(List<String> connectedBlocks, boolean open) {
|
||||||
for (String targetLocStr : connectedBlocks) {
|
for (String locStr : connectedBlocks) {
|
||||||
Location targetLoc = parseLocation(targetLocStr);
|
Location tl = parseLocation(locStr);
|
||||||
if (targetLoc == null) continue;
|
if (tl == null) continue;
|
||||||
|
Block tb = tl.getBlock();
|
||||||
Block targetBlock = targetLoc.getBlock();
|
if (tb.getBlockData() instanceof org.bukkit.block.data.Openable) {
|
||||||
if (targetBlock.getBlockData() instanceof org.bukkit.block.data.Openable) {
|
org.bukkit.block.data.Openable o = (org.bukkit.block.data.Openable) tb.getBlockData();
|
||||||
org.bukkit.block.data.Openable openable = (org.bukkit.block.data.Openable) targetBlock.getBlockData();
|
o.setOpen(open);
|
||||||
openable.setOpen(open);
|
tb.setBlockData(o);
|
||||||
targetBlock.setBlockData(openable);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Location parseLocation(String locStr) {
|
// -----------------------------------------------------------------------
|
||||||
String[] parts = locStr.split(",");
|
// Befehle
|
||||||
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;
|
|
||||||
|
|
||||||
NoteBlock noteBlock = (NoteBlock) block.getBlockData();
|
|
||||||
try {
|
|
||||||
org.bukkit.Instrument bukkitInstrument = org.bukkit.Instrument.valueOf(instrument.toUpperCase());
|
|
||||||
noteBlock.setInstrument(bukkitInstrument);
|
|
||||||
noteBlock.setNote(new Note(0, Tone.C, false));
|
|
||||||
block.setBlockData(noteBlock);
|
|
||||||
loc.getWorld().playSound(loc, bukkitInstrument.getSound(), 1.0f, 1.0f);
|
|
||||||
|
|
||||||
if (configManager.getConfig().getBoolean("double-note-enabled", true)) {
|
|
||||||
int delayMs = configManager.getConfig().getInt("double-note-delay-ms", 1000);
|
|
||||||
long delayTicks = (long) (delayMs / 50.0);
|
|
||||||
getServer().getScheduler().runTaskLater(this, () -> {
|
|
||||||
if (block.getType() == Material.NOTE_BLOCK) {
|
|
||||||
loc.getWorld().playSound(loc, bukkitInstrument.getSound(), 1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
}, delayTicks);
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
getLogger().warning("Ungültiges Instrument: " + instrument);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
if (!command.getName().equalsIgnoreCase("bc")) return false;
|
if (!command.getName().equalsIgnoreCase("bc")) return false;
|
||||||
|
|
||||||
if (args.length == 0) {
|
if (args.length == 0) {
|
||||||
sender.sendMessage("§6[ButtonControl] §7Verwende: /bc <info|reload|note|trust|untrust|public|private>");
|
sender.sendMessage("§6[BC] §7/bc <info|reload|note|list|rename|schedule|trust|untrust|public|private>");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- INFO BEFEHL ---
|
String sub = args[0].toLowerCase();
|
||||||
if (args[0].equalsIgnoreCase("info")) {
|
|
||||||
sender.sendMessage("§6§lButtonControl §7- v" + getDescription().getVersion());
|
// INFO
|
||||||
sender.sendMessage("§eAuthor: §fM_Viper");
|
if (sub.equals("info")) {
|
||||||
sender.sendMessage("§eFeatures: §fTüren, Lampen, Notenblöcke, Sensoren");
|
sender.sendMessage("§6§lButtonControl §7v" + getDescription().getVersion() + " §8by §7M_Viper");
|
||||||
|
sender.sendMessage("§7Features: §fTüren · Lampen · Notenblöcke · Sensoren · Teppiche · Schilder · Zeitpläne");
|
||||||
|
sender.sendMessage("§7Controller aktiv: §f" + dataManager.getAllPlacedControllers().size());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- RELOAD BEFEHL ---
|
// RELOAD
|
||||||
if (args[0].equalsIgnoreCase("reload")) {
|
if (sub.equals("reload")) {
|
||||||
if (!sender.hasPermission("buttoncontrol.reload")) {
|
if (!sender.hasPermission("buttoncontrol.reload")) {
|
||||||
sender.sendMessage(configManager.getMessage("keine-berechtigung"));
|
sender.sendMessage(configManager.getMessage("keine-berechtigung")); return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
configManager.reloadConfig();
|
configManager.reloadConfig();
|
||||||
updateConfigWithDefaults();
|
|
||||||
dataManager.reloadData();
|
dataManager.reloadData();
|
||||||
|
timedControllerLastState.clear();
|
||||||
sender.sendMessage(configManager.getMessage("konfiguration-neugeladen"));
|
sender.sendMessage(configManager.getMessage("konfiguration-neugeladen"));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NOTE BEFEHL ---
|
// NOTE
|
||||||
if (args[0].equalsIgnoreCase("note") && sender instanceof Player) {
|
if (sub.equals("note") && sender instanceof Player) {
|
||||||
Player player = (Player) sender;
|
Player player = (Player) sender;
|
||||||
if (args.length < 2) {
|
if (args.length < 2) { player.sendMessage("§7/bc note <Instrument>"); return true; }
|
||||||
player.sendMessage("§6[ButtonControl] §7Verwende: /bc note <Instrument>");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
org.bukkit.Instrument.valueOf(args[1].toUpperCase());
|
org.bukkit.Instrument.valueOf(args[1].toUpperCase());
|
||||||
dataManager.setPlayerInstrument(player.getUniqueId(), args[1].toUpperCase());
|
dataManager.setPlayerInstrument(player.getUniqueId(), args[1].toUpperCase());
|
||||||
@@ -413,78 +397,184 @@ public class ButtonControl extends JavaPlugin {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- TRUST / PUBLIC / PRIVATE SYSTEM ---
|
// Alle folgenden Befehle erfordern Spieler + angescauten Controller
|
||||||
if (sender instanceof Player) {
|
if (!(sender instanceof Player)) {
|
||||||
|
sender.sendMessage("§cNur Spieler können diesen Befehl verwenden.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Player player = (Player) sender;
|
Player player = (Player) sender;
|
||||||
if (args[0].equalsIgnoreCase("trust") || args[0].equalsIgnoreCase("untrust") ||
|
|
||||||
args[0].equalsIgnoreCase("public") || args[0].equalsIgnoreCase("private")) {
|
if (sub.equals("list") || sub.equals("rename") || sub.equals("schedule")
|
||||||
|
|| sub.equals("trust") || sub.equals("untrust")
|
||||||
|
|| sub.equals("public") || sub.equals("private")) {
|
||||||
|
|
||||||
Block target = player.getTargetBlockExact(5);
|
Block target = player.getTargetBlockExact(5);
|
||||||
|
if (!isValidController(target)) {
|
||||||
// Erkennt nun Stein- und alle Holzbuttons sowie Sensoren
|
player.sendMessage(configManager.getMessage("kein-controller-im-blick")); return true;
|
||||||
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 targetLoc = toLoc(target);
|
||||||
String targetLoc = target.getWorld().getName() + "," + target.getX() + "," + target.getY() + "," + target.getZ();
|
|
||||||
String buttonId = dataManager.getButtonIdForLocation(targetLoc);
|
String buttonId = dataManager.getButtonIdForLocation(targetLoc);
|
||||||
|
|
||||||
if (buttonId == null) {
|
if (buttonId == null) {
|
||||||
player.sendMessage(configManager.getMessage("keine-bloecke-verbunden"));
|
player.sendMessage(configManager.getMessage("keine-bloecke-verbunden")); return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
boolean isAdmin = player.hasPermission("buttoncontrol.admin");
|
||||||
|
boolean isOwner = dataManager.isOwner(buttonId, player.getUniqueId());
|
||||||
|
|
||||||
if (!dataManager.isOwner(buttonId, player.getUniqueId())) {
|
switch (sub) {
|
||||||
player.sendMessage(configManager.getMessage("nur-besitzer-abbauen"));
|
case "list":
|
||||||
return true;
|
if (!dataManager.canAccess(buttonId, player.getUniqueId()) && !isAdmin) {
|
||||||
|
player.sendMessage(configManager.getMessage("keine-berechtigung-controller")); return true;
|
||||||
}
|
}
|
||||||
|
sendListInfo(player, buttonId);
|
||||||
|
break;
|
||||||
|
|
||||||
// Spieler hinzufügen
|
case "rename":
|
||||||
if (args[0].equalsIgnoreCase("trust")) {
|
if (!isOwner && !isAdmin) { player.sendMessage(configManager.getMessage("nur-besitzer-abbauen")); return true; }
|
||||||
if (args.length < 2) {
|
if (args.length < 2) { player.sendMessage("§7/bc rename <Name>"); return true; }
|
||||||
player.sendMessage("§6[ButtonControl] §7Verwende: /bc trust <Spieler>");
|
String newName = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
|
||||||
return true;
|
if (newName.length() > 32) { player.sendMessage("§cName zu lang (max. 32 Zeichen)."); return true; }
|
||||||
|
dataManager.setControllerName(buttonId, newName);
|
||||||
|
player.sendMessage(String.format(configManager.getMessage("controller-umbenannt"), newName));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "schedule":
|
||||||
|
if (!isOwner && !isAdmin) { player.sendMessage(configManager.getMessage("nur-besitzer-abbauen")); return true; }
|
||||||
|
new ScheduleGUI(this, player, buttonId).open();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "trust":
|
||||||
|
if (!isOwner && !isAdmin) { player.sendMessage(configManager.getMessage("nur-besitzer-abbauen")); return true; }
|
||||||
|
if (args.length < 2) { player.sendMessage("§7/bc trust <Spieler>"); return true; }
|
||||||
|
org.bukkit.OfflinePlayer tp = Bukkit.getOfflinePlayer(args[1]);
|
||||||
|
if (!tp.hasPlayedBefore() && !tp.isOnline()) {
|
||||||
|
player.sendMessage(configManager.getMessage("spieler-nicht-gefunden")); return true;
|
||||||
}
|
}
|
||||||
Player targetPlayer = Bukkit.getPlayer(args[1]);
|
dataManager.addTrustedPlayer(buttonId, tp.getUniqueId());
|
||||||
if (targetPlayer == null) {
|
player.sendMessage(String.format(configManager.getMessage("trust-hinzugefuegt"), args[1]));
|
||||||
player.sendMessage(configManager.getMessage("spieler-nicht-gefunden"));
|
break;
|
||||||
return true;
|
|
||||||
}
|
case "untrust":
|
||||||
dataManager.addTrustedPlayer(buttonId, targetPlayer.getUniqueId());
|
if (!isOwner && !isAdmin) { player.sendMessage(configManager.getMessage("nur-besitzer-abbauen")); return true; }
|
||||||
player.sendMessage(String.format(configManager.getMessage("trust-hinzugefuegt"), targetPlayer.getName()));
|
if (args.length < 2) { player.sendMessage("§7/bc untrust <Spieler>"); return true; }
|
||||||
}
|
dataManager.removeTrustedPlayer(buttonId, Bukkit.getOfflinePlayer(args[1]).getUniqueId());
|
||||||
// Spieler entfernen
|
|
||||||
else if (args[0].equalsIgnoreCase("untrust")) {
|
|
||||||
if (args.length < 2) {
|
|
||||||
player.sendMessage("§6[ButtonControl] §7Verwende: /bc untrust <Spieler>");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
UUID targetUUID = Bukkit.getOfflinePlayer(args[1]).getUniqueId();
|
|
||||||
dataManager.removeTrustedPlayer(buttonId, targetUUID);
|
|
||||||
player.sendMessage(String.format(configManager.getMessage("trust-entfernt"), args[1]));
|
player.sendMessage(String.format(configManager.getMessage("trust-entfernt"), args[1]));
|
||||||
}
|
break;
|
||||||
// Status umschalten (Public) oder erzwingen (Private)
|
|
||||||
else if (args[0].equalsIgnoreCase("public") || args[0].equalsIgnoreCase("private")) {
|
default: // public / private
|
||||||
boolean newState = args[0].equalsIgnoreCase("public") ? !dataManager.isPublic(buttonId) : false;
|
if (!isOwner && !isAdmin) { player.sendMessage(configManager.getMessage("nur-besitzer-abbauen")); return true; }
|
||||||
dataManager.setPublic(buttonId, newState);
|
boolean pub = sub.equals("public");
|
||||||
String statusColor = newState ? "§aÖffentlich" : "§cPrivat";
|
dataManager.setPublic(buttonId, pub);
|
||||||
player.sendMessage(String.format(configManager.getMessage("status-geandert"), statusColor));
|
player.sendMessage(String.format(configManager.getMessage("status-geandert"),
|
||||||
}
|
pub ? "§aÖffentlich" : "§cPrivat"));
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigManager getConfigManager() {
|
private void sendListInfo(Player player, String buttonId) {
|
||||||
return configManager;
|
String name = dataManager.getControllerName(buttonId);
|
||||||
|
String header = name != null
|
||||||
|
? "§6§l" + name
|
||||||
|
: "§6§lController §8§o(ID: " + buttonId.substring(0, 8) + "...)";
|
||||||
|
player.sendMessage(header);
|
||||||
|
|
||||||
|
List<String> connected = dataManager.getConnectedBlocks(buttonId);
|
||||||
|
if (connected == null || connected.isEmpty()) {
|
||||||
|
player.sendMessage(" §cKeine Blöcke verbunden.");
|
||||||
|
} else {
|
||||||
|
player.sendMessage("§7Verbundene Blöcke §8(" + connected.size() + ")§7:");
|
||||||
|
for (int i = 0; i < connected.size(); i++) {
|
||||||
|
String ls = connected.get(i);
|
||||||
|
Location l = parseLocation(ls);
|
||||||
|
String typeLabel = l != null ? "§e" + l.getBlock().getType().name() : "§8unbekannt";
|
||||||
|
String[] p = ls.split(",");
|
||||||
|
String coords = p.length == 4
|
||||||
|
? "§8(" + p[1] + "§7, §8" + p[2] + "§7, §8" + p[3] + " §7in §f" + p[0] + "§8)" : "";
|
||||||
|
player.sendMessage(" §8" + (i + 1) + ". " + typeLabel + " " + coords);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataManager getDataManager() {
|
player.sendMessage("§7Status: " + (dataManager.isPublic(buttonId) ? "§aÖffentlich" : "§cPrivat"));
|
||||||
return dataManager;
|
|
||||||
|
long openT = dataManager.getScheduleOpenTime(buttonId);
|
||||||
|
long closeT = dataManager.getScheduleCloseTime(buttonId);
|
||||||
|
if (openT >= 0 && closeT >= 0) {
|
||||||
|
player.sendMessage("§7Zeitplan: §aÖffnet §7um §e" + ticksToTime(openT)
|
||||||
|
+ " §7· §cSchließt §7um §e" + ticksToTime(closeT));
|
||||||
|
} else {
|
||||||
|
player.sendMessage("§7Zeitplan: §8Nicht gesetzt §7(§e/bc schedule§7)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Utility
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Wandelt Minecraft-Ticks (0–23999) in "HH:MM" um. Ingame-Tag startet um 06:00. */
|
||||||
|
public String ticksToTime(long ticks) {
|
||||||
|
long shifted = (ticks + 6000) % 24000;
|
||||||
|
long hours = shifted / 1000;
|
||||||
|
long minutes = (shifted % 1000) * 60 / 1000;
|
||||||
|
return String.format("%02d:%02d", hours, minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wandelt "HH:MM" zurück in Minecraft-Ticks */
|
||||||
|
public long timeToTicks(int hours, int minutes) {
|
||||||
|
long totalMinutes = hours * 60L + minutes;
|
||||||
|
long ticks = (totalMinutes * 1000L / 60L - 6000 + 24000) % 24000;
|
||||||
|
return ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidController(Block b) {
|
||||||
|
if (b == null) return false;
|
||||||
|
Material m = b.getType();
|
||||||
|
return m.name().endsWith("_BUTTON")
|
||||||
|
|| m == Material.DAYLIGHT_DETECTOR
|
||||||
|
|| m == Material.TRIPWIRE_HOOK
|
||||||
|
|| m.name().endsWith("_SIGN")
|
||||||
|
|| m.name().endsWith("_CARPET");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toLoc(Block b) {
|
||||||
|
return b.getWorld().getName() + "," + b.getX() + "," + b.getY() + "," + b.getZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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;
|
||||||
|
NoteBlock noteBlock = (NoteBlock) block.getBlockData();
|
||||||
|
try {
|
||||||
|
org.bukkit.Instrument inst = org.bukkit.Instrument.valueOf(instrument.toUpperCase());
|
||||||
|
noteBlock.setInstrument(inst);
|
||||||
|
noteBlock.setNote(new Note(0, Tone.C, false));
|
||||||
|
block.setBlockData(noteBlock);
|
||||||
|
loc.getWorld().playSound(loc, inst.getSound(), 1.0f, 1.0f);
|
||||||
|
if (configManager.getConfig().getBoolean("double-note-enabled", true)) {
|
||||||
|
long delayTicks = (long)(configManager.getConfig().getInt("double-note-delay-ms", 1000) / 50.0);
|
||||||
|
getServer().getScheduler().runTaskLater(this, () -> {
|
||||||
|
if (block.getType() == Material.NOTE_BLOCK)
|
||||||
|
loc.getWorld().playSound(loc, inst.getSound(), 1.0f, 1.0f);
|
||||||
|
}, delayTicks);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
getLogger().warning("Ungültiges Instrument: " + instrument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigManager getConfigManager() { return configManager; }
|
||||||
|
public DataManager getDataManager() { return dataManager; }
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@ package viper;
|
|||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.Sound;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.data.Bisected;
|
||||||
import org.bukkit.block.data.Openable;
|
import org.bukkit.block.data.Openable;
|
||||||
import org.bukkit.block.data.Lightable;
|
import org.bukkit.block.data.Lightable;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@@ -13,6 +15,7 @@ import org.bukkit.event.block.Action;
|
|||||||
import org.bukkit.event.block.BlockBreakEvent;
|
import org.bukkit.event.block.BlockBreakEvent;
|
||||||
import org.bukkit.event.block.BlockPlaceEvent;
|
import org.bukkit.event.block.BlockPlaceEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.ItemMeta;
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
|
||||||
@@ -31,34 +34,55 @@ public class ButtonListener implements Listener {
|
|||||||
this.dataManager = dataManager;
|
this.dataManager = dataManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Interact – Benutzung + Verbinden
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||||
|
// Doppelt-Feuern bei Items verhindern (Haupt- und Nebenhand)
|
||||||
|
if (event.getHand() != EquipmentSlot.HAND) return;
|
||||||
|
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
UUID playerUUID = player.getUniqueId();
|
UUID playerUUID = player.getUniqueId();
|
||||||
ItemStack item = event.getItem();
|
ItemStack item = event.getItem();
|
||||||
Block block = event.getClickedBlock();
|
Block block = event.getClickedBlock();
|
||||||
|
|
||||||
// 1. Logik für bereits platzierte Controller (Benutzung)
|
// ── 1. Bereits platzierter Controller ──────────────────────────────
|
||||||
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && block != null) {
|
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && block != null) {
|
||||||
String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ();
|
String blockLocation = plugin.toLoc(block);
|
||||||
String buttonId = dataManager.getButtonIdForLocation(blockLocation);
|
String buttonId = dataManager.getButtonIdForLocation(blockLocation);
|
||||||
|
|
||||||
if (buttonId != null) {
|
if (buttonId != null) {
|
||||||
if (!dataManager.canAccess(buttonId, playerUUID)) {
|
// Admin-Bypass
|
||||||
|
if (!dataManager.canAccess(buttonId, playerUUID)
|
||||||
|
&& !player.hasPermission("buttoncontrol.admin")) {
|
||||||
player.sendMessage(configManager.getMessage("keine-berechtigung-controller"));
|
player.sendMessage(configManager.getMessage("keine-berechtigung-controller"));
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block.getType() == Material.TRIPWIRE_HOOK) {
|
// Tripwire & Teppich → MotionSensorGUI öffnen
|
||||||
|
if (block.getType() == Material.TRIPWIRE_HOOK
|
||||||
|
|| block.getType().name().endsWith("_CARPET")) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
new MotionSensorGUI(plugin, player, blockLocation, buttonId).open();
|
new MotionSensorGUI(plugin, player, blockLocation, buttonId).open();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isButton(block.getType()) || block.getType() == Material.DAYLIGHT_DETECTOR) {
|
// Schild → nur mit Shift+Klick Blöcke toggeln,
|
||||||
List<String> connectedBlocks = dataManager.getConnectedBlocks(buttonId);
|
// normaler Klick öffnet den Schildeditor (Vanilla)
|
||||||
|
if (block.getType().name().endsWith("_SIGN")
|
||||||
|
|| block.getType().name().endsWith("_BUTTON")
|
||||||
|
|| block.getType() == Material.DAYLIGHT_DETECTOR) {
|
||||||
|
|
||||||
|
if (player.isSneaking()) {
|
||||||
|
// Shift+Klick → Vanilla-Verhalten zulassen (Schildeditor etc.)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.setCancelled(true);
|
||||||
|
List<String> connectedBlocks = dataManager.getConnectedBlocks(buttonId);
|
||||||
if (connectedBlocks != null && !connectedBlocks.isEmpty()) {
|
if (connectedBlocks != null && !connectedBlocks.isEmpty()) {
|
||||||
toggleConnectedBlocks(player, playerUUID, connectedBlocks);
|
toggleConnectedBlocks(player, playerUUID, connectedBlocks);
|
||||||
} else {
|
} else {
|
||||||
@@ -69,106 +93,216 @@ public class ButtonListener implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Logik für das Verbinden von neuen Blöcken (Item in der Hand)
|
// ── 2. Verbinden mit Controller-Item in der Hand ───────────────────
|
||||||
if (item == null || !item.hasItemMeta() || !item.getItemMeta().getDisplayName().contains("§6Steuer-")) {
|
if (item == null || !item.hasItemMeta()) return;
|
||||||
return;
|
String displayName = item.getItemMeta().getDisplayName();
|
||||||
}
|
if (!displayName.contains("§6Steuer-")) return;
|
||||||
|
if (event.getAction() != Action.RIGHT_CLICK_BLOCK || block == null) return;
|
||||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK || block == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInteractableTarget(block.getType())) {
|
if (isInteractableTarget(block.getType())) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
|
|
||||||
|
// Doppeltür: immer untersten Block speichern
|
||||||
|
Block targetBlock = getBottomDoorBlock(block);
|
||||||
|
|
||||||
ItemMeta meta = item.getItemMeta();
|
ItemMeta meta = item.getItemMeta();
|
||||||
String buttonId = null;
|
String buttonId = extractButtonId(meta);
|
||||||
|
|
||||||
// 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) {
|
if (buttonId == null) {
|
||||||
buttonId = UUID.randomUUID().toString();
|
buttonId = UUID.randomUUID().toString();
|
||||||
updateButtonLore(item, buttonId);
|
updateButtonLore(item, buttonId);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> connectedBlocks = dataManager.getConnectedBlocks(buttonId);
|
List<String> connectedBlocks = dataManager.getConnectedBlocks(buttonId);
|
||||||
if (connectedBlocks == null) {
|
if (connectedBlocks == null) connectedBlocks = new ArrayList<>();
|
||||||
connectedBlocks = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ();
|
String targetLocStr = plugin.toLoc(targetBlock);
|
||||||
if (connectedBlocks.contains(blockLocation)) {
|
if (connectedBlocks.contains(targetLocStr)) {
|
||||||
player.sendMessage(configManager.getMessage("block-bereits-verbunden"));
|
player.sendMessage(configManager.getMessage("block-bereits-verbunden"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkLimits(player, block.getType(), connectedBlocks)) {
|
if (checkLimits(player, targetBlock.getType(), connectedBlocks)) {
|
||||||
connectedBlocks.add(blockLocation);
|
connectedBlocks.add(targetLocStr);
|
||||||
dataManager.setConnectedBlocks(playerUUID.toString(), buttonId, connectedBlocks);
|
dataManager.setConnectedBlocks(playerUUID.toString(), buttonId, connectedBlocks);
|
||||||
player.sendMessage(configManager.getMessage("block-verbunden"));
|
player.sendMessage(configManager.getMessage("block-verbunden"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Block-Break: Controller abbauen
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onBlockBreak(BlockBreakEvent event) {
|
||||||
|
Block block = event.getBlock();
|
||||||
|
String blockLocation = plugin.toLoc(block);
|
||||||
|
String buttonId = dataManager.getButtonIdForLocation(blockLocation);
|
||||||
|
|
||||||
|
if (buttonId != null) {
|
||||||
|
if (!dataManager.isOwner(buttonId, event.getPlayer().getUniqueId())
|
||||||
|
&& !event.getPlayer().hasPermission("buttoncontrol.admin")) {
|
||||||
|
event.getPlayer().sendMessage(configManager.getMessage("nur-besitzer-abbauen"));
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dataManager.removeController(blockLocation);
|
||||||
|
event.getPlayer().sendMessage(configManager.getMessage("controller-entfernt"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Block-Break: Verbundener Block abgebaut → Eintrag bereinigen (NEU)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onConnectedBlockBreak(BlockBreakEvent event) {
|
||||||
|
Block block = event.getBlock();
|
||||||
|
if (!isInteractableTarget(block.getType())) return;
|
||||||
|
|
||||||
|
// Bei Türen normalisieren auf Unterblock
|
||||||
|
Block bottomBlock = getBottomDoorBlock(block);
|
||||||
|
String locStr = plugin.toLoc(bottomBlock);
|
||||||
|
|
||||||
|
if (dataManager.removeFromAllConnectedBlocks(locStr)) {
|
||||||
|
event.getPlayer().sendMessage(configManager.getMessage("block-verbindung-entfernt"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Controller platzieren
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onBlockPlace(BlockPlaceEvent event) {
|
||||||
|
ItemStack item = event.getItemInHand();
|
||||||
|
if (item == null || !item.hasItemMeta()) return;
|
||||||
|
if (!item.getItemMeta().getDisplayName().contains("§6Steuer-")) return;
|
||||||
|
|
||||||
|
Block block = event.getBlockPlaced();
|
||||||
|
String buttonId = extractButtonId(item.getItemMeta());
|
||||||
|
if (buttonId == null) {
|
||||||
|
buttonId = UUID.randomUUID().toString();
|
||||||
|
updateButtonLore(item, buttonId);
|
||||||
|
}
|
||||||
|
|
||||||
|
dataManager.registerController(
|
||||||
|
plugin.toLoc(block), event.getPlayer().getUniqueId(), buttonId);
|
||||||
|
event.getPlayer().sendMessage(configManager.getMessage("controller-platziert"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Toggle-Logik
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
private void toggleConnectedBlocks(Player player, UUID playerUUID, List<String> connectedBlocks) {
|
private void toggleConnectedBlocks(Player player, UUID playerUUID, List<String> connectedBlocks) {
|
||||||
boolean anyDoorOpened = false, anyDoorClosed = false;
|
boolean anyDoorOpened = false, anyDoorClosed = false;
|
||||||
boolean anyGateOpened = false, anyGateClosed = false;
|
boolean anyGateOpened = false, anyGateClosed = false;
|
||||||
boolean anyTrapOpened = false, anyTrapClosed = false;
|
boolean anyTrapOpened = false, anyTrapClosed = false;
|
||||||
|
boolean anyIronDoorOpened = false, anyIronDoorClosed = false;
|
||||||
|
boolean anyIronTrapOpened = false, anyIronTrapClosed = false;
|
||||||
boolean anyLampOn = false, anyLampOff = false;
|
boolean anyLampOn = false, anyLampOff = false;
|
||||||
boolean anyNoteBlockPlayed = false;
|
boolean anyNoteBlockPlayed = false;
|
||||||
boolean anyBellPlayed = false;
|
boolean anyBellPlayed = false;
|
||||||
|
|
||||||
|
boolean soundsEnabled = configManager.getConfig().getBoolean("sounds.enabled", true);
|
||||||
|
|
||||||
for (String locStr : connectedBlocks) {
|
for (String locStr : connectedBlocks) {
|
||||||
Location location = parseLocation(locStr);
|
Location location = parseLocation(locStr);
|
||||||
if (location == null) continue;
|
if (location == null) continue;
|
||||||
Block targetBlock = location.getBlock();
|
Block targetBlock = location.getBlock();
|
||||||
|
Material mat = targetBlock.getType();
|
||||||
|
|
||||||
if (isDoor(targetBlock.getType()) || isGate(targetBlock.getType()) || isTrapdoor(targetBlock.getType())) {
|
// ── Eisentür (NEU) ──────────────────────────────────────────────
|
||||||
|
// Eisentüren implementieren Openable in der Bukkit-API.
|
||||||
|
// Wir setzen den Zustand direkt – kein Redstone-Signal nötig.
|
||||||
|
if (mat == Material.IRON_DOOR) {
|
||||||
if (targetBlock.getBlockData() instanceof Openable) {
|
if (targetBlock.getBlockData() instanceof Openable) {
|
||||||
Openable openable = (Openable) targetBlock.getBlockData();
|
Openable op = (Openable) targetBlock.getBlockData();
|
||||||
boolean wasOpen = openable.isOpen();
|
boolean wasOpen = op.isOpen();
|
||||||
openable.setOpen(!wasOpen);
|
op.setOpen(!wasOpen);
|
||||||
targetBlock.setBlockData(openable);
|
targetBlock.setBlockData(op);
|
||||||
|
if (soundsEnabled) {
|
||||||
|
String soundKey = wasOpen ? "sounds.iron-door-close" : "sounds.iron-door-open";
|
||||||
|
playConfigSound(location, soundKey,
|
||||||
|
wasOpen ? "BLOCK_IRON_DOOR_CLOSE" : "BLOCK_IRON_DOOR_OPEN");
|
||||||
|
}
|
||||||
|
if (!wasOpen) anyIronDoorOpened = true; else anyIronDoorClosed = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (isDoor(targetBlock.getType())) {
|
// ── Eisenfalltür (NEU) ─────────────────────────────────────────
|
||||||
if (!wasOpen) anyDoorOpened = true; else anyDoorClosed = true;
|
if (mat == Material.IRON_TRAPDOOR) {
|
||||||
} else if (isGate(targetBlock.getType())) {
|
if (targetBlock.getBlockData() instanceof Openable) {
|
||||||
if (!wasOpen) anyGateOpened = true; else anyGateClosed = true;
|
Openable op = (Openable) targetBlock.getBlockData();
|
||||||
} else if (isTrapdoor(targetBlock.getType())) {
|
boolean wasOpen = op.isOpen();
|
||||||
if (!wasOpen) anyTrapOpened = true; else anyTrapClosed = true;
|
op.setOpen(!wasOpen);
|
||||||
|
targetBlock.setBlockData(op);
|
||||||
|
if (soundsEnabled) {
|
||||||
|
String soundKey = wasOpen ? "sounds.iron-door-close" : "sounds.iron-door-open";
|
||||||
|
playConfigSound(location, soundKey,
|
||||||
|
wasOpen ? "BLOCK_IRON_TRAPDOOR_CLOSE" : "BLOCK_IRON_TRAPDOOR_OPEN");
|
||||||
|
}
|
||||||
|
if (!wasOpen) anyIronTrapOpened = true; else anyIronTrapClosed = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Holztür / Zauntore / Falltüren ────────────────────────────
|
||||||
|
if (isDoor(mat) || isGate(mat) || isTrapdoor(mat)) {
|
||||||
|
if (targetBlock.getBlockData() instanceof Openable) {
|
||||||
|
Openable op = (Openable) targetBlock.getBlockData();
|
||||||
|
boolean wasOpen = op.isOpen();
|
||||||
|
op.setOpen(!wasOpen);
|
||||||
|
targetBlock.setBlockData(op);
|
||||||
|
|
||||||
|
if (soundsEnabled) {
|
||||||
|
String soundKey = wasOpen ? "sounds.door-close" : "sounds.door-open";
|
||||||
|
String fallback = wasOpen ? "BLOCK_WOODEN_DOOR_CLOSE" : "BLOCK_WOODEN_DOOR_OPEN";
|
||||||
|
playConfigSound(location, soundKey, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDoor(mat)) { if (!wasOpen) anyDoorOpened = true; else anyDoorClosed = true; }
|
||||||
|
else if (isGate(mat)) { if (!wasOpen) anyGateOpened = true; else anyGateClosed = true; }
|
||||||
|
else { if (!wasOpen) anyTrapOpened = true; else anyTrapClosed = true; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// ── Redstone-Lampe ────────────────────────────────────────────
|
||||||
else if (targetBlock.getType() == Material.REDSTONE_LAMP) {
|
else if (mat == Material.REDSTONE_LAMP) {
|
||||||
Lightable lamp = (Lightable) targetBlock.getBlockData();
|
Lightable lamp = (Lightable) targetBlock.getBlockData();
|
||||||
boolean wasLit = lamp.isLit();
|
boolean wasLit = lamp.isLit();
|
||||||
lamp.setLit(!wasLit);
|
lamp.setLit(!wasLit);
|
||||||
targetBlock.setBlockData(lamp);
|
targetBlock.setBlockData(lamp);
|
||||||
|
if (soundsEnabled) {
|
||||||
|
playConfigSound(location,
|
||||||
|
wasLit ? "sounds.lamp-off" : "sounds.lamp-on",
|
||||||
|
"BLOCK_LEVER_CLICK");
|
||||||
|
}
|
||||||
if (!wasLit) anyLampOn = true; else anyLampOff = true;
|
if (!wasLit) anyLampOn = true; else anyLampOff = true;
|
||||||
}
|
}
|
||||||
else if (targetBlock.getType() == Material.NOTE_BLOCK) {
|
// ── Notenblock ────────────────────────────────────────────────
|
||||||
|
else if (mat == Material.NOTE_BLOCK) {
|
||||||
String instrument = dataManager.getPlayerInstrument(playerUUID);
|
String instrument = dataManager.getPlayerInstrument(playerUUID);
|
||||||
if (instrument == null) {
|
if (instrument == null)
|
||||||
instrument = configManager.getConfig().getString("default-note", "PIANO");
|
instrument = configManager.getConfig().getString("default-note", "PIANO");
|
||||||
}
|
|
||||||
plugin.playDoorbellSound(location, instrument);
|
plugin.playDoorbellSound(location, instrument);
|
||||||
anyNoteBlockPlayed = true;
|
anyNoteBlockPlayed = true;
|
||||||
}
|
}
|
||||||
else if (targetBlock.getType() == Material.BELL) {
|
// ── Glocke ───────────────────────────────────────────────────
|
||||||
targetBlock.getWorld().playSound(location, org.bukkit.Sound.BLOCK_BELL_USE, 3.0f, 1.0f);
|
else if (mat == Material.BELL) {
|
||||||
|
targetBlock.getWorld().playSound(location, Sound.BLOCK_BELL_USE, 3.0f, 1.0f);
|
||||||
anyBellPlayed = true;
|
anyBellPlayed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Feedback-Nachrichten
|
||||||
if (anyDoorOpened) player.sendMessage(configManager.getMessage("tueren-geoeffnet"));
|
if (anyDoorOpened) player.sendMessage(configManager.getMessage("tueren-geoeffnet"));
|
||||||
if (anyDoorClosed) player.sendMessage(configManager.getMessage("tueren-geschlossen"));
|
if (anyDoorClosed) player.sendMessage(configManager.getMessage("tueren-geschlossen"));
|
||||||
|
if (anyIronDoorOpened) player.sendMessage(configManager.getMessage("eisentueren-geoeffnet"));
|
||||||
|
if (anyIronDoorClosed) player.sendMessage(configManager.getMessage("eisentueren-geschlossen"));
|
||||||
|
if (anyIronTrapOpened) player.sendMessage(configManager.getMessage("eisenfallturen-geoeffnet"));
|
||||||
|
if (anyIronTrapClosed) player.sendMessage(configManager.getMessage("eisenfallturen-geschlossen"));
|
||||||
if (anyGateOpened) player.sendMessage(configManager.getMessage("gates-geoeffnet"));
|
if (anyGateOpened) player.sendMessage(configManager.getMessage("gates-geoeffnet"));
|
||||||
if (anyGateClosed) player.sendMessage(configManager.getMessage("gates-geschlossen"));
|
if (anyGateClosed) player.sendMessage(configManager.getMessage("gates-geschlossen"));
|
||||||
if (anyTrapOpened) player.sendMessage(configManager.getMessage("fallturen-geoeffnet"));
|
if (anyTrapOpened) player.sendMessage(configManager.getMessage("fallturen-geoeffnet"));
|
||||||
@@ -179,107 +313,91 @@ public class ButtonListener implements Listener {
|
|||||||
if (anyBellPlayed) player.sendMessage(configManager.getMessage("glocke-gelaeutet"));
|
if (anyBellPlayed) player.sendMessage(configManager.getMessage("glocke-gelaeutet"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spielt einen Sound ab dessen Name aus der config.yml gelesen wird.
|
||||||
|
* Ist der Key nicht gesetzt oder der Sound-Name ungültig, wird der Fallback verwendet.
|
||||||
|
*/
|
||||||
|
private void playConfigSound(Location loc, String configKey, String fallback) {
|
||||||
|
String soundName = configManager.getConfig().getString(configKey, fallback);
|
||||||
|
try {
|
||||||
|
Sound sound = Sound.valueOf(soundName.toUpperCase());
|
||||||
|
loc.getWorld().playSound(loc, sound, 1.0f, 1.0f);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
try {
|
||||||
|
Sound sound = Sound.valueOf(fallback.toUpperCase());
|
||||||
|
loc.getWorld().playSound(loc, sound, 1.0f, 1.0f);
|
||||||
|
} catch (IllegalArgumentException ignored) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Limits
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
private boolean checkLimits(Player player, Material type, List<String> connected) {
|
private boolean checkLimits(Player player, Material type, List<String> connected) {
|
||||||
if (isDoor(type)) {
|
if (type == Material.IRON_DOOR) {
|
||||||
if (connected.stream().filter(l -> isDoor(getMaterialFromLocation(l))).count() >= configManager.getMaxDoors()) {
|
if (connected.stream().filter(l -> getMaterialAt(l) == Material.IRON_DOOR).count()
|
||||||
player.sendMessage(configManager.getMessage("max-tueren-erreicht"));
|
>= configManager.getMaxDoors()) {
|
||||||
return false;
|
player.sendMessage(configManager.getMessage("max-tueren-erreicht")); return false;
|
||||||
|
}
|
||||||
|
} else if (type == Material.IRON_TRAPDOOR) {
|
||||||
|
if (connected.stream().filter(l -> getMaterialAt(l) == Material.IRON_TRAPDOOR).count()
|
||||||
|
>= configManager.getMaxTrapdoors()) {
|
||||||
|
player.sendMessage(configManager.getMessage("max-fallturen-erreicht")); return false;
|
||||||
|
}
|
||||||
|
} else if (isDoor(type)) {
|
||||||
|
if (connected.stream().filter(l -> isDoor(getMaterialAt(l))).count()
|
||||||
|
>= configManager.getMaxDoors()) {
|
||||||
|
player.sendMessage(configManager.getMessage("max-tueren-erreicht")); return false;
|
||||||
}
|
}
|
||||||
} else if (isGate(type)) {
|
} else if (isGate(type)) {
|
||||||
if (connected.stream().filter(l -> isGate(getMaterialFromLocation(l))).count() >= configManager.getMaxGates()) {
|
if (connected.stream().filter(l -> isGate(getMaterialAt(l))).count()
|
||||||
player.sendMessage(configManager.getMessage("max-gates-erreicht"));
|
>= configManager.getMaxGates()) {
|
||||||
return false;
|
player.sendMessage(configManager.getMessage("max-gates-erreicht")); return false;
|
||||||
}
|
}
|
||||||
} else if (isTrapdoor(type)) {
|
} else if (isTrapdoor(type)) {
|
||||||
if (connected.stream().filter(l -> isTrapdoor(getMaterialFromLocation(l))).count() >= configManager.getMaxTrapdoors()) {
|
if (connected.stream().filter(l -> isTrapdoor(getMaterialAt(l))).count()
|
||||||
player.sendMessage(configManager.getMessage("max-fallturen-erreicht"));
|
>= configManager.getMaxTrapdoors()) {
|
||||||
return false;
|
player.sendMessage(configManager.getMessage("max-fallturen-erreicht")); return false;
|
||||||
}
|
}
|
||||||
} else if (type == Material.REDSTONE_LAMP) {
|
} else if (type == Material.REDSTONE_LAMP) {
|
||||||
if (connected.stream().filter(l -> getMaterialFromLocation(l) == Material.REDSTONE_LAMP).count() >= configManager.getMaxLamps()) {
|
if (connected.stream().filter(l -> getMaterialAt(l) == Material.REDSTONE_LAMP).count()
|
||||||
player.sendMessage(configManager.getMessage("max-lampen-erreicht"));
|
>= configManager.getMaxLamps()) {
|
||||||
return false;
|
player.sendMessage(configManager.getMessage("max-lampen-erreicht")); return false;
|
||||||
}
|
}
|
||||||
} else if (type == Material.NOTE_BLOCK) {
|
} else if (type == Material.NOTE_BLOCK) {
|
||||||
if (connected.stream().filter(l -> getMaterialFromLocation(l) == Material.NOTE_BLOCK).count() >= configManager.getMaxNoteBlocks()) {
|
if (connected.stream().filter(l -> getMaterialAt(l) == Material.NOTE_BLOCK).count()
|
||||||
player.sendMessage(configManager.getMessage("max-notenbloecke-erreicht"));
|
>= configManager.getMaxNoteBlocks()) {
|
||||||
return false;
|
player.sendMessage(configManager.getMessage("max-notenbloecke-erreicht")); return false;
|
||||||
}
|
}
|
||||||
} else if (type == Material.BELL) {
|
} else if (type == Material.BELL) {
|
||||||
if (connected.stream().filter(l -> getMaterialFromLocation(l) == Material.BELL).count() >= configManager.getMaxBells()) {
|
if (connected.stream().filter(l -> getMaterialAt(l) == Material.BELL).count()
|
||||||
player.sendMessage(configManager.getMessage("max-glocken-erreicht"));
|
>= configManager.getMaxBells()) {
|
||||||
return false;
|
player.sendMessage(configManager.getMessage("max-glocken-erreicht")); return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
// -----------------------------------------------------------------------
|
||||||
public void onBlockPlace(BlockPlaceEvent event) {
|
// Hilfsmethoden
|
||||||
ItemStack item = event.getItemInHand();
|
// -----------------------------------------------------------------------
|
||||||
if (item == null || !item.hasItemMeta() || !item.getItemMeta().getDisplayName().contains("§6Steuer-")) {
|
|
||||||
return;
|
/** Gibt bei zweiteiligen Türen immer den untersten Block zurück. */
|
||||||
|
private Block getBottomDoorBlock(Block block) {
|
||||||
|
Material mat = block.getType();
|
||||||
|
if (!isDoor(mat) && mat != Material.IRON_DOOR) return block;
|
||||||
|
if (block.getBlockData() instanceof Bisected) {
|
||||||
|
Bisected b = (Bisected) block.getBlockData();
|
||||||
|
if (b.getHalf() == Bisected.Half.TOP) return block.getRelative(0, -1, 0);
|
||||||
|
}
|
||||||
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
Block block = event.getBlockPlaced();
|
private String extractButtonId(ItemMeta meta) {
|
||||||
ItemMeta meta = item.getItemMeta();
|
if (meta == null || !meta.hasLore() || meta.getLore().isEmpty()) return null;
|
||||||
String buttonId = null;
|
String first = meta.getLore().get(0);
|
||||||
|
return first.startsWith("§8ID: ") ? first.replace("§8ID: ", "") : null;
|
||||||
if (meta != null && meta.hasLore() && !meta.getLore().isEmpty()) {
|
|
||||||
String firstLine = meta.getLore().get(0);
|
|
||||||
if (firstLine.startsWith("§8ID: ")) {
|
|
||||||
buttonId = firstLine.replace("§8ID: ", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buttonId == null) {
|
|
||||||
buttonId = UUID.randomUUID().toString();
|
|
||||||
updateButtonLore(item, buttonId);
|
|
||||||
}
|
|
||||||
|
|
||||||
String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ();
|
|
||||||
dataManager.registerController(blockLocation, event.getPlayer().getUniqueId(), buttonId);
|
|
||||||
event.getPlayer().sendMessage(configManager.getMessage("controller-platziert"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onBlockBreak(BlockBreakEvent event) {
|
|
||||||
Block block = event.getBlock();
|
|
||||||
String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ();
|
|
||||||
String buttonId = dataManager.getButtonIdForLocation(blockLocation);
|
|
||||||
|
|
||||||
if (buttonId != null) {
|
|
||||||
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 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) {
|
|
||||||
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) {
|
private void updateButtonLore(ItemStack item, String buttonId) {
|
||||||
@@ -292,4 +410,29 @@ public class ButtonListener implements Listener {
|
|||||||
item.setItemMeta(meta);
|
item.setItemMeta(meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isButton(Material m) { return m.name().endsWith("_BUTTON"); }
|
||||||
|
private boolean isDoor(Material m) { return m.name().endsWith("_DOOR") && m != Material.IRON_DOOR; }
|
||||||
|
private boolean isGate(Material m) { return m.name().endsWith("_FENCE_GATE"); }
|
||||||
|
private boolean isTrapdoor(Material m) { return m.name().endsWith("_TRAPDOOR") && m != Material.IRON_TRAPDOOR; }
|
||||||
|
|
||||||
|
private boolean isInteractableTarget(Material m) {
|
||||||
|
return isDoor(m) || isGate(m) || isTrapdoor(m)
|
||||||
|
|| m == Material.IRON_DOOR || m == Material.IRON_TRAPDOOR
|
||||||
|
|| m == Material.REDSTONE_LAMP || m == Material.NOTE_BLOCK || m == Material.BELL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Material getMaterialAt(String locString) {
|
||||||
|
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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,11 @@ import java.util.List;
|
|||||||
|
|
||||||
public class ButtonTabCompleter implements TabCompleter {
|
public class ButtonTabCompleter implements TabCompleter {
|
||||||
|
|
||||||
private final List<String> commands = Arrays.asList("info", "reload", "note", "trust", "untrust", "public", "private");
|
private final List<String> commands = Arrays.asList(
|
||||||
|
"info", "reload", "note", "list", "rename", "schedule",
|
||||||
|
"trust", "untrust", "public", "private"
|
||||||
|
);
|
||||||
|
|
||||||
private final List<String> instruments = Arrays.asList(
|
private final List<String> instruments = Arrays.asList(
|
||||||
"PIANO", "BASS_DRUM", "SNARE_DRUM", "STICKS", "BASS_GUITAR",
|
"PIANO", "BASS_DRUM", "SNARE_DRUM", "STICKS", "BASS_GUITAR",
|
||||||
"FLUTE", "BELL", "CHIME", "GUITAR", "XYLOPHONE",
|
"FLUTE", "BELL", "CHIME", "GUITAR", "XYLOPHONE",
|
||||||
@@ -26,19 +30,21 @@ public class ButtonTabCompleter implements TabCompleter {
|
|||||||
|
|
||||||
List<String> completions = new ArrayList<>();
|
List<String> completions = new ArrayList<>();
|
||||||
|
|
||||||
// Erste Ebene: /bc <Tab>
|
|
||||||
if (args.length == 1) {
|
if (args.length == 1) {
|
||||||
StringUtil.copyPartialMatches(args[0], commands, completions);
|
StringUtil.copyPartialMatches(args[0], commands, completions);
|
||||||
}
|
|
||||||
|
|
||||||
// Zweite Ebene: /bc note <Tab>
|
} else if (args.length == 2) {
|
||||||
else if (args.length == 2 && args[0].equalsIgnoreCase("note")) {
|
switch (args[0].toLowerCase()) {
|
||||||
|
case "note":
|
||||||
StringUtil.copyPartialMatches(args[1], instruments, completions);
|
StringUtil.copyPartialMatches(args[1], instruments, completions);
|
||||||
|
break;
|
||||||
|
case "trust":
|
||||||
|
case "untrust":
|
||||||
|
return null; // Bukkit schlägt automatisch Online-Spieler vor
|
||||||
|
case "rename":
|
||||||
|
completions.add("<Name>");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zweite Ebene: /bc trust/untrust <Tab> (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);
|
Collections.sort(completions);
|
||||||
|
|||||||
@@ -21,106 +21,134 @@ public class ConfigManager {
|
|||||||
|
|
||||||
private void loadConfig() {
|
private void loadConfig() {
|
||||||
configFile = new File(plugin.getDataFolder(), "config.yml");
|
configFile = new File(plugin.getDataFolder(), "config.yml");
|
||||||
if (!configFile.exists()) {
|
if (!configFile.exists()) plugin.saveResource("config.yml", false);
|
||||||
plugin.saveResource("config.yml", false);
|
|
||||||
}
|
|
||||||
config = YamlConfiguration.loadConfiguration(configFile);
|
config = YamlConfiguration.loadConfiguration(configFile);
|
||||||
|
|
||||||
mergeDefaults(config, "config.yml", configFile);
|
mergeDefaults(config, "config.yml", configFile);
|
||||||
setConfigDefaults();
|
setConfigDefaults();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadLang() {
|
private void loadLang() {
|
||||||
langFile = new File(plugin.getDataFolder(), "lang.yml");
|
langFile = new File(plugin.getDataFolder(), "lang.yml");
|
||||||
if (!langFile.exists()) {
|
if (!langFile.exists()) plugin.saveResource("lang.yml", false);
|
||||||
plugin.saveResource("lang.yml", false);
|
|
||||||
}
|
|
||||||
lang = YamlConfiguration.loadConfiguration(langFile);
|
lang = YamlConfiguration.loadConfiguration(langFile);
|
||||||
|
|
||||||
mergeDefaults(lang, "lang.yml", langFile);
|
mergeDefaults(lang, "lang.yml", langFile);
|
||||||
setLangDefaults();
|
setLangDefaults();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIX: Null-Check VOR dem try-Block – verhindert NPE wenn Ressource nicht im JAR.
|
||||||
|
*/
|
||||||
private void mergeDefaults(FileConfiguration file, String resourceName, File targetFile) {
|
private void mergeDefaults(FileConfiguration file, String resourceName, File targetFile) {
|
||||||
try (InputStream is = plugin.getResource(resourceName);
|
InputStream is = plugin.getResource(resourceName);
|
||||||
InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
|
|
||||||
if (is == null) {
|
if (is == null) {
|
||||||
plugin.getLogger().warning(resourceName + " nicht im Plugin-Jar gefunden, Merge übersprungen.");
|
plugin.getLogger().warning(resourceName + " nicht im JAR gefunden.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
|
||||||
FileConfiguration defaults = YamlConfiguration.loadConfiguration(reader);
|
FileConfiguration defaults = YamlConfiguration.loadConfiguration(reader);
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
for (String key : defaults.getKeys(true)) {
|
for (String key : defaults.getKeys(true)) {
|
||||||
if (!file.contains(key)) {
|
if (!file.contains(key)) {
|
||||||
file.set(key, defaults.get(key));
|
file.set(key, defaults.get(key));
|
||||||
changed = true;
|
changed = true;
|
||||||
plugin.getLogger().info("[ConfigManager] Neuer Key in " + resourceName + " hinzugefügt: " + key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changed) {
|
if (changed) {
|
||||||
file.save(targetFile);
|
file.save(targetFile);
|
||||||
plugin.getLogger().info(resourceName + " wurde mit neuen Standardwerten ergänzt.");
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
plugin.getLogger().severe("Fehler beim Mergen von " + resourceName + ": " + e.getMessage());
|
plugin.getLogger().severe("Merge-Fehler " + resourceName + ": " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setConfigDefaults() {
|
private void setConfigDefaults() {
|
||||||
if (!config.contains("max-doors")) config.set("max-doors", 20);
|
def(config, "max-doors", 20);
|
||||||
if (!config.contains("max-lamps")) config.set("max-lamps", 50);
|
def(config, "max-lamps", 50);
|
||||||
if (!config.contains("max-noteblocks")) config.set("max-noteblocks", 10);
|
def(config, "max-noteblocks", 10);
|
||||||
if (!config.contains("max-gates")) config.set("max-gates", 20);
|
def(config, "max-gates", 20);
|
||||||
if (!config.contains("max-trapdoors")) config.set("max-trapdoors", 20);
|
def(config, "max-trapdoors", 20);
|
||||||
if (!config.contains("max-bells")) config.set("max-bells", 5);
|
def(config, "max-bells", 5);
|
||||||
if (!config.contains("default-note")) config.set("default-note", "PIANO");
|
def(config, "default-note", "PIANO");
|
||||||
if (!config.contains("double-note-enabled")) config.set("double-note-enabled", true);
|
def(config, "double-note-enabled", true);
|
||||||
if (!config.contains("double-note-delay-ms")) config.set("double-note-delay-ms", 1000);
|
def(config, "double-note-delay-ms", 1000);
|
||||||
if (!config.contains("motion-detection-radius")) config.set("motion-detection-radius", 5.0);
|
def(config, "motion-detection-radius", 5.0);
|
||||||
if (!config.contains("motion-close-delay-ms")) config.set("motion-close-delay-ms", 5000);
|
def(config, "motion-close-delay-ms", 5000);
|
||||||
|
def(config, "motion-trigger-cooldown-ms", 2000);
|
||||||
|
|
||||||
|
// Sounds (NEU)
|
||||||
|
def(config, "sounds.enabled", true);
|
||||||
|
def(config, "sounds.door-open", "BLOCK_WOODEN_DOOR_OPEN");
|
||||||
|
def(config, "sounds.door-close", "BLOCK_WOODEN_DOOR_CLOSE");
|
||||||
|
def(config, "sounds.iron-door-open", "BLOCK_IRON_DOOR_OPEN");
|
||||||
|
def(config, "sounds.iron-door-close", "BLOCK_IRON_DOOR_CLOSE");
|
||||||
|
def(config, "sounds.lamp-on", "BLOCK_LEVER_CLICK");
|
||||||
|
def(config, "sounds.lamp-off", "BLOCK_LEVER_CLICK");
|
||||||
|
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setLangDefaults() {
|
private void setLangDefaults() {
|
||||||
if (!lang.contains("tueren-geoeffnet")) lang.set("tueren-geoeffnet", "§aTüren wurden geöffnet.");
|
// Türen (Holz)
|
||||||
if (!lang.contains("tueren-geschlossen")) lang.set("tueren-geschlossen", "§cTüren wurden geschlossen.");
|
def(lang, "tueren-geoeffnet", "§aTüren wurden geöffnet.");
|
||||||
if (!lang.contains("max-tueren-erreicht")) lang.set("max-tueren-erreicht", "§cMaximale Anzahl an Türen erreicht.");
|
def(lang, "tueren-geschlossen", "§cTüren wurden geschlossen.");
|
||||||
|
def(lang, "max-tueren-erreicht", "§cMaximale Anzahl an Türen erreicht.");
|
||||||
if (!lang.contains("lampen-eingeschaltet")) lang.set("lampen-eingeschaltet", "§aLampen wurden eingeschaltet.");
|
// Eisentüren (NEU)
|
||||||
if (!lang.contains("lampen-ausgeschaltet")) lang.set("lampen-ausgeschaltet", "§cLampen wurden ausgeschaltet.");
|
def(lang, "eisentueren-geoeffnet", "§aEisentüren wurden geöffnet.");
|
||||||
if (!lang.contains("max-lampen-erreicht")) lang.set("max-lampen-erreicht", "§cMaximale Anzahl an Lampen erreicht.");
|
def(lang, "eisentueren-geschlossen", "§cEisentüren wurden geschlossen.");
|
||||||
|
def(lang, "eisenfallturen-geoeffnet", "§aEisen-Falltüren wurden geöffnet.");
|
||||||
if (!lang.contains("notenblock-ausgeloest")) lang.set("notenblock-ausgeloest", "§aNotenblock-Klingel wurde ausgelöst.");
|
def(lang, "eisenfallturen-geschlossen", "§cEisen-Falltüren wurden geschlossen.");
|
||||||
if (!lang.contains("max-notenbloecke-erreicht")) lang.set("max-notenbloecke-erreicht", "§cMaximale Anzahl an Notenblöcken erreicht.");
|
// Zauntore
|
||||||
|
def(lang, "gates-geoeffnet", "§aZauntore wurden geöffnet.");
|
||||||
if (!lang.contains("gates-geoeffnet")) lang.set("gates-geoeffnet", "§aZauntore wurden geöffnet.");
|
def(lang, "gates-geschlossen", "§cZauntore wurden geschlossen.");
|
||||||
if (!lang.contains("gates-geschlossen")) lang.set("gates-geschlossen", "§cZauntore wurden geschlossen.");
|
def(lang, "max-gates-erreicht", "§cMaximale Anzahl an Zauntoren erreicht.");
|
||||||
if (!lang.contains("max-gates-erreicht")) lang.set("max-gates-erreicht", "§cMaximale Anzahl an Zauntoren erreicht.");
|
// Falltüren
|
||||||
|
def(lang, "fallturen-geoeffnet", "§aFalltüren wurden geöffnet.");
|
||||||
if (!lang.contains("fallturen-geoeffnet")) lang.set("fallturen-geoeffnet", "§aFalltüren wurden geöffnet.");
|
def(lang, "fallturen-geschlossen", "§cFalltüren wurden geschlossen.");
|
||||||
if (!lang.contains("fallturen-geschlossen")) lang.set("fallturen-geschlossen", "§cFalltüren wurden geschlossen.");
|
def(lang, "max-fallturen-erreicht", "§cMaximale Anzahl an Falltüren erreicht.");
|
||||||
if (!lang.contains("max-fallturen-erreicht")) lang.set("max-fallturen-erreicht", "§cMaximale Anzahl an Falltüren erreicht.");
|
// Lampen
|
||||||
|
def(lang, "lampen-eingeschaltet", "§aLampen wurden eingeschaltet.");
|
||||||
if (!lang.contains("glocke-gelaeutet")) lang.set("glocke-gelaeutet", "§eGlocke wurde geläutet.");
|
def(lang, "lampen-ausgeschaltet", "§cLampen wurden ausgeschaltet.");
|
||||||
if (!lang.contains("max-glocken-erreicht")) lang.set("max-glocken-erreicht", "§cMaximale Anzahl an Glocken erreicht.");
|
def(lang, "max-lampen-erreicht", "§cMaximale Anzahl an Lampen erreicht.");
|
||||||
|
// Glocken
|
||||||
if (!lang.contains("bloecke-umgeschaltet")) lang.set("bloecke-umgeschaltet", "§eBlöcke wurden umgeschaltet.");
|
def(lang, "glocke-gelaeutet", "§aGlocke wurde geläutet.");
|
||||||
if (!lang.contains("keine-bloecke-verbunden")) lang.set("keine-bloecke-verbunden", "§cKeine Blöcke sind verbunden.");
|
def(lang, "max-glocken-erreicht", "§cMaximale Anzahl an Glocken erreicht.");
|
||||||
if (!lang.contains("block-verbunden")) lang.set("block-verbunden", "§aBlock verbunden.");
|
// Notenblöcke
|
||||||
if (!lang.contains("block-bereits-verbunden")) lang.set("block-bereits-verbunden", "§cBlock ist bereits verbunden.");
|
def(lang, "notenblock-ausgeloest", "§aNotenblock-Klingel wurde ausgelöst.");
|
||||||
if (!lang.contains("controller-platziert")) lang.set("controller-platziert", "§aController platziert.");
|
def(lang, "instrument-gesetzt", "§aDein Instrument wurde auf %s gesetzt.");
|
||||||
if (!lang.contains("controller-entfernt")) lang.set("controller-entfernt", "§cController entfernt.");
|
def(lang, "ungueltiges-instrument", "§cUngültiges Instrument! /bc note <Instrument>");
|
||||||
if (!lang.contains("instrument-gesetzt")) lang.set("instrument-gesetzt", "§aDein Notenblock-Instrument wurde auf %s gesetzt.");
|
def(lang, "max-notenbloecke-erreicht", "§cMaximale Anzahl an Notenblöcken erreicht.");
|
||||||
if (!lang.contains("ungueltiges-instrument")) lang.set("ungueltiges-instrument", "§cUngültiges Instrument! Verwende: /bc note <Instrument>");
|
// Kolben (vorbereitet)
|
||||||
if (!lang.contains("konfiguration-reloaded")) lang.set("konfiguration-reloaded", "§aKonfiguration und Daten erfolgreich neu geladen!");
|
def(lang, "kolben-ausgefahren", "§6[ButtonControl] §7Kolben wurden ausgefahren.");
|
||||||
if (!lang.contains("keine-berechtigung")) lang.set("keine-berechtigung", "§cDu hast keine Berechtigung für diesen Befehl!");
|
def(lang, "kolben-eingefahren", "§6[ButtonControl] §7Kolben wurden eingezogen.");
|
||||||
if (!lang.contains("kolben-ausgefahren")) lang.set("kolben-ausgefahren", "§6[ButtonControl] §7Kolben wurden ausgefahren.");
|
def(lang, "max-kolben-erreicht", "§6[ButtonControl] §7Maximale Anzahl an Kolben erreicht.");
|
||||||
if (!lang.contains("kolben-eingefahren")) lang.set("kolben-eingefahren", "§6[ButtonControl] §7Kolben wurden eingezogen.");
|
// Controller
|
||||||
if (!lang.contains("max-kolben-erreicht")) lang.set("max-kolben-erreicht", "§6[ButtonControl] §7Maximale Anzahl an Kolben erreicht.");
|
def(lang, "block-verbunden", "§aBlock verbunden.");
|
||||||
|
def(lang, "block-bereits-verbunden", "§cBlock ist bereits verbunden.");
|
||||||
|
def(lang, "block-verbindung-entfernt", "§7Verbindung zu abgebautem Block automatisch entfernt.");
|
||||||
|
def(lang, "keine-bloecke-verbunden", "§cKeine Blöcke sind verbunden.");
|
||||||
|
def(lang, "bloecke-umgeschaltet", "§eBlöcke wurden umgeschaltet.");
|
||||||
|
def(lang, "controller-platziert", "§aController platziert.");
|
||||||
|
def(lang, "controller-entfernt", "§cController entfernt.");
|
||||||
|
def(lang, "controller-umbenannt", "§aController umbenannt zu: §f%s"); // NEU
|
||||||
|
// Trust & Berechtigungen
|
||||||
|
def(lang, "keine-berechtigung", "§cDu hast keine Berechtigung!");
|
||||||
|
def(lang, "keine-berechtigung-controller", "§cDu darfst diesen Controller nicht benutzen!");
|
||||||
|
def(lang, "nur-besitzer-abbauen", "§cNur der Besitzer kann diesen Controller verwalten!");
|
||||||
|
def(lang, "spieler-nicht-gefunden", "§cSpieler nicht gefunden.");
|
||||||
|
def(lang, "status-geandert", "§6[BC] §7Controller ist nun %s§7.");
|
||||||
|
def(lang, "trust-hinzugefuegt", "§a%s darf diesen Controller nun benutzen.");
|
||||||
|
def(lang, "trust-entfernt", "§c%s wurde das Vertrauen entzogen.");
|
||||||
|
def(lang, "kein-controller-im-blick", "§cBitte sieh einen Controller direkt an!");
|
||||||
|
// System
|
||||||
|
// FIX: Korrekte Key-Bezeichnung (war fälschlich "konfiguration-reloaded")
|
||||||
|
def(lang, "konfiguration-neugeladen", "§aKonfiguration erfolgreich neu geladen!");
|
||||||
saveLang();
|
saveLang();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void def(FileConfiguration cfg, String key, Object value) {
|
||||||
|
if (!cfg.contains(key)) cfg.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
public void reloadConfig() {
|
public void reloadConfig() {
|
||||||
config = YamlConfiguration.loadConfiguration(configFile);
|
config = YamlConfiguration.loadConfiguration(configFile);
|
||||||
lang = YamlConfiguration.loadConfiguration(langFile);
|
lang = YamlConfiguration.loadConfiguration(langFile);
|
||||||
@@ -130,34 +158,26 @@ public class ConfigManager {
|
|||||||
setLangDefaults();
|
setLangDefaults();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileConfiguration getConfig() {
|
public FileConfiguration getConfig() { return config; }
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxDoors() { return config.getInt("max-doors", 20); }
|
public int getMaxDoors() { return config.getInt("max-doors", 20); }
|
||||||
public int getMaxLamps() { return config.getInt("max-lamps", 50); }
|
public int getMaxLamps() { return config.getInt("max-lamps", 50); }
|
||||||
public int getMaxNoteBlocks() { return config.getInt("max-noteblocks", 10); }
|
public int getMaxNoteBlocks() { return config.getInt("max-noteblocks", 10); }
|
||||||
public int getMaxGates() { return config.getInt("max-gates", getMaxDoors()); }
|
public int getMaxGates() { return config.getInt("max-gates", 20); }
|
||||||
public int getMaxTrapdoors() { return config.getInt("max-trapdoors", getMaxDoors()); }
|
public int getMaxTrapdoors() { return config.getInt("max-trapdoors", 20); }
|
||||||
public int getMaxBells() { return config.getInt("max-bells", 5); }
|
public int getMaxBells() { return config.getInt("max-bells", 5); }
|
||||||
|
|
||||||
public String getMessage(String key) {
|
public String getMessage(String key) {
|
||||||
return lang.getString(key, "§cNachricht nicht gefunden: " + key);
|
return lang.getString(key, "§cNachricht fehlt: " + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveConfig() {
|
public void saveConfig() {
|
||||||
try {
|
try { config.save(configFile); }
|
||||||
config.save(configFile);
|
catch (IOException e) { plugin.getLogger().severe("config.yml Fehler: " + e.getMessage()); }
|
||||||
} catch (IOException e) {
|
|
||||||
plugin.getLogger().severe("Konnte config.yml nicht speichern: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveLang() {
|
public void saveLang() {
|
||||||
try {
|
try { lang.save(langFile); }
|
||||||
lang.save(langFile);
|
catch (IOException e) { plugin.getLogger().severe("lang.yml Fehler: " + e.getMessage()); }
|
||||||
} catch (IOException e) {
|
|
||||||
plugin.getLogger().severe("Konnte lang.yml nicht speichern: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import org.bukkit.configuration.file.FileConfiguration;
|
|||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -21,9 +22,7 @@ public class DataManager {
|
|||||||
|
|
||||||
private void loadData() {
|
private void loadData() {
|
||||||
dataFile = new File(plugin.getDataFolder(), "data.yml");
|
dataFile = new File(plugin.getDataFolder(), "data.yml");
|
||||||
if (!dataFile.exists()) {
|
if (!dataFile.exists()) plugin.saveResource("data.yml", false);
|
||||||
plugin.saveResource("data.yml", false);
|
|
||||||
}
|
|
||||||
data = YamlConfiguration.loadConfiguration(dataFile);
|
data = YamlConfiguration.loadConfiguration(dataFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,38 +30,154 @@ public class DataManager {
|
|||||||
data = YamlConfiguration.loadConfiguration(dataFile);
|
data = YamlConfiguration.loadConfiguration(dataFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getButtonIdForLocation(String location) {
|
// -----------------------------------------------------------------------
|
||||||
return getButtonIdForPlacedController(location);
|
// Zugriff & Berechtigungen
|
||||||
}
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
public boolean canAccess(String buttonId, UUID playerUUID) {
|
public boolean canAccess(String buttonId, UUID playerUUID) {
|
||||||
if (isPublic(buttonId)) return true;
|
if (isPublic(buttonId)) return true;
|
||||||
if (isOwner(buttonId, playerUUID)) return true;
|
if (isOwner(buttonId, playerUUID)) return true;
|
||||||
List<String> trusted = data.getStringList("trust." + buttonId);
|
return data.getStringList("trust." + buttonId).contains(playerUUID.toString());
|
||||||
return trusted.contains(playerUUID.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOwner(String buttonId, UUID playerUUID) {
|
public boolean isOwner(String buttonId, UUID playerUUID) {
|
||||||
// Wir prüfen direkt im globalen Pfad der Buttons
|
return data.contains("players." + playerUUID + ".buttons." + buttonId)
|
||||||
return data.contains("players." + playerUUID.toString() + ".buttons." + buttonId);
|
|| data.contains("players." + playerUUID + ".placed-controllers");
|
||||||
|
// Zweite Bedingung: prüft ob irgendein placed-controller dieser UUID die buttonId enthält
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Controller-Verwaltung
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
public String getButtonIdForLocation(String location) {
|
||||||
|
return getButtonIdForPlacedController(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerController(String location, UUID ownerUUID, String buttonId) {
|
public void registerController(String location, UUID ownerUUID, String buttonId) {
|
||||||
addPlacedController(ownerUUID.toString(), location, buttonId);
|
data.set("players." + ownerUUID + ".placed-controllers." + location, buttonId);
|
||||||
|
// Leere buttons-Liste anlegen damit isOwner() sofort greift
|
||||||
|
if (!data.contains("players." + ownerUUID + ".buttons." + buttonId)) {
|
||||||
|
data.set("players." + ownerUUID + ".buttons." + buttonId, new ArrayList<>());
|
||||||
|
}
|
||||||
|
saveData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeController(String location) {
|
public void removeController(String location) {
|
||||||
if (data.getConfigurationSection("players") == null) return;
|
if (data.getConfigurationSection("players") == null) return;
|
||||||
for (String playerUUID : data.getConfigurationSection("players").getKeys(false)) {
|
for (String uuid : data.getConfigurationSection("players").getKeys(false)) {
|
||||||
String path = "players." + playerUUID + ".placed-controllers." + location;
|
String path = "players." + uuid + ".placed-controllers." + location;
|
||||||
if (data.contains(path)) {
|
if (data.contains(path)) data.set(path, null);
|
||||||
data.set(path, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
removeMotionSensorSettings(location);
|
removeMotionSensorSettings(location);
|
||||||
saveData();
|
saveData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getButtonIdForPlacedController(String location) {
|
||||||
|
if (data.getConfigurationSection("players") == null) return null;
|
||||||
|
for (String uuid : data.getConfigurationSection("players").getKeys(false)) {
|
||||||
|
String val = data.getString("players." + uuid + ".placed-controllers." + location);
|
||||||
|
if (val != null) return val;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getAllPlacedControllers() {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
if (data.getConfigurationSection("players") == null) return result;
|
||||||
|
for (String uuid : data.getConfigurationSection("players").getKeys(false)) {
|
||||||
|
var sec = data.getConfigurationSection("players." + uuid + ".placed-controllers");
|
||||||
|
if (sec != null) result.addAll(sec.getKeys(false));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Verbundene Blöcke
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
public void setConnectedBlocks(String playerUUID, String buttonId, List<String> blocks) {
|
||||||
|
data.set("players." + playerUUID + ".buttons." + buttonId, blocks);
|
||||||
|
saveData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getConnectedBlocks(String buttonId) {
|
||||||
|
if (data.getConfigurationSection("players") == null) return new ArrayList<>();
|
||||||
|
for (String uuid : data.getConfigurationSection("players").getKeys(false)) {
|
||||||
|
String path = "players." + uuid + ".buttons." + buttonId;
|
||||||
|
if (data.contains(path)) return data.getStringList(path);
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entfernt eine Block-Location aus ALLEN Verbindungslisten aller Controller.
|
||||||
|
* Wird aufgerufen wenn ein verbundener Block abgebaut wird.
|
||||||
|
*/
|
||||||
|
public boolean removeFromAllConnectedBlocks(String locStr) {
|
||||||
|
if (data.getConfigurationSection("players") == null) return false;
|
||||||
|
boolean changed = false;
|
||||||
|
for (String uuid : data.getConfigurationSection("players").getKeys(false)) {
|
||||||
|
var buttonsSection = data.getConfigurationSection("players." + uuid + ".buttons");
|
||||||
|
if (buttonsSection == null) continue;
|
||||||
|
for (String buttonId : buttonsSection.getKeys(false)) {
|
||||||
|
String path = "players." + uuid + ".buttons." + buttonId;
|
||||||
|
List<String> blocks = data.getStringList(path);
|
||||||
|
if (blocks.remove(locStr)) {
|
||||||
|
data.set(path, blocks);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) saveData();
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Controller-Name (NEU)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
public void setControllerName(String buttonId, String name) {
|
||||||
|
data.set("names." + buttonId, name);
|
||||||
|
saveData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getControllerName(String buttonId) {
|
||||||
|
return data.getString("names." + buttonId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Zeitplan (NEU)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
public void setScheduleOpenTime(String buttonId, long ticks) {
|
||||||
|
data.set("schedules." + buttonId + ".open-time", ticks);
|
||||||
|
saveData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getScheduleOpenTime(String buttonId) {
|
||||||
|
return data.getLong("schedules." + buttonId + ".open-time", -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScheduleCloseTime(String buttonId, long ticks) {
|
||||||
|
data.set("schedules." + buttonId + ".close-time", ticks);
|
||||||
|
saveData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getScheduleCloseTime(String buttonId) {
|
||||||
|
return data.getLong("schedules." + buttonId + ".close-time", -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Entfernt den kompletten Zeitplan für einen Controller. */
|
||||||
|
public void clearSchedule(String buttonId) {
|
||||||
|
data.set("schedules." + buttonId, null);
|
||||||
|
saveData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Trust & Public/Private
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
public void addTrustedPlayer(String buttonId, UUID targetUUID) {
|
public void addTrustedPlayer(String buttonId, UUID targetUUID) {
|
||||||
List<String> trusted = data.getStringList("trust." + buttonId);
|
List<String> trusted = data.getStringList("trust." + buttonId);
|
||||||
if (!trusted.contains(targetUUID.toString())) {
|
if (!trusted.contains(targetUUID.toString())) {
|
||||||
@@ -88,60 +203,23 @@ public class DataManager {
|
|||||||
return data.getBoolean("public-status." + buttonId, false);
|
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<String> blocks) {
|
// Instrumente
|
||||||
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 location) {
|
|
||||||
if (data.getConfigurationSection("players") == null) return null;
|
|
||||||
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<String> getConnectedBlocks(String buttonId) {
|
|
||||||
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 new ArrayList<>(); // Niemals null zurückgeben, um Fehler im Listener zu vermeiden
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getAllPlacedControllers() {
|
|
||||||
List<String> 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) {
|
public void setPlayerInstrument(UUID playerUUID, String instrument) {
|
||||||
data.set("players." + playerUUID.toString() + ".instrument", instrument);
|
data.set("players." + playerUUID + ".instrument", instrument);
|
||||||
saveData();
|
saveData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPlayerInstrument(UUID playerUUID) {
|
public String getPlayerInstrument(UUID playerUUID) {
|
||||||
return data.getString("players." + playerUUID.toString() + ".instrument");
|
return data.getString("players." + playerUUID + ".instrument");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Motion-Sensor-Einstellungen
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
public void setMotionSensorRadius(String location, double radius) {
|
public void setMotionSensorRadius(String location, double radius) {
|
||||||
data.set("motion-sensors." + location + ".radius", radius);
|
data.set("motion-sensors." + location + ".radius", radius);
|
||||||
saveData();
|
saveData();
|
||||||
@@ -165,11 +243,28 @@ public class DataManager {
|
|||||||
saveData();
|
saveData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Speichern – asynchron
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialisiert die Daten synchron (thread-safe), schreibt dann asynchron auf Disk.
|
||||||
|
* Verhindert I/O-Lags auf dem Main-Thread.
|
||||||
|
*/
|
||||||
public void saveData() {
|
public void saveData() {
|
||||||
|
final String serialized;
|
||||||
try {
|
try {
|
||||||
data.save(dataFile);
|
serialized = data.saveToString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getLogger().severe("Serialisierungsfehler data.yml: " + e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
|
try (FileWriter fw = new FileWriter(dataFile, false)) {
|
||||||
|
fw.write(serialized);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
plugin.getLogger().severe("Konnte data.yml nicht speichern: " + e.getMessage());
|
plugin.getLogger().severe("Konnte data.yml nicht speichern: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
216
src/main/java/viper/ScheduleGUI.java
Normal file
216
src/main/java/viper/ScheduleGUI.java
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
package viper;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GUI zur Konfiguration der zeitgesteuerten Automatisierung eines Controllers.
|
||||||
|
*
|
||||||
|
* Layout (9×3 = 27 Slots):
|
||||||
|
* Slot 10 – Öffnungszeit (LIME_DYE / Sonne) ← Links/Rechts: ±1h | Shift: ±15min
|
||||||
|
* Slot 13 – Aktivierung an/aus (LEVER)
|
||||||
|
* Slot 16 – Schließzeit (RED_DYE / Mond) ← Links/Rechts: ±1h | Shift: ±15min
|
||||||
|
* Slot 22 – Speichern & Schließen (EMERALD)
|
||||||
|
*
|
||||||
|
* Zeit wird als Ingame-Ticks gespeichert (0–23999).
|
||||||
|
* ticksToTime() / timeToTicks() in ButtonControl konvertieren nach "HH:MM".
|
||||||
|
*/
|
||||||
|
public class ScheduleGUI implements Listener {
|
||||||
|
private final ButtonControl plugin;
|
||||||
|
private final DataManager dataManager;
|
||||||
|
private final Player player;
|
||||||
|
private final String buttonId;
|
||||||
|
private final Inventory inv;
|
||||||
|
|
||||||
|
// Aktuelle Werte während die GUI offen ist
|
||||||
|
private long openTime;
|
||||||
|
private long closeTime;
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
|
public ScheduleGUI(ButtonControl plugin, Player player, String buttonId) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.dataManager = plugin.getDataManager();
|
||||||
|
this.player = player;
|
||||||
|
this.buttonId = buttonId;
|
||||||
|
this.inv = Bukkit.createInventory(null, 27, "§6Zeitplan-Einstellungen");
|
||||||
|
|
||||||
|
// Gespeicherte Werte laden (oder Standardwerte)
|
||||||
|
long savedOpen = dataManager.getScheduleOpenTime(buttonId);
|
||||||
|
long savedClose = dataManager.getScheduleCloseTime(buttonId);
|
||||||
|
this.openTime = savedOpen >= 0 ? savedOpen : plugin.timeToTicks(7, 0); // 07:00
|
||||||
|
this.closeTime = savedClose >= 0 ? savedClose : plugin.timeToTicks(19, 0); // 19:00
|
||||||
|
this.enabled = savedOpen >= 0;
|
||||||
|
|
||||||
|
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open() {
|
||||||
|
renderItems();
|
||||||
|
player.openInventory(inv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// GUI aufbauen
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
private void renderItems() {
|
||||||
|
// Füllung
|
||||||
|
ItemStack filler = makeItem(Material.GRAY_STAINED_GLASS_PANE, ChatColor.RESET + "");
|
||||||
|
for (int i = 0; i < 27; i++) inv.setItem(i, filler);
|
||||||
|
|
||||||
|
// Slot 10 – Öffnungszeit
|
||||||
|
inv.setItem(10, makeTimeItem(
|
||||||
|
Material.LIME_DYE,
|
||||||
|
"§a§lÖffnungszeit",
|
||||||
|
openTime,
|
||||||
|
"§7Linksklick: §f+1 Stunde",
|
||||||
|
"§7Rechtsklick: §f−1 Stunde",
|
||||||
|
"§7Shift+Links: §f+15 Minuten",
|
||||||
|
"§7Shift+Rechts: §f−15 Minuten"
|
||||||
|
));
|
||||||
|
|
||||||
|
// Slot 13 – Aktivierung an/aus
|
||||||
|
Material leverMat = enabled ? Material.LEVER : Material.DEAD_BUSH;
|
||||||
|
String leverName = enabled ? "§a§lZeitplan aktiv" : "§c§lZeitplan deaktiviert";
|
||||||
|
String leverDesc = enabled ? "§7Klick zum §cDeaktivieren" : "§7Klick zum §aAktivieren";
|
||||||
|
inv.setItem(13, makeItem(leverMat, leverName, leverDesc, "§8(Zeitplan wird gespeichert)"));
|
||||||
|
|
||||||
|
// Slot 16 – Schließzeit
|
||||||
|
inv.setItem(16, makeTimeItem(
|
||||||
|
Material.RED_DYE,
|
||||||
|
"§c§lSchließzeit",
|
||||||
|
closeTime,
|
||||||
|
"§7Linksklick: §f+1 Stunde",
|
||||||
|
"§7Rechtsklick: §f−1 Stunde",
|
||||||
|
"§7Shift+Links: §f+15 Minuten",
|
||||||
|
"§7Shift+Rechts: §f−15 Minuten"
|
||||||
|
));
|
||||||
|
|
||||||
|
// Slot 22 – Speichern
|
||||||
|
inv.setItem(22, makeItem(Material.EMERALD,
|
||||||
|
"§a§lSpeichern & Schließen",
|
||||||
|
"§7Speichert den aktuellen Zeitplan."));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemStack makeTimeItem(Material mat, String name, long ticks, String... loreLines) {
|
||||||
|
String timeStr = plugin.ticksToTime(ticks);
|
||||||
|
List<String> lore = new ArrayList<>();
|
||||||
|
lore.add("§e§l" + timeStr + " Uhr §7(Ingame)");
|
||||||
|
lore.add("");
|
||||||
|
lore.addAll(Arrays.asList(loreLines));
|
||||||
|
ItemStack item = new ItemStack(mat);
|
||||||
|
ItemMeta meta = item.getItemMeta();
|
||||||
|
if (meta != null) {
|
||||||
|
meta.setDisplayName(name);
|
||||||
|
meta.setLore(lore);
|
||||||
|
item.setItemMeta(meta);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemStack makeItem(Material mat, String name, String... lore) {
|
||||||
|
ItemStack item = new ItemStack(mat);
|
||||||
|
ItemMeta meta = item.getItemMeta();
|
||||||
|
if (meta != null) {
|
||||||
|
meta.setDisplayName(name);
|
||||||
|
meta.setLore(Arrays.asList(lore));
|
||||||
|
item.setItemMeta(meta);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Event-Handler
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onInventoryClick(InventoryClickEvent event) {
|
||||||
|
if (!event.getInventory().equals(inv)) return;
|
||||||
|
if (!event.getWhoClicked().equals(player)) return;
|
||||||
|
event.setCancelled(true);
|
||||||
|
|
||||||
|
ItemStack clicked = event.getCurrentItem();
|
||||||
|
if (clicked == null || clicked.getType() == Material.GRAY_STAINED_GLASS_PANE) return;
|
||||||
|
|
||||||
|
int slot = event.getRawSlot();
|
||||||
|
// Nur Klicks in unserer GUI (0–26) verarbeiten
|
||||||
|
if (slot < 0 || slot > 26) return;
|
||||||
|
|
||||||
|
// Schrittgröße: Shift = 15 Min (250 Ticks), sonst 1 Std (1000 Ticks)
|
||||||
|
long step = event.isShiftClick() ? 250L : 1000L;
|
||||||
|
|
||||||
|
if (slot == 10) {
|
||||||
|
// Öffnungszeit anpassen
|
||||||
|
if (event.isLeftClick()) openTime = (openTime + step + 24000) % 24000;
|
||||||
|
if (event.isRightClick()) openTime = (openTime - step + 24000) % 24000;
|
||||||
|
inv.setItem(10, makeTimeItem(Material.LIME_DYE, "§a§lÖffnungszeit", openTime,
|
||||||
|
"§7Linksklick: §f+1 Stunde", "§7Rechtsklick: §f−1 Stunde",
|
||||||
|
"§7Shift+Links: §f+15 Minuten", "§7Shift+Rechts: §f−15 Minuten"));
|
||||||
|
|
||||||
|
} else if (slot == 13) {
|
||||||
|
// Aktivierung umschalten
|
||||||
|
enabled = !enabled;
|
||||||
|
renderItems();
|
||||||
|
|
||||||
|
} else if (slot == 16) {
|
||||||
|
// Schließzeit anpassen
|
||||||
|
if (event.isLeftClick()) closeTime = (closeTime + step + 24000) % 24000;
|
||||||
|
if (event.isRightClick()) closeTime = (closeTime - step + 24000) % 24000;
|
||||||
|
inv.setItem(16, makeTimeItem(Material.RED_DYE, "§c§lSchließzeit", closeTime,
|
||||||
|
"§7Linksklick: §f+1 Stunde", "§7Rechtsklick: §f−1 Stunde",
|
||||||
|
"§7Shift+Links: §f+15 Minuten", "§7Shift+Rechts: §f−15 Minuten"));
|
||||||
|
|
||||||
|
} else if (slot == 22) {
|
||||||
|
// Speichern
|
||||||
|
save();
|
||||||
|
player.closeInventory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onInventoryDrag(InventoryDragEvent event) {
|
||||||
|
if (event.getInventory().equals(inv) && event.getWhoClicked().equals(player))
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onInventoryClose(InventoryCloseEvent event) {
|
||||||
|
if (!event.getPlayer().equals(player) || !event.getInventory().equals(inv)) return;
|
||||||
|
InventoryClickEvent.getHandlerList().unregister(this);
|
||||||
|
InventoryDragEvent.getHandlerList().unregister(this);
|
||||||
|
InventoryCloseEvent.getHandlerList().unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Speichern
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
private void save() {
|
||||||
|
if (enabled) {
|
||||||
|
dataManager.setScheduleOpenTime(buttonId, openTime);
|
||||||
|
dataManager.setScheduleCloseTime(buttonId, closeTime);
|
||||||
|
player.sendMessage("§a[BC] §7Zeitplan gespeichert: §aÖffnet §7um §e"
|
||||||
|
+ plugin.ticksToTime(openTime)
|
||||||
|
+ " §7· §cSchließt §7um §e"
|
||||||
|
+ plugin.ticksToTime(closeTime));
|
||||||
|
} else {
|
||||||
|
dataManager.clearSchedule(buttonId);
|
||||||
|
player.sendMessage("§7[BC] Zeitplan deaktiviert.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# Maximale Anzahl an steuerbaren Blöcken pro Controller
|
# ButtonControl – Konfiguration
|
||||||
|
|
||||||
|
# ── Maximale Anzahl verbundener Blöcke pro Controller ──────────────────────
|
||||||
max-doors: 20
|
max-doors: 20
|
||||||
max-lamps: 50
|
max-lamps: 50
|
||||||
max-noteblocks: 10
|
max-noteblocks: 10
|
||||||
@@ -6,13 +8,28 @@ max-gates: 20
|
|||||||
max-trapdoors: 20
|
max-trapdoors: 20
|
||||||
max-bells: 5
|
max-bells: 5
|
||||||
|
|
||||||
# Standard-Instrument für Notenblöcke
|
# ── Notenblöcke ─────────────────────────────────────────────────────────────
|
||||||
default-note: "PIANO"
|
default-note: "PIANO"
|
||||||
|
|
||||||
# Doppelter Notenblock-Ton
|
|
||||||
double-note-enabled: true
|
double-note-enabled: true
|
||||||
double-note-delay-ms: 1000
|
double-note-delay-ms: 1000
|
||||||
|
|
||||||
# Bewegungsmelder-Einstellungen
|
# ── Bewegungsmelder ──────────────────────────────────────────────────────────
|
||||||
|
# Gilt als Standardwert wenn für einen Sensor nichts individuell gesetzt wurde
|
||||||
motion-detection-radius: 5.0
|
motion-detection-radius: 5.0
|
||||||
motion-close-delay-ms: 5000
|
motion-close-delay-ms: 5000
|
||||||
|
# Cooldown: wie lange nach dem Schließen der Sensor nicht erneut auslöst
|
||||||
|
motion-trigger-cooldown-ms: 2000
|
||||||
|
|
||||||
|
# ── Sounds beim Öffnen/Schließen (NEU) ──────────────────────────────────────
|
||||||
|
# Auf false setzen um alle Controller-Sounds zu deaktivieren
|
||||||
|
sounds:
|
||||||
|
enabled: true
|
||||||
|
# Holztüren, Zauntore, Falltüren
|
||||||
|
door-open: BLOCK_WOODEN_DOOR_OPEN
|
||||||
|
door-close: BLOCK_WOODEN_DOOR_CLOSE
|
||||||
|
# Eisentüren und Eisenfalltüren
|
||||||
|
iron-door-open: BLOCK_IRON_DOOR_OPEN
|
||||||
|
iron-door-close: BLOCK_IRON_DOOR_CLOSE
|
||||||
|
# Redstone-Lampen
|
||||||
|
lamp-on: BLOCK_LEVER_CLICK
|
||||||
|
lamp-off: BLOCK_LEVER_CLICK
|
||||||
@@ -1,57 +1,64 @@
|
|||||||
# lang.yml - ButtonControl Nachrichten
|
# lang.yml – ButtonControl Nachrichten
|
||||||
|
|
||||||
# --- Bestehende Nachrichten (Türen/Tore/Falltüren) ---
|
# ── Holztüren ────────────────────────────────────────────────────────────────
|
||||||
tueren-geoeffnet: "§aTüren wurden geöffnet."
|
tueren-geoeffnet: "§aTüren wurden geöffnet."
|
||||||
tueren-geschlossen: "§cTüren wurden geschlossen."
|
tueren-geschlossen: "§cTüren wurden geschlossen."
|
||||||
max-tueren-erreicht: "§cMaximale Anzahl an Türen erreicht."
|
max-tueren-erreicht: "§cMaximale Anzahl an Türen erreicht."
|
||||||
|
|
||||||
|
# ── Eisentüren (NEU) ─────────────────────────────────────────────────────────
|
||||||
|
eisentueren-geoeffnet: "§aEisentüren wurden geöffnet."
|
||||||
|
eisentueren-geschlossen: "§cEisentüren wurden geschlossen."
|
||||||
|
eisenfallturen-geoeffnet: "§aEisen-Falltüren wurden geöffnet."
|
||||||
|
eisenfallturen-geschlossen: "§cEisen-Falltüren wurden geschlossen."
|
||||||
|
|
||||||
|
# ── Zauntore ─────────────────────────────────────────────────────────────────
|
||||||
gates-geoeffnet: "§aZauntore wurden geöffnet."
|
gates-geoeffnet: "§aZauntore wurden geöffnet."
|
||||||
gates-geschlossen: "§cZauntore wurden geschlossen."
|
gates-geschlossen: "§cZauntore wurden geschlossen."
|
||||||
max-gates-erreicht: "§cMaximale Anzahl an Zauntoren erreicht."
|
max-gates-erreicht: "§cMaximale Anzahl an Zauntoren erreicht."
|
||||||
|
|
||||||
|
# ── Falltüren ────────────────────────────────────────────────────────────────
|
||||||
fallturen-geoeffnet: "§aFalltüren wurden geöffnet."
|
fallturen-geoeffnet: "§aFalltüren wurden geöffnet."
|
||||||
fallturen-geschlossen: "§cFalltüren wurden geschlossen."
|
fallturen-geschlossen: "§cFalltüren wurden geschlossen."
|
||||||
max-fallturen-erreicht: "§cMaximale Anzahl an Falltüren erreicht."
|
max-fallturen-erreicht: "§cMaximale Anzahl an Falltüren erreicht."
|
||||||
|
|
||||||
# --- Lampen & Glocken ---
|
# ── Lampen & Glocken ─────────────────────────────────────────────────────────
|
||||||
lampen-eingeschaltet: "§aLampen wurden eingeschaltet."
|
lampen-eingeschaltet: "§aLampen wurden eingeschaltet."
|
||||||
lampen-ausgeschaltet: "§cLampen wurden ausgeschaltet."
|
lampen-ausgeschaltet: "§cLampen wurden ausgeschaltet."
|
||||||
max-lampen-erreicht: "§cMaximale Anzahl an Lampen erreicht."
|
max-lampen-erreicht: "§cMaximale Anzahl an Lampen erreicht."
|
||||||
glocke-gelaeutet: "§aGlocke wurde geläutet."
|
glocke-gelaeutet: "§aGlocke wurde geläutet."
|
||||||
max-glocken-erreicht: "§cMaximale Anzahl an Glocken erreicht."
|
max-glocken-erreicht: "§cMaximale Anzahl an Glocken erreicht."
|
||||||
|
|
||||||
# --- Notenblöcke & Instrumente ---
|
# ── Notenblöcke ──────────────────────────────────────────────────────────────
|
||||||
notenblock-ausgeloest: "§aNotenblock-Klingel wurde ausgelöst."
|
notenblock-ausgeloest: "§aNotenblock-Klingel wurde ausgelöst."
|
||||||
instrument-gesetzt: "§aDein Notenblock-Instrument wurde auf %s gesetzt."
|
instrument-gesetzt: "§aDein Instrument wurde auf %s gesetzt."
|
||||||
ungueltiges-instrument: "§cUngültiges Instrument! Verwende: /bc note <Instrument>"
|
ungueltiges-instrument: "§cUngültiges Instrument! Verwende: /bc note <Instrument>"
|
||||||
max-notenbloecke-erreicht: "§cMaximale Anzahl an Notenblöcken erreicht."
|
max-notenbloecke-erreicht: "§cMaximale Anzahl an Notenblöcken erreicht."
|
||||||
|
|
||||||
# --- Kolben (Erweiterung) ---
|
# ── Kolben (vorbereitet für zukünftige Erweiterung) ─────────────────────────
|
||||||
kolben-ausgefahren: "§6[ButtonControl] §7Kolben wurden ausgefahren."
|
kolben-ausgefahren: "§6[ButtonControl] §7Kolben wurden ausgefahren."
|
||||||
kolben-eingefahren: "§6[ButtonControl] §7Kolben wurden eingefahren."
|
kolben-eingefahren: "§6[ButtonControl] §7Kolben wurden eingezogen."
|
||||||
max-kolben-erreicht: "§6[ButtonControl] §7Maximale Anzahl an Kolben erreicht."
|
max-kolben-erreicht: "§6[ButtonControl] §7Maximale Anzahl an Kolben erreicht."
|
||||||
|
|
||||||
# --- Controller & Verbindung ---
|
# ── Controller & Verbindung ──────────────────────────────────────────────────
|
||||||
block-verbunden: "§aBlock verbunden."
|
block-verbunden: "§aBlock verbunden."
|
||||||
block-bereits-verbunden: "§cBlock ist bereits verbunden."
|
block-bereits-verbunden: "§cBlock ist bereits verbunden."
|
||||||
|
block-verbindung-entfernt: "§7Verbindung zu abgebautem Block automatisch entfernt."
|
||||||
keine-bloecke-verbunden: "§cKeine Blöcke sind verbunden."
|
keine-bloecke-verbunden: "§cKeine Blöcke sind verbunden."
|
||||||
bloecke-umgeschaltet: "§eBlöcke wurden umgeschaltet."
|
bloecke-umgeschaltet: "§eBlöcke wurden umgeschaltet."
|
||||||
controller-platziert: "§aController platziert."
|
controller-platziert: "§aController platziert."
|
||||||
controller-entfernt: "§cController entfernt."
|
controller-entfernt: "§cController entfernt."
|
||||||
|
controller-umbenannt: "§aController umbenannt zu: §f%s"
|
||||||
|
|
||||||
# --- Trust- & Sicherheitssystem ---
|
# ── Trust- & Sicherheitssystem ───────────────────────────────────────────────
|
||||||
keine-berechtigung: "§cDu hast keine Berechtigung für diesen Befehl!"
|
keine-berechtigung: "§cDu hast keine Berechtigung für diesen Befehl!"
|
||||||
keine-berechtigung-controller: "§cDu hast keine Berechtigung, diesen Controller zu benutzen!"
|
keine-berechtigung-controller: "§cDu darfst diesen Controller nicht benutzen!"
|
||||||
nur-besitzer-abbauen: "§cNur der Besitzer darf diesen Controller verwalten oder abbauen!"
|
nur-besitzer-abbauen: "§cNur der Besitzer kann diesen Controller verwalten!"
|
||||||
spieler-nicht-gefunden: "§cDieser Spieler wurde nicht gefunden."
|
spieler-nicht-gefunden: "§cDieser Spieler wurde nicht gefunden."
|
||||||
|
# %s = "§aÖffentlich" oder "§cPrivat"
|
||||||
|
status-geandert: "§6[BC] §7Controller ist nun %s§7."
|
||||||
|
trust-hinzugefuegt: "§a%s darf diesen Controller nun benutzen."
|
||||||
|
trust-entfernt: "§c%s wurde das Vertrauen entzogen."
|
||||||
|
kein-controller-im-blick: "§cBitte sieh einen Controller direkt an!"
|
||||||
|
|
||||||
# %s wird durch "§aÖffentlich" oder "§cPrivat" ersetzt
|
# ── System ───────────────────────────────────────────────────────────────────
|
||||||
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!"
|
konfiguration-neugeladen: "§aKonfiguration und Daten erfolgreich neu geladen!"
|
||||||
@@ -1,26 +1,32 @@
|
|||||||
name: ButtonControl
|
name: ButtonControl
|
||||||
version: 1.5
|
version: 1.6
|
||||||
main: viper.ButtonControl
|
main: viper.ButtonControl
|
||||||
api-version: 1.21
|
api-version: 1.21
|
||||||
author: M_Viper
|
author: M_Viper
|
||||||
description: Ein Plugin, um Türen, Redstone-Lampen und Notenblöcke mit einem Button, Tageslichtsensor oder Bewegungsmelder zu steuern.
|
description: >
|
||||||
|
Steuert Türen, Eisentüren, Lampen, Notenblöcke und Glocken mit Buttons,
|
||||||
|
Tageslichtsensoren, Bewegungsmeldern, Teppich-Sensoren und Schildern.
|
||||||
|
Unterstützt Zeitpläne, Trust-System und Admin-Bypass.
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
bc:
|
bc:
|
||||||
description: Hauptbefehl für ButtonControl
|
description: Hauptbefehl für ButtonControl
|
||||||
usage: /bc <info|reload|note|trust|untrust|public|private>
|
usage: /bc <info|reload|note|list|rename|schedule|trust|untrust|public|private>
|
||||||
aliases: [buttoncontrol]
|
aliases: [buttoncontrol]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
buttoncontrol.admin:
|
||||||
|
description: Admin-Bypass – Zugriff auf und Verwaltung aller Controller
|
||||||
|
default: op
|
||||||
buttoncontrol.reload:
|
buttoncontrol.reload:
|
||||||
description: Erlaubt das Neuladen der Plugin-Konfiguration
|
description: Konfiguration neu laden
|
||||||
default: op
|
default: op
|
||||||
buttoncontrol.note:
|
buttoncontrol.note:
|
||||||
description: Erlaubt das Ändern des Notenblock-Instruments
|
description: Notenblock-Instrument ändern
|
||||||
default: true
|
default: true
|
||||||
buttoncontrol.update:
|
buttoncontrol.update:
|
||||||
description: Erlaubt das Empfangen von Update-Benachrichtigungen
|
description: Update-Benachrichtigungen empfangen
|
||||||
default: op
|
default: op
|
||||||
buttoncontrol.trust:
|
buttoncontrol.trust:
|
||||||
description: Erlaubt das Verwalten von Vertrauen und Status (Public/Private)
|
description: Trust-System und Public/Private-Status verwalten
|
||||||
default: true
|
default: true
|
||||||
Reference in New Issue
Block a user