From 3ec1d905dcd7f120942fe5042fbe45baea01fe9f Mon Sep 17 00:00:00 2001 From: M_Viper Date: Sun, 11 Oct 2026 17:15:35 +0000 Subject: [PATCH] Upload via GUI (84 Dateien) --- pom.xml | 2 +- .../nexuslobby/modules/ScoreboardModule.java | 8 +- .../modules/player/PlayerInspectModule.java | 390 ++++++++--------- .../modules/portal/PortalManager.java | 4 +- .../modules/servers/ServerChecker.java | 268 ++++++------ .../servers/ServerSwitcherListener.java | 6 +- .../de/nexuslobby/utils/ConfigValidator.java | 311 +++++++------- .../java/de/nexuslobby/utils/LangManager.java | 75 ++-- .../de/nexuslobby/utils/UpdateChecker.java | 70 +-- src/main/resources/config.yml | 3 + src/main/resources/lang.yml | 406 +++++++++--------- src/main/resources/plugin.yml | 2 +- 12 files changed, 780 insertions(+), 765 deletions(-) diff --git a/pom.xml b/pom.xml index 9ca7469..0278905 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ de.nexuslobby NexusLobby - 1.9 + 2.0 jar NexusLobby diff --git a/src/main/java/de/nexuslobby/modules/ScoreboardModule.java b/src/main/java/de/nexuslobby/modules/ScoreboardModule.java index 903a9b2..3757fb4 100644 --- a/src/main/java/de/nexuslobby/modules/ScoreboardModule.java +++ b/src/main/java/de/nexuslobby/modules/ScoreboardModule.java @@ -173,22 +173,22 @@ public class ScoreboardModule implements Module, Listener { playerBoards.put(player.getUniqueId(), Bukkit.getScoreboardManager().getNewScoreboard()); } player.setScoreboard(playerBoards.get(player.getUniqueId())); - player.sendMessage("§7[§6Nexus§7] §aScoreboard eingeschaltet."); + player.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§aScoreboard eingeschaltet."); updateSidebar(player); // Sofort aktualisieren } else { hiddenPlayers.add(player.getUniqueId()); player.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard()); - player.sendMessage("§7[§6Nexus§7] §cScoreboard ausgeschaltet."); + player.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§cScoreboard ausgeschaltet."); } } public void setAdminMode(Player player, boolean admin) { if (admin) { adminModePlayers.add(player.getUniqueId()); - player.sendMessage("§7[§6Nexus§7] §eModus: §cAdmin-Scoreboard §7(Owner-Sektion)"); + player.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§eModus: §cAdmin-Scoreboard §7(Owner-Sektion)"); } else { adminModePlayers.remove(player.getUniqueId()); - player.sendMessage("§7[§6Nexus§7] §eModus: §6Spieler-Scoreboard §7(Default-Sektion)"); + player.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§eModus: §6Spieler-Scoreboard §7(Default-Sektion)"); } updateSidebar(player); } diff --git a/src/main/java/de/nexuslobby/modules/player/PlayerInspectModule.java b/src/main/java/de/nexuslobby/modules/player/PlayerInspectModule.java index 4a93140..45c46b6 100644 --- a/src/main/java/de/nexuslobby/modules/player/PlayerInspectModule.java +++ b/src/main/java/de/nexuslobby/modules/player/PlayerInspectModule.java @@ -1,196 +1,196 @@ -package de.nexuslobby.modules.player; - -import de.nexuslobby.NexusLobby; -import de.nexuslobby.api.Module; -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.Statistic; -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.PlayerInteractEntityEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.SkullMeta; - -import java.util.ArrayList; -import java.util.List; - -/** - * Modul zur Inspektion von Spielern in der Lobby. - * Zeigt detaillierte Statistiken in einer interaktiven GUI an. - */ -public class PlayerInspectModule implements Module, Listener { - - @Override - public String getName() { - return "PlayerInspect"; - } - - @Override - public void onEnable() { - Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance()); - } - - @Override - public void onDisable() { - org.bukkit.event.HandlerList.unregisterAll(this); - } - - @EventHandler - public void onPlayerInteract(PlayerInteractEntityEvent event) { - // Prüfen, ob ein Spieler rechtsgeklickt wurde - if (event.getRightClicked() instanceof Player target) { - Player viewer = event.getPlayer(); - - // GUI nur öffnen, wenn die Hand leer ist (verhindert Konflikte mit Items) - if (viewer.getInventory().getItemInMainHand().getType() == Material.AIR) { - openDetailedInspectGUI(viewer, target); - } - } - } - - @EventHandler - public void onInventoryClick(InventoryClickEvent event) { - // Sicherstellen, dass es unser Statistik-Inventar ist anhand des Titels - if (event.getView().getTitle().contains("Statistiken")) { - event.setCancelled(true); // Verhindert, dass Items herausgenommen werden - - ItemStack clickedItem = event.getCurrentItem(); - if (clickedItem == null || clickedItem.getType() == Material.AIR) return; - - // Logik für den Schließen-Button (Barriere) - if (clickedItem.getType() == Material.BARRIER) { - event.getWhoClicked().closeInventory(); - } - } - } - - public void openDetailedInspectGUI(Player viewer, Player target) { - // Titel mit Farbcodes - String title = "§8» §3Statistiken: §b" + target.getName(); - Inventory gui = Bukkit.createInventory(null, 45, title); - - // --- Design: Hintergrund mit Glasscheiben füllen --- - ItemStack separator = createSimpleItem(Material.GRAY_STAINED_GLASS_PANE, " "); - int[] borderSlots = { - 0,1,2,3,4,5,6,7,8, - 9,17, - 18,26, - 27,35, - 36,37,38,39,41,42,43,44 - }; - for (int slot : borderSlots) gui.setItem(slot, separator); - - // --- Kopf des Spielers mit LuckPerms Prefix via PAPI --- - ItemStack head = new ItemStack(Material.PLAYER_HEAD); - SkullMeta headMeta = (SkullMeta) head.getItemMeta(); - if (headMeta != null) { - headMeta.setOwningPlayer(target); - headMeta.setDisplayName("§e§l" + target.getName()); - - List lore = new ArrayList<>(); - lore.add("§8§m-----------------------"); - - // LuckPerms Prefix über PlaceholderAPI auslesen (falls vorhanden) - String prefix = "%luckperms_prefix%"; - if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { - try { - prefix = PlaceholderAPI.setPlaceholders(target, prefix); - } catch (NoClassDefFoundError ignored) { - // PlaceholderAPI fehlt zur Laufzeit - } - } - - // KORREKTUR: Farbcodes (&) in echte Minecraft-Farben (§) umwandeln - prefix = ChatColor.translateAlternateColorCodes('&', prefix); - - lore.add("§7Rang: " + (prefix.isEmpty() ? "§fSpieler" : prefix)); - lore.add("§7Level: §a" + target.getLevel()); - lore.add("§7Status: §aOnline"); - lore.add("§8§m-----------------------"); - - headMeta.setLore(lore); - head.setItemMeta(headMeta); - } - gui.setItem(4, head); - - // --- Statistik Kategorie: KAMPF (Slot 19) --- - gui.setItem(19, createStatsItem(Material.DIAMOND_SWORD, "§c§lKampf-Statistiken", - "§8» §7Spieler-Kills: §f" + target.getStatistic(Statistic.PLAYER_KILLS), - "§8» §7Mob-Kills: §f" + target.getStatistic(Statistic.MOB_KILLS), - "§8» §7Tode gesamt: §f" + target.getStatistic(Statistic.DEATHS), - "§8» §7Schaden verursacht: §f" + (target.getStatistic(Statistic.DAMAGE_DEALT) / 10) + " ❤")); - - // --- Statistik Kategorie: ARBEIT (Slot 21) --- - // Optimierte Abfrage für wichtige Blöcke - int totalBlocks = target.getStatistic(Statistic.MINE_BLOCK, Material.STONE) + - target.getStatistic(Statistic.MINE_BLOCK, Material.DIRT) + - target.getStatistic(Statistic.MINE_BLOCK, Material.COBBLESTONE); - - gui.setItem(21, createStatsItem(Material.IRON_PICKAXE, "§e§lHandwerk & Fleiß", - "§8» §7Blöcke (S/D/C): §f" + totalBlocks, - "§8» §7Items gedroppt: §f" + target.getStatistic(Statistic.DROP_COUNT), - "§8» §7Fische gefangen: §f" + target.getStatistic(Statistic.FISH_CAUGHT), - "§8» §7Glocken geläutet: §f" + target.getStatistic(Statistic.BELL_RING))); - - // --- Statistik Kategorie: BEWEGUNG (Slot 23) --- - double walkKm = target.getStatistic(Statistic.WALK_ONE_CM) / 100000.0; - double flyKm = target.getStatistic(Statistic.FLY_ONE_CM) / 100000.0; - gui.setItem(23, createStatsItem(Material.GOLDEN_BOOTS, "§b§lReise-Statistiken", - "§8» §7Gelaufen: §f" + String.format("%.2f", walkKm) + " km", - "§8» §7Geflogen: §f" + String.format("%.2f", flyKm) + " km", - "§8» §7Sprünge: §f" + target.getStatistic(Statistic.JUMP))); - - // --- Statistik Kategorie: ZEIT (Slot 25) --- - long ticks = target.getStatistic(Statistic.PLAY_ONE_MINUTE); - long hours = ticks / 72000; - long mins = (ticks % 72000) / 1200; - gui.setItem(25, createStatsItem(Material.CLOCK, "§a§lZeit-Statistiken", - "§8» §7Spielzeit: §f" + hours + " Std. " + mins + " Min.", - "§8» §7Letzter Tod vor: §f" + (target.getStatistic(Statistic.TIME_SINCE_DEATH) / 1200) + " Min.", - "§8» §7Tage auf Server: §f" + (target.getStatistic(Statistic.PLAY_ONE_MINUTE) / 1728000))); - - // --- Schließen Button (Slot 40) --- - gui.setItem(40, createSimpleItem(Material.BARRIER, "§c§lMenü schließen")); - - // Inventar für den Zuschauer öffnen - viewer.openInventory(gui); - } - - /** - * Erstellt ein ItemStack mit Name und Lore-Zeilen inklusive einer Leerzeile am Anfang. - */ - private ItemStack createStatsItem(Material material, String displayName, String... loreLines) { - ItemStack item = new ItemStack(material); - ItemMeta meta = item.getItemMeta(); - if (meta != null) { - meta.setDisplayName(displayName); - List lore = new ArrayList<>(); - lore.add(" "); // Leerzeile für sauberes Design - for (String line : loreLines) { - lore.add(line); - } - meta.setLore(lore); - item.setItemMeta(meta); - } - return item; - } - - /** - * Erstellt ein einfaches ItemStack ohne Lore für Designzwecke. - */ - private ItemStack createSimpleItem(Material material, String displayName) { - ItemStack item = new ItemStack(material); - ItemMeta meta = item.getItemMeta(); - if (meta != null) { - meta.setDisplayName(displayName); - item.setItemMeta(meta); - } - return item; - } +package de.nexuslobby.modules.player; + +import de.nexuslobby.NexusLobby; +import de.nexuslobby.api.Module; +import me.clip.placeholderapi.PlaceholderAPI; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.Statistic; +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.PlayerInteractEntityEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.ArrayList; +import java.util.List; + +/** + * Modul zur Inspektion von Spielern in der Lobby. + * Zeigt detaillierte Statistiken in einer interaktiven GUI an. + */ +public class PlayerInspectModule implements Module, Listener { + + @Override + public String getName() { + return "PlayerInspect"; + } + + @Override + public void onEnable() { + Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance()); + } + + @Override + public void onDisable() { + org.bukkit.event.HandlerList.unregisterAll(this); + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEntityEvent event) { + // Prüfen, ob ein Spieler rechtsgeklickt wurde + if (event.getRightClicked() instanceof Player target) { + Player viewer = event.getPlayer(); + + // GUI nur öffnen, wenn die Hand leer ist (verhindert Konflikte mit Items) + if (viewer.getInventory().getItemInMainHand().getType() == Material.AIR) { + openDetailedInspectGUI(viewer, target); + } + } + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + // Sicherstellen, dass es unser Statistik-Inventar ist anhand des Titels + if (event.getView().getTitle().contains("Statistiken")) { + event.setCancelled(true); // Verhindert, dass Items herausgenommen werden + + ItemStack clickedItem = event.getCurrentItem(); + if (clickedItem == null || clickedItem.getType() == Material.AIR) return; + + // Logik für den Schließen-Button (Barriere) + if (clickedItem.getType() == Material.BARRIER) { + event.getWhoClicked().closeInventory(); + } + } + } + + public void openDetailedInspectGUI(Player viewer, Player target) { + // Titel mit Farbcodes + String title = "§8» §3Statistiken: §b" + target.getName(); + Inventory gui = Bukkit.createInventory(null, 45, title); + + // --- Design: Hintergrund mit Glasscheiben füllen --- + ItemStack separator = createSimpleItem(Material.GRAY_STAINED_GLASS_PANE, " "); + int[] borderSlots = { + 0,1,2,3,4,5,6,7,8, + 9,17, + 18,26, + 27,35, + 36,37,38,39,41,42,43,44 + }; + for (int slot : borderSlots) gui.setItem(slot, separator); + + // --- Kopf des Spielers mit LuckPerms Prefix via PAPI --- + ItemStack head = new ItemStack(Material.PLAYER_HEAD); + SkullMeta headMeta = (SkullMeta) head.getItemMeta(); + if (headMeta != null) { + headMeta.setOwningPlayer(target); + headMeta.setDisplayName("§e§l" + target.getName()); + + List lore = new ArrayList<>(); + lore.add("§8§m-----------------------"); + + // LuckPerms Prefix über PlaceholderAPI auslesen (falls vorhanden) + String prefix = "%luckperms_prefix%"; + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + try { + prefix = PlaceholderAPI.setPlaceholders(target, prefix); + } catch (NoClassDefFoundError ignored) { + // PlaceholderAPI fehlt zur Laufzeit + } + } + + // KORREKTUR: Farbcodes (&) in echte Minecraft-Farben (§) umwandeln + prefix = ChatColor.translateAlternateColorCodes('&', prefix); + + lore.add("§7Rang: " + (prefix.isEmpty() ? "§fSpieler" : prefix)); + lore.add("§7Level: §a" + target.getLevel()); + lore.add("§7Status: §aOnline"); + lore.add("§8§m-----------------------"); + + headMeta.setLore(lore); + head.setItemMeta(headMeta); + } + gui.setItem(4, head); + + // --- Statistik Kategorie: KAMPF (Slot 19) --- + gui.setItem(19, createStatsItem(Material.DIAMOND_SWORD, "§c§lKampf-Statistiken", + "§8» §7Spieler-Kills: §f" + target.getStatistic(Statistic.PLAYER_KILLS), + "§8» §7Mob-Kills: §f" + target.getStatistic(Statistic.MOB_KILLS), + "§8» §7Tode gesamt: §f" + target.getStatistic(Statistic.DEATHS), + "§8» §7Schaden verursacht: §f" + (target.getStatistic(Statistic.DAMAGE_DEALT) / 10) + " ❤")); + + // --- Statistik Kategorie: ARBEIT (Slot 21) --- + // Optimierte Abfrage für wichtige Blöcke + int totalBlocks = target.getStatistic(Statistic.MINE_BLOCK, Material.STONE) + + target.getStatistic(Statistic.MINE_BLOCK, Material.DIRT) + + target.getStatistic(Statistic.MINE_BLOCK, Material.COBBLESTONE); + + gui.setItem(21, createStatsItem(Material.IRON_PICKAXE, "§e§lHandwerk & Fleiß", + "§8» §7Blöcke (S/D/C): §f" + totalBlocks, + "§8» §7Items gedroppt: §f" + target.getStatistic(Statistic.DROP_COUNT), + "§8» §7Fische gefangen: §f" + target.getStatistic(Statistic.FISH_CAUGHT), + "§8» §7Glocken geläutet: §f" + target.getStatistic(Statistic.BELL_RING))); + + // --- Statistik Kategorie: BEWEGUNG (Slot 23) --- + double walkKm = target.getStatistic(Statistic.WALK_ONE_CM) / 100000.0; + double flyKm = target.getStatistic(Statistic.FLY_ONE_CM) / 100000.0; + gui.setItem(23, createStatsItem(Material.GOLDEN_BOOTS, "§b§lReise-Statistiken", + "§8» §7Gelaufen: §f" + String.format("%.2f", walkKm) + " km", + "§8» §7Geflogen: §f" + String.format("%.2f", flyKm) + " km", + "§8» §7Sprünge: §f" + target.getStatistic(Statistic.JUMP))); + + // --- Statistik Kategorie: ZEIT (Slot 25) --- + long ticks = target.getStatistic(Statistic.PLAY_ONE_MINUTE); + long hours = ticks / 72000; + long mins = (ticks % 72000) / 1200; + gui.setItem(25, createStatsItem(Material.CLOCK, "§a§lZeit-Statistiken", + "§8» §7Spielzeit: §f" + hours + " Std. " + mins + " Min.", + "§8» §7Letzter Tod vor: §f" + (target.getStatistic(Statistic.TIME_SINCE_DEATH) / 1200) + " Min.", + "§8» §7Tage auf Server: §f" + (target.getStatistic(Statistic.PLAY_ONE_MINUTE) / 1728000))); + + // --- Schließen Button (Slot 40) --- + gui.setItem(40, createSimpleItem(Material.BARRIER, "§c§lMenü schließen")); + + // Inventar für den Zuschauer öffnen + viewer.openInventory(gui); + } + + /** + * Erstellt ein ItemStack mit Name und Lore-Zeilen inklusive einer Leerzeile am Anfang. + */ + private ItemStack createStatsItem(Material material, String displayName, String... loreLines) { + ItemStack item = new ItemStack(material); + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + meta.setDisplayName(displayName); + List lore = new ArrayList<>(); + lore.add(" "); // Leerzeile für sauberes Design + for (String line : loreLines) { + lore.add(line); + } + meta.setLore(lore); + item.setItemMeta(meta); + } + return item; + } + + /** + * Erstellt ein einfaches ItemStack ohne Lore für Designzwecke. + */ + private ItemStack createSimpleItem(Material material, String displayName) { + ItemStack item = new ItemStack(material); + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + meta.setDisplayName(displayName); + item.setItemMeta(meta); + } + return item; + } } \ No newline at end of file diff --git a/src/main/java/de/nexuslobby/modules/portal/PortalManager.java b/src/main/java/de/nexuslobby/modules/portal/PortalManager.java index c9ff6b2..86b5d10 100644 --- a/src/main/java/de/nexuslobby/modules/portal/PortalManager.java +++ b/src/main/java/de/nexuslobby/modules/portal/PortalManager.java @@ -131,10 +131,10 @@ public class PortalManager implements Module, Listener { p.sendMessage("§7§m----------------------------------"); if (volume < 500) { - p.sendMessage("§e[Nexus] Kleiner Bereich erkannt (Portal-Größe)"); + p.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§eKleiner Bereich erkannt (Portal-Größe)"); p.sendMessage("§fBefehl: §b/portal create "); } else { - p.sendMessage("§6[Nexus] Großer Bereich erkannt (WorldBorder-Größe)"); + p.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§6Großer Bereich erkannt (WorldBorder-Größe)"); p.sendMessage("§fBefehl: §a/border square"); } p.sendMessage("§7§m----------------------------------"); diff --git a/src/main/java/de/nexuslobby/modules/servers/ServerChecker.java b/src/main/java/de/nexuslobby/modules/servers/ServerChecker.java index 2a2fcde..25f4e2a 100644 --- a/src/main/java/de/nexuslobby/modules/servers/ServerChecker.java +++ b/src/main/java/de/nexuslobby/modules/servers/ServerChecker.java @@ -1,135 +1,135 @@ -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 org.bukkit.scheduler.BukkitTask; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.concurrent.CompletableFuture; - -public class ServerChecker { - - private static BukkitTask globalCheckerTask = null; - - /** - * Prüft asynchron, ob ein Server unter der angegebenen IP und Port erreichbar ist. - */ - public static CompletableFuture 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() { - // Cancel alten Task falls vorhanden - stopGlobalChecker(); - - // WICHTIG: runTaskTimer (synchron), um den Main-Thread für getEntitiesByClass zu nutzen - globalCheckerTask = 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); - } - - /** - * Stoppt den Server-Checker Task - */ - public static void stopGlobalChecker() { - if (globalCheckerTask != null && !globalCheckerTask.isCancelled()) { - globalCheckerTask.cancel(); - globalCheckerTask = null; - } - } - - /** - * 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; - } - } - } +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 org.bukkit.scheduler.BukkitTask; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.concurrent.CompletableFuture; + +public class ServerChecker { + + private static BukkitTask globalCheckerTask = null; + + /** + * Prüft asynchron, ob ein Server unter der angegebenen IP und Port erreichbar ist. + */ + public static CompletableFuture 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() { + // Cancel alten Task falls vorhanden + stopGlobalChecker(); + + // WICHTIG: runTaskTimer (synchron), um den Main-Thread für getEntitiesByClass zu nutzen + globalCheckerTask = 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); + } + + /** + * Stoppt den Server-Checker Task + */ + public static void stopGlobalChecker() { + if (globalCheckerTask != null && !globalCheckerTask.isCancelled()) { + globalCheckerTask.cancel(); + globalCheckerTask = null; + } + } + + /** + * 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; + } + } + } } \ No newline at end of file diff --git a/src/main/java/de/nexuslobby/modules/servers/ServerSwitcherListener.java b/src/main/java/de/nexuslobby/modules/servers/ServerSwitcherListener.java index 241f5c6..04261b8 100644 --- a/src/main/java/de/nexuslobby/modules/servers/ServerSwitcherListener.java +++ b/src/main/java/de/nexuslobby/modules/servers/ServerSwitcherListener.java @@ -178,7 +178,7 @@ public class ServerSwitcherListener implements Listener, CommandExecutor { private void connectBungee(Player player, String serverName) { if (!Bukkit.getMessenger().isOutgoingChannelRegistered( NexusLobby.getInstance(), "BungeeCord")) { - player.sendMessage("§8[§6Nexus§8] §cKein BungeeCord-Kanal registriert."); + player.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§cKein BungeeCord-Kanal registriert."); return; } try { @@ -187,9 +187,9 @@ public class ServerSwitcherListener implements Listener, CommandExecutor { out.writeUTF("Connect"); out.writeUTF(serverName); player.sendPluginMessage(NexusLobby.getInstance(), "BungeeCord", b.toByteArray()); - player.sendMessage("§8[§6Nexus§8] §7Verbinde mit §e" + serverName + "§7..."); + player.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§7Verbinde mit §e" + serverName + "§7..."); } catch (java.io.IOException ex) { - player.sendMessage("§8[§6Nexus§8] §cFehler beim Verbinden: " + ex.getMessage()); + player.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§cFehler beim Verbinden: " + ex.getMessage()); } } } \ No newline at end of file diff --git a/src/main/java/de/nexuslobby/utils/ConfigValidator.java b/src/main/java/de/nexuslobby/utils/ConfigValidator.java index 4470523..ed71357 100644 --- a/src/main/java/de/nexuslobby/utils/ConfigValidator.java +++ b/src/main/java/de/nexuslobby/utils/ConfigValidator.java @@ -1,155 +1,156 @@ -package de.nexuslobby.utils; - -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.plugin.java.JavaPlugin; - -import java.util.ArrayList; -import java.util.List; - -/** - * Validiert Config-Werte und setzt Default-Werte wenn nötig - */ -public class ConfigValidator { - - private final JavaPlugin plugin; - private final FileConfiguration config; - private final List warnings = new ArrayList<>(); - private boolean modified = false; - - public ConfigValidator(JavaPlugin plugin, FileConfiguration config) { - this.plugin = plugin; - this.config = config; - } - - /** - * Validiert alle Config-Werte und gibt Warnings aus - */ - public void validate() { - validateSpawn(); - validateLobbySettings(); - validateTablist(); - validateBall(); - validateModules(); - - // Warnings ausgeben - if (!warnings.isEmpty()) { - plugin.getLogger().warning("=== Config-Validierung ==="); - warnings.forEach(msg -> plugin.getLogger().warning(" " + msg)); - plugin.getLogger().warning("========================="); - } - - // Config speichern wenn Änderungen vorgenommen wurden - if (modified) { - plugin.saveConfig(); - plugin.getLogger().info("Config wurde mit Default-Werten aktualisiert."); - } - } - - private void validateSpawn() { - ensureDefault("spawn.world", "world"); - ensureDefault("spawn.x", 0.5); - ensureDefault("spawn.y", 64.0); - ensureDefault("spawn.z", 0.5); - ensureDefault("spawn.yaw", 0.0); - ensureDefault("spawn.pitch", 0.0); - - // Validiere Y-Koordinate - double y = config.getDouble("spawn.y", 64.0); - if (y < -64 || y > 320) { - warnings.add("spawn.y (" + y + ") außerhalb des gültigen Bereichs (-64 bis 320)"); - } - } - - private void validateLobbySettings() { - ensureDefault("lobby.allow-fly", false); - ensureDefault("lobby.pvp-enabled", false); - ensureDefault("lobby.build-enabled", false); - ensureDefault("lobby.default-gamemode", "Adventure"); - ensureDefault("lobby.clear-inventory-on-join", true); - - // Validiere GameMode - String gamemode = config.getString("lobby.default-gamemode", "Adventure"); - if (!isValidGameMode(gamemode)) { - warnings.add("lobby.default-gamemode '" + gamemode + "' ist ungültig. Nutze: Survival, Creative, Adventure oder Spectator"); - } - } - - private void validateTablist() { - ensureDefault("tablist.enabled", true); - ensureDefault("tablist.header", "&6Willkommen auf &eNexusLobby"); - ensureDefault("tablist.footer", "&7Viel Spaß!"); - ensureDefault("tablist.refresh-interval", 40); - - // Validiere Refresh-Interval - int interval = config.getInt("tablist.refresh-interval", 40); - if (interval < 1) { - warnings.add("tablist.refresh-interval (" + interval + ") ist zu klein. Minimum ist 1 Tick."); - config.set("tablist.refresh-interval", 1); - modified = true; - } else if (interval > 200) { - warnings.add("tablist.refresh-interval (" + interval + ") ist sehr hoch. Empfohlen: 20-100 Ticks."); - } - } - - private void validateBall() { - ensureDefault("ball.respawn_delay", 60); - - // Validiere Respawn-Delay - long delay = config.getLong("ball.respawn_delay", 60); - if (delay < 5) { - warnings.add("ball.respawn_delay (" + delay + "s) ist zu kurz. Minimum empfohlen: 5 Sekunden."); - } else if (delay > 600) { - warnings.add("ball.respawn_delay (" + delay + "s) ist sehr lang. Empfohlen: 30-120 Sekunden."); - } - - // Prüfe ob Spawn gesetzt ist - if (!config.contains("ball.spawn")) { - warnings.add("ball.spawn nicht gesetzt. Nutze /nexuslobby ball setspawn"); - } - } - - private void validateModules() { - // Worldborder - if (config.contains("worldborder.enabled")) { - ensureDefault("worldborder.type", "SQUARE"); - ensureDefault("worldborder.radius", 50.0); - - double radius = config.getDouble("worldborder.radius", 50.0); - if (radius < 10) { - warnings.add("worldborder.radius (" + radius + ") ist sehr klein. Minimum empfohlen: 10 Blöcke."); - } else if (radius > 10000) { - warnings.add("worldborder.radius (" + radius + ") ist extrem groß. Performance-Warnung!"); - } - - String type = config.getString("worldborder.type", "SQUARE"); - if (!type.equalsIgnoreCase("SQUARE") && !type.equalsIgnoreCase("CIRCLE")) { - warnings.add("worldborder.type '" + type + "' ist ungültig. Nutze: SQUARE oder CIRCLE"); - } - } - - // Items Modul - if (config.contains("items.lobby-tools")) { - // Prüfe ob Slot-Nummern gültig sind (0-8) - for (String toolKey : config.getConfigurationSection("items.lobby-tools").getKeys(false)) { - int slot = config.getInt("items.lobby-tools." + toolKey + ".slot", -1); - if (slot < 0 || slot > 8) { - warnings.add("items.lobby-tools." + toolKey + ".slot (" + slot + ") ist ungültig (0-8)"); - } - } - } - } - - private void ensureDefault(String path, Object defaultValue) { - if (!config.contains(path)) { - config.set(path, defaultValue); - modified = true; - } - } - - private boolean isValidGameMode(String mode) { - return mode.equalsIgnoreCase("Survival") || - mode.equalsIgnoreCase("Creative") || - mode.equalsIgnoreCase("Adventure") || - mode.equalsIgnoreCase("Spectator"); - } -} +package de.nexuslobby.utils; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.ArrayList; +import java.util.List; + +/** + * Validiert Config-Werte und setzt Default-Werte wenn nötig + */ +public class ConfigValidator { + + private final JavaPlugin plugin; + private final FileConfiguration config; + private final List warnings = new ArrayList<>(); + private boolean modified = false; + + public ConfigValidator(JavaPlugin plugin, FileConfiguration config) { + this.plugin = plugin; + this.config = config; + } + + /** + * Validiert alle Config-Werte und gibt Warnings aus + */ + public void validate() { + ensureDefault("prefix", "&8[&6Nexus&8] "); + validateSpawn(); + validateLobbySettings(); + validateTablist(); + validateBall(); + validateModules(); + + // Warnings ausgeben + if (!warnings.isEmpty()) { + plugin.getLogger().warning("=== Config-Validierung ==="); + warnings.forEach(msg -> plugin.getLogger().warning(" " + msg)); + plugin.getLogger().warning("========================="); + } + + // Config speichern wenn Änderungen vorgenommen wurden + if (modified) { + plugin.saveConfig(); + plugin.getLogger().info("Config wurde mit Default-Werten aktualisiert."); + } + } + + private void validateSpawn() { + ensureDefault("spawn.world", "world"); + ensureDefault("spawn.x", 0.5); + ensureDefault("spawn.y", 64.0); + ensureDefault("spawn.z", 0.5); + ensureDefault("spawn.yaw", 0.0); + ensureDefault("spawn.pitch", 0.0); + + // Validiere Y-Koordinate + double y = config.getDouble("spawn.y", 64.0); + if (y < -64 || y > 320) { + warnings.add("spawn.y (" + y + ") außerhalb des gültigen Bereichs (-64 bis 320)"); + } + } + + private void validateLobbySettings() { + ensureDefault("lobby.allow-fly", false); + ensureDefault("lobby.pvp-enabled", false); + ensureDefault("lobby.build-enabled", false); + ensureDefault("lobby.default-gamemode", "Adventure"); + ensureDefault("lobby.clear-inventory-on-join", true); + + // Validiere GameMode + String gamemode = config.getString("lobby.default-gamemode", "Adventure"); + if (!isValidGameMode(gamemode)) { + warnings.add("lobby.default-gamemode '" + gamemode + "' ist ungültig. Nutze: Survival, Creative, Adventure oder Spectator"); + } + } + + private void validateTablist() { + ensureDefault("tablist.enabled", true); + ensureDefault("tablist.header", "&6Willkommen auf &eNexusLobby"); + ensureDefault("tablist.footer", "&7Viel Spaß!"); + ensureDefault("tablist.refresh-interval", 40); + + // Validiere Refresh-Interval + int interval = config.getInt("tablist.refresh-interval", 40); + if (interval < 1) { + warnings.add("tablist.refresh-interval (" + interval + ") ist zu klein. Minimum ist 1 Tick."); + config.set("tablist.refresh-interval", 1); + modified = true; + } else if (interval > 200) { + warnings.add("tablist.refresh-interval (" + interval + ") ist sehr hoch. Empfohlen: 20-100 Ticks."); + } + } + + private void validateBall() { + ensureDefault("ball.respawn_delay", 60); + + // Validiere Respawn-Delay + long delay = config.getLong("ball.respawn_delay", 60); + if (delay < 5) { + warnings.add("ball.respawn_delay (" + delay + "s) ist zu kurz. Minimum empfohlen: 5 Sekunden."); + } else if (delay > 600) { + warnings.add("ball.respawn_delay (" + delay + "s) ist sehr lang. Empfohlen: 30-120 Sekunden."); + } + + // Prüfe ob Spawn gesetzt ist + if (!config.contains("ball.spawn")) { + warnings.add("ball.spawn nicht gesetzt. Nutze /nexuslobby ball setspawn"); + } + } + + private void validateModules() { + // Worldborder + if (config.contains("worldborder.enabled")) { + ensureDefault("worldborder.type", "SQUARE"); + ensureDefault("worldborder.radius", 50.0); + + double radius = config.getDouble("worldborder.radius", 50.0); + if (radius < 10) { + warnings.add("worldborder.radius (" + radius + ") ist sehr klein. Minimum empfohlen: 10 Blöcke."); + } else if (radius > 10000) { + warnings.add("worldborder.radius (" + radius + ") ist extrem groß. Performance-Warnung!"); + } + + String type = config.getString("worldborder.type", "SQUARE"); + if (!type.equalsIgnoreCase("SQUARE") && !type.equalsIgnoreCase("CIRCLE")) { + warnings.add("worldborder.type '" + type + "' ist ungültig. Nutze: SQUARE oder CIRCLE"); + } + } + + // Items Modul + if (config.contains("items.lobby-tools")) { + // Prüfe ob Slot-Nummern gültig sind (0-8) + for (String toolKey : config.getConfigurationSection("items.lobby-tools").getKeys(false)) { + int slot = config.getInt("items.lobby-tools." + toolKey + ".slot", -1); + if (slot < 0 || slot > 8) { + warnings.add("items.lobby-tools." + toolKey + ".slot (" + slot + ") ist ungültig (0-8)"); + } + } + } + } + + private void ensureDefault(String path, Object defaultValue) { + if (!config.contains(path)) { + config.set(path, defaultValue); + modified = true; + } + } + + private boolean isValidGameMode(String mode) { + return mode.equalsIgnoreCase("Survival") || + mode.equalsIgnoreCase("Creative") || + mode.equalsIgnoreCase("Adventure") || + mode.equalsIgnoreCase("Spectator"); + } +} diff --git a/src/main/java/de/nexuslobby/utils/LangManager.java b/src/main/java/de/nexuslobby/utils/LangManager.java index 83c8b9e..53b63d9 100644 --- a/src/main/java/de/nexuslobby/utils/LangManager.java +++ b/src/main/java/de/nexuslobby/utils/LangManager.java @@ -1,32 +1,43 @@ -package de.nexuslobby.utils; - -import org.bukkit.plugin.java.JavaPlugin; -import org.yaml.snakeyaml.Yaml; - -import java.io.InputStream; -import java.util.Map; - -public class LangManager { - private static Map> langMap; - private static String currentLang = "de"; - - public static void load(JavaPlugin plugin) { - Yaml yaml = new Yaml(); - try (InputStream in = plugin.getResource("lang.yml")) { - langMap = yaml.load(in); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public static void setLanguage(String lang) { - currentLang = lang; - } - - public static String get(String key) { - if (langMap == null) return key; - Map translations = langMap.get(key); - if (translations == null) return key; - return translations.getOrDefault(currentLang, key); - } -} +package de.nexuslobby.utils; + +import de.nexuslobby.NexusLobby; +import org.bukkit.ChatColor; +import org.bukkit.plugin.java.JavaPlugin; +import org.yaml.snakeyaml.Yaml; + +import java.io.InputStream; +import java.util.Map; + +public class LangManager { + private static Map> langMap; + private static String currentLang = "de"; + + public static void load(JavaPlugin plugin) { + Yaml yaml = new Yaml(); + try (InputStream in = plugin.getResource("lang.yml")) { + langMap = yaml.load(in); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void setLanguage(String lang) { + currentLang = lang; + } + + public static String get(String key) { + if (langMap == null) return key; + Map translations = langMap.get(key); + if (translations == null) return key; + return translations.getOrDefault(currentLang, key) + .replace("§8[§6Nexus§8] ", getPrefix()); + } + + public static String getPrefix() { + NexusLobby plugin = NexusLobby.getInstance(); + String prefix = plugin == null + ? "&8[&6Nexus&8] " + : plugin.getConfig().getString("prefix", "&8[&6Nexus&8] "); + return ChatColor.translateAlternateColorCodes('&', prefix); + } +} diff --git a/src/main/java/de/nexuslobby/utils/UpdateChecker.java b/src/main/java/de/nexuslobby/utils/UpdateChecker.java index 56e88ca..d9e0364 100644 --- a/src/main/java/de/nexuslobby/utils/UpdateChecker.java +++ b/src/main/java/de/nexuslobby/utils/UpdateChecker.java @@ -1,36 +1,36 @@ -package de.nexuslobby.utils; - -import de.nexuslobby.NexusLobby; -import org.bukkit.Bukkit; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Scanner; -import java.util.function.Consumer; - -public class UpdateChecker { - - private final NexusLobby plugin; - private static final int RESOURCE_ID = 132388; - - public UpdateChecker(NexusLobby plugin) { - this.plugin = plugin; - } - - public void getVersion(final Consumer consumer) { - Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { - try (InputStream is = new URL( - "https://api.spigotmc.org/legacy/update.php?resource=" + RESOURCE_ID + "/~").openStream(); - Scanner scanner = new Scanner(is)) { - - if (scanner.hasNext()) { - consumer.accept(scanner.next()); - } - - } catch (IOException e) { - plugin.getLogger().warning("Update-Check fehlgeschlagen: " + e.getMessage()); - } - }); - } +package de.nexuslobby.utils; + +import de.nexuslobby.NexusLobby; +import org.bukkit.Bukkit; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Scanner; +import java.util.function.Consumer; + +public class UpdateChecker { + + private final NexusLobby plugin; + private static final int RESOURCE_ID = 132388; + + public UpdateChecker(NexusLobby plugin) { + this.plugin = plugin; + } + + public void getVersion(final Consumer consumer) { + Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { + try (InputStream is = new URL( + "https://api.spigotmc.org/legacy/update.php?resource=" + RESOURCE_ID + "/~").openStream(); + Scanner scanner = new Scanner(is)) { + + if (scanner.hasNext()) { + consumer.accept(scanner.next()); + } + + } catch (IOException e) { + plugin.getLogger().warning("Update-Check fehlgeschlagen: " + e.getMessage()); + } + }); + } } \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 3274c35..2bd981e 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -20,6 +20,9 @@ # Verfügbare Optionen: de (Deutsch), en (Englisch), fr (Französisch) language: de +# Prefix vor Plugin-Nachrichten im Chat (&-Farbcodes sind erlaubt) +prefix: "&8[&6Nexus&8] " + # ══════════════════════════════════════════════════════════════════════════════ # SPAWN EINSTELLUNGEN # ══════════════════════════════════════════════════════════════════════════════ diff --git a/src/main/resources/lang.yml b/src/main/resources/lang.yml index 04adf4a..3be4246 100644 --- a/src/main/resources/lang.yml +++ b/src/main/resources/lang.yml @@ -1,203 +1,203 @@ -# _ __ __ __ __ -# / | / /__ _ ____ _______/ / ____ / /_ / /_ __ __ -# / |/ / _ \| |/_/ / / / ___/ / / __ \/ __ \/ __ \/ / / / -# / /| / __/> " - en: "§8[§6Nexus§8] §7Usage: §e/nexus parkour " -parkour_run_aborted: - de: "§8[§6Nexus§8] §7Dein aktueller Lauf wurde abgebrochen." - en: "§8[§6Nexus§8] §7Your current run was aborted." -parkour_besttimes_cleared: - de: "§8[§6Nexus§8] §aAlle Parkour-Bestzeiten wurden gelöscht!" - en: "§8[§6Nexus§8] §aAll parkour best times have been cleared!" -parkour_track_removed: - de: "§8[§6Nexus§8] §cDie gesamte Strecke (Checkpoints & Ziel) wurde gelöscht!" - en: "§8[§6Nexus§8] §cThe entire track (checkpoints & finish) has been removed!" -unknown_subcommand: - de: "§cUnbekannter Unterbefehl." - en: "§cUnknown subcommand." -parkour_npc_marked: - de: "§8[§6Nexus§8] §aArmorStand als Parkour-NPC markiert!" - en: "§8[§6Nexus§8] §aArmorStand marked as parkour NPC!" -parkour_start_set: - de: "§8[§6Nexus§8] §aParkour-Startpunkt an deiner Position gesetzt!" - en: "§8[§6Nexus§8] §aParkour start set at your position!" -scoreboard_usage: - de: "§cBenutzung: /nexus sb " - en: "§cUsage: /nexus sb " -scoreboard_module_disabled: - de: "§cScoreboard-Modul ist deaktiviert." - en: "§cScoreboard module is disabled." -info_header: - de: "§8§m--------------------------------------" - en: "§8§m--------------------------------------" -info_title: - de: "§6§lNexusLobby §7- Informationen" - en: "§6§lNexusLobby §7- Information" -info_spawn: - de: "§f/spawn §7- Zum Spawn" - en: "§f/spawn §7- To spawn" -info_parkour: - de: "§f/setstart §8| §f/setcheckpoint §8| §f/setfinish" - en: "§f/setstart §8| §f/setcheckpoint §8| §f/setfinish" -info_removeall: - de: "§f/nexus parkour removeall §7- Strecke löschen" - en: "§f/nexus parkour removeall §7- Remove track" -info_ball: - de: "§f/nexus ball setspawn §7- Fußball Spawn setzen" - en: "§f/nexus ball setspawn §7- Set soccer spawn" -info_setspawn: - de: "§f/nexus setspawn §7- Spawn setzen" - en: "§f/nexus setspawn §7- Set spawn" -info_scoreboard: - de: "§f/nexus sb §7- Scoreboard" - en: "§f/nexus sb §7- Scoreboard" -info_reload: - de: "§f/nexus reload §7- Config laden" - en: "§f/nexus reload §7- Reload config" -info_footer: - de: "§8§m--------------------------------------" - en: "§8§m--------------------------------------" -parkour_trainer_greeting: - de: "§6§lTrainer §8» §aViel Erfolg beim Parkour! Gib dein Bestes!" - en: "§6§lTrainer §8» §aGood luck with the parkour! Give it your best!" -update_available: - de: "§8[§6Nexus§8] §aEin neues §6Update §afür §eNexusLobby §aist verfügbar!" - en: "§8[§6Nexus§8] §aA new §6update §afor §eNexusLobby §ais available!" -update_version: - de: "§8» §7Version: §c{old} §8-> §a{new}" - en: "§8» §7Version: §c{old} §8-> §a{new}" -update_download_link: - de: "§8» §6Klicke §e§l[HIER] §6zum Herunterladen." - en: "§8» §6Click §e§l[HERE] §6to download." -update_download_hover: - de: "§7Öffnet die Release-Seite" - en: "§7Opens the release page" -hider_all: - de: "§aAlle Spieler" - en: "§aAll players" -hider_none: - de: "§cKeine Spieler" - en: "§cNo players" -void_protection_message: - de: "§cDu wurdest vor dem Void gerettet!" - en: "§cYou were saved from the void!" -proxy_not_registered: - de: "§cProxy-Verbindung nicht möglich (BungeeCord Kanal nicht registriert)." - en: "§cProxy connection not possible (BungeeCord channel not registered)." -connecting_to_server: - de: "§eVerbinde zu: §f{server}" - en: "§eConnecting to: §f{server}" -server_connect_error: - de: "§cFehler beim Verbinden zum Server." - en: "§cError connecting to server." -proxy_connect_error: - de: "§cProxy-Verbindung nicht möglich." - en: "§cProxy connection not possible." -armorstand_lookat_required: - de: "§cDu musst einen ArmorStand direkt anschauen (Fadenkreuz)!" - en: "§cYou must look directly at an ArmorStand (crosshair)!" -conv_link_usage: - de: "§cNutze: /nexuscmd conv link " - en: "§cUsage: /nexuscmd conv link " -conv_mark_npcs: - de: "§cBitte markiere mindestens die ersten beiden NPCs (select1 & select2)!" - en: "§cPlease mark at least the first two NPCs (select1 & select2)!" -conv_link_created: - de: "§a§lDauerhafte Verknüpfung erstellt!" - en: "§a§lPermanent link created!" -conv_link_npcs: - de: "§7Beteiligte NPCs: §e{count}" - en: "§7Linked NPCs: §e{count}" -conv_speaker_not_found: - de: "§cFehler: Sprecher 1 nicht gefunden." - en: "§cError: Speaker 1 not found." -conv_unlink_lookat: - de: "§cSchau den NPC an, dessen Verknüpfung du lösen willst!" - en: "§cLook at the NPC you want to unlink!" -conv_unlinked: - de: "§eNPC-Verknüpfung wurde aufgehoben." - en: "§eNPC link has been removed." -conv_start_usage: - de: "§cNutze: /nexuscmd conv start " - en: "§cUsage: /nexuscmd conv start " -conv_mark_two_npcs: - de: "§cBitte markiere mindestens zwei NPCs!" - en: "§cPlease mark at least two NPCs!" -npc_bubble_sent: - de: "§aNPC-Sprechblase gesendet." - en: "§aNPC speech bubble sent." -lookat_off: - de: "§cBlickkontakt aus." - en: "§cLook-at disabled." -lookat_on: - de: "§aBlickkontakt an." - en: "§aLook-at enabled." -name_removed: - de: "§eName entfernt." - en: "§eName removed." -name_set: - de: "§7Name gesetzt: {name}" - en: "§7Name set: {name}" -status_backup_saved: - de: "§8(Status-Backup wurde gespeichert)" - en: "§8(Status backup saved)" -command_bound: - de: "§aBefehl gebunden." - en: "§aCommand bound." -no_target: - de: "§cKein Ziel!" - en: "§cNo target!" -commands_and_tags: - de: "§6§lBefehle & Tags:" - en: "§6§lCommands & Tags:" -commands_removed: - de: "§eAlle Befehle gelöscht." - en: "§eAll commands removed." -welcome: - de: "Willkommen auf dem Server!" - en: "Welcome to the server!" -no_permission: - de: "§cKeine Berechtigung." - en: "§cNo permission." -only_player: - de: "§cDieser Befehl ist nur für Spieler!" - en: "§cThis command is for players only!" -parkour_checkpoint_set: - de: "§8[§6Nexus§8] §aParkour-Checkpoint gesetzt!" - en: "§8[§6Nexus§8] §aParkour checkpoint set!" -parkour_finish_set: - de: "§8[§6Nexus§8] §aParkour-Zielpunkt gesetzt!" - en: "§8[§6Nexus§8] §aParkour finish set!" -teleport_spawn: - de: "§8[§6Nexus§8] §aDu wurdest zum Spawn teleportiert." - en: "§8[§6Nexus§8] §aYou have been teleported to spawn." -spawn_world_missing: - de: "§cFehler: Die Spawn-Welt existiert nicht." - en: "§cError: The spawn world does not exist." -spawn_not_set: - de: "§cEs wurde noch kein Spawn gesetzt." - en: "§cNo spawn has been set yet." -plugin_reloaded: - de: "§8[§6Nexus§8] §aPlugin erfolgreich neu geladen!" - en: "§8[§6Nexus§8] §aPlugin successfully reloaded!" -spawn_set: - de: "§8[§6Nexus§8] §aLobby-Spawn erfolgreich gesetzt!" - en: "§8[§6Nexus§8] §aLobby spawn set successfully!" -silentjoin_on: - de: "§8[§6Nexus§8] §7Silent Join: §aAktiviert" - en: "§8[§6Nexus§8] §7Silent Join: §aEnabled" -silentjoin_off: - de: "§8[§6Nexus§8] §7Silent Join: §cDeaktiviert" - en: "§8[§6Nexus§8] §7Silent Join: §cDisabled" +# _ __ __ __ __ +# / | / /__ _ ____ _______/ / ____ / /_ / /_ __ __ +# / |/ / _ \| |/_/ / / / ___/ / / __ \/ __ \/ __ \/ / / / +# / /| / __/> " + en: "§8[§6Nexus§8] §7Usage: §e/nexus parkour " +parkour_run_aborted: + de: "§8[§6Nexus§8] §7Dein aktueller Lauf wurde abgebrochen." + en: "§8[§6Nexus§8] §7Your current run was aborted." +parkour_besttimes_cleared: + de: "§8[§6Nexus§8] §aAlle Parkour-Bestzeiten wurden gelöscht!" + en: "§8[§6Nexus§8] §aAll parkour best times have been cleared!" +parkour_track_removed: + de: "§8[§6Nexus§8] §cDie gesamte Strecke (Checkpoints & Ziel) wurde gelöscht!" + en: "§8[§6Nexus§8] §cThe entire track (checkpoints & finish) has been removed!" +unknown_subcommand: + de: "§cUnbekannter Unterbefehl." + en: "§cUnknown subcommand." +parkour_npc_marked: + de: "§8[§6Nexus§8] §aArmorStand als Parkour-NPC markiert!" + en: "§8[§6Nexus§8] §aArmorStand marked as parkour NPC!" +parkour_start_set: + de: "§8[§6Nexus§8] §aParkour-Startpunkt an deiner Position gesetzt!" + en: "§8[§6Nexus§8] §aParkour start set at your position!" +scoreboard_usage: + de: "§cBenutzung: /nexus sb " + en: "§cUsage: /nexus sb " +scoreboard_module_disabled: + de: "§cScoreboard-Modul ist deaktiviert." + en: "§cScoreboard module is disabled." +info_header: + de: "§8§m--------------------------------------" + en: "§8§m--------------------------------------" +info_title: + de: "§6§lNexusLobby §7- Informationen" + en: "§6§lNexusLobby §7- Information" +info_spawn: + de: "§f/spawn §7- Zum Spawn" + en: "§f/spawn §7- To spawn" +info_parkour: + de: "§f/setstart §8| §f/setcheckpoint §8| §f/setfinish" + en: "§f/setstart §8| §f/setcheckpoint §8| §f/setfinish" +info_removeall: + de: "§f/nexus parkour removeall §7- Strecke löschen" + en: "§f/nexus parkour removeall §7- Remove track" +info_ball: + de: "§f/nexus ball setspawn §7- Fußball Spawn setzen" + en: "§f/nexus ball setspawn §7- Set soccer spawn" +info_setspawn: + de: "§f/nexus setspawn §7- Spawn setzen" + en: "§f/nexus setspawn §7- Set spawn" +info_scoreboard: + de: "§f/nexus sb §7- Scoreboard" + en: "§f/nexus sb §7- Scoreboard" +info_reload: + de: "§f/nexus reload §7- Config laden" + en: "§f/nexus reload §7- Reload config" +info_footer: + de: "§8§m--------------------------------------" + en: "§8§m--------------------------------------" +parkour_trainer_greeting: + de: "§6§lTrainer §8» §aViel Erfolg beim Parkour! Gib dein Bestes!" + en: "§6§lTrainer §8» §aGood luck with the parkour! Give it your best!" +update_available: + de: "§8[§6Nexus§8] §aEin neues §6Update §afür §eNexusLobby §aist verfügbar!" + en: "§8[§6Nexus§8] §aA new §6update §afor §eNexusLobby §ais available!" +update_version: + de: "§8» §7Version: §c{old} §8-> §a{new}" + en: "§8» §7Version: §c{old} §8-> §a{new}" +update_download_link: + de: "§8» §6Klicke §e§l[HIER] §6zum Herunterladen." + en: "§8» §6Click §e§l[HERE] §6to download." +update_download_hover: + de: "§7Öffnet die Release-Seite" + en: "§7Opens the release page" +hider_all: + de: "§aAlle Spieler" + en: "§aAll players" +hider_none: + de: "§cKeine Spieler" + en: "§cNo players" +void_protection_message: + de: "§cDu wurdest vor dem Void gerettet!" + en: "§cYou were saved from the void!" +proxy_not_registered: + de: "§cProxy-Verbindung nicht möglich (BungeeCord Kanal nicht registriert)." + en: "§cProxy connection not possible (BungeeCord channel not registered)." +connecting_to_server: + de: "§eVerbinde zu: §f{server}" + en: "§eConnecting to: §f{server}" +server_connect_error: + de: "§cFehler beim Verbinden zum Server." + en: "§cError connecting to server." +proxy_connect_error: + de: "§cProxy-Verbindung nicht möglich." + en: "§cProxy connection not possible." +armorstand_lookat_required: + de: "§cDu musst einen ArmorStand direkt anschauen (Fadenkreuz)!" + en: "§cYou must look directly at an ArmorStand (crosshair)!" +conv_link_usage: + de: "§cNutze: /nexuscmd conv link " + en: "§cUsage: /nexuscmd conv link " +conv_mark_npcs: + de: "§cBitte markiere mindestens die ersten beiden NPCs (select1 & select2)!" + en: "§cPlease mark at least the first two NPCs (select1 & select2)!" +conv_link_created: + de: "§a§lDauerhafte Verknüpfung erstellt!" + en: "§a§lPermanent link created!" +conv_link_npcs: + de: "§7Beteiligte NPCs: §e{count}" + en: "§7Linked NPCs: §e{count}" +conv_speaker_not_found: + de: "§cFehler: Sprecher 1 nicht gefunden." + en: "§cError: Speaker 1 not found." +conv_unlink_lookat: + de: "§cSchau den NPC an, dessen Verknüpfung du lösen willst!" + en: "§cLook at the NPC you want to unlink!" +conv_unlinked: + de: "§eNPC-Verknüpfung wurde aufgehoben." + en: "§eNPC link has been removed." +conv_start_usage: + de: "§cNutze: /nexuscmd conv start " + en: "§cUsage: /nexuscmd conv start " +conv_mark_two_npcs: + de: "§cBitte markiere mindestens zwei NPCs!" + en: "§cPlease mark at least two NPCs!" +npc_bubble_sent: + de: "§aNPC-Sprechblase gesendet." + en: "§aNPC speech bubble sent." +lookat_off: + de: "§cBlickkontakt aus." + en: "§cLook-at disabled." +lookat_on: + de: "§aBlickkontakt an." + en: "§aLook-at enabled." +name_removed: + de: "§eName entfernt." + en: "§eName removed." +name_set: + de: "§7Name gesetzt: {name}" + en: "§7Name set: {name}" +status_backup_saved: + de: "§8(Status-Backup wurde gespeichert)" + en: "§8(Status backup saved)" +command_bound: + de: "§aBefehl gebunden." + en: "§aCommand bound." +no_target: + de: "§cKein Ziel!" + en: "§cNo target!" +commands_and_tags: + de: "§6§lBefehle & Tags:" + en: "§6§lCommands & Tags:" +commands_removed: + de: "§eAlle Befehle gelöscht." + en: "§eAll commands removed." +welcome: + de: "Willkommen auf dem Server!" + en: "Welcome to the server!" +no_permission: + de: "§cKeine Berechtigung." + en: "§cNo permission." +only_player: + de: "§cDieser Befehl ist nur für Spieler!" + en: "§cThis command is for players only!" +parkour_checkpoint_set: + de: "§8[§6Nexus§8] §aParkour-Checkpoint gesetzt!" + en: "§8[§6Nexus§8] §aParkour checkpoint set!" +parkour_finish_set: + de: "§8[§6Nexus§8] §aParkour-Zielpunkt gesetzt!" + en: "§8[§6Nexus§8] §aParkour finish set!" +teleport_spawn: + de: "§8[§6Nexus§8] §aDu wurdest zum Spawn teleportiert." + en: "§8[§6Nexus§8] §aYou have been teleported to spawn." +spawn_world_missing: + de: "§cFehler: Die Spawn-Welt existiert nicht." + en: "§cError: The spawn world does not exist." +spawn_not_set: + de: "§cEs wurde noch kein Spawn gesetzt." + en: "§cNo spawn has been set yet." +plugin_reloaded: + de: "§8[§6Nexus§8] §aPlugin erfolgreich neu geladen!" + en: "§8[§6Nexus§8] §aPlugin successfully reloaded!" +spawn_set: + de: "§8[§6Nexus§8] §aLobby-Spawn erfolgreich gesetzt!" + en: "§8[§6Nexus§8] §aLobby spawn set successfully!" +silentjoin_on: + de: "§8[§6Nexus§8] §7Silent Join: §aAktiviert" + en: "§8[§6Nexus§8] §7Silent Join: §aEnabled" +silentjoin_off: + de: "§8[§6Nexus§8] §7Silent Join: §cDeaktiviert" + en: "§8[§6Nexus§8] §7Silent Join: §cDisabled" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 28e11fe..da2f024 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: NexusLobby main: de.nexuslobby.NexusLobby -version: "1.9" +version: "2.0" api-version: "1.21" author: M_Viper description: Modular Lobby Plugin with an invisible Particle-Parkour system.