Upload folder via GUI - src
This commit is contained in:
@@ -43,8 +43,8 @@ public class TablistModule implements Module, Listener {
|
|||||||
|
|
||||||
// Grid – rows ist IMMER 20 (Minecraft-Client-Layout: N Slots → ceil(N/20) Spalten à 20 Zeilen)
|
// Grid – rows ist IMMER 20 (Minecraft-Client-Layout: N Slots → ceil(N/20) Spalten à 20 Zeilen)
|
||||||
private static final int ROWS = 20;
|
private static final int ROWS = 20;
|
||||||
private int rows = ROWS, columns = 3, total = 60, tabSizeMax = 60;
|
private int rows = ROWS, columns = 6, total = 120, tabSizeMax = 180;
|
||||||
private int configuredTabSize = 0; // 0 = auto-detect aus BungeeCord
|
private int configuredTabSize = 180; // Default: 180 (9 Spalten möglich)
|
||||||
private UUID[] fakeUuids;
|
private UUID[] fakeUuids;
|
||||||
|
|
||||||
// Skin-Cache (pro Spieler)
|
// Skin-Cache (pro Spieler)
|
||||||
@@ -113,6 +113,7 @@ public class TablistModule implements Module, Listener {
|
|||||||
private Plugin plugin;
|
private Plugin plugin;
|
||||||
private ScheduledTask updateTask;
|
private ScheduledTask updateTask;
|
||||||
private Method sendPacketQueuedMethod;
|
private Method sendPacketQueuedMethod;
|
||||||
|
private java.lang.reflect.Field tabListHandlerField; // BungeeCords interner Tab-Handler
|
||||||
|
|
||||||
@Override public String getName() { return "TablistModule"; }
|
@Override public String getName() { return "TablistModule"; }
|
||||||
|
|
||||||
@@ -129,13 +130,19 @@ public class TablistModule implements Module, Listener {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
plugin.getLogger().warning("[TablistModule] initGridSize Fehler: " + e.getMessage() + " – nutze Fallback 3x20");
|
plugin.getLogger().warning("[TablistModule] initGridSize Fehler: " + e.getMessage() + " – nutze Fallback 3x20");
|
||||||
int fbSize = configuredTabSize > 0 ? configuredTabSize : 60;
|
int fbSize = configuredTabSize > 0 ? configuredTabSize : 60;
|
||||||
tabSizeMax = fbSize; rows = ROWS; columns = Math.min(Math.max(3, fbSize / ROWS), 8); total = ROWS * columns;
|
int maxCols = fbSize / ROWS; // kein hartes Limit mehr – tab_size entscheidet
|
||||||
|
tabSizeMax = fbSize; rows = ROWS; columns = Math.max(3, maxCols); total = ROWS * columns;
|
||||||
}
|
}
|
||||||
initUuids();
|
initUuids();
|
||||||
try {
|
try {
|
||||||
Class<?> uc = Class.forName("net.md_5.bungee.UserConnection");
|
Class<?> uc = Class.forName("net.md_5.bungee.UserConnection");
|
||||||
sendPacketQueuedMethod = uc.getMethod("sendPacketQueued", net.md_5.bungee.protocol.DefinedPacket.class);
|
sendPacketQueuedMethod = uc.getMethod("sendPacketQueued", net.md_5.bungee.protocol.DefinedPacket.class);
|
||||||
sendPacketQueuedMethod.setAccessible(true);
|
sendPacketQueuedMethod.setAccessible(true);
|
||||||
|
// BungeeCords internen tabListHandler-Field finden und nullbar machen
|
||||||
|
try {
|
||||||
|
tabListHandlerField = uc.getDeclaredField("tabListHandler");
|
||||||
|
tabListHandlerField.setAccessible(true);
|
||||||
|
} catch (Exception ignored) {}
|
||||||
plugin.getLogger().info("[TablistModule] sendPacketQueued gefunden.");
|
plugin.getLogger().info("[TablistModule] sendPacketQueued gefunden.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
plugin.getLogger().severe("[TablistModule] sendPacketQueued NICHT gefunden: " + e.getMessage());
|
plugin.getLogger().severe("[TablistModule] sendPacketQueued NICHT gefunden: " + e.getMessage());
|
||||||
@@ -163,14 +170,19 @@ public class TablistModule implements Module, Listener {
|
|||||||
|
|
||||||
private void initGridSize() {
|
private void initGridSize() {
|
||||||
int tabSize = 60;
|
int tabSize = 60;
|
||||||
try {
|
// FIX: configuredTabSize (aus tablist.properties) hat Vorrang.
|
||||||
for (ListenerInfo li : ProxyServer.getInstance().getConfig().getListeners()) {
|
// Nur wenn nicht gesetzt, per Reflection aus BungeeCord lesen.
|
||||||
try { Object v = li.getClass().getMethod("getTabSize").invoke(li);
|
if (configuredTabSize > 0) {
|
||||||
if (v instanceof Number && ((Number)v).intValue() > 0) { tabSize = ((Number)v).intValue(); break; }
|
tabSize = configuredTabSize;
|
||||||
} catch (Exception ignored) {}
|
} else {
|
||||||
}
|
try {
|
||||||
} catch (Exception ignored) {}
|
for (ListenerInfo li : ProxyServer.getInstance().getConfig().getListeners()) {
|
||||||
if (configuredTabSize > 0) tabSize = configuredTabSize;
|
try { Object v = li.getClass().getMethod("getTabListSize").invoke(li);
|
||||||
|
if (v instanceof Number && ((Number)v).intValue() > 0) { tabSize = ((Number)v).intValue(); break; }
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
tabSizeMax = tabSize;
|
tabSizeMax = tabSize;
|
||||||
rows = ROWS;
|
rows = ROWS;
|
||||||
boolean hasInfo = !"compact".equalsIgnoreCase(layoutMode);
|
boolean hasInfo = !"compact".equalsIgnoreCase(layoutMode);
|
||||||
@@ -179,11 +191,13 @@ public class TablistModule implements Module, Listener {
|
|||||||
columns = Math.max(hasInfo ? 2 : 1, Math.min(needed, tabSize / ROWS));
|
columns = Math.max(hasInfo ? 2 : 1, Math.min(needed, tabSize / ROWS));
|
||||||
total = ROWS * columns;
|
total = ROWS * columns;
|
||||||
if (needed > tabSize / ROWS) {
|
if (needed > tabSize / ROWS) {
|
||||||
plugin.getLogger().warning("[TablistModule] Nicht alle Server passen in die Tablist! "
|
plugin.getLogger().warning("[TablistModule] Nicht alle Server passen in die Tablist!");
|
||||||
+ "Erhöhe tab-size in der BungeeCord config.yml auf mindestens " + (needed * ROWS)
|
plugin.getLogger().warning("[TablistModule] LOESUNG: Setze in BungeeCord config.yml -> tab-list-size: " + (needed * ROWS));
|
||||||
+ " (aktuell: " + tabSize + ")");
|
plugin.getLogger().warning("[TablistModule] Und in tablist.properties -> tablist.tab_size=" + (needed * ROWS));
|
||||||
}
|
}
|
||||||
|
// Der Client zeigt exakt (tab-list-size / 20) Spalten — BungeeCord config.yml muss stimmen!
|
||||||
plugin.getLogger().info("[TablistModule] tab_size=" + tabSize + " -> " + columns + "x" + ROWS + "=" + total + " (" + serverCount + " Server)");
|
plugin.getLogger().info("[TablistModule] tab_size=" + tabSize + " -> " + columns + "x" + ROWS + "=" + total + " (" + serverCount + " Server)");
|
||||||
|
plugin.getLogger().info("[TablistModule] Stelle sicher dass BungeeCord config.yml 'tab-list-size: " + tabSize + "' gesetzt ist!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initUuids() {
|
private void initUuids() {
|
||||||
@@ -199,7 +213,9 @@ public class TablistModule implements Module, Listener {
|
|||||||
ProxiedPlayer p = e.getPlayer();
|
ProxiedPlayer p = e.getPlayer();
|
||||||
net.md_5.bungee.protocol.data.Property[] skin = fetchSkin(p);
|
net.md_5.bungee.protocol.data.Property[] skin = fetchSkin(p);
|
||||||
if (skin != null && skin.length > 0) skinCache.put(p.getUniqueId(), skin);
|
if (skin != null && skin.length > 0) skinCache.put(p.getUniqueId(), skin);
|
||||||
|
disableBungeeTabHandler(p);
|
||||||
ProxyServer.getInstance().getScheduler().schedule(plugin, () -> {
|
ProxyServer.getInstance().getScheduler().schedule(plugin, () -> {
|
||||||
|
disableBungeeTabHandler(p);
|
||||||
updateTablist(p);
|
updateTablist(p);
|
||||||
ProxyServer.getInstance().getScheduler().schedule(plugin, this::updateAll, 2L, TimeUnit.SECONDS);
|
ProxyServer.getInstance().getScheduler().schedule(plugin, this::updateAll, 2L, TimeUnit.SECONDS);
|
||||||
}, 2L, TimeUnit.SECONDS);
|
}, 2L, TimeUnit.SECONDS);
|
||||||
@@ -212,8 +228,10 @@ public class TablistModule implements Module, Listener {
|
|||||||
|
|
||||||
net.md_5.bungee.protocol.data.Property[] skin = fetchSkin(switched);
|
net.md_5.bungee.protocol.data.Property[] skin = fetchSkin(switched);
|
||||||
if (skin != null && skin.length > 0) skinCache.put(switched.getUniqueId(), skin);
|
if (skin != null && skin.length > 0) skinCache.put(switched.getUniqueId(), skin);
|
||||||
|
disableBungeeTabHandler(switched);
|
||||||
|
|
||||||
ProxyServer.getInstance().getScheduler().schedule(plugin, () -> {
|
ProxyServer.getInstance().getScheduler().schedule(plugin, () -> {
|
||||||
|
disableBungeeTabHandler(switched);
|
||||||
net.md_5.bungee.protocol.data.Property[] freshSkin = fetchSkin(switched);
|
net.md_5.bungee.protocol.data.Property[] freshSkin = fetchSkin(switched);
|
||||||
if (freshSkin != null && freshSkin.length > 0) skinCache.put(switched.getUniqueId(), freshSkin);
|
if (freshSkin != null && freshSkin.length > 0) skinCache.put(switched.getUniqueId(), freshSkin);
|
||||||
|
|
||||||
@@ -269,10 +287,12 @@ public class TablistModule implements Module, Listener {
|
|||||||
} else {
|
} else {
|
||||||
int serverCount = getServerOrder().size();
|
int serverCount = getServerOrder().size();
|
||||||
int needed = (hasInfo ? 1 : 0) + Math.max(1, serverCount);
|
int needed = (hasInfo ? 1 : 0) + Math.max(1, serverCount);
|
||||||
newColumns = Math.max(hasInfo ? 2 : 1, Math.min(needed, tabSizeMax / ROWS));
|
int effectiveTabMax = configuredTabSize > 0 ? configuredTabSize : tabSizeMax;
|
||||||
|
newColumns = Math.max(hasInfo ? 2 : 1, Math.min(needed, effectiveTabMax / ROWS));
|
||||||
}
|
}
|
||||||
int newTotal = ROWS * newColumns;
|
int newTotal = ROWS * newColumns;
|
||||||
if (newColumns == columns && newTotal == total) return;
|
// Nur abbrechen wenn Grid identisch UND fakeUuids bereits korrekt initialisiert
|
||||||
|
if (newColumns == columns && newTotal == total && fakeUuids != null && fakeUuids.length == newTotal) return;
|
||||||
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
|
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
|
||||||
try { removeFakeSlots(p); } catch (Exception ignored) {}
|
try { removeFakeSlots(p); } catch (Exception ignored) {}
|
||||||
}
|
}
|
||||||
@@ -286,6 +306,7 @@ public class TablistModule implements Module, Listener {
|
|||||||
private void updateTablist(ProxiedPlayer viewer) {
|
private void updateTablist(ProxiedPlayer viewer) {
|
||||||
if (viewer == null || !viewer.isConnected()) return;
|
if (viewer == null || !viewer.isConnected()) return;
|
||||||
try {
|
try {
|
||||||
|
// DEBUG: zeige aktuelle Grid-Werte im Chat (wird nach erstem Fix entfernt)
|
||||||
String srv = viewer.getServer() != null ? capitalize(viewer.getServer().getInfo().getName()) : "\u2014";
|
String srv = viewer.getServer() != null ? capitalize(viewer.getServer().getInfo().getName()) : "\u2014";
|
||||||
String world = net.viper.status.StatusAPI.playerWorlds.getOrDefault(viewer.getUniqueId(), "world");
|
String world = net.viper.status.StatusAPI.playerWorlds.getOrDefault(viewer.getUniqueId(), "world");
|
||||||
String rank = getRank(viewer);
|
String rank = getRank(viewer);
|
||||||
@@ -307,8 +328,11 @@ public class TablistModule implements Module, Listener {
|
|||||||
viewer.setTabHeader(
|
viewer.setTabHeader(
|
||||||
new net.md_5.bungee.api.chat.TextComponent(hComps),
|
new net.md_5.bungee.api.chat.TextComponent(hComps),
|
||||||
new net.md_5.bungee.api.chat.TextComponent(fComps));
|
new net.md_5.bungee.api.chat.TextComponent(fComps));
|
||||||
hideRealPlayers(viewer);
|
// Erst Slots senden (ADD_PLAYER), DANN echte Spieler verstecken (UPDATE_LISTED=false).
|
||||||
|
// Umgekehrte Reihenfolge führt zu "Ignoring player info update for unknown player"
|
||||||
|
// weil der Client UPDATE_LISTED für unbekannte UUIDs ignoriert.
|
||||||
sendSlots(viewer, buildItems(viewer));
|
sendSlots(viewer, buildItems(viewer));
|
||||||
|
hideRealPlayers(viewer);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
plugin.getLogger().warning("[TablistModule] " + viewer.getName() + ": " + ex.getMessage());
|
plugin.getLogger().warning("[TablistModule] " + viewer.getName() + ": " + ex.getMessage());
|
||||||
}
|
}
|
||||||
@@ -363,8 +387,11 @@ public class TablistModule implements Module, Listener {
|
|||||||
for (int i = 0; i < total; i++) { texts[i] = " "; skins[i] = EMPTY_SKIN; pings[i] = 0; }
|
for (int i = 0; i < total; i++) { texts[i] = " "; skins[i] = EMPTY_SKIN; pings[i] = 0; }
|
||||||
|
|
||||||
boolean compact = "compact".equalsIgnoreCase(layoutMode);
|
boolean compact = "compact".equalsIgnoreCase(layoutMode);
|
||||||
// Ob der Spalten-Header einen Slot belegt (= "full") oder nicht (= "none"/"small")
|
// Ob der Spalten-Header einen Slot belegt:
|
||||||
boolean useSlotHeader = "full".equalsIgnoreCase(columnHeaderMode);
|
// "full" = explizit aktiviert, "none"/"small" = früher deaktiviert.
|
||||||
|
// FIX: Im Server-Modus immer den Servernamen in Zeile 0 schreiben,
|
||||||
|
// sonst weiß der Spieler nicht welche Spalte welcher Server ist.
|
||||||
|
boolean useSlotHeader = !"none".equalsIgnoreCase(columnHeaderMode);
|
||||||
|
|
||||||
// Info-Spalte (nur classic)
|
// Info-Spalte (nur classic)
|
||||||
if (!compact) {
|
if (!compact) {
|
||||||
@@ -476,51 +503,73 @@ public class TablistModule implements Module, Listener {
|
|||||||
private void hideRealPlayers(ProxiedPlayer viewer) {
|
private void hideRealPlayers(ProxiedPlayer viewer) {
|
||||||
if (sendPacketQueuedMethod == null) return;
|
if (sendPacketQueuedMethod == null) return;
|
||||||
try {
|
try {
|
||||||
|
// Echte Spieler: nur listed=false setzen (KEIN Remove, sonst geht der Skin verloren!)
|
||||||
|
// Der Client kennt sie bereits per ADD_PLAYER von BungeeCord → UPDATE_LISTED reicht.
|
||||||
Collection<ProxiedPlayer> online = ProxyServer.getInstance().getPlayers();
|
Collection<ProxiedPlayer> online = ProxyServer.getInstance().getPlayers();
|
||||||
|
if (!online.isEmpty()) {
|
||||||
|
PlayerListItemUpdate playerPkt = new PlayerListItemUpdate();
|
||||||
|
playerPkt.setActions(EnumSet.of(PlayerListItemUpdate.Action.UPDATE_LISTED));
|
||||||
|
Item[] playerItems = new Item[online.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (ProxiedPlayer p : online) {
|
||||||
|
Item it = new Item(); it.setUuid(p.getUniqueId()); it.setListed(false);
|
||||||
|
playerItems[i++] = it;
|
||||||
|
}
|
||||||
|
playerPkt.setItems(playerItems);
|
||||||
|
sendPacketQueuedMethod.invoke(viewer, playerPkt);
|
||||||
|
}
|
||||||
|
|
||||||
// Sammle alle UUIDs die versteckt werden sollen:
|
// Server-UUIDs (BungeeCord schreibt pro Server 2 Einträge in die Tablist):
|
||||||
// 1. Echte Spieler
|
// Diese per PlayerListItemRemove entfernen – das funktioniert auch für
|
||||||
// 2. BungeeCord-interne Server-Eintraege (werden von BC selbst in die Tablist geschrieben)
|
// UUIDs die der Client noch nicht kennt, ohne Skin-Schaden.
|
||||||
List<UUID> toHide = new ArrayList<>();
|
List<UUID> serverUuids = new ArrayList<>();
|
||||||
for (ProxiedPlayer p : online) toHide.add(p.getUniqueId());
|
|
||||||
|
|
||||||
// BungeeCord schreibt fuer jeden Server einen eigenen Eintrag mit einer
|
|
||||||
// deterministischen UUID (nameUUIDFromBytes des Server-Namens).
|
|
||||||
// Diese auch auf listed=false setzen damit die grossen Spalten-Header verschwinden.
|
|
||||||
for (String srvName : ProxyServer.getInstance().getServers().keySet()) {
|
for (String srvName : ProxyServer.getInstance().getServers().keySet()) {
|
||||||
try {
|
try {
|
||||||
UUID srvUuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + srvName).getBytes(StandardCharsets.UTF_8));
|
serverUuids.add(UUID.nameUUIDFromBytes(("OfflinePlayer:" + srvName).getBytes(StandardCharsets.UTF_8)));
|
||||||
toHide.add(srvUuid);
|
serverUuids.add(UUID.nameUUIDFromBytes(srvName.getBytes(StandardCharsets.UTF_8)));
|
||||||
UUID srvUuid2 = UUID.nameUUIDFromBytes(srvName.getBytes(StandardCharsets.UTF_8));
|
|
||||||
toHide.add(srvUuid2);
|
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
}
|
}
|
||||||
|
if (!serverUuids.isEmpty()) {
|
||||||
if (toHide.isEmpty()) return;
|
try {
|
||||||
PlayerListItemUpdate pkt = new PlayerListItemUpdate();
|
Class<?> removeClass = Class.forName("net.md_5.bungee.protocol.packet.PlayerListItemRemove");
|
||||||
pkt.setActions(EnumSet.of(PlayerListItemUpdate.Action.UPDATE_LISTED));
|
Object removePkt = removeClass.getDeclaredConstructor().newInstance();
|
||||||
Item[] items = new Item[toHide.size()];
|
removeClass.getMethod("setUuids", UUID[].class).invoke(removePkt,
|
||||||
int idx = 0;
|
(Object) serverUuids.toArray(new UUID[0]));
|
||||||
for (UUID uuid : toHide) {
|
sendPacketQueuedMethod.invoke(viewer, removePkt);
|
||||||
Item it = new Item(); it.setUuid(uuid); it.setListed(false); items[idx++] = it;
|
} catch (Exception ignored) {
|
||||||
|
// Fallback: UPDATE_LISTED=false (Warnungen im Client-Log sind harmlos)
|
||||||
|
PlayerListItemUpdate srvPkt = new PlayerListItemUpdate();
|
||||||
|
srvPkt.setActions(EnumSet.of(PlayerListItemUpdate.Action.UPDATE_LISTED));
|
||||||
|
Item[] srvItems = new Item[serverUuids.size()];
|
||||||
|
for (int j = 0; j < serverUuids.size(); j++) {
|
||||||
|
Item it = new Item(); it.setUuid(serverUuids.get(j)); it.setListed(false);
|
||||||
|
srvItems[j] = it;
|
||||||
|
}
|
||||||
|
srvPkt.setItems(srvItems);
|
||||||
|
sendPacketQueuedMethod.invoke(viewer, srvPkt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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")
|
@SuppressWarnings("unchecked")
|
||||||
private void sendSlots(ProxiedPlayer viewer, Item[] items) {
|
private void sendSlots(ProxiedPlayer viewer, Item[] items) {
|
||||||
if (sendPacketQueuedMethod == null) return;
|
if (sendPacketQueuedMethod == null) return;
|
||||||
PlayerListItemUpdate pkt = new PlayerListItemUpdate();
|
try {
|
||||||
pkt.setActions(EnumSet.of(
|
// Alles in einem Paket – ADD_PLAYER muss UPDATE_LISTED enthalten
|
||||||
PlayerListItemUpdate.Action.ADD_PLAYER,
|
// damit der Client den Slot sofort als sichtbar markiert.
|
||||||
PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME,
|
// Die "Ignoring unknown player" Warnung entsteht wenn hideRealPlayers()
|
||||||
PlayerListItemUpdate.Action.UPDATE_LISTED,
|
// VOR sendSlots läuft und der Client die UUIDs noch nicht kennt.
|
||||||
PlayerListItemUpdate.Action.UPDATE_LATENCY));
|
// Fix: hideRealPlayers wird NACH sendSlots aufgerufen (siehe updateTablist).
|
||||||
pkt.setItems(items);
|
PlayerListItemUpdate pkt = new PlayerListItemUpdate();
|
||||||
try { sendPacketQueuedMethod.invoke(viewer, pkt); }
|
pkt.setActions(EnumSet.of(
|
||||||
catch (Exception e) { plugin.getLogger().warning("[TablistModule] sendSlots: " + e.getMessage()); }
|
PlayerListItemUpdate.Action.ADD_PLAYER,
|
||||||
|
PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME,
|
||||||
|
PlayerListItemUpdate.Action.UPDATE_LISTED,
|
||||||
|
PlayerListItemUpdate.Action.UPDATE_LATENCY));
|
||||||
|
pkt.setItems(items);
|
||||||
|
sendPacketQueuedMethod.invoke(viewer, pkt);
|
||||||
|
} catch (Exception e) { plugin.getLogger().warning("[TablistModule] sendSlots: " + e.getMessage()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeFakeSlots(ProxiedPlayer viewer) {
|
private void removeFakeSlots(ProxiedPlayer viewer) {
|
||||||
@@ -544,6 +593,34 @@ public class TablistModule implements Module, Listener {
|
|||||||
|
|
||||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ersetzt BungeeCords internen tabListHandler durch einen No-Op Proxy.
|
||||||
|
* null setzen crasht BungeeCord (NPE in DownstreamBridge).
|
||||||
|
* Ein leerer Proxy ignoriert alle Aufrufe lautlos.
|
||||||
|
*/
|
||||||
|
private void disableBungeeTabHandler(ProxiedPlayer player) {
|
||||||
|
if (tabListHandlerField == null) return;
|
||||||
|
try {
|
||||||
|
Class<?> tabListClass = Class.forName("net.md_5.bungee.tab.TabList");
|
||||||
|
// Wenn schon ein Proxy gesetzt ist, nichts tun
|
||||||
|
Object current = tabListHandlerField.get(player);
|
||||||
|
if (current != null && java.lang.reflect.Proxy.isProxyClass(current.getClass())) return;
|
||||||
|
|
||||||
|
Object noopProxy = java.lang.reflect.Proxy.newProxyInstance(
|
||||||
|
tabListClass.getClassLoader(),
|
||||||
|
new Class<?>[]{ tabListClass },
|
||||||
|
(proxy, method, args) -> {
|
||||||
|
Class<?> ret = method.getReturnType();
|
||||||
|
if (ret == boolean.class || ret == Boolean.class) return false;
|
||||||
|
if (ret == int.class || ret == Integer.class) return 0;
|
||||||
|
if (ret == long.class || ret == Long.class) return 0L;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
tabListHandlerField.set(player, noopProxy);
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ── ULTIMATE: Gibt das konfigurierte Server-Symbol für den Spieler zurück.
|
* ── ULTIMATE: Gibt das konfigurierte Server-Symbol für den Spieler zurück.
|
||||||
* Leer wenn kein Symbol für den aktuellen Server definiert ist.
|
* Leer wenn kein Symbol für den aktuellen Server definiert ist.
|
||||||
|
|||||||
Reference in New Issue
Block a user