Delete src/main/java/net/viper/status/modules/tablist/TablistModule.java via Git Manager GUI
This commit is contained in:
@@ -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<String> serverOrder = new ArrayList<>();
|
||||
private Set<String> 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<String> 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<ProxiedPlayer> 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<String> 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<String> getServerOrder() {
|
||||
if (!serverOrder.isEmpty()) return new ArrayList<>(serverOrder);
|
||||
List<String> 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<n;i++) sb.append(ch); return sb.toString(); }
|
||||
private int parseInt(String s, int fb) { try { return Integer.parseInt(s == null ? "" : s.trim()); } catch (Exception e) { return fb; } }
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
// Config
|
||||
// ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private void ensureConfigExists() {
|
||||
File f = new File(plugin.getDataFolder(), CONFIG_FILE);
|
||||
if (f.exists()) return;
|
||||
if (!plugin.getDataFolder().exists()) plugin.getDataFolder().mkdirs();
|
||||
String sep = rep('\u2501', 53);
|
||||
String content =
|
||||
"# TablistModule Konfiguration\n" +
|
||||
"tablist.enabled=true\n" +
|
||||
"tablist.update_interval=5\n\n" +
|
||||
"# Server-Spalten Reihenfolge (leer = Lobby zuerst, dann alle alphabetisch)\n" +
|
||||
"# Beispiel: tablist.server_order=lobby,survival,citybuild\n" +
|
||||
"tablist.server_order=\n\n" +
|
||||
"# Server die NICHT angezeigt werden (kommagetrennt, leer = alle anzeigen)\n" +
|
||||
"tablist.hidden_servers=\n\n" +
|
||||
"tablist.header.line1=&8&m" + sep + "\n" +
|
||||
"tablist.header.line2= &6&lViper Network\n" +
|
||||
"tablist.header.line3=&8&m" + sep + "\n\n" +
|
||||
"tablist.footer.line1=&8&m" + sep + "\n" +
|
||||
"tablist.footer.line2= &7Discord: &ediscord.viper-network.de &8| &7Shop: &eviper-network.de/shop\n" +
|
||||
"tablist.footer.line3=&8&m" + sep + "\n\n" +
|
||||
"tablist.info.label.website=&b&lWebsite:\n" +
|
||||
"tablist.info.value.website=&fviper-network.de\n" +
|
||||
"tablist.info.label.name=&b&lName:\n" +
|
||||
"tablist.info.label.rank=&b&lRank:\n" +
|
||||
"tablist.info.label.server=&b&lServer:\n" +
|
||||
"tablist.info.label.world=&b&lWorld:\n" +
|
||||
"tablist.info.label.time=&b&lTime:\n" +
|
||||
"tablist.info.label.teamspeak=&b&lTeamspeak:\n" +
|
||||
"tablist.info.value.teamspeak=&fts.viper-network.de\n\n" +
|
||||
"tablist.color.server_header=&6&l\n" +
|
||||
"tablist.time_format=HH:mm:ss / h:mm a\n";
|
||||
try (OutputStream out = new FileOutputStream(f)) { out.write(content.getBytes(StandardCharsets.UTF_8)); }
|
||||
catch (Exception e) { plugin.getLogger().warning("[TablistModule] Config-Fehler: " + e.getMessage()); }
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
File file = new File(plugin.getDataFolder(), CONFIG_FILE);
|
||||
Properties p = new Properties();
|
||||
if (file.exists()) {
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
p.load(new InputStreamReader(fis, StandardCharsets.UTF_8));
|
||||
} 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);
|
||||
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);
|
||||
labelWebsite = p.getProperty("tablist.info.label.website", labelWebsite);
|
||||
valueWebsite = p.getProperty("tablist.info.value.website", valueWebsite);
|
||||
labelName = p.getProperty("tablist.info.label.name", labelName);
|
||||
labelRank = p.getProperty("tablist.info.label.rank", labelRank);
|
||||
labelServer = p.getProperty("tablist.info.label.server", labelServer);
|
||||
labelWorld = p.getProperty("tablist.info.label.world", labelWorld);
|
||||
labelTime = p.getProperty("tablist.info.label.time", labelTime);
|
||||
labelTeamspeak = p.getProperty("tablist.info.label.teamspeak", labelTeamspeak);
|
||||
valueTeamspeak = p.getProperty("tablist.info.value.teamspeak", valueTeamspeak);
|
||||
colorSrvHeader = p.getProperty("tablist.color.server_header", colorSrvHeader);
|
||||
timeFormat = p.getProperty("tablist.time_format", timeFormat);
|
||||
try { sdf = new SimpleDateFormat(timeFormat); }
|
||||
catch (Exception e) { sdf = new SimpleDateFormat("HH:mm:ss / h:mm a"); }
|
||||
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()); }
|
||||
}
|
||||
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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user