Differences

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

Link to this comparison view

pjv:laboratoare:2025:a04 [2025/09/29 08:25]
alexandru.gradinaru created
pjv:laboratoare:2025:a04 [2025/11/26 17:53] (current)
andrei.lapusteanu Fixed cloudscript incorrect code
Line 1: Line 1:
 ===== 4. Servicii web dedicate ===== ===== 4. Servicii web dedicate =====
 +
 +<​hidden>​
 +
 +de explicat sumar:
 +  * - cum se utilizeaza un SDK extern in Unity
 +  * - cum se foloseste/​configureaza PlayFab
 +  * - prezentare exemple de pe github/​tutoriale (in special SpaceRumble probabil care are majoritatea elementelor din cerinta .. 
 +[[https://​github.com/​PlayFab/​PlayFab-Samples/​tree/​master/​Samples/​Unity/​SpaceRumble]])
 +    * Nu mai e valid repo 🥲  ​
 +
 +
 +</​hidden>​
 +
 +==== Cerinte =====
 +
 +Realizarea unui joc de pick-up folosind serviciile PlayFab
 +  * Creati o scena care sa:
 +    * contina un formular de autentificare
 +    * selectarea unui personaj/​tip de personaj (dintr-un set de minim 2) si salvare informatii player in PlayFab (nume si tip de personaj)
 +    * actualizare statistici user prin event automation si cloud scripts pentru urmatoarele atribute:
 +      * numar rulari ale jocului de catre un player
 +      * timpul petrecut in joc
 +      * numar de colectibile/​obiecte colectate ​
 +      * XP (la fiecare obiect colectat creste XP)
 +      * level
 +    * Mentinerea unui leaderboard cu informatiile de mai sus
 +    * Sistem de Level up 
 +      * se activeaza dupa ce ai acumulat suficient XP 
 +      * dupa acumulare de suficient XP dureaza (de ex 1 min) - dupa care se trimite un email de notificare {{ :​pjv:​laboratoare:​2025:​whatsapp_image_2025-11-26_at_12.33.01.jpeg?​700 |}}
 +
 +      * dupa levelup, la o noua lansare a jocului, se va lansa o alta scena cu nivel de dificultate ridicat (informatia este citita din Title Data)
 + 
 +Bonus: ​
 +  * interfata grafica pentru selectarea personajului/​avatarului si a leaderboardului
 +  * login cu Google, Facebook etc
 +
 +
 +
 +===== Documentatie video ======
 +
 +Gasiti pe MS Teams inregistrat
 +
 +===== Documentatie text ======
 +
 +==== Introducere în PlayFab ====
 +
 +PlayFab este unul dintre serviicile web dedicate jocurilor video, oferit de Microsoft, similar cu alte servicii de la AWS, Epic Online Services, Unity, Steam si altele. Este un backend as a service (BaaS) care oferă funcționalități esențiale pentru jocuri: autentificare,​ date persistente pentru playeri, leaderboard-uri,​ matchmaking,​ economy, etc.
 +
 +In general aceste servicii vin deja cu un SDK implementat pentru motoarele grafice cunoscute (Unity, Unreal etc), dar se pot utiliza si direct prin cereri HTTP intr-o arhitectura REST. SDK-ul este in general un wrapper peste un REST API.
 +
 +Resurse oficiale:
 +
 +  * [[https://​playfab.com/​|Site oficial PlayFab]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​|Documentație oficială Microsoft]]
 +  * [[https://​github.com/​PlayFab/​UnitySDK|PlayFab Unity SDK pe GitHub]]
 +
 +==== Instalarea și configurarea SDK-ului ====
 +
 +=== 1. Crearea unui cont PlayFab ===
 +
 +  * Accesați [[https://​developer.playfab.com/​|PlayFab Developer Portal]]
 +  * Creați-vă un cont
 +  * Creați un nou Title (proiect/​joc) - veți primi un Title ID unic
 +
 +=== 2. Import PlayFab SDK ===
 +
 +  * Accesați pagina de GitHub a SDK-ului pentru Unity, [[https://​github.com/​PlayFab/​UnitySDK|Unity SDK]]
 +  * Urmați pașii de la secțiunea //3. Installing or Upgrading the PlayFab UnitySdk//
 +    * Recomandăm să integrați Unity package-ul pentru **PlayFab Unity Editor Extensions**
 +    * Din Unity, puteți apoi să instalați SDK-ul -- găsiți fereastra de setup în Window → Window → Editor Extensions
 +
 +<note tip>
 +Găsiți mai multe detalii, dacă aveți nevoie, în [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​sdks/​unity3d/​quickstart|Quickstart:​ PlayFab Client library for C# in Unity]]
 +</​note>​
 +
 +==== Concepte PlayFab ====
 +
 +=== Autentificare ===
 +
 +Primul pas pentru folosirea oricarui serviciu dedicat este autentificare. Procesul este simplificat prin folosirea SDK-ului.
 +
 +PlayFab acceptă o varietate de furnizori de autentificare (Microsoft, Facebook, Google etc). Pentru development și testare rapidă, cel mai simplu este **LoginWithCustomID** care folosește device ID-ul:
 +
 +<code csharp>
 +void LoginWithCustomId()
 +{
 +    var request = new LoginWithCustomIDRequest
 +    {
 +        CreateAccount = true,  // Should be true if account does *not* exist (does a sort of create-and-login).
 +        CustomId = PlayFabSettings.DeviceUniqueIdentifier
 +    };
 +    ​
 +    PlayFabClientAPI.LoginWithCustomID(request,​
 +        result => Debug.Log($"​Login successful! PlayFabId: {result.PlayFabId}"​),​
 +        error => Debug.LogError($"​Error:​ {error.GenerateErrorReport()}"​)
 +    );
 +}
 +</​code>​
 +
 +<​note>​
 +Pentru alte metode de autentificare (Facebook, Google, etc), consultați [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​identity/​player-identity/​platform-specific-authentication/​|documentația oficială]].
 +</​note>​
 +
 +=== Player Data ===
 +
 +PlayFab oferă mai multe tipuri de stocare pentru date asociate playerilor:
 +
 +**User Data (client R/W, server R/W)**
 +  * Date la care clientul are acces complet
 +  * Exemple: preferințe setări joc, cosmetics, etc.
 +  * Nu este indicat să le folosiți pentru date critice/​sensibile (progres, currency)
 +
 +**User Read-Only Data (client read-only, server R/W)**
 +  * Date pe care doar serverul le poate modifica (R/W)
 +  * Clientul poate doar să le citească (read-only)
 +  * Utilizare: progres quest-uri, achievement-uri
 +
 +**Internal Data (server R/W only)**
 +  * Complet invizibil pentru client - nu poate fi nici citit
 +  * Accesibil doar din CloudScript prin ''​server.GetUserInternalData''​ / ''​UpdateUserInternalData''​
 +  * Utilizare: date pentru logica de backend (cooldowns, timestamps server-side,​ rate limiting)
 +
 +**Exemplu User Data:**
 +
 +<​note>​Pentru interacțiunea cu sistemul **User Data** ne vom folosi de API-ul clientului, ''​PlayFabClientAPI''​.</​note>​
 +
 +<code csharp>
 +void SavePlayerString(string playerStr)
 +{
 +    var request = new UpdateUserDataRequest
 +    {
 +        Data = new Dictionary<​string,​ string>
 +        {
 +            { "​SomeStringValue",​ playerStr }
 +        }
 +    };
 +    PlayFabClientAPI.UpdateUserData(request,​
 +        result => Debug.Log("​Data has been saved remotely!"​),​
 +        error => Debug.LogError(error.GenerateErrorReport())
 +    );
 +}
 +
 +void LoadPlayerString()
 +{
 +    PlayFabClientAPI.GetUserData(new GetUserDataRequest(),​
 +        result =>
 +        {
 +            if (result.Data.TryGetValue("​SomeStringValue",​ out var data))
 +            {
 +                string fetchedStr = data.Value;
 +                Debug.Log($"​Fetched data: {fetchedStr}"​);​
 +            }
 +        },
 +        error => Debug.LogError(error.GenerateErrorReport())
 +    );
 +}
 +</​code>​
 +
 +<note tip>
 +Datele stocate pot fi vizualizate pe dashboard: Mergeți la secțiunea **Players**,​ găsiți player-ul pentru care ați scris datele, accesați tab-ul **Player Data**.
 +</​note>​
 +
 +<​note>​
 +Mai multe detalii: [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​player-progression/​player-data|Player Data]]
 +</​note>​
 +
 +=== CloudScript ===
 +
 +CloudScript permite executarea de cod **pe serverele PlayFab**, nu pe client. Esențial pentru validare server-side și prevenirea cheating-ului.
 +
 +**De ce CloudScript?​** Pentru orice logică critică (quest progress, achievements,​ currency) avem nevoie de validare server-side.
 +
 +<note important>​
 +În producție, recomandăm folosirea **Azure Functions** (C#) în loc de CloudScript (JavaScript) pentru logică backend complexă. Acestea oferă mai multă flexibilitate,​ debugging mai bun și support pentru limbaje moderne. Setup-ul pentru Azure Functions este însă mai complex și depășește scope-ul acestui laborator, astfel că vom folosi CloudScript clasic (JavaScript) pentru simplitate.
 +</​note>​
 +
 +<note important>​
 +CloudScript nu este exclusiv pentru a scrie/citi din date ale player-ului. Puteți efectua multe alte operațiuni,​ de exemplu grant de item sau virtual currency, schimbare date generale ale proiectului PlayFab (Title Data), etc.
 +</​note>​
 +
 +**Exemplu utilizare:​**
 +
 +1. Pentru a scrie cod backend in CloudScript:​
 +  * Dashboard → Automation → CloudScript → Revisions (Legacy)
 +  * Scrieți codul JavaScript
 +  * Apăsați **Upload new revision**
 +  * Bifați **Deploy this revision after save**, apăsați **Save and deploy** ​
 +
 +<code javascript>​
 +handlers.incrementCounter = function(args,​ context) {
 +    var res = server.GetUserReadOnlyData({
 +        PlayFabId: currentPlayerId,​
 +        Keys: ["​Counter"​]
 +    });
 + 
 +    var count = 0;
 +    if (res.Data["​Counter"​] !== undefined) {
 +        count = JSON.parse(res.Data["​Counter"​].Value);​
 +    }
 +    count++;
 + 
 +    server.UpdateUserReadOnlyData({
 +        PlayFabId: currentPlayerId,​
 +        Data: { "​Counter":​ count }
 +    });
 + 
 +    return count;
 +};
 +</​code>​
 +
 +2. Apel CloudScript function din Unity:
 +
 +<code csharp>
 +private void CallCloudScript()
 +{
 +    var request = new ExecuteCloudScriptRequest
 +    {
 +        FunctionName = "​incrementCounter",​
 +        GeneratePlayStreamEvent = true
 +    };
 +    PlayFabClientAPI.ExecuteCloudScript(request,​
 +    result =>
 +    {
 +        var counterValue = Convert.ToInt32(result.FunctionResult);​
 +        Debug.Log($"​Returned counter value: {counterValue}"​);​
 +    },
 +    error =>
 +    {            ​
 +        Debug.LogError($"​Cloud Script error: {error.GenerateErrorReport()}"​);​
 +    });
 +}
 +</​code>​
 +
 +<note tip>
 +Pentru debugging, folosiți ''​log.info("​message"​)''​ în CloudScript. Verificați output-ul în **Dashboard → Title Overview → PlayStream Monitor**.
 +</​note>​
 +
 +<​note>​
 +Mai multe detalii: [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​features/​automation/​cloudscript/​quickstart|CloudScript Quickstart]]
 +</​note>​
 +
 +=== Event Automation ===
 +
 +PlayFab poate declanșa automat CloudScript functions în două moduri:
 +
 +**Rules (PlayStream Events)** - se execută când anumite evenimente apar în joc:
 +  * Player login/​logout
 +  * Statistic updated
 +  * Player data changed
 +  * Item purchased
 +  * Custom events definite de voi
 +  * Util pentru a reacționa (aproape) **instant** la acțiunile playerilor
 +
 +**Scheduled Tasks** - se execută periodic la intervale fixe:
 +  * Daily/​weekly/​monthly rewards
 +  * Season resets
 +  * Leaderboard cleanup
 +  * Maintenance tasks
 +  * Util pentru task-uri **recurente** independente de acțiunile playerilor
 +
 +**Exemplu de utilizare Rules:**
 +
 +Puteți crea o regulă care să incrementeze automat un counter de login-uri de fiecare dată când un player se autentifică:​
 +  * **Automation → Rules → New Rule**
 +  * Event Type: ''​player_logged_in''​
 +  * Actions → Execute Entity Cloud Script
 +  * Function Name: ''​updatePlayerLogins''​ (presupunem că există funcția în CloudScript)
 +
 +Astfel, de fiecare dată când un player face login, PlayFab va apela automat funcția CloudScript fără să fie nevoie de apel explicit din client.
 +
 +**Exemplu de utilizare Scheduled Tasks:**
 +
 +Puteți crea un task care să reseteze daily rewards pentru toți playerii la miezul nopții:
 +  * **Automation → Scheduled Tasks → New Task**
 +  * Schedule type: Recurring (ex: Daily la 00:00 UTC)
 +  * Type of task: Run Cloud Script function one
 +  * Function Name: ''​resetDailyRewards''​ (presupunem că există funcția în CloudScript)
 +
 +<note tip>
 +Verificați execuția task-urilor în **Automation → Scheduled Tasks (scroll în josul paginii)** pentru debugging.
 +</​note>​
 +
 +<​note>​
 +Mai multe detalii:
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​data-analytics/​acting-data/​scheduled-tasks|Scheduled Tasks]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​data-analytics/​acting-data/​action-rules-quickstart|Actions and rules quickstart]]
 +</​note>​
 +
 +==== Notificări Email Asincrone ====
 +
 +=== Context ===
 +
 +Pentru scenarii unde playerul nu este online când un proces se termină (ex: training care dureaza mai mult timp), putem trimite notificări prin email.
 +
 +**Problema:​** PlayFab nu trimite email-uri direct - metoda oficială necesită configurare SMTP putin mai complexă (Add-on → Email).
 +
 +**Alternativă:​** Folosim un serviciu extern (ex: Resend) și facem HTTP requests din CloudScript.
 +
 +=== Procesul General ===
 +
 +  - **Configurare serviciu email** - Cont gratuit pe [[https://​resend.com|Resend]],​ obțineți API key
 +  - **CloudScript face HTTP request** - Adaptăm exemplul existent ''​makeHTTPRequest''​ din codul deja existent in CloudScript
 +  - **Scheduled Task verifică periodic** - Rulează la 10-15 min, verifică playeri cu training complet
 +
 +=== Implementare CloudScript ===
 +
 +**Trimitere email:**
 +
 +<code javascript>​
 +handlers.SendLevelUpEmail = function(args,​ context) {
 +    var playerEmail = args.playerEmail;​
 +    ​
 +    var headers = {
 +        "​Authorization":​ "​Bearer YOUR_RESEND_API_KEY",​
 +        "​Content-Type":​ "​application/​json"​
 +    };
 +    ​
 +    var body = {
 +        from: "​onboarding@resend.dev",​
 +        to: playerEmail,​
 +        subject: "Level Up Complete!",​
 +        html: "<​p>​Your training is complete!</​p>"​
 +    };
 +
 +    var url = "​https://​api.resend.com/​emails";​
 +    var response = http.request(url,​ "​post",​ JSON.stringify(body),​ "​application/​json",​ headers);
 +    ​
 +    return { emailSent: true };
 +};
 +</​code>​
 + 
 +=== SMTP ===
 +
 +**PlayFab SMTP Add-on** (Add-ons → Email):
 +  * Integrare nativă cu email templates
 +  * Necesită configurare SMTP (Gmail, SendGrid, etc.)
 +  * Recomandat pentru producție
 +
 +<​note>​
 +Pentru testare rapidă, API extern (Resend) este mai simplu. Pentru producție, folosiți Add-on-ul SMTP din PlayFab.
 +</​note>​
 +
 +<note tip>
 +CloudScript include deja un exemplu de HTTP request - vezi funcția ''​makeHTTPRequest''​ în samples.
 +</​note>​
 +
 +**Resurse:​**
 +  * [[https://​resend.com/​docs|Resend API]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​features/​engagement/​emails/​quickstart|PlayFab SMTP Setup]]
 +
 +
 +=== Statistics și Leaderboards ===
 +
 +PlayFab folosește două concepte separate pentru datele jucătorilor ce țin de scor, progres, etc., **statistics** și **leaderboard-uri** -- dar în backend-ul din PlayFab acestea sunt (destul de) legate și utilitatea acestora constă în sinergia dintre cele două concepte.
 +
 +**Statistics** = valori numerice tracked per player (high scores, kills, wins, etc.)
 +  * Se creează în **Leaderboards → Statistics → New statistic**
 +  * Setări de interes:
 +    * **Statistic name** - numele statisticii (ex: "​HighScore"​)
 +    * **Aggregation method** - cum se calculează valoarea:
 +      * Last - ultima valoare
 +      * Max - cea mai mare (pentru high scores)
 +      * Min - cea mai mică (pentru best times)
 +      * Sum - se însumează toate valorile
 +    * **Statistic frequency** - Manual / Day / Week / Month (când se resetează statistica - PlayFab face asta automat)
 +
 +**Leaderboards** = clasamente bazate pe statistici
 +  * Se creează în **Leaderboards → Leaderboards → New leaderboard**
 +  * Trebuie linked la o statistică existentă (**Linked statistic name**)
 +  * Leaderboard-ul se actualizează automat când se modifică statistica
 +
 +Pentru a putea folosi API-ul de stats/​leaderboards pe clienți (să le permiteți actualizarea acestora):
 +  * **Settings → API Features**
 +  * Bifați "Allow client to post player statistics"​
 +
 +<​note>​
 +În mod ideal această setare este destul de **sensibilă**,​ întrucât permite jucătorilor actualizarea acestor valori - în scop didactic este OK.
 +</​note>​
 +
 +**Update statistică din Unity:**
 +
 +<code csharp>
 +using PlayFab;
 +using PlayFab.ProgressionModels;​
 +
 +void SubmitScore(int score)
 +{
 +    var request = new UpdateStatisticsRequest
 +    {
 +        Entity = new EntityKey
 +        {
 +            Id = "<​Entity_ID>", ​ // Player'​s entity ID (from login response).
 +            Type = "​title_player_account"​
 +        },
 +        Statistics = new List<​StatisticUpdate>​
 +        {
 +            new StatisticUpdate
 +            {
 +                Name = "​HighScore",​
 +                Scores = new List<​string>​ { score.ToString() }
 +            }
 +        }
 +    };
 +    ​
 +    PlayFabProgressionAPI.UpdateStatistics(request,​
 +        result => Debug.Log("​Statistic updated!"​),​
 +        error => Debug.LogError(error.GenerateErrorReport())
 +    );
 +}
 +</​code>​
 +
 +**Citire leaderboard:​**
 +
 +<code csharp>
 +using PlayFab;
 +using PlayFab.ProgressionModels;​
 +
 +private void GetLeaderboard()
 +{
 +    var request = new GetEntityLeaderboardRequest
 +    {
 +        LeaderboardName = "​HighScore_Leaderboard",​
 +        StartingPosition = 1,  // 1 = beginning of leaderboard (not 0!).
 +        PageSize = 10          // Min 1, max 100.
 +    };
 +    PlayFabProgressionAPI.GetLeaderboard(request,​
 +        result =>
 +        {
 +            foreach (var entry in result.Rankings)
 +            {
 +                Debug.Log($"​{entry.Rank}. {entry.DisplayName}:​ {entry.Scores[0]}"​);​
 +            }
 +        },
 +        error => Debug.LogError(error.GenerateErrorReport())
 +    );
 +}
 +</​code>​
 +
 +**Citire leaderboard în jurul playerului:​**
 +
 +<code csharp>
 +using PlayFab;
 +using PlayFab.ProgressionModels;​
 +
 +void GetLeaderboardAroundPlayer()
 +{
 +    var request = new GetLeaderboardAroundEntityRequest
 +    {
 +        Entity = new EntityKey
 +        {
 +            Id = "<​Entity_ID>", ​    // Player'​s entity ID (from login response).
 +            Type = "​title_player_account"​
 +        },
 +        LeaderboardName = "​HighScore_Leaderboard",​
 +        MaxSurroundingEntries = 10  // Number of entries above and below (these are split ~half/​half).
 +    };
 +    PlayFabProgressionAPI.GetLeaderboardAroundEntity(request,​
 +        result =>
 +        {
 +            foreach (var entry in result.Rankings)
 +            {
 +                Debug.Log($"​{entry.Rank}. {entry.DisplayName}:​ {entry.Scores[0]}"​);​
 +            }
 +        },
 +        error => Debug.LogError(error.GenerateErrorReport())
 +    );
 +}
 +</​code>​
 +
 +<note tip>
 +Pentru a obține Entity ID-ul playerului după login, folosiți ''​result.EntityToken.Entity.Id''​ din response-ul de la LoginWithCustomID.
 +</​note>​
 +
 +<note tip>
 +Vizualizați statisticile în **Leaderboards → Statistics** și leaderboard-ul în **Leaderboards → Leaderboards**.
 +</​note>​
 +
 +<​note>​
 +Mai multe detalii: ​
 +
 +[Statistics]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​player-progression/​statistics|Azure PlayFab Statistics overview]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​player-progression/​statistics/​quickstart-statistics|Quickstart statistics]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​player-progression/​statistics/​create-basic-statistics|Create a basic statistic]]
 +[Leaderboards]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​community/​leaderboards|Azure PlayFab Leaderboards overview]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​community/​leaderboards/​quickstart-leaderboards|Quickstart leaderboards]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​community/​leaderboards/​create-basic-leaderboard|Create a basic leaderboard]]
 +</​note>​
 +==== Resurse utile ====
 +
 +**Documentație:​**
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​identity/​player-identity/​platform-specific-authentication/​|Authentication]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​player-progression/​player-data|Player Data]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​features/​automation/​cloudscript/​quickstart|CloudScript]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​player-progression/​statistics|Statistics overview]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​community/​leaderboards|Leaderboards overview]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​data-analytics/​acting-data/​scheduled-tasks|Scheduled Tasks]]
 +  * [[https://​learn.microsoft.com/​en-us/​gaming/​playfab/​data-analytics/​acting-data/​action-rules-quickstart|Actions and rules quickstart]]
 +
 +**Exemple:​**
 +  * [[https://​github.com/​PlayFab/​PlayFab-Samples|PlayFab Samples]]
 +  * [[https://​github.com/​PlayFab/​CloudScriptSamples|CloudScript Samples]]
 +
 +**Support:​**
 +  * [[https://​discord.com/​invite/​msftgamedev|PlayFab Community (Discord)]]
 +
pjv/laboratoare/2025/a04.1759123539.txt.gz · Last modified: 2025/09/29 08:25 by alexandru.gradinaru
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