• Nu S-Au Găsit Rezultate

Planes – Jocul copilăriei într-un format modern

N/A
N/A
Protected

Academic year: 2022

Share "Planes – Jocul copilăriei într-un format modern"

Copied!
53
0
0

Text complet

(1)

UNIVERSITATEA „ALEXANDRU IOAN CUZA” IAŞI FACULTATEA DE INFORMATICĂ

LUCRARE DE LICENŢĂ

Planes – Jocul copilăriei într-un format modern

propusă de

Ionuț Mihai Burlacu

Sesiunea: februarie, 2017

Coordonator ştiinţific

Asist. Dr. Vasile Alaiba

(2)

1

UNIVERSITATEA „ALEXANDRU IOAN CUZA” IAŞI FACULTATEA DE INFORMATICĂ

Planes - Jocul copilăriei într-un format modern

Ionuț Mihai Burlacu

Sesiunea: februarie, 2017

Coordonator ştiinţific

Asist. Dr. Vasile Alaiba

(3)

2

D ECLARAŢIE PRIVIND ORIGINALITATE ŞI RESPECTAREA DREPTURILOR DE AUTOR

Prin prezenta declar că Lucrarea de licenţă cu titlul „Planes – Jocul copilăriei într-un format modern” este scrisă de mine şi nu a mai fost prezentată niciodată la o altă facultate sau instituţie de învăţământ superior din ţară sau străinătate. De asemenea, declar că toate sursele utilizate, inclusiv cele preluate de pe Internet, sunt indicate în lucrare, cu respectarea regulilor de evitare a plagiatului:

- toate fragmentele de text reproduse exact, chiar şi în traducere proprie din altă limbă, sunt scrise între ghilimele şi deţin referinţa precisă a sursei;

- reformularea în cuvinte proprii a textelor scrise de către alţi autori deţine referinţa precisă;

- codul sursă, imaginile etc. preluate din proiecte open-source sau alte surse sunt utilizate cu respectarea drepturilor de autor şi deţin referinţe precise;

- rezumarea ideilor altor autori precizează referinţa precisă la textul original.

Iaşi, data

Absolvent Ionuț Mihai Burlacu

___________________________

(4)

3

D ECLARAŢIE DE CONSIMŢĂMÂNT

Prin prezenta declar că sunt de acord ca Lucrarea de licență cu titlul „Planes - Jocul copilăriei într-un format modern”, codul sursă al programelor şi celelalte conţinuturi (grafice, multimedia, date de test etc.) care însoţesc această lucrare să fie utilizate în cadrul Facultăţii de Informatică.

De asemenea, sunt de acord ca Facultatea de Informatică de la Universitatea „Alexandru Ioan Cuza” Iași să utilizeze, modifice, reproducă şi să distribuie în scopuri necomerciale programele-calculator, format executabil şi sursă, realizate de mine în cadrul prezentei lucrări de licenţă.

Iaşi, data

Absolvent Ionuț Mihai Burlacu

_________________________

(5)

4

Cuprins

1. Introducere ... 6

1.1 Motivație ... 6

1.2 Context ... 6

1.3 Cerințe funcționale ... 7

1.4 Abordare tehnică ... 8

1.5 Contribuții ... 9

2. Dezvoltarea aplicației server ... 10

2.1 Sails.js ... 10

2.2 Socket.io ... 12

2.3 Configurarea proiectului ... 13

2.4 Cele 3 servere ... 16

2.5 MongoDB ... 17

2.6 isAuthorized ... 19

2.7 Multiple logins ... 20

2.8 Lucrul cu imagini ... 22

2.9 Push Notification ... 23

2.10 Email Service ... 24

2.11 Main Service ... 25

3. Dezvoltarea aplicației client ... 27

3.1 AngularJS 2 ... 27

3.2 Ionic 2 ... 28

3.3 localStorage ... 30

3.4 ApiService ... 31

3.5 DataService ... 33

3.6 Push Notification ... 34

3.7 Globals ... 35

(6)

5

3.8 LoadingService ... 36

3.9 Electron ... 37

4. Descrierea aplicației ... 39

4.1 Login ... 39

4.2 Register ... 40

4.3 Forgot Password ... 41

4.4 Home ... 42

4.5 Side Menu ... 43

4.6 Game Invitations ... 44

4.7 Profile ... 45

4.8 Update Info / Change Password ... 46

4.9 Friend List / Search Players ... 47

4.10 Friend Requests / Match History ... 48

4.11 Game Setup ... 49

4.12 Game ... 50

4.13 End Game ... 51

5. Bibliografie ... 52

(7)

6

1. Introducere 1.1 Motivație

Majoritatea dintre noi cred că am jucat în pauzele de la liceu sau chiar de la facultate jocul Avioane, însă cel mai mare impediment era faptul că la fiecare joc trebuiau trasate pe foi cele două tabele necesare jocului, lucru care făcea ca jocul să se termine de obicei după o singură rundă. Am ales să realizez o aplicație atât mobilă cât și desktop cu ajutorul căreia să se poată juca Avioane fără a avea nevoie de hârtie și pix, fiind nevoie doar de conexiune la internet și de un dispozitiv mobil sau de un desktop/laptop.

1.2 Context

Jocurile de strategie jucate în două persoane au apărut de foarte mult timp, primul joc de acest gen fiind ”X și 0” care se presupune că își are originile în antichitate.

Jocul Avioane este o variantă alternativă a jocului Battleships1 care datează din timpul primului război mondial. Avioane este un joc de strategie ce se joacă în două persoane. Pentru a juca acest joc, fiecare jucător are nevoie de o foaie și un instrument de scris. La începutul

jocului, fiecare persoană trebuie să își amplaseze cele 3 avioane pe un chenar de 10x10 căsuțe.

După ce jocul a început, fiecare jucător trebuie să ghicească pe rând câte o căsuță în care se află unul din avioanele inamicului. Jocul se termină atunci când unul din jucători a descoperit toate cele 3 avioane ale jucătorului. Putem vedea mai jos un exemplu de chenar aparținând unuia dintre jucători:

1 https://en.wikipedia.org/wiki/Battleship_(game)

(8)

7

Figura 1 – Chenar 10x10 cu cele 3 avioane poziționate2

1.3 Cerințe funcționale

 Posibilitatea de a juca jocul Avioane contra unei alte persoane aleasă de către server care dorește să joace în același timp cu tine sau cu o persoană la alegere din lista de prieteni;

 Crearea de cont, fie direct din aplicație, fie prin intermediul rețelei de socializare Facebook. Contul este necesar pentru a putea avea o listă de prieteni și pentru a putea vedea istoricul meciurilor jucate. Jocul se va putea juca și fără

autentificare, însă utilizatorul nu va avea acces la lista de prieteni sau la istoricul meciurilor, acesta numindu-se în joc “guest” (Oaspete);

 Accesul la lista de prieteni, aceștia fiind adăugați în urma căutării după nume;

 Accesul la istoricul meciurilor jucate unde se va putea vedea între cine și cine s-a jucat meciul, cine a fost câștigătorul și în urmă cu cât timp s-a jucat fiecare meci;

 În cazul în care unul dintre jucători părăsește jocul din greșeală sau din cauza lipsei conexiunii la internet, aplicația va permite acestuia să revină în joc timp de 30 de secunde, în caz contrar celălalt jucător va fi ales învingător.

 Invitarea altor persoane la joc se va putea realiza și pentru jucătorii care se află în aplicație în acel moment, dar și pentru cei care nu se află în aplicație prin intermediul notificărilor push.

2 https://ro.wikipedia.org/wiki/Avioane_(joc)

(9)

8

 Accesul la profilul utilizatorului unde acesta își va putea schimba imaginea de profil, username-ul sau parola contului.

1.4 Abordare tehnică

Pentru partea de server (back-end) am folosit NodeJS împreună cu bibliotecile Sails.js și Socket.IO. Baza de date este una de tip NoSQL realizată cu ajutorul arhitecturii MongoDB.

Comunicarea între aplicație și server se va realiza prin intermediul websocket-urilor în timpul jocului propriu-zis și prin intermediul serviciilor web REST pentru restul funcționalităților.

Aplicația va fi de două feluri, pentru desktop și pentru mobil. Pentru aplicația de mobil voi folosi biblioteca Ionic, iar pentru aplicația de desktop voi folosi biblioteca Electron. Datorită tehnologiilor folosite aplicația de mobil va putea fi rulată pe Android, iOS și Windows Phone, iar aplicația de desktop va putea fi rulată pe Windows, OS X și Ubuntu. Eu voi alege să prezint aplicația doar pe Android în cazul celei pentru mobil și doar pe Windows în cazul celei pentru desktop. Avantajul folosirii acestor tehnologii este faptul că aplicația este scrisă o singură dată, iar apoi compilată pentru platforma dorită.

JavaScript este un limbaj de programare orientat obiect bazat pe conceptul

prototipurilor care a apărut în anul 1995 și care a fost dezvoltat inițial de către Brendan Eich.

Acest limbaj a fost inițial conceput pentru a putea adăuga funcționalități paginilor web care până la acea vreme erau statice, însă mai târziu a început să fie utilizat și pentru crearea serverelor web.

NodeJS este un mediu de programare bazat pe limbajul JavaScript, multiplatformă, cu sursă deschisă, cu ajutorul căruia se pot realiza servere web. NodeJS oferă o arhitectură bazată pe evenimente capabilă să realizeze transfer de date asincron. Aceasta permite realizarea de aplicații web scalabile care conțin multe operații de intrare/ieșire, dar și realizarea de aplicații care rulează în timp real (”real-time”) cum ar fi o aplicație de chat sau un joc.

(10)

9

1.5 Contribuții

Prezenta lucrare este împărțită în trei capitole: Dezvoltarea aplicației server, Dezvoltarea aplicației client și Descrierea aplicației.

Capitolele Dezvoltarea aplicației server și Dezvoltarea aplicației client prezintă în primul rând tehnologiile folosite în dezvoltarea acestora, dar și unele funcționalități importante ce merită a fi menționate. Ambele capitole au atașate secțiuni de cod relevante din ambele aplicații, client și server.

Capitolul Descrierea aplicației prezintă succint fiecare ecran din aplicația client împreună cu o scurtă descriere a funcționalităților acestora. Și aici avem atașate capturi de ecran realizate pe un dispozitiv mobil ce rulează aplicația client.

(11)

10

2. Dezvoltarea aplicației server 2.1 Sails.js

Sails.js este o bibliotecă cu sursă deschisă de tip MVC (Model-View-Controller) scrisă în totalitate în limbajul JavaScript. Aceasta este una dintre cele mai populare biblioteci bazate pe Node.js. Sails.js poate fi folosit pentru realizarea de aplicații de tip enterprise, dar mai cu seamă pentru aplicații de tip chat sau jocuri multiplayer.

Figura 2: Structura de tip MVC3

Structura unui proiect bazat pe biblioteca Sails.js este una de tip modulară pentru a permite o dezvoltare cât mai ușoară a aplicației. Împărțirea este realizată după funcționalitatea pe care o are fiecare fișier în proiect.

Din structura proiectului merită menționate următoarele dosare/fișiere importante:

Dosarul /api conține toată logica aplicației server și are în componență următoarele dosare:

Dosarul /controllers conține toate clasele care manipulează datele definite în modele cu ajutorul serviciilor;

Dosarul /models conține clasele ce ajută la reprezentarea datelor din baza de date;

Dosarul /policies conține politicile ce sunt aplicate în momentul realizării unei cereri către server, dar înainte ca aceasta să ajungă în controller (ex. Verificarea că un utilizator este autentificat atunci când face o cerere);

 Dosarul /services conține clasele care realizează interogări în baza de date cu ajutorul modelelor și apoi returnează răspunsul înapoi către controller.

Dosarul /config are în componență toate fișierele de configurare necesare funcționării serverului cum ar fi: datele de conectare la baza de date, regulile de rutare, constante folosite în clase, etc.;

3 https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller

(12)

11

Dosarul /node_modules conține toate librăriile externe ce au fost instalate în prealabil utilizând comanda npm install nume_librărie ;

Fișierul app.js reprezintă nucleul serverului, el fiind cel care înglobează toate fișierele descrise mai sus într-un singur proces;

Fisierul package.json este un fișier ce conține toate datele necesare instalării aplicației pe o mașină ce rulează Node.js.

Figura 3: Structura aplicației server Interacțiunea cu serverul se poate defini în câțiva pași:

1. Utilizatorul realizează o cerere către server printr-o anumită adresă definită în fișierul routes.js;

2. Cererea trece mai întâi prin politicile definite pentru această rută dacă este cazul, iar apoi ajunge în controller;

3. Controllerul apelează un serviciu pentru o interogare în baza de date;

4. Serviciul realizează interogarea cu ajutorul modelului definit pentru respectivul tabel din baza de date și returnează un răspuns către controllerul care l-a apelat;

5. Controllerul preia răspunsul returnat de către serviciu și îl trimite înapoi către utilizator.

(13)

12

2.2 Socket.io

Socket.io este o bibliotecă cu sursă deschisă care permite comunicarea bidirecțională în timp real între server și clienți. Comunicarea este bazată pe evenimente, iar protocolul de comunicare este WebSocket.

Această bibliotecă este alcătuită din două părți: o librărie pentru partea de client ce poate fi fie un browser, fie o aplicație pentru mobil și o librărie pentru partea de server.

Un scenariu simplu de interacțiune între client și server poate fi definit astfel:

1. Clientul apeleaza funcția connect() ce primește ca parametru un URL și un port definit de către server;

2. Serverul acceptă cererea de conectare trimisă de către client, iar apoi ascultă pe mai multe evenimente ce ar putea fi emise de către client;

3. Se realizează schimbul de date între client și server;

4. Clientul se deconectează de la server apelând funcția disconnect() sau prin distrugerea obiectul în care era păstrată conexiunea.

Am ales să folosesc această bibliotecă pentru a asigura o experiență de joc cât mai plăcută, toată acțiunea jocului petrecându-se în timp real.

Figura 4: Secțiune din codul unui server ce acceptă conexiuni de timp WebSocket

(14)

13

2.3 Configurarea proiectului

În urma creării proiectului cu ajutorul comenzii sails new Planes-Backend au fost

generate în mod implicit fișierele de configurare pentru a putea rula aplicația server fără a face nici o modificare în ele.

Pentru a asigura comunicarea dintre client și server, avem nevoie mai întâi să definim un set de reguli de rutare pentru a asigna fiecărei metode din controller un URL prin care poate fi apelată. Acest lucru se realizează în fișierul routes.js din dosarul /config. Mai jos avem

prezentată o secțiune din acest fișier de configurare:

Figura 5: Secțiune din fișierul /config/routes.js

Apoi, pentru setarea portului pe care va rula aplicația server, dar și pentru configurarea modulului SSL am modificat în fișierul local.js din dosarul /config proprietatea port pentru a defini portul și proprietatea ssl pentru configurarea modulului SSL.

Figura 6: Secțiune din fișierul /config/local.js

Cheia ca vine din engleză de la Certificate Authority și reprezintă un lanț de încredere ce asigură browser-ul sau aplicația că certificatul folosit în cheia cert este unul valid. Acest lanț este format din mai multe certificate ce sunt oferite de către companiile ce vând certificate SSL.

Cheia key reprezintă cheia de criptare cu care a fost realizat certificatul, iar cheia cert reprezintă certificatul în sine. Acest modul asigură un protocol de comunicare sigur între client și server, evitând astfel atacuri de timp man-in-the-middle de exemplu. Serviciile web sunt apelate folosind protocolul https în loc de http cum este definit în mod implicit.

(15)

14

Pentru comunicarea cu baza de date a trebuit să configurez în fișierul connections.js din dosarul /config o conexiune oferind adresa, portul și credențialele serverului de tip MongoDB.

Inițial, la instalarea serverului de baze de date nu este configurat un utilizator cu care să se facă conexiunea. Am configurat manual un utilizator pentru a nu lăsa serverul pe oricine să acceseze datele din el.

Figura 7: Exemplu conexiune cu serverul MongoDB

Unele rute ce au fost definite în routes.js trebuie să poată fi accesate doar de către utilizatorii ce s-au autentificat înainte. Acest lucru se poate face după ce am creat o politică ce verifică dacă utilizatorul este autentificat, iar rutele ce folosesc această politică se configurează în fișierul policies.js din dosarul /config astfel:

Figura 8: Definirea rutelor ce folosesc politici

Cheia * reprezintă faptul că toate metodele din acel controller vor folosi politica isAuthorized, iar la MainController doar cele două metode vor folosi aceeași politică. În cazul controllerului AuthController este setat valoarea true deoarece în acest caz nu avem nevoie de nici o politică în metodele din acel controller.

(16)

15

Ultima setare care merită menționată este făcută în fișierul cron.js din dosarul /config.

În acest fișier se definesc procesele de tip cron4 ce rulează în mod independent de serverul propriu-zis la o anumită perioadă de timp definită în prealabil.

Figura 9: Exemplu configurare cron

Acest cron din exemplu rulează la fiecare cincisprezece secunde și are rolul de a verifica utilizatorii care sunt online în aplicație în acel moment. Logica din spate este următoarea:

1. După ce un utilizator s-a autentificat în aplicație, aceste anunță serverul la fiecare trei secunde printr-o cerere faptul că el este online;

2. În momentul în care serverul a primit o astfel de cerere, el marchează pentru acest utilizator timestamp5-ul curent;

3. Atunci când cron-ul rulează, el va verifica pentru fiecare utilizator din lista celor online diferența dintre timestamp-ul curent și cel al utilizatorului;

4. Dacă diferența este mai mare de zece secunde, înseamnă că acel utilizator nu a mai anunțat serverul la fiecare trei secunde că este online, deci va fi marcat ca fiind offline.

Constantele folosite în aplicația server sunt definite tot în dosarul /config într-un fișier creat de mine care este numit const.js. Folosirea constantelor asigură faptul că atunci când dorim să schimbăm o valoare, această va fi schimbată automat în tot proiectul. Ele pot fi accesate în proiect folosind variabila globală sails.config.const.NUME_CONST.

Figura 10: Exemplu definire constante

4 https://en.wikipedia.org/wiki/Cron

5 https://en.wikipedia.org/wiki/Timestamp

(17)

16

2.4 Cele trei servere

În urma unei analize realizată înainte de începerea lucrului la aplicația server am ajuns la concluzia că folosirea protocolului WebSocket pentru întreaga comunicare dintre client și server nu este o soluție viabilă. Folosirea acestui protocol presupune menținerea unei conexiuni permanente, iar acest lucru face ca nivelul de date consumate să crească, iar durata de viață a bateriei dispozitivului mobil să scadă.

Așadar, am decis să împart aplicația server în trei părți:

1. Primul server este realizat pe bază de servicii web REST6 și se ocupă de toate funcționalitățile aplicației mobile, înafară de ecranele Game Setup și Game, și funcționalitatea Search For Opponent;

2. Al doilea server este realizat cu ajutorul bibliotecii Socket.io și se ocupă de toată logica din spatele jocului propriu-zis. Conexiunea cu acest server începe în momentul când utilizatorul a ajuns în ecranul Game Setup și se termină atunci când revine în ecranul acasă.

3. Al treilea server este realizat tot cu ajutorul bibliotecii Socket.io și conține doar logica din spatele funcționalității Search For Opponent. Conexiunea cu acest server începe în momentul când utilizatorul apasă pe butonul Search for Opponent și se termină atunci când acesta și-a găsit un adversar sau când a renunțat la căutare.

În acest fel, împărțirea aceasta asigură comunicare în timp real între clienți și server doar acolo unde este într-adevăr nevoie, iar bateria dispozitivelor mobile este păstrată în cel mai bun mod posibil.

Mai merită menționat faptul că cele trei servere rulează pe trei porturi diferite însă comunicarea cu serverul de baze de date MongoDB se realizează printr-o singură conexiune.

6 https://spring.io/understanding/REST

(18)

17

2.5 MondoDB

Pentru a păstra datele utilizatorilor avem nevoie de o bază de date. Unul dintre avantajele bibliotecii Sails.js este ușurința și flexibilitatea cu care poate comunica cu multe tipuri de baze de date cum ar fi: Postgres, Oracle, MySQL, MongoDB, etc.

MongoDB este un program de baza de date cu sursă deschisă, multiplatformă orientat pe obiecte. MongoDB este clasificat ca fiind NoSQL și folosește documente de tip JSON pentru stocarea datelor. Acesta a fost lansat în anul 2009 de către compania MongoDB Inc. MongoDB prezintă o soluție din ce în ce mai căutată pentru aplicațiile care au nevoie de acces rapid asupra datelor.

Eu am ales să folosesc MongoDB datorită vitezei cu care poate extrage datele și a scalabilității. Datele sunt stocate sub formă de documente, în format JSON, împăcându-se foarte bine cu Node.js.

Pentru a defini un document în MongoDB avem nevoie de un model declarat în aplicația server. Un model este ca o schemă pentru acel document deoarece în el se declară coloanele din acel document și tipul acestora. Tipul datelor poate fi: string, integer, date, etc.

Figura 11: Exemplu Model GameInvitation

(19)

18

În figura 11 mai putem vedea declarate și două relații de tip one-to-many7 între modelul User și modelul GameInvitation. Aceste relații ușurează extragerea datelor din baza de date și sporește viteza de lucru.

Manipularea datelor se realizează în totalitate cu ajutorul ORM8-ului numit Waterline.

Waterline este o colecție de API-uri ce face ca lucrul cu datele din baza de date să fie unul foarte curat și ușor de înțeles, indiferent de tipul de baze de date folosit.

Figura 12: Exemplu de interogare ce returnează invitațiile la joc acceptate trimise de către utilizator

În figura 11, metoda find() este echivalentul lui SELECT din limbajul SQL, metoda populate() este echivalentul lui JOIN din limbajul SQL, iar method exec() este cea care execută interogarea și returnează rezultatul într-o funcție de tip callback9 în parametrul result.

Pentru vizualizarea mai ușoară și mai rapidă a datelor în faza de dezvoltare am folosit un utilitar gratuit numit Robomongo10. Acesta permite reprezentarea vizuală a datelor, dar și execuția de interogări în același mod ca și în terminal.

7 https://en.wikipedia.org/wiki/One-to-many_(data_model)

8 https://en.wikipedia.org/wiki/Object-relational_mapping

9 https://en.wikipedia.org/wiki/Callback_(computer_programming)

10 https://robomongo.org/

(20)

19

2.6 isAuthorized

isAuthorized este o politică creată pentru a limita utilizatorii care nu sunt autentificați în aplicație să folosească anumite servicii web. Logica din spate poate fi descrisă astfel:

1. Clientul trece prin procesul de autentificare și primește un token de la server ca și răspuns dacă autentificarea se face cu succes;

2. Atunci când clientul face o cerere către server, acesta atașează în header11 o cheie numită authorization-token ce va conține token-ul primit la autentificare;

3. Token-ul este mai întâi validat de către server, iar dacă validarea se face cu succes, cererea merge mai departe către controller-ul corespunzător. În caz contrar, serverul returnează un răspuns prin care anunță clientul de faptul că token-ul trimis de el este invalid. Același lucru se întâmplă și în cazul în care token-ul lipsește din header.

Figura 13: Secțiune de cod din generarea token-ului

Generarea token-ului pe server se realizează cu ajutorul librăriei numită jsonwebtoken12. Această librărie conține trei funcții principale: sign(), verify() și decode().

Funcția sign() este exemplificată în figura 13 și este folosită pentru generarea de token.

Primul parametru reprezintă datele ce vor fi criptate, iar al doilea parametru reprezintă o cheie unică cu ajutorul căreia se criptează datele din primul parametru. Metoda de criptare folosită se numește HS25613.

Funcția verify() este folosită pentru a verifica dacă un token trimis de către client este unul valid și poate fi decriptat mai departe. Aceasta primește ca parametrii token-ul și cheia de criptare folosită în funcția sign().

Funcția decode() este folosită, după cum sugerează și numele acesteia, pentru

decriptarea token-ului trimis de către client. Parametrii acestei funcții sunt aceiași cu cei folosiți în funcția verify().

11 https://en.wikipedia.org/wiki/List_of_HTTP_header_fields

12 https://github.com/auth0/node-jsonwebtoken

13 https://en.wikipedia.org/wiki/JSON_Web_Token

(21)

20

Figura 14: Secțiune de cod din procesul de decriptare a unui token

După fiecare proces de decriptare se verifică dacă id-ul conținut de acel token este unul din baza de date și dacă aparține unui utilizator. În cazul în care acesta este găsit în baza de date, trimite cererea mai departe către controller cu ajutorul metodei next(). Dacă id-ul nu există în baza de date, se trimite un răspuns clientului prin care se anunță acest lucru.

În realitate, acest token nu ar putea fi niciodată invalid dacă este trimis de cineva care folosește aplicația. El ar putea fi invalid doar dacă cineva ar face reverse engineering14 la aplicație și ar descoperi că trebuie să trimită acest token pentru a avea acces la aceste servicii web.

2.7 Multiple logins

Serverul tratează într-un mod elegant cazul în care un utilizator dorește să se autentifice în aplicație, dar acesta deja este autentificat pe un alt dispozitiv în acel moment. Dacă acest lucru nu ar fi tratat, atunci ar putea apărea conflicte la nivel de server și eventual crash-uri.

La fiecare proces de autentificare sau la fiecare revenire în aplicație fiind deja

autentificat, se salvează în baza de date pentru acel utilizator token-ul folosit mai departe în cereri. În acest mod, un utilizator poate avea doar un singur token valid într-un moment oarecare.

Putem distinge două cazuri de autentificare multiplă:

14 https://en.wikipedia.org/wiki/Reverse_engineering

(22)

21

1. Utilizatorul dorește să se autentifice. Dacă acesta este deja autentificat pe un alt dispozitiv și este într-un meci, va primi o alertă pe noul dispozitiv ce îl va anunța că nu se poate autentifica din această cauză. Dacă deja este autentificat, dar nu este într-un meci, atunci acesta va trece de procesul de autentificare, noul token generat îl va suprascrie pe cel vechi, iar celălalt dispozitiv va fi anunțat în câteva secunde că sesiunea i-a expirat și va fi trimis către ecranul de autentificare.

2. Utilizatorul este deja autentificat dintr-o sesiune anterioară și intră în aplicație.

Serverul va face o verificare în baza de date după ce token-ul a fost decriptat pentru a verifica dacă acest token se potrivește cu cel din baza de date. Dacă se potrivește atunci el va fi trimis către ecranul acasă, altfel va fi anunțat ca

sesiunea i-a expirat și va fi trimis către ecranul de autentificare.

Figura 15: Secțiune de cod din funcția care verifică token-ul în cazul în care utilizatorul este deja autentificat

(23)

22

2.8 Lucrul cu imagini

Aplicația permite utilizatorilor care și-au creat cont în aplicație să își seteze o imagine de profil după propria lor dorință. Fișierul poate proveni din orice sursă, atât timp cât el este încărcat prin aplicație.

Pentru prelucrarea imaginilor am folosit librăria numită gm ce are la bază o altă librărie numită GraphicsMagick15.

GraphicsMagick este o colecție de funcții folosite pentru procesarea de imagini. Ea poate fi instalată și folosită pe majoritatea sistemelor de operare. Printre numeroasele funcții se numără și convertirea de fișiere, redimensionarea, rotirea, modificarea culorilor chiar și compararea a doua imagini.

Fișierele trimise de către utilizatori sunt redimensionate la o dimensiune fixată de 500 pe 500 de pixeli, sunt decupate în formă de pătrat pentru a se putea afișa bine în aplicația mobilă și apoi sunt salvate pe disc într-o locație aflată în exteriorul dosarului unde se afla codul serverului. Acest lucru previne ștergerea accidentală a imaginilor atunci când se trag modificări ale codului de pe GitHub. Fișierele care nu mai sunt folosite sunt șterse pentru a nu ocupa spațiu inutil pe disc.

Pentru a putea servi imaginile din server în aplicație am creat un server în Apache16 care redirecționează cererile făcute pe portul 443 către dosarul cu imagini de pe disc.

Figura 16: Secțiune de cod din procesul de salvare a imaginilor pe disc

15 http://www.graphicsmagick.org/

16 https://httpd.apache.org/

(24)

23

2.9 Push Notification

Firebase Cloud Messaging (FCM) este o soluție gratuită multiplatformă care permite trimiterea de notificări push către dispozitivele mobile și care este oferită de către Google.

Am ales să folosesc acest serviciu pentru a anunța utilizatorii de anumite evenimente petrecute în aplicație chiar și când aceștia au aplicația închisă. Un exemplu de eveniment este primirea unei invitații la joc de la un alt utilizator al aplicației.

În primul rând, pentru folosirea serviciului FCM a fost nevoie să creez o aplicație pe site- ul celor de la Google Firebase, iar de acolo să iau un cheie numită Server key care este folosită mai departe pentru a trimite notificări către dispozitive.

În al doilea rând, pentru folosirea serviciului FCM în aplicația server a mai fost nevoie să instalez biblioteca node-gcm ce permite trimiterea de notificări dintr-un server scris în Node.js.

Un utilizator poate avea într-un moment oarecare maxim un token pentru notificări push salvat în baza de date. Acest lucru face ca utilizatorul să primească notificări push doar pe dispozitivul pe care este autentificat în acel moment.

Figura 17: Secțiune de cod din procesul de trimitere a notificărilor push

(25)

24

2.10 Email service

Pentru trimiterea de email-uri către utilizatori am folosit librăria numită nodemailer ce permite trimiterea de email-uri cu conținut text, dar și cu conținut de tip HTML.

Protocolul prin care se trimit aceste email-uri este SMTPS care reprezintă o metodă de securizare a protocolului STMP. Protocolul SMTP este un standard al internetului ce permite transmiterea de email-uri. Acesta a fost definit prima dată în anul 1982 și a fost actualizat ultima dată în anul 2008. Portul folosit de protocolul SMTPS este 587.

În inițializarea librăriei nodemailer avem nevoie de o adresă de email validă ce va fi folosită pe post de server pentru trimiterea de email-uri. În cazul meu am folosit adresa personală de Gmail. Utilizatorii primesc email-uri doar după primul pas din procesul de înregistrare sau după primul pas din procesul de resetare parolă.

Figura 18: Secțiune de cod din procesul de trimite a unui email

(26)

25

2.11 Main service

Pentru a realiza funcționalitatea jocului propriu-zis a trebuit să stochez undeva datele despre meciurile aflate în progres, dar nu am putut să le stochez în baza de date. Motivul este acela că baza de date ar fi să facă foarte multe scrieri și citiri pentru fiecare meci în parte ceea ce ar îngreuna timpul de răspuns, respectiv experiența de joc. Așa că am ales să creez un serviciu numit MainService ce păstrează aceste date direct pe server.

Structura acestui serviciu este următoarea:

Figura 19: Secțiune de cod din clasa MainService

În figura 19 putem observa următoarele chei: onlinePlayers și onlinePlayersTimestamps care reprezintă doi vectori în care sunt stocate id-urile utilizatorilor online, respectiv ultimul timestamp din momentul când aceștia au anunțat serverul că sunt online. Mai putem observa și cheile inGamePlayers unde sunt stocate id-urile utilizatorilor care se află într-un meci și cheia waitingOpponent care este folosită pentru funcționalitatea Search for Opponent. Cea mai importantă cheie această clasă este gameRooms unde sunt păstrate toate informațiile despre meciurile aflate în derulare în orice moment pe server.

O cameră de joc se creează în momentul în care un utilizator trimite o invitație de joc unui alt utilizator sau în momentul când doi utilizatori sunt introduși în meci în urma apăsării butonului Search for Opponent.

O cameră de joc este distrusă în momentul când meciul s-a terminat, iar ambii utilizatori au refuzat remiza sau atunci când ambii utilizatori părăsesc jocul, fie intenționat, fie din cauza lipsei de conexiune la internet.

(27)

26

Figura 20: Secțiune de cod din procesul de creare a unei camere de joc

Fiecărei camere de joc îi este atribuit un id unic generat cu ajutorul librăriei uuid care asigură că la fiecare generare returnează un șir de caractere unic.

În cazul în care în timpul meciului unul dintre jucători este deconectat de la server din cauza lipsei conexiunii la internet, starea meciului se păstrează intactă timp de treizeci de secunde, timp în care jucătorul deconectat se poate reconecta în meci și poate continua de unde a rămas în momentul deconectării. Toate informațiile necesare pentru reconectarea în meci sunt păstrate în aceste obiecte numite camere de joc.

Dacă jucătorul deconectat nu s-a reconectat în cele treizeci de secunde acordate, el este declarat pierzător, iar oponentul său este declarat învingător.

(28)

27

3. Dezvoltarea aplicației client 3.1 AngularJS 2

AngularJS 2 este urmașul binecunoscutului AngularJS 1, bibliotecă ce permite realizarea părții de front-end a aplicațiilor web.Acesta a fost lansat în anul 2010 de către compania Google, iar principala lui caracteristică este posibilitatea de a crea aplicații web pe o singură pagină (“single-page”).

AngularJS 2 nu a fost lansat încă, acesta fiind în beta17. Schimbările față de predecesorul său sunt în primul rând în materie de performanță, dar și de organizare a codului. Limbajul în care este scris AngularJS 2 este TypeScript.

TypeScript este un limbaj realizat de către cei de la GitHub ce permite scrierea de clase, interfețe, etc. ca apoi acestea să fie compilate și transformate în cod JavaScript.

Figura 21: Secțiune de cod scrisă în TypeScript

17 https://en.wikipedia.org/wiki/Software_release_life_cycle#Beta

(29)

28

3.2 Ionic 2

Ionic 2 este o bibliotecă cu sursă deschisă ce permite realizarea de aplicații mobile folosind limbaje web. Acesta este succesorul bibliotecii Ionic 1 ce a fost lansată în anul 2013 de către compania Drifty.

Ionic 2 este scrisă cu ajutorul bibliotecii AngularJS 2 și se află în același stagiu ca și AngularJS2, adică în beta. Pentru a putea compila aplicații pentru dispozitive mobile, Ionic 2 se folosește de librăria Cordova. Cordova permite folosirea de funcționalități native ale

dispozitivelor în cod scris în JavaScript/TypeScript, cum ar fi: camera foto, geolocation, vibration, etc.

Structura unui proiect realizat cu ajutorul lui Ionic 2 este una de tip modulară.

Împărțirea este realizată în funcție de ecran, fiecare ecran având dosarul său.

Din structura proiectului merită menționate următoarele dosare/fișiere importante:

Dosarul node_modules conține librăriile instalate cu ajutorul comenzii npm install;

Dosarul platforms conține build-urile aferente fiecărei platforme: android, ios, windows phone;

Dosarul plugins conține plugin-urile ce fac legătura între funcționalitățile native ale dispozitivului și AngularJS 2;

Dosarul resources conține resursele folosite în aplicație: pictogramele și ecranele splash

Dosarul res reprezintă nucleul aplicației, aici aflându-se toată logica aplicației;

Dosarul app conține componenta principală ce rulează la deschiderea aplicației;

Dosarul pages conține toate ecranele prezente în aplicație, fiecare ecran având în dosarul său un fișier HTML, unul SCSS și unul TypeScript;

Fișierul index.html ce reprezintă primul fișier executat la deschiderea aplicației, acesta dând startul celorlalte component din aplicație.

Dosarul www conține același cod cu dosarul res doar că în acest caz toate fișierele de tip JavaScript sunt comprimate într-un singur fișier, la fel și pentru fișierele de tip SCSS.

Conținutul acestui dosar este folosit în momentul în care se creează un build pentru o anumită platformă;

Fișierul package.json este un fișier ce conține toate datele necesare instalării aplicației pe o mașină ce rulează Node.js.

(30)

29

Figura 22: Structura unui proiect realizat cu ajutorul bibliotecii Ionic 2

În faza de dezvoltare, aplicația se poate testa în browser rulând comanda ionic serve.

Această comandă va deschide un nou tab cu aplicația simulând astfel un dispozitiv mobil.

Pentru rularea proiectului pe un dispozitiv cu sistemul de operare Android, se rulează comanda ionic run android --prod , iar pentru realizarea unui build se rulează comanda ionic build android --release --prod . După rulare, build-ul creat se va afla în locația

/platforms/android/build/outputs/apk/android-release-unsigned.apk.

Principalul avantaj al bibliotecii Ionic 2 este acela că aplicația în sine este scrisă o singură dată, iar apoi este compilată pentru orice sistem de operare mobil. Singurele intervenții

necesare sunt în cadrul design-ului unde elementele din ecran pot arăta diferit pe două sisteme de operare diferite.

(31)

30

3.3 localStorage

localStorage este un obiect disponibil în orice browser care suportă limbajul HTML în versiunea cinci și este folosit în stocarea permanentă a datelor.

Acest obiect seamănă ca și funcționalitate cu obiectul care păstrează sesiunile în browser, diferența fiind lipsa unei date de expirare a datelor din obiect în cazul localStorage.

Datele din localStorage pot fi șterse în două moduri: prin apelarea directă a funcției localStorage.removeItem(”nume_cheie”) sau atunci când aplicația este dezinstalată din dispozitivul mobil.

Manipularea datelor din localStorage se realizează ușor cu ajutorul metodelor setItem() și getItem().

Metoda setItem() primește doi parametrii, primul fiind numele cheii unde se stochează datele, iar al doilea parametru reprezintă datele se urmează a fi stocate, sub formă de șir de caractere. Pentru a transforma datele într-un șir de caractere se folosește metoda

JSON.stringify() înainte de apelararea metodei setItem().

Metoda getItem() primește doar un parametru, acesta fiind numele cheii unde sunt stocate datele și returnează datele sub formă de șir de caractere. Pentru a transforma acest șir de caractere înapoi în obiectul inițial, se apelează metoda JSON.parse().

În aplicația client am folosit localStorage pentru a memora anumite date ce fac ca utilizarea aplicației de către utilizator să fie cât mai plăcută.

Un exemplu este memorarea token-ului primit după autentificare pentru a permite autentificarea automată a utilizatorului la următoarele accesări ale aplicației. Putem spune că acest lucru funcționează pe același principiu ca și cookie18-urile din browser. În momentul în care utilizatorul deschide aplicația, se verifică dacă acest token se află în localStorage în cheia authorization-token, iar dacă acesta se află atunci el este trimis către server pentru a verifica dacă acesta este valid. În același timp, serverul verifică și dacă de la ultima accesare a aplicației acest utilizator s-a mai autentificat și pe alt dispozitiv. În acest caz el va fi anunțat că sesiunea a expirat și va trimis către ecranul de autentificare. Dacă serverul spune că token-ul trimis este valid, atunci utilizatorul este trimis direct către ecranul acasă.

Un alt exemplu este salvarea id-ului camerei de joc la începutul fiecărui meci și

ștergerea acestuia la sfârșitul fiecărui meci. În acest mod, în momentul când utilizatorul intră în aplicație se verifică dacă acesta are un meci care se află în desfășurare, iar dacă se află atunci acesta este anunțat că se poate reconecta pentru a continua meciul de unde a rămas în momentul deconectării de la server sau în momentul când acesta a pierdut conexiunea la internet.

18 https://en.wikipedia.org/wiki/HTTP_cookie

(32)

31

Figura 23: Secțiune de cod din metoda utilizată pentru a verifica validitatea token-ului din localStorage

3.4 ApiService

Pentru ca aplicația să poată funcționa are nevoie să comunice cu aplicația server, iar pentru a comunica cu aceasta avem nevoie de modulul Http deja inclus în codul de bază al bibliotecii AngularJS 2.

Cu scopul de a face codul mai ușor de citit, dar și pentru a menține o anumită ordine în cod am ales să creez un serviciu numit ApiService care se ocupă cu trimiterea de cereri către server de tip și manipularea răspunsurilor primite de la acesta.

Primele două metode prezente în acest serviciu sunt folosite pentru a adăuga în header valorile necesare înainte de a face cererea către server.

Prima dintre ele adaugă în header cheia Authorization-Token pentru serviciile web ce necesită ca utilizatorul să fie autentificat, iar valoarea pentru această cheie este preluată din obiectul global localStorage.

Cea de-a doua metodă adaugă în header cheia Content-Type și valoarea

application/json, aceasta fiind folosită doar în cazul cererilor de tip POST pentru a anunța aplicația server că datele trimise de către aplicație sunt în format JSON.

(33)

32

Figura 24: Secțiune de cod cu funcțiile ce modifică header-ul

În continuare am declarat alte două metode ce se ocupă de trimiterea de cereri către server de tip GET, respectiv POST. Aceste două metode au functionalități foarte asemănătoare, iar din această cauză voi descrie doar pe una dintre ele, adică metoda post().

În metoda post(), în apelul funcției subscribe(), metodă ce așteaptă de la server un răspuns, avem dați ca parametrii două funcții de tip callback, prima dintre ele executându-se în momentul în care aplicația server a returnat un răspuns, iar cea de-a doua executându-se atunci când serverul nu răspunde sau când dispozitivul mobil nu dispune de conexiune la internet.

Figura 25: Secțiune de cod din metoda post()

Dacă aplicația a primit un răspuns de la server, mai întâi se verifică dacă codul primit corespunde cu cel pentru evenimentul de sesiune expirată, în acest caz utilizatorul fiind trimis către ecranul de autentificare. Dacă sesiunea nu a expirat, atunci se apelează funcția callback

(34)

33

trimisă ca parametru prin care se returnează datele în controller sau serviciu. Parametrul secure se trimite cu valoarea true dacă acel serviciu necesită trimiterea în header a parametrului Authorization-Token, iar cu valoarea false în caz contrar.

În cazul în care aplicația nu a primit un răspuns de la server atunci se afișează pe ecran un mesaj care îl anunță pe utilizator că acesta s-a deconectat și în același moment se apelează o funcție care verifică la fiecare trei secunde dacă aplicația s-a reconectat cu aplicația server.

3.5 DataService

Unele date din aplicație trebuie să poată fi accesibile din toate controllerele și serviciile acesteia cum ar fi datele despre utilizator după autentificare sau datele despre meciul aflat în desfășurare. Pentru acest lucru am creat un nou serviciu numit DataService.

DataService este o clasă de tip Injectable19 ce se instanțiază o singură dată la

deschiderea aplicației și păstrează în interiorul ei anumite date la fel ca și obiectul localStorage, doar că datele din DataService se vor pierde la închiderea aplicației. Acest serviciu este injectat în clasa AppModule pentru a avea o singură instanță a acestuia pe toată durata de execuție a aplicației.

Un exemplu de date stocate în acest serviciu sunt datele despre utilizator ce sunt afișate în meniul lateral sau în pagina de profil a utilizatorului. Obiectul userData este inițializat ca fiind un obiect gol în funcția constructor a clasei DataService și îi sunt atribuite date în momentul când aplicația primește răspuns de la server în urma unei autentificări realizate cu succes sau în momentul când aplicația este deschisă și se verifică dacă token-ul stocat în localStorage este valid. În aceeași variabilă userData se mai reține și variabila booleană isGuest care va avea valoarea true dacă utilizatorul este autentificat în aplicație ca fiind Oaspete și valoarea false în caz contrar.

Un al doilea exemplu în acest serviciu sunt datele despre meciul aflat în desfășurare ce sunt stocate în variabila gameData. Motivul principal pentru folosirea acestei variabile este nevoia de a avea acces la datele despre meci din ecranul Game Setup în ecranul Game.

19 https://angular.io/docs/ts/latest/api/core/index/Injectable-decorator.html

(35)

34

Figura 26: Secțiune de cod cu clasa DataService

3.6 Push Notification

Pentru a putea primi notificări push trimise de către aplicația server am instalat plugin-ul cordova-plugin-fcm. Acest plugin este unul cu sursă deschisă și permite acceptarea și afișarea notificărilor native ale dispozitivului mobil cu ajutorul bibliotecii AngularJS 2.

Configurarea acestuia este foarte rapidă, fiind nevoie de un apel al metodei getToken() și de un apel al metodei onNotification().

Metoda getToken() generează un token ce va fi folosit de către serviciul FCM20 pentru a trimite notificări push către acest dispozitiv mobil. Răspunsul returnat de către această metodă este apoi trimis către server pentru a fi salvat în baza de date.

Figura 27: Secțiune de cod cu metoda getToken()

20 https://firebase.google.com/docs/cloud-messaging/

(36)

35

Metoda onNotification() este apelată la intrarea în aplicație și are rolul de a asculta orice notificare push ce va fi trimisă de către server. În datele trimise prin intermediul notificării va fi prezentă tot timpul o cheie ce va fi folosită de către aplicația client să trimită utilizatorul către ecranul corespunzător în urma apăsării pe notificare.

Figura 28: Secțiune de cod cu metoda onNotification()

3.7 Globals

La fel ca în aplicația server, și în aplicația client am creat un obiect numit GlobalVariable ce are rolul de a reține variabile constante folosite în toată aplicația. Acest lucru ușurează foarte mult munca în cazul în care trebuie schimbată una dintre valori nefiind nevoie să schimbi în tot proiectul unde e folosită.

Printre datele stocate ca și constante în acest obiect merită menționate următoarele:

 Adresele împreună cu port-urile către cele trei servere ale aplicației server, cât și adresa de unde pot fi accesate imaginile de profil ale utilizatorilor. Folosirea constantelor în acest caz ajută în momentul în care se lucrează cu două sau mai multe mașini pe care este instalată aplicație server, de exemplu putem avea o mașină folosită în faza de dezvoltare și o mașină în faza de producție;

(37)

36

 Căile către fiecare serviciu web oferit de către aplicația server. La fel ca și în cazul precedent acest lucru ajută în cazul în care una dintre căi este schimbată dintr- un oarecare motiv;

 Codurile primite ca și răspuns de la server în urma unei cereri efectuate către acesta. Exemplu: 500 pentru sesiune expirată, 400 pentru eroare, etc.

Figura 29: Secțiune de cod cu obiectul GlobalVariable

3.8 LoadingService

LoadingService este un serviciu ce se ocupă de toate ecranele de încărcare din aplicație.

Nevoia de a avea un astfel de serviciu a venit după ce mi-am dat seama că unele ecrane de încărcare trebuie să poată apărea peste orice ecran din aplicație, însă acestea nu ar putea fi afișate deoarece nu se află în domeniul de aplicare al controllerului ce conține ecranul afișat în acel moment. LoadingService rezolvă acest lucru prin utilizarea unui vector de obiecte de tip Observable21 care ascultă anumite evenimente din aplicație, iar la primirea unui eveniment execută o anumită funcție declarată în ecranul de încărcare dorit. Clasa LoadingService se inițializează o singură dată la pornirea aplicației.

Un astfel de ecran de încărcare este cel ce apare atunci când utilizatorul a trimis o invitație la joc către alt utilizator, iar acel utilizator a acceptat invitația. În acel moment, pe întreg ecranul va aparea peste ecranul curent un nou ecran care îl va anunța ca oponentul său a acceptat invitația, iar după o secundă acesta este introdus în meci, adică în ecranul Game Setup.

21 https://angular-2-training-book.rangle.io/handout/observables/using_observables.html

(38)

37

Figura 30: Secțiune de cod din clasa LoadingService

Un alt ecran de acest fel este afișat în momentul când aplicația server a picat sau când utilizatorul nu mai dispune de conexiune la internet. Dacă după orice cerere făcută către server, clasa ApiService primește răspuns pe funcția callback de eroare, atunci peste ecranul curent va apărea un ecran de încărcare prin care utilizatorul va fi anunțat că a pierdut conexiunea cu serverul și că se încearcă recuperarea acesteia.

Figura 31: Exemplu de afișare a ecranului de încărcare în cazul pierderii conexiunii

3.9 Electron

Electron este o bibliotecă cu sursă deschisă dezvoltată de compania GitHub și permite realizarea de aplicații native pentru sistemele de operare Windows, MacOS și Ubuntu cu ajutorul limbajelor web.

Structura unui proiect realizat cu Electron este asemănătoare cu cea din Ionic 2, iar realizarea aplicației Planes pentru sistemul de operare Windows a fost foarte ușoară, fiind necesari doar trei pași simpli:

1. Crearea unui proiect gol în care am creat fișierul main.js ce conține minimul necesar pentru crearea unei aplicații utilizând Electron și fișierul package.json în care au fost declarate dependențele necesare pentru instalare;

2. Rularea comenzii npm install pentru instalarea tuturor librăriile necesare ce au fost declarate în package.json;

(39)

38

3. Copierea conținutului din dosarul /www al proiectului Ionic 2 în același dosar cu fișierele menționate mai sus, main.js și package.json.

(40)

39

4. Descrierea aplicației 4.1 Login

Primul ecran cu care utilizatorul este întâmpinat atunci când intră în aplicație pentru prima dată este cel de autentificare.

Accesul propriu-zis în aplicație se face numai după ce utilizatorul trece de acest ecran și primește de la server o așa zisă identitate pentru a putea fi identificat mai târziu în ecranele următoare.

Autentificarea se poate face în două moduri: cu ajutorul unui cont ce a fost creat în prealabil din ecranul de înregistrare sau cu un cont de pe rețeaua de socializare Facebook. Dacă utilizatorul nu dorește să se autentifice, dar totuși dorește să folosească aplicația, acesta poate să continue ca și Oaspete apăsând butonul Continue as Guest. În cazul acesta îi va fi

restricționat accesul la principalele funcționalități ale aplicației, acesta putând doar să joace contra unor utilizatori aleși aleatoriu de către server.

Figura 32: Ecranul de autentificare

(41)

40

4.2 Register

Ecranul de înregistrare este cel ce permite utilizatorului să își creeze un cont pentru a putea folosi toate funcționalitățile oferite de aplicație. Cele 4 câmpuri necesare înregistrării sunt: Email, Username, Parolă și Confirmare Parolă. Email-ul va fi utilizat ulterior în procesul de autentificare, iar username-ul va fi folosit pentru a putea fi identificat în aplicație și în timpul jocului.

Modalitatea de verificare a identității unui utilizator în momentul înregistrării este obligativitatea confirmării contului cu ajutorul unui cod aleatoriu de patru cifre primit pe adresa de email introdusă în câmpul Email. În acest fel putem fi siguri că acea adresă de email este în realitate deținută de către acest utilizator.

În cazul în care un utilizator introduce o adresă de email pe care nu o poate confirma, contul creat va rămâne inactiv până în momentul în care adevăratul posesor al adresei de email își va crea un cont. Acesta din urmă va suprascrie informațiile introduse de primul utilizator în formularul de înregistrare.

Figura 33: Ecranul de înregistrare Figura 34: Ecranul de confirmare cont

(42)

41

4.3 Forgot Password

Ecranul de resetare parolă oferă utilizatorului posibilitatea de a-și reseta parola contului în cazul în care acesta nu și-o mai amintește. Pentru a face acest lucru, acesta trebuie să

introducă adresa de email cu ajutorul căreia și-a creat contul în trecut.

Dacă adresa de email introdusă de către utilizator există în baza de date se va genera un cod aleatoriu de patru cifre pentru a confirma că el este posesorul adresei de email înainte de a-i oferi posibilitatea de a-și reseta parola. Acest lucru împiedică ca o persoană rău intenționată să poată să schimbe parola contului unui alt utilizator dacă acesta cunoaște adresa de email a acestuia.

Figura 35: Ecranul de resetare parolă Figura 36: Ecranul de confirmare a identității

(43)

42

4.4 Home

Ecranul Acasă reprezintă ecranul cu care este întâmpinat utilizatorul după ce acesta s-a autentificat sau după ce acesta a deschis aplicația, el fiind autentificat în trecut.

Aici putem observa o listă cu ultimele patru meciuri ale utilizatorului în ordine

descrescătoare a datei în care a fost jucate. În partea în care culoarea textului este verde este reprezentat câștigătorul meciului, iar în partea opusă, cu roșu, este reprezentat pierzătorul.

Această listă nu va conține nici un meci la prima accesare după înregistrare și se va umple pe măsură ce utilizatorul va juca.

În partea de sus avem butonul Search for Opponent care odată apăsat introduce

utilizatorul într-o coadă de așteptare până când un alt utilizator va apăsa și el pe acest buton. În cazul în care în momentul apăsării un alt jucător este în așteptare, cei doi vor fi introduși în meci automat.

Mai putem observa în colțul din dreapta sus un buton ce duce utilizatorul pe ecranul cu invitațiile la joc. Numărul aflat în cerculețul roșu reprezintă numărul de invitații active primite până în acel moment. Acest număr este actualizat în mod automat la fiecare 3 secunde.

Figura 37: Ecranul Acasă

(44)

43

4.5 Side Menu

Meniul principal este de două tipuri. Utilizatorului autentificat ca fiind Oaspete i se oferă doar două butoane: Home și Logout ce permite deconectarea din aplicație. Utilizatorului

autentificat în mod clasic i se oferă toate butoanele aferente ecranelor din aplicație.

Figura 38: Meniul lateral

(45)

44

4.6 Game Invitations

Ecranul cu invitații la joc conține toate invitațiile active primite de către utilizator.

Fiecare invitație menționează cât timp a trecut de la primirea acesteia, iar utilizatorul poate lua două decizii asupra acestora.

Dacă utilizatorul decide să refuze invitația la joc, celălalt utilizator este anunțat prin notificare push în privința acestui lucru.

Dacă utilizatorul decide să accepte invitația la joc, acesta este trimis în ecranul de start joc, iar celălalt utilizator este trimis și el în mod automat către același ecran. În cazul în care în urma acceptării unei invitații serverul anunță că cel care a trimis invitația este în alt meci sau este ieșit din aplicație, utilizatorul este anunțat de acest lucru, iar invitația este ștearsă.

Figura 39: Ecranul cu invitații la joc

(46)

45

4.7 Profile

Ecranul de profil reprezintă ecranul în care sunt afișate informații despre utilizator. În partea de sus avem imaginea de profil împreună cu un buton ce permite schimbarea acesteia cu una aleasă de către utilizator. La înregistrare, contului i se atribuie în mod implicit o imagine stocată în prealabil pe server.

Sub secțiunea cu informațiile despre utilizator mai avem două butoane, Update Info și Change Password ce permit acestuia să iși schimbe informațiile despre cont, respectiv să își schimbe parola contului.

Figura 40: Ecranul de profil

(47)

46

4.8 Update Info / Change Password

Ecranul Update Info permite utilizatorului să își modifice username-ul ales la înregistrare cu un altul preferat de el. Nu există restricție ca acesta să fie unic, însă există restricție ca acesta să conțină maximum douăsprezece caractere.

Ecranul Change Password permite utilizatorului să își schimbe parola curentă a contului.

Aici avem o primă validare care constă în introducerea parolei vechi pentru a proteja contul de persoane rău intenționate. Apoi mai avem o restricție ca parola să conțină minimum opt caractere pentru a spori securitatea contului.

Figura 41: Ecranul Update Info Figura 42: Ecranul Change Password

(48)

47

4.9 Friend List / Search Players

Ecranul Friend List conține lista de prieteni ai utilizatorului. Pentru fiecare utilizator avem în stânga o bulina ce poate avea una din următoarele culori: gri dacă acel prieten este deconectat, verde dacă acel prieten este conectat, iar albastru dacă se află într-un meci. În partea dreaptă avem două butoane, primul permite invitarea acelui prieten la joc, iar al doilea permite ștergerea acelui prieten din listă. În cazul în care un prieten este invitat la joc, acel utilizator va primi o notificare push prin care este anunțat că a primit o invitație. Din acel moment utilizatorul care a trimis invitația trebuie doar să aștepte ca celălalt să accepte

invitația. Dacă un prieten este șters din lista, automat și acest utilizator este șters din lista lui de prieteni.

Dacă utilizatorul apasă pe buton + din colțul dreapta sus, acesta este trimis într-un alt ecran numit Search Players unde poate adăuga noi persoane la lista de prieteni. Căutarea se face dupa username, iar ca rezultat sunt afișați primii 15 utilizatori în ordinea relevanței căutării. Rezultatele căutarii sunt extrase din tabelul cu toți utilizatorii, exceptând pe cei care sunt deja prieteni cu utilizatorul care face căutarea.

Figura 43: Ecranul Friend List Figura 44: Ecranul Search Players

(49)

48

4.10 Friend Requests / Match History

Ecranul Friend Requests conține toate cererile de prietenie primite de către utilizator care nu au fost acceptate sau refuzate încă. Dacă utilizatorul dorește să accepte sau să refuze o cerere de prietenie, atunci celălalt utilizator este anunțat prin notificare push în privința deciziei acestuia.

Ecranul Match History istoricul ultimelor 20 de meciuri ale utilizatorului. Meciurile cu fundal verde reprezintă victoria, iar cele cu fundal roșu reprezintă înfrângerea.

Figura 45: Ecranul Friend Requests Figura 46: Ecranul Match History

(50)

49

4.11 Game Setup

Ecranul Game Setup reprezintă primul ecran din jocul propriu-zis. În acest ecran fiecare dintre cei doi jucători trebuie să își amplaseze strategic cele trei avioane pentru a fi cât mai greu de ghicit de către adversar. După ce toate avioanele au fost amplasate, apăsarea butonului Ready îl trimite pe utilizator în ecranul Game. Dacă adversarul nu și-a amplasat încă avioanele utilizatorul trebuie să aștepte până acesta termină de amplasat, iar dacă adversarul era deja gata atunci jocul poate începe.

Figura 47: Ecranul Game Setup

(51)

50

4.12 Game

Ecranul Game reprezintă ecranul pe care utilizatorul ajunge după ce a apăsat butonul Ready pe ecranul precedent, Game Setup.

În acest moment fiecare dintre cei doi jucători vor încerca pe rând să ghicească câte o celula din avionul adversarului. Dacă unul dintre jucători nimerește capul unuia dintre avioanele adversarului (marcate cu albastru) atunci acel avion este complet distrus.

Jocul se termină în momentul în care unul dintre jucători are toate cele trei avioane complet distruse de către adversar. Câștigător este considerat jucătorul care nu are toate avioanele distruse, iar pierzător celălalt jucător.

Figura 48: Ecranul Game, runda Figura 49: Ecranul Game, runda adversarului jucătorului curent

(52)

51

4.13 End Game

Ecranul End Game apare atunci când un joc s-a terminat, iar cei doi jucători au fost declarați învingător, respectiv pierzător. Dacă oricare dintre cei doi dorește remiza, atunci acesta îl poate invita pe adversar să joace din nou între ei. În momentul în care adversarul acceptă, cei doi sunt trimiși înapoi în ecranul Game Setup pentru a-și amplasa din nou avioanele pentru un nou joc.

Figura 50: Ecran End Game, Figura 51: Ecran End Game, jucător victorios jucător înfrânt

(53)

52

5. Bibliografie

1.

JavaScript

Bhangal, Sham; Jankowski, Tomasz (2003). Foundation Web Design:

Essential HTML, JavaScript, CSS, PhotoShop, Fireworks, and Flash.

2. Node.js

https://en.wikipedia.org/wiki/Node.js

3. Sails.js

http://sailsjs.org/

4. Socket.io

https://en.wikipedia.org/wiki/Socket.IO

5. MongoDB

Banker, Kyle (March 28, 2011), MongoDB in Action (1st ed.) 6. AngularJS

https://en.wikipedia.org/wiki/AngularJS

7. Ionic

https://github.com/driftyco/ionic

8. Electron

https://github.com/electron/electron

9. Jocul X și O

http://familist.ro/jocul-x-si-0-pe-hartie-cel-mai-vechi-joc-din-istorie

10. Avioane (Joc)

https://ro.wikipedia.org/wiki/Avioane_(joc)

Referințe

DOCUMENTE SIMILARE

între salariaţii unei firme – ca un tip distinct de consumatori – şi angajaţii firmelor cu care se află într-un.

Dacă acesta este un nume de funcţie în linia de comandă vor apărea informaţiile de care avem nevoie despre funcţia căutată, dar acestea nu vor conţine

În mod semnificativ, în stabilirea faptului dacă cineva este sau nu autentic ca „om în carne şi oase”, Unamuno foloseşte un criteriu pragmatist: operari sequitur

Referitor la problema (II) se va arăta cum se face adunarea şi înmulţirea numerelor naturale reprezentate într-o bază u. În particular, dacă u=10, se regăsesc

În legătură cu închiderea trebuie, însă, reţinut un lucru important: dacă se închide un fişier, fără salvare, modificările făcute (asupra copiei din memorie) se

28 Desigur, un anumit gap (fie la nivelul cantităŃii, fie al calităŃii, al structurii sau al ritmului), poate fi acceptat doar dacă el se află în interiorul unei marje

dernităţii, căci postmodernitatea nu este decât o modernitate care se ia atât de în serios qua modernitate încât are impresia că se joacă, nici din

Nu, totul s- a întâmplat acum, în momentul în care viaţa ta se pregãteşte în linişte pentru a se împlini în viaţa de femeie, când trebuie sã iei cu tine mai departe tot ce