Contexto
En el juego en el que estoy trabajando (una especie de aventura gráfica de apuntar y hacer clic), casi todo lo que sucede en el mundo del juego está controlado por un administrador de acciones que está estructurado un poco como:
Entonces, por ejemplo, si el resultado de examinar un objeto hace que el personaje diga hola, camine un poco y luego se siente, simplemente engendro el siguiente código:
var actionGroup = actionManager.CreateGroup();
actionGroup.Add(new TalkAction("Guybrush", "Hello there!");
actionGroup.Add(new WalkAction("Guybrush", new Vector2(300, 300));
actionGroup.Add(new SetAnimationAction("Guybrush", "Sit"));
Esto crea un nuevo grupo de acción (una línea completa en la imagen de arriba) y lo agrega al administrador. Todos los grupos se ejecutan en paralelo, pero las acciones dentro de cada grupo se encadenan para que el segundo solo comience después de que termine el primero. Cuando finaliza la última acción en un grupo, el grupo se destruye.
Problema
Ahora necesito replicar esta información en una red, de modo que en una sesión multijugador, todos los jugadores vean lo mismo. Serializar las acciones individuales no es el problema. Pero soy un principiante absoluto cuando se trata de redes y tengo algunas preguntas. Creo que, por simplicidad en esta discusión, podemos abstraer el componente del administrador de acciones para que sea simplemente:
var actionManager = new List<List<string>>();
¿Cómo debo proceder para mantener sincronizados los contenidos de la estructura de datos anterior entre todos los jugadores?
Además de la pregunta central, también tengo algunas otras preocupaciones relacionadas con ella (es decir, todas las posibles implicaciones del mismo problema anterior):
- Si utilizo una arquitectura de servidor / cliente (con uno de los jugadores actuando como servidor y como cliente), y uno de los clientes ha generado un grupo de acciones, en caso de que las agregue directamente al gerente, o solo envíe una solicitud al servidor, que a su vez ordenará a cada cliente agregar ese grupo ?
- ¿Qué pasa con las pérdidas de paquetes y similares? El juego es determinista, pero creo que cualquier discrepancia en la secuencia de acciones ejecutadas en un cliente podría conducir a estados inconsistentes del mundo. ¿Cómo me protejo contra ese tipo de problema ?
- ¿Qué sucede si agrego demasiadas acciones a la vez, eso no causará problemas para la conexión? ¿Alguna forma de aliviar eso?
fuente
Respuestas:
Si utilizo una arquitectura de servidor / cliente (con uno de los jugadores actuando como servidor y como cliente), y uno de los clientes ha generado un grupo de acciones, en caso de que las agregue directamente al gerente, o solo envíe una solicitud al servidor, que a su vez ordenará a cada cliente agregar ese grupo?
Tiene tres opciones en este caso, dos de las cuales ya ha mencionado. La elección de qué opción tomar depende de una variedad de factores de los que solo usted puede ser el mejor juez:
1: El cliente genera un grupo de acciones que las agrega directamente y luego las envía al servidor. El servidor lo acepta sin ningún control.
* Los beneficios de este enfoque son que, en lo que respecta al cliente, sus propias acciones les proporcionan una respuesta instantánea independiente del retraso de la red. La desventaja es que los mensajes del cliente son aceptados por el servidor como un hecho (que pueden no serlo) *
2: El cliente envía una solicitud al servidor, que a su vez difunde la solicitud a todos, incluido el cliente que originó la solicitud.
Los beneficios de este enfoque son que el servidor ahora tiene el control de todo lo que sucede en el juego que alivia la mayoría de los problemas con la actividad ilegal (aquí, ilegal puede variar desde que el cliente corta un muro hasta realizar un movimiento que ahora es ilegal porque el servidor tiene más información que el cliente no tenía cuando hizo un movimiento en particular)
3: El cliente genera un grupo de acciones, las agrega directamente y luego las envía al servidor. El servidor procesa la solicitud, ejecuta sus propias verificaciones de las acciones para asegurarse de que no sean ilegales y luego transmite el movimiento a todos los jugadores, incluido el cliente.
Esto toma lo mejor de 1 y 2 a costa de un poco más de requisitos de ancho de banda. Los beneficios son comentarios instantáneos al cliente para sus propios movimientos y si los movimientos que realiza el cliente son ilegales, el servidor toma las medidas apropiadas (corrige al cliente con fuerza).
¿Qué pasa con las pérdidas de paquetes y similares? El juego es determinista, pero creo que cualquier discrepancia en la secuencia de acciones ejecutadas en un cliente podría conducir a estados inconsistentes del mundo. ¿Cómo me protejo contra ese tipo de problema?
La pérdida de paquetes y el retraso extremo es un problema difícil de solucionar . Se emplean diferentes soluciones (ninguna de ellas perfectas) para lidiar con el problema dependiendo del tipo de juego (por ejemplo, ajuste de cuentas). Asumiré que tu juego no tiene que sincronizar la física.
Una de las primeras cosas que debe hacer es sellar todos sus movimientos. Su sistema debería tenerlos en cuenta y ejecutar los movimientos en consecuencia (tal vez el servidor tiene esta responsabilidad y luego negar movimientos ilegales). Al implementar este sistema, asuma la latencia 0 (prueba localmente).
0 latencia, por supuesto, no es una buena suposición, incluso en las redes locales, así que ahora que todos los movimientos respetan el tiempo, ¿en qué hora confía? Ahí es donde entra en juego la cuestión de la sincronización horaria. Muchos artículos / documentos en línea lo guiarán para resolver este problema.
¿Qué sucede si agrego demasiadas acciones a la vez, eso no causará problemas para la conexión? ¿Alguna forma de aliviar eso?
Primero lo obvio. Nunca envíe cadenas u objetos serializados. No envíes mensajes tan rápido como el juego se esté actualizando. Envíelos en trozos (por ejemplo, 10 veces por segundo). Habrá un error (especialmente un error de redondeo) que el servidor / cliente necesitará corregir los errores de vez en cuando (así que cada segundo, el servidor envía todo [todo = todos los datos que son importantes para mantener las cosas en su juego en el correcto Estado] que el cliente, a continuación se ajusta a y / o tiene en cuenta para corregir su estado).EDITAR: Uno de mis colegas mencionó que si está utilizando UDP [que debería, si tiene muchos datos], entonces no hay pedidos de red. Enviar más de 10 paquetes por segundo aumenta los cambios de los paquetes que llegan fuera de servicio. Veré si puedo citar esa afirmación. En cuanto a los paquetes fuera de servicio, puede descartarlos o agregarlos a la cola de mensajes / movimiento de su sistema, que está diseñada para manejar cambios en el pasado para corregir el estado actual
Debe tener un sistema de mensajería que se base en algo que ocupe muy poco espacio. Si tiene que enviar algo que solo puede tener dos valores, envíe solo un bit. Si sabe que solo puede tener 256 movimientos, envíe un personaje sin firmar. Hay varios trucos de giro de bits para empaquetar los mensajes con la mayor precisión posible.
Lo más probable es que su sistema de mensajería utilice muchas enumeraciones para permitirle extraer fácilmente movimientos de un mensaje entrante (sugeriría echar un vistazo a RakNet y ejemplos allí si no entiende lo que quiero decir aquí).
Estoy seguro de que ya sabe que no puede solucionar los problemas que surgen con la latencia alta y la pérdida de paquetes, pero puede hacer que sus efectos sean menos pronunciados. ¡Buena suerte!
EDITAR: el comentario de Patrick Hughes anterior con el enlace hace que esta respuesta sea incompleta y específica en el mejor de los casos a la pregunta de OP. En su lugar, recomendaría leer los temas vinculados.
fuente
so every second, the server sends everything ... which the client then snaps to
. ¿Puedo enviar una gran cantidad de información como esta a la vez? ¿Qué es un tamaño máximo razonable?