Upload folder via GUI - src
This commit is contained in:
@@ -132,6 +132,7 @@ public class ScoreboardModule implements Module, Listener {
|
||||
private Method sendPkt;
|
||||
private boolean ready = false;
|
||||
|
||||
|
||||
private static final String[] ENTRIES = {
|
||||
"§0","§1","§2","§3","§4","§5","§6","§7",
|
||||
"§8","§9","§a","§b","§c","§d","§e",
|
||||
@@ -345,6 +346,7 @@ public class ScoreboardModule implements Module, Listener {
|
||||
}
|
||||
|
||||
private void tickAll() {
|
||||
// Nametags (Prefix über dem Kopf) periodisch aktualisieren
|
||||
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
|
||||
if (!p.isConnected()) continue;
|
||||
UUID id = p.getUniqueId();
|
||||
@@ -1578,6 +1580,11 @@ public class ScoreboardModule implements Module, Listener {
|
||||
"scoreboard.news.width=20\n" +
|
||||
"scoreboard.news.speed=1\n" +
|
||||
"\n" +
|
||||
"# ===================================================\n" +
|
||||
"# NAMETAG - Prefix ueber dem Spieler-Kopf\n" +
|
||||
"# ===================================================\n" +
|
||||
"nametag.enabled=true\n" +
|
||||
"\n" +
|
||||
"scoreboard.rotation_interval=4\n" +
|
||||
"# ===================================================\n" +
|
||||
"# ZEILEN - max 15 sichtbar\n" +
|
||||
@@ -1747,10 +1754,12 @@ public class ScoreboardModule implements Module, Listener {
|
||||
supporterLineMap.clear();
|
||||
loadLineMap(map, "scoreboard.supporter_lines.", supporterLineMap);
|
||||
|
||||
|
||||
plugin.getLogger().info("[ScoreboardModule] "
|
||||
+ playerLineMap.size() + " Player-Zeilen, "
|
||||
+ adminLineMap.size() + " Admin-Zeilen, "
|
||||
+ supporterLineMap.size() + " Supporter-Zeilen. RotInterval=" + rotationInterval + "s");
|
||||
+ supporterLineMap.size() + " Supporter-Zeilen. RotInterval=" + rotationInterval + "s"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1867,6 +1876,7 @@ public class ScoreboardModule implements Module, Listener {
|
||||
event.getSuggestions().addAll(suggestions);
|
||||
}
|
||||
|
||||
|
||||
private class ScoreboardToggleCommand extends Command {
|
||||
|
||||
ScoreboardToggleCommand() {
|
||||
|
||||
@@ -3,7 +3,6 @@ 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;
|
||||
@@ -30,7 +29,6 @@ import java.util.concurrent.TimeUnit;
|
||||
public class TablistModule implements Module, Listener {
|
||||
|
||||
private static final String CONFIG_FILE = "tablist.properties";
|
||||
// ── NEU: Server-Symbol-Config ──────────────────────────────────────────────
|
||||
|
||||
// Leerer Skin (grauer Kopf) für Platzhalter-Slots
|
||||
private static final net.md_5.bungee.protocol.data.Property[] EMPTY_SKIN = {
|
||||
@@ -41,29 +39,18 @@ public class TablistModule implements Module, Listener {
|
||||
)
|
||||
};
|
||||
|
||||
// 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 = 6, total = 120, tabSizeMax = 180;
|
||||
private int configuredTabSize = 180; // Default: 180 (9 Spalten möglich)
|
||||
private int configuredColumns = 0; // 0 = automatisch, >0 = direkt nutzen
|
||||
private int rows = ROWS, columns = 6, total = 120, tabSizeMax = 0;
|
||||
private int configuredTabSize = 0;
|
||||
private int configuredColumns = 0;
|
||||
private UUID[] fakeUuids;
|
||||
|
||||
// Skin-Cache (pro Spieler)
|
||||
private final ConcurrentHashMap<UUID, net.md_5.bungee.protocol.data.Property[]> skinCache = new ConcurrentHashMap<>();
|
||||
|
||||
// ── NEU: Server-Symbol-Map (serverName lowercase → colored symbol string) ─
|
||||
private final Map<String, String> serverSymbols = new LinkedHashMap<>();
|
||||
|
||||
// ── NEU: Spalten-Header-Modus ──────────────────────────────────────────────
|
||||
// "full" → bisheriges Verhalten: Spalten-Header belegt Zeile 0 (große Markierung)
|
||||
// "none" → kein Header; Zeile 0 ist frei für Spieler oder bleibt leer
|
||||
// "small" → kein Slot-Header, aber der Spalten-Name erscheint im Tab-Header/Footer
|
||||
// (empfohlen für MuckiDEE: kleine Markierungen bleiben, große weg)
|
||||
private String columnHeaderMode = "none"; // default: keine großen Markierungen
|
||||
// player_display: "server" = Server-basiert (default) | "custom" = alle zusammen, links→rechts nach Rang
|
||||
private String columnHeaderMode = "none";
|
||||
private String playerDisplayMode = "server";
|
||||
|
||||
// Config
|
||||
private boolean enabled = true;
|
||||
private int updateInterval = 5;
|
||||
private String layoutMode = "compact";
|
||||
@@ -75,12 +62,15 @@ public class TablistModule implements Module, Listener {
|
||||
private String footerLine2 = " &7Discord: &ediscord.viper-network.de &8| &7Shop: &eviper-network.de/shop";
|
||||
private String footerLine3 = "&8&m" + rep('\u2501', 53);
|
||||
|
||||
private String compactHeader1 = "&6&lViper Network &8• &7%online% Spieler online";
|
||||
private String compactHeader1 = "&6&lViper Network &8• &2Hallo, &a%player%&7! &6Schön dass du da bist!";
|
||||
private String compactHeader2 = "";
|
||||
private String compactHeader3 = "";
|
||||
private boolean compactHeader1Spacer = false;
|
||||
private boolean compactHeader2Spacer = false;
|
||||
private boolean compactHeader3Spacer = false;
|
||||
private String compactHeader4 = "";
|
||||
private boolean compactHeader4Spacer = false;
|
||||
|
||||
private String compactFooter1 = "";
|
||||
private String compactFooter2 = "&7Zeit: &f%time% &8| &7Spieler: &f%online% &8| &7Ping: &f%ping%ms";
|
||||
private String compactFooter3 = "&7Kontostand: &a$%balance% &8| &7Server: &f%server% &8| &7Welt: &f%world%";
|
||||
@@ -95,7 +85,7 @@ public class TablistModule implements Module, Listener {
|
||||
private boolean compactFooter6Spacer = false;
|
||||
|
||||
private String colorSrvHeader = "&6&l";
|
||||
private boolean showFooterServerList = true; // tablist.compact.footer.serverlist=true/false
|
||||
private boolean showFooterServerList = true;
|
||||
private String timeFormat = "HH:mm:ss / h:mm a";
|
||||
private String timeZone = "Europe/Berlin";
|
||||
private SimpleDateFormat sdf;
|
||||
@@ -103,18 +93,16 @@ public class TablistModule implements Module, Listener {
|
||||
private Set<String> hiddenServers = new HashSet<>();
|
||||
private List<String> rankOrder = new ArrayList<>();
|
||||
|
||||
// Info-Spalte
|
||||
private static class InfoEntry {
|
||||
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<InfoEntry> infoEntries = new ArrayList<>();
|
||||
|
||||
// State
|
||||
private Plugin plugin;
|
||||
private ScheduledTask updateTask;
|
||||
private Method sendPacketQueuedMethod;
|
||||
private java.lang.reflect.Field tabListHandlerField; // BungeeCords interner Tab-Handler
|
||||
private java.lang.reflect.Field tabListHandlerField;
|
||||
|
||||
@Override public String getName() { return "TablistModule"; }
|
||||
|
||||
@@ -129,10 +117,12 @@ public class TablistModule implements Module, Listener {
|
||||
try {
|
||||
initGridSize();
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("[TablistModule] initGridSize Fehler: " + e.getMessage() + " – nutze Fallback 3x20");
|
||||
int fbSize = configuredTabSize > 0 ? configuredTabSize : 60;
|
||||
int maxCols = fbSize / ROWS; // kein hartes Limit mehr – tab_size entscheidet
|
||||
tabSizeMax = fbSize; rows = ROWS; columns = Math.max(3, maxCols); total = ROWS * columns;
|
||||
plugin.getLogger().warning("[TablistModule] initGridSize Fehler: " + e.getMessage());
|
||||
int fbSize = configuredTabSize > 0 ? configuredTabSize : 120;
|
||||
int maxCols = fbSize / ROWS;
|
||||
tabSizeMax = fbSize; rows = ROWS;
|
||||
columns = configuredColumns > 0 ? configuredColumns : Math.max(6, maxCols);
|
||||
total = ROWS * columns;
|
||||
}
|
||||
initUuids();
|
||||
try {
|
||||
@@ -170,39 +160,26 @@ public class TablistModule implements Module, Listener {
|
||||
}
|
||||
|
||||
private void initGridSize() {
|
||||
int tabSize = 60;
|
||||
// FIX: configuredTabSize (aus tablist.properties) hat Vorrang.
|
||||
// Nur wenn nicht gesetzt, per Reflection aus BungeeCord lesen.
|
||||
// configuredTabSize aus loadConfig() hat immer Vorrang.
|
||||
if (configuredTabSize > 0) {
|
||||
tabSize = configuredTabSize;
|
||||
tabSizeMax = configuredTabSize;
|
||||
} else {
|
||||
try {
|
||||
for (ListenerInfo li : ProxyServer.getInstance().getConfig().getListeners()) {
|
||||
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) {}
|
||||
tabSizeMax = 400; // Sicherer Fallback
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
tabSizeMax = tabSize;
|
||||
rows = ROWS;
|
||||
boolean hasInfo = !"compact".equalsIgnoreCase(layoutMode);
|
||||
|
||||
int serverCount = getServerOrder().size();
|
||||
int needed = (hasInfo ? 1 : 0) + Math.max(1, serverCount);
|
||||
|
||||
// SIMPEL & SICHER: configuredColumns direkt nehmen, kein komplexes Berechnen
|
||||
if (configuredColumns > 0) {
|
||||
columns = configuredColumns;
|
||||
} else {
|
||||
columns = Math.max(hasInfo ? 2 : 1, Math.min(needed, tabSize / ROWS));
|
||||
// Kein Wert in config → automatisch: 1 Spalte pro Server, mindestens 6
|
||||
columns = Math.max(6, serverCount);
|
||||
}
|
||||
|
||||
total = ROWS * columns;
|
||||
if (needed > tabSize / ROWS) {
|
||||
plugin.getLogger().warning("[TablistModule] Nicht alle Server passen in die Tablist!");
|
||||
plugin.getLogger().warning("[TablistModule] LOESUNG: Setze in BungeeCord config.yml -> tab-list-size: " + (needed * ROWS));
|
||||
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] Stelle sicher dass BungeeCord config.yml 'tab-list-size: " + tabSize + "' gesetzt ist!");
|
||||
plugin.getLogger().info("[TablistModule] Grid: " + columns + " Spalten × " + ROWS + " = " + total + " Slots | " + serverCount + " Server | tabSizeMax=" + tabSizeMax);
|
||||
}
|
||||
|
||||
private void initUuids() {
|
||||
@@ -219,11 +196,16 @@ public class TablistModule implements Module, Listener {
|
||||
net.md_5.bungee.protocol.data.Property[] skin = fetchSkin(p);
|
||||
if (skin != null && skin.length > 0) skinCache.put(p.getUniqueId(), skin);
|
||||
disableBungeeTabHandler(p);
|
||||
// BungeeCord resettet tabListHandler nach PostLoginEvent intern nochmals.
|
||||
// Deshalb: 0.5s, 1s, 2s nacheinander überschreiben.
|
||||
long[] delays = {500L, 1000L, 2000L};
|
||||
for (long delayMs : delays) {
|
||||
ProxyServer.getInstance().getScheduler().schedule(plugin, () -> {
|
||||
disableBungeeTabHandler(p);
|
||||
updateTablist(p);
|
||||
ProxyServer.getInstance().getScheduler().schedule(plugin, this::updateAll, 2L, TimeUnit.SECONDS);
|
||||
}, 2L, TimeUnit.SECONDS);
|
||||
}, delayMs, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
ProxyServer.getInstance().getScheduler().schedule(plugin, this::updateAll, 3L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -233,28 +215,23 @@ public class TablistModule implements Module, Listener {
|
||||
|
||||
net.md_5.bungee.protocol.data.Property[] skin = fetchSkin(switched);
|
||||
if (skin != null && skin.length > 0) skinCache.put(switched.getUniqueId(), skin);
|
||||
// Sofort deaktivieren
|
||||
disableBungeeTabHandler(switched);
|
||||
|
||||
// BungeeCord setzt den tabListHandler nach ServerSwitch intern mehrfach zurück.
|
||||
// Deshalb: 0.5s, 1s, 2s, 3s nacheinander überschreiben + tablist neu senden.
|
||||
long[] delays = {500L, 1000L, 2000L, 3000L};
|
||||
for (long delayMs : delays) {
|
||||
ProxyServer.getInstance().getScheduler().schedule(plugin, () -> {
|
||||
disableBungeeTabHandler(switched);
|
||||
net.md_5.bungee.protocol.data.Property[] freshSkin = fetchSkin(switched);
|
||||
if (freshSkin != null && freshSkin.length > 0) skinCache.put(switched.getUniqueId(), freshSkin);
|
||||
|
||||
for (ProxiedPlayer viewer : ProxyServer.getInstance().getPlayers()) {
|
||||
try { removeFakeSlots(viewer); } catch (Exception ignored) {}
|
||||
}
|
||||
updateAll();
|
||||
|
||||
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) {}
|
||||
}, delayMs, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
updateAll();
|
||||
}, 2L, TimeUnit.SECONDS);
|
||||
|
||||
}, 1L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -284,23 +261,13 @@ public class TablistModule implements Module, Listener {
|
||||
}
|
||||
|
||||
private void recalculateGrid() {
|
||||
boolean hasInfo = !"compact".equalsIgnoreCase(layoutMode);
|
||||
int newColumns;
|
||||
if ("custom".equalsIgnoreCase(playerDisplayMode)) {
|
||||
// Custom-Modus: immer 3 Spalten (konfigurierbar via tab_size)
|
||||
newColumns = Math.min(3, tabSizeMax / ROWS);
|
||||
} else {
|
||||
// tabSizeMax synchronisieren
|
||||
if (configuredTabSize > 0) tabSizeMax = configuredTabSize;
|
||||
|
||||
int serverCount = getServerOrder().size();
|
||||
int needed = (hasInfo ? 1 : 0) + Math.max(1, serverCount);
|
||||
if (configuredColumns > 0) {
|
||||
newColumns = configuredColumns;
|
||||
} else {
|
||||
int effectiveTabMax = configuredTabSize > 0 ? configuredTabSize : tabSizeMax;
|
||||
newColumns = Math.max(hasInfo ? 2 : 1, Math.min(needed, effectiveTabMax / ROWS));
|
||||
}
|
||||
}
|
||||
int newColumns = configuredColumns > 0 ? configuredColumns : Math.max(6, serverCount);
|
||||
int newTotal = ROWS * newColumns;
|
||||
// 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()) {
|
||||
try { removeFakeSlots(p); } catch (Exception ignored) {}
|
||||
@@ -309,7 +276,7 @@ public class TablistModule implements Module, Listener {
|
||||
columns = newColumns;
|
||||
total = newTotal;
|
||||
initUuids();
|
||||
plugin.getLogger().info("[TablistModule] Grid: " + columns + "x" + rows + "=" + total + " (" + getServerOrder().size() + " Server, Modus: " + playerDisplayMode + ")");
|
||||
plugin.getLogger().info("[TablistModule] Grid: " + columns + "x" + rows + "=" + total + " (" + serverCount + " Server)");
|
||||
}
|
||||
|
||||
private void updateTablist(ProxiedPlayer viewer) {
|
||||
@@ -354,6 +321,7 @@ public class TablistModule implements Module, Listener {
|
||||
appendLine(sb, compactHeader1, compactHeader1Spacer, 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);
|
||||
appendLine(sb, compactHeader4, compactHeader4Spacer, viewer, srv, world, rank, time, balance, online);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -565,11 +533,7 @@ public class TablistModule implements Module, Listener {
|
||||
private void sendSlots(ProxiedPlayer viewer, Item[] items) {
|
||||
if (sendPacketQueuedMethod == null) return;
|
||||
try {
|
||||
// Alles in einem Paket – ADD_PLAYER muss UPDATE_LISTED enthalten
|
||||
// damit der Client den Slot sofort als sichtbar markiert.
|
||||
// Die "Ignoring unknown player" Warnung entsteht wenn hideRealPlayers()
|
||||
// VOR sendSlots läuft und der Client die UUIDs noch nicht kennt.
|
||||
// Fix: hideRealPlayers wird NACH sendSlots aufgerufen (siehe updateTablist).
|
||||
// Paket 1: ADD_PLAYER + UPDATE_DISPLAY_NAME + UPDATE_LISTED + UPDATE_LATENCY
|
||||
PlayerListItemUpdate pkt = new PlayerListItemUpdate();
|
||||
pkt.setActions(EnumSet.of(
|
||||
PlayerListItemUpdate.Action.ADD_PLAYER,
|
||||
@@ -578,6 +542,14 @@ public class TablistModule implements Module, Listener {
|
||||
PlayerListItemUpdate.Action.UPDATE_LATENCY));
|
||||
pkt.setItems(items);
|
||||
sendPacketQueuedMethod.invoke(viewer, pkt);
|
||||
|
||||
// Paket 2: Explizit UPDATE_LISTED=true für alle Fake-Slots nochmal senden.
|
||||
// BungeeCord sendet nach ServerSwitch ein eigenes Tab-Paket das einige Slots
|
||||
// auf listed=false setzt – dieses zweite Paket überschreibt das wieder.
|
||||
PlayerListItemUpdate listedPkt = new PlayerListItemUpdate();
|
||||
listedPkt.setActions(EnumSet.of(PlayerListItemUpdate.Action.UPDATE_LISTED));
|
||||
listedPkt.setItems(items); // items haben alle listed=true gesetzt
|
||||
sendPacketQueuedMethod.invoke(viewer, listedPkt);
|
||||
} catch (Exception e) { plugin.getLogger().warning("[TablistModule] sendSlots: " + e.getMessage()); }
|
||||
}
|
||||
|
||||
@@ -611,10 +583,8 @@ public class TablistModule implements Module, Listener {
|
||||
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;
|
||||
|
||||
// IMMER neu setzen – BungeeCord resettet den tabListHandler nach jedem
|
||||
// ServerSwitch intern, daher reicht ein einmaliges Setzen nicht aus.
|
||||
Object noopProxy = java.lang.reflect.Proxy.newProxyInstance(
|
||||
tabListClass.getClassLoader(),
|
||||
new Class<?>[]{ tabListClass },
|
||||
@@ -1225,6 +1195,8 @@ public class TablistModule implements Module, Listener {
|
||||
compactHeader1Spacer = Boolean.parseBoolean(get.apply("tablist.compact.header.line1.spacer", "false"));
|
||||
compactHeader2Spacer = Boolean.parseBoolean(get.apply("tablist.compact.header.line2.spacer", "false"));
|
||||
compactHeader3Spacer = Boolean.parseBoolean(get.apply("tablist.compact.header.line3.spacer", "false"));
|
||||
compactHeader4 = get.apply("tablist.compact.header.line4", compactHeader4);
|
||||
compactHeader4Spacer = Boolean.parseBoolean(get.apply("tablist.compact.header.line4.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);
|
||||
|
||||
@@ -138,3 +138,10 @@ scoreboard.admin_lines.13=&7Spieler: %online% &8/ &7%maxplayers%
|
||||
scoreboard.admin_lines.14=%line%
|
||||
scoreboard.admin_lines.15=&7%compass%
|
||||
scoreboard.admin_lines.15.2=&7Pos: X:&f%x% &7Y:&f%y% &7Z:&f%z%
|
||||
|
||||
# ===================================================
|
||||
# NAMETAG - Prefix ueber dem Spieler-Kopf
|
||||
# ===================================================
|
||||
# Zeigt den LuckPerms-Prefix ueber dem Spieler-Avatar an.
|
||||
# Auf false setzen zum Deaktivieren.
|
||||
nametag.enabled=true
|
||||
|
||||
Reference in New Issue
Block a user