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

# Estadísticas y logros

> Porte los logros y las estadísticas de Steam a la API administrada por el título del GDK de XBOX, realizando un seguimiento del progreso en su juego y sincronizando los registros con los servicios de XBOX.

En el XBOX Game Development Kit (GDK) y en Steam, los logros tienen información como el nombre, la descripción, el icono y si se ha desbloqueado para el usuario actual y, opcionalmente, el progreso hacia su consecución. Steam también tiene una API de estadísticas que los juegos usan para comparar el progreso de un usuario con algún objetivo predeterminado y desbloquear logros cuando se superan estos objetivos.

De hecho, el XBOX Game Development Kit (GDK) tiene en realidad dos API de logros: *basada en eventos* (anteriormente llamada *Logros 2013*) y *administrada por el título* (anteriormente llamada *Logros 2017*). Aunque ambos sistemas son opciones viables, los logros y las estadísticas administrados por el título le resultarán mucho más familiares si viene de Steam y coincidirán con la lógica existente que probablemente ya esté presente en su juego. Por este motivo, describiremos esa API en este tema. Para obtener más información sobre las diferencias entre las dos API, consulte [Logros basados en eventos frente a logros administrados por el título](/services/xbox-services/player-data/achievements/index). Las secciones siguientes describen las diferencias entre los logros en la API de Steamworks y el XBOX Game Development Kit (GDK).

Para usar las API de logros administrados por el título, debe crear un identificador de contexto de servicios de XBOX. Esto puede hacerse mediante la función [XblContextCreateHandle](/reference/live/xsapi-c/xbox_live_context_c/xbox_live_context_c_members) y un `XUserHandle` de la [API XUserAddResult](/build/core-features/common/user/xuser_howto_best_practice_signing_in) (del tema [Autenticación y propiedad del usuario](/build/steam-porting-guide/features/user-authentication-and-ownership)).

## Seguimiento del progreso de los logros

En la API de Steam, puede usar la API `ISteamUserStats::GetStat`/`ISteamUser::SetStat` para realizar un seguimiento del progreso de un usuario hacia un logro, lo que hace que los valores almacenados en la API de estadísticas de Steam sean la fuente de verdad del valor de progreso actual.

Con los logros administrados por el título, su juego es la única fuente de verdad y debe realizar el seguimiento del progreso actual del usuario por su cuenta. La actualización del progreso actual en la nube con una llamada a la API se realiza como una especie de "registro" (check in) y se usa para mostrar el progreso de un usuario en algunas superficies del ecosistema de XBOX. No hay una guía específica sobre dónde debe residir el valor de progreso actual, pero muchos juegos eligen almacenarlo en su archivo de guardado (y en la nube), mientras que otros podrían almacenarlo en su propio servicio back-end. Asegúrese de mantener sincronizados entre sí los valores de la nube y los del sistema interno de administración de estadísticas de su juego.

## Enumeración de todos los logros

Para obtener todas las estadísticas de un usuario con la API de Steamworks, normalmente llamaría a `ISteamUserStats::RequestUserStats` para extraer las estadísticas más recientes del servidor. Una vez disparada la devolución de llamada correspondiente, podría iterar por cada logro mediante su nombre de API usando las funciones `ISteamUserStats::GetStat`/`ISteamUserStats::GetAchievement` e inicializando el estado del juego con los datos que devolvieran.

Con el XBOX Game Development Kit (GDK), puede usar simplemente la función [XblAchievementsGetAchievementsForTitleIdAsync](/reference/live/xsapi-c/achievements_c/achievements_c_members) y, a continuación, usar los datos de cada objeto de logro de la matriz proporcionada como parámetro de salida (Out) de [XblAchievementsResultGetAchievements](/reference/live/xsapi-c/achievements_c/achievements_c_members) para inicializar el estado de su juego. Cada logro se proporciona como una estructura [XblAchievement](/reference/live/xsapi-c/achievements_c/achievements_c_members). Para obtener más información sobre los datos que contiene, consulte [su tema de referencia](/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();
}
```

Para obtener más información sobre los logros administrados por el título, consulte [Obtención de logros administrados por el título](/services/xbox-services/player-data/achievements/index).

## Actualización del progreso de un logro y desbloqueo de logros

En el XBOX Game Development Kit (GDK) y en Steam, el código del juego determina cuándo debe desbloquearse un logro. Con el XBOX Game Development Kit (GDK), establecer un logro con un progreso de 100 lo desbloquea. Las secciones siguientes describen los pasos necesarios para actualizar el progreso de un usuario en un logro especificado con ambas API.

A continuación se muestra un ejemplo de código de un método `UpdateAchievement`, implementado en ambos SDK, que recibe un identificador/nombre de API del logro (y el nombre de API de la estadística en Steam), cuánto incrementar el logro y cuál es el valor objetivo para desbloquear ese logro, y actualiza el progreso y desbloquea el estado del logro en consecuencia.

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

Para obtener más información sobre los logros administrados por el título, consulte [Actualización de logros administrados por el título](/services/xbox-services/player-data/achievements/index).

## Logros sin conexión

Desbloquear un logro de XBOX no requiere una conexión de red. Se desbloquea automáticamente con el servidor más adelante, sin código adicional, si el usuario no está conectado a la red de XBOX (también conocida como XBOX Live).

## Obtención de los logros de otro usuario

Para obtener los logros de otro usuario, puede llamar simplemente a las funciones [XblAchievementsGetAchievementsForTitleAsync](/reference/live/xsapi-c/achievements_c/achievements_c_members)/[XblAchievementsGetAchievementAsync](/reference/live/xsapi-c/achievements_c/achievements_c_members) con el identificador de usuario de XBOX (XUID) del usuario cuyos logros quiere recuperar. Esto es similar a usar las funciones `ISteamUser::RequestUserStats`, `ISteamUserStats::GetUserStat` e `ISteamUserStats::GetUserAchievement` en Steam.

## Restablecimiento de logros

A diferencia de `ISteamUserStats::ResetAllStats` de la API de Steamworks, que puede usarse para restablecer el progreso del usuario en todas sus estadísticas y, si se especifica, en sus logros, el XBOX Game Development Kit (GDK) no tiene ninguna capacidad documentada para restablecer mediante programación el progreso o el estado de desbloqueo de los logros. Intentar llamar a [XblAchievementsUpdateAchievementAsync](/reference/live/xsapi-c/achievements_c/achievements_c_members) con un valor de progreso de 0 deja el valor de progreso sin cambios. Sin embargo, puede usar la [herramienta Player Data Reset (XblPlayerDataReset.exe)](/tools/tools-services/live-player-data-reset) durante las pruebas para lograrlo.

## Límites de frecuencia

Al actualizar el progreso de los logros, tenga cuidado de no llamar a la API de servicios de XBOX (XSAPI) con demasiada frecuencia, porque puede hacer que su juego alcance su límite de frecuencia. Esto puede causar un comportamiento inesperado y una mala experiencia para sus usuarios.

Para los logros que realizan un seguimiento de estadísticas que se actualizan con frecuencia, intente agrupar las llamadas a la API en lotes para que se disparen después de que se produzca un número determinado de eventos, o para que se actualicen en el servidor en un intervalo de tiempo determinado.

Puede depurar sus llamadas a la API mediante el [analizador de seguimiento de los servicios de XBOX (XblTraceAnalyzer.exe)](/tools/tools-services/live-trace-analyzer).

Para obtener más información sobre los límites de frecuencia en los servicios de XBOX, consulte [Limitación de frecuencia específica](/services/xbox-services/develop/best-practices/live-fine-grained-rate-limiting).


## Related topics

- [Configuración de los servicios de XBOX](/es/publishing/game-publishing/concepts/xbox-services.md)
- [Información general sobre los datos de jugador de XBOX](/es/services/xbox-services/player-data/live-playerdata-overview.md)
- [Estadísticas basadas en eventos frente a estadísticas administradas por el título](/es/services/xbox-services/player-data/stats-leaderboards/live-stats-eb-vs-tm.md)
- [Características del GDK que no están en Steamworks](/es/build/steam-porting-guide/gdk-features-not-in-steamworks.md)
- [Logros](/es/services/xbox-services/player-data/achievements/index.md)
