メインコンテンツまでスキップ

MySQL

ファクトとアーティファクト向けの共有 MySQL または PostgreSQL ストレージ。Redis 同期、プレゼンス、サーバーレジストリ付き。

プライベートv0.0.1ユーティリティ
必要TypeWriter0.9.0-beta-177 (BTC build)MySQL 8+ or PostgreSQLRedis or Valkey任意

BornToCraft ネットワーク向けに作られた拡張機能です。参照用にドキュメント化されており、配布されていません。

概要​

MySQL 拡張機能は、複数の TypeWriter サーバーが同じデータを共有できるようにするブリッジです。有効にすると、ファクト(プレイヤーの進行状況)とアーティファクト(他の拡張機能が作成するデータファイル)は、plugins/Typewriter/ 以下のファイルには保存されなくなります。代わりに、ネットワーク内のすべてのサーバーが読み書きする、MySQL または PostgreSQL のデータベースに保存されます。

また、任意の Redis(または Valkey)接続も担当します。Redis は、即時の変更通知、「このプレイヤーはどのサーバーにいるか」というレジストリ、そしてソーシャル拡張機能(Friend、Party、Guild)と Ghost が使うメッセージを運びます。BTC の他のいくつかの拡張機能は、設定、ゲームモード、サーバー名を、この拡張機能から読み取ります。

この拡張機能にはエントリがありません。ファイルで設定し、任意で BTC の Web パネルからも設定します。

機能​

  • データベースストレージ — MySQL 8 または PostgreSQL にファクトとアーティファクトを保存します。テーブルは自動作成され、コネクションプールも使います。
  • デフォルトで安全 — 共有ストレージが要求されたのに接続できない場合、サーバーは、何も通知せずにローカルファイルで動き続けるのではなく、停止します。
  • ゲームモード — 同じデータベース内で、ゲームモード(サバイバル、スカイブロック、ミニゲーム)ごとに、データを分けます。
  • サーバー間の同期 — 変更は数秒以内に他のサーバーに反映され、Redis があれば即座に反映されます。
  • Redis と Valkey — Pub/Sub メッセージング、ネットワーク全体のプレイヤーのプレゼンス、プロキシ経由のプレイヤーの転送。
  • 拡張機能ごとのストレージルール — どの拡張機能がデータベースを使うか、そのデータをプロファイルごとにするか、ゲームモード間で共有するかを選べます。
  • 初回起動時の移行 — データベースが空の場合、既存のファクトとアセットのファイルがデータベースにコピーされます。
  • サーバーレジストリ — 各サーバーが、30 秒ごとに、状態(TPS、プレイヤー数、メモリ、拡張機能)を報告します。
  • 環境変数からのシークレット — どの設定も、環境変数から読み取れます。
  • 管理パネルのサポート — BTC の Web パネル向けに、サーバーの状態、ゲームモード、データのエクスポート、テーブルの閲覧に対応します。

インストール​

  1. MySQL 8 以降、または PostgreSQL に、空のデータベースと、そのためのユーザーを作成します。ユーザーには、テーブルを作成および変更する権限が必要です。
  2. 任意、複数サーバーの場合: すべてのバックエンドから到達できる Redis または Valkey のサーバーを稼働させます。
  3. MySQL の JAR を plugins/Typewriter/extensions/ に置き、一度再起動します。plugins/Typewriter/mysql/config.yml ファイルが作成されます。
  4. はじめにの説明に従ってファイルを編集し、再起動します。データベースドライバー、コネクションプール、Redis クライアントは、拡張機能に同梱されています。

はじめに​

サーバーを共有データベースに移行します。

  1. サーバーを停止し、plugins/Typewriter/mysql/config.yml を開きます。
  2. mysql の下で、host、port、database、username、password を入力します。PostgreSQL の場合は、database-type: "POSTGRESQL" も追加し、ポートは 5432 を使います。
  3. mysql.game-mode.current に、このサーバーのゲームモード(たとえば survival)を設定します。ファクトを共有する必要があるサーバーは、同じ値にする必要があります。
  4. mysql.enabled: true に設定します。
  5. ネットワークの場合は、Redis を有効にします。redis.enabled: true にし、host、port、password を設定し、サーバーごとに一意の redis.server-name を、まとまって動作するすべてのサーバーで同じ redis.group-id を設定します。
  6. サーバーを起動します。ログに「Database storage initialized」と表示されます。データベースがまだ空の場合、既存のファクトとファイルがデータベースにコピーされます(移行を参照)。
  7. 他のサーバーでも、それぞれの server-name で繰り返します。
enabled は意図して選ぶ

mysql.enabled: false の場合、サーバーはすべてを自身のファイルに保存します。常設の 1 台のサーバーであれば問題ありませんが、起動時にそのディレクトリがテンプレートから再作成される場合、データは次回には存在しないフォルダに書き込まれ、何の警告も出ません。mysql.enabled: true の場合、データベースに接続できないサーバーは、ローカルファイルでプレイヤーにサービスを提供する代わりに、シャットダウンします。

仕組み​

拡張機能は、起動すると、他の拡張機能がデータを読み取る前に、TypeWriter のファイルストレージを、データベースストレージに置き換えます。TypeWriter はファクトをメモリ上に保持し、変更を小さなバッチでデータベースに書き込みます。他のサーバーは、データベースをポーリングして、まだ見ていない変更を読み取り、何かが変わったときには、Redis がすぐにそれを通知します。プレイヤーが退出またはキックされると、そのプレイヤーの保留中のファクトは、すぐに書き込まれます。

データベースには、変更されたものだけが保存されます。削除されたファクトは、値が 0 の行(トゥームストーン)として保存されるため、他のサーバーは、古い値を再保存するのではなく、削除を知ることができます。fact-tombstone-retention-days より古いトゥームストーンは、起動時に削除されます。

設定​

ファイルは plugins/Typewriter/mysql/config.yml です。ほとんどのセクションは任意で、存在しないキーにはデフォルトが使われます。

データベース​

キーデフォルト意味
mysql.enabledfalseデータベースを使用します。true の場合、データベースに到達できない、または設定が不完全だと、サーバーは停止します。
mysql.database-typeMYSQLMYSQL または POSTGRESQL。生成されるファイルにはないので、必要なら追加してください。
mysql.host, mysql.portlocalhost, 3306データベースのアドレス。PostgreSQL の場合は 5432 を使います。
mysql.databasetypewriterデータベース名。
mysql.username, mysql.passwordtypewriter、空認証情報。
mysql.use-sslfalseSSL を使用します(PostgreSQL では require)。
mysql.allow-public-key-retrievaltrueMySQL のみ。
mysql.table-prefixbtc_この拡張機能が作成するテーブルのプレフィックス。英数字と _ 以外の文字は _ になり、プレフィックスは小文字になります。
mysql.migrate-from-filestrueアセットのテーブルが空の場合、既存のアセットのファイルをデータベースにコピーします。
mysql.fact-tombstone-retention-days30削除の行が削除されるまでの日数。どのサーバーでも、オフラインのままでいる期間より長くする必要があります。
mysql.sync-interval-seconds30読み取られますが、このバージョンでは使われません。ポーリング間隔は TypeWriter 自身が設定します。
mysql.pool.*下記参照コネクションプール。
mysql.game-mode.currentdefaultこのサーバーのゲームモード。

プールのキーは、maximum-pool-size(10)、minimum-idle(2)、connection-timeout(30000 ms)、max-lifetime(1800000 ms)、validation-timeout(5000 ms)、keepalive-time(0、無効)です。

環境変数​

値がちょうど ${env:NAME} の場合、ファイルの読み込み時に、環境変数 NAME から読み取られます。

mysql:
password: ${env:BTC_TYPEWRITER_DB_PASSWORD}

数値や真偽値(たとえばポート)は、正しい型で読み取られます。設定されていない、または空の変数があると、変数名を示すメッセージとともに、サーバーが停止します。サポートされるのは値全体だけで、${env:...} を単に含むテキストはサポートされません。

Redis と Valkey​

キーデフォルト意味
redis.enabledfalseRedis または Valkey に接続します。mysql.enabled が false でも動作します。
redis.typeredisredis または valkey。どちらも同じプロトコルを使います。
redis.host, redis.portlocalhost, 6379サーバーのアドレス。
redis.username, redis.password空認証情報。ユーザー名が default または空の場合、ユーザー名なしとして扱われます。
redis.channeltypewriter:eventsすべてのサーバーが共有する Pub/Sub チャンネル。
redis.server-nameコードでは survival、生成されるファイルでは server1このサーバーの一意の名前。転送に使われる、プロキシのサーバー名でもあります。
redis.group-id空このサーバーのネットワークグループ。英数字、.、_、- で、最大 64 文字。空の場合は、孤立した状態を意味します。

group-id は、サーバー間でやり取りするすべての機能の範囲を決めます。ソーシャルメッセージ、ゴースト、ネットワークの名簿は、空でない同じグループ ID を持つサーバー間でのみ伝わります。グループが異なるサーバーは、互いを認識しません。

拡張機能ごとのストレージ​

mysql.extensions.<name> の下で、各エントリには 3 つのキーがあります。

  • enabled — 拡張機能が、自身のデータをデータベースに保存するかどうか。
  • profile-mode — shared(全プロファイルで 1 組のデータ)または per-profile。
  • network-mode — shared(すべてのゲームモードで同じデータ)または isolated(ゲームモードごとに 1 組)。

生成されるファイルには、profiles、cosmetics、party、battlepass、translate などのデフォルトが列挙されています。これらの設定は、それに対応する拡張機能(たとえば Profiles)が読み取ります。mysql.enabled が true の場合に常にデータベースへ送られる、TypeWriter 自身のファクトとアーティファクトには、影響しません。

設定は、config.yml のエントリ、次に、現在のゲームモードに対するテーブル <prefix>game_mode_extensions の行、の順に解決されます。起動時に、読み込まれたすべての拡張機能について、そこに無効の状態で行が作られ、管理者が有効にするのを待ちます。キー名は正規化されます。TypeWriter-ProfilesExtension と profiles は同じもので、friends は party のエイリアスです。

profiles セクション​

同じファイルの profiles セクションは、Profiles 拡張機能を設定します。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、creation-menu-entry-id です。その意味は、そのページを参照してください。

保存されるもの​

テーブル内容
<prefix>factsすべてのプレイヤーとグループのファクト: エントリ ID、グループ ID、ゲームモード、値、最終更新。
<prefix>assetsアーティファクトとその他のアセット(テキストまたはバイナリ)。パスとゲームモードごと。
<prefix>game_modesネットワークに知られているゲームモード。
<prefix>game_mode_extensionsゲームモードと拡張機能ごとのストレージルール。
<prefix>server_extensions, <prefix>extension_entry_filtersサーバーごと、エントリごとの同期設定。
servers稼働中のサーバーのレジストリ。名前にプレフィックスはありません。

BTC の他の拡張機能は、同じデータベースに独自のテーブルを作成します。たとえば、Guild のバンクには btc_guild_bank、Profiles には typewriter_profiles です。テーブル名とカラム名は自動的に作成され、足りないカラムは起動時に追加されます。

すべてのファクトとアセットの行は、ゲームモードを持ちます。サーバーは、自身の mysql.game-mode.current(と global と記された行)の行だけを読み書きするため、同じデータベース内でも、2 つのゲームモードがファクトやアーティファクトを共有することはありません。

サーバー間の同期​

データ。 Redis がなくても、サーバーは同じ状態に収束します。各サーバーが、他のサーバーによる変更をデータベースからポーリングで取得するためです。Redis があれば、ファクトを書き込んだサーバーが短い通知を発行し、他のサーバーは即座に同期します。ファクトのタイムスタンプは、各サーバーのローカル時間で保存されるため、すべてのサーバーを同じタイムゾーンで運用してください。

ソーシャルメッセージ。 Friend、Party、Guild は、変更を、Redis チャンネル上の JSON メッセージとして発行します。サーバーは、自身のメッセージを無視します。メッセージが送られるのは、Redis が接続されていて、redis.group-id が空でない場合だけです。

プレゼンス。 各サーバーは、オンラインのすべてのプレイヤーについて、Redis に presence:<player name> を書き込みます。有効期限は 120 秒で、50 秒ごとに更新され、プレイヤーが退出すると削除されます。「フレンドのもとへテレポート」などの機能は、これを読み取って、プレイヤーのサーバーを見つけます。

転送。 プレイヤーを別のサーバーに移動するために、拡張機能は、標準の BungeeCord プラグインチャンネルを通じてプロキシに依頼し、クラスター全体の移動には、BTC のプロキシが理解する btc:bridge チャンネルを使います。各サーバーの redis.server-name は、プロキシが認識しているサーバー名と一致している必要があります。

再接続。 Redis が切断された場合、発行側は再接続を試み、購読側は、遅延を増やしながら(5 秒から 5 分まで)最大 10 回再試行します。

移行​

mysql.enabled: true で、あるゲームモードを初めて起動したとき:

  • ファクトのテーブルに、このゲームモードのファクトがない場合、現在メモリ上にあるファクト(ローカルファイルから)が、データベースにコピーされます。そうでなければ、ファクトはデータベースから読み込まれ、ローカルのものは無視されます。
  • mysql.migrate-from-files が true で、このゲームモードのアセットのテーブルが空の場合、plugins/Typewriter/assets/ のファイルが、そこにコピーされます。

最初の起動の前に、plugins/Typewriter/ のバックアップを取ってください。ファイルに戻すには、mysql.enabled: false に設定します。データベースの内容は、コピーして戻されません。

管理パネル​

拡張機能は、BTC の Web パネルからの管理リクエストに応答します。サーバーとその状態(TPS、プレイヤー数、メモリ、インストール済みの拡張機能、Redis のレイテンシ、プールの統計)の一覧表示、ゲームモードの作成と削除、ゲームモードごとの拡張機能の有効化、ゲームモードのデータのエクスポート、インポート、移行、ストレージのテーブルの閲覧と編集、最近のデータベースログの読み取りです。各リクエストは、パネルを使う人の権限を確認し、レート制限されます。

権限​

これらの権限は、Web パネルのユーザーに対して確認されます。デフォルトでは登録されていないため、管理者に付与してください。

権限許可される内容
typewriter.admin.server.listサーバー、ゲームモード、ストレージ、テーブル、検出された拡張機能の閲覧。
typewriter.admin.server.healthヘルスチェックとデータベースログの閲覧。
typewriter.admin.server.deregisterレジストリからのサーバーの削除。
typewriter.admin.gamemode.createゲームモードの作成と削除。
typewriter.admin.gamemode.configure拡張機能の設定、エントリのフィルター、ストレージの上書きの変更、データのインポート、エクスポート、移行、テーブルの行の編集。

運用​

  • リロード。 Redis チャンネルに config_update を発行すると、すべてのサーバーが mysql/config.yml を再読み込みし、新しい拡張機能を登録します。通常の運用では、ファイルは起動時に一度だけ読み取られます。
  • サーバーレジストリ。 servers テーブルは、30 秒ごとに更新されます。60 秒より古いエントリは、オフラインとして扱われ、起動時に削除され、JVM が停止すると、サーバーは自身を削除します。
  • デッドロック。 MySQL がデッドロックを報告した場合、ファクトのバッチは、最大 3 回再試行されます。
  • バックアップ。 <prefix>facts と <prefix>assets のテーブルをバックアップしてください。削除は値が 0 の行なので、トゥームストーンの保持期間より古いバックアップを復元すると、削除されたファクトが復活することがあります。
  • 更新。 すべてのサーバーの JAR を、同じ再起動の時間帯に更新してください。スキーマは、起動時にその場でアップグレードされます。

トラブルシューティング​

サーバーが起動時に「Refusing to serve players on local file storage.」で停止する。 mysql.enabled が true で、データベースに到達できないか、値が不完全です。その前の行を読んでください。SEVERE のメッセージに、user@host:port/database とエラーが示されています。認証情報、ネットワーク、データベースを修正するか、このサーバーがファイルを使うべき場合は、mysql.enabled: false に設定してください。

「Configuration incomplete: environment variable(s) X are referenced ... but are not set.」と出る。 ${env:X} の値に対して、サーバープロセスに、対応する空でない環境変数がありません。シェルだけでなく、サービス定義で設定してください。

2 つのサーバー間でファクトが共有されない。 どちらも mysql.enabled: true で、同じデータベースと、同じ mysql.game-mode.current であることを確認してください。ゲームモードが異なると、別々のファクトが保存されます。Redis がない場合、変更が現れるまで、ポーリング間隔だけかかることがあります。

フレンド、パーティ、ギルド、ゴーストがサーバーをまたがない。 Redis が有効で接続されていて、redis.group-id がすべてのサーバーで同じ空でない値に設定され、redis.server-name がサーバーごとに一意である必要があります。グループ ID が空の場合、ソーシャルメッセージは送られません。

ログに「Failed to connect to Redis」と表示される。 ホスト、ポート、ユーザー名、パスワードを確認してください。Redis の requirepass のパスワードには、redis.password が必要です。拡張機能は、Redis がなくても、ポーリングだけで動作し続けます。

拡張機能が、データをデータベースに保存しない。 起動時に検出された拡張機能は、無効の状態で登録されます。config.yml の mysql.extensions の下、または管理パネルで拡張機能を有効にしてから、再起動してください。

削除されたプレイヤーや、削除されたファクトが復活する。 あるサーバーが、fact-tombstone-retention-days より長くオフラインだったため、古いデータを再保存しました。保持期間を延ばし、サーバーの古いコピーを復元しないようにしてください。

「HikariPool ... connection is not available」やタイムアウトが出る。 プールが枯渇しています。mysql.pool.maximum-pool-size(とデータベース自身の接続数の上限)を引き上げ、データベースサーバーでの遅いクエリを確認してください。

タイムスタンプがおかしい、または変更が見逃される。 データベースを共有しているサーバーが、異なるタイムゾーンで動作しています。揃えてください。

関連する拡張機能​

  • Profiles — profiles セクションと、プロファイルとゲームモードのストレージルールを使います。
  • Friend、Party、Guild — Redis を通じて同期されるソーシャル機能。
  • Ghost — Redis 接続を使って、他のサーバーのプレイヤーを表示します。
  • Translate、Rewards、BattlePass — この拡張機能に依存する拡張機能。