Mark Abent torna in azione in una puntata dedicata al desync dei server!


 

TLDR

·         Mark Abent oggi ci illustra un bug del server.

·         Si tratta di un problema che si verifica quando un giocatore di disconnette mentre si trova seduto sul sedile di una nave qualsiasi.

·         Quando il giocatore in questione si disconnette e poi riconnette al server, gli altri client non riceveranno l’informazione aggiornata sulla sua posizione e lo continueranno a vedere seduto su quel sedile.

o   Questa situazione può portare ad una serie di exploit, come ad esempio la possibilità di uccidere il giocatore semplicemente sparando alla nave, anche se lui in realtà si troverà altrove.

·         Si verifica così un desync tra quello che vedono tutti gli altri client e quello che vedono il giocatore in questione ed il server stesso.

·         Il problema nasce dal fatto che, quando un giocatore si disconnette mentre è seduto all’interno di una nave, il server non ripulisce tutte le informazioni a lui relative e non informa gli altri client della sua disconnessione.

o   Si tratta di un bug parzialmente correlato alla persistenza, in quanto tutti i giocatori hanno un ID entità univoco e persistente che viene collegato al loro personaggio ed utilizzato per tracciare la loro posizione in giro per l’universo.

·         Il bug può essere sistemato semplicemente costringendo il server ad effettuare tutta la procedura di pulizia prima di avvisare gli altri client della rimozione del giocatore dall’universo di gioco.

·         In questo modo, quando il giocatore tornerà online lui non verrà più visualizzato all’interno della nave in cui si trovava in precedenza, eliminando così il desync.


Trascrizione

Salve a tutti e benvenuti a Bugsmashers! Io sono il vostro presentatore, Mark Abent. Sono Programmatore di Gioco Senior qui agli uffici di Los Angeles della CIG ed oggi vi porterò dietro le quinte di un fantastico bug.

Okay, al momento ci troviamo su Crusader e ho due client attivi: il client tre, o forse è il client due, e poi il client uno. La ragione per cui i client sono numerati due e tre è che il server dedicato rappresenta il numero uno, per cui abbiamo uno, due e tre. Dunque, ho due client e farò salire il terzo su una nave da debug. Normalmente dovrei andare ad un terminale e richiedere la nave, ma siccome sono pigro e non ho voglia di correre fin là, la farò spawnare… Facciamo spawnare una Gladius, woooh! Allora, abbiamo fatto spawnare una Gladius e facciamo teletrasportare automaticamente questo tizio al suo interno… Amo i controlli di debug! E poi abbiamo l’altro client… Andiamo a vedere dove si trova quella nave. Andiamo alla velocità della luce e… Oh, eccola. Ok, e c’è un tizio seduto al suo interno. Per ora è tutto abbastanza normale. Quello che faremo adesso è tornare al tizio seduto nel sedile e… Oh, il tizio sta correndo in giro. Corri! E ci disconnetteremo. Du du duuu! Arrivederci. Oh, mettiamo un punto di segnalazione. Disconnettiamoci. Ed ovviamente ho attivato i punti di segnalazione che avevo messo altrove perché c’erano altre cose in debugging.

Ok. Mentre i processi di questi tizio si arrestano, fatemi spiegare il bug che andremo a vedere oggi e perché ve lo sto mostrando. Sostanzialmente, il bug si verifica quando ci sono due persone, o diverse persone, ed una di queste, il tizio che si trova nella nave, si disconnette. Al suo ritorno, lui si rialzerà dal letto nella zona standard di Crusader. Però i computer di tutti gli altri, comprese le persone che prima stavano guardando il tizio nella nave, come questo che sto usando ora, lo vedranno ricomparire direttamente all’interno della nave. Per cui, sfortunatamente, tutti penseranno che lui si troverà nella nave, ad eccezione del server e del giocatore interessato. Per questi ultimi, il giocatore si troverà da qualche parte in giro per Crusader a farsi i fatti suoi. Pertanto, a questo punto ci ritroviamo con un desync tra quello che tutti gli altri vedono e quello che invece vedono il server ed il giocatore interessato. Inoltre, questa situazione causa un’altra serie di strani problemi. Ad esempio, è possibile uccidere il giocatore nell’astronave anche se lui non si trova fisicamente lì e sta correndo da tutt’altra parte. Improvvisamente, si ritroverà morto senza apparente motivo…

Beh, questo succede perché c’è un errore nella sua posizione riportata sul sistema. Quando questo tizio si sarà disconnesso del tutto vedremo esattamente cosa sta succedendo. In realtà, la disconnessione richiede un po’ di tempo. Dunque, quello che andremo a vedere è situato qui, nel codice relativo al giocatore. Quando un giocatore viene rimosso o distrutto, prima che questo accada dobbiamo effettuare una serie di passaggi di pulizia. Questa sezione del codice in particolare si occupa di questa pulizia e, come potete vedere qui, quando si effettua tale procedura diciamo al veicolo…Questo qui è quello che definiamo il controllo del sedile, perché… Che bricconata! Per farla breve, diciamo al veicolo di controllare tutti i suoi sedili e poi, sostanzialmente, di ripulire tutto quello che c’era. Specialmente il giocatore che li controllava, perché sta venendo rimosso dal server. Di contro, il server dovrebbe comunicare questa rimozione a tutti gli altri e richiamare il codice del server necessario a completare la procedura. Se però ci avete fatto caso, il server al momento sta lanciando il codice chiamato “ExitInit”, che invece è l’implementazione per la rimozione del giocatore dal sedile della nave. Quando questa procedura viene attivata, viene riprodotta una piccola animazione per teletrasportare il giocatore ad una posizione di uscita e fare qualsiasi altra cosa sia necessaria. Si tratta di un’implementazione che verrà lanciata su tutti i client. Tuttavia, al momento c’è un problema che impedisce l’avvio di questo codice sugli altri client e questo problema non è causato dal percorso del codice stesso.

Pertanto questo codice è responsabile dell’uscita del giocatore dal server e c’è poi un’implementazione che ha il compito di inviare a tutti i client l’istruzione riguardante l’avvio di suddetto codice. Invece di lanciare la nostra implementazione “ExitInit”, dovremmo lanciare… Nope… Il comando di uscita… Ah, dai… Ok, così… Magia… Dicevo, dovremmo lanciare il comando di uscita, che a sua volta lancerà l’esecuzione che effettuerà le procedure di uscita e quindi riprodurrà le animazioni necessarie, oltre a mandare l’avviso dello stato di uscita a tutte le altre macchine. Ma dal momento che il server non esegue questa porzione specifica del codice, essa viene sostanzialmente ignorata ed il server si limita a rimuovere il giocatore in uscita, mentre tutte le altre macchine non vengono informate della cosa e quindi non sanno che questo tizio non dovrebbe essere più visibile in gioco.

Allora, sta ancora effettuando le operazioni di chiusura e… Do do dooo! Ok, resettiamo. Idealmente, qui dovremmo cambiare il codice in uscita così che possa comunque lanciare l’intera serie di istruzioni, ma senza tralasciare di mandare questa informazione anche a tutti gli altri client. Allora, adesso questo tizio è tornato online, mentre quest’altro sta ancora guardando la nave, che adesso è vuota. Ora riconnettiamoci e vediamo il bug. Eccolo qui. Per assicurarci di sistemarlo, dobbiamo controllare che il server esegua tutte le operazioni di uscita ed in più vogliamo che vengano saltate le transizioni, perché non vogliamo che venga riprodotta l’animazione di uscita e vogliamo forzare l’intero codice. Ora, un’altra cosa che avremmo potuto fare, invece di trasmettere l’evento a tutti i client… Avremmo potuto far fare la cosa ai client stessi. In questo modo, sarebbero stati loro stessi ad eseguire le procedure di uscita, invece di obbligare il server ad informarli della cosa. La differenza in tutto ciò è che vogliamo che il server abbia il controllo su tutto, soprattutto a causa della Persistenza. Quello che voglio dire è che ogni entità ha lo stesso ID e dobbiamo assicurarci che tutti siano aggiornati su quello che sta succedendo, per evitare che soltanto alcuni client siano consapevoli di determinati eventi. Fare in modo che sia il server a gestire queste cose ci dà sempre un vantaggio in più.

Ora, altrove c’è un’altra porzione di codice che gestisce la rimozione del giocatore quando questo si trova seduto su un sedile, ma al momento non resetta correttamente il suo ID entità ed è per questo che dobbiamo dire al server di avvisare tutti di questo evento. Ed è tanto più importante perché, prima della rimozione delle entità, spetta al server dire cosa debba essere rimosso. Per cui successivamente il servizio avvisa i sistemi di rimuovere questo tizio dal sedile e di cancellarlo. Fatto questo, ci dobbiamo assicurare che tutti i client seguano la stessa logica. Ricarichiamo il livello. Allora, questo tizio sta spawnando, per cui crede di essere nel letto ed il server lo vede nel letto. Tuttavia, gli altri client credono che lui sia ancora agganciato alla nave… Ed ha persino le animazioni non funzionanti! Per cui abbiamo un desync tra tutti gli altri client e questo tizio.

Ok, proviamo ad usare il nostro fix e vediamo cosa succede. Compila, compila, compila. Questa è la parte che non vi mostriamo mai. Già, potremmo anche riuscire a sistemare il bug, ma ricompilare tutto richiede del tempo. Poi dobbiamo far ripartire i server, verificare che il fix funzioni, quindi dobbiamo passarlo al QA per assicurarci che verifichino che non ci siano altri problemi. Fatto tutto questo, se non ci sono altri problemi, uno o due giorni dopo possiamo rilasciare la correzione. Oppure, può saltare fuori un altro bug semplicemente perché abbiamo scoperto un altro problema che prima era nascosto dall’altro bug. Ah, game design… E’ un processo davvero duro. Ahah.

Ok, siamo tornati online. Procediamo. Spawniamo la Gladius. Poi facciamo comparire l’altro tizio, usiamo qualche trucchetto magico per farlo arrivare velocemente alla nave. Ok, dove è spawnata?… Eccola. Wiiii! Ok, ci siamo. Ora usciremo nuovamente dal gioco. Bam! Ora stiamo elaborando l’uscita, per cui il server dirà a tutti gli altri client remoti che questo tizio se ne è andato. Come potete vedere, se ne è andato. E… Do do doooo! Gli altri tizi se ne vanno. E mi sono appena accorto che… Vero, in realtà, questo codice attiva le transizioni… Dove non vogliamo che ci siano. Ok. Per essere super sicuri che non ci siano altri problemi, rimuoveremo questa sezione prima di inviare il nuovo codice. In questo modo anche il client locale potrà eseguire questa porzione di codice. Tuttavia, sarà ancora il server a gestire l’operazione di uscita dal gioco. Per cui il client locale si assicurerà di trovarsi nello stato di uscita ed il server si occuperà di ripulire tutto. In ogni modo, il bug sarà scomparso. E quando questo tizio… Do do dooo! … Oh no, devo passare sul client due… Si disconnetterà per poi riconnettersi, non sarà più presente nel sedile e tutte le macchine sapranno che si troverà nel suo lettino su Port Olisar. Do do dooo! Okay, come potete vedere si è riconnesso e non figura più nella nave. Per cui abbiamo risolto il bug e sistemato il problema. Spero che questa puntata vi sia piaciuta ed arrivederci al prossimo appuntamento. Uhuh.

Outro

Dunque, come avete visto c’era un bug sul server che impediva la corretta rimozione dei giocatori dal server quando questi si trovavano in un sedile di una nave durante la procedura di uscita. Quando succedeva ciò, il server rimuoveva effettivamente il giocatore dal gioco, ma non avvisava le altre macchine dell’evento. Tutti i nostri sistemi vengono gestiti direttamente dal server, per cui se il server non avvisa i client di un qualche avvenimento in corso, la situazione diventa instabile. Anche se il server e la macchina locale sanno che il giocatore ha lasciato il sedile, tutti gli altri penseranno che lui si trovi ancora lì e quando quel giocatore tornerà online, dal momento che gli ID entità collegati ai giocatori sono numeri persistenti, tutti lo vedranno ancora nel sedile in cui si trovava in precedenza, anche se la sua posizione reale sarà a chilometri di distanza da quel sedile. Pertanto, dal loro punto di vista lui si troverà ancora all’interno di quel veicolo anche se non sarà così. Adesso, invece, il server informerà correttamente tutte le macchine del fatto che quel giocatore se ne sia andato e tutti vedranno quel sedile vuoto, per cui quando il giocatore in questione si riconnetterà, lui non sarà più lì e questo impedirà l’utilizzo di exploit a suo danno. Spero che voi ragazzi abbiate gradito questo bug ed arrivederci alla prossima puntata!

Traduzione e trascrizione a cura di Darnos.
Articolo originale disponibile presso le Roberts Space Industries.