Sincronizzazione client a basso traffico con server in MMO


22

Sto implementando MMO in cui il giocatore vola nello spazio sulla sua astronave controllandolo con i tasti freccia e cooperando con altri giocatori.

Voglio implementarlo in modo che il giocatore sia in grado di schivare la sua nave dal razzo o qualcos'altro, quindi sto cercando di prevedere l'intero stato del gioco sul lato client usando lo stesso mondo simulando l'algoritmo utilizzato dal server. Quel mondo di gioco è scritto su C # e verrà chiamato direttamente all'interno del client (è scritto su Unity3D) e tramite CLR sul server C ++ (sotto Linux). Connessione tramite UDP.

Il problema è come mantenere, ad esempio, 1000 giocatori all'interno di una singola mappa (escludendo tutti gli altri oggetti di gioco, mob ...): Diciamo che dovrò:

  • sincronizzare il server con i client 50 volte al secondo
  • invia a ciascun cliente gli stati degli oggetti di gioco (e dei giocatori) che è in grado di vedere (entro un certo raggio)
  • deve inviare 100 oggetti a ciascun giocatore nel suo raggio di vista
  • deve inviare mediamente 50 byte per oggetto di gioco (è id, x, y coords, rotazione, stato ...)

quindi, dovrà disporre di tale larghezza di banda di rete: 1000 (client) * 50 (volte al secondo) * 100 (oggetti da inviare a ciascun giocatore) * 50 (byte per oggetto) = 250.000.000 di byte al secondo! È impossibile!

È possibile ridurre questo valore in qualche modo? Ad esempio, consentire ai client di simulare completamente i loro mondi di gioco (per un lungo periodo di tempo) e inviare loro solo input di altri client e sincronizzare mondi di gioco, diciamo, ogni pochi secondi, ma causerà strani problemi di desincronizzazione al guinzaglio a causa di calcoli float .

Ad ogni modo, come sono programmati tali giochi in modo comune? Grazie.


1
Mando solo informazioni logiche sugli oggetti (posizione del mondo, stato attuale (che è un byte) e così via) - nessuna grafica.
Slav

1
@Slav: Bello! Tutto quel cambiamento di bit mi ricorda i miei giorni di programmazione ASM.
Randolf Richardson,

1
Perché non "oggi"? :) Quando scrivo su AS3, Java, Lua, C # e devo affrontare prestazioni così scarse, mi manca C ++ e ricordo di ASM.
Slav

1
@Slav: Heheh, non ho fatto molto ASM di recente. La maggior parte delle cose per me sono in Java e Perl (principalmente mod_perl2) in questi giorni, ma mi piacciono molto anche queste lingue.
Randolf Richardson,

2
@Slav, hai scritto: "Quando scrivo su AS3, Java, Lua, C # e devo affrontare prestazioni così scarse, mi manca C ++ e ricordo di ASM". Dovresti imparare come usare correttamente Lua e C #, forse potresti trovare le prestazioni meno spaventose. Inoltre, lamentarsi del (presumibilmente) linguaggio di scripting più veloce là fuori è nella migliore delle ipotesi singolare ... È un gioco sull'analisi del genoma umano in tempo reale?
Raine,

Risposte:


20

Hai solo bisogno di circa 30 aggiornamenti (o anche meno forse 10 o 20) al secondo. interpolare le posizioni degli oggetti in movimento sul lato client. In generale, è necessario inviare i dati solo quando sono REALMENTE necessari. In WoW potresti ricevere più aggiornamenti dai giocatori con cui sei in un gruppo che dai giocatori che si trovano nella stessa posizione. Inoltre, se un altro giocatore è lontano da te, non ricevi più aggiornamenti al secondo su di lui.

Quindi, invia solo un'istantanea completa a ciascun giocatore quando si connette. Successivamente invia solo le modifiche agli oggetti di gioco. Se non si è verificato alcun cambiamento, non inviarlo.

Quindi, fai un uso intensivo di BitVector o di come potresti chiamarli per ridurre la quantità di dati non necessari! Esempio: puoi anche provare a scrivere un float usando solo un byte (in un intervallo da 0 a 1 o da -1 a 1) in modo da avere solo 256 o 128 valori diversi. Ma il giocatore non noterà alcun movimento a scatti grazie alle interpolazioni.

Guarda questo per un esempio con LidgrenLibrary su come comprimere i dati: http://code.google.com/p/lidgren-network-gen3/wiki/Optimization

Successivo: prova a ridurre il raggio di visione dei giocatori mentre si muovono e trasmetti solo informazioni importanti in quel momento. Quindi, quando si fermano, aumenta di nuovo il loro raggio visivo. È possibile utilizzare un sistema di hashing spaziale o un albero bsp per ridurre il sovraccarico di cercare oggetti "nel raggio di azione". Questa è una buona lettura per l'argomento: http://en.wikipedia.org/wiki/Collision_detection

Comprime anche i dati TE solo tu conosci la struttura dei dati e la coerenza temporale nei dati (che può e deve essere sfruttata). Un algoritmo generale come Bzip2, Deflate, qualunque cosa, dovrebbe essere usato, ma solo come fase finale di compressione!

Inoltre, per informazioni non critiche per il gioco, potresti anche utilizzare tecniche P2P aggiuntive. Esempio: un giocatore gioca l'animazione "ciao" (solo un effetto grafico) Il giocatore invia queste informazioni al server, ma il server non trasmette le informazioni agli altri giocatori. Invece questo effetto non critico viene inviato dal giocatore stesso agli altri client nel raggio di azione.

MODIFICA (a causa del commento):

Metodi aggiuntivi per ridurre il numero medio di bit al secondo per ciascun giocatore:

  1. Hai scritto che hai inviato "L'oggetto non è cambiato". Non c'è motivo di farlo. Se ti preoccupi della perdita di pacchetti (e non sincronizzi la tua simulazione per questo motivo) considera quanto segue: Ad ogni timestep fisso (es. 100, 200, 300, 400 ...) esegui lo hash dello stato di simulazione e invialo al server . il server conferma o invia indietro un'istantanea completa di tutti i dati.

  2. Per cose come i razzi o anche i giocatori, puoi impiegare non solo l'interpolazione ma anche l'estrapolazione per rendere la simulazione più realistica. Esempio "Razzo": invece di aggiornare con messaggi come "È ora in posizione x" basta inviare un messaggio una volta contenente quanto segue: "Razzo generato: posizione (vettore), Tempo (in corrispondenza del quale è stato generato il razzo), velocità ( vettore)". Quindi non devi nemmeno includere la rotazione perché la punta sarà sempre nella direzione della "velocità".

  3. Combina più comandi in un messaggio e non inviare mai messaggi più piccoli di 16-20 byte perché l'intestazione udp sarà più grande del messaggio stesso. Inoltre, non inviare pacchetti più grandi dell'MTU del protocollo perché la frammentazione rallenterà la velocità della trasmissione.


Oh, è una buona idea aggiornare alcuni oggetti più frequentemente di altri, usare P2P, degradare la precisione in virgola mobile, inviare solo le modifiche (il che non è banale per me perché intendevo sincronizzare periodicamente gli oggetti ma "l'oggetto non è cambiato" è l'informazione pure). Con tutte queste modifiche l'intera immagine sembra più realistica!
Slav

1
L'invio di avvisi di tipo "oggetto non è cambiato" potrebbe essere una tecnica utile a scopo di test in cui si desidera vedere come si comporta il gioco quando i giocatori sono attivi durante i periodi di maggiore affluenza, poiché ha il potenziale di imporre requisiti sull'elaborazione e sulla rete, ma ci sono ancora soluzioni migliori di questa (come la creazione di un demone autonomo che controlla un vero personaggio in-game, quindi eseguendo quel demone più volte da macchine diverse).
Randolf Richardson,

5

Ecco due approcci:

Primo:
passa alla fisica deterministica, invia i comandi del giocatore, le azioni ai, gli oggetti che vengono visualizzati e tutto ciò che non può essere determinato dal lato client ai clienti. Ciò deve includere i non-comandi, una conferma che fino a un certo momento non si applicano altro che i comandi che sono stati inviati e ricevuti.

Il client deve eseguire due o tre simulazioni simultanee.
1: si interrompe quando mancano i dati per il passaggio successivo.
2: continuare a utilizzare i dati di ipotesi e fornire lo stato utilizzato per il rendering. 3: Ogni volta che il numero 1 si interrompe, questa simulazione copia lo stato del numero 1, raggiungendo l'ora corrente e sostituendo il numero 2, che viene quindi rilasciato.

Se il recupero è abbastanza veloce, puoi tralasciare differenze tra il n. 2 e il n. 3 e rilasciare immediatamente i vecchi dati.

Secondo:
non usare la fisica deterministica, fai come sopra, ma invia "fotogrammi completi" una volta ogni pochi secondi. Puoi facilmente tralasciare completamente il trasferimento di oggetti temporanei come i proiettili.

In entrambi i casi potresti voler diffidare del fatto che il cliente preveda chiunque muoia, è un po 'sciocco vedere un avversario esplodere.

E +1 per fare i calcoli, troppe persone non riescono a fare semplici stime sull'uso delle risorse.


2
"Fisica deterministica" significa che non posso usare valori in virgola mobile o diversi passaggi di simulazione? Mi chiedo che la desincronizzazione critica possa avvenire se, ad esempio, un razzo passerà da una torretta nemica sul client ma la colpirà sul server (a causa di un'inesattezza in virgola mobile) che farà sì che il giocatore continui a combattere quella torretta fino al pacchetto di sincronizzazione del server in arrivo successivo (diversi secondi).
Slav

3
Significa numeri interi e passo temporale fisso. Teoricamente potresti deridere i punti float per comportarti, ma usare numeri interi è molto più semplice. Hai un punto con l'esempio del missile mancante, se usi una fisica non deterministica è probabilmente meglio lasciare che il server gestisca completamente la morte e trasmetta rapidamente i casi di morte / distruzione.
aaaaaaaaaaaa,

5

Poche domande prima.

I "razzi o qualcos'altro" sono intelligenti o stupidi? Se sono stupidi, tutto ciò di cui hai bisogno è il timestamp del fuoco, l'origine e il vettore per simulare il loro percorso. Se sono intelligenti quanto sono intelligenti? Puoi calcolare al momento del fuoco che colpiranno o mancheranno? In tal caso, puoi simulare l'intero percorso sul client. ("Al T13 il missile colpirà la nave perché il gioco ha perso il tiro di schivata / il tiratore ha segnato un colpo critico.")

In generale, tuttavia, non c'è praticamente alcun motivo per: A) avere una frequenza di clock di 50Hz, (la maggior parte dei tiratori riesce a cavarsela con 15-20 e MMO in meno). B) invia lo stato completo ad ogni fotogramma. (La rotazione di un missile nello spazio è importante? O puoi semplicemente supporre che il suo "fronte" sia orientato lungo il vettore che sta viaggiando?)

Trascorri del tempo con la previsione e l'interpolazione e vedrai precipitare la larghezza di banda. Un progetto su cui ho lavorato aveva una frequenza di aggiornamento di 10Hz e una rappresentazione dello stato dell'oggetto di 14 byte. (Comprimi tutto ciò che puoi! Credo che abbiamo usato 6 bit per definire la rotazione attorno al piano x e quindi altri 6 bit per un'inclinazione sopra / sotto quel piano, sembrava indistinguibile dall'invio di una matrice / quaternione reale.)

Un'altra cosa che puoi fare è dare la priorità agli oggetti. Mostra, forse ci sono 100 oggetti nel relativo set, ma conosci la sua vista sul server? Se qualcosa non è nella sua vista, puoi abbassare la frequenza di aggiornamento di un ordine di grandezza?

L'idea generale non è quella di fare una simulazione perfetta sul client, è impossibile, l'idea è quella di creare un gioco divertente in cui i giocatori non noteranno che non è una simulazione perfetta.

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.