TechPastWeek logo
Desarrollo y herramientas15 min de lectura

Netcode determinista explicado: predicción, rollback y compensación de latencia

Los juegos en línea ocultan la latencia con predicción, simulación determinista, rollback y compensación de latencia. Un análisis técnico de sus compromisos.

El editor del motor Godot abierto en un proyecto 3D, mostrando el árbol de escena, el viewport y los paneles del inspector
En esta página

Dos jugadores pulsan el mismo botón en el mismo instante. En un sofá, el resultado es inmediato e idéntico. Por internet, esas entradas deben sobrevivir a un viaje de ida y vuelta a través de routers, congestión y física. La luz en fibra tarda decenas de milisegundos en cruzar un océano, y las rutas reales de internet añaden encolamiento y jitter por encima. Todo juego competitivo en línea es, por tanto, un ejercicio de engaño: hacer creer a los jugadores que sus acciones surten efecto al instante cuando la verdad aún está viajando por la red.

Lectura relacionada: Epic Games apuesta por la IA como base en su retrasado shooter de Disney · Google lanza Playground, una plataforma de IA que crea juegos jugables a partir de instrucciones.

Esta es la historia de los trucos que hay detrás de esa ilusión (predicción, simulación determinista, rollback y compensación de latencia), por qué existe cada uno, cuánto cuesta y por qué un juego de lucha, un juego de estrategia en tiempo real y un shooter resuelven el mismo problema de formas genuinamente distintas.

La restricción: la latencia es inevitable

Un juego en red tiene una autoridad (normalmente un servidor) que decide qué es real. La entrada de un jugador debe llegar a esa autoridad, procesarse y el estado resultante debe volver. Ese viaje de ida y vuelta es el ping, y nunca es cero.

A 60 fotogramas por segundo, un fotograma son 16,7 milisegundos. Un ping de 100 ms perfectamente normal abarca por tanto unos seis fotogramas entre pulsar un botón y ver responder al mundo. Para un juego por turnos eso es irrelevante. Para un juego de lucha, donde un solo fotograma decide si conecta un combo, o para un shooter de arena, donde un headshot es cuestión de grados, es todo el problema.

Los juegos lo atacan desde dos direcciones: predecir el futuro y corregir después, o retrasar la entrada del jugador local para que los jugadores remotos tengan tiempo de ponerse al día. Casi todo netcode real es una mezcla de ambas.

Predicción en el cliente y reconciliación del servidor

El modelo más simple (enviar la entrada, esperar al servidor, dibujar el resultado) se siente roto. El Quake original funcionaba así, y los jugadores notaban cada milisegundo.

La solución, popularizada por QuakeWorld y documentada por Gabriel Gambetta, consiste en dejar que el cliente ejecute localmente el mismo código de movimiento en el momento en que se pulsa una tecla, mientras el servidor sigue siendo autoritativo. Cliente y servidor solo necesitan coincidir estrechamente en el propio personaje del jugador, así que el cliente predice ese personaje de inmediato.

Cuando llega la actualización del servidor, no sobrescribe sin más el mundo del cliente. En su lugar dice, en la práctica: aquí está el estado autoritativo, y aquí está el último número de secuencia de entrada que he procesado. El cliente descarta las entradas que el servidor ya ha confirmado, se restablece al estado del servidor y luego reproduce las entradas que el servidor aún no ha visto.

# Cliente, cada tick fijo de simulación
inputs = sample_local_input()
send_to_server(tick, inputs)
buffer[tick] = inputs

state = simulate(state, inputs)          # actuar ahora, no esperar

# Cuando llega una actualización autoritativa...
on_server_update(server_state, acked_tick):
    state = server_state
    for t in range(acked_tick + 1, current_tick + 1):
        state = simulate(state, buffer[t])   # reproducir la entrada no confirmada

Esa reproducción es la esencia de la reconciliación del servidor. Mantiene al jugador receptivo y al servidor al mando. Las correcciones son raras (el mismo código de movimiento determinista sobre las mismas entradas suele coincidir) y se suavizan en lugar de imponerse de golpe.

La predicción en el cliente funciona mejor cuando un resultado depende sobre todo de la propia entrada del jugador local. No puede predecir, por ejemplo, que te dispare alguien a quien el jugador no puede ver; ese tipo de acontecimiento llega desde el servidor como una corrección.

Determinismo, aritmética de punto fijo y lockstep

Otra familia de juegos no predice en absoluto. Solo envía entradas y hace que cada máquina ejecute la misma simulación exacta. Si el estado inicial es idéntico y las entradas son idénticas, los resultados permanecen idénticos para siempre. Este es el modelo lockstep, descrito en el clásico artículo de Age of Empires "1500 Archers on a 28.8".

El lockstep se apoya en el determinismo: para un estado de juego dado y un conjunto de entradas dado, avanzar un tick debe producir el mismo resultado en todas las máquinas.

La aritmética de punto flotante (la predeterminada en la mayoría de motores) cumple IEEE-754 para operaciones básicas, pero no está garantizado que sea reproducible bit a bit entre distintas CPU, compiladores, ajustes de optimización o incluso compilaciones de depuración y de producción. El estudio de Glenn Fiedler sobre el determinismo en punto flotante recopila las historias de guerra: funciones trascendentales que difieren entre AMD e Intel, motores que fijan la precisión de la FPU y el modo de redondeo al arrancar, y proyectos que prohíben por completo el punto flotante en el gameplay.

La respuesta robusta es la aritmética de punto fijo: números almacenados como enteros escalados, como un formato Q16.16 con 16 bits para la parte entera y 16 para la fracción. El punto fijo tiene menos rango y puede ser más lento que los floats nativos, pero redondea de forma predecible y produce resultados idénticos en todas partes. Los generadores de números aleatorios también deben ser deterministas, con la misma semilla y avanzando al unísono para que cada tick obtenga las mismas tiradas. Los juegos intercambian habitualmente sumas de verificación del estado para detectar una desincronización en el momento en que empieza.

El determinismo no es gratis y es frágil. Cualquier dependencia oculta del orden de iteración, del threading o de un solucionador de física de terceros puede romperlo. Pero compra algo poderoso: como solo viajan las entradas, el ancho de banda se mantiene bajo incluso con miles de unidades.

Retardo de entrada: la solución honesta más simple

La forma más directa de lidiar con la latencia es esperar. El retardo de entrada muestrea la entrada del jugador local y la retiene unos fotogramas antes de simular, dando tiempo a que lleguen las entradas remotas. El retardo de entrada es trivialmente correcto y no necesita predicción, pero el retardo es real y el jugador lo nota.

GGPO, el SDK de rollback pionero, trata el retardo de entrada como un dial ajustable en lugar de un coste fijo. La guía para desarrolladores sugiere elegir el número de fotogramas retrasados según la conexión, y advierte de que un retardo demasiado corto produce rollbacks más largos y más fallos visibles. Retardo y predicción son complementarios, no rivales.

Por qué 'menos fotogramas retrasados' no siempre es mejor

Cada fotograma de retardo de entrada que eliminas hay que pagarlo en algún sitio, normalmente en rollbacks más largos cuando una predicción resulta errónea. El ajuste correcto depende de cuánta corrección puede ocultar un juego determinado.

Rollback: predecir al otro jugador

El netcode con rollback es predicción en el cliente aplicada a todos, no solo a uno mismo. Cada cliente simula de inmediato la entrada del jugador local y adivina la entrada del jugador remoto a partir de lo que este ha hecho recientemente. Ejecuta el fotograma al momento en lugar de esperar. Cuando por fin llega la entrada remota real, el cliente la compara con la conjetura. Si coinciden, no ocurre nada. Si difieren, el cliente rebobina hasta el último estado confirmado, sustituye la entrada correcta y vuelve a simular todos los fotogramas hacia delante hasta el presente, normalmente en una fracción de fotograma, mucho más rápido que en tiempo real.

La documentación de GGPO formula el objetivo con claridad: el diseño pretende dar a los jugadores la "ilusión de una red de latencia cero", de modo que el timing, los reflejos y la memoria muscular adquiridos offline se trasladen al juego online. El SDK se creó en 2009 y popularizó el rollback en los juegos de lucha; su autor, Tony Cannon, explicó el enfoque en la revista Game Developer.

El rollback impone duras exigencias de ingeniería a un motor:

  • La simulación del juego debe ser determinista y serializable, hay que poder guardar y restaurar el estado completo del juego.
  • Debe poder avanzar un fotograma sin renderizarlo, porque el rollback puede ejecutar muchos fotogramas ocultos a la vez.
  • La simulación debe estar separada de la presentación, para que los efectos y sonidos generados durante un rollback puedan diferirse hasta el fotograma visible.
  • Debe ejecutarse con un paso de tiempo fijo, para que una tasa de ticks variable no distorsione la repetición.

El proyecto Slippi de la comunidad de Melee es una demostración bien conocida: el rollback se adaptó a un juego de GameCube de 2001 que se ejecuta en un emulador, permitiendo a los jugadores competir a distancia sin el retardo de entrada que exigía el netplay basado en retardo.

El rollback no es invisible. Si una predicción era errónea, la corrección puede provocar un breve salto o "teletransporte" en pantalla. Los juegos lo ajustan con una pequeña cantidad de retardo de entrada y limitando hasta dónde especulan. En Overwatch, Timothy Ford, de Blizzard, describió cómo se predicen incluso los proyectiles para que los cohetes se sientan instantáneos, aceptando que un cohete mal predicho aparezca ocasionalmente desvaneciéndose.

Compensación de latencia: rebobinar el servidor

Los shooters suelen hacer autoritativo al servidor y se niegan a confiar en los clientes, lo cual es bueno para la seguridad pero crea un nuevo problema. Para suavizar el jitter, los clientes renderizan a los demás jugadores ligeramente en el pasado. Cuando apuntas y disparas a lo que ves, estás apuntando a donde estaba el mundo del servidor.

La compensación de latencia resuelve esto haciendo que el servidor rebobine el mundo hasta el momento en que se generó la entrada del tirador. Almacena un breve historial de la posición y las hitboxes de cada jugador y, al producirse un disparo, devuelve a los demás jugadores a donde los vio el cliente del tirador y comprueba allí el impacto. Por eso a la compensación de latencia se la llama a menudo rebobinado del lado del servidor.

El motor Source de Valve popularizó la técnica, y su documentación y su código fuente muestran cómo el servidor reconstruye un tick anterior dentro de una ventana acotada. Riot describe el mismo diseño para VALORANT: «Cuando el servidor oye que has disparado, necesita rebobinar el estado del mundo a lo que estabas mirando cuando apretaste el gatillo para determinar con precisión si tu disparo dio en el blanco», explicó la compañía.

El efecto secundario es real y muy discutido. Rebobinar favorece al tirador, lo que configura una asimetría común a todo el género conocida como ventaja del que asoma: un atacante que dobla una esquina puede ver y disparar a un defensor antes de que al cliente del defensor se le haya dicho que el atacante se movió. Como la visión que el defensor tiene del atacante está a su vez retrasada, el jugador agresivo obtiene una ventana de tiempo de reacción extra. Los juegos acotan la ventana de rebobinado para evitar que un jugador con un ping muy alto mate a alguien que ya se ha puesto a cubierto, y ajustan la tasa de ticks y el enrutamiento para reducir la ventana en lugar de eliminarla.

El lockstep determinista solo envía entradas y confía en que todas las máquinas lleguen a la misma respuesta. Los shooters con servidor autoritativo no confían en nadie y lo pagan con un mundo rebobinado.

Tasa de ticks e interpolación de instantáneas

La tasa de ticks de un servidor es cuántas veces por segundo avanza la simulación. Un servidor de 64 ticks simula cada 15,6 ms; uno de 128 ticks, cada 7,8 ms. Tasas más altas hacen que el registro de impactos y el movimiento sean más frescos, pero el coste es lineal: los ingenieros de Riot escribieron que ejecutar un fotograma de 128 ticks en 7,8 ms consumía un núcleo de CPU entero por partida, lo que les obligó a optimizar hasta un presupuesto de unos pocos milisegundos para que varios juegos pudieran compartir un host.

Los clientes rara vez simulan todo el mundo por sí mismos. Reciben instantáneas del estado del servidor y renderizan una aproximación fluida. La interpolación de instantáneas, tal como la describe Fiedler, almacena en búfer las instantáneas entrantes durante un breve retardo e interpola entre las dos más recientes, cambiando un poco de latencia añadida por fluidez. El búfer debe ser lo bastante profundo para que perder uno o dos paquetes no cause un tirón; la regla empírica de Fiedler es aproximadamente el triple del intervalo entre paquetes, y luego se recorta con una tasa de envío más alta. La extrapolación (adivinar por adelantado cuando una instantánea llega tarde) se usa con moderación, porque la física y las colisiones hacen que las conjeturas fallen rápido.

La misma tensión aparece en los shooters como command buffering. El servidor mantiene una pequeña cola de los movimientos de un cliente para poder reproducirlos con fluidez en lugar de predecir en nombre del cliente. Riot documentó el equilibrio: con poco búfer, el servidor predice movimientos y emite correcciones; con demasiado, el juego se siente como un ping alto. La simulación se ejecuta con un paso de tiempo fijo desacoplado de la tasa de fotogramas de renderizado, de modo que un cliente a 30 fps y un servidor a 128 Hz avanzan igualmente el mismo reloj.

P2P, servidores autoritativos y por qué los géneros divergen

La topología que elige un juego da forma a todo lo demás.

  • Lockstep entre pares (RTS clásico): cada jugador envía comandos a todos los demás, y cada máquina simula el mismo mundo. No necesita servidores dedicados y usa muy poco ancho de banda, pero avanza solo tan rápido como el jugador más lento, requiere conexiones directas que los cortafuegos suelen bloquear y entrega a cada cliente el estado completo del juego, lo que facilita hacer trampas leyendo información que el jugador no debería ver.
  • Rollback entre pares (juegos de lucha): dos jugadores intercambian entradas y cada uno simula ambos personajes localmente. Un estado pequeño y totalmente visible hace que el determinismo y la serialización sean manejables, y las filtraciones de información importan menos cuando toda la partida ya está en pantalla.
  • Cliente-servidor autoritativo (la mayoría de los shooters): un servidor posee la verdad; los clientes predicen solo sus propias acciones y renderizan a todos los demás a partir de las instantáneas del servidor. Resiste las trampas, absorbe hardware dispar y escala a muchos jugadores, a costa de servidores, ancho de banda e infraestructura.

El dilema de seguridad

La autoridad del servidor es la posición anti-cheat más sólida: un cliente hackeado puede mentirse a sí mismo, pero el mundo del servidor es lo que experimentan los demás jugadores. Por eso los shooters competitivos centralizan la autoridad y están dispuestos a pagarla con latencia.

Pero la compensación de latencia crea su propia superficie de ataque. Como el servidor confía en la visión declarada por el cliente para el registro de impactos, un jugador puede inducir o falsificar deliberadamente la latencia para ampliar su ventaja del que asoma, una práctica conocida como lag switching o abuso de lag. Los límites de la ventana de rebobinado y de cuánto se puede rebobinar la posición de un jugador son la defensa estándar, junto con tratar las conexiones asimétricas como sospechosas.

El lockstep determinista tiene una historia de seguridad más sutil. Como todos los clientes deben llegar al mismo resultado, cualquier divergencia puede detectarse, y una simulación manipulada se descubre cuando su suma de verificación no coincide. Eso hace difícil hackear las reglas. No hace nada por detener las trampas de información, porque el estado completo del juego (incluidas las posiciones de los enemigos) ya está en el cliente.

Advertencias y problemas abiertos

El rollback y la predicción no son disolventes universales. Exigen una serialización de estado barata y una simulación capaz de ejecutar muchos fotogramas más rápido que en tiempo real; una simulación de física pesada con un solucionador lento puede sencillamente ser incapaz de re-simular con la suficiente rapidez. El determinismo es fácil de romper por accidente y caro de mantener entre plataformas y compiladores. La compensación de latencia cambia justicia por sensación, y siempre favorece a quien dispara.

También hay un problema de medición. Los jugadores culpan al "lag" de los tirones causados por su propia tasa de fotogramas, por el servidor o por el jitter, y estos tienen soluciones distintas. Los equipos instrumentan cada vez más todo ello (el timing de los ticks, la profundidad del búfer, el tiempo de ida y vuelta y el jitter) y lo exponen a los jugadores para que el diagnóstico no sea una adivinanza.

Qué observar

Es probable que tres tendencias sigan dando forma a este campo. Primera, más juegos mezclan modelos: simulaciones con servidor autoritativo con técnicas de rollback dentro de ellas, en lugar de elegir una sola filosofía. Segunda, el soporte de los motores está madurando, con bibliotecas deterministas de aritmética de punto fijo y herramientas de rollback que llegan a equipos más pequeños que no pueden construir netcode desde cero. Tercera, se ataca la propia latencia en la capa de infraestructura, mediante enrutamiento privado y ubicación regional de servidores, porque el milisegundo más barato es el que nunca hay que ocultar.

El hilo conductor es que un buen netcode es un conjunto de compromisos deliberados, no un único algoritmo. La predicción compra capacidad de respuesta arriesgando correcciones. El determinismo compra un ancho de banda minúsculo exigiendo perfección. La compensación de latencia compra disparos que se sienten justos rebobinando el tiempo. Entender qué coste está dispuesto a pagar un juego es la vía más rápida para entender por qué se siente como se siente online.

Etiquetas

Share

¿Te resultó útil? Compártelo con tu equipo.

TPW Editorial

TPW Editorial

Redacción

El equipo editorial de TechPastWeek investiga y edita cada artículo que publicamos. Cada historia se contrasta con fuentes primarias y secundarias fiables, y un editor la revisa antes de publicarse.

Destacados y más leídos

Sigue leyendo