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 .
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.
fuente
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.
fuente