05 feb 2019

Migliorie al calcolo del ping

Ciao a tutti! Sono Bart Hawthorne, networking engineer senior del team di engine qui in Epic, e vorrei parlarvi di come calcoliamo il ping in Fortnite, del nuovo sistema provato nell'aggiornamento 7.30, di perché non funziona bene e infine di come intendiamo aumentare la sua precisione in futuro.

Per prima cosa, una rapida introduzione su alcuni termini che utilizzeremo spesso, per chi non ne ha familiarità:

Client e server: Ogni console, dispositivo mobile, PC o Mac è un client, e i client si connettono a un server dedicato che ospita il gioco. Il server conserva lo stato generale del gioco (ubicazione dei giocatori, lo stato del cerchio, quali forzieri sono attivi e un sacco di altre cose). Il server ha poi la responsabilità di aggiornare tutti i client quando lo stato del gioco viene cambiato.

Pacchetto: Un pacchetto è un blocco di dati inviato sulla rete, dal client al server e viceversa. È il canale con cui client e server comunicano tra di loro. In Fortnite (e in UE4), utilizziamo questi pacchetti come metodo di calcolo dei ping.

Frame: Lato server, un frame è la frequenza con la quale il server esegue tutti i compiti assegnati: rilevazione di nuovi pacchetti, aggiornamento del gioco, invio di aggiornamenti ai client. Lato client, un frame è la frequenza di aggiornamento delle immagini sullo schermo. Il frame rate, conosciuto anche come FPS, o frame per secondo, rappresenta il numero di frame elaborati lato server o client in un secondo. Quindi, quando diciamo che un client gira a 60FPS, significa che ogni secondo contiene 60 frame.

E con questo, possiamo parlare di ping!

Il vecchio metodo di calcolo dei pacchetti ping

Quando la maggior parte delle persone pensa al "ping", pensa al tempo necessario perché un client invii un pacchetto al server e perché il server risponda, o viceversa. È conosciuto anche come round-trip time (tempo di andata e ritorno), o RTT. È l'informazione più importante per i giocatori: quanto tempo occorre perché il client riceva gli aggiornamenti del server e viceversa. Più il ping di un giocatore è elevato, maggiori sono le probabilità che si presentino anomalie all'interno del gioco, come teletrasporto dei giocatori, eliminazioni inaspettate, ritardi nell'interazione con forzieri o altri oggetti.

Misuriamo i ping in millisecondi, e meno sono e meglio è. Se per caso abitate a fianco di un data center che ospita i nostri server, potreste ottenere un ping di pochi millisecondi. Al contrario, se state giocando su un server dall'altra parte del mondo, il vostro ping potrebbe arrivare a 100 ms o perfino di più.

Ma la comunicazione tra client e server non si riduce al solo tempo di trasmissione dei pacchetti fra client e server. In Fortnite, rileviamo la presenza di nuovi pacchetti attorno all'inizio di ciascun frame, sia lato server che lato client. Se un pacchetto arriva appena dopo che questa verifica è stata fatta, potrebbe aspettare fino a un frame intero prima di essere rilevato da quella successiva. Inoltre, la lettura del pacchetto e l'aggiornamento dello stato del gioco richiedono del tempo che dipende dai dati contenuti.

Ecco uno schema per capirci:

Le frecce nere di "client" e "server" rappresentano il tempo. In questo esempio, entrambi viaggiano perfettamente sincronizzati sullo stesso framerate. Le linee verticali nere rappresentano l'inizio di ciascun frame, le tacche blu indicano il momento in cui il client o il server rilevano se ci sono pacchetti da elaborare, le linee arancio indicano il momento in cui il client o il server inviano i pacchetti. Le linee tratteggiate viola rappresentano il viaggio compiuto da un pacchetto su internet.

Questa è la sequenza degli eventi:

  1. Il client invia un pacchetto al server
  2. Il server riceve il pacchetto inviato dal client.
  3. Il server controlla la presenza di nuovi pacchetti: questo è il momento in cui il server rileva il pacchetto inviato dal client. Se ci fate caso, il pacchetto è rimasto in attesa per quasi un intero frame, perché è arrivato quando il server aveva appena controllato la presenza di nuovi pacchetti.
  4. Il server risponde al client dicendo di aver ricevuto il pacchetto.
  5. La risposta del server arriva al client.
  6. Il client legge la risposta del server.
Vogliamo visualizzare solamente il tempo di andata e ritorno, indicato dalla freccia verde, sottraendo il tempo indicato dalle frecce rosse. Facciamo del nostro meglio: quando inviamo un pacchetto dal server al client, questo include il frame time del server, quindi per un pacchetto specifico sottraiamo sia il frame time del server, sia il frame time del client, fino al momento in cui elaboriamo il pacchetto. Il nostro calcolo del ping si presenta così:
Ping = Tempo necessario perché il pacchetto viaggi fino al server + Tempo necessario perché il pacchetto viaggi fino al client - Frame time del server - Frame time del client

Per ottenere valori più costanti, il valore di ping che mostriamo è una media dei tempi misurati su diversi secondi.

Ma nella pratica, non è preciso quanto vorremmo. Non tenendo conto del tempo d'attesa per il trattamento dei pacchetti in arrivo, il valore di ping visualizzato è spesso troppo alto. Purtroppo, la maggior parte delle piattaforme non ci consente di sapere esattamente quando un dato pacchetto è stato ricevuto, quindi non sappiamo che valore sottrarre.

Il nostro metodo di calcolo del ping nella patch 7.30

Per la versione 7.30 abbiamo deciso di provare qualcosa di diverso: utilizzare soltanto i pacchetti che hanno impiegato il tempo minore per viaggiare dal client al server e poi ricevere risposta. In linea teorica, i pacchetti che hanno impiegato il tempo minore sono anche quelli che sono stati elaborati immediatamente. E se quel tempo d'attesa è prossimo allo zero, possiamo ignorarlo nei nostri calcoli. Perciò, abbiamo deciso di usare il 25% dei pacchetti più veloci per il nostro calcolo del ping, in modo da trovare un equilibrio fra il ritardo e la quantità/accuratezza dei dati.

Cosa è andato storto?

Dopo aver pubblicato la patch 7.30, abbiamo ricevuto diverse segnalazioni di giocatori con valori di ping prossimi allo zero, o addirittura nulli. Se fosse vero, vorrebbe dire che i nostri pacchetti viaggiano più veloci della luce e, per quanto UE4 sia fantastico, non siamo ancora a quel punto! Chiaramente, qualcosa è andato storto.

Il problema riguarda una parte del nostro calcolo del ping. Eccolo di nuovo, per riferimento:
Ping = Tempo necessario perché il pacchetto viaggi fino al server + Tempo necessario perché il pacchetto viaggi fino al client - Frame time del server - Frame time del client

Il problema è come sottrarre il frame time del server. Quando consideriamo solo i pacchetti più veloci, finiamo per sottrarre troppo: se un pacchetto viene trattato dal server non appena arriva, possono bastare alcuni millisecondi per elaborarne il contenuto e mandare una risposta. Ma il frame time completo del server potrebbe essere cinque volte maggiore!

Ecco uno schema di come funziona:

Questo è un client con un framerate nettamente superiore rispetto al server. Dato che consideriamo soltanto i pacchetti verdi, questi arrivano appena prima che il server possa rilevarli. In questo caso, il server sta lavorando più velocemente del framerate di gioco, perciò ha un tempo d'attesa all'inizio del frame, prima d'iniziare a rilevare i pacchetti.

La linea rossa indica cosa stiamo sottraendo come framerate del server, ma il valore corretto sarebbe quello della linea gialla. È un valore chiaramente eccessivo, e questo spiega il ping nullo visualizzato da alcuni client.

A dire il vero, lo stesso problema poteva sorgere anche con il nostro vecchio metodo di considerare una maggiore varietà di pacchetti. Semplicemente, in quel caso era più probabile che il tempo sottratto in eccesso venisse mitigato dai pacchetti elaborati dopo un lungo tempo d'attesa.

Progetti futuri

Per i prossimi aggiornamenti, abbiamo pianificato diverse migliorie per aumentare la precisione del valore di ping. Come dicevo prima:

"Purtroppo, la maggior parte delle piattaforme non ci consente di sapere esattamente quando un dato pacchetto è stato ricevuto, quindi non sappiamo che valore sottrarre."


Fortunatamente, Linux lo consente! Dato che tutti i nostri server usano Linux, possiamo modificare il calcolo del ping in questo modo:

Ping = Tempo necessario perché il pacchetto viaggi fino al server + Tempo necessario perché il pacchetto viaggi fino al client - Tempo fra l'arrivo del pacchetto al server e la sua risposta - Frame time del client


Questo azzera ogni congettura sul frame time del server da sottrarre. Pensiamo che questo aumenterà di molto la precisione dei valori, ma ci resta ancora molto testing da fare.

Stiamo anche indagando su come aumentare la frequenza di rilevazione dei pacchetti sul client, in modo da rimuovere anche il frame time del client dai nostri calcoli.

Combinando queste due correzioni, ci auguriamo di poter mostrare un valore di ping molto più preciso

Per concludere

Spero che questa spiegazione sia stata utile per capire il nostro sistema di calcolo del ping e come intendiamo migliorarlo. Quando si mostrano informazioni come queste, la precisione è essenziale. Mostrare dei dati imprecisi può essere peggiore di non mostrarne nessuno. Quando i giocatori mancano un tiro, o trovano che il gioco vada "lento", devono sapere chiaramente se si tratta di un problema di ping prima di poterlo correggere.

Leggerò i vostri commenti per eventuali domande e sarò lieto di rispondere ogni volta che posso. Grazie dell'attenzione!