This is an old revision of the document!


4. Servicii web dedicate

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
  • 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:

Instalarea și configurarea SDK-ului

1. Crearea unui cont PlayFab

  • 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, 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

Găsiți mai multe detalii, dacă aveți nevoie, în Quickstart: PlayFab Client library for C# in Unity

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:

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()}")
    );
}

Pentru alte metode de autentificare (Facebook, Google, etc), consultați documentația oficială.

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:

Pentru interacțiunea cu sistemul User Data ne vom folosi de API-ul clientului, PlayFabClientAPI.

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())
    );
}

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.

Mai multe detalii: Player Data

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.

Î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.

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.

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
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;
};

2. Apel CloudScript function din Unity:

void CallCloudScript()
{
    PlayFabClientAPI.ExecuteCloudScript(new ExecuteCloudScriptRequest
    {
        FunctionName = "IncrementCounter"
    },
    result => Debug.Log($"Counter: {result.FunctionResult}"),
    error => Debug.LogError(error.GenerateErrorReport())
    );
}

Pentru debugging, folosiți log.info(“message”) în CloudScript. Verificați output-ul în Dashboard → Title Overview → PlayStream Monitor.

Mai multe detalii: CloudScript Quickstart

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)

Verificați execuția task-urilor în Automation → Scheduled Tasks (scroll în josul paginii) pentru debugging.

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

  1. Configurare serviciu email - Cont gratuit pe Resend, obțineți API key
  2. CloudScript face HTTP request - Adaptăm exemplul existent makeHTTPRequest din codul deja existent in CloudScript
  3. Scheduled Task verifică periodic - Rulează la 10-15 min, verifică playeri cu training complet

Implementare CloudScript

Trimitere email:

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 };
};

SMTP

PlayFab SMTP Add-on (Add-ons → Email):

  • Integrare nativă cu email templates
  • Necesită configurare SMTP (Gmail, SendGrid, etc.)
  • Recomandat pentru producție

Pentru testare rapidă, API extern (Resend) este mai simplu. Pentru producție, folosiți Add-on-ul SMTP din PlayFab.

CloudScript include deja un exemplu de HTTP request - vezi funcția makeHTTPRequest în samples.

Resurse:

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”

În mod ideal această setare este destul de sensibilă, întrucât permite jucătorilor actualizarea acestor valori - în scop didactic este OK.

Update statistică din Unity:

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())
    );
}

Citire leaderboard:

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())
    );
}

Citire leaderboard în jurul playerului:

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())
    );
}

Pentru a obține Entity ID-ul playerului după login, folosiți result.EntityToken.Entity.Id din response-ul de la LoginWithCustomID.

Vizualizați statisticile în Leaderboards → Statistics și leaderboard-ul în Leaderboards → Leaderboards.

Resurse utile

pjv/laboratoare/2025/a04.1764156135.txt.gz · Last modified: 2025/11/26 13:22 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