Come mantenere sincronizzati gli orologi server-client per giochi di rete di precisione come Quake 3?


15

Sto lavorando su uno sparatutto 2D top-down e sto facendo del mio meglio per copiare concetti usati in giochi in rete come Quake 3.

  • Ho un server autorevole.
  • Il server invia snapshot ai client.
  • Le istantanee contengono un timestamp e posizioni entità.
  • Le entità sono interpolate tra le posizioni dell'istantanea in modo che il movimento appaia regolare.
  • Per necessità, l'interpolazione di entità si verifica leggermente "in passato" in modo da avere più istantanee tra cui interpolare.

Il problema che sto affrontando è "sincronizzazione dell'orologio".

  • Per semplicità, supponiamo solo per un momento che non ci sia latenza zero durante il trasferimento di pacchetti da e verso il server.
  • Se l'orologio del server è 60 secondi avanti rispetto all'orologio del client, un timestamp dell'istantanea sarà 60000ms prima del timestamp locale del client.
  • Pertanto, le istantanee delle entità verranno raccolte e rimarranno attive per circa 60 secondi prima che il client veda una determinata entità fare le sue mosse, poiché il clock del client impiega così tanto tempo a recuperare il ritardo.

Sono riuscito a ovviare a questo problema calcolando la differenza tra l'orologio del server e quello del client ogni volta che viene ricevuta un'istantanea.

// For simplicity, don't worry about latency for now...
client_server_clock_delta = snapshot.server_timestamp - client_timestamp;

Nel determinare la distanza dell'interpolazione dall'entità, aggiungo semplicemente la differenza all'ora corrente del cliente. Il problema con questo, tuttavia, è che causerà scossoni perché la differenza tra i due orologi fluttuerà bruscamente a causa di istantanee che arrivano più velocemente / più lentamente degli altri.

Come posso sincronizzare gli orologi abbastanza strettamente che l'unico ritardo percepibile è quello che è hardcoded per l'interpolazione e quello che è causato dalla normale latenza di rete?

In altre parole, come posso impedire che l'interpolazione inizi troppo tardi o troppo presto quando gli orologi sono significativamente desincronizzati, senza introdurre scatti?

Modifica: Secondo Wikipedia , NTP può essere utilizzato per sincronizzare gli orologi su Internet entro pochi millisecondi. Tuttavia, il protocollo sembra complicato e forse eccessivo per l'uso nei giochi?


com'è complicato ? È una richiesta e una risposta ognuna con i timestamp di trasmissione e arrivo, quindi un po 'di matematica per ottenere il delta
maniaco del cricchetto

@ratchetfreak: Secondo ( mine-control.com/zack/timesync/timesync.html ), "Sfortunatamente, l'NTP è molto complicato e, soprattutto, lento a convergere sull'accurato delta temporale. Ciò rende l'NTP meno che ideale per la rete partita in cui il giocatore si aspetta che una partita inizi immediatamente ... "
Joncom,

Risposte:


9

Dopo aver cercato, sembra che sincronizzare gli orologi di 2 o più computer non sia un compito banale. Un protocollo come NTP fa un buon lavoro ma è presumibilmente lento e troppo complesso per essere pratico nei giochi. Inoltre, utilizza UDP che non funzionerà per me perché sto lavorando con socket Web, che non supportano UDP.

Ho trovato un metodo qui , che sembra relativamente semplice:

Afferma di sincronizzare gli orologi entro 150ms (o meglio) l'uno dall'altro.

Non so se sarà abbastanza buono per i miei scopi, ma non sono stato in grado di trovare un'alternativa più precisa.

Ecco l'algoritmo che fornisce:

Per i giochi è necessaria una semplice tecnica di sincronizzazione dell'orologio. Idealmente, dovrebbe avere le seguenti proprietà: ragionevolmente preciso (150 ms o superiore), rapido da convergere, semplice da implementare, in grado di funzionare su protocolli basati su stream come TCP.

Un semplice algoritmo con queste proprietà è il seguente:

  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 primo risultato dovrebbe essere immediatamente utilizzato per aggiornare l'orologio poiché porterà l'orologio locale almeno nel giusto ballpark (almeno nel giusto fuso orario!)
  5. 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 i migliori risultati
  6. I risultati delle ricevute dei pacchetti vengono accumulati e ordinati in ordine di latenza minima e latenza massima. La latenza mediana viene determinata selezionando il campione del punto medio da questo elenco ordinato.
  7. 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.

L'unica sottigliezza di questo algoritmo è che i pacchetti sopra una deviazione standard sopra la mediana vengono scartati. Lo scopo è quello di eliminare i pacchetti che sono stati ritrasmessi da TCP. Per visualizzarlo, immagina che un campione di cinque pacchetti sia stato inviato su TCP e che non ci sia stata alcuna ritrasmissione. In questo caso, l'istogramma della latenza avrà una modalità singola (cluster) centrata attorno alla latenza mediana. Ora immagina che in un altro processo, un singolo pacchetto dei cinque sia ritrasmesso. La ritrasmissione farà cadere questo campione a destra sull'istogramma della latenza, in media il doppio rispetto alla mediana della modalità primaria. Semplicemente tagliando tutti i campioni che cadono più di una deviazione standard dalla mediana, queste modalità vaganti vengono facilmente eliminate supponendo che non comprendano la maggior parte delle statistiche.

Questa soluzione sembra rispondere in modo soddisfacente alla mia domanda, perché sincronizza l'orologio e poi si ferma, consentendo al tempo di fluire in modo lineare. Considerando che il mio metodo iniziale ha aggiornato costantemente l'orologio, facendo in modo che il tempo salti un po 'alla ricezione delle istantanee.


In che modo questo ha dimostrato di funzionare per te allora? Sono nella stessa situazione ora. Sto usando un framework server che supporta solo TCP, quindi non posso usare NTP, che invia datagrammi UDP. Faccio fatica a trovare qualsiasi algoritmo di sincronizzazione dell'ora che pretenda di eseguire una sincronizzazione dell'ora affidabile su TCP. La sincronizzazione entro un secondo sarebbe sufficiente per le mie esigenze.
dynamokaj,

@dynamokaj Funziona abbastanza bene.
Joncom,

Freddo. È possibile condividere l'implementazione?
dynamokaj,

@dynamokaj Sembra che non riesca a trovare una simile implementazione in nessun progetto a cui riesco a pensare in questo momento. In alternativa, ciò che funziona abbastanza bene per me è: 1) utilizzare immediatamente la latenza calcolata da una richiesta / risposta di ping e quindi, 2) per tutte le future risposte di questo tipo interpolate gradualmente verso il nuovo valore, non istantaneamente. Questo ha un effetto "medio" che è stato molto accurato per i miei scopi.
Joncom,

Nessun problema. Sto eseguendo il mio servizio di back-end su Google App Engine, quindi sull'infrastruttura di Google in cui i server sono sincronizzati utilizzando il server NTP di Google: time.google.com ( developers.google.com/time ) Pertanto utilizzo il seguente client NTP per il mio client Xamarin Mobile per ottenere l'offset tra client e server. components.xamarin.com/view/rebex-time - Grazie per il tempo dedicato a rispondere.
dynamokaj,

1

Fondamentalmente, non puoi aggiustare il mondo [intero] e, alla fine, dovrai disegnare la linea.

Se il server e tutti i client condividono lo stesso frame rate, devono solo sincronizzarsi al momento della connessione e, occasionalmente, in seguito, specialmente dopo un evento di latenza. La latenza non influisce sul flusso del tempo o sulla capacità del PC di misurarlo, quindi, in molti casi, anziché interpolare, è necessario estrapolare. Questo crea effetti ugualmente indesiderati ma, di nuovo, è quello che è e devi scegliere il minimo di tutti i mali disponibili.

Considera che in molti MMO popolari, i giocatori in ritardo sono visivamente ovvi. Se li vedi in esecuzione sul posto, direttamente in un muro, il tuo client sta estrapolando. Quando il tuo cliente riceve i nuovi dati, il giocatore (sul suo client) potrebbe essersi spostato a una distanza considerevole e si "elastico" o si teletrasporterà in una nuova posizione (il "jerkiness" che hai citato?). Questo succede anche nei principali giochi di marca.

Tecnicamente, questo è un problema con l'infrastruttura di rete del giocatore, non con il tuo gioco. Il punto in cui va dall'uno all'altro è proprio la linea che devi tracciare. Il tuo codice, su 3 computer separati, dovrebbe più o meno registrare la stessa quantità di tempo trascorso. Se non ricevi un aggiornamento, ciò non dovrebbe influire sul frame rate di Update (); semmai, dovrebbe essere più veloce poiché probabilmente c'è meno da aggiornare.

"Se hai Internet scadente, non puoi giocare a questo gioco in modo competitivo."
Questo non è un problema.

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.