3 Commits
1.0.5 ... 1.0.7

Author SHA1 Message Date
250bf2fea6 README.md aktualisiert 2026-01-24 16:13:45 +00:00
64a03b539e Upload pom.xml via GUI 2026-01-24 16:03:51 +00:00
1db87e81f0 Update from Git Manager GUI 2026-01-24 17:03:48 +01:00
15 changed files with 1137 additions and 405 deletions

196
README.md
View File

@@ -4,7 +4,7 @@
<img src="https://m-viper.de/img/NexusLobby.png" width="500" alt="NexusLobby"> <img src="https://m-viper.de/img/NexusLobby.png" width="500" alt="NexusLobby">
</p> </p>
Ein umfassendes Lobby-Plugin fur Minecraft Server (Paper/Spigot 1.21+) mit modularem Aufbau, umfangreichen Sicherheitsfunktionen und voller Konfigurierbarkeit. Ein umfassendes Lobby-Plugin für Minecraft Server (Paper/Spigot 1.21+) mit modularem Aufbau, High-End NPC-System, umfangreichen Sicherheitsfunktionen und voller Konfigurierbarkeit.
![Minecraft](https://img.shields.io/badge/Minecraft-1.21+-green) ![Minecraft](https://img.shields.io/badge/Minecraft-1.21+-green)
![Java](https://img.shields.io/badge/Java-21+-orange) ![Java](https://img.shields.io/badge/Java-21+-orange)
@@ -16,62 +16,43 @@ Ein umfassendes Lobby-Plugin fur Minecraft Server (Paper/Spigot 1.21+) mit modul
**ALLE RECHTE VORBEHALTEN** **ALLE RECHTE VORBEHALTEN**
Dieses Plugin ist urheberrechtlich geschutzt. Es gelten folgende Bedingungen: Dieses Plugin ist urheberrechtlich geschützt. Es gelten folgende Bedingungen:
- Die Nutzung ist ausschliesslich fur den persönlichen Gebrauch gestattet - Die Nutzung ist ausschließlich für den persönlichen Gebrauch gestattet.
- Die Weitergabe, Verbreitung oder Veröffentlichung des Plugins ist **strengstens untersagt** - Die Weitergabe, Verbreitung oder Veröffentlichung des Plugins ist **strengstens untersagt**.
- Jegliche Anderung, Modifikation oder Dekompilierung des Codes ist **verboten** - Jegliche Änderung, Modifikation oder Dekompilierung des Codes ist **verboten**.
- Das Plugin darf nicht verkauft, vermietet oder anderweitig kommerziell genutzt werden - Das Plugin darf nicht verkauft, vermietet oder anderweitig kommerziell genutzt werden.
- Eine Weitergabe an Dritte ist ohne ausdrückliche schriftliche Genehmigung nicht gestattet - Eine Weitergabe an Dritte ist ohne ausdrückliche schriftliche Genehmigung nicht gestattet.
Bei Verstoss gegen diese Bedingungen behalten wir uns rechtliche Schritte vor. Bei Verstoß gegen diese Bedingungen behalten wir uns rechtliche Schritte vor.
--- ---
## Features ## Features
### Lobby-Management ### 🤖 High-End NPC & ArmorStand System
- **Spawn-System** - Automatischer Teleport zum Spawn bei Join/Respawn - **Conversation Manager** - Komplexe Dialoge zwischen NPCs mit Sprechblasen und Sound-Effekten.
- **Void-Schutz** - Automatischer Teleport bei Fall ins Void - **Dynamic KI** - NPCs reagieren auf Tageszeit (Fackel nachts) und ziehen bei Annäherung von Spielern das Schwert.
- **Doppelsprung** - Konfigurierbarer Double-Jump mit Cooldown - **LookAt-Logik** - NPCs verfolgen flüssig die Kopfbewegungen von Spielern in der Nähe.
- **Build-Modus** - Schnelles Umschalten zwischen Bau- und Spielmodus - **Command Binding** - Binde Spieler-, Konsolen- oder Bungee-Befehle an NPC-Slots (0-9).
- **Status-Backup** - Automatisches Speichern von NPC-Namen via PersistentDataContainer & Tags.
### Visuelle Elemente ### 🌍 Lobby-Management
- **Scoreboard** - Anpassbares Sidebar-Scoreboard mit Animationen - **Spawn-System** - Automatischer Teleport zum Spawn bei Join/Respawn.
- **Tablist** - Header und Footer mit PlaceholderAPI-Support - **Portal-System** - BungeeCord-Portale für nahtlose Server-Wechsel.
- **BossBar** - Animierte Boss-Bar mit wechselnden Nachrichten - **Build-Modus** - Schnelles Umschalten zwischen Bau- und Spielmodus.
- **ActionBar** - Permanente ActionBar-Nachrichten - **Double-Jump** - Konfigurierbarer Doppelsprung mit Cooldown und Partikeln.
### Sicherheit ### 🛡️ Sicherheit & Protection
- **VPN-Blocker** - Blockiert VPN/Proxy-Verbindungen (proxycheck.io API) - **VPN-Blocker** - Blockiert VPN/Proxy-Verbindungen via proxycheck.io API.
- **Country-Blocker** - Erlaubt nur bestimmte Lander (Whitelist/Blacklist) - **Country-Blocker** - Geo-IP Filter (Whitelist/Blacklist für Länder).
- **Wartungsmodus** - Sperrt den Server für nicht-berechtigte Spieler - **Maintenance** - Vollständiger Wartungsmodus mit Whitelist-Funktion.
- **Lobby-Schutz** - Verhindert Griefing und unerwünschte Aktionen - **World-Protection** - Schutz vor Griefing, Hunger, Fallschaden und PvP.
### Zusatzliche Module ### 📊 Visuelle Elemente
- **Portal-System** - Erstelle Portale fur Server-Wechsel (BungeeCord) - **Scoreboard & Tablist** - Vollständig animiert mit PlaceholderAPI-Support.
- **ArmorStand-Tools** - Bearbeite ArmorStands mit GUI und Command-Binding - **BossBar & ActionBar** - Rotierende Nachrichten und permanente Status-Anzeigen.
- **Server-Switcher** - GUI-basierter Server-Wechsel - **Hologramme** - Einfache Erstellung von Text-Displays in der Lobby.
- **Spieler verstecken** - Toggle fur Spieler-Sichtbarkeit
- **Chat-Suppressor** - Unterdrückung fur globalen Chat
---
## Installation
### Voraussetzungen
- Paper/Spigot Server 1.21 oder höher
- Java 21 oder hoher
### Optionale Abhangigkeiten
- [PlaceholderAPI](https://www.spigotmc.org/resources/placeholderapi.6245/) - Für Platzhalter-Support
- [LuckPerms](https://luckperms.net/) - Für Berechtigungsverwaltung
### Schritte
1. Lade `NexusLobby.jar` herunter
2. Kopiere die JAR in den `plugins/` Ordner deines Servers
3. Starte den Server neu
4. Konfiguriere das Plugin in `plugins/NexusLobby/`
--- ---
@@ -79,73 +60,31 @@ Bei Verstoss gegen diese Bedingungen behalten wir uns rechtliche Schritte vor.
| Befehl | Beschreibung | Berechtigung | | Befehl | Beschreibung | Berechtigung |
|--------|--------------|--------------| |--------|--------------|--------------|
| `/nexuslobby` | Hauptbefehl mit Hilfe | `nexuslobby.use` | | `/nexuslobby` | Hauptbefehl (reload, setspawn, silentjoin) | `nexuslobby.admin` |
| `/nexuslobby reload` | Konfiguration neu laden | `nexuslobby.reload` | | `/nexuscmd` | NPC Command/Dialog Verwaltung (aliases: `ncmd`, `conv`) | `nexuslobby.armorstand.cmd` |
| `/nexuslobby setspawn` | Spawn-Punkt setzen | `nexuslobby.setspawn` | | `/nexustools` | NPC Editor GUI (Rotation, KI, Sichtbarkeit) | `nexuslobby.armorstand.use` |
| `/build` | Build-Modus umschalten | `nexuslobby.build` | | `/build` | Aktiviert/Deaktiviert den Baumodus | `nexuslobby.build` |
| `/maintenance` | Wartungsmodus verwalten | `nexuslobby.maintenance` | | `/maintenance` | Schaltet den Wartungsmodus (on/off) | `nexuslobby.maintenance` |
| `/portal` | Portal-System verwalten | `nexuslobby.portal` | | `/portal` | Verwaltung der Server-Portale | `nexuslobby.portal` |
| `/armorstand` | ArmorStand-Editor | `nexuslobby.armorstand` | | `/holo` | Erstellt oder löscht Text-Hologramme | `nexuslobby.hologram` |
| `/lobbysettings` | Spieler-Einstellungen | `nexuslobby.settings` | | `/mapart` | Erstellt Bilder aus URLs auf Karten-Rahmen | `nexuslobby.mapart` |
--- ---
## Konfiguration ## Konfiguration (Auszug)
### config.yml ### conversations.yml
```yaml ```yaml
prefix: "&8[&6NexusLobby&8] " conversations:
willkommen:
dialogue:
- "&eWächter: &7Willkommen auf dem Server!"
- "&aBürger: &7Schön, dass du da bist!"
spawn: links:
teleport_on_join: true UUID-NPC1:
teleport_on_respawn: true partner: UUID-NPC2
world: "world" dialog: willkommen
x: 0.5
y: 100.0
z: 0.5
double_jump:
enabled: true
cooldown: 3
velocity_y: 1.0
velocity_multiplier: 1.5
```
### settings.yml
```yaml
player_visibility: true
double_jump: true
flight: false
gamerules:
block_break: false
block_place: false
pvp: false
hunger: false
fall_damage: false
```
### visuals.yml
```yaml
scoreboard:
enabled: true
title: "&6&lNEXUS LOBBY"
lines:
- "&7"
- "&fSpieler: &a%online%"
- "&fRang: &e%luckperms_primary_group_name%"
tablist:
enabled: true
header:
- "&6&lNEXUS NETWORK"
footer:
- "&7Spieler online: &a%online%"
bossbar:
enabled: true
messages:
- "&6Willkommen auf NexusNetwork!"
``` ```
--- ---
@@ -154,47 +93,28 @@ bossbar:
| Berechtigung | Beschreibung | | Berechtigung | Beschreibung |
|--------------|--------------| |--------------|--------------|
| `nexuslobby.*` | Alle Berechtigungen | | `nexuslobby.admin` | Voller Zugriff auf alle System-Einstellungen |
| `nexuslobby.use` | Grundlegende Nutzung | | `nexuslobby.armorstand.cmd` | NPCs konfigurieren und Dialoge verknüpfen |
| `nexuslobby.reload` | Konfiguration neu laden | | `nexuslobby.armorstand.use` | Zugriff auf die ArmorStand-Editor GUI |
| `nexuslobby.setspawn` | Spawn setzen | | `nexuslobby.build` | Berechtigung für den Baumodus |
| `nexuslobby.build` | Build-Modus nutzen | | `nexuslobby.portal` | Portale erstellen und löschen |
| `nexuslobby.maintenance` | Wartungsmodus verwalten |
| `nexuslobby.portal` | Portale verwalten |
| `nexuslobby.armorstand` | ArmorStand-Editor nutzen |
### Bypass-Berechtigungen ### Bypass-Berechtigungen
| Berechtigung | Beschreibung | | Berechtigung | Beschreibung |
|--------------|--------------| |--------------|--------------|
| `nexuslobby.bypass.protection` | Lobby-Schutz umgehen | | `nexuslobby.bypass.maintenance` | Server trotz Wartungsmodus betreten |
| `nexuslobby.bypass.maintenance` | Wartungsmodus umgehen | | `nexuslobby.bypass.vpn` | VPN-Check überspringen |
| `nexuslobby.bypass.vpn` | VPN-Blocker umgehen |
| `nexuslobby.bypass.country` | Country-Blocker umgehen |
--- ---
## PlaceholderAPI ## Support & Kontakt
Das Plugin registriert eigene Platzhalter unter der Expansion `nexuslobby`: - **Wiki:** Ausführliche Dokumentation der Module im [Wiki](../../../wiki).
- **Bug-Reports:** Erstelle ein [Issue](../../../issues) bei technischen Problemen.
| Platzhalter | Beschreibung |
|-------------|--------------|
| `%nexuslobby_online%` | Spieler online |
| `%nexuslobby_max_players%` | Maximale Spieler |
| `%nexuslobby_maintenance%` | Wartungsmodus Status |
| `%nexuslobby_build_mode%` | Build-Modus Status |
| `%nexuslobby_double_jump%` | Double-Jump Status |
--- ---
## Support **Copyright (c) 2026 - M_Viper - Alle Rechte vorbehalten**
- [Wiki](../../../wiki) - Ausführliche Dokumentation
- [Issues](../../../issues) - Bug-Reports und Feature-Requests
---
**Copyright (c) 2025 - Alle Rechte vorbehalten**
Die unbefugte Vervielfältigung, Verbreitung oder Weitergabe dieses Plugins ist strafbar und wird rechtlich verfolgt. Die unbefugte Vervielfältigung, Verbreitung oder Weitergabe dieses Plugins ist strafbar und wird rechtlich verfolgt.

View File

@@ -6,7 +6,7 @@
<groupId>de.nexuslobby</groupId> <groupId>de.nexuslobby</groupId>
<artifactId>NexusLobby</artifactId> <artifactId>NexusLobby</artifactId>
<version>1.0.4</version> <version>1.0.5</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>NexusLobby</name> <name>NexusLobby</name>

View File

@@ -13,6 +13,7 @@ import de.nexuslobby.modules.settings.LobbySettingsModule;
import de.nexuslobby.modules.portal.PortalManager; import de.nexuslobby.modules.portal.PortalManager;
import de.nexuslobby.modules.portal.PortalCommand; import de.nexuslobby.modules.portal.PortalCommand;
import de.nexuslobby.modules.servers.ServerSwitcherListener; import de.nexuslobby.modules.servers.ServerSwitcherListener;
import de.nexuslobby.modules.servers.ServerChecker; // Hinzugefügt
import de.nexuslobby.modules.armorstandtools.*; import de.nexuslobby.modules.armorstandtools.*;
import de.nexuslobby.modules.gadgets.GadgetModule; import de.nexuslobby.modules.gadgets.GadgetModule;
import de.nexuslobby.modules.hologram.HologramModule; import de.nexuslobby.modules.hologram.HologramModule;
@@ -29,18 +30,23 @@ import org.bukkit.Bukkit;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.command.PluginCommand;
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 org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class NexusLobby extends JavaPlugin implements Listener { public class NexusLobby extends JavaPlugin implements Listener {
@@ -57,16 +63,28 @@ public class NexusLobby extends JavaPlugin implements Listener {
private IntroModule introModule; private IntroModule introModule;
private BorderModule borderModule; private BorderModule borderModule;
private ConversationManager conversationManager;
private File visualsFile; private File visualsFile;
private FileConfiguration visualsConfig; private FileConfiguration visualsConfig;
private boolean updateAvailable = false; private boolean updateAvailable = false;
private String latestVersion = ""; private String latestVersion = "";
private final Set<UUID> silentPlayers = new HashSet<>();
public static NexusLobby getInstance() { public static NexusLobby getInstance() {
return instance; return instance;
} }
public Set<UUID> getSilentPlayers() {
return silentPlayers;
}
public ConversationManager getConversationManager() {
return conversationManager;
}
@Override @Override
public void onEnable() { public void onEnable() {
instance = this; instance = this;
@@ -76,12 +94,21 @@ public class NexusLobby extends JavaPlugin implements Listener {
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
moduleManager = new ModuleManager(this); moduleManager = new ModuleManager(this);
this.conversationManager = new ConversationManager(this);
ArmorStandGUI.init(); ArmorStandGUI.init();
registerModules(); registerModules();
moduleManager.enableAll(); moduleManager.enableAll();
registerListeners(); registerListeners();
// --- STATUS CHECKER START ---
ServerChecker.startGlobalChecker();
new ArmorStandLookAtModule();
startAutoConversationTimer();
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
new NexusLobbyExpansion().register(); new NexusLobbyExpansion().register();
getLogger().info("NexusLobby PAPI Expansion registriert."); getLogger().info("NexusLobby PAPI Expansion registriert.");
@@ -93,24 +120,51 @@ public class NexusLobby extends JavaPlugin implements Listener {
getLogger().info("NexusLobby v" + getDescription().getVersion() + " wurde erfolgreich gestartet."); getLogger().info("NexusLobby v" + getDescription().getVersion() + " wurde erfolgreich gestartet.");
} }
private void checkUpdates() { private void startAutoConversationTimer() {
new UpdateChecker(this).getVersion(version -> { new BukkitRunnable() {
if (!this.getDescription().getVersion().equalsIgnoreCase(version)) { @Override
this.updateAvailable = true; public void run() {
this.latestVersion = version; if (conversationManager == null) return;
getLogger().warning("====================================================");
getLogger().warning("Update gefunden! v" + getDescription().getVersion() + " -> v" + version); for (World world : Bukkit.getWorlds()) {
getLogger().warning("Autor: M_Viper"); for (ArmorStand as : world.getEntitiesByClass(ArmorStand.class)) {
getLogger().warning("===================================================="); if (as.getScoreboardTags().stream().anyMatch(tag -> tag.startsWith("conv_id:"))) {
} else { boolean playerNearby = false;
getLogger().info("NexusLobby ist aktuell (v" + version + ")."); for (Entity nearby : as.getNearbyEntities(30, 30, 30)) {
if (nearby instanceof Player) {
playerNearby = true;
break;
}
}
if (playerNearby) {
String dialogId = null;
String partnerUUID = null;
for (String tag : as.getScoreboardTags()) {
if (tag.startsWith("conv_id:")) dialogId = tag.split(":")[1];
if (tag.startsWith("conv_partner:")) partnerUUID = tag.split(":")[1];
}
if (dialogId != null && partnerUUID != null) {
try {
UUID partnerId = UUID.fromString(partnerUUID);
conversationManager.playConversation(as.getUniqueId(), partnerId, dialogId);
} catch (Exception ignored) {}
}
}
}
}
}
} }
}); }.runTaskTimer(this, 20L * 30, 20L * 90);
} }
public void reloadPlugin() { public void reloadPlugin() {
getLogger().info("Plugin Reload wird gestartet..."); getLogger().info("Plugin Reload wird gestartet...");
Bukkit.getScheduler().cancelTasks(this);
if (moduleManager != null) { if (moduleManager != null) {
moduleManager.disableAll(); moduleManager.disableAll();
} }
@@ -120,7 +174,10 @@ public class NexusLobby extends JavaPlugin implements Listener {
reloadVisualsConfig(); reloadVisualsConfig();
Config.load(); Config.load();
// Border-Settings nach Config-Reload synchronisieren if (conversationManager != null) {
conversationManager.setupFile();
}
if (borderModule != null) { if (borderModule != null) {
borderModule.reloadConfig(); borderModule.reloadConfig();
} }
@@ -128,15 +185,34 @@ public class NexusLobby extends JavaPlugin implements Listener {
if (portalManager != null) { if (portalManager != null) {
portalManager.loadPortals(); portalManager.loadPortals();
} }
ArmorStandGUI.init(); ArmorStandGUI.init();
if (moduleManager != null) { if (moduleManager != null) {
moduleManager.enableAll(); moduleManager.enableAll();
} }
ServerChecker.startGlobalChecker();
new ArmorStandLookAtModule();
startAutoConversationTimer();
getLogger().info("Plugin Reload abgeschlossen. Änderungen wurden übernommen."); getLogger().info("Plugin Reload abgeschlossen. Änderungen wurden übernommen.");
} }
private void checkUpdates() {
new UpdateChecker(this).getVersion(version -> {
if (!this.getDescription().getVersion().equalsIgnoreCase(version)) {
this.updateAvailable = true;
this.latestVersion = version;
getLogger().warning("====================================================");
getLogger().warning("Update gefunden! v" + getDescription().getVersion() + " -> v" + version);
getLogger().warning("====================================================");
} else {
getLogger().info("NexusLobby ist aktuell (v" + version + ").");
}
});
}
private void registerModules() { private void registerModules() {
moduleManager.registerModule(new ProtectionModule()); moduleManager.registerModule(new ProtectionModule());
moduleManager.registerModule(new ScoreboardModule()); moduleManager.registerModule(new ScoreboardModule());
@@ -153,6 +229,8 @@ public class NexusLobby extends JavaPlugin implements Listener {
this.dynamicArmorStandModule = new DynamicArmorStandModule(); this.dynamicArmorStandModule = new DynamicArmorStandModule();
moduleManager.registerModule(this.dynamicArmorStandModule); moduleManager.registerModule(this.dynamicArmorStandModule);
moduleManager.registerModule(new ArmorStandStatusModule());
this.mapArtModule = new MapArtModule(); this.mapArtModule = new MapArtModule();
moduleManager.registerModule(this.mapArtModule); moduleManager.registerModule(this.mapArtModule);
@@ -191,7 +269,6 @@ public class NexusLobby extends JavaPlugin implements Listener {
Player player = event.getPlayer(); Player player = event.getPlayer();
event.setJoinMessage(null); event.setJoinMessage(null);
// Teleport zum Lobby-Spawn beim Beitreten
teleportToSpawn(player); teleportToSpawn(player);
player.getInventory().clear(); player.getInventory().clear();
@@ -214,7 +291,7 @@ public class NexusLobby extends JavaPlugin implements Listener {
TextComponent link = new TextComponent("§8» §6Klicke §e§l[HIER] §6zum Herunterladen."); TextComponent link = new TextComponent("§8» §6Klicke §e§l[HIER] §6zum Herunterladen.");
link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://git.viper.ipv64.net/M_Viper/NexusLobby/releases")); link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://git.viper.ipv64.net/M_Viper/NexusLobby/releases"));
link.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, link.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
new ComponentBuilder("§7Öffnet die Gitea Release-Seite").create())); new ComponentBuilder("§7Öffnet die Release-Seite").create()));
player.spigot().sendMessage(link); player.spigot().sendMessage(link);
player.sendMessage(" "); player.sendMessage(" ");
@@ -241,37 +318,26 @@ public class NexusLobby extends JavaPlugin implements Listener {
} }
private void initCustomConfigs() { private void initCustomConfigs() {
if (!getDataFolder().exists()) { if (!getDataFolder().exists()) getDataFolder().mkdirs();
getDataFolder().mkdirs();
}
File configFile = new File(getDataFolder(), "config.yml"); File configFile = new File(getDataFolder(), "config.yml");
if (!configFile.exists()) { if (!configFile.exists()) saveResource("config.yml", false);
saveResource("config.yml", false);
}
reloadConfig(); reloadConfig();
File settingsFile = new File(getDataFolder(), "settings.yml"); File settingsFile = new File(getDataFolder(), "settings.yml");
if (!settingsFile.exists()) { if (!settingsFile.exists()) saveResource("settings.yml", false);
saveResource("settings.yml", false);
}
visualsFile = new File(getDataFolder(), "visuals.yml"); visualsFile = new File(getDataFolder(), "visuals.yml");
if (!visualsFile.exists()) { if (!visualsFile.exists()) saveResource("visuals.yml", false);
saveResource("visuals.yml", false);
}
reloadVisualsConfig(); reloadVisualsConfig();
Config.load(); Config.load();
} }
public void reloadVisualsConfig() { public void reloadVisualsConfig() {
if (visualsFile == null) { if (visualsFile == null) visualsFile = new File(getDataFolder(), "visuals.yml");
visualsFile = new File(getDataFolder(), "visuals.yml");
}
visualsConfig = YamlConfiguration.loadConfiguration(visualsFile); visualsConfig = YamlConfiguration.loadConfiguration(visualsFile);
getLogger().info("visuals.yml erfolgreich geladen.");
} }
public FileConfiguration getVisualsConfig() { public FileConfiguration getVisualsConfig() {
@@ -290,22 +356,19 @@ public class NexusLobby extends JavaPlugin implements Listener {
LobbyTabCompleter tabCompleter = new LobbyTabCompleter(portalManager, hologramModule); LobbyTabCompleter tabCompleter = new LobbyTabCompleter(portalManager, hologramModule);
NexusLobbyCommand nexusCommand = new NexusLobbyCommand(); NexusLobbyCommand nexusCommand = new NexusLobbyCommand();
PluginCommand portalCmd = this.getCommand("portal"); if (getCommand("portal") != null) {
if (portalCmd != null) { getCommand("portal").setExecutor(new PortalCommand(portalManager));
portalCmd.setExecutor(new PortalCommand(portalManager)); getCommand("portal").setTabCompleter(tabCompleter);
portalCmd.setTabCompleter(tabCompleter);
} }
PluginCommand holoCmd = this.getCommand("holo"); if (getCommand("holo") != null) {
if (holoCmd != null) { getCommand("holo").setExecutor(new HoloCommand(hologramModule));
holoCmd.setExecutor(new HoloCommand(hologramModule)); getCommand("holo").setTabCompleter(tabCompleter);
holoCmd.setTabCompleter(tabCompleter);
} }
PluginCommand maintenanceCmd = this.getCommand("maintenance"); if (getCommand("maintenance") != null) {
if (maintenanceCmd != null) { getCommand("maintenance").setExecutor(new MaintenanceCommand());
maintenanceCmd.setExecutor(new MaintenanceCommand()); getCommand("maintenance").setTabCompleter(tabCompleter);
maintenanceCmd.setTabCompleter(tabCompleter);
} }
if (getCommand("giveportalwand") != null) getCommand("giveportalwand").setExecutor(new GivePortalToolCommand(this)); if (getCommand("giveportalwand") != null) getCommand("giveportalwand").setExecutor(new GivePortalToolCommand(this));
@@ -322,32 +385,28 @@ public class NexusLobby extends JavaPlugin implements Listener {
getCommand("nexuscmd").setTabCompleter(tabCompleter); getCommand("nexuscmd").setTabCompleter(tabCompleter);
} }
// Haupt- und Spawn-Befehl Registrierung (NexusLobbyCommand verarbeitet beides) if (getCommand("nexuslobby") != null) {
PluginCommand nexusCmd = this.getCommand("nexuslobby"); getCommand("nexuslobby").setExecutor(nexusCommand);
if (nexusCmd != null) { getCommand("nexuslobby").setTabCompleter(tabCompleter);
nexusCmd.setExecutor(nexusCommand);
nexusCmd.setTabCompleter(tabCompleter);
} }
PluginCommand spawnCmd = this.getCommand("spawn"); if (getCommand("spawn") != null) {
if (spawnCmd != null) { getCommand("spawn").setExecutor(nexusCommand);
spawnCmd.setExecutor(nexusCommand); getCommand("spawn").setTabCompleter(tabCompleter);
spawnCmd.setTabCompleter(tabCompleter);
} }
if (getCommand("mapart") != null) getCommand("mapart").setTabCompleter(tabCompleter); if (getCommand("mapart") != null) getCommand("mapart").setTabCompleter(tabCompleter);
if (getCommand("intro") != null) getCommand("intro").setTabCompleter(tabCompleter); if (getCommand("intro") != null) getCommand("intro").setTabCompleter(tabCompleter);
PluginCommand borderCmd = this.getCommand("border"); if (getCommand("border") != null) {
if (borderCmd != null) { getCommand("border").setExecutor(new BorderCommand());
borderCmd.setExecutor(new BorderCommand()); getCommand("border").setTabCompleter(tabCompleter);
borderCmd.setTabCompleter(tabCompleter);
} }
} }
public class NexusLobbyExpansion extends PlaceholderExpansion { public class NexusLobbyExpansion extends PlaceholderExpansion {
@Override public @NotNull String getIdentifier() { return "nexuslobby"; } @Override public @NotNull String getIdentifier() { return "nexuslobby"; }
@Override public @NotNull String getAuthor() { return String.join(", ", NexusLobby.this.getDescription().getAuthors()); } @Override public @NotNull String getAuthor() { return "M_Viper"; }
@Override public @NotNull String getVersion() { return NexusLobby.this.getDescription().getVersion(); } @Override public @NotNull String getVersion() { return NexusLobby.this.getDescription().getVersion(); }
@Override public boolean persist() { return true; } @Override public boolean persist() { return true; }
@@ -357,15 +416,18 @@ public class NexusLobby extends JavaPlugin implements Listener {
if (params.equalsIgnoreCase("maintenance_status")) return MaintenanceListener.isMaintenance() ? "§cAktiv" : "§aDeaktiviert"; if (params.equalsIgnoreCase("maintenance_status")) return MaintenanceListener.isMaintenance() ? "§cAktiv" : "§aDeaktiviert";
if (params.equalsIgnoreCase("version")) return NexusLobby.this.getDescription().getVersion(); if (params.equalsIgnoreCase("version")) return NexusLobby.this.getDescription().getVersion();
if (params.equalsIgnoreCase("build_mode")) return BuildCommand.isInBuildMode(player) ? "§aAktiv" : "§cInaktiv"; if (params.equalsIgnoreCase("build_mode")) return BuildCommand.isInBuildMode(player) ? "§aAktiv" : "§cInaktiv";
if (params.equalsIgnoreCase("silent_join")) return silentPlayers.contains(player.getUniqueId()) ? "§aEin" : "§cAus";
return null; return null;
} }
} }
public ModuleManager getModuleManager() { return moduleManager; } public ModuleManager getModuleManager() { return moduleManager; }
public PortalManager getPortalManager() { return portalManager; } // Hinzugefügt/Wiederhergestellt
public TablistModule getTablistModule() { return tablistModule; } public TablistModule getTablistModule() { return tablistModule; }
public LobbySettingsModule getLobbySettingsModule() { return lobbySettingsModule; } public LobbySettingsModule getLobbySettingsModule() { return lobbySettingsModule; }
public ItemsModule getItemsModule() { return itemsModule; } public ItemsModule getItemsModule() { return itemsModule; }
public GadgetModule getGadgetModule() { return gadgetModule; } public GadgetModule getGadgetModule() { return gadgetModule; }
public HologramModule getHologramModule() { return hologramModule; } public HologramModule getHologramModule() { return hologramModule; }
public DynamicArmorStandModule getDynamicArmorStandModule() { return dynamicArmorStandModule; } public DynamicArmorStandModule getDynamicArmorStandModule() { return dynamicArmorStandModule; }
public MapArtModule getMapArtModule() { return mapArtModule; } // Wiederhergestellt
} }

View File

@@ -1,5 +1,6 @@
package de.nexuslobby.commands; package de.nexuslobby.commands;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.modules.portal.PortalManager; import de.nexuslobby.modules.portal.PortalManager;
import de.nexuslobby.modules.hologram.HologramModule; import de.nexuslobby.modules.hologram.HologramModule;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@@ -25,31 +26,31 @@ public class LobbyTabCompleter implements TabCompleter {
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
List<String> suggestions = new ArrayList<>(); List<String> suggestions = new ArrayList<>();
String cmdName = command.getName().toLowerCase();
// --- NexusLobby Hauptbefehl (/nexus) --- // --- NexusLobby Hauptbefehl (/nexus) ---
if (command.getName().equalsIgnoreCase("nexuslobby") || command.getName().equalsIgnoreCase("nexus")) { if (cmdName.equals("nexuslobby") || cmdName.equals("nexus")) {
if (args.length == 1) { if (args.length == 1) {
// Hier fügen wir 'setspawn' hinzu
if (sender.hasPermission("nexuslobby.admin")) { if (sender.hasPermission("nexuslobby.admin")) {
suggestions.add("reload"); suggestions.addAll(Arrays.asList("reload", "setspawn", "silentjoin"));
suggestions.add("setspawn");
} }
suggestions.add("sb"); suggestions.add("sb");
} else if (args.length == 2 && args[0].equalsIgnoreCase("sb")) { } else if (args.length == 2) {
suggestions.add("on"); if (args[0].equalsIgnoreCase("sb")) {
suggestions.add("off"); suggestions.addAll(Arrays.asList("on", "off"));
if (sender.hasPermission("nexuslobby.scoreboard.admin")) { if (sender.hasPermission("nexuslobby.scoreboard.admin")) {
suggestions.add("admin"); suggestions.addAll(Arrays.asList("admin", "spieler"));
suggestions.add("spieler"); }
} else if (args[0].equalsIgnoreCase("silentjoin")) {
suggestions.addAll(Arrays.asList("on", "off"));
} }
} }
} }
// --- Hologram Befehl --- // --- Hologram Befehl ---
else if (command.getName().equalsIgnoreCase("holo")) { else if (cmdName.equals("holo")) {
if (args.length == 1) { if (args.length == 1) {
suggestions.add("create"); suggestions.addAll(Arrays.asList("create", "delete"));
suggestions.add("delete");
} else if (args.length == 2 && args[0].equalsIgnoreCase("delete")) { } else if (args.length == 2 && args[0].equalsIgnoreCase("delete")) {
if (hologramModule != null) { if (hologramModule != null) {
suggestions.addAll(hologramModule.getHologramIds()); suggestions.addAll(hologramModule.getHologramIds());
@@ -58,19 +59,16 @@ public class LobbyTabCompleter implements TabCompleter {
} }
// --- Wartungsmodus --- // --- Wartungsmodus ---
else if (command.getName().equalsIgnoreCase("maintenance")) { else if (cmdName.equals("maintenance")) {
if (args.length == 1) { if (args.length == 1) {
suggestions.add("on"); suggestions.addAll(Arrays.asList("on", "off"));
suggestions.add("off");
} }
} }
// --- Portalsystem --- // --- Portalsystem ---
else if (command.getName().equalsIgnoreCase("portal")) { else if (cmdName.equals("portal")) {
if (args.length == 1) { if (args.length == 1) {
suggestions.add("create"); suggestions.addAll(Arrays.asList("create", "delete", "list"));
suggestions.add("delete");
suggestions.add("list");
} else if (args.length == 2 && args[0].equalsIgnoreCase("delete")) { } else if (args.length == 2 && args[0].equalsIgnoreCase("delete")) {
if (portalManager != null) { if (portalManager != null) {
suggestions.addAll(portalManager.getPortalNames()); suggestions.addAll(portalManager.getPortalNames());
@@ -79,7 +77,7 @@ public class LobbyTabCompleter implements TabCompleter {
} }
// --- MapArt --- // --- MapArt ---
else if (command.getName().equalsIgnoreCase("mapart")) { else if (cmdName.equals("mapart")) {
if (args.length == 1) { if (args.length == 1) {
suggestions.add("https://"); suggestions.add("https://");
} else if (args.length == 2) { } else if (args.length == 2) {
@@ -88,31 +86,73 @@ public class LobbyTabCompleter implements TabCompleter {
} }
// --- Intro System --- // --- Intro System ---
else if (command.getName().equalsIgnoreCase("intro")) { else if (cmdName.equals("intro")) {
if (args.length == 1) { if (args.length == 1) {
suggestions.addAll(Arrays.asList("add", "clear", "start")); suggestions.addAll(Arrays.asList("add", "clear", "start"));
} }
} }
// --- WorldBorder --- // --- WorldBorder ---
else if (command.getName().equalsIgnoreCase("border")) { else if (cmdName.equals("border")) {
if (args.length == 1) { if (args.length == 1) {
suggestions.add("circle"); suggestions.addAll(Arrays.asList("circle", "square", "disable"));
suggestions.add("square");
suggestions.add("disable");
} else if (args.length == 2 && args[0].equalsIgnoreCase("circle")) { } else if (args.length == 2 && args[0].equalsIgnoreCase("circle")) {
suggestions.addAll(Arrays.asList("10", "25", "50", "100", "250")); suggestions.addAll(Arrays.asList("10", "25", "50", "100", "250"));
} }
} }
// --- NexusCmd & Tools (Verkürzte Logik) --- // --- NexusCmd (ArmorStand Commands & Dialoge) ---
else if (command.getName().equalsIgnoreCase("nexuscmd") || command.getName().equalsIgnoreCase("ncmd")) { else if (cmdName.equals("nexuscmd") || cmdName.equals("ncmd") || cmdName.equals("ascmd")) {
if (args.length == 1) { if (args.length == 1) {
suggestions.addAll(Arrays.asList("name", "list", "add", "remove")); suggestions.addAll(Arrays.asList("add", "remove", "list", "name", "lookat", "conv"));
}
else if (args.length == 2) {
if (args[0].equalsIgnoreCase("add")) {
suggestions.addAll(Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"));
} else if (args[0].equalsIgnoreCase("name")) {
suggestions.addAll(Arrays.asList("<Anzeigename>", "none"));
} else if (args[0].equalsIgnoreCase("conv")) {
// NEU: unlink hinzugefügt
suggestions.addAll(Arrays.asList("select1", "select2", "link", "unlink", "start"));
} else if (args[0].equalsIgnoreCase("remove")) {
suggestions.add("all");
}
}
else if (args.length == 3) {
if (args[0].equalsIgnoreCase("add")) {
suggestions.addAll(Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"));
} else if (args[0].equalsIgnoreCase("conv")) {
if (args[1].equalsIgnoreCase("start") || args[1].equalsIgnoreCase("link")) {
if (NexusLobby.getInstance().getConversationManager() != null) {
suggestions.addAll(NexusLobby.getInstance().getConversationManager().getConversationIds());
}
}
}
}
else if (args.length == 4 && args[0].equalsIgnoreCase("add")) {
suggestions.addAll(Arrays.asList("bungee", "console", "player"));
}
else if (args.length == 5 && args[0].equalsIgnoreCase("add") && args[3].equalsIgnoreCase("bungee")) {
if (NexusLobby.getInstance().getConfig().getConfigurationSection("servers") != null) {
suggestions.addAll(NexusLobby.getInstance().getConfig().getConfigurationSection("servers").getKeys(false));
}
} }
} }
// Filtert die Vorschläge basierend auf dem, was der Spieler bereits getippt hat // --- ArmorStandTools (/astools) ---
else if (cmdName.equals("astools") || cmdName.equals("nt") || cmdName.equals("ntools")) {
if (args.length == 1) {
suggestions.addAll(Arrays.asList("dynamic", "lookat", "addplayer", "addconsole", "remove", "reload"));
}
else if (args.length == 2 && (args[0].equalsIgnoreCase("addplayer") || args[0].equalsIgnoreCase("addconsole"))) {
suggestions.addAll(Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"));
}
else if (args.length == 3 && (args[0].equalsIgnoreCase("addplayer") || args[0].equalsIgnoreCase("addconsole"))) {
suggestions.addAll(Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"));
}
}
// Filtert die Liste basierend auf der bisherigen Eingabe (für Case-Insensitivity und Teilübereinstimmung)
return suggestions.stream() return suggestions.stream()
.filter(s -> s.toLowerCase().startsWith(args[args.length - 1].toLowerCase())) .filter(s -> s.toLowerCase().startsWith(args[args.length - 1].toLowerCase()))
.collect(Collectors.toList()); .collect(Collectors.toList());

View File

@@ -18,15 +18,12 @@ public class NexusLobbyCommand implements CommandExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player)) { if (!(sender instanceof Player player)) {
sender.sendMessage("§cDieser Befehl ist nur für Spieler!"); sender.sendMessage("§cDieser Befehl ist nur für Spieler!");
return true; return true;
} }
Player player = (Player) sender;
// ========================================== // --- SPAWN BEFEHL ---
// LOGIK FÜR /spawn
// ==========================================
if (command.getName().equalsIgnoreCase("spawn")) { if (command.getName().equalsIgnoreCase("spawn")) {
FileConfiguration config = NexusLobby.getInstance().getConfig(); FileConfiguration config = NexusLobby.getInstance().getConfig();
if (config.contains("spawn.world")) { if (config.contains("spawn.world")) {
@@ -44,75 +41,94 @@ public class NexusLobbyCommand implements CommandExecutor {
return true; return true;
} }
// ========================================== // --- HAUPTBEFEHL ARGUMENTE ---
// LOGIK FÜR /nexus (Hauptbefehl) if (args.length == 0) {
// ========================================== sendInfo(player);
// Sub-Befehl: /nexus setspawn
if (args.length == 1 && args[0].equalsIgnoreCase("setspawn")) {
if (!player.hasPermission("nexuslobby.admin")) {
player.sendMessage("§cDu hast keine Berechtigung, den Spawn zu setzen.");
return true;
}
Location loc = player.getLocation();
FileConfiguration config = NexusLobby.getInstance().getConfig();
config.set("spawn.world", loc.getWorld().getName());
config.set("spawn.x", loc.getX());
config.set("spawn.y", loc.getY());
config.set("spawn.z", loc.getZ());
config.set("spawn.yaw", (double) loc.getYaw());
config.set("spawn.pitch", (double) loc.getPitch());
NexusLobby.getInstance().saveConfig();
player.sendMessage("§8[§6Nexus§8] §aLobby-Spawn erfolgreich gesetzt!");
player.sendMessage("§7X: §f" + String.format("%.2f", loc.getX()) + " §7Y: §f" + String.format("%.2f", loc.getY()) + " §7Z: §f" + String.format("%.2f", loc.getZ()));
return true; return true;
} }
// Sub-Befehl: /nexus sb <on|off|admin|spieler> switch (args[0].toLowerCase()) {
if (args.length >= 2 && args[0].equalsIgnoreCase("sb")) { case "reload":
ScoreboardModule sbModule = (ScoreboardModule) NexusLobby.getInstance().getModuleManager().getModule(ScoreboardModule.class); if (!player.hasPermission("nexuslobby.admin")) {
if (sbModule == null) { player.sendMessage("§cKeine Berechtigung.");
player.sendMessage("§cScoreboard-Modul ist deaktiviert."); return true;
return true; }
}
String sub = args[1].toLowerCase(); // Aufruf der Reload-Methode in der Hauptklasse
switch (sub) { NexusLobby.getInstance().reloadPlugin();
case "on": sbModule.setVisibility(player, true); break;
case "off": sbModule.setVisibility(player, false); break; player.sendMessage("§8[§6Nexus§8] §aPlugin erfolgreich neu geladen!");
case "admin": player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1f, 1.5f);
if (player.hasPermission("nexuslobby.scoreboard.admin")) sbModule.setAdminMode(player, true); break;
else player.sendMessage("§cKeine Rechte.");
break; case "setspawn":
case "spieler": if (!player.hasPermission("nexuslobby.admin")) {
if (player.hasPermission("nexuslobby.scoreboard.admin")) sbModule.setAdminMode(player, false); player.sendMessage("§cKeine Berechtigung.");
else player.sendMessage("§cKeine Rechte."); return true;
break; }
default: player.sendMessage("§cBenutzung: /nexus sb <on|off|admin|spieler>"); break; Location loc = player.getLocation();
} FileConfiguration config = NexusLobby.getInstance().getConfig();
return true; config.set("spawn.world", loc.getWorld().getName());
config.set("spawn.x", loc.getX());
config.set("spawn.y", loc.getY());
config.set("spawn.z", loc.getZ());
config.set("spawn.yaw", (double) loc.getYaw());
config.set("spawn.pitch", (double) loc.getPitch());
NexusLobby.getInstance().saveConfig();
player.sendMessage("§8[§6Nexus§8] §aLobby-Spawn erfolgreich gesetzt!");
break;
case "silentjoin":
if (!player.hasPermission("nexuslobby.silentjoin")) {
player.sendMessage("§cKeine Berechtigung.");
return true;
}
if (NexusLobby.getInstance().getSilentPlayers().contains(player.getUniqueId())) {
NexusLobby.getInstance().getSilentPlayers().remove(player.getUniqueId());
player.sendMessage("§8[§6Nexus§8] §7Silent Join: §cDeaktiviert");
} else {
NexusLobby.getInstance().getSilentPlayers().add(player.getUniqueId());
player.sendMessage("§8[§6Nexus§8] §7Silent Join: §aAktiviert");
}
break;
case "sb":
handleScoreboard(player, args);
break;
default:
sendInfo(player);
break;
} }
// Sub-Befehl: /nexus reload
if (args.length == 1 && args[0].equalsIgnoreCase("reload")) {
if (!player.hasPermission("nexuslobby.admin")) {
player.sendMessage("§cKeine Rechte.");
return true;
}
NexusLobby.getInstance().reloadPlugin();
player.sendMessage("§7[§6NexusLobby§7] §aPlugin erfolgreich neu geladen!");
return true;
}
// Info-Screen
sendInfo(player);
return true; return true;
} }
private void handleScoreboard(Player player, String[] args) {
if (args.length < 2) {
player.sendMessage("§cBenutzung: /nexus sb <on|off|admin|spieler>");
return;
}
ScoreboardModule sbModule = (ScoreboardModule) NexusLobby.getInstance().getModuleManager().getModule(ScoreboardModule.class);
if (sbModule == null) {
player.sendMessage("§cScoreboard-Modul ist deaktiviert.");
return;
}
String sub = args[1].toLowerCase();
switch (sub) {
case "on": sbModule.setVisibility(player, true); break;
case "off": sbModule.setVisibility(player, false); break;
case "admin":
if (player.hasPermission("nexuslobby.scoreboard.admin")) sbModule.setAdminMode(player, true);
else player.sendMessage("§cKeine Rechte.");
break;
case "spieler":
if (player.hasPermission("nexuslobby.scoreboard.admin")) sbModule.setAdminMode(player, false);
else player.sendMessage("§cKeine Rechte.");
break;
}
}
private Location getSpawnFromConfig(FileConfiguration config) { private Location getSpawnFromConfig(FileConfiguration config) {
World world = Bukkit.getWorld(config.getString("spawn.world", "world")); World world = Bukkit.getWorld(config.getString("spawn.world", "world"));
if (world == null) return null; if (world == null) return null;
@@ -122,14 +138,14 @@ public class NexusLobbyCommand implements CommandExecutor {
} }
private void sendInfo(Player player) { private void sendInfo(Player player) {
String version = NexusLobby.getInstance().getDescription().getVersion();
player.sendMessage("§8§m--------------------------------------"); player.sendMessage("§8§m--------------------------------------");
player.sendMessage("§6§lNexusLobby §7- Informationen"); player.sendMessage("§6§lNexusLobby §7- Informationen");
player.sendMessage(""); player.sendMessage("");
player.sendMessage("§f/spawn §7- Zum Spawn teleportieren"); player.sendMessage("§f/spawn §7- Zum Spawn teleportieren");
player.sendMessage("§f/nexus setspawn §7- Spawn setzen"); player.sendMessage("§f/nexus setspawn §7- Spawn setzen");
player.sendMessage("§f/nexus reload §7- Konfiguration laden"); player.sendMessage("§f/nexus silentjoin §7- Join-Nachricht umschalten");
player.sendMessage("§f/nexus sb <on|off> §7- Scoreboard"); player.sendMessage("§f/nexus sb <on|off> §7- Scoreboard");
player.sendMessage("§f/nexus reload §7- Konfiguration laden");
player.sendMessage("§8§m--------------------------------------"); player.sendMessage("§8§m--------------------------------------");
} }
} }

View File

@@ -1,5 +1,9 @@
package de.nexuslobby.modules.armorstandtools; package de.nexuslobby.modules.armorstandtools;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import de.nexuslobby.NexusLobby;
import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.ArmorStand; import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -14,40 +18,65 @@ import org.bukkit.util.EulerAngle;
public class ASTListener implements Listener { public class ASTListener implements Listener {
/**
* Erkennt den Rechtsklick auf den ArmorStand.
* Öffnet bei Sneak + Rechtsklick die Haupt-GUI.
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onInteract(PlayerInteractAtEntityEvent event) { public void onInteract(PlayerInteractAtEntityEvent event) {
// Sicherstellen, dass nur die Haupthand zählt (verhindert Doppel-Trigger)
if (event.getHand() != EquipmentSlot.HAND) return; if (event.getHand() != EquipmentSlot.HAND) return;
if (!(event.getRightClicked() instanceof ArmorStand as)) return; if (!(event.getRightClicked() instanceof ArmorStand as)) return;
Player p = event.getPlayer(); Player p = event.getPlayer();
// Prüfen ob Spieler schleicht (Shift) // --- 1. FALL: SNEAKING -> Editor öffnen ---
if (p.isSneaking()) { if (p.isSneaking()) {
if (p.hasPermission("nexuslobby.armorstand.use")) { if (p.hasPermission("nexuslobby.armorstand.use")) {
event.setCancelled(true); event.setCancelled(true);
// ArmorStand für diesen Spieler zwischenspeichern
AST.selectedArmorStand.put(p.getUniqueId(), as); AST.selectedArmorStand.put(p.getUniqueId(), as);
// Haupt-GUI öffnen
new ArmorStandGUI(as, p); new ArmorStandGUI(as, p);
} }
return;
}
// --- 2. FALL: NORMALER KLICK -> Befehle ausführen (Bungee, etc.) ---
for (String tag : as.getScoreboardTags()) {
if (tag.startsWith("ascmd:")) {
String[] parts = tag.split(":");
if (parts.length < 5) continue;
String type = parts[3].toLowerCase();
String command = parts[4];
event.setCancelled(true); // Verhindert z.B. dass man Items klaut
switch (type) {
case "bungee":
connectToServer(p, command);
break;
case "player":
p.performCommand(command);
break;
case "console":
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command.replace("%player%", p.getName()));
break;
}
break; // Nur den ersten gefundenen Befehl ausführen
}
} }
} }
/** /**
* Verwaltet alle Klicks innerhalb der verschiedenen GUIs. * Sendet den Spieler per Plugin-Message an BungeeCord
*/ */
private void connectToServer(Player player, String server) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("Connect");
out.writeUTF(server);
player.sendPluginMessage(NexusLobby.getInstance(), "BungeeCord", out.toByteArray());
player.sendMessage("§8[§6Nexus§8] §7Du wirst mit §e" + server + " §7verbunden...");
}
@EventHandler(priority = EventPriority.LOW) @EventHandler(priority = EventPriority.LOW)
public void onInventoryClick(InventoryClickEvent event) { public void onInventoryClick(InventoryClickEvent event) {
String title = event.getView().getTitle(); String title = event.getView().getTitle();
// Prüfen, ob es eine unserer GUIs ist
if (!title.equals("Nexus ArmorStand Editor") && if (!title.equals("Nexus ArmorStand Editor") &&
!title.equals("Pose: Körperteil wählen") && !title.equals("Pose: Körperteil wählen") &&
!title.startsWith("Achsen:")) return; !title.startsWith("Achsen:")) return;
@@ -64,19 +93,15 @@ public class ASTListener implements Listener {
ItemStack item = event.getCurrentItem(); ItemStack item = event.getCurrentItem();
if (item == null || item.getType() == Material.AIR) return; if (item == null || item.getType() == Material.AIR) return;
// --- 1. LOGIK: HAUPTMENÜ ---
if (title.equals("Nexus ArmorStand Editor")) { if (title.equals("Nexus ArmorStand Editor")) {
ArmorStandTool tool = ArmorStandTool.get(item); ArmorStandTool tool = ArmorStandTool.get(item);
if (tool != null) { if (tool != null) {
tool.execute(as, p); tool.execute(as, p);
// GUI erneuern (außer sie wurde durch execute geschlossen)
if (p.getOpenInventory().getTitle().equals(title)) { if (p.getOpenInventory().getTitle().equals(title)) {
new ArmorStandGUI(as, p); new ArmorStandGUI(as, p);
} }
} }
} }
// --- 2. LOGIK: KÖRPERTEIL-AUSWAHL ---
else if (title.equals("Pose: Körperteil wählen")) { else if (title.equals("Pose: Körperteil wählen")) {
if (item.getType() == Material.BARRIER) { if (item.getType() == Material.BARRIER) {
new ArmorStandGUI(as, p); new ArmorStandGUI(as, p);
@@ -95,8 +120,6 @@ public class ASTListener implements Listener {
ArmorStandPoseGUI.openAxisDetailMenu(p, as, targetPart); ArmorStandPoseGUI.openAxisDetailMenu(p, as, targetPart);
} }
} }
// --- 3. LOGIK: ACHSEN-FEINJUSTIERUNG ---
else if (title.startsWith("Achsen:")) { else if (title.startsWith("Achsen:")) {
if (item.getType() == Material.ARROW) { if (item.getType() == Material.ARROW) {
ArmorStandPoseGUI.openPartSelectionMenu(p, as); ArmorStandPoseGUI.openPartSelectionMenu(p, as);
@@ -106,7 +129,6 @@ public class ASTListener implements Listener {
String partName = title.split(": ")[1]; String partName = title.split(": ")[1];
ArmorStandTool tool = ArmorStandTool.valueOf(partName); ArmorStandTool tool = ArmorStandTool.valueOf(partName);
// Berechnung des Winkels (Links/Rechts & Shift)
double change = event.isShiftClick() ? Math.toRadians(1) : Math.toRadians(15); double change = event.isShiftClick() ? Math.toRadians(1) : Math.toRadians(15);
if (event.isRightClick()) change *= -1; if (event.isRightClick()) change *= -1;
@@ -122,7 +144,6 @@ public class ASTListener implements Listener {
} }
ArmorStandPoseGUI.setAngleForPart(as, tool, newPose); ArmorStandPoseGUI.setAngleForPart(as, tool, newPose);
// GUI aktualisieren um Werte in der Lore anzuzeigen
ArmorStandPoseGUI.openAxisDetailMenu(p, as, partName); ArmorStandPoseGUI.openAxisDetailMenu(p, as, partName);
} }
} }

View File

@@ -1,17 +1,26 @@
package de.nexuslobby.modules.armorstandtools; package de.nexuslobby.modules.armorstandtools;
import de.nexuslobby.NexusLobby;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Particle;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.ArmorStand; import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.util.EulerAngle;
import org.bukkit.util.RayTraceResult;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.UUID;
import java.util.Set;
/**
* ArmorStandCmdExecutor - Erweiterte Steuerung für ArmorStand-Interaktionen.
* Nutzt Raytracing für präzise Auswahl und permanentes Dialog-Linking sowie Status-Backup.
*/
public class ArmorStandCmdExecutor implements CommandExecutor { public class ArmorStandCmdExecutor implements CommandExecutor {
private final String prefix = "§8[§6Nexus§8] "; private final String prefix = "§8[§6Nexus§8] ";
@@ -25,74 +34,175 @@ public class ArmorStandCmdExecutor implements CommandExecutor {
return true; return true;
} }
// 1. Priorität: Name setzen (verwendet den in der Map gespeicherten AS aus der GUI) if (args.length == 0) return sendHelp(p);
if (args.length >= 2 && args[0].equalsIgnoreCase("name")) {
ArmorStand target = AST.selectedArmorStand.get(p.getUniqueId()); // --- CONVERSATION BEFEHLE ---
if (target == null || !target.isValid()) { if (args[0].equalsIgnoreCase("conv")) {
p.sendMessage(prefix + "§cBitte wähle zuerst einen ArmorStand im GUI (Sneak-Rechtsklick) aus!"); if (args.length < 2) {
return true; return sendConvHelp(p);
} }
switch (args[1].toLowerCase()) {
case "select1":
case "select2":
ArmorStand target = getTargetArmorStand(p);
if (target == null) {
p.sendMessage(prefix + "§cDu musst einen ArmorStand direkt anschauen (Fadenkreuz)!");
return true;
}
boolean isFirst = args[1].equalsIgnoreCase("select1");
String metaKey = isFirst ? "conv_npc1" : "conv_npc2";
UUID targetUUID = target.getUniqueId();
p.setMetadata(metaKey, new FixedMetadataValue(NexusLobby.getInstance(), targetUUID.toString()));
p.sendMessage(prefix + "§aNPC §e" + (isFirst ? "1" : "2") + " §amarkiert (§7" + targetUUID.toString().substring(0, 8) + "...§a)");
p.spawnParticle(Particle.WITCH, target.getLocation().add(0, 1.0, 0), 15, 0.2, 0.2, 0.2, 0.05);
break;
case "link":
if (args.length < 3) {
p.sendMessage(prefix + "§cNutze: /nexuscmd conv link <Dialog-ID>");
return true;
}
if (!p.hasMetadata("conv_npc1") || !p.hasMetadata("conv_npc2")) {
p.sendMessage(prefix + "§cBitte markiere erst beide NPCs (select1 & select2)!");
return true;
}
UUID id1 = UUID.fromString(p.getMetadata("conv_npc1").get(0).asString());
UUID id2 = UUID.fromString(p.getMetadata("conv_npc2").get(0).asString());
String dialogId = args[2];
Entity entity1 = Bukkit.getEntity(id1);
if (entity1 instanceof ArmorStand as1) {
as1.getScoreboardTags().removeIf(tag -> tag.startsWith("conv_partner:") || tag.startsWith("conv_id:"));
as1.addScoreboardTag("conv_partner:" + id2.toString());
as1.addScoreboardTag("conv_id:" + dialogId);
NexusLobby.getInstance().getConversationManager().saveLink(id1, id2, dialogId);
p.sendMessage(prefix + "§a§lDauerhafte Verknüpfung erstellt!");
p.spawnParticle(Particle.HAPPY_VILLAGER, as1.getLocation().add(0, 1.5, 0), 20, 0.4, 0.4, 0.4, 0.1);
} else {
p.sendMessage(prefix + "§cFehler: Sprecher 1 nicht gefunden.");
}
break;
case "unlink":
ArmorStand targetUnlink = getTargetArmorStand(p);
if (targetUnlink == null) {
p.sendMessage(prefix + "§cSchau den NPC an, dessen Verknüpfung du lösen willst!");
return true;
}
// Ingame Tags entfernen
targetUnlink.getScoreboardTags().removeIf(tag -> tag.startsWith("conv_partner:") || tag.startsWith("conv_id:"));
// Aus Konfiguration löschen
NexusLobby.getInstance().getConversationManager().removeLink(targetUnlink.getUniqueId());
p.sendMessage(prefix + "§eNPC-Verknüpfung wurde aufgehoben.");
p.spawnParticle(Particle.SMOKE, targetUnlink.getLocation().add(0, 1.0, 0), 20, 0.2, 0.2, 0.2, 0.02);
break;
case "start":
if (args.length < 3) {
p.sendMessage(prefix + "§cNutze: /nexuscmd conv start <ID>");
return true;
}
if (!p.hasMetadata("conv_npc1") || !p.hasMetadata("conv_npc2")) {
p.sendMessage(prefix + "§cBitte markiere erst beide NPCs!");
return true;
}
UUID s1 = UUID.fromString(p.getMetadata("conv_npc1").get(0).asString());
UUID s2 = UUID.fromString(p.getMetadata("conv_npc2").get(0).asString());
NexusLobby.getInstance().getConversationManager().playConversation(s1, s2, args[2]);
break;
default:
return sendConvHelp(p);
}
return true;
}
// --- STANDARD TOOLS (LOOKAT / NAME / ADD) ---
ArmorStand target = getTargetArmorStand(p);
if (args[0].equalsIgnoreCase("lookat")) {
if (target == null) { p.sendMessage(prefix + "§cSchau einen ArmorStand an!"); return true; }
if (target.getScoreboardTags().contains("as_lookat")) {
target.removeScoreboardTag("as_lookat");
target.setHeadPose(new EulerAngle(0, 0, 0));
p.sendMessage(prefix + "§cBlickkontakt aus.");
} else {
target.addScoreboardTag("as_lookat");
p.sendMessage(prefix + "§aBlickkontakt an.");
}
return true;
}
if (args[0].equalsIgnoreCase("name") && args.length >= 2) {
if (target == null) { p.sendMessage(prefix + "§cSchau einen ArmorStand an!"); return true; }
String nameInput = buildString(args, 1); String nameInput = buildString(args, 1);
if (nameInput.equalsIgnoreCase("none")) { if (nameInput.equalsIgnoreCase("none")) {
target.setCustomName(""); target.setCustomName("");
target.setCustomNameVisible(false); target.setCustomNameVisible(false);
target.getScoreboardTags().removeIf(tag -> tag.startsWith("asname:"));
p.sendMessage(prefix + "§eName entfernt."); p.sendMessage(prefix + "§eName entfernt.");
} else { } else {
String colored = ChatColor.translateAlternateColorCodes('&', nameInput); String colored = ChatColor.translateAlternateColorCodes('&', nameInput);
target.setCustomName(colored); target.setCustomName(colored);
target.setCustomNameVisible(true); target.setCustomNameVisible(true);
target.getScoreboardTags().removeIf(tag -> tag.startsWith("asname:"));
target.addScoreboardTag("asname:" + nameInput);
p.sendMessage(prefix + "§7Name gesetzt: " + colored); p.sendMessage(prefix + "§7Name gesetzt: " + colored);
p.sendMessage(prefix + "§8(Status-Backup wurde gespeichert)");
} }
return true; return true;
} }
// 2. Priorität: Action-Commands (verwendet Nearby-Suche für /nexuscmd add...) if (args[0].equalsIgnoreCase("add") && args.length >= 5) {
ArmorStand target = getNearbyArmorStand(p); if (target == null) { p.sendMessage(prefix + "§cSchau einen ArmorStand an!"); return true; }
if (target == null) { String slot1 = args[1], slot2 = args[2], type = args[3].toLowerCase();
p.sendMessage(prefix + "§cKein ArmorStand in der Nähe (4 Blöcke) gefunden!"); String cmdStr = buildString(args, 4);
target.addScoreboardTag("ascmd:" + slot1 + ":" + slot2 + ":" + type + ":" + cmdStr);
p.sendMessage(prefix + "§aBefehl gebunden.");
return true; return true;
} }
if (args.length >= 5 && args[0].equalsIgnoreCase("add")) { if (args[0].equalsIgnoreCase("list")) {
String type = args[3].toLowerCase(); if (target == null) { p.sendMessage(prefix + "§cKein Ziel!"); return true; }
String cmd = buildString(args, 4); p.sendMessage("§6§lBefehle & Tags:");
target.getScoreboardTags().forEach(t -> p.sendMessage(" §8» §e" + t));
if (!type.equals("player") && !type.equals("console") && !type.equals("bungee")) {
p.sendMessage(prefix + "§cTypen: §eplayer, console, bungee");
return true;
}
target.addScoreboardTag("ascmd:" + type + ":" + cmd);
p.sendMessage(prefix + "§aBefehl an ArmorStand gebunden!");
return true; return true;
} }
if (args.length >= 1 && args[0].equalsIgnoreCase("list")) { if (args[0].equalsIgnoreCase("remove")) {
p.sendMessage("§6§lBefehle auf diesem ArmorStand:"); if (target == null) { p.sendMessage(prefix + "§cKein Ziel!"); return true; }
for (String tag : target.getScoreboardTags()) { target.getScoreboardTags().removeIf(tag -> tag.startsWith("ascmd:"));
if (tag.startsWith("ascmd:")) { p.sendMessage(prefix + "§eAlle Befehle gelöscht.");
p.sendMessage(" §8» §e" + tag.replace("ascmd:", ""));
}
}
return true;
}
if (args.length >= 1 && args[0].equalsIgnoreCase("remove")) {
Set<String> tags = target.getScoreboardTags();
for (String tag : new ArrayList<>(tags)) {
if (tag.startsWith("ascmd:")) target.removeScoreboardTag(tag);
}
p.sendMessage(prefix + "§eAlle Befehle entfernt.");
return true; return true;
} }
return sendHelp(p); return sendHelp(p);
} }
private ArmorStand getNearbyArmorStand(Player p) { private ArmorStand getTargetArmorStand(Player p) {
for (Entity e : p.getNearbyEntities(4, 4, 4)) { RayTraceResult result = p.getWorld().rayTraceEntities(
if (e instanceof ArmorStand as) return as; p.getEyeLocation(),
p.getLocation().getDirection(),
8,
0.3,
entity -> entity instanceof ArmorStand
);
if (result != null && result.getHitEntity() instanceof ArmorStand as) {
return as;
} }
return null; return null;
} }
@@ -105,12 +215,26 @@ public class ArmorStandCmdExecutor implements CommandExecutor {
return sb.toString(); return sb.toString();
} }
private boolean sendConvHelp(Player p) {
p.sendMessage(" ");
p.sendMessage("§6§lConversation Setup:");
p.sendMessage("§e/nexuscmd conv select1 §7- Sprecher 1");
p.sendMessage("§e/nexuscmd conv select2 §7- Sprecher 2");
p.sendMessage("§e/nexuscmd conv link <ID> §7- Speichern");
p.sendMessage("§e/nexuscmd conv unlink §7- Verknüpfung lösen");
p.sendMessage("§e/nexuscmd conv start <ID> §7- Testen");
p.sendMessage(" ");
return true;
}
private boolean sendHelp(Player p) { private boolean sendHelp(Player p) {
p.sendMessage("§6§lNexus Command-Binder Hilfe:"); p.sendMessage("§6§lNexus Tools Hilfe:");
p.sendMessage("§e/nexuscmd name <Text> §7- Namen setzen (AS vorher anklicken)"); p.sendMessage("§e/nexuscmd name <Text> §7- Setzt Namen & Status-Backup");
p.sendMessage("§e/nexuscmd add 0 0 <type> <cmd> §7- Befehl binden"); p.sendMessage("§e/nexuscmd lookat §7- Blickkontakt umschalten");
p.sendMessage("§e/nexuscmd list §7- Befehle anzeigen"); p.sendMessage("§e/nexuscmd add <s1> <s2> bungee <Server> §7- Bungee-Bindung");
p.sendMessage("§e/nexuscmd remove §7- Befehle löschen"); p.sendMessage("§e/nexuscmd conv §7- Gesprächs-Menü");
p.sendMessage("§e/nexuscmd list §7- Zeigt alle Tags");
p.sendMessage("§e/nexuscmd remove §7- Löscht Befehle");
return true; return true;
} }
} }

View File

@@ -9,16 +9,39 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.ArmorStand; import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.EulerAngle;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Set; import java.util.UUID;
/** /**
* ArmorStandCommand - Vollständige Steuerung für ArmorStands. * ArmorStandCommand - Vollständige Steuerung für ArmorStands.
* Inklusive Dynamic-Modus Erkennung und visueller Rückmeldung. * Inklusive Dynamic-Modus, Look-At Funktion, Befehls-Slots und Conversation-Sprecher.
*/ */
public class ArmorStandCommand implements CommandExecutor { public class ArmorStandCommand implements CommandExecutor {
// Statische Variablen für die aktuell markierten Sprecher
private static UUID speaker1;
private static UUID speaker2;
// Getter-Methoden für die NexusLobby Hauptklasse
public static UUID getSpeaker1() {
return speaker1;
}
public static UUID getSpeaker2() {
return speaker2;
}
// Setter-Methoden (werden vom ASTListener oder der GUI aufgerufen)
public static void setSpeaker1(UUID id) {
speaker1 = id;
}
public static void setSpeaker2(UUID id) {
speaker2 = id;
}
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player p)) { if (!(sender instanceof Player p)) {
@@ -72,36 +95,56 @@ public class ArmorStandCommand implements CommandExecutor {
} }
if (NexusLobby.getInstance().getDynamicArmorStandModule() != null) { if (NexusLobby.getInstance().getDynamicArmorStandModule() != null) {
// Toggle Logik
if (target.getScoreboardTags().contains("as_dynamic")) { if (target.getScoreboardTags().contains("as_dynamic")) {
target.removeScoreboardTag("as_dynamic"); target.removeScoreboardTag("as_dynamic");
p.sendMessage(prefix + "§c§l[-] §7Dynamic-Modus §cdeaktiviert§7."); p.sendMessage(prefix + "§c§l[-] §7Dynamic-Modus §cdeaktiviert§7.");
p.spawnParticle(Particle.SMOKE, target.getLocation().add(0, 1, 0), 15, 0.3, 0.3, 0.3, 0.05); p.spawnParticle(Particle.SMOKE, target.getLocation().add(0, 1, 0), 15, 0.3, 0.3, 0.3, 0.05);
} else { } else {
target.addScoreboardTag("as_dynamic"); target.addScoreboardTag("as_dynamic");
p.sendMessage(prefix + "§a§l[+] §7Dynamic-Modus §aaktiviert§7 (Wetter/Zeit)."); p.sendMessage(prefix + "§a§l[+] §7Dynamic-Modus §aaktiviert§7.");
// Visueller Erfolgseffekt (Grüne Sternchen)
p.spawnParticle(Particle.HAPPY_VILLAGER, target.getLocation().add(0, 1, 0), 25, 0.5, 0.5, 0.5, 0.1); p.spawnParticle(Particle.HAPPY_VILLAGER, target.getLocation().add(0, 1, 0), 25, 0.5, 0.5, 0.5, 0.1);
} }
// Internes Update sofort triggern
NexusLobby.getInstance().getDynamicArmorStandModule().toggleDynamicStatus(target); NexusLobby.getInstance().getDynamicArmorStandModule().toggleDynamicStatus(target);
} else { } else {
p.sendMessage(prefix + "§cFehler: Dynamic-Modul ist nicht aktiv!"); p.sendMessage(prefix + "§cFehler: Dynamic-Modul ist nicht aktiv!");
} }
break; break;
case "lookat":
if (!p.hasPermission("nexuslobby.armorstand.lookat")) {
p.sendMessage(prefix + ChatColor.RED + "Keine Rechte für Look-At!");
return true;
}
if (target.getScoreboardTags().contains("as_lookat")) {
target.removeScoreboardTag("as_lookat");
// Kopf-Pose zurücksetzen für sauberen Übergang
target.setHeadPose(new EulerAngle(0, 0, 0));
p.sendMessage(prefix + "§c§l[-] §7Blickkontakt §cdeaktiviert§7.");
p.spawnParticle(Particle.SMOKE, target.getLocation().add(0, 1, 0), 10, 0.2, 0.2, 0.2, 0.02);
} else {
target.addScoreboardTag("as_lookat");
p.sendMessage(prefix + "§a§l[+] §7Blickkontakt §aaktiviert§7.");
p.spawnParticle(Particle.HAPPY_VILLAGER, target.getLocation().add(0, 1, 0), 15, 0.4, 0.4, 0.4, 0.05);
}
break;
case "addplayer": case "addplayer":
if (args.length < 2) return sendHelp(p, prefix); if (args.length < 4) return sendHelp(p, prefix);
String pCmd = buildString(args, 1); String s1P = args[1];
target.addScoreboardTag("ascmd:player:" + pCmd); String s2P = args[2];
p.sendMessage(prefix + "§aBefehl (Player) gespeichert: §e/" + pCmd); String pCmd = buildString(args, 3);
target.addScoreboardTag("ascmd:" + s1P + ":" + s2P + ":player:" + pCmd);
p.sendMessage(prefix + "§aBefehl (Player) auf Slot " + s1P + "/" + s2P + " gespeichert.");
break; break;
case "addconsole": case "addconsole":
if (args.length < 2) return sendHelp(p, prefix); if (args.length < 4) return sendHelp(p, prefix);
String cCmd = buildString(args, 1); String s1C = args[1];
target.addScoreboardTag("ascmd:console:" + cCmd); String s2C = args[2];
p.sendMessage(prefix + "§aBefehl (Konsole) gespeichert: §e" + cCmd); String cCmd = buildString(args, 3);
target.addScoreboardTag("ascmd:" + s1C + ":" + s2C + ":console:" + cCmd);
p.sendMessage(prefix + "§aBefehl (Konsole) auf Slot " + s1C + "/" + s2C + " gespeichert.");
break; break;
case "remove": case "remove":
@@ -117,18 +160,12 @@ public class ArmorStandCommand implements CommandExecutor {
return true; return true;
} }
/**
* Sucht den am besten passenden ArmorStand.
* Priorität 1: Der ArmorStand, den der Spieler direkt ansieht.
* Priorität 2: Der absolut nächste ArmorStand im Umkreis.
*/
private ArmorStand getBestTargetArmorStand(Player p) { private ArmorStand getBestTargetArmorStand(Player p) {
ArmorStand best = null; ArmorStand best = null;
double bestDot = 0.95; // Schwellenwert für das "Anschauen" double bestDot = 0.95;
for (Entity e : p.getNearbyEntities(8, 8, 8)) { for (Entity e : p.getNearbyEntities(8, 8, 8)) {
if (e instanceof ArmorStand as) { if (e instanceof ArmorStand as) {
// Berechne, ob der Spieler in Richtung des ArmorStands schaut
double dot = p.getLocation().getDirection().dot( double dot = p.getLocation().getDirection().dot(
as.getLocation().toVector().subtract(p.getLocation().toVector()).normalize() as.getLocation().toVector().subtract(p.getLocation().toVector()).normalize()
); );
@@ -140,7 +177,6 @@ public class ArmorStandCommand implements CommandExecutor {
} }
} }
// Falls nichts aktiv angeschaut wird, nimm einfach den nächsten im 4-Block-Radius
if (best == null) { if (best == null) {
for (Entity e : p.getNearbyEntities(4, 4, 4)) { for (Entity e : p.getNearbyEntities(4, 4, 4)) {
if (e instanceof ArmorStand as) { if (e instanceof ArmorStand as) {
@@ -163,12 +199,12 @@ public class ArmorStandCommand implements CommandExecutor {
private boolean sendHelp(Player p, String prefix) { private boolean sendHelp(Player p, String prefix) {
p.sendMessage(" "); p.sendMessage(" ");
p.sendMessage("§8§m-----------§r " + prefix + "§8§m-----------"); p.sendMessage("§8§m-----------§r " + prefix + "§8§m-----------");
p.sendMessage("§e/astools §7- Editor öffnen"); p.sendMessage("§e/astools §7- Editor GUI öffnen");
p.sendMessage("§b/astools dynamic §7- Wetter/Zeit Logik umschalten"); p.sendMessage("§b/astools dynamic §7- Näherung/Zeit Logik");
p.sendMessage("§e/astools addplayer <cmd> §7- Befehl als Spieler"); p.sendMessage("§b/astools lookat §7- Blickkontakt umschalten");
p.sendMessage("§e/astools addconsole <cmd> §7- Befehl via Konsole"); p.sendMessage("§e/astools addplayer <S1> <S2> <cmd> §7- Befehl (Spieler)");
p.sendMessage("§e/astools addconsole <S1> <S2> <cmd> §7- Befehl (Konsole)");
p.sendMessage("§e/astools remove §7- Befehle löschen"); p.sendMessage("§e/astools remove §7- Befehle löschen");
p.sendMessage("§e/astools reload §7- Konfig neu laden");
p.sendMessage("§8§m---------------------------------------"); p.sendMessage("§8§m---------------------------------------");
return true; return true;
} }

View File

@@ -0,0 +1,113 @@
package de.nexuslobby.modules.armorstandtools;
import de.nexuslobby.NexusLobby;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.EulerAngle;
import org.bukkit.util.Vector;
public class ArmorStandLookAtModule {
public ArmorStandLookAtModule() {
startRotationTask();
}
private void startRotationTask() {
new BukkitRunnable() {
@Override
public void run() {
// Wir gehen alle Welten durch, um markierte ArmorStands zu finden
for (World world : Bukkit.getWorlds()) {
for (ArmorStand as : world.getEntitiesByClass(ArmorStand.class)) {
// Nur ArmorStands mit dem Tag "as_lookat"
if (as.getScoreboardTags().contains("as_lookat")) {
// Suche den nächsten Spieler im Umkreis von 7 Blöcken
Player nearest = getNearestPlayer(as, 7.0);
if (nearest != null) {
// Spieler gefunden -> Kopf zum Spieler drehen
updateLookAt(as, nearest);
} else {
// Kein Spieler da -> Kopf sanft auf 0,0,0 zurücksetzen
resetHeadPose(as);
}
}
}
}
}
}.runTaskTimer(NexusLobby.getInstance(), 0L, 1L);
}
private void updateLookAt(ArmorStand as, Player player) {
Location asLoc = as.getEyeLocation();
Location target = player.getEyeLocation();
Vector direction = target.toVector().subtract(asLoc.toVector()).normalize();
// Ziel-Winkel aus Vektor berechnen
double targetYaw = Math.toDegrees(Math.atan2(-direction.getX(), direction.getZ()));
double targetPitch = Math.toDegrees(Math.asin(direction.getY()));
// Relativer Yaw zum Körper-Yaw
double relativeYaw = targetYaw - as.getLocation().getYaw();
relativeYaw = ((relativeYaw + 180) % 360 + 360) % 360 - 180;
double yawRad = Math.toRadians(relativeYaw);
double pitchRad = Math.toRadians(-targetPitch);
// Begrenzung (Nacken-Schutz)
if (yawRad > 1.2) yawRad = 1.2;
if (yawRad < -1.2) yawRad = -1.2;
if (pitchRad > 0.8) pitchRad = 0.8;
if (pitchRad < -0.8) pitchRad = -0.8;
applySmoothPose(as, pitchRad, yawRad);
}
private void resetHeadPose(ArmorStand as) {
EulerAngle current = as.getHeadPose();
// Wenn der Kopf schon (fast) gerade ist, nichts tun
if (Math.abs(current.getX()) < 0.01 && Math.abs(current.getY()) < 0.01) {
if (current.getX() != 0 || current.getY() != 0) {
as.setHeadPose(new EulerAngle(0, 0, 0));
}
return;
}
// Ziel: 0, 0, 0 (Geradeaus schauen)
applySmoothPose(as, 0, 0);
}
private void applySmoothPose(ArmorStand as, double pitchRad, double yawRad) {
EulerAngle current = as.getHeadPose();
double lerp = 0.15; // Geschmeidigkeit
double finalPitch = current.getX() + (pitchRad - current.getX()) * lerp;
double finalYaw = current.getY() + (yawRad - current.getY()) * lerp;
as.setHeadPose(new EulerAngle(finalPitch, finalYaw, 0));
}
private Player getNearestPlayer(ArmorStand as, double range) {
Player nearest = null;
double dSquared = range * range;
for (Player p : as.getWorld().getPlayers()) {
// Falls Spieler im Vanish/Spectator ist, ignorieren (optional)
if (p.getGameMode().name().equals("SPECTATOR")) continue;
double dist = p.getLocation().distanceSquared(as.getLocation());
if (dist < dSquared) {
dSquared = dist;
nearest = p;
}
}
return nearest;
}
}

View File

@@ -0,0 +1,80 @@
package de.nexuslobby.modules.armorstandtools;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import de.nexuslobby.modules.servers.ServerChecker;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.World;
public class ArmorStandStatusModule implements Module {
@Override
public String getName() {
return "ArmorStandStatus";
}
@Override
public void onEnable() {
// Alle 10 Sekunden prüfen
Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), this::updateAllServerArmorStands, 100L, 200L);
}
@Override
public void onDisable() {}
private void updateAllServerArmorStands() {
for (World world : Bukkit.getWorlds()) {
for (Entity entity : world.getEntitiesByClass(ArmorStand.class)) {
if (entity instanceof ArmorStand as) {
checkAndRefreshStatus(as);
}
}
}
}
private void checkAndRefreshStatus(ArmorStand as) {
String bungeeTag = null;
for (String tag : as.getScoreboardTags()) {
if (tag.startsWith("ascmd:bungee:")) {
bungeeTag = tag.replace("ascmd:bungee:", "");
break;
}
}
if (bungeeTag == null) return;
String serverName = bungeeTag.toLowerCase();
String ip = NexusLobby.getInstance().getConfig().getString("servers." + serverName + ".ip", "127.0.0.1");
int port = NexusLobby.getInstance().getConfig().getInt("servers." + serverName + ".port", 25565);
ServerChecker.isOnline(ip, port).thenAccept(isOnline -> {
Bukkit.getScheduler().runTask(NexusLobby.getInstance(), () -> {
String originalDisplayName = getOriginalName(as);
if (originalDisplayName == null) return;
String translatedName = ChatColor.translateAlternateColorCodes('&', originalDisplayName);
if (isOnline) {
// Zeigt nur den normalen Namen an, wenn online
as.setCustomName(translatedName);
} else {
// Zeigt den Namen an und darunter (getrennt durch Leerzeichen/Format) den Status
// Da Minecraft Namen meist einzeilig sind, nutzen wir eine klare farbliche Trennung
as.setCustomName(translatedName + " §7- §cOffline");
}
});
});
}
private String getOriginalName(ArmorStand as) {
for (String tag : as.getScoreboardTags()) {
if (tag.startsWith("as_displayname:")) {
return tag.replace("as_displayname:", "").replace("§§", ":");
}
}
return null;
}
}

View File

@@ -6,11 +6,12 @@ import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import de.nexuslobby.NexusLobby;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public enum ArmorStandTool { public enum ArmorStandTool {
// --- HAUPTMENÜ ATTRIBUTE ---
INVIS(Material.GLASS_PANE, 10), INVIS(Material.GLASS_PANE, 10),
ARMS(Material.STICK, 11), ARMS(Material.STICK, 11),
BASE(Material.STONE_SLAB, 12), BASE(Material.STONE_SLAB, 12),
@@ -19,10 +20,11 @@ public enum ArmorStandTool {
INVUL(Material.BEDROCK, 15), INVUL(Material.BEDROCK, 15),
SET_NAME(Material.NAME_TAG, 16), SET_NAME(Material.NAME_TAG, 16),
// Zentrales Item zum Öffnen des Pose-Editors // Neuer Button für Gespräche
CONV_SETUP(Material.WRITABLE_BOOK, 17),
OPEN_POSE_EDITOR(Material.ARMOR_STAND, 31), OPEN_POSE_EDITOR(Material.ARMOR_STAND, 31),
// --- DIESE TEILE WANDERN IN DAS UNTERMENÜ (isForGui = false) ---
HEAD_ROT(Material.PLAYER_HEAD, -1), HEAD_ROT(Material.PLAYER_HEAD, -1),
BODY_ROT(Material.IRON_CHESTPLATE, -1), BODY_ROT(Material.IRON_CHESTPLATE, -1),
L_ARM_ROT(Material.STICK, -1), L_ARM_ROT(Material.STICK, -1),
@@ -50,16 +52,28 @@ public enum ArmorStandTool {
case INVUL -> as.setInvulnerable(!as.isInvulnerable()); case INVUL -> as.setInvulnerable(!as.isInvulnerable());
case SET_NAME -> { case SET_NAME -> {
p.closeInventory(); p.closeInventory();
p.sendMessage("§6§lHologramm-Editor: §7Nutze §e/nexuscmd name <Text>"); p.sendMessage("§8[§6Nexus§8] §7Nutze §e/nexuscmd name <Text>");
AST.selectedArmorStand.put(p.getUniqueId(), as); AST.selectedArmorStand.put(p.getUniqueId(), as);
} }
case CONV_SETUP -> {
// Automatisches Markieren via GUI
if (!p.hasMetadata("conv_npc1")) {
p.setMetadata("conv_npc1", new FixedMetadataValue(NexusLobby.getInstance(), as.getUniqueId().toString()));
p.sendMessage("§8[§6Nexus§8] §aNPC als Sprecher 1 markiert.");
} else {
p.setMetadata("conv_npc2", new FixedMetadataValue(NexusLobby.getInstance(), as.getUniqueId().toString()));
p.sendMessage("§8[§6Nexus§8] §aNPC als Sprecher 2 markiert.");
p.sendMessage("§8[§6Nexus§8] §7Nutze nun §e/nexuscmd conv start <ID>");
}
p.closeInventory();
}
case REMOVE -> { case REMOVE -> {
as.remove(); as.remove();
p.closeInventory(); p.closeInventory();
p.sendMessage("§cNexus ArmorStand entfernt."); p.sendMessage("§cNexus ArmorStand entfernt.");
} }
case OPEN_POSE_EDITOR -> ArmorStandPoseGUI.openPartSelectionMenu(p, as); case OPEN_POSE_EDITOR -> ArmorStandPoseGUI.openPartSelectionMenu(p, as);
default -> {} // Pose-Detailklicks werden direkt im PoseGUI/Listener behandelt default -> {}
} }
} }
@@ -70,8 +84,12 @@ public enum ArmorStandTool {
ItemStack item = new ItemStack(material); ItemStack item = new ItemStack(material);
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
if (meta != null) { if (meta != null) {
String name = (this == OPEN_POSE_EDITOR) ? "§a§lPose Editor öffnen" : "§6" + this.name().replace("_", " "); String displayName = switch (this) {
meta.setDisplayName(name); case OPEN_POSE_EDITOR -> "§a§lPose Editor öffnen";
case CONV_SETUP -> "§d§lGesprächs-Setup";
default -> "§6" + this.name().replace("_", " ");
};
meta.setDisplayName(displayName);
List<String> lore = new ArrayList<>(); List<String> lore = new ArrayList<>();
lore.add("§7Klicken zum Verwalten"); lore.add("§7Klicken zum Verwalten");
meta.setLore(lore); meta.setLore(lore);
@@ -83,8 +101,8 @@ public enum ArmorStandTool {
public static ArmorStandTool get(ItemStack item) { public static ArmorStandTool get(ItemStack item) {
if (item == null || !item.hasItemMeta() || !item.getItemMeta().hasDisplayName()) return null; if (item == null || !item.hasItemMeta() || !item.getItemMeta().hasDisplayName()) return null;
String name = ChatColor.stripColor(item.getItemMeta().getDisplayName()).replace(" ", "_"); String name = ChatColor.stripColor(item.getItemMeta().getDisplayName()).replace(" ", "_");
// Spezielles Mapping für den Pose-Editor-Button im Listener
if (name.equals("Pose_Editor_öffnen")) return OPEN_POSE_EDITOR; if (name.equals("Pose_Editor_öffnen")) return OPEN_POSE_EDITOR;
if (name.equals("Gesprächs-Setup")) return CONV_SETUP;
try { return valueOf(name); } catch (Exception e) { return null; } try { return valueOf(name); } catch (Exception e) { return null; }
} }
} }

View File

@@ -0,0 +1,166 @@
package de.nexuslobby.modules.armorstandtools;
import de.nexuslobby.NexusLobby;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class ConversationManager {
private final NexusLobby plugin;
private File file;
private FileConfiguration config;
public ConversationManager(NexusLobby plugin) {
this.plugin = plugin;
setupFile();
}
public void setupFile() {
this.file = new File(plugin.getDataFolder(), "conversations.yml");
if (!file.exists()) {
try {
if (!plugin.getDataFolder().exists()) {
plugin.getDataFolder().mkdirs();
}
plugin.saveResource("conversations.yml", false);
} catch (Exception e) {
try {
file.createNewFile();
YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(file);
defaultConfig.set("conversations.test.dialogue", Arrays.asList(
"&eNPC 1: &7Hallo!",
"&aNPC 2: &7Hi, wie geht es dir?",
"&eNPC 1: &7Bestens, danke!"
));
defaultConfig.save(file);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
this.config = YamlConfiguration.loadConfiguration(file);
}
public void saveLink(UUID id1, UUID id2, String dialogId) {
config.set("links." + id1.toString() + ".partner", id2.toString());
config.set("links." + id1.toString() + ".dialog", dialogId); // WICHTIG: ".dialog"
saveConfig();
}
public void removeLink(UUID id) {
if (config.contains("links." + id.toString())) {
config.set("links." + id.toString(), null);
saveConfig();
}
}
public void startAllAutomatedConversations() {
if (config.getConfigurationSection("links") == null) return;
for (String npc1String : config.getConfigurationSection("links").getKeys(false)) {
try {
UUID id1 = UUID.fromString(npc1String);
UUID id2 = UUID.fromString(config.getString("links." + npc1String + ".partner"));
String dialogId = config.getString("links." + npc1String + ".dialog"); // WICHTIG: ".dialog"
Entity e1 = Bukkit.getEntity(id1);
Entity e2 = Bukkit.getEntity(id2);
if (e1 instanceof ArmorStand as1 && e2 instanceof ArmorStand as2) {
if (as1.getNearbyEntities(15, 15, 15).stream().anyMatch(e -> e instanceof Player)) {
playConversation(id1, id2, dialogId);
}
}
} catch (Exception ignored) {}
}
}
public List<String> getConversationIds() {
if (config == null || !config.contains("conversations")) {
return new ArrayList<>();
}
return new ArrayList<>(config.getConfigurationSection("conversations").getKeys(false));
}
public void playConversation(UUID id1, UUID id2, String key) {
// Sicherstellen, dass wir auf "conversations.KEY.dialogue" prüfen
if (config == null || !config.contains("conversations." + key)) {
Bukkit.getLogger().warning("[NexusLobby] Dialog-ID '" + key + "' nicht in conversations.yml gefunden!");
return;
}
List<String> lines = config.getStringList("conversations." + key + ".dialogue");
if (lines == null || lines.isEmpty()) return;
new BukkitRunnable() {
int step = 0;
@Override
public void run() {
if (step >= lines.size()) {
this.cancel();
return;
}
UUID speakerUUID = (step % 2 == 0) ? id1 : id2;
Entity entity = Bukkit.getEntity(speakerUUID);
if (entity instanceof ArmorStand as) {
as.getWorld().playSound(as.getLocation(), Sound.ENTITY_CHICKEN_EGG, 0.5f, 1.5f);
showBubble(as, lines.get(step));
} else {
this.cancel();
return;
}
step++;
}
}.runTaskTimer(plugin, 0L, 70L);
}
private void showBubble(ArmorStand as, String text) {
Location loc = as.getEyeLocation().add(0, 0.6, 0);
ArmorStand bubble = as.getWorld().spawn(loc, ArmorStand.class, s -> {
s.setMarker(true);
s.setVisible(false);
s.setGravity(false);
s.setCustomName(ChatColorTranslate(text));
s.setCustomNameVisible(true);
s.setInvulnerable(true);
});
Bukkit.getScheduler().runTaskLater(plugin, bubble::remove, 60L);
}
private String ChatColorTranslate(String text) {
return text.replace("&", "§");
}
private void saveConfig() {
try {
config.save(file);
} catch (IOException e) {
e.printStackTrace();
}
}
public void reload() {
this.config = YamlConfiguration.loadConfiguration(file);
}
}

View File

@@ -0,0 +1,119 @@
package de.nexuslobby.modules.servers;
import de.nexuslobby.NexusLobby;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.entity.ArmorStand;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.CompletableFuture;
public class ServerChecker {
/**
* Prüft asynchron, ob ein Server unter der angegebenen IP und Port erreichbar ist.
*/
public static CompletableFuture<Boolean> isOnline(String ip, int port) {
return CompletableFuture.supplyAsync(() -> {
try (Socket socket = new Socket()) {
// 500ms Timeout für stabilere Erkennung
socket.connect(new InetSocketAddress(ip, port), 500);
return true;
} catch (IOException e) {
return false;
}
});
}
/**
* Startet den Scheduler, der alle ArmorStands in allen Welten regelmäßig prüft.
*/
public static void startGlobalChecker() {
// WICHTIG: runTaskTimer (synchron), um den Main-Thread für getEntitiesByClass zu nutzen
Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), () -> {
for (World world : Bukkit.getWorlds()) {
// Das Abrufen der Entities muss auf dem Main-Thread passieren
for (ArmorStand as : world.getEntitiesByClass(ArmorStand.class)) {
checkAndUpdateArmorStand(as);
}
}
}, 100L, 200L);
}
/**
* Analysiert die Tags eines ArmorStands und aktualisiert den Namen basierend auf dem Serverstatus.
*/
private static void checkAndUpdateArmorStand(ArmorStand as) {
for (String tag : as.getScoreboardTags()) {
// Erwartetes Format: ascmd:slot1:slot2:type:command
if (tag.startsWith("ascmd:")) {
String[] parts = tag.split(":");
if (parts.length < 5) continue;
String type = parts[3].toLowerCase();
if (!type.equals("bungee")) continue;
String serverName = parts[4];
// Suche den passenden Key in der Config
String configKey = null;
if (NexusLobby.getInstance().getConfig().getConfigurationSection("servers") != null) {
for (String key : NexusLobby.getInstance().getConfig().getConfigurationSection("servers").getKeys(false)) {
if (key.equalsIgnoreCase(serverName)) {
configKey = key;
break;
}
}
}
if (configKey == null) continue;
String ip = NexusLobby.getInstance().getConfig().getString("servers." + configKey + ".ip");
int port = NexusLobby.getInstance().getConfig().getInt("servers." + configKey + ".port");
if (ip == null) continue;
// Der Ping selbst läuft asynchron weg vom Main-Thread
isOnline(ip, port).thenAccept(online -> {
// Zurück zum Main-Thread für die Änderung des ArmorStands
Bukkit.getScheduler().runTask(NexusLobby.getInstance(), () -> {
if (!as.isValid()) return; // Sicherheitscheck falls AS gelöscht wurde
if (online) {
restoreName(as);
} else {
String offlineText = "§c● §lOFFLINE §c●";
if (!offlineText.equals(as.getCustomName())) {
as.setCustomName(offlineText);
as.setCustomNameVisible(true);
}
}
});
});
break;
}
}
}
/**
* Stellt den ursprünglichen Namen des ArmorStands wieder her.
* Nutzt den Tag "asname:NAME" als Backup.
*/
private static void restoreName(ArmorStand as) {
for (String t : as.getScoreboardTags()) {
if (t.startsWith("asname:")) {
String originalName = t.substring(7);
String translatedName = ChatColor.translateAlternateColorCodes('&', originalName);
if (!translatedName.equals(as.getCustomName())) {
as.setCustomName(translatedName);
as.setCustomNameVisible(true);
}
return;
}
}
}
}

View File

@@ -27,6 +27,16 @@ lobby:
default-gamemode: Adventure default-gamemode: Adventure
clear-inventory-on-join: true clear-inventory-on-join: true
# Mapping für den Server-Status-Ping der ArmorStands
# Der Name (z.B. survival) muss exakt dem Bungee-Servernamen entsprechen
servers:
survival:
ip: "127.0.0.1"
port: 25566
skyblock:
ip: "127.0.0.1"
port: 25567
# --- Tablist Einstellungen --- # --- Tablist Einstellungen ---
tablist: tablist:
enabled: true enabled: true
@@ -54,7 +64,7 @@ items:
portals: portals:
default-particle: "PORTAL" default-particle: "PORTAL"
portal-cooldown: 40 # Ticks, 2 Sekunden portal-cooldown: 40 # Ticks, 2 Sekunden
save-file: "portals.yml" # Datei im Plugin-Ordner save-file: "portals.yml"
# ----------------------------------------------------- # -----------------------------------------------------
# COMPASS MENU # COMPASS MENU

View File

@@ -1,6 +1,6 @@
name: NexusLobby name: NexusLobby
main: de.nexuslobby.NexusLobby main: de.nexuslobby.NexusLobby
version: "1.0.4" version: "1.0.5"
api-version: "1.21" api-version: "1.21"
author: M_Viper author: M_Viper
description: Modular Lobby Plugin description: Modular Lobby Plugin
@@ -40,13 +40,16 @@ commands:
nexuslobby: nexuslobby:
description: Zeigt Informationen über das Plugin an oder lädt es neu description: Zeigt Informationen über das Plugin an oder lädt es neu
usage: /nexuslobby [reload|setspawn] usage: /nexuslobby [reload|setspawn]
aliases: [nexus] aliases: [nexus, lobby]
nexustools: nexustools:
description: Nexus ArmorStand Editor description: Nexus ArmorStand Editor (LookAt, Dynamic, etc.)
aliases: [nt, ntools, astools] aliases: [nt, ntools, astools]
nexuscmd: nexuscmd:
description: Nexus Command Binder description: Nexus Command Binder (Slots 0-9) und Gesprächs-System
aliases: [ncmd, ascmd] usage: /nexuscmd <args>
permission: nexuslobby.armorstand.cmd
permission-message: "§cDu hast keine Rechte!"
aliases: [ncmd, ascmd, conv]
holo: holo:
description: Verwalte Lobby Hologramme (Text-Displays) description: Verwalte Lobby Hologramme (Text-Displays)
usage: /holo <create|delete> <id> [text] usage: /holo <create|delete> <id> [text]
@@ -66,6 +69,7 @@ commands:
spawn: spawn:
description: Teleportiert dich zum Lobby-Spawnpunkt description: Teleportiert dich zum Lobby-Spawnpunkt
usage: /spawn usage: /spawn
aliases: [l, hub]
permissions: permissions:
nexuslobby.portal: nexuslobby.portal:
@@ -90,11 +94,14 @@ permissions:
description: Erlaubt die Nutzung der NexusTools GUI description: Erlaubt die Nutzung der NexusTools GUI
default: op default: op
nexuslobby.armorstand.cmd: nexuslobby.armorstand.cmd:
description: Erlaubt das Binden von Commands via NexusCmd description: Erlaubt das Binden von Commands und das Verwalten von Conversations
default: op default: op
nexuslobby.armorstand.dynamic: nexuslobby.armorstand.dynamic:
description: Erlaubt das Markieren von dynamischen NPCs description: Erlaubt das Markieren von dynamischen NPCs
default: op default: op
nexuslobby.armorstand.lookat:
description: Erlaubt das Aktivieren des Blickkontakts bei NPCs
default: op
nexuslobby.hologram: nexuslobby.hologram:
description: Erlaubt das Erstellen von Text-Display Hologrammen description: Erlaubt das Erstellen von Text-Display Hologrammen
default: op default: op