Megapack: Cómo Factorio resolvió el problema del multijugador de 200 jugadores

Megapack: Cómo Factorio resolvió el problema del multijugador de 200 jugadores
En mayo de este año participé como jugador en Eventos MMO de KatherineOfSky. Noté que cuando el número de jugadores llega a cierto número, cada pocos minutos algunos de ellos "se caen". Por suerte para ti (pero no para mí), yo era uno de esos jugadores cada vezincluso con una buena conexión. Lo tomé como un desafío personal y comencé a buscar las causas del problema. Después de tres semanas de depuración, prueba y reparación, el error finalmente se solucionó, pero el viaje no ha sido tan fácil.

Los problemas en los juegos multijugador son muy difíciles de rastrear. Suelen ocurrir bajo parámetros de red muy específicos y bajo estados de juego muy específicos (en este caso, más de 200 jugadores). E incluso cuando un problema se puede reproducir, no se puede depurar correctamente porque la inserción de puntos de interrupción detiene el juego, estropea los temporizadores y, por lo general, hace que la conexión se agote debido a un tiempo de espera. Pero gracias a la perseverancia y a una maravillosa herramienta llamada torpe Pude averiguar qué está pasando.

En resumen, debido a un error y a una implementación incompleta de la simulación del estado de retraso, el cliente a veces se encontraba en una situación en la que tenía que enviar un paquete de red en un ciclo de reloj, que consistía en acciones de entrada del jugador para seleccionar aproximadamente 400 entidades del juego ( lo llamamos un "megapaquete"). Después de eso, el servidor no solo necesita recibir correctamente todas estas acciones de entrada, sino también enviarlas a todos los demás clientes. Si tienes 200 clientes, esto se convierte rápidamente en un problema. El canal al servidor se obstruye rápidamente, lo que provoca la pérdida de paquetes y una cascada de paquetes solicitados nuevamente. Posponer las acciones de entrada hace que más clientes comiencen a enviar megapaquetes, y su avalancha se vuelve aún más fuerte. Los clientes exitosos logran recuperarse, todo el resto se cae.

Megapack: Cómo Factorio resolvió el problema del multijugador de 200 jugadores
El problema era bastante fundamental y me tomó 2 semanas solucionarlo. Es bastante técnico, así que explicaré los jugosos detalles técnicos a continuación. Pero primero, debes saber que desde la versión 0.17.54, lanzada el 4 de junio, ante los problemas de conexión temporales, el modo multijugador se ha vuelto más estable y la ocultación de demoras tiene muchos menos errores (menos frenado y teletransportación). Además, cambié la forma en que se ocultan los retrasos en los combates y espero que esto los haga un poco más suaves.

Mega Pack multijugador - Detalles técnicos

En pocas palabras, el modo multijugador en un juego funciona así: todos los clientes simulan el estado del juego al recibir y enviar solo la entrada del jugador (llamadas "acciones de entrada" Acciones de entrada). La tarea principal del servidor es transferir Acciones de entrada y asegurando que todos los clientes realicen las mismas acciones en el mismo ciclo. Puedes leer más sobre esto en la publicación. FFF-149.

Dado que el servidor tiene que tomar decisiones sobre qué acciones tomar, las acciones del jugador se mueven a lo largo de la siguiente ruta: acción del jugador -> cliente del juego -> red -> servidor -> red -> cliente del juego. Esto significa que cada acción del jugador se realiza solo después de haber realizado un recorrido de ida y vuelta a través de la red. Debido a esto, el juego habría parecido terriblemente lento, por lo que casi inmediatamente después de la aparición del modo multijugador en el juego, se introdujo un mecanismo para ocultar los retrasos. La ocultación de latencia simula la entrada del jugador sin tener en cuenta las acciones de otros jugadores y la toma de decisiones del servidor.

Megapack: Cómo Factorio resolvió el problema del multijugador de 200 jugadores
Factorio tiene un estado de juego Estado del juego es el estado completo del mapa, el jugador, las entidades y todo lo demás. Se simula de forma determinista en todos los clientes en función de las acciones recibidas desde el servidor. El estado del juego es sagrado, y si alguna vez comienza a diferir del servidor o cualquier otro cliente, se produce una desincronización.

excepto Estado del juego tenemos un estado de retrasos Estado de latencia. Contiene un pequeño subconjunto del estado principal. Estado de latencia no es sagrado y solo representa una imagen de cómo se verá el estado del juego en el futuro según las entradas del jugador Acciones de entrada.

Para ello, guardamos una copia de los generados. Acciones de entrada en la cola de retraso.

Megapack: Cómo Factorio resolvió el problema del multijugador de 200 jugadores
Es decir, al final del proceso en el lado del cliente, la imagen se ve así:

  1. Aplicar Acciones de entrada todos los jugadores a Estado del juego la forma en que estas acciones de entrada se recibieron del servidor.
  2. Eliminar todo de la cola de retraso Acciones de entrada, que, según el servidor, ya se han aplicado a Estado del juego.
  3. Eliminar Estado de latencia y reinícielo para que se vea exactamente igual que Estado del juego.
  4. Aplicar todas las acciones de la cola de retraso a Estado de latencia.
  5. Basado en datos Estado del juego и Estado de latencia renderizar el juego al jugador.

Todo esto se repite en cada medida.

¿Demasiado difícil? No te relajes, eso no es todo. Para compensar las conexiones a Internet poco fiables, hemos creado dos mecanismos:

  • Marcas omitidas: cuando el servidor decide que Acciones de entrada se ejecutará en el tacto del juego, entonces si no ha recibido Acciones de entrada algún jugador (por ejemplo, debido a un mayor retraso), no esperará, pero le informará a este cliente "No tomé en cuenta tu Acciones de entrada, intentaré agregarlos en la siguiente barra. Esto se hace para que debido a problemas con la conexión (o con la computadora) de un jugador, la actualización del mapa no se ralentice para todos los demás. Cabe resaltar que Acciones de entrada no se ignoran, sino que simplemente se posponen.
  • Latencia de ida y vuelta completa: el servidor intenta adivinar cuál es la latencia de ida y vuelta entre el cliente y el servidor para cada cliente. Cada 5 segundos, negocia una nueva demora con el cliente según sea necesario (dependiendo de cómo se haya comportado la conexión en el pasado) y aumenta o disminuye la demora de ida y vuelta en consecuencia.

Por sí mismos, estos mecanismos son bastante simples, pero cuando se usan juntos (lo que sucede a menudo con problemas de conexión), la lógica del código se vuelve difícil de manejar y con muchos casos extremos. Además, cuando estos mecanismos entran en juego, el servidor y la cola de retraso deben implementar correctamente un especial Acción de entrada intitulado Detener movimiento en el siguiente tick. Gracias a esto, en caso de problemas de conexión, el personaje no correrá solo (por ejemplo, debajo de un tren).

Ahora necesito explicarte cómo funciona la selección de entidades. Uno de los tipos aprobados. Acción de entrada es un cambio en el estado de selección de una entidad. Les dice a todos sobre qué entidad pasó el mouse con el mouse. Como puedes ver, esta es una de las acciones de entrada más frecuentes que envían los clientes, por lo que para ahorrar ancho de banda, la hemos optimizado para que ocupe el menor espacio posible. Esto se implementa así: cuando se selecciona cada entidad, en lugar de almacenar coordenadas de mapa absolutas y de alta precisión, el juego almacena un desplazamiento relativo de baja precisión de la selección anterior. Esto funciona bien porque la selección del mouse generalmente ocurre muy cerca de la selección anterior. Esto da lugar a dos requisitos importantes: Acciones de entrada nunca debe omitirse y debe hacerse en el orden correcto. Estos requisitos se cumplen para Estado del juego. Pero dado que la tarea estado de latencia al "verse lo suficientemente bien" para el jugador, no están satisfechos en el estado de retraso. Estado de latencia no tiene en cuenta muchos casos límiteasociado con saltos de reloj y cambios en los retrasos de transmisión de ida y vuelta.

Ya puedes adivinar a dónde va esto. Finalmente, estamos empezando a ver las causas del problema del megapaquete. La raíz del problema es que la lógica de selección de entidades se basa en Estado de latencia, y este estado no siempre contiene la información correcta. Entonces el megapaquete se genera así:

  1. El reproductor está experimentando problemas de conexión.
  2. Entran en juego los mecanismos de salto de ciclos y regulación del retardo de transmisión de ida y vuelta.
  3. La cola de estado de retardo no tiene en cuenta estos mecanismos. Esto hace que algunas acciones se eliminen prematuramente o se ejecuten en el orden incorrecto, lo que da como resultado un error Estado de latencia.
  4. El reproductor no tiene problema de conexión y simula hasta 400 ciclos para ponerse al día con el servidor.
  5. En cada ciclo se genera y prepara una nueva acción para ser enviada al servidor, cambiando la selección de entidad.
  6. El cliente envía un megapaquete de más de 400 cambios de selección de entidades al servidor (y con otras acciones: estado de disparo, estado de marcha, etc. también sufrieron este problema).
  7. El servidor recibe 400 acciones de entrada. Dado que no se permite omitir una sola acción de entrada, indica a todos los clientes que realicen estas acciones y las envía a través de la red.

La ironía es que un mecanismo diseñado para conservar el ancho de banda resultó en enormes paquetes de red.

Resolvimos este problema solucionando todos los casos extremos de actualización y la compatibilidad con la cola de demora. Aunque tomó bastante tiempo, valió la pena hacerlo bien al final en lugar de depender de trucos rápidos.

Fuente: habr.com

Añadir un comentario