¿Con qué frecuencia actualizar un Game Client sobre el mundo?

10

Usando socket.io , tengo una comunicación similar a la de otros MMORPG, una conexión constante con los mensajes.

En mi diseño hasta ahora, el cliente envía la posición del jugador y el cuadro de animación con cada cuadro de actualización. Cuando el servidor recibe ese mensaje, lo transmite a todos los clientes, que luego moverán el gráfico en consecuencia.

¿Sería una mejor idea 'recolectarlos' y transmitirlos, digamos, una vez en 1/10 de segundo?

Además, ¿debe el cliente enviar muchos mensajes diferentes (experiencia obtenida, hacer clic en el elemento) tan pronto como ocurran o, mejor dicho, solo uno recogido? El primero se implementaría más fácilmente.

Lanbo
fuente

Respuestas:

16

Voy a abordar esto desde una discusión de alto nivel y luego trabajaré hacia sus preguntas. En aras de la divulgación, no tengo experiencia personal en el uso de socket.io, pero sí mucha exposición al espacio del problema con respecto a los MMORPG.

El diseño de la arquitectura de red de un motor MMORPG y / o la selección de un proyecto de código abierto o de middleware para proporcionar la funcionalidad es una de las decisiones más desafiantes influenciadas por el diseño del juego, el presupuesto y la experiencia técnica del equipo. La elección final influirá en otras decisiones arquitectónicas (y a veces también en decisiones de diseño).

Como desarrolladores de MMORPG, planeamos un gran éxito (a menudo también conocido como éxito catastrófico) donde grandes números activan luces de advertencia y sirenas. Uno de los grandes números que surgen es en algoritmos que son N al cuadrado (N ^ 2 en adelante), en su pregunta, lo primero que me llamó la atención es que sonaba como si el diseño exigiera que una entidad transmitiera información a Todas las demás entidades conectadas. Este es el ejemplo clásico de un problema N ^ 2.

Los MMO generalmente se enfocan en abordar los problemas de N ^ 2 atacando el problema de varias maneras diferentes; sistemas de conciencia (situacionales, espaciales, etc.) donde una entidad es consciente de algún subconjunto de todas las otras entidades, dividiendo a los jugadores en diferentes "fragmentos", dividiendo a los jugadores en "zonas" y / o instancias, implementando mecánicas de juego que desalientan demasiados jugadores de congregarse juntos (tormentas de teletransporte de Asheron Call), etc.

La mayoría de los MMORPG y muchos motores FPS tienen arquitecturas de red bastante sofisticadas que admiten una variedad de características que incluyen:

  • vías de comunicación confiables y poco confiables (TCP, implementaciones personalizadas de paquetes UDP y UDP confiables)
  • configuración de ancho de banda (priorización, vidas, etc.)
  • replicación automática de campo / datos viables y llamadas a funciones
  • conjuntos atómicos de datos (es decir, datos que se comunican juntos)
  • actualizaciones discretas (es decir, donde cada transición es importante)
  • corrección de latencia
  • Una variedad de trucos para que el cliente se sienta receptivo

Encuentro que la Documentación de redes de Unreal y la Documentación de redes de válvulas proporcionan una buena introducción sobre una variedad de problemas.

Entonces, ahora abordemos las preguntas.

¿Sería una mejor idea 'recolectarlos' y transmitirlos, digamos, una vez en 1/10 de segundo?

Es difícil proporcionar una respuesta simple de sí o no aquí ... porque depende de la escala (número de entidades de observación), la frecuencia de las actualizaciones y el tamaño de las actualizaciones. Por ejemplo, recopilarlos a todos puede ser terriblemente incorrecto si el tamaño de las actualizaciones puede volar un búfer en alguna parte.

El cliente para juegos MMORPG y FPS generalmente está diseñado de modo que visualice algo que "se ve" bien, incluso si no recibe una actualización para muchos más marcos de actualización de lo que es "normal". Al usar una comunicación no confiable (UDP), puede esperar perder algunas actualizaciones en el vacío, los clientes pueden compensar esto enviando actualizaciones más frecuentes de las que podrían usarse con un transporte confiable.

De una revisión superficial de la documentación de socket.io, parece que admite vías de comunicación confiables y poco confiables (volátiles en su terminología).

Primero abordaría esto abordando, con qué frecuencia se necesitan actualizaciones ...

Si un jugador se mueve en línea recta a una velocidad constante, una frecuencia de actualización más baja está bien porque los clientes observadores pueden predecir con alta precisión dónde estará el jugador en cualquier momento. Cuando un jugador gira en un círculo cerrado o realiza cambios rápidos de dirección, se requieren actualizaciones mucho más frecuentes. Por el contrario, cuando un jugador no se mueve en absoluto, no hay ninguna razón para enviar actualizaciones de movimiento.

Pase lo que pase, probablemente no sea (generalmente) necesario enviar actualizaciones cada trama desde el cliente al servidor. El servidor en sí puede elegir enviar mensajes a cada cuadro donde los tiene, o retrasarlos (ver configuración del ancho de banda, priorización y actualización de la vida útil).

Otros tipos de actualizaciones tienen características diferentes ... por ejemplo, considere un campo de "salud" que se modifica cuando un jugador o criatura está dañado. Una forma de implementar esto es difundir cada cambio inmediatamente cuando ocurre, pero esto lleva a desperdiciar el procesamiento y el ancho de banda si el valor se cambia varias veces en un marco o marcos consecutivos (las arquitecturas de red que implementan la configuración del ancho de banda resuelven este problema al fusionar las actualizaciones a solo el más reciente se envía a un cliente observador cuando tiene ancho de banda disponible).

¿Debería el cliente enviar muchos mensajes diferentes (experiencia obtenida, hacer clic en el elemento) tan pronto como ocurran o, mejor dicho, solo uno recogido?

Una vez más, ninguna respuesta simple de sí o no funcionará aquí. Dependiendo de lo que precisamente quiere decir con recopilado ... ambos pueden ser correctos en diferentes circunstancias y también dependen de la implementación de la capa de red.

La recopilación de mensajes para que se envíe una entidad específica como un mensaje puede (dependiendo de la implementación) reducir la sobrecarga de ancho de banda para enviar un mensaje (reduciendo sus costos) a la inversa (dependiendo de la implementación, como las asignaciones de campo / valor comunicadas por cadenas) puede aumentar el ancho de banda requisitos en comparación con un tipo de mensaje específico más simple.

Al revisar la documentación de socket.io, me parece que la sobrecarga del mensaje está en el extremo superior del espectro, lo que favorecería la recopilación de actualizaciones para enviar como un mensaje agregado en lugar de muchas actualizaciones individuales.

Recomiendo revisar todas las actualizaciones que contempla replicar, por ejemplo, la mayoría de los MMORPG y FPS no se molestan en enviar al jugador cliqueado en eventos X para observar a los clientes a menos que eso resulte en un cambio de estado para un objeto del que también estaban al tanto .

Christopher Larsen
fuente
3
+1 Gran respuesta. También sugeriría comenzar de manera simple y optimizar desde allí. Mensajes de spam, claro, vuélvete loco y ve que tu ancho de banda comienza a desvanecerse. Es por eso que es bueno tener pruebas de estrés por etapas. Implementa el enfoque más ingenuo posible y realiza algunas pruebas; haces algunas optimizaciones; prueba de estrés nuevamente, optimiza más, enjuaga, repite. Si es absolutamente trivial implementar una optimización el primer día, adelante; pero tenga en cuenta que incluso las optimizaciones más triviales pueden tener suposiciones que luego pueden ser refutadas en un entorno de producción.
Ingeniero
5

Aquí hay un ejemplo del mundo real: RuneScape "marca" una vez cada ~ 0.6 segundos. Puedes leer sobre esto aquí . Me imagino que simplifica las cosas desde su perspectiva, ya que en sus scripts probablemente especifiquen tiempos y retrasos en ticks en lugar de milisegundos. Y, por supuesto, con una tasa de tics tan grande / lenta, los usuarios con conexiones lentas no están en una gran desventaja en comparación con otros jugadores.

Ricket
fuente
0

Una forma de trabajar es desacoplar los cambios de datos reales de la transmisión de esos cambios. Cuando un objeto cambia, realice la modificación y establezca una bandera "sucia", y cuando llegue el momento de transmitir cualquier cambio, solo envíe datos para los objetos marcados y borre la bandera. Los valores que se cambian varias veces se fusionan automáticamente aquí porque su actualización solo envía el estado en el momento de la transmisión. También puede marcar subobjetos o propiedades individuales para que solo transmita lo que ha cambiado.

Ah, y por lo general no enviarías los cuadros de animación al servidor ni a los otros clientes. El estado preciso de la animación generalmente se considera un detalle de presentación y se deja que cada cliente lo resuelva, y en su lugar solo se asegura de que cada cliente sepa qué animación se está reproduciendo actualmente.

Kylotan
fuente
Yo no? ¿Qué pasa con las poses o algo así? Necesito asegurarme de que todos los clientes vean lo mismo.
Lanbo
Es poco práctico asegurarse de que cada cliente vea exactamente lo mismo porque la información tarda en viajar, los diferentes clientes tienen una latencia de red diferente, renderizan a diferentes velocidades, etc. Por lo tanto, tratar de hacer que todos muestren exactamente el mismo marco simultáneamente es generalmente esfuerzo inútil. En su lugar, puede intentar asegurarse de que comiencen la misma animación aproximadamente al mismo tiempo, y eso es lo suficientemente bueno.
Kylotan