DataStore Robust: Save de Jogador com Retry e Segurança
Este prompt cria um Script de servidor Luau completo para persistência robusta de dados de jogadores no Roblox. A solução usa DataStoreService com UpdateAsync, tentativas automáticas com backoff exponencial, proteção contra dados inválidos, carregamento seguro, autosave escalonado e salvamento no encerramento do servidor.
Ideal para desenvolvedores que precisam salvar moedas, nível, XP, inventário serializável, configurações e progressão sem confiar no cliente. O prompt também solicita adaptação ao Explorer e aos RemoteEvents já existentes no projeto, mantendo o servidor como autoridade sobre qualquer dado persistente.
Atue como um desenvolvedor Roblox Luau sênior, especializado em sistemas de persistência seguros, escaláveis e resilientes usando DataStoreService. Gere um sistema completo de salvamento robusto dos dados de jogadores, adaptado ao contexto do meu jogo que fornecerei abaixo. O resultado principal deve ser um único **Script** de servidor, para ser colocado em **ServerScriptService** (por exemplo, `PlayerDataService.server.lua`). Não gere LocalScript para persistência e não permita que o cliente decida, envie ou sobrescreva diretamente moeda, XP, inventário, dano, nível ou qualquer dado salvo. O servidor deve ser inteiramente autoritativo. Caso meu contexto mencione RemoteEvents ou RemoteFunctions, trate-os apenas como canais de solicitação: valide rigorosamente jogador, tipo, limites, permissões e estado no servidor antes de alterar dados. Se não forem necessários para o salvamento, não crie remotes desnecessários. Antes de escrever o código, analise o contexto que vou preencher. Se alguma informação essencial estiver ausente, adote convenções seguras e deixe observações objetivas no começo da resposta, sem bloquear a entrega do script. Preserve a compatibilidade com objetos e nomes existentes quando eu os informar. ### CONTEXTO DO MEU JOGO (vou preencher) - Nome do jogo/sistema: - Objetos e caminhos relevantes no Explorer: - Estrutura de dados que deve ser salva (ex.: Coins, Gems, Level, XP, Inventory): - Onde esses valores existem durante a sessão (Attributes, leaderstats, Folder/ValueObjects, tabela etc.): - Valores padrão para jogador novo: - RemoteEvents/RemoteFunctions existentes e suas finalidades: - Regras de progressão/economia que o servidor deve respeitar: - Nome desejado para a chave/DataStore (opcional): - Restrições ou sistemas já existentes que não podem ser alterados: Implemente um sistema profissional usando `DataStoreService:GetDataStore()` e `UpdateAsync()` para minimizar risco de sobrescrita entre servidores. Use uma chave por jogador baseada em `Player.UserId`, com prefixo/versionamento configurável. Estruture os dados em uma tabela serializável, sem Instances, funções, userdata ou valores não aceitos por DataStore. Inclua `schemaVersion`, valores padrão e uma função de normalização/migração preparada para versões futuras. O script deve: carregar dados em `Players.PlayerAdded`; validar o retorno de `GetAsync`/`UpdateAsync`; diferenciar jogador novo de falha de carregamento; impedir alterações e salvamento inseguro quando a sessão não estiver marcada como carregada; manter cache de dados por UserId no servidor; aplicar os valores carregados aos objetos/Attributes indicados no meu contexto; e ler os valores atuais de volta para uma tabela sanitizada antes de salvar. Nunca aceite tabelas de inventário arbitrárias do cliente. Valide números com limites razoáveis, arredondamento quando aplicável, tipos de itens permitidos e tamanho máximo de coleções/strings para evitar dados corrompidos ou excessivos. Crie uma função reutilizável de salvamento com proteção contra chamadas concorrentes por jogador. Ela deve usar retry com quantidade configurável de tentativas, espera exponencial com pequeno jitter, `pcall`, mensagens de aviso úteis e retorno de sucesso/falha. Não faça loops agressivos nem chamadas desnecessárias ao DataStore. Inclua autosave periódico com intervalo configurável e distribuição gradual das operações para reduzir picos de orçamento. Salve também em `PlayerRemoving` e em `game:BindToClose()`, explicando em comentários que o desligamento não é garantia absoluta e que o autosave é essencial. Evite salvar jogador que não foi carregado com sucesso ou que já esteja sendo salvo, exceto quando uma estratégia segura de fila/pendência for necessária. Use APIs e sintaxe Luau atuais, `task.wait`, `task.spawn` quando fizer sentido e comentários em português explicando decisões importantes. Não use `wait()`, não use `DataStoreIncrementOptions` inexistente, não use APIs depreciadas e não invente propriedades de Roblox. Trate erros sem expor informações privadas ao cliente. Caso o carregamento falhe repetidamente, escolha uma política conservadora e documente-a no código, como impedir a entrada do jogador com uma mensagem clara ou manter sessão sem persistência, justificando a escolha. Entregue a resposta nesta ordem: 1) breve lista das premissas adotadas; 2) o código Luau completo, pronto para colar, obrigatoriamente dentro de um único bloco markdown ` ```lua `; 3) instruções práticas para instalar no Explorer, configurar os campos no topo do script e testar no Roblox Studio. Nas instruções de teste, inclua como publicar uma experiência de teste, ativar **Enable Studio Access to API Services** apenas em ambiente apropriado, testar entrada/saída de jogadores, simular falhas temporárias e verificar dados persistidos. Não entregue pseudocódigo, trechos incompletos, dependências externas nem código de cliente para salvar dados.