ˇ ´IRODOVEDECK ˇ ´ FAKULTA UNIVERZITY PALACKEHO ´ PR A KATEDRA INFORMATIKY
´ RSK ˇ ´ PRACE ´ BAKALA A
S´ıt’ov´a hra ”ˇclovˇeˇce, nezlob se!”
2011
Adam Dohnal
Anotace ˇ eˇce, nezlob se je spoleˇcensk´ Clovˇ a deskov´a hra pro v´ıce hr´aˇc˚ u. C´ılem t´eto bakal´ aˇrsk´e pr´ace je vytvoˇrit serverov´e rozhran´ı, umoˇzn ˇuj´ıc´ı komunikaci klient˚ u mezi sebou, vˇcetnˇe chatov´an´ı, zakl´ad´an´ı a sledov´ an´ı her. Aplikace je naps´ana v jazyce C# pod platformou .NET. Hra nab´ız´ı uˇzivatel˚ um pˇr´ıjemn´e uˇzivatelsk´e rozhran´ı s velk´ym mnoˇzstv´ım hern´ıch pl´an˚ u aˇz pro ˇsest hr´aˇc˚ u.
Dˇekuji Mgr. Petru Osiˇckovi za odborn´e rady a veden´ı pˇri zpracov´an´ı t´eto bakal´aˇrsk´e pr´ace.
Spuˇstˇen´ı serveru z pˇr´ıkazov´e ˇr´adky . . . . . Pˇripojen´ı k serveru . . . . . . . . . . . . . . Hlavn´ı kan´al . . . . . . . . . . . . . . . . . . M´ıstnost . . . . . . . . . . . . . . . . . . . . Hra . . . . . . . . . . . . . . . . . . . . . . . Diagram z´avislost´ı modul˚ u v aplikaci . . . . Thread Pooling . . . . . . . . . . . . . . . . Diagram z´avislost´ı v modulu Server . . . . . Diagram tˇr´ıd a z´avislost´ı v modulu ServerUI Diagram tˇr´ıd a z´avislost´ı v modulu Game . .
6
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
10 11 11 12 13 14 16 17 25 31
Seznam tabulek
7
1.
´ Uvod
C´ılem t´eto bakal´aˇrsk´e pr´ace je vytvoˇrit dvˇe nez´avisl´e aplikace. Server, kter´ y se spust´ı jako konzolov´a aplikace a na urˇcit´em portu zaˇcne naslouchat pˇr´ıchoz´ı spojen´ı od klient˚ u. Souˇc´ast´ı programu je textov´ y soubor, umoˇzn ˇuj´ıc´ı spr´avci serveru zadat IP adresy, se kter´ ymi by se spojen´ı nemˇelo nav´azat. Druh´a aplikace je klient s grafick´ ym uˇzivatelsk´ ym rozhran´ım, kter´ y umoˇzn´ı uˇzivatel˚ um pˇripojit se na server, komunikovat s ostatn´ımi uˇzivately, zakl´adat hry a samozˇrejme obsahuje implementaci hry ˇclovˇeˇce, nezlob se. C´ılem nebylo vytvoˇrit jednoduchou deskovou hru a umoˇznit j´ı hr´at po s´ıti, ale zamˇeˇrit se sp´ıˇse na serverouvou ˇc´ast. S t´ım souvis´ı mnoho probl´em˚ u jak co nejefektivnˇeji zpracov´avat poˇzadavky klient˚ u, tak aby server nebyl pˇr´ıliˇs zahlcen nebo nepl´ ytval zdroji. V´ıce k t´eto problematice uvedu v sekci program´atorsk´a dokumentace. Hru ˇclovˇece, nezlob se jsem si vybral proto, ˇze je z´aroveˇ n popul´arn´ı a lehk´a na implementaci, ale z´aroveˇ n umoˇzn ˇuje hru aˇz pro ˇsest hr´aˇc˚ u, coˇz bylo pro mˇe mnohem zaj´ımavˇejˇs´ı neˇz hry jen pro dva hr´aˇce. V dalˇs´ıch ˇc´astech pr´ace bych chtˇel objastnit c´ıle, kter´e jsem si stanovil. Uˇzivatelskou dokumentaci, aby kaˇzd´ y uˇzivatel vˇedˇel jak aplikaci spr´avnˇe pouˇz´ıvat a co vˇse je potˇreba pro jej´ı chod. Vˇetˇsinu pr´ace bych chtˇel vˇenovat program´atorsk´e dokumentaci, popisuj´ıc´ı pouˇzit´e postupy a probl´emy, se kter´ ymi jsem se setkal pˇri implementaci. Na z´avˇer u ´vodn´ı ˇc´asti bych chtˇel dodat, ˇze jsem aplikaci vypracoval, tak abych co nejv´ıce oddˇelil jednotliv´e logick´e celky od sebe, vˇcetnˇe prezentaˇcn´ı ˇc´asti od logick´e, takˇze serverov´a ˇc´ast je zcela nez´avisl´a na typu klientovi. Samozˇrejme, ˇze pro jin´ y typ klienta by se musel napsat vlastn´ı komunikaˇcn´ı protokol a implementovat vlastn´ı obsluhy reaguj´ıc´ı na poˇzadavky klienta. J´adro, kter´e je zodpovˇedn´e za pˇrij´ım´an´ı, zpracov´an´ı a odes´ıl´an´ı dat je nez´avisl´e a vytvoˇreno tak aby bylo efektivn´ı, i kdyˇz to tento typ klienta pˇr´ıliˇs nevyˇzaduje.
8
2.
Poˇ zadavky na pr´ aci Aplikace se bude skl´adat ze dvou hlavn´ıch ˇc´ast´ı:
1. serveru, ke kter´emu se budou moci hr´aˇci • pˇrihl´asit • vytv´aˇret a pˇripojovat se do her • komunikovat mezi sebou pomoc´ı chatu 2. grafick´eho klienta, kter´ y bude komunikovat s uˇzivatelem a serverem implementuj´ıc´ı deskovou hru ˇclovˇeˇce, nezlob se Dalˇs´ı poˇzadavky: • podpora hry 2-6 hr´aˇc˚ u • v´ ybˇer z v´ıce hern´ıch pl´an˚ u • soukrom´e zpr´avy mezi hr´aˇci
9
3.
Uˇ zivatelsk´ a dokumentace
V t´eto kapitole pop´ıˇsu spuˇstˇen´ı a zp˚ usob pr´ace s aplikac´ı, jak se pˇripojit k serveru a pop´ıˇsu uˇzivatelsk´e rozhran´ı.
3.1.
Spuˇ stˇ en´ı serveru
Server m˚ uˇzeme spustit dvˇema zp˚ usoby. Prvn´ı zp˚ usob je spuˇstˇen´ı spustiteln´eho souboru ServerUI.exe. Server bude naslouchat defaultnˇe na portu 4000. Pokud z nˇejak´eho d˚ uvodu budeme potˇrebovat server spustit na vlastn´ım portu, m˚ uˇzeme server spustit z pˇr´ıkazov´e ˇr´adky jak ukazuje obr´azek 1.. Pokud inicializace probˇehla u ´spˇeˇsnˇe, tak je server pˇripraven zpracov´avat poˇzadavky od klient˚ u.
Obr´azek 1. Spuˇstˇen´ı serveru z pˇr´ıkazov´e ˇr´adky
3.2.
Spuˇ stˇ en´ı klienta
Klienta spust´ıme pomoc´ı spustiteln´eho souboru ClientUI.exe. Po spuˇstˇen´ı se objev´ı standartn´ı okno s menu. Menu Server umoˇzn ˇuje pˇripojen´ı/odpojen´ı od serveru a ukonˇcen´ı aplikace. Po kliknut´ı na nab´ıdku Connect se objev´ı dialog, kter´ y ukazuje obr´azek 2.. V dialogu jsou nastaveny nˇekter´e defaultn´ı hodnoty: IP adresa lok´aln´ıho rozhran´ı (loopback) a defaultn´ı port 4000. Pokud by jsme server provozovali na jin´em portu a na jin´e veˇrejn´e IP adrese, bylo by nutn´e tyto u ´daje pozmˇenit.
10
Obr´azek 2. Pˇripojen´ı k serveru
3.3.
Hlavn´ı kan´ al
Po u ´spˇeˇsn´em pˇripojen´ı se v klientovi zobraz´ı hlavn´ı kan´al jak ukazuje obr´azek 3..
Obr´azek 3. Hlavn´ı kan´al
V tomto kan´ale jsou vidˇet vˇsichni hr´aˇci, kteˇr´ı jsou pˇripojeni do hlavn´ıho kan´alu a je moˇzn´e s nimi komunikovat pomoc´ı chatu. D´ale zde m˚ uˇzeme vidˇet seznam zaloˇzen´ ych m´ıstnost´ı, kter´e ˇcekaj´ı na pˇripojen´ı ostatn´ıch hr´aˇc˚ u. Do 11
m´ıstnost´ı je moˇzn´e se pˇripojovat, ale i vytv´aˇret nov´e. Zaj´ımavou funkc´ı je pos´ılan´ı soukrom´ ych zpr´av. Pokud v´ıme jm´eno hr´aˇce, kter´ y je pˇripojen na serveru, ale je v jin´em kan´ale nebo nechceme aby tuto zpr´avu vidˇel nˇekdo jin´ y, nap´ıˇseme do chatu /w jm´ eno hr´ aˇ ce zpr´ ava. Pokud chceme napsat soukromou zpr´avu v r´amci stejn´eho kan´alu, m˚ uˇzeme dvakr´at kliknout myˇs´ı na hr´aˇce v seznamu nebo prav´ ym tlaˇcitkem vybrat moˇznost Whisper.
3.4.
M´ıstnosti
Po vytvoˇren´ı nebo pˇripojen´ı do m´ıstnosti se klient pˇresune do novˇe vytvoˇren´eho kan´alu. Tento kan´al slouˇz´ı ke spuˇstˇen´ı hry. Hru m˚ uˇze spustit pouze hr´aˇc, kter´ y m´ıstnost zaloˇzil a v m´ıstnosti mus´ı b´ yt minim´alnˇe dva hr´aˇci. V kan´ale je moˇzn´e komunikovat pomoc´ı chatu a pos´ıl´an´ı soukrom´ ych zpr´av.
Obr´azek 4. M´ıstnost
3.5.
Hra
Po spuˇsten´ı hry v m´ıstnosti se serverov´e rozhran´ı schov´a a objev´ı se nov´e okno s inicializovanou hrou. V prav´em horn´ım rohu m˚ uˇzeme vidˇet n´azev hry, zvolenou mapu a naˇse jm´eno. Kaˇzd´emu hr´aˇci byla pˇridˇelena urˇcit´a barva. Seznam barev hr´aˇc˚ u je zobrazen vpravo dole. D´ale m˚ uˇzeme vidˇet, kter´ y hr´aˇc je na tahu a pod n´ım je zobrazena n´apovˇeda, kter´a n´am rad´ı co m´ame dˇelat. Pokud se na n´as dostane ˇrada, mus´ıme hodit kostkou. Klikneme na tlaˇc´ıtko Throw a server n´am vygeneruje ˇc´ıslo. M´ame urˇcit´ y poˇcet pokus˚ u podle toho, jestli m´ame vˇsechny 12
figurky v domeˇcku nebo ne. Vpravo od hrac´ı kostky se n´am zaznamen´av´a fronta hod˚ u, takˇze kdyˇz pak t´ahneme figurkou vid´ıme, jak´e m´ame aktu´aln´ı ˇc´ıslo hodu. Ve hˇre je moˇzn´e zase chatovat a pos´ılat soukrom´e zpr´avy. Hru je moˇzn´e opustit a v tom pˇr´ıpadˇe se n´am objev´ı hlavn´ı kan´al.
Obr´azek 5. Hra
13
4.
Program´ atorsk´ a dokumentace
4.1.
Architektura aplikace
Aplikace je naps´ana v programovac´ım jazyce C# pod platformout .NET. Byl pouˇzit objektovˇe orientovan´ y n´avrh a snaˇzil jsem se co nejv´ıce rozdˇelit projekt do v´ıce samostatn´ ych a nezavisl´ ych celk˚ u, kter´e spolu spolupracuj´ı. V´ yhoda tohoto n´avrhu je vˇetˇs´ı ˇcitelnost, odolnost v˚ uˇci zmˇen´am (napˇr. zmˇena grafick´eho rozhran´ı by nemˇela zasahovat do logiky aplikace) a hlavnˇe znovupouˇzitelnost funkˇcn´ıch ˇc´ast´ı aplikace. Celkem je tedy v projektu pˇet samostatn´ ych ˇc´ast´ı, jejichˇz z´avislosti ukazuje ˇ diagram na obr´azku 6.. C´asti Server, Protocol a Game jsou preloˇzeny jako knihovny a ˇc´asti ServerUI a ClientUI jsou spustiteln´e soubory s vlastn´ım uˇzivatelsk´ ym rozhran´ım. N´asleduje jednoduch´ y popis jednotliv´ ych ˇc´ast´ı: • Server - naslouch´a na urˇcit´em portu pˇripojen´ı od dlient˚ u, umoˇzn ˇuje filtrovat spojen´ı na z´akladˇe IP adresy a ˇcte/zapisuje data do streamu. Data, kter´a od klienta pˇreˇcte pos´ıl´a zpracovat do modulu ServerUI. • ServerUI - zpracov´av´a data od klient˚ u a odes´ıl´a odpovˇedi. • Game - obsahuje logiku hry, v tomto pˇr´ıpadˇe ˇclovˇeˇce, nezlob se • ClientUI - vrstva mezi uˇzivatelem a serverem. Zpracov´av´a data, kter´a pˇriˇsla ze serveru, zobrazuje je a ˇr´ıd´ı chod hry.
Game
ClientUI
Server UI
Protocol
Server
Obr´azek 6. Diagram z´avislost´ı modul˚ u v aplikaci
14
4.2.
Server
C´ılem tohoto modulu je vytvoˇrit v´ıcevl´aknov´ y TCP/IP server co nejv´ıce nez´avisl´ y na typu klienta a protokolu tak, aby byl pouˇziteln´ y a efektivn´ı v co nejˇsirˇs´ı oblasti. 4.2.1.
Z´ akladn´ı poˇ zadavky
Po spuˇstˇen´ı serveru by mela probˇehnout jeho inicializace. S t´ım souvis´ı nastaven´ı portu, naˇcten´ı souboru obsahuj´ıc´ı IP adresy, kter´e se budou filtrovat a dalˇs´ı moˇznosti. Po u ´spˇeˇsn´e inicializaci by mˇel server naslouchat na dan´em ˇ an´ı na pˇripojen´ı klient˚ portu pˇripojen´ı od klient˚ u. Cek´ u je blokujic´ı operace, coˇz znamen´a, ˇze program se zastav´ı a nebude pokraˇcovat dokud se nˇejak´ y klient nepˇripoj´ı nebo se program neukonˇc´ı. To znamen´a, ˇze naslouch´an´ı mus´ı prob´ıhat ve vlastn´ım vl´aknˇe. Po nav´az´an´ı TCP/IP spojen´ı s klientem se nad t´ımto spojen´ım mus´ı vytvoˇrit tzv. network stream, ˇcili datov´ y proud umoˇznuj´ıc´ı ˇcten´ı a z´apis. Posledn´ı ˇc´ast´ı je obsluha klient˚ u, ˇcili naˇcten´ı dat, jejich n´asledn´e zpracov´an´ı jinou logickou ˇc´ast´ı aplikace a z´apis odpovˇedi zpˇet klientovi. Nast´av´a ot´azka, jak´ ym zp˚ usobem obsluhovat co nejv´ıc klient˚ u z´aroveˇ n tak, aby byla doba od pˇrijmut´ı poˇzadavku do jeho vyˇr´ızen´ı co nejmenˇs´ı a z´arovˇen ˇ aby se efektivnˇe vyuˇz´ıvali zdroje na serveru. Prvn´ı zp˚ usob je m´ıt pro kaˇzd´eho klienta vlastn´ı vl´akno, ve kter´em bude dan´a obsluha prob´ıhat. Ve skuteˇcnosti to znamen´a, ˇze jakmile je akceptov´ano nov´e spojen´ı, je vytvoˇreno nov´e vl´akno a tomu je tohle spojen´ı pˇred´ano jako parametr. Pro menˇs´ı aplikace, kde se nepˇripojuje mnoho klient˚ u je toto ˇreˇsen´ı postaˇcuj´ıc´ı. Tento zp˚ usob je jednoduch´ y na implementaci, ale nese s sebou urˇcit´e nev´ yhody: • m˚ uˇze nastat situace, ˇze se na server pˇripoj´ı mnoho klient˚ u a poˇcet vytvoˇren´ ych vl´aken bude rychle nar˚ ustat a celkov´a odezva bude klesat. Nav´ıc maxim´aln´ı poˇcet vl´aken je v operaˇcn´ım syst´emu nˇejak omezen. Pˇri takov´e situaci je tato metoda dost neefektivn´ı. • druh´ y probl´em nastane tehdy, pokud je sice na server pˇripojeno relativnˇe m´alo klient˚ u z´aroveˇ n, ale ˇcasto se spojen´ı navazuj´ı a ukonˇcuj´ı. Pˇr´ıkladem m˚ uˇze b´ yt webserver, kter´ y pˇrijme poˇzadavek, vrat´ı odpovˇed’ a ukonˇc´ı spojen´ı. Probl´em spoˇc´ıv´a v tom, ˇze vytv´aˇren´ı a uvolˇ nov´an´ı vl´akem je ˇcasovˇe n´aroˇcn´a operace.
15
4.2.2.
Thread pooling
Lepˇs´ı metoda obsluhy klient˚ u je tzv. thread pooling. Jde o to, ˇze m´ame frontu poˇzadavk˚ u, na jej´ıˇz konec jsou vkl´ad´any poˇzadavky od klient˚ u v takov´em poˇrad´ı v jak´em pˇriˇsli. D´ale jeˇstˇe pˇred spuˇstˇen´ım serveru vytvoˇr´ıme urˇcitou mnoˇzinu vl´aken, kter´e pˇristupuj´ı do fronty, kde vyberou prvn´ı poˇzadavek, obslouˇz´ı ho a jdou na dalˇs´ı. Je d˚ uleˇzit´e, aby pˇr´ıstup do fronty byl synchronizovan´ y, tzn. aby nedoˇslo k tomu, ˇze v´ıce vl´aken vezme souˇcasnˇe stejn´ y poˇzadavek. Tato metoda ˇc´asteˇcnˇe ˇreˇs´ı probl´em s neust´al´ ym vytv´aˇren´ım a uvolˇ nov´an´ım vlak´em po pˇripojen´ı nov´eho klienta. Klienti se m˚ uˇzou pˇripojovat a odpojovat libovolnˇe, ale mnoˇzina vl´aken z˚ ustane stejn´a. Napsal jsem shchv´alnˇe ˇc´asteˇcnˇe, protoˇze mnoˇzina, kter´a ma staticky dan´ y poˇcet vl´aken m˚ uˇze m´ıt vysokou odezvu. Ot´azka tedy zn´ı, jak urˇcit velikost mnoˇziny vl´aken tak, aby nedoch´azelo ke zbyteˇcn´emu vytv´aˇren´ı a uvolˇ nov´an´ı vl´aken, ale doba ˇcek´an´ı ve frontˇe byla co nejmenˇs´ı. Pˇredstavme si pˇr´ıklad, ˇze mnoˇzina obsahuje 3 vl´akna a ve frontˇe ˇcek´a na vyˇr´ızen´ı 10 poˇzadavk˚ u. Doba ˇcek´an´ı posledn´ıho poˇzadavku tedy bude zhruba ˇctyˇrikr´at vˇetˇs´ı neˇz pr˚ umˇern´a doba vyˇr´ızen´ı poˇzadavku. Kdyby jsme mˇeli mnoˇzinu, kter´a obsahuje 10 vl´aken, doba ˇcek´an´ı by nebyla tˇemˇeˇr ˇz´adn´a. M˚ uˇze n´as napadnout vytvoˇrit ze zaˇc´atku mnoho vl´aken a pozdˇeji se vyhneme vytv´aˇren´ı dalˇs´ıch a doba ˇcek´an´ı ve frontˇe bude mal´a, ale nastav´a dalˇs´ı probl´em. Co kdyˇz budou k serveru pˇripojeni jen 2 klienti a poˇcet vl´aken bude 20. Obsluhu tˇechto dvou klient˚ u by zvl´adly 1-2 vl´akna podle charakteru aplikace a zbytek by nic nedˇelal. Takov´emu pl´ ytv´an´ı zdroj˚ u je tak´e dobr´e se vyhnout a t´ım se dostav´ame k tomu, ˇze nejlepˇs´ı zp˚ usob je dynamick´a mnoˇzina vl´aken, tedy poˇcet vl´aken se za bˇehu mˇen´ı podle velikosti fronty a doby zpracov´an´ı poˇzadavku od klienta. Algoritm˚ u, kter´e ˇreˇs´ı dynamick´e pˇrid´av´an´ı a odstraˇ nov´an´ı vl´aken v mnoˇzinˇe existuje cel´a ˇrada. Nˇeco maj´ı spoleˇcnˇe a v nˇeˇcem se liˇs´ı podle typu aplikace. Obecnˇe m˚ uˇzeme tedy c´ıl takov´eho algoritmu definovat jako omezen´ı doby ˇcek´an´ı na minimum, minimalizovat poˇcet vl´aken, kter´e jsou v mnoˇzinˇe zbyteˇcn´e a zabr´anit ˇcast´emu vytv´aˇren´ı nebo odstraˇ nov´an´ı vl´aken v mnoˇzine.
1st req
...
nth req
Thread Pool
Obr´azek 7. Thread Pooling
16
Listener
4.2.3.
Implementace
Pˇri implementaci jsem si vybral metodu thread pooling s jednoduch´ ym algoritmem dynamick´eho vytv´aˇren´ı a uvolˇ nov´an´ı vl´aken v mnoˇzinˇe. Algoritmus zjiˇst’uje velikost fronty poˇzadavk˚ u, vydˇel´ı ji aktu´aln´ım poˇctem vl´aken a v´ ysledek vyn´asob´ı pr˚ umˇernou dobou zpracov´an´ı poˇzadavku, kterou si sama vl´akna poˇc´ıtaj´ı. V´ ysledkem je zhruba maxim´aln´ı doba ˇcek´an´ı poˇzadavku. Pokud doba pˇresahuje urˇcitou mez je potˇreba vytvoˇrit dalˇs´ı vl´akna, tak aby doba ˇcek´an´ı ve frontˇe klesla pod danou hranici. Dopoˇc´ıt´a se kolik je potˇreba vytvoˇrit vl´aken a ty se vytvoˇr´ı. Naopak pokud do je fronta daleko menˇs´ı neˇz poˇcet vl´aken, vl´akna se uvoln´ı. D´ale je stanovena hranice mininum a maximum vl´aken, kter´e nen´ı moˇzno pˇrekroˇcit. Jˇeˇstˇe neˇz pop´ıˇsu vˇsechny tˇr´ıdy modulu, chtˇel bych ˇr´ıct nˇeco a zpracov´an´ı poˇzadavk˚ u. V tomto modulu je definov´ana abstraktn´ı tˇr´ıda, kter´a zodpov´ıd´a za zpracov´an´ı poˇzadavk˚ u a odesl´an´ı odpovˇed´ı. Je ˇcistˇe abstraktn´ı takˇze program´ator mus´ı tuto tˇr´ıdu zdˇedit, doplnit vlastn´ı protokol a vytvoˇrit vlastn´ı handler pro kaˇzdou zpr´avu. V modulu je pak singleton, kter´ y m´a v sobˇe pr´avˇe tuto uˇzivatelsky definovanou tˇr´ıdu (tato tˇr´ıda se mus´ı pˇredat v konstruktoru pˇri vytv´aˇren´ı instance modulu), kter´e bude modul pˇrepos´ılat vˇsechny pˇrijat´e data. T´ım se pouˇzit´ı velice zjednoduˇsuje. Program´atorovi staˇc´ı zdˇedit tˇr´ıdu, napsat svoje handlery, vytvoˇrit novou instanci serveru a tuto tˇr´ıdu mu pˇredat a vˇse bude fungovat. O tom jakou strukturu ma poˇzadavek a o tom jak vypad´a komunikaˇcn´ı protokol bude ˇreˇc v nˇekter´e z dalˇs´ıch kapitol.
ConnectionPool
ClientService
SocketListener
ClientHandler
Server
RequestFilter
AbstractRequestHandler
Obr´azek 8. Diagram z´avislost´ı v modulu Server
17
Client Handler Client handler je tˇr´ıda, kter´a obaluje spojen´ı mezi serverem a klientem. Poskytuje obsluhu klienta, tzn. ˇcten´ı a z´apis do datov´eho proudu. Atributy • int id - identifik´ator, kter´ y se automaticky pˇridˇeluje po pˇripojen´ı klienta na server, je to jedin´a moˇznost jak pˇresnˇe identifikovat, kter´ y objekt obsluhuje dan´eho klienta. • TcpClient clientSocket - spojen´ı klienta ze serverem. • NetworkStream networkStream - datov´ y proud umoˇzn ˇuj´ıc´ı ˇcten´ı/z´apis. • string ip - IP adresa rozhran´ı, ze kter´eho se klient pˇripojil. • byte[] data - pole byt˚ u, kam se ukl´ad´aj´ı pˇr´ıchoz´ı data od klienta. Metody • Constructor(TcpClient client) - pˇridˇel´ı se ID nov´eho spojen´ı a vytvoˇr´ı se network stream. • void Process() - hlavn´ı metoda obsluhy klienta. Pokus´ı se z network streamu naˇc´ıst data a pˇredat je do funkce ProcessDataRecieved, popˇr. pokud dojde k vyvol´an´ı vyj´ımky ukonˇc´ı spojen´ı a informuje o tom okol´ı. • void ProcessDataRecieved() - data, kter´a byli naˇcteny rozdˇel´ı na pˇr´ısluˇsn´e poˇzadavky a ty pak odeˇsle uˇzivateli definovan´emu objektu, kter´ y tyto poˇzadavky d´ale zpracuje. • void Close() - uzavˇre network stream a ukonˇc´ı spojen´ı ze serverem. • void SendResponse(Request response) - odeˇsle odpovˇed’ klientovi. Client Connection Pool Fronta objekt˚ u typu ClientHandler.
Request Filter Udrˇzuje v sobˇe seznam IP adres, kter´e filtruje a nenech´a pˇripojit. Dan´e IP adresy naˇc´ıt´a ze vstupn´ıho souboru, kter´ y je uloˇzen ve stejn´em adres´aˇri jako server a jeho jm´eno bere jako parametr. Atributy 18
• string filename - jm´eno souboru s IP adresami. • List<string> bannedIPs - seznam IP adres Metody • Conctructor(string filename) - pokud soubor existuje zavol´a metodu Load. • void Load() - naˇcte ze souboru IP adresy a vloˇz´ı je do seznamu. • void Save() - uloˇz´ı seznam IP adres do souboru. • void Add(string ip) - pˇrid´a IP adresu do seznamu. • void Remove(string ip) - odebere IP adresu ze seznamu. • bool IsBanned(string ip) - zjist´ı jestli je dan´a IP adresa v seznamu. Client Service Tato tˇr´ıda m´a na starosti obsluhu fronty poˇzadavk˚ u a spravuje mnoˇzinu vl´aken. Atributy • ClientConnectionPool connectionPool - fronta poˇzadavk˚ u. • Thread[] threads - mnoˇzina vl´aken. • int NumOfThreads - poˇcet vl´aken. Metody • Constructor(ClientConnectionPool connPool) - pˇred´an´ı fronty • void Start() - vytvoˇr´ı a spust´ı vl´akna, kter´a zaˇcnou vykon´avat metodu Process. • void Process() - pokud nen´ı fronta pr´azdn´a, naˇcte prvn´ı poˇzadavek (ClientHandler) a zavol´a jeho metodu Process. Pˇr´ıstup do fronty je synchronizov´an pomoc´ı z´amku. • void Stop() - poˇck´a aˇz vˇsechny vl´akna dokonˇc´ı aktu´aln´ı poˇzadavek a ukonˇc´ı je, n´aslednˇe vypr´azdn´ı frontu poˇzadavk˚ u a odeˇsle vˇsem kliet˚ um, ˇze server se ukonˇcil.
19
AbstractRequestHandler ˇ e abstraktn´ı tˇr´ıda, kter´a slouˇz´ı ke zpracov´av´an´ı poˇzadavk˚ Cistˇ u od klient˚ u. Program´ator mus´ı vytvoˇrit vlastn´ı instanci tˇr´ıdy, kter´a tuto tˇr´ıdu dˇed´ı a mus´ı pˇrepsat jej´ı jedinou metodu. Metody • virtual void HandleRequest(ClientHandler client, Request req) RequestHandler Tˇr´ıda je navrˇzen´a jako vzor singleton tak, aby byla pˇr´ıstupn´a z cel´eho modulu a byla vytvoˇrena jen jedna instance, kter´a v sobˇe zapouzdˇruje objekt uˇzivatelsky definovan´e tˇr´ıdy na obsluhu poˇzadavk˚ u.
Socket Listener Vstupn´ı bod mudulu, kter´ y vytvaˇr´ı frontu, mnoˇzinu vl´aken, filtruje pˇr´ıchoz´ı spojen´ı, zabaluje je do ClientHandler objekt˚ u a ty n´aslednˇe vkl´ad´a do fronty. Atributy • int port - port, na kter´em server naslouch´a • string banlist - jmeno souboru obsahujici seznam filtrovanych IP adres • RequestFilter filter - filter IP adres Metody • Constructor(int portNumber, string banList) - nastav´ı port, vytvoˇr´ı filter a vloˇz´ı do nˇej IP adresy ze souboru • void StartListening() - vytvoˇr´ı pr´azdnou frontu, inicializuje ClientService a zaˇcne naslouchat na portu nov´e spojen´ı. Po obdrˇzen´ı poˇzadavku na spojen´ı zjist´ı IP adresu klienta a zkontroluje, jestli nen´ı obsaˇzen´a ve filtru. Pot´e vytvoˇr´ı instanci tˇr´ıdy ClientHandler nad t´ımto spojen´ım a um´ıst´ı jej do fronty. • void Start() - vytvoˇr´ı nov´e vl´akno, kter´e zaˇcne vykon´avat metodu StartListening() • void Stop() - zastav´ı ClientService a ukonˇc´ı naslouch´an´ı • void Restart() - restartuje server 20
4.3.
Protocol
Komunikaˇcn´ı protokol je d˚ uleˇzit´a ˇc´ast v klient-server aplikaci. Urˇcuje jak´ ym zp˚ usobem jsou data uspoˇr´ad´ana na aplikaˇcn´ı vrstvˇe v paketu a t´ım p´adem jak s nimi m´a aplikace naloˇzit. Struktura dat m˚ uˇze b´ yt libovoln´a, ale obˇe strany, jak klient, tak server by mˇeli strukturu zn´at a vˇedˇet jak data zpracovat. Proto je tento modul pˇreloˇzen jako knihovna, kterou vyuˇz´ıv´a jak server tak klient. V t´eto kapitole bych rozvedl strukturu komunikaˇcn´ıho protokolu, kterou jsem navrhl, uvedl a popsal zpr´avy z parametry, kter´e se m˚ uˇzou pˇren´aˇset. 4.3.1.
Struktura
Jak jsem uˇz uvedl, struktura mus´ı b´ yt takov´a, aby se urˇcit´ ym zp˚ usobem c´ılov´a aplikace dozvˇela o jakou zpr´avu jde, popˇr´ıpadˇe kde jsou uloˇzeny jej´ı parametry a kde konˇc´ı. V m´em projektu jsem si vybral jednoduch´ y zp˚ usob, kter´ y data obaluje do tag˚ u pˇripom´ınaj´ıc´ı jazyk HTML. Obecnˇe pak data vypadaj´ı takto:
message_code
param 1
param 2
...
param n
Tag obsahuje k´od zpr´avy, kterou odes´ıl´a a za n´ı jsou v taz´ıch uloˇzeny parametry zpr´avy. Obˇcas se m˚ uˇze st´at, ˇze v jednom paketu se odeˇsle v´ıce neˇz jedna zpr´ava a c´ılov´a aplikace mus´ı jednotliv´e zpr´avy od sebe odliˇsit. K tomuto u ´ˇcelu slouˇz´ı tag . Aby byla pr´ace c´ılov´e aplikace ze strukturou tˇechto dat co nejlehˇc´ı, modul definuje pomocnou tˇr´ıdu Request, kter´a umoˇzn ˇuje snadno zpracov´avat a vytv´aˇret zpr´avy takov´eto struktury. Request Tato tˇr´ıda zapouzdˇruje strukturu dat zpr´avy pˇred sv´ ym okol´ım a pomoc´ı sv´eho rozhran´ı umoˇzn ˇuje aplikaci jednoduˇse zpracov´avat a vytv´aˇret tak´ev´eto zpr´avy aniˇz by program´ator nˇeco vˇedel o struktuˇre jak´ ym se data budou odes´ılat. Pokud by program´ator chtˇel vytvoˇrit vlastn´ı strukturu zpr´av, musel by pˇrepsat tuto tˇr´ıdu, protoˇze ostatn´ı moduly, kter´e data zpracov´avaj´ı nebo vytv´aˇr´ı se pr´avˇe odkazuj´ı na tuto tˇr´ıdu. Metody • int GetHeader() - vrac´ı k´od zpr´avy, uloˇzen´ y mezi tagy . • List<string> GetParams() - vrac´ı seznam vˇsech parametr˚ u, uloˇzen´ ych mezi tagy . 21
Protokol definuje standard, podle kter´eho prob´ıh´a komunikace a pˇrenos dat mezi dvˇema koncov´ ymi body. V naˇsem pˇr´ıpadˇe komunikace prob´ıh´a pomoc´ı zpr´av, kter´e jsou form´atov´any podle v´ yˇse uveden´eho zp˚ usobu a pˇren´aˇseny TCP segmentem. Oba konce mus´ı pˇresnˇe vˇedˇet jak´e zpr´avy maj´ı pˇr´ıjimat a co s nimi d´ale dˇelat. V t´eto ˇc´asti vyjmenuji d˚ uleˇzit´e zpr´avy i s parametry pomoc´ı nichˇz prob´ıha komunikace klient˚ u. • AUTH REQUEST nick - klient ˇz´ad´a server o autorizaci - server odpov´ı: – AUTH RESPONSE FAIL nick - pokud je na serveru pˇrihl´aˇsen uˇzivatel ze stejn´ ym jm´enem – GLOBAL CHANNEL - pokud autorizace probˇehla u ´spˇeˇsnˇe a hr´aˇc je pˇresunut do hlavn´ıho kan´alu – IP BANNED nick - pokud je hr´aˇc na seznamu filtrovan´ ych IP adres • SERVER QUIT - server se vyp´ın´a, d´a vˇedˇet vˇsem pˇripojen´ ym klient˚ um • PLAYER ADD nick - pokud se hr´aˇc pˇripoj´ı do kan´alu jsou mu odesl´any vˇsechny jm´ena hr´aˇc˚ uv kan´alu • PLAYER JOIN nick - pokud se hr´aˇc pˇripoj´ı do kan´alu, vˇsem hr´aˇc˚ um v kan´alu se odeˇsle, ˇze se pˇripojil nov´ y hr´aˇc • PLAYER LEFT nick - pokud se hr´aˇc odpoj´ı z kan´alu, vˇsem hr´aˇc˚ um v kan´alu se odeˇsle, ˇze se hr´aˇc odpojil • MESSAGE nick text - hr´aˇc odeˇsle zpr´avu na kan´al, zpr´ava je n´aslednˇe rozesl´ana vˇsem hr´aˇc˚ um na kan´ale 22
• CREATE ROOM name map hostname size - klient odeˇsle ˇz´adost na server o vytvoˇren´ı nov´e m´ıstnosti/hry - server odpov´ı: – CREATE GAME FAIL - pokud m´ıstnost ze stejn´ ym jmˇenem uˇz existuje – CREATE GAME OK - pokud vytvoˇren´ı m´ıstnosti probˇehlo u ´spˇeˇsnˇe, hr´aˇc hostname je odebr´an z hlavn´ıho kan´alu a pˇrid´an do novˇe vytvoˇren´e m´ıstnosti • ROOM CRATED name size players size hostname - po vytvoˇren´ı nov´e m´ıstnosti je tato zpr´ava odesl´ana vˇsem hr´aˇc˚ um v hlavn´ım kan´ale • JOIN ROOM name - hr´aˇc odeˇsle ˇz´adost o pˇripojen´ı do m´ıstnosti - server odpov´ı: – JOIN ROOM FAIL - pokud poˇcet hr´aˇc˚ u v m´ıstnosti je roven maxim´aln´ımu poˇctu – JOIN ROOM OK - hr´aˇc je u ´spˇeˇsnˇe pˇripojen do m´ıstnosti • ROOM UPDATE name players size - po pˇripojen´ı nebo odpojen´ı hr´aˇce v m´ıstnosti se tato zpr´ava odeˇsle vˇsem hr´aˇc˚ um v hlavn´ım kan´ale • LEAVE ROOM - hr´aˇc opustil m´ıstnost a je pˇripojen na hlavn´ı kan´al • ROOM HOST LEFT - pokud hr´aˇc opustil m´ıstnost a z´aroveˇ n tuto m´ıstnost vytvoˇril, je tato zpr´ava odesl´ana vˇsem hr´aˇc˚ um v m´ıstnosti a jsou pˇripojeni na hlavn´ı kan´al • ROOM REMOVED name - pokud m´ıstnost opustil hˇr´aˇc, kter´ y ji vytvoˇril je m´ıstnost zruˇsena a tato zpr´ava je odesl´ana vˇsem hr´aˇc˚ um v hlavn´ım kan´ale • GAME START REQUEST map players name player 1 ... player n - klient ˇz´ad´a server o spuˇstˇen´ı hry - server odpov´ı: – GAME START map players name player 1 ... player n - je odesl´ana vˇsem hr´aˇc˚ um v m´ıstnosti
23
– GAME RUNNING name - je odesl´ana vˇsem hr´aˇc˚ um v hlavn´ım kan´ale • GAME END - pokud ve hˇre zb´ yv´a posledn´ı hr´aˇc s´am, je mu odesl´ana tato zpr´ava • CUBE NUMBER GET - klient ˇz´ad´a server o vygenerov´an´ı ˇc´ısla - server odpov´ı: – CUBE NUMBER number - je odesl´ana vˇsem hr´aˇc˚ um ve hˇre • CHANGE PLAYER - je odesl´ana vˇsem hr´aˇc˚ um v m´ıstnosti • DO MOVE x1 y1 x2 y2 - je odesl´ana vˇsem hr´aˇc˚ um v m´ıstnosti Seznam tˇechto zpr´av je implementov´an jako enum, kde kaˇzd´ y symbol ma svoj´ı vlastn´ı celoˇc´ıselnout hodnotu.
24
4.4.
Server UI
Modul ServerUI je v´ ystupn´ı ˇc´ast´ı serverov´e ˇc´asti aplikace. Je to konzolov´a aplikace vyuˇz´ıvaj´ı knihovny Server a Protocol. Pˇri spuˇstˇen´ı inicializuje a spust´ı SocketListener z modulu Server. Modul definuje logiku na serveru a implementuje obsluhu zpr´av z modulu Protocol. Diagram tˇr´ıd s jejich z´avislostmi je na obr´azku 9.. Logikou serveru se mysl´ı spr´ava hr´aˇc˚ u a m´ıstnost´ı na serveru. Po pˇrihl´aˇsen´ı klienta k serveru a po jeho u ´spˇeˇsn´e autorizaci se vytvoˇr´ı nov´a instance typu Player a uchov´a se v glob´aln´ı mnoˇzinˇe hr´aˇc˚ u, kter´a slouˇz´ı pro rychl´e hledan´ı hr´aˇc˚ u podle identifik´atoru jejich spojen´ı. Kaˇzd´ y hr´aˇc, kter´ y je pˇripojen na serveru mus´ı b´ yt v nˇejak´em kan´ale. Po pˇrihl´aˇsen´ı na server je hr´aˇc automaticky pˇripojen na hlavn´ı kan´al tzv. GlobalChannel. V tomto kan´ale hˇr´aˇc mimo jin´e m˚ uˇze vytv´aˇret a pˇripojovat se m´ıstnost´ı, coˇz jsou zase kan´aly. Po vytvoˇren´ı m´ıstnosti je m´ıstnost pˇrid´ana do mnoˇziny m´ıstnosti, kter´a umoˇzn ˇuje rychl´e vyhled´av´an´ı podle n´azvu. Hr´aˇc, kter´ y m´ıstnost zaloˇzil m˚ uˇze hru spustit. To znamen´a, ˇze aby tento modul umˇel zpracov´avat poˇzadavky klient˚ u, mus´ı si v pamˇeti nˇejak´ ym zp˚ usobem organizovat data o tom, jak´ y hr´aˇc je v jak´e m´ıstnosti, jak´e m´a pr´ava apod.
Player
PlayerPool
RequestHandler
Channel
GlobalChannel
Room
RoomPool
Obr´azek 9. Diagram tˇr´ıd a z´avislost´ı v modulu ServerUI
25
4.4.1.
Player
Tato tˇr´ıda pˇredstavuje jednoho hr´aˇce, kter´ y je pˇripojen´ y na serveru. V objektu si drˇz´ı nejenom informace jako jm´eno apod., ale i odkaz na objekt ClientHandler, kter´ y umoˇzn ˇuje odes´ılat zpr´avy po network streamu, kter´ y je asociov´an s t´ım to hr´aˇcem. Atributy • string nick - jm´eno hr´aˇce • ClientHandler client - objekt obsluhy klienta • Channel channel - odkaz na kan´al, ke kter´emu je hr´aˇc pˇripojen
4.4.2.
PlayerPool
´ Ukolem t´eto statick´e tˇr´ıdy je uchov´avat vˇsechny hr´aˇce na server v jedn´e kolekci, kter´a by umoˇzn ˇovala rychl´e hledan´ı hˇr´aˇc˚ u podle identifik´atoru jejich spojen´ı. Vˇzdy kdyˇz n´am pˇrijde zprava od urˇcit´eho klienta zn´ame identifik´ator tohoto spojen´ı a potˇrebujeme zn´at, o kter´eho hr´aˇce se jedn´a. Pro uchov´av´an´ı jsem vybral kolekci Dictionary, kter´a je vnitˇrnˇe implementov´ana jako hashovac´ı tabulka kde kl´ıˇc je identifik´ator spojen´ı a hodnota kl´ıˇce je objekt typu Player, coˇz umoˇzn ˇuje velice rychl´e vyhled´av´an´ı. Atributy • Dictionary players - hashovac´ı tabulka obsahuj´ı vˇsechny hr´aˇce na serveru Metody • bool AddPlayer(Player player) - pokus´ı se pˇridat hr´aˇce do kolekce, pokud v kolekci existuje hr´aˇc ze stejn´ ym jm´enem, funkce vr´ati false • void RemovePlayer(int ID) - vymaˇze z kolecke hr´aˇce s identifik´atorem spojen´ı pokud existuje • Player GetPlayerById(int ID) - podle identifik8toru spojen´ı vr´at´ı objekt hr´aˇce
26
4.4.3.
Channel
Tato tˇr´ıda pˇredstavuje abstraktn´ı kan´al na serveru. Definuje z´akladn´ı vlastnosti a funkˇcnost kan´alu a ostatn´ı tˇr´ıdy, kter´e tuto funkˇcnost vyˇzaduj´ı mus´ı tuto tˇr´ıdu dˇedit. Kan´al si m˚ uˇzeme pˇredstavit jako jak´ ysi kontejner v nˇemˇz je obsaˇzena urˇc´ıt´a mnoˇzina hr´aˇc˚ u, kteˇr´ı maj´ı nˇeco spoleˇcn´e. Hr´aˇci se navz´ajem vid´ı a m˚ uˇzou spolu komunikovat pomoc´ı chatu. Tato tˇr´ıda definuje ud´alosti pˇri pˇripojen´ı nov´eho hr´aˇce na kan´al, odpojen´ı nebo odesl´an´ı zpr´avy. Tyto 3 vlastnosti jsou spoleˇcn´e vˇsem typ˚ um kan´alu na serveru. Atributy • List players - seznam hr´aˇc˚ u v kan´ale Metody • virtual void AddPlayer(Player player) - obsluha pˇripojen´ı hr´aˇce do kan´alu. Vˇsem hr´aˇc˚ um v m´ıstnosti je odesl´ana zpr´ava PLAYER JOIN se jm´enem hr´aˇce, kter´ y se pˇripojil a jemu jsou pomoc´ı zpr´av typu PLAYER ADD odesl´ani vˇsichni hr´aˇci v kan´ale. Metoda je virtu´aln´ı pro pˇr´ıpad toho, ˇze nˇejak´a speci´aln´ı tˇr´ıda by potˇrebovala pozmˇenit implementaci. • virtual void RemovePlayer(Player player) - obsluha odpojen´ı hr´aˇce z kan´alu. Vˇsem hr´aˇc˚ um v kan´ale je odesl´ana zpr´ava PLAYER LEFT se jm´enem hr´aˇce, kter´ y se odpojil. Metoda je rovnˇeˇz virtu´aln´ı. • void SendMessage(Player player, string message) - nastane pokud hr´aˇc v kan´ale odeslal textovou zpr´avu. Zpr´ava je odesl´ana pomoc´ı zpr´avy MESSAGE s dan´ ymi parametry.
4.4.4.
Room
Tˇr´ıda Room pˇredstavuje m´ıstnost neboli hru na serveru. Tato tˇr´ıda dˇed´ı funkˇcnost od tˇr´ıdy Channel. Narozd´ıl od norm´aln´ıho kan´alu m´a kaˇzd´a m´ıstnost maxim´aln´ı poˇcet hr´aˇc˚ u, kter´e se do n´ı m˚ uˇzou pˇripojit. Tento poˇcet z´avis´ı na mapˇe, kterou zvolil hr´aˇc, kter´ y tuto m´ıstnost zaloˇzil. Ten m´a nav´ıc pr´avo hru odstartovat a t´ım uzamknout m´ıstnost ostatn´ım hraˇc˚ um, kteˇr´ı ji vid´ı v hlavn´ım kan´ale. M´ıstnost se vytv´aˇri pˇri poˇzadavku klienta zpr´avou CREATE ROOM a je zruˇsena pokud j´ı opust´ı posledn´ı hr´aˇc. M´ıstnost m´a dvˇe f´aze. Prvn´ı f´aze nast´av´a po vytvoˇren´ı m´ıstnosti na serveru a m´ıstnost je v tzv. pˇr´ıpravn´e f´azi, kdy se ˇcek´a na pˇripojen´ı ostatn´ıch klient˚ u. Jakmile hr´aˇc, kter´ y m´ıstnost vytvoˇril odstartuje hru, f´aze m´ıstnosti se zmˇen´ı na typ hra. Ve hˇre proti sobˇe hr´aˇci hraj´ı hru a m´ıstnost v t´eto f´azi mus´ı umˇet 27
zpracov´avat zpr´avy, kter´e souvis´ı s hrou. V t´eto f´az´ı tato m´ıstnost miz´ı z dohledu ostatn´ıch hr´aˇc˚ u v hlavn´ım kan´ale, kteˇr´ı by se do n´ı snad chtˇeli pˇripojit. Atributy • string name - n´azev m´ıstnosti • string map - mapa • string hostname - jm´eno hr´aˇce, kter´ y m´ıstnost vytvoˇril • int size - maxim´aln´ı poˇcet hr´aˇc˚ u v m´ıstnosti • bool isGame - pˇr´ıznak, urˇcuj´ıc´ı jestli je m´ıstnost ve f´azi hry Metody • override void AddPlayer(Player player) - k definici funkce v pˇredkovi pˇrid´av´a vlastn´ı implementaci, kter´a provede aktualizaci m´ıstnosti v hlavn´ım kan´ale • override void RemovePlayer(Player player) - k definici funkce v pˇredkovi pˇrid´av´a vlastn´ı implementaci, kter´a hr´aˇce, kter´ y se odpojil pˇripoj´ı do hlavn´ıho kan´alu. Pokud je m´ıstnost ve stavu pˇr´ıpravy a hru opustil hr´aˇc, kter´ y ji zaloˇzil, m´ıstnost je zruˇsena. Pokud je m´ıstnost ve stavu hry a hr´aˇc, kter´ y m´ıstnost opustil je pˇredposledn´ı, posledn´ımu hr´aˇci je odesl´ana zpr´ava GAME END. Pokud m´ıstnost opustil posledn´ı hr´aˇc, m´ıstnost je zruˇsena. • void GameStart(List<string> param) - pokud hr´aˇc poˇz´ad´a o start hry, je zmˇenˇena f´aze m´ıstnosti a vˇsem hr´aˇc˚ um je odesl´ana zpr´ava GAME START. • void PlayerThrowCube() - pot´e co je serverem vygenerov´an hod kostkou je n´aslednˇe rozesl´ana vˇsem hr´aˇc˚ um zpr´avu CUBE NUMBER. • void ChangePlayer() - vˇsem hr´aˇc˚ um v m´ıstnosti je rozesl´ana zpr´ava CHANGE PLAYER. • void DoMove(int fromX, int fromY, int toX, int toY) hr´aˇc˚ um je rozesl´ana zpr´ava DO MOVE.
28
-
vˇsem
4.4.5.
RoomPool
Podobnˇe jako PlayerPool tato tˇr´ıda slouˇz´ı k uchov´av´an´ı vˇsech m´ıstnost´ı na serveru v hashovac´ı tabulce, kde kl´ıˇc je n´azev m´ıstnosti a hodnota kl´ıˇce je objekt typu Room. Tˇr´ıda umoˇzn ˇuje rychl´e vyhledav´an´ı pˇrid´av´an´ı a odeb´ır´an´ı m´ıstnost´ı z kolekce. Atributy • Dictionary<string, Room> rooms - hashovac´ı tabulka Metody • bool AddRoom(Room room) - pokus´ı se pˇridat m´ıstnost do kolekce, pokud v kolekci existuje m´ıstnost ze stejn´ ym jm´enem, funkce vr´ati false • void RemoveRoom(Room room) - odebere m´ıstnost z kolekce pokud existuje • Room GetRoomByName(string name) - podle jm´ena vr´at´ı objekt typu Room
4.4.6.
GlobalChannel
GlobalChannel je zvl´aˇstn´ı typ kan´alu, do kter´eho jsou automaticky hr´aˇci um´ıstˇeni po prihl´aˇsen´ı a autorizaci na serveru. Zvl´aˇstn´ı je v tom, ˇze mimo funkc´ı, kter´e umoˇzn ˇuje obecn´a tˇr´ıda Channel, obsahuje seznam vˇsech m´ıstnost´ı na serveru, kter´e se nach´azej´ı v pˇr´ıpravn´em stavu. V tomto kan´ale hr´aˇci m˚ uˇzou vytv´aˇret nov´e m´ıstnosti nebo se pˇripojovat do jiˇz vytvoˇren´ ych m´ıstnost´ı. Jelikoˇz je hlavn´ı kan´al potˇreba vˇzdy jeden je tato tˇr´ıda implementov´ana jako singleton. Atributy • List rooms - seznam m´ıstnost´ı Metody • override void AddPlayer(Player player) - po pˇripojen´ı hr´aˇce na hlavn´ı kan´al jsou mu odesl´any vˇsechny vytvoˇren´e m´ıstnosti pomoc´ı zpr´avy ROOM CREATED. • void AddRoom(Room room) - pˇrid´a m´ıstnost do seznamu a odeˇsle vˇsem hr´aˇc˚ um v hlavn´ım kan´ale zpr´avu ROOM CREATED. • void RemoveRoom(Room room) - odebere m´ıstnosti ze seznamu a odeˇsle vˇsem hr´aˇc˚ um zpr´avu ROOM REMOVED.
29
• void UpdateRoom(Room room) - vˇsem hr´aˇc˚ um v hlavn´ım kan´ale odeˇsle zpr´avu ROOM UPDATE. • void GameStart(Room room) - vˇsem hr´aˇc˚ um v hlavn´ım kan´ale odeˇsle zpr´avu GAME RUNNING.
4.4.7.
RequestHandler
Tato tˇr´ıda dˇed´ı tˇr´ıdu AbstractRequestHandler z modulu Server. Jsou j´ı pˇrepos´ıl´any vˇsechny zpr´avy, kter´e server obdrˇzel od klient˚ u a je zodpovˇedn´a za jejich zpracov´an´ı a odesl´an´ı odpovˇedi. Tˇr´ıda implementuje jednu metodu, ve kter´e jsou jako parametry pˇred´any zpr´ava a ClientHandler spojen´ı, ze kter´eho zpr´ava pˇriˇsla. Metoda ze zpr´avy vyt´ahne k´od zpr´avy a jej´ı parametry. Podle typu zpr´avy zavol´a pˇr´ısuˇsnou obsluhu s dan´ ymi parametry. Obsluhy • void AuthorizationHandler(ClientHandler client, List<string> param) - provede autorizaci klienta • void CreateRoom(ClientHandler client, List<string> param) vytvoˇr´ı na serveru novou m´ıstnost
-
• void JoinRoom(ClientHandler client, List<string> param) pˇresune hr´aˇce z hlavn´ıho kan´alu do m´ıstnosti
-
• void LeaveRoom(ClientHandler client, List<string> param) pˇresune hr´aˇce z m´ıstnosti, ve kter´e se nach´az´ı zp´atky do hlavn´ıho kan´alu • void GameStartHandler(ClientHandler client, List<string> param) - v dan´e m´ıstnosti se odstartovala hra • void CubeNumberHandler(ClientHandler client) - vygeneruje hod kostkou • void ChangePlayerHandler(ClientHandler client) - v dan´e m´ıstnosti zmˇen´ı hr´aˇce na tahu • void DoMoveHandler(ClientHandler client, List<string> param) hr´aˇc vykonal tah v dan´e hˇre
30
4.5.
Game
Funkc´ı tohoto modulu je implementace logiky hry, v naˇsem pˇr´ıpadˇe deskov´e hry ˇclovˇeˇce, nezlob se. Jednou z ˇc´ast´ı tohoto modulu je zp˚ usob ukl´ad´an´ı, naˇc´ıt´an´ı a pr´ace z mapou, na kter´e se hra hraje. Dalˇs´ı ˇc´ast´ı je ukl´ad´an´ı figurek na hrac´ım poli, prov´adˇen´ı tah˚ u a kontrola pravidel hry. Neˇz se pust´ıme do zp˚ usobu implementace jednotliv´ ych tˇr´ıd tak bych jen struˇcne poznamenal pravidla t´eto hry. Hru m˚ uˇze hr´at proti sobˇe 2 aˇz 6 hr´aˇc˚ u mezi sebou, pˇriˇcemˇz kaˇzd´ y hr´aˇc m´a odliˇsnou barvu figurek. Hra se hraje na urˇcit´e mapˇe, kter´a obsahuje cestu s pol´ıˇcek, na kter´em se nach´az´ı pro kaˇzd´eho hr´aˇce domeˇcek sloˇzen´ y ze 4 pol´ıˇcek. Kaˇzd´ y hr´aˇc m´a k dispozici 4 figurky, kter´e jsou na zaˇc´atku hry um´ıstˇen´e na startovn´ı pozici. Hr´aˇci se mezi sebou stˇr´ıdaj´ı. Hr´aˇc, kter´ y je na tahu, h´az´ı kostkou. Pokud m´a hr´aˇc vˇsechny sv´e figurku na startovn´ı pozici m´a 3 hodky kostkou. V opaˇcn´em pˇr´ıpadˇe pouze 1. Pokud hr´aˇc hod´ı ˇc´ıslo 6 m´a jeˇstˇe jeden pokus. V pˇr´ıpadˇe, ˇze hr´aˇc hod´ı kostkou ˇc´ıslo 6, m˚ uˇze si jednu figurku nasadit. C´ılem hry je dostat vˇsechny figurky do domeˇcku dˇr´ıve neˇz soupeˇri. Modul tvoˇr´ı 8 jednotliv´ ych tˇr´ıd, jejichˇz diagram je zobrazen na obr´azku ˇc´ıslo 3. V n´asleduj´ıch sekc´ıch bych chtˇel rozebrat implementaci logiky hry od struktury ukl´ad´an´ı dat aˇz po pr´ac´ı s nimi.
Map
MapList
Board
Figure
StartRoom
Position
FigureImage
StartRoomPosition
Obr´azek 10. Diagram tˇr´ıd a z´avislost´ı v modulu Game
31
4.5.1.
Map
Jak jiˇz n´azev t´eto tˇr´ıdy napov´ıd´a, jedn´a se o tˇr´ıdu reprezentuj´ıc´ı mapu hry. Jednotliv´e mapy jsou uloˇzeny v b´ınarn´ım souboru urˇcit´e struktury, o kter´e bude reˇc posl´eze. Tˇr´ıda umoˇzn ˇuje pomoc´ı jedn´e ze sv´ ych metod naˇc´ıst data ze souboru a uloˇzit si je do sv´ ych promˇen´ ych tak, aby okol´ı mohla sdˇelit, na jak´e pozici ma dan´ y hr´aˇc startovn´ı pozici, domeˇcek nebo na jakou pozici se figurka dostane za pˇredpokladu, ˇze hr´aˇc hodil urˇcit´e ˇc´ıslo. Instance t´eto tˇr´ıdy tedy definuje strukturu jedn´e z map a logiku nech´av´a na nˇekom jin´em. Struktura souboru Mapa je uloˇzena byte po bytu v bin´arn´ım souboru, kter´ y je zkompilov´an jako zdroj s cel´ ym projektem. Tato volba neumoˇzn ˇuje dynamick´e pˇrid´av´an´ı dalˇs´ıch map za provozu aplikace a projekt by se musel pˇrekompilovat. Vytvoˇril sem urˇcit´e mnoˇzstv´ı map vˇsech moˇzn´ ych typ˚ u a nepˇredpokl´adal sem, ˇze v budoucnu by byla potˇreba pˇrid´avat mapy nov´e. Jak sem ˇr´ıkal informace jsou v souboru uloˇzeny jako pole byt˚ u. Pojd’me se pod´ıvat jak´e vˇsechny informace tam jsou uloˇzen´e: • velikost mapy - poˇcet pol´ıˇcek na ˇs´ıˇrku a na v´ yˇsku, celkem tedy 2B. • poˇ cet hr´ aˇ c˚ u - 1B. • startovn´ı pozice - startovn´ı pozici tvoˇr´ı 4 pol´ıˇcka pro kaˇzd´eho hr´aˇce, pˇr´ıˇcemˇz kaˇzd´e pol´ıˇcko m´a souˇradnici x a y, celkem tedy pocet hracu ∗ 4 ∗ 2 byt˚ u. • d´ elka cesty - poˇcet pol´ıˇcek od m´ısta, kde hr´aˇc nasad´ı figurku po posledn´ı pol´ıˇcko v domeˇcku. • cesty - pro kaˇzd´eho hr´aˇce je uloˇzena cesta od pol´ıˇcka, kde hr´aˇc nasazuje figurku aˇz po posledn´ı pol´ıˇcko v jeho domeˇcku. Cesta je uloˇzena jako seznam pol´ıˇcek s dan´ ymi souˇradnicemi. Kdyˇz uˇz v´ıme jak jsou data v souboru uloˇzen´a, nen´ı problem je z tama naˇc´ıst do pamˇeti. Ot´azka zn´ı jakou strukturu dat zvolit. Atributy • string name - n´azev mapy • int size - poˇcet hr´aˇc˚ u • int width - ˇs´ıˇrka • int height - v´ yˇska
32
• List[] startRoom - seznam pol´ıˇcek reprezentuj´ı startovn´ı pozici pro kaˇzd´eho hr´aˇce • List[] path - seznam pol´ıˇcek tvoˇr´ıc´ı cestu kaˇzd´eho hr´aˇce • byte[] data - data mapy Metody • void Load() - naˇcte strukturu mapy do atribut˚ u objektu z pole byt˚ u • Position getStartPosition(int player) - vr´at´ı pozici, kde hr´aˇc nahazuje vlastn´ı figurku • List getStartRoom(int player) - vr´at´ı seznam pol´ıˇcek tvoˇr´ıc´ı startovn´ı pozici pro dan´eho hr´aˇce • List getFinishPositions(int player) pol´ıˇcek tvoˇr´ıc´ı domeˇcek dan´eho hr´aˇce
-
vr´at´ı seznam
• Position getMovePosition(Figure figure, int value) - vr´at´ı pozici, na kterou by se figurka dostala, kdyby hr´aˇc hodil dan´e ˇc´ıslo • bool isFigureInStartRoom(Figure figure) - vr´at´ı true, pokud je figurka na startovn´ı pozici
4.5.2.
MapList
Tato statick´a tˇr´ıda obsahuje kolekci vˇsech vytvoˇren´ ych map. Slouˇz´ı jako datov´ y zdroj, kter´ y se pak snadno napoj´ı na ovl´adac´ı prvek ComboBox a umoˇzn´ı dynamick´e naˇc´ıt´an´ı map pˇri zmˇenˇe kolekce. Atributy • static List<Map> maps - seznam map Metody • static Map getMapByName(string name) - vrat´ı mapu na z´akladˇe jej´ıho jm´ena
33
4.5.3.
Figure
Instance t´eto tˇr´ıdy pˇredstavuj´ı jednotliv´e figurky rozm´ıstˇen´e na aktu´aln´ı mapˇe bˇehem hry. Kaˇzd´a figurka patˇr´ı nˇejak´emu hr´aˇci a m´a svoji pozici. Atributy • Position pos - pozice figurky • int player - identifik´ator hr´aˇce, kter´emu figurka patˇr´ı • FigurePosition mapPosition - logick´a pozice figurky na mapˇe
4.5.4.
FigureImage
Pomocn´a statick´a tˇr´ıda, kter´a pro danou figurku vrac´ı jej´ı bitmapu. U figurky zkoum´a, kter´emu patˇr´ı hr´aˇci, jestli je figurka oznˇcen´a a jak´a je jej´ı aktu´aln´ı pozice na mapˇe a podle toho vr´at´ı bitmapu. V´ ysledn´a bitmapa m´a pr˚ uhlednou b´ılou barvu. Vˇsechny obr´azky figurek jsou zakompilov´any v projektu jako zdroje. Metody • static Bitmap getFigureImage(Figure figure) - vr´at´ı pro danou figurku jej´ı bitmapu
4.5.5.
Position, StartRoomPosition
Pomocn´e tˇr´ıdy reprezentuj´ı pol´ıˇcko na hern´ı mapˇe a pol´ıˇcko na startovn´ı pozici hr´aˇce. Tˇr´ıda Position definuje pouze atributy X a Y pˇredstavuj´ıc´ı souˇradnice na mapˇe. Tˇr´ıda StartRoomPosition dˇed´ı tyto vlatnosti a nav´ıc pˇrid´av´a atribut, kter´ y ud´av´a jestli je pozice obsazen´a figurkou hr´aˇce.
4.5.6.
StartRoom
Jednotliv´e instance t´eto tˇr´ıdy reprezentuj´ı seznam pol´ıˇcek, na kter´ ych jsou ze zaˇc´atku hry um´ıstˇeny hr´aˇcovi figurky. Tˇr´ıda poskytuje dvˇe pomocn´e metody pro snadnou manipulaci s figurkami. Atributy • List<StartRoomPosition> positions - seznam startovn´ıch pozic Metody 34
• StartRoomPosition getEmptyPosition() - pomocn´a metody vracej´ıc´ı prvn´ı volnou pozici v seznamu • void moveToHome(Figure figure) - um´ıst´ı figurku na prvn´ı volnou pozici • void moveFromHome(Figure figure) - um´ıst´ı figurku pryˇc ze startovn´ı pozice
4.5.7.
Board
Hlavn´ı tˇr´ıda reprezentuj´ıc´ı desku hry umoˇzn ˇuj´ıc´ı manipulaci s figurkami a kontrolu pravidel. Obsahuje kolekci jednotliv´ ych figurek a umoˇzn ˇuje hr´aˇci s nimi t´ahnout, pˇriˇcemˇz kontrolue platnost tahu a ostatn´ı pravidla napˇr. vyhazov´an´ı figurek, konec hry, nasazov´an´ı atd. Atributy • Map map - aktu´aln´ı mapa • int playersCount - aktu´aln´ı poˇcet hr´aˇc˚ u • List<StartRoom> startRooms - seznam startovn´ıch pozic pro kaˇzd´eho hr´aˇce • List