Update from Git Manager GUI

This commit is contained in:
2026-01-24 17:03:48 +01:00
parent 4c847401a0
commit 1db87e81f0
13 changed files with 1077 additions and 265 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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