Megapack: come Factorio ha risolto il problema del multiplayer da 200 giocatori

Megapack: come Factorio ha risolto il problema del multiplayer da 200 giocatori
Nel maggio di quest'anno ho partecipato come giocatore Eventi MMO di KatherineOfSky. Ho notato che quando il numero di giocatori raggiunge un certo numero, ogni pochi minuti alcuni di loro "cadono". Fortunatamente per te (ma non per me), ero uno di quei giocatori ogni voltaanche con una buona connessione. L'ho presa come una sfida personale e ho iniziato a cercare le cause del problema. Dopo tre settimane di debug, test e correzione, il bug è stato finalmente risolto, ma il viaggio non è stato così facile.

I problemi nei giochi multiplayer sono molto difficili da rintracciare. Di solito si verificano con parametri di rete molto specifici e con stati di gioco molto specifici (in questo caso, oltre 200 giocatori). E anche quando un problema può essere riprodotto, non può essere adeguatamente debuggato perché l'inserimento di punti di interruzione interrompe il gioco, incasina i timer e di solito causa il timeout della connessione a causa di un timeout. Ma grazie alla perseveranza e ad uno strumento meraviglioso chiamato goffo Sono riuscito a capire cosa sta succedendo.

In breve, a causa di un bug e di un'implementazione incompleta della simulazione dello stato di ritardo, il client a volte si trovava nella situazione in cui doveva inviare un pacchetto di rete in un ciclo di clock, costituito da azioni di input del giocatore per la selezione di circa 400 entità di gioco ( lo chiamiamo "megapacchetto"). Successivamente, il server non solo deve ricevere correttamente tutte queste azioni di input, ma anche inviarle a tutti gli altri client. Se hai 200 clienti, questo diventa rapidamente un problema. Il canale verso il server si intasa rapidamente, provocando la perdita di pacchetti e una cascata di pacchetti nuovamente richiesti. Il rinvio delle azioni di input fa sì che più client inizino a inviare megapacchetti e la loro valanga diventa ancora più forte. I clienti di successo riescono a riprendersi, tutto il resto crolla.

Megapack: come Factorio ha risolto il problema del multiplayer da 200 giocatori
Il problema era piuttosto fondamentale e mi ci sono volute 2 settimane per risolverlo. È piuttosto tecnico, quindi spiegherò i succosi dettagli tecnici di seguito. Ma prima devi sapere che dalla versione 0.17.54, rilasciata il 4 giugno, nonostante temporanei problemi di connessione, il multiplayer è diventato più stabile e il ritardo nell'occultamento è molto meno buggato (meno frenate e teletrasporto). Inoltre, ho cambiato il modo in cui i ritardi di combattimento vengono nascosti e spero che questo li renda un po' più fluidi.

Mega Pack multigiocatore - Dettagli tecnici

Per dirla semplicemente, il multiplayer in un gioco funziona in questo modo: tutti i client simulano lo stato del gioco ricevendo e inviando solo l'input del giocatore (chiamato "azioni di input" Azioni di input). Il compito principale del server è trasferire Azioni di input e garantire che tutti i client eseguano le stesse azioni nello stesso ciclo. Puoi leggere di più a riguardo nel post. FFF-149.

Poiché il server deve prendere decisioni su quali azioni intraprendere, le azioni del giocatore si muovono lungo il seguente percorso: azione del giocatore -> client di gioco -> rete -> server -> rete -> client di gioco. Ciò significa che ogni azione del giocatore viene eseguita solo dopo aver effettuato un percorso di andata e ritorno attraverso la rete. Per questo motivo, il gioco sarebbe sembrato terribilmente lento, quindi quasi immediatamente dopo l'apparizione del multiplayer nel gioco, è stato introdotto un meccanismo per nascondere i ritardi. L'occultamento della latenza simula l'input del giocatore senza considerare le azioni degli altri giocatori e il processo decisionale del server.

Megapack: come Factorio ha risolto il problema del multiplayer da 200 giocatori
Factorio ha uno stato di gioco stato del gioco è lo stato completo della mappa, del giocatore, delle entità e di tutto il resto. Viene simulato in modo deterministico in tutti i client in base alle azioni ricevute dal server. Lo stato del gioco è sacro e, se mai inizia a differire da quello del server o di qualsiasi altro client, si verifica la desincronizzazione.

Ma stato del gioco abbiamo uno stato di ritardi Stato di latenza. Contiene un piccolo sottoinsieme dello stato principale. Stato di latenza non è sacro e rappresenta solo un'immagine di come sarà lo stato del gioco in futuro in base agli input del giocatore Azioni di input.

Per fare ciò, conserviamo una copia del file generato Azioni di input nella coda del ritardo.

Megapack: come Factorio ha risolto il problema del multiplayer da 200 giocatori
Cioè, alla fine del processo lato client, l'immagine assomiglia a questa:

  1. Applicare Azioni di input tutti i giocatori a stato del gioco il modo in cui queste azioni di input sono state ricevute dal server.
  2. Rimuovi tutto dalla coda di ritardo Azioni di input, a cui, secondo il server, sono già stati applicati stato del gioco.
  3. Elimina Stato di latenza e reimpostarlo in modo che appaia esattamente uguale a stato del gioco.
  4. Applica tutte le azioni dalla coda di ritardo a Stato di latenza.
  5. Basato sui dati stato del gioco и Stato di latenza rendere il gioco al giocatore.

Tutto questo si ripete in ogni misura.

Troppo difficile? Non rilassarti, non è tutto. Per compensare le connessioni Internet inaffidabili, abbiamo creato due meccanismi:

  • Ticchettii saltati: quando il server lo decide Azioni di input verrà eseguito nel tatto del gioco, quindi se non ha ricevuto Azioni di input qualche giocatore (ad esempio, a causa di un maggiore ritardo), non aspetterà, ma informerà questo cliente “Non ho tenuto conto del tuo Azioni di input, proverò ad aggiungerli nella barra successiva. Questo viene fatto in modo che a causa di problemi con la connessione (o con il computer) di un giocatore, l'aggiornamento della mappa non rallenti per tutti gli altri. Vale la pena notare che Azioni di input non vengono ignorati, ma semplicemente rinviati.
  • Latenza di andata e ritorno completa: il server tenta di indovinare qual è la latenza di andata e ritorno tra client e server per ciascun client. Ogni 5 secondi, negozia un nuovo ritardo con il client secondo necessità (a seconda di come si è comportata la connessione in passato) e aumenta o diminuisce di conseguenza il ritardo di andata e ritorno.

Di per sé questi meccanismi sono abbastanza semplici, ma quando vengono utilizzati insieme (cosa che spesso accade con problemi di connessione), la logica del codice diventa difficile da gestire e presenta molti casi limite. Inoltre, quando questi meccanismi entrano in gioco, il server e la coda di ritardo devono implementare correttamente uno speciale Azione di input dal titolo Interrompi movimento nel segno successivo. Grazie a questo, in caso di problemi di connessione, il personaggio non potrà correre da solo (ad esempio sotto un treno).

Ora devo spiegarti come funziona la selezione delle entità. Uno dei tipi passati Azione di input è un cambiamento nello stato di selezione di un'entità. Dice a tutti su quale entità il giocatore ha passato il mouse. Come puoi vedere, questa è una delle azioni di input più frequenti inviate dai client, quindi per risparmiare larghezza di banda l'abbiamo ottimizzata in modo che occupi il minor spazio possibile. Ciò viene implementato in questo modo: quando viene selezionata ciascuna entità, invece di memorizzare coordinate assolute della mappa ad alta precisione, il gioco memorizza un offset relativo a bassa precisione rispetto alla selezione precedente. Funziona bene perché la selezione del mouse di solito avviene molto vicino alla selezione precedente. Ciò fa sorgere due importanti requisiti: Azioni di input non devono mai essere saltati e devono essere eseguiti nell'ordine corretto. Questi requisiti sono soddisfatti stato del gioco. Ma dal momento che il compito stato di latenza nel "sembrare abbastanza buono" per il giocatore, non è soddisfatto nello stato di ritardo. Stato di latenza non tiene conto molti casi limiteassociato al salto degli orologi e alla modifica dei ritardi di trasmissione di andata e ritorno.

Puoi già indovinare dove andrà a finire. Finalmente stiamo iniziando a vedere le cause del problema dei megapacchetti. La radice del problema è che si basa la logica di selezione delle entità Stato di latenzae questo stato non sempre contiene le informazioni corrette. Quindi il megapacket viene generato in questo modo:

  1. Il giocatore sta riscontrando problemi di connessione.
  2. Entrano in gioco i meccanismi per saltare i cicli e regolare il ritardo della trasmissione di andata e ritorno.
  3. La coda dello stato di ritardo non tiene conto di questi meccanismi. Ciò fa sì che alcune azioni vengano rimosse prematuramente o eseguite nell'ordine sbagliato, risultando in un errore Stato di latenza.
  4. Il lettore non ha problemi di connessione e simula fino a 400 cicli per mettersi al passo con il server.
  5. In ogni ciclo, una nuova azione viene generata e preparata per essere inviata al server, modificando la selezione dell'entità.
  6. Il client invia un megapacchetto di oltre 400 modifiche alla selezione dell'entità al server (e anche con altre azioni: stato di attivazione, stato di camminata, ecc. ha sofferto di questo problema).
  7. Il server riceve 400 azioni di input. Poiché non è consentito saltare una singola azione di input, istruisce tutti i client a eseguire queste azioni e le invia sulla rete.

L'ironia è che il meccanismo progettato per risparmiare larghezza di banda ha prodotto enormi pacchetti di rete.

Abbiamo risolto questo problema risolvendo tutti i casi limite di aggiornamento e il supporto delle code di ritardo. Anche se ci è voluto un po' di tempo, alla fine valeva la pena farlo bene piuttosto che affidarsi a rapidi hack.

Fonte: habr.com

Aggiungi un commento