跳到主要内容

MySQL

为事实和构件提供共享的 MySQL 或 PostgreSQL 存储,支持 Redis 同步、在线状态和服务器注册表。

私有v0.0.1实用工具
依赖TypeWriter0.9.0-beta-177 (BTC build)MySQL 8+ or PostgreSQLRedis or Valkey可选

专为 BornToCraft 服务器网络打造。此扩展仅作参考文档,不对外分发。

概述​

MySQL 扩展是让多个 TypeWriter 服务器共享同一份数据的桥梁。启用它之后,fact(玩家进度)和 artifact(由其他扩展创建的数据文件)就不再保存在 plugins/Typewriter/ 下的文件中。它们会存储在你网络中每个服务器都读写的 MySQL 或 PostgreSQL 数据库里。

它还负责可选的 Redis(或 Valkey)连接。Redis 承载即时的变更通知、“这位玩家在哪个服务器上”的登记表,以及社交扩展(Friend、Party、Guild)和 Ghost 所使用的消息。其他几个 BTC 扩展会从本扩展读取它们的设置、游戏模式和服务器名称。

此扩展没有条目:你通过一个文件来配置它,也可以选择通过 BTC 网页面板来配置。

功能​

  • 数据库存储 — fact 和 artifact 存放在 MySQL 8 或 PostgreSQL 中,自动建表,并带有连接池。
  • 默认即安全 — 当要求使用共享存储却无法连接时,服务器会停止,而不是悄悄地改用本地文件继续运行。
  • 游戏模式 — 在同一个数据库中为每种游戏模式(生存、空岛、小游戏)分别保存数据。
  • 跨服务器同步 — 其他服务器会在几秒内获取变更,使用 Redis 时则是即时的。
  • Redis 和 Valkey — 发布/订阅消息、全网络的玩家在线状态,以及通过代理服务器进行的玩家转移。
  • 按扩展的存储规则 — 选择哪些扩展使用数据库,以及它们的数据是按档案存储,还是跨游戏模式共享。
  • 首次启动迁移 — 当数据库为空时,现有的 fact 和资源文件会被复制到数据库中。
  • 服务器注册表 — 每个服务器每 30 秒报告一次状态(TPS、玩家数、内存、扩展)。
  • 从环境变量读取机密 — 任何设置都可以从环境变量读取。
  • 管理面板支持 — 为 BTC 网页面板提供服务器健康状况、游戏模式、数据导出和表格浏览。

安装​

  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。需要共享 fact 的服务器必须使用相同的值。
  4. 设置 mysql.enabled: true。
  5. 对于多服务器网络,请启用 Redis:redis.enabled: true,并设置 host、port 和 password,为每个服务器设置唯一的 redis.server-name,并在所有属于同一组的服务器上使用相同的 redis.group-id。
  6. 启动服务器。日志会显示 “Database storage initialized”。当数据库仍为空时,现有的 fact 和文件会被复制到数据库中(参见迁移)。
  7. 在其他服务器上重复此操作,并使用它们各自的 server-name。
请有意识地选择 enabled

当 mysql.enabled: false 时,服务器会把所有内容存储在自己的文件中。对于一个永久运行的服务器这没有问题,但如果它的目录在启动时由模板重新创建,数据就会被写入一个下次不存在的文件夹,而且不会有任何警告。当 mysql.enabled: true 时,无法连接数据库的服务器会选择关闭,而不是用本地文件为玩家提供服务。

工作原理​

扩展启动时,会在任何其他扩展读取其数据之前,用数据库存储取代 TypeWriter 的文件存储。TypeWriter 把 fact 保存在内存中,并将变更分小批量写入数据库。其他服务器通过轮询数据库来读取它们尚未见过的变更,而 Redis 会在发生变化时立即通知它们。当玩家退出或被踢出时,他们尚未写入的 fact 会立即写入。

数据库只存储发生了变化的内容。被删除的 fact 会作为值为 0 的行存储(墓碑记录,tombstone),这样其他服务器就能得知删除,而不是重新保存旧值。早于 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(对于 PostgreSQL 为 require)。
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所有服务器共用的发布/订阅频道。
redis.server-name代码中为 survival,生成的文件中为 server1此服务器的唯一名称。它同时也是用于转移的代理服务器名称。
redis.group-id空此服务器所属的网络组。可使用字母、数字、.、_、-,最多 64 个字符。为空表示隔离。

group-id 限定了每个在服务器之间通信的功能的范围:社交消息、幽灵(ghost)和网络名册只会在具有相同且非空的组 id 的服务器之间传递。组不同的服务器彼此看不到对方。

按扩展的存储​

在 mysql.extensions.<name> 下,每个条目有三个键:

  • enabled — 该扩展是否把自己的数据存储在数据库中。
  • profile-mode — shared(所有档案共用一套数据)或 per-profile。
  • network-mode — shared(在每种游戏模式中使用相同的数据)或 isolated(每种游戏模式一套数据)。

生成的文件列出了 profiles、cosmetics、party、battlepass、translate 等的默认值。这些设置由支持它们的扩展读取,例如 Profiles。它们不会影响 TypeWriter 自己的 fact 和 artifact,当 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每位玩家和每个组的 fact:条目 id、组 id、游戏模式、值、最后更新时间。
<prefix>assetsartifact 和其他资源(文本或二进制),按路径和游戏模式存储。
<prefix>game_modes网络已知的游戏模式。
<prefix>game_mode_extensions每种游戏模式和每个扩展的存储规则。
<prefix>server_extensions, <prefix>extension_entry_filters按服务器和按条目的同步设置。
servers运行中服务器的注册表。其名称没有前缀。

其他 BTC 扩展会在同一个数据库中创建它们自己的表,例如 Guild 银行的 btc_guild_bank,以及 Profiles 的 typewriter_profiles。表名和列名会自动创建,缺少的列会在启动时添加。

每一行 fact 和资源都带有一个游戏模式。服务器只会读写其自身 mysql.game-mode.current 的行(以及标记为 global 的行),因此即使在同一个数据库中,两种游戏模式也永远不会共享 fact 或 artifact。

跨服务器同步​

数据。 没有 Redis 时,服务器仍然会趋于一致:每个服务器都会轮询数据库,查看其他服务器所做的更改。使用 Redis 时,写入 fact 的服务器会发布一条简短的通知,其他服务器会立即同步。请让所有服务器使用相同的时区,因为 fact 的时间戳是按各服务器的本地时间存储的。

社交消息。 Friend、Party 和 Guild 会将它们的变更以 JSON 消息的形式发布到 Redis 频道上。服务器会忽略它自己的消息。只有在 Redis 已连接并且 redis.group-id 不为空时才会发送。

在线状态。 每个服务器会为每位在线玩家在 Redis 中写入 presence:<player name>,有效期 120 秒,每 50 秒刷新一次,并在玩家退出时移除。诸如“传送到好友”这样的功能会读取它来找到玩家所在的服务器。

转移。 若要将玩家移动到另一个服务器,扩展会通过标准的 BungeeCord 插件通道向代理服务器发出请求,对于整个集群范围的移动,则通过 BTC 代理服务器能够理解的 btc:bridge 通道。每个服务器的 redis.server-name 必须与代理服务器所知道的服务器名称一致。

重新连接。 如果 Redis 断开,发布操作会尝试重新连接,订阅端会以递增的延迟(5 秒直至 5 分钟)重试最多 10 次。

迁移​

首次以 mysql.enabled: true 启动某个游戏模式时:

  • 如果 fact 表中没有这个游戏模式的 fact,则当前内存中的 fact(来自本地文件)会被复制到数据库。否则会从数据库加载 fact,本地的则被忽略。
  • 如果 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更改扩展设置、条目过滤器和存储覆盖,导入、导出和迁移数据,编辑表中的行。

运维​

  • 重新加载。 在 Redis 频道上发布 config_update 会让每个服务器重新加载 mysql/config.yml 并注册新的扩展。正常运行时只会在启动时读取一次该文件。
  • 服务器注册表。 servers 表每 30 秒刷新一次。超过 60 秒的条目被视为离线,并在启动时被移除,服务器会在 JVM 停止时移除自己。
  • 死锁。 当 MySQL 报告死锁时,fact 批处理最多重试 3 次。
  • 备份。 请备份 <prefix>facts 和 <prefix>assets 表。由于删除是一行值为 0 的记录,因此恢复的备份如果早于墓碑保留期,可能会让已删除的 fact 重新出现。
  • 更新。 请在同一个重启窗口内更新所有服务器上的 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} 的值在服务器进程中没有对应的非空环境变量。请在服务定义中设置它,而不仅仅是在你的 shell 中。

两个服务器之间没有共享 fact。 请检查两者是否都设置了 mysql.enabled: true、使用相同的数据库,以及相同的 mysql.game-mode.current。不同的游戏模式会存储各自独立的 fact。没有 Redis 时,变更可能需要等待一个轮询间隔才会出现。

好友、队伍、公会或幽灵无法跨服务器。 Redis 必须已启用并已连接,每个服务器上的 redis.group-id 必须设置为相同的非空值,并且每个服务器的 redis.server-name 必须唯一。组 id 为空时,不会发送社交消息。

日志显示 “Failed to connect to Redis”。 请检查主机、端口、用户名和密码。Redis 的 requirepass 密码需要填入 redis.password。没有 Redis 时,扩展仍会继续工作,只使用轮询。

某个扩展没有把它的数据存储在数据库中。 启动时发现的扩展会被注册为禁用状态。请在 config.yml 的 mysql.extensions 下,或在管理面板中启用该扩展,然后重启。

已删除玩家的 fact 或已删除的 fact 又回来了。 某个服务器离线的时间超过了 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 — 依赖本扩展的扩展。