Protocolo de juego RTS

18

He estado pensando en un juego RTS multijugador. La parte que parece que no puedo evitar es mantener sincronizados los movimientos de la unidad. Si muevo la unidad A para detectar XY, tengo que comunicarlo al servidor que lo retransmite al otro cliente.

Tengo curiosidad por cómo se verían las comunicaciones. ¿Le comunicaría al servidor que estoy moviendo la unidad A a XY desde JZ? ¿Quizás necesites comunicar el movimiento coord por coord en su lugar? ¿Cuál es la metodología más eficiente para comunicar el movimiento de unidades de un cliente a otro?

EDITAR

Esta es una pregunta publicada de stackoverflow . Descubrí que este sitio era probablemente un mejor lugar para la pregunta.

Una de las mejores respuestas de esa publicación:

¿Supongo que tiene la intención de usar el paradigma de red Cliente-Servidor? En cuyo caso no puede confiar en que los clientes manejarán el posicionamiento real de las unidades, debe delegar esa tarea al servidor. Luego toma la lista de comandos de cada cliente por tic y calcula el movimiento de cada unidad, una vez que se ha completado, el siguiente tic retransmite la posición de cada unidad relevante para cada cliente (ya sea en un mapa completo o por vista) e inicie el proceso nuevamente.

Darthg8r
fuente
2
La respuesta realmente depende del método que quieras usar. Cliente - cliente o cliente - servidor. Cliente a servidor es más fácil pero requiere un servidor confiable
Cem Kalyoncu

Respuestas:

25

No desea sincronizar las posiciones de todas las unidades del servidor a cada cliente; eso ocupará mucho más ancho de banda de lo que necesita. También tendría que ocuparse de los puestos de unidades de interpolación / extrapolación, etc. ¡ Casi ningún profesional de RTS utiliza cliente / servidor!

En cambio, solo quieres enviar los comandos de los jugadores. En lugar de mover las unidades inmediatamente cuando el jugador hace clic, colocará en cola el comando de movimiento para que se realice en algún momento en el futuro, generalmente solo un par de fotogramas. Todos envían sus comandos a todos. Un par de cuadros más tarde, todos ejecutan todos los comandos y, debido a que el juego es determinista, todos ven el mismo resultado.

La desventaja es que cada jugador es tan lento como el jugador más lento: si alguien se atrasa en el envío de comandos, todos deben reducir la velocidad y esperar a que se ponga al día (en Starcraft 2, este es el "XXX está ralentizando el juego"). " diálogo).


De hecho, hay una cosa más que generalmente se hace: eliminar el servidor por completo . Haga que cada cliente envíe sus comandos a todos los demás clientes. Esto reduce el retraso (en lugar de un comando que va desde usted -> servidor -> oponente, solo va desde usted -> oponente) y facilita la codificación, ya que ya no necesita codificar un servidor separado. Este tipo de arquitectura se llama punto a punto (P2P).

La desventaja es que ahora necesitas una forma de resolver conflictos, pero dado que los comandos de los jugadores son independientes entre sí en la mayoría de los RTS, esto generalmente no es un gran problema. Además, no se escala bien: cada vez que agrega un nuevo jugador, cada jugador debe enviarle sus comandos. No vas a hacer un MMO RTS usando P2P.


Esta configuración (enviando solo comandos usando P2P) es la forma en que funcionan la mayoría de los RTS, incluidos Starcraft, C&C y AoE, y es la única forma en que AoE podría admitir 1500 unidades en una conexión de 28.8 kbps .

(imagen de redes en AoE)

Aquí hay algunos consejos más para escribir un P2P RTS:

  • Por razones obvias, esta configuración solo puede funcionar si su juego usa un tiempo de paso fijo : ¡no desea que los resultados de un cálculo dependan de la velocidad de fotogramas! El paso fijo es más fácil de trabajar para la mayoría de las cosas, por lo que esto no debería ser un problema.
  • Para que esto funcione, los resultados de cada comando deben ser completamente deterministas .
    • Esto suele ser bastante fácil si se limita a un sistema (como Windows de 32 bits) y obliga a todos los clientes a usar el mismo ejecutable: asegúrese de que todos los generadores de números aleatorios tengan la misma semilla y siempre se llamen en el mismo orden; tenga mucho cuidado al iterar sobre colecciones desordenadas ; etc.
    • Esto es extremadamente difícil si planeas hacer que el juego se pueda jugar en diferentes plataformas, o (como suele ser el caso con Linux) permitir que los clientes compilen el código ellos mismos. No solo se garantiza que las diferentes bibliotecas del sistema utilicen diferentes implementaciones de rand(), cos()etc., sino que prácticamente todas las matemáticas de punto flotante están fuera de discusión (ver aquí , aquí y aquí ) . En ese caso, es mejor que use cliente-servidor.
  • Querrá enviar todas las posiciones de la unidad de vez en cuando, al menos durante la depuración, para detectar errores de desincronización (que, confía en mí, lo tendrá ). Si mantienes eso en el juego final depende de ti, sincronizaría al menos algunas unidades (o usaría algún tipo de suma de comprobación) para detectar intentos de piratería.
BlueRaja - Danny Pflughoeft
fuente
Buen post. Una pequeña cosa para agregar, incluso los mismos compiladores optimizan, depuran / liberan y otras banderas pueden cambiar el resultado. ¡Ten cuidado!
Peter Ølsted
14

Hice un RTS en red TCP, en el que pasé los comandos ellos mismos, en lugar de los resultados de los comandos . Por ejemplo, un jugador da una orden de movimiento. Si la orden de movimiento es válida de acuerdo con ese cliente, se envía al servidor. El servidor luego lo envía de vuelta a todos los clientes, quienes lo validan y ejecutan.

Entonces, todas las máquinas cliente ejecutan el juego ellas mismas, el código del servidor acepta mensajes y los envía de vuelta a todos los clientes. Si un cliente da una orden de movimiento, no comenzará a ejecutarla hasta que la reciba del servidor.

El servidor también envía un número de 'tick' para ejecutar el comando, que se encuentra unos pocos tics antes del tick 'actual'. De esta forma, todos los comandos se pueden ejecutar con el mismo 'tic' en todas las máquinas.

Un beneficio de este método es que no depende de ninguna máquina cliente individual para validar el comando. Si supero los resultados del movimiento, podría piratearlo para mover mis unidades más rápido. Todos los clientes tienen que ejecutar el mismo comando y si una máquina lo ejecuta de manera diferente, será obvio.

No es necesario validar el comando del lado del cliente antes de enviarlo al servidor, pero en teoría ahorra tráfico de red. Usé el mismo código de validación para decirle a la interfaz de usuario que el movimiento era posible, por lo que no requería escribir código adicional.

En cuanto a cómo se verían los mensajes. No me preocupaba la ultraeficiencia, ya que era mi primer juego en red. Pasé comandos como cadenas. Los comandos se formatearían así:"<player_id>:<command>:<parameters>"

Para un ejemplo artificial, un comando de desplazamiento podría tener este aspecto: "3:move:522:100:200". Esto significa que el jugador 3quiere moveunir 522a ( 100, 200).

El servidor pasa el mando a todos los clientes, entre ellos el que lo envió, con un número garrapata como esto: "153238:3:move:522:100:200".

Entonces, todos los clientes ejecutarán este comando cuando se ejecute la marca 153238.

Philip
fuente
Agregué un poco más de información a la pregunta. La respuesta de SO parece ser contraria a lo que dijiste y me encantaría discutir los detalles más finos.
Darthg8r
Sí, esa es otra forma de hacerlo, pero me parece que sería más difícil pasar gran parte del estado del juego, en lugar de solo los comandos. Mi juego fue lo suficientemente simple como para que todo se pueda ejecutar en cada máquina cliente. Para un MMO, o para algo como Minecraft, no tienes toda la simulación ejecutándose en el lado del cliente, por lo que solo pasas información relevante para cada cliente individualmente.
Philip