Zum Inhalt springen

· Anleitung

Scoreboard-Plugin: eine Seitenleiste ohne Flackern

Scoreboard-Seitenleiste auf einem Minecraft-Server

Die Seitenleiste ist das Erste, was ein Serverbetreiber auf seinen Namen umbauen will, und das Erste, was er dabei kaputt macht. Das Symptom ist immer dasselbe: die Zeilen zucken, die Anzeige scheint zu atmen, und auf einem Laptop-Bildschirm kannst du sie nicht mehr lesen. Das ist kein Fehler im Client. Es ist die direkte Folge einer Entwurfsentscheidung in der Bukkit-API, und wenn du sie einmal verstanden hast, schreibst du den Code nie wieder falsch. Hier ist der genaue Mechanismus, die Korrektur, der Takt, der praktisch nichts kostet, und die echte Herkunft der Werte, die du anzeigen willst.

Was ein Scoreboard wirklich ist

Eine Seitenleiste ist kein Bedienelement. Sie ist ein Scoreboard-Ziel, ein Objective, angezeigt im Slot SIDEBAR. Ihre Zeilen sind Einträge, an denen jeweils eine ganze Zahl als Punktwert hängt. Der Client sortiert die Einträge absteigend nach diesem Wert und schreibt ihn rechts in Rot dazu. Das System ist auf minecraft.wiki dokumentiert, die genauen Signaturen der Klassen findest du im Javadoc von Paper.

Drei Konsequenzen, die die meisten Tutorials verschweigen:

  • Maximal fünfzehn Zeilen unter dem Titel. Eine sechzehnte Zeile wirft keine Ausnahme und landet in keinem Log: sie erscheint einfach nie. Das ist einer der frustrierendsten Fehler überhaupt, gerade weil er komplett stumm bleibt.
  • Ein Eintrag ist ein eindeutiger Schlüssel. Zwei völlig identische Zeilen können nicht koexistieren: die zweite überschreibt die erste. Genau deshalb verschmelzen deine beiden leeren Trennzeilen zu einer, und unten in der Leiste bleibt ein Loch.
  • Die rote Zahl rechts ist die Sortierung, keine Deko. Seit 1.20.3 kann das Spiel sie selbst ausblenden, über das Zahlenformat, und Paper macht das am Objective zugänglich. Die alten Tricks mit doppelten Punktwerten oder unsichtbaren Farben brauchst du nicht mehr.

Warum die rohe API flackert

Das ist der Code, den du überall findest, und die einzige Ursache des Flackerns. Lies die beiden Zeilen in der Mitte genau:

Java
// Das Muster, das in fast jedem Tutorial steht.
// Genau das, und nichts anderes, laesst die Seitenleiste flackern.
Scoreboard board = Bukkit.getScoreboardManager().getNewScoreboard();
Objective objective = board.registerNewObjective(
        "sb", Criteria.DUMMY, Component.text("Mein Server"));
objective.setDisplaySlot(DisplaySlot.SIDEBAR);

// Der sichtbare Text IST der Schluessel der Zeile.
objective.getScore("\u00A7fGeld \u00A76" + balance).setScore(2);
objective.getScore("\u00A7fPing \u00A76" + player.getPing()).setScore(1);

player.setScoreboard(board);

Der sichtbare Text ist der Schlüssel. Bei der nächsten Aktualisierung ist das Guthaben von 100 auf 105 gestiegen, die Zeichenkette hat sich also geändert, damit ist es nicht mehr derselbe Eintrag. Der Server muss ein Löschpaket für die alte Zeile und ein Erzeugungspaket für die neue senden. Dazwischen hat der Client eine Seitenleiste, in der eine Zeile fehlt, und er zeichnet sie genau so. Mach das zwanzigmal pro Sekunde, und dein Auge nimmt die Lücke wahr. Das ist das Flackern.

Noch zerstörerischer ist die Variante, bei der jedes Mal ein komplett neues Scoreboard gebaut und setScoreboard erneut aufgerufen wird. Dann reißt der Client die gesamte Anzeige ab und baut sie neu auf, Titel inklusive. Die Leiste flackert dann nicht mehr, sie springt.

Zwei Nebenfallen

Fass das Haupt-Scoreboard des Servers nie an: es gehört allen gleichzeitig und wird in den Weltdaten gespeichert, deine Testzeilen überleben also den Neustart. Und denk daran, dass setScoreboard ein vollständiger Ersatz ist. Zwei Plugins, die beide die Seitenleiste wollen, kämpfen darum, und wer zuletzt schreibt, gewinnt. Wenn deine Leiste ohne Fehlermeldung verschwindet, such erst das andere Plugin, dann deinen Bug.

Die Lösung: Schlüssel und Text trennen

Die ganze Lösung steckt in einem Satz: der Schlüssel darf sich nie bewegen. Du erzeugst fünfzehn unsichtbare Einträge ein einziges Mal, hängst jeden an ein Team, und der sichtbare Text kommt aus dem Team-Präfix. Ein Präfix zu ändern ist ein Update an Ort und Stelle: ein Paket, kein Löschen, keine Lücke. Dazu bekommst du etwas gratis, denn zwei identische Trennzeilen sind wieder erlaubt: es sind ihre Schlüssel, die sich unterscheiden, nicht ihr Text.

Java
package fun.minax.sidebar;

import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scoreboard.Criteria;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;

import java.util.List;

/** Eine Seitenleiste pro Spieler. Fuenfzehn Zeilen, genau einmal erzeugt. */
public final class Sidebar {

    private static final int MAX_LINES = 15;
    private static final String[] KEYS = new String[MAX_LINES];

    static {
        // Ein unsichtbarer, eindeutiger Schluessel pro Zeile: ein Farbcode.
        // Der Client indexiert diesen Schluessel, nie deinen sichtbaren Text.
        String codes = "0123456789abcde";
        for (int i = 0; i < MAX_LINES; i++) {
            KEYS[i] = "\u00A7" + codes.charAt(i);
        }
    }

    private final Scoreboard board;
    private final Objective objective;

    public Sidebar(Player player, Component title) {
        this.board = Bukkit.getScoreboardManager().getNewScoreboard();
        this.objective = board.registerNewObjective("minax_sidebar", Criteria.DUMMY, title);
        this.objective.setDisplaySlot(DisplaySlot.SIDEBAR);

        for (int i = 0; i < MAX_LINES; i++) {
            Team line = board.registerNewTeam("minax_line_" + i);
            line.addEntry(KEYS[i]);
        }
        player.setScoreboard(board);
    }

    /** Laeuft bei jeder Aktualisierung. Nichts wird erzeugt, nichts geloescht. */
    public void render(List<Component> lines) {
        int shown = Math.min(lines.size(), MAX_LINES);
        for (int i = 0; i < MAX_LINES; i++) {
            Team line = board.getTeam("minax_line_" + i);
            if (line == null) continue;

            if (i < shown) {
                line.prefix(lines.get(i));                     // Update an Ort und Stelle
                objective.getScore(KEYS[i]).setScore(shown - i);
            } else {
                board.resetScores(KEYS[i]);                     // ungenutzte Zeile
            }
        }
    }
}

Zwei Hinweise zu dieser Klasse. Erstens: die berühmte Grenze von sechzehn Zeichen pro Präfix ist Vergangenheit, sie galt für 1.8-Server. Seit 1.13 ist das Präfix eine Textkomponente, jedes Tutorial, das dich eine Zeile in Präfix plus Eintrag plus Suffix zerlegen lässt, verschwendet nur deine Zeit. Zweitens: nimm line.prefix(Component) und nicht die alte Variante mit einer Zeichenkette. Warum das so ist, verdient einen eigenen Abschnitt.

Die Adventure-5-Falle, und warum es die Seitenleiste trifft

Paper 26.x bringt Adventure 5 mit, und dort wurden API-Teile entfernt, die vorher nur als veraltet markiert waren. Der Großteil dieser Entfernungen betrifft Nachrichten und den Text, den Spieler zu sehen bekommen. Eine Seitenleiste ist genau das: Text für Spieler, fünfzehn Zeilen davon. Sie ist damit statistisch die erste Datei deines Plugins, die beim Wechsel von 1.21 auf 26.x nicht mehr kompiliert. Das ist keine Vermutung, sondern mechanisch: es ist die Klasse mit der höchsten Dichte an Aufrufen der Text-API.

Damit kommt die Frage, die kaum jemand stellt: gegen welche Version kompilierst du? Die bStats-Erhebung über 62.948 Server zeigt 1.21.11 bei 36,4 %, 26.1.2 bei 14,9 %, 26.2 bei 7,4 % und 1.21.4 bei 7,2 %. Die 1.21-Familie ist also weiterhin die Mehrheit, und 26.2 liegt wenige Wochen nach dem Erscheinen am 16.06.2026 nur knapp vor 1.21.4. Nachsehen kannst du das bei bStats.

Mein Build-Ziel für ein Scoreboard-Plugin

Java 21, API 1.21, Lasttest auf 26.1.2. Java 25 ist die Untergrenze der kalendarischen Versionen, aber ein in Java 21 kompiliertes .jar lädt auch auf einer neueren Laufzeitumgebung, umgekehrt gilt das nicht: mit einer einzigen Datei deckst du also zwei Drittel des Bestands ab. Auf der Serverseite hat Paper 26.2 noch kein stabiles Build, das vernünftige Ziel bleibt 26.1.2. Angenehmes Detail am Rand: Minecraft ist seit 26.1 nicht mehr obfuskiert und der Remapper von Paper ist weg, zwischen deinem Code und dem Spiel liegt also keine Übersetzungsschicht mehr.

FastBoard, und der genaue Punkt, ab dem es gewinnt

FastBoard existiert, weil alles davor Fleißarbeit ist, die niemand zweimal machen will. Die Bibliothek schickt die Scoreboard-Pakete direkt, kümmert sich um die unsichtbaren Schlüssel, die Grenze von fünfzehn Zeilen und die Unterschiede zwischen Spielversionen, und zeigt dir nur eine Handvoll Methoden. Ihre Lizenz ist permissiv, du darfst sie also in deinem .jar mitgeben, ohne deinen eigenen Code anzustecken, und das gilt nicht für das ganze Ökosystem. Das Repository liegt auf GitHub.

Das nimmt dir FastBoard ab

Die unsichtbaren Schlüssel

Nur geänderte Zeilen senden

Unterschiede zwischen Versionen

Eine Leiste pro Spieler, ohne Aufwand

Permissive Lizenz, weitergebbar

Das löst es nicht

Eine Abhängigkeit zum Relocaten

Eine Abstraktion ohne deine Kontrolle

Nichts zur Herkunft deiner Daten

Nichts zu den Kosten deiner Abfragen

Nichts zum Aktualisierungstakt

Meine klare Empfehlung. Unter zwanzig gleichzeitigen Spielern, mit einer identischen Leiste für alle und einem Server auf genau einer Version, reicht die rohe Bukkit-API mit Teams völlig: du behältst ein .jar ohne Abhängigkeiten und verstehst jede Zeile, die du ausgeliefert hast. Sobald die Leiste persönlich wird, also jeder Spieler sein eigenes Geld, seinen Rang oder seinen Platz sieht, oder sobald du über zwanzig gleichzeitige Spieler kommst, wechsle ich ohne Diskussion zu FastBoard. Der Code, den du für fünfzehn Schlüssel pro Spieler über mehrere Spielversionen sauber schreiben müsstest, ist genau das, was FastBoard schon tut, nur besser getestet. Und jenseits von hundert Spielern ist der Flaschenhals gar nicht mehr die Scoreboard-API, sondern die Quelle deiner Platzhalter.

Wie oft du aktualisieren solltest

Ein Server läuft mit zwanzig Ticks pro Sekunde, und der Reflex ist, die Leiste jeden Tick neu zu zeichnen. Das ist um eine Größenordnung zu viel, und einfache Arithmetik zeigt es: fünfzehn Zeilen, zwanzig Aktualisierungen pro Sekunde, hundert Spieler online, das sind 30.000 Zeilen-Updates pro Sekunde, jedes davon ein Paket an einen Client. Dieselbe Leiste einmal pro Sekunde, also alle zwanzig Ticks, kostet 1.500 Updates pro Sekunde. Zwanzigmal weniger, bei einem Ergebnis, das niemand unterscheiden kann, denn kein Spieler liest ein Guthaben, das sich zwanzigmal pro Sekunde ändert.

Meine Regel, ohne unnötige Zwischentöne:

  • 20 Ticks als Standard. Geld, Rang, Spielerzahl, Ping, Statistiken. Einmal pro Sekunde, und niemand wird je einen Unterschied merken.
  • 4 Ticks nur für einen Countdown oder einen Fortschrittsbalken, auf den der Spieler gerade aktiv schaut. Fünfmal pro Sekunde wirkt flüssig und kostet fünfmal weniger als eine Aktualisierung in jedem Tick.
  • 1 Tick: nie. Kein Wert, den du in eine Seitenleiste schreiben kannst, rechtfertigt diesen Takt. Wenn du ein Gegenbeispiel im Kopf hast, gehört es wahrscheinlich in einen Titel oder in die Actionbar.
  • Zeichne nur geänderte Zeilen neu. Merk dir den vorherigen Zustand und vergleiche. Bei fünf Zeilen, von denen sich nur der Ping bewegt, schickst du eine von fünf.

Versetze die Aufgaben zwischen den Spielern

Wenn alle Leisten im selben Tick aktualisieren, erzeugst du einmal pro Sekunde eine Lastspitze statt eines gleichmäßigen Flusses. Auf einem gut gefüllten Server siehst du diese Spitze in den TPS-Werten. Gib jedem Spieler eine andere Startverzögerung, abgeleitet aus seiner Kennung: die Arbeit bleibt gleich, sie ist nur verteilt.

Platzhalter: woher die Daten wirklich kommen

Was Leute anzeigen wollen, ist immer dieselbe Liste: Geld, Rang, Ping, Spielerzahl, manchmal ein Platz in der Bestenliste. Was sie nicht wissen: diese fünf Zeilen kosten völlig unterschiedlich viel. Zwei sind gratis, eine ist unvorhersehbar, und eine kann deine TPS allein nach unten ziehen.

Echte HerkunftKostenEmpfohlener Takt
SpielerzahlServerliste im SpeicherVernachlässigbarJede Aktualisierung
PingVon der Paper-API geliefertVernachlässigbarJede Aktualisierung
RangLuckPerms oder VaultIm Speicher, Plugin-AufrufAlle 5 s
GeldEconomy-Service von VaultUnbekannt, oft SQLAsynchron, im Cache
StatistikenDeine SQLite oder MySQLPlatte oder NetzwerkAsynchron, im Cache
BestenlisteAggregat über die TabelleDas Teuerste überhauptEinmal pro Minute, global

Die Geldzeile ist die eigentliche Falle und verdient einen Absatz. Vault ist nur ein Standard für die Zusammenarbeit: hinter dem Aufruf, der dir das Guthaben liefert, kann die echte Implementierung eine Tabelle im Speicher lesen oder MySQL befragen. Du weißt nicht, welches von beidem, und du kannst es nicht wissen, denn es hängt am Wirtschafts-Plugin, das dein Nutzer installiert hat. Fazit: du rufst das nie aus der Anzeigeschleife auf. Ein Datenbankzugriff im Hauptthread drückt die TPS, und eine Leiste, die jede Sekunde aktualisiert, löst diesen Zugriff in einer völlig anderen Größenordnung aus als ein Spieler, der einen Kontostand-Befehl tippt.

Die richtige Architektur hat also zwei Geschwindigkeiten. Eine langsame asynchrone Aufgabe füllt einen Cache. Die schnelle Anzeigeschleife liest nur diesen Cache und Werte, die schon im Speicher liegen. Der Spieler sieht in der ersten Sekunde nach dem Verbinden drei Punkte, und mehr kostet es nicht.

Und PlaceholderAPI? Das ist der De-facto-Standard für Variablen, aber es ist ein Plugin, das dein Admin installiert, keine Bibliothek, die du mitgibst: die Lizenz verbietet das in weitergegebenem Code. Dasselbe gilt für EssentialsX. Der richtige Weg ist, sie als weiche Abhängigkeit zu deklarieren, sie nur aufzurufen, wenn sie da sind, und deine Zeilen so zu schreiben, dass sie auch ohne sie lesbar bleiben. Vault dagegen ist genau dafür gemacht.

plugin.yml
name: MinaxSidebar
version: 1.0.0
main: fun.minax.sidebar.SidebarPlugin
api-version: '1.21'

# Vault, LuckPerms und PlaceholderAPI installiert DEIN ADMIN.
# Als softdepend startet dein Plugin auch dann, wenn sie fehlen.
softdepend: [Vault, LuckPerms, PlaceholderAPI]

libraries:
  # Der SQLite-JDBC-Treiber liegt Paper schon bei. HikariCP nicht:
  # hier deklarieren, der Server laedt ihn, kein Shading noetig.
  - com.zaxxer:HikariCP:<version>

Der Block libraries ersetzt das Shading: du deklarierst die Abhängigkeit, und der Server lädt sie herunter, statt dass du sie einpackst. Wichtig, weil es alle überrascht: der SQLite-JDBC-Treiber liegt Paper schon bei, dafür deklarierst du nichts. HikariCP nicht. Die Blöcke von plugin.yml sind auf docs.papermc.io dokumentiert, die ältere Bukkit-Referenz liest du weiterhin auf hub.spigotmc.org.

Hier ist der vollständige Service, mit asynchronem Cache und Anzeigeschleife getrennt:

Java
package fun.minax.sidebar;

import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public final class SidebarPlugin extends JavaPlugin implements Listener {

    /** Der teure Teil: alle 5 Sekunden gelesen, abseits des Hauptthreads. */
    private record Stats(double money, String group) {}

    private final Map<UUID, Stats> cache = new ConcurrentHashMap<>();
    private final Map<UUID, Sidebar> boards = new ConcurrentHashMap<>();
    private final Set<UUID> tracked = ConcurrentHashMap.newKeySet();

    private Economy economy;

    @Override
    public void onEnable() {
        getServer().getPluginManager().registerEvents(this, this);

        RegisteredServiceProvider<Economy> provider =
                getServer().getServicesManager().getRegistration(Economy.class);
        if (provider != null) {
            economy = provider.getProvider();
        } else {
            getLogger().info("Keine Vault-Wirtschaft gefunden, die Geldzeile bleibt aus.");
        }

        // Langsames Lesen: nie im Hauptthread, nie in der Anzeigeschleife.
        getServer().getAsyncScheduler()
                .runAtFixedRate(this, task -> refreshStats(), 1L, 5L, TimeUnit.SECONDS);
    }

    @Override
    public void onDisable() {
        boards.clear();
        cache.clear();
        tracked.clear();
    }

    @EventHandler
    public void onJoin(PlayerJoinEvent event) {
        Player player = event.getPlayer();
        UUID id = player.getUniqueId();
        tracked.add(id);

        Sidebar sidebar = new Sidebar(player, Component.text("MEIN SERVER", NamedTextColor.GOLD));
        boards.put(id, sidebar);

        // Der Scheduler des Spielers folgt ihm von Region zu Region: derselbe
        // Code laeuft auf Paper und auf Folia. Das null-Argument ist der
        // "retired"-Callback, falls der Spieler vor der Aufgabe verschwindet.
        // Die Startverzoegerung ist pro Spieler versetzt, gegen Lastspitzen.
        long offset = Math.abs(id.hashCode() % 20);
        player.getScheduler().runAtFixedRate(
                this, task -> sidebar.render(build(player)), null, 20L + offset, 20L);
    }

    @EventHandler
    public void onQuit(PlayerQuitEvent event) {
        UUID id = event.getPlayer().getUniqueId();
        tracked.remove(id);
        boards.remove(id);
        cache.remove(id);
    }

    /** Laeuft asynchron. Wir iterieren unsere eigene Liste, nicht getOnlinePlayers(). */
    private void refreshStats() {
        for (UUID id : tracked) {
            double money = economy == null
                    ? 0D
                    : economy.getBalance(Bukkit.getOfflinePlayer(id));
            cache.put(id, new Stats(money, groupOf(id)));
        }
    }

    private String groupOf(UUID id) {
        // Hier LuckPerms oder den Permission-Service von Vault anbinden.
        return "Spieler";
    }

    /** Laeuft im Thread des Spielers. Liest nur Cache und Speicher. */
    private List<Component> build(Player player) {
        Stats stats = cache.get(player.getUniqueId());
        String money = stats == null ? "..." : String.format("%.0f", stats.money());
        String group = stats == null ? "..." : stats.group();

        return List.of(
                Component.text("Rang ", NamedTextColor.WHITE)
                        .append(Component.text(group, NamedTextColor.GOLD)),
                Component.text("Geld ", NamedTextColor.WHITE)
                        .append(Component.text(money, NamedTextColor.GOLD)),
                Component.text("Ping ", NamedTextColor.WHITE)
                        .append(Component.text(player.getPing() + " ms", NamedTextColor.GOLD)),
                Component.text("Online ", NamedTextColor.WHITE)
                        .append(Component.text(
                                String.valueOf(Bukkit.getOnlinePlayers().size()),
                                NamedTextColor.GOLD)),
                Component.text("minax.fun", NamedTextColor.GRAY));
    }
}

Was kaputtgehen wird

Dieser Teil ist der nützlichste im ganzen Artikel, denn es sind stumme Ausfälle: nichts in den Logs, nur Zahlen, die leise falsch werden.

  • Eine Umbenennung im Offline-Modus. Das ist Falle Nummer eins, und fast niemand schreibt darüber. 75,1 % der Server laufen im Offline-Modus, und dort wird die Kennung eines Spielers aus seinem Namen abgeleitet. Der Spieler benennt sich um, seine Kennung ändert sich, und das Geld, das deine Leiste anzeigte, ist verwaist: die Zeile fällt auf null, ohne einen einzigen Fehler. Am Tag, an dem dieser Server in den Online-Modus wechselt, verwaist die gesamte Tabelle auf einmal. Speichere auf die Kennung, halte aber den letzten bekannten Namen in einer indexierten Spalte, sonst hast du keine Chance, die Daten wieder zuzuordnen.
  • Folia. Eine Aktualisierungsaufgabe, die auf dem alten globalen Scheduler aufsetzt, stürzt auf Folia ab, denn Folia führt die Weltregionen auf mehreren Threads aus. Du brauchst den RegionScheduler, und für eine Leiste pro Spieler ist der Scheduler des Spielers der richtige, weil er ihm von Region zu Region folgt. Die Dokumentation liegt auf docs.papermc.io.
  • Die sechzehnte Zeile, und Duplikate. In beiden Fällen keine Fehlermeldung. Wenn in deiner Leiste eine Zeile fehlt, zähl sie erst, bevor du debuggst.
  • Der Reload-Befehl im laufenden Betrieb. Er hinterlässt verwaiste Aufgaben, die weiter auf eine Leiste zeichnen, die es nicht mehr gibt, aus einem Plugin, das schon entladen ist. Starte den Server stattdessen neu.
  • Ein einziges .jar für beide Familien. Adventure 5 hat API-Teile entfernt, nicht bloß als veraltet markiert, und dein Compiler sieht immer nur die Version, gegen die du baust. Der Code kompiliert also sauber und bricht dann beim Laden auf der anderen Familie ab, als fehlende Methode. Ein .jar, das gegen 26.x gebaut ist, fällt auf 1.21 um, und ein .jar, das gegen 1.21 gebaut ist, fällt auf 26.x um, sobald es eine der verschwundenen Methoden aufruft. Keine der beiden Richtungen ist sicher: baue ein Artefakt pro Familie.

Deine Seitenleiste, generiert und online getestet

Beschreibe die Zeilen, den Takt und die Quellen. Minax schreibt den Java-Code, kompiliert ihn, und du prüfst das Ergebnis auf einem echten Paper-Server, bevor du das .jar installierst.

Plugin erstellen

Mein Fazit in drei Sätzen

Flackern ist kein Schicksal, es ist ein Schlüssel, der sich bewegt: setz den Schlüssel fest, lass den Text im Team-Präfix stecken, und das Problem ist dauerhaft weg. Aktualisiere alle zwanzig Ticks, nie jeden Tick, und zeichne nur neu, was sich geändert hat. Und lass vor allem nie eine Datenbankabfrage in deine Anzeigeschleife: dort verliert dein Server die TPS, nicht in den Scoreboard-Paketen.

Unter zwanzig Spielern reicht die rohe Bukkit-API mit Teams. Darüber, oder sobald die Leiste persönlich wird, FastBoard. Wenn du das fertige .jar danach aufsetzen willst, führt dich die Installationsanleitung durch die Schritte, und für die aktuellen Versionen liest du weiter bei Plugins für Minecraft 26.

Ähnliche Artikel