Filtro de texto: o jogo sai do ar até você adicionar, e a responsabilidade é sua
A Roblox filtra o chat sozinha. Todo o resto do texto que o jogo mostra é responsabilidade de quem faz — e jogo detectado sem filtro é removido até o filtro existir.
Produzido com IA a partir das fontes citadas abaixo, e conferido por checagens automáticas contra essas mesmas fontes. Nenhuma pessoa leu este texto antes da publicação. Política Editorial

Existe uma frase na documentação de filtro de texto que muda o peso do assunto inteiro, e ela não está escrita como aviso: se a Roblox receber denúncias ou detectar automaticamente que o seu jogo não aplica filtro de texto, o sistema remove o jogo até você adicionar o filtro.
Não é advertência, não é redução de alcance. É o jogo fora do ar. Este guia cobre o que precisa ser filtrado, como se filtra, e os erros que deixam buraco.
O problema
O que o filtro faz: ele impede que o jogador veja linguagem imprópria e informação pessoal, como número de telefone. Os dois importam, e o segundo costuma ser esquecido — o filtro não existe só contra palavrão.
A divisão de responsabilidade é onde quase todo mundo erra, e ela cabe em duas frases. A Roblox filtra automaticamente as saídas comuns, como mensagem que passou pelo chat do jogo. E a responsabilidade por filtrar qualquer texto exibido que você não controla explicitamente é sua.
O engano é achar que "o Roblox filtra" vale para tudo. Vale para o chat. O resto — placa, nome de bicho, apelido, mural, qualquer lugar onde o jogador escreve e outra pessoa lê — é do jogo.
E a documentação lista quatro cenários, que juntos cobrem praticamente todo caso:
- Entrada direta do jogador: por
TextBox, por uma interface de teclado feita com botões, ou por um teclado interativo no espaço 3D. - Texto gerado: um jogo que gera palavras a partir de caracteres aleatórios e as mostra. É o cenário que ninguém prevê — há chance de criar palavra imprópria por acaso, e a chance não é pequena em escala.
- Conteúdo externo: um jogo que busca conteúdo de um servidor externo para exibir dentro do jogo. A documentação explica por que isso é perigoso mesmo sendo o seu servidor: é comum não se ter controle do conteúdo, e um terceiro pode editar a informação.
- Texto guardado: texto em
DataStore— nome de bicho é o exemplo dado — que pode conter palavra imprópria e precisa ser filtrado na leitura.
Esse último merece atenção porque ele é retroativo. Todo texto que os seus jogadores salvaram antes de você implementar o filtro continua lá, e vai aparecer na tela na próxima vez que alguém carregar aquele save.
Passo a passo
1. Colete no cliente, filtre no servidor. O exemplo oficial coleta a entrada no evento FocusLost e manda ao servidor por um RemoteEvent. No servidor, o texto passa primeiro pelo FilterStringAsync e depois pelo método de transmissão.
2. Chame o FilterStringAsync. Ele recebe o texto e o UserId de quem escreveu. O UserId não é formalidade: é ele que diz ao filtro de quem é o texto.
3. Escolha o método certo do resultado. A chamada devolve um objeto TextFilterResult, com dois métodos adicionais, e a escolha entre eles é sobre quem vai ler:
- Um deles é para texto visível a todos os jogadores do servidor — o caso da placa que todo mundo enxerga.
- O outro exibe o texto filtrado para um jogador específico, levando em conta idade e outros detalhes dele.
Essa diferença é real e não é cosmética. O filtro por usuário é mais restritivo ou mais permissivo dependendo de quem está lendo; o de transmissão precisa servir para todos ao mesmo tempo, e por isso aperta mais.
4. Filtre depois do envio, não durante. A documentação é explícita: não filtre em tempo real, a cada caractere digitado no TextBox. O motivo técnico é que isso produz texto visível só para quem está digitando. A orientação é filtrar depois que o jogador envia.
5. Trate a falha como ausência de texto. Este passo não está na documentação como passo, mas é o que decide se a implementação sobrevive a um dia ruim de rede. A próxima seção detalha.
Vale ainda entender por que os dois métodos existem em vez de um. O filtro não é uma lista fixa de palavras proibidas: ele é sensível a quem lê. O que um adulto vê e o que uma criança vê não são a mesma coisa, e a plataforma sabe a diferença. Quando o texto vai para uma pessoa só, dá para aplicar o critério dela; quando vai para o servidor inteiro, o filtro precisa servir para o leitor mais restrito que possa estar ali.
Daí sai uma consequência de desenho: texto público é mais filtrado que texto privado. Se a sua placa aparece censurada demais, a resposta não é trocar de método — é aceitar que texto que todo mundo lê tem um padrão mais apertado, e que isso é o comportamento pedido.
Artefato
O módulo abaixo expõe os dois métodos com nomes que dizem a decisão — paraTodos e paraUm — e traz os três usos no rodapé.
A regra que vale copiar mesmo que o resto não sirva está no comentário do meio: na falha, não mostre o original.
O filtro é uma chamada de rede, e chamada de rede falha. A tentação, ao tratar o erro, é cair para o texto que o jogador escreveu — afinal, é melhor mostrar alguma coisa. Essa linha transforma uma instabilidade momentânea em texto não filtrado na tela, que é exatamente a condição que tira o jogo do ar. Todas as funções do artefato devolvem nil na falha, e o chamador trata nil como "não tenho texto para mostrar".
No caso da placa, isso significa manter o texto anterior. Texto velho é melhor que texto cru.
Erro comum
Filtrar no cliente. O erro mais frequente e o mais inútil. O filtro roda no servidor porque o cliente não é confiável — um cliente modificado simplesmente não chama a função, e o texto chega ao servidor cru. Filtrar no cliente dá a sensação de proteção e não protege nada.
Filtrar a cada tecla. Contra a orientação explícita, e além disso custa uma chamada de rede por caractere. O FocusLost existe para isso.
Esquecer o texto guardado. É o buraco mais comum em jogo que já existe. Você implementa o filtro na entrada, testa, funciona — e todo o texto salvo antes continua saindo sem filtro na leitura. A correção é filtrar na leitura também, o que parece redundante e não é: o dado antigo nunca passou por filtro nenhum.
Confiar no próprio servidor externo. Se o jogo busca texto de uma API sua, é natural pensar que aquilo é conteúdo controlado. A documentação desmonta isso: um terceiro pode editar a informação. E mesmo sem terceiro, um erro seu na API vira texto não filtrado no jogo — sem filtro na borda, a sua API passa a ser parte da superfície de moderação do jogo.
Passar o UserId errado. O UserId é de quem escreveu, não de quem está lendo. No caso do nome de bicho lido de um DataStore, quem escreveu foi o dono do save, possivelmente há meses, e pode nem estar no servidor. Passar o UserId do leitor muda o que o filtro considera.
Não validar antes de filtrar. O filtro não é validação: ele não confere tamanho, não confere tipo, não limita frequência. Uma string de um milhão de caracteres chega ao FilterStringAsync e vira uma chamada de rede cara. Validar tipo, tamanho e taxa vem antes.
Um cuidado adicional vale para jogo que já está no ar: implementar o filtro não é o fim do trabalho, porque a superfície cresce sozinha. Todo recurso novo que aceite texto — um campo de apelido, uma caixa de mensagem, um sistema de clã com nomes — nasce sem filtro a menos que alguém lembre. A defesa que sobrevive é ter uma única porta por onde todo texto de jogador passa, como o módulo do artefato, em vez de repetir a chamada em cada lugar.
Próximo passo
O exercício concreto de hoje é uma varredura, e ela é curta: liste todo lugar do seu jogo onde um texto que veio de fora aparece na tela. Vale texto digitado, texto gerado, texto lido de banco e texto buscado na web.
Para cada item da lista, responda duas coisas: ele passa pelo filtro? E o filtro roda no servidor?
A lista costuma ser mais longa do que a memória sugere. O TextBox óbvio está lá, mas também o nome do time que alguém escolheu, o apelido no placar, o texto do mural, a descrição do item que o jogador nomeou. Cada um deles é um lugar onde o jogo mostra a uma pessoa algo que outra escreveu — que é a definição do que precisa ser filtrado.
E, se o jogo tem texto salvo de antes, vale programar a leitura filtrada como prioridade. Não há como saber o que está guardado até alguém carregar — e quando carregar, já está na tela.
Fontes
- Text filtering | Documentation - Roblox Creator Hub — consultada em
Artefato pronto pra usar
-- ServerScriptService: o único lugar do jogo que filtra texto.
--
-- A escolha entre os dois métodos NÃO é de gosto -- ela é sobre QUEM VAI
-- LER, e errar significa ou mostrar o que não devia, ou censurar o que
-- não precisava:
--
-- ParaTodos -> o texto aparece para o servidor inteiro. Placa,
-- nome de bicho, mensagem no mural.
-- ParaUm -> o texto aparece para UMA pessoa, e o filtro leva em
-- conta a idade e outros detalhes DELA.
local TextService = game:GetService("TextService")
local Filtro = {}
-- Passo comum: FilterStringAsync com o texto e o UserId de QUEM ESCREVEU.
-- O UserId não é enfeite: é ele que diz ao filtro de quem é o texto.
local function resultado(texto, deUserId)
local r
local ok, erro = pcall(function()
r = TextService:FilterStringAsync(texto, deUserId)
end)
if not ok then
warn("FilterStringAsync falhou: " .. tostring(erro))
return nil
end
return r
end
--[[
A REGRA QUE SALVA O JOGO: na falha, NÃO MOSTRE O ORIGINAL.
O filtro é uma chamada de rede e pode falhar. A tentação é "se falhou,
mostra o que o jogador escreveu" -- e essa linha transforma uma falha
de rede em texto não filtrado na tela, que é exatamente o que faz o
jogo ser removido. Todas as funções abaixo devolvem nil na falha, e o
chamador precisa tratar nil como "não tenho texto para mostrar".
]]
function Filtro.paraTodos(texto, deUserId)
if type(texto) ~= "string" or texto == "" then
return nil
end
local r = resultado(texto, deUserId)
if not r then
return nil
end
local ok, filtrado = pcall(function()
return r:GetNonChatStringForBroadcastAsync()
end)
return ok and filtrado or nil
end
function Filtro.paraUm(texto, deUserId, paraUserId)
if type(texto) ~= "string" or texto == "" then
return nil
end
local r = resultado(texto, deUserId)
if not r then
return nil
end
local ok, filtrado = pcall(function()
return r:GetNonChatStringForUserAsync(paraUserId)
end)
return ok and filtrado or nil
end
--[[
═══════════════════════════════════════════════════════════════════════
OS TRÊS USOS
═══════════════════════════════════════════════════════════════════════
-- 1. ENTRADA DE TEXTO. O cliente manda no FocusLost; o servidor filtra.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remote = ReplicatedStorage:WaitForChild("TextoDaPlaca")
local placa = workspace:WaitForChild("Placa").SurfaceGui.TextLabel
remote.OnServerEvent:Connect(function(player, texto)
-- validar ANTES de filtrar: string, tamanho, taxa
if type(texto) ~= "string" or #texto == 0 or #texto > 200 then return end
local limpo = Filtro.paraTodos(texto, player.UserId)
if limpo then
placa.Text = limpo
end
-- se limpo for nil, a placa NÃO muda. Melhor texto velho que texto cru.
end)
-- 2. TEXTO VINDO DO DATASTORE. Salvo antes da regra, ou salvo por uma
-- versão antiga do jogo: filtre na LEITURA, não confie no que está lá.
local function nomeDoBicho(player, nomeSalvo, donoUserId)
return Filtro.paraUm(nomeSalvo, donoUserId, player.UserId)
end
-- 3. CONTEÚDO DE SERVIDOR EXTERNO. Você não controla o que vem, e um
-- terceiro pode editar a fonte a qualquer momento. Filtre igual.
-- O UserId aqui é o de quem RECEBE, na falta de um autor conhecido.
local function textoExterno(player, vindoDaWeb)
return Filtro.paraUm(vindoDaWeb, player.UserId, player.UserId)
end
]]
return Filtro


