Перейти к основному содержимому

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.

Возможности​

  • Хранение в базе данных — факты и артефакты в MySQL 8 или PostgreSQL с автоматическим созданием таблиц и пулом соединений.
  • Безопасно по умолчанию — если запрошено общее хранилище, а оно недоступно, сервер останавливается, а не продолжает молча работать на локальных файлах.
  • Игровые режимы — отдельные данные для каждого игрового режима (выживание, скайблок, мини-игры) в одной базе данных.
  • Синхронизация между серверами — изменения подхватываются другими серверами в течение секунд, а с Redis — мгновенно.
  • Redis и Valkey — обмен сообщениями pub/sub, присутствие игроков во всей сети и переносы игроков через прокси.
  • Правила хранения для каждого расширения — выбирайте, какие расширения используют базу данных и хранятся ли их данные по профилям и общие ли они для игровых режимов.
  • Миграция при первом запуске — существующие факты и файлы ресурсов копируются в базу данных, когда она пуста.
  • Реестр серверов — каждый сервер сообщает своё состояние (TPS, игроки, память, расширения) каждые 30 секунд.
  • Секреты из окружения — любую настройку можно читать из переменной окружения.
  • Поддержка панели администрирования — состояние серверов, игровые режимы, экспорт данных и просмотр таблиц для веб-панели BTC.

Установка​

  1. Создайте пустую базу данных и пользователя для неё на MySQL 8 или новее либо на PostgreSQL. Пользователю нужно право создавать и изменять таблицы.
  2. По желанию, для нескольких серверов: запустите сервер Redis или Valkey, доступный каждому бэкенду.
  3. Положите JAR-файл MySQL в 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 сервер хранит всё в собственных файлах. Это нормально для одного постоянного сервера, но если его каталог пересоздаётся из шаблона при запуске, данные записываются в папку, которой в следующий раз не будет, и ничто вас не предупредит. При 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-sslfalseИспользовать SSL (require для PostgreSQL).
mysql.allow-public-key-retrievaltrueТолько для MySQL.
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 мс), max-lifetime (1800000 мс), validation-timeout (5000 мс) и keepalive-time (0, отключено).

Переменные окружения​

Любое значение, в точности равное ${env:NAME}, при загрузке файла читается из переменной окружения NAME:

mysql:
password: ${env:BTC_TYPEWRITER_DB_PASSWORD}

Числовое или булево значение (например порт) читается с правильным типом. Незаданная или пустая переменная останавливает сервер с сообщением, называющим переменную. Поддерживаются только целые значения, а не текст, который лишь содержит ${env:...}.

Redis и Valkey​

КлючПо умолчаниюЗначение
redis.enabledfalseПодключаться к Redis или Valkey. Работает, даже когда mysql.enabled равно false.
redis.typeredisredis или valkey. Оба используют один протокол.
redis.host, redis.portlocalhost, 6379Адрес сервера.
redis.username, redis.passwordпустоУчётные данные. Имя пользователя default или пустое считается отсутствием имени.
redis.channeltypewriter:eventsКанал pub/sub, общий для всех серверов.
redis.server-namesurvival в коде, server1 в сгенерированном файлеУникальное имя этого сервера. Оно же является именем сервера на прокси, используемым для переносов.
redis.group-idпустоГруппа сети этого сервера. Буквы, цифры, ., _, -, до 64 символов. Пустое значение означает изоляцию.

group-id ограничивает каждую функцию, взаимодействующую между серверами: социальные сообщения, призраки и списки сети передаются только между серверами с одинаковым непустым id группы. Серверы с другой группой не видят друг друга.

Хранение для каждого расширения​

В mysql.extensions.<name> у каждой записи есть три ключа:

  • enabled — хранит ли расширение собственные данные в базе данных.
  • profile-mode — shared (один набор данных для всех профилей) или per-profile.
  • network-mode — shared (одни и те же данные во всех игровых режимах) или isolated (свой набор для каждого игрового режима).

В сгенерированном файле перечислены значения по умолчанию для profiles, cosmetics, party, battlepass, translate и других. Эти настройки читают расширения, которые их поддерживают, например Profiles. Они не влияют на собственные факты и артефакты TypeWriter, которые всегда идут в базу данных, когда mysql.enabled равно true.

Настройки разрешаются в таком порядке: запись в 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 создают собственные таблицы в той же базе данных, например btc_guild_bank для банка Guild и typewriter_profiles для Profiles. Названия таблиц и столбцов создаются автоматически, а недостающие столбцы добавляются при запуске.

Каждая строка факта и ресурса несёт игровой режим. Сервер читает и записывает только строки своего mysql.game-mode.current (и строки с пометкой global), поэтому два игровых режима никогда не делят факты или артефакты, даже в одной базе данных.

Синхронизация между серверами​

Данные. Без Redis серверы всё равно сходятся: каждый опрашивает базу данных на предмет изменений, сделанных другими. С Redis сервер, записывающий факты, публикует короткое уведомление, и остальные синхронизируются немедленно. Запускайте все серверы в одном часовом поясе, потому что метки времени фактов хранятся в местном времени каждого сервера.

Социальные сообщения. Friend, Party и Guild публикуют свои изменения в виде сообщений JSON в канале Redis. Сервер игнорирует собственные сообщения. Они отправляются, только когда Redis подключён и redis.group-id не пуст.

Присутствие. Каждый сервер записывает в Redis presence:<player name> для каждого игрока в сети со сроком действия 120 секунд, обновляемым каждые 50 секунд и удаляемым, когда игрок выходит. Функции вроде «телепортироваться к другу» читают его, чтобы найти сервер игрока.

Переносы. Чтобы перенести игрока на другой сервер, расширение обращается к прокси через стандартный канал плагина BungeeCord, а для переносов по всему кластеру — через канал btc:bridge, понятный прокси BTC. redis.server-name каждого сервера должен совпадать с именем сервера, известным прокси.

Переподключение. Если Redis отключается, публикация пытается переподключиться, а подписчик повторяет попытки до 10 раз с растущей задержкой (от 5 секунд до 5 минут).

Миграция​

При первом запуске игрового режима с mysql.enabled: true:

  • Если в таблице фактов нет фактов для этого игрового режима, факты, находящиеся сейчас в памяти (из локальных файлов), копируются в базу данных. В противном случае факты загружаются из базы данных, а локальные игнорируются.
  • Если mysql.migrate-from-files равно true и таблица ресурсов для этого игрового режима пуста, в неё копируются файлы plugins/Typewriter/assets/.

Сделайте резервную копию plugins/Typewriter/ перед первым запуском. Возврат к файлам означает установку mysql.enabled: false: содержимое базы данных обратно не копируется.

Панель администрирования​

Расширение отвечает на запросы управления из веб-панели BTC: список серверов и их состояние (TPS, игроки, память, установленные расширения, задержка Redis, статистика пула), создание и удаление игровых режимов, включение расширений для каждого игрового режима, экспорт, импорт и миграция данных игрового режима, просмотр и редактирование таблиц хранилища и чтение последних журналов базы данных. Каждый запрос проверяет право пользователя панели и ограничивается по частоте.

Права​

Эти права проверяются для пользователя веб-панели. Они регистрируются без значения по умолчанию, поэтому выдавайте их администраторам.

ПравоДаёт доступ к
typewriter.admin.server.listПросмотру серверов, игровых режимов, хранилища, таблиц и обнаруженных расширений.
typewriter.admin.server.healthПросмотру проверок состояния и журналов базы данных.
typewriter.admin.server.deregisterУдалению сервера из реестра.
typewriter.admin.gamemode.createСозданию и удалению игровых режимов.
typewriter.admin.gamemode.configureИзменению настроек расширений, фильтров записей и переопределений хранилища, импорту, экспорту и миграции данных, редактированию строк таблиц.

Эксплуатация​

  • Перезагрузка. Публикация config_update в канале Redis заставляет каждый сервер перезагрузить mysql/config.yml и зарегистрировать новые расширения. В обычной работе файл читается один раз при запуске.
  • Реестр серверов. Таблица servers обновляется каждые 30 секунд. Записи старше 60 секунд считаются отключёнными и удаляются при запуске, а сервер удаляет себя при остановке JVM.
  • Взаимоблокировки. Пакеты фактов повторяются до 3 раз, когда MySQL сообщает о взаимоблокировке.
  • Резервные копии. Делайте резервные копии таблиц <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} нет соответствующей непустой переменной окружения в процессе сервера. Задайте её в определении службы, а не только в вашей оболочке.

Факты не делятся между двумя серверами. Проверьте, что у обоих 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, используя только опрос.

Расширение не хранит свои данные в базе данных. Расширения, обнаруженные при запуске, регистрируются отключёнными. Включите расширение в mysql.extensions в config.yml или в панели администрирования, затем перезапустите.

Факты удалённого игрока или удалённый факт возвращаются. Сервер был выключен дольше, чем 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 — расширения, зависящие от этого.