Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pjv:laboratoare:2024:a03 [2024/11/04 00:15]
andrei.lapusteanu Added C# forms & backend info
pjv:laboratoare:2024:a03 [2024/11/04 12:42] (current)
andrei.lapusteanu Updated note for back4app and parse
Line 87: Line 87:
 Aceste sistem foloseste corutine intrucat cererile HTTP sunt in general asincrone - trebuie sa asteptam un raspuns, care poate dura si cateva secunde, fara a afecta/opri rularea aplicatiei Unity/a jocului. Aceste sistem foloseste corutine intrucat cererile HTTP sunt in general asincrone - trebuie sa asteptam un raspuns, care poate dura si cateva secunde, fara a afecta/opri rularea aplicatiei Unity/a jocului.
  
-Mai jos gasiti un exemplu, preluat din pagina de documentatie oficiala [[https://​docs.unity3d.com/​2022.3/​Documentation/​ScriptReference/​Networking.UnityWebRequest.Get.html]],​ care prezinta cum se trateaza un request simplu de preluare de informatii (GET).+Mai jos gasiti un exemplu, preluat din pagina de documentatie oficiala [[https://​docs.unity3d.com/​2022.3/​Documentation/​ScriptReference/​Networking.UnityWebRequest.Get.html]],​ care prezinta cum se trateaza un request simplu de preluare de informatii (GET). ​
  
 <code c#> <code c#>
Line 289: Line 289:
 === C# === === C# ===
  
-In .NET Framework > 4.5 (ar trebui sa fie default ​in versiune ​2022) putem folosi si direct sistemul de client HTTP din C#+In .NET Framework > 4.5 (ar trebui sa fie disponibil implicit ​in versiunea ​2022) putem folosi si direct sistemul de client HTTP din C#
 [[https://​docs.unity3d.com/​2022.3/​Documentation/​Manual/​dotnetProfileSupport.html]]. [[https://​docs.unity3d.com/​2022.3/​Documentation/​Manual/​dotnetProfileSupport.html]].
  
 +<code c#>
 +{
 +        var httpClient = new HttpClient();​
  
 +        HttpClient.BaseAddress = new Uri(Global.BaseUrl);​
 +        HttpClient.DefaultRequestHeaders.Clear();​
 +        HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("​application/​json"​));​ // if you're using json service
 +
 +        // make request
 +        var response = Global.HttpClient.PostAsJsonAsync(subPath.TrimLeadingSlash(),​ payload).Result;​
 +
 +        // check for error
 +        response.EnsureSuccessStatusCode();​
 +
 +        // return result
 +        return response.Content;​
 +    }
 +</​code>​
 +
 +Totusi, dupa cum se poate observa, trebuie gestionate cumva evenimentele de asteptare/​asincrone. Acest lucru se poate face in Unity tot prin rularea acesteia prin corutine si asteptarea raspunsului,​ sau, intr-un mod mai eficient si mai avansat prin folosirea de Promises. ​
 +
 +La ce ne ajuta sistemul de Promises ? În timp ce sistemul de callback/​corutine funcționează pentru situații simple, poate scăpa cu ușurință de sub control și poate fi greu de gestionat. De exemplu, este des intalnit să executați operațiuni asincrone într-o secvență, una după alta. Iată cum arată dacă folosim apeluri standard: ​
 +
 +
 +<code c#>
 +// our main function that calls the async function
 +public void Main()
 +{
 +  // call the first async function
 +  AsyncOperation(() => {
 +    // call the second async function
 +    AsyncOperation(() => {
 +      // call the third async function
 +      AsyncOperation(() => {
 +        // continue ...
 +      });
 +    });
 +  });
 +}
 +
 +public void AsyncOperation(Action callback)
 +{
 +  // execute async operation (takes some unknown amount of time)...
 +
 +  // async op is done, call the callback function
 +  callback.Invoke();​
 +}
 +</​code>​
 +
 +In mare parte, simplifica metoda de lucrul cu callback-uri sau asteptari, in special in cazul in care avem request-uri interdependente. ​
 +
 +Ce sunt Promises/​Promisiunile?​
 +O Promisiune este un obiect care reprezintă eventuala finalizare sau eșec a unei operațiuni asincrone. Dacă o funcție returnează o Promisiune, nu returnează imediat o valoare, dar Promisiunea o va returna odată ce operația de asincronizare este finalizată.
 +
 +Poate fi mai ușor de înțeles uitându-ne la un exemplu:
 +
 +<code c#>
 +public void Main()
 +{
 +  // DownloadData is the async operation, and it returns a Promise
 +  DownloadData("<​some https url>"​)
 +    .Then((data) => {
 +      // This function gets called if DownloadData is successful
 +      // the '​data'​ argument is the result of the download
 +    })
 +    .Catch(() => {
 +      // This function gets called if DownloadData fails
 +      // In that case, the above .Then will not be called
 +    });
 +}
 +</​code>​
 +
 +După cum puteți vedea mai sus, obiectul Promise expune funcții precum .Then() sau .Catch(). Funcția transmisă la .Then() este apelată odată ce Promisiunea „se rezolvă” (operația asincronă se finalizează cu succes) și ia rezultatul operației asincrone ca parametru. Funcția transmisă la .Catch() este apelată dacă apar erori în timpul operației asincrone.
 +
 +De asemenea, metodele .Then() și .Catch() returnează promisiuni, astfel încât promisiunile pot fi înlănțuite (permițându-ne să executăm secvențe de evenimente asincrone!).
 +
 +Pentru o descriere mult mai bună a Promises, consultați [[https://​developer.mozilla.org/​en-US/​docs/​Web/​JavaScript/​Reference/​Global_Objects/​Promise|MDN Docs]].
 +
 +
 +Din nefericire C# nu are integrate Promises ca funcitonalitate nativa, astfel incat le puteti folosi prin adaugarea de pachete noi, cum ar fi:
 +  * [[https://​github.com/​Real-Serious-Games/​C-Sharp-Promise]]
 +  * [[https://​github.com/​AgeOfLearning/​promises]]
 +
 +
 +In Unity totusi, puteti folosi mai simplu acest fel de sistem prin pachetul [[https://​assetstore.unity.com/​packages/​tools/​network/​rest-client-for-unity-102501|REST Client for Unity]] care are deja integrata partea de promises si o parte de serializare JSON.
 +
 +Un exemplu de utilizare:
 +
 +<code c#>
 +
 +//exemplu de structura de date pentru parsare JSON
 +
 +[Serializable]
 +public class UserData
 +{
 +    public int id;
 +    public string name;
 +    public string email;
 +    public UserAvatar avatar;
 +    public UserProfile profile;
 +    public UserQuest[] quests;
 +    public UserTour[] tours;
 +    public LibraryEvent[] libraryEvents;​
 +}
 +
 +
 +//exemplu de functie care returneaza un Promise
 +public RSG.IPromise<​UserData>​ GetUserProfile() {
 +
 +        return RestClient.Get<​UserData>​(Config.apiURL + "​user"​)
 +        .Then(result => {
 +
 +     //​Debug.Log(JsonUtility.ToJson(result,​true));​
 +
 +            data = result;
 +            email = data.email;
 +            name = data.name;
 +            quests = data.quests;​
 +            id = Convert.ToUInt32(data.id);​
 +
 +           ​return data;
 +        });
 +    }
 +
 +
 +//utilizare in cascada de evenimente ​  
 +void Start () 
 +{
 +
 +        User.instance.GetUserProfile()
 +        .Then(result => {
 +            Debug.Log("​get library"​);​
 +            return Library.instance.GetLibraryProfile();​
 +        })
 +        .Then(result => {
 +            Debug.Log("​load scene"​);​
 +            SceneManager.LoadScene(Config.libraryTitle);​
 +            // SceneManager.LoadScene(1);​
 +            // username.text = result.name;​
 +        });
 +        ​
 +}
 +</​code>​
 +
 +Gasiti mai multa documentatie aici [[https://​github.com/​proyecto26/​RestClient]].
  
 === Gestionare autentificare === === Gestionare autentificare ===
 +
 +Pentru a prelua din web informatii speficice pentru un anumit utilizator, cererile HTTP trebuie complementate cu informatia de autentificare prin folosirea unui header de Autorizare (Authorization). ​
 +
 +In acest header transmitem in general un token. In functie de nevoi/​capabilitati/​dispozitive,​ exista mai multe tipuri de token-uri care pot fi folosite:
 +  * Access tokens
 +  * ID tokens
 +  * Self-signed JWTs
 +  * Refresh tokens
 +  * Federated tokens
 +  * Bearer tokens
 +
 +Cel mai dese folosite sunt Bearer tokens, care, dupa cum descrie si numele, sunt o clasă generală de jeton care acordă acces părții în posesia tokenului. Token-urile de acces, ID tokens și JWT-urile sunt toate tokenuri Bearer.
 +
 +Mai multe detalii despre tokenuri puteti citi aici: [[https://​cloud.google.com/​docs/​authentication/​token-types]].
 +
 +Pentru a folosi un token de tip Bearer, fiecare cerere HTTP trebuie sa conta un header de Autorizare de tip Bearer. De exemplu:
 +
 +<code c#>
 +//Pentru RestClient package
 +
 +//​autorizare
 +RestClient.DefaultRequestHeaders["​Authorization"​] = "​Bearer "​+token.access_token;​
 +//alte headere in functie de nevoie
 +RestClient.DefaultRequestHeaders["​Accept"​] = "​application/​json";​
 +
 +//pentru UnityWebRequests
 +
 +var uwr = new UnityWebRequest(url,​ "​POST"​);​
 +//​autorizare
 +uwr.SetRequestHeader("​Authorization",​ "​Bearer "​+token.access_token);​
 +//alte headere in functie de nevoie
 +uwr.SetRequestHeader("​Content-Type",​ "​application/​json"​);​
 +</​code>​
  
 ==== Game Launcher in C# ==== ==== Game Launcher in C# ====
Line 319: Line 496:
   * In mijloc este **designer-ul** (fereastra cu fundal alb), unde veti putea adauga componente UI   * In mijloc este **designer-ul** (fereastra cu fundal alb), unde veti putea adauga componente UI
   * In stanga este toolbox-ul (acesta il puteti adauga din **View -> Toolbox**)   * In stanga este toolbox-ul (acesta il puteti adauga din **View -> Toolbox**)
-  * In dreapta este **codul C#** aferent acestei ferestre - aici veti lega evenimentele ​comonentelor ​din UI cu logica voastra+  * In dreapta este **codul C#** aferent acestei ferestre - aici veti lega evenimentele ​componentelor ​din UI cu logica voastra
   * In mijloc-jos este codul XAML - elementele continute in fereastra sunt definita folosind acele tag-uri (nu e de interes daca faceti doar drag-and-drop)   * In mijloc-jos este codul XAML - elementele continute in fereastra sunt definita folosind acele tag-uri (nu e de interes daca faceti doar drag-and-drop)
  
Line 347: Line 524:
 === HTTP === === HTTP ===
  
-Pentru partea de backend ne vom folosi de **Back4App**,​ ce este o platoforma de tip backend-as-a-service (BaaS), construita pe **Parse**, ce ofera stocare cloud, servicii de autentificare,​ baze de date, etc.+Pentru partea de backend ​in general este nevoie de un serviciu Web, cu o baza de date in spate. 
 +Pentru exemplificare ​ne vom folosi de **Back4App**,​ ce este o platoforma de tip backend-as-a-service (BaaS), construita pe **Parse**, ce ofera stocare cloud, servicii de autentificare,​ baze de date, etc.
  
 In mod special pentru acest laborator ne vom folosi de apeluri catre endpoint-uri via un REST API (request-uri HTTP). In mod special pentru acest laborator ne vom folosi de apeluri catre endpoint-uri via un REST API (request-uri HTTP).
Line 410: Line 588:
 Nu uitati sa schimbati ''​YOUR_APP_ID''​ si ''​YOUR_REST_API_KEY''​. Le gasiti in **dashboard-ul proiectului Back4App -> App Settings -> Security & Keys**. Nu uitati sa schimbati ''​YOUR_APP_ID''​ si ''​YOUR_REST_API_KEY''​. Le gasiti in **dashboard-ul proiectului Back4App -> App Settings -> Security & Keys**.
 </​note>​ </​note>​
 +
 +=== Unity ===
 +
 +Dupa acesti pasi va trebui sa va folositi de celalalt buton din form (Start Game), prin care veti lansa in executie build-ul de Unity, caruia ii trimiteti argumentele necesare. Va trebui sa trimiteti si acel **session token** capturat anterior.
 +
 +<​note>​
 +  * In cazul in care ati folosit Back4App, aveti [[https://​dashboard.back4app.com/​apidocs#​user-api|aici documentatia API-urilor]].
 +  * Back4App se bazeaza pe Parse, aveti [[https://​docs.parseplatform.org/​rest/​guide/#​users|aici documentatia API-urilor]] pentru acesta
 +</​note>​
 +
 +
 +Va trebui sa va alegeti un call care necesita acest session token, anume un call care are nevoie de campul din header ''​X-Parse-Session-Token''​.
 +
 +Folositi ''​UnityWebRequest''​ pentru a face request-ul. In cerinte vi se specifica sa faceti un call pentru a returna numele utilizatorului (poate fi in exemplul nostru acest ''​displayName''​).
 +
 +Un exemplu de API call ce se foloseste de acest session token este ilustrat mai jos.
 +
 +{{ :​pjv:​laboratoare:​2024:​pajv_l3_back4app_api.png?​700 |}}
  
pjv/laboratoare/2024/a03.1730672134.txt.gz · Last modified: 2024/11/04 00:15 by andrei.lapusteanu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0