Upload via GUI (4 Dateien)
This commit is contained in:
+31
-9
@@ -6,14 +6,20 @@
|
||||
|
||||
<groupId>net.viper</groupId>
|
||||
<artifactId>StatusAPIBridge</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<!-- Niedrigste gemeinsame Basis: Java 17 (läuft auf 1.21.1 und 26.1.2) -->
|
||||
<java.version>17</java.version>
|
||||
<!--
|
||||
Java 21 (LTS) – kompatibel mit 1.21.x (benötigt mind. Java 21) und 26.1.x
|
||||
(benötigt Java 25 zum Starten, akzeptiert aber Java-21-Bytecode problemlos,
|
||||
da Java abwärtskompatibel ist).
|
||||
Für einen reinen 26.1.x-Build kann java.version auf 25 gesetzt werden.
|
||||
-->
|
||||
<java.version>21</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven.compiler.release>${java.version}</maven.compiler.release>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!-- Standard-API-Version (wird durch Profile überschrieben) -->
|
||||
<spigot.version>1.21.1-R0.1-SNAPSHOT</spigot.version>
|
||||
@@ -60,8 +66,9 @@
|
||||
|
||||
<!-- ══════════════════════════════════════════════════════════════════════
|
||||
Maven-Profile für Multi-Version-Build
|
||||
mvn package → mc-1.21.1 (Standard)
|
||||
mvn package -P mc-26.1.2 → mc-26.1.2
|
||||
mvn package → mc-1.21.1 (Standard, Java 21)
|
||||
mvn package -P mc-26.1.2 → mc-26.1.2 (Java 21 Bytecode, läuft auf Java 25)
|
||||
mvn package -P mc-26.1.2-j25 → mc-26.1.2 mit Java-25-Bytecode (benötigt JDK 25)
|
||||
══════════════════════════════════════════════════════════════════════ -->
|
||||
<profiles>
|
||||
|
||||
@@ -76,11 +83,25 @@
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<!-- Profil 2: Minecraft 26.1.2 -->
|
||||
<!-- Profil 2: Minecraft 26.1.2 – kompiliert gegen 1.21.1-API (kompatibel),
|
||||
erzeugt Java-21-Bytecode der auf Java 25 läuft -->
|
||||
<profile>
|
||||
<id>mc-26.1.2</id>
|
||||
<properties>
|
||||
<spigot.version>1.21.1-R0.1-SNAPSHOT</spigot.version>
|
||||
<spigot.version>26.1.2-R0.1-SNAPSHOT</spigot.version>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<!-- Profil 3: Minecraft 26.1.2 mit nativem Java-25-Bytecode.
|
||||
Benötigt JDK 25 als JAVA_HOME beim Build. -->
|
||||
<profile>
|
||||
<id>mc-26.1.2-j25</id>
|
||||
<properties>
|
||||
<spigot.version>26.1.2-R0.1-SNAPSHOT</spigot.version>
|
||||
<java.version>25</java.version>
|
||||
<maven.compiler.source>25</maven.compiler.source>
|
||||
<maven.compiler.target>25</maven.compiler.target>
|
||||
<maven.compiler.release>25</maven.compiler.release>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
@@ -92,17 +113,18 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<version>3.14.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<release>${java.version}</release>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
@@ -24,15 +25,19 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
public class StatusAPIBridge extends JavaPlugin implements Listener, PluginMessageListener {
|
||||
|
||||
private Economy economy;
|
||||
private String statusApiUrl;
|
||||
private int pushDelayTicks;
|
||||
private int liveSyncIntervalTicks;
|
||||
private int scoreboardSyncIntervalTicks;
|
||||
private int compassMinPushIntervalMs;
|
||||
|
||||
private final Map<UUID, Double> lastPushedBalance = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, Double> lastPushedHealth = new ConcurrentHashMap<>();
|
||||
@@ -40,6 +45,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
private final Map<UUID, String> lastPushedWorld = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, String> lastPushedData = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, String> lastPushedStats = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, Long> lastCompassPushAt = new ConcurrentHashMap<>();
|
||||
private String lastPushedTicketGlobal = "";
|
||||
|
||||
// ── PlaceholderAPI ────────────────────────────────────────────────────────
|
||||
@@ -54,17 +60,31 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
private final Map<UUID, String> lastNametagPrefix = new ConcurrentHashMap<>();
|
||||
/** Feature aktivierbar via config: nametag-enabled */
|
||||
private boolean nametagEnabled = true;
|
||||
/** AFK-Status pro Spieler – wird via Plugin-Message von BungeeCord aktualisiert */
|
||||
private final Map<UUID, Boolean> afkState = new ConcurrentHashMap<>();
|
||||
|
||||
// ── Versions-Detection ────────────────────────────────────────────────────
|
||||
// true = 1.21.x-Modus (Spigot/Paper)
|
||||
// false = 26.1.x-Modus (neuere Server-Version, kein NMS-Fallback)
|
||||
private boolean isLegacyMode = true;
|
||||
|
||||
private final ExecutorService httpExecutor = Executors.newSingleThreadExecutor(r -> {
|
||||
// BUGFIX: DiscardOldestPolicy kann HTTP-Requests silently verwerfen obwohl lastPushed* schon
|
||||
// aktualisiert wurde → der nächste Sync-Zyklus schickt keinen erneuten Push mehr.
|
||||
// CallerRunsPolicy blockiert stattdessen den aufrufenden Thread, was verhindert dass
|
||||
// Economy/Stats-Updates verloren gehen. Queue-Größe auf 2000 reduziert (war 4000, unnötig groß).
|
||||
private final ExecutorService httpExecutor = new ThreadPoolExecutor(
|
||||
2,
|
||||
2,
|
||||
30L,
|
||||
TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(2000),
|
||||
r -> {
|
||||
Thread t = new Thread(r, "StatusAPIBridge-HTTP");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
},
|
||||
new ThreadPoolExecutor.CallerRunsPolicy()
|
||||
);
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
@@ -80,6 +100,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
pushDelayTicks = getConfig().getInt("push-delay-ticks", 40);
|
||||
liveSyncIntervalTicks = Math.max(20, getConfig().getInt("live-sync-interval-ticks", 20));
|
||||
scoreboardSyncIntervalTicks = Math.max(20, getConfig().getInt("scoreboard-sync-interval-ticks", 20));
|
||||
compassMinPushIntervalMs = Math.max(50, getConfig().getInt("compass-min-push-interval-ms", 150));
|
||||
|
||||
if (!setupEconomy()) {
|
||||
getLogger().warning("Vault/Economy nicht gefunden – Economy-Push deaktiviert.");
|
||||
@@ -88,6 +109,8 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
}
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(this, this);
|
||||
// Plugin-Message-Channel für AFK-Status-Updates von BungeeCord registrieren
|
||||
getServer().getMessenger().registerIncomingPluginChannel(this, "statusapi:afk", this);
|
||||
Bukkit.getScheduler().runTaskTimer(this, this::pushChangedBalancesForOnlinePlayers,
|
||||
liveSyncIntervalTicks, liveSyncIntervalTicks);
|
||||
Bukkit.getScheduler().runTaskTimer(this, this::pushScoreboardData,
|
||||
@@ -123,6 +146,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
getServer().getMessenger().unregisterIncomingPluginChannel(this, "statusapi:afk");
|
||||
httpExecutor.shutdownNow();
|
||||
}
|
||||
|
||||
@@ -161,6 +185,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
lastPushedWorld.remove(id);
|
||||
lastPushedData.remove(id);
|
||||
lastPushedStats.remove(id);
|
||||
lastCompassPushAt.remove(id);
|
||||
lastPapiValues.remove(id);
|
||||
lastNametagPrefix.remove(id);
|
||||
// Nametag-Team beim Quit aufräumen
|
||||
@@ -215,8 +240,9 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
pushWorldIfChanged(player);
|
||||
pushPlayerDataIfChanged(player);
|
||||
pushStatsIfChanged(player);
|
||||
// Nametag periodisch aktualisieren (reagiert auf Rang-Änderungen)
|
||||
if (nametagEnabled) applyNametag(player);
|
||||
// BUGFIX: applyNametag() hier NICHT aufrufen – es überschreibt bei jedem Scoreboard-Sync-Tick
|
||||
// das Scoreboard ALLER Online-Spieler und zerstört dabei Scoreboards anderer Plugins.
|
||||
// Nametag-Updates laufen jetzt nur noch event-basiert (Join + expliziter Rang-Wechsel).
|
||||
}
|
||||
|
||||
// ── Push-Methoden ─────────────────────────────────────────────────────────
|
||||
@@ -226,7 +252,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
}
|
||||
|
||||
private void pushEconomyAsync(UUID uuid, String name, double balance) {
|
||||
httpExecutor.execute(() -> {
|
||||
submitHttp(() -> {
|
||||
try {
|
||||
sendPost(statusApiUrl + "/economy/update",
|
||||
"{\"uuid\":\"" + uuid + "\",\"name\":\"" + escapeName(name)
|
||||
@@ -235,7 +261,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
} catch (Exception e) {
|
||||
getLogger().warning("Economy-Push fehlgeschlagen fuer " + name + ": " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}, "economy");
|
||||
}
|
||||
|
||||
private void pushHealthIfChanged(Player player) {
|
||||
@@ -244,7 +270,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
if (last != null && Math.abs(health - last) < 0.01) return;
|
||||
lastPushedHealth.put(player.getUniqueId(), health);
|
||||
UUID uuid = player.getUniqueId(); String name = player.getName();
|
||||
httpExecutor.execute(() -> {
|
||||
submitHttp(() -> {
|
||||
try {
|
||||
sendPost(statusApiUrl + "/scoreboard/health",
|
||||
"{\"uuid\":\"" + uuid + "\",\"name\":\"" + escapeName(name)
|
||||
@@ -252,17 +278,22 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
} catch (Exception e) {
|
||||
getLogger().warning("Health-Push fehlgeschlagen: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}, "health");
|
||||
}
|
||||
|
||||
private void pushCompassIfChanged(Player player) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
long now = System.currentTimeMillis();
|
||||
Long lastPushAt = lastCompassPushAt.get(uuid);
|
||||
if (lastPushAt != null && (now - lastPushAt) < compassMinPushIntervalMs) return;
|
||||
|
||||
// Rohen Yaw normalisieren auf 0..360 (0 = Süden, wie MC-Konvention)
|
||||
float rawYaw = player.getLocation().getYaw();
|
||||
float normYaw = ((rawYaw % 360) + 360) % 360;
|
||||
String yawStr = String.format(java.util.Locale.US, "%.1f", normYaw);
|
||||
|
||||
// Nur senden wenn Änderung >= 0.5° – fein genug für 1-Grad-Slots
|
||||
String lastStr = lastPushedCompass.get(player.getUniqueId());
|
||||
String lastStr = lastPushedCompass.get(uuid);
|
||||
if (lastStr != null) {
|
||||
try {
|
||||
float lastYaw = Float.parseFloat(lastStr);
|
||||
@@ -272,9 +303,10 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
lastPushedCompass.put(player.getUniqueId(), yawStr);
|
||||
UUID uuid = player.getUniqueId(); String name = player.getName();
|
||||
httpExecutor.execute(() -> {
|
||||
lastPushedCompass.put(uuid, yawStr);
|
||||
lastCompassPushAt.put(uuid, now);
|
||||
String name = player.getName();
|
||||
submitHttp(() -> {
|
||||
try {
|
||||
sendPost(statusApiUrl + "/scoreboard/compass",
|
||||
"{\"uuid\":\"" + uuid + "\",\"name\":\"" + escapeName(name)
|
||||
@@ -282,7 +314,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
} catch (Exception e) {
|
||||
getLogger().warning("Compass-Push fehlgeschlagen: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}, "compass");
|
||||
}
|
||||
|
||||
private void pushWorldIfChanged(Player player) {
|
||||
@@ -290,7 +322,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
if (world.equals(lastPushedWorld.get(player.getUniqueId()))) return;
|
||||
lastPushedWorld.put(player.getUniqueId(), world);
|
||||
UUID uuid = player.getUniqueId(); String name = player.getName();
|
||||
httpExecutor.execute(() -> {
|
||||
submitHttp(() -> {
|
||||
try {
|
||||
sendPost(statusApiUrl + "/player/world",
|
||||
"{\"uuid\":\"" + uuid + "\",\"name\":\"" + escapeName(name)
|
||||
@@ -298,7 +330,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
} catch (Exception e) {
|
||||
getLogger().warning("World-Push fehlgeschlagen: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}, "world");
|
||||
}
|
||||
|
||||
private void pushPlayerDataIfChanged(Player player) {
|
||||
@@ -315,7 +347,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
lastPushedData.put(player.getUniqueId(), key);
|
||||
UUID uuid = player.getUniqueId();
|
||||
String name = player.getName();
|
||||
httpExecutor.execute(() -> {
|
||||
submitHttp(() -> {
|
||||
try {
|
||||
sendPost(statusApiUrl + "/player/data",
|
||||
"{\"uuid\":\"" + uuid + "\",\"name\":\"" + escapeName(name)
|
||||
@@ -330,7 +362,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
} catch (Exception e) {
|
||||
getLogger().warning("PlayerData-Push fehlgeschlagen: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}, "player-data");
|
||||
}
|
||||
|
||||
private void pushStatsIfChanged(Player player) {
|
||||
@@ -346,7 +378,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
|
||||
UUID uuid = player.getUniqueId();
|
||||
String name = player.getName();
|
||||
httpExecutor.execute(() -> {
|
||||
submitHttp(() -> {
|
||||
try {
|
||||
sendPost(statusApiUrl + "/stats/update",
|
||||
"{\"uuid\":\"" + uuid + "\",\"name\":\"" + escapeName(name)
|
||||
@@ -356,7 +388,7 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
} catch (Exception e) {
|
||||
getLogger().warning("Stats-Push fehlgeschlagen: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}, "stats");
|
||||
}
|
||||
|
||||
// ── TicketSystem Push ──────────────────────────────────────────────────
|
||||
@@ -485,26 +517,59 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
if (snapshot.equals(lastPapiValues.get(p.getUniqueId()))) return;
|
||||
lastPapiValues.put(p.getUniqueId(), snapshot);
|
||||
String json = "{\"uuid\":\"" + p.getUniqueId() + "\",\"placeholders\":{" + snapshot + "}}";
|
||||
httpExecutor.execute(() -> {
|
||||
submitHttp(() -> {
|
||||
try { sendPost(statusApiUrl + "/player/papi", json); }
|
||||
catch (Exception e) { getLogger().warning("[PAPI] Push fehlgeschlagen: " + e.getMessage()); }
|
||||
});
|
||||
}, "papi");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
} catch (Exception e) { getLogger().warning("[PAPI] Fehler: " + e.getMessage()); }
|
||||
}
|
||||
|
||||
private static String esc(String s) {
|
||||
if (s == null) return "";
|
||||
return s.replace("\\", "\\\\").replace("\"", "\\\"");
|
||||
// BUGFIX: Steuerzeichen escapen (JSON-Injection-Schutz für PAPI-Werte)
|
||||
return s.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\"")
|
||||
.replace("\n", "\\n")
|
||||
.replace("\r", "\\r")
|
||||
.replace("\t", "\\t");
|
||||
}
|
||||
|
||||
private void pushTpsAsync(UUID uuid, double tps) {
|
||||
httpExecutor.execute(() -> {
|
||||
submitHttp(() -> {
|
||||
try {
|
||||
sendPost(statusApiUrl + "/scoreboard/tps",
|
||||
"{\"uuid\":\"" + uuid + "\",\"tps\":" + tps + "}");
|
||||
} catch (Exception ignored) {}
|
||||
});
|
||||
}, "tps");
|
||||
}
|
||||
|
||||
// ── Plugin-Message-Handler ───────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Empfängt AFK-Status-Updates von BungeeCord (AfkModule).
|
||||
* Channel: "statusapi:afk", Format: "UUID:true" oder "UUID:false"
|
||||
* Aktualisiert den Nametag-Prefix des Spielers sofort.
|
||||
*/
|
||||
@Override
|
||||
public void onPluginMessageReceived(String channel, Player unused, byte[] message) {
|
||||
if (!"statusapi:afk".equals(channel)) return;
|
||||
try {
|
||||
String msg = new String(message, java.nio.charset.StandardCharsets.UTF_8);
|
||||
int sep = msg.lastIndexOf(':');
|
||||
if (sep < 0) return;
|
||||
UUID uuid = UUID.fromString(msg.substring(0, sep));
|
||||
boolean afk = Boolean.parseBoolean(msg.substring(sep + 1));
|
||||
afkState.put(uuid, afk);
|
||||
// Nametag sofort aktualisieren wenn Spieler auf diesem Server ist
|
||||
Player target = Bukkit.getPlayer(uuid);
|
||||
if (target != null && target.isOnline() && nametagEnabled) {
|
||||
// Muss im Hauptthread laufen (Scoreboard-API ist nicht thread-safe)
|
||||
Bukkit.getScheduler().runTask(this, () -> applyNametag(target));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
getLogger().warning("[StatusAPIBridge] Fehler beim Verarbeiten der AFK-Message: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ── Nametag-Methoden ──────────────────────────────────────────────────────
|
||||
@@ -517,7 +582,16 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
@SuppressWarnings("deprecation")
|
||||
private void applyNametag(Player player) {
|
||||
if (!nametagEnabled || nametagBoard == null) return;
|
||||
String prefix = getLuckPermsPrefix(player);
|
||||
String rawPrefix = getLuckPermsPrefix(player);
|
||||
// AFK-Prefix ERSETZT den Rang-Prefix (nicht voranstellen). Muss identisch zu
|
||||
// ScoreboardModule.updateNametag() im StatusAPI-Proxy sein: beide Systeme
|
||||
// setzen Nametag-Teams, und je nachdem wer zuletzt geschrieben hat gewinnt
|
||||
// eines von beiden. Bei unterschiedlichen Formaten sieht der Spieler sonst
|
||||
// mal "[AFK] Rang Name" und mal "[AFK] Name".
|
||||
// Ergebnis hier nach dem angehaengten Leerzeichen: "§7[AFK]§r " – exakt wie
|
||||
// "§7[AFK] §r" auf der Proxy-Seite.
|
||||
boolean isAfk = Boolean.TRUE.equals(afkState.get(player.getUniqueId()));
|
||||
String prefix = isAfk ? "&7[AFK]&r" : rawPrefix;
|
||||
|
||||
// Change-Detection: nicht neu setzen wenn Prefix gleich geblieben
|
||||
String last = lastNametagPrefix.get(player.getUniqueId());
|
||||
@@ -541,17 +615,16 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
team.setSuffix("");
|
||||
|
||||
// Spieler dem Team zuweisen
|
||||
if (!team.hasEntry(player.getName())) {
|
||||
team.addEntry(player.getName());
|
||||
}
|
||||
|
||||
// Scoreboard dem Spieler zuweisen
|
||||
// BUGFIX: Scoreboard NUR dem betroffenen Spieler selbst zuweisen.
|
||||
// Das vorherige Verhalten hat bei jedem Nametag-Update das Scoreboard ALLER
|
||||
// Online-Spieler überschrieben, was Scoreboards anderer Plugins (Sidebar etc.) zerstört.
|
||||
// Clients sehen Nametag-Teams korrekt sobald BEIDE Spieler dasselbe Scoreboard haben –
|
||||
// das wird beim Join jedes neuen Spielers via onJoin() automatisch gesetzt.
|
||||
player.setScoreboard(nametagBoard);
|
||||
|
||||
// Alle anderen Spieler auf dasselbe Scoreboard setzen damit sie den Prefix sehen
|
||||
for (Player other : Bukkit.getOnlinePlayers()) {
|
||||
if (!other.equals(player) && other.getScoreboard() != nametagBoard) {
|
||||
other.setScoreboard(nametagBoard);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
getLogger().warning("[Nametag] Fehler beim Setzen des Prefixes für " + player.getName() + ": " + e.getMessage());
|
||||
}
|
||||
@@ -566,7 +639,9 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
try {
|
||||
org.bukkit.scoreboard.Team team = nametagBoard.getTeam(teamName);
|
||||
if (team != null) {
|
||||
if (team.hasEntry(player.getName())) {
|
||||
team.removeEntry(player.getName());
|
||||
}
|
||||
// Team löschen wenn leer
|
||||
if (team.getEntries().isEmpty()) team.unregister();
|
||||
}
|
||||
@@ -656,6 +731,14 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
return 20.0;
|
||||
}
|
||||
|
||||
private void submitHttp(Runnable task, String kind) {
|
||||
try {
|
||||
httpExecutor.execute(task);
|
||||
} catch (RejectedExecutionException ex) {
|
||||
getLogger().warning("HTTP-Queue voll, verwerfe " + kind + " Push.");
|
||||
}
|
||||
}
|
||||
|
||||
private void sendPost(String urlStr, String json) throws Exception {
|
||||
@SuppressWarnings("deprecation")
|
||||
URL url = new java.net.URI(urlStr).toURL();
|
||||
@@ -676,6 +759,11 @@ public class StatusAPIBridge extends JavaPlugin implements Listener {
|
||||
|
||||
private String escapeName(String name) {
|
||||
if (name == null) return "";
|
||||
return name.replace("\\", "\\\\").replace("\"", "\\\"");
|
||||
// BUGFIX: Neben \ und " auch Steuerzeichen escapen (JSON-Injection-Schutz)
|
||||
return name.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\"")
|
||||
.replace("\n", "\\n")
|
||||
.replace("\r", "\\r")
|
||||
.replace("\t", "\\t");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user