En los juegos multijugador de Roblox, los scripts anti-cheat en el cliente son ineficaces frente a inyectores de memoria modernos que pueden eludir o eliminar los LocalScripts.
Para mantener la integridad competitiva, es fundamental una arquitectura con autoridad en el servidor. Al calcular desplazamientos en delta-time, verificar colisiones con raycasts y aplicar rubberbanding, el juego permanece blindado ante trampas.
1. Por qué fallan los sistemas anti-cheat locales
Fundamentos de la manipulación de clientes en el entorno de Roblox:
- Compromiso del entorno local: Métodos como hookmetamethod permiten interceptar remotos.
- Destrucción de LocalScripts: El cliente puede congelar o borrar scripts locales a voluntad.
- Propiedad física de red: El cliente calcula la física de su propio HumanoidRootPart.
- Principio de Confianza Cero: Todo dato proveniente del cliente debe validarse en el servidor.
2. Verificación de velocidad y teletransporte en el servidor
Medición de distancias en delta-time sin falsos positivos por latencia:
- Cálculo de desplazamiento: Distancia euclidiana entre la posición previa y la actual.
- Límite dinámico de velocidad: Margen de tolerancia según WalkSpeed y jitter de red.
- Búfer de anomalías deslizante: Acumulación de discrepancias antes de sancionar.
- Rubberbanding: Devolución inmediata a la última coordenada válida.
--!strict
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local AntiCheatService = {}
type PlayerRecord = {
lastPosition: Vector3,
lastTick: number,
anomalyBuffer: number,
isFalling: boolean
}
local trackedPlayers: { [Player]: PlayerRecord } = {}
local RAY_PARAMS = RaycastParams.new()
RAY_PARAMS.FilterType = RaycastFilterType.Exclude
function AntiCheatService.InitPlayer(player: Player)
local char = player.Character or player.CharacterAdded:Wait()
local hrp = char:WaitForChild("HumanoidRootPart") :: BasePart
trackedPlayers[player] = {
lastPosition = hrp.Position,
lastTick = os.clock(),
anomalyBuffer = 0,
isFalling = false
}
end
function AntiCheatService.OnHeartbeat(dt: number)
local now = os.clock()
for player, record in pairs(trackedPlayers) do
local char = player.Character
if not char then continue end
local hrp = char:FindFirstChild("HumanoidRootPart") :: BasePart?
local humanoid = char:FindFirstChildOfClass("Humanoid")
if not hrp or not humanoid or humanoid.Health <= 0 then continue end
local currentPos = hrp.Position
local displacement = (currentPos - record.lastPosition).Magnitude
local maxAllowed = (humanoid.WalkSpeed * dt * 1.35) + 0.5
if displacement > maxAllowed and not record.isFalling then
record.anomalyBuffer += (displacement - maxAllowed)
if record.anomalyBuffer > 15 then
hrp.CFrame = CFrame.new(record.lastPosition)
hrp.AssemblyLinearVelocity = Vector3.zero
record.anomalyBuffer = 0
continue
end
else
record.anomalyBuffer = math.max(0, record.anomalyBuffer - (dt * 5))
end
RAY_PARAMS.FilterDescendantsInstances = { char }
local dir = currentPos - record.lastPosition
if dir.Magnitude > 0.1 then
local hit = Workspace:Raycast(record.lastPosition, dir, RAY_PARAMS)
if hit and hit.Instance and hit.Instance.CanCollide then
hrp.CFrame = CFrame.new(record.lastPosition)
hrp.AssemblyLinearVelocity = Vector3.zero
continue
end
end
record.lastPosition = currentPos
record.lastTick = now
end
end
RunService.Heartbeat:Connect(AntiCheatService.OnHeartbeat)
Players.PlayerAdded:Connect(AntiCheatService.InitPlayer)
return AntiCheatService
3. Prevención de noclip y traspaso de muros con raycasting
Evitar que los jugadores atraviesen paredes sólidas y zonas seguras:
- Comprobación por trayectoria: Lanzamiento de raycast entre coordenadas consecutivas.
- Cápsula de colisión multirrayo: Rayos a nivel de pies, torso y cabeza para evitar huecos.
- Filtrado dinámico: Exclusión de objetos decorativos sin colisión activa.
- Excepciones controladas: Notificación al servidor al usar teletransportes válidos.
4. Compensación de ping y absorción de paquetes
Ajuste de tolerancia para jugadores con conexiones inestables:
- Monitoreo de RTT: Adaptación dinámica de límites según el ping del jugador.
- Absorción de ráfagas: Gestión de paquetes de movimiento acumulados en móviles.
- Historial de rollback: Verificación retrospectiva de impactos.
- Avisos visuales: Mensajes de conexión en lugar de bloqueos injustificados.
5. Arquitectura de producción y rendimiento
Validación en servidores de alta concurrencia sin caídas de framerate:
- Comprobación escalonada: Distribución de jugadores en fotogramas pares e impares.
- Registro silencioso: Envío de alertas a webhooks sin alertar a los tramposos.
- Instantáneas de repetición: Guardado de los últimos segundos de movimiento sospechoso.
- Ajustes del motor: Uso de RejectCharacterDeletions y CollisionGroups estrictos.
Frequently Asked Questions
¿Por qué no sirve comprobar la velocidad en un LocalScript?
El usuario puede modificar la memoria del cliente o desactivar el script por completo.
¿Cómo se evitan falsos positivos por empujes o explosiones?
Las mecánicas de empuje del servidor aumentan temporalmente el umbral de movimiento permitido.
¿El raycasting en el servidor produce lag?
En servidores de 50 jugadores, 50 raycasts optimizados toman menos de 0.2 ms de CPU por fotograma.