Pular para o conteúdo principal

MySQL

Armazenamento compartilhado MySQL ou PostgreSQL para fatos e artefatos, com sincronização Redis, presença e registro de servidores.

Privadasv0.0.1Utilitários
RequerTypeWriter0.9.0-beta-177 (BTC build)MySQL 8+ or PostgreSQLRedis or Valkeyopcional

Criada para a rede BornToCraft. Esta extensão é documentada como referência e não é distribuída.

Visão geral​

A extensão MySQL é a ponte que permite que vários servidores TypeWriter compartilhem os mesmos dados. Quando você a ativa, os facts (o progresso dos jogadores) e os artifacts (arquivos de dados criados por outras extensões) deixam de ser mantidos em arquivos em plugins/Typewriter/. Eles são armazenados em um banco de dados MySQL ou PostgreSQL que todos os servidores da sua rede leem e gravam.

Ela também é dona da conexão opcional com o Redis (ou Valkey). O Redis transporta notificações instantâneas de alterações, o registro de "em qual servidor este jogador está" e as mensagens usadas pelas extensões sociais (Friend, Party, Guild) e pelo Ghost. Várias outras extensões da BTC leem suas configurações, o modo de jogo e o nome do servidor a partir desta extensão.

Esta extensão não tem entries: você a configura com um arquivo e, opcionalmente, pelo painel web da BTC.

Recursos​

  • Armazenamento em banco de dados — facts e artifacts em MySQL 8 ou PostgreSQL, com criação automática de tabelas e um pool de conexões.
  • Seguro por padrão — quando o armazenamento compartilhado é solicitado e não pode ser alcançado, o servidor para em vez de continuar silenciosamente em arquivos locais.
  • Modos de jogo — dados separados por modo de jogo (sobrevivência, skyblock, minigames) no mesmo banco de dados.
  • Sincronização entre servidores — as alterações são captadas pelos outros servidores em segundos, instantaneamente com o Redis.
  • Redis e Valkey — mensageria pub/sub, presença de jogadores em toda a rede e transferências de jogadores pelo proxy.
  • Regras de armazenamento por extensão — escolha quais extensões usam o banco de dados e se seus dados são por perfil e compartilhados entre modos de jogo.
  • Migração na primeira inicialização — os facts e arquivos de assets existentes são copiados para o banco de dados quando ele está vazio.
  • Registro de servidores — cada servidor informa seu status (TPS, jogadores, memória, extensões) a cada 30 segundos.
  • Segredos a partir do ambiente — qualquer configuração pode ser lida de uma variável de ambiente.
  • Suporte ao painel de administração — saúde dos servidores, modos de jogo, exportação de dados e navegação pelas tabelas para o painel web da BTC.

Instalação​

  1. Crie um banco de dados vazio e um usuário para ele no MySQL 8 ou posterior, ou no PostgreSQL. O usuário precisa do direito de criar e alterar tabelas.
  2. Opcional, para vários servidores: execute um servidor Redis ou Valkey que todos os backends consigam alcançar.
  3. Coloque o JAR do MySQL em plugins/Typewriter/extensions/ e reinicie uma vez. O arquivo plugins/Typewriter/mysql/config.yml é criado.
  4. Edite o arquivo conforme descrito em Primeiros passos e reinicie. Os drivers de banco de dados, o pool de conexões e o cliente Redis acompanham a extensão.

Primeiros passos​

Mova um servidor para um banco de dados compartilhado:

  1. Pare o servidor e abra plugins/Typewriter/mysql/config.yml.
  2. Em mysql, preencha host, port, database, username e password. Para o PostgreSQL, adicione também database-type: "POSTGRESQL" e use a porta 5432.
  3. Defina mysql.game-mode.current com o modo de jogo deste servidor, por exemplo survival. Os servidores que devem compartilhar facts precisam do mesmo valor.
  4. Defina mysql.enabled: true.
  5. Para uma rede, ative o Redis: redis.enabled: true, com host, port e password, um redis.server-name único por servidor e o mesmo redis.group-id em todos os servidores que pertencem ao mesmo grupo.
  6. Inicie o servidor. O log mostra "Database storage initialized". Os facts e arquivos existentes são copiados para o banco de dados quando ele ainda está vazio (veja Migração).
  7. Repita nos outros servidores com seu próprio server-name.
Escolha enabled de propósito

Com mysql.enabled: false, um servidor armazena tudo em seus próprios arquivos. Isso é aceitável para um único servidor permanente, mas, se o diretório dele for recriado a partir de um template ao iniciar, os dados são gravados em uma pasta que não existirá da próxima vez, e nada avisa você. Com mysql.enabled: true, um servidor que não consegue alcançar o banco de dados é desligado em vez de atender os jogadores com arquivos locais.

Como funciona​

Quando a extensão inicia, ela substitui o armazenamento em arquivos do TypeWriter pelo armazenamento em banco de dados, antes que qualquer outra extensão leia seus dados. O TypeWriter mantém os facts na memória e grava as alterações no banco de dados em pequenos lotes. Os outros servidores leem as alterações que ainda não viram consultando o banco de dados periodicamente, e o Redis os avisa imediatamente quando algo muda. Quando um jogador sai ou é expulso, seus facts pendentes são gravados na hora.

O banco de dados armazena apenas o que mudou. Um fact excluído é armazenado como uma linha com valor 0 (um tombstone), para que os outros servidores saibam da exclusão em vez de salvar novamente o valor antigo. Os tombstones mais antigos que fact-tombstone-retention-days são removidos na inicialização.

Configuração​

O arquivo é plugins/Typewriter/mysql/config.yml. A maioria das seções é opcional: qualquer chave ausente assume seu padrão.

Banco de dados​

ChavePadrãoSignificado
mysql.enabledfalseUsar o banco de dados. Quando true, o servidor para se o banco de dados estiver inacessível ou uma configuração estiver incompleta.
mysql.database-typeMYSQLMYSQL ou POSTGRESQL. Não está no arquivo gerado; adicione-o se necessário.
mysql.host, mysql.portlocalhost, 3306Endereço do banco de dados. Use 5432 para o PostgreSQL.
mysql.databasetypewriterNome do banco de dados.
mysql.username, mysql.passwordtypewriter, vazioCredenciais.
mysql.use-sslfalseUsar SSL (require para o PostgreSQL).
mysql.allow-public-key-retrievaltrueSomente MySQL.
mysql.table-prefixbtc_Prefixo das tabelas criadas por esta extensão. Os caracteres que não sejam letras, dígitos e _ viram _, e o prefixo é convertido para minúsculas.
mysql.migrate-from-filestrueCopiar os arquivos de assets existentes para o banco de dados quando a tabela de assets está vazia.
mysql.fact-tombstone-retention-days30Dias antes de as linhas de exclusão serem removidas. Deve ser maior que o tempo que qualquer servidor fica offline.
mysql.sync-interval-seconds30Lido, mas não usado nesta versão: o intervalo de consulta é definido pelo próprio TypeWriter.
mysql.pool.*veja abaixoPool de conexões.
mysql.game-mode.currentdefaultModo de jogo deste servidor.

As chaves do pool são maximum-pool-size (10), minimum-idle (2), connection-timeout (30000 ms), max-lifetime (1800000 ms), validation-timeout (5000 ms) e keepalive-time (0, desativado).

Variáveis de ambiente​

Qualquer valor que seja exatamente ${env:NAME} é lido da variável de ambiente NAME quando o arquivo é carregado:

mysql:
password: ${env:BTC_TYPEWRITER_DB_PASSWORD}

Um valor numérico ou booleano (por exemplo, uma porta) é lido com o tipo correto. Uma variável não definida ou vazia interrompe o servidor com uma mensagem que indica o nome da variável. Apenas valores inteiros são suportados, não textos que apenas contenham ${env:...}.

Redis e Valkey​

ChavePadrãoSignificado
redis.enabledfalseConectar ao Redis ou Valkey. Funciona mesmo quando mysql.enabled é false.
redis.typeredisredis ou valkey. Ambos usam o mesmo protocolo.
redis.host, redis.portlocalhost, 6379Endereço do servidor.
redis.username, redis.passwordvazioCredenciais. O usuário default ou um vazio é tratado como sem usuário.
redis.channeltypewriter:eventsCanal pub/sub compartilhado por todos os servidores.
redis.server-namesurvival no código, server1 no arquivo geradoNome único deste servidor. Também é o nome do servidor no proxy usado nas transferências.
redis.group-idvazioGrupo de rede deste servidor. Letras, dígitos, ., _, -, até 64 caracteres. Vazio significa isolado.

O group-id delimita todo recurso que conversa entre servidores: as mensagens sociais, os ghosts e as listas da rede só trafegam entre servidores com o mesmo id de grupo não vazio. Os servidores de grupos diferentes não se enxergam.

Armazenamento por extensão​

Em mysql.extensions.<name>, cada entrada tem três chaves:

  • enabled — se a extensão armazena seus próprios dados no banco de dados.
  • profile-mode — shared (um único conjunto de dados para todos os perfis) ou per-profile.
  • network-mode — shared (os mesmos dados em todos os modos de jogo) ou isolated (um conjunto por modo de jogo).

O arquivo gerado lista padrões para profiles, cosmetics, party, battlepass, translate e outros. Essas configurações são lidas pelas extensões que as suportam, por exemplo o Profiles. Elas não afetam os facts e artifacts do próprio TypeWriter, que sempre vão para o banco de dados quando mysql.enabled é true.

As configurações são resolvidas nesta ordem: a entrada no config.yml e depois a linha da tabela <prefix>game_mode_extensions para o modo de jogo atual. Na inicialização, toda extensão carregada recebe ali uma linha, desativada, aguardando que um administrador a ative. Os nomes das chaves são normalizados: TypeWriter-ProfilesExtension e profiles são a mesma coisa, e friends é um alias de party.

Seção profiles​

A seção profiles do mesmo arquivo configura a extensão 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 e creation-menu-entry-id. Veja a página dela para o significado de cada um.

O que é armazenado​

TabelaConteúdo
<prefix>factsFacts de cada jogador e grupo: id da entry, id do grupo, modo de jogo, valor, última atualização.
<prefix>assetsArtifacts e outros assets (texto ou binário), por caminho e modo de jogo.
<prefix>game_modesModos de jogo conhecidos pela rede.
<prefix>game_mode_extensionsRegras de armazenamento por modo de jogo e extensão.
<prefix>server_extensions, <prefix>extension_entry_filtersConfigurações de sincronização por servidor e por entry.
serversRegistro dos servidores em execução. Seu nome não tem prefixo.

Outras extensões da BTC criam suas próprias tabelas no mesmo banco de dados, por exemplo btc_guild_bank para o banco do Guild e typewriter_profiles para o Profiles. Os nomes das tabelas e colunas são criados automaticamente e as colunas ausentes são adicionadas na inicialização.

Toda linha de fact e de asset carrega um modo de jogo. Um servidor só lê e grava as linhas do seu próprio mysql.game-mode.current (e as linhas marcadas como global), então dois modos de jogo nunca compartilham facts ou artifacts, mesmo no mesmo banco de dados.

Sincronização entre servidores​

Dados. Sem o Redis, os servidores ainda convergem: cada um consulta periodicamente o banco de dados em busca de alterações feitas pelos outros. Com o Redis, um servidor que grava facts publica um breve aviso, e os outros sincronizam imediatamente. Execute todos os servidores no mesmo fuso horário, pois os timestamps dos facts são armazenados no horário local de cada servidor.

Mensagens sociais. O Friend, o Party e o Guild publicam suas alterações como mensagens JSON no canal do Redis. Um servidor ignora suas próprias mensagens. Elas só são enviadas quando o Redis está conectado e redis.group-id não está vazio.

Presença. Cada servidor grava presence:<player name> no Redis para cada jogador online, com uma expiração de 120 segundos renovada a cada 50 segundos e removida quando o jogador sai. Recursos como "teletransportar para um amigo" a leem para encontrar o servidor do jogador.

Transferências. Para mover um jogador para outro servidor, a extensão pede ao proxy pelo canal de plugin padrão BungeeCord e, para movimentos em todo o cluster, pelo canal btc:bridge compreendido pelo proxy da BTC. O redis.server-name de cada servidor deve corresponder ao nome do servidor conhecido pelo proxy.

Reconexão. Se o Redis cair, a publicação tenta reconectar, e o assinante tenta até 10 vezes com um atraso crescente (de 5 segundos até 5 minutos).

Migração​

Na primeira vez que um modo de jogo é iniciado com mysql.enabled: true:

  • Se a tabela de facts não contém nenhum fact para este modo de jogo, os facts atualmente na memória (dos arquivos locais) são copiados para o banco de dados. Caso contrário, os facts são carregados do banco de dados e os locais são ignorados.
  • Se mysql.migrate-from-files for true e a tabela de assets estiver vazia para este modo de jogo, os arquivos de plugins/Typewriter/assets/ são copiados para ela.

Mantenha um backup de plugins/Typewriter/ antes da primeira inicialização. Voltar aos arquivos significa definir mysql.enabled: false: o conteúdo do banco de dados não é copiado de volta.

Painel de administração​

A extensão responde a solicitações de gerenciamento do painel web da BTC: listar servidores e sua saúde (TPS, jogadores, memória, extensões instaladas, latência do Redis, estatísticas do pool), criar e excluir modos de jogo, ativar extensões por modo de jogo, exportar, importar e migrar os dados de um modo de jogo, navegar e editar as tabelas de armazenamento e ler os logs recentes do banco de dados. Cada solicitação verifica uma permissão da pessoa que usa o painel e tem limite de taxa.

Permissões​

Estas permissões são verificadas para o usuário do painel web. Elas não são registradas com um padrão, então conceda-as aos administradores.

PermissãoConcede
typewriter.admin.server.listVer servidores, modos de jogo, armazenamento, tabelas e extensões descobertas.
typewriter.admin.server.healthVer verificações de saúde e logs do banco de dados.
typewriter.admin.server.deregisterRemover um servidor do registro.
typewriter.admin.gamemode.createCriar e excluir modos de jogo.
typewriter.admin.gamemode.configureAlterar configurações de extensões, filtros de entries e substituições de armazenamento, importar, exportar e migrar dados, editar linhas de tabelas.

Operações​

  • Recarga. Publicar config_update no canal do Redis faz todos os servidores recarregarem mysql/config.yml e registrarem novas extensões. A operação normal lê o arquivo uma única vez na inicialização.
  • Registro de servidores. A tabela servers é atualizada a cada 30 segundos. As entradas com mais de 60 segundos são tratadas como offline e removidas na inicialização, e um servidor se remove quando a JVM para.
  • Deadlocks. Os lotes de facts são repetidos até 3 vezes quando o MySQL relata um deadlock.
  • Backups. Faça backup das tabelas <prefix>facts e <prefix>assets. Como uma exclusão é uma linha com valor 0, um backup restaurado mais antigo que a retenção de tombstones pode trazer de volta facts excluídos.
  • Atualizações. Atualize o JAR em todos os servidores na mesma janela de reinicialização. O esquema é atualizado no local na inicialização.

Solução de problemas​

O servidor para na inicialização com "Refusing to serve players on local file storage." mysql.enabled é true e o banco de dados está inacessível ou um valor está incompleto. Leia a linha anterior: a mensagem SEVERE indica user@host:port/database e o erro. Corrija as credenciais, a rede ou o banco de dados, ou defina mysql.enabled: false se este servidor deve usar arquivos.

"Configuration incomplete: environment variable(s) X are referenced ... but are not set." Um valor ${env:X} não tem nenhuma variável de ambiente correspondente e não vazia no processo do servidor. Defina-a na definição do serviço, não apenas no seu shell.

Os facts não são compartilhados entre dois servidores. Verifique se ambos têm mysql.enabled: true, o mesmo banco de dados e o mesmo mysql.game-mode.current. Modos de jogo diferentes armazenam facts separados. Sem o Redis, uma alteração pode levar até o intervalo de consulta para aparecer.

Amigos, parties, guildas ou ghosts não atravessam servidores. O Redis deve estar ativado e conectado, redis.group-id deve estar definido com o mesmo valor não vazio em todos os servidores e redis.server-name deve ser único por servidor. Com um id de grupo vazio, as mensagens sociais não são enviadas.

O log mostra "Failed to connect to Redis". Verifique o host, a porta, o usuário e a senha. Uma senha requirepass do Redis precisa de redis.password. A extensão continua funcionando sem o Redis, usando apenas a consulta periódica.

Uma extensão não armazena seus dados no banco de dados. As extensões descobertas na inicialização são registradas como desativadas. Ative a extensão em mysql.extensions no config.yml, ou no painel de administração, e reinicie.

Facts de um jogador excluído ou um fact excluído voltam. Um servidor ficou offline por mais tempo que fact-tombstone-retention-days e salvou novamente dados antigos. Aumente a retenção e evite restaurar cópias antigas de um servidor.

"HikariPool ... connection is not available" ou timeouts. O pool está esgotado. Aumente mysql.pool.maximum-pool-size (e o limite de conexões do próprio banco de dados) e verifique consultas lentas no servidor de banco de dados.

Os timestamps parecem errados ou alterações são perdidas. Servidores que compartilham um banco de dados rodam em fusos horários diferentes. Alinhe-os.

Extensões relacionadas​

  • Profiles — usa a seção profiles, as regras de armazenamento de perfis e modos de jogo.
  • Friend, Party, Guild — recursos sociais sincronizados pelo Redis.
  • Ghost — mostra jogadores de outros servidores, usando a conexão Redis.
  • Translate, Rewards, BattlePass — extensões que dependem desta.