Anti-Duplicação DataStore Multi-Servidor Roblox
Ideal para jogos com economia, trading, inventário, pets, gacha, progressão ou qualquer sistema persistente de alto valor. Basta colar o contexto do seu Explorer, seus RemoteEvents e o formato atual dos dados para receber scripts completos, comentados e prontos para adaptar no Roblox Studio.
Atue como um desenvolvedor Roblox sênior, especialista em Luau, DataStoreService, MemoryStoreService e sistemas distribuídos de persistência. Crie uma implementação pronta para produção que previna duplicação e sobrescrita de dados quando o mesmo UserId tenta jogar em múltiplos servidores, reconecta em alta velocidade ou entra durante um encerramento inesperado. Antes de gerar o código, considere o contexto abaixo. Se algum item estiver vazio, adote nomes seguros e declare claramente suas premissas sem bloquear a solução: --- CONTEXTO DO MEU JOGO (PREENCHER) --- - Nome do DataStore principal: - Estrutura dos dados do jogador (moedas, inventário, pets, progresso etc.): - Objetos e pastas existentes no Explorer, com caminhos completos: - RemoteEvents/RemoteFunctions existentes e respectivos caminhos: - Sistema atual que concede moedas, itens ou altera inventário: - Existe sistema de trade, drop, gacha, compra, teleporte ou gift? Descreva: - Já uso ProfileService, DataStore2 ou alguma biblioteca externa?: - Intervalo de autosave desejado: - Política desejada se uma sessão antiga estiver travada (recusar entrada, aguardar ou expulsar): - Versão do schema atual, se existir: --- FIM DO CONTEXTO --- Projete uma solução nativa, sem depender de bibliotecas externas, usando DataStoreService e MemoryStoreService. Gere exatamente dois arquivos completos: (1) um ModuleScript chamado `PlayerDataSessionService`, colocado em `ServerScriptService/Modules/PlayerDataSessionService`; e (2) um Script chamado `PlayerDataBootstrap`, colocado em `ServerScriptService/PlayerDataBootstrap`. Se os caminhos informados no contexto forem diferentes, adapte-os e explique a alteração. O ModuleScript deve expor uma API segura para obter dados em modo somente leitura, alterar dados exclusivamente no servidor por meio de uma função de mutação controlada, salvar, encerrar uma sessão e consultar o estado de carregamento. O Script bootstrap deve conectar Players.PlayerAdded, Players.PlayerRemoving e game:BindToClose. A implementação deve impedir que dois servidores tenham autorização para gravar os dados do mesmo jogador ao mesmo tempo. Use uma estratégia de lock distribuído por UserId no MemoryStoreService, com identificador único do servidor/sessão baseado em `game.JobId` (inclua fallback seguro para Studio), TTL/lease configurável, renovação periódica do lease e liberação explícita ao sair. O lock deve ser adquirido antes de carregar ou permitir o jogador no jogo. Caso o lock já pertença a outro servidor ativo, não carregue os dados em paralelo: implemente retentativas com backoff e limite configurável; ao exceder o limite, expulse o jogador com mensagem amigável e não revele detalhes internos. Use DataStoreService:UpdateAsync para toda escrita persistente e nunca use SetAsync para substituir o registro inteiro. Salve um envelope de dados contendo pelo menos `Data`, `SchemaVersion`, `LastSave`, `SessionId` e metadados necessários para detectar proprietário da sessão. Inclua função de normalização/migração de schema e dados padrão profundos, evitando referências compartilhadas entre jogadores. Explique no código que o MemoryStore é a autoridade operacional do lock enquanto o DataStore mantém uma marca de auditoria e recuperação; não trate DataStore como lock instantâneo. Proteja todas as chamadas de DataStore e MemoryStore com `pcall`, aplique retentativas controladas para erros transitórios e nunca apague ou sobrescreva dados válidos em caso de falha. Implemente cache em memória apenas no servidor proprietário da sessão. O autosave deve ocorrer em intervalo configurável, somente para perfis marcados como dirty e somente enquanto o lease estiver válido. Antes de cada gravação crítica e antes do autosave, confirme que a sessão ainda possui o lock; se perder a propriedade, bloqueie novas mutações, registre um warn detalhado no servidor e remova/expulse o jogador de forma segura para evitar duplicação. Durante PlayerRemoving e BindToClose, pare mutações, tente salvar dentro de um orçamento de tempo, libere o lock apenas depois da tentativa de save e trate o fato de BindToClose ter tempo limitado. Segurança obrigatória: o cliente nunca pode decidir moedas, dano, inventário, preço, quantidade, recompensa ou conteúdo a salvar. Caso eu tenha listado RemoteEvents, mostre um exemplo de handler estritamente server-side que valida tipos, limites, estado do jogador, ownership e cooldown antes de chamar a API de mutação. Não aceite tabelas de inventário enviadas pelo cliente como fonte da verdade. Não exponha referências mutáveis do cache ao cliente nem a outros scripts sem uma cópia segura. Inclua validação de número finito, limites de saldo/quantidade e proteção contra chamadas enquanto o perfil não estiver carregado ou estiver em encerramento. Entregue primeiro uma seção curta chamada `Arquitetura e premissas`, seguida de uma árvore do Explorer com os dois arquivos e dependências. Depois forneça os dois arquivos integralmente, cada um em seu próprio bloco markdown ` ```lua `, sem pseudocódigo, sem trechos omitidos e com comentários claros em português. O código deve ser Luau compatível com Roblox Studio, usar `--!strict` quando viável e declarar constantes fáceis de configurar. Ao final, inclua uma seção `Como testar no Roblox Studio` com passos para habilitar API Services, simular dois servidores via publicação/teleporte ou teste apropriado, testar reconexão rápida, lock ativo, expiração de lease, falha de DataStore e desligamento. Inclua também uma lista objetiva de limitações reais e recomendações de produção, sem prometer garantia absoluta contra todos os cenários distribuídos.