11 Commits
1.0.2 ... 1.0.5

Author SHA1 Message Date
4c847401a0 Update from Git Manager GUI 2026-01-23 22:08:30 +01:00
aac2482511 Upload pom.xml via GUI 2026-01-23 21:08:30 +00:00
8ea8ab50f3 Upload pom.xml via GUI 2026-01-23 17:09:05 +00:00
addab7245d Update from Git Manager GUI 2026-01-23 18:09:04 +01:00
fa6c79aa53 README.md aktualisiert 2026-01-23 11:40:28 +00:00
27b9563a45 Update from Git Manager GUI 2026-01-23 12:17:49 +01:00
96973d44e5 Upload pom.xml via GUI 2026-01-23 11:17:49 +00:00
6630621ba1 Upload pom.xml via GUI 2026-01-23 09:43:54 +00:00
5f2c05d85e Update from Git Manager GUI 2026-01-23 10:43:53 +01:00
9fd0690ba8 Update from Git Manager GUI 2026-01-22 23:16:25 +01:00
233639dd52 Upload pom.xml via GUI 2026-01-22 22:16:25 +00:00
33 changed files with 3055 additions and 498 deletions

View File

@@ -18,11 +18,11 @@ Ein umfassendes Lobby-Plugin fur Minecraft Server (Paper/Spigot 1.21+) mit modul
Dieses Plugin ist urheberrechtlich geschutzt. Es gelten folgende Bedingungen: Dieses Plugin ist urheberrechtlich geschutzt. Es gelten folgende Bedingungen:
- Die Nutzung ist ausschliesslich fur den personlichen Gebrauch gestattet - Die Nutzung ist ausschliesslich fur den persönlichen Gebrauch gestattet
- Die Weitergabe, Verbreitung oder Veroffentlichung 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 Anderung, 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 ausdruckliche 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 Verstoss gegen diese Bedingungen behalten wir uns rechtliche Schritte vor.
@@ -45,27 +45,27 @@ Bei Verstoss gegen diese Bedingungen behalten wir uns rechtliche Schritte vor.
### Sicherheit ### Sicherheit
- **VPN-Blocker** - Blockiert VPN/Proxy-Verbindungen (proxycheck.io API) - **VPN-Blocker** - Blockiert VPN/Proxy-Verbindungen (proxycheck.io API)
- **Country-Blocker** - Erlaubt nur bestimmte Lander (Whitelist/Blacklist) - **Country-Blocker** - Erlaubt nur bestimmte Lander (Whitelist/Blacklist)
- **Wartungsmodus** - Sperrt den Server fur nicht-berechtigte Spieler - **Wartungsmodus** - Sperrt den Server für nicht-berechtigte Spieler
- **Lobby-Schutz** - Verhindert Griefing und unerwunschte Aktionen - **Lobby-Schutz** - Verhindert Griefing und unerwünschte Aktionen
### Zusatzliche Module ### Zusatzliche Module
- **Portal-System** - Erstelle Portale fur Server-Wechsel (BungeeCord) - **Portal-System** - Erstelle Portale fur Server-Wechsel (BungeeCord)
- **ArmorStand-Tools** - Bearbeite ArmorStands mit GUI und Command-Binding - **ArmorStand-Tools** - Bearbeite ArmorStands mit GUI und Command-Binding
- **Server-Switcher** - GUI-basierter Server-Wechsel - **Server-Switcher** - GUI-basierter Server-Wechsel
- **Spieler verstecken** - Toggle fur Spieler-Sichtbarkeit - **Spieler verstecken** - Toggle fur Spieler-Sichtbarkeit
- **Chat-Suppressor** - Unterdruckung fur globalen Chat - **Chat-Suppressor** - Unterdrückung fur globalen Chat
--- ---
## Installation ## Installation
### Voraussetzungen ### Voraussetzungen
- Paper/Spigot Server 1.21 oder hoher - Paper/Spigot Server 1.21 oder höher
- Java 21 oder hoher - Java 21 oder hoher
### Optionale Abhangigkeiten ### Optionale Abhangigkeiten
- [PlaceholderAPI](https://www.spigotmc.org/resources/placeholderapi.6245/) - Fur Platzhalter-Support - [PlaceholderAPI](https://www.spigotmc.org/resources/placeholderapi.6245/) - Für Platzhalter-Support
- [LuckPerms](https://luckperms.net/) - Fur Berechtigungsverwaltung - [LuckPerms](https://luckperms.net/) - Für Berechtigungsverwaltung
### Schritte ### Schritte
1. Lade `NexusLobby.jar` herunter 1. Lade `NexusLobby.jar` herunter
@@ -195,6 +195,6 @@ Das Plugin registriert eigene Platzhalter unter der Expansion `nexuslobby`:
--- ---
**Copyright (c) 2024 - Alle Rechte vorbehalten** **Copyright (c) 2025 - Alle Rechte vorbehalten**
Die unbefugte Vervielfaltigung, 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.1</version> <version>1.0.4</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>NexusLobby</name> <name>NexusLobby</name>

View File

@@ -14,12 +14,12 @@ 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.armorstandtools.*; import de.nexuslobby.modules.armorstandtools.*;
import de.nexuslobby.utils.VoidProtection; import de.nexuslobby.modules.gadgets.GadgetModule;
import de.nexuslobby.utils.DoubleJump; import de.nexuslobby.modules.hologram.HologramModule;
import de.nexuslobby.utils.PlayerHider; import de.nexuslobby.modules.mapart.MapArtModule;
import de.nexuslobby.utils.MaintenanceListener; import de.nexuslobby.modules.intro.IntroModule;
import de.nexuslobby.utils.ConfigUpdater; import de.nexuslobby.modules.border.BorderModule;
import de.nexuslobby.utils.UpdateChecker; import de.nexuslobby.utils.*;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.ComponentBuilder;
@@ -27,6 +27,8 @@ import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.PluginCommand; 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;
@@ -47,6 +49,13 @@ public class NexusLobby extends JavaPlugin implements Listener {
private PortalManager portalManager; private PortalManager portalManager;
private TablistModule tablistModule; private TablistModule tablistModule;
private LobbySettingsModule lobbySettingsModule; private LobbySettingsModule lobbySettingsModule;
private ItemsModule itemsModule;
private GadgetModule gadgetModule;
private HologramModule hologramModule;
private DynamicArmorStandModule dynamicArmorStandModule;
private MapArtModule mapArtModule;
private IntroModule introModule;
private BorderModule borderModule;
private File visualsFile; private File visualsFile;
private FileConfiguration visualsConfig; private FileConfiguration visualsConfig;
@@ -81,7 +90,7 @@ public class NexusLobby extends JavaPlugin implements Listener {
registerCommands(); registerCommands();
checkUpdates(); checkUpdates();
getLogger().info("NexusLobby wurde erfolgreich gestartet."); getLogger().info("NexusLobby v" + getDescription().getVersion() + " wurde erfolgreich gestartet.");
} }
private void checkUpdates() { private void checkUpdates() {
@@ -111,6 +120,11 @@ public class NexusLobby extends JavaPlugin implements Listener {
reloadVisualsConfig(); reloadVisualsConfig();
Config.load(); Config.load();
// Border-Settings nach Config-Reload synchronisieren
if (borderModule != null) {
borderModule.reloadConfig();
}
if (portalManager != null) { if (portalManager != null) {
portalManager.loadPortals(); portalManager.loadPortals();
} }
@@ -126,18 +140,39 @@ public class NexusLobby extends JavaPlugin implements Listener {
private void registerModules() { private void registerModules() {
moduleManager.registerModule(new ProtectionModule()); moduleManager.registerModule(new ProtectionModule());
moduleManager.registerModule(new ScoreboardModule()); moduleManager.registerModule(new ScoreboardModule());
moduleManager.registerModule(new ItemsModule());
this.itemsModule = new ItemsModule();
moduleManager.registerModule(this.itemsModule);
this.gadgetModule = new GadgetModule();
moduleManager.registerModule(this.gadgetModule);
this.hologramModule = new HologramModule();
moduleManager.registerModule(this.hologramModule);
this.dynamicArmorStandModule = new DynamicArmorStandModule();
moduleManager.registerModule(this.dynamicArmorStandModule);
this.mapArtModule = new MapArtModule();
moduleManager.registerModule(this.mapArtModule);
this.introModule = new IntroModule();
moduleManager.registerModule(this.introModule);
this.borderModule = new BorderModule();
moduleManager.registerModule(this.borderModule);
moduleManager.registerModule(new SecurityModule()); moduleManager.registerModule(new SecurityModule());
moduleManager.registerModule(new BossBarModule()); moduleManager.registerModule(new BossBarModule());
moduleManager.registerModule(new ActionBarModule()); moduleManager.registerModule(new ActionBarModule());
lobbySettingsModule = new LobbySettingsModule(); this.lobbySettingsModule = new LobbySettingsModule();
moduleManager.registerModule(lobbySettingsModule); moduleManager.registerModule(lobbySettingsModule);
tablistModule = new TablistModule(); this.tablistModule = new TablistModule();
moduleManager.registerModule(tablistModule); moduleManager.registerModule(tablistModule);
portalManager = new PortalManager(this); this.portalManager = new PortalManager(this);
moduleManager.registerModule(portalManager); moduleManager.registerModule(portalManager);
} }
@@ -151,15 +186,16 @@ public class NexusLobby extends JavaPlugin implements Listener {
getServer().getPluginManager().registerEvents(new ASTListener(), this); getServer().getPluginManager().registerEvents(new ASTListener(), this);
} }
// Priorität auf LOWEST, damit das Inventar VOR den Modulen geleert wird
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
public void onJoin(PlayerJoinEvent event) { public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
event.setJoinMessage(null); event.setJoinMessage(null);
// FEHLERBEHEBUNG: Inventar leeren, um doppelte Items zu vermeiden // Teleport zum Lobby-Spawn beim Beitreten
teleportToSpawn(player);
player.getInventory().clear(); player.getInventory().clear();
player.getInventory().setArmorContents(null); // Auch Rüstung entfernen player.getInventory().setArmorContents(null);
BuildCommand.removePlayerFromBuildMode(player); BuildCommand.removePlayerFromBuildMode(player);
@@ -185,6 +221,25 @@ public class NexusLobby extends JavaPlugin implements Listener {
} }
} }
private void teleportToSpawn(Player player) {
FileConfiguration config = getConfig();
if (config.contains("spawn.world")) {
String worldName = config.getString("spawn.world");
World world = Bukkit.getWorld(worldName);
if (world != null) {
Location spawnLoc = new Location(
world,
config.getDouble("spawn.x"),
config.getDouble("spawn.y"),
config.getDouble("spawn.z"),
(float) config.getDouble("spawn.yaw"),
(float) config.getDouble("spawn.pitch")
);
player.teleport(spawnLoc);
}
}
}
private void initCustomConfigs() { private void initCustomConfigs() {
if (!getDataFolder().exists()) { if (!getDataFolder().exists()) {
getDataFolder().mkdirs(); getDataFolder().mkdirs();
@@ -194,7 +249,7 @@ public class NexusLobby extends JavaPlugin implements Listener {
if (!configFile.exists()) { if (!configFile.exists()) {
saveResource("config.yml", false); saveResource("config.yml", false);
} }
ConfigUpdater.updateConfig("config.yml");
reloadConfig(); reloadConfig();
File settingsFile = new File(getDataFolder(), "settings.yml"); File settingsFile = new File(getDataFolder(), "settings.yml");
@@ -216,13 +271,11 @@ public class NexusLobby extends JavaPlugin implements Listener {
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 vom Speicher geladen."); getLogger().info("visuals.yml erfolgreich geladen.");
} }
public FileConfiguration getVisualsConfig() { public FileConfiguration getVisualsConfig() {
if (visualsConfig == null) { if (visualsConfig == null) reloadVisualsConfig();
reloadVisualsConfig();
}
return visualsConfig; return visualsConfig;
} }
@@ -230,11 +283,12 @@ public class NexusLobby extends JavaPlugin implements Listener {
public void onDisable() { public void onDisable() {
getServer().getMessenger().unregisterOutgoingPluginChannel(this, "BungeeCord"); getServer().getMessenger().unregisterOutgoingPluginChannel(this, "BungeeCord");
if (moduleManager != null) moduleManager.disableAll(); if (moduleManager != null) moduleManager.disableAll();
getLogger().info("NexusLobby disabled"); getLogger().info("NexusLobby deaktiviert.");
} }
private void registerCommands() { private void registerCommands() {
LobbyTabCompleter tabCompleter = new LobbyTabCompleter(portalManager); LobbyTabCompleter tabCompleter = new LobbyTabCompleter(portalManager, hologramModule);
NexusLobbyCommand nexusCommand = new NexusLobbyCommand();
PluginCommand portalCmd = this.getCommand("portal"); PluginCommand portalCmd = this.getCommand("portal");
if (portalCmd != null) { if (portalCmd != null) {
@@ -242,6 +296,12 @@ public class NexusLobby extends JavaPlugin implements Listener {
portalCmd.setTabCompleter(tabCompleter); portalCmd.setTabCompleter(tabCompleter);
} }
PluginCommand holoCmd = this.getCommand("holo");
if (holoCmd != null) {
holoCmd.setExecutor(new HoloCommand(hologramModule));
holoCmd.setTabCompleter(tabCompleter);
}
PluginCommand maintenanceCmd = this.getCommand("maintenance"); PluginCommand maintenanceCmd = this.getCommand("maintenance");
if (maintenanceCmd != null) { if (maintenanceCmd != null) {
maintenanceCmd.setExecutor(new MaintenanceCommand()); maintenanceCmd.setExecutor(new MaintenanceCommand());
@@ -262,11 +322,27 @@ public class NexusLobby extends JavaPlugin implements Listener {
getCommand("nexuscmd").setTabCompleter(tabCompleter); getCommand("nexuscmd").setTabCompleter(tabCompleter);
} }
// Haupt- und Spawn-Befehl Registrierung (NexusLobbyCommand verarbeitet beides)
PluginCommand nexusCmd = this.getCommand("nexuslobby"); PluginCommand nexusCmd = this.getCommand("nexuslobby");
if (nexusCmd != null) { if (nexusCmd != null) {
nexusCmd.setExecutor(new NexusLobbyCommand()); nexusCmd.setExecutor(nexusCommand);
nexusCmd.setTabCompleter(tabCompleter); nexusCmd.setTabCompleter(tabCompleter);
} }
PluginCommand spawnCmd = this.getCommand("spawn");
if (spawnCmd != null) {
spawnCmd.setExecutor(nexusCommand);
spawnCmd.setTabCompleter(tabCompleter);
}
if (getCommand("mapart") != null) getCommand("mapart").setTabCompleter(tabCompleter);
if (getCommand("intro") != null) getCommand("intro").setTabCompleter(tabCompleter);
PluginCommand borderCmd = this.getCommand("border");
if (borderCmd != null) {
borderCmd.setExecutor(new BorderCommand());
borderCmd.setTabCompleter(tabCompleter);
}
} }
public class NexusLobbyExpansion extends PlaceholderExpansion { public class NexusLobbyExpansion extends PlaceholderExpansion {
@@ -288,4 +364,8 @@ public class NexusLobby extends JavaPlugin implements Listener {
public ModuleManager getModuleManager() { return moduleManager; } public ModuleManager getModuleManager() { return moduleManager; }
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 GadgetModule getGadgetModule() { return gadgetModule; }
public HologramModule getHologramModule() { return hologramModule; }
public DynamicArmorStandModule getDynamicArmorStandModule() { return dynamicArmorStandModule; }
} }

View File

@@ -12,7 +12,6 @@ import java.util.UUID;
public class BuildCommand implements CommandExecutor { public class BuildCommand implements CommandExecutor {
// Liste der Spieler im Baumodus
private static final ArrayList<UUID> buildModePlayers = new ArrayList<>(); private static final ArrayList<UUID> buildModePlayers = new ArrayList<>();
@Override @Override
@@ -30,7 +29,16 @@ public class BuildCommand implements CommandExecutor {
// BAUMODUS DEAKTIVIEREN // BAUMODUS DEAKTIVIEREN
buildModePlayers.remove(uuid); buildModePlayers.remove(uuid);
// Gamemode zurücksetzen (aus Config) // Inventar leeren
player.getInventory().clear();
player.getInventory().setArmorContents(null);
// Lobby-Items über das Modul wiedergeben
if (NexusLobby.getInstance().getItemsModule() != null) {
NexusLobby.getInstance().getItemsModule().giveLobbyItems(player);
}
// Gamemode zurücksetzen
String defaultGmName = NexusLobby.getInstance().getConfig().getString("default-gamemode", "ADVENTURE"); String defaultGmName = NexusLobby.getInstance().getConfig().getString("default-gamemode", "ADVENTURE");
try { try {
GameMode gm = GameMode.valueOf(defaultGmName.toUpperCase()); GameMode gm = GameMode.valueOf(defaultGmName.toUpperCase());
@@ -43,6 +51,9 @@ public class BuildCommand implements CommandExecutor {
} else { } else {
// BAUMODUS AKTIVIEREN // BAUMODUS AKTIVIEREN
buildModePlayers.add(uuid); buildModePlayers.add(uuid);
player.getInventory().clear();
player.getInventory().setArmorContents(null);
player.setGameMode(GameMode.CREATIVE); player.setGameMode(GameMode.CREATIVE);
player.sendMessage("§8» §6§lBuild §8| §7Baumodus §aaktiviert§7."); player.sendMessage("§8» §6§lBuild §8| §7Baumodus §aaktiviert§7.");

View File

@@ -1,9 +1,11 @@
package de.nexuslobby.commands; package de.nexuslobby.commands;
import de.nexuslobby.modules.portal.PortalManager; import de.nexuslobby.modules.portal.PortalManager;
import de.nexuslobby.modules.hologram.HologramModule;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter; import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -13,19 +15,25 @@ import java.util.stream.Collectors;
public class LobbyTabCompleter implements TabCompleter { public class LobbyTabCompleter implements TabCompleter {
private final PortalManager portalManager; private final PortalManager portalManager;
private final HologramModule hologramModule;
public LobbyTabCompleter(PortalManager portalManager) { public LobbyTabCompleter(PortalManager portalManager, HologramModule hologramModule) {
this.portalManager = portalManager; this.portalManager = portalManager;
this.hologramModule = hologramModule;
} }
@Override @Override
public List<String> onTabComplete(CommandSender sender, Command command, 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<>();
// --- NexusLobby Hauptbefehl --- // --- NexusLobby Hauptbefehl (/nexus) ---
if (command.getName().equalsIgnoreCase("nexuslobby") || command.getName().equalsIgnoreCase("nexus")) { if (command.getName().equalsIgnoreCase("nexuslobby") || command.getName().equalsIgnoreCase("nexus")) {
if (args.length == 1) { if (args.length == 1) {
if (sender.hasPermission("nexuslobby.admin")) suggestions.add("reload"); // Hier fügen wir 'setspawn' hinzu
if (sender.hasPermission("nexuslobby.admin")) {
suggestions.add("reload");
suggestions.add("setspawn");
}
suggestions.add("sb"); suggestions.add("sb");
} else if (args.length == 2 && args[0].equalsIgnoreCase("sb")) { } else if (args.length == 2 && args[0].equalsIgnoreCase("sb")) {
suggestions.add("on"); suggestions.add("on");
@@ -37,6 +45,18 @@ public class LobbyTabCompleter implements TabCompleter {
} }
} }
// --- Hologram Befehl ---
else if (command.getName().equalsIgnoreCase("holo")) {
if (args.length == 1) {
suggestions.add("create");
suggestions.add("delete");
} else if (args.length == 2 && args[0].equalsIgnoreCase("delete")) {
if (hologramModule != null) {
suggestions.addAll(hologramModule.getHologramIds());
}
}
}
// --- Wartungsmodus --- // --- Wartungsmodus ---
else if (command.getName().equalsIgnoreCase("maintenance")) { else if (command.getName().equalsIgnoreCase("maintenance")) {
if (args.length == 1) { if (args.length == 1) {
@@ -52,48 +72,47 @@ public class LobbyTabCompleter implements TabCompleter {
suggestions.add("delete"); suggestions.add("delete");
suggestions.add("list"); suggestions.add("list");
} else if (args.length == 2 && args[0].equalsIgnoreCase("delete")) { } else if (args.length == 2 && args[0].equalsIgnoreCase("delete")) {
if (portalManager != null) {
suggestions.addAll(portalManager.getPortalNames()); suggestions.addAll(portalManager.getPortalNames());
} }
} }
}
// --- NexusTools (ehemals astools) --- // --- MapArt ---
else if (command.getName().equalsIgnoreCase("nexustools") || command.getName().equalsIgnoreCase("astools") || command.getName().equalsIgnoreCase("nt")) { else if (command.getName().equalsIgnoreCase("mapart")) {
if (args.length == 1) { if (args.length == 1) {
suggestions.add("reload"); suggestions.add("https://");
} else if (args.length == 2) {
suggestions.addAll(Arrays.asList("1x1", "3x2", "6x4", "8x5"));
} }
} }
// --- NexusCmd (ehemals ascmd) --- // --- Intro System ---
else if (command.getName().equalsIgnoreCase("nexuscmd") || command.getName().equalsIgnoreCase("ascmd") || command.getName().equalsIgnoreCase("ncmd")) { else if (command.getName().equalsIgnoreCase("intro")) {
if (args.length == 1) { if (args.length == 1) {
suggestions.add("name"); // NEU suggestions.addAll(Arrays.asList("add", "clear", "start"));
suggestions.add("list");
suggestions.add("add");
suggestions.add("remove");
}
// Vorschläge für: /nexuscmd name <Text>
else if (args.length == 2 && args[0].equalsIgnoreCase("name")) {
suggestions.add("none");
suggestions.add("<Text>");
}
// Vorschläge für: /nexuscmd add <delay> <cooldown> <type>
else if (args.length == 2 && args[0].equalsIgnoreCase("add")) {
suggestions.add("0");
}
else if (args.length == 3 && args[0].equalsIgnoreCase("add")) {
suggestions.add("0");
}
else if (args.length == 4 && args[0].equalsIgnoreCase("add")) {
suggestions.add("player");
suggestions.add("console");
suggestions.add("bungee");
}
else if (args.length == 5 && args[0].equalsIgnoreCase("add") && args[3].equalsIgnoreCase("bungee")) {
suggestions.add("<Servername>");
} }
} }
// Filtert die Liste basierend auf dem, was der Spieler bereits getippt hat // --- WorldBorder ---
else if (command.getName().equalsIgnoreCase("border")) {
if (args.length == 1) {
suggestions.add("circle");
suggestions.add("square");
suggestions.add("disable");
} else if (args.length == 2 && args[0].equalsIgnoreCase("circle")) {
suggestions.addAll(Arrays.asList("10", "25", "50", "100", "250"));
}
}
// --- NexusCmd & Tools (Verkürzte Logik) ---
else if (command.getName().equalsIgnoreCase("nexuscmd") || command.getName().equalsIgnoreCase("ncmd")) {
if (args.length == 1) {
suggestions.addAll(Arrays.asList("name", "list", "add", "remove"));
}
}
// Filtert die Vorschläge basierend auf dem, was der Spieler bereits getippt hat
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

@@ -2,89 +2,134 @@ package de.nexuslobby.commands;
import de.nexuslobby.NexusLobby; import de.nexuslobby.NexusLobby;
import de.nexuslobby.modules.ScoreboardModule; import de.nexuslobby.modules.ScoreboardModule;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.World;
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.configuration.file.FileConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class NexusLobbyCommand implements CommandExecutor { public class NexusLobbyCommand implements CommandExecutor {
@Override @Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
// Sub-Befehl: /nexus sb <on|off|admin|spieler>
if (args.length >= 2 && args[0].equalsIgnoreCase("sb")) {
if (!(sender instanceof Player)) { if (!(sender instanceof 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; Player player = (Player) sender;
ScoreboardModule sbModule = (ScoreboardModule) NexusLobby.getInstance().getModuleManager().getModule(ScoreboardModule.class);
// ==========================================
// LOGIK FÜR /spawn
// ==========================================
if (command.getName().equalsIgnoreCase("spawn")) {
FileConfiguration config = NexusLobby.getInstance().getConfig();
if (config.contains("spawn.world")) {
Location loc = getSpawnFromConfig(config);
if (loc != null) {
player.teleport(loc);
player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1.0f, 1.2f);
player.sendMessage("§8[§6Nexus§8] §aDu wurdest zum Spawn teleportiert.");
} else {
player.sendMessage("§cFehler: Die Spawn-Welt existiert nicht.");
}
} else {
player.sendMessage("§cEs wurde noch kein Spawn gesetzt.");
}
return true;
}
// ==========================================
// LOGIK FÜR /nexus (Hauptbefehl)
// ==========================================
// 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;
}
// Sub-Befehl: /nexus sb <on|off|admin|spieler>
if (args.length >= 2 && args[0].equalsIgnoreCase("sb")) {
ScoreboardModule sbModule = (ScoreboardModule) NexusLobby.getInstance().getModuleManager().getModule(ScoreboardModule.class);
if (sbModule == null) { if (sbModule == null) {
player.sendMessage("§cDas Scoreboard-Modul ist aktuell deaktiviert."); player.sendMessage("§cScoreboard-Modul ist deaktiviert.");
return true; return true;
} }
String sub = args[1].toLowerCase(); String sub = args[1].toLowerCase();
switch (sub) { switch (sub) {
case "on": case "on": sbModule.setVisibility(player, true); break;
sbModule.setVisibility(player, true); case "off": sbModule.setVisibility(player, false); break;
break;
case "off":
sbModule.setVisibility(player, false);
break;
case "admin": case "admin":
if (!player.hasPermission("nexuslobby.scoreboard.admin")) { if (player.hasPermission("nexuslobby.scoreboard.admin")) sbModule.setAdminMode(player, true);
player.sendMessage("§cKeine Rechte für den Admin-Modus."); else player.sendMessage("§cKeine Rechte.");
return true;
}
sbModule.setAdminMode(player, true);
break; break;
case "spieler": case "spieler":
if (!player.hasPermission("nexuslobby.scoreboard.admin")) { if (player.hasPermission("nexuslobby.scoreboard.admin")) sbModule.setAdminMode(player, false);
player.sendMessage("§cKeine Rechte für den Admin-Modus."); else player.sendMessage("§cKeine Rechte.");
return true;
}
sbModule.setAdminMode(player, false);
break;
default:
player.sendMessage("§cBenutzung: /nexus sb <on|off|admin|spieler>");
break; break;
default: player.sendMessage("§cBenutzung: /nexus sb <on|off|admin|spieler>"); break;
} }
return true; return true;
} }
// Sub-Befehl: /nexus reload // Sub-Befehl: /nexus reload
if (args.length == 1 && args[0].equalsIgnoreCase("reload")) { if (args.length == 1 && args[0].equalsIgnoreCase("reload")) {
if (!sender.hasPermission("nexuslobby.admin")) { if (!player.hasPermission("nexuslobby.admin")) {
sender.sendMessage("§cDu hast keine Berechtigung für diesen Befehl."); player.sendMessage("§cKeine Rechte.");
return true; return true;
} }
sender.sendMessage("§7[§6NexusLobby§7] §eKonfigurationen und Module werden neu geladen...");
NexusLobby.getInstance().reloadPlugin(); NexusLobby.getInstance().reloadPlugin();
sender.sendMessage("§7[§6NexusLobby§7] §aDas Plugin wurde erfolgreich neu geladen!"); player.sendMessage("§7[§6NexusLobby§7] §aPlugin erfolgreich neu geladen!");
return true; return true;
} }
// Standard-Info // Info-Screen
sendInfo(player);
return true;
}
private Location getSpawnFromConfig(FileConfiguration config) {
World world = Bukkit.getWorld(config.getString("spawn.world", "world"));
if (world == null) return null;
return new Location(world,
config.getDouble("spawn.x"), config.getDouble("spawn.y"), config.getDouble("spawn.z"),
(float) config.getDouble("spawn.yaw"), (float) config.getDouble("spawn.pitch"));
}
private void sendInfo(Player player) {
String version = NexusLobby.getInstance().getDescription().getVersion(); String version = NexusLobby.getInstance().getDescription().getVersion();
sender.sendMessage("§8§m--------------------------------------"); player.sendMessage("§8§m--------------------------------------");
sender.sendMessage("§6§lNexusLobby §7- Informationen"); player.sendMessage("§6§lNexusLobby §7- Informationen");
sender.sendMessage(""); player.sendMessage("");
sender.sendMessage("§ePlugin Name: §fNexusLobby"); player.sendMessage("§f/spawn §7- Zum Spawn teleportieren");
sender.sendMessage("§eVersion: §f" + version); player.sendMessage("§f/nexus setspawn §7- Spawn setzen");
sender.sendMessage("§eAutor: §fM_Viper"); player.sendMessage("§f/nexus reload §7- Konfiguration laden");
sender.sendMessage(""); player.sendMessage("§f/nexus sb <on|off> §7- Scoreboard");
sender.sendMessage("§6Befehle:"); player.sendMessage("§8§m--------------------------------------");
sender.sendMessage("§f/nexus reload §7- Plugin neu laden");
sender.sendMessage("§f/nexus sb <on|off> §7- Scoreboard schalten");
if (sender.hasPermission("nexuslobby.scoreboard.admin")) {
sender.sendMessage("§f/nexus sb <admin|spieler> §7- Modus wechseln");
}
sender.sendMessage("§8§m--------------------------------------");
return true;
} }
} }

View File

@@ -0,0 +1,47 @@
package de.nexuslobby.modules;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.UUID;
public class BuildModule implements Module {
private final ArrayList<UUID> buildModePlayers = new ArrayList<>();
@Override
public String getName() {
return "Build";
}
@Override
public void onEnable() {
// Falls du hier den Command registrierst, achte darauf,
// dass er nicht doppelt in der NexusLobby.java registriert wird.
}
public void toggleBuildMode(Player player) {
if (!player.hasPermission("nexuslobby.build")) return;
if (buildModePlayers.contains(player.getUniqueId())) {
buildModePlayers.remove(player.getUniqueId());
player.setGameMode(GameMode.SURVIVAL);
player.getInventory().clear();
// KORREKTUR: Hier muss getItemsModule() mit "s" stehen!
if (NexusLobby.getInstance().getItemsModule() != null) {
NexusLobby.getInstance().getItemsModule().giveLobbyItems(player);
}
} else {
buildModePlayers.add(player.getUniqueId());
player.setGameMode(GameMode.CREATIVE);
player.getInventory().clear();
}
}
@Override
public void onDisable() {}
}

View File

@@ -1,12 +1,18 @@
package de.nexuslobby.modules; package de.nexuslobby.modules;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module; import de.nexuslobby.api.Module;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class ItemsModule implements Module, Listener { public class ItemsModule implements Module, Listener {
@@ -17,12 +23,95 @@ public class ItemsModule implements Module, Listener {
@Override @Override
public void onEnable() { public void onEnable() {
Bukkit.getPluginManager().registerEvents(this, Bukkit.getPluginManager().getPlugin("NexusLobby")); Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance());
} }
@EventHandler @EventHandler
public void onJoin(PlayerJoinEvent event) { public void onJoin(PlayerJoinEvent event) {
event.getPlayer().getInventory().addItem(new ItemStack(Material.COMPASS)); Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), () -> {
giveLobbyItems(event.getPlayer());
}, 2L);
}
@EventHandler
public void onInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
ItemStack item = event.getItem();
if (item == null || item.getType() == Material.AIR) return;
if (!item.hasItemMeta() || !item.getItemMeta().hasDisplayName()) return;
if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
FileConfiguration config = NexusLobby.getInstance().getConfig();
String displayName = item.getItemMeta().getDisplayName();
// 1. Baumodus Logik
String buildName = colorize(config.getString("items.lobby-tools.build-toggle.displayname", "&aBaumodus"));
if (displayName.equals(buildName)) {
player.performCommand("build");
event.setCancelled(true);
return;
}
// 2. Gadget GUI Logik
String gadgetName = colorize(config.getString("items.lobby-tools.gadget.displayname", "&bGadgets"));
if (displayName.equals(gadgetName)) {
// Öffnet die GUI aus dem GadgetModule
NexusLobby.getInstance().getGadgetModule().openGUI(player);
event.setCancelled(true);
return;
}
// 3. Kompass / Teleporter Logik (optional, falls du den Befehl schon hast)
String compassName = colorize(config.getString("items.lobby-tools.compass.displayname", "&eTeleporter"));
if (displayName.equals(compassName)) {
// Hier könnte z.B. player.performCommand("cp"); stehen
}
}
}
public void giveLobbyItems(Player player) {
player.getInventory().clear();
FileConfiguration config = NexusLobby.getInstance().getConfig();
// 1. Kompass (Slot 4)
if (config.getBoolean("items.lobby-tools.compass.enabled", true)) {
int slot = config.getInt("items.lobby-tools.compass.slot", 4);
String name = config.getString("items.lobby-tools.compass.displayname", "&eTeleporter");
player.getInventory().setItem(slot, createItem(Material.COMPASS, name));
}
// 2. Baumodus (Slot 0)
if (player.isOp() || player.hasPermission("nexuslobby.build")) {
if (config.getBoolean("items.lobby-tools.build-toggle.enabled", true)) {
int slot = config.getInt("items.lobby-tools.build-toggle.slot", 0);
String name = config.getString("items.lobby-tools.build-toggle.displayname", "&aBaumodus");
player.getInventory().setItem(slot, createItem(Material.REDSTONE_TORCH, name));
}
}
// 3. Gadgets (Slot 8)
if (config.getBoolean("items.lobby-tools.gadget.enabled", true)) { // Auf true gesetzt, damit es erscheint
int slot = config.getInt("items.lobby-tools.gadget.slot", 8);
String name = config.getString("items.lobby-tools.gadget.displayname", "&bGadgets");
player.getInventory().setItem(slot, createItem(Material.CHEST, name));
}
player.updateInventory();
}
private ItemStack createItem(Material mat, String name) {
ItemStack item = new ItemStack(mat);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.setDisplayName(colorize(name));
item.setItemMeta(meta);
}
return item;
}
private String colorize(String s) {
return (s == null) ? "" : s.replace("&", "§");
} }
@Override @Override

View File

@@ -1,9 +1,5 @@
package de.nexuslobby.modules.armorstandtools; package de.nexuslobby.modules.armorstandtools;
import de.nexuslobby.NexusLobby;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
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,84 +10,120 @@ import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerInteractAtEntityEvent; import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.util.EulerAngle;
import java.util.Set;
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();
// Sneak-Rechtsklick zum Bearbeiten // Prüfen ob Spieler schleicht (Shift)
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;
}
// Normale Interaktion (Befehl ausführen)
Set<String> tags = as.getScoreboardTags();
for (String tag : tags) {
if (tag.startsWith("ascmd:")) {
event.setCancelled(true);
executeNexusCommand(p, tag);
break;
}
} }
} }
/**
* Verwaltet alle Klicks innerhalb der verschiedenen GUIs.
*/
@EventHandler(priority = EventPriority.LOW) @EventHandler(priority = EventPriority.LOW)
public void onInventoryClick(InventoryClickEvent event) { public void onInventoryClick(InventoryClickEvent event) {
if (!event.getView().getTitle().equals("Nexus ArmorStand Editor")) return; String title = event.getView().getTitle();
// Prüfen, ob es eine unserer GUIs ist
if (!title.equals("Nexus ArmorStand Editor") &&
!title.equals("Pose: Körperteil wählen") &&
!title.startsWith("Achsen:")) return;
event.setCancelled(true); event.setCancelled(true);
if (!(event.getWhoClicked() instanceof Player p)) return; if (!(event.getWhoClicked() instanceof Player p)) return;
ItemStack item = event.getCurrentItem();
if (item == null || item.getType() == Material.AIR) return;
ArmorStand as = AST.selectedArmorStand.get(p.getUniqueId()); ArmorStand as = AST.selectedArmorStand.get(p.getUniqueId());
if (as == null || !as.isValid()) { if (as == null || !as.isValid()) {
p.closeInventory(); p.closeInventory();
return; return;
} }
ItemStack item = event.getCurrentItem();
if (item == null || item.getType() == Material.AIR) return;
// --- 1. LOGIK: HAUPTMENÜ ---
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 aktualisieren, falls noch offen (SET_NAME schließt es z.B.) // GUI erneuern (außer sie wurde durch execute geschlossen)
if (p.getOpenInventory().getTitle().equals("Nexus ArmorStand Editor")) { if (p.getOpenInventory().getTitle().equals(title)) {
new ArmorStandGUI(as, p); new ArmorStandGUI(as, p);
} }
} }
} }
private void executeNexusCommand(Player p, String tag) { // --- 2. LOGIK: KÖRPERTEIL-AUSWAHL ---
try { else if (title.equals("Pose: Körperteil wählen")) {
String[] parts = tag.split(":", 3); if (item.getType() == Material.BARRIER) {
if (parts.length < 3) return; new ArmorStandGUI(as, p);
String type = parts[1].toLowerCase(); return;
String finalizedCommand = parts[2].replace("%player%", p.getName());
switch (type) {
case "player" -> p.performCommand(finalizedCommand);
case "console" -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalizedCommand);
case "bungee" -> sendToBungee(p, finalizedCommand);
}
} catch (Exception e) { e.printStackTrace(); }
} }
private void sendToBungee(Player p, String server) { String targetPart = null;
ByteArrayDataOutput out = ByteStreams.newDataOutput(); switch (item.getType()) {
out.writeUTF("Connect"); case PLAYER_HEAD -> targetPart = "HEAD_ROT";
out.writeUTF(server); case IRON_CHESTPLATE -> targetPart = "BODY_ROT";
p.sendPluginMessage(NexusLobby.getInstance(), "BungeeCord", out.toByteArray()); case STICK -> targetPart = (event.getSlot() == 14) ? "L_ARM_ROT" : "R_ARM_ROT";
case LEATHER_BOOTS -> targetPart = (event.getSlot() == 28) ? "L_LEG_ROT" : "R_LEG_ROT";
}
if (targetPart != null) {
ArmorStandPoseGUI.openAxisDetailMenu(p, as, targetPart);
}
}
// --- 3. LOGIK: ACHSEN-FEINJUSTIERUNG ---
else if (title.startsWith("Achsen:")) {
if (item.getType() == Material.ARROW) {
ArmorStandPoseGUI.openPartSelectionMenu(p, as);
return;
}
String partName = title.split(": ")[1];
ArmorStandTool tool = ArmorStandTool.valueOf(partName);
// Berechnung des Winkels (Links/Rechts & Shift)
double change = event.isShiftClick() ? Math.toRadians(1) : Math.toRadians(15);
if (event.isRightClick()) change *= -1;
EulerAngle oldPose = ArmorStandPoseGUI.getAngleForPart(as, tool);
EulerAngle newPose = oldPose;
if (item.getType() == Material.RED_DYE) {
newPose = oldPose.setX(oldPose.getX() + change);
} else if (item.getType() == Material.GREEN_DYE) {
newPose = oldPose.setY(oldPose.getY() + change);
} else if (item.getType() == Material.BLUE_DYE) {
newPose = oldPose.setZ(oldPose.getZ() + change);
}
ArmorStandPoseGUI.setAngleForPart(as, tool, newPose);
// GUI aktualisieren um Werte in der Lore anzuzeigen
ArmorStandPoseGUI.openAxisDetailMenu(p, as, partName);
}
} }
} }

View File

@@ -1,6 +1,8 @@
package de.nexuslobby.modules.armorstandtools; package de.nexuslobby.modules.armorstandtools;
import de.nexuslobby.NexusLobby;
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;
@@ -11,85 +13,143 @@ import org.jetbrains.annotations.NotNull;
import java.util.Set; import java.util.Set;
/**
* ArmorStandCommand - Vollständige Steuerung für ArmorStands.
* Inklusive Dynamic-Modus Erkennung und visueller Rückmeldung.
*/
public class ArmorStandCommand implements CommandExecutor { public class ArmorStandCommand 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 p)) return true; if (!(sender instanceof Player p)) {
sender.sendMessage("Nur Spieler können diesen Befehl ausführen.");
if (!p.hasPermission("nexuslobby.armorstand.use")) {
p.sendMessage(Config.prefix() + ChatColor.RED + Config.generalNoPerm);
return true; return true;
} }
// Suche ArmorStand im Umkreis von 5 Blöcken // Fester Prefix für maximale Build-Sicherheit
ArmorStand target = getNearbyArmorStand(p); String prefix = "§8[§6Nexus§8] ";
// Fall 1: Nur /astools -> GUI öffnen if (!p.hasPermission("nexuslobby.armorstand.use")) {
p.sendMessage(prefix + ChatColor.RED + "Dazu hast du keine Rechte!");
return true;
}
// 1. Suche nach dem ArmorStand (Präzise & Umkreis)
ArmorStand target = getBestTargetArmorStand(p);
// Fall: Nur /astools oder /nt (Ohne Argumente)
if (args.length == 0) { if (args.length == 0) {
if (target != null) { if (target != null) {
AST.selectedArmorStand.put(p.getUniqueId(), target); AST.selectedArmorStand.put(p.getUniqueId(), target);
new ArmorStandGUI(target, p); new ArmorStandGUI(target, p);
p.sendMessage(prefix + "§aEditor für ArmorStand geöffnet.");
} else { } else {
p.sendMessage(Config.prefix() + ChatColor.RED + "Kein ArmorStand in der Nähe gefunden!"); p.sendMessage(prefix + ChatColor.RED + "Kein ArmorStand in der Nähe gefunden!");
} }
return true; return true;
} }
// Fall 2: Unterbefehle (add, remove, list)
String sub = args[0].toLowerCase(); String sub = args[0].toLowerCase();
// Globaler Reload
if (sub.equals("reload")) {
NexusLobby.getInstance().reloadPlugin();
p.sendMessage(prefix + "§aKonfiguration und Module wurden neu geladen.");
return true;
}
// Für alle anderen Aktionen ist ein ArmorStand zwingend erforderlich
if (target == null) { if (target == null) {
p.sendMessage(Config.prefix() + ChatColor.RED + "Du musst nah an einem ArmorStand stehen!"); p.sendMessage(prefix + ChatColor.RED + "Du musst einen ArmorStand anschauen oder direkt davor stehen!");
return true; return true;
} }
switch (sub) { switch (sub) {
case "addplayer": // /astools addplayer <command> case "dynamic":
if (args.length < 2) return sendHelp(p); if (!p.hasPermission("nexuslobby.armorstand.dynamic")) {
p.sendMessage(prefix + ChatColor.RED + "Keine Rechte für Dynamic-NPCs!");
return true;
}
if (NexusLobby.getInstance().getDynamicArmorStandModule() != null) {
// Toggle Logik
if (target.getScoreboardTags().contains("as_dynamic")) {
target.removeScoreboardTag("as_dynamic");
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);
} else {
target.addScoreboardTag("as_dynamic");
p.sendMessage(prefix + "§a§l[+] §7Dynamic-Modus §aaktiviert§7 (Wetter/Zeit).");
// 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);
}
// Internes Update sofort triggern
NexusLobby.getInstance().getDynamicArmorStandModule().toggleDynamicStatus(target);
} else {
p.sendMessage(prefix + "§cFehler: Dynamic-Modul ist nicht aktiv!");
}
break;
case "addplayer":
if (args.length < 2) return sendHelp(p, prefix);
String pCmd = buildString(args, 1); String pCmd = buildString(args, 1);
target.addScoreboardTag("ascmd:player:" + pCmd); target.addScoreboardTag("ascmd:player:" + pCmd);
p.sendMessage(Config.prefix() + "§aBefehl (Player) hinzugefügt: §e" + pCmd); p.sendMessage(prefix + "§aBefehl (Player) gespeichert: §e/" + pCmd);
break; break;
case "addconsole": // /astools addconsole <command> case "addconsole":
if (args.length < 2) return sendHelp(p); if (args.length < 2) return sendHelp(p, prefix);
String cCmd = buildString(args, 1); String cCmd = buildString(args, 1);
target.addScoreboardTag("ascmd:console:" + cCmd); target.addScoreboardTag("ascmd:console:" + cCmd);
p.sendMessage(Config.prefix() + "§aBefehl (Konsole) hinzugefügt: §e" + cCmd); p.sendMessage(prefix + "§aBefehl (Konsole) gespeichert: §e" + cCmd);
break; break;
case "addbungee": // /astools addbungee <server> case "remove":
if (args.length < 2) return sendHelp(p); target.getScoreboardTags().removeIf(tag -> tag.startsWith("ascmd:"));
String server = args[1]; p.sendMessage(prefix + "§cAlle Befehls-Tags wurden entfernt.");
target.addScoreboardTag("ascmd:bungee:" + server);
p.sendMessage(Config.prefix() + "§aBefehl (Bungee) hinzugefügt: §eConnect zu " + server);
break;
case "remove": // /astools remove (Löscht alle ascmd Tags)
Set<String> tags = target.getScoreboardTags();
tags.removeIf(tag -> tag.startsWith("ascmd:"));
p.sendMessage(Config.prefix() + "§cAlle Befehle von diesem ArmorStand entfernt.");
break;
case "reload":
Config.load();
p.sendMessage(Config.prefix() + "§aKonfiguration neu geladen.");
break; break;
default: default:
sendHelp(p); sendHelp(p, prefix);
break; break;
} }
return true; return true;
} }
private ArmorStand getNearbyArmorStand(Player p) { /**
for (Entity e : p.getNearbyEntities(5, 5, 5)) { * Sucht den am besten passenden ArmorStand.
if (e instanceof ArmorStand as) return as; * 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) {
ArmorStand best = null;
double bestDot = 0.95; // Schwellenwert für das "Anschauen"
for (Entity e : p.getNearbyEntities(8, 8, 8)) {
if (e instanceof ArmorStand as) {
// Berechne, ob der Spieler in Richtung des ArmorStands schaut
double dot = p.getLocation().getDirection().dot(
as.getLocation().toVector().subtract(p.getLocation().toVector()).normalize()
);
if (dot > bestDot) {
bestDot = dot;
best = as;
} }
return null; }
}
// Falls nichts aktiv angeschaut wird, nimm einfach den nächsten im 4-Block-Radius
if (best == null) {
for (Entity e : p.getNearbyEntities(4, 4, 4)) {
if (e instanceof ArmorStand as) {
return as;
}
}
}
return best;
} }
private String buildString(String[] args, int start) { private String buildString(String[] args, int start) {
@@ -100,14 +160,16 @@ public class ArmorStandCommand implements CommandExecutor {
return sb.toString(); return sb.toString();
} }
private boolean sendHelp(Player p) { private boolean sendHelp(Player p, String prefix) {
p.sendMessage("§6§lArmorStandTools Hilfe:"); p.sendMessage(" ");
p.sendMessage("§e/astools §7- Öffnet Editor"); p.sendMessage("§8§m-----------§r " + prefix + "§8§m-----------");
p.sendMessage("§e/astools addplayer <cmd> §7- Befehl als Spieler ausführen"); p.sendMessage("§e/astools §7- Editor öffnen");
p.sendMessage("§e/astools addconsole <cmd> §7- Befehl als Konsole ausführen"); p.sendMessage("§b/astools dynamic §7- Wetter/Zeit Logik umschalten");
p.sendMessage("§e/astools addbungee <server> §7- Serverwechsel"); p.sendMessage("§e/astools addplayer <cmd> §7- Befehl als Spieler");
p.sendMessage("§e/astools remove §7- Entfernt alle Befehle"); p.sendMessage("§e/astools addconsole <cmd> §7- Befehl via Konsole");
p.sendMessage("§e/astools reload §7- Lädt Config neu"); p.sendMessage("§e/astools remove §7- Befehle löschen");
p.sendMessage("§e/astools reload §7- Konfig neu laden");
p.sendMessage("§8§m---------------------------------------");
return true; return true;
} }
} }

View File

@@ -0,0 +1,108 @@
package de.nexuslobby.modules.armorstandtools;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.util.EulerAngle;
import java.util.Arrays;
public class ArmorStandPoseGUI {
// Schritt 1: Auswahl welches Körperteil (Die "letzte Reihe" ist jetzt hier)
public static void openPartSelectionMenu(Player p, ArmorStand as) {
Inventory inv = Bukkit.createInventory(null, 45, "Pose: Körperteil wählen");
ItemStack filler = new ItemStack(Material.BLACK_STAINED_GLASS_PANE);
for (int i = 0; i < 45; i++) inv.setItem(i, filler);
inv.setItem(10, createPartIcon(Material.PLAYER_HEAD, "§eKopf", as.getHeadPose()));
inv.setItem(12, createPartIcon(Material.IRON_CHESTPLATE, "§eTorso", as.getBodyPose()));
inv.setItem(14, createPartIcon(Material.STICK, "§eLinker Arm", as.getLeftArmPose()));
inv.setItem(16, createPartIcon(Material.STICK, "§eRechter Arm", as.getRightArmPose()));
inv.setItem(28, createPartIcon(Material.LEATHER_BOOTS, "§eLinkes Bein", as.getLeftLegPose()));
inv.setItem(30, createPartIcon(Material.LEATHER_BOOTS, "§eRechtes Bein", as.getRightLegPose()));
inv.setItem(40, createNamedItem(Material.BARRIER, "§cZurück zum Hauptmenü"));
p.openInventory(inv);
}
// Schritt 2: Detail-Einstellung der Achsen (X, Y, Z)
public static void openAxisDetailMenu(Player p, ArmorStand as, String partName) {
Inventory inv = Bukkit.createInventory(null, 27, "Achsen: " + partName);
ItemStack filler = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
for (int i = 0; i < 27; i++) inv.setItem(i, filler);
ArmorStandTool tool = ArmorStandTool.valueOf(partName);
EulerAngle angle = getAngleForPart(as, tool);
inv.setItem(11, createAxisItem(Material.RED_DYE, "§c§lX-Achse (Pitch)", angle.getX()));
inv.setItem(13, createAxisItem(Material.GREEN_DYE, "§a§lY-Achse (Yaw)", angle.getY()));
inv.setItem(15, createAxisItem(Material.BLUE_DYE, "§b§lZ-Achse (Roll)", angle.getZ()));
inv.setItem(22, createNamedItem(Material.ARROW, "§7Zurück zur Auswahl"));
p.openInventory(inv);
}
private static ItemStack createPartIcon(Material mat, String name, EulerAngle angle) {
ItemStack item = new ItemStack(mat);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(name);
meta.setLore(Arrays.asList(
"§8Rotation:",
"§7X: §f" + Math.round(Math.toDegrees(angle.getX())) + "°",
"§7Y: §f" + Math.round(Math.toDegrees(angle.getY())) + "°",
"§7Z: §f" + Math.round(Math.toDegrees(angle.getZ())) + "°",
"", "§6Klicken zum Justieren"
));
item.setItemMeta(meta);
return item;
}
private static ItemStack createAxisItem(Material mat, String name, double angleRad) {
ItemStack item = new ItemStack(mat);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(name);
meta.setLore(Arrays.asList(
"§7Aktueller Winkel: §f" + Math.round(Math.toDegrees(angleRad)) + "°",
"",
"§eLinksklick: §f+15° §8| §eRechtsklick: §f-15°",
"§6Shift-Klick: §f+1° §8| §6Shift-Rechts: §f-1°"
));
item.setItemMeta(meta);
return item;
}
private static ItemStack createNamedItem(Material mat, String name) {
ItemStack item = new ItemStack(mat);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(name);
item.setItemMeta(meta);
return item;
}
public static EulerAngle getAngleForPart(ArmorStand as, ArmorStandTool part) {
return switch (part) {
case HEAD_ROT -> as.getHeadPose();
case BODY_ROT -> as.getBodyPose();
case L_ARM_ROT -> as.getLeftArmPose();
case R_ARM_ROT -> as.getRightArmPose();
case L_LEG_ROT -> as.getLeftLegPose();
case R_LEG_ROT -> as.getRightLegPose();
default -> EulerAngle.ZERO;
};
}
public static void setAngleForPart(ArmorStand as, ArmorStandTool part, EulerAngle angle) {
switch (part) {
case HEAD_ROT -> as.setHeadPose(angle);
case BODY_ROT -> as.setBodyPose(angle);
case L_ARM_ROT -> as.setLeftArmPose(angle);
case R_ARM_ROT -> as.setRightArmPose(angle);
case L_LEG_ROT -> as.setLeftLegPose(angle);
case R_LEG_ROT -> as.setRightLegPose(angle);
}
}
}

View File

@@ -10,80 +10,27 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public enum ArmorStandTool { public enum ArmorStandTool {
// Sichtbarkeit & Basis-Attribute // --- HAUPTMENÜ ATTRIBUTE ---
INVIS(Material.GLASS_PANE, 10) { INVIS(Material.GLASS_PANE, 10),
@Override public void execute(ArmorStand as, Player p) { as.setVisible(!as.isVisible()); } ARMS(Material.STICK, 11),
}, BASE(Material.STONE_SLAB, 12),
ARMS(Material.STICK, 11) { SIZE(Material.PUMPKIN_SEEDS, 13),
@Override public void execute(ArmorStand as, Player p) { as.setArms(!as.hasArms()); } GRAV(Material.ANVIL, 14),
}, INVUL(Material.BEDROCK, 15),
BASE(Material.STONE_SLAB, 12) { SET_NAME(Material.NAME_TAG, 16),
@Override public void execute(ArmorStand as, Player p) { as.setBasePlate(!as.hasBasePlate()); }
},
SIZE(Material.PUMPKIN_SEEDS, 13) {
@Override public void execute(ArmorStand as, Player p) { as.setSmall(!as.isSmall()); }
},
GRAV(Material.ANVIL, 14) {
@Override public void execute(ArmorStand as, Player p) { as.setGravity(!as.hasGravity()); }
},
INVUL(Material.BEDROCK, 15) {
@Override public void execute(ArmorStand as, Player p) { as.setInvulnerable(!as.isInvulnerable()); }
},
// Hologramm / Name setzen // Zentrales Item zum Öffnen des Pose-Editors
SET_NAME(Material.NAME_TAG, 16) { OPEN_POSE_EDITOR(Material.ARMOR_STAND, 31),
@Override public void execute(ArmorStand as, Player p) {
p.closeInventory();
p.sendMessage(" ");
p.sendMessage("§8§m--------------------------------------");
p.sendMessage("§6§lHologramm-Editor");
p.sendMessage("§7Nutze: §e/nexuscmd name <Text> §7um den Namen zu setzen.");
p.sendMessage("§7Nutze: §e/nexuscmd name none §7um den Namen zu entfernen.");
p.sendMessage("§7Farbcodes mit §6& §7werden unterstützt.");
p.sendMessage("§8§m--------------------------------------");
AST.selectedArmorStand.put(p.getUniqueId(), as);
}
},
// KÖRPERGESTALTUNG (Rotationen) // --- DIESE TEILE WANDERN IN DAS UNTERMENÜ (isForGui = false) ---
HEAD_ROT(Material.PLAYER_HEAD, 28) { HEAD_ROT(Material.PLAYER_HEAD, -1),
@Override public void execute(ArmorStand as, Player p) { BODY_ROT(Material.IRON_CHESTPLATE, -1),
as.setHeadPose(as.getHeadPose().add(Math.toRadians(15), 0, 0)); L_ARM_ROT(Material.STICK, -1),
} R_ARM_ROT(Material.STICK, -1),
}, L_LEG_ROT(Material.LEATHER_BOOTS, -1),
BODY_ROT(Material.IRON_CHESTPLATE, 29) { R_LEG_ROT(Material.LEATHER_BOOTS, -1),
@Override public void execute(ArmorStand as, Player p) {
as.setBodyPose(as.getBodyPose().add(0, Math.toRadians(15), 0));
}
},
L_ARM_ROT(Material.STICK, 30) {
@Override public void execute(ArmorStand as, Player p) {
as.setLeftArmPose(as.getLeftArmPose().add(Math.toRadians(15), 0, 0));
}
},
R_ARM_ROT(Material.STICK, 31) {
@Override public void execute(ArmorStand as, Player p) {
as.setRightArmPose(as.getRightArmPose().add(Math.toRadians(15), 0, 0));
}
},
L_LEG_ROT(Material.LEATHER_BOOTS, 32) {
@Override public void execute(ArmorStand as, Player p) {
as.setLeftLegPose(as.getLeftLegPose().add(Math.toRadians(15), 0, 0));
}
},
R_LEG_ROT(Material.LEATHER_BOOTS, 33) {
@Override public void execute(ArmorStand as, Player p) {
as.setRightLegPose(as.getRightLegPose().add(Math.toRadians(15), 0, 0));
}
},
REMOVE(Material.BARRIER, 40) { REMOVE(Material.BARRIER, 40);
@Override public void execute(ArmorStand as, Player p) {
as.remove();
p.closeInventory();
p.sendMessage("§cNexus ArmorStand entfernt.");
}
};
private final Material material; private final Material material;
private final int slot; private final int slot;
@@ -93,20 +40,40 @@ public enum ArmorStandTool {
this.slot = slot; this.slot = slot;
} }
public abstract void execute(ArmorStand as, Player p); public void execute(ArmorStand as, Player p) {
switch (this) {
case INVIS -> as.setVisible(!as.isVisible());
case ARMS -> as.setArms(!as.hasArms());
case BASE -> as.setBasePlate(!as.hasBasePlate());
case SIZE -> as.setSmall(!as.isSmall());
case GRAV -> as.setGravity(!as.hasGravity());
case INVUL -> as.setInvulnerable(!as.isInvulnerable());
case SET_NAME -> {
p.closeInventory();
p.sendMessage("§6§lHologramm-Editor: §7Nutze §e/nexuscmd name <Text>");
AST.selectedArmorStand.put(p.getUniqueId(), as);
}
case REMOVE -> {
as.remove();
p.closeInventory();
p.sendMessage("§cNexus ArmorStand entfernt.");
}
case OPEN_POSE_EDITOR -> ArmorStandPoseGUI.openPartSelectionMenu(p, as);
default -> {} // Pose-Detailklicks werden direkt im PoseGUI/Listener behandelt
}
}
public int getSlot() { return slot; } public int getSlot() { return slot; }
public boolean isForGui() { return slot != -1; }
// Diese Methode hat im letzten Code gefehlt:
public boolean isForGui() { return true; }
public ItemStack updateLore(ArmorStand as) { public ItemStack updateLore(ArmorStand as) {
ItemStack item = new ItemStack(material); ItemStack item = new ItemStack(material);
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
if (meta != null) { if (meta != null) {
meta.setDisplayName("§6" + this.name().replace("_", " ")); String name = (this == OPEN_POSE_EDITOR) ? "§a§lPose Editor öffnen" : "§6" + this.name().replace("_", " ");
meta.setDisplayName(name);
List<String> lore = new ArrayList<>(); List<String> lore = new ArrayList<>();
lore.add("§7Klicken zum Bearbeiten"); lore.add("§7Klicken zum Verwalten");
meta.setLore(lore); meta.setLore(lore);
item.setItemMeta(meta); item.setItemMeta(meta);
} }
@@ -116,6 +83,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;
try { return valueOf(name); } catch (Exception e) { return null; } try { return valueOf(name); } catch (Exception e) { return null; }
} }
} }

View File

@@ -0,0 +1,132 @@
package de.nexuslobby.modules.armorstandtools;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.World;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.util.EulerAngle;
public class DynamicArmorStandModule implements Module {
private final NamespacedKey npcKey = new NamespacedKey(NexusLobby.getInstance(), "dynamic_npc");
@Override
public String getName() {
return "DynamicArmorStands";
}
@Override
public void onEnable() {
startUpdateTask();
Bukkit.getLogger().info("§a[NexusLobby] DynamicArmorStandModule aktiv: Ingame-Zeit & Sternen-Effekt geladen.");
}
private void startUpdateTask() {
// Alle 5 Ticks (0.25s) für flüssige Partikel
Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), this::updateAllArmorStands, 20L, 5L);
}
public void updateAllArmorStands() {
for (World world : Bukkit.getWorlds()) {
long time = world.getTime();
boolean isIngameNight = (time >= 13000 && time <= 23000);
for (Entity entity : world.getEntities()) {
if (entity instanceof ArmorStand as) {
if (as.getPersistentDataContainer().has(npcKey, PersistentDataType.BYTE) ||
as.getScoreboardTags().contains("as_dynamic")) {
applyDynamicChanges(as, isIngameNight);
if (isIngameNight && as.getEquipment().getItemInOffHand().getType() == Material.TORCH) {
spawnStarParticles(as);
}
}
}
}
}
}
private void applyDynamicChanges(ArmorStand as, boolean isNight) {
if (!as.hasArms()) as.setArms(true);
// --- SPIELER-NÄHE ---
boolean playerNearby = false;
for (Entity nearby : as.getNearbyEntities(2.0, 2.0, 2.0)) {
if (nearby instanceof Player) { playerNearby = true; break; }
}
if (playerNearby) {
if (as.getEquipment().getItemInMainHand().getType() != Material.STONE_SWORD) {
as.getEquipment().setItemInMainHand(new ItemStack(Material.STONE_SWORD));
as.setRightArmPose(new EulerAngle(Math.toRadians(-80), Math.toRadians(-10), 0));
}
} else {
if (as.getEquipment().getItemInMainHand().getType() == Material.STONE_SWORD) {
as.getEquipment().setItemInMainHand(null);
as.setRightArmPose(EulerAngle.ZERO);
}
}
// --- INGAME-ZEIT ---
if (isNight) {
if (as.getEquipment().getItemInOffHand().getType() != Material.TORCH) {
as.getEquipment().setItemInOffHand(new ItemStack(Material.TORCH));
as.setLeftArmPose(new EulerAngle(Math.toRadians(-50), Math.toRadians(15), 0));
}
} else {
if (as.getEquipment().getItemInOffHand().getType() == Material.TORCH) {
as.getEquipment().setItemInOffHand(null);
as.setLeftArmPose(EulerAngle.ZERO);
}
}
}
private void spawnStarParticles(ArmorStand as) {
Location loc = as.getLocation();
double yawRad = Math.toRadians(loc.getYaw());
// Vektoren für Blickrichtung und Seite
double dirX = -Math.sin(yawRad);
double dirZ = Math.cos(yawRad);
double sideX = Math.cos(yawRad);
double sideZ = Math.sin(yawRad);
// Position: Vorne (0.4), Links (0.5), Oben (1.45)
double finalX = loc.getX() + (dirX * 0.8) + (sideX * 0.2);
double finalY = loc.getY() + 1.45;
double finalZ = loc.getZ() + (dirZ * 0.8) + (sideZ * 0.2);
Location particleLoc = new Location(as.getWorld(), finalX, finalY, finalZ);
as.getWorld().spawnParticle(Particle.WAX_OFF, particleLoc, 2, 0.05, 0.05, 0.05, 0.01);
}
public void toggleDynamicStatus(ArmorStand as) {
if (as.getPersistentDataContainer().has(npcKey, PersistentDataType.BYTE)) {
as.getPersistentDataContainer().remove(npcKey);
as.removeScoreboardTag("as_dynamic");
as.getEquipment().setItemInMainHand(null);
as.getEquipment().setItemInOffHand(null);
as.setRightArmPose(EulerAngle.ZERO);
as.setLeftArmPose(EulerAngle.ZERO);
} else {
as.getPersistentDataContainer().set(npcKey, PersistentDataType.BYTE, (byte) 1);
as.addScoreboardTag("as_dynamic");
applyDynamicChanges(as, (as.getWorld().getTime() >= 13000 && as.getWorld().getTime() <= 23000));
}
}
@Override
public void onDisable() {
// Bereinigung falls nötig
}
}

View File

@@ -0,0 +1,86 @@
package de.nexuslobby.commands;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.modules.portal.PortalCommand;
import de.nexuslobby.modules.border.BorderModule;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class BorderCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player p)) return true;
if (!p.hasPermission("nexuslobby.admin")) {
p.sendMessage("§8[§6Nexus§8] §cKeine Rechte.");
return true;
}
if (args.length == 0) {
p.sendMessage("§8§m------------------------------------");
p.sendMessage("§6§lNexus WorldBorder Setup");
p.sendMessage("§e/border circle <Radius> §7- Kreis um dich herum");
p.sendMessage("§e/border square §7- Nutzt Axt-Markierung (Viereck)");
p.sendMessage("§e/border disable §7- Grenze deaktivieren");
p.sendMessage("§8§m------------------------------------");
return true;
}
var config = NexusLobby.getInstance().getConfig();
switch (args[0].toLowerCase()) {
case "circle" -> {
if (args.length < 2) {
p.sendMessage("§cBitte gib einen Radius an.");
return true;
}
try {
double radius = Double.parseDouble(args[1]);
config.set("worldborder.type", "CIRCLE");
config.set("worldborder.center", p.getLocation());
config.set("worldborder.radius", radius);
config.set("worldborder.enabled", true);
p.sendMessage("§8[§6Nexus§8] §aKreis-Grenze (Radius: " + radius + ") gesetzt.");
} catch (NumberFormatException e) {
p.sendMessage("§cUngültige Zahl.");
return true;
}
}
case "square" -> {
Location l1 = PortalCommand.getSelection1(p);
Location l2 = PortalCommand.getSelection2(p);
if (l1 == null || l2 == null) {
p.sendMessage("§8[§6Nexus§8] §cBitte markiere erst 2 Punkte mit der Portalwand!");
return true;
}
config.set("worldborder.type", "SQUARE");
config.set("worldborder.pos1", l1);
config.set("worldborder.pos2", l2);
config.set("worldborder.enabled", true);
p.sendMessage("§8[§6Nexus§8] §aViereckige Grenze erfolgreich gesetzt.");
}
case "disable" -> {
config.set("worldborder.enabled", false);
p.sendMessage("§8[§6Nexus§8] §cGrenze wurde deaktiviert.");
}
default -> {
p.sendMessage("§cUnbekannter Unterbefehl.");
return true;
}
}
NexusLobby.getInstance().saveConfig();
// Update das Modul direkt im RAM
BorderModule module = NexusLobby.getInstance().getModuleManager().getModule(BorderModule.class);
if (module != null) {
module.reloadConfig();
}
return true;
}
}

View File

@@ -0,0 +1,132 @@
package de.nexuslobby.modules.border;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
/**
* Verwaltet die Lobby-Begrenzung (Kreis oder Rechteck).
* Speichert Daten in der Haupt-config.yml unter 'worldborder'.
*/
public class BorderModule implements Module, Listener {
private String type;
private Location pos1, pos2, center;
private double radius;
private boolean enabled;
@Override
public String getName() { return "WorldBorder"; }
@Override
public void onEnable() {
reloadConfig();
Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance());
}
@Override
public void onDisable() {
// Aufräumarbeiten falls nötig
}
/**
* Lädt die Border-Einstellungen aus der config.yml.
* Wird auch von NexusLobby.reloadPlugin() aufgerufen.
*/
public void reloadConfig() {
FileConfiguration config = NexusLobby.getInstance().getConfig();
// Pfad in der config.yml: worldborder.enabled etc.
this.enabled = config.getBoolean("worldborder.enabled", false);
this.type = config.getString("worldborder.type", "CIRCLE");
this.radius = config.getDouble("worldborder.radius", 50.0);
// Locations laden
this.center = config.getLocation("worldborder.center");
this.pos1 = config.getLocation("worldborder.pos1");
this.pos2 = config.getLocation("worldborder.pos2");
}
@EventHandler
public void onMove(PlayerMoveEvent event) {
if (!enabled || event.getTo() == null) return;
// Performance: Nur prüfen, wenn sich die Block-Koordinaten ändern
if (event.getFrom().getBlockX() == event.getTo().getBlockX() &&
event.getFrom().getBlockZ() == event.getTo().getBlockZ() &&
event.getFrom().getBlockY() == event.getTo().getBlockY()) return;
Player player = event.getPlayer();
// Admins und Spectators ignorieren
if (player.hasPermission("nexuslobby.admin") || player.getGameMode().name().equals("SPECTATOR")) return;
Location to = event.getTo();
boolean outside = false;
// --- Prüfung: Kreis-Border ---
if (type.equalsIgnoreCase("CIRCLE") && center != null) {
if (to.getWorld().equals(center.getWorld())) {
double distSq = Math.pow(to.getX() - center.getX(), 2) + Math.pow(to.getZ() - center.getZ(), 2);
if (distSq > Math.pow(radius, 2)) outside = true;
}
}
// --- Prüfung: Rechteck-Border (Square) ---
else if (type.equalsIgnoreCase("SQUARE") && pos1 != null && pos2 != null) {
if (to.getWorld().equals(pos1.getWorld())) {
double minX = Math.min(pos1.getX(), pos2.getX());
double maxX = Math.max(pos1.getX(), pos2.getX());
double minZ = Math.min(pos1.getZ(), pos2.getZ());
double maxZ = Math.max(pos1.getZ(), pos2.getZ());
if (to.getX() < minX || to.getX() > maxX || to.getZ() < minZ || to.getZ() > maxZ) {
outside = true;
}
}
}
// --- Aktion wenn außerhalb ---
if (outside) {
Location spawnLocation = getMainSpawnLocation();
if (spawnLocation != null) {
// Sofortiger Teleport zum definierten Spawn
player.teleport(spawnLocation);
// Feedback an den Spieler
player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1.0f, 0.5f);
player.sendMessage("§8[§6Nexus§8] §cDu hast den Lobby-Bereich verlassen!");
}
}
}
/**
* Holt den zentralen Spawnpunkt aus der Config (Pfad: spawn.world, spawn.x, etc.)
*/
private Location getMainSpawnLocation() {
FileConfiguration config = NexusLobby.getInstance().getConfig();
String worldName = config.getString("spawn.world");
if (worldName != null) {
World w = Bukkit.getWorld(worldName);
if (w != null) {
return new Location(w,
config.getDouble("spawn.x"),
config.getDouble("spawn.y"),
config.getDouble("spawn.z"),
(float) config.getDouble("spawn.yaw"),
(float) config.getDouble("spawn.pitch"));
}
}
// Fallback falls kein Spawn gesetzt ist
return Bukkit.getWorlds().isEmpty() ? null : Bukkit.getWorlds().get(0).getSpawnLocation();
}
}

View File

@@ -0,0 +1,77 @@
package de.nexuslobby.modules.gadgets;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.util.UUID;
public class Balloon {
private final UUID playerUUID;
private final LivingEntity balloonEntity;
private final ArmorStand headStand;
public Balloon(Player player, Material balloonMaterial) {
this.playerUUID = player.getUniqueId();
Location loc = player.getLocation().add(0, 2, 0);
// Das unsichtbare Träger-Entity
this.balloonEntity = (LivingEntity) loc.getWorld().spawnEntity(loc, EntityType.PIG);
this.balloonEntity.setInvisible(true);
this.balloonEntity.setSilent(true);
this.balloonEntity.setInvulnerable(true);
this.balloonEntity.setGravity(false);
this.balloonEntity.setLeashHolder(player);
// Der ArmorStand, der den farbigen Block trägt
this.headStand = (ArmorStand) loc.getWorld().spawnEntity(loc, EntityType.ARMOR_STAND);
this.headStand.setVisible(false);
this.headStand.setGravity(false);
this.headStand.setMarker(true);
// Hier wird das übergebene Material gesetzt (z.B. RED_WOOL, BLUE_WOOL etc.)
this.headStand.setHelmet(new ItemStack(balloonMaterial));
}
public void update() {
Player player = org.bukkit.Bukkit.getPlayer(playerUUID);
if (player == null || !player.isOnline() || balloonEntity == null || !balloonEntity.isValid()) {
remove();
return;
}
if (!balloonEntity.isLeashed()) {
remove();
return;
}
Location targetLoc = player.getLocation().clone().add(0, 2.5, 0);
Vector direction = targetLoc.toVector().subtract(balloonEntity.getLocation().toVector());
double distance = direction.length();
if (distance > 5) {
balloonEntity.teleport(targetLoc);
} else if (distance > 0.1) {
balloonEntity.setVelocity(direction.multiply(0.4));
}
headStand.teleport(balloonEntity.getLocation().clone().subtract(0, 1.5, 0));
}
public void remove() {
if (balloonEntity != null) {
balloonEntity.setLeashHolder(null);
balloonEntity.remove();
}
if (headStand != null) {
headStand.remove();
}
}
}

View File

@@ -0,0 +1,54 @@
package de.nexuslobby.modules.gadgets;
import de.nexuslobby.NexusLobby;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.entity.Chicken;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Random;
public class ChickenRain {
public static void start(Player player) {
new BukkitRunnable() {
int ticks = 0;
final Random random = new Random();
@Override
public void run() {
if (!player.isOnline() || ticks > 100) { // 100 Ticks = 5 Sekunden
this.cancel();
return;
}
// Alle 2 Ticks ein Huhn spawnen
if (ticks % 2 == 0) {
Location spawnLoc = player.getLocation().add(
(random.nextDouble() - 0.5) * 4,
4.0,
(random.nextDouble() - 0.5) * 4
);
Chicken chicken = (Chicken) spawnLoc.getWorld().spawnEntity(spawnLoc, EntityType.CHICKEN);
chicken.setBaby();
chicken.setInvulnerable(true);
// Nach 1.5 Sekunden "ploppt" das Huhn
Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), () -> {
if (chicken.isValid()) {
chicken.getWorld().spawnParticle(Particle.CLOUD, chicken.getLocation(), 5, 0.2, 0.2, 0.2, 0.1);
chicken.getWorld().playSound(chicken.getLocation(), Sound.ENTITY_CHICKEN_EGG, 1.0f, 1.5f);
chicken.remove();
}
}, 30L);
}
ticks++;
}
}.runTaskTimer(NexusLobby.getInstance(), 0L, 1L);
}
}

View File

@@ -0,0 +1,297 @@
package de.nexuslobby.modules.gadgets;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
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.player.PlayerFishEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class GadgetModule implements Module, Listener {
private final Map<UUID, Balloon> activeBalloons = new HashMap<>();
private final Map<UUID, ParticleEffect> activeEffects = new HashMap<>();
private final Set<UUID> activeShields = new HashSet<>();
private final String MAIN_TITLE = "§b§lGadgets §8- §7Menü";
private final String BALLOON_TITLE = "§b§lGadgets §8- §eBallons";
private final String PARTICLE_TITLE = "§b§lGadgets §8- §dPartikel";
private final String FUN_TITLE = "§b§lGadgets §8- §6Lustiges";
private final String HAT_TITLE = "§b§lGadgets §8- §aHüte & Köpfe";
private final String PET_TITLE = "§b§lGadgets §8- §dBegleiter";
@Override
public String getName() { return "Gadgets"; }
@Override
public void onEnable() {
Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance());
Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), () -> {
PetManager.updatePets();
activeBalloons.values().forEach(Balloon::update);
for (Player p : Bukkit.getOnlinePlayers()) {
UUID uuid = p.getUniqueId();
handleSpecialHatEffects(p);
if (activeEffects.containsKey(uuid)) activeEffects.get(uuid).update(p);
if (activeShields.contains(uuid)) ShieldTask.handleShield(p);
}
}, 1L, 1L);
}
private void handleSpecialHatEffects(Player p) {
ItemStack hat = p.getInventory().getHelmet();
if (hat == null || hat.getType() == Material.AIR) return;
switch (hat.getType()) {
case CAMPFIRE:
p.getWorld().spawnParticle(Particle.CAMPFIRE_COSY_SMOKE, p.getLocation().add(0, 2.2, 0), 1, 0.05, 0.05, 0.05, 0.02);
break;
case SPAWNER:
p.getWorld().spawnParticle(Particle.FLAME, p.getLocation().add(0, 2.1, 0), 1, 0.12, 0.12, 0.12, 0.02);
break;
case SEA_LANTERN:
case BEACON:
p.getWorld().spawnParticle(Particle.END_ROD, p.getLocation().add(0, 2.1, 0), 1, 0.1, 0.1, 0.1, 0.03);
break;
case ENCHANTING_TABLE:
p.getWorld().spawnParticle(Particle.ENCHANT, p.getLocation().add(0, 2.3, 0), 1, 0.2, 0.2, 0.2, 0.5);
break;
}
}
public void openGUI(Player player) {
Inventory gui = Bukkit.createInventory(null, 27, MAIN_TITLE);
fillEdges(gui);
gui.setItem(10, createItem(Material.LEAD, "§e§lBallons", "§7Wähle einen fliegenden Begleiter"));
gui.setItem(11, createItem(Material.GOLDEN_HELMET, "§a§lHüte", "§7Setze dir etwas auf den Kopf"));
gui.setItem(13, createItem(Material.BONE, "§d§lBegleiter", "§7Echte Tiere, die dir folgen"));
gui.setItem(15, createItem(Material.FIREWORK_ROCKET, "§6§lLustiges", "§7Witzige Effekte"));
gui.setItem(16, createItem(Material.NETHER_STAR, "§d§lPartikel", "§7Magische Auren & Effekte"));
gui.setItem(22, createItem(Material.BARRIER, "§c§lStopp", "§7Alle Gadgets entfernen"));
player.openInventory(gui);
}
private void openHatGUI(Player player) {
Inventory gui = Bukkit.createInventory(null, 45, HAT_TITLE);
fillEdges(gui);
gui.setItem(10, createItem(Material.JACK_O_LANTERN, "§6Kürbis-Hut", "§7Es ist immer Halloween!"));
gui.setItem(11, createItem(Material.SEA_LANTERN, "§bMeeres-Leuchten", "§7§oEffekt: Glitzern"));
gui.setItem(12, createItem(Material.GLOWSTONE, "§eGlowstone-Kopf", "§7Werde zur Lampe"));
gui.setItem(13, createItem(Material.TNT, "§cExplosiv-Hut", "§7Vorsicht, heiß!"));
gui.setItem(14, createItem(Material.GLASS, "§fAstronaut", "§7Bereit für den Mond?"));
gui.setItem(15, createItem(Material.DRAGON_HEAD, "§5Enderdrache", "§7Der König der Lüfte"));
gui.setItem(16, createItem(Material.CAKE, "§dKuchen-Kopf", "§7Jeder mag Kuchen!"));
gui.setItem(19, createItem(Material.SLIME_BLOCK, "§aGlibber-Block", "§7Ziemlich klebrig..."));
gui.setItem(20, createItem(Material.MELON, "§aMelonen-Helm", "§7Frisch und saftig"));
gui.setItem(21, createItem(Material.HAY_BLOCK, "§eStrohhut", "§7Sommer auf dem Land"));
gui.setItem(22, createItem(Material.SPAWNER, "§8Monster-Käfig", "§7§oEffekt: Flammen"));
gui.setItem(23, createItem(Material.CRAFTING_TABLE, "§6Werkbank", "§7Immer am Basteln"));
gui.setItem(24, createItem(Material.BOOKSHELF, "§fBücherregal", "§7Ein wahrer Schlaukopf"));
gui.setItem(25, createItem(Material.HONEY_BLOCK, "§6Honig-Hut", "§7Süß und klebrig"));
gui.setItem(28, createItem(Material.GOLD_BLOCK, "§6Gold-Bonze", "§7Zeig was du hast"));
gui.setItem(29, createItem(Material.DIAMOND_ORE, "§bDiamant-Erz", "§7Bau mich bloß nicht ab!"));
gui.setItem(30, createItem(Material.BEACON, "§fLeuchtfeuer", "§7§oEffekt: Glitzern"));
gui.setItem(31, createItem(Material.CONDUIT, "§3Auge des Meeres", "§7Die Macht von Atlantis"));
gui.setItem(32, createItem(Material.ENCHANTING_TABLE, "§dMagier", "§7§oEffekt: Runen"));
gui.setItem(33, createItem(Material.CAMPFIRE, "§cHeißer Kopf", "§7§oEffekt: Rauch"));
gui.setItem(34, createItem(Material.SKELETON_SKULL, "§7Skelett", "§7Ein wenig gruselig"));
gui.setItem(40, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü"));
player.openInventory(gui);
}
private void openPetGUI(Player player) {
Inventory gui = Bukkit.createInventory(null, 27, PET_TITLE);
fillEdges(gui);
gui.setItem(11, createItem(Material.BONE, "§fWolf", "§7Ein treuer Begleiter"));
gui.setItem(13, createItem(Material.CAT_SPAWN_EGG, "§6Katze", "§7Ein verschmuster Freund"));
gui.setItem(15, createItem(Material.PANDA_SPAWN_EGG, "§aPanda", "§7Ein gemütlicher Zeitgenosse"));
gui.setItem(22, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü"));
player.openInventory(gui);
}
private void openBalloonGUI(Player player) {
Inventory gui = Bukkit.createInventory(null, 36, BALLOON_TITLE);
fillEdges(gui);
Material[] wools = {Material.WHITE_WOOL, Material.ORANGE_WOOL, Material.MAGENTA_WOOL, Material.LIGHT_BLUE_WOOL,
Material.YELLOW_WOOL, Material.LIME_WOOL, Material.PINK_WOOL, Material.GRAY_WOOL,
Material.CYAN_WOOL, Material.PURPLE_WOOL, Material.BLUE_WOOL, Material.BROWN_WOOL,
Material.GREEN_WOOL, Material.RED_WOOL};
int slot = 10;
for (Material m : wools) {
if (slot == 17) slot = 19;
gui.setItem(slot++, createItem(m, "§fBallon: " + m.name().replace("_WOOL", ""), "§7Klicke zum Ausrüsten"));
}
gui.setItem(31, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü"));
player.openInventory(gui);
}
private void openParticleGUI(Player player) {
Inventory gui = Bukkit.createInventory(null, 27, PARTICLE_TITLE);
fillEdges(gui);
gui.setItem(11, createItem(Material.POPPY, "§cHerzchen-Aura", "§7Verbreite Liebe in der Lobby"));
gui.setItem(13, createItem(Material.BLAZE_POWDER, "§6Flammen-Ring", "§7Lass es brennen!"));
gui.setItem(15, createItem(Material.WATER_BUCKET, "§bRegenwolke", "§7Deine persönliche Abkühlung"));
gui.setItem(22, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü"));
player.openInventory(gui);
}
private void openFunGUI(Player player) {
Inventory gui = Bukkit.createInventory(null, 27, FUN_TITLE);
fillEdges(gui);
gui.setItem(11, createItem(Material.FISHING_ROD, "§b§lEnterhaken", "§7Zieh dich durch die Luft!"));
gui.setItem(13, createItem(Material.SHIELD, "§5§lSchutzzone", "§7Halte andere auf Distanz"));
gui.setItem(15, createItem(Material.EGG, "§f§lChicken-Rain", "§7Gack-Gack! Hühner überall!"));
gui.setItem(22, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü"));
player.openInventory(gui);
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
String title = event.getView().getTitle();
if (!title.startsWith("§b§lGadgets")) return;
event.setCancelled(true);
Player player = (Player) event.getWhoClicked();
ItemStack item = event.getCurrentItem();
if (item == null || item.getType() == Material.AIR) return;
if (item.getType() == Material.ARROW) { openGUI(player); return; }
if (title.equals(MAIN_TITLE)) {
if (item.getType() == Material.LEAD) openBalloonGUI(player);
else if (item.getType() == Material.GOLDEN_HELMET) openHatGUI(player);
else if (item.getType() == Material.BONE) openPetGUI(player);
else if (item.getType() == Material.NETHER_STAR) openParticleGUI(player);
else if (item.getType() == Material.FIREWORK_ROCKET) openFunGUI(player);
else if (item.getType() == Material.BARRIER) { removeGadgets(player); player.closeInventory(); }
}
else if (title.equals(HAT_TITLE)) {
if (item.getType() != Material.GRAY_STAINED_GLASS_PANE) {
HatManager.setHat(player, item.getType(), item.getItemMeta().getDisplayName());
player.playSound(player.getLocation(), Sound.ITEM_ARMOR_EQUIP_GENERIC, 1, 1);
player.closeInventory();
}
}
else if (title.equals(PET_TITLE)) {
if (item.getType() == Material.BONE) PetManager.spawnEntityPet(player, "WOLF");
else if (item.getType() == Material.CAT_SPAWN_EGG) PetManager.spawnEntityPet(player, "CAT");
else if (item.getType() == Material.PANDA_SPAWN_EGG) PetManager.spawnEntityPet(player, "PANDA");
player.sendMessage("§8[§6Nexus§8] §dDein Pet wurde gerufen!");
player.closeInventory();
}
else if (title.equals(BALLOON_TITLE)) {
if (item.getType().toString().endsWith("_WOOL")) {
if (activeBalloons.containsKey(player.getUniqueId())) activeBalloons.get(player.getUniqueId()).remove();
activeBalloons.put(player.getUniqueId(), new Balloon(player, item.getType()));
player.sendMessage("§8[§6Nexus§8] §aBallon aktiviert!");
player.closeInventory();
}
}
else if (title.equals(PARTICLE_TITLE)) {
if (item.getType() == Material.POPPY) activeEffects.put(player.getUniqueId(), new ParticleEffect("hearts"));
else if (item.getType() == Material.BLAZE_POWDER) activeEffects.put(player.getUniqueId(), new ParticleEffect("flames"));
else if (item.getType() == Material.WATER_BUCKET) activeEffects.put(player.getUniqueId(), new ParticleEffect("cloud"));
player.sendMessage("§8[§6Nexus§8] §aPartikel aktiviert!");
player.closeInventory();
}
else if (title.equals(FUN_TITLE)) {
if (item.getType() == Material.EGG) {
ChickenRain.start(player);
player.sendMessage("§8[§6Nexus§8] §fHühnerregen gestartet!");
player.closeInventory();
} else if (item.getType() == Material.FISHING_ROD) {
player.getInventory().addItem(createItem(Material.FISHING_ROD, "§b§lEnterhaken", "§7Rechtsklick zum Katapultieren"));
player.closeInventory();
} else if (item.getType() == Material.SHIELD) {
if (activeShields.contains(player.getUniqueId())) {
activeShields.remove(player.getUniqueId());
player.sendMessage("§8[§6Nexus§8] §cSchutzzone deaktiviert.");
} else {
activeShields.add(player.getUniqueId());
player.sendMessage("§8[§6Nexus§8] §5Schutzzone aktiviert!");
}
player.closeInventory();
}
}
}
@EventHandler
public void onFish(PlayerFishEvent event) {
Player player = event.getPlayer();
ItemStack item = player.getInventory().getItemInMainHand();
if (item != null && item.getType() == Material.FISHING_ROD && item.hasItemMeta() && item.getItemMeta().getDisplayName().equals("§b§lEnterhaken")) {
if (event.getState() == PlayerFishEvent.State.IN_GROUND || event.getState() == PlayerFishEvent.State.REEL_IN || event.getState() == PlayerFishEvent.State.CAUGHT_ENTITY) {
if (event.getHook() != null) {
GrapplingHook.pullPlayer(player, event.getHook().getLocation());
}
}
}
}
private void removeGadgets(Player player) {
if (activeBalloons.containsKey(player.getUniqueId())) {
activeBalloons.get(player.getUniqueId()).remove();
activeBalloons.remove(player.getUniqueId());
}
activeEffects.remove(player.getUniqueId());
activeShields.remove(player.getUniqueId());
PetManager.removePet(player);
HatManager.removeHat(player);
player.getInventory().remove(Material.FISHING_ROD);
player.sendMessage("§8[§6Nexus§8] §cAlle Gadgets abgelegt.");
}
private void fillEdges(Inventory inv) {
ItemStack glass = createItem(Material.GRAY_STAINED_GLASS_PANE, " ", null);
for (int i = 0; i < inv.getSize(); i++) {
if (i < 9 || i >= inv.getSize() - 9 || i % 9 == 0 || (i + 1) % 9 == 0) inv.setItem(i, glass);
}
}
private ItemStack createItem(Material mat, String name, String lore) {
ItemStack item = new ItemStack(mat);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.setDisplayName(name);
if (lore != null) {
List<String> l = new ArrayList<>();
l.add(lore);
meta.setLore(l);
}
item.setItemMeta(meta);
}
return item;
}
@Override
public void onDisable() {
PetManager.clearAll();
activeBalloons.values().forEach(Balloon::remove);
activeBalloons.clear();
activeEffects.clear();
activeShields.clear();
}
}

View File

@@ -0,0 +1,37 @@
package de.nexuslobby.modules.gadgets;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public class GrapplingHook {
public static void pullPlayer(Player player, Location target) {
Location playerLoc = player.getLocation();
// Vektor vom Spieler zum Ziel berechnen
double distance = target.distance(playerLoc);
// Wenn das Ziel zu nah oder zu weit weg ist, nichts tun
if (distance < 2 || distance > 50) return;
// Berechnung des Wurfs (Vektor)
Vector v = target.toVector().subtract(playerLoc.toVector());
// Den Vektor normalisieren und skalieren (Stärke des Zugs)
v.multiply(0.3); // Basis-Geschwindigkeit
v.setY(v.getY() * 0.6 + 0.5); // Etwas mehr Höhe für den Bogen-Effekt
// Geschwindigkeit begrenzen, damit man nicht aus der Map schießt
if (v.length() > 2.5) {
v.normalize().multiply(2.5);
}
player.setVelocity(v);
// Sound-Effekt für das "Ziehen"
player.playSound(playerLoc, Sound.ENTITY_WIND_CHARGE_WIND_BURST, 1.0f, 1.2f);
player.playSound(playerLoc, Sound.ITEM_TRIDENT_RIPTIDE_1, 0.5f, 1.5f);
}
}

View File

@@ -0,0 +1,26 @@
package de.nexuslobby.modules.gadgets;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class HatManager {
public static void setHat(Player player, Material material, String name) {
ItemStack hat = new ItemStack(material);
ItemMeta meta = hat.getItemMeta();
if (meta != null) {
meta.setDisplayName("§6Hut: " + name);
hat.setItemMeta(meta);
}
// Den Gegenstand auf den Kopf setzen
player.getInventory().setHelmet(hat);
player.sendMessage("§8[§6Nexus§8] §aDu trägst nun: " + name);
}
public static void removeHat(Player player) {
player.getInventory().setHelmet(null);
}
}

View File

@@ -0,0 +1,31 @@
package de.nexuslobby.modules.gadgets;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
public class ParticleEffect {
private final String type;
private double angle = 0;
public ParticleEffect(String type) { this.type = type; }
public void update(Player player) {
Location loc = player.getLocation();
switch (type.toLowerCase()) {
case "hearts":
player.getWorld().spawnParticle(Particle.HEART, loc.clone().add(0, 2.2, 0), 1, 0.3, 0.3, 0.3, 0);
break;
case "flames":
double x = 0.6 * Math.cos(angle);
double z = 0.6 * Math.sin(angle);
player.getWorld().spawnParticle(Particle.FLAME, loc.clone().add(x, 0.1, z), 1, 0, 0, 0, 0);
angle += 0.2;
break;
case "cloud":
player.getWorld().spawnParticle(Particle.CLOUD, loc.clone().add(0, 2.5, 0), 2, 0.2, 0.05, 0.2, 0);
player.getWorld().spawnParticle(Particle.FALLING_WATER, loc.clone().add(0, 2.4, 0), 1, 0.1, 0, 0.1, 0);
break;
}
}
}

View File

@@ -0,0 +1,138 @@
package de.nexuslobby.modules.gadgets;
import de.nexuslobby.NexusLobby;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Tameable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityTargetEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class PetManager implements Listener {
private static final Map<UUID, Entity> activePets = new HashMap<>();
public PetManager() {
Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance());
}
/**
* Spawnt ein echtes Tier-Entity für den Spieler.
*/
public static void spawnEntityPet(Player player, String type) {
removePet(player);
EntityType entityType;
try {
entityType = EntityType.valueOf(type);
} catch (IllegalArgumentException e) {
player.sendMessage("§cFehler: Tier-Typ nicht gefunden.");
return;
}
Location loc = player.getLocation();
Entity pet = player.getWorld().spawnEntity(loc, entityType);
pet.setCustomName("§d" + player.getName() + "'s " + capitalize(type.toLowerCase()));
pet.setCustomNameVisible(true);
pet.setInvulnerable(true);
pet.setPersistent(false);
if (pet instanceof LivingEntity) {
LivingEntity le = (LivingEntity) pet;
le.setRemoveWhenFarAway(false);
// Verhindert, dass das Pet andere angreift
if (le instanceof Tameable) {
((Tameable) le).setTamed(true);
((Tameable) le).setOwner(player);
}
}
activePets.put(player.getUniqueId(), pet);
}
/**
* Steuert das Folgen der Tiere. Wird vom GadgetModule-Timer aufgerufen.
*/
public static void updatePets() {
for (Map.Entry<UUID, Entity> entry : activePets.entrySet()) {
Player owner = Bukkit.getPlayer(entry.getKey());
Entity pet = entry.getValue();
if (owner == null || !owner.isOnline() || pet.isDead()) {
continue;
}
// Wenn das Pet in einer anderen Welt ist oder zu weit weg, teleportiere es
if (!pet.getWorld().equals(owner.getWorld()) || pet.getLocation().distance(owner.getLocation()) > 10) {
pet.teleport(owner.getLocation());
continue;
}
// Sanftes Folgen: Wenn das Pet weiter als 3 Blöcke weg ist
if (pet.getLocation().distance(owner.getLocation()) > 3) {
Location target = owner.getLocation().clone().add(owner.getLocation().getDirection().multiply(-1.5));
target.setY(owner.getLocation().getY());
// Teleportiert das Pet leicht zum Ziel (simuliert Laufen)
pet.teleport(pet.getLocation().add(target.toVector().subtract(pet.getLocation().toVector()).normalize().multiply(0.2)));
// Blickrichtung anpassen
Location lookAt = pet.getLocation();
lookAt.setDirection(owner.getLocation().toVector().subtract(pet.getLocation().toVector()));
pet.teleport(lookAt);
}
}
}
public static void removePet(Player player) {
if (activePets.containsKey(player.getUniqueId())) {
activePets.get(player.getUniqueId()).remove();
activePets.remove(player.getUniqueId());
}
}
public static void clearAll() {
for (Entity pet : activePets.values()) {
pet.remove();
}
activePets.clear();
}
private static String capitalize(String str) {
if (str == null || str.isEmpty()) return str;
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
// --- Events um die Pets zu schützen ---
@EventHandler
public void onPetDamage(EntityDamageEvent event) {
if (activePets.containsValue(event.getEntity())) {
event.setCancelled(true);
}
}
@EventHandler
public void onPetTarget(EntityTargetEvent event) {
if (activePets.containsValue(event.getEntity())) {
event.setCancelled(true);
}
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
removePet(event.getPlayer());
}
}

View File

@@ -0,0 +1,32 @@
package de.nexuslobby.modules.gadgets;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public class ShieldTask {
public static void handleShield(Player owner) {
// Erzeuge einen Partikel-Ring um den Spieler
for (double i = 0; i < Math.PI * 2; i += Math.PI / 8) {
double x = Math.cos(i) * 2.2;
double z = Math.sin(i) * 2.2;
owner.getWorld().spawnParticle(Particle.WITCH, owner.getLocation().add(x, 0.5, z), 1, 0, 0, 0, 0);
}
// Stoße andere Spieler weg
for (Entity entity : owner.getNearbyEntities(2.2, 2.0, 2.2)) {
if (entity instanceof Player && entity != owner) {
Player target = (Player) entity;
Vector direction = target.getLocation().toVector().subtract(owner.getLocation().toVector()).normalize();
direction.multiply(0.4).setY(0.2);
target.setVelocity(direction);
target.playSound(target.getLocation(), Sound.ENTITY_CHICKEN_EGG, 0.5f, 0.5f);
}
}
}
}

View File

@@ -0,0 +1,85 @@
package de.nexuslobby.commands;
import de.nexuslobby.modules.hologram.HologramModule;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class HoloCommand implements CommandExecutor {
private final HologramModule module;
public HoloCommand(HologramModule module) {
this.module = module;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) return true;
if (!player.hasPermission("nexuslobby.hologram")) {
player.sendMessage("§8[§6Nexus§8] §cKeine Rechte.");
return true;
}
if (args.length < 1) {
sendHelp(player);
return true;
}
if (args[0].equalsIgnoreCase("create")) {
// Wir brauchen id, typ und mindestens ein Wort Text
if (args.length < 4) {
player.sendMessage("§cNutze: /holo create <id> <NONE|FAST|SLOW> <text>");
return true;
}
String id = args[1];
// Der Typ (args[2]) wird im neuen System ignoriert, da wir Seiten nutzen
StringBuilder sb = new StringBuilder();
for (int i = 3; i < args.length; i++) {
sb.append(args[i]).append(i == args.length - 1 ? "" : " ");
}
String fullText = sb.toString().trim();
List<String> pages = new ArrayList<>();
// Support für mehrere Seiten via ";"
if (fullText.contains(";")) {
pages.addAll(Arrays.asList(fullText.split(";")));
} else {
pages.add(fullText);
}
// Aufruf der neuen Methode mit List<String>
module.createHologram(id, player.getLocation(), pages);
player.sendMessage("§8[§6Nexus§8] §aHologramm §e" + id + " §aerstellt (" + pages.size() + " Seiten).");
}
else if (args[0].equalsIgnoreCase("delete")) {
if (args.length < 2) {
player.sendMessage("§cBitte gib eine ID an: /holo delete <id>");
return true;
}
module.removeHologram(args[1]);
player.sendMessage("§8[§6Nexus§8] §cHologramm §e" + args[1] + " §agelöscht.");
} else {
sendHelp(player);
}
return true;
}
private void sendHelp(Player player) {
player.sendMessage("§8§m-----------§r §6Hologramme §8§m-----------");
player.sendMessage("§e/holo create <id> <NONE|FAST|SLOW> <text>");
player.sendMessage("§e/holo delete <id>");
player.sendMessage("§7Nutze §b; §7für neue Seiten.");
player.sendMessage("§7Nutze §b\\n §7für Zeilenumbruch.");
}
}

View File

@@ -0,0 +1,178 @@
package de.nexuslobby.modules.hologram;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Interaction;
import org.bukkit.entity.Player;
import org.bukkit.entity.TextDisplay;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class HologramModule implements Module, Listener {
private final Map<String, NexusHologram> holograms = new ConcurrentHashMap<>();
private File file;
private FileConfiguration config;
@Override
public String getName() { return "Holograms"; }
@Override
public void onEnable() {
loadConfig();
loadHolograms();
Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance());
// Render-Task: Prüft alle 5 Ticks Sichtbarkeit und Placeholder-Updates
Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), () -> {
for (Player player : Bukkit.getOnlinePlayers()) {
holograms.values().forEach(h -> h.renderForPlayer(player));
}
}, 20L, 5L);
}
private void loadConfig() {
file = new File(NexusLobby.getInstance().getDataFolder(), "holograms.yml");
config = YamlConfiguration.loadConfiguration(file);
}
private void loadHolograms() {
// Vorherige Instanzen säubern
holograms.values().forEach(NexusHologram::removeAll);
holograms.clear();
for (String id : config.getKeys(false)) {
String worldName = config.getString(id + ".world");
if (worldName == null) continue;
World world = Bukkit.getWorld(worldName);
if (world == null) continue;
Location loc = new Location(world,
config.getDouble(id + ".x"),
config.getDouble(id + ".y"),
config.getDouble(id + ".z"));
List<String> pages;
if (config.isList(id + ".text")) {
pages = config.getStringList(id + ".text");
} else {
pages = new ArrayList<>();
pages.add(config.getString(id + ".text", "No Text"));
}
holograms.put(id, new NexusHologram(id, loc, pages));
}
}
@EventHandler
public void onInteract(PlayerInteractEntityEvent event) {
// Nur auf Interaction-Entities reagieren (Hologramm-Hitboxen)
if (!(event.getRightClicked() instanceof Interaction)) return;
for (NexusHologram holo : holograms.values()) {
if (holo.isInteractionEntity(event.getRightClicked().getUniqueId())) {
holo.nextPage(event.getPlayer());
break;
}
}
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
holograms.values().forEach(h -> h.removeForPlayer(event.getPlayer()));
}
@EventHandler
public void onWorldChange(PlayerChangedWorldEvent event) {
holograms.values().forEach(h -> h.removeForPlayer(event.getPlayer()));
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
// Cleanup alter Entity-Reste, die eventuell noch in der Welt schweben
Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), () -> {
Player p = event.getPlayer();
for (Entity entity : p.getWorld().getEntities()) {
if (entity.getCustomName() != null && entity.getCustomName().startsWith("nexus_h_")) {
// Wenn das Hologramm nicht exakt für diesen Spieler benannt ist -> verstecken
if (!entity.getCustomName().endsWith("_" + p.getName())) {
p.hideEntity(NexusLobby.getInstance(), entity);
}
}
}
}, 10L);
}
public void createHologram(String id, Location loc, List<String> pages) {
// Falls ID bereits existiert, altes Hologramm sauber entfernen
if (holograms.containsKey(id)) {
removeHologram(id);
}
config.set(id + ".world", loc.getWorld().getName());
config.set(id + ".x", loc.getX());
config.set(id + ".y", loc.getY());
config.set(id + ".z", loc.getZ());
config.set(id + ".text", pages);
saveHoloConfig();
NexusHologram holo = new NexusHologram(id, loc, pages);
holograms.put(id, holo);
}
public void removeHologram(String id) {
NexusHologram holo = holograms.remove(id);
if (holo != null) {
// Erst für alle Spieler visuell entfernen
for (Player player : Bukkit.getOnlinePlayers()) {
holo.removeForPlayer(player);
}
// Dann Entities serverseitig löschen
holo.removeAll();
}
config.set(id, null);
saveHoloConfig();
}
private void saveHoloConfig() {
try {
config.save(file);
} catch (IOException e) {
NexusLobby.getInstance().getLogger().severe("Konnte holograms.yml nicht speichern!");
e.printStackTrace();
}
}
/**
* WICHTIG: Diese Methode wird vom LobbyTabCompleter benötigt!
* @return Set aller registrierten Hologramm-IDs
*/
public Set<String> getHologramIds() {
return holograms.keySet();
}
@Override
public void onDisable() {
holograms.values().forEach(NexusHologram::removeAll);
holograms.clear();
}
}

View File

@@ -0,0 +1,118 @@
package de.nexuslobby.modules.hologram;
import de.nexuslobby.NexusLobby;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.entity.Display;
import org.bukkit.entity.Interaction;
import org.bukkit.entity.Player;
import org.bukkit.entity.TextDisplay;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class NexusHologram {
private final String id;
private final Location location;
private final List<String> pages;
private final Map<UUID, TextDisplay> playerEntities = new ConcurrentHashMap<>();
private final Map<UUID, Interaction> playerInteractions = new ConcurrentHashMap<>();
private final Map<UUID, Integer> currentPage = new ConcurrentHashMap<>();
public NexusHologram(String id, Location location, List<String> pages) {
this.id = id;
this.location = location;
this.pages = pages;
}
public void nextPage(Player player) {
if (pages.size() <= 1) return;
int next = (currentPage.getOrDefault(player.getUniqueId(), 0) + 1) % pages.size();
currentPage.put(player.getUniqueId(), next);
renderForPlayer(player);
}
public void renderForPlayer(Player player) {
if (!player.getWorld().equals(location.getWorld()) || player.getLocation().distanceSquared(location) > 2304) {
removeForPlayer(player);
return;
}
int pageIdx = currentPage.getOrDefault(player.getUniqueId(), 0);
if (pageIdx >= pages.size()) pageIdx = 0;
String rawText = pages.get(pageIdx);
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
rawText = PlaceholderAPI.setPlaceholders(player, rawText);
}
final String finalText = rawText.replace("&", "§").replace("\\n", "\n");
TextDisplay display = playerEntities.get(player.getUniqueId());
if (display == null || !display.isValid()) {
// Text-Display erstellen (Hier lassen wir den Namen zur internen Identifikation,
// aber schalten ihn strikt unsichtbar)
display = location.getWorld().spawn(location, TextDisplay.class, entity -> {
entity.setCustomName("nexus_h_" + id + "_" + player.getName());
entity.setCustomNameVisible(false);
entity.setPersistent(false);
entity.setBillboard(Display.Billboard.CENTER);
entity.setBackgroundColor(Color.fromARGB(0, 0, 0, 0));
entity.setText(finalText);
entity.setInvulnerable(true);
});
// Interaction Entity (Hitbox) erstellen
// FIX: WIR SETZEN KEINEN CUSTOM NAME MEHR.
// Das verhindert zu 100%, dass Minecraft etwas anzeigt.
Interaction interact = location.getWorld().spawn(location, Interaction.class, entity -> {
entity.setInteractionWidth(2.5f);
entity.setInteractionHeight(2.0f);
entity.setCustomNameVisible(false);
entity.setPersistent(false);
});
// Nur für den Zielspieler sichtbar machen
for (Player other : Bukkit.getOnlinePlayers()) {
if (!other.getUniqueId().equals(player.getUniqueId())) {
other.hideEntity(NexusLobby.getInstance(), display);
other.hideEntity(NexusLobby.getInstance(), interact);
}
}
playerEntities.put(player.getUniqueId(), display);
playerInteractions.put(player.getUniqueId(), interact);
} else {
if (!display.getText().equals(finalText)) {
display.setText(finalText);
}
}
}
public void removeForPlayer(Player player) {
TextDisplay display = playerEntities.remove(player.getUniqueId());
if (display != null) display.remove();
Interaction interact = playerInteractions.remove(player.getUniqueId());
if (interact != null) interact.remove();
}
public void removeAll() {
playerEntities.values().forEach(TextDisplay::remove);
playerInteractions.values().forEach(Interaction::remove);
playerEntities.clear();
playerInteractions.clear();
currentPage.clear();
}
public boolean isInteractionEntity(UUID entityId) {
// Da wir keinen Namen mehr nutzen, verlassen wir uns rein auf die UUID in der Map
return playerInteractions.values().stream().anyMatch(i -> i.getUniqueId().equals(entityId));
}
}

View File

@@ -0,0 +1,219 @@
package de.nexuslobby.modules.intro;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerToggleSneakEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class IntroModule implements Module, Listener, CommandExecutor {
private final Set<UUID> activeIntro = new HashSet<>();
private final List<Location> points = new ArrayList<>();
private File configFile;
private FileConfiguration config;
// --- Einstellungen ---
private final int TICKS_FLUG = 70; // Dauer der Fahrt zwischen zwei Punkten (3.5 Sek)
private final int TICKS_PAUSE = 30; // Standzeit an jedem Punkt (1.5 Sek)
@Override
public String getName() { return "Intro"; }
@Override
public void onEnable() {
Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance());
if (NexusLobby.getInstance().getCommand("intro") != null) {
NexusLobby.getInstance().getCommand("intro").setExecutor(this);
}
loadPoints();
}
@Override
public void onDisable() {
activeIntro.clear();
}
private void loadPoints() {
points.clear();
configFile = new File(NexusLobby.getInstance().getDataFolder(), "intro.yml");
if (!configFile.exists()) {
try { configFile.createNewFile(); } catch (IOException ignored) {}
}
config = YamlConfiguration.loadConfiguration(configFile);
List<?> list = config.getList("points");
if (list != null) {
for (Object obj : list) {
if (obj instanceof Location loc) points.add(loc);
}
}
}
private void savePoints() {
config.set("points", points);
try { config.save(configFile); } catch (IOException e) { e.printStackTrace(); }
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
if (!event.getPlayer().hasPlayedBefore() && points.size() >= 2) {
startIntro(event.getPlayer());
}
}
@EventHandler
public void onSneak(PlayerToggleSneakEvent event) {
if (activeIntro.contains(event.getPlayer().getUniqueId()) && event.isSneaking()) {
stopIntro(event.getPlayer(), true);
}
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (!(sender instanceof Player p)) return true;
if (!p.hasPermission("nexuslobby.admin")) return true;
if (args.length == 0) {
p.sendMessage("§8§m------------------------------------");
p.sendMessage("§6§lNexus Intro System (Cinematic)");
p.sendMessage("§e/intro add §7- Punkt hinzufügen");
p.sendMessage("§e/intro clear §7- Alle Punkte löschen");
p.sendMessage("§e/intro start §7- Teste die Fahrt");
p.sendMessage("§8§m------------------------------------");
return true;
}
switch (args[0].toLowerCase()) {
case "add" -> {
points.add(p.getLocation());
savePoints();
p.sendMessage("§8[§6Nexus§8] §aPunkt #" + points.size() + " wurde gesetzt!");
}
case "clear" -> {
points.clear();
savePoints();
p.sendMessage("§8[§6Nexus§8] §cAlle Intro-Punkte wurden gelöscht.");
}
case "start" -> {
if (points.size() < 2) {
p.sendMessage("§8[§6Nexus§8] §cDu brauchst mindestens 2 Punkte für eine Fahrt.");
} else {
startIntro(p);
}
}
}
return true;
}
public void startIntro(Player player) {
activeIntro.add(player.getUniqueId());
player.setGameMode(GameMode.SPECTATOR);
new BukkitRunnable() {
int currentSegment = 0;
int tickInSegment = 0;
boolean isPausing = true;
@Override
public void run() {
try {
if (!player.isOnline() || !activeIntro.contains(player.getUniqueId())) {
this.cancel();
return;
}
if (currentSegment >= points.size() - 1) {
stopIntro(player, false);
this.cancel();
return;
}
Location start = points.get(currentSegment);
Location end = points.get(currentSegment + 1);
if (isPausing) {
// Kamera steht am aktuellen Punkt
player.teleport(start);
tickInSegment++;
if (tickInSegment >= TICKS_PAUSE) {
isPausing = false;
tickInSegment = 0;
}
} else {
// Kamera fliegt zum nächsten Punkt
double progress = (double) tickInSegment / (double) TICKS_FLUG;
// "Smooth Step" für flüssigeres Beschleunigen/Bremsen
double smoothT = progress * progress * (3 - 2 * progress);
Location nextLoc = interpolate(start, end, smoothT);
player.teleport(nextLoc);
tickInSegment++;
if (tickInSegment >= TICKS_FLUG) {
isPausing = true;
tickInSegment = 0;
currentSegment++;
}
}
player.spigot().sendMessage(ChatMessageType.ACTION_BAR,
new TextComponent("§6§lINTRO-TOUR §8| §ePunkt " + (currentSegment + 1) + " §8| §7Sneak zum Abbrechen"));
} catch (Exception e) {
this.cancel();
stopIntro(player, true);
}
}
}.runTaskTimer(NexusLobby.getInstance(), 0L, 1L);
}
private Location interpolate(Location start, Location end, double t) {
double x = start.getX() + (end.getX() - start.getX()) * t;
double y = start.getY() + (end.getY() - start.getY()) * t;
double z = start.getZ() + (end.getZ() - start.getZ()) * t;
// Sanfte Drehung
float startYaw = start.getYaw();
float endYaw = end.getYaw();
// Verhindert ruckartige 360-Grad Dreher
float diff = (endYaw - startYaw) % 360;
if (diff > 180) diff -= 360;
if (diff < -180) diff += 360;
float yaw = startYaw + diff * (float)t;
float pitch = (float) (start.getPitch() + (end.getPitch() - start.getPitch()) * t);
return new Location(start.getWorld(), x, y, z, yaw, pitch);
}
private void stopIntro(Player player, boolean canceled) {
activeIntro.remove(player.getUniqueId());
player.setGameMode(GameMode.ADVENTURE);
player.teleport(player.getWorld().getSpawnLocation());
if (canceled) {
player.sendMessage("§8[§6Nexus§8] §cIntro abgebrochen.");
} else {
player.sendMessage("§8[§6Nexus§8] §aWillkommen auf dem Netzwerk!");
}
}
}

View File

@@ -0,0 +1,298 @@
package de.nexuslobby.modules.mapart;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.map.MapCanvas;
import org.bukkit.map.MapRenderer;
import org.bukkit.map.MapView;
import org.bukkit.util.RayTraceResult;
import org.jetbrains.annotations.NotNull;
import javax.imageio.ImageIO;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class MapArtModule implements Module, CommandExecutor {
private File storageFile;
private FileConfiguration storageConfig;
// RAM-Schutz: Cache für bereits geladene Bild-Kacheln
private final Map<String, BufferedImage> tileCache = new ConcurrentHashMap<>();
// Hilfs-Set um parallele Downloads der gleichen URL zu verhindern
private final Map<String, Boolean> loadingShield = new ConcurrentHashMap<>();
@Override
public String getName() { return "MapArt"; }
@Override
public void onEnable() {
if (NexusLobby.getInstance().getCommand("mapart") != null) {
NexusLobby.getInstance().getCommand("mapart").setExecutor(this);
}
initStorage();
reloadMaps();
}
@Override
public void onDisable() {
saveStorage();
tileCache.clear();
}
private void initStorage() {
storageFile = new File(NexusLobby.getInstance().getDataFolder(), "mapart.yml");
if (!storageFile.exists()) {
try {
storageFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
storageConfig = YamlConfiguration.loadConfiguration(storageFile);
}
private void saveStorage() {
try {
storageConfig.save(storageFile);
} catch (IOException e) {
e.printStackTrace();
}
}
private void reloadMaps() {
ConfigurationSection section = storageConfig.getConfigurationSection("active_maps");
if (section == null) return;
for (String idStr : section.getKeys(false)) {
try {
int mapId = Integer.parseInt(idStr);
String url = section.getString(idStr + ".url");
int x = section.getInt(idStr + ".x");
int y = section.getInt(idStr + ".y");
int totalW = section.getInt(idStr + ".totalW");
int totalH = section.getInt(idStr + ".totalH");
@SuppressWarnings("deprecation")
MapView view = Bukkit.getMap(mapId);
if (view != null) {
applyRenderer(view, url, x, y, totalW, totalH);
}
} catch (Exception ignored) {}
}
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (!(sender instanceof Player player)) return true;
if (!player.hasPermission("nexuslobby.mapart")) {
player.sendMessage("§8[§6Nexus§8] §cKeine Rechte.");
return true;
}
if (args.length == 0) {
sendHelp(player);
return true;
}
if (args[0].equalsIgnoreCase("delete")) {
int radius = 3;
if (args.length == 2) {
try {
radius = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
player.sendMessage("§8[§6Nexus§8] §cUngültiger Radius.");
return true;
}
}
deleteNearbyMaps(player, radius);
return true;
}
if (args.length != 2) {
sendHelp(player);
return true;
}
RayTraceResult rayTrace = player.rayTraceBlocks(5);
if (rayTrace == null || rayTrace.getHitBlock() == null || rayTrace.getHitBlockFace() == null) {
player.sendMessage("§8[§6Nexus§8] §cBitte schaue eine Wand direkt an.");
return true;
}
Block targetBlock = rayTrace.getHitBlock();
BlockFace hitFace = rayTrace.getHitBlockFace();
if (hitFace == BlockFace.UP || hitFace == BlockFace.DOWN) {
player.sendMessage("§8[§6Nexus§8] §cAktuell nur an vertikalen Wänden möglich.");
return true;
}
String url = args[0];
int width, height;
try {
String[] parts = args[1].toLowerCase().split("x");
width = Integer.parseInt(parts[0]);
height = Integer.parseInt(parts[1]);
} catch (Exception e) {
player.sendMessage("§8[§6Nexus§8] §cUngültiges Format (z.B. 6x4).");
return true;
}
player.sendMessage("§8[§6Nexus§8] §7Bild wird verarbeitet...");
createRaster(player, targetBlock, hitFace, url, width, height);
return true;
}
private void sendHelp(Player p) {
p.sendMessage("§8§m------------------------------------");
p.sendMessage("§6§lNexus MapArt");
p.sendMessage("§e/mapart <URL> <BxH> §7- Bild erstellen");
p.sendMessage("§e/mapart delete [Radius] §7- Bilder in der Nähe löschen");
p.sendMessage("§8§m------------------------------------");
}
private void deleteNearbyMaps(Player player, int radius) {
AtomicInteger count = new AtomicInteger(0);
player.getNearbyEntities(radius, radius, radius).forEach(entity -> {
if (entity instanceof ItemFrame frame) {
ItemStack item = frame.getItem();
if (item != null && item.getType() == Material.FILLED_MAP && item.getItemMeta() instanceof MapMeta meta) {
if (meta.hasMapView()) {
int id = meta.getMapView().getId();
if (storageConfig.contains("active_maps." + id)) {
storageConfig.set("active_maps." + id, null);
frame.remove();
count.incrementAndGet();
}
}
}
}
});
saveStorage();
player.sendMessage("§8[§6Nexus§8] §aErfolgreich §e" + count.get() + " §aKartenelemente gelöscht.");
}
private void createRaster(Player player, Block startBlock, BlockFace face, String url, int gridW, int gridH) {
BlockFace rightDirection;
switch (face) {
case NORTH: rightDirection = BlockFace.WEST; break;
case SOUTH: rightDirection = BlockFace.EAST; break;
case EAST: rightDirection = BlockFace.NORTH; break;
case WEST: rightDirection = BlockFace.SOUTH; break;
default: rightDirection = BlockFace.EAST;
}
Block origin = startBlock.getRelative(face);
for (int y = 0; y < gridH; y++) {
for (int x = 0; x < gridW; x++) {
Block currentPos = origin.getRelative(rightDirection, x).getRelative(BlockFace.DOWN, y);
spawnPersistentFrame(player, currentPos, face, url, x, y, gridW, gridH);
}
}
player.sendMessage("§8[§6Nexus§8] §aBild-Raster wurde permanent platziert!");
}
private void spawnPersistentFrame(Player player, Block pos, BlockFace face, String url, int x, int y, int totalW, int totalH) {
MapView view = Bukkit.createMap(player.getWorld());
applyRenderer(view, url, x, y, totalW, totalH);
String path = "active_maps." + view.getId();
storageConfig.set(path + ".url", url);
storageConfig.set(path + ".x", x);
storageConfig.set(path + ".y", y);
storageConfig.set(path + ".totalW", totalW);
storageConfig.set(path + ".totalH", totalH);
saveStorage();
ItemStack mapStack = new ItemStack(Material.FILLED_MAP);
MapMeta meta = (MapMeta) mapStack.getItemMeta();
if (meta != null) {
meta.setMapView(view);
mapStack.setItemMeta(meta);
}
try {
ItemFrame frame = player.getWorld().spawn(pos.getLocation(), ItemFrame.class);
frame.setFacingDirection(face);
frame.setItem(mapStack);
frame.setVisible(false);
frame.setFixed(true);
} catch (Exception ignored) {}
}
private void applyRenderer(MapView view, String url, int tileX, int tileY, int totalW, int totalH) {
view.getRenderers().forEach(view::removeRenderer);
view.addRenderer(new MapRenderer() {
private final String cacheKey = url + "_" + totalW + "x" + totalH + "_" + tileX + "_" + tileY;
private boolean errorLogged = false;
@Override
public void render(@NotNull MapView map, @NotNull MapCanvas canvas, @NotNull Player p) {
// 1. Wenn Kachel im Cache, sofort zeichnen (extrem schnell)
if (tileCache.containsKey(cacheKey)) {
canvas.drawImage(0, 0, tileCache.get(cacheKey));
return;
}
// 2. Wenn bereits ein Thread für diese URL lädt, abbrechen (verhindert Spam)
if (loadingShield.containsKey(url)) return;
// 3. Bild asynchron laden
loadingShield.put(url, true);
Bukkit.getScheduler().runTaskAsynchronously(NexusLobby.getInstance(), () -> {
try {
BufferedImage original = ImageIO.read(new URL(url));
if (original != null) {
int targetW = totalW * 128;
int targetH = totalH * 128;
BufferedImage fullScaled = new BufferedImage(targetW, targetH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = fullScaled.createGraphics();
g.drawImage(original.getScaledInstance(targetW, targetH, Image.SCALE_SMOOTH), 0, 0, null);
g.dispose();
// Alle benötigten Kacheln für dieses Bild in den Cache legen
for (int ty = 0; ty < totalH; ty++) {
for (int tx = 0; tx < totalW; tx++) {
String key = url + "_" + totalW + "x" + totalH + "_" + tx + "_" + ty;
tileCache.put(key, fullScaled.getSubimage(tx * 128, ty * 128, 128, 128));
}
}
}
} catch (Exception e) {
if (!errorLogged) {
NexusLobby.getInstance().getLogger().warning("Fehler beim Laden von MapArt: " + url);
errorLogged = true;
}
} finally {
loadingShield.remove(url);
}
});
}
});
}
}

View File

@@ -6,14 +6,40 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class PortalCommand implements CommandExecutor { public class PortalCommand implements CommandExecutor {
private final PortalManager portalManager; private final PortalManager portalManager;
// Statische Maps, damit wir von überall (z.B. BorderCommand) auf die Auswahl zugreifen können
private static final Map<UUID, Location> selection1 = new HashMap<>();
private static final Map<UUID, Location> selection2 = new HashMap<>();
public PortalCommand(PortalManager portalManager) { public PortalCommand(PortalManager portalManager) {
this.portalManager = portalManager; this.portalManager = portalManager;
} }
// Statische Hilfsmethoden für andere Klassen
public static Location getSelection1(Player player) {
return selection1.get(player.getUniqueId());
}
public static Location getSelection2(Player player) {
return selection2.get(player.getUniqueId());
}
// Methoden zum Setzen (für deinen Wand-Listener oder Befehle)
public static void setSelection1(Player player, Location loc) {
selection1.put(player.getUniqueId(), loc);
}
public static void setSelection2(Player player, Location loc) {
selection2.put(player.getUniqueId(), loc);
}
@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 (!(sender instanceof Player)) { if (!(sender instanceof Player)) {
@@ -23,13 +49,11 @@ public class PortalCommand implements CommandExecutor {
Player p = (Player) sender; Player p = (Player) sender;
// Wenn keine Argumente da sind, Hilfe zeigen
if (args.length == 0) { if (args.length == 0) {
sendHelp(p); sendHelp(p);
return true; return true;
} }
// Switch case für die Unterbefehle
switch (args[0].toLowerCase()) { switch (args[0].toLowerCase()) {
case "create": case "create":
if (args.length < 3) { if (args.length < 3) {
@@ -46,7 +70,9 @@ public class PortalCommand implements CommandExecutor {
case "setpos1": case "setpos1":
if (args.length < 2) { p.sendMessage("§cBenutzung: /portal setpos1 <Name>"); return true; } if (args.length < 2) { p.sendMessage("§cBenutzung: /portal setpos1 <Name>"); return true; }
if (portalManager.setPortalPos(args[1], 1, p.getLocation())) { Location loc1 = p.getLocation();
if (portalManager.setPortalPos(args[1], 1, loc1)) {
setSelection1(p, loc1); // Speichert es auch in der statischen Map für die Border
p.sendMessage("§aPosition 1 gesetzt für " + args[1]); p.sendMessage("§aPosition 1 gesetzt für " + args[1]);
} else { } else {
p.sendMessage("§cPortal nicht gefunden."); p.sendMessage("§cPortal nicht gefunden.");
@@ -55,7 +81,9 @@ public class PortalCommand implements CommandExecutor {
case "setpos2": case "setpos2":
if (args.length < 2) { p.sendMessage("§cBenutzung: /portal setpos2 <Name>"); return true; } if (args.length < 2) { p.sendMessage("§cBenutzung: /portal setpos2 <Name>"); return true; }
if (portalManager.setPortalPos(args[1], 2, p.getLocation())) { Location loc2 = p.getLocation();
if (portalManager.setPortalPos(args[1], 2, loc2)) {
setSelection2(p, loc2); // Speichert es auch in der statischen Map für die Border
p.sendMessage("§aPosition 2 gesetzt für " + args[1]); p.sendMessage("§aPosition 2 gesetzt für " + args[1]);
} else { } else {
p.sendMessage("§cPortal nicht gefunden."); p.sendMessage("§cPortal nicht gefunden.");
@@ -65,12 +93,9 @@ public class PortalCommand implements CommandExecutor {
case "setdest": case "setdest":
if (args.length < 3) { if (args.length < 3) {
p.sendMessage("§cBenutzung: /portal setdest <Name> <Ziel>"); p.sendMessage("§cBenutzung: /portal setdest <Name> <Ziel>");
p.sendMessage("§7Server: ServerName");
p.sendMessage("§7Welt: Weltname;X;Y;Z;Yaw;Pitch");
return true; return true;
} }
String dest = args[2]; String dest = args[2];
// Falls Koordinaten getrennt mit Leerzeichen gegeben werden (z.B. /portal setdest name world 100 64 100)
if (args.length > 3) dest += ";" + args[3]; if (args.length > 3) dest += ";" + args[3];
if (args.length > 4) dest += ";" + args[4]; if (args.length > 4) dest += ";" + args[4];
if (args.length > 5) dest += ";" + args[5]; if (args.length > 5) dest += ";" + args[5];
@@ -96,20 +121,16 @@ public class PortalCommand implements CommandExecutor {
break; break;
case "setspawn": case "setspawn":
// Neuer Befehl: /portal setspawn <Name>
if (args.length < 2) { if (args.length < 2) {
p.sendMessage("§cBenutzung: /portal setspawn <Name>"); p.sendMessage("§cBenutzung: /portal setspawn <Name>");
return true; return true;
} }
// Optional: Berechtigungscheck (anpassbar)
if (!p.hasPermission("nexuslobby.portal")) { if (!p.hasPermission("nexuslobby.portal")) {
p.sendMessage("§cKeine Rechte!"); p.sendMessage("§cKeine Rechte!");
return true; return true;
} }
// Wir speichern die aktuelle Position leicht versetzt (2 Blöcke), damit Spieler nicht direkt wieder im Portal landen.
Location spawnLoc = p.getLocation().clone(); Location spawnLoc = p.getLocation().clone();
spawnLoc.add(0, 0, 2); // einfacher Offset als default spawnLoc.add(0, 0, 2);
if (portalManager.setPortalReturnSpawn(args[1], spawnLoc)) { if (portalManager.setPortalReturnSpawn(args[1], spawnLoc)) {
p.sendMessage("§aPortal-Spawnpunkt für '" + args[1] + "' gesetzt!"); p.sendMessage("§aPortal-Spawnpunkt für '" + args[1] + "' gesetzt!");

View File

@@ -31,7 +31,7 @@ import java.util.UUID;
import java.util.Set; import java.util.Set;
/** /**
* PortalManager - verwaltet Portale, lädt/speichert sie, spawnt Partikel und teleporiert Spieler. * PortalManager - Verwaltet Portale, Markierungen und den globalen Grenzschutz.
*/ */
public class PortalManager implements Module, Listener { public class PortalManager implements Module, Listener {
@@ -42,6 +42,11 @@ public class PortalManager implements Module, Listener {
private final NamespacedKey wandKey; private final NamespacedKey wandKey;
private BukkitTask particleTask; private BukkitTask particleTask;
// Boundary Cache
private Location borderMin;
private Location borderMax;
private boolean borderEnabled = false;
public PortalManager(NexusLobby plugin) { public PortalManager(NexusLobby plugin) {
this.plugin = plugin; this.plugin = plugin;
this.wandKey = new NamespacedKey(plugin, "nexuslobby_portal_wand"); this.wandKey = new NamespacedKey(plugin, "nexuslobby_portal_wand");
@@ -55,9 +60,10 @@ public class PortalManager implements Module, Listener {
@Override @Override
public void onEnable() { public void onEnable() {
loadPortals(); loadPortals();
loadBorderSettings();
Bukkit.getPluginManager().registerEvents(this, plugin); Bukkit.getPluginManager().registerEvents(this, plugin);
startParticleTask(); startParticleTask();
plugin.getLogger().info("PortalManager geladen."); plugin.getLogger().info("PortalManager vollständig geladen.");
} }
@Override @Override
@@ -70,15 +76,25 @@ public class PortalManager implements Module, Listener {
plugin.getLogger().info("PortalManager deaktiviert."); plugin.getLogger().info("PortalManager deaktiviert.");
} }
/** public void loadBorderSettings() {
* Gibt alle Namen der aktuell geladenen Portale zurück. if (plugin.getConfig().contains("border.pos1") && plugin.getConfig().contains("border.pos2")) {
* Wird vom LobbyTabCompleter genutzt. Location p1 = plugin.getConfig().getLocation("border.pos1");
*/ Location p2 = plugin.getConfig().getLocation("border.pos2");
if (p1 != null && p2 != null) {
this.borderMin = getMinLocation(p1, p2);
this.borderMax = getMaxLocation(p1, p2);
this.borderEnabled = true;
}
} else {
this.borderEnabled = false;
}
}
public Set<String> getPortalNames() { public Set<String> getPortalNames() {
return portals.keySet(); return portals.keySet();
} }
// --- Wand / Selection --- // --- Wand / Selection Logic ---
@org.bukkit.event.EventHandler @org.bukkit.event.EventHandler
public void onPlayerInteract(PlayerInteractEvent event) { public void onPlayerInteract(PlayerInteractEvent event) {
if (event.getAction() == Action.PHYSICAL) return; if (event.getAction() == Action.PHYSICAL) return;
@@ -87,9 +103,7 @@ public class PortalManager implements Module, Listener {
if (item == null || !item.hasItemMeta()) return; if (item == null || !item.hasItemMeta()) return;
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
if (meta == null) return; if (meta == null || !meta.getPersistentDataContainer().has(wandKey, PersistentDataType.BYTE)) return;
if (!meta.getPersistentDataContainer().has(wandKey, PersistentDataType.BYTE)) return;
Player p = event.getPlayer(); Player p = event.getPlayer();
if (!p.hasPermission("nexuslobby.portal")) { if (!p.hasPermission("nexuslobby.portal")) {
@@ -98,7 +112,6 @@ public class PortalManager implements Module, Listener {
} }
event.setCancelled(true); event.setCancelled(true);
if (!event.hasBlock()) { if (!event.hasBlock()) {
p.sendMessage("§cDu musst auf einen Block klicken!"); p.sendMessage("§cDu musst auf einen Block klicken!");
return; return;
@@ -113,15 +126,34 @@ public class PortalManager implements Module, Listener {
if (event.getAction() == Action.LEFT_CLICK_BLOCK) { if (event.getAction() == Action.LEFT_CLICK_BLOCK) {
selectionMap.get(uuid)[0] = clickedLoc; selectionMap.get(uuid)[0] = clickedLoc;
PortalCommand.setSelection1(p, clickedLoc);
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 2.0f); p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 2.0f);
p.sendMessage("§aPosition 1 gesetzt: " + clickedLoc.getBlockX() + ", " + clickedLoc.getBlockY() + ", " + clickedLoc.getBlockZ()); p.sendMessage("§aPosition 1 gesetzt: " + clickedLoc.getBlockX() + ", " + clickedLoc.getBlockY() + ", " + clickedLoc.getBlockZ());
} else if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { } else if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
selectionMap.get(uuid)[1] = clickedLoc; selectionMap.get(uuid)[1] = clickedLoc;
PortalCommand.setSelection2(p, clickedLoc);
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 1.0f); p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 1.0f);
p.sendMessage("§bPosition 2 gesetzt: " + clickedLoc.getBlockX() + ", " + clickedLoc.getBlockY() + ", " + clickedLoc.getBlockZ()); p.sendMessage("§bPosition 2 gesetzt: " + clickedLoc.getBlockX() + ", " + clickedLoc.getBlockY() + ", " + clickedLoc.getBlockZ());
if (selectionMap.get(uuid)[0] != null) { Location loc1 = selectionMap.get(uuid)[0];
p.sendMessage("§eBenutze jetzt: /portal create <Name> <server|world>"); if (loc1 != null) {
int width = Math.abs(loc1.getBlockX() - clickedLoc.getBlockX()) + 1;
int height = Math.abs(loc1.getBlockY() - clickedLoc.getBlockY()) + 1;
int length = Math.abs(loc1.getBlockZ() - clickedLoc.getBlockZ()) + 1;
long volume = (long) width * height * length;
p.sendMessage("§7§m----------------------------------");
if (volume < 500) {
p.sendMessage("§e[Nexus] Kleiner Bereich erkannt (Portal-Größe)");
p.sendMessage("§fBefehl: §b/portal create <Name> <server|world>");
} else {
p.sendMessage("§6[Nexus] Großer Bereich erkannt (WorldBorder-Größe)");
p.sendMessage("§fBefehl: §a/border square");
}
p.sendMessage("§7§m----------------------------------");
} }
} }
} }
@@ -140,14 +172,8 @@ public class PortalManager implements Module, Listener {
return YamlConfiguration.loadConfiguration(file); return YamlConfiguration.loadConfiguration(file);
} }
/**
* Lädt alle Portale aus der Konfiguration.
* PUBLIC für den Zugriff durch NexusLobby.java beim Reload.
*/
public void loadPortals() { public void loadPortals() {
// Liste leeren, um Duplikate beim Reload zu vermeiden
portals.clear(); portals.clear();
YamlConfiguration portalConfig = loadPortalConfig(); YamlConfiguration portalConfig = loadPortalConfig();
ConfigurationSection section = portalConfig.getConfigurationSection("portals"); ConfigurationSection section = portalConfig.getConfigurationSection("portals");
if (section == null) return; if (section == null) return;
@@ -262,31 +288,7 @@ public class PortalManager implements Module, Listener {
return true; return true;
} }
// --- Particles --- // --- Movement / Teleport / Boundary Logic ---
private void startParticleTask() {
particleTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
for (Portal portal : portals.values()) {
if (portal.getPos1() == null || portal.getPos2() == null) continue;
spawnParticles(portal);
}
}, 0L, 5L);
}
private void spawnParticles(Portal portal) {
Location min = getMinLocation(portal.getPos1(), portal.getPos2());
Location max = getMaxLocation(portal.getPos1(), portal.getPos2());
World world = portal.getPos1().getWorld();
if (world == null) return;
for (int i = 0; i < 30; i++) {
double x = min.getX() + 0.5 + (Math.random() * (max.getX() - min.getX()));
double y = min.getY() + 0.5 + (Math.random() * (max.getY() - min.getY()));
double z = min.getZ() + 0.5 + (Math.random() * (max.getZ() - min.getZ()));
world.spawnParticle(portal.getParticle(), new Location(world, x, y, z), 3, 0.1, 0.1, 0.1, 0);
}
}
// --- Teleport / Movement ---
@org.bukkit.event.EventHandler @org.bukkit.event.EventHandler
public void onPlayerMove(PlayerMoveEvent event) { public void onPlayerMove(PlayerMoveEvent event) {
if (event.getFrom().getX() == event.getTo().getX() && if (event.getFrom().getX() == event.getTo().getX() &&
@@ -296,8 +298,22 @@ public class PortalManager implements Module, Listener {
} }
Player player = event.getPlayer(); Player player = event.getPlayer();
Location loc = player.getLocation(); Location loc = event.getTo();
// 1. Grenzschutz (Boundary Protection)
if (borderEnabled && !player.hasPermission("nexuslobby.border.bypass")) {
if (!isWithinBorder(loc)) {
Location spawn = getMainSpawnLocation();
if (spawn != null) {
player.teleport(spawn);
player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1.0f, 0.5f);
player.sendMessage("§c§lHEY! §7Du hast den erlaubten Bereich verlassen.");
}
return;
}
}
// 2. Portal Logik
for (Portal portal : portals.values()) { for (Portal portal : portals.values()) {
if (portal.getPos1() == null || portal.getPos2() == null) continue; if (portal.getPos1() == null || portal.getPos2() == null) continue;
if (!isInArea(loc, portal.getPos1(), portal.getPos2())) continue; if (!isInArea(loc, portal.getPos1(), portal.getPos2())) continue;
@@ -313,11 +329,18 @@ public class PortalManager implements Module, Listener {
} }
} }
private boolean isWithinBorder(Location loc) {
if (borderMin == null || borderMax == null) return true;
if (!loc.getWorld().equals(borderMin.getWorld())) return true;
return loc.getX() >= borderMin.getX() && loc.getX() <= borderMax.getX() &&
loc.getY() >= borderMin.getY() && loc.getY() <= borderMax.getY() &&
loc.getZ() >= borderMin.getZ() && loc.getZ() <= borderMax.getZ();
}
private void executeTeleport(Player player, Portal portal) { private void executeTeleport(Player player, Portal portal) {
if ("SERVER".equalsIgnoreCase(portal.getType())) { if ("SERVER".equalsIgnoreCase(portal.getType())) {
String serverName = portal.getDestination(); String serverName = portal.getDestination();
player.sendMessage("§eVerbinde zum Server: " + serverName); player.sendMessage("§eVerbinde zum Server: " + serverName);
plugin.getLogger().info("Verbinde " + player.getName() + " -> " + serverName);
Location loc = portal.getReturnSpawn(); Location loc = portal.getReturnSpawn();
if (loc == null) { if (loc == null) {
@@ -335,7 +358,6 @@ public class PortalManager implements Module, Listener {
loc.add(0,0,2); loc.add(0,0,2);
} }
} }
player.teleport(loc); player.teleport(loc);
connectToServer(player, serverName); connectToServer(player, serverName);
return; return;
@@ -352,8 +374,6 @@ public class PortalManager implements Module, Listener {
if (spawnLoc != null) { if (spawnLoc != null) {
player.teleport(spawnLoc); player.teleport(spawnLoc);
player.sendMessage("§aDu wurdest zum Spawn teleportiert!"); player.sendMessage("§aDu wurdest zum Spawn teleportiert!");
} else {
player.sendMessage("§cSpawn konnte nicht gefunden werden.");
} }
return; return;
} }
@@ -361,99 +381,84 @@ public class PortalManager implements Module, Listener {
String[] parts = dest.split(";"); String[] parts = dest.split(";");
if (parts.length >= 4) { if (parts.length >= 4) {
World world = Bukkit.getWorld(parts[0]); World world = Bukkit.getWorld(parts[0]);
if (world == null) { if (world == null) return;
player.sendMessage("§cZielwelt nicht gefunden: " + parts[0]);
return;
}
try { try {
double x = Double.parseDouble(parts[1]); double x = Double.parseDouble(parts[1]);
double y = Double.parseDouble(parts[2]); double y = Double.parseDouble(parts[2]);
double z = Double.parseDouble(parts[3]); double z = Double.parseDouble(parts[3]);
float yaw = 0f, pitch = 0f; float yaw = parts.length >= 6 ? Float.parseFloat(parts[4]) : 0f;
if (parts.length >= 6) { float pitch = parts.length >= 6 ? Float.parseFloat(parts[5]) : 0f;
yaw = Float.parseFloat(parts[4]); player.teleport(new Location(world, x, y, z, yaw, pitch));
pitch = Float.parseFloat(parts[5]);
}
Location target = new Location(world, x, y, z, yaw, pitch);
player.teleport(target);
player.sendMessage("§aDu wurdest teleportiert!"); player.sendMessage("§aDu wurdest teleportiert!");
} catch (NumberFormatException e) { } catch (NumberFormatException ignored) {}
player.sendMessage("§cUngültige Koordinaten im Portalziel!");
}
} else {
player.sendMessage("§cUngültiges Portalzielformat!");
} }
} }
private void connectToServer(Player player, String serverName) { private void connectToServer(Player player, String serverName) {
try { try {
if (!Bukkit.getMessenger().isOutgoingChannelRegistered(plugin, "BungeeCord")) {
plugin.getLogger().warning("BungeeCord outgoing channel not registered; cannot send plugin message.");
player.sendMessage("§cProxy-Verbindung nicht möglich: BungeeCord-Kanal nicht registriert.");
return;
}
ByteArrayOutputStream b = new ByteArrayOutputStream(); ByteArrayOutputStream b = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(b); DataOutputStream out = new DataOutputStream(b);
out.writeUTF("Connect"); out.writeUTF("Connect");
out.writeUTF(serverName); out.writeUTF(serverName);
player.sendPluginMessage(plugin, "BungeeCord", b.toByteArray()); player.sendPluginMessage(plugin, "BungeeCord", b.toByteArray());
} catch (IOException e) { } catch (IOException e) {
plugin.getLogger().severe("Fehler beim Senden der BungeeCord-Message: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
player.sendMessage("§cFehler beim Verbinden zum Proxy.");
} catch (RuntimeException e) {
plugin.getLogger().warning("Konnte Plugin-Message nicht senden: " + e.getMessage());
player.sendMessage("§cProxy-Verbindung nicht möglich.");
} }
} }
// --- Hilfsfunktionen ---
private Location getMainSpawnLocation() { private Location getMainSpawnLocation() {
String worldName = plugin.getConfig().getString("spawn.world", null); String worldName = plugin.getConfig().getString("spawn.world", null);
if (worldName != null) { if (worldName != null) {
World w = Bukkit.getWorld(worldName); World w = Bukkit.getWorld(worldName);
if (w != null) { if (w != null) {
double x = plugin.getConfig().getDouble("spawn.x", w.getSpawnLocation().getX()); return new Location(w,
double y = plugin.getConfig().getDouble("spawn.y", w.getSpawnLocation().getY()); plugin.getConfig().getDouble("spawn.x"),
double z = plugin.getConfig().getDouble("spawn.z", w.getSpawnLocation().getZ()); plugin.getConfig().getDouble("spawn.y"),
float yaw = (float) plugin.getConfig().getDouble("spawn.yaw", w.getSpawnLocation().getYaw()); plugin.getConfig().getDouble("spawn.z"),
float pitch = (float) plugin.getConfig().getDouble("spawn.pitch", w.getSpawnLocation().getPitch()); (float) plugin.getConfig().getDouble("spawn.yaw"),
return new Location(w, x, y, z, yaw, pitch); (float) plugin.getConfig().getDouble("spawn.pitch"));
} }
} }
if (!Bukkit.getWorlds().isEmpty()) { return !Bukkit.getWorlds().isEmpty() ? Bukkit.getWorlds().get(0).getSpawnLocation() : null;
return Bukkit.getWorlds().get(0).getSpawnLocation();
}
return null;
} }
// --- Utils & Particles ---
private boolean isInArea(Location loc, Location loc1, Location loc2) { private boolean isInArea(Location loc, Location loc1, Location loc2) {
if (loc == null || loc1 == null || loc2 == null) return false;
if (!loc.getWorld().equals(loc1.getWorld())) return false; if (!loc.getWorld().equals(loc1.getWorld())) return false;
int x = loc.getBlockX();
int y = loc.getBlockY();
int z = loc.getBlockZ();
int headY = y + 1;
Location min = getMinLocation(loc1, loc2); Location min = getMinLocation(loc1, loc2);
Location max = getMaxLocation(loc1, loc2); Location max = getMaxLocation(loc1, loc2);
return (loc.getX() >= min.getX() && loc.getX() <= max.getX() + 1) &&
boolean xMatch = (x >= min.getBlockX()) && (x <= max.getBlockX()); (loc.getY() >= min.getY() && loc.getY() <= max.getY() + 1) &&
boolean zMatch = (z >= min.getBlockZ()) && (z <= max.getBlockZ()); (loc.getZ() >= min.getZ() && loc.getZ() <= max.getZ() + 1);
boolean yMatch = (y >= min.getBlockY()) && (headY <= max.getBlockY());
return xMatch && yMatch && zMatch;
} }
private Location getMinLocation(Location a, Location b) { private Location getMinLocation(Location a, Location b) {
return new Location(a.getWorld(), Math.min(a.getX(), b.getX()), return new Location(a.getWorld(), Math.min(a.getX(), b.getX()), Math.min(a.getY(), b.getY()), Math.min(a.getZ(), b.getZ()));
Math.min(a.getY(), b.getY()), Math.min(a.getZ(), b.getZ()));
} }
private Location getMaxLocation(Location a, Location b) { private Location getMaxLocation(Location a, Location b) {
return new Location(a.getWorld(), Math.max(a.getX(), b.getX()), return new Location(a.getWorld(), Math.max(a.getX(), b.getX()), Math.max(a.getY(), b.getY()), Math.max(a.getZ(), b.getZ()));
Math.max(a.getY(), b.getY()), Math.max(a.getZ(), b.getZ())); }
private void startParticleTask() {
particleTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
for (Portal portal : portals.values()) {
if (portal.getPos1() != null && portal.getPos2() != null) {
spawnParticles(portal);
}
}
}, 0L, 5L);
}
private void spawnParticles(Portal portal) {
Location min = getMinLocation(portal.getPos1(), portal.getPos2());
Location max = getMaxLocation(portal.getPos1(), portal.getPos2());
World world = portal.getPos1().getWorld();
for (int i = 0; i < 15; i++) {
double x = min.getX() + Math.random() * (max.getX() - min.getX() + 1);
double y = min.getY() + Math.random() * (max.getY() - min.getY() + 1);
double z = min.getZ() + Math.random() * (max.getZ() - min.getZ() + 1);
world.spawnParticle(portal.getParticle(), x, y, z, 1, 0.1, 0.1, 0.1, 0);
}
} }
} }

View File

@@ -1,210 +1,311 @@
package de.nexuslobby.modules.tablist; package de.nexuslobby.modules.tablist;
import de.nexuslobby.NexusLobby; import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module; import de.nexuslobby.api.Module;
import net.luckperms.api.LuckPerms; import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider; import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.model.group.Group;
import net.luckperms.api.model.user.User; import net.luckperms.api.model.user.User;
import me.clip.placeholderapi.PlaceholderAPI; import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import org.bukkit.scoreboard.Scoreboard; import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team; import org.bukkit.scoreboard.Team;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
public class TablistModule implements Module { public class TablistModule implements Module {
private BukkitTask refreshTask; private BukkitTask refreshTask;
private int currentHeaderIndex = 0; private int currentHeaderIndex = 0;
private int currentFooterIndex = 0; private int currentFooterIndex = 0;
private LuckPerms luckPerms; private LuckPerms luckPerms;
private boolean placeholderAPIEnabled; private boolean placeholderAPIEnabled;
// Pixel-Breiten Tabelle für die Standard Minecraft Schriftart
private static final Map<Character, Integer> CHAR_WIDTHS = new HashMap<>();
private static final int TARGET_PIXEL_WIDTH = 90; // Ziel-Breite für den Namensteil vor dem Ping
static {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for (char c : chars.toCharArray()) CHAR_WIDTHS.put(c, 6);
CHAR_WIDTHS.put('i', 2); CHAR_WIDTHS.put('l', 3); CHAR_WIDTHS.put('t', 4);
CHAR_WIDTHS.put('I', 4); CHAR_WIDTHS.put('f', 5); CHAR_WIDTHS.put('k', 5);
CHAR_WIDTHS.put('.', 2); CHAR_WIDTHS.put(',', 2); CHAR_WIDTHS.put('!', 2);
CHAR_WIDTHS.put('(', 5); CHAR_WIDTHS.put(')', 5); CHAR_WIDTHS.put('[', 4);
CHAR_WIDTHS.put(']', 4); CHAR_WIDTHS.put('{', 5); CHAR_WIDTHS.put('}', 5);
CHAR_WIDTHS.put('|', 2); CHAR_WIDTHS.put('*', 5); CHAR_WIDTHS.put(' ', 4);
CHAR_WIDTHS.put('_', 6);
}
@Override @Override
public String getName() { return "Tablist"; } public String getName() { return "Tablist"; }
@Override @Override
public void onEnable() { public void onEnable() {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig(); FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
if (!vConfig.getBoolean("tablist.enabled", true)) return; if (!vConfig.getBoolean("tablist.enabled", true)) return;
if (Bukkit.getPluginManager().getPlugin("LuckPerms") != null) luckPerms = LuckPermsProvider.get(); if (Bukkit.getPluginManager().getPlugin("LuckPerms") != null) luckPerms = LuckPermsProvider.get();
placeholderAPIEnabled = Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null; placeholderAPIEnabled = Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null;
// Nutzt jetzt das Intervall aus der visuals.yml
long interval = vConfig.getLong("tablist.interval-ticks", 40L); long interval = vConfig.getLong("tablist.interval-ticks", 40L);
refreshTask = Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), this::refreshAll, 10L, interval); refreshTask = Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), this::refreshAll, 10L, interval);
} }
private void refreshAll() { private void refreshAll() {
updateAnimationIndices(); updateAnimationIndices();
for (Player viewer : Bukkit.getOnlinePlayers()) { for (Player viewer : Bukkit.getOnlinePlayers()) {
viewer.setPlayerListHeader(getHeader(viewer)); viewer.setPlayerListHeader(getHeader(viewer));
viewer.setPlayerListFooter(getFooter(viewer)); viewer.setPlayerListFooter(getFooter(viewer));
updateTeamsAndFormatting(viewer);
} updateTeams(viewer);
} }
private void updateTeamsAndFormatting(Player viewer) { }
private void updateTeams(Player viewer) {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig(); FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
boolean showPrefix = vConfig.getBoolean("tablist.show-prefix-in-playerlist", true);
if (!vConfig.getBoolean("tablist.show-prefix-in-playerlist", true)) return;
Scoreboard sb = viewer.getScoreboard(); Scoreboard sb = viewer.getScoreboard();
if (sb == Bukkit.getScoreboardManager().getMainScoreboard()) return; if (sb == Bukkit.getScoreboardManager().getMainScoreboard()) return;
for (Player target : Bukkit.getOnlinePlayers()) { for (Player target : Bukkit.getOnlinePlayers()) {
String sortPriority = getSortPriority(target);
String teamName = sortPriority + target.getName(); String teamName = "tab_" + target.getName();
if (teamName.length() > 16) teamName = teamName.substring(0, 16); if (teamName.length() > 16) teamName = teamName.substring(0, 16);
Team team = sb.getTeam(teamName); Team team = sb.getTeam(teamName);
if (team == null) team = sb.registerNewTeam(teamName); if (team == null) team = sb.registerNewTeam(teamName);
String prefix = showPrefix ? getPlayerPrefix(target) : "§7";
String prefix = getPlayerPrefix(target);
String suffix = getPlayerSuffix(target);
if (!team.getPrefix().equals(prefix)) team.setPrefix(prefix); if (!team.getPrefix().equals(prefix)) team.setPrefix(prefix);
// Hier wird der Pixel-Ausgleich berechnet, damit der Trenner | immer an der gleichen Stelle ist if (!team.getSuffix().equals(suffix)) team.setSuffix(suffix);
String pingSuffix = getAlignedPingSuffix(target.getName(), target.getPing());
if (!team.getSuffix().equals(pingSuffix)) team.setSuffix(pingSuffix);
if (!team.hasEntry(target.getName())) { if (!team.hasEntry(target.getName())) {
team.addEntry(target.getName()); team.addEntry(target.getName());
} }
// Setzt den Listnamen zurück, damit Team-Prefix/Suffix greifen
if (target.getPlayerListName() != null) {
target.setPlayerListName(null);
}
}
} }
private String getAlignedPingSuffix(String name, int ping) {
int currentWidth = 0;
for (char c : name.toCharArray()) {
currentWidth += CHAR_WIDTHS.getOrDefault(c, 6);
} }
int diff = TARGET_PIXEL_WIDTH - currentWidth;
StringBuilder spacer = new StringBuilder(" ");
// Ausgleich mit fetten (5px) und normalen (4px) Leerzeichen für maximale Präzision
while (diff > 0) {
if (diff >= 5) {
spacer.append("§l ");
diff -= 5;
} else {
spacer.append(" ");
diff -= 4;
}
}
String pingColor = (ping < 50) ? "§a" : (ping < 100 ? "§e" : "§c");
// §r bricht das fettgedruckte Leerzeichen ab, damit der Ping normal aussieht
return "§r" + spacer.toString() + "§8| " + pingColor + ping + "ms";
}
private String getSortPriority(Player player) {
if (luckPerms == null) return "z_";
User user = luckPerms.getUserManager().getUser(player.getUniqueId());
if (user == null) return "z_";
Group group = luckPerms.getGroupManager().getGroup(user.getPrimaryGroup());
if (group == null) return "z_";
int weight = group.getWeight().orElse(0);
int invertedWeight = 1000 - weight;
return String.format("%03d", invertedWeight) + "_";
}
private String getPlayerPrefix(Player player) { private String getPlayerPrefix(Player player) {
String prefix = ""; String prefix = "";
if (luckPerms != null) { if (luckPerms != null) {
User user = luckPerms.getUserManager().getUser(player.getUniqueId()); User user = luckPerms.getUserManager().getUser(player.getUniqueId());
if (user != null && user.getCachedData().getMetaData().getPrefix() != null) { if (user != null && user.getCachedData().getMetaData().getPrefix() != null) {
prefix = user.getCachedData().getMetaData().getPrefix(); prefix = user.getCachedData().getMetaData().getPrefix();
} }
} }
if (prefix.isEmpty() && placeholderAPIEnabled) { if (prefix.isEmpty() && placeholderAPIEnabled) {
prefix = PlaceholderAPI.setPlaceholders(player, "%luckperms_prefix%"); prefix = PlaceholderAPI.setPlaceholders(player, "%luckperms_prefix%");
} }
return colorize(prefix.isEmpty() ? "&7" : prefix + " "); return colorize(prefix.isEmpty() ? "&7" : prefix + " ");
} }
private String getPlayerSuffix(Player player) {
// Hier könnte man später auch Suffixe aus der Config laden
return colorize(" &8[&a" + player.getPing() + "ms&8]");
}
private void updateAnimationIndices() { private void updateAnimationIndices() {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig(); FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
List<String> h = vConfig.getStringList("tablist.header-animations"); List<String> h = vConfig.getStringList("tablist.header-animations");
List<String> f = vConfig.getStringList("tablist.footer-animations"); List<String> f = vConfig.getStringList("tablist.footer-animations");
if (!h.isEmpty()) currentHeaderIndex = (currentHeaderIndex + 1) % h.size(); if (!h.isEmpty()) currentHeaderIndex = (currentHeaderIndex + 1) % h.size();
if (!f.isEmpty()) currentFooterIndex = (currentFooterIndex + 1) % f.size(); if (!f.isEmpty()) currentFooterIndex = (currentFooterIndex + 1) % f.size();
} }
private String getHeader(Player p) { private String getHeader(Player p) {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig(); FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
List<String> list = vConfig.getStringList("tablist.header-animations"); List<String> list = vConfig.getStringList("tablist.header-animations");
if (list.isEmpty()) return "§6NexusLobby"; if (list.isEmpty()) return "§6NexusLobby";
return replacePlaceholders(list.get(currentHeaderIndex), p); return replacePlaceholders(list.get(currentHeaderIndex), p);
} }
private String getFooter(Player p) { private String getFooter(Player p) {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig(); FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
List<String> list = vConfig.getStringList("tablist.footer-animations"); List<String> list = vConfig.getStringList("tablist.footer-animations");
if (list.isEmpty()) return "§7Willkommen"; if (list.isEmpty()) return "§7Willkommen";
return replacePlaceholders(list.get(currentFooterIndex), p); return replacePlaceholders(list.get(currentFooterIndex), p);
} }
private String replacePlaceholders(String text, Player p) { private String replacePlaceholders(String text, Player p) {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig(); FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
text = text.replace("{server}", vConfig.getString("tablist.server-name", "NexusLobby")); text = text.replace("{server}", vConfig.getString("tablist.server-name", "NexusLobby"));
text = text.replace("{player}", p.getName()); text = text.replace("{player}", p.getName());
text = text.replace("{online}", String.valueOf(Bukkit.getOnlinePlayers().size())); text = text.replace("{online}", String.valueOf(Bukkit.getOnlinePlayers().size()));
text = text.replace("{staff}", String.valueOf(getOnlineStaffCount())); text = text.replace("{staff}", String.valueOf(getOnlineStaffCount()));
text = text.replace("{separator}", vConfig.getString("tablist.separator-line", "")); text = text.replace("{separator}", vConfig.getString("tablist.separator-line", ""));
text = text.replace("{website}", vConfig.getBoolean("tablist.show-website") ? vConfig.getString("tablist.website", "") : ""); text = text.replace("{website}", vConfig.getBoolean("tablist.show-website") ? vConfig.getString("tablist.website", "") : "");
text = text.replace("{teamspeak}", vConfig.getBoolean("tablist.show-teamspeak") ? vConfig.getString("tablist.teamspeak-address", "") : ""); text = text.replace("{teamspeak}", vConfig.getBoolean("tablist.show-teamspeak") ? vConfig.getString("tablist.teamspeak-address", "") : "");
text = text.replace("{discord}", vConfig.getBoolean("tablist.show-discord") ? vConfig.getString("tablist.discord-address", "") : ""); text = text.replace("{discord}", vConfig.getBoolean("tablist.show-discord") ? vConfig.getString("tablist.discord-address", "") : "");
if (placeholderAPIEnabled) { if (placeholderAPIEnabled) {
text = PlaceholderAPI.setPlaceholders(p, text); text = PlaceholderAPI.setPlaceholders(p, text);
} }
return colorize(text); return colorize(text);
} }
private int getOnlineStaffCount() { private int getOnlineStaffCount() {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig(); FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
String permission = vConfig.getString("tablist.staff-permission", "nexuslobby.staff"); String permission = vConfig.getString("tablist.staff-permission", "nexuslobby.staff");
int count = 0; int count = 0;
for (Player p : Bukkit.getOnlinePlayers()) { for (Player p : Bukkit.getOnlinePlayers()) {
if (p.hasPermission(permission)) count++; if (p.hasPermission(permission)) count++;
} }
return count; return count;
} }
private String colorize(String s) { private String colorize(String s) {
return s == null ? "" : s.replace("&", "§"); return s == null ? "" : s.replace("&", "§");
} }
@Override @Override
public void onDisable() { public void onDisable() {
if (refreshTask != null) refreshTask.cancel(); if (refreshTask != null) refreshTask.cancel();
} }
} }

View File

@@ -11,6 +11,14 @@ spawn:
yaw: 0.0 # Blickrichtung yaw: 0.0 # Blickrichtung
pitch: 0.0 # Blickrichtung pitch: 0.0 # Blickrichtung
worldborder:
enabled: true
type: "SQUARE" # oder "CIRCLE"
radius: 50.0
center:
pos1:
pos2:
# --- Lobby Einstellungen --- # --- Lobby Einstellungen ---
lobby: lobby:
allow-fly: false # Spieler dürfen fliegen allow-fly: false # Spieler dürfen fliegen
@@ -36,11 +44,11 @@ items:
build-toggle: build-toggle:
enabled: true enabled: true
displayname: "&aBaumodus" displayname: "&aBaumodus"
slot: 1 slot: 0
gadget: gadget:
enabled: true enabled: false
displayname: "&bGadgets" displayname: "&bGadgets"
slot: 2 slot: 8
# --- Portal Einstellungen --- # --- Portal Einstellungen ---
portals: portals:

View File

@@ -1,6 +1,6 @@
name: NexusLobby name: NexusLobby
main: de.nexuslobby.NexusLobby main: de.nexuslobby.NexusLobby
version: "1.0.1" version: "1.0.4"
api-version: "1.21" api-version: "1.21"
author: M_Viper author: M_Viper
description: Modular Lobby Plugin description: Modular Lobby Plugin
@@ -12,49 +12,60 @@ commands:
usage: /portal <args> usage: /portal <args>
permission: nexuslobby.portal permission: nexuslobby.portal
permission-message: "§cKeine Rechte!" permission-message: "§cKeine Rechte!"
giveportalwand: giveportalwand:
description: Gibt das Portal-Werkzeug description: Gibt das Portal-Werkzeug
usage: /giveportalwand usage: /giveportalwand
permission: nexuslobby.portal.give permission: nexuslobby.portal.give
permission-message: "§cDu hast keine Berechtigung dafür." permission-message: "§cDu hast keine Berechtigung dafür."
maintenance: maintenance:
description: Aktiviert oder deaktiviert den Wartungsmodus description: Aktiviert oder deaktiviert den Wartungsmodus
usage: /maintenance <on|off> usage: /maintenance <on|off>
permission: nexuslobby.maintenance permission: nexuslobby.maintenance
permission-message: "§cDu hast keine Rechte!" permission-message: "§cDu hast keine Rechte!"
serverswitcher: serverswitcher:
description: Öffnet die Server Switcher GUI description: Öffnet die Server Switcher GUI
usage: /serverswitcher usage: /serverswitcher
permission: nexuslobby.serverswitcher permission: nexuslobby.serverswitcher
permission-message: "§cDu hast keine Rechte!" permission-message: "§cDu hast keine Rechte!"
settings: settings:
description: Öffnet das Lobby-Einstellungsmenü (Gamerules) description: Öffnet das Lobby-Einstellungsmenü (Gamerules)
usage: /settings usage: /settings
permission: nexuslobby.admin permission: nexuslobby.admin
permission-message: "§cDu hast keine Rechte für die Admin-Einstellungen!" permission-message: "§cDu hast keine Rechte für die Admin-Einstellungen!"
build: build:
description: Aktiviert oder deaktiviert den Baumodus description: Aktiviert oder deaktiviert den Baumodus
usage: /build usage: /build
permission: nexuslobby.build permission: nexuslobby.build
permission-message: "§cDu hast keine Rechte!" permission-message: "§cDu hast keine Rechte!"
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] usage: /nexuslobby [reload|setspawn]
aliases: [nexus] aliases: [nexus]
# --- ArmorStandTools Sektion ---
nexustools: nexustools:
description: Nexus ArmorStand Editor description: Nexus ArmorStand Editor
aliases: [nt, ntools, astools] aliases: [nt, ntools, astools]
nexuscmd: nexuscmd:
description: Nexus Command Binder description: Nexus Command Binder
aliases: [ncmd, ascmd] aliases: [ncmd, ascmd]
holo:
description: Verwalte Lobby Hologramme (Text-Displays)
usage: /holo <create|delete> <id> [text]
permission: nexuslobby.hologram
mapart:
description: Erstellt ein Bild-Raster auf unsichtbaren Rahmen
usage: /mapart <URL> <BxH>
permission: nexuslobby.mapart
intro:
description: Verwalte und teste die Cinematic Intro Tour
usage: /intro <add|clear|start>
permission: nexuslobby.admin
border:
description: Verwalte die unsichtbare Lobby-Begrenzung
usage: /border <circle|square|disable>
permission: nexuslobby.admin
spawn:
description: Teleportiert dich zum Lobby-Spawnpunkt
usage: /spawn
permissions: permissions:
nexuslobby.portal: nexuslobby.portal:
@@ -70,7 +81,7 @@ permissions:
description: Zugriff auf den Server Switcher description: Zugriff auf den Server Switcher
default: true default: true
nexuslobby.admin: nexuslobby.admin:
description: Voller Zugriff auf Lobby-Gamerules, Einstellungen und Reload description: Voller Zugriff auf Lobby-Gamerules, Einstellungen, Intro, Border und Reload
default: op default: op
nexuslobby.build: nexuslobby.build:
description: Erlaubt das Umgehen des Lobby-Schutzes zum Bauen description: Erlaubt das Umgehen des Lobby-Schutzes zum Bauen
@@ -81,3 +92,12 @@ permissions:
nexuslobby.armorstand.cmd: nexuslobby.armorstand.cmd:
description: Erlaubt das Binden von Commands via NexusCmd description: Erlaubt das Binden von Commands via NexusCmd
default: op default: op
nexuslobby.armorstand.dynamic:
description: Erlaubt das Markieren von dynamischen NPCs
default: op
nexuslobby.hologram:
description: Erlaubt das Erstellen von Text-Display Hologrammen
default: op
nexuslobby.mapart:
description: Erlaubt das Erstellen von Map-Art Bildern
default: op