Intercambio de mensajes de red cliente-servidor y ayuda de sincronización de reloj

10

Estoy haciendo un juego de física de ritmo rápido que es un hockey de mesa. Con dos mazos y un disco. El juego se ejecuta en iphone / ipad y estoy haciendo la parte multijugador a través de GameCenter.

Así es como funciona el sistema de red. El cliente que protagoniza la coincidencia se establecerá como el servidor y el que acepta la solicitud de coincidencia es el cliente.

El 'servidor' tiene la física en ejecución y la respuesta es inmediata y el cliente también tiene su física en ejecución para que se vea suave entre el intercambio de mensajes. Lo que hago como servidor es enviarle al cliente mi velocidad de disco y mi posición, y el cliente ajusta su velocidad / posición de disco relacionada con el servidor para mantenerla sincronizada. De lo contrario, la física se desincroniza y la arruina.

Cuando la latencia de la red es buena, por debajo de 100 ms, los resultados son bastante buenos, obtuve un juego jugable sin problemas en el lado del cliente y el comportamiento extraño es mínimo. El problema ocurre cuando el retraso es de alrededor de 150 a 200 ms. En ese caso, sucede que el disco de mi cliente ya golpeó un borde e invirtió la dirección, pero recibe un mensaje de retraso del servidor y retrocede un poco causando una extraña sensación en el comportamiento de la pelota.

He leído algunas cosas al respecto:

Ejemplo de red Pong

Haga clic en Ejemplo de sincronización

Wikipedia sobre sincronización de reloj

Entonces, ¿cómo puedo resolver esto? Por lo que he leído, la mejor opción que tengo es hacer una sincronización de reloj en el servidor / cliente con una marca de tiempo para que cuando reciba mensajes de retraso relacionados con mi reloj, simplemente ignoro y deje que la simulación de clientes haga trabajo. ¿Están de acuerdo con eso? Y dado que estoy enviando datos poco confiables (UDP), puedo recibir mensajes retrasados ​​o mensajes fuera de servicio.

Si ese es el mejor enfoque, ¿cómo implemento la sincronización del reloj? He leído los pasos sobre cómo hacerlo, pero no lo entendí del todo.

Dice que:

  1. El cliente marca la hora local actual en un paquete de "solicitud de hora" y lo envía al servidor.
  2. Al recibir el servidor, el servidor marca la hora del servidor y devuelve
  3. Al recibirlo el cliente, el cliente resta el tiempo actual del tiempo enviado y lo divide por dos para calcular la latencia. Resta la hora actual de la hora del servidor para determinar el delta de tiempo cliente-servidor y agrega la latencia media para obtener el delta de reloj correcto. (Hasta ahora este algothim es muy similar al SNTP)
  4. El cliente repite los pasos 1 a 3 cinco o más veces, haciendo una pausa de unos segundos cada vez. Se puede permitir otro tráfico en el ínterin, pero se debe minimizar para obtener mejores resultados. Los resultados de los recibos de paquetes se acumulan y ordenan en orden de latencia más baja a más alta. La latencia media se determina seleccionando la muestra de punto medio de esta lista ordenada.
  5. Todas las muestras por encima de aproximadamente 1 desviación estándar de la mediana se descartan y las muestras restantes se promedian usando una media aritmética.

Siguiendo este ejemplo, tendría esto:

Supongamos que el juego se ha cargado y que el tiempo de mi cliente es 0 ahora, así que le envío al servidor que mi tiempo es 0.

Los mensajes tardan 150 ms en llegar al servidor, pero el reloj del servidor ya se había iniciado y está 1 segundo por delante del cliente. Cuando el servidor recibe el mensaje, la hora será: 1.15 y envía esa hora al cliente, ¿estamos bien? Supongamos que nuestro retraso es constante a 150 ms.

Ahora el cliente recibe el tiempo 1.15 y resta el tiempo actual del tiempo enviado y lo divide por dos para calcular la latencia. Cuál es: 0.3 - 0 = 0.3 / 2 -> 150ms.

Resta la hora actual de la hora del servidor para determinar el delta de tiempo cliente-servidor y agrega la latencia media para obtener el delta de reloj correcto:
Hora del cliente: 0.3 Hora del servidor 1.15
0.3 - 1.15 = .85 + latencia (.15) = 1

¿Cómo se sincroniza eso? ¿Qué me estoy perdiendo?

Es mi primera vez en multijugador y experiencia en red, así que estoy un poco confundido.

Gracias.

gmemario
fuente
Buena pregunta. ¿Podría corregir: 0.3 - 1.15 para ser 1.15 - 0.3?
Ciaran

Respuestas:

12

El algoritmo publicado fue correcto, pero en su ejemplo se está olvidando del tiempo que tarda el paquete del servidor en llegar al cliente, por lo tanto:

Server time: 1
Client time: 0
Client sends 0 to server

... 150ms to get to server  (ping is 300! not 150ms in this case. Ping is round-trip)

Server time: 1.15
Client time: 0.15
Server receives packet and sends client 1.15

... 150ms to get back to client

Server time: 1.30
Client time: 0.30
Client receives 1.15 from server

Ahora, como puede ver, si el cliente cambió su reloj a 1.15, estaría 0.15 detrás del servidor, es por eso que debe ajustar el Ping (también conocido como Tiempo de ida y vuelta [RTT]). Aquí está el cálculo del tiempo delta completo realizado en muchos pasos:

Server Time - Current Time + Ping / 2
= Server Time - Current Time + (Current Time - First Packet Time) / 2
= 1.15 (Perceived, not actual!) - 0.30 + (0.30 - 0.00) / 2
= 1.00

Esto nos da el tiempo delta correcto de 1.00 segundos

John McDonald
fuente
Lo estoy obteniendo, así que el reloj de mi cliente es 1.00 y el servidor es 1.30. Después de recibir el mensaje del servidor, ¿debo agregar el ping para verificar si hay un mensaje tardío o algo así? Otra cosa, ¿qué pasa si cambia la latencia, tengo que seguir haciendo esos cálculos todo el tiempo?
gmemario
El tiempo delta de 1.00s se debe agregar al reloj actual para que la hora del cliente sea igual a la hora del servidor. La latencia siempre cambia, cada paquete tendrá un RTT ligeramente diferente. En un corto período de tiempo, como en un juego, simplemente haría esta sincronización de tiempo solo una vez, el cliente tendrá una buena suposición sobre la hora del servidor y ambos relojes deberían avanzar aproximadamente al mismo ritmo. La única excepción sería si recibió un paquete que parece ser del futuro. En ese caso, hay 2 soluciones: 1) Realice una nueva sincronización de tiempo. 2) Avance su reloj para que coincida con el reloj futuro
John McDonald
Ok, echemos un vistazo a esta situación solo para asegurarme de que la tengo: Hora del servidor: 1s Hora del cliente: 0s 150ms para llegar al servidor Hora del servidor: 1.15s Hora del cliente: 0.15s El servidor envía al cliente 1.15s 200ms para llegar al cliente Entonces, mi ping ahora es de 350 ms. ¿Es eso correcto? Hora del servidor: 1.35 Hora del cliente: 0.35 Hacer los cálculos: 1.15 - .35 + (0.35 - 0.00) / 2 Ahora mi deltaTime = 0,975 Si agrego la hora delta 0,975 a mi .35 obtengo: 1,325 que es una especie de desincronización. ¿Es eso correcto también?
gmemario
@Gilson, eso es correcto. Si la latencia de los dos paquetes es diferente (casi garantizada), el cálculo del tiempo delta del cliente no será perfecto. No hay forma de que el cliente sea perfecto. En el algoritmo que describió en la pregunta, el tiempo delta se calculó varias veces y había un método para seleccionar y promediar estos resultados. Muchos métodos de sincronización de tiempo harán algo como esto, pero generalmente son para servidores críticos de negocios y de larga duración, como las bolsas de valores. Para la precisión de tiempo que necesitas para un juego corto, creo que sería exagerado.
John McDonald
@Gilson, siempre y cuando el cliente tenga una estimación razonable del tiempo del servidor y ambos relojes avancen aproximadamente a la misma velocidad, no debería notar ningún problema. Cuando el cliente o el servidor envía una acción al otro lado, enviarán su hora local. En el lado receptor, el mensaje siempre debe estar en el pasado y deberá interpretarse como un evento pasado. Esto significa que necesita toda la información en el paquete, como ubicación, velocidad (magnitud + dirección) y tiempo. Luego puede colocar el disco allí y luego, y mover el disco del pasado al presente. Estoy en el chat por cierto
John McDonald