Ir al contenido principal

De "Big Data" y aplicaciones en tiempo real

Hoy en día, encontramos alta demanda en los sistemas de información empresarial... blah, blah, blah, ¡no!.

¿Quieren saber de aplicaciones en tiempo real con alta demanda? ¡Vídeo juegos!


Si Google se demora medio segundo cargando, yo se lo perdono. Pero si estoy jugando un juego de acción en tiempo real con decenas de miles de usuarios conectados en el mismo servidor (uno de varios servidores regionales), todos moviendo sus avatares, corriendo, saltando y disparando... enviando información al servidor al menos 30 veces por segundo... y yo disparo un arma, y pasan 100 milisegundos antes que le haga daño a mi objetivo... yo me estoy quejando.

Netflix puede que sepa hacer streaming, y puede que Google sepa MapReduce, pero son las empresas de vídeo juegos las que saben de aplicaciones en tiempo real. Para una empresa de vídeo juegos, un juego lento significa perder usuarios, y perder usuarios significa menos ventas.


El problema que enfrentan los vídeo juegos multijugador masivos en Internet, es que tienes miles de avatares corriendo y molestando... y el servidor tiene que enviarle actualizaciones de lo que hace todo el mundo a todo el mundo. Esto es O(N2) para los que me entienden.

Netflix no tiene ese problema, Google no tiene ese problema. Ni siquiera Facebook y Twitter tienen ese problema porque no le muestran todas las actualizaciones de todo el mundo a todo el mundo, y - aunque aveces lo pareciera - la gente no hace actualizaciones decenas de veces por segundo.


Lo que plantea la pregunta... ¿Cómo lo hacen? Bueno aquí les cuento...


Predicción

Usaré el termino host para referirme al responsable del estado del juego, que puede ser un servidor centralizado, o un cliente que ha sido elevando a esta posición.

Al diseñar el sistema lo primero que queremos es capacidad de respuesta. Como saben, no es bueno tener que esperar al host para cada entrada. Si escribes una letra, no debes tener que esperar a que el servidor confirme la acción para que aparezca en tu pantalla. En su lugar, enviaremos los la entrada al host y simularemos (o predeciremos) localmente mientras esperamos el host. Luego, cuando la respuesta del anfitrión, decidimos si tenemos que corregir la simulación.

Recordemos que hablamos de vídeo juegos, así que voy a poner ejemplos de vídeo juegos...

Tal vez hemos movido el avatar más allá de la posición que el host nos dice en su respuesta, pero lo que debemos verificar es si estuviéramos allí, con algún margen de error, cuando deberíamos haber estado allí. Eso significa que el cliente tendrá un registro de estados recientes para comparar con la respuesta del host.

Si otro jugador te ataca con un aturdimiento, ese jugador enviara eso al host. Tu cliente no es consciente de esto, pero el host sí lo está. Como resultado, la respuesta del anfitrión difiere de la simulación local, y tenemos que hacer una corrección. El cliente necesita volver a simular a partir de los datos recibidos del host. Y lo que se presenta al jugador será, por lo general, una interpolación de la simulación equivocada a la corregida, con el tiempo.

Podemos mejorar la calidad de la simulación si enviamos datos de entrada de jugador a jugador (por ejemplo con tecnologías de penetración de NAT - no, eso no es un eufemismo). Es decir, si enviamos datos de entrada de su cliente a los clientes de jugadores que están cerca de usted, podrán simular lo que hace su avatar en función de su información.

La clave aquí es enviar esta información solo para jugadores que estén cerca y puedan verlo de acuerdo con tu simulación. De esa manera, su cliente se ahorra ancho de banda al no enviar estos datos a todo el mundo.

En algunas situaciones, el cliente tendrá que esperar que el host simule algunos aspectos del juego. Eso sería en casos donde el cliente necesita permiso para realizar una acción (como prevención de trampa), o no puede predecir el resultado de la acción por sí mismo.

Notas

:
  • Habrá discrepancias entre el cliente y el host, lo que significa que el jugador apunta con su arma a una posición en la que el objetivo ya no se encuentra y, por lo tanto, un jugador de latencia alta prácticamente nunca podría atinar. Tiene dos soluciones: 1) Extrapolar en el cliente la posición futura del objetivo. 2) Enviar información semántica sobre lo que el jugador está apuntando al host. Sin embargo, en cualquier caso, le corresponde al host decidir si atina.
  • El host debe conocer la latencia y tenerla en cuenta en cualquier simulación que requiera un retraso. Es decir, debería compensar el retraso. La interpolación en el cliente no cubre todos los casos de compensación de retraso.
  • También hay predicción del lado del host. por ejemplo, si el jugador ha estado avanzando, y luego hay problemas de red y no obtenemos nada del jugador, el anfitrión puede suponer que el jugador sigue haciendo lo mismo.
  • No espere al servidor para las animaciones de anticipación. Deje que la animación suceda en el cliente tan pronto como sea posible después de la entrada del usuario. De hecho, oculte la latencia en las animaciones... en su lugar, espere para los efectos reales. Espere al host antes de reproducir la animación que notifica al jugador que el efecto se ha producido.

Administración de la capacidad del canal

La capacidad del canal es conocida de forma coloquial como ancho de banda, debido a esto, en adelante me referiré a la capacidad del canal como ancho de banda.

Enviar notificaciones de eventos sobre todo el mundo a todos no es escalable. Puede funcionar para un pequeño número de jugadores usuarios en una buena red, pero realmente no sabemos cuán buena es la red.

Por lo tanto, no queremos enviar notificaciones a todos. En cambio, dividiremos el mundo en áreas. Y cada cliente estará suscrito a una o más áreas, de las cuales recibirán notificaciones.

Para poder enviar las notificaciones a los usuarios que están desactualizados, el host deberá tener un registro de eventos recientes. Sin embargo, el host puede eliminar las entradas más antiguas para ahorrar memoria. En algún momento, el registro de eventos recientes será lo suficientemente largo como para que sea más barato enviar el estado actualizado de los objetos que enviar el registro. En ese punto, debería eliminar las entradas antiguas del registro para mantener los requisitos de memoria del host bajo control.

Ahora, dado un flujo de notificaciones sobre lo que sucede con los objetos en el mundo, el cliente debería poder calcular el estado del mundo.

Sin embargo, si el cliente no recibió una cantidad de notificación lo suficientemente grande (porque el cliente no estaba suscrito, porque la red falló, porque el host ha eliminado las entradas que debería enviar, o lo que sea) o si el cliente descargó el objeto de la memoria, el host debería poder enviar todo el estado. Esto puede ocurrir cuando el cliente se vuelve a conectar, se suscribe a un área nueva o heurísticamente a discreción del anfitrión (de hecho, algunos eventos pueden forzar una actualización completa del estado, por ejemplo, si tenemos que garantizar que dos avatares estén al alcance para ataques cuerpo a cuerpo).

Nota: no permita que las actualizaciones de estado completo coman el ancho de banda.


De la estructura del código de red

El host tendrá el estado completo del mundo y lo tendrá listo para ser enviado a los clientes.

Sin embargo, como se dijo anteriormente, el host no enviará todo. Por lo tanto, debemos decidir qué enviar y qué no, y cuál es la prioridad de las cosas que enviaremos.

Una vez que hemos decidido enviar algo a un cliente, debemos resolver si estamos enviando notificaciones de eventos o actualizaciones de estado completo.

Luego tenemos que limitar el consumo de la red. Esto es, no vamos a mandar todo lo que podemos esperando que se saturen la red... por el contrario, debemos vigilar la latencia de los clientes, y dosificar (a falta de una mejor palabra) la transferencia de datos. Esto también es útil para hacer pruebas, puesto que nos permite probar como se comporta el sistema simulando una conexión lenta.

Nota: Si, estoy diciendo que un registro de eventos no reemplaza una base de datos en todos los casos. Si bien hay dualidad entre lo uno y lo otro, al igual que los puentes y los arcos, a veces necesitas los unos y aveces necesitas los otros... puedes hacer puentes más eficientes si usas arcos para sostenerlos.

Y, por supuesto, tenemos que lidiar con cualquier otro aspecto de bajo nivel de cualquier solución de red que estemos usando.


Es necesario recordar que priorizamos qué enviar, si el cliente solo recibió algunas actualizaciones, deberían haber sido sobre las partes más importantes. las prioridades serán diferentes para cada juego aplicación, sin embargo, en general, deseas priorizar las actualizaciones sobre qué puede afectar al usuario y lo que ha hecho el usuario.

Mientras el cliente esté conectado, debería, eventualmente, recibir el estado más actualizado del mundo. Ya sea mediante notificaciones de eventos o como actualizaciones de estado completo. Y el cliente los interpolará.

Nota: no existe garantía de que los clientes tengan la posición intermedia, solo que eventualmente obtendrán la información más actualizada.


Ejemplo

Supongamos que un jugador a roto una ventana un usuario actualiza su estado.

El anfitrión enviará la notificación del evento a los usuarios que estén mirando. Y a partir de ahí, el cliente simulará el cristal rompiéndose se encarga de presentar el cambio al usuario.

Ok, ahora esto es interesante porque una red social tiene preocupaciones de privacidad que un juego no tiene...

Para un usuario que está viendo el estado, siempre y cuando mantener oculto el hecho que el usuario está conectado no sea un requerimiento, y si el sistema utiliza conexión entre clientes, este medio puede enviar una notificación temprana de que hay una actualización de estado. Esta notificación puede llegar antes que la notificación del host. De ser así, el cliente pude mostrar que hay una actualización de estado - aunque, por privacidad, no puede mostrar cual es - y luego cuando llega la actualización del host, la puede mostrar. Sin embargo - rara vez - la actualización del host no llega (hablamos de un problema de conexión, o de un usuario que "hace trampa"). En este caso, el cliente deberá corregir el estado retirando la notificación.

Pero ese es un usuario que está viendo el estado, si no está viendo el estado (está en otra parte de la aplicación, por ejemplo) el caso es distinto. Primero que todo, la conexión entre clientes no funciona en este caso... así que no existe el caso de falsa notificación. Segundo, la información que recibe el usuario depende de la prioridad de esta información. Es posible que esta notificación sea de baja prioridad, y que al "dosificar" la hagamos esperar para enviar otra información más prioritaria. Debido a esto, la notificación puede tardar en llegar.

Veamos el caso de un usuario con latencia alta... Esencialmente es el mismo caso que el anterior. Sin embargo, si la actualización es de baja prioridad, puede que esta notificación quede en espera indefinidamente, tal vez hasta que su prioridad aumente.

Por ultimo, veamos el caso del usuario que no está suscrito a esta área. Este usuario no recibirá la notificación, en su lugar, cuando entre al área recibirá una actualización de estado, y verá el estado ya cambiado.


Esto es basado en la presentación I Shot You First: Networking the Gameplay of HALO: REACH.

Comentarios