20 Commits
1.1 ... 1.5

Author SHA1 Message Date
65f9621c84 Upload dependency-reduced-pom.xml via GUI 2026-01-25 19:28:57 +00:00
68a4a670ee Update from Git Manager GUI 2026-01-25 20:28:55 +01:00
bae5e29db3 Upload pom.xml via GUI 2026-01-25 19:28:54 +00:00
137ba95a24 Dateien nach "/" hochladen 2025-08-22 11:14:57 +00:00
a1e415d6f0 src/main/resources/plugin.yml aktualisiert 2025-08-22 11:12:26 +00:00
b6b44d25c5 src/main/java/viper/MotionSensorGUI.java aktualisiert 2025-08-22 11:11:59 +00:00
dc173691a4 Dateien nach "src/main/java/viper" hochladen 2025-08-13 16:36:29 +00:00
f42e91f253 src/main/java/viper/DataManager.java aktualisiert 2025-08-13 16:35:56 +00:00
50f4827ac1 src/main/java/viper/ConfigManager.java aktualisiert 2025-08-13 16:35:45 +00:00
221151dfd2 src/main/java/viper/ButtonListener.java aktualisiert 2025-08-13 16:35:33 +00:00
b1de85aa22 src/main/java/viper/ButtonControl.java aktualisiert 2025-08-13 16:35:23 +00:00
85e78307b5 src/main/resources/config.yml aktualisiert 2025-08-13 16:34:24 +00:00
3dedf24f92 Dateien nach "src/main/java/viper" hochladen 2025-08-11 15:32:15 +00:00
f05c63307a src/main/java/viper/ConfigManager.java aktualisiert 2025-08-11 15:31:31 +00:00
edb366acd6 src/main/java/viper/ButtonListener.java aktualisiert 2025-08-11 15:31:19 +00:00
8e753d1479 src/main/java/viper/ButtonControl.java aktualisiert 2025-08-11 15:31:06 +00:00
a7af54e61c src/main/resources/plugin.yml aktualisiert 2025-08-11 15:30:51 +00:00
33759bb44b src/main/resources/lang.yml aktualisiert 2025-08-11 15:30:41 +00:00
b8715ced69 src/main/resources/config.yml aktualisiert 2025-08-11 15:30:29 +00:00
302b188f50 pom.xml aktualisiert 2025-08-11 15:30:10 +00:00
13 changed files with 1243 additions and 429 deletions

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>viper</groupId>
<artifactId>ButtonControl</artifactId>
<version>1.4</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
</properties>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- bStats Monitoring -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>de.viper.bstats</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

51
pom.xml
View File

@@ -1,14 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>viper</groupId> <groupId>viper</groupId>
<artifactId>ButtonControl</artifactId> <artifactId>ButtonControl</artifactId>
<version>1.1</version> <version>1.5</version>
<packaging>jar</packaging>
<name>ButtonControl</name>
<repositories> <repositories>
<!-- Spigot-Repository -->
<repository> <repository>
<id>spigot-repo</id> <id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url> <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
@@ -16,16 +21,32 @@
</repositories> </repositories>
<dependencies> <dependencies>
<!-- Spigot API (bereitgestellt vom Server) -->
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.21.1-R0.1-SNAPSHOT</version> <version>1.21.1-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- bStats Bukkit -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.2</version>
</dependency>
<!-- org.json für UpdateChecker JSON-Parsing -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20240303</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<!-- Compiler Plugin -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
@@ -33,8 +54,34 @@
<configuration> <configuration>
<source>17</source> <source>17</source>
<target>17</target> <target>17</target>
<encoding>UTF-8</encoding>
</configuration> </configuration>
</plugin> </plugin>
<!-- Shade Plugin zum Relocaten von bStats -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>viper.shaded.bstats</shadedPattern>
</relocation>
</relocations>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@@ -17,32 +17,137 @@ 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.List;
import java.util.UUID; import java.util.UUID;
import java.util.HashMap;
import java.util.Map;
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<>();
@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);
// Initialize config with defaults if not present // Spigot Update Checker beim Serverstart ausführen
updateConfigWithDefaults(); new UpdateChecker(this, 127702).getVersion(version -> {
String currentVersion = this.getDescription().getVersion();
String normalizedLatest = version.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim();
String normalizedCurrent = currentVersion.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim();
if (isNewerVersion(normalizedLatest, normalizedCurrent)) {
// Konsole bleibt sachlich
getLogger().info("Update verfügbar: v" + version);
// Schicke die stylische Nachricht an Admins
Bukkit.getScheduler().runTask(this, () -> {
Bukkit.getOnlinePlayers().stream()
.filter(p -> p.hasPermission("buttoncontrol.update"))
.forEach(p -> {
p.sendMessage(""); // Leerzeile für Abstand
p.sendMessage("§8§m-----------------------------------------");
p.sendMessage(" §6§lButtonControl §7- §e§lUpdate verfügbar!");
p.sendMessage("");
p.sendMessage(" §7Aktuelle Version: §c" + currentVersion);
p.sendMessage(" §7Neue Version: §a" + version);
p.sendMessage("");
p.sendMessage(" §eDownload hier:");
p.sendMessage(" §bhttps://www.spigotmc.org/resources/127702/");
p.sendMessage("§8§m-----------------------------------------");
p.sendMessage("");
});
});
} else {
getLogger().info("ButtonControl ist auf dem neuesten Stand (v" + currentVersion + ").");
}
});
// Listener für Spieler-Joins (Update-Benachrichtigung beim Betreten des Servers)
getServer().getPluginManager().registerEvents(new org.bukkit.event.Listener() {
@org.bukkit.event.EventHandler
public void onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent event) {
Player player = event.getPlayer();
if (!player.hasPermission("buttoncontrol.update")) return;
new UpdateChecker(ButtonControl.this, 127702).getVersion(version -> {
String currentVersion = getDescription().getVersion();
String normalizedLatest = version.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim();
String normalizedCurrent = currentVersion.replaceFirst("(?i)^(version\\s*|v\\.?\\s*)", "").trim();
if (isNewerVersion(normalizedLatest, normalizedCurrent)) {
// Stylische Box für den Spieler beim Joinen
player.sendMessage("");
player.sendMessage("§8§m-----------------------------------------");
player.sendMessage(" §6§lButtonControl §7- §e§lUpdate verfügbar!");
player.sendMessage("");
player.sendMessage(" §7Aktuelle Version: §c" + currentVersion);
player.sendMessage(" §7Neue Version: §a" + version);
player.sendMessage("");
player.sendMessage(" §eDownload hier:");
player.sendMessage(" §bhttps://www.spigotmc.org/resources/127702/");
player.sendMessage("§8§m-----------------------------------------");
player.sendMessage("");
}
});
}
}, this);
// Konfiguration und Spielmechaniken laden
updateConfigWithDefaults();
// --- COMMANDS & TAB-COMPLETER ---
// Registriert den Executor (Befehlsverarbeitung) und den TabCompleter (Vorschläge)
if (getCommand("bc") != null) {
getCommand("bc").setExecutor(this); // 'this' setzt voraus, dass ButtonControl 'onCommand' enthält
getCommand("bc").setTabCompleter(new ButtonTabCompleter());
}
// Event-Listener registrieren
getServer().getPluginManager().registerEvents(new ButtonListener(this, configManager, dataManager), this); getServer().getPluginManager().registerEvents(new ButtonListener(this, configManager, dataManager), this);
// Rezepte und bStats/Metrics
registerRecipes(); registerRecipes();
MetricsHandler.startMetrics(this);
// Scheduler zum Prüfen der Tageslichtsensoren alle 10 Sekunden (20 Ticks = 1 Sekunde) // --- 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);
getLogger().info("ButtonControl v" + getDescription().getVersion() + " wurde erfolgreich aktiviert!");
}
private boolean isNewerVersion(String latest, String current) {
try {
String[] latestParts = latest.split("\\.");
String[] currentParts = current.split("\\.");
int length = Math.max(latestParts.length, currentParts.length);
for (int i = 0; i < length; i++) {
int latestPart = (i < latestParts.length) ? Integer.parseInt(latestParts[i]) : 0;
int currentPart = (i < currentParts.length) ? Integer.parseInt(currentParts[i]) : 0;
if (latestPart > currentPart) return true;
if (latestPart < currentPart) return false;
}
return false;
} catch (NumberFormatException e) {
return !latest.equalsIgnoreCase(current);
}
} }
private void updateConfigWithDefaults() { private void updateConfigWithDefaults() {
// Add default note block sound and double note settings if not present
if (!configManager.getConfig().contains("default-note")) { if (!configManager.getConfig().contains("default-note")) {
configManager.getConfig().set("default-note", "PIANO"); configManager.getConfig().set("default-note", "PIANO");
} }
@@ -52,50 +157,93 @@ public class ButtonControl extends JavaPlugin {
if (!configManager.getConfig().contains("double-note-delay-ms")) { if (!configManager.getConfig().contains("double-note-delay-ms")) {
configManager.getConfig().set("double-note-delay-ms", 1000); 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(); configManager.saveConfig();
} }
private void registerRecipes() { private void registerRecipes() {
ItemStack controlButton = new ItemStack(Material.STONE_BUTTON); // --- 1. Dynamische Steuer-Buttons für JEDE Holz/Stein-Art ---
ItemMeta buttonMeta = controlButton.getItemMeta(); for (Material mat : Material.values()) {
buttonMeta.setDisplayName("§6Steuer-Button"); if (mat.name().endsWith("_BUTTON")) {
controlButton.setItemMeta(buttonMeta); // Wir erstellen für jeden Button-Typ ein eigenes Ergebnis
ItemStack controlButton = new ItemStack(mat);
ItemMeta buttonMeta = controlButton.getItemMeta();
if (buttonMeta != null) {
buttonMeta.setDisplayName("§6Steuer-Button");
List<String> lore = new ArrayList<>();
lore.add("§7Ein universeller Controller für");
lore.add("§7Türen, Lampen und mehr.");
buttonMeta.setLore(lore);
controlButton.setItemMeta(buttonMeta);
}
NamespacedKey buttonKey = new NamespacedKey(this, "control_button"); // Wir brauchen für jedes Material einen eindeutigen Key (z.B. control_button_oak_button)
ShapedRecipe buttonRecipe = new ShapedRecipe(buttonKey, controlButton); NamespacedKey key = new NamespacedKey(this, "control_" + mat.name().toLowerCase());
buttonRecipe.shape("123", "456", "789"); if (Bukkit.getRecipe(key) != null) Bukkit.removeRecipe(key);
buttonRecipe.setIngredient('2', Material.STONE_BUTTON);
buttonRecipe.setIngredient('5', Material.STONE_BUTTON);
buttonRecipe.setIngredient('8', Material.STONE_BUTTON);
Bukkit.addRecipe(buttonRecipe);
ItemStack controlDaylight = new ItemStack(Material.DAYLIGHT_DETECTOR); ShapedRecipe recipe = new ShapedRecipe(key, controlButton);
ItemMeta daylightMeta = controlDaylight.getItemMeta(); recipe.shape("123", "456", "789");
// Das Material muss an allen drei Stellen gleich sein (z.B. 3x Eiche)
recipe.setIngredient('2', mat);
recipe.setIngredient('5', mat);
recipe.setIngredient('8', mat);
Bukkit.addRecipe(recipe);
}
}
// --- 2. Steuer-Tageslichtsensor Rezept ---
ItemStack controlDaylight = new ItemStack(Material.DAYLIGHT_DETECTOR);
ItemMeta daylightMeta = controlDaylight.getItemMeta();
if (daylightMeta != null) {
daylightMeta.setDisplayName("§6Steuer-Tageslichtsensor"); daylightMeta.setDisplayName("§6Steuer-Tageslichtsensor");
controlDaylight.setItemMeta(daylightMeta); controlDaylight.setItemMeta(daylightMeta);
}
NamespacedKey daylightKey = new NamespacedKey(this, "control_daylight");
if (Bukkit.getRecipe(daylightKey) != null) Bukkit.removeRecipe(daylightKey);
ShapedRecipe daylightRecipe = new ShapedRecipe(daylightKey, controlDaylight);
daylightRecipe.shape("123", "456", "789");
daylightRecipe.setIngredient('2', Material.DAYLIGHT_DETECTOR);
daylightRecipe.setIngredient('5', Material.DAYLIGHT_DETECTOR);
daylightRecipe.setIngredient('8', Material.DAYLIGHT_DETECTOR);
Bukkit.addRecipe(daylightRecipe);
NamespacedKey daylightKey = new NamespacedKey(this, "control_daylight"); // --- 3. Steuer-Notenblock Rezept ---
ShapedRecipe daylightRecipe = new ShapedRecipe(daylightKey, controlDaylight); ItemStack controlNoteBlock = new ItemStack(Material.NOTE_BLOCK);
daylightRecipe.shape("123", "456", "789"); ItemMeta noteBlockMeta = controlNoteBlock.getItemMeta();
daylightRecipe.setIngredient('2', Material.DAYLIGHT_DETECTOR); if (noteBlockMeta != null) {
daylightRecipe.setIngredient('5', Material.DAYLIGHT_DETECTOR);
daylightRecipe.setIngredient('8', Material.DAYLIGHT_DETECTOR);
Bukkit.addRecipe(daylightRecipe);
// Recipe for Control Note Block
ItemStack controlNoteBlock = new ItemStack(Material.NOTE_BLOCK);
ItemMeta noteBlockMeta = controlNoteBlock.getItemMeta();
noteBlockMeta.setDisplayName("§6Steuer-Notenblock"); noteBlockMeta.setDisplayName("§6Steuer-Notenblock");
controlNoteBlock.setItemMeta(noteBlockMeta); controlNoteBlock.setItemMeta(noteBlockMeta);
NamespacedKey noteBlockKey = new NamespacedKey(this, "control_noteblock");
ShapedRecipe noteBlockRecipe = new ShapedRecipe(noteBlockKey, controlNoteBlock);
noteBlockRecipe.shape("123", "456", "789");
noteBlockRecipe.setIngredient('2', Material.NOTE_BLOCK);
noteBlockRecipe.setIngredient('5', Material.NOTE_BLOCK);
noteBlockRecipe.setIngredient('8', Material.NOTE_BLOCK);
Bukkit.addRecipe(noteBlockRecipe);
} }
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(); List<String> allControllers = dataManager.getAllPlacedControllers();
@@ -103,16 +251,8 @@ public class ButtonControl extends JavaPlugin {
String buttonId = dataManager.getButtonIdForPlacedController(controllerLoc); String buttonId = dataManager.getButtonIdForPlacedController(controllerLoc);
if (buttonId == null) continue; if (buttonId == null) continue;
String[] parts = controllerLoc.split(","); Location loc = parseLocation(controllerLoc);
if (parts.length != 4) continue; if (loc == null) continue;
World world = getServer().getWorld(parts[0]);
if (world == null) continue;
Location loc = new Location(world,
Integer.parseInt(parts[1]),
Integer.parseInt(parts[2]),
Integer.parseInt(parts[3]));
Block block = loc.getBlock(); Block block = loc.getBlock();
if (block.getType() != Material.DAYLIGHT_DETECTOR) continue; if (block.getType() != Material.DAYLIGHT_DETECTOR) continue;
@@ -124,19 +264,10 @@ public class ButtonControl extends JavaPlugin {
if (connectedBlocks == null) continue; if (connectedBlocks == null) continue;
for (String targetLocStr : connectedBlocks) { for (String targetLocStr : connectedBlocks) {
String[] targetParts = targetLocStr.split(","); Location targetLoc = parseLocation(targetLocStr);
if (targetParts.length != 4) continue; if (targetLoc == null) continue;
World targetWorld = getServer().getWorld(targetParts[0]);
if (targetWorld == null) continue;
Location targetLoc = new Location(targetWorld,
Integer.parseInt(targetParts[1]),
Integer.parseInt(targetParts[2]),
Integer.parseInt(targetParts[3]));
Block targetBlock = targetLoc.getBlock(); Block targetBlock = targetLoc.getBlock();
if (targetBlock.getType() == Material.REDSTONE_LAMP) { if (targetBlock.getType() == Material.REDSTONE_LAMP) {
Lightable lamp = (Lightable) targetBlock.getBlockData(); Lightable lamp = (Lightable) targetBlock.getBlockData();
lamp.setLit(!isDay); lamp.setLit(!isDay);
@@ -146,25 +277,84 @@ public class ButtonControl extends JavaPlugin {
} }
} }
// Play note block sound for doorbell public void checkMotionSensors() {
long now = System.currentTimeMillis();
List<String> allControllers = dataManager.getAllPlacedControllers();
for (String controllerLoc : allControllers) {
Location loc = parseLocation(controllerLoc);
if (loc == null) continue;
Block block = loc.getBlock();
if (block.getType() != Material.TRIPWIRE_HOOK) continue;
String buttonId = dataManager.getButtonIdForPlacedController(controllerLoc);
if (buttonId == null) continue;
double radius = dataManager.getMotionSensorRadius(controllerLoc);
if (radius == -1) radius = configManager.getConfig().getDouble("motion-detection-radius", 5.0);
long delay = dataManager.getMotionSensorDelay(controllerLoc);
if (delay == -1) delay = configManager.getConfig().getLong("motion-close-delay-ms", 5000L);
boolean detected = !loc.getWorld().getNearbyEntities(loc, radius, radius, radius, e -> e instanceof Player).isEmpty();
List<String> connectedBlocks = dataManager.getConnectedBlocks(buttonId);
if (connectedBlocks == null || connectedBlocks.isEmpty()) continue;
if (detected) {
setOpenables(connectedBlocks, true);
lastMotionDetections.put(controllerLoc, now);
} else {
Long last = lastMotionDetections.get(controllerLoc);
if (last != null && now - last >= delay) {
setOpenables(connectedBlocks, false);
lastMotionDetections.remove(controllerLoc);
}
}
}
}
private void setOpenables(List<String> connectedBlocks, boolean open) {
for (String targetLocStr : connectedBlocks) {
Location targetLoc = parseLocation(targetLocStr);
if (targetLoc == null) continue;
Block targetBlock = targetLoc.getBlock();
if (targetBlock.getBlockData() instanceof org.bukkit.block.data.Openable) {
org.bukkit.block.data.Openable openable = (org.bukkit.block.data.Openable) targetBlock.getBlockData();
openable.setOpen(open);
targetBlock.setBlockData(openable);
}
}
}
private Location parseLocation(String locStr) {
String[] parts = locStr.split(",");
if (parts.length != 4) return null;
World world = getServer().getWorld(parts[0]);
if (world == null) return null;
try {
return new Location(world, Integer.parseInt(parts[1]), Integer.parseInt(parts[2]), Integer.parseInt(parts[3]));
} catch (NumberFormatException e) {
return null;
}
}
public void playDoorbellSound(Location loc, String instrument) { public void playDoorbellSound(Location loc, String instrument) {
Block block = loc.getBlock(); Block block = loc.getBlock();
if (block.getType() != Material.NOTE_BLOCK) return; if (block.getType() != Material.NOTE_BLOCK) return;
NoteBlock noteBlock = (NoteBlock) block.getBlockData(); NoteBlock noteBlock = (NoteBlock) block.getBlockData();
try { try {
// Set instrument based on config or player preference
org.bukkit.Instrument bukkitInstrument = org.bukkit.Instrument.valueOf(instrument.toUpperCase()); org.bukkit.Instrument bukkitInstrument = org.bukkit.Instrument.valueOf(instrument.toUpperCase());
noteBlock.setInstrument(bukkitInstrument); noteBlock.setInstrument(bukkitInstrument);
noteBlock.setNote(new Note(0, Tone.C, false)); // Default to C note noteBlock.setNote(new Note(0, Tone.C, false));
block.setBlockData(noteBlock); block.setBlockData(noteBlock);
loc.getWorld().playSound(loc, bukkitInstrument.getSound(), 1.0f, 1.0f); loc.getWorld().playSound(loc, bukkitInstrument.getSound(), 1.0f, 1.0f);
// Check if double note is enabled
if (configManager.getConfig().getBoolean("double-note-enabled", true)) { if (configManager.getConfig().getBoolean("double-note-enabled", true)) {
// Schedule the second note after the configured delay in milliseconds
int delayMs = configManager.getConfig().getInt("double-note-delay-ms", 1000); int delayMs = configManager.getConfig().getInt("double-note-delay-ms", 1000);
long delayTicks = (long) (delayMs / 50.0); // Convert milliseconds to ticks (1000 ms = 20 ticks) long delayTicks = (long) (delayMs / 50.0);
getServer().getScheduler().runTaskLater(this, () -> { getServer().getScheduler().runTaskLater(this, () -> {
if (block.getType() == Material.NOTE_BLOCK) { if (block.getType() == Material.NOTE_BLOCK) {
loc.getWorld().playSound(loc, bukkitInstrument.getSound(), 1.0f, 1.0f); loc.getWorld().playSound(loc, bukkitInstrument.getSound(), 1.0f, 1.0f);
@@ -178,58 +368,116 @@ public class ButtonControl extends JavaPlugin {
@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")) { if (!command.getName().equalsIgnoreCase("bc")) return false;
if (args.length == 0) {
sender.sendMessage("§6[ButtonControl] §7Verwende: /bc <info|reload|note>"); if (args.length == 0) {
sender.sendMessage("§6[ButtonControl] §7Verwende: /bc <info|reload|note|trust|untrust|public|private>");
return true;
}
// --- INFO BEFEHL ---
if (args[0].equalsIgnoreCase("info")) {
sender.sendMessage("§6§lButtonControl §7- v" + getDescription().getVersion());
sender.sendMessage("§eAuthor: §fM_Viper");
sender.sendMessage("§eFeatures: §fTüren, Lampen, Notenblöcke, Sensoren");
return true;
}
// --- RELOAD BEFEHL ---
if (args[0].equalsIgnoreCase("reload")) {
if (!sender.hasPermission("buttoncontrol.reload")) {
sender.sendMessage(configManager.getMessage("keine-berechtigung"));
return true; return true;
} }
configManager.reloadConfig();
updateConfigWithDefaults();
dataManager.reloadData();
sender.sendMessage(configManager.getMessage("konfiguration-neugeladen"));
return true;
}
if (args[0].equalsIgnoreCase("info")) { // --- NOTE BEFEHL ---
sender.sendMessage("§6[ButtonControl] §7Informationen zum Plugin:"); if (args[0].equalsIgnoreCase("note") && sender instanceof Player) {
sender.sendMessage("§eVersion: §f" + getDescription().getVersion()); Player player = (Player) sender;
sender.sendMessage("§eErsteller: §fM_Viper"); if (args.length < 2) {
sender.sendMessage("§ePlugin: §fButtonControl"); player.sendMessage("§6[ButtonControl] §7Verwende: /bc note <Instrument>");
sender.sendMessage("§eGetestet für Minecraft: §f1.21.5 - 1.21.8");
sender.sendMessage("§eWeitere Infos: §fTüren, Lampen & Notenblöcke mit Buttons oder Tageslichtsensoren steuern");
return true; return true;
} }
try {
if (args[0].equalsIgnoreCase("reload")) { org.bukkit.Instrument.valueOf(args[1].toUpperCase());
if (!sender.hasPermission("buttoncontrol.reload")) { dataManager.setPlayerInstrument(player.getUniqueId(), args[1].toUpperCase());
sender.sendMessage(configManager.getMessage("keine-berechtigung")); player.sendMessage(String.format(configManager.getMessage("instrument-gesetzt"), args[1].toUpperCase()));
return true; } catch (Exception e) {
} player.sendMessage(configManager.getMessage("ungueltiges-instrument"));
configManager.reloadConfig();
updateConfigWithDefaults(); // Ensure new defaults are added without overwriting
dataManager.reloadData();
sender.sendMessage(configManager.getMessage("konfiguration-reloaded"));
return true;
} }
return true;
}
if (args[0].equalsIgnoreCase("note") && sender instanceof Player) { // --- TRUST / PUBLIC / PRIVATE SYSTEM ---
Player player = (Player) sender; if (sender instanceof Player) {
if (!player.hasPermission("buttoncontrol.note")) { Player player = (Player) sender;
player.sendMessage(configManager.getMessage("keine-berechtigung")); if (args[0].equalsIgnoreCase("trust") || args[0].equalsIgnoreCase("untrust") ||
return true; args[0].equalsIgnoreCase("public") || args[0].equalsIgnoreCase("private")) {
}
if (args.length < 2) { Block target = player.getTargetBlockExact(5);
sender.sendMessage("§6[ButtonControl] §7Verwende: /bc note <Instrument>");
sender.sendMessage("§7Verfügbare Instrumente: PIANO, BASS_DRUM, SNARE, STICKS, BASS_GUITAR, FLUTE, BELL, GUITAR, CHIME, XYLOPHONE, etc."); // Erkennt nun Stein- und alle Holzbuttons sowie Sensoren
if (target == null || (!target.getType().name().endsWith("_BUTTON") &&
target.getType() != Material.DAYLIGHT_DETECTOR &&
target.getType() != Material.TRIPWIRE_HOOK)) {
player.sendMessage(configManager.getMessage("kein-controller-im-blick"));
return true; return true;
} }
String instrument = args[1].toUpperCase(); String targetLoc = target.getWorld().getName() + "," + target.getX() + "," + target.getY() + "," + target.getZ();
try { String buttonId = dataManager.getButtonIdForLocation(targetLoc);
org.bukkit.Instrument.valueOf(instrument);
dataManager.setPlayerInstrument(player.getUniqueId(), instrument); if (buttonId == null) {
sender.sendMessage(String.format(configManager.getMessage("instrument-gesetzt"), instrument)); player.sendMessage(configManager.getMessage("keine-bloecke-verbunden"));
} catch (IllegalArgumentException e) { return true;
sender.sendMessage(configManager.getMessage("ungueltiges-instrument")); }
if (!dataManager.isOwner(buttonId, player.getUniqueId())) {
player.sendMessage(configManager.getMessage("nur-besitzer-abbauen"));
return true;
}
// Spieler hinzufügen
if (args[0].equalsIgnoreCase("trust")) {
if (args.length < 2) {
player.sendMessage("§6[ButtonControl] §7Verwende: /bc trust <Spieler>");
return true;
}
Player targetPlayer = Bukkit.getPlayer(args[1]);
if (targetPlayer == null) {
player.sendMessage(configManager.getMessage("spieler-nicht-gefunden"));
return true;
}
dataManager.addTrustedPlayer(buttonId, targetPlayer.getUniqueId());
player.sendMessage(String.format(configManager.getMessage("trust-hinzugefuegt"), targetPlayer.getName()));
}
// Spieler entfernen
else if (args[0].equalsIgnoreCase("untrust")) {
if (args.length < 2) {
player.sendMessage("§6[ButtonControl] §7Verwende: /bc untrust <Spieler>");
return true;
}
UUID targetUUID = Bukkit.getOfflinePlayer(args[1]).getUniqueId();
dataManager.removeTrustedPlayer(buttonId, targetUUID);
player.sendMessage(String.format(configManager.getMessage("trust-entfernt"), args[1]));
}
// Status umschalten (Public) oder erzwingen (Private)
else if (args[0].equalsIgnoreCase("public") || args[0].equalsIgnoreCase("private")) {
boolean newState = args[0].equalsIgnoreCase("public") ? !dataManager.isPublic(buttonId) : false;
dataManager.setPublic(buttonId, newState);
String statusColor = newState ? "§aÖffentlich" : "§cPrivat";
player.sendMessage(String.format(configManager.getMessage("status-geandert"), statusColor));
} }
return true; return true;
} }
} }
return false; return true;
} }
public ConfigManager getConfigManager() { public ConfigManager getConfigManager() {

View File

@@ -1,10 +1,12 @@
package viper; package viper;
import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.data.type.Door; 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.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
@@ -31,90 +33,44 @@ public class ButtonListener implements Listener {
@EventHandler @EventHandler
public void onPlayerInteract(PlayerInteractEvent event) { public void onPlayerInteract(PlayerInteractEvent event) {
String playerUUID = event.getPlayer().getUniqueId().toString(); Player player = event.getPlayer();
UUID playerUUID = player.getUniqueId();
ItemStack item = event.getItem(); ItemStack item = event.getItem();
Block block = event.getClickedBlock(); Block block = event.getClickedBlock();
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && block != null && // 1. Logik für bereits platzierte Controller (Benutzung)
(block.getType() == Material.STONE_BUTTON || block.getType() == Material.DAYLIGHT_DETECTOR)) { if (event.getAction() == Action.RIGHT_CLICK_BLOCK && block != null) {
String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ(); String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ();
String buttonId = dataManager.getButtonIdForPlacedController(playerUUID, blockLocation); String buttonId = dataManager.getButtonIdForLocation(blockLocation);
if (buttonId != null) { if (buttonId != null) {
event.setCancelled(true); if (!dataManager.canAccess(buttonId, playerUUID)) {
List<String> connectedBlocks = dataManager.getConnectedBlocks(playerUUID, buttonId); player.sendMessage(configManager.getMessage("keine-berechtigung-controller"));
if (connectedBlocks != null && !connectedBlocks.isEmpty()) { event.setCancelled(true);
boolean anyDoorOpened = false; return;
boolean anyDoorClosed = false;
boolean anyLampOn = false;
boolean anyLampOff = false;
boolean anyNoteBlockPlayed = false;
for (String loc : connectedBlocks) {
String[] parts = loc.split(",");
Location location = new Location(plugin.getServer().getWorld(parts[0]),
Integer.parseInt(parts[1]),
Integer.parseInt(parts[2]),
Integer.parseInt(parts[3]));
Block targetBlock = location.getBlock();
if (isDoor(targetBlock.getType())) {
Door door = (Door) targetBlock.getBlockData();
boolean wasOpen = door.isOpen();
door.setOpen(!wasOpen);
targetBlock.setBlockData(door);
if (!wasOpen) {
anyDoorOpened = true;
} else {
anyDoorClosed = true;
}
} else if (targetBlock.getType() == Material.REDSTONE_LAMP) {
Lightable lamp = (Lightable) targetBlock.getBlockData();
boolean wasLit = lamp.isLit();
lamp.setLit(!wasLit);
targetBlock.setBlockData(lamp);
if (!wasLit) {
anyLampOn = true;
} else {
anyLampOff = true;
}
} else if (targetBlock.getType() == Material.NOTE_BLOCK) {
String instrument = dataManager.getPlayerInstrument(event.getPlayer().getUniqueId());
if (instrument == null) {
instrument = configManager.getConfig().getString("default-note", "PIANO");
}
plugin.playDoorbellSound(location, instrument);
anyNoteBlockPlayed = true;
}
}
if (anyDoorOpened) {
event.getPlayer().sendMessage(configManager.getMessage("tueren-geoeffnet"));
}
if (anyDoorClosed) {
event.getPlayer().sendMessage(configManager.getMessage("tueren-geschlossen"));
}
if (anyLampOn) {
event.getPlayer().sendMessage(configManager.getMessage("lampen-eingeschaltet"));
}
if (anyLampOff) {
event.getPlayer().sendMessage(configManager.getMessage("lampen-ausgeschaltet"));
}
if (anyNoteBlockPlayed) {
event.getPlayer().sendMessage(configManager.getMessage("notenblock-ausgeloest"));
}
} else {
event.getPlayer().sendMessage(configManager.getMessage("keine-bloecke-verbunden"));
} }
if (block.getType() == Material.TRIPWIRE_HOOK) {
event.setCancelled(true);
new MotionSensorGUI(plugin, player, blockLocation, buttonId).open();
return;
}
if (isButton(block.getType()) || block.getType() == Material.DAYLIGHT_DETECTOR) {
List<String> connectedBlocks = dataManager.getConnectedBlocks(buttonId);
if (connectedBlocks != null && !connectedBlocks.isEmpty()) {
toggleConnectedBlocks(player, playerUUID, connectedBlocks);
} else {
player.sendMessage(configManager.getMessage("keine-bloecke-verbunden"));
}
}
return;
} }
return;
} }
if (item == null || (!item.getType().equals(Material.STONE_BUTTON) && !item.getType().equals(Material.DAYLIGHT_DETECTOR) && !item.getType().equals(Material.NOTE_BLOCK))) { // 2. Logik für das Verbinden von neuen Blöcken (Item in der Hand)
return; if (item == null || !item.hasItemMeta() || !item.getItemMeta().getDisplayName().contains("§6Steuer-")) {
}
if (!item.hasItemMeta() || !item.getItemMeta().getDisplayName().contains("§6Steuer-")) {
return; return;
} }
@@ -122,104 +78,218 @@ public class ButtonListener implements Listener {
return; return;
} }
if (isDoor(block.getType()) || block.getType() == Material.REDSTONE_LAMP || block.getType() == Material.NOTE_BLOCK) { if (isInteractableTarget(block.getType())) {
event.setCancelled(true); event.setCancelled(true);
String buttonId = item.getItemMeta().hasLore() ? item.getItemMeta().getLore().get(0) : UUID.randomUUID().toString(); ItemMeta meta = item.getItemMeta();
List<String> connectedBlocks = dataManager.getConnectedBlocks(playerUUID, buttonId); String buttonId = null;
// ID aus der Lore extrahieren (wir suchen nach dem Präfix §8ID: )
if (meta != null && meta.hasLore() && !meta.getLore().isEmpty()) {
String firstLine = meta.getLore().get(0);
if (firstLine.startsWith("§8ID: ")) {
buttonId = firstLine.replace("§8ID: ", "");
}
}
// Falls keine ID existiert (neues Item), generieren und SOFORT speichern
if (buttonId == null) {
buttonId = UUID.randomUUID().toString();
updateButtonLore(item, buttonId);
}
List<String> connectedBlocks = dataManager.getConnectedBlocks(buttonId);
if (connectedBlocks == null) { if (connectedBlocks == null) {
connectedBlocks = new ArrayList<>(); connectedBlocks = new ArrayList<>();
} }
int maxDoors = configManager.getMaxDoors();
int maxLamps = configManager.getMaxLamps();
int maxNoteBlocks = configManager.getMaxNoteBlocks();
int doorCount = (int) connectedBlocks.stream().filter(loc -> isDoor(getMaterialFromLocation(loc))).count();
int lampCount = (int) connectedBlocks.stream().filter(loc -> getMaterialFromLocation(loc) == Material.REDSTONE_LAMP).count();
int noteBlockCount = (int) connectedBlocks.stream().filter(loc -> getMaterialFromLocation(loc) == Material.NOTE_BLOCK).count();
if (isDoor(block.getType()) && doorCount >= maxDoors) {
event.getPlayer().sendMessage(configManager.getMessage("max-tueren-erreicht"));
return;
}
if (block.getType() == Material.REDSTONE_LAMP && lampCount >= maxLamps) {
event.getPlayer().sendMessage(configManager.getMessage("max-lampen-erreicht"));
return;
}
if (block.getType() == Material.NOTE_BLOCK && noteBlockCount >= maxNoteBlocks) {
event.getPlayer().sendMessage(configManager.getMessage("max-notenbloecke-erreicht"));
return;
}
String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ(); String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ();
if (!connectedBlocks.contains(blockLocation)) { if (connectedBlocks.contains(blockLocation)) {
player.sendMessage(configManager.getMessage("block-bereits-verbunden"));
return;
}
if (checkLimits(player, block.getType(), connectedBlocks)) {
connectedBlocks.add(blockLocation); connectedBlocks.add(blockLocation);
dataManager.setConnectedBlocks(playerUUID, buttonId, connectedBlocks); dataManager.setConnectedBlocks(playerUUID.toString(), buttonId, connectedBlocks);
updateButtonLore(item, buttonId); player.sendMessage(configManager.getMessage("block-verbunden"));
event.getPlayer().sendMessage(configManager.getMessage("block-verbunden"));
} else {
event.getPlayer().sendMessage(configManager.getMessage("block-bereits-verbunden"));
} }
} }
} }
private void toggleConnectedBlocks(Player player, UUID playerUUID, List<String> connectedBlocks) {
boolean anyDoorOpened = false, anyDoorClosed = false;
boolean anyGateOpened = false, anyGateClosed = false;
boolean anyTrapOpened = false, anyTrapClosed = false;
boolean anyLampOn = false, anyLampOff = false;
boolean anyNoteBlockPlayed = false;
boolean anyBellPlayed = false;
for (String locStr : connectedBlocks) {
Location location = parseLocation(locStr);
if (location == null) continue;
Block targetBlock = location.getBlock();
if (isDoor(targetBlock.getType()) || isGate(targetBlock.getType()) || isTrapdoor(targetBlock.getType())) {
if (targetBlock.getBlockData() instanceof Openable) {
Openable openable = (Openable) targetBlock.getBlockData();
boolean wasOpen = openable.isOpen();
openable.setOpen(!wasOpen);
targetBlock.setBlockData(openable);
if (isDoor(targetBlock.getType())) {
if (!wasOpen) anyDoorOpened = true; else anyDoorClosed = true;
} else if (isGate(targetBlock.getType())) {
if (!wasOpen) anyGateOpened = true; else anyGateClosed = true;
} else if (isTrapdoor(targetBlock.getType())) {
if (!wasOpen) anyTrapOpened = true; else anyTrapClosed = true;
}
}
}
else if (targetBlock.getType() == Material.REDSTONE_LAMP) {
Lightable lamp = (Lightable) targetBlock.getBlockData();
boolean wasLit = lamp.isLit();
lamp.setLit(!wasLit);
targetBlock.setBlockData(lamp);
if (!wasLit) anyLampOn = true; else anyLampOff = true;
}
else if (targetBlock.getType() == Material.NOTE_BLOCK) {
String instrument = dataManager.getPlayerInstrument(playerUUID);
if (instrument == null) {
instrument = configManager.getConfig().getString("default-note", "PIANO");
}
plugin.playDoorbellSound(location, instrument);
anyNoteBlockPlayed = true;
}
else if (targetBlock.getType() == Material.BELL) {
targetBlock.getWorld().playSound(location, org.bukkit.Sound.BLOCK_BELL_USE, 3.0f, 1.0f);
anyBellPlayed = true;
}
}
if (anyDoorOpened) player.sendMessage(configManager.getMessage("tueren-geoeffnet"));
if (anyDoorClosed) player.sendMessage(configManager.getMessage("tueren-geschlossen"));
if (anyGateOpened) player.sendMessage(configManager.getMessage("gates-geoeffnet"));
if (anyGateClosed) player.sendMessage(configManager.getMessage("gates-geschlossen"));
if (anyTrapOpened) player.sendMessage(configManager.getMessage("fallturen-geoeffnet"));
if (anyTrapClosed) player.sendMessage(configManager.getMessage("fallturen-geschlossen"));
if (anyLampOn) player.sendMessage(configManager.getMessage("lampen-eingeschaltet"));
if (anyLampOff) player.sendMessage(configManager.getMessage("lampen-ausgeschaltet"));
if (anyNoteBlockPlayed) player.sendMessage(configManager.getMessage("notenblock-ausgeloest"));
if (anyBellPlayed) player.sendMessage(configManager.getMessage("glocke-gelaeutet"));
}
private boolean checkLimits(Player player, Material type, List<String> connected) {
if (isDoor(type)) {
if (connected.stream().filter(l -> isDoor(getMaterialFromLocation(l))).count() >= configManager.getMaxDoors()) {
player.sendMessage(configManager.getMessage("max-tueren-erreicht"));
return false;
}
} else if (isGate(type)) {
if (connected.stream().filter(l -> isGate(getMaterialFromLocation(l))).count() >= configManager.getMaxGates()) {
player.sendMessage(configManager.getMessage("max-gates-erreicht"));
return false;
}
} else if (isTrapdoor(type)) {
if (connected.stream().filter(l -> isTrapdoor(getMaterialFromLocation(l))).count() >= configManager.getMaxTrapdoors()) {
player.sendMessage(configManager.getMessage("max-fallturen-erreicht"));
return false;
}
} else if (type == Material.REDSTONE_LAMP) {
if (connected.stream().filter(l -> getMaterialFromLocation(l) == Material.REDSTONE_LAMP).count() >= configManager.getMaxLamps()) {
player.sendMessage(configManager.getMessage("max-lampen-erreicht"));
return false;
}
} else if (type == Material.NOTE_BLOCK) {
if (connected.stream().filter(l -> getMaterialFromLocation(l) == Material.NOTE_BLOCK).count() >= configManager.getMaxNoteBlocks()) {
player.sendMessage(configManager.getMessage("max-notenbloecke-erreicht"));
return false;
}
} else if (type == Material.BELL) {
if (connected.stream().filter(l -> getMaterialFromLocation(l) == Material.BELL).count() >= configManager.getMaxBells()) {
player.sendMessage(configManager.getMessage("max-glocken-erreicht"));
return false;
}
}
return true;
}
@EventHandler @EventHandler
public void onBlockPlace(BlockPlaceEvent event) { public void onBlockPlace(BlockPlaceEvent event) {
String playerUUID = event.getPlayer().getUniqueId().toString();
ItemStack item = event.getItemInHand(); ItemStack item = event.getItemInHand();
if (item == null || !item.hasItemMeta() || !item.getItemMeta().getDisplayName().contains("§6Steuer-")) {
return;
}
Block block = event.getBlockPlaced(); Block block = event.getBlockPlaced();
ItemMeta meta = item.getItemMeta();
String buttonId = null;
if (item == null || (!item.getType().equals(Material.STONE_BUTTON) && !item.getType().equals(Material.DAYLIGHT_DETECTOR) && !item.getType().equals(Material.NOTE_BLOCK))) { if (meta != null && meta.hasLore() && !meta.getLore().isEmpty()) {
return; String firstLine = meta.getLore().get(0);
if (firstLine.startsWith("§8ID: ")) {
buttonId = firstLine.replace("§8ID: ", "");
}
} }
if (!item.hasItemMeta() || !item.getItemMeta().getDisplayName().contains("§6Steuer-")) { if (buttonId == null) {
return; buttonId = UUID.randomUUID().toString();
updateButtonLore(item, buttonId);
} }
String buttonId = item.getItemMeta().hasLore() ? item.getItemMeta().getLore().get(0) : UUID.randomUUID().toString();
String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ(); String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ();
dataManager.addPlacedController(playerUUID, blockLocation, buttonId); dataManager.registerController(blockLocation, event.getPlayer().getUniqueId(), buttonId);
event.getPlayer().sendMessage(configManager.getMessage("controller-platziert")); event.getPlayer().sendMessage(configManager.getMessage("controller-platziert"));
} }
@EventHandler @EventHandler
public void onBlockBreak(BlockBreakEvent event) { public void onBlockBreak(BlockBreakEvent event) {
String playerUUID = event.getPlayer().getUniqueId().toString();
Block block = event.getBlock(); Block block = event.getBlock();
String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ(); String blockLocation = block.getWorld().getName() + "," + block.getX() + "," + block.getY() + "," + block.getZ();
String buttonId = dataManager.getButtonIdForPlacedController(playerUUID, blockLocation); String buttonId = dataManager.getButtonIdForLocation(blockLocation);
if (buttonId != null) { if (buttonId != null) {
dataManager.removePlacedController(playerUUID, blockLocation); if (!dataManager.isOwner(buttonId, event.getPlayer().getUniqueId())) {
dataManager.setConnectedBlocks(playerUUID, buttonId, null); event.getPlayer().sendMessage(configManager.getMessage("nur-besitzer-abbauen"));
event.setCancelled(true);
return;
}
dataManager.removeController(blockLocation);
event.getPlayer().sendMessage(configManager.getMessage("controller-entfernt")); event.getPlayer().sendMessage(configManager.getMessage("controller-entfernt"));
} }
} }
private boolean isDoor(Material material) { private boolean isButton(Material m) { return m.name().endsWith("_BUTTON"); }
return material.toString().endsWith("_DOOR"); 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) { private Material getMaterialFromLocation(String locString) {
String[] parts = locString.split(","); Location l = parseLocation(locString);
if (parts.length != 4) return null; return l != null ? l.getBlock().getType() : Material.AIR;
Location loc = new Location(plugin.getServer().getWorld(parts[0]), }
Integer.parseInt(parts[1]),
Integer.parseInt(parts[2]), private Location parseLocation(String s) {
Integer.parseInt(parts[3])); String[] p = s.split(",");
return loc.getBlock().getType(); 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) {
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
if (meta != null) { if (meta != null) {
List<String> lore = meta.hasLore() ? meta.getLore() : new ArrayList<>(); List<String> lore = new ArrayList<>();
if (!lore.contains(buttonId)) { lore.add("§8ID: " + buttonId);
lore.add(buttonId); lore.add("§7Ein universeller Controller für");
meta.setLore(lore); meta.setLore(lore);
item.setItemMeta(meta); item.setItemMeta(meta);
}
} }
} }
} }

View File

@@ -0,0 +1,47 @@
package viper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class ButtonTabCompleter implements TabCompleter {
private final List<String> commands = Arrays.asList("info", "reload", "note", "trust", "untrust", "public", "private");
private final List<String> instruments = Arrays.asList(
"PIANO", "BASS_DRUM", "SNARE_DRUM", "STICKS", "BASS_GUITAR",
"FLUTE", "BELL", "CHIME", "GUITAR", "XYLOPHONE",
"IRON_XYLOPHONE", "COW_BELL", "DIDGERIDOO", "BIT", "BANJO", "PLING"
);
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
if (!(sender instanceof Player)) return Collections.emptyList();
List<String> completions = new ArrayList<>();
// Erste Ebene: /bc <Tab>
if (args.length == 1) {
StringUtil.copyPartialMatches(args[0], commands, completions);
}
// Zweite Ebene: /bc note <Tab>
else if (args.length == 2 && args[0].equalsIgnoreCase("note")) {
StringUtil.copyPartialMatches(args[1], instruments, completions);
}
// 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);
return completions;
}
}

View File

@@ -3,8 +3,8 @@ package viper;
import org.bukkit.configuration.file.FileConfiguration; 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.*;
import java.io.IOException; import java.nio.charset.StandardCharsets;
public class ConfigManager { public class ConfigManager {
private final ButtonControl plugin; private final ButtonControl plugin;
@@ -25,7 +25,8 @@ public class ConfigManager {
plugin.saveResource("config.yml", false); plugin.saveResource("config.yml", false);
} }
config = YamlConfiguration.loadConfiguration(configFile); config = YamlConfiguration.loadConfiguration(configFile);
// Setze Standardwerte, falls sie fehlen
mergeDefaults(config, "config.yml", configFile);
setConfigDefaults(); setConfigDefaults();
} }
@@ -35,116 +36,113 @@ public class ConfigManager {
plugin.saveResource("lang.yml", false); plugin.saveResource("lang.yml", false);
} }
lang = YamlConfiguration.loadConfiguration(langFile); lang = YamlConfiguration.loadConfiguration(langFile);
// Setze Standardnachrichten, falls sie fehlen
mergeDefaults(lang, "lang.yml", langFile);
setLangDefaults(); setLangDefaults();
} }
private void mergeDefaults(FileConfiguration file, String resourceName, File targetFile) {
try (InputStream is = plugin.getResource(resourceName);
InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
if (is == null) {
plugin.getLogger().warning(resourceName + " nicht im Plugin-Jar gefunden, Merge übersprungen.");
return;
}
FileConfiguration defaults = YamlConfiguration.loadConfiguration(reader);
boolean changed = false;
for (String key : defaults.getKeys(true)) {
if (!file.contains(key)) {
file.set(key, defaults.get(key));
changed = true;
plugin.getLogger().info("[ConfigManager] Neuer Key in " + resourceName + " hinzugefügt: " + key);
}
}
if (changed) {
file.save(targetFile);
plugin.getLogger().info(resourceName + " wurde mit neuen Standardwerten ergänzt.");
}
} catch (IOException e) {
plugin.getLogger().severe("Fehler beim Mergen von " + resourceName + ": " + e.getMessage());
}
}
private void setConfigDefaults() { private void setConfigDefaults() {
if (!config.contains("max-doors")) { if (!config.contains("max-doors")) config.set("max-doors", 20);
config.set("max-doors", 20); if (!config.contains("max-lamps")) config.set("max-lamps", 50);
} if (!config.contains("max-noteblocks")) config.set("max-noteblocks", 10);
if (!config.contains("max-lamps")) { if (!config.contains("max-gates")) config.set("max-gates", 20);
config.set("max-lamps", 50); if (!config.contains("max-trapdoors")) config.set("max-trapdoors", 20);
} if (!config.contains("max-bells")) config.set("max-bells", 5);
if (!config.contains("max-noteblocks")) { if (!config.contains("default-note")) config.set("default-note", "PIANO");
config.set("max-noteblocks", 10); if (!config.contains("double-note-enabled")) config.set("double-note-enabled", true);
} if (!config.contains("double-note-delay-ms")) config.set("double-note-delay-ms", 1000);
if (!config.contains("default-note")) { if (!config.contains("motion-detection-radius")) config.set("motion-detection-radius", 5.0);
config.set("default-note", "PIANO"); if (!config.contains("motion-close-delay-ms")) config.set("motion-close-delay-ms", 5000);
}
if (!config.contains("double-note-enabled")) {
config.set("double-note-enabled", true);
}
if (!config.contains("double-note-delay-ms")) {
config.set("double-note-delay-ms", 1000);
}
saveConfig(); saveConfig();
} }
private void setLangDefaults() { private void setLangDefaults() {
// Standardnachrichten hinzufügen, ohne bestehende zu überschreiben if (!lang.contains("tueren-geoeffnet")) lang.set("tueren-geoeffnet", "§aTüren wurden geöffnet.");
if (!lang.contains("tueren-geoeffnet")) { if (!lang.contains("tueren-geschlossen")) lang.set("tueren-geschlossen", "§cTüren wurden geschlossen.");
lang.set("tueren-geoeffnet", "§aTüren wurden geöffnet."); if (!lang.contains("max-tueren-erreicht")) lang.set("max-tueren-erreicht", "§cMaximale Anzahl an Türen erreicht.");
}
if (!lang.contains("tueren-geschlossen")) { if (!lang.contains("lampen-eingeschaltet")) lang.set("lampen-eingeschaltet", "§aLampen wurden eingeschaltet.");
lang.set("tueren-geschlossen", "§cTüren wurden geschlossen."); if (!lang.contains("lampen-ausgeschaltet")) lang.set("lampen-ausgeschaltet", "§cLampen wurden ausgeschaltet.");
} if (!lang.contains("max-lampen-erreicht")) lang.set("max-lampen-erreicht", "§cMaximale Anzahl an Lampen erreicht.");
if (!lang.contains("lampen-eingeschaltet")) {
lang.set("lampen-eingeschaltet", "§aLampen wurden eingeschaltet."); if (!lang.contains("notenblock-ausgeloest")) lang.set("notenblock-ausgeloest", "§aNotenblock-Klingel wurde ausgelöst.");
} if (!lang.contains("max-notenbloecke-erreicht")) lang.set("max-notenbloecke-erreicht", "§cMaximale Anzahl an Notenblöcken erreicht.");
if (!lang.contains("lampen-ausgeschaltet")) {
lang.set("lampen-ausgeschaltet", "§cLampen wurden ausgeschaltet."); if (!lang.contains("gates-geoeffnet")) lang.set("gates-geoeffnet", "§aZauntore wurden geöffnet.");
} if (!lang.contains("gates-geschlossen")) lang.set("gates-geschlossen", "§cZauntore wurden geschlossen.");
if (!lang.contains("bloecke-umgeschaltet")) { if (!lang.contains("max-gates-erreicht")) lang.set("max-gates-erreicht", "§cMaximale Anzahl an Zauntoren erreicht.");
lang.set("bloecke-umgeschaltet", "§eBlöcke wurden umgeschaltet.");
} if (!lang.contains("fallturen-geoeffnet")) lang.set("fallturen-geoeffnet", "§aFalltüren wurden geöffnet.");
if (!lang.contains("keine-bloecke-verbunden")) { if (!lang.contains("fallturen-geschlossen")) lang.set("fallturen-geschlossen", "§cFalltüren wurden geschlossen.");
lang.set("keine-bloecke-verbunden", "§cKeine Blöcke sind verbunden."); if (!lang.contains("max-fallturen-erreicht")) lang.set("max-fallturen-erreicht", "§cMaximale Anzahl an Falltüren erreicht.");
}
if (!lang.contains("max-tueren-erreicht")) { if (!lang.contains("glocke-gelaeutet")) lang.set("glocke-gelaeutet", "§eGlocke wurde geläutet.");
lang.set("max-tueren-erreicht", "§cMaximale Anzahl an Türen erreicht."); if (!lang.contains("max-glocken-erreicht")) lang.set("max-glocken-erreicht", "§cMaximale Anzahl an Glocken erreicht.");
}
if (!lang.contains("max-lampen-erreicht")) { if (!lang.contains("bloecke-umgeschaltet")) lang.set("bloecke-umgeschaltet", "§eBlöcke wurden umgeschaltet.");
lang.set("max-lampen-erreicht", "§cMaximale Anzahl an Lampen erreicht."); if (!lang.contains("keine-bloecke-verbunden")) lang.set("keine-bloecke-verbunden", "§cKeine Blöcke sind verbunden.");
} if (!lang.contains("block-verbunden")) lang.set("block-verbunden", "§aBlock verbunden.");
if (!lang.contains("max-notenbloecke-erreicht")) { if (!lang.contains("block-bereits-verbunden")) lang.set("block-bereits-verbunden", "§cBlock ist bereits verbunden.");
lang.set("max-notenbloecke-erreicht", "§cMaximale Anzahl an Notenblöcken erreicht."); if (!lang.contains("controller-platziert")) lang.set("controller-platziert", "§aController platziert.");
} if (!lang.contains("controller-entfernt")) lang.set("controller-entfernt", "§cController entfernt.");
if (!lang.contains("block-verbunden")) { if (!lang.contains("instrument-gesetzt")) lang.set("instrument-gesetzt", "§aDein Notenblock-Instrument wurde auf %s gesetzt.");
lang.set("block-verbunden", "§aBlock verbunden."); if (!lang.contains("ungueltiges-instrument")) lang.set("ungueltiges-instrument", "§cUngültiges Instrument! Verwende: /bc note <Instrument>");
} if (!lang.contains("konfiguration-reloaded")) lang.set("konfiguration-reloaded", "§aKonfiguration und Daten erfolgreich neu geladen!");
if (!lang.contains("block-bereits-verbunden")) { if (!lang.contains("keine-berechtigung")) lang.set("keine-berechtigung", "§cDu hast keine Berechtigung für diesen Befehl!");
lang.set("block-bereits-verbunden", "§cBlock ist bereits verbunden."); if (!lang.contains("kolben-ausgefahren")) lang.set("kolben-ausgefahren", "§6[ButtonControl] §7Kolben wurden ausgefahren.");
} if (!lang.contains("kolben-eingefahren")) lang.set("kolben-eingefahren", "§6[ButtonControl] §7Kolben wurden eingezogen.");
if (!lang.contains("controller-platziert")) { if (!lang.contains("max-kolben-erreicht")) lang.set("max-kolben-erreicht", "§6[ButtonControl] §7Maximale Anzahl an Kolben erreicht.");
lang.set("controller-platziert", "§aController platziert.");
}
if (!lang.contains("controller-entfernt")) {
lang.set("controller-entfernt", "§cController entfernt.");
}
if (!lang.contains("notenblock-ausgeloest")) {
lang.set("notenblock-ausgeloest", "§aNotenblock-Klingel wurde ausgelöst.");
}
if (!lang.contains("instrument-gesetzt")) {
lang.set("instrument-gesetzt", "§aDein Notenblock-Instrument wurde auf %s gesetzt.");
}
if (!lang.contains("ungueltiges-instrument")) {
lang.set("ungueltiges-instrument", "§cUngültiges Instrument! Verwende: /bc note <Instrument>");
}
if (!lang.contains("konfiguration-reloaded")) {
lang.set("konfiguration-reloaded", "§aKonfiguration und Daten erfolgreich neu geladen!");
}
if (!lang.contains("keine-berechtigung")) {
lang.set("keine-berechtigung", "§cDu hast keine Berechtigung für diesen Befehl!");
}
saveLang(); saveLang();
} }
public void reloadConfig() { public void reloadConfig() {
config = YamlConfiguration.loadConfiguration(configFile); config = YamlConfiguration.loadConfiguration(configFile);
lang = YamlConfiguration.loadConfiguration(langFile); lang = YamlConfiguration.loadConfiguration(langFile);
setConfigDefaults(); // Stelle sicher, dass neue Standardwerte hinzugefügt werden mergeDefaults(config, "config.yml", configFile);
setLangDefaults(); // Stelle sicher, dass neue Nachrichten hinzugefügt werden mergeDefaults(lang, "lang.yml", langFile);
setConfigDefaults();
setLangDefaults();
} }
public FileConfiguration getConfig() { public FileConfiguration getConfig() {
return config; return config;
} }
public int getMaxDoors() { public int getMaxDoors() { return config.getInt("max-doors", 20); }
return config.getInt("max-doors", 20); public int getMaxLamps() { return config.getInt("max-lamps", 50); }
} public int getMaxNoteBlocks() { return config.getInt("max-noteblocks", 10); }
public int getMaxGates() { return config.getInt("max-gates", getMaxDoors()); }
public int getMaxLamps() { public int getMaxTrapdoors() { return config.getInt("max-trapdoors", getMaxDoors()); }
return config.getInt("max-lamps", 50); public int getMaxBells() { return config.getInt("max-bells", 5); }
}
public int getMaxNoteBlocks() {
return config.getInt("max-noteblocks", 10);
}
public String getMessage(String key) { public String getMessage(String key) {
return lang.getString(key, "Nachricht nicht gefunden: " + key); return lang.getString(key, "§cNachricht nicht gefunden: " + key);
} }
public void saveConfig() { public void saveConfig() {

View File

@@ -7,7 +7,6 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
public class DataManager { public class DataManager {
@@ -32,76 +31,107 @@ public class DataManager {
data = YamlConfiguration.loadConfiguration(dataFile); data = YamlConfiguration.loadConfiguration(dataFile);
} }
// --- Spielerbasierte Methoden --- public String getButtonIdForLocation(String location) {
return getButtonIdForPlacedController(location);
public List<String> getConnectedBlocks(String playerUUID, String buttonId) {
return data.getStringList("players." + playerUUID + ".buttons." + buttonId);
} }
public boolean canAccess(String buttonId, UUID playerUUID) {
if (isPublic(buttonId)) return true;
if (isOwner(buttonId, playerUUID)) return true;
List<String> trusted = data.getStringList("trust." + buttonId);
return trusted.contains(playerUUID.toString());
}
public boolean isOwner(String buttonId, UUID playerUUID) {
// Wir prüfen direkt im globalen Pfad der Buttons
return data.contains("players." + playerUUID.toString() + ".buttons." + buttonId);
}
public void registerController(String location, UUID ownerUUID, String buttonId) {
addPlacedController(ownerUUID.toString(), location, buttonId);
}
public void removeController(String location) {
if (data.getConfigurationSection("players") == null) return;
for (String playerUUID : data.getConfigurationSection("players").getKeys(false)) {
String path = "players." + playerUUID + ".placed-controllers." + location;
if (data.contains(path)) {
data.set(path, null);
}
}
removeMotionSensorSettings(location);
saveData();
}
public void addTrustedPlayer(String buttonId, UUID targetUUID) {
List<String> trusted = data.getStringList("trust." + buttonId);
if (!trusted.contains(targetUUID.toString())) {
trusted.add(targetUUID.toString());
data.set("trust." + buttonId, trusted);
saveData();
}
}
public void removeTrustedPlayer(String buttonId, UUID targetUUID) {
List<String> trusted = data.getStringList("trust." + buttonId);
trusted.remove(targetUUID.toString());
data.set("trust." + buttonId, trusted);
saveData();
}
public void setPublic(String buttonId, boolean isPublic) {
data.set("public-status." + buttonId, isPublic);
saveData();
}
public boolean isPublic(String buttonId) {
return data.getBoolean("public-status." + buttonId, false);
}
// Speichert die Blöcke für eine ID unter einem spezifischen Spieler
public void setConnectedBlocks(String playerUUID, String buttonId, List<String> blocks) { public void setConnectedBlocks(String playerUUID, String buttonId, List<String> blocks) {
data.set("players." + playerUUID + ".buttons." + buttonId, blocks); data.set("players." + playerUUID + ".buttons." + buttonId, blocks);
saveData(); saveData();
} }
public void addPlacedController(String playerUUID, String location, String buttonId) { 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); data.set("players." + playerUUID + ".placed-controllers." + location, buttonId);
saveData(); saveData();
} }
public String getButtonIdForPlacedController(String playerUUID, String location) {
return data.getString("players." + playerUUID + ".placed-controllers." + location);
}
public void removePlacedController(String playerUUID, String location) {
data.set("players." + playerUUID + ".placed-controllers." + location, null);
saveData();
}
public List<String> getAllPlacedControllers(String playerUUID) {
if (data.getConfigurationSection("players." + playerUUID + ".placed-controllers") == null) {
return new ArrayList<>();
}
Set<String> keys = data.getConfigurationSection("players." + playerUUID + ".placed-controllers").getKeys(false);
return new ArrayList<>(keys);
}
// --- Neue globale Methoden für Tageslichtsensoren etc. ---
public List<String> getAllPlacedControllers() {
List<String> allControllers = new ArrayList<>();
if (data.getConfigurationSection("players") == null) {
return allControllers;
}
Set<String> players = data.getConfigurationSection("players").getKeys(false);
for (String playerUUID : players) {
allControllers.addAll(getAllPlacedControllers(playerUUID));
}
return allControllers;
}
public String getButtonIdForPlacedController(String location) { public String getButtonIdForPlacedController(String location) {
if (data.getConfigurationSection("players") == null) return null; if (data.getConfigurationSection("players") == null) return null;
Set<String> players = data.getConfigurationSection("players").getKeys(false); for (String playerUUID : data.getConfigurationSection("players").getKeys(false)) {
for (String playerUUID : players) { String buttonId = data.getString("players." + playerUUID + ".placed-controllers." + location);
String buttonId = getButtonIdForPlacedController(playerUUID, location);
if (buttonId != null) return buttonId; if (buttonId != null) return buttonId;
} }
return null; return null;
} }
// VERBESSERT: Sucht gezielt nach den Blöcken für diese ID
public List<String> getConnectedBlocks(String buttonId) { public List<String> getConnectedBlocks(String buttonId) {
if (data.getConfigurationSection("players") == null) return null; if (data.getConfigurationSection("players") == null) return new ArrayList<>();
Set<String> players = data.getConfigurationSection("players").getKeys(false);
for (String playerUUID : players) { for (String playerUUID : data.getConfigurationSection("players").getKeys(false)) {
List<String> connected = getConnectedBlocks(playerUUID, buttonId); String path = "players." + playerUUID + ".buttons." + buttonId;
if (connected != null && !connected.isEmpty()) { if (data.contains(path)) {
return connected; return data.getStringList(path);
} }
} }
return null; return new ArrayList<>(); // Niemals null zurückgeben, um Fehler im Listener zu vermeiden
} }
// --- Notenblock-Instrument Methoden --- 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.toString() + ".instrument", instrument);
@@ -112,6 +142,29 @@ public class DataManager {
return data.getString("players." + playerUUID.toString() + ".instrument"); return data.getString("players." + playerUUID.toString() + ".instrument");
} }
public void setMotionSensorRadius(String location, double radius) {
data.set("motion-sensors." + location + ".radius", radius);
saveData();
}
public double getMotionSensorRadius(String location) {
return data.getDouble("motion-sensors." + location + ".radius", -1);
}
public void setMotionSensorDelay(String location, long delay) {
data.set("motion-sensors." + location + ".delay", delay);
saveData();
}
public long getMotionSensorDelay(String location) {
return data.getLong("motion-sensors." + location + ".delay", -1);
}
public void removeMotionSensorSettings(String location) {
data.set("motion-sensors." + location, null);
saveData();
}
public void saveData() { public void saveData() {
try { try {
data.save(dataFile); data.save(dataFile);

View File

@@ -0,0 +1,14 @@
package viper;
import org.bukkit.plugin.java.JavaPlugin;
// Import aus dem korrekten verschobenen Package:
import org.bstats.bukkit.Metrics;
public class MetricsHandler {
private static final int BSTATS_PLUGIN_ID = 26862;
public static void startMetrics(JavaPlugin plugin) {
new Metrics(plugin, BSTATS_PLUGIN_ID);
}
}

View File

@@ -0,0 +1,153 @@
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.Arrays;
public class MotionSensorGUI implements Listener {
private final ButtonControl plugin;
private final DataManager dataManager;
private final ConfigManager configManager;
private final String blockLocation;
private final String buttonId;
private final Player player;
private final Inventory inv;
public MotionSensorGUI(ButtonControl plugin, Player player, String blockLocation, String buttonId) {
this.plugin = plugin;
this.dataManager = plugin.getDataManager();
this.configManager = plugin.getConfigManager();
this.blockLocation = blockLocation;
this.buttonId = buttonId;
this.player = player;
this.inv = Bukkit.createInventory(player, 27, "Bewegungsmelder Einstellungen");
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
public void open() {
// Aktuelle Werte aus DataManager holen oder Standardwerte aus Config
double radius = dataManager.getMotionSensorRadius(blockLocation);
if (radius == -1) radius = configManager.getConfig().getDouble("motion-detection-radius", 5.0);
long delay = dataManager.getMotionSensorDelay(blockLocation);
if (delay == -1) delay = configManager.getConfig().getLong("motion-close-delay-ms", 5000L);
// Füllitems für leere Slots (graue Glasscheiben)
ItemStack filler = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
ItemMeta fillerMeta = filler.getItemMeta();
fillerMeta.setDisplayName(ChatColor.RESET + "");
filler.setItemMeta(fillerMeta);
for (int i = 0; i < 27; i++) {
inv.setItem(i, filler);
}
// Items für die GUI
ItemStack radiusItem = new ItemStack(Material.COMPASS);
ItemMeta radiusMeta = radiusItem.getItemMeta();
radiusMeta.setDisplayName(ChatColor.GREEN + "Erkennungsradius: " + radius + " Blöcke");
radiusMeta.setLore(Arrays.asList(
ChatColor.GRAY + "Linksklick: +0.5 Blöcke",
ChatColor.GRAY + "Rechtsklick: -0.5 Blöcke"
));
radiusItem.setItemMeta(radiusMeta);
ItemStack delayItem = new ItemStack(Material.CLOCK);
ItemMeta delayMeta = delayItem.getItemMeta();
delayMeta.setDisplayName(ChatColor.GREEN + "Schließverzögerung: " + (delay / 1000.0) + " Sekunden");
delayMeta.setLore(Arrays.asList(
ChatColor.GRAY + "Linksklick: +1 Sekunde",
ChatColor.GRAY + "Rechtsklick: -1 Sekunde"
));
delayItem.setItemMeta(delayMeta);
ItemStack saveItem = new ItemStack(Material.EMERALD);
ItemMeta saveMeta = saveItem.getItemMeta();
saveMeta.setDisplayName(ChatColor.GREEN + "Speichern und Schließen");
saveItem.setItemMeta(saveMeta);
// Items in die GUI setzen
inv.setItem(11, radiusItem);
inv.setItem(15, delayItem);
inv.setItem(22, saveItem);
player.openInventory(inv);
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (!event.getInventory().equals(inv) || !event.getWhoClicked().equals(player)) return;
if (event.getCurrentItem() == null) return;
event.setCancelled(true); // Alle Klicks standardmäßig abbrechen
int slot = event.getRawSlot();
ItemStack clicked = event.getCurrentItem();
// Nur Klicks auf Slots 11, 15 und 22 verarbeiten
if (slot != 11 && slot != 15 && slot != 22) return;
double radius = dataManager.getMotionSensorRadius(blockLocation);
if (radius == -1) radius = configManager.getConfig().getDouble("motion-detection-radius", 5.0);
long delay = dataManager.getMotionSensorDelay(blockLocation);
if (delay == -1) delay = configManager.getConfig().getLong("motion-close-delay-ms", 5000L);
if (clicked.getType() == Material.COMPASS && slot == 11) {
if (event.isLeftClick()) {
radius = Math.min(radius + 0.5, 20.0); // Max. Radius: 20 Blöcke
} else if (event.isRightClick()) {
radius = Math.max(radius - 0.5, 0.5); // Min. Radius: 0.5 Blöcke
}
dataManager.setMotionSensorRadius(blockLocation, radius);
ItemMeta meta = clicked.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Erkennungsradius: " + radius + " Blöcke");
meta.setLore(Arrays.asList(
ChatColor.GRAY + "Linksklick: +0.5 Blöcke",
ChatColor.GRAY + "Rechtsklick: -0.5 Blöcke"
));
clicked.setItemMeta(meta);
inv.setItem(11, clicked);
} else if (clicked.getType() == Material.CLOCK && slot == 15) {
if (event.isLeftClick()) {
delay = Math.min(delay + 1000, 30000); // Max. Verzögerung: 30 Sekunden
} else if (event.isRightClick()) {
delay = Math.max(delay - 1000, 1000); // Min. Verzögerung: 1 Sekunde
}
dataManager.setMotionSensorDelay(blockLocation, delay);
ItemMeta meta = clicked.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Schließverzögerung: " + (delay / 1000.0) + " Sekunden");
meta.setLore(Arrays.asList(
ChatColor.GRAY + "Linksklick: +1 Sekunde",
ChatColor.GRAY + "Rechtsklick: -1 Sekunde"
));
clicked.setItemMeta(meta);
inv.setItem(15, clicked);
} else if (clicked.getType() == Material.EMERALD && slot == 22) {
player.closeInventory();
}
}
@EventHandler
public void onInventoryDrag(InventoryDragEvent event) {
if (!event.getInventory().equals(inv) || !event.getWhoClicked().equals(player)) return;
event.setCancelled(true); // Verhindert Drag-and-Drop
}
@EventHandler
public void onInventoryClose(InventoryCloseEvent event) {
if (event.getPlayer().equals(player) && event.getInventory().equals(inv)) {
InventoryClickEvent.getHandlerList().unregister(this);
InventoryDragEvent.getHandlerList().unregister(this);
InventoryCloseEvent.getHandlerList().unregister(this);
}
}
}

View File

@@ -0,0 +1,42 @@
package viper;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
import java.util.function.Consumer;
import org.json.JSONObject; // -> Dependency in pom.xml nötig
public class UpdateChecker {
private final JavaPlugin plugin;
private final int resourceId;
public UpdateChecker(JavaPlugin plugin, int resourceId) {
this.plugin = plugin;
this.resourceId = resourceId;
}
public void getVersion(final Consumer<String> consumer) {
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> {
try {
HttpURLConnection connection = (HttpURLConnection)
new URL("https://api.spiget.org/v2/resources/" + resourceId + "/versions/latest").openConnection();
connection.setRequestMethod("GET");
connection.addRequestProperty("User-Agent", "Mozilla/5.0");
try (Scanner scanner = new Scanner(connection.getInputStream())) {
String response = scanner.useDelimiter("\\A").next();
JSONObject json = new JSONObject(response);
String version = json.optString("name", "").trim();
consumer.accept(version);
}
} catch (Exception e) {
plugin.getLogger().warning("Konnte nicht nach Updates suchen: " + e.getMessage());
consumer.accept("");
}
});
}
}

View File

@@ -1,6 +1,18 @@
# Maximale Anzahl an steuerbaren Blöcken pro Controller
max-doors: 20 max-doors: 20
max-lamps: 50 max-lamps: 50
max-noteblocks: 10 max-noteblocks: 10
default-note: PIANO max-gates: 20
max-trapdoors: 20
max-bells: 5
# Standard-Instrument für Notenblöcke
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
motion-detection-radius: 5.0
motion-close-delay-ms: 5000

View File

@@ -1,18 +1,57 @@
# lang.yml - ButtonControl Nachrichten
# --- Bestehende Nachrichten (Türen/Tore/Falltü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."
gates-geoeffnet: "§aZauntore wurden geöffnet."
gates-geschlossen: "§cZauntore wurden geschlossen."
max-gates-erreicht: "§cMaximale Anzahl an Zauntoren erreicht."
fallturen-geoeffnet: "§aFalltüren wurden geöffnet."
fallturen-geschlossen: "§cFalltüren wurden geschlossen."
max-fallturen-erreicht: "§cMaximale Anzahl an Falltüren erreicht."
# --- Lampen & Glocken ---
lampen-eingeschaltet: "§aLampen wurden eingeschaltet." lampen-eingeschaltet: "§aLampen wurden eingeschaltet."
lampen-ausgeschaltet: "§cLampen wurden ausgeschaltet." lampen-ausgeschaltet: "§cLampen wurden ausgeschaltet."
bloecke-umgeschaltet: "§eBlöcke wurden umgeschaltet."
keine-bloecke-verbunden: "§cKeine Blöcke sind verbunden."
max-tueren-erreicht: "§cMaximale Anzahl an Türen erreicht."
max-lampen-erreicht: "§cMaximale Anzahl an Lampen erreicht." max-lampen-erreicht: "§cMaximale Anzahl an Lampen erreicht."
max-notenbloecke-erreicht: cMaximale Anzahl an Notenblöcken erreicht." glocke-gelaeutet: aGlocke wurde geläutet."
block-verbunden: "§aBlock verbunden." max-glocken-erreicht: "§cMaximale Anzahl an Glocken erreicht."
block-bereits-verbunden: "§cBlock ist bereits verbunden."
controller-platziert: "§aController platziert." # --- Notenblöcke & Instrumente ---
controller-entfernt: "§cController entfernt."
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 Notenblock-Instrument wurde auf %s gesetzt."
ungueltiges-instrument: "§cUngültiges Instrument! Verwende: /bc note <Instrument>" ungueltiges-instrument: "§cUngültiges Instrument! Verwende: /bc note <Instrument>"
konfiguration-neugeladen: "§aKonfiguration und Daten erfolgreich neu geladen!" max-notenbloecke-erreicht: "§cMaximale Anzahl an Notenblöcken erreicht."
# --- Kolben (Erweiterung) ---
kolben-ausgefahren: "§6[ButtonControl] §7Kolben wurden ausgefahren."
kolben-eingefahren: "§6[ButtonControl] §7Kolben wurden eingefahren."
max-kolben-erreicht: "§6[ButtonControl] §7Maximale Anzahl an Kolben erreicht."
# --- Controller & Verbindung ---
block-verbunden: "§aBlock verbunden."
block-bereits-verbunden: "§cBlock ist bereits verbunden."
keine-bloecke-verbunden: "§cKeine Blöcke sind verbunden."
bloecke-umgeschaltet: "§eBlöcke wurden umgeschaltet."
controller-platziert: "§aController platziert."
controller-entfernt: "§cController entfernt."
# --- Trust- & Sicherheitssystem ---
keine-berechtigung: "§cDu hast keine Berechtigung für diesen Befehl!" keine-berechtigung: "§cDu hast keine Berechtigung für diesen Befehl!"
keine-berechtigung-controller: "§cDu hast keine Berechtigung, diesen Controller zu benutzen!"
nur-besitzer-abbauen: "§cNur der Besitzer darf diesen Controller verwalten oder abbauen!"
spieler-nicht-gefunden: "§cDieser Spieler wurde nicht gefunden."
# %s wird durch "§aÖffentlich" oder "§cPrivat" ersetzt
status-geandert: "§6[ButtonControl] §7Der Controller ist nun %s§7."
# Trust Nachrichten
trust-hinzugefuegt: "§aDu hast %s Vertrauen für diesen Controller gegeben."
trust-entfernt: "§cDu hast %s das Vertrauen für diesen Controller entzogen."
kein-controller-im-blick: "§cDu musst einen Controller (Button, Haken oder Sensor) direkt ansehen!"
# --- System ---
konfiguration-neugeladen: "§aKonfiguration und Daten erfolgreich neu geladen!"

View File

@@ -1,14 +1,14 @@
name: ButtonControl name: ButtonControl
version: 1.1 version: 1.5
main: viper.ButtonControl main: viper.ButtonControl
api-version: 1.21 api-version: 1.21
author: viper author: M_Viper
description: Ein Plugin, um Türen, Redstone-Lampen und Notenblöcke mit einem Button oder Tageslichtsensor zu steuern. description: Ein Plugin, um Türen, Redstone-Lampen und Notenblöcke mit einem Button, Tageslichtsensor oder Bewegungsmelder zu steuern.
commands: commands:
bc: bc:
description: Steuert das ButtonControl Plugin description: Hauptbefehl für ButtonControl
usage: /<command> [info|reload|note <instrument>] usage: /bc <info|reload|note|trust|untrust|public|private>
aliases: [buttoncontrol] aliases: [buttoncontrol]
permissions: permissions:
@@ -18,3 +18,9 @@ permissions:
buttoncontrol.note: buttoncontrol.note:
description: Erlaubt das Ändern des Notenblock-Instruments description: Erlaubt das Ändern des Notenblock-Instruments
default: true default: true
buttoncontrol.update:
description: Erlaubt das Empfangen von Update-Benachrichtigungen
default: op
buttoncontrol.trust:
description: Erlaubt das Verwalten von Vertrauen und Status (Public/Private)
default: true