UNIVERSITÀ DEGLI STUDI DI PARMA Dipartimento di Matematica e Informatica Corso di Laurea in Informatica Progetto MaNGOS: Un server open source per videogiochi online Candidato: Simone Boni Relatore: Prof. Enea Zaanella Anno Accademico 2013/2014 Ringrazio mia moglie Elena e tutta la mia famiglia che ha sempre creduto in me, mi ha sempre sostenuto, mi è stata vicina, mi ha sempre incitato a non fermarmi mai ed è sempre stata pronta a festeggiare quando era momento. Un ringraziamento ai colleghi di lavoro che hanno fatto di tutto per lasciarmi lo spazio e i tempi per concludere questa tappa della mia carriera universitaria. Dedicato al mio papà, che è sempre stato il migliore tra i maestri. Introduzione Fin dall'adolescenza ho avuto una particolare attrazione per il mondo dei computer e dei videogiochi, tanto che la mia carriera scolastica si è poi incentrata nell'approfondimento delle mie conoscenze sul mondo della programmazione e costruzione dei software. Sullo sfondo di quella che poi è diventata la mia attività lavorativa, ho sempre avuto la curiosità di scoprire come viene realizzato un videogioco e, da qui, la mia spinta a prendere un caso reale da poter toccare con mano e su cui poter fare esperimenti. Ciò su cui mi sono orientato è un videogioco online molto in voga e famoso di cui sono state create anche versioni open source e di libero accesso. Lo scopo della tesi è di analizzare uno di questi emulatori per scoprire come l'architettura client-server viene adattata e sfruttata per lo sviluppo dei giochi online. Non analizzerò, quindi, gli aspetti graci come i modelli 3D, la costruzione degli ambienti di gioco, i problemi di rendering e gestione delle texture, ecc., ma focalizzerò il mio studio sulla costruzione dei componenti che consentono l'interazione tra utente e ambiente del gioco, tra più utenti nello stesso ambiente e altre caratteristiche legate all'architettura di gestione dei vari oggetti che interagiscono nel videogioco. Il videogioco che ho scelto per lo studio, e sul quale ho anche trascorso parecchie ore della mia vita, è il celebre, e anche discusso, World Of WarcrafT M della software house Blizzard Entertainment. Questo gioco appartiene alla categoria dei MMORPG (Massive(ly) Multiplayer Online Role-Playing Game, ovvero gioco di ruolo in rete multi giocatore di massa), un gioco di ruolo che viene svolto tramite Internet contemporaneamente da più persone che, dalla sua prima uscita nel 2004, ha raccolto più di dieci milioni di utenti con un picco massimo di dodici milioni di utenti nel 20101 . La tesi si divide in 6 capitoli: • Il primo capitolo fornisce alcuni preliminari sui giochi di ruolo online. Vengono illustrate brevemente le nozioni di base dei giochi di ruolo e 1 I dati sugli utenti sono stati tratti da Wikipedia 5 le caratteristiche di quello preso in analisi. Nel capitolo si illustreranno anche le informazioni sull'emulatore usato e analizzato per ricreare l'ambiente di gioco. • Il secondo capitolo illustra la struttura dell'emulatore, analizzando la sua installazione e i componenti che lo costituiscono. • Il terzo capitolo mostra la struttura del database, illustrando le relazioni tra gli oggetti e la loro costruzione. • Il quarto capitolo spiega gli sviluppi eettuati e le analisi sui dati per raggiungere gli obiettivi di sviluppo. • Il quinto capitolo presenta un esempio di server con emulatori per il videogioco in oggetto. • Nel capitolo conclusivo verrà brevemente riassunto il lavoro fatto Indice Introduzione 4 1 World of WarcraftT M ed emulatore MaNGOS 1.1 I MMORPG . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 World Of WarcraftT M . . . . . . . . . . . . . . . . . . . . . 1.2.1 Struttura del gioco . . . . . . . . . . . . . . . . . . . 1.2.2 Personaggi . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3 Espansioni . . . . . . . . . . . . . . . . . . . . . . . . 1.2.4 Architettura software . . . . . . . . . . . . . . . . . . 1.3 Progetto MaNGOS . . . . . . . . . . . . . . . . . . . . . . . 1.3.1 Storia della sua evoluzione . . . . . . . . . . . . . . . 1.3.2 Logica operativa della community . . . . . . . . . . . 1.3.3 Limitazioni nello sviluppo derivanti da oggetti Close Source . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Il database 2.1 Studio del Database . . . . . . . . . . . . . . . 2.1.1 Dierenze tra i cores . . . . . . . . . . . 2.1.2 Dierenze tra InnoDB e MyIsam . . . . 2.1.3 Relazioni fra macro-oggetti del database 2.2 Esempio di analisi . . . . . . . . . . . . . . . . . . . . . . . . . . 8 8 9 9 10 10 11 11 12 13 . 16 . . . . . . . . . . 17 17 18 19 20 26 3 Struttura dell'emulatore 3.1 Ambiente Server . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Repository GitHub . . . . . . . . . . . . . . . . . . . . . . . 3.3 Download, compilazione, installazione e prima congurazione 3.4 Le componenti del server . . . . . . . . . . . . . . . . . . . . 3.4.1 Realmd . . . . . . . . . . . . . . . . . . . . . . . . . 3.4.2 Mangosd . . . . . . . . . . . . . . . . . . . . . . . . . 3.5 I le DBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 29 31 31 33 33 34 44 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . INDICE 7 4 Implementazioni 4.1 Tipologie di implementazione . . . . 4.2 Modalità di implementazione . . . . . 4.2.1 Database script . . . . . . . . 4.2.2 C++ in ScriptDev2 . . . . . . 4.2.3 Lua script . . . . . . . . . . . 4.3 Ricerca e risoluzione . . . . . . . . . 4.3.1 Errore di cambio fase . . . . . 4.3.2 Personaggio senza animazioni 4.4 Pull Request sul repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 48 48 49 49 50 50 52 57 74 5 Un esempio di community 5.1 Un esempio di Server Privato . 5.1.1 Organizzazione . . . . . 5.1.2 Bugtracker e changelog . 5.1.3 il sistema delle donazioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 77 78 78 79 . . . . . . . . . . . . 6 Conclusioni 81 Bibliograa 83 Glossario 84 Capitolo 1 World of WarcraftT M ed emulatore MaNGOS In questa sezione si andranno a presentare i concetti dei giochi di ruolo online, si presenterà il gioco considerato e la community dell'emulatore analizzato. Per una breve descrizione dei principali termini utilizzati si rimanda al glossario. 1.1 I MMORPG Un Massive(ly) Multiplayer Online Role-Playing Game (MMORPG), ovvero gioco di ruolo in rete multi giocatore di massa, è un gioco di ruolo che viene svolto tramite Internet contemporaneamente da più persone reali. I MMORPG possono essere di vario tipo, sia che utilizzino sosticati software, sia che utilizzino un browser web, sia che usino un semplice emulatore di terminale telnet (MUD, MUSH, MOO e simili). Negli ultimi anni gli sviluppatori di videogiochi, hanno ampiamente arricchito la panoramica dei MMORPG esistenti sul mercato, orendo ai giocatori ambientazioni grache curate nei minimi dettagli (città, strade, paesaggi immensi...), sfruttano le più moderne tecnologie in termini di texture e graca 3D. Molto comune in questo tipo di giochi è l'ambientazione fantasy: il mondo è popolato da diverse razze (el, orchi, nani, umani, ecc) o anche personaggi simili a noi che possono essere scelti dal giocatore e che possono essere suddivise in fazioni nemiche. Esiste anche l'ambientazione reale: con personaggi simili a noi (uomini) dove in alcuni si può, ad esempio, andare a cavallo o in macchina, sfruttando innovazioni della tecnologia 3D. 1.2 World Of WarcraftT M 9 L'eterna contrapposizione tra bene e male è rappresentata principalmente dalla lotta di giocatori contro mostri ma in ogni MMORPG, c'è sempre un obiettivo che va al di là della sda con i mostri e l'ambiente (in inglese Player versus Monster (PvM) o Player versus Environment (PvE)): i mostri in effetti rappresentano il mezzo per far crescere di livello il proprio personaggio, in modo che possa poi essere più forte per l'obiettivo nale, ad esempio conquistare un castello e mantenerne il possesso contro gli attacchi degli altri giocatori oppure combattere contro la fazione nemica e ottenere punteggi. Per questo particolare aspetto, questo tipo di giochi possono essere considerati come Never-Ending Games, perché soggetti a costanti aggiornamenti nel tempo e perché non presentano una ne ben precisa.[10] 1.2 World Of WarcraftT M World of WarcraftT M (letteralmente "il mondo di Warcraft", spesso abbreviato in WoW) è un videogioco fantasy tridimensionale di tipo MMORPG, giocabile esclusivamente con l'utilizzo di Internet e con il pagamento di un canone. Sviluppato dalla Blizzard EntertainmentT M , è stato pubblicato il 23 novembre 2004. 1.2.1 Struttura del gioco Le dinamiche di gioco di World of Warcraft riprendono quelle tradizionali dei videogiochi di ruolo online. Il giocatore agisce nel mondo virtuale per mezzo di un avatar ("personaggi" o "PG"), interagendo con personaggi controllati dal computer (in inglese Non Playing Character (NPC)), svolgendo una serie di missioni che gli vengono via via assegnate. Procedendo di missione in missione, progredisce nelle proprie capacità di combattimento e acquisisce nuovi oggetti che può utilizzare direttamente, commerciare con NPC o altri giocatori. Parallelamente, può acquisire capacità secondarie (per esempio imparare a creare pozioni magiche con le erbe, a forgiare armi e armature, a pescare, a cucinare e così via). Rispetto ai tradizionali MMORPG, in cui i giocatori sono costretti a molte ore di azioni ripetitive per progredire, in World of WarcraftT M il ritmo di gioco è molto più veloce, e le "perdite di tempo " tipiche di questo genere sono ridotte al minimo. World of WarcraftT M utilizza diversi server chiamati "reami". I reami hanno caratteristiche diverse per far sì che il giocatore possa scegliere lo stile di gioco che più gli si addice (Normale, Player versus Player, Roleplaying, Roleplaying Player versus Player). 1.2 World Of WarcraftT M 10 1.2.2 Personaggi Il personaggio ottiene dei punti, denominati comunemente Experience Points (EXP), ad ogni avventura completata, con l'uccisione delle creature nemiche nelle quali si imbatte o semplicemente esplorando territori sconosciuti. Scopo del personaggio è aumentare il proprio livello e la qualità del proprio equipaggiamento, al ne di confrontarsi in sde sempre più dicili. Ogni personaggio è caratterizzato da elementi tipici dei giochi di ruolo, come caratteristiche, abilità, talenti, magie o mosse speciali, una classe. Ogni classe ha abilità dierenti, un diverso stile di gioco e la possibilità di utilizzare o meno alcuni tipi di equipaggiamento, come ad esempio cotte di maglia, stoa, cuoio, corazza a piastre o spade, mazze, bastoni, pugnali, armi da pugno, archi, fucili, ecc. [11] 1.2.3 Espansioni Dopo la prima pubblicazione del gioco sono state realizzate delle espansioni ovvero delle aggiunte di contenuti al gioco originale, principalmente per contrastare la caratteristica di tutti i giochi di ruolo per cui arrivati al massimo livello raggiungibile e completate tutte le sde possibile il giocatore tende a cercare nuove sde da arontare, magari cambiando videogioco. Le espansioni sono quindi mirate a non perdere i giocatori abbonati quando "niscono" il gioco, ma a orono nuove sde per avere eettivamente l'eetto never-ending game. Attualmente sono state rilasciate cinque espansioni che hanno di volta in volta aggiunto, modicato e ampliato le possibilità di gioco dei personaggi. La popolarità del gioco e la sua costruzione articolata di una story line di base che motivasse la costruzione del mondo di gioco, oltre a dare una caratterizzazione speciali a vari personaggi che si possono incontrare nel gioco, ha fatto sì che si costruisse attorno al gioco un vero e proprio mondo fantasy con tanto di romanzi. Le espansioni, nel tempo, sono diventate la storia dell'evoluzione del mondo di Warcraft nell'eterna lotta tra bene e male, tema classico dei romanzi fantasy. L'ambiente di gioco non è quindi solo frutto di una buona creatività da parte degli sviluppatori, ma frutto di un accurato studio e lavoro costruttivo per rendere gli ambienti, i personaggi e gli oggetti parte di un mondo fantastico con una storia. Questo, a dierenza di altri giochi MMORPG è stato sicuramente un punto di maggiore forza e motivo dei tanto successo nel corso degli anni. 1.3 Progetto MaNGOS 11 1.2.4 Architettura software Il videogioco è strutturato nel classico stile client-server. C'è quindi una continua e indispensabile comunicazione tra il client installato sul computer del giocatore e il server a cui è collegato. La velocità di comunicazione e la latenza della connessione di rete è una componente fondamentale per avere sessioni di gioco uide e senza ritardi nell'esecuzione delle azioni. Essendo sia il client che il server "close source" non è stato possibile analizzare le logiche di scambio delle informazioni tanto più che, anche effettuando analisi sul traco di rete, i pacchetti intercettati risultano essere cifrati o in formato non direttamente interpretabile. Le uniche informazioni che si posso carpire provengono dal client sono i dati di gioco contenuti nei le archivio che lo compongono. Il client, infatti, è corredato di tutte le informazioni grache e sonore che vengono visualizzate al giocatore durante le sessioni di gioco. Questa logica di dislocazione dei dati di gioco nel client consente di ridurre la mole di dati inviati dal server al client, lasciando più spazio per le sole informazioni essenziali alle azioni di gioco. Esistono alcuni strumenti che consentono di visualizzare in modo abbastanza user-friendly i contenuti di questi archivi, permettendo l'analisi degli oggetti che sono poi usati nel videogioco e le loro informazioni basilari per il funzionamento nelle logiche del gioco stesso. Queste informazioni risultano utili per interpretare gli eetti di azione (del client) e reazione (del server) che si vericano durante le sessioni di gioco, e ricostruire le logiche di funzionamento. 1.3 Progetto MaNGOS Il videogioco non nasce open source, ma la sua popolarità ha spinto alcuni programmatori a costruire degli emulatori open source che ricreano le funzionalità del gioco originale. Data la complessità che il videogioco ha raggiunto nel tempo, lo sviluppo di questi emulatori non ha mai raggiunto gli stessi livelli dei server originali ma le caratteristiche di base per poter funzionare sono state comunque realizzate. Dovendo costruire questi emulatori senza poter visionare il codice sorgente originale dei server uciali della software house, è di grande aiuto agli sviluppatori il gran numero di informazioni sui contenuti del gioco e sulle sue logiche di funzionamento che i videogiocatori hanno organizzato in alcuni portali dedicati con libero accesso. Un'altra motivazione che ha contribuito la creazione di questi emulatori è l'abbonamento mensile a pagamento che la software house richiede per 1.3 Progetto MaNGOS 12 poter usare il videogioco in oggetto. Gli emulatori open source non prevedono questo abbonamento e quindi risultano essere anche gratuiti per i giocatori, i quali sono consapevoli però che il videogioco non è completo di tutte le sue parti ma solo di quelle che sono state ricostruite. L'emulatore su cui ho focalizzato il mio studio è il progetto open source Massively Network Game Object Server (MaNGOS) scritto in C++ volto a creare un applicativo server per MMORPG. Il progetto è sotto licenza GPL e il codice sorgente delle nuove versioni viene costantemente rilasciato nel servizio git di GitHub. Attualmente il progetto supporta il protocollo di rete del MMORPG World of WarcraftT M . MaNGOS non è considerato un emulatore server di uno specico MMORPG, bensì è un progetto educativo per la programmazione ad oggetti in C++, a cui tutti gli utenti possono apportare il proprio contributo, aumentando così il patrimonio culturale di tutta la community di utenti/sviluppatori. 1.3.1 Storia della sua evoluzione Dalla creazione di MaNGOS (intorno al 28 agosto 2005) no al dicembre 2012 theLuda era il leader di MaNGOS. La sua idea originale di un progetto open-source educativo continua ancora oggi. Durante questo periodo si sono vericati diverse divisioni (come per tutti i grandi progetti), da cui sono nati i progetti Trinity, MangosR2 e CMaNGOS. Dopo l'hacking del sito, forum, server e GitHub nel maggio 2013, theLuda e gli altri sviluppatori di alto livello hanno deciso di abbandonare il progetto e alcuni hanno lasciato il gruppo per sempre. Durante questo periodo Antz chiese il permesso di mantenere il progetto in corso e con l'aiuto del MadMax ricostruirono il forum, il sito web e il repository GitHub. La perdita di theLuda come il leader e alcuni degli sviluppatori di alto livello è stato un duro colpo per la squadra MaNGOS, ma questa situazione non durò troppo a lungo. Nel luglio 2013 theLuda fu convinto a tornare come sviluppatore senior e consulente per il progetto MaNGOS. Da allora hanno continuato a sviluppare i cores ed espandere la loro base di conoscenze nonché incoraggiare e allenare la prossima generazione di esperti MaNGOS. In agosto 2013 il team MaNGOS raccolse le persone provenienti da altri progetti di emulatori morti. In settembre 2013 uno dei fondatori di un progetto esterno (wowAdict) si è unito al team per lavorare al progetto parallelo rinominato MangosVB. Gli obiettivi principali di questo progetto sono di realizzare un porting in VB.NET dei sorgenti C++ relativi ai vari cores di MaNGOS.[3] 1.3 Progetto MaNGOS 13 1.3.2 Logica operativa della community La community si è primariamente suddivisa in cinque progetti (core), uno per ogni espansione (esclusa l'ultima) più la versione originale, denominati: MaNGOS Zero (versione originale chiamata anche Vanilla); MaNGOS One (prima espansione chiamata anche The Burning Crusade o TbC); MaNGOS Two (seconda espansione chiamata Wrath of the Lich King o WotLK); MaNGOS Three (terza espansione chiama Cataclysm o Cata); MaNGOS Four (quarta espansione chiamata Mist of Pandaria o MoP). Le funzioni essenziali di ciascun progetto sono condivise e sviluppate partendo da MaNGOS Zero. Quando sono vericate essere stabili, vengono riportate anche sugli altri cores in modo da avere una distribuzione uniforme delle funzioni che stanno alla base di tutti i progetti. Questa strategia aiuta la community ad avere una distribuzione uniforme del codice sorgente prodotto e migliora le attività manutentive e correttive tra i vari cores. Le gure operative che lavorano sui vari progetti sono le seguenti: Community Manager; Junior Developer; Developer; Senior Developer; Eluna Developer; Donator; VIP Donator; Utente registrato. Il nodo attorno a cui tutto ruota è il forum della community, dove gli utenti esterni e i membri uciali del progetto MaNGOS possono confrontarsi, scrivere le note sulle evoluzioni, richiedere supporto nello sviluppo agli altri membri e condividere anche le scoperte o le innovazioni introdotte. Il forum risulta quindi lo strumento cardine per l'evoluzione del progetto. I membri uciali che ricoprono le varie gure operative descritte prima ad eccezione degli utenti registrati sono attivi giornalmente pur essendo poco più di una dozzina in tutto. Internamente si sono suddivisi i compiti principali, per evitare di fare lo stesso lavoro due volte. Gli utenti registrati che operano come satelliti attorno al progetto sono invece molti di più, ma sono anche molto meno costanti nelle attività. Tra questi ancora oggi sono attivi Antz e MadMax, già citati nella storia di MaNGOS. Indubbiamente la parte più complessa del progetto non è tanto la costruzione del codice ma la gestione di tutte le persone che direttamente o indirettamente operano su di esso. Evitare la scrittura delle stesse cose da più persone e assistere nelle richieste di supporto da parte degli utenti che si approcciano per la prima volta alle tematiche di sviluppo sono i temi che più frequentemente devono essere arontati. Navigando per qualche tempo nel forum e partecipando attivamente anche alle evoluzioni, ho notato alcuni proli di utenti che si approcciano al progetto. Le impressioni sono di una vasta gamma di approcci dierenti, come chi si presenta come un grande sviluppatore che ha fatto tante cose ma quando gli si chiede di estendere i suoi sviluppi alla community sparisce nel 1.3 Progetto MaNGOS 14 nulla oppure chi fa richieste di assistenza nella compilazione del progetto per la sua esecuzione e poi non torna a dare feedback sui suoi tentativi. Questo da una idea, se pur parziale e approssimata, di quanto la programmazione e sviluppo di applicativi sia spesso approcciata senza vere basi e in modo superciale. Attualmente la community si sta concentrando principalmente sullo sviluppo del server MaNGOS Zero con il focus principale all'ambiente Windows, creando quindi le librerie e l'ambiente di sviluppo su Visual Studio. Questa decisione probabilmente deriva anche dal fatto che spesso i potenziali sviluppatori esterni alla community hanno maggiore familiarità con i tool di sviluppo su Windows, che hanno un impatto graco molto più diretto dei gli strumenti disponibili in ambiente Linux. Per un programmatore alle prime armi con la gestione degli oggetti, delle librerie e le relative dipendenze, un ambiente di sviluppo come Visual Studio di Microsoft risulta avere un apprendimento più semplice e intuitivo sfruttando molti tool integrati di assistenza allo sviluppo come, a esempio, la ricerca della denizione dei metodi di una classe all'interno di tutto i sorgenti del progetto aperto. Le stesse operazioni però si possono svolgere anche in ambiente Linux, ma richiedono una maggiore conoscenza dei comandi shell. Nonostante questa preferenza per l'ambiente graco, sono comunque tenute aggiornate anche tutte le procedure Linux per la compilazione, che sfruttano CMake come motore per la gestione delle dipendenze e di costruzione degli oggetti. Lo strumento scelto dalla community per gestire ed organizzare i sorgenti dei vari progetti è il repository GitHub. In passato veniva utilizzato il servizio SVN, ma è stato poi rimpiazzato con quello attuale per questioni di miglior gestione delle revisioni. Ogni espansione, sul repository, è suddivisa in tre blocchi: • Server: questo repository contiene tutte le classi C++ essenziali del server e le librerie C++ che gestiscono tutte le funzionalità del gioco. • Database: questo repository contiene tutti gli script SQL per popolare il database MySQL con gli oggetti di gioco. • Scripts: Questo repository contiene una libreria C++ aggiuntiva di sup- porto per gli sviluppatori che si integra con il core del server per rendere funzionanti oggetti di gioco che necessitano di funzioni più avanzate o speciche rispetto a quelle generiche presenti nel core. Dato l'obiettivo del progetto MaNGOS, i sorgenti sono liberamente scaricabili e usabili in vari modi, dalla banale copia in locale diretta al più articolato 1.3 Progetto MaNGOS 15 fork sul proprio account GitHub per poter gestire la propria copia del progetto e svilupparla parallelamente a quella originale. Con questo metodo poi è possibile eettuare dei "Pull Request", ovvero trasferire le modiche fatte sulla copia nell'account personale di GitHub nel repository originale del progetto e contribuire quindi personalmente allo sviluppo del medesimo. I trasferimenti di modiche sul progetto originale non sono diretti, ma passano sotto la supervisione di alcuni moderatori che convalidano quanto proposto. In questo modo si evitano potenziali manomissioni o alterazioni errate del codice, lasciando comunque la possibilità a chiunque di contribuire allo sviluppo del progetto MaNGOS. Ritengo che la libertà di partecipazione ad un progetto così complesso, ma anche più in generale a qualsiasi progetto di sviluppo, da parte di persone qualsiasi che non sempre hanno un background o conoscenze sulla programmazione e sulle logiche di sviluppo di un applicativo solide, possa innescare un processo di autodistruzione se non viene controllato e gestito con accuratezza. La metafora del gigante con i piedi di argilla può essere molto calzante in quanto spesso un bug o un errore di programmazione in una funzione essenziale (causato da un programmatore inesperto) può inciare tutto il processo di funzionamento di un applicativo molto articolato. Un altro obiettivo della community è la gestione della documentazione del progetto. Essendo questo un progetto educativo la documentazione è fondamentale e di primaria importanza anché chi si approccia a MaNGOS possa apprendere gli esperimenti e le evoluzioni apportate, possa imparare e scoprire come realizzare un oggetto così complesso e articolato. Questo compito è tutt'altro che semplice, sia per l'eterogeneità delle persone che contribuiscono, sia per i continui cambiamenti evolutivi che il progetto stesso subisce. Durante la vita del progetto MaNGOS sono nati alcuni progetti paralleli da membri appartenenti alla community che hanno preferito intraprendere strade diverse da quelle che la community stessa voleva percorrere. Questi progetti sono il TrinityCore e il CMaNGOS. Queste biforcazioni hanno mantenuto la losoa open source di MaNGOS, ma hanno poi intrapreso percorsi di sviluppo dierenti dal progetto originale. Ho condotto qualche breve ricerca su questi progetti gli e alla ne ho raggiunto la conclusione che pur percorrendo strade dierenti, il risultato è abbastanza simile: uno sviluppo lento ma continuo, volto si a costruire un server quanto più funzionante possibile ma al tempo stesso sono anche delle continue fonti di nuove conoscenze per chi partecipa alle community. I membri uciali della community non percepiscono un salario sso e mensile, ma svolgono le attività nel tempo libero e come hobby al di fuori degli orari di lavoro. Sul forum è presente una funzione di donazione naliz- 1.3 Progetto MaNGOS 16 zata principalmente alla manutenzione dei server privati e del dominio del progetto. Non ci sono quindi aspetti commerciali o nanziari che tengono viva la community, ma la semplice voglia di ciascun membro nel contribuire liberamente all'evoluzione del progetto MaNGOS. 1.3.3 Limitazioni nello sviluppo derivanti da oggetti Close Source Come accennato in precedenza, sia il server che il client sono close source. Dei server uciali non esistono nemmeno i le binari per eventuali installazioni di test. Questo ha reso complicata la creazione delle funzioni "core" dei progetti. Una possibile apertura per ricostruire le funzionalità del gioco è data dal client, che pur essendo close source, contiene tutti i dettagli degli oggetti di gioco in le-database consultabili con opportuni strumenti. Per analizzare i le del client ho usato gli strumenti: • Mydbceditor: Questo tool consente di visualizzare e modicare i le- database che contengono i dati strutturati dei vari oggetti che nel gioco vengono visualizzati ed usati. • Ladik's MPQ Editor: Questo tool consente di visualizzare il contenuto dei le MPQ contenenti oggetti graci e multimediali quali texture, clip video, clip audio e altri contenuti visualizzati durante il gioco. Durante la fase di analisi degli oggetti di gioco ho potuto scoprirne le caratteristiche grazie a questi strumenti. Capitolo 2 Il database In questa sezione si presenterà la base di dati che contiene tutte le informazioni degli elementi del gioco. Per una breve descrizione dei principali termini utilizzati si rimanda al glossario. 2.1 Studio del Database Gli script di costruzione e popolazione del database e anche le guide di installazione sono costruiti per il database relazionale MySQL di Oracle. All'interno dei vari les sono presenti però anche alcune bozze per costruire la base di dati anche per il database PostgreSQL, ma questi ultimi non sono stati oggetto di analisi. La costruzione del server si suddivide in tre più uno schemi. I primi tre sono caratteristici del reame2 , contengono quindi informazioni speciche relative all'espansione di gioco del reame: • mangos: contiene le informazioni sugli oggetti di gioco, le creature, gli NPC, ecc... ovvero le componenti con cui il giocatore può interagire durante le sessioni di gioco. • character: contiene le informazioni relative ai personaggi che il gioca- tore ha creato, con tutte le informazioni di corredo sul loro avanzamento nel gioco partendo dai dati caratteristici, l'equipaggiamento (ovvero gli oggetti che il personaggio possiede), le quest che ha completato e tutte gli altri elementi che ha usato o incontrato durante le sessioni di gioco. 2 Il concetto di reame sarà meglio esplicitato nel prossimo capitolo. Anticipando brevemente il signicato, il reame è il processo che gestisce le interazioni tra giocatore e ambiente di gioco. 2.1 Studio del Database 18 • scriptdev2:contiene informazioni di supporto per gli script aggiuntivi del server, ovvero gli script per il funzionamento specico di alcuni eventi, oggetti o elementi che non sono gestiti gestiti con le normali funzioni basilari, ma sono realizzati separatamente. Il quarto schema, invece, contiene informazioni relative ai reami che il server ospita • realmd: contiene le informazioni per le connessioni ai reami quali i riferimenti dei server, le credenziali di accesso (account e password) e quanto serve per il processo di login, scelta del reame e del personaggio con cui giocare. Possiamo quindi trovare all'interno della stessa istanza del database la coesistenza di più reami con nomi di schemi opportuni per distinguerli e un singolo schema per le informazioni di connessioni ai reami. Nulla vieta però di gestire su istanze di database separate i singoli reami e il singolo schema con le informazioni sui reami stessi. In questo modo è possibile costruire una struttura scalabile e/o decentralizzata a seconda delle esigenze, dimensioni e numero dei reami disponibili per i giocatori. 2.1.1 Dierenze tra i cores Come descritto in precedenza, il progetto MaNGOS è suddiviso in diversi cores. Le dierenze nel database tra i cores sono essenzialmente di due tipi: • strutturali: ogni espansione aggiunge nuove tipologie di oggetti, am- bienti e attività che il giocatore può usare, esplorare e svolgere. Queste nuove funzionalità si riettono sulla struttura del database con l'aggiunta di colonne a tabelle esistenti o nuove tabelle. La logica delle colonne esistenti rimane immutata o al più viene dismessa quando una nuova funzionalità sostituisce una vecchia. Questa modalità di costruzione delle espansioni consente di preservare le strutture dei dati originali e introdurre quindi i nuovi elementi senza dover ricostruire tutta la struttura da zero mano a mano che si aggiungono le espansioni successive. • contenuti: ogni espansione aggiunge nuovi contenuti e può rende ob- soleti o non più usati alcuni contenuti precedenti. I dati quindi tendono sostanzialmente ad aumentare e mai ad essere rimossi. Anche in questo caso l'introduzione di nuove espansioni conserva i dati precedenti e non è quindi necessario reinserire tutti i dati precedenti per le nuove funzionalità. Al più sarà necessario aggiungere i nuovi valori di default 2.1 Studio del Database 19 per le eventuali nuove colonne delle tabelle che contengono i dettagli delle nuove funzionalità. L'evoluzione tra un core e quello successivo, ovvero il passaggio da una espansione a quella successiva, è abbastanza conservativo nei confronti dei dati e della struttura del database, per cui non dovrà mai essere necessaria la riscrittura completa di tutta la base dati per il passaggio ad una nuova espansione. 2.1.2 Dierenze tra InnoDB e MyIsam Il database relazionale MySQL utilizza due tipologie di motori per la gestione dei dati nelle tabelle: MyISAM e InnoDB. Gli script di costruzione e importazione dei dati nel database specicano per ogni tabella quale motore usare. Questa scelta costruttiva non è casuale ma ben studiata per orire il massimo delle prestazioni. Il motore MyISAM ha la caratteristica di essere molto performante per le letture di dati a scapito dei controlli di integrità referenziale. Infatti le tabelle con questo motore non possono avere chiavi esterne (o Foreign Key) utilizzate nel database relazionali per garantire la coerenza degli indici chiave tra tabelle. Sono tuttavia disponibili gli indici di tabella, per ottimizzare le ricerche. Inoltre per questo motore non esiste il concetto di transazione ma i lock dei dati avvengono per singola riga della tabella e non per blocco di operazioni sulla tabella.[7] Il motore InnoDB invece possiede tutte le caratteristiche richieste da una base dati relazionale (quindi sono disponibili le Foreign Key e tutti i controlli e peculiarità ad esse relative oltre alla gestione delle transazioni) ma la complessità delle operazioni per garantire le caratteristiche relazionali (Atomicità, Consistenza, Isolamento e Durabilità delle transazioni) richiedono maggiori risorse e, per tanto, queste tabelle possono risultare più lente sia in lettura che in scrittura.[7] La decisione sul motore con cui creare una tabella, quindi, dipende dalle modalità d'uso dei dati in essa contenuti. Se verranno fatte poche modiche ma tante letture potrebbe essere più indicato un motore MyISAM; se invece dovessero risultare maggiori le modiche e inserimenti di dati oltre ad essere necessaria la presenza di riferimenti coerenti a chiavi esterne (quindi l'uso di foreign key) la scelta ricadrebbe ovviamente sul motore InnoDB. Analizzando i le di script di costruzione e popolamento del database è possibile notare che gli schemi mangos e scriptdev2 contengono unicamente tabelle con motore MyISAM, mentre gli schemi characters e 2.1 Studio del Database 20 realmd contengono tabelle con entrambi i motori, a seconda della presenza o meno di relazioni fra tabelle. Questa costruzione lascia intendere che gli schemi mangos e scriptdev2 contengano dati che cambiano poco o niente durante l'esecuzione del server, mentre gli altri schemi hanno dati che sono modicati più frequentemente e sono in relazione tra loro. Nell'analisi dei componenti del server del prossimo capitolo sarà dettagliata maggiormente il motivo di questa dierenza costruttiva. 2.1.3 Relazioni fra macro-oggetti del database Schema realmd Questo schema contiene i dati relativi agli account dei giocatori e le informazioni relative ai reami disponibili. Gli account sono memorizzati nella tabella accounts e i dati essenziali in essa contenuti sono: • id: contiene il numero identicativo dell'account, usato in tutte le ta- belle che contengono dati ad esso relativi (come ad esempio la tabella characters nello schema mangosd). • username: contiene il nome dell'account scelto dal giocatore. Questo nome deve essere univoco. • gmlevel: contiene il valore che stabilisce il livello di autorizzazioni per l'account. Normalmente tutti gli account sono di tipo giocatore, esistono però altre due tipologie di autorizzazioni: Game Master e Administrator, che hanno più libertà di violare le meccaniche di gioco per scopi di gestione e assistenza ai giocatori. • expansion: contiene il valore che indica l'espansione a cui il giocatore è abilitato. Ovviamente questo valore abilita solo a espansioni uguali o precedenti a quella del reame a cui si è collegati. Non sarà mai possibile avere un giocatore abilitato ad una espansione più recente di quella congurata nel reame. I reami sono memorizzati nella tabella realmlist e i dati essenziali in essa contenuti sono: • nome: il nome del reame; • IP: indirizzo IP del reame; • porta: porta TCP del reame; 2.1 Studio del Database 21 Schema mangos Pur non essendoci dei legami espliciti fra le tabelle dello schema mangos, analizzando i dati e le strutture è possibile ricostruire le relazioni tra le tabelle e tra i contenuti delle tabelle stesse anche senza le foreign key. La verica della relazione reale tra gli oggetti sarà lasciata al reame durante l'esecuzione. Per l'analisi della struttura del database non sono disponibili online molte informazioni, ma la community ha realizzato un manuale PDF scaricabile3 [4] abbastanza esaustivo con le informazioni sulla struttura del database e di ogni singola tabella. Con questo manuale e portali internet dedicati è possibile ricostruire le relazioni logiche tra gli elementi codicati nel database. A titolo esemplicativo dalla mia analisi dei dati e delle relazioni logiche tra le tabelle dello schema mangos ne riporto una porzione partendo da quella che risulta essere la struttura portante del gioco: le quest (o missioni). Tutto parte dalla tabella quest_template4 la cui struttura contiene i dati per lo svolgimento e il completamento della quest. Delle 168 colonne di cui la tabella è composta, ne analizziamo solo le principali per il nostro obiettivo: scoprire come funziona la relazione tra una quest e gli oggetti in essa coinvolti. I campi di nostro interesse sono i seguenti: • entry: contiene il codice numerico identicativo della quest. Questo identicativo è usato in tutte le altre tabelle che hanno dei riferimenti a questa. • requiredClasses: contiene un bitmask che identica la classe del per- sonaggio che può accettare la quest (non tutte le quest sono libere, ma esistono quest che sono speciche di alcune classi di personaggio, ed è corretto che solo quelle classi di personaggio possano accettare le quest a loro dedicate). I valori possibili della bitmask sono deniti nel le ChrClasses.dbc. • requiredRaces contiene un bitmask che identica la razza del perso- naggio che può accettare la quest (non tutte le quest sono libere, ma esistono quest che sono speciche di alcune razze di personaggio, ed è corretto che solo quelle razze di personaggio possano accettare le quest a loro dedicate). I valori possibili della bitmask sono deniti nel le ChrRaces.dbc. 3 Il documento è consultabile a MaNGOS-Zero docs https://media.readthedocs.org/pdf/mangoszero-docs/latest/mangoszero-docs.pdf 4 le informazioni sono state identicate nel documento MaNGOS-Zero docs in data 10/11/2014 2.1 Studio del Database 22 • prevQuestId: contiene il codice numerico della quest che precede quel- la corrente e che deve essere completa prima di poter accettare quella attuale. • nextQuestId: contiene il codice numerico della quest successiva a quella corrente. • reqItemId1: contiene il codice numerico della colonna entry nella tabella item_template richiesto da questa quest. • reqItemCount1: contiene il numero di oggetti identicati dalla colonna reqItemId1 necessari per poter terminare la quest. • ReqCreatureOrGOId1: contiene il codice numerico della colonna entry nella tabella creature_template oppure nella tabella gameobject_template richiesto da questa quest. Queste due tabelle fan- no riferimento ad una unica colonna in quanto sono mutamente esclusive. • ReqCreatureOrGOCount1: contiene il numero di creature o oggetti identicati dalla colonna ReqCreatureOrGOId1 con cui è necessario interagire per poter terminare la quest. Da questo estratto della struttura relativa alla tabella delle quest è quindi possibile notare che non tutti i dati sono codicati all'interno del database, ma alcuni sono presenti nei le-database DBC estratti dal client e conservati nell'apposita cartella del percorso di esecuzione del reame di cui parleremo nel prossimo capitolo. Altre colonne, invece, possono contenere i codici identicativi degli altri oggetti (codicati nel database) che sono coinvolti e recuperare quindi le loro caratteristiche. Un altro esempio di come sono costruiti gli elementi del gioco all'interno del database è dato dall'analisi degli elementi nella tabella creature_template. Questa tabella contiene tutti gli elementi di tipo creatura (mostri da combattere, NPC e altre creature speciali). Per questi elementi esisto varie tabelle di supporto in quanto le azioni e le relazioni con gli altri elementi del gioco sono tante e complesse. La tabella che contiene la denizione di una creatura è quindi la tabella creature_template dove la colonna entry è chiave e consente di identicare in modo univoco una creatura. Questo codice sarà poi utilizzato in tutte le altre tabelle per le varie relazioni, come già abbiamo visto nella tabella delle quest. Oltre a questa tabella dove sono denite le informazioni di base e più generiche della creatura ce ne sono altre di supporto di cui, le principali e più rilevanti, sono: 2.1 Studio del Database 23 • creature_questrelation: denisce quale creatura inizia una quest, ovvero da quale NPC il giocatore deve recarsi per iniziare una specica quest. • creature_involvedrelation: denisce quale creatura termina la que- st, ovvero da quale NPC il giocatore deve recarsi quando ha completato gli obiettivi della quest per terminarla e ricevere la ricompensa. • creature_movement_template:denisce il percorso che una crea- tura percorre in modo autonomo. • creature_loot_template: identica quali oggetti la creatura può orire al giocatore quando viene uccisa. • creature_ai_scripts: contiene le informazioni per consentire alla crea- tura di eseguire azioni al vericarsi di determinati eventi. Questa tabella viene usata per il Database Scripting descritto nel quarto capitolo. • creature: contiene le informazioni su dove la creatura si trova nell'am- biente di gioco e il suo comportamento. All'interno di questa tabella è possibile trovare più volte il codice identicativo del template della creatura in quanto alcune di esse si possono trovare in zone dierenti del gioco. L'uso di una tabella template per la denizione delle caratteristiche di una creatura facilita la gestione delle sue caratteristiche quando viene replicata più volte. Da questa macro analisi è possibile costruire il seguente schema: Figura 2.1: Schema logico relazionale della tabella quest_template e sue dipendenze 2.1 Studio del Database 24 Come per le creature, anche altri elementi presenti nel database sfruttano una organizzazione simile dei dati, in modo da ottimizzare al massimo la gestione delle caratteristiche di ogni elemento cercando di rispettare quanto più possibile il paradigma della normalizzazione delle tabelle di un database. Schema characters In questo schema sono presenti le tabelle che contengono tutti i riferimenti e i dati dei personaggi che ogni giocatore ha creato. Queste tabelle sono costantemente lette e aggiornate dal reame e risultano quindi le più critiche per le performance. La tabella cuore di tutto il sistema di riferimenti è characters che contiene i dati di base di tutti i personaggi. A questa tabella sono poi collegate tutte le altre, che contengono dati aggiuntivi relativi ad ogni singolo personaggio. I dati basilari e più importanti contenuti in questa tabella sono: • guid: contiene l'identicativo univoco del personaggio. Questo iden- ticativo sarà usato in tutte le tabelle di relazione per identicare univocamente il personaggio a cui appartengono gli attributi aggiuntivi. • account: contiene l'identicatore dell'account. Questo identicatore è memorizzato nella tabella realmd.accounts. • name: contiene il nome del personaggio assegnato dal giocatore. Questo nome deve essere univoco. • race: contiene la razza del personaggio. • class: contiene la classe del personaggio. • gender: contiene il sesso del personaggio. • level: contiene il livello del personaggio. • xp: contiene il valore dell'esperienza correntemente raggiunta dal per- sonaggio. • money: contiene il valore dei soldi che il personaggio possiede. Oltre a questo insieme basilari, sono presenti anche altre colonne che svolgono funzioni statistiche e organizzative per il personaggio. Altre tabelle che si collegano a questa appena descritta per conservare informazioni aggiuntive sono: 2.1 Studio del Database 25 • character_queststatus: contiene tutte le informazioni di relazione tra ogni personaggio e le quest che ha accettato, completato o abbandonato nel corso delle sessioni di gioco. • character_talent: contiene le informazioni sui talenti che ogni perso- naggio apprende nel progredire della sua esperienza di gioco. • character_inventory: contiene l'elenco di tutti gli oggetti che ogni personaggio ha nel proprio inventario. • character_ticket: contiene tutte le richieste di assistenza che i per- sonaggi hanno fatto. Oltre ai dati dei personaggi, in questo schema sono presenti altre informazioni: • gilde 5 : In queste tabelle sono raccolte le informazioni di base sulla gilda più tutti i vantaggi di cui i personaggi possono usufruire se appartengono alla gilda. Le informazioni sono contenute in: guild, guild_member, guild_rank, guild_bank_item e guild_bank_right. • posta: In queste tabelle sono gestite le comunicazioni postali tra i giocatori. Le informazioni sono contenute in: mail e mail_items • arena6 : In queste tabelle sono gestite le squadre di giocatori che si scon- trano nelle arene oltre ai punteggi di ogni squadra. Le informazioni sono contenute in: arena_team, arena_team_member e arena_team_stats Per dare un'idea della quantità di informazioni che vengono archiviate e gestite per ogni singolo personaggio di un account riporto un esempio. La tabella characters si può considerare la radice di tutte le informazioni sui personaggi. Essa contiene, per ogni personaggio7 , una riga composta da sessantadue colonne. Questa tabella è associata a circa cinquanta altre tabelle contenute nello stesso schema, ognuna delle quali ha un numero di colonne compreso tra due a venti. Non tutte queste relazioni sono di tipo uno 5 Le gilde sono tipicamente delle associazioni di giocatori. Vengono formate per coordinare e organizzare in modo più funzionale la partecipazione agli eventi di gioco in gruppo, oltre a migliorare la collaborazione tra giocatori nella progressione di gioco, dove i più esperti aiutano i neo arrivati. 6 Le arene sono aree di gioco in cui le squadre di giocatori possono competere uno contro l'altro in combattimenti di sopravvivenza. La vittoria viene aggiudicata alla squadra che riesce ad sconggere tutti i giocatori avversari senza limiti di tempo. 7 Supponendo che giocatore possieda un solo account può avere un numero massimo di dieci personaggi, corrispondente al limite di personaggi per account. 2.2 Esempio di analisi 26 a molti, ma alcune lo sono e associano, ad ogni personaggio, un numero di righe che aumenta mano a mano che il personaggio progredisce nel gioco, raggiungendo anche qualche migliaia di record nel caso della tabella character_queststatus8 . Quando un personaggio è vicino al completamento di tutte le sde che il gioco può proporre, il numero di ennuple ad esso relative può essere decisamente molto maggiore di una decina di migliaia (di cui la maggior parte saranno, come visto, relative alle quest). Schema scriptdev2 Questo schema contiene poche tabelle, in quanto il suo scopo è solo di fornire un supporto molto mirato e specico per le implementazioni che non trovano risoluzione nella struttura di base del reame. Per questo motivo ci sono solo tre tabelle: • gossip_text: In questa tabella sono memorizzati i testi dei dialoghi che i NPC possono avere quando interagiscono con un personaggio, e vengono impostati dagli script C++ del progetto ScriptDev2. • script_text: In questa tabella sono memorizzati i testi aggiuntivi che i NPC possono dire, e vengono richiamati dagli script C++ del progetto ScriptDev2. • script_waypoint: In questa tabella sono memorizzati i percorsi dei NPC che sono richiamati dagli script C++ del progetto ScriptDev2. 2.2 Esempio di analisi Per fare un esempio di analisi del contenuto del database prendiamo l'esempio della quest "All Hell Breaks Loose" che un giocatore può arontare. Innanzitutto per scoprire i dettagli della quest è possibile consultare le sue caratteristiche e scoprire il suo codice identicativo in uno dei vari portali online che raccolgono tutti i dati degli elementi del gioco. Nel nostro caso useremo il portale WoWHead9 . Dal portale possiamo risalire all'identicativo della quest 14093 che dovremo cercare nella tabella quest_template. 8 Nell'espansione di gioco considerata ci sono circa 10565 quest disponibili per entrambe le fazioni. Un personaggio di una fazione avrà quindi a disposizione circa più di 5000 quest da poter svolgere. La fonte di questi dati è stato il collegamento http://www.wowhead.com/quests?lter=minrl=1;maxrl=85 del portale dedicato WoWHead visitato in data 14/03/2015 9 Informazioni disponibili nel collegamento http://www.wowhead.com/quest=14093/allhell-breaks-loose in data 13/03/2015 2.2 Esempio di analisi 27 Possiamo quindi costruire una query per cercare nella tabella delle quest il codice trovato: 1 2 3 4 SELECT entry, requiredClasses, requiredRaces, prevQuestId, nextQuestId, reqItemId1, reqItemCount1, ReqCreatureOrGOId1, ReqCreatureOrGOCount1 FROM quest_template WHERE entry = 14093; Il risultato sarà il seguente: Colonna Valore entry 14093 requiredClasses 0 requiredRaces 2097152 prevQuestId 14091 nextQuestId 14099 reqItemId1 0 reqItemCount1 0 ReqCreatureOrGOId1 34884 ReqCreatureOrGOCount1 6 Come possiamo notare dai dati estratti, la colonna reqItemId1 è valorizzata a 0, quindi per questa quest non ci sono oggetti in relazione da cercare. Diversamente la colonna ReqCreatureOrGOId1 ha un valore. É quindi questo il numero identicativo che dovremo vericare essere coerente con quanto riportato sul portale WoWHead. Per procedere con l'analisi si possono intraprendere due dierenti strade che portano al medesimo risultato: 1. Verichiamo sul portale WoWHead il codice trovato a quale creatura corrisponde, oppure nel dettaglio della quest sempre sul portale WoWHead verichiamo che la creatura associata abbia il codice trovato nella nostra tabella. 2. Verichiamo nel database il codice che abbiamo ottenuto a quale creatura o gameObject appartiene, e successivamente confrontiamo il risultato con quanto riportato sul portale WoWHead per la quest in oggetto di analisi. Se intraprendiamo la prima strada verichiamo velocemente che il valore della colonna corrisponde con il codice della creatura indicata sul portale WoWHead. 2.2 Esempio di analisi 28 Provando a percorrere anche la seconda strada dovremo cercare nella colonna entry della tabella creature_template oppure della tabella gameobject_template il valore 34884. 1 SELECT entry, name 2 FROM gameobject_template WHERE entry = 34884; Il risultato sarà il seguente: Colonna Valore entry NULL name NULL 1 SELECT entry, name 2 FROM creature_template WHERE entry = 34884; Il risultato sarà il seguente: Colonna Valore entry 34884 name Rampaging Worgen Dai risultati ottenuti abbiamo scoperto che il valore della colonna ReqCreatureOrGOId1 fa riferimento ad una creatura. Vericando ora il nome della creatura e il suo codice sul portale WoWHead relativamente alla quest in analisi avremo la conferma della correttezza dei dati. Oltre a vericare il valore della colonna ReqCreatureOrGOId1 possiamo constatare che anche la colonna ReqCreatureOrGOCount1 è valorizzata correttamente con il valore 6 come richiesto dalla quest presente sul portale WoWHead. Con questi controlli abbiamo appurato che la quest "All Hell Breaks Loose"10 è correttamente codicata nel database. I dati presenti nei portali si suppone siano corretti, in quanto inseriti e vericati da milioni di utenti che li consultano per progredire nella loro esperienza di gioco. 10 Informazioni disponibili nel collegamento http://www.wowhead.com/quest=14093/all- hell-breaks-loose in data 13/03/2015 Capitolo 3 Struttura dell'emulatore In questa sezione si andranno a presentare l'ambiente dove l'emulatore è stato installato e i componenti principali del progetto. Per una breve descrizione dei principali termini utilizzati si rimanda al glossario. 3.1 Ambiente Server L'emulatore è sviluppato interamente in linguaggio C++ e, per sua natura, può essere compilato ed eseguito su qualsiasi piattaforma per cui esista un compilatore. Gli sviluppatori però si sono focalizzati su due principali ambienti: Microsoft Windows e distribuzioni Linux quali Debian e Ubuntu. Le principali dierenze nei progetti tra le due piattaforme risiedono nelle librerie usate per la gestione della memoria, dei thread e di altre risorse relative per lo più al sistema operativo. Per entrambe le piattaforme sono stati predisposti e pre-congurati i relativi ambienti di compilazione: Microsoft Visual Studio dalla versione 2010 alla 2013 per l'ambiente Microsoft Windows, e CMake per gli ambienti Linux. Nella mia sperimentazione ho provato la compilazione e l'esecuzione del progetto in entrambi gli ambienti, senza riscontrare dierenze nel prodotto compilato e pronto per l'esecuzione. La compilazione in ambiente Windows può risultare più intuitiva grazie all'integrazione con un IDE come Visual Studio. Probabilmente però un ambiente di sviluppo così integrato può portare a limitazioni nelle congurazioni e personalizzazioni dei parametri di compilazione che il compilatore CMake invece può avere. Nelle fasi di implementazione di alcune funzionalità mancanti del gioco mi sono orientato su una distribuzione Debian virtualizzata. I vantaggi della virtualizzazione si sono rivelati estremamente utili per un progetto di sperimentazione dove, a volte, si commettono errori o si modicano dati per cui 3.1 Ambiente Server 30 un ripristino della macchina virtuale è più semplice di una re-installazione e congurazione di tutto il sistema. Sul sito uciale del progetto MaNGOS esistono alcune guide che spiegano le operazioni necessarie per il download dei sorgenti e di tutti i componenti, la compilazione, la congurazione di base e il primo avvio del server di gioco. Questa documentazione però non sempre risulta essere adabile per avere tutto funzionante al primo tentativo. Esistono infatti alcune situazioni dipendenti dal sistema operativo e dagli strumenti usati che richiedono interventi correttivi manuali per arrivare alla soluzione funzionante. Queste correzioni spesso sono anche dovute al continuo sviluppo del progetto, e di fronte alla costruzione di nuove strutture e nuovi oggetti la documentazione non sempre viene aggiornata di pari passo. La postazione ospitante la macchina virtuale è stata un computer con le seguenti caratteristiche: Figura 3.1: Dettagli del computer host per la macchina virtuale Per il sistema di virtualizzazione è stato usato il software Hyper-V[9] 3.2 Repository GitHub 31 preinstallato in Windows 8.1 Professional. La preferenza di questo prodotto ad altri di uguali caratteristiche è stata fatta per massimizzare le prestazioni. La distribuzione Linux usata per il server virtualizzato è stata la Debian 7.5. La scelta è stata fatta per i seguenti motivi: guide di installazione Linux maggiormente orientate a questa distribuzione; Debian è una distribuzione che ore un giusto compromesso tra libertà di uso degli strumenti shell e un ambiente graco minimale che lascia risorse libere per gli applicativi in esecuzione. 3.2 Repository GitHub Il servizio GitHub è un repository online dove gli utenti possono depositare i propri progetti e usare le potenzialità del sistema Git per gestirli in collaborazione con altre persone.[5] Il progetto MaNGOS si avvale di questo repository per gestire l'evoluzione e l'implementazione dei suoi cores, oltre a rendere disponibili per gli utenti i sorgenti. I contributori al progetto sono di due principali categorie, con dierenti privilegi: • membri sviluppatori: sono utenti che sono stati autorizzati dai gesto- ri della community a sviluppare determinate parti dei cores ed eseguono liberamente le operazioni di push 11 . • utenti sviluppatori: sono utenti generici (anche membri della commu- nity) che non hanno il permesso di eseguire operazioni di push. Possono ugualmente contribuire al progetto con le operazioni pull request 12 . Nella mia esperienza, non essendo un membro sviluppatore, per le modiche che ho apportato al progetto MaNGOS Three ho sempre eettuato delle operazioni di pull request, sempre accettate. 3.3 Download, compilazione, installazione e prima congurazione Per l'analisi e lo sviluppo di alcune parti del server ho usato il progetto MaNGOS Three. Questa scelta è stata fatta per alcuni semplici motivi: ero 11 L'operazione di push in un repository corrisponde alla possibilità di upload dei le sorgenti modicati nel repository. 12 L'operazione pull request in un repository consiste nel richiedere ai gestori la verica dei le sorgenti modicati e l'importazione nel repository se considerati validi. 3.3 Download, compilazione, installazione e prima congurazione 32 già in possesso del client di gioco, da cui devono essere estratti alcuni dati per l'esecuzione del server; per l'ambiente Linux Debian le guide di installazione menzionavano questo progetto. Tutte le mie analisi, ricerche ed elaborazioni riguarderanno ora il progetto MaNGOS Three (chiamato anche Cataclysm o abbreviato in Cata, nome uciale della terza espansione del gioco). Dopo aver congurato l'ambiente Debian con tutti i tool necessari per la compilazione e il funzionamento del server MaNGOS (gcc, g++, CMake, MySQL e altri), la prima cosa da fare è stato il download dei sorgenti dal repository GitHub personale su cui avevo precedentemente eseguito una fork del progetto MaNGOS Three originale. Le parti copiate localmente dal repository sono i tre oggetti che compongono il server MaNGOS Three: database, server e scripts. Gli oggetti sono stati organizzati in due cartelle: server e database, mentre il progetto degli scripts è una parte aggiuntiva del server, per tanto è stato posizionato all'interno delle cartelle del server. Il server sarebbe già pronto per essere compilato. Quindi ho creato una cartella (denominata build) per le istruzioni di compilazione e una cartella (denominata run) in cui sono stati messi i le binari prodotti dalla compilazione. Creata la struttura di compilazione con il comando cmake nella cartella build ho eseguito il comando make install per avviare la prima compilazione, dove sono state vericate tutte le dipendenze degli oggetti da costruire e compilati i sorgenti. Il risultato della compilazione ha prodotto nella cartella run le sottocartelle classiche si un applicativo: bin (contenente i le binari o eseguibili), etc (contenente i le di congurazione), include (contenete gli header della libreria ACE) e la cartella lib (contenente le librerie compilate di supporto ai le binari). Prima di poter eseguire il server compilato è necessario aggiungere la cartella data che contiene gli oggetti estratti dal client di gioco. Questi oggetti sono: • Dbc: le che contengono i dati strutturati degli oggetti presenti nel gioco. • Map: le che contengono le informazioni sulle mappe di gioco. • Vmap: le che contengono i modelli graci degli oggetti di gioco. Per estrarre queste informazioni dai le del client ho usato i tool già compilati per windows in quanto il client è unicamente per questi sistemi operativi (esistono però porting anche per Linux e Mac). Terminata l'estrazione dei dati e il loro posizionamento nella cartella deputata ho eseguito lo script di popolamento del database nell'apposita cartella. 3.4 Le componenti del server 33 A questo punto, il sistema è pronto per entrare in esecuzione. Il suo funzionamento si basa su due processi demoni: il reame (mangosd) e l'interfaccia di connessione (realmd) presenti nella cartella bin. Dopo averli avviati entrambi il server è pronto per ricevere la prima connessione. 3.4 Le componenti del server Dopo aver analizzato la procedura di download dei sorgenti e compilazione, nella cartella bin si trovano i due eseguibili che compongono il server. Il primo è realmd e il secondo è mangosd. Saranno ora analizzati separatamente per mostrare il loro funzionamento. 3.4.1 Realmd Il demone realmd gestisce la fase iniziale di connessione al reame. Questo processo che si interpone tra il client e il reame ha la funzione di gestire i reami e le connessioni ad essi. Un reame è una istanza di un server con caratteristiche ben denite quali: l'espansione di gioco, il rate (o moltiplicatore) per i punti esperienza e per le logiche probabilistiche del gioco, più le informazioni relative all'indirizzo IP e la porta TCP a cui il reame risponde. Su un server possono coesistere più reami, ciascuno con le proprie congurazioni, a cui il giocatore può accedere. È quindi compito del processo realmd mostrare all'utente l'elenco dei reami disponibili fra cui scegliere dopo l'autenticazione. Questo demone gestisce le credenziali di accesso (account e password) e ne verica la correttezza. Se l'autenticazione avviene con successo nel client verrà mostrato l'elenco dei reami disponibili fra cui scegliere come, ad esempio, nella gura 3.2 in cui sono disponibili per la scelta due reami. Un'altra funzionalità di questo processo è quella di vericare che la versione del client sia compatibile con il reame a cui ci si collega. Questo è un prerequisito fondamentale per evitare incongruenze ed errori durante le sessioni di gioco, causate dalle peculiarità speciche di ogni espansione. Per stabilire i parametri di congurazione di questo demone bisogna vericare il le realmd.conf nella cartella etc. I parametri fondamentali per il corretto funzionamento sono: • LoginDatabaseInfo: contiene i riferimenti al database e all'utente da usare per la connessione al database separati da ; nel seguente ordine: IP;porta;username;password;schema. Di default ha questo valore: "127.0.0.1;3306;mangos;mangos;realmd". 3.4 Le componenti del server 34 Figura 3.2: Visualizzazione elenco di selezione del reame • RealmServerPort: contiene il riferimento alla porta TCP su cui il processo sarà in ascolto di connessioni. Di default il valore è impostato a 3724. • BindIP: contiene il riferimento all'indirizzo IP su cui il processo potrà ricevere le connessioni. Di default il valore è 0.0.0.0 in modo da poter ricevere connessioni da qualsiasi fonte interna o esterna al server. • LogFile: contiene il riferimento al le di log che il processo produce. Di default il valore è Realmd.log ma, se lasciato vuoto, vengono disattivati i log su le. I log prodotti da questo demone non contengono dati rilevanti, se non in caso di eccezioni o errori. 3.4.2 Mangosd Il demone mangosd gestisce tutte le connessioni con il client. È il cuore di tutte le meccaniche di gioco, delle interazioni tra gli elementi del gioco e tra i giocatori stessi. Questo processo è quindi il reame a cui i giocatori si 3.4 Le componenti del server 35 collegano per le loro sessioni di gioco dopo essere passati dall'interfaccia di gestione dei reami. Per rendere tutto questo funzionante nel modo più eciente possibile il processo, all'avvio, esegue una lettura dei dati statici dal database (legge tutte le informazioni presenti nello schema mangosd del database) e conserva in memoria tutti i dati. Risulta quindi importante che il sistema operativo disponga di un suciente quantitativo di memoria RAM disponibile per questo applicativo. Questa strategia di gestione dei dati statici è molto vincente per garantire la risposta del reame alle richieste del client in tempo reale. L'utilizzo costante di connessioni al database per leggere gli elementi di gioco statici per ogni personaggio connesso incerebbe notevolmente le prestazioni all'aumentare delle connessioni. L'utilizzo dei dati memorizzati all'interno della memoria del processo favorisce la velocità di risposta anche al crescere del numero di connessioni. Indicativamente, dai dettagli del le di congurazione, il limite di default di connessioni che il reame può ricevere è 1024, che risulta essere un numero piuttosto elevato. Durante l'analisi del reame non è stato possibile ricreare un numero di connessioni elevato e, per tanto, non è stato possibile vericare la variazione di prestazioni con dati signicativi. Per rendere una idea maggiore di cosa comporti l'avvio del reame sono riportate nelle gure 3.3 e 3.4 le informazioni sulle risorse occupate nel sistema operativo. Terminato quindi il caricamento di tutti gli oggetti il reame è pronto per ricevere le connessioni che il processo realmd gli passerà quando un giocatore si collega. Durante il caricamento, la console di esecuzione e anche il le di log vengono popolati con tutti gli stati di avanzamento e gli eventuali errori di controllo dei dati letti che si possono presentare. Ai dati che sono letti dal database vengono applicate delle logiche di controllo per vericarne la consistenza e coerenza, in modo da non caricare informazioni che potrebbero destabilizzare il funzionamento del reame. Tutte le anomalie sono quindi segnalate e riportate nel le di log anché possano essere valutate e corrette. Terminato il caricamento, prima di ricevere una qualsiasi connessione, il reame è un processo apparentemente fermo. Il suo output in console non riporta nulla di rilevante come anche il log. Non appena riceve una connessione, invece, si attivano tutte le chiamate al database per recuperare le informazioni sul giocatore e sul personaggio in uso, per poi attivare i meccanismi di popolamento della mappa di gioco in cui il personaggio si trova. Per rendere scalabile e performante il reame la mappa di gioco di tutto il mondo è suddivisa in tre livelli. La mappa, la zona e l'area. 3.4 Le componenti del server Figura 3.3: Graco uso risorse in fase di avvio Figura 3.4: Elenco dei processi attivi e utilizzo memoria 36 3.4 Le componenti del server 37 • La mappa è banalmente associabile al concetto di continente. Il mon- do è suddiviso in continenti (che variano di numero a seconda dell'espansione di gioco del reame) e ciascun continente è una mappa di gioco. • La zona è una regione di un continente. • L'area è una porzione ristretta della zona. Con questi tre livelli tutto l'ambiente di gioco è suddiviso in piccole aree e, quando un giocatore si collega, il personaggio sarà all'interno di una di queste aree. Il reame attiva gli eventi relativi agli elementi di gioco (NPC, mostri, oggetti) che sono all'interno dell'area in cui il personaggio si trova, e trasmette queste informazioni al client che visualizza gli elementi animati (dal reame) nel gioco. Questa operazione di animazione degli elementi di gioco appartenenti ad una specica area viene fatta per ciascun giocatore connesso. Quando un giocatore si sposta da un'area ad un'altra vengono terminate le azioni degli elementi dell'area abbandonata e contestualmente avviati gli eventi della nuova area di gioco. Essendo tutto nella memoria del processo del reame, quando due giocatori si trovano nella medesima area gli elementi animati sono condivisi, permettendo un risparmio di risorse, e consentendo anche l'interazione tra i giocatori. Oltre a questi livelli di suddivisione, ogni area possiede delle coordinate geograche (coordinate in ascisse e ordinate di un piano cartesiano) con cui identicare con esattezza la posizione di ogni elemento e i movimenti dei personaggi. Queste coordinate geograche sono anche presenti nelle tabelle del database che contengono i riferimenti alle posizioni degli elementi di gioco. In questo modo il reame sa dove devono essere visualizzati gli elementi di gioco nel client. Nelle gure 3.5, 3.6, 3.7 e 3.8 sono riportate alcune immagini che esemplicano i tre livelli di suddivisione del mondo di gioco e il sistema di coordinate nell'area dove si trova il personaggio. Analizzando i log del server sono riportati i riferimenti ai cambi di area. 1 32 GameObjects, 78 Creatures, and 0 Corpses/Bones loaded for grid 1481 on map 530 2 Grid[23,8] on map 530 moved to IDLE state 3 Grid[23,9] on map 530 moved to IDLE state 4 WORLD: Received opcode CMSG_ZONEUPDATE: newzone is 3524 Come è possibile notare nel log e nell'immagine, oltre ai concetti di mappa, area e zona sono presenti anche altri sottolivelli usati dal sistema e trasparenti per l'utente: 3.4 Le componenti del server Figura 3.5: Immagine del mondo Figura 3.6: Immagine di un continente 38 3.4 Le componenti del server 39 Figura 3.7: Immagine di una regione Figura 3.8: Immagine dei dati dettagliati sulla posizione del giocatore • griglia: è una ulteriore suddivisione della zona. • cella: è l'ultima suddivisione in cui una griglia è ripartita. Questa suddivisione in parti molto piccole del mondo di gioco risulta 3.4 Le componenti del server 40 molto eciente in quanto i movimenti dei giocatori avvengono a velocità costanti. Inoltre, il caricamento degli oggetti di gioco, avviene solo in funzione di un cambio di griglia. Essendo le griglie piccole, gli oggetti di gioco da caricare sono limitati,mantenendo un buon rapporto tra funzionalità del gioco e performance (ovvero, tempi di risposta). La comunicazione tramite OPCODE La comunicazione tra client e reame avviene tramite pacchetti di rete costruiti con determinati codici identicativi dell'operazione richiesta chiamati OPCODE. Questi pacchetti di rete contengono le informazioni che il reame o il client richiedono di volta in volta e dipendono delle azioni che il personaggio compie nell'ambiente di gioco. Esistono diverse decine di OPCODE codicati e la classe C++ in cui sono censiti e gestiti è Opcodes.cpp situata nella cartella src/game della directory che contenente i sorgenti del progetto server. Guardando nel le di log è possibile trovare vari OPCODE che server e client si sono scambiati durante una sessione di gioco: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 WORLD: CMSG_CREATURE_QUERY ’Alliance Axeman’ - Entry: 17244. WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE WORLD: CMSG_CREATURE_QUERY ’Alliance Logger’ - Entry: 17247. WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE WORLD: CMSG_CREATURE_QUERY ’Rat’ - Entry: 4075. WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE WORLD: CMSG_CREATURE_QUERY ’Chicken’ - Entry: 620. WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE WORLD: CMSG_CREATURE_QUERY ’Stormwind Marine’ - Entry: 20556. WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE PLAYER: Player 1 discovered a new area: 3573 WORLD: CMSG_CREATURE_QUERY ’Admiral Odesyus’ - Entry: 17240. WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE WORLD: CMSG_CREATURE_QUERY ’Archaeologist Adamant Ironheart’ - Entry: 17242. WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE WORLD: CMSG_CREATURE_QUERY ’Priestess Kyleen Il’dinare’Entry: 17241. WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE WORLD: CMSG_CREATURE_QUERY ’Pack Mule’ - Entry: 16225. WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE WORLD: CMSG_GAMEOBJECT_QUERY ’Campfire’ - Entry: 181712. WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE WORLD: CMSG_GAMEOBJECT_QUERY ’Campfire’ - Entry: 1870. WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE 3.4 Le componenti del server 41 24 WORLD: Received opcode CMSG_QUESTGIVER_STATUS_QUERY - for 25 26 27 28 29 30 31 Player Corsol (Guid: 1) to Creature (Entry: 17240 Guid: 68995) WORLD: Sent SMSG_QUESTGIVER_STATUS for Creature (Entry: 17240 Guid: 68995) WORLD: Received opcode CMSG_QUESTGIVER_STATUS_QUERY - for Player Corsol (Guid: 1) to Creature (Entry: 17242 Guid: 68994) WORLD: Sent SMSG_QUESTGIVER_STATUS for Creature (Entry: 17242 Guid: 68994) WORLD: Received opcode CMSG_QUESTGIVER_STATUS_QUERY - for Player Corsol (Guid: 1) to Creature (Entry: 17241 Guid: 67018) WORLD: Sent SMSG_QUESTGIVER_STATUS for Creature (Entry: 17241 Guid: 67018) WORLD: Received opcode CMSG_ZONEUPDATE: newzone is 3524 STORAGE: DestroyZoneLimitedItem in map 530 and area 3524 Nella porzione di log riportato è visibile lo scambio di informazioni tra client e server tramite OPCODE per popolare l'area visualizzata dal personaggio con gli elementi di gioco in essa contenuti. Nello scambio di OPCODE è anche visibile l'uso dei codici identicativi degli elementi di gioco (entry) e gli identicativi univoci dei singoli oggetti (guid). Nelle gure 3.9 e 3.10 sono ragurati il usso dei pacchetti di rete scambiati tra client e reame (in azzurro ciò che il client ha mandato al reame e in rosso ciò che il reame ha mandato al client) ed i dettagli del protocollo TCP/IP di un pacchetto di rete. Come si può notare il contenuto dei pacchetti è in formato binario e quindi non è interpretabile direttamente. Per capire meglio come vengono costruiti dal server i pacchetti di dati vediamo una porzione di codice in cui il client ha richiesto al reame il tempo di gioco del personaggio, e il reame risponde componendo l'apposito pacchetto binario e lo rimanda in risposta. 1 void WorldSession::HandlePlayedTime(WorldPacket& recv_data) 2 { 3 uint8 unk1; 4 recv_data >> unk1; 5 6 WorldPacket data(SMSG_PLAYED_TIME, 4 + 4 + 1); 7 data << uint32(_player->GetTotalPlayedTime()); 8 data << uint32(_player->GetLevelPlayedTime()); 9 data << uint8(unk1); 10 SendPacket(&data); 11 } 3.4 Le componenti del server Figura 3.9: Ricostruzione parziale del usso dei pacchetti di rete Figura 3.10: Dettagli TCP e IP di un pacchetto di rete 42 3.4 Le componenti del server 43 Per mantenere la connessione tra reame e client sempre attiva e stabilire anche i parametri di velocità della connessione stessa esiste un OPCODE che il client invia costantemente al reame per vericare la sua attività e la latenza della connessione. 1 WORLD: Received opcode CMSG_TIME_SYNC_RESP: counter 3,client ticks 248165080, time since last sync 10062 2 WORLD: Opcode CMSG_TIME_SYNC_RESP -- Our ticks: 248165132, diff 52, latency 131 3 WORLD: Received opcode CMSG_TIME_SYNC_RESP: counter 4, client ticks 248175169, time since last sync 10089 4 WORLD: Opcode CMSG_TIME_SYNC_RESP -- Our ticks: 248175221, diff 52, latency 131 5 WORLD: Received opcode CMSG_TIME_SYNC_RESP: counter 5, client ticks 248185169, time since last sync 10000 6 WORLD: Opcode CMSG_TIME_SYNC_RESP -- Our ticks: 248185222, diff 53, latency 131 Come riportato dalla sequenza estratta dal le di log si può notare il costante invio da parte del client ogni 10 secondi di un OPCODE di sincronizzazione e verica della latenza. Oltre alle funzioni di gestione delle connessioni ai client la shell dove viene eseguito l'oggetto realmd può svolgere funzioni di console per il reame. Questa interfaccia ha dei suoi comandi costruiti per gestire i personaggi dei giocatori e altre funzioni di manutenzione del reame. Alcuni comandi, per esempio, sono: • account create: con l'aggiunta dei parametri username e password, consente la creazione di un nuovo account per il reame corrente. • reload all_item: consente il caricamento di tutti gli oggetti di gioco. Può servire nel caso di aggiornamento sui dati nel database ed evitare di dover riavviare il reame per rendere disponibili ai giocatori le modiche apportate. • server info: visualizza le informazioni sulla versione del server e sul numero di giocatori connessi. • server restart: serve per riavviare il reame dopo un intervallo di tempo (in secondi) impostato. Questi comandi sono anche invocabili nel client durante una sessione di gioco per quei giocatori che sono autorizzati. Per stabilire i parametri di congurazione di questo eseguibile bisogna vericare il le mangosd.conf nella cartella etc. I parametri fondamentali per il corretto funzionamento sono: 3.5 I le DBC 44 • RealmID: stabilisce a qual'è l'id codicato nel database di questo reame. • DataDir: contiene il percorso assoluto della cartella in cui sono contenuti i le .dbc, .map e .vmap con i dati estratti dal client che il reame consulterà durante la sua esecuzione. • LoginDatabaseInfo: stabilisce le informazioni di connessione al da- tabase per lo schema relativo alle informazioni di login dell'account. Il valore di default è 127.0.0.1;3306;mangos;mangos;realmd • WorldDatabaseInfo: stabilisce le informazioni di connessione al da- tabase per lo schema relativo alle informazioni degli elementi statici di gioco. Il valore di default è 127.0.0.1;3306;mangos;mangos;mangos • CharacterDatabaseInfo: stabilisce le informazioni di connessione al database per lo schema relativo alle informazioni dei personaggi di ogni account. Il valore di default è 127.0.0.1;3306;mangos;mangos;characters • WorldServerPort: contiene il riferimento alla porta TCP su cui il processo resterà in ascolto delle connessioni dei client. Di default il valore è 8085, ma è fondamentale modicarlo quando sul medesimo server vengono eseguite più istanze di reami. • BindIP: contiene il riferimento all'indirizzo IP su cui il processo potrà ricevere le connessioni. Di default il valore è 0.0.0.0 in modo da poter ricevere connessioni da qualsiasi fonte interna o esterna al server. Oltre a queste impostazioni, questo le contiene anche tutti i parametri per personalizzare il comportamento del reame nella gestione di vari aspetti di gioco. Il le contiene anche le descrizioni dei singoli parametri con i relativi valori di default (o congurati) per personalizzare il reame a proprio uso e consumo. 3.5 I le DBC I le DBC sono le binari estratti dal client del gioco che contengono informazioni strutturate riguardanti alcuni elementi del gioco stesso. Essendo queste informazioni statiche e già presenti nel client, non vengono riportate anche nel database del server, ma i le devono essere posizionati in una cartella specica in cui il reame possa andare a recuperare queste informazioni. 3.5 I le DBC 45 L'uso di questi le archivio risulta fondamentale per minimizzare le informazioni da veicolare nella comunicazione tra client e server come accennato nel capitolo 1.2.4. La caratteristica fondamentale di questi le archivio è la staticità dei dati: essendo estratti direttamente dal client e non essendo il client soggetto a modiche (se non cambiando espansione di gioco, per cui è necessario cambiare anche il reame), questa peculiarità è garantita. Anche queste informazioni sono caricate dal reame in fase di avvio. Quando un oggetto di gioco, le cui informazioni provengono da un le DBC, viene attivato, il reame invia al client unicamente il codice identicativo (presente nel le archivio) dell'oggetto di gioco, lasciando al client la lettura di tutte le altre caratteristiche dai suoi dati locali. Come accennato nel primo capitolo con il tool MyDBCExplorer è possibile visualizzare il contenuto di questi le-archivio. Uno fra i più importanti di questi le è Spell.dbc. Figura 3.11: Visualizzazione contenuto le Spell.dbc Come è possibile vedere dalla gura 3.11, il le contiene i dati con valori numerici (sono pochi i valori testuali). Per poter sapere il signicato dei valori nelle colonne è possibile sfruttare le informazioni presenti nel le .cpp 3.5 I le DBC 46 che svolge le funzioni di interfaccia di lettura dei dati per il reame, dove sono commentati e spiegate in parte le colonne. Ecco quindi l'estratto dal le DBCStructure.h: 1 // Spell.dbc 2 struct MANGOS_DLL_SPEC SpellEntry 3 { 4 uint32 Id; // 0 m_ID 5 uint32 Attributes; // 1 m_attribute 6 uint32 AttributesEx; // 2 m_attributesEx 7 uint32 AttributesEx2;// 3 m_attributesExB 8 uint32 AttributesEx3;// 4 m_attributesExC 9 uint32 AttributesEx4;// 5 m_attributesExD 10 uint32 AttributesEx5;// 6 m_attributesExE 11 uint32 AttributesEx6;// 7 m_attributesExF 12 uint32 AttributesEx7;// 8 m_attributesExG 13 uint32 AttributesEx8;// 9 m_attributesExH 14 uint32 AttributesEx9;// 10 m_attributesExI 15 uint32 AttributesEx10;// 11 m_attributesExJ 16 uint32 CastingTimeIndex;// 12 m_castingTimeIndex 17 uint32 DurationIndex;// 13 m_durationIndex 18 uint32 powerType; // 14 m_powerType 19 uint32 rangeIndex; // 15 m_rangeIndex 20 float speed; // 16 m_speed 21 uint32 SpellVisual[2];// 17-18 m_spellVisualID 22 uint32 SpellIconID; // 19 m_spellIconID 23 uint32 activeIconID; // 20 m_activeIconID 24 DBCString SpellName; // 21 m_name_lang 25 DBCString Rank; // 22 m_nameSubtext_lang 26 //DBCString Description;// 23 m_description_lang not 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 used //DBCString ToolTip; // 24 m_auraDescription_lang not used uint32 SchoolMask; // 25 m_schoolMask uint32 runeCostID; // 26 m_runeCostID //uint32 spellMissileID;// 27 m_spellMissileID not used //uint32 spellDescriptionVariableID; // 28 m_spellDescriptionVariableID, 3.2.0 uint32 SpellDifficultyId; // 29 m_spellDifficultyID - id from SpellDifficulty.dbc //float unk_f1; // 30 uint32 SpellScalingId; // 31 SpellScaling.dbc uint32 SpellAuraOptionsId; // 32 SpellAuraOptions.dbc uint32 SpellAuraRestrictionsId; // 33 SpellAuraRestrictions.dbc uint32 SpellCastingRequirementsId; // 34 SpellCastingRequirements.dbc 3.5 I le DBC 44 45 46 47 48 49 50 51 52 53 54 55 56 57 47 uint32 SpellCategoriesId; // 35 SpellCategories.dbc uint32 SpellClassOptionsId; // 36 SpellClassOptions.dbc uint32 SpellCooldownsId; // 37 SpellCooldowns.dbc //uint32 unkIndex7; // 38 all zeros... uint32 SpellEquippedItemsId; // 39 SpellEquippedItems.dbc uint32 SpellInterruptsId; // 40 SpellInterrupts.dbc uint32 SpellLevelsId; // 41 SpellLevels.dbc uint32 SpellPowerId; // 42 SpellPower.dbc uint32 SpellReagentsId; // 43 SpellReagents.dbc uint32 SpellShapeshiftId; // 44 SpellShapeshift.dbc uint32 SpellTargetRestrictionsId; // 45 SpellTargetRestrictions.dbc uint32 SpellTotemsId; // 46 SpellTotems.dbc //uint32 ResearchProject; // 47 ResearchProject.dbc Come è possibile notare nel le .cpp alcune colonne descrittive del learchivio DBC sono commentate. Questo indica che queste colonne appartengono a precedenti espansioni e nella corrente non sono più in uso o sono state rimpiazzate da nuove informazioni più esaustive. Capitolo 4 Implementazioni In questo capitolo saranno mostrati i processi di verica dei bug di gioco con le relative analisi e ricerche per costruire il codice o i dati nel database anché il problema sia risolto. Per una breve descrizione dei principali termini utilizzati si rimanda al glossario. 4.1 Tipologie di implementazione Il lavoro svolto si è concentrato sulla corretta implementazione delle interazioni tra i vari oggetti di gioco. Non si sono invece considerate modiche strutturali dell'architettura del videogioco, o sui servizi di base quali la graca ed la simulazione 3D, in quanto queste sono di fatto attribuite in esclusiva ad alcuni specici membri della community, in possesso non solo delle adeguate conoscenze tecniche, ma soprattutto della reputazione necessaria per fare accettare le modiche proposte dalla community stessa. 4.2 Modalità di implementazione L'implementazione (o anche in gergo scripting) degli elementi di gioco di un reame (le funzioni basilari del reame che sono implementate esclusivamente in linguaggio C++) può essere fatto in tre modi distinti. Queste tre tipologie di scripting, oltre ad essere complementari, possono anche essere usate in modo ibrido, ovvero ottenere le funzionalità volute per un elemento di gioco mescolando parti di Database script con le C++ di ScriptDev2, in base alla complessità delle funzionalità da implementare. 4.2 Modalità di implementazione 49 4.2.1 Database script Questa tipologia di scripting si basa unicamente su alcune tabelle presenti nello schema mangos che consentono di codicare azioni aggiuntive per gli elementi del gioco. Queste azioni verranno poi eseguite direttamente dal reame che è già in grado di interpretare (ovvero decodicare ed eseguire) questi script aggiuntivi. Alcune delle tabelle coinvolte in questa tipologia di scripting sono: creature_ai_scripts, creature_ai_texts, dbscripts_on_go_template_use, dbscripts_on_spell. Per questa tipologia di scripting è suciente conoscere le strutture dati del database e la logica di funzionamento e interazione degli elementi del gioco. Dopo aver inserito le informazioni nel database sarà suciente riavviare il reame per renderle operative. 4.2.2 C++ in ScriptDev2 In questo secondo caso, per implementare una modica occorre scrivere porzioni di codice C++ da integrare con il codice esistente, che poi dovrà essere ricompilato: è quindi chiaro che siamo in presenza di un "abuso" del termine scripting. All'interno della community si parla comunque di scripting in quanto il codice C++ che viene aggiunto usa pesantemente i servizi forniti dalla libreria ScriptDev2 ; in pratica, è come se le funzioni fornite dalla libreria denissero un linguaggio di livello più alto rispetto al C++.[2] Come appena descritto, questa tipologia di scripting prevede la scrittura di codice in C++ nel progetto ScriptDev2 in abbinamento ad alcune informazioni salvate nell'omonimo schema del database. Anché questa metodologia di scripting sia usata dal reame, sarà necessario compilare il progetto e il reame per avere l'integrazione nel sistema di gioco. Per poter costruire gli script C++ è necessaria la conoscenza delle basi di programmazione C++ ad oggetti più un minimo di logica di funzionamento e interazione degli elementi del gioco ottenibile, quest'ultima, dall'analisi del database e tramite l'esperienza diretta di gioco.[8] Anché lo script realizzato nella libreria venga interpretato dal reame è necessario specicare nella colonna ScriptName presente in tutte le tabelle "template " dei principali elementi del gioco (es: creature_template, gameobject_template, ecc...) il nome usato nella denizione della classe C++ implementante le funzionalità nel progetto ScriptDev2 13 . Con questa associazione logica l'oggetto di gioco implementato userà le direttive costruite in C++ per eseguire quanto programmato. 13 Nel capitolo 4.3.2 sarà presentato un esempio di implementazione che chiarirà maggiormente il legame tra elementi di gioco nel database e gli script in ScriptDev2 4.3 Ricerca e risoluzione 50 Non è stato possibile trovare ulteriori informazioni su questo progetto in quanto il portale http://www.scriptdev2.com/ con tutte i dati di sviluppo è risultato essere chiuso, per tutto il periodo di analisi, in seguito ad un attacco hacker che ne ha violato i contenuti. Per garantire comunque la continuità del progetto e la disponibilità dei sorgenti, le informazioni sul repository GitHub14 sono state mantenute attive, ma prive di documentazione rilevante. 4.2.3 Lua script Questa tipologia di scripting è di recente introduzione nel progetto MaNGOS e per ora è applicabile unicamente alla versione base del gioco (MaNGOS Zero). Questo metodo prevede un motore (chiamato ELUNA) integrato nelle funzionalità di base del reame che interpreta degli script esterni scritti in linguaggio Lua. Diversamente rispetto al caso precedente questi script possono essere aggiunti anche "a caldo" ovvero durante l'esecuzione del reame e non richiedono nessun intervento di compilazione come i veri linguaggi di scripting. Il motore ELUNA legge, interpreta ed esegue gli script ogni volta che un oggetto di gioco per cui sono stati progettati viene animato dal reame. Il videogioco World of WarcraftT M ha molte delle sue funzionalità costruite con LUA script, non è quindi un caso che anche questo emulatore abbia integrato un motore per questo linguaggio di programmazione. Questo motore rende disponibili alcune oggetti di base che lo sviluppatore può usare per far interagire il proprio script con il reame e ottenere le animazioni volute. Il linguaggio LUA è un linguaggio che combina la semplicità della sintassi procedurale con costrutti basi di tipo array associativi. La tipizzazione delle variabili e degli oggetti è dinamica e il codice sorgente è processato da un interprete in una macchina virtuale (in parte similare a JavaT M ). I suo punti di forza sono la velocità di esecuzione, la portabilità e la facilità di integrazione con ambienti sviluppati in altri linguaggi.[1] 4.3 Ricerca e risoluzione Nell'analisi del reame ho potuto vericare che esistono due categorie principali di errori: • errori nelle funzioni basilari: le funzioni basilari del reame possono presentare, a loro volta, due tipologie di errori: logici: questi errori derivano da interpretazioni non corrette della logica di funzionamento delle funzionalità di base da parte degli 14 Informazioni disponibili all'indirizzo https://github.com/scriptdev2 4.3 Ricerca e risoluzione 51 sviluppatori che si traducono in un comportamento del reame diverso da quanto aspettato da parte dei giocatori durante le sessioni di gioco (ad esempio un incantesimo che non ha eetti sul bersaglio, oppure una abilità di un personaggio che ha eetti diversi da quelli attesi, ecc...). implementativi: questi errori sono caratterizzati dall'assenza di implementazioni per la logica di gioco. Il giocatore ne ha evidenza quando una funzione del gioco risulta non attiva. Nel reame questi bug sono spesso individuabili nei log quando è presente un OPCODE (descritto nel capitolo precedente) segnalato come sconosciuto oppure le funzioni sono solo denite nel codice C++ ma non implementate (ad esempio l'incapacità di un personaggio di eseguire una metamorfosi, oppure l'impossibilità di volare nelle aree di gioco dove consentito, ecc...). • errori negli elementi di gioco: derivano da informazioni assenti nel database e non codicate nelle librerie esterne (ScriptDev2 o ELUNA). L'interazione con essi risulta quindi limitata o impossibile (ad esempio un NPC senza azioni, un oggetto non usabile, ecc...). L'obiettivo primario di ogni reame è quello di evitare un crash del sistema, con conseguente disconnessione degli utenti e la necessità di un riavvio manuale del reame. Per questo motivo si preferisce avere bug silenti, che limitino l'esperienza di gioco unicamente al loro scopo d'uso e non abbiano eetto su tutto il resto del reame. In questo modo il giocatore potrà proseguire la sua sessione di gioco con altre attività che non prevedano l'uso della funzionalità mancante o errata. Questa strategia ha il vantaggio di rendere più stabile l'esecuzione del reame, ma lo svantaggio di complicare l'identicazione degli errori, specialmente quelli relativi alle funzionalità basilari. Se per gli elementi di gioco le informazioni sulle loro caratteristiche abbondano nei portali dedicati, per le funzionalità di base invece le informazioni sono più carenti e necessitano di maggior interpretazione e verica. Il metodo migliore per poter identicare i bug e vericare la correttezza delle implementazioni resta soltanto uno: giocare. Il modo migliore per farlo sarebbe tramite il confronto delle dierenze tra una sessione di gioco su un server uciale e quella sul reame in costruzione, per vericare che le implementazioni realizzate rispecchino il più possibile il comportamento del gioco uciale. Per percorrere questa metodologia di verica è però necessario abbonarsi al servizio a pagamento mensile della software house che gestisce e sviluppa il gioco. In alternativa è possibile fare 4.3 Ricerca e risoluzione 52 confronti con le altre realtà non uciali presenti in rete che orono un accesso gratuito al gioco, tenendo in considerazione che anche queste realtà usano reami in via di sviluppo e quindi non pienamente funzionanti o con libere interpretazioni delle logiche di gioco dierenti da quelle uciali. Date le limitate disponibilità di documentazione ho limitato la mia ricerca e correzione degli errori agli elementi di gioco, trascurando le funzioni basilari del reame. Di seguito saranno illustrate alcune delle fasi di ricerca e risoluzione di bug, con approcci sia di Database Scripting che con soluzioni in ScriptDev2. Per la ricerca ho scelto appositamente un'area di gioco che sapevo avesse evidenti problemi e nei paragra seguenti saranno presentate alcune delle modalità di controllo degli errori, la verica del comportamento atteso tramite i portali dedicati, la ricerca delle informazioni mancanti nel database e la correzione apportata per ottenere il risultato corretto. 4.3.1 Errore di cambio fase Durante una delle prime sessioni di gioco volte ad identicare i problemi, ho subito riscontrato un errore di cambio di fase. Cambio di fase Il cambio di fase è una tecnica comunemente usata nei MMORPG che consente di visualizzare un zona di gioco in modo dierente ai personaggi che la attraversano. Il suo funzionamento è legato alle informazioni che il reame manda al client sugli elementi di gioco da visualizzare. Ogni elemento di gioco appartiene ad una o più fasi e, in base a questa appartenenza, il reame veicola gli elementi di gioco da visualizzare al client. Ad esempio è possibile accettare una quest da un NPC che è presente in una fase iniziale ma non nella fase successiva. Se la quest prevede che il personaggio subisca un cambio di fase e quindi passi nella fase successiva, l'NPC da cui ha preso la quest non sarà più visibile in quanto il server avrà inviato al client le informazioni di reset degli elementi di gioco visualizzati e i nuovi elementi di gioco da mostrare in cui l'NPC in questione non è presente. Pur rimanendo nella stessa area di gioco è quindi possibile visualizzare elementi di gioco e scenari (terreni, strutture, ecc...) dierenti sfruttando le fasi.[12] Inoltre, per avere una conferma anche visiva del risultato atteso, è stato controllato il comportamento delle fasi eettuando una sessione di gioco su un server privato, presentato nel prossimo capitolo. 4.3 Ricerca e risoluzione 53 Individuazione dell'errore Un errore di cambio fase è stato riscontrato con una la quest "Something's Amiss ". Durante il gioco l'accettazione della quest non attivava il cambio di fase e non era quindi possibile trovare l'NPC da cui andare per il completamento della quest. Per identicare l'errore, già evidente durante il gioco per la mancanza del NPC nella sua posizione come mostrato dalla gura 4.1, ho vericato sul portale WoWHead il funzionamento della quest. Figura 4.1: Area senza il cambio di fase Correzione dell'errore Una possibile modalità di risoluzione del problema del cambio di fase è stata realizzata tramite un intervento di codica degli eventi che attivano le funzionalità del reame anché avvenga il cambio di fase all'accettazione della quest e rimanga attiva (la nuova fase) quando la quest è stata completata. Per apportare questa correzione ho eettuato un intervento sul database, usando quindi la modalità di Database scripting. La soluzione pote- 4.3 Ricerca e risoluzione 54 va essere codicata anche in C++ usando le funzionalità ScriptDev2, ma avrebbe richiesto un sforzo maggiore di programmazione, oltre a risultare probabilmente meno eciente. Per risolvere questo problema non ho trovato documentazione specica e quindi ho analizzato il database per cercare di scoprire dove fossero le informazioni per gestire il cambio di fase, scoprendo la tabella mangos.phase_definitions. Trovata la tabella ho cercato nuovamente documentazione su come valorizzare i dati, senza successo. L'assenza di documentazione in merito può essere imputata al fatto che questa versione del gioco è sviluppata parallelamente al progetto MaNGOS Zero come già spiegato nei capitoli precedenti e quindi la documentazione disponibile fa principalmente riferimento a progetto Zero dove non esistono le fasi. Le novità introdotte dalle espansioni successive non sono ancora state documentate completamente e, per trovare le risposte, ho alterato le funzionalità basilari del reame relative al cambio di fase aggiungendo output per la console di gioco e il le di log nel le phaseMgr.cpp che, unite a tante prove, mi hanno consentito di capire la logica di funzionamento delle colonne della tabella: • zoneID: contiene il codice dell'area di gioco in cui si deve eettuare il cambio di fase. Come abbiamo visto nell'analisi delle zone di gioco, esiste il comando .gps da eseguire durante una sessione di gioco che ci consente di scoprire il codice della zona per la nostra quest da correggere. • entry: contiene un numero progressivo per la gestione di più cambi di fase per la stessa zona se necessario. • phasemask: contiene il codice della fase di destinazione dopo il cambio. Se si valorizza con il valore della fase corrente non verrà cambiata la fase, diversamente verranno avviate le procedure, dal reame, di cambio fase per mostrare gli oggetti della fase indicata in questa colonna nel client. • phaseId: contene un valore che indica al reame di inviare le informa- zioni di cambio fase al client. • terrainswap: contene un valore che indica al reame di inviare le infor- mazioni di cambio fase al client se non valorizzata la colonna phaseId. • ags: contiene una combinazione di ag fra i seguenti: PHASE_FLAG_OVERWRITE_EXISTING: con valore 0x01, indica gli elementi di gioco della nuova fase sostituiranno quelli della fase precedente. 4.3 Ricerca e risoluzione 55 PHASE_FLAG_NO_MORE_PHASES: con valore 0x02, indica che non ci saranno fasi successive a quella corrente. PHASE_FLAG_NEGATE_PHASE: con valore 0x04, indica che il cambio di fase non potrà avvenire se non aggiungendo il valore nella colonna phasemask, consentendo di vedere più fasi in contemporanea. • condition_id: contiene il codice identicativo della condizione che attiva il cambio di fase. • comment: contiene un commento o una descrizione su questo cambio di fase, utile per la verica o ricerca delle informazioni. Analizzando le colonne della tabella è possibile notare che la penultima richiede il riferimento ad una condizione. Per le condizioni esiste una tabella dedicata conditions documentata nel manuale mangozeros-docs.pdf 15 [4] già menzionato nel capitolo di analisi del database. Le condizioni sono delle regole che si attivano al vericarsi di determinati eventi relativi agli elementi di gioco ad esse associati. Leggendo le speciche di questa tabella sono state trovate due condizioni che possono scatenare il cambio di fase per la quest in oggetto di correzione, in particolare le condizioni: CONDITION_QUESTTAKEN e CONDITION_QUESTREWARDED. Con la prima è possibile innescare il cambio di fase, mentre con la seconda è possibile mantenere la nuova fase successivamente. Le informazioni necessarie in questa tabella sono quindi due: la condizione e l'oggetto scatenante la condizione. La prima informazione la abbiamo appena trovata, resta qui da trovare la seconda. Sfruttando il portale WoWHead, come visto nel capitolo dell'analisi del database, è possibile identicare il codice entry di una quest che è ciò che ci serve. Trovate le informazioni necessarie ovvero: quest entry = 14091, CONDITION_QUESTTAKEN = 9, CONDITION_QUESTREWARDED = 8, id zona = 4755, fase iniziale = 1 e fase successiva = 2, sono state inserite nel database le due condizioni, una per il cambio fase e una per il mantenimento della nuova fase e, successivamente, sono stati inseriti i dati per il cambio di fase. Di seguito il codice SQL per l'aggiunta delle informazioni: 1 INSERT INTO conditions (type, value1, value2) VALUES 2 (9, 14091, 2), 15 Il documento è consultabile a MaNGOS-Zero https://media.readthedocs.org/pdf/mangoszero-docs/latest/mangoszero-docs.pdf visionato in data 12/11/2014 docs 4.3 Ricerca e risoluzione 56 3 (8, 14091, 0); 4 5 INSERT INTO phase_definitions (zoneId, entry, phasemask, 6 7 8 9 10 11 12 13 14 15 phaseId, terrainswapmap, flags, condition_id, comment) VALUES (4755, 1, 2, 1, 0, 1, ( SELECT condition_entry FROM conditions WHERE type = 9 AND value1 = 14091 AND value2 = 0), ’Gilneas city entering in second phase’), (4755, 2, 2, 1, 0, 2, ( SELECT condition_entry FROM conditions WHERE type = 8 AND value1 = 14091 AND value2 = 0), ’Gilneas city stay in second phase’); Dopo il riavvio del reame, la prova sul campo ha confermato la correttezza delle informazioni, come mostrato nella Figura 4.2. Figura 4.2: Area dopo il corretto cambio di fase 4.3 Ricerca e risoluzione 57 Per contribuire alla documentazione sulla gestione delle fasi, ho pubblicato nel forum del progetto MaNGOS una mini guida con le spiegazioni sulla tabella phase_denitions e come valorizzare i campi usando questa correzione della quest come esempio. La documentazione pubblicata in data 14 Novembre 2014 è raggiungibile al url https://www.getmangos.eu/database/10176gilneas-starting-zone-phase-change-post73574.html (consultabile soltanto per gli utenti registrati al forum del progetto MaNGOS). 4.3.2 Personaggio senza animazioni Un altro problema riscontrato durante una sessioni di gioco per identicare gli errori, è stato la mancanza di animazioni e azioni per un NPC. Azioni e animazioni Ogni personaggio non giocante NPC ha delle azioni e animazioni che ripete anche in funzione dell'ambiente che lo circonda. Le azioni e animazioni che un NPC può compiere sono svariate, le più comuni sono: partecipare a dialoghi (in formato testuale o anche audio se codicato), spostarsi seguendo un percorso prestabilito, attaccare nemici entro un raggio di minaccia (chiamato anche Aggro ) e utilizzare incantesimi (chiamati anche Spell ). Queste azioni solitamente sono codicate nel database ma, per alcuni casi, sono necessari gli script esterni del progetto ScriptDev2 per realizzare tutte le azioni e animazioni. Individuazione dell'errore Un personaggio per cui le azioni e animazioni non erano codicate era il NPC "Prince Liam Greymane", (presente nei dettagli della quest relativa alla risoluzione dell'errore precedente). Dopo il cambio di fase, di cui abbiamo visto la correzione nel paragrafo precedente, l'NPC in oggetto di analisi dovrebbe pronunciare delle frasi di incitazione e combattere contro i Worgen16 presenti nell'area. Ma ciò non avviene come mostrato in gura 4.3. Correzione dell'errore Un esempio di risoluzione problema dell'assenza di azioni e animazioni è stato realizzato modicando il le gilneas_city.cpp del progetto ScriptDev2. Per implementare un elemento di gioco con uno script in C++ nel progetto ScriptDev2 è necessario costruire due strutture dati principali: 16 I Worgen sono una razza di creature del gioco molto similari a lupi mannari. 4.3 Ricerca e risoluzione 58 Figura 4.3: NPC "Prince Liam Greymane" senza animazioni e azioni • Enum: Questa struttura dati è stata usata per organizzare i riferimenti di codici identicanti testi, azioni o altri valori statici. • Struct: Questa struttura dati contiene gli attributi e i metodi per animare l'NPC. Oltre a queste strutture dati, è necessario codicare i testi con gli eventuali audio nella tabella script_texts del database scriptdev2. Queste informazioni saranno richiamate tramite i codici identicativi memorizzati in apposite variabili nella struttura dati enum del le cpp. Per stabilire il corretto comportamento del NPC sono state vericate le sui caratteristiche sul portale WoWWiki (per trovare i testi da visualizzare) e sul portale WoWHead (per trovare gli audio vocali). Inoltre, per avere una conferma anche visiva del risultato atteso, è stato controllato il comportamento del NPC in oggetto di analisi eettuando una sessione di gioco su un server privato, presentato nel prossimo capitolo. 4.3 Ricerca e risoluzione 59 Trovati i testi che l'NPC deve pronunciare, bisogna codicarli nella tabella del database con la seguente struttura (documentata sempre nel documento mangoszero-doc.pdf già nominato): • entry: contiene il codice identicativo del testo. Questo identicativo è costruito secondo un precisa struttura: -1<codice mappa><contatore a tre cifre decimali> • content_default: contiene il testo da far pronunciare all'NPC. • sound: contiene il codice di riferimento per l'audio da riprodurre. Questo codice è un riferimento al contenuto del le SoundEntries.dbc. • type: contiene il codice che stabilisce come il testo verrà mostrato al giocatore. Questo valore è un riferimento al contenuto del le Languages.dbc. • language: contiene il codice che stabilisce la lingua di gioco con cui il testo sarà scritto (le lingue di gioco variano in base alle razze dei personaggi). L'elenco dei possibile valori codicati per questa colonna è specicato nel documento mangoszero-doc.pdf 17 . • emote: contiene il codice che stabilisce quale animazione l'NPC dovrà compiere quando pronuncia il testo. Questo valore è un riferimento al contenuto del le Emotes.dbc. • comment: contiene un commento al testo, spesso utile per sapere in quale script C++ il testo è utilizzato. Oltre al testo da inserire sono necessarie anche altre informazioni non tutte di facile deduzione. Le codiche presenti nei le DBC da usare sono tutte abbastanza intuitive ad eccezione dell'audio. Per identicare quest'ultimo è stato usato, oltre al visualizzatore di le DBC già visto, anche lo strumento di analisi dei l MPQ presentato nel paragrafo 1.3.4 del primo capitolo. Con lo strumento Ladik’s MPQ Editor è stato aperto il le del client (le archivio .MPQ) contenente gli audio degli NPC e sono state ricercate le voci relative all'NPC "Prince Liam Greymane" trovandone un cospicuo elenco come mostrato in gura 4.4. Fortunatamente i nomi dei le contengono indizi che consentono di limitarne l'ascolto a poche unità per trovare i testi corretti da associare. Trovati i nomi dei le audio, li ho ricercati all'interno del le SoundEntries.dbc con il programma MyDbcEditor, ricavando il codice da usare nella tabella come mostrato in gura 4.5. 4.3 Ricerca e risoluzione 60 Figura 4.4: Ricerca audio per l'NPC "Prince Liam Greymane" nei le MPQ del client Figura 4.5: Ricerca del codice relativo ai le audio del NPC "Prince Liam Greymane" del le DBC 4.3 Ricerca e risoluzione 61 Ottenute le informazioni necessarie ho potuto popolare la tabella. 1 INSERT INTO script_texts (entry, content_default, sound, type , language, emote, comment) VALUES 2 (-1654004, ’Stand your ground men!’, 19617, 1, 7, 424, ’ npc_prince_liam_greymane_phase2’), 3 (-1654005, ’Defeat these foul beasts!’, 19618, 1, 7, 424, ’ npc_prince_liam_greymane_phase2’), 4 (-1654006, ’Protect the Civilians!’, 19619, 1, 7, 424, ’ npc_prince_liam_greymane_phase2’), 5 (-1654007, ’Push them back!’, 19620, 1, 7, 424, ’ npc_prince_liam_greymane_phase2’), 6 (-1654008, ’Take heart men, we must protect our city!’, 19621, 1, 7, 424, ’npc_prince_liam_greymane_phase2’); La costruzione dello script che consente l'animazione del personaggio in questione si basa sull'estensione della classe ScriptedAI dichiarata nel le sc_creature.h del progetto ScriptDev2 e parzialmente implementata nel le sc_creature.cpp. I principali metodi che questa struttura ore sono: 1 struct MANGOS_DLL_DECL ScriptedAI : public CreatureAI 2 { 3 public: 4 explicit ScriptedAI(Creature* pCreature); 5 ~ScriptedAI() {} 6 7 // ************* 8 // CreatureAI Functions 9 // ************* 10 11 // == Information about AI ======================== 12 // Get information about the AI 13 void GetAIInformation(ChatHandler& reader) override; 14 15 // == Reactions At ================================= 16 17 // Called if IsVisible(Unit* pWho) is true at each 18 19 20 21 22 23 relative pWho move void MoveInLineOfSight(Unit* pWho) override; // Called for reaction at enter to combat if not in combat yet (enemy can be NULL) void EnterCombat(Unit* pEnemy) override; // Called at any Heal received 17 Il documento è consultabile a MaNGOS-Zero https://media.readthedocs.org/pdf/mangoszero-docs/latest/mangoszero-docs.pdf visionato in data 15/12/2014 docs 4.3 Ricerca e risoluzione 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 62 void HealedBy(Unit* /*pHealer*/, uint32& /*uiHealedAmount */) override {} // Called at any Damage to any victim (before damage apply) void DamageDeal(Unit* /*pDoneTo*/, uint32& /*uiDamage*/) override {} // Called at any Damage from any attacker (before damage apply) void DamageTaken(Unit* /*pDealer*/, uint32& /*uiDamage*/) override {} // Called at creature killing another unit void KilledUnit(Unit* /*pVictim*/) override {} // Called when hit by a spell void SpellHit(Unit* /*pCaster*/, const SpellEntry* /*pSpell*/) override {} // Called when spell hits creature’s target void SpellHitTarget(Unit* /*pTarget*/, const SpellEntry* /*pSpell*/) override {} // Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc) /// This will by default result in reattacking, if the creature has no victim void AttackedBy(Unit* pAttacker) override { CreatureAI:: AttackedBy(pAttacker); } // Called at text emote receive from player void ReceiveEmote(Player* /*pPlayer*/, uint32 /*uiEmote */) override {} // Called at each attack of m_creature by any victim void AttackStart(Unit* pWho) override; // Called at World update tick void UpdateAI(const uint32) override; // == State checks ================================= // Check if unit is visible for MoveInLineOfSight bool IsVisible(Unit* pWho) const override; // Called when victim entered water and creature can not enter water bool canReachByRangeAttack(Unit* pWho) override { 4.3 Ricerca e risoluzione 63 return CreatureAI::canReachByRangeAttack(pWho) 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 ; } // ************* // Variables // ************* // ************* // Pure virtual functions // ************* /** * This is a SD2 internal function, that every AI must implement * Usally used to reset combat variables * Called by default on creature evade and respawn * In most scripts also called in the constructor of the AI */ virtual void Reset() = 0; /// Called at creature EnterCombat with an enemy /** * This is a SD2 internal function * Called by default on creature EnterCombat with an enemy / * virtual void Aggro(Unit* /*pWho*/) {} // ************* // AI Helper Functions // ************* // Start movement toward victim void DoStartMovement(Unit* pVictim, float fDistance = 0, float fAngle = 0); // Start no movement on victim void DoStartNoMovement(Unit* pVictim); // Stop attack of current victim void DoStopAttack(); // Cast spell by Id void DoCast(Unit* pTarget, uint32 uiSpellId, bool bTriggered = false); // Cast spell by spell info void DoCastSpell(Unit* pTarget, SpellEntry const* 4.3 Ricerca e risoluzione 64 pSpellInfo, bool bTriggered = false); 103 104 // Returns spells that meet the specified criteria from the creatures spell list SpellEntry const* SelectSpell(Unit* pTarget, int32 uiSchool, int32 iMechanic, SelectTarget selectTargets, uint32 uiPowerCostMin, uint32 uiPowerCostMax, float fRangeMin, float fRangeMax, SelectEffect selectEffect) ; 105 106 107 108 // Checks if you can cast the specified spell bool CanCast(Unit* pTarget, SpellEntry const* pSpell, bool bTriggered = false); 109 110 111 112 }; void SetEquipmentSlots(bool bLoadDefault, int32 iMainHand = EQUIP_NO_CHANGE, int32 iOffHand = EQUIP_NO_CHANGE, int32 iRanged = EQUIP_NO_CHANGE); Vista la numerosità dei metodi disponibili vediamo ora lo scopo di quelli che saranno coinvolti nella realizzazione della soluzione al bug in oggetto di analisi: • virtual void Reset() = 0;: viene utilizzato per reimpostare le strut- ture dati e le azioni della creatura allo stato originale (prima della sua attivazione). • virtual void Aggro(Unit* /*pWho*/) {}: viene richiamato ogni volta che la creatura può entrare in combattimento con altre creature. L'invocazione di questo metodo avviene direttamente dalla libreria ScriptDev2 durante l'esecuzione del reame. • void AttackedBy(Unit* pAttacker) override { CreatureAI::- AttackedBy(pAttacker); }: viene invocato dalla libreria quando la creatura viene attaccata da un'altra creatura. Le funzioni sono ereditate da un'altra classe CreatureAI dichiarata nelle funzioni basilari del reame. Le creature della libreria ScriptDev2 ereditano tutti i metodi delle creature denite nel reame per garantire la consistenza e coerenza degli oggetti. • void AttackStart(Unit* pWho) override;: viene invocato quando la creatura rappresentata da questa classe deve compiere una azione di attacco. 4.3 Ricerca e risoluzione 65 • void UpdateAI(const uint32) override;: viene invocato dal reame ciclicamente per consentire l'esecuzione continua delle abilità implementate. Anche quanto l'NPC viene ucciso e nel periodo per cui il suo corpo rimane nell'area di gioco questo metodo viene sempre invocato. Per meglio utilizzare e organizzare gli identicativi numerici relativi alle informazioni archiviate nel database e agli elementi di gioco è stata realizzata una struttura di tipo Enum, adatta a questo scopo, così popolata: 1 enum { 2 SAY_STORYLINE4 = -1654004, 3 SAY_STORYLINE5 = -1654005, 4 SAY_STORYLINE6 = -1654006, 5 SAY_STORYLINE7 = -1654007, 6 SAY_STORYLINE8 = -1654008, 7 8 SAY_STORY_DELAY = 30000, 9 10 SPELL_SHOOT = 50092 11 }; I dati membri di questa classe, usati per gestire le azioni del NPC in oggetto di analisi saranno: 1 2 3 4 5 6 7 uint32 m_uiSayStoryTimer; int m_uiSayStoryLast; Unit* lastVictim; bool m_bCanSayStory; bool IsSelfRooted; Vediamo quindi lo scopo di ciascun dato membro: • m_uiSayStoryTimer: serve per temporizzare la successione dei testi da pronunciare. • lastVictim: conterrà la classe relativa all'ultima creatura bersaglio del nostro NPC . • m_uiSayStoryLast: serve per tenere la traccia dell'ultimo testo pro- nunciato e proseguire in sequenza con i testi successivi, no a ricominciare dal primo dopo che l'ultimo è stato pronunciato. • m_bCanSayStory: serve come "semaforo" per stabilire quando è il momento corretto per pronunciare il testo. Viene usato in combinazione con m_uiSayStoryTimer. 4.3 Ricerca e risoluzione 66 • IsSelfRooted: serve per vericare se il nostro NPC ha il come bersaglio se stesso o una creatura diversa. Oltre a questi dati membri da me creati esiste l'oggetto m_creature che appartiene alla classe madre CreatureAI, ereditato automaticamente, così denito: 1 Creature* const m_creature; I dati membri sono inizializzati (e reimpostati ogni volta) con i seguenti valori: 1 void Reset() override 2 { 3 m_uiSayStoryTimer = SAY_STORY_DELAY; 4 m_uiSayStoryLast = 0; 5 lastVictim = NULL; 6 m_bCanSayStory = true; 7 IsSelfRooted = false; 8 } Il metodo Aggro(), di cui è stata mostrata la denizione in precedenza, sarà così denito: 1 void Aggro(Unit* who) override 2 { 3 if (!m_creature->CanReachWithMeleeAttack(who) && ! m_creature->CanUseEquippedWeapon(RANGED_ATTACK)) 4 { 5 IsSelfRooted = true; 6 } 7 } Questo metodo viene usato per stabilire se il nostro NPC può attaccare una creatura che ha vicino o entro il suo raggio di azione delle sue armi a distanza. Il metodo CanReachWithMeleeAttack(who) consente di stabilire se la creatura contenuta nell'oggetto who può essere attaccata dal nostro NPC con un'arma da combattimento di mischia, mentre il metodo CanUseEquippedWeapon(RANGED_ATTACK) consente di stabilire se il nostro NPC ha la possibilità di usare un'arma da distanza presente nel suo equipaggiamento (RANGED_ATTACK è una costante denita globalmente nel progetto ScriptDev2 per identicare gli attacchi con armi da distanza, ad esempio: archi, balestre, fucili, ecc.). Se entrambe le condizioni non sono soddisfatte, viene impostata la condizione di bersaglio sul NPC stesso per impedirgli di compiere azioni di attacco. 4.3 Ricerca e risoluzione 67 Il metodo AttackedBy, di cui è stata mostrata la denizione in precedenza, sarà così denito: 1 void AttackedBy(Unit* pAttacker) override 2 { // Check if Liam is attacking who attack he and don’t jump 3 on multiple attackers if (m_creature->getVictim() && (m_creature->getVictim() == pAttacker || lastVictim == pAttacker)) return; 4 5 6 if (m_creature->IsFriendlyTo(pAttacker)) 7 return; 8 9 lastVictim = pAttacker; 10 AttackStart(pAttacker); 11 } Questo metodo viene usato per mantenere il bersaglio sso su una sola creatura quando più creature attaccano il nostro NPC. Con l'opportuna combinazione delle condizioni di controllo sul metodo getVictim() che identica l'attuale bersaglio, sull'oggetto pAttacker che identica la creatura che attacca il nostro NPC e lastVictim che, come visto nella denizione dei dati membro, contiene la classe dell'ultimo bersaglio del nostro NPC, il bersaglio degli attacchi viene mantenuto sso su una singola creatura no alla sua uccisione. Infatti il metodo AttackStart(pAttacker) verrà invocato unicamente quando il bersaglio corrente è uguale a quello precedente (se era stato già identicato) e il bersaglio corrente è diverso dal NPC stesso. Questo metodo consente di eseguire una azione di attacco. Il metodo UpdateAI(), di cui è stata mostrata la denizione in precedenza, è il punto nevralgico in cui gestire le attività del NPC e sarà così denito: 1 void UpdateAI(const uint32 uiDiff) override 2 { 3 // Ready to say emote 4 if (m_bCanSayStory) 5 { 6 m_uiSayStoryTimer = SAY_STORY_DELAY; 7 8 // Sequence switch between 3 texts 9 switch (m_uiSayStoryLast) 10 { 11 case 0: DoScriptText(SAY_STORYLINE4, m_creature); 12 m_uiSayStoryLast++; break; case 1: DoScriptText(SAY_STORYLINE5, m_creature); m_uiSayStoryLast++; break; 4.3 Ricerca e risoluzione 13 case 2: DoScriptText(SAY_STORYLINE6, m_creature); m_uiSayStoryLast++; break; case 3: DoScriptText(SAY_STORYLINE7, m_creature); m_uiSayStoryLast++; break; case 4: DoScriptText(SAY_STORYLINE8, m_creature); m_uiSayStoryLast = 0; break; 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 68 } m_bCanSayStory = false; } if (m_uiSayStoryTimer < uiDiff) { m_bCanSayStory = true; m_uiSayStoryTimer = SAY_STORY_DELAY; } else m_uiSayStoryTimer -= uiDiff; // Return since we have no target if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) return; // Equipped ranged weapon usable and not close to victim if (m_creature->GetCombatDistance(m_creature->getVictim(), false) > 0 && m_creature->CanUseEquippedWeapon( RANGED_ATTACK) ) { // Make sure our attack is ready if (m_creature->isAttackReady(RANGED_ATTACK)) { // Use spell instead of normal ranged attack that seem not working. TODO: check ranged attack! DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHOOT); m_creature->resetAttackTimer(RANGED_ATTACK); } } else if (m_creature->CanReachWithMeleeAttack(m_creature-> getVictim())) { // If we are within range melee the target // Make sure our attack is ready if (m_creature->isAttackReady()) { m_creature->AttackerStateUpdate(m_creature->getVictim() ); m_creature->resetAttackTimer(); 4.3 Ricerca e risoluzione 53 54 55 56 57 58 59 60 61 } 69 } } else if (IsSelfRooted) { // Cancel our current spell and then allow movement again m_creature->InterruptNonMeleeSpells(false); IsSelfRooted = false; } In questo metodo vengono gestite le azioni di combattimento e la pronuncia dei testi con le strutture dati dichiarate nella parte iniziale della classe. Il controllo sulla struttura dati m_bCanSayStory viene fatto per vericare se la condizione di possibilità di pronuncia dei testi è soddisfatta. A quel punto vengono reimpostati i timer e viene richiamata la funzione della libreria ScriptDev2 DoScriptText che attiva l'animazione di dialogo, seguendo la sequenza di testi preimpostata. Il controllo sulla struttura dati m_uiSayStoryTimer serve per stabilire quando sarà nuovamente possibile pronunciare testi sfruttando la variabile uiDi che contiene la dierenza tra una chiamata al metodo in oggetto e l'altra. Questa dierenza è ssa al valore di 1000 millisecondi (ovvero il metodo UpdateAI() viene invocato dalla libreria ScriptDev2 ogni secondo). Se la struttura dati contatore ha un valore inferiore vengono attivate le condizioni per la riproduzione dei dialoghi alla prossima invocazione del metodo, altrimenti viene decrementata. Il controllo sulle condizioni SelectHostileTarget() e getVictim() servono per stabilire se la creatura in oggetto di scripting non ha un bersaglio da poter attaccare o non è in combattimento con un'altra creatura. Se almeno una di queste due condizioni è soddisfatta l'esecuzione del metodo viene interrotta in quanto le azioni successive non sono da eseguire. La sequenza delle ultime condizioni da vericare serve per stabilire se la creatura può attaccare con un'arma a distanza, deve attaccare in corpo a corpo oppure, da ultimo, se non deve fare azioni di attacco in quanto il suo bersaglio è se stessa. Con il metodo GetCombatDistance(m_creature->getVictim(), false) viene vericata la condizione per cui la nostra creatura possa compiere un attacco in distanza con il suo attuale bersaglio identicato dall'oggetto ritornato dal metodo getVictim() (il parametro false serve per indicare che la valutazione della distanza deve essere fatta per le armi da distanza e non da corpo a corpo, valutabile con lo stesso metodo e questo secondo parametro impostato a true ). Il secondo metodo per la verica della condizione CanUseEquippedWeapon(RANGED_ATTACK) serve per vericare che la nostra creatura 4.3 Ricerca e risoluzione 70 abbia a disposizione un'arma per combattere dalla distanza, specicata dalla struttura dati globale al progetto ScriptDev2 RANGED_ATTACK. Confermate le valutazioni appena descritte resta un ultimo controllo prima di poter far eseguire l'attacco: la verica che l'arma sia pronta per l'uso con il metodo isAttackReady(RANGED_ATTACK) 18 . Se tutte queste condizioni sono soddisfatte viene invocato il metodo DoCastSpellIfCan(...), che farà eseguire l'azione di attacco alla nostra creatura, e il metodo per attivare il tempo di ricarica dell'arma. Se invece l'attacco dalla distanza non fosse possibile viene vericata la possibilità di eseguire un attacco in corpo a corpo con il metodo CanReachWithMeleeAttack(...). Dopo aver vericato che l'arma per il combattimento sia pronta viene invocato il metodo AttackerStateUpdate(...) per questa tipologia di combattimenti e attivato il tempo di ricarica dell'arma. L'ultimo controllo della sequenza è la verica di avere come obiettivo se stessi e quindi interrompere qualsiasi attività di combattimento e rimuovere l'obiettivo. La classe completa di tutte le sue parti sarà quindi così costruita: 1 struct MANGOS_DLL_DECL npc_prince_liam_greymane_phase2AI : public ScriptedAI 2 { 3 npc_prince_liam_greymane_phase2AI(Creature* pCreature) : 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ScriptedAI(pCreature) { Reset(); } uint32 m_uiSayStoryTimer; int m_uiSayStoryLast; Unit* lastVictim; bool m_bCanSayStory; bool IsSelfRooted; void Reset() override { 18 L'uso delle armi non avviene tramite click del mouse o tasti speciali, ma è gestito in automatico dalle logiche del sistema di gioco. In particolare ogni arma, ma anche gli incantesimi e tutti gli oggetti di gioco, hanno un tempo di ricarica prima di poter essere usati e, successivamente al tempo di ricarica, l'azione dell'oggetto avviene in automatico. Quando una creatura inizia una azione di combattimento l'uso dell'arma avviene in automatico in base al suo tempo di ricarica, sia per le creature che per i giocatori. Questo sistema consente l'equità e l'ordine nel numero di attacchi consecutivi in un combattimento tra creature e tra giocatori 4.3 Ricerca e risoluzione 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 71 m_uiSayStoryTimer = SAY_STORY_DELAY; m_uiSayStoryLast = 0; lastVictim = NULL; m_bCanSayStory = true; IsSelfRooted = false; } void Aggro(Unit* who) override { if (!m_creature->CanReachWithMeleeAttack(who) && !m_creature->CanUseEquippedWeapon( RANGED_ATTACK)) { IsSelfRooted = true; } } void AttackedBy(Unit* pAttacker) override { // Check if Liam is attacking who attack he and don’t jump on multiple attackers if (m_creature->getVictim() && (m_creature->getVictim() == pAttacker || lastVictim == pAttacker)) return; if (m_creature->IsFriendlyTo(pAttacker)) return; lastVictim = pAttacker; AttackStart(pAttacker); } void UpdateAI(const uint32 uiDiff) override { // Ready to say emote if (m_bCanSayStory) { m_uiSayStoryTimer = SAY_STORY_DELAY; // Sequence switch between 3 texts switch (m_uiSayStoryLast) { case 0: DoScriptText(SAY_STORYLINE4, m_uiSayStoryLast++; break; case 1: DoScriptText(SAY_STORYLINE5, m_uiSayStoryLast++; break; case 2: DoScriptText(SAY_STORYLINE6, m_uiSayStoryLast++; break; case 3: DoScriptText(SAY_STORYLINE7, m_creature); m_creature); m_creature); m_creature); 4.3 Ricerca e risoluzione m_uiSayStoryLast++; break; case 4: DoScriptText(SAY_STORYLINE8, m_creature); m_uiSayStoryLast = 0; break; //case 3: DoScriptText(SAY_HELP4, m_creature); break; 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 72 } m_bCanSayStory = false; } if (m_uiSayStoryTimer < uiDiff) { m_bCanSayStory = true; m_uiSayStoryTimer = SAY_STORY_DELAY; } else m_uiSayStoryTimer -= uiDiff; // Return since we have no target if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) return; // Equipped ranged weapon usable and not close to victim if (m_creature->GetCombatDistance(m_creature->getVictim() , false) > 0 && m_creature->CanUseEquippedWeapon( RANGED_ATTACK) ) { // Make sure our attack is ready if (m_creature->isAttackReady(RANGED_ATTACK)) { // Use spell instead of normal ranged attack that seem not working. TODO: check ranged attack! DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHOOT ); //m_creature->AttackerStateUpdate(m_creature-> getVictim(),RANGED_ATTACK); m_creature->resetAttackTimer(RANGED_ATTACK); } } else if (m_creature->CanReachWithMeleeAttack(m_creature-> getVictim())) { // If we are within range melee the target // Make sure our attack is ready if (m_creature->isAttackReady()) { m_creature->AttackerStateUpdate(m_creature-> getVictim()); 4.3 Ricerca e risoluzione 99 100 101 102 103 104 105 73 m_creature->resetAttackTimer(); } 106 107 108 109 110 } 111 }; } else if (IsSelfRooted) { // Cancel our current spell and then allow movement again m_creature->InterruptNonMeleeSpells(false); IsSelfRooted = false; } Per terminare le operazioni di implementazione è necessario aggiungere la classe relativa al NPC in oggetto di script nel metodo che istanzia tutti gli oggetti presenti nel le di script C++ invocato dalla libreria ScriptDev2 quando un personaggio si trova nell'area di gioco: 1 void AddSC_gilneas_city() 2 { 3 Script* pNewScript; 4 5 pNewScript->Name = "npc_prince_liam_greymane_phase2"; 6 pNewScript->GetAI = & 7 8 } GetAI_npc_prince_liam_greymane_phase2; pNewScript->RegisterSelf(); Ora l'NPC è pronto per eseguire le azioni e le animazioni corrette. Per consentire al reame di attivare le procedure della libreria ScriptDev2 relative allo script realizzato, è necessario inserire nella tabella creature_template, già vista nel terzo capitolo durante l'analisi del database, il nome dello script creato, ovvero npc_prince_liam_greymane_phase2 nella colonna ScriptName. Inoltre bisogna valorizzare la colonna flags_extra a 0, per rendere l'NPC attivo (il signicato dei valori associati a questa colonna è spiegato sempre nel documento mangoszero-doc.pdf 19 ). Da ultimo è necessario cambiare anche il valore della colonna unit_flags con il nuovo valore 33024. Questa codica non è documentata, ma sono arrivato a ritenere corretto questo valore sulla base di altri NPC che hanno comportamenti simili per cui sono state apportate le implementazioni. 19 Il documento è consultabile a MaNGOS-Zero https://media.readthedocs.org/pdf/mangoszero-docs/latest/mangoszero-docs.pdf visionato in data 15/12/2014 docs 4.4 Pull Request sul repository 74 1 UPDATE creature_template SET ScriptName = ’ npc_prince_liam_greymane_phase2’, flags_extra = ’0’, unit_flags = ’33024’ WHERE entry = 34913 ; Dopo aver ricompilato il reame, con il progetto ScriptDev2 contenente il nuovo script come mostrato in gura 4.6, l'NPC avrà le azioni e animazioni corrette come mostrato in gura 4.7. Figura 4.6: Compilazione del reame con le modiche apportate Per contribuire alla documentazione sullo scripting degli NPC, ho pubblicato nel forum del progetto MaNGOS una mini guida per associare un testo ad un NPC. La documentazione pubblicata in data 17 dicembre 2014 è raggiungibile all'url https://www.getmangos.eu/mangos-university/10209scripting-howto-add-emote-text-creature.html (consultabile soltanto per gli utenti registrati al forum del progetto MaNGOS). 4.4 Pull Request sul repository Tutte le correzioni, sia con Database Scripts che in ScriptDev2, apportate e testate essere funzionanti, sono state proposte al repository principale dove i responsabili e moderatori le hanno accettate e integrate nel progetto. Di 4.4 Pull Request sul repository 75 Figura 4.7: NPC "Prince Liam Greymane" con animazioni e azioni seguito è riportato un breve elenco delle correzioni che sono state eettuate all'inizio del periodo di analisi: • NPC Prince Liam Greymane in fase 1: Assegnati testi e voce. • Cambio di fase: Corretto cambio di fase dopo accettazione e completamento della quest Something’s Amiss. • NPC Prince Liam Greymane in fase 2: Assegnati testi, voce e azioni. • Creatura Rampaging Worgen: associata abilità Enrage. • Corretta quest Evacuate the Merchant Square: aggiunta abili- tà Knoking per tutti i giocatori, aggiunta la comparsa delle creature Frightened Citizen e Rampaging Worgen con relative azioni all'apertura della porta e associata la progressione della quest quando vengono aperte le porte indicate. Successivamente non ho più eettuato proposte di correzioni al repository in quanto sto cercando di raggrupparle in porzioni di codice più corpose ed omogenee ed omogenei. 4.4 Pull Request sul repository 76 Sebbene alla pull request corrisponda una fase di peer review, non esiste un vero e proprio meccanismo di validazione automatica delle correzioni: la loro bontà viene vericata empiricamente provando le varie sessioni di gioco. Figura 4.8: Pull Request accettate con le modiche fatte su GitHub Capitolo 5 Un esempio di community In questo capitolo sarà presentato brevemente un modello reale di server per il gioco World Of WarcraftT M basato su un motore open source.20 Per una breve descrizione dei principali termini utilizzati si rimanda al glossario. 5.1 Un esempio di Server Privato La community www.molten-wow.com, ribattezzata di recente in www.Warmane.com, è un esempio di realtà costruita attorno allo sviluppo di un server per il gioco World Of WarcraftT M basato sul progetto open source TrinityCore, glio di MaNGOS. Questa community, fondata nel 2009 è cresciuta molto nel corso degli anni arrivando ad avere più di 20.000 utenti attivi nei momenti di picco delle connessioni. Dall'agosto del 2010 sono iscritto a questa community e con altalenanti periodi di assidua frequenza ho assistito in prima persona alla sua storia. Ci sono stati periodi in cui la community era più attiva e periodi di minor attività non solo da parte dei giocatori, ma anche da parte dello sta che curava lo sviluppo dei reami e la gestione degli utenti. Da ultimo, con l'inizio dell'anno 2015, si sono vericati due eventi cruciali: • Verso la ne di gennaio 2015 la community ha subito un abbandono da parte di alcuni membri dello sta che hanno causato un blackout generale di tutti i servizi oerti per alcuni giorni, oltre alla perdita dei dati di gioco di tutti i giocatori. La motivazione di questo boicottaggio sembra essere stata di carattere economico, ovvero di pagamenti non ricevuti per il lavoro svolto da alcuni sviluppatori. 20 Tutte le informazioni riportate in questo capitolo sono frutto di una rielaborazione della informazioni frammentate in più parti del forum della community. 5.1 Un esempio di Server Privato 78 • Dalla ne di febbraio 2015 la community di molten-wow ha avviato un processo di fusione con un'altra community per arrivare alla creazione di quella attuale warmane. 5.1.1 Organizzazione Ad oggi, la community, ospita sette reami con tre espansioni di gioco dierenti: • Tre reami Wrath of the Lich King, la seconda espansione del gioco. • Due reami Cataclysm, la terza espansione del gioco. • Due reami Mist of Pandaria, la quarta espansione del gioco. Recentemente è stato anche annunciato un reame PTR (Public Test Realm, ovvero un reame in versione pre release con lo scopo di vericare le funzionalità di base del gioco) Warlords of Draenor, la quinta e ultima (per ora) espansione del gioco. Questi reami non sono totalmente funzionanti, anzi, hanno tutti delle funzionalità mancanti o errate in particolare nel caso di espansioni recenti quanto l'espansione del gioco è più recente. Per correggere le funzionalità mancanti, lo sta è composto da una cospicua squadra di sviluppatori che lavorano a tempo pieno alle evoluzioni. Oltre ai reami, la community è incentrata su un portale/forum dove avvengono le comunicazioni fra sta e giocatori. Inoltre il portale è una piccola estensione del gioco in quanto per ogni account registrato sono disponibili un insieme di strumenti dedicati alla gestione dei personaggi, degli oggetti di gioco acquistabili e altre funzionalità di supporto. Per rendere maggiormente l'idea delle dimensioni di questa community riporto alcune informazioni statistiche sul forum: • Numero di utenti registrati: 2.119.170. • Numero di articoli del forum: 37.852. • Numero di risposte negli articoli del forum: 533.651. 5.1.2 Bugtracker e changelog Per gestire al meglio lo sviluppo dei reami nella correzione dei bug, i giocatori della community possono segnalare le funzionalità mancanti dei reami 5.1 Un esempio di Server Privato 79 in un apposito portale dedicato alla raccolta delle segnalazioni, chiamato BugTracker. In questo portale le segnalazioni vengono suddivise primariamente per espansione di gioco, poi per ogni espansione sono presenti sezioni che identicano puntualmente la tipologia di oggetto che riscontra il problema (quest, spell, abilità, oggetto, ecc.). In questo portale vengono riportati tutti i dettagli sugli errori riscontrati e lo sta, che prende in carico la segnalazione, può tracciare lo stato di avanzamento dei lavori di correzione, suddividersi le correzioni e pianicare gli aggiornamenti. Questi ultimi avvengono tramite annunci sul portale della community, che avvisano i giocatori quando il reame coinvolto verrà spento per applicare gli aggiornamenti delle funzionalità mancanti o errate. Per informare i giocatori sui progressi delle correzioni, dopo ogni aggiornamento di una espansione, viene pubblicato un changelog21 con i dettagli sulle funzionalità corrette. 5.1.3 il sistema delle donazioni I gestori della community hanno avviato con successo, come anche in altri server privati per questo videogioco, una politica di mantenimento dei costi di hosting e sviluppo tramite un sistema di scambio della valuta corrente in gettoni della community. Questi gettoni possono essere usati da ogni giocatore per comprare oggetti di gioco o benet per i personaggi. Per incentivare l'uso dei gettoni gli oggetti di gioco e i benet acquistabili sono esclusivi di queste transazioni e non possono essere ottenuti durante le sessioni di gioco. Il costante ricambio di giocatori in combinazione con questa "strategia di mercato aggressiva" uniti ai nuovi contenuti che ogni nuova espansione porta, favoriscono degli introiti abbastanza costanti che consentono il mantenimento di un organico salariato dedicato agli sviluppi e alle migliorie dei reami e di tutte le altre infrastrutture della community. Lo sta raccoglie le candidature che i giocatori fanno tramite presentazione del proprio Curriculum Vitae. Se ritenuti sucientemente abili per le attività di sviluppo, i giocatori possono entrare nell'organico dello sta a pagamento. A titolo esemplicativo riporto i dettagli dei requisiti richiesti per ricoprire alcune posizioni di sviluppatore:[6] 21 Il changelog (tradotto in italiano diario dei cambiamenti) è solitamente un documento che riepiloga le modiche apportate nell'ultima versione di un software. 5.1 Un esempio di Server Privato 80 • Senior Reverse Engineer con una vasta esperienza in reverse engi- neering e conoscenza della programmazione C++. Stipendio mensile circa $5.000. Requisiti: Vasta conoscenza di Assembly (x86-64,mips) C, C++, Java, C#, Lua, Pascal, Verilog, VHDL. Esperienza di lavoro con Ida PRO, WinDbg, WinICE, Visual Studio ecc. Abilità nell'uso dei sistemi UNIX Abilità di lavoro con sistemi di gestione dei sorgenti come GIT e SVN • Senior C++ Programmer con vasta esperienza nello sviluppo di infrastrutture. Stipendio mensile circa $4.000. Requisiti: Vasta esperienza nella programmazione C++. Buona conoscenza del linguaggio SQL. Buona capacità di comprensione del codice Abilità di scrivere e sviluppare codice eciente e ottimizzato per sistemi multiutente. Abilità nell'uso dei sistemi UNIX Abilità di lavoro con sistemi di gestione dei sorgenti come GIT e SVN • Senior Database Developer con vasta esperienza e conoscenza di MySQL, DB Scripting (Quests, NPCs, General). Stipendio mensile circa $3.000. Requisiti: Obbligatoria la conoscenza del sistema TrinityCore SmartAI. Abilità di lavoro con sistemi di gestione dei sorgenti come GIT e SVN La conoscenza della struttura del database TrinityCore sarà considerata come bonus Osservando le proposte di salario mensile per questi ruoli è possibile intuire che una community abbastanza grande come quella in oggetto di analisi comporti anche introiti sucienti a mantenere uno sta motivato nello sviluppo non solo spinto da spirito di volontariato personale. Capitolo 6 Conclusioni L'obiettivo di questo lavoro era di analizzare un emulatore open source per un gioco online molto popolare e famoso, al ne di capire quali siano le logiche di costruzione e sviluppo incrementale di un videogioco. Come riportato nell'introduzione ai concetti del videogioco in analisi, non esistono dei sorgenti con libero accesso sia per la componente client che per la componente server e, per tanto, tutte le funzionalità dell'emulatore sono state costruite sulla base di esperienze di gioco, analisi dei dati presenti nei portali dedicati ed esperimenti pratici. La costruzione di un emulatore è quindi un processo lento, che richiede molte risorse (non solo economiche) e che dicilmente eguaglierà le funzionalità del gioco originale. Ciò nonostante esistono realtà che riescono a far progredire l'evoluzione di questi emulatori per fornire un servizio (quasi) gratuito ai giocatori. La mia trattazione è stata volutamente un percorso a tappe partito dal concetto generico di videogioco online, passando ad un esempio concreto di sua realizzazione (l'emulatore), addentrandosi nell'ambiente in cui questo emulatore vive, guardando come questo emulatore funziona e consente l'interazione con il giocatore, per arrivare a realizzare una porzione di gioco e capire i legami tra gli oggetti che all'apparenza possono sembrare banali o scontati (per chi li usa frequentemente) ma che in realtà nascondono tutta una serie di logiche e schemi che rendono possibile la magia del gioco. Guardando il lavoro svolto nel suo complesso, oltre a poter identicare aspetti dell'emulatore che possono ricevere una maggiore attenzione, si può avere l'impressione che mi sia dedicato a questa analisi per un banale scopo ludico. La realtà dei fatti invece è molto dierente. Sicuramente una compo- 82 nente ludica nella scelta dell'oggetto di studio è stata presa in considerazione ma, in modo molto maggiore, la scelta è derivata dalla connessione stretta che lega il gioco con l'apprendimento e, ancora maggiormente, il legame tra gioco e creatività. Il lavoro svolto da chi analizza e sviluppa software ritengo che sia fondamentalmente un esercizio di creatività. Senza la creatività non si possono costruire software innovativi, senza la creatività non si possono risolvere problemi apparentemente irrisolvibili, senza la creatività non si può migliorare il mondo. Per questo motivo l'analisi condotta è stata in primis uno stimolo di ricerca della creatività in un gioco, piuttosto che un mero esercizio pratico di analisi applicata ad un software. Bibliograa [1] LUA about. LUA. Visionato in data: 24 Marzo 2015. url: http : //www.lua.org/about.html. [2] CPP. C++ Standards. Visionato in data: 3 Aprile 2015. url: https: //isocpp.org/std/status. [3] getMaNGOS. getMaNGOS History. Visionato in data: 2 Marzo 2015. url: https://www.getmangos.eu/history.php. [4] getMaNGOS. MaNGOS Zero docs. Visionato in data: da Ottobre 2014 a Aprile 2015. url: https://media.readthedocs.org/pdf/ mangoszero-docs/latest/mangoszero-docs.pdf. [5] gitHub. The GitHub. Visionato in data: 26 Marzo 2015. url: https: //github.com/about. [6] Molten-WoW. Molten Team Is Hiring! Visionato in data: 25 Marzo 2015. url: http://forum.warmane.com/showthread.php? t=282774. [7] MySQL. MySQL Sorage Engine. Visionato in data: 10 Marzo 2015. url: https : / / dev . mysql . com / doc / refman / 5 . 6 / en / storage-engines.html. [8] ScriptDev2 Team. ScriptDev2 Home. Visionato in data: da Ottobre 2014 a Aprile 2015. url: http://www.scriptdev2.com/. [9] Microsoft TechNet. Installare il ruolo Hyper-V e congurare una macchina virtuale. Visionato in data: 3 Ottobre 2014. url: https : / / technet.microsoft.com/it-it/library/hh846766.aspx. [10] Wikipedia. MMORPG. Visionato in data: 24 Febbraio 2015. url: https: //it.wikipedia.org/wiki/MMORPG. [11] Wikipedia. World Of Warcraft. Visionato in data: 25 Febbraio 2015. url: https://it.wikipedia.org/wiki/World_of_Warcraft. [12] WoWPedia. Phasing. Visionato in data: 20 Marzo 2015. url: http: //wow.gamepedia.com/Phasing. Glossario bitmask Un bitmask è una tipologia di dato binario posizionale su cui vengono eettuati confronti bit a bit tramite gli operatori logici (AND, OR e NOT). CMake è un tool per automatizzare la fase di costruzione (compilazione, collegamento, test, distribuzione, ecc.) di un sistema software; in particolare, consente di generare i Makele (da usare se si lavora in ambiente Linux). EXP Experience Points. GitHub è un portale web con lo scopo di fornire servizi come repository e controllo delle versioni. MaNGOS Massively Network Game Object Server. MMORPG Massive(ly) Multiplayer Online Role-Playing Game. MySQL è un Relational Database Management System (RDBMS) open source. NPC Non Playing Character. PostgreSQL è un Relational Database Management System (RDBMS) open source. PvE Player versus Environment. PvM Player versus Monster. quest Una quest (o missione) consiste in un insieme di sde che il giocatore deve superare per ottenere una ricompensa. repository Un repository è un ambiente di un sistema informativo (ad es. di tipo ERP), in cui vengono gestiti i metadati, attraverso tabelle relazionali. scripting Il termine scripting indica la costruzione di codice sorgente usando un metalinguaggio che sarà interpretato da un interprete costruito ad-hoc prima di essere eseguito. Glossario 85 shell La shell è una interfaccia del sistema operativo testuale, in cui i comandi vengono inseriti testualmente. SVN , abbreviazione di Subversion, è un sistema di controllo di versione per le sorgenti. Visual Studio è un ambiente IDE per lo sviluppo software. WoWHead è un portale web dedicato alla visualizzazione dei dettagli sugli elementi di gioco per World Of WarcraftT M . WoWWiki è un portale web dedicato alla visualizzazione dei dettagli sugli elementi di gioco per World Of WarcraftT M .