From 03ea5527b16f0b22bdbc3354b4e46301033f6c97 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Thu, 7 May 2026 19:49:20 +0000 Subject: [PATCH] Delete _trash/2026-05-07T19-39-23-130Z/src/main/java/net/viper/status/modules/tablist/TablistModule.java via Git Manager GUI --- .../status/modules/tablist/TablistModule.java | 579 ------------------ 1 file changed, 579 deletions(-) delete mode 100644 _trash/2026-05-07T19-39-23-130Z/src/main/java/net/viper/status/modules/tablist/TablistModule.java diff --git a/_trash/2026-05-07T19-39-23-130Z/src/main/java/net/viper/status/modules/tablist/TablistModule.java b/_trash/2026-05-07T19-39-23-130Z/src/main/java/net/viper/status/modules/tablist/TablistModule.java deleted file mode 100644 index 9d3b96d..0000000 --- a/_trash/2026-05-07T19-39-23-130Z/src/main/java/net/viper/status/modules/tablist/TablistModule.java +++ /dev/null @@ -1,579 +0,0 @@ -package net.viper.status.modules.tablist; - -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.config.ListenerInfo; -import net.md_5.bungee.api.config.ServerInfo; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PlayerDisconnectEvent; -import net.md_5.bungee.api.event.PostLoginEvent; -import net.md_5.bungee.api.event.ServerSwitchEvent; -import net.md_5.bungee.api.plugin.Listener; -import net.md_5.bungee.api.plugin.Plugin; -import net.md_5.bungee.api.scheduler.ScheduledTask; -import net.md_5.bungee.event.EventHandler; -import net.md_5.bungee.protocol.packet.PlayerListItem; -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.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.concurrent.TimeUnit; - -/** - * TablistModule fuer StatusAPI (BungeeCord 1.19.3+). - * - * Liest die tab_size aus der BungeeCord-Konfiguration und berechnet - * ROWS/COLUMNS dynamisch: - * tab_size=60 -> 3 Spalten x 20 Zeilen - * tab_size=80 -> 4 Spalten x 20 Zeilen - * - * Layout: - * Spalte 0 = Info (Website / Name / Rank / Server / World / Time / TS) - * Spalte 1 = Spieler auf Server 1 (Lobby) - * Spalte 2 = Spieler auf Server 2 - * Spalte 3 = Spieler auf Server 3 (nur bei tab_size=80) - */ -public class TablistModule implements Module, Listener { - - private static final String CONFIG_FILE = "tablist.properties"; - - // Wird beim Start dynamisch aus BungeeCord tab_size ermittelt - private int rows = 20; - private int columns = 4; - private int total = rows * columns; - - // Fake-UUIDs – werden nach Bestimmung von total initialisiert - private UUID[] fakeUuids; - - // ── Config ───────────────────────────────────────────────────────────────── - private boolean enabled = true; - private int updateInterval = 5; - - 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); - - private String labelWebsite = "&b&lWebsite:"; - private String valueWebsite = "&fviper-network.de"; - private String labelName = "&b&lName:"; - private String labelRank = "&b&lRank:"; - private String labelServer = "&b&lServer:"; - private String labelWorld = "&b&lWorld:"; - private String labelTime = "&b&lTime:"; - private String labelTeamspeak = "&b&lTeamspeak:"; - private String valueTeamspeak = "&fts.viper-network.de"; - private String colorSrvHeader = "&6&l"; - - private String timeFormat = "HH:mm:ss / h:mm a"; - private SimpleDateFormat sdf; - private List serverOrder = new ArrayList<>(); - private Set hiddenServers = new HashSet<>(); - - // ── State ────────────────────────────────────────────────────────────────── - private Plugin plugin; - private ScheduledTask updateTask; - private Method sendPacketQueuedMethod; - private int tabSizeMax = 80; // maximale Slots laut BungeeCord tab_size - - // ══════════════════════════════════════════════════════════════════════════ - - @Override public String getName() { return "TablistModule"; } - - @Override - public void onEnable(Plugin plugin) { - this.plugin = plugin; - ensureConfigExists(); - loadConfig(); - if (!enabled) { plugin.getLogger().info("[TablistModule] Deaktiviert."); return; } - - // Tab-Size aus BungeeCord auslesen und ROWS/COLUMNS berechnen - initGridSize(); - - // Fake-UUIDs initialisieren - fakeUuids = new UUID[total]; - for (int i = 0; i < total; i++) { - fakeUuids[i] = new UUID(0xFFFEDEAD00000000L, (long) i); - } - - // Reflection-Cache fuer sendPacketQueued - try { - Class ucClass = Class.forName("net.md_5.bungee.UserConnection"); - sendPacketQueuedMethod = ucClass.getMethod("sendPacketQueued", - net.md_5.bungee.protocol.DefinedPacket.class); - sendPacketQueuedMethod.setAccessible(true); - } catch (Exception e) { - 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); - - // Server-Erkennung loggen - 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()); - }, 3L, TimeUnit.SECONDS); - - plugin.getLogger().info("[TablistModule] Aktiviert. Grid=" + columns + "x" + rows - + " (total=" + total + "), Interval=" + updateInterval + "s"); - } - - @Override - public void onDisable(Plugin plugin) { - if (updateTask != null) { updateTask.cancel(); updateTask = null; } - for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) { - try { - removeFakeSlots(p); - p.setTabHeader(new TextComponent(""), new TextComponent("")); - } catch (Exception ignored) {} - } - } - - /** - * Liest tab_size aus dem ersten BungeeCord-Listener und berechnet ROWS/COLUMNS. - * Minecraft zeigt immer 20 Zeilen, die Spaltenanzahl ergibt sich aus tab_size/20. - * Fallback: 4 Spalten x 20 Zeilen = 80. - */ - private void initGridSize() { - int tabSize = 80; - try { - for (ListenerInfo li : ProxyServer.getInstance().getConfig().getListeners()) { - // getTabSize() nicht im API-Interface, daher Reflection - try { - java.lang.reflect.Method m = li.getClass().getMethod("getTabSize"); - Object val = m.invoke(li); - if (val instanceof Number && ((Number) val).intValue() > 0) { - tabSize = ((Number) val).intValue(); - break; - } - } catch (Exception ignored) {} - } - } catch (Exception e) { - plugin.getLogger().warning("[TablistModule] Konnte tab_size nicht lesen, nutze 80."); - } - rows = 20; - tabSizeMax = tabSize; - // Beim Start noch keine Server bekannt → maxColumns als Startwert, recalculateGrid korrigiert später - columns = Math.max(2, tabSize / rows); - total = rows * columns; - - // Sofort korrekt berechnen falls Server bereits bekannt - int serverCount = getServerOrder().size(); - if (serverCount > 0) { - int needed = 1 + serverCount; - columns = Math.max(2, Math.min(needed, tabSize / rows)); - total = rows * columns; - } - plugin.getLogger().info("[TablistModule] BungeeCord tab_size=" + tabSize - + " -> " + columns + " Spalten x " + rows + " Zeilen = " + total + " Slots"); - } - - // ══════════════════════════════════════════════════════════════════════════ - // Events - // ══════════════════════════════════════════════════════════════════════════ - - @EventHandler public void onLogin(PostLoginEvent e) { - if (!enabled) return; - ProxyServer.getInstance().getScheduler().schedule(plugin, - () -> updateTablist(e.getPlayer()), 3L, TimeUnit.SECONDS); - } - - @EventHandler public void onSwitch(ServerSwitchEvent e) { - if (!enabled) return; - ProxyServer.getInstance().getScheduler().schedule(plugin, - () -> updateTablist(e.getPlayer()), 1L, TimeUnit.SECONDS); - } - - @EventHandler public void onDisconnect(PlayerDisconnectEvent e) { - if (!enabled) return; - ProxyServer.getInstance().getScheduler().schedule(plugin, this::updateAll, 1L, TimeUnit.SECONDS); - } - - // ══════════════════════════════════════════════════════════════════════════ - // Core - // ══════════════════════════════════════════════════════════════════════════ - - private void updateAll() { - recalculateGrid(); - for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) updateTablist(p); - } - - /** - * Berechnet Spaltenanzahl dynamisch anhand der aktuell sichtbaren Server. - * Spalte 0 = Info, Spalten 1..n = Server - * Minimum: 2 Spalten (Info + 1 Server) - * Maximum: durch tab_size begrenzt - */ - private void recalculateGrid() { - int serverCount = getServerOrder().size(); - int needed = 1 + serverCount; // Info-Spalte + Server-Spalten - int maxColumns = tabSizeMax / rows; - int newColumns = Math.max(2, Math.min(needed, maxColumns)); - int newTotal = rows * newColumns; - - if (newColumns == columns && newTotal == total) return; - - // Grid hat sich geändert – alte Fake-Slots bei allen Spielern entfernen - for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) { - try { removeFakeSlots(p); } catch (Exception ignored) {} - } - columns = newColumns; - total = newTotal; - - // Fake-UUIDs neu initialisieren - fakeUuids = new UUID[total]; - for (int i = 0; i < total; i++) { - fakeUuids[i] = new UUID(0xFFFEDEAD00000000L, (long) i); - } - - plugin.getLogger().info("[TablistModule] Grid: " - + columns + " Spalten x " + rows + " Zeilen = " + total - + " Slots (" + serverCount + " Server)"); - } - - private void updateTablist(ProxiedPlayer viewer) { - if (viewer == null || !viewer.isConnected()) return; - try { - String header = c(headerLine1) + "\n" + c(headerLine2) + "\n" + c(headerLine3); - String 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()); - } - } - - /** - * Setzt alle echten Spieler-Slots auf listed=false damit sie in der Tablist - * unsichtbar werden und unsere Fake-Slots nicht verschieben. - */ - @SuppressWarnings("unchecked") - private void hideRealPlayers(ProxiedPlayer viewer) { - if (sendPacketQueuedMethod == null) return; - try { - java.util.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; - } - pkt.setItems(items); - sendPacketQueuedMethod.invoke(viewer, pkt); - } catch (Exception e) { - plugin.getLogger().warning("[TablistModule] hideRealPlayers: " + e.getMessage()); - } - } - - // ══════════════════════════════════════════════════════════════════════════ - // Item-Matrix aufbauen - // ══════════════════════════════════════════════════════════════════════════ - - private Item[] buildItems(ProxiedPlayer viewer) { - 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] = new net.md_5.bungee.protocol.data.Property[0]; - pings[i] = 0; // Fake-Ping fuer leere Slots - } - - // ── Spalte 0: Info ──────────────────────────────────────────────────── - int base = 0, row = 0; - row = set(texts, base, row, c(labelWebsite)); - row = set(texts, base, row, c(valueWebsite)); - row = set(texts, base, row, " "); - row = set(texts, base, row, c(labelName)); - row = set(texts, base, row, c("&f" + viewer.getName())); - row = set(texts, base, row, " "); - row = set(texts, base, row, c(labelRank)); - row = set(texts, base, row, c("&f" + getRank(viewer))); - row = set(texts, base, row, " "); - String srv = viewer.getServer() != null ? capitalize(viewer.getServer().getInfo().getName()) : "\u2014"; - row = set(texts, base, row, c(labelServer)); - row = set(texts, base, row, c("&f" + srv)); - row = set(texts, base, row, " "); - row = set(texts, base, row, c(labelWorld)); - String world = net.viper.status.StatusAPI.playerWorlds.getOrDefault(viewer.getUniqueId(), "world"); - row = set(texts, base, row, c("&f" + world)); - row = set(texts, base, row, " "); - row = set(texts, base, row, c(labelTime)); - row = set(texts, base, row, c("&f[" + sdf.format(new Date()) + "]")); - row = set(texts, base, row, " "); - row = set(texts, base, row, c(labelTeamspeak)); - row = set(texts, base, row, c(valueTeamspeak)); - - // ── Spalten 1 bis (columns-1): Server-Spieler ───────────────────────── - List servers = getServerOrder(); - for (int col = 1; col < columns && (col - 1) < servers.size(); col++) { - base = col * rows; - row = 0; - String sName = servers.get(col - 1); - row = set(texts, base, row, c(colorSrvHeader + capitalize(sName))); - - ServerInfo info = ProxyServer.getInstance().getServerInfo(sName); - if (info != null) { - for (ProxiedPlayer p : info.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); - pings[base + row] = Math.max(0, p.getPing()); - row++; - } - } - } - - // Items zusammenbauen - Item[] items = new Item[total]; - for (int i = 0; i < total; i++) { - Item item = new Item(); - item.setUuid(fakeUuids[i]); - item.setUsername(fakeName(i)); - item.setProperties(skins[i]); - item.setGamemode(0); - item.setPing(pings[i]); - item.setListed(true); - String text = texts[i]; - item.setDisplayName(new TextComponent(text == null || text.isEmpty() ? " " : text)); - items[i] = item; - } - return items; - } - - 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) { - 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(); - } - } catch (Exception ignored) {} - return new net.md_5.bungee.protocol.data.Property[0]; - } - - // ══════════════════════════════════════════════════════════════════════════ - // Pakete senden - // ══════════════════════════════════════════════════════════════════════════ - - @SuppressWarnings("unchecked") - private void sendSlots(ProxiedPlayer viewer, Item[] items) { - if (sendPacketQueuedMethod == null) return; - PlayerListItemUpdate pkt = new PlayerListItemUpdate(); - EnumSet actions = EnumSet.of( - PlayerListItemUpdate.Action.ADD_PLAYER, - PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME, - PlayerListItemUpdate.Action.UPDATE_LISTED, - PlayerListItemUpdate.Action.UPDATE_LATENCY - ); - pkt.setActions(actions); - pkt.setItems(items); - try { sendPacketQueuedMethod.invoke(viewer, pkt); } - catch (Exception e) { plugin.getLogger().warning("[TablistModule] sendPacketQueued: " + 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(); - cls.getMethod("setUuids", UUID[].class).invoke(pkt, (Object) fakeUuids.clone()); - sendPacketQueuedMethod.invoke(viewer, pkt); - } catch (Exception e) { - try { - PlayerListItem rem = new PlayerListItem(); - rem.setAction(PlayerListItem.Action.REMOVE_PLAYER); - Item[] items = new Item[total]; - for (int i = 0; i < total; i++) { Item it = new Item(); it.setUuid(fakeUuids[i]); items[i] = it; } - rem.setItems(items); - sendPacketQueuedMethod.invoke(viewer, rem); - } catch (Exception ignored) {} - } - } - - // ══════════════════════════════════════════════════════════════════════════ - // Helpers - // ══════════════════════════════════════════════════════════════════════════ - - 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() - .filter(s -> lobbyKey[0] == null || !s.equalsIgnoreCase(lobbyKey[0])) - .filter(s -> !hiddenServers.contains(s.toLowerCase())) - .sorted(String.CASE_INSENSITIVE_ORDER) - .forEach(list::add); - return list; - } - - 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 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); - 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); - 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() + "]"; - } - } catch (Exception ignored) {} - return "NONE"; - } - - private String getLuckPermsPrefix(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) { - 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); - if (pfx != null) return ChatColor.translateAlternateColorCodes('&', pfx.toString()); - } - } catch (Exception ignored) {} - return ""; - } - - 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