MySQL
Überblick
Die MySQL-Erweiterung ist die Brücke, über die mehrere TypeWriter-Server dieselben Daten teilen. Wenn du sie aktivierst, werden die Facts (Spielerfortschritt) und die Artifacts (von anderen Erweiterungen erstellte Datendateien) nicht mehr in Dateien unter plugins/Typewriter/ gehalten. Sie werden in einer MySQL- oder PostgreSQL-Datenbank gespeichert, die jeder Server deines Netzwerks liest und beschreibt.
Sie besitzt außerdem die optionale Redis-Verbindung (oder Valkey). Redis transportiert sofortige Änderungsbenachrichtigungen, das Register „auf welchem Server ist dieser Spieler“ und die Nachrichten der sozialen Erweiterungen (Friend, Party, Guild) und von Ghost. Mehrere andere BTC-Erweiterungen lesen ihre Einstellungen, ihren Spielmodus und ihren Servernamen aus dieser Erweiterung.
Diese Erweiterung hat keine Entries: Du konfigurierst sie mit einer Datei und optional über das BTC-Web-Panel.
Funktionen
- Datenbankspeicherung — Facts und Artifacts in MySQL 8 oder PostgreSQL, mit automatischer Tabellenerstellung und einem Verbindungspool.
- Standardmäßig sicher — wenn gemeinsamer Speicher angefordert wird und nicht erreichbar ist, stoppt der Server, statt stillschweigend auf lokalen Dateien weiterzuspielen.
- Spielmodi — getrennte Daten pro Spielmodus (Survival, Skyblock, Minispiele) in derselben Datenbank.
- Serverübergreifende Synchronisierung — Änderungen werden von anderen Servern innerhalb von Sekunden übernommen, mit Redis sofort.
- Redis und Valkey — Pub/Sub-Messaging, netzwerkweite Spielerpräsenz und Spielertransfers über den Proxy.
- Speicherregeln pro Erweiterung — wähle, welche Erweiterungen die Datenbank nutzen und ob ihre Daten pro Profil gelten und über Spielmodi hinweg geteilt werden.
- Migration beim ersten Start — vorhandene Facts und Asset-Dateien werden in die Datenbank kopiert, wenn sie leer ist.
- Server-Register — jeder Server meldet seinen Status (TPS, Spieler, Speicher, Erweiterungen) alle 30 Sekunden.
- Geheimnisse aus der Umgebung — jede Einstellung kann aus einer Umgebungsvariablen gelesen werden.
- Unterstützung des Administrationspanels — Serverzustand, Spielmodi, Datenexport und Tabellenansicht für das BTC-Web-Panel.
Installation
- Erstelle auf MySQL 8 oder neuer oder auf PostgreSQL eine leere Datenbank und einen Benutzer dafür. Der Benutzer benötigt das Recht, Tabellen zu erstellen und zu ändern.
- Optional, für mehrere Server: Betreibe einen Redis- oder Valkey-Server, den jedes Backend erreichen kann.
- Lege die MySQL-JAR in
plugins/Typewriter/extensions/ab und starte einmal neu. Die Dateiplugins/Typewriter/mysql/config.ymlwird erstellt. - Bearbeite die Datei wie unter Erste Schritte beschrieben und starte dann neu. Die Datenbanktreiber, der Verbindungspool und der Redis-Client sind in der Erweiterung enthalten.
Erste Schritte
So stellst du einen Server auf eine gemeinsame Datenbank um:
- Stoppe den Server und öffne
plugins/Typewriter/mysql/config.yml. - Fülle unter
mysqlhost,port,database,usernameundpassword. Füge für PostgreSQL außerdemdatabase-type: "POSTGRESQL"hinzu und verwende Port5432. - Setze
mysql.game-mode.currentauf den Spielmodus dieses Servers, zum Beispielsurvival. Server, die Facts teilen sollen, benötigen denselben Wert. - Setze
mysql.enabled: true. - Aktiviere für ein Netzwerk Redis:
redis.enabled: true, mithost,portundpassword, einem eindeutigenredis.server-namepro Server und derselbenredis.group-idauf allen Servern, die zusammengehören. - Starte den Server. Das Log zeigt „Database storage initialized“. Vorhandene Facts und Dateien werden in die Datenbank kopiert, wenn sie noch leer ist (siehe Migration).
- Wiederhole das auf den anderen Servern mit ihrem eigenen
server-name.
enabled bewusstBei mysql.enabled: false speichert ein Server alles in seinen eigenen Dateien. Das ist für einen dauerhaften Server in Ordnung, aber wird sein Verzeichnis beim Start aus einer Vorlage neu erstellt, werden die Daten in einen Ordner geschrieben, den es beim nächsten Mal nicht mehr gibt, und nichts warnt dich. Bei mysql.enabled: true fährt ein Server, der die Datenbank nicht erreichen kann, herunter, statt Spieler auf lokalen Dateien zu bedienen.
So funktioniert es
Beim Start ersetzt die Erweiterung die Dateispeicherung von TypeWriter durch die Datenbankspeicherung, bevor eine andere Erweiterung ihre Daten liest. TypeWriter hält Facts im Speicher und schreibt die Änderungen in kleinen Stapeln in die Datenbank. Andere Server lesen die Änderungen, die sie noch nicht gesehen haben, durch Abfragen der Datenbank, und Redis teilt ihnen sofort mit, wenn sich etwas geändert hat. Wenn ein Spieler den Server verlässt oder gekickt wird, werden seine ausstehenden Facts sofort geschrieben.
Die Datenbank speichert nur, was sich geändert hat. Ein gelöschter Fact wird als Zeile mit dem Wert 0 gespeichert (ein Tombstone), damit andere Server von der Löschung erfahren, statt den alten Wert erneut zu speichern. Tombstones, die älter als fact-tombstone-retention-days sind, werden beim Start bereinigt.
Konfiguration
Die Datei ist plugins/Typewriter/mysql/config.yml. Die meisten Abschnitte sind optional: Jeder fehlende Key übernimmt seinen Standardwert.
Datenbank
| Key | Standard | Bedeutung |
|---|---|---|
mysql.enabled | false | Die Datenbank verwenden. Bei true stoppt der Server, wenn die Datenbank nicht erreichbar ist oder eine Einstellung unvollständig ist. |
mysql.database-type | MYSQL | MYSQL oder POSTGRESQL. Nicht in der generierten Datei, füge es bei Bedarf hinzu. |
mysql.host, mysql.port | localhost, 3306 | Datenbankadresse. Verwende 5432 für PostgreSQL. |
mysql.database | typewriter | Datenbankname. |
mysql.username, mysql.password | typewriter, leer | Zugangsdaten. |
mysql.use-ssl | false | SSL verwenden (require bei PostgreSQL). |
mysql.allow-public-key-retrieval | true | Nur MySQL. |
mysql.table-prefix | btc_ | Präfix der von dieser Erweiterung erstellten Tabellen. Zeichen außer Buchstaben, Ziffern und _ werden zu _, und das Präfix wird kleingeschrieben. |
mysql.migrate-from-files | true | Vorhandene Asset-Dateien in die Datenbank kopieren, wenn die Asset-Tabelle leer ist. |
mysql.fact-tombstone-retention-days | 30 | Tage, bevor Löschzeilen bereinigt werden. Muss länger sein als die Zeit, die ein Server offline bleibt. |
mysql.sync-interval-seconds | 30 | Wird gelesen, in dieser Version aber nicht verwendet: Das Abfrageintervall legt TypeWriter selbst fest. |
mysql.pool.* | siehe unten | Verbindungspool. |
mysql.game-mode.current | default | Spielmodus dieses Servers. |
Die Pool-Keys sind maximum-pool-size (10), minimum-idle (2), connection-timeout (30000 ms), max-lifetime (1800000 ms), validation-timeout (5000 ms) und keepalive-time (0, deaktiviert).
Umgebungsvariablen
Jeder Wert, der genau ${env:NAME} lautet, wird beim Laden der Datei aus der Umgebungsvariablen NAME gelesen:
mysql:
password: ${env:BTC_TYPEWRITER_DB_PASSWORD}
Ein numerischer oder boolescher Wert (zum Beispiel ein Port) wird mit dem richtigen Typ gelesen. Eine nicht gesetzte oder leere Variable stoppt den Server mit einer Meldung, die die Variable nennt. Es werden nur ganze Werte unterstützt, nicht Text, der lediglich ${env:...} enthält.
Redis und Valkey
| Key | Standard | Bedeutung |
|---|---|---|
redis.enabled | false | Mit Redis oder Valkey verbinden. Funktioniert auch, wenn mysql.enabled false ist. |
redis.type | redis | redis oder valkey. Beide verwenden dasselbe Protokoll. |
redis.host, redis.port | localhost, 6379 | Serveradresse. |
redis.username, redis.password | leer | Zugangsdaten. Der Benutzername default oder ein leerer gilt als kein Benutzername. |
redis.channel | typewriter:events | Von allen Servern gemeinsam genutzter Pub/Sub-Kanal. |
redis.server-name | survival im Code, server1 in der generierten Datei | Eindeutiger Name dieses Servers. Er ist auch der für Transfers verwendete Proxy-Servername. |
redis.group-id | leer | Netzwerkgruppe dieses Servers. Buchstaben, Ziffern, ., _, -, bis zu 64 Zeichen. Leer bedeutet isoliert. |
Die group-id begrenzt jede Funktion, die zwischen Servern kommuniziert: Soziale Nachrichten, Ghosts und Netzwerklisten wandern nur zwischen Servern mit derselben, nicht leeren Gruppen-ID. Server mit einer anderen Gruppe sehen sich nicht.
Speicherung pro Erweiterung
Unter mysql.extensions.<name> hat jeder Eintrag drei Keys:
enabled— ob die Erweiterung ihre eigenen Daten in der Datenbank speichert.profile-mode—shared(ein Datensatz für alle Profile) oderper-profile.network-mode—shared(dieselben Daten in jedem Spielmodus) oderisolated(ein Satz pro Spielmodus).
Die generierte Datei listet Standardwerte für Profiles, Cosmetics, Party, BattlePass, Translate und weitere auf. Diese Einstellungen werden von den Erweiterungen gelesen, die sie unterstützen, zum Beispiel Profiles. Sie wirken sich nicht auf die eigenen Facts und Artifacts von TypeWriter aus, die immer in die Datenbank gehen, wenn mysql.enabled true ist.
Einstellungen werden in dieser Reihenfolge aufgelöst: der Eintrag in config.yml, dann die Zeile der Tabelle <prefix>game_mode_extensions für den aktuellen Spielmodus. Beim Start erhält jede geladene Erweiterung dort eine Zeile, deaktiviert, die darauf wartet, dass ein Administrator sie aktiviert. Key-Namen werden normalisiert: TypeWriter-ProfilesExtension und profiles sind dasselbe, und friends ist ein Alias für party.
Abschnitt profiles
Der Abschnitt profiles derselben Datei konfiguriert die Profiles-Erweiterung: enabled, max-profiles, default-profile-name, save-statistics, save-inventory, save-ender-chest, require-permission, auto-create-default, force-profile-selection, switch-cooldown-seconds, coop-supported und creation-menu-entry-id. Ihre Bedeutung findest du auf der dortigen Seite.
Was gespeichert wird
| Tabelle | Inhalt |
|---|---|
<prefix>facts | Facts jedes Spielers und jeder Gruppe: Entry-ID, Gruppen-ID, Spielmodus, Wert, letzte Aktualisierung. |
<prefix>assets | Artifacts und andere Assets (Text oder binär), nach Pfad und Spielmodus. |
<prefix>game_modes | Dem Netzwerk bekannte Spielmodi. |
<prefix>game_mode_extensions | Speicherregeln pro Spielmodus und Erweiterung. |
<prefix>server_extensions, <prefix>extension_entry_filters | Synchronisierungseinstellungen pro Server und pro Entry. |
servers | Register der laufenden Server. Sein Name hat kein Präfix. |
Andere BTC-Erweiterungen erstellen ihre eigenen Tabellen in derselben Datenbank, zum Beispiel btc_guild_bank für die Guild-Bank und typewriter_profiles für Profiles. Tabellen- und Spaltennamen werden automatisch erstellt, und fehlende Spalten werden beim Start hinzugefügt.
Jede Fact- und Asset-Zeile trägt einen Spielmodus. Ein Server liest und schreibt nur die Zeilen seines eigenen mysql.game-mode.current (und als global markierte Zeilen), sodass zwei Spielmodi auch in derselben Datenbank nie Facts oder Artifacts teilen.
Serverübergreifende Synchronisierung
Daten. Ohne Redis konvergieren die Server dennoch: Jeder fragt die Datenbank nach Änderungen der anderen ab. Mit Redis veröffentlicht ein Server, der Facts schreibt, eine kurze Meldung, und die anderen synchronisieren sofort. Betreibe alle Server in derselben Zeitzone, denn Fact-Zeitstempel werden in der lokalen Zeit jedes Servers gespeichert.
Soziale Nachrichten. Friend, Party und Guild veröffentlichen ihre Änderungen als JSON-Nachrichten auf dem Redis-Kanal. Ein Server ignoriert seine eigenen Nachrichten. Sie werden nur gesendet, wenn Redis verbunden und redis.group-id nicht leer ist.
Präsenz. Jeder Server schreibt für jeden Online-Spieler presence:<player name> in Redis, mit 120 Sekunden Ablaufzeit, alle 50 Sekunden erneuert und entfernt, wenn der Spieler geht. Funktionen wie „zu einem Freund teleportieren“ lesen sie, um den Server des Spielers zu finden.
Transfers. Um einen Spieler auf einen anderen Server zu verschieben, fragt die Erweiterung den Proxy über den Standard-Plugin-Kanal BungeeCord, und für clusterweite Verschiebungen über den vom BTC-Proxy verstandenen Kanal btc:bridge. Der redis.server-name jedes Servers muss dem dem Proxy bekannten Servernamen entsprechen.
Wiederverbindung. Fällt Redis aus, versucht das Veröffentlichen, sich wieder zu verbinden, und der Subscriber versucht es bis zu 10 Mal mit wachsender Verzögerung (5 Sekunden bis 5 Minuten).
Migration
Wenn ein Spielmodus zum ersten Mal mit mysql.enabled: true gestartet wird:
- Enthält die Fact-Tabelle keine Facts für diesen Spielmodus, werden die aktuell im Speicher befindlichen Facts (aus den lokalen Dateien) in die Datenbank kopiert. Andernfalls werden die Facts aus der Datenbank geladen, und die lokalen werden ignoriert.
- Ist
mysql.migrate-from-filestrueund die Asset-Tabelle für diesen Spielmodus leer, werden die Dateien vonplugins/Typewriter/assets/dorthin kopiert.
Bewahre vor dem ersten Start eine Sicherung von plugins/Typewriter/ auf. Zurück zu Dateien bedeutet, mysql.enabled: false zu setzen: Der Datenbankinhalt wird nicht zurückkopiert.
Administrationspanel
Die Erweiterung beantwortet Verwaltungsanfragen aus dem BTC-Web-Panel: Server und ihren Zustand auflisten (TPS, Spieler, Speicher, installierte Erweiterungen, Redis-Latenz, Pool-Statistiken), Spielmodi erstellen und löschen, Erweiterungen pro Spielmodus aktivieren, die Daten eines Spielmodus exportieren, importieren und migrieren, die Speichertabellen ansehen und bearbeiten und aktuelle Datenbank-Logs lesen. Jede Anfrage prüft eine Berechtigung der Person, die das Panel nutzt, und ist ratenbegrenzt.
Berechtigungen
Diese Berechtigungen werden für den Web-Panel-Benutzer geprüft. Sie sind ohne Standardwert registriert, vergib sie also an Administratoren.
| Berechtigung | Erlaubt |
|---|---|
typewriter.admin.server.list | Server, Spielmodi, Speicher, Tabellen und gefundene Erweiterungen ansehen. |
typewriter.admin.server.health | Zustandsprüfungen und Datenbank-Logs ansehen. |
typewriter.admin.server.deregister | Einen Server aus dem Register entfernen. |
typewriter.admin.gamemode.create | Spielmodi erstellen und löschen. |
typewriter.admin.gamemode.configure | Erweiterungseinstellungen, Entry-Filter und Speicher-Überschreibungen ändern, Daten importieren, exportieren und migrieren, Tabellenzeilen bearbeiten. |
Betrieb
- Reload. Das Veröffentlichen von
config_updateauf dem Redis-Kanal lässt jeden Servermysql/config.ymlneu laden und neue Erweiterungen registrieren. Im Normalbetrieb wird die Datei einmal beim Start gelesen. - Server-Register. Die Tabelle
serverswird alle 30 Sekunden aktualisiert. Einträge, die älter als 60 Sekunden sind, gelten als offline und werden beim Start entfernt, und ein Server entfernt sich selbst, wenn die JVM stoppt. - Deadlocks. Fact-Stapel werden bis zu 3 Mal wiederholt, wenn MySQL einen Deadlock meldet.
- Sicherungen. Sichere die Tabellen
<prefix>factsund<prefix>assets. Da eine Löschung eine Zeile mit dem Wert0ist, kann eine wiederhergestellte Sicherung, die älter als die Tombstone-Aufbewahrung ist, gelöschte Facts zurückbringen. - Updates. Aktualisiere die JAR auf allen Servern im selben Neustartfenster. Das Schema wird beim Start direkt aktualisiert.
Fehlerbehebung
Der Server stoppt beim Start mit „Refusing to serve players on local file storage.“ mysql.enabled ist true und die Datenbank ist nicht erreichbar oder ein Wert ist unvollständig. Lies die Zeile davor: Die SEVERE-Meldung nennt user@host:port/database und den Fehler. Korrigiere die Zugangsdaten, das Netzwerk oder die Datenbank oder setze mysql.enabled: false, wenn dieser Server Dateien verwenden soll.
„Configuration incomplete: environment variable(s) X are referenced ... but are not set.“ Ein ${env:X}-Wert hat im Serverprozess keine passende, nicht leere Umgebungsvariable. Setze sie in der Dienstdefinition, nicht nur in deiner Shell.
Facts werden zwischen zwei Servern nicht geteilt. Prüfe, ob beide mysql.enabled: true, dieselbe Datenbank und dasselbe mysql.game-mode.current haben. Unterschiedliche Spielmodi speichern getrennte Facts. Ohne Redis kann es bis zum Abfrageintervall dauern, bis eine Änderung erscheint.
Freunde, Parties, Gilden oder Ghosts überschreiten keine Servergrenzen. Redis muss aktiviert und verbunden sein, redis.group-id muss auf jedem Server auf denselben nicht leeren Wert gesetzt sein, und redis.server-name muss pro Server eindeutig sein. Bei leerer Gruppen-ID werden soziale Nachrichten nicht gesendet.
Das Log zeigt „Failed to connect to Redis“. Prüfe Host, Port, Benutzername und Passwort. Ein Redis-Passwort aus requirepass benötigt redis.password. Die Erweiterung arbeitet ohne Redis weiter, nur mit Abfragen.
Eine Erweiterung speichert ihre Daten nicht in der Datenbank. Beim Start gefundene Erweiterungen werden deaktiviert registriert. Aktiviere die Erweiterung unter mysql.extensions in config.yml oder im Administrationspanel und starte dann neu.
Facts eines gelöschten Spielers oder ein gelöschter Fact kommen zurück. Ein Server war länger als fact-tombstone-retention-days offline und hat alte Daten erneut gespeichert. Erhöhe die Aufbewahrung und vermeide es, alte Kopien eines Servers wiederherzustellen.
„HikariPool ... connection is not available“ oder Timeouts. Der Pool ist erschöpft. Erhöhe mysql.pool.maximum-pool-size (und das eigene Verbindungslimit der Datenbank) und suche auf dem Datenbankserver nach langsamen Abfragen.
Zeitstempel wirken falsch oder Änderungen werden verpasst. Server, die sich eine Datenbank teilen, laufen in verschiedenen Zeitzonen. Gleiche sie an.