Pular para o conteúdo

Anticheat

O pipeline de violações descrito nesta página roda no servidor. Ele usa pacotes e respostas enviados pelo cliente, mas os confronta com o estado e os limites mantidos pelo servidor — por exemplo, um sync fora do envelope aceito, um tiro com arma que nunca foi concedida ou um pacote que chegou sem criptografia.

Ele vem ligado por padrão. set ac_enabled 0 desliga por completo.

Toda violação passa pelos mesmos quatro passos:

  1. Relato. Quem detectou (a thread de rede, o envelope de sync, a checagem de combate) relata a violação para um jogador. Relatos de uma família isenta são descartados aqui.
  2. Contagem. Cada par (jogador, violação) mantém um contador dentro de uma janela deslizante. Quando a janela expira, o contador reinicia.
  3. Decisão. Quando o contador atinge o limite da política, a ação dela dispara. O evento onPlayerAnticheatViolation é emitido para cada ocorrência contada.
  4. Punição. kick desconecta o jogador — a menos que um script tenha cancelado o evento dessa ocorrência, ou o modo sombra esteja ligado.

Depois que um kick dispara, a janela reinicia, para que uma punição já em andamento não dispare de novo no próximo relato da fila.

AçãoContaGrava na trilhaAparece no consoleDesconecta
ignorenãonãonãonão
logsimsimnãonão
warnsimsimsimnão
kicksimsimsimsim

As únicas ações aceitas são ignore, log, warn e kick. Banimentos persistentes devem ser implementados pelo próprio resource.

O evento Lua roda depois da contagem, da escrita na trilha e da mensagem de console. Portanto, cancelEvent() impede apenas o kick daquela ocorrência; ele não apaga o registro nem desfaz a contagem.

Os nomes são os que o ac policy, o ac_kick_on e o setAnticheatPolicy aceitam. Padrão é a ação quando nada a sobrescreve, e limite é quantas ocorrências dentro da janela são necessárias para disparar.

Posição, velocidade e pose de um jogador a pé. Política padrão: log, 30 em 10s.

ViolaçãoPadrãoLimiteJanelaDispara quando
movement-speedlog3010sA velocidade horizontal no chão, medida entre duas posições reportadas, passa do ac_player_max_speed. A amostra é recusada e o jogador é puxado de volta à última posição aceita.
movement-teleportlog3010sO salto entre duas posições passa do limite de teleporte.
movement-out-of-boundslog3010sPosição fora do ac_world_bound, ou fora da faixa de Z.
movement-not-finitelog3010sCoordenada NaN ou infinita.
movement-bad-statlog3010sVida ou colete fora da faixa aceita.
movement-not-ownerlog3010sO peer sincronizou um elemento que não é dele.
movement-velocitylog3010sVetor de velocidade cujo módulo passa do ac_max_velocity; o vetor informado é zerado.
movement-aimlog3010sPonto de mira longe demais do jogador.
movement-timingwarn260sPacotes de sync chegando bem mais rápido que a cadência de envio.
movement-flylog360sVoo sustentado sem veículo nem jetpack.
movement-climblog360sGanho vertical incompatível com a distância horizontal percorrida.

Tiros e dano. Política padrão: warn, 10 em 10s.

ViolaçãoPadrãoLimiteJanelaDispara quando
combat-weapon-not-grantedlog310sTiro com arma que o servidor nunca deu ao jogador.
combat-rangewarn1010sAlvo além do alcance da arma.
combat-ratewarn1010sCadência de tiro acima do que a arma permite.
combat-damagewarn1010sDano acima do que a arma consegue causar.
combat-no-ammolog510sTiro sem munição.
combat-targetwarn1010sAlvo inválido ou inalcançável.
combat-aim-snaplog1010sMira grudando no alvo em um único frame — a assinatura clássica de aimbot.

Com ac_validate_damage 1, o servidor também acumula o dano reportado por cada atacante em janelas de aproximadamente 1 segundo. Ao ultrapassar ac_damage_burst_cap (padrão 900.0), ele relata combat-damage; a política dessa violação continua determinando quando uma ação dispara.

Assentos e estado de veículo. Política padrão: warn, 10 em 10s.

ViolaçãoPadrãoLimiteJanelaDispara quando
vehicle-seat-claimwarn1010sReivindica um assento que não pode ocupar.
vehicle-seat-occupiedwarn1010sReivindica um assento que já tem alguém.
vehicle-unseated-claimwarn1010sSincroniza um veículo no qual não está sentado.
vehicle-enter-deniedwarn1010sComeçou a animação de entrada depois de o servidor negar.
vehicle-bad-statewarn1010sVida ou estado fora da faixa aceita.
vehicle-velocitywarn1010sVelocidade ou velocidade de giro cujo módulo passa do ac_max_velocity — o mesmo limite de envelope do lado a pé. O vetor informado é zerado.

Tráfego malformado. É a única família que expulsa já na primeira ocorrência por padrão, porque nenhum cliente legítimo produz isso.

ViolaçãoPadrãoLimiteJanelaDispara quando
protocol-plaintextkick110sPacote chegou sem criptografia depois do handshake.
protocol-state-gatewarn810sPacote enviado em um estado de conexão que não o aceita.
protocol-event-sourcewarn1010sO source informado no triggerServerEvent não é o jogador nem um filho dele. Depende do ac_strict_event_source.
protocol-oversizedkick110sPacote acima do limite de tamanho.
protocol-ratekick110sPacotes acima da taxa aceita.
protocol-malformedkick110sPacote que falha ao ser decodificado.
protocol-event-ratewarn310sEventos remotos disparados acima da taxa aceita.

Respostas ao desafio de integridade. Só dispara com ac_integrity 1.

ViolaçãoPadrãoLimiteJanelaDispara quando
integrity-world-statelog25minO digest do estado do mundo do cliente não bate com o do servidor.
integrity-weapon-statslog25minO digest da tabela de armas do cliente não bate com o do servidor.
integrity-no-reportlog35minO cliente não respondeu ao desafio a tempo.

Detecção de macro e autoclicker. O cliente mede o próprio teclado e envia os números; o servidor é quem decide se aquilo é macro e o que acontece com o jogador. Política padrão: kick, 3 em 60s — nunca no primeiro relato, porque um único achado em hardware real é indício, não prova.

ViolaçãoPadrãoLimiarJanelaDispara quando
input-macro-injectedkick260sO sistema operacional marcou as teclas como sintetizadas — SendInput/keybd_event, ou seja AutoHotkey, autoclickers e ferramentas de acesso remoto. Relatado após 12 dessas teclas em 10s.
input-macro-timingkick360sUma sequência de teclas numa mesma tecla uniforme demais para ser uma mão: 16 amostras cujo intervalo e tempo de pressão quase não variam.

O detalhe escrito na trilha carrega os números por trás do relato — quantas teclas, o intervalo médio e, no input-macro-timing, a variação em torno dele.

set ac_macro 0 põe a família em ignore: o cliente continua relatando e o servidor descarta o que chega.

As políticas escalonadas de gameplay e integridade começam em modo de observação. Há duas chaves opcionais para elevá-las durante a inicialização; violações de protocolo mantêm suas ações padrão, inclusive os kick imediatos da tabela acima:

ConvarPolíticas aplicadas quando ativada
ac_enforcemovement-fly, movement-climb e movement-teleport: kick em 4/60s; combat-weapon-not-granted: kick em 3/10s; vehicle-velocity: kick em 10/10s.
ac_integrity_enforceintegrity-world-state e integrity-weapon-stats: kick em 3/5min; integrity-no-report: kick em 2/5min.
ac_macroLigado por padrão. Colocar 0 põe input-macro-injected e input-macro-timing em ignore.

ac_integrity_enforce só tem efeito prático quando ac_integrity 1 também está ativo. ac_kick_on é aplicado depois desses modos e força as violações listadas a kick, preservando o limite atual; apesar do nome, o atalho não redefine o limite para 1.

set ac_shadow 1 — ou ac shadow on no console — mantém todo o fluxo rodando: as violações são contadas, gravadas na trilha e exibidas no console conforme a tabela de ações, e o onPlayerAnticheatViolation continua disparando. O modo sombra não muda qual ação dispara, então uma violação com ação log continua fora do console aqui também. Só a desconexão é suprimida, e a linha de kick vem marcada com [shadow].

É assim que se coloca uma política mais rígida no ar: liga o modo sombra, roda por alguns dias, lê a trilha e só então desliga.

Cada janela de contagem é anexada ao arquivo em ac_trail (padrão logs/anticheat.log) uma vez, no instante em que cruza o limite. Ocorrências posteriores na mesma janela não geram novas linhas. O formato é separado por tabs:

timestampMs elementId família violação contagemNaJanela contagemTotal ação detalhe
1754390112345 42 combat combat-aim-snap 10 37 log target 91 delta 178deg

O arquivo é aberto em modo append e sofre flush a cada linha, então sobrevive a um crash. ac_trail "" desativa.

Com ac_integrity 1, o servidor envia periodicamente um nonce para cada jogador. O cliente calcula o hash da tabela de armas ou do estado do mundo que tem carregado e devolve o digest; o servidor compara com o digest que ele calcula a partir do próprio estado.

Os dois tipos de desafio se alternam por jogador, então ac_integrity_interval 60 significa que cada tabela é verificada a cada 2 minutos. Um jogador que nunca responde levanta integrity-no-report.

Os desafios ficam retidos por um instante depois de qualquer mudança no mundo, para que um setGravity em trânsito não seja lido como divergência.

Um jogador pode ser dispensado de famílias inteiras — útil para um admin em noclip, ou um membro da equipe testando um veículo em velocidade impossível.

-- dispensa as famílias de gameplay: movement, combat e vehicle
setPlayerAnticheatEnabled(admin, false)
-- dispensa apenas o necessário
setPlayerAnticheatEnabled(admin, false, "movement,vehicle")
-- de volta ao normal
setPlayerAnticheatEnabled(admin, true)

A dispensa também cobre o envelope de sincronização: as amostras de um jogador isento nunca são recusadas, então o admin em noclip acima não é puxado de volta pelo ac_player_max_speed nem pelos limites de velocidade. Apenas movement-not-finite e movement-not-owner continuam recusando a amostra mesmo com isenção — nenhuma dispensa aceita coordenada NaN ou elemento roubado.

Nomes aceitos: movement, combat, vehicle, protocol, integrity, input, mais os atalhos gameplay (movement + combat + vehicle) e all.

Lido apenas na inicialização. Veja a referência das convars.

Terminal window
set ac_enabled 1
set ac_shadow 0
set ac_enforce 0
set ac_kick_on "protocol-plaintext,vehicle-seat-occupied"

O ac_kick_on é um atalho: ele muda a ação das violações listadas para kick, preservando o limite que já estiver configurado para cada uma.

Vale imediatamente e dura até o próximo restart. Veja ac.

Terminal window
ac status
ac violations Kalel
ac policy combat-aim-snap kick 5 10000
FunçãoO que faz
setAnticheatPolicyMuda a ação, o limite e a janela de uma violação.
getPlayerAnticheatViolationsLê a ficha de um jogador.
setPlayerAnticheatEnabledConcede ou remove uma isenção.
isPlayerAnticheatEnabledLê a isenção atual.

E o evento que dá a você a palavra final:

addEventHandler("onPlayerAnticheatViolation", root,
function(violacao, id, familia, contagem, detalhe)
if familia == "movement" and isPlayerStaff(source) then
cancelEvent() -- nenhuma punição para esta
end
end)