Upload folder via GUI - src

This commit is contained in:
Git Manager GUI
2026-05-22 19:26:19 +02:00
parent 63d65313b2
commit 7c51370293
3 changed files with 86 additions and 97 deletions

View File

@@ -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() {

View File

@@ -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);

View File

@@ -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