diff --git a/StatusAPI/src/main/java/net/viper/status/StatusAPI.java b/StatusAPI/src/main/java/net/viper/status/StatusAPI.java index 49443f1..23c6dc5 100644 --- a/StatusAPI/src/main/java/net/viper/status/StatusAPI.java +++ b/StatusAPI/src/main/java/net/viper/status/StatusAPI.java @@ -53,6 +53,14 @@ public class StatusAPI extends Plugin implements Runnable { // Kontostand pro Spieler (UUID -> Balance), wird von StatusAPIBridge gepusht public static final ConcurrentHashMap playerBalances = new ConcurrentHashMap<>(); + // Debug-Modus (aus verify.properties) + public static boolean DEBUG = false; + + /** Gibt eine Info-Meldung nur im Debug-Modus aus */ + public static void debugLog(Plugin plugin, String message) { + if (DEBUG) plugin.getLogger().info(message); + } + private volatile Thread thread; private volatile ServerSocket serverSocket; private volatile boolean shuttingDown = false; @@ -83,6 +91,9 @@ public class StatusAPI extends Plugin implements Runnable { port = 9191; } + // Debug-Modus + DEBUG = verifyProperties != null && Boolean.parseBoolean(verifyProperties.getProperty("debug", "false")); + moduleManager = new ModuleManager(); // Module in korrekter Reihenfolge registrieren @@ -192,8 +203,9 @@ public class StatusAPI extends Plugin implements Runnable { File file = new File(getDataFolder(), "verify.properties"); verifyProperties = new Properties(); if (file.exists()) { - try (FileInputStream fis = new FileInputStream(file)) { - verifyProperties.load(fis); + try (java.io.InputStreamReader reader = new java.io.InputStreamReader( + new FileInputStream(file), StandardCharsets.UTF_8)) { + verifyProperties.load(reader); } } else { getLogger().warning("verify.properties nicht gefunden."); diff --git a/StatusAPI/src/main/java/net/viper/status/modules/antibot/AntiBotModule.java b/StatusAPI/src/main/java/net/viper/status/modules/antibot/AntiBotModule.java index 3ff9864..c17ee3d 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/antibot/AntiBotModule.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/antibot/AntiBotModule.java @@ -121,7 +121,7 @@ public class AntiBotModule implements Module, Listener { ensureSecurityLogFile(); if (!enabled) { - this.plugin.getLogger().info("[AntiBotModule] deaktiviert via " + CONFIG_FILE_NAME); + StatusAPI.debugLog(this.plugin, "[AntiBotModule] deaktiviert via " + CONFIG_FILE_NAME); return; } @@ -129,7 +129,7 @@ public class AntiBotModule implements Module, Listener { ProxyServer.getInstance().getPluginManager().registerCommand(this.plugin, new AntiBotCommand()); ProxyServer.getInstance().getScheduler().schedule(this.plugin, this::tick, 1, 1, TimeUnit.SECONDS); - this.plugin.getLogger().info("[AntiBotModule] aktiviert. maxCps=" + maxCps + this.plugin.getLogger().fine("[AntiBotModule] aktiviert. maxCps=" + maxCps + ", attackStartCps=" + attackStartCps + ", ip/min=" + ipConnectionsPerMinute); } diff --git a/StatusAPI/src/main/java/net/viper/status/modules/broadcast/BroadcastModule.java b/StatusAPI/src/main/java/net/viper/status/modules/broadcast/BroadcastModule.java index 14c16be..1ff0112 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/broadcast/BroadcastModule.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/broadcast/BroadcastModule.java @@ -1,12 +1,20 @@ package net.viper.status.modules.broadcast; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.ChatColor; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Plugin; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.BaseComponent; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.ClickEvent; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.ComponentBuilder; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.TextComponent; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Listener; import net.viper.status.module.Module; @@ -57,7 +65,7 @@ public class BroadcastModule implements Module, Listener { loadConfig(); if (!enabled) return; try { plugin.getProxy().getPluginManager().registerListener(plugin, this); } catch (Throwable ignored) {} - plugin.getLogger().info("[BroadcastModule] aktiviert. Format: " + format); + plugin.getLogger().fine("[BroadcastModule] aktiviert. Format: " + format); loadSchedules(); plugin.getProxy().getScheduler().schedule(plugin, this::processScheduled, 1, 1, TimeUnit.SECONDS); } @@ -142,7 +150,7 @@ public class BroadcastModule implements Module, Listener { for (ProxiedPlayer p : plugin.getProxy().getPlayers()) { try { p.sendMessage(components); sent++; } catch (Throwable ignored) {} } - plugin.getLogger().info("[BroadcastModule] Broadcast gesendet (Empfänger=" + sent + "): " + message); + StatusAPI.debugLog(plugin, "[BroadcastModule] Broadcast gesendet (Empfänger=" + sent + "): " + message); return true; } @@ -279,7 +287,7 @@ public class BroadcastModule implements Module, Listener { } } scheduledByClientId.putAll(loaded); - plugin.getLogger().info("[BroadcastModule] " + loaded.size() + " geplante Broadcasts aus Datei wiederhergestellt."); + plugin.getLogger().fine("[BroadcastModule] geplante Broadcasts wiederhergestellt."); } public boolean scheduleBroadcast(long timestampMillis, String sourceName, String message, String type, @@ -311,7 +319,7 @@ public class BroadcastModule implements Module, Listener { prefix, prefixColor, bracketColor, messageColor, recur); scheduledByClientId.put(id, sb); saveSchedules(); - plugin.getLogger().info("[BroadcastModule] Neue geplante Nachricht registriert: " + id + StatusAPI.debugLog(plugin, "[BroadcastModule] Neue geplante Nachricht registriert: " + id + " @ " + dateFormat.format(new Date(timestampMillis))); return true; } @@ -319,7 +327,7 @@ public class BroadcastModule implements Module, Listener { public boolean cancelScheduled(String clientScheduleId) { if (clientScheduleId == null || clientScheduleId.trim().isEmpty()) return false; ScheduledBroadcast removed = scheduledByClientId.remove(clientScheduleId); - if (removed != null) { plugin.getLogger().info("[BroadcastModule] Schedule abgebrochen: " + clientScheduleId); saveSchedules(); return true; } + if (removed != null) { StatusAPI.debugLog(plugin, "[BroadcastModule] Schedule abgebrochen: " + clientScheduleId); saveSchedules(); return true; } return false; } @@ -332,7 +340,7 @@ public class BroadcastModule implements Module, Listener { for (Map.Entry entry : scheduledByClientId.entrySet()) { ScheduledBroadcast sb = entry.getValue(); if (sb.nextRunMillis <= now) { - plugin.getLogger().info("[BroadcastModule] ⏰ Sende geplante Nachricht (ID: " + entry.getKey() + ")"); + StatusAPI.debugLog(plugin, "[BroadcastModule] ⏰ Sende geplante Nachricht (ID: " + entry.getKey() + ")"); handleBroadcast(sb.sourceName, sb.message, sb.type, "", sb.prefix, sb.prefixColor, sb.bracketColor, sb.messageColor); if (!"none".equalsIgnoreCase(sb.recur)) { long next = computeNextMillis(sb.nextRunMillis, sb.recur); diff --git a/StatusAPI/src/main/java/net/viper/status/modules/chat/ChatConfig.java b/StatusAPI/src/main/java/net/viper/status/modules/chat/ChatConfig.java index 104b41e..aec5131 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/chat/ChatConfig.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/chat/ChatConfig.java @@ -103,7 +103,7 @@ public class ChatConfig { config = new Configuration(); } parseConfig(); - plugin.getLogger().info("[ChatModule] " + channels.size() + " Kanäle geladen."); + plugin.getLogger().fine("[ChatModule] " + channels.size() + " Kanäle geladen."); } private void parseConfig() { @@ -392,7 +392,7 @@ public class ChatConfig { try (java.io.FileWriter fw = new java.io.FileWriter(filterFile)) { fw.write("# StatusAPI - Wort-Blacklist\n# words:\n# - beispielwort\nwords:\n"); } - plugin.getLogger().info("[ChatModule] filter.yml erstellt."); + plugin.getLogger().fine("[ChatModule] filter.yml erstellt."); } catch (IOException e) { plugin.getLogger().warning("[ChatModule] Konnte filter.yml nicht erstellen: " + e.getMessage()); } return; } diff --git a/StatusAPI/src/main/java/net/viper/status/modules/chat/ChatModule.java b/StatusAPI/src/main/java/net/viper/status/modules/chat/ChatModule.java index 2b90c68..e89ab71 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/chat/ChatModule.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/chat/ChatModule.java @@ -113,7 +113,7 @@ public class ChatModule implements Module, Listener { // ChatLogger if (config.isChatlogEnabled()) { chatLogger = new ChatLogger(plugin.getDataFolder(), logger, config.getChatlogRetentionDays()); - logger.info("[ChatModule] Chat-Log aktiviert (" + config.getChatlogRetentionDays() + " Tage Aufbewahrung)."); + logger.fine("[ChatModule] Chat-Log aktiviert (" + config.getChatlogRetentionDays() + " Tage Aufbewahrung)."); } // ReportManager @@ -144,7 +144,7 @@ public class ChatModule implements Module, Listener { ProxyServer.getInstance().getPluginManager().registerListener(plugin, this); registerCommands(); - logger.info("[ChatModule] Aktiviert – " + config.getChannels().size() + " Kanäle geladen."); + logger.fine("[ChatModule] Aktiviert – " + config.getChannels().size() + " Kanäle geladen."); } @Override diff --git a/StatusAPI/src/main/java/net/viper/status/modules/commandblocker/CommandBlockerModule.java b/StatusAPI/src/main/java/net/viper/status/modules/commandblocker/CommandBlockerModule.java index a04637e..00bf3fe 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/commandblocker/CommandBlockerModule.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/commandblocker/CommandBlockerModule.java @@ -62,7 +62,7 @@ public class CommandBlockerModule implements Module, Listener { } }); - this.plugin.getLogger().info("[CommandBlocker] aktiviert (" + blocked.size() + " Commands)."); + this.plugin.getLogger().fine("[CommandBlocker] aktiviert (" + blocked.size() + " Commands)."); } diff --git a/StatusAPI/src/main/java/net/viper/status/modules/customcommands/CustomCommandModule.java b/StatusAPI/src/main/java/net/viper/status/modules/customcommands/CustomCommandModule.java index 0b46042..083a16a 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/customcommands/CustomCommandModule.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/customcommands/CustomCommandModule.java @@ -48,7 +48,7 @@ public class CustomCommandModule implements Module, Listener { // Hier casten wir 'Plugin' zu 'StatusAPI', da wir wissen, dass es das ist this.plugin = (StatusAPI) plugin; - this.plugin.getLogger().info("Lade CustomCommandModule..."); + this.plugin.getLogger().fine("Lade CustomCommandModule..."); reloadConfig(); if (this.config == null) { this.config = new Configuration(); diff --git a/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyDatabase.java b/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyDatabase.java index 69dcec2..7ade6e4 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyDatabase.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyDatabase.java @@ -2,6 +2,7 @@ package net.viper.status.modules.economy; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Plugin; import java.sql.*; @@ -23,6 +24,9 @@ public class EconomyDatabase { this.log = plugin.getLogger(); HikariConfig cfg = new HikariConfig(); + // HikariCP Startup-Logs unterdrücken + java.util.logging.Logger.getLogger("com.zaxxer.hikari").setLevel(java.util.logging.Level.WARNING); + java.util.logging.Logger.getLogger("com.zaxxer.hikari.HikariDataSource").setLevel(java.util.logging.Level.WARNING); cfg.setJdbcUrl("jdbc:mysql://" + host + ":" + port + "/" + database + "?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&useUnicode=true"); cfg.setUsername(user); @@ -67,7 +71,7 @@ public class EconomyDatabase { " `updated` BIGINT NOT NULL" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")) { ps.executeUpdate(); - log.info("[Economy] MySQL verbunden – Tabellen bereit."); + if (StatusAPI.DEBUG) log.info("[Economy] MySQL verbunden – Tabellen bereit."); } catch (SQLException e) { log.severe("[Economy] Tabellen-Setup (bc_player_names) fehlgeschlagen: " + e.getMessage()); } diff --git a/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyManager.java b/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyManager.java index a57e03a..97bd671 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyManager.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyManager.java @@ -1,5 +1,6 @@ package net.viper.status.modules.economy; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Plugin; import java.util.UUID; @@ -64,7 +65,7 @@ public class EconomyManager { UUID uuid = UUID.fromString(formatted); // Für künftige Lookups speichern db.saveNameMapping(uuid, name); - plugin.getLogger().info("[Economy] Mojang-Lookup: " + name + " → " + uuid); + StatusAPI.debugLog(plugin, "[Economy] Mojang-Lookup: " + name + " → " + uuid); return uuid; } catch (Exception e) { plugin.getLogger().warning("[Economy] Mojang-Lookup fehlgeschlagen für " + name + ": " + e.getMessage()); diff --git a/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyModule.java b/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyModule.java index 26498a3..33246fd 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyModule.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/economy/EconomyModule.java @@ -57,14 +57,14 @@ public class EconomyModule implements Module { plugin.getProxy().getPluginManager().registerCommand(plugin, new PayCommand(plugin, manager)); plugin.getProxy().getPluginManager().registerCommand(plugin, new EcoAdminCommand(plugin, manager)); - plugin.getLogger().info("[Economy] EconomyModule aktiviert (start-balance: " + startBal + ")."); + StatusAPI.debugLog(plugin, "[Economy] EconomyModule aktiviert (start-balance: " + startBal + ")."); } @Override public void onDisable(Plugin plugin) { if (database != null) { database.close(); - plugin.getLogger().info("[Economy] MySQL-Verbindung geschlossen."); + StatusAPI.debugLog(plugin, "[Economy] MySQL-Verbindung geschlossen."); } } diff --git a/StatusAPI/src/main/java/net/viper/status/modules/forum/ForumBridgeModule.java b/StatusAPI/src/main/java/net/viper/status/modules/forum/ForumBridgeModule.java index 4106fe4..47aa8b1 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/forum/ForumBridgeModule.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/forum/ForumBridgeModule.java @@ -1,17 +1,30 @@ package net.viper.status.modules.forum; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.ChatColor; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.CommandSender; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.ProxyServer; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.ClickEvent; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.ComponentBuilder; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.HoverEvent; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.TextComponent; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.event.PostLoginEvent; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Command; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Listener; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Plugin; +import net.viper.status.StatusAPI; import net.md_5.bungee.event.EventHandler; import net.viper.status.module.Module; @@ -46,7 +59,7 @@ public class ForumBridgeModule implements Module, Listener { public void onEnable(Plugin plugin) { this.plugin = plugin; loadConfig(plugin); - if (!enabled) { plugin.getLogger().info("ForumBridgeModule ist deaktiviert."); return; } + if (!enabled) { StatusAPI.debugLog(plugin, "ForumBridgeModule ist deaktiviert."); return; } storage = new ForumNotifStorage(plugin.getDataFolder(), plugin.getLogger()); storage.load(); @@ -60,12 +73,12 @@ public class ForumBridgeModule implements Module, Listener { }, 10, 10, TimeUnit.MINUTES); plugin.getProxy().getScheduler().schedule(plugin, () -> storage.purgeOld(30), 1, 24, TimeUnit.HOURS); - plugin.getLogger().info("ForumBridgeModule aktiviert."); + plugin.getLogger().fine("ForumBridgeModule aktiviert."); } @Override public void onDisable(Plugin plugin) { - if (storage != null) { storage.save(); plugin.getLogger().info("Forum-Benachrichtigungen gespeichert."); } + if (storage != null) { storage.save(); StatusAPI.debugLog(plugin, "Forum-Benachrichtigungen gespeichert."); } } private void loadConfig(Plugin plugin) { diff --git a/StatusAPI/src/main/java/net/viper/status/modules/serverswitcher/ServerSwitcherModule.java b/StatusAPI/src/main/java/net/viper/status/modules/serverswitcher/ServerSwitcherModule.java index f9a1519..68085ce 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/serverswitcher/ServerSwitcherModule.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/serverswitcher/ServerSwitcherModule.java @@ -1,18 +1,32 @@ package net.viper.status.modules.serverswitcher; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.ChatColor; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.CommandSender; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.ProxyServer; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.ClickEvent; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.ComponentBuilder; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.HoverEvent; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.chat.TextComponent; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.config.ServerInfo; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.event.TabCompleteEvent; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Command; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Listener; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Plugin; +import net.viper.status.StatusAPI; import net.md_5.bungee.event.EventHandler; import net.viper.status.module.Module; @@ -57,7 +71,7 @@ public class ServerSwitcherModule implements Module { loadConfig(); if (!enabled) { - plugin.getLogger().info("[ServerSwitcherModule] Deaktiviert."); + StatusAPI.debugLog(plugin, "[ServerSwitcherModule] Deaktiviert."); return; } @@ -67,7 +81,7 @@ public class ServerSwitcherModule implements Module { ProxyServer.getInstance().getPluginManager().registerListener(plugin, new GoTabListener()); - plugin.getLogger().info("[ServerSwitcherModule] Aktiviert. Command: /" + commandName + plugin.getLogger().fine("[ServerSwitcherModule] Aktiviert. Command: /" + commandName + " | Aliases: " + aliases + " | Permission: " + permission); } diff --git a/StatusAPI/src/main/java/net/viper/status/modules/tablist/TablistModule.java b/StatusAPI/src/main/java/net/viper/status/modules/tablist/TablistModule.java index f5e4037..9374c35 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/tablist/TablistModule.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/tablist/TablistModule.java @@ -18,29 +18,19 @@ import net.md_5.bungee.protocol.packet.PlayerListItem.Item; import net.md_5.bungee.protocol.packet.PlayerListItemUpdate; import net.viper.status.module.Module; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; +import java.io.*; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Properties; -import java.util.Set; -import java.util.UUID; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; public class TablistModule implements Module, Listener { private static final String CONFIG_FILE = "tablist.properties"; - // Leerer Skin (grauer Kopf) fuer Platzhalter-Slots – selber Skin wie TAB-Plugin + // Leerer Skin (grauer Kopf) für Platzhalter-Slots private static final net.md_5.bungee.protocol.data.Property[] EMPTY_SKIN = { new net.md_5.bungee.protocol.data.Property( "textures", @@ -49,30 +39,27 @@ public class TablistModule implements Module, Listener { ) }; - private int rows = 20; - private int columns = 4; - private int total = rows * columns; - private int tabSizeMax = 80; - + // Grid – rows ist IMMER 20 (Minecraft-Client-Layout: N Slots → ceil(N/20) Spalten à 20 Zeilen) + private static final int ROWS = 20; + private int rows = ROWS, columns = 3, total = 60, tabSizeMax = 60; + private int configuredTabSize = 0; // 0 = auto-detect aus BungeeCord private UUID[] fakeUuids; - // ── Config ───────────────────────────────────────────────────────────────── - private boolean enabled = true; - private int updateInterval = 5; + // Skin-Cache (pro Spieler) + private final ConcurrentHashMap skinCache = new ConcurrentHashMap<>(); - private String headerLine1 = "&8&m" + rep('\u2501', 53); - private String headerLine2 = " &6&lViper Network"; - private String headerLine3 = "&8&m" + rep('\u2501', 53); - private String footerLine1 = "&8&m" + rep('\u2501', 53); - private String footerLine2 = " &7Discord: &ediscord.viper-network.de &8| &7Shop: &eviper-network.de/shop"; - private String footerLine3 = "&8&m" + rep('\u2501', 53); + // Config + private boolean enabled = true; + private int updateInterval = 5; + private String layoutMode = "compact"; - private String colorSrvHeader = "&6&l"; + private String headerLine1 = "&8&m" + rep('\u2501', 53); + private String headerLine2 = " &6&lViper Network"; + private String headerLine3 = "&8&m" + rep('\u2501', 53); + private String footerLine1 = "&8&m" + rep('\u2501', 53); + private String footerLine2 = " &7Discord: &ediscord.viper-network.de &8| &7Shop: &eviper-network.de/shop"; + private String footerLine3 = "&8&m" + rep('\u2501', 53); - // Header/Footer Layout-Modus: "classic" oder "compact" - private String layoutMode = "classic"; - - // Compact-Layout Header/Footer private String compactHeader1 = "&6&lViper Network &8• &7%online% Spieler online"; private String compactHeader2 = ""; private String compactHeader3 = ""; @@ -85,70 +72,61 @@ public class TablistModule implements Module, Listener { private boolean compactFooter1Spacer = false; private boolean compactFooter4Spacer = false; - // Konfigurierbare Info-Eintraege (Reihenfolge aus Config) + private String colorSrvHeader = "&6&l"; + private String timeFormat = "HH:mm:ss / h:mm a"; + private String timeZone = "Europe/Berlin"; + private SimpleDateFormat sdf; + private List serverOrder = new ArrayList<>(); + private Set hiddenServers = new HashSet<>(); + private List rankOrder = new ArrayList<>(); + + // Info-Spalte private static class InfoEntry { - String label; - String type; // website, name, rank, server, world, time, teamspeak, custom - String value; // fuer custom und statische Werte - boolean enabled; - InfoEntry(String label, String type, String value, boolean enabled) { - this.label = label; this.type = type; this.value = value; this.enabled = enabled; - } + String label, type, value; boolean enabled; + InfoEntry(String l, String t, String v, boolean e) { label=l; type=t; value=v; enabled=e; } } private List infoEntries = new ArrayList<>(); - private String timeFormat = "HH:mm:ss / h:mm a"; - private String timeZone = "Europe/Berlin"; - private SimpleDateFormat sdf; - private List serverOrder = new ArrayList<>(); - private Set hiddenServers = new HashSet<>(); - // Rang-Reihenfolge fuer Spieler-Sortierung (hoechster Rang zuerst) - private List rankOrder = new ArrayList<>(); - - // ── State ────────────────────────────────────────────────────────────────── - private Plugin plugin; + // State + private Plugin plugin; private ScheduledTask updateTask; - private Method sendPacketQueuedMethod; - // Spieler die bereits ADD_PLAYER erhalten haben – nur noch UPDATE nötig - private final Set initializedViewers = new HashSet<>(); - - // ══════════════════════════════════════════════════════════════════════════ + private Method sendPacketQueuedMethod; @Override public String getName() { return "TablistModule"; } @Override public void onEnable(Plugin plugin) { this.plugin = plugin; + plugin.getLogger().info("[TablistModule] Starte..."); ensureConfigExists(); loadConfig(); + plugin.getLogger().info("[TablistModule] Config geladen. Layout=" + layoutMode + " enabled=" + enabled); if (!enabled) { plugin.getLogger().info("[TablistModule] Deaktiviert."); return; } - - initGridSize(); - - fakeUuids = new UUID[total]; - for (int i = 0; i < total; i++) - fakeUuids[i] = new UUID(0xFFFEDEAD00000000L, (long) i); - + try { + initGridSize(); + } catch (Exception e) { + plugin.getLogger().warning("[TablistModule] initGridSize Fehler: " + e.getMessage() + " – nutze Fallback 3x20"); + int fbSize = configuredTabSize > 0 ? configuredTabSize : 60; + tabSizeMax = fbSize; rows = ROWS; columns = Math.min(Math.max(3, fbSize / ROWS), 8); total = ROWS * columns; + } + initUuids(); try { Class uc = Class.forName("net.md_5.bungee.UserConnection"); sendPacketQueuedMethod = uc.getMethod("sendPacketQueued", net.md_5.bungee.protocol.DefinedPacket.class); sendPacketQueuedMethod.setAccessible(true); + plugin.getLogger().info("[TablistModule] sendPacketQueued gefunden."); } catch (Exception e) { - plugin.getLogger().severe("[TablistModule] sendPacketQueued nicht gefunden: " + e.getMessage()); + plugin.getLogger().severe("[TablistModule] sendPacketQueued NICHT gefunden: " + e.getMessage()); return; } - ProxyServer.getInstance().getPluginManager().registerListener(plugin, this); - updateTask = ProxyServer.getInstance().getScheduler().schedule( - plugin, this::updateAll, 2L, Math.max(1, updateInterval), TimeUnit.SECONDS); - + updateTask = ProxyServer.getInstance().getScheduler().schedule(plugin, this::updateAll, 2L, Math.max(1, updateInterval), TimeUnit.SECONDS); ProxyServer.getInstance().getScheduler().schedule(plugin, () -> { - List all = new ArrayList<>(ProxyServer.getInstance().getServers().keySet()); - plugin.getLogger().info("[TablistModule] Alle BungeeCord-Server: " + all); - plugin.getLogger().info("[TablistModule] Tablist-Spalten (" + columns + "x" + rows + "): " + getServerOrder()); + plugin.getLogger().info("[TablistModule] Alle BungeeCord-Server: " + new ArrayList<>(ProxyServer.getInstance().getServers().keySet())); + plugin.getLogger().info("[TablistModule] Tablist-Spalten: " + getServerOrder()); + recalculateGrid(); }, 3L, TimeUnit.SECONDS); - - plugin.getLogger().info("[TablistModule] Aktiviert. Grid=" + columns + "x" + rows + ", Interval=" + updateInterval + "s"); + plugin.getLogger().info("[TablistModule] Aktiviert. Grid=" + columns + "x" + rows + " layout=" + layoutMode); } @Override @@ -161,84 +139,143 @@ public class TablistModule implements Module, Listener { } private void initGridSize() { - int tabSize = 80; + int tabSize = 60; try { for (ListenerInfo li : ProxyServer.getInstance().getConfig().getListeners()) { - try { - Object val = li.getClass().getMethod("getTabSize").invoke(li); - if (val instanceof Number && ((Number) val).intValue() > 0) { - tabSize = ((Number) val).intValue(); break; - } + try { Object v = li.getClass().getMethod("getTabSize").invoke(li); + if (v instanceof Number && ((Number)v).intValue() > 0) { tabSize = ((Number)v).intValue(); break; } } catch (Exception ignored) {} } } catch (Exception ignored) {} + if (configuredTabSize > 0) tabSize = configuredTabSize; // manuell gesetzt in tablist.properties tabSizeMax = tabSize; - rows = 20; + rows = ROWS; // immer 20 – Minecraft-Client-Pflicht + boolean hasInfo = !"compact".equalsIgnoreCase(layoutMode); int serverCount = getServerOrder().size(); - // +1 fuer Info-Spalte - int needed = 1 + serverCount; - columns = Math.max(2, Math.min(needed, tabSize / rows)); - total = rows * columns; - plugin.getLogger().info("[TablistModule] tab_size=" + tabSize + " -> " + columns + "x" + rows + "=" + total); + int needed = (hasInfo ? 1 : 0) + Math.max(1, serverCount); + // Spalten = benötigte Spalten, aber max was tab-size erlaubt (tab-size/20) + columns = Math.max(hasInfo ? 2 : 1, Math.min(needed, tabSize / ROWS)); + total = ROWS * columns; + if (needed > tabSize / ROWS) { + plugin.getLogger().warning("[TablistModule] Nicht alle Server passen in die Tablist! " + + "Erhöhe tab-size in der BungeeCord config.yml auf mindestens " + (needed * ROWS) + + " (aktuell: " + tabSize + ")"); + } + plugin.getLogger().info("[TablistModule] tab_size=" + tabSize + " -> " + columns + "x" + ROWS + "=" + total + " (" + serverCount + " Server)"); + } + + private void initUuids() { + fakeUuids = new UUID[total]; + for (int i = 0; i < total; i++) fakeUuids[i] = new UUID(0xFFFEDEAD00000000L, (long) i); } // ── Events ───────────────────────────────────────────────────────────────── - @EventHandler public void onLogin(PostLoginEvent e) { + @EventHandler + public void onLogin(PostLoginEvent e) { if (!enabled) return; - ProxyServer.getInstance().getScheduler().schedule(plugin, - () -> updateTablist(e.getPlayer()), 3L, TimeUnit.SECONDS); + ProxiedPlayer p = e.getPlayer(); + net.md_5.bungee.protocol.data.Property[] skin = fetchSkin(p); + if (skin != null && skin.length > 0) skinCache.put(p.getUniqueId(), skin); + ProxyServer.getInstance().getScheduler().schedule(plugin, () -> { + updateTablist(p); + // Nach 2s nochmals für alle damit der neue Spieler mit Kopf erscheint + ProxyServer.getInstance().getScheduler().schedule(plugin, this::updateAll, 2L, TimeUnit.SECONDS); + }, 2L, TimeUnit.SECONDS); } - @EventHandler public void onSwitch(ServerSwitchEvent e) { + @EventHandler + public void onSwitch(ServerSwitchEvent e) { if (!enabled) return; - initializedViewers.remove(e.getPlayer().getUniqueId()); - ProxyServer.getInstance().getScheduler().schedule(plugin, - () -> updateTablist(e.getPlayer()), 1L, TimeUnit.SECONDS); + ProxiedPlayer switched = e.getPlayer(); + + // Skin sofort cachen (noch auf dem alten Server, LoginProfile noch verfügbar) + net.md_5.bungee.protocol.data.Property[] skin = fetchSkin(switched); + if (skin != null && skin.length > 0) skinCache.put(switched.getUniqueId(), skin); + + // Nach 1s: alle Fake-Slots bei allen Viewern entfernen → erzwingt frisches ADD_PLAYER mit neuem Skin + ProxyServer.getInstance().getScheduler().schedule(plugin, () -> { + // Skin nochmals versuchen (jetzt auf neuem Server) + net.md_5.bungee.protocol.data.Property[] freshSkin = fetchSkin(switched); + if (freshSkin != null && freshSkin.length > 0) skinCache.put(switched.getUniqueId(), freshSkin); + + // Alle Slots bei allen Viewern entfernen + for (ProxiedPlayer viewer : ProxyServer.getInstance().getPlayers()) { + try { removeFakeSlots(viewer); } catch (Exception ignored) {} + } + + // Sofort neu aufbauen (kein weiterer Delay nötig da removeFakeSlots synchron ist) + updateAll(); + + // Nochmal nach 2s als Sicherheit + ProxyServer.getInstance().getScheduler().schedule(plugin, () -> { + net.md_5.bungee.protocol.data.Property[] s2 = fetchSkin(switched); + if (s2 != null && s2.length > 0) skinCache.put(switched.getUniqueId(), s2); + for (ProxiedPlayer viewer : ProxyServer.getInstance().getPlayers()) { + try { removeFakeSlots(viewer); } catch (Exception ignored) {} + } + updateAll(); + }, 2L, TimeUnit.SECONDS); + + }, 1L, TimeUnit.SECONDS); } - @EventHandler public void onDisconnect(PlayerDisconnectEvent e) { + @EventHandler + public void onDisconnect(PlayerDisconnectEvent e) { if (!enabled) return; - initializedViewers.remove(e.getPlayer().getUniqueId()); - ProxyServer.getInstance().getScheduler().schedule(plugin, this::updateAll, 1L, TimeUnit.SECONDS); + skinCache.remove(e.getPlayer().getUniqueId()); + // Erst alle Fake-Slots entfernen, dann nach kurzer Pause neu aufbauen + // So verschwindet der Kopf des Spielers zuverlässig + ProxyServer.getInstance().getScheduler().schedule(plugin, () -> { + for (ProxiedPlayer viewer : ProxyServer.getInstance().getPlayers()) { + try { removeFakeSlots(viewer); } catch (Exception ignored) {} + } + ProxyServer.getInstance().getScheduler().schedule(plugin, + this::updateAll, 1L, TimeUnit.SECONDS); + }, 1L, TimeUnit.SECONDS); } // ── Core ─────────────────────────────────────────────────────────────────── private void updateAll() { recalculateGrid(); + // Fehlende Skins nachladen + for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) { + if (!skinCache.containsKey(p.getUniqueId())) { + net.md_5.bungee.protocol.data.Property[] skin = fetchSkin(p); + if (skin != null && skin.length > 0) skinCache.put(p.getUniqueId(), skin); + } + } for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) updateTablist(p); } private void recalculateGrid() { + boolean hasInfo = !"compact".equalsIgnoreCase(layoutMode); int serverCount = getServerOrder().size(); - // Im compact-Modus keine Info-Spalte, alle Spalten fuer Server - boolean hasInfoCol = !"compact".equalsIgnoreCase(layoutMode); - int needed = (hasInfoCol ? 1 : 0) + serverCount; - int newColumns = Math.max(hasInfoCol ? 2 : 1, Math.min(needed, tabSizeMax / rows)); - int newTotal = rows * newColumns; + int needed = (hasInfo ? 1 : 0) + Math.max(1, serverCount); + // Spalten = benötigte Spalten, aber max was tab-size erlaubt (tab-size/20) + int newColumns = Math.max(hasInfo ? 2 : 1, Math.min(needed, tabSizeMax / ROWS)); + int newTotal = ROWS * newColumns; if (newColumns == columns && newTotal == total) return; for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) { try { removeFakeSlots(p); } catch (Exception ignored) {} } - initializedViewers.clear(); + rows = ROWS; columns = newColumns; - total = newTotal; - fakeUuids = new UUID[total]; - for (int i = 0; i < total; i++) - fakeUuids[i] = new UUID(0xFFFEDEAD00000000L, (long) i); - plugin.getLogger().info("[TablistModule] Grid: " + columns + "x" + rows + "=" + total + " (" + serverCount + " Server, layout=" + layoutMode + ")"); + total = newTotal; + initUuids(); + plugin.getLogger().info("[TablistModule] Grid: " + columns + "x" + rows + "=" + total + " (" + serverCount + " Server)"); } private void updateTablist(ProxiedPlayer viewer) { if (viewer == null || !viewer.isConnected()) return; try { - String srv = viewer.getServer() != null ? capitalize(viewer.getServer().getInfo().getName()) : "\u2014"; - String world = net.viper.status.StatusAPI.playerWorlds.getOrDefault(viewer.getUniqueId(), "world"); - String rank = getRank(viewer); - String time = sdf.format(new Date()); + String srv = viewer.getServer() != null ? capitalize(viewer.getServer().getInfo().getName()) : "\u2014"; + String world = net.viper.status.StatusAPI.playerWorlds.getOrDefault(viewer.getUniqueId(), "world"); + String rank = getRank(viewer); + String time = sdf.format(new Date()); String balance = getBalance(viewer); - int online = ProxyServer.getInstance().getOnlineCount(); + int online = ProxyServer.getInstance().getOnlineCount(); String header, footer; if ("compact".equalsIgnoreCase(layoutMode)) { @@ -248,139 +285,111 @@ public class TablistModule implements Module, Listener { header = c(headerLine1) + "\n" + c(headerLine2) + "\n" + c(headerLine3); footer = c(footerLine1) + "\n" + c(footerLine2) + "\n" + c(footerLine3); } - viewer.setTabHeader(new TextComponent(header), new TextComponent(footer)); hideRealPlayers(viewer); sendSlots(viewer, buildItems(viewer)); } catch (Exception ex) { - plugin.getLogger().warning("[TablistModule] Fehler fuer " + viewer.getName() + ": " + ex.getMessage()); + plugin.getLogger().warning("[TablistModule] " + viewer.getName() + ": " + ex.getMessage()); } } - private String buildCompactHeader(ProxiedPlayer viewer, String srv, String world, - String rank, String time, String balance, int online) { + // ── Header / Footer ──────────────────────────────────────────────────────── + + private String buildCompactHeader(ProxiedPlayer viewer, String srv, String world, String rank, String time, String balance, int online) { StringBuilder sb = new StringBuilder(); - appendLine(sb, compactHeader1, false, viewer, srv, world, rank, time, balance, online); + appendLine(sb, compactHeader1, false, viewer, srv, world, rank, time, balance, online); appendLine(sb, compactHeader2, compactHeader2Spacer, viewer, srv, world, rank, time, balance, online); appendLine(sb, compactHeader3, compactHeader3Spacer, viewer, srv, world, rank, time, balance, online); return sb.toString(); } - private String buildCompactFooter(ProxiedPlayer viewer, String srv, String world, - String rank, String time, String balance, int online) { + private String buildCompactFooter(ProxiedPlayer viewer, String srv, String world, String rank, String time, String balance, int online) { StringBuilder sb = new StringBuilder(); appendLine(sb, compactFooter1, compactFooter1Spacer, viewer, srv, world, rank, time, balance, online); - // Automatische Server-Übersicht List servers = getServerOrder(); if (!servers.isEmpty()) { - StringBuilder serverLine = new StringBuilder(); + StringBuilder sLine = new StringBuilder(); for (String sName : servers) { - ServerInfo info = ProxyServer.getInstance().getServerInfo(sName); - int count = info != null ? info.getPlayers().size() : 0; - if (serverLine.length() > 0) serverLine.append(" &8| "); - serverLine.append(c(colorSrvHeader)).append(capitalize(sName)) - .append(" &8\u25cf &7").append(count); + ServerInfo si = ProxyServer.getInstance().getServerInfo(sName); + int cnt = si != null ? si.getPlayers().size() : 0; + if (sLine.length() > 0) sLine.append(" &8| "); + sLine.append(c(colorSrvHeader)).append(capitalize(sName)).append(" &8\u25cf &7").append(cnt); } if (sb.length() > 0) sb.append("\n"); - sb.append(c(serverLine.toString())); + sb.append(c(sLine.toString())); } - appendLine(sb, compactFooter2, false, viewer, srv, world, rank, time, balance, online); - appendLine(sb, compactFooter3, false, viewer, srv, world, rank, time, balance, online); + appendLine(sb, compactFooter2, false, viewer, srv, world, rank, time, balance, online); + appendLine(sb, compactFooter3, false, viewer, srv, world, rank, time, balance, online); appendLine(sb, compactFooter4, compactFooter4Spacer, viewer, srv, world, rank, time, balance, online); return sb.toString(); } - /** - * Hängt eine Zeile an: - * - spacer=true + leer → fügt eine leere Abstandszeile ein - * - spacer=false + leer → Zeile wird komplett übersprungen - * - Text vorhanden → wird immer angezeigt - */ - private void appendLine(StringBuilder sb, String line, boolean spacer, - ProxiedPlayer viewer, String srv, String world, String rank, - String time, String balance, int online) { - boolean isEmpty = line == null || line.trim().isEmpty(); - if (isEmpty && !spacer) return; // überspringen + private void appendLine(StringBuilder sb, String line, boolean spacer, ProxiedPlayer viewer, String srv, String world, String rank, String time, String balance, int online) { + boolean empty = line == null || line.trim().isEmpty(); + if (empty && !spacer) return; if (sb.length() > 0) sb.append("\n"); - if (isEmpty) { - sb.append(" "); // Abstandszeile - } else { - sb.append(c(replacePlaceholders(line, viewer, srv, world, rank, time, balance, online))); - } + sb.append(empty ? " " : c(replacePlaceholders(line, viewer, srv, world, rank, time, balance, online))); } + // ── Items ────────────────────────────────────────────────────────────────── + private Item[] buildItems(ProxiedPlayer viewer) { - String[] texts = new String[total]; + String[] texts = new String[total]; net.md_5.bungee.protocol.data.Property[][] skins = new net.md_5.bungee.protocol.data.Property[total][]; - int[] pings = new int[total]; - for (int i = 0; i < total; i++) { - texts[i] = " "; - skins[i] = EMPTY_SKIN; - pings[i] = 0; - } + int[] pings = new int[total]; + for (int i = 0; i < total; i++) { texts[i] = " "; skins[i] = EMPTY_SKIN; pings[i] = 0; } - // ── Spalte 0: Info (nur im classic Layout) ─────────────────────────── - int base = 0, row = 0; - String srv = viewer.getServer() != null ? capitalize(viewer.getServer().getInfo().getName()) : "\u2014"; - String world = net.viper.status.StatusAPI.playerWorlds.getOrDefault(viewer.getUniqueId(), "world"); - String rank = getRank(viewer); - String time = sdf.format(new Date()); - String balance = getBalance(viewer); - int online = ProxyServer.getInstance().getOnlineCount(); + boolean compact = "compact".equalsIgnoreCase(layoutMode); - boolean compactMode = "compact".equalsIgnoreCase(layoutMode); - - if (!compactMode) { + // Info-Spalte (nur classic) + if (!compact) { + int base = 0, row = 0; + String srv = viewer.getServer() != null ? capitalize(viewer.getServer().getInfo().getName()) : "\u2014"; + String world = net.viper.status.StatusAPI.playerWorlds.getOrDefault(viewer.getUniqueId(), "world"); + String rank = getRank(viewer); String time = sdf.format(new Date()); + String balance = getBalance(viewer); int online = ProxyServer.getInstance().getOnlineCount(); for (InfoEntry entry : infoEntries) { - if (!entry.enabled) continue; - if (row + 1 >= rows) break; - if (entry.label != null && !entry.label.isEmpty()) { + if (!entry.enabled || row + 1 >= rows) continue; + if (entry.label != null && !entry.label.isEmpty()) row = set(texts, base, row, c(replacePlaceholders(entry.label, viewer, srv, world, rank, time, balance, online))); - } String val; switch (entry.type) { - case "name": val = "&f" + viewer.getName(); break; - case "rank": val = "&f" + rank; break; - case "server": val = "&f" + srv; break; - case "world": val = "&f" + world; break; - case "time": val = "&f[" + time + "]"; break; - case "balance": val = "&f" + balance; break; - case "online": val = "&f" + online; break; - default: val = replacePlaceholders(entry.value, viewer, srv, world, rank, time, balance, online); break; + case "name": val = "&f" + viewer.getName(); break; + case "rank": val = "&f" + rank; break; + case "server": val = "&f" + srv; break; + case "world": val = "&f" + world; break; + case "time": val = "&f[" + time + "]"; break; + case "balance": val = "&f" + balance; break; + case "online": val = "&f" + online; break; + default: val = replacePlaceholders(entry.value, viewer, srv, world, rank, time, balance, online); break; } row = set(texts, base, row, c(val)); row = set(texts, base, row, " "); } } - // ── Server-Spieler Spalten ──────────────────────────────────────────── + // Server-Spalten List servers = getServerOrder(); - int startCol = compactMode ? 0 : 1; + int startCol = compact ? 0 : 1; for (int col = startCol; col < columns && (col - startCol) < servers.size(); col++) { - base = col * rows; - row = 0; + int base = col * rows, row = 0; String sName = servers.get(col - startCol); row = set(texts, base, row, c(colorSrvHeader + capitalize(sName))); - ServerInfo info = ProxyServer.getInstance().getServerInfo(sName); - if (info != null) { - // Spieler nach Rang-Reihenfolge sortieren - List sorted = sortPlayersByRank(new ArrayList<>(info.getPlayers())); - for (ProxiedPlayer p : sorted) { + ServerInfo si = ProxyServer.getInstance().getServerInfo(sName); + if (si != null) { + for (ProxiedPlayer p : sortPlayersByRank(new ArrayList<>(si.getPlayers()))) { if (row >= rows) break; - String prefix = getLuckPermsPrefix(p); - String display = prefix.isEmpty() - ? c("&7" + p.getName()) - : c(prefix + "&r " + p.getName()); - set(texts, base, row, display); - skins[base + row] = getPlayerSkin(p); - int ping = p.getPing(); - pings[base + row] = ping < 0 ? 1 : ping; + String prefix = getLuckPermsPrefix(p); + set(texts, base, row, prefix.isEmpty() ? c("&7" + p.getName()) : c(prefix + "&r " + p.getName())); + // Skin aus Cache – immer aktuell + net.md_5.bungee.protocol.data.Property[] skin = skinCache.get(p.getUniqueId()); + skins[base + row] = (skin != null && skin.length > 0) ? skin : EMPTY_SKIN; + pings[base + row] = p.getPing() < 0 ? 1 : p.getPing(); row++; } } } - // Alle Slots listed=true – Layout bleibt erhalten Item[] items = new Item[total]; for (int i = 0; i < total; i++) { Item item = new Item(); @@ -402,65 +411,40 @@ public class TablistModule implements Module, Listener { private void hideRealPlayers(ProxiedPlayer viewer) { if (sendPacketQueuedMethod == null) return; try { - java.util.Collection online = ProxyServer.getInstance().getPlayers(); + Collection online = ProxyServer.getInstance().getPlayers(); if (online.isEmpty()) return; PlayerListItemUpdate pkt = new PlayerListItemUpdate(); pkt.setActions(EnumSet.of(PlayerListItemUpdate.Action.UPDATE_LISTED)); Item[] items = new Item[online.size()]; int idx = 0; for (ProxiedPlayer p : online) { - Item item = new Item(); - item.setUuid(p.getUniqueId()); - item.setListed(false); - items[idx++] = item; + Item it = new Item(); it.setUuid(p.getUniqueId()); it.setListed(false); items[idx++] = it; } pkt.setItems(items); sendPacketQueuedMethod.invoke(viewer, pkt); - } catch (Exception e) { - plugin.getLogger().warning("[TablistModule] hideRealPlayers: " + e.getMessage()); - } + } catch (Exception e) { plugin.getLogger().warning("[TablistModule] hideRealPlayers: " + e.getMessage()); } } @SuppressWarnings("unchecked") private void sendSlots(ProxiedPlayer viewer, Item[] items) { if (sendPacketQueuedMethod == null) return; - - boolean isNew = initializedViewers.add(viewer.getUniqueId()); - - if (isNew) { - // Erstes Mal: ADD_PLAYER + UPDATE_DISPLAY_NAME + UPDATE_LISTED - PlayerListItemUpdate addPkt = new PlayerListItemUpdate(); - addPkt.setActions(EnumSet.of( - PlayerListItemUpdate.Action.ADD_PLAYER, - PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME, - PlayerListItemUpdate.Action.UPDATE_LISTED)); - addPkt.setItems(items); - try { sendPacketQueuedMethod.invoke(viewer, addPkt); } - catch (Exception e) { plugin.getLogger().warning("[TablistModule] ADD_PLAYER: " + e.getMessage()); return; } - } else { - // Folgeupdate: nur DisplayName + Listed aktualisieren (kein Flackern) - PlayerListItemUpdate updPkt = new PlayerListItemUpdate(); - updPkt.setActions(EnumSet.of( - PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME, - PlayerListItemUpdate.Action.UPDATE_LISTED)); - updPkt.setItems(items); - try { sendPacketQueuedMethod.invoke(viewer, updPkt); } - catch (Exception e) { plugin.getLogger().warning("[TablistModule] UPDATE_DISPLAY_NAME: " + e.getMessage()); return; } - } - - // Ping immer separat senden - PlayerListItemUpdate pingPkt = new PlayerListItemUpdate(); - pingPkt.setActions(EnumSet.of(PlayerListItemUpdate.Action.UPDATE_LATENCY)); - pingPkt.setItems(items); - try { sendPacketQueuedMethod.invoke(viewer, pingPkt); } - catch (Exception e) { plugin.getLogger().warning("[TablistModule] UPDATE_LATENCY: " + e.getMessage()); } + // Immer vollständiges ADD_PLAYER – einfach und zuverlässig + PlayerListItemUpdate pkt = new PlayerListItemUpdate(); + pkt.setActions(EnumSet.of( + PlayerListItemUpdate.Action.ADD_PLAYER, + PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME, + PlayerListItemUpdate.Action.UPDATE_LISTED, + PlayerListItemUpdate.Action.UPDATE_LATENCY)); + pkt.setItems(items); + try { sendPacketQueuedMethod.invoke(viewer, pkt); } + catch (Exception e) { plugin.getLogger().warning("[TablistModule] sendSlots: " + e.getMessage()); } } private void removeFakeSlots(ProxiedPlayer viewer) { if (sendPacketQueuedMethod == null || fakeUuids == null) return; try { Class cls = Class.forName("net.md_5.bungee.protocol.packet.PlayerListItemRemove"); - Object pkt = cls.getDeclaredConstructor().newInstance(); + Object pkt = cls.getDeclaredConstructor().newInstance(); cls.getMethod("setUuids", UUID[].class).invoke(pkt, (Object) fakeUuids.clone()); sendPacketQueuedMethod.invoke(viewer, pkt); } catch (Exception e) { @@ -477,49 +461,71 @@ public class TablistModule implements Module, Listener { // ── Helpers ──────────────────────────────────────────────────────────────── - private int set(String[] arr, int base, int row, String text) { - if (base + row < total) arr[base + row] = text == null ? " " : text; - return row + 1; - } - - private net.md_5.bungee.protocol.data.Property[] getPlayerSkin(ProxiedPlayer player) { + private net.md_5.bungee.protocol.data.Property[] fetchSkin(ProxiedPlayer player) { try { Object pending = player.getPendingConnection(); net.md_5.bungee.connection.LoginResult profile = - (net.md_5.bungee.connection.LoginResult) - pending.getClass().getMethod("getLoginProfile").invoke(pending); - if (profile != null && profile.getProperties() != null) return profile.getProperties(); + (net.md_5.bungee.connection.LoginResult) pending.getClass().getMethod("getLoginProfile").invoke(pending); + if (profile != null && profile.getProperties() != null && profile.getProperties().length > 0) + return profile.getProperties(); } catch (Exception ignored) {} return new net.md_5.bungee.protocol.data.Property[0]; } private List getServerOrder() { - if (!serverOrder.isEmpty()) return new ArrayList<>(serverOrder); - List list = new ArrayList<>(); - final String[] lobbyKey = { null }; - for (String key : ProxyServer.getInstance().getServers().keySet()) - if (key.equalsIgnoreCase("lobby")) { lobbyKey[0] = key; break; } - if (lobbyKey[0] != null) list.add(lobbyKey[0]); - ProxyServer.getInstance().getServers().keySet().stream() + List list; + if (!serverOrder.isEmpty()) { + list = new ArrayList<>(serverOrder); + // Versteckte Server auch aus manueller Liste entfernen + list.removeIf(s -> hiddenServers.contains(s.toLowerCase())); + } else { + list = new ArrayList<>(); + final String[] lobbyKey = {null}; + for (String key : ProxyServer.getInstance().getServers().keySet()) + if (key.equalsIgnoreCase("lobby")) { lobbyKey[0] = key; break; } + if (lobbyKey[0] != null) list.add(lobbyKey[0]); + ProxyServer.getInstance().getServers().keySet().stream() .filter(s -> lobbyKey[0] == null || !s.equalsIgnoreCase(lobbyKey[0])) .filter(s -> !hiddenServers.contains(s.toLowerCase())) - .sorted(String.CASE_INSENSITIVE_ORDER) - .forEach(list::add); + .sorted(String.CASE_INSENSITIVE_ORDER).forEach(list::add); + } return list; } + private List sortPlayersByRank(List players) { + if (rankOrder.isEmpty()) return players; + players.sort((a, b) -> { int ia = getRankIndex(a), ib = getRankIndex(b); + return ia != ib ? Integer.compare(ia, ib) : a.getName().compareToIgnoreCase(b.getName()); }); + return players; + } + + private int getRankIndex(ProxiedPlayer player) { + try { + Class prov = Class.forName("net.luckperms.api.LuckPermsProvider"); + Object api = prov.getMethod("get").invoke(null); + Object um = api.getClass().getMethod("getUserManager").invoke(api); + Object usr = um.getClass().getMethod("getUser", UUID.class).invoke(um, player.getUniqueId()); + if (usr != null) { + Object pg = usr.getClass().getMethod("getPrimaryGroup").invoke(usr); + if (pg != null) { String g = pg.toString().toLowerCase(); + for (int i = 0; i < rankOrder.size(); i++) if (rankOrder.get(i).equalsIgnoreCase(g)) return i; } + } + } catch (Exception ignored) {} + return rankOrder.size(); + } + private String getRank(ProxiedPlayer player) { try { Class prov = Class.forName("net.luckperms.api.LuckPermsProvider"); Object api = prov.getMethod("get").invoke(null); - Object um = api.getClass().getMethod("getUserManager").invoke(api); + Object um = api.getClass().getMethod("getUserManager").invoke(api); Object usr = um.getClass().getMethod("getUser", UUID.class).invoke(um, player.getUniqueId()); if (usr != null) { - Class qo = Class.forName("net.luckperms.api.query.QueryOptions"); - Object opts = qo.getMethod("defaultContextualOptions").invoke(null); + Class qo = Class.forName("net.luckperms.api.query.QueryOptions"); + Object opts = qo.getMethod("defaultContextualOptions").invoke(null); Object cache = usr.getClass().getMethod("getCachedData").invoke(usr); - Object meta = cache.getClass().getMethod("getMetaData", qo).invoke(cache, opts); - Object pfx = meta.getClass().getMethod("getPrefix").invoke(meta); + Object meta = cache.getClass().getMethod("getMetaData", qo).invoke(cache, opts); + Object pfx = meta.getClass().getMethod("getPrefix").invoke(meta); if (pfx != null && !pfx.toString().isEmpty()) return pfx.toString(); Object pg = usr.getClass().getMethod("getPrimaryGroup").invoke(usr); if (pg != null && !pg.toString().isEmpty()) return "[" + pg.toString().toUpperCase() + "]"; @@ -532,95 +538,81 @@ public class TablistModule implements Module, Listener { try { Class prov = Class.forName("net.luckperms.api.LuckPermsProvider"); Object api = prov.getMethod("get").invoke(null); - Object um = api.getClass().getMethod("getUserManager").invoke(api); + Object um = api.getClass().getMethod("getUserManager").invoke(api); Object usr = um.getClass().getMethod("getUser", UUID.class).invoke(um, player.getUniqueId()); if (usr != null) { - Class qo = Class.forName("net.luckperms.api.query.QueryOptions"); - Object opts = qo.getMethod("defaultContextualOptions").invoke(null); + Class qo = Class.forName("net.luckperms.api.query.QueryOptions"); + Object opts = qo.getMethod("defaultContextualOptions").invoke(null); Object cache = usr.getClass().getMethod("getCachedData").invoke(usr); - Object meta = cache.getClass().getMethod("getMetaData", qo).invoke(cache, opts); - Object pfx = meta.getClass().getMethod("getPrefix").invoke(meta); + Object meta = cache.getClass().getMethod("getMetaData", qo).invoke(cache, opts); + Object pfx = meta.getClass().getMethod("getPrefix").invoke(meta); if (pfx != null) return ChatColor.translateAlternateColorCodes('&', pfx.toString()); } } catch (Exception ignored) {} return ""; } - /** - * Sortiert Spieler nach der konfigurierten Rang-Reihenfolge. - * Spieler mit hohem Rang (Index 0 in rankOrder) kommen zuerst. - * Spieler mit unbekanntem Rang kommen ans Ende, alphabetisch sortiert. - */ - private List sortPlayersByRank(List players) { - if (rankOrder.isEmpty()) return players; - players.sort((a, b) -> { - int idxA = getRankIndex(a); - int idxB = getRankIndex(b); - if (idxA != idxB) return Integer.compare(idxA, idxB); - return a.getName().compareToIgnoreCase(b.getName()); - }); - return players; - } - - /** Gibt den Index des Spielers in der rankOrder-Liste zurück (niedrig = höher). */ - private int getRankIndex(ProxiedPlayer player) { - try { - Class prov = Class.forName("net.luckperms.api.LuckPermsProvider"); - Object api = prov.getMethod("get").invoke(null); - Object um = api.getClass().getMethod("getUserManager").invoke(api); - Object usr = um.getClass().getMethod("getUser", UUID.class).invoke(um, player.getUniqueId()); - if (usr != null) { - Object pg = usr.getClass().getMethod("getPrimaryGroup").invoke(usr); - if (pg != null) { - String group = pg.toString().toLowerCase(); - for (int i = 0; i < rankOrder.size(); i++) { - if (rankOrder.get(i).equalsIgnoreCase(group)) return i; - } - } - } - } catch (Exception ignored) {} - return rankOrder.size(); // unbekannter Rang ans Ende - } - - private static String fakeName(int i) { return String.format("~vt%03d", i); } - private static String c(String s) { return ChatColor.translateAlternateColorCodes('&', s == null ? "" : s); } - private static String capitalize(String s) { return s == null || s.isEmpty() ? s : Character.toUpperCase(s.charAt(0)) + s.substring(1); } - private static String rep(char ch, int n) { StringBuilder sb = new StringBuilder(n); for (int i=0;i balances = (java.util.Map) net.viper.status.StatusAPI.class - .getField("playerBalances").get(null); + Map balances = (Map) net.viper.status.StatusAPI.class.getField("playerBalances").get(null); Object val = balances.get(player.getUniqueId()); - if (val != null) { - double d = ((Number) val).doubleValue(); - return String.format("%,.2f", d); - } + if (val != null) return String.format("%,.2f", ((Number) val).doubleValue()); } catch (Exception ignored) {} return "0.00"; } + private String replacePlaceholders(String text, ProxiedPlayer viewer, String srv, String world, String rank, String time, String balance, int online) { + if (text == null) return ""; + return text.replace("%player%", viewer.getName()).replace("%rank%", rank) + .replace("%server%", srv).replace("%world%", world).replace("%time%", time) + .replace("%balance%", balance).replace("%ping%", String.valueOf(viewer.getPing())) + .replace("%online%", String.valueOf(online)); + } + + private int set(String[] arr, int base, int row, String text) { + if (base + row < total) arr[base + row] = text == null ? " " : text; return row + 1; + } + + private static String c(String s) { + if (s == null) return ""; + s = replaceHexColors(s); + return ChatColor.translateAlternateColorCodes('&', s); + } + + private static String replaceHexColors(String text) { + if (text == null || (!text.contains("&#") && !text.contains("{#"))) return text; + StringBuilder sb = new StringBuilder(); + int i = 0; + while (i < text.length()) { + if (i + 7 <= text.length() && text.charAt(i) == '&' && text.charAt(i+1) == '#') { + String hex = text.substring(i+2, i+8); + if (hex.matches("[0-9a-fA-F]{6}")) { + sb.append('\u00A7').append('x'); + for (char ch : hex.toCharArray()) sb.append('\u00A7').append(ch); + i += 8; continue; + } + } + if (i + 8 < text.length() && text.charAt(i) == '{' && text.charAt(i+1) == '#') { + int end = text.indexOf('}', i+2); + if (end == i+8) { + String hex = text.substring(i+2, i+8); + if (hex.matches("[0-9a-fA-F]{6}")) { + sb.append('\u00A7').append('x'); + for (char ch : hex.toCharArray()) sb.append('\u00A7').append(ch); + i += 9; continue; + } + } + } + sb.append(text.charAt(i)); i++; + } + return sb.toString(); + } + + private static String fakeName(int i) { return String.format("~vt%03d", i); } + private static String capitalize(String s){ return s==null||s.isEmpty()?s:Character.toUpperCase(s.charAt(0))+s.substring(1); } + private static String rep(char ch, int n) { StringBuilder sb=new StringBuilder(n); for(int i=0;i map = new LinkedHashMap<>(); if (file.exists()) { - try (FileInputStream fis = new FileInputStream(file)) { - p.load(new InputStreamReader(fis, StandardCharsets.UTF_8)); + try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) continue; + int eq = line.indexOf('='); + if (eq < 1) continue; + map.put(line.substring(0, eq).trim(), line.substring(eq + 1)); + } } catch (Exception e) { plugin.getLogger().warning("[TablistModule] Ladefehler: " + e.getMessage()); } } - enabled = Boolean.parseBoolean(p.getProperty("tablist.enabled", "true")); - updateInterval = parseInt(p.getProperty("tablist.update_interval", "5"), 5); - layoutMode = p.getProperty("tablist.layout", "classic").trim().toLowerCase(); - headerLine1 = p.getProperty("tablist.header.line1", headerLine1); - headerLine2 = p.getProperty("tablist.header.line2", headerLine2); - headerLine3 = p.getProperty("tablist.header.line3", headerLine3); - footerLine1 = p.getProperty("tablist.footer.line1", footerLine1); - footerLine2 = p.getProperty("tablist.footer.line2", footerLine2); - footerLine3 = p.getProperty("tablist.footer.line3", footerLine3); - compactHeader1 = p.getProperty("tablist.compact.header.line1", compactHeader1); - compactHeader2 = p.getProperty("tablist.compact.header.line2", compactHeader2); - compactHeader3 = p.getProperty("tablist.compact.header.line3", compactHeader3); - compactHeader2Spacer = Boolean.parseBoolean(p.getProperty("tablist.compact.header.line2.spacer", "false")); - compactHeader3Spacer = Boolean.parseBoolean(p.getProperty("tablist.compact.header.line3.spacer", "false")); - compactFooter1 = p.getProperty("tablist.compact.footer.line1", compactFooter1); - compactFooter2 = p.getProperty("tablist.compact.footer.line2", compactFooter2); - compactFooter3 = p.getProperty("tablist.compact.footer.line3", compactFooter3); - compactFooter4 = p.getProperty("tablist.compact.footer.line4", compactFooter4); - compactFooter1Spacer = Boolean.parseBoolean(p.getProperty("tablist.compact.footer.line1.spacer", "false")); - compactFooter4Spacer = Boolean.parseBoolean(p.getProperty("tablist.compact.footer.line4.spacer", "false")); - colorSrvHeader = p.getProperty("tablist.color.server_header", colorSrvHeader); - timeFormat = p.getProperty("tablist.time_format", timeFormat); - timeZone = p.getProperty("tablist.timezone", timeZone); - try { - sdf = new SimpleDateFormat(timeFormat); - sdf.setTimeZone(java.util.TimeZone.getTimeZone(timeZone)); - } catch (Exception e) { - sdf = new SimpleDateFormat("HH:mm:ss / h:mm a"); - sdf.setTimeZone(java.util.TimeZone.getTimeZone("Europe/Berlin")); - } + java.util.function.BiFunction get = (k,d) -> map.getOrDefault(k,d); - // Info-Eintraege laden - infoEntries.clear(); - String orderRaw = p.getProperty("tablist.info.order", - "website,name,rank,server,world,time,teamspeak").trim(); - for (String id : orderRaw.split(",")) { - id = id.trim(); - if (id.isEmpty()) continue; - boolean enabled = Boolean.parseBoolean(p.getProperty("tablist.info." + id + ".enabled", "true")); - String label = p.getProperty("tablist.info." + id + ".label", ""); - String type = p.getProperty("tablist.info." + id + ".type", "custom"); - String value = p.getProperty("tablist.info." + id + ".value", ""); - infoEntries.add(new InfoEntry(label, type, value, enabled)); - } - // Fallback wenn keine Eintraege konfiguriert - if (infoEntries.isEmpty()) { - infoEntries.add(new InfoEntry("&b&lWebsite:", "website", "&fviper-network.de", true)); - infoEntries.add(new InfoEntry("&b&lName:", "name", "", true)); - infoEntries.add(new InfoEntry("&b&lRank:", "rank", "", true)); - infoEntries.add(new InfoEntry("&b&lServer:", "server", "", true)); - infoEntries.add(new InfoEntry("&b&lWorld:", "world", "", true)); - infoEntries.add(new InfoEntry("&b&lTime:", "time", "", true)); - infoEntries.add(new InfoEntry("&b&lTeamspeak:", "teamspeak", "&fts.viper-network.de", true)); - } + configuredTabSize = parseInt(get.apply("tablist.tab_size", "0"), 0); + enabled = Boolean.parseBoolean(get.apply("tablist.enabled", "true")); + updateInterval = parseInt(get.apply("tablist.update_interval", "5"), 5); + layoutMode = get.apply("tablist.layout", "compact").trim().toLowerCase(); + headerLine1 = get.apply("tablist.header.line1", headerLine1); + headerLine2 = get.apply("tablist.header.line2", headerLine2); + headerLine3 = get.apply("tablist.header.line3", headerLine3); + footerLine1 = get.apply("tablist.footer.line1", footerLine1); + footerLine2 = get.apply("tablist.footer.line2", footerLine2); + footerLine3 = get.apply("tablist.footer.line3", footerLine3); + compactHeader1 = get.apply("tablist.compact.header.line1", compactHeader1); + compactHeader2 = get.apply("tablist.compact.header.line2", compactHeader2); + compactHeader3 = get.apply("tablist.compact.header.line3", compactHeader3); + compactHeader2Spacer = Boolean.parseBoolean(get.apply("tablist.compact.header.line2.spacer", "false")); + compactHeader3Spacer = Boolean.parseBoolean(get.apply("tablist.compact.header.line3.spacer", "false")); + compactFooter1 = get.apply("tablist.compact.footer.line1", compactFooter1); + compactFooter2 = get.apply("tablist.compact.footer.line2", compactFooter2); + compactFooter3 = get.apply("tablist.compact.footer.line3", compactFooter3); + compactFooter4 = get.apply("tablist.compact.footer.line4", compactFooter4); + compactFooter1Spacer = Boolean.parseBoolean(get.apply("tablist.compact.footer.line1.spacer", "false")); + compactFooter4Spacer = Boolean.parseBoolean(get.apply("tablist.compact.footer.line4.spacer", "false")); + colorSrvHeader = get.apply("tablist.color.server_header", colorSrvHeader); + timeFormat = get.apply("tablist.time_format", timeFormat); + timeZone = get.apply("tablist.timezone", timeZone); + try { sdf = new SimpleDateFormat(timeFormat); sdf.setTimeZone(java.util.TimeZone.getTimeZone(timeZone)); } + catch (Exception e) { sdf = new SimpleDateFormat("HH:mm:ss / h:mm a"); } rankOrder.clear(); - String rankRaw = p.getProperty("tablist.rank_order", "").trim(); - if (!rankRaw.isEmpty()) - for (String s : rankRaw.split(",")) { String t = s.trim(); if (!t.isEmpty()) rankOrder.add(t.toLowerCase()); } + String rankRaw = get.apply("tablist.rank_order", "").trim(); + if (!rankRaw.isEmpty()) for (String s : rankRaw.split(",")) { String t=s.trim(); if(!t.isEmpty()) rankOrder.add(t.toLowerCase()); } serverOrder.clear(); - String raw = p.getProperty("tablist.server_order", "").trim(); - if (!raw.isEmpty()) - for (String s : raw.split(",")) { String t = s.trim(); if (!t.isEmpty()) serverOrder.add(t.toLowerCase()); } + String raw = get.apply("tablist.server_order", "").trim(); + if (!raw.isEmpty()) for (String s : raw.split(",")) { String t=s.trim(); if(!t.isEmpty()) serverOrder.add(t.toLowerCase()); } + hiddenServers.clear(); - String hiddenRaw = p.getProperty("tablist.hidden_servers", "").trim(); - if (!hiddenRaw.isEmpty()) - for (String s : hiddenRaw.split(",")) { String t = s.trim().toLowerCase(); if (!t.isEmpty()) hiddenServers.add(t); } + String hRaw = get.apply("tablist.hidden_servers", "").trim(); + if (!hRaw.isEmpty()) for (String s : hRaw.split(",")) { String t=s.trim().toLowerCase(); if(!t.isEmpty()) hiddenServers.add(t); } + + infoEntries.clear(); + String orderRaw = get.apply("tablist.info.order", "website,name,rank,server,world,time,teamspeak").trim(); + for (String id : orderRaw.split(",")) { + id = id.trim(); if (id.isEmpty()) continue; + boolean en = Boolean.parseBoolean(get.apply("tablist.info." + id + ".enabled", "true")); + String label = get.apply("tablist.info." + id + ".label", ""); + String type = get.apply("tablist.info." + id + ".type", "custom"); + String value = get.apply("tablist.info." + id + ".value", ""); + infoEntries.add(new InfoEntry(label, type, value, en)); + } + if (infoEntries.isEmpty()) { + infoEntries.add(new InfoEntry("&b&lWebsite:", "website", "&fviper-network.de", true)); + infoEntries.add(new InfoEntry("&b&lName:", "name", "", true)); + infoEntries.add(new InfoEntry("&b&lRank:", "rank", "", true)); + infoEntries.add(new InfoEntry("&b&lServer:", "server", "", true)); + infoEntries.add(new InfoEntry("&b&lWorld:", "world", "", true)); + infoEntries.add(new InfoEntry("&b&lTime:", "time", "", true)); + infoEntries.add(new InfoEntry("&b&lTeamspeak:", "teamspeak", "&fts.viper-network.de", true)); + } } -} +} \ No newline at end of file diff --git a/StatusAPI/src/main/java/net/viper/status/modules/verify/VerifyModule.java b/StatusAPI/src/main/java/net/viper/status/modules/verify/VerifyModule.java index bb6f91b..5aeb5a6 100644 --- a/StatusAPI/src/main/java/net/viper/status/modules/verify/VerifyModule.java +++ b/StatusAPI/src/main/java/net/viper/status/modules/verify/VerifyModule.java @@ -1,10 +1,16 @@ package net.viper.status.modules.verify; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.ChatColor; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.CommandSender; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.ProxyServer; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Command; +import net.viper.status.StatusAPI; import net.md_5.bungee.api.plugin.Plugin; import net.viper.status.module.Module; @@ -38,7 +44,7 @@ public class VerifyModule implements Module { public void onEnable(Plugin plugin) { loadConfig(plugin); ProxyServer.getInstance().getPluginManager().registerCommand(plugin, new VerifyCommand()); - plugin.getLogger().info("VerifyModule aktiviert. " + serverConfigs.size() + " Server-Konfigurationen geladen."); + plugin.getLogger().fine("VerifyModule aktiviert. " + serverConfigs.size() + " Server-Konfigurationen geladen."); } @Override @@ -56,7 +62,7 @@ public class VerifyModule implements Module { if (in == null) { plugin.getLogger().warning("Standard-config '" + fileName + "' nicht in JAR."); return; } byte[] buffer = new byte[1024]; int length; while ((length = in.read(buffer)) > 0) out.write(buffer, 0, length); - plugin.getLogger().info("Konfigurationsdatei '" + fileName + "' erstellt."); + StatusAPI.debugLog(plugin, "Konfigurationsdatei '" + fileName + "' erstellt."); } catch (Exception e) { plugin.getLogger().severe("Fehler beim Erstellen der Config: " + e.getMessage()); return; } } diff --git a/StatusAPI/src/main/resources/plugin.yml b/StatusAPI/src/main/resources/plugin.yml index bdb0b75..667b0fa 100644 --- a/StatusAPI/src/main/resources/plugin.yml +++ b/StatusAPI/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: StatusAPI main: net.viper.status.StatusAPI -version: 4.1.1 +version: 4.1.0 author: M_Viper description: StatusAPI für BungeeCord inkl. Update-Checker, Modul-System und ChatModule # Mindestanforderung: Minecraft 1.20 / BungeeCord mit PlayerChatEvent-Unterstützung