> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Statistiques et succès

> Portez les succès et statistiques Steam vers l'API gérée par le titre du GDK XBOX, en suivant la progression dans votre jeu et en synchronisant les mises à jour avec les services XBOX.

Dans le XBOX Game Development Kit (GDK) comme dans Steam, les succès comportent des informations telles que le nom, la description, l'icône, l'indication de leur déverrouillage pour l'utilisateur actuel et, éventuellement, la progression vers leur obtention. Steam dispose également d'une API de statistiques que les jeux utilisent pour comparer la progression d'un utilisateur à une cible prédéfinie et déverrouiller des succès lorsque ces cibles sont dépassées.

En fait, le XBOX Game Development Kit (GDK) dispose de deux API de succès : *basée sur les événements* (anciennement appelée *Achievements 2013*) et *gérée par le titre* (anciennement appelée *Achievements 2017*). Bien que les deux systèmes soient des options viables, les succès/statistiques gérés par le titre vous sembleront beaucoup plus familiers si vous venez de Steam et correspondront à la logique existante probablement présente dans votre jeu. C'est pourquoi nous décrivons cette API dans cette rubrique. Pour plus d'informations sur les différences entre les deux API, consultez [Succès basés sur les événements et gérés par le titre](/fr/services/xbox-services/player-data/achievements/index). Les sections suivantes décrivent les différences entre les succès dans l'API Steamworks et dans le XBOX Game Development Kit (GDK).

Pour utiliser les API de succès gérés par le titre, vous devez créer un handle de contexte des services XBOX. Pour cela, utilisez la fonction [XblContextCreateHandle](/fr/reference/live/xsapi-c/xbox_live_context_c/xbox_live_context_c_members) et un `XUserHandle` provenant de l'[API XUserAddResult](/fr/build/core-features/common/user/xuser_howto_best_practice_signing_in) (voir la rubrique [Authentification et propriété des utilisateurs](/fr/build/steam-porting-guide/features/user-authentication-and-ownership)).

## Suivi de la progression des succès

Dans l'API Steam, vous pouvez utiliser l'API `ISteamUserStats::GetStat`/`ISteamUser::SetStat` pour suivre la progression d'un utilisateur vers un succès, ce qui fait des valeurs stockées dans l'API Steam Stats la source de vérité pour la valeur de progression actuelle.

Avec les succès gérés par le titre, votre jeu est la source unique de vérité et doit suivre lui-même la progression actuelle de l'utilisateur. La mise à jour de la progression actuelle dans le cloud au moyen d'un appel d'API constitue une sorte de « point de contrôle » et sert à afficher la progression d'un utilisateur à différents endroits de l'écosystème XBOX. Il n'existe pas de recommandation précise sur l'emplacement de stockage de la valeur de progression actuelle, mais de nombreux jeux choisissent de la stocker dans leur fichier de sauvegarde (et dans le cloud), tandis que d'autres peuvent la stocker sur leur propre service back-end. Veillez à maintenir la synchronisation des valeurs entre le cloud et le système de gestion interne des statistiques de votre jeu.

## Énumération de tous les succès

Pour obtenir toutes les statistiques d'un utilisateur avec l'API Steamworks, vous appelez généralement `ISteamUserStats::RequestUserStats` pour récupérer les dernières statistiques depuis le serveur. Une fois le rappel correspondant déclenché, vous pouvez parcourir chaque succès par son nom d'API à l'aide des fonctions `ISteamUserStats::GetStat`/`ISteamUserStats::GetAchievement` et initialiser l'état du jeu à partir des données renvoyées.

Avec le XBOX Game Development Kit (GDK), vous pouvez utiliser la fonction [XblAchievementsGetAchievementsForTitleIdAsync](/fr/reference/live/xsapi-c/achievements_c/achievements_c_members), puis utiliser les données de chaque objet succès du tableau fourni comme paramètre de sortie de [XblAchievementsResultGetAchievements](/fr/reference/live/xsapi-c/achievements_c/achievements_c_members) pour initialiser l'état de votre jeu. Chaque succès est fourni sous la forme d'une struct [XblAchievement](/fr/reference/live/xsapi-c/achievements_c/achievements_c_members). Pour plus d'informations sur les données qu'elle contient, consultez [sa rubrique de référence](/fr/reference/live/xsapi-c/achievements_c/achievements_c_members).

### Steamworks

```cpp theme={null}
void MyGameClass::Initialize()
{
    // ...
    SteamUserStats()->RequestCurrentStats();
    STEAM_CALLBACK( MyGameClass, OnUserStatsReceived, UserStatsReceived_t );
}

void MyGameClass::OnUserStatsReceived()
{
    // Iterate over stats/achievements, and initialize any necessary game data.
    // Assume that m_achievementIds is an array of "API names" for achievements.
    for (int i = 0; i < m_achievementIds.length; i++)
    {
        const char *achievementName = SteamUserStats()->GetAchievementDisplayAttribute(m_achievementIds[i], "name");
        const char *description = SteamUserStats()->GetAchievementDisplayAttribute(m_achievementIds[i], "desc");
        bool hidden = SteamUserStats()->GetAchievementDisplayAttribute(m_achievementIds[i], "hidden");
        bool isUnlocked;
        SteamUserStats()->GetAchievement(m_achievementIds[i], &isUnlocked);
        // Do any necessary achievement initialization...
    }
}
```

### XBOX Game Development Kit (GDK)

```cpp theme={null}
auto asyncBlock = std::make_unique<XAsyncBlock>();
asyncBlock->context = nullptr;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
    // Take over ownership of the XAsyncBlock*
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; 

    XblAchievementsResultHandle resultHandle;
    auto hr = XblAchievementsGetAchievementsForTitleIdResult(asyncBlock, &resultHandle);

    if (SUCCEEDED(hr))
    {
        const XblAchievement* achievements = nullptr;
        size_t achievementsCount = 0;
        hr = XblAchievementsResultGetAchievements(resultHandle, &achievements, &achievementsCount);

        for (size_t i = 0; i < achievements.length; i++)
        {
            auto name = achievement[i].name;
            char description[256];
            char progress[4] = "100";
            if (achievement[i].progressState == XblAchievementProgressState::Achieved)
            {
                strcpy(description, achievement[i].unlockedDescription);
            }
            else
            {
                strcpy(description, achievement[i].lockedDescription);
                strcpy(progress, achievement[i].progression.requirements[0].currentProgressValue);
            }
            // Use this data to initialize achievements...
        }

        // When you're done with the handle, close it. This will free the achievements list 
        // from memory.
        XblAchievementsResultCloseHandle(resultHandle);
        achievements = nullptr; 
        // Instead, you couldn't close the handle and store it.  
        // If you needed to copy the handle, call XblAchievementsResultDuplicateHandle()
    }
};

// Assume m_xblContext is an XBOX services Context handle that has been initialized properly.
uint64_t xuid;
XblContextGetXboxUserId(m_xblContext, &xuid);

HRESULT hr = XblAchievementsGetAchievementsForTitleIdAsync(
    m_xblContext,
    xuid, 
    m_titleId, // Assume this is a uint32_t holding your game's title ID.
    XblAchievementType::Persistent, // achievementType: You probably want to use XblAchievementType::Persistent.
    false, //unlockedOnly: If true, returns only unlocked achievements.
    XblAchievementOrderBy::DefaultOrder, // orderBy: How to order the incoming achievements list. 
    0, // skipItems: The number of achievements to skip. 
    0, // maxItems: Set to 0 to get all achievements/disable pagination.
    asyncBlock.get()
);
if (SUCCEEDED(hr))
{
    // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership.
    // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock*
    asyncBlock.release();
}
```

Pour plus d'informations sur les succès gérés par le titre, consultez [Obtention des succès gérés par le titre](/fr/services/xbox-services/player-data/achievements/index).

## Mise à jour de la progression d'un succès et déverrouillage des succès

Dans le XBOX Game Development Kit (GDK) comme dans Steam, c'est le code du jeu qui détermine quand un succès doit être déverrouillé. Avec le XBOX Game Development Kit (GDK), définir la progression d'un succès sur 100 le déverrouille. Les sections suivantes décrivent les étapes nécessaires pour mettre à jour la progression d'un utilisateur sur un succès donné avec les deux API.

Voici un exemple de code d'une méthode `UpdateAchievement`, implémentée dans les deux SDK, qui prend en entrée un ID/nom d'API de succès (et un nom d'API de statistique dans Steam), la valeur d'incrémentation du succès et la valeur cible pour déverrouiller ce succès, puis met à jour la progression et déverrouille le succès en conséquence.

### Steamworks

```cpp theme={null}
void MyGame::UpdateAchievement(const char *achievementApiName, const char *statApiName, int diff, int target)
{
    bool isCompleted;
    int value;
    bool result = SteamUserStats()->GetAchievement(achievementApiName, &isCompleted);
    if (!result) {
        // Handle error.
    }

    result = SteamUserStats()->GetStat(statApiName, &value);
    if (!result) {
        // Handle error.
    }

    SteamUserStats()->SetStat(statApiName, value + diff);
    
    if (!isCompleted && value + diff >= target)
    {
             // Unlock the achievement.
        SteamUserStats()->SetAchievement(achievementApiName);
        SteamUserStats()->StoreStats();
    }
}

// At the next checkpoint...
if(!SteamUserStats()->StoreStats()) {
    // Handle error.
}
```

### XBOX Game Development Kit (GDK)

```cpp theme={null}
void MyGame::UpdateAchievement(const std::string& achievementId, int diff, int target)
{
    // Assume this function returns the specified achievement's progress from wherever you're storing it.
    auto previousValue = MyGetAchievementProgress(achievementId);
    // If you're storing progress elsewhere, make sure to update it there, too! Again, we're assuming 
    // that this is a function you've written to update the progress value wherever it's stored.
    MySetAchievementProgress(previousValue + diff);
    // Calculate the percent of the target that the user has reached after adding the value of diff.
    uint32_t percentComplete = (previousValue + diff) / target;

    auto async = std::make_unique<XAsyncBlock>();
    async->context = nullptr;
    async->callback = [](XAsyncBlock *async)
    {
        // Take over ownership of the XAsyncBlock*
        std::unique_ptr<XAsyncBlock> asyncBlockPtr{ async }; 

        HRESULT result = XAsyncGetStatus(async, true);

        if (SUCCEEDED(result))
        {
            // Success!
        }
        else
        {
            if (result == HTTP_E_STATUS_NOT_MODIFIED)
            {
                // The achievement was already completed or it was previously
                // set to a higher value, so it's unchanged.
            }
            else
            {
                // Handle error.
            }
        }
    };

    // Assume m_xblContext is an XBOX services Context handle that has been initialized properly.
    uint64_t xuid;
    XblContextGetXboxUserId(m_xblContext, &xuid);
    HRESULT hr = XblAchievementsUpdateAchievementAsync(
        m_xblContext,
        xuid,
        achievementId.c_str(),
        percentComplete,
        async
    );

    if (SUCCEEDED(hr))
    {
        // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership.
        // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock*
        asyncBlock.release();
    }
}
```

Pour plus d'informations sur les succès gérés par le titre, consultez [Mise à jour des succès gérés par le titre](/fr/services/xbox-services/player-data/achievements/index).

## Succès hors connexion

Le déverrouillage d'un succès XBOX ne nécessite pas de connexion réseau. Si l'utilisateur n'est pas connecté au réseau XBOX, le succès se synchronise automatiquement avec le serveur plus tard, sans code supplémentaire.

## Obtention des succès d'un autre utilisateur

Pour obtenir les succès d'un autre utilisateur, vous pouvez appeler les fonctions [XblAchievementsGetAchievementsForTitleAsync](/fr/reference/live/xsapi-c/achievements_c/achievements_c_members)/[XblAchievementsGetAchievementAsync](/fr/reference/live/xsapi-c/achievements_c/achievements_c_members) avec l'ID utilisateur XBOX (XUID) de l'utilisateur dont vous souhaitez récupérer les succès. Cela revient à utiliser les fonctions `ISteamUser::RequestUserStats`, `ISteamUserStats::GetUserStat` et `ISteamUserStats::GetUserAchievement` dans Steam.

## Réinitialisation des succès

Contrairement à `ISteamUserStats::ResetAllStats` de l'API Steamworks, qui permet de réinitialiser la progression de l'utilisateur sur toutes ses statistiques et, si cela est spécifié, sur ses succès, le XBOX Game Development Kit (GDK) n'offre aucune fonctionnalité documentée pour réinitialiser par programmation la progression ou l'état de déverrouillage des succès. Appeler [XblAchievementsUpdateAchievementAsync](/fr/reference/live/xsapi-c/achievements_c/achievements_c_members) avec une valeur de progression de 0 laisse la valeur de progression inchangée. Toutefois, vous pouvez utiliser l'[outil Player Data Reset (XblPlayerDataReset.exe)](/fr/tools/tools-services/live-player-data-reset) pendant les tests pour y parvenir.

## Limitation du débit

Lors de la mise à jour de la progression des succès, veillez à ne pas solliciter trop souvent l'API des services XBOX (XSAPI), car votre jeu risque d'atteindre sa limite de débit. Cela peut entraîner un comportement inattendu et une mauvaise expérience pour vos utilisateurs.

Pour les succès qui suivent des statistiques fréquemment mises à jour, essayez de regrouper les appels d'API afin qu'ils soient déclenchés après un certain nombre d'événements ou de mettre à jour le serveur à intervalles réguliers.

Vous pouvez déboguer vos appels d'API à l'aide de l'[analyseur de traces des services XBOX (XblTraceAnalyzer.exe)](/fr/tools/tools-services/live-trace-analyzer).

Pour plus d'informations sur la limitation du débit des services XBOX, consultez [Limitation du débit précise](/fr/services/xbox-services/develop/best-practices/live-fine-grained-rate-limiting).


## Related topics

- [Configuration des XBOX services](/fr/publishing/game-publishing/concepts/xbox-services.md)
- [Porter depuis Steam](/fr/paths/porting/from-steam.md)
- [Vue d'ensemble du guide de portage Steam](/fr/build/steam-porting-guide/overview.md)
- [Succès basés sur les événements et succès gérés par le titre](/fr/services/xbox-services/player-data/achievements/live-achievements-eb-vs-tm.md)
- [Statistiques basées sur les événements et statistiques gérées par le titre](/fr/services/xbox-services/player-data/stats-leaderboards/live-stats-eb-vs-tm.md)
