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 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; } } } }