Progetto MaNGOS: Un server open source per videogiochi online

Transcript

Progetto MaNGOS: Un server open source per videogiochi online
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 .