Saltar al contenido principal

MySQL

Almacenamiento compartido MySQL o PostgreSQL para hechos y artefactos, con sincronización Redis, presencia y registro de servidores.

Privadasv0.0.1Utilidades
RequiereTypeWriter0.9.0-beta-177 (BTC build)MySQL 8+ or PostgreSQLRedis or Valkeyopcional

Creada para la red BornToCraft. Esta extensión está documentada como referencia y no se distribuye.

Descripción general​

La extensión MySQL es el puente que permite a varios servidores de TypeWriter compartir los mismos datos. Cuando la activas, los facts (el progreso de los jugadores) y los artefactos (archivos de datos creados por otras extensiones) dejan de guardarse en archivos bajo plugins/Typewriter/. Se almacenan en una base de datos MySQL o PostgreSQL que leen y escriben todos los servidores de tu red.

También gestiona la conexión opcional con Redis (o Valkey). Redis transporta las notificaciones instantáneas de cambios, el registro de "en qué servidor está este jugador" y los mensajes que usan las extensiones sociales (Friend, Party, Guild) y Ghost. Varias otras extensiones de BTC leen de esta extensión sus ajustes, su modo de juego y el nombre del servidor.

Esta extensión no tiene entradas: la configuras con un archivo y, opcionalmente, desde el panel web de BTC.

Características​

  • Almacenamiento en base de datos — facts y artefactos en MySQL 8 o PostgreSQL, con creación automática de tablas y un pool de conexiones.
  • Seguro por defecto — cuando se solicita almacenamiento compartido y no se puede alcanzar, el servidor se detiene en lugar de jugar en silencio con archivos locales.
  • Modos de juego — datos separados por modo de juego (supervivencia, skyblock, minijuegos) en la misma base de datos.
  • Sincronización entre servidores — los cambios los recogen otros servidores en cuestión de segundos, al instante con Redis.
  • Redis y Valkey — mensajería pub/sub, presencia de jugadores en toda la red y transferencias de jugadores a través del proxy.
  • Reglas de almacenamiento por extensión — elige qué extensiones usan la base de datos y si sus datos son por perfil y se comparten entre modos de juego.
  • Migración en el primer inicio — los facts y archivos de assets existentes se copian a la base de datos cuando está vacía.
  • Registro de servidores — cada servidor informa de su estado (TPS, jugadores, memoria, extensiones) cada 30 segundos.
  • Secretos desde el entorno — cualquier ajuste se puede leer de una variable de entorno.
  • Soporte para el panel de administración — estado del servidor, modos de juego, exportación de datos y exploración de tablas para el panel web de BTC.

Instalación​

  1. Crea una base de datos vacía y un usuario para ella en MySQL 8 o posterior, o en PostgreSQL. El usuario necesita permiso para crear y modificar tablas.
  2. Opcional, para varios servidores: ejecuta un servidor Redis o Valkey al que puedan llegar todos los backends.
  3. Coloca el JAR de MySQL en plugins/Typewriter/extensions/ y reinicia una vez. Se crea el archivo plugins/Typewriter/mysql/config.yml.
  4. Edita el archivo como se describe en Primeros pasos y reinicia. Los drivers de base de datos, el pool de conexiones y el cliente de Redis vienen incluidos con la extensión.

Primeros pasos​

Traslada un servidor a una base de datos compartida:

  1. Detén el servidor y abre plugins/Typewriter/mysql/config.yml.
  2. En mysql, rellena host, port, database, username y password. Para PostgreSQL añade también database-type: "POSTGRESQL" y usa el puerto 5432.
  3. Establece mysql.game-mode.current con el modo de juego de este servidor, por ejemplo survival. Los servidores que deban compartir facts necesitan el mismo valor.
  4. Establece mysql.enabled: true.
  5. Para una red, activa Redis: redis.enabled: true, con host, port y password, un redis.server-name único por servidor y el mismo redis.group-id en todos los servidores que van juntos.
  6. Inicia el servidor. El registro muestra "Database storage initialized". Los facts y archivos existentes se copian a la base de datos cuando todavía está vacía (consulta Migración).
  7. Repite en los demás servidores con su propio server-name.
Elige enabled a propósito

Con mysql.enabled: false, un servidor almacena todo en sus propios archivos. Eso está bien para un único servidor permanente, pero si su directorio se recrea a partir de una plantilla al iniciarse, los datos se escriben en una carpeta que no existirá la próxima vez, y nada te avisa. Con mysql.enabled: true, un servidor que no puede alcanzar la base de datos se apaga en lugar de atender a los jugadores con archivos locales.

Cómo funciona​

Cuando la extensión se inicia, sustituye el almacenamiento en archivos de TypeWriter por el almacenamiento en base de datos, antes de que cualquier otra extensión lea sus datos. TypeWriter mantiene los facts en memoria y escribe los cambios en la base de datos en pequeños lotes. Los demás servidores leen los cambios que aún no han visto consultando periódicamente la base de datos, y Redis les avisa de inmediato cuando algo cambia. Cuando un jugador se desconecta o es expulsado, sus facts pendientes se escriben al instante.

La base de datos solo almacena lo que cambió. Un fact eliminado se almacena como una fila con valor 0 (una lápida), de modo que los demás servidores se enteren de la eliminación en lugar de volver a guardar el valor antiguo. Las lápidas más antiguas que fact-tombstone-retention-days se purgan al iniciar.

Configuración​

El archivo es plugins/Typewriter/mysql/config.yml. La mayoría de las secciones son opcionales: cualquier clave ausente toma su valor por defecto.

Base de datos​

ClavePor defectoSignificado
mysql.enabledfalseUsar la base de datos. Cuando es true, el servidor se detiene si la base de datos es inalcanzable o un ajuste está incompleto.
mysql.database-typeMYSQLMYSQL o POSTGRESQL. No está en el archivo generado, añádela si hace falta.
mysql.host, mysql.portlocalhost, 3306Dirección de la base de datos. Usa 5432 para PostgreSQL.
mysql.databasetypewriterNombre de la base de datos.
mysql.username, mysql.passwordtypewriter, vacíoCredenciales.
mysql.use-sslfalseUsar SSL (require para PostgreSQL).
mysql.allow-public-key-retrievaltrueSolo MySQL.
mysql.table-prefixbtc_Prefijo de las tablas creadas por esta extensión. Los caracteres que no sean letras, dígitos o _ se convierten en _, y el prefijo se pasa a minúsculas.
mysql.migrate-from-filestrueCopiar los archivos de assets existentes a la base de datos cuando la tabla de assets está vacía.
mysql.fact-tombstone-retention-days30Días antes de que se purguen las filas de eliminación. Debe ser mayor que el tiempo que cualquier servidor permanece desconectado.
mysql.sync-interval-seconds30Se lee pero no se usa en esta versión: el intervalo de consulta lo establece el propio TypeWriter.
mysql.pool.*ver abajoPool de conexiones.
mysql.game-mode.currentdefaultModo de juego de este servidor.

Las claves del pool son maximum-pool-size (10), minimum-idle (2), connection-timeout (30000 ms), max-lifetime (1800000 ms), validation-timeout (5000 ms) y keepalive-time (0, desactivado).

Variables de entorno​

Cualquier valor que sea exactamente ${env:NAME} se lee de la variable de entorno NAME cuando se carga el archivo:

mysql:
password: ${env:BTC_TYPEWRITER_DB_PASSWORD}

Un valor numérico o booleano (por ejemplo, un puerto) se lee con el tipo correcto. Una variable sin definir o vacía detiene el servidor con un mensaje que nombra la variable. Solo se admiten valores completos, no texto que simplemente contenga ${env:...}.

Redis y Valkey​

ClavePor defectoSignificado
redis.enabledfalseConectarse a Redis o Valkey. Funciona incluso cuando mysql.enabled es false.
redis.typeredisredis o valkey. Ambos usan el mismo protocolo.
redis.host, redis.portlocalhost, 6379Dirección del servidor.
redis.username, redis.passwordvacíoCredenciales. El usuario default o uno vacío se trata como sin usuario.
redis.channeltypewriter:eventsCanal pub/sub compartido por todos los servidores.
redis.server-namesurvival en el código, server1 en el archivo generadoNombre único de este servidor. Es también el nombre del servidor en el proxy usado para las transferencias.
redis.group-idvacíoGrupo de red de este servidor. Letras, dígitos, ., _, -, hasta 64 caracteres. Vacío significa aislado.

El group-id delimita cada función que se comunica entre servidores: los mensajes sociales, los fantasmas y las listas de red solo viajan entre servidores con el mismo id de grupo no vacío. Los servidores con un grupo distinto no se ven entre sí.

Almacenamiento por extensión​

En mysql.extensions.<name>, cada entrada tiene tres claves:

  • enabled — si la extensión almacena sus propios datos en la base de datos.
  • profile-mode — shared (un solo conjunto de datos para todos los perfiles) o per-profile.
  • network-mode — shared (los mismos datos en todos los modos de juego) o isolated (un conjunto por modo de juego).

El archivo generado lista valores por defecto para profiles, cosmetics, party, battlepass, translate y otros. Estos ajustes los leen las extensiones que los soportan, por ejemplo Profiles. No afectan a los facts y artefactos propios de TypeWriter, que siempre van a la base de datos cuando mysql.enabled es true.

Los ajustes se resuelven en este orden: la entrada de config.yml y luego la fila de la tabla <prefix>game_mode_extensions para el modo de juego actual. Al iniciar, cada extensión cargada obtiene allí una fila, desactivada, a la espera de que un administrador la active. Los nombres de las claves se normalizan: TypeWriter-ProfilesExtension y profiles son lo mismo, y friends es un alias de party.

Sección profiles​

La sección profiles del mismo archivo configura la extensión 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 y creation-menu-entry-id. Consulta esa página para su significado.

Qué se almacena​

TablaContenido
<prefix>factsFacts de cada jugador y grupo: id de entrada, id de grupo, modo de juego, valor, última actualización.
<prefix>assetsArtefactos y otros assets (texto o binario), por ruta y modo de juego.
<prefix>game_modesModos de juego conocidos por la red.
<prefix>game_mode_extensionsReglas de almacenamiento por modo de juego y extensión.
<prefix>server_extensions, <prefix>extension_entry_filtersAjustes de sincronización por servidor y por entrada.
serversRegistro de servidores en ejecución. Su nombre no lleva prefijo.

Otras extensiones de BTC crean sus propias tablas en la misma base de datos, por ejemplo btc_guild_bank para el banco de Guild y typewriter_profiles para Profiles. Los nombres de tablas y columnas se crean automáticamente y las columnas que faltan se añaden al iniciar.

Cada fila de fact y de asset lleva un modo de juego. Un servidor solo lee y escribe las filas de su propio mysql.game-mode.current (y las filas marcadas como global), así que dos modos de juego nunca comparten facts ni artefactos, ni siquiera en la misma base de datos.

Sincronización entre servidores​

Datos. Sin Redis, los servidores igualmente convergen: cada uno consulta periódicamente la base de datos para ver los cambios hechos por los demás. Con Redis, un servidor que escribe facts publica un breve aviso, y los demás sincronizan de inmediato. Ejecuta todos los servidores en la misma zona horaria, porque las marcas de tiempo de los facts se almacenan en la hora local de cada servidor.

Mensajes sociales. Friend, Party y Guild publican sus cambios como mensajes JSON en el canal de Redis. Un servidor ignora sus propios mensajes. Solo se envían cuando Redis está conectado y redis.group-id no está vacío.

Presencia. Cada servidor escribe presence:<player name> en Redis para cada jugador conectado, con una caducidad de 120 segundos renovada cada 50 segundos y eliminada cuando el jugador se desconecta. Funciones como "teletransportarse a un amigo" la leen para encontrar el servidor del jugador.

Transferencias. Para mover a un jugador a otro servidor, la extensión se lo pide al proxy a través del canal de plugin estándar BungeeCord, y para traslados en todo el clúster a través del canal btc:bridge que entiende el proxy de BTC. El redis.server-name de cada servidor debe coincidir con el nombre de servidor que conoce el proxy.

Reconexión. Si Redis se cae, la publicación intenta reconectar, y el suscriptor reintenta hasta 10 veces con un retardo creciente (de 5 segundos hasta 5 minutos).

Migración​

La primera vez que se inicia un modo de juego con mysql.enabled: true:

  • Si la tabla de facts no contiene facts para este modo de juego, los facts que están ahora en memoria (de los archivos locales) se copian a la base de datos. En caso contrario, los facts se cargan desde la base de datos y se ignoran los locales.
  • Si mysql.migrate-from-files es true y la tabla de assets está vacía para este modo de juego, los archivos de plugins/Typewriter/assets/ se copian a ella.

Conserva una copia de seguridad de plugins/Typewriter/ antes del primer inicio. Volver a los archivos significa establecer mysql.enabled: false: el contenido de la base de datos no se copia de vuelta.

Panel de administración​

La extensión responde a las solicitudes de gestión del panel web de BTC: listar servidores y su estado (TPS, jugadores, memoria, extensiones instaladas, latencia de Redis, estadísticas del pool), crear y eliminar modos de juego, activar extensiones por modo de juego, exportar, importar y migrar los datos de un modo de juego, explorar y editar las tablas de almacenamiento, y leer los registros recientes de la base de datos. Cada solicitud comprueba un permiso de la persona que usa el panel y tiene un límite de frecuencia.

Permisos​

Estos permisos se comprueban para el usuario del panel web. No se registran con un valor por defecto, así que concédelos a los administradores.

PermisoConcede
typewriter.admin.server.listVer servidores, modos de juego, almacenamiento, tablas y extensiones descubiertas.
typewriter.admin.server.healthVer comprobaciones de estado y registros de la base de datos.
typewriter.admin.server.deregisterEliminar un servidor del registro.
typewriter.admin.gamemode.createCrear y eliminar modos de juego.
typewriter.admin.gamemode.configureCambiar ajustes de extensiones, filtros de entradas y sobrescrituras de almacenamiento, importar, exportar y migrar datos, editar filas de tablas.

Operaciones​

  • Recarga. Publicar config_update en el canal de Redis hace que todos los servidores recarguen mysql/config.yml y registren las extensiones nuevas. El funcionamiento normal lee el archivo una sola vez al iniciar.
  • Registro de servidores. La tabla servers se actualiza cada 30 segundos. Las entradas de más de 60 segundos se tratan como desconectadas y se eliminan al iniciar, y un servidor se elimina a sí mismo cuando la JVM se detiene.
  • Interbloqueos. Los lotes de facts se reintentan hasta 3 veces cuando MySQL informa de un interbloqueo.
  • Copias de seguridad. Haz copia de seguridad de las tablas <prefix>facts y <prefix>assets. Como una eliminación es una fila con valor 0, una copia restaurada más antigua que la retención de lápidas puede devolver facts eliminados.
  • Actualizaciones. Actualiza el JAR en todos los servidores en la misma ventana de reinicio. El esquema se actualiza en el sitio al iniciar.

Solución de problemas​

El servidor se detiene al iniciar con "Refusing to serve players on local file storage." mysql.enabled es true y la base de datos es inalcanzable o un valor está incompleto. Lee la línea anterior: el mensaje SEVERE nombra user@host:port/database y el error. Corrige las credenciales, la red o la base de datos, o establece mysql.enabled: false si este servidor debe usar archivos.

"Configuration incomplete: environment variable(s) X are referenced ... but are not set." Un valor ${env:X} no tiene una variable de entorno correspondiente y no vacía en el proceso del servidor. Defínela en la definición del servicio, no solo en tu shell.

Los facts no se comparten entre dos servidores. Comprueba que ambos tienen mysql.enabled: true, la misma base de datos y el mismo mysql.game-mode.current. Los modos de juego distintos almacenan facts separados. Sin Redis, un cambio puede tardar hasta el intervalo de consulta en aparecer.

Los amigos, parties, gremios o fantasmas no cruzan servidores. Redis debe estar activado y conectado, redis.group-id debe establecerse con el mismo valor no vacío en cada servidor, y redis.server-name debe ser único por servidor. Con un id de grupo vacío, no se envían mensajes sociales.

El registro muestra "Failed to connect to Redis". Comprueba el host, el puerto, el usuario y la contraseña. Una contraseña requirepass de Redis necesita redis.password. La extensión sigue funcionando sin Redis, usando solo consultas periódicas.

Una extensión no almacena sus datos en la base de datos. Las extensiones descubiertas al iniciar se registran desactivadas. Activa la extensión en mysql.extensions de config.yml, o en el panel de administración, y reinicia.

Los facts de un jugador eliminado o un fact eliminado vuelven. Un servidor estuvo desconectado más tiempo que fact-tombstone-retention-days y volvió a guardar datos antiguos. Aumenta la retención y evita restaurar copias antiguas de un servidor.

"HikariPool ... connection is not available" o tiempos de espera agotados. El pool está agotado. Aumenta mysql.pool.maximum-pool-size (y el propio límite de conexiones de la base de datos), y busca consultas lentas en el servidor de la base de datos.

Las marcas de tiempo parecen incorrectas o se pierden cambios. Los servidores que comparten una base de datos se ejecutan en zonas horarias distintas. Alinéalas.

Extensiones relacionadas​

  • Profiles — usa la sección profiles y las reglas de almacenamiento de perfiles y modos de juego.
  • Friend, Party, Guild — funciones sociales sincronizadas mediante Redis.
  • Ghost — muestra jugadores de otros servidores, usando la conexión de Redis.
  • Translate, Rewards, BattlePass — extensiones que dependen de esta.