Guida alla sincronizzazione dello scambio di messaggi client-server di rete e sincronizzazione dell'orologio


10

sto facendo un gioco di fisica veloce che è un hockey da tavolo. Con due mazze e un disco. Il gioco funziona su iPhone / iPad e sto facendo la parte multiplayer tramite GameCenter.

Ecco come funziona il sistema di rete. Il client che inizia la partita, verrà impostato come server e quello che accetta la richiesta di corrispondenza è il client.

Il 'server' ha la fisica in esecuzione e la risposta è immediata e il client ha anche la sua fisica in esecuzione, quindi sembra regolare tra lo scambio di messaggi. Quello che faccio come server è che invio al client la mia velocità del disco e la mia posizione e il client regola la sua velocità / posizione del disco in relazione al server per mantenerlo sincronizzato. Altrimenti la fisica si disincronizza e la rovina.

Quando la latenza della rete è buona, sotto 100 ms i risultati sono abbastanza buoni, ho un gioco giocabile liscio sul lato client e il comportamento strano è minimo. Il problema si verifica quando il ritardo è compreso tra 150 e 200 ms. In quel caso, succede che il mio disco client ha già colpito un bordo e una direzione invertita ma riceve un messaggio di ritardo dal server e si arresta un po 'causando una strana sensazione al comportamento della palla.

Ho letto alcune cose a riguardo:

Esempio di rete Pong

Fai clic su Esempio di sincronizzazione

Wikipedia su Clock Sync

Quindi, come posso risolvere questo? Per quanto ho letto l'opzione migliore che ho è quella di fare una sincronizzazione dell'orologio sul server / client con un timestamp in modo che quando ricevo messaggi di ritardo relativi al mio orologio, ignoro solo allora e lascio che la simulazione dei client faccia il lavoro. Siete d'accordo con questo? E poiché sto inviando dati inaffidabili (UDP), potrei ricevere messaggi ritardati o messaggi fuori servizio.

Se questo è l'approccio migliore, come posso implementare la sincronizzazione dell'orologio. Ho letto i passaggi su come farlo ma non l'ho capito del tutto.

Dice che:

  1. Il client stampa l'ora locale corrente su un pacchetto "richiesta ora" e lo invia al server.
  2. Alla ricezione da parte del server, il server indica l'ora e i resi del server
  3. Alla ricezione da parte del cliente, il cliente sottrae l'ora corrente dall'ora inviata e si divide per due per calcolare la latenza. Sottrae l'ora corrente dall'ora del server per determinare il delta temporale client-server e aggiunge la mezza latenza per ottenere il delta dell'orologio corretto. (Finora questo algothim è molto simile a SNTP)
  4. Il client ripete i passaggi da 1 a 3 per cinque o più volte, facendo una pausa di alcuni secondi ogni volta. Nel frattempo potrebbe essere consentito altro traffico, ma dovrebbe essere ridotto al minimo per ottenere i migliori risultati. I risultati delle entrate dei pacchetti vengono accumulati e ordinati nell'ordine di latenza minima a quello di latenza più elevata. La latenza mediana viene determinata selezionando il campione del punto medio da questo elenco ordinato.
  5. Tutti i campioni superiori a circa 1 deviazione standard dalla mediana vengono scartati e la media dei campioni rimanenti viene utilizzata con una media aritmetica.

Seguendo questo esempio vorrei questo:

Facciamo finta che il gioco sia caricato e il tempo del mio client sia 0 ora, quindi invio al server che il mio tempo è 0.

I messaggi impiegano 150 ms per arrivare al server ma l'orologio del server era già stato avviato ed è 1 secondo avanti rispetto al client. Quando il server riceve il messaggio, l'ora sarà: 1.15 e invierà l'ora al client, siamo a posto? Facciamo finta che il nostro ritardo sia costante a 150 ms.

Ora il client riceve l'ora 1.15 e sottrae l'ora corrente dall'ora inviata e si divide per due per calcolare la latenza. Quale è: 0,3 - 0 = 0,3 / 2 -> 150 ms.

Sottrae l'ora corrente dall'ora del server per determinare il delta dell'ora client-server e aggiunge la mezza latenza per ottenere il delta dell'orologio corretto:
Tempo del client: 0,3 Tempo del server 1,15
0,3 - 1,15 = .85 + latenza (.15) = 1

Come è sincronizzato? Cosa mi sto perdendo?

È la prima volta che faccio esperienza multiplayer e di rete, quindi sono un po 'confuso.

Grazie.


Buona domanda. Potresti correggere: 0,3 - 1,15 per essere 1,15 - 0,3?
Ciaran,

Risposte:


12

L'algoritmo pubblicato era corretto, ma nel tuo esempio ti stai dimenticando del tempo impiegato dal pacchetto server per raggiungere il client, quindi:

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

Ora come puoi vedere, se il client ha cambiato il suo orologio a 1.15, sarebbe 0.15 dietro il server, ecco perché devi adattarti al Ping (aka Round Trip Time [RTT]). Ecco il calcolo completo del tempo delta eseguito su più passaggi:

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

Questo ci dà il tempo delta corretto di 1,00 secondi


Lo sto ottenendo, quindi l'orologio del mio client è 1.00 e il server è 1.30 Dopo aver ricevuto il messaggio dal server, devo aggiungere il ping per verificare se è un messaggio in ritardo o qualcosa del genere? Un'altra cosa, e se la latenza cambia, devo continuare a fare quei calcoli tutto il tempo?
gmemario,

Il tempo delta di 1.00s deve essere aggiunto all'orologio corrente per rendere il tempo del client uguale al tempo del server. La latenza cambia sempre, ogni pacchetto avrà un RTT leggermente diverso. In un breve lasso di tempo, come in un gioco, farei semplicemente questa volta la sincronizzazione solo una volta, il client avrà una buona idea di quale sia il tempo del server ed entrambi gli orologi dovrebbero andare avanti allo stesso ritmo. L'unica eccezione sarebbe se si ricevesse un pacchetto che sembra provenire dal futuro. In tal caso, esistono 2 soluzioni: 1) Esegui una nuova sincronizzazione dell'ora. 2) Fai avanzare l'orologio in modo che corrisponda all'orologio futuro
John McDonald,

Ok, diamo un'occhiata a questa situazione solo per essere sicuro di averla: Tempo del server: 1s Tempo del client: 0s 150ms per arrivare al server Tempo del server: 1.15s Tempo del client: 0.15s Il server invia il client 1.15s 200ms per arrivare al client Quindi, il mio ping ora è 350ms. È corretto? Tempo del server: 1,35 Tempo del client: 0,35 Effettuare i calcoli: 1,15 - 0,35 + (0,35 - 0,00) / 2 Ora il mio deltaTime = 0,975 Se aggiungo il delta 0,975 al mio .35 ottengo: 1,325 Che è un po 'desincrono. Anche quello è corretto?
gmemario,

@Gilson, è corretto. Se la latenza per i due pacchetti è diversa (quasi garantita), il calcolo del tempo delta del client non sarà perfetto. Non c'è modo che il cliente sia mai perfetto. Nell'algoritmo che hai descritto nella domanda, il delta time è stato calcolato più volte e c'era un metodo per selezionare e calcolare la media di questi risultati. Molti metodi di sincronizzazione temporale fanno qualcosa del genere, ma di solito sono per server business-critical e di lunga durata come per le borse. Per la precisione del tempo richiesta per un gioco corto, penso che sarebbe eccessivo.
John McDonald,

@Gilson, fintanto che il client ha una stima ragionevole dell'ora del server ed entrambi gli orologi procedono all'incirca alla stessa velocità, non dovresti notare alcun problema. Quando il client o il server invia un'azione dall'altra parte, invieranno l'ora locale. Per quanto riguarda la ricezione, il messaggio dovrebbe essere sempre nel passato e dovrà essere interpretato come un evento passato. Ciò significa che hai bisogno di tutte le informazioni nel pacchetto, come posizione, velocità (magnitudine + direzione) e tempo. Quindi puoi posizionare il disco lì e poi, e spostare il disco dal passato al presente. Sono in chat a proposito
John McDonald il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.