Introdução
A seguir, encontra-se é um guia passo a passo rápido para integrar estatísticas do Steam bem básicas no seu aplicativo em menos de 10 minutos e com menos de 10 linhas de código integradas à sua base de código. O
SDK do Steamworks vem com um ótimo aplicativo de exemplo denominado
Spacewar que demonstra os diversos recursos do Steam e deve ser o primeiro lugar para ver todos os recursos do Steam em ação. Com a finanlidade se ser o mais direto possível, este tutorial detalha apenas as informações encontradas no Spacewar e na API de estatísticas e conquistas necessárias para estatísticas. Por favor, observe que há muita coisa em comum entre estatísticas e conquistas, portanto, se estiver integrando ambas, esteja ciente de que muitas chamadas podem ser consolidadas.
1º passo — Definição das estatísticas do jogo
Estatísticas são separadas por aplicativo e configuradas na página de
configuração de estatísticas no backend de administração de aplicativo do Steamworks. Segue abaixo a lista de estatísticas do aplicativo de exemplo do Steamworks, denominado Spacewar:

2º passo — Encapsulamento do funcionamento de estatísticas
O código a seguir independe do jogo e pode ser adicionado ao seu jogo da forma que achar melhor. A classe é completamente funcional, mas pode ser estendida para atender a necessidades adicionais. O código foi extraído dos arquivos de exemplo do Spacewar
StatsAndAchievements.cpp/h
. Strings literais e comentários foram traduzidos para uma melhor compreensão.
Arquivo de cabeçalho
Primeiro definimos uma estrutura para armazenar os dados de estatística recebidos do Steam e um atalho para criar objetos do tipo. Esses dados são mapeados diretamente aos existentes na página de
configuração de estatísticas.
#define _STAT_ID( id,type,name ) { id, type, name, 0, 0, 0, 0 }
enum EStatTypes
{
STAT_INT = 0,
STAT_FLOAT = 1,
STAT_AVGRATE = 2,
};
struct Stat_t
{
int m_ID;
EStatTypes m_eStatType;
const char *m_pchStatName;
int m_iValue;
float m_flValue;
float m_flAvgNumerator;
float m_flAvgDenominator;
};
Em seguida, definimos uma classe auxiliar que encapsulará todas as chamadas à API de estatísticas do Steam, assim como criará todos os
retornos de chamada do Steam.
class CSteamStats
{
private:
int64 m_iAppID; // Nosso AppID atual
Stat_t *m_pStats; // Dados das estatisticas
int m_iNumStats; // Quantidade de estatisticas
bool m_bInitialized; // Chamamos a funcao RequestStats e recebemos o retorno de chamada?
public:
CSteamStats(Stat_t *Stats, int NumStats);
~CSteamStats();
bool RequestStats();
bool StoreStats();
STEAM_CALLBACK( CSteamStats, OnUserStatsReceived, UserStatsReceived_t,
m_CallbackUserStatsReceived );
STEAM_CALLBACK( CSteamStats, OnUserStatsStored, UserStatsStored_t,
m_CallbackUserStatsStored );
};
Arquivo de código
Construtor
Parâmetros — O construtor recebe um ponteiro para um vetor de estatísticas e o tamanho do mesmo. A formatação do vetor será abordada no código principal do jogo, mais adiante.
Retorna — Nada
O que faz — O construtor inicializa alguns membros e recupera o AppID em execução. Além disso, ele associa os métodos de retorno de chamada para tratar chamadas assíncronas feitas ao Steam. Por último, realiza uma chamada inicial à função
RequestStats()
para recuperar as estatísticas e conquistas do usuário atual.
CSteamStats::CSteamStats(Stat_t *Stats, int NumStats) :
m_iAppID( 0 ),
m_bInitialized( false ),
m_CallbackUserStatsReceived( this, &CSteamStats::OnUserStatsReceived ),
m_CallbackUserStatsStored( this, &CSteamStats::OnUserStatsStored )
{
m_iAppID = SteamUtils()->GetAppID();
m_pStats = Stats;
m_iNumStats = NumStats;
RequestStats();
}
RequestStats()
Parâmetros — Nenhum
Retorna — um bool que representa o sucesso da chamada. Se a chamada falhou, a causa comum é a não inicialização do Steam. Confirme que há uma instância do cliente Steam aberta ao realizar esta chamada e que a função
SteamAPI_Init foi chamada anteriormente.
O que faz — Este método basicamente encapsula uma chamada à função
ISteamUserStats::RequestCurrentStats, que é uma chamada assíncrona ao Steam requisitando as estatísticas do usuário atual. Esta chamada deve ser realizada antes de definir qualquer estatística ou conquista. A chamada inicial a este método é realizada no construtor. É possível chamá-la posteriormente, quando quiser, caso deseje verificar se há estatísticas ou conquistas atualizadas.
bool CSteamStats::RequestStats()
{
// Steam carregado? Caso contrario, nao podemos recuperar estatisticas.
if ( NULL == SteamUserStats() || NULL == SteamUser() )
{
return false;
}
// Usuario com sessao inciada? Caso contrario, nao podemos recuperar estatisticas.
if ( !SteamUser()->BLoggedOn() )
{
return false;
}
// Recuperar estatisticas do usuario.
return SteamUserStats()->RequestCurrentStats();
}
StoreStats()
Parâmetros — Nenhum
Retorna — um bool que representa o sucesso da chamada. Se a chamada falhou, a causa comum é a não inicialização do Steam. Confirme que há uma instância do cliente Steam aberta ao realizar esta chamada e que a função
SteamAPI_Init foi chamada anteriormente.
O que faz — Este método basicamente encapsula uma chamada à função
ISteamUserStats::StoreStats, que é uma chamada assíncrona ao Steam para armazenar as estatísticas atuais do usuário atual no servidor. Deve ser realizada sempre que desejar atualizar as estatísticas do usuário.
bool CSteamStats::StoreStats()
{
if (m_bInitialized)
{
// Carregar estatisticas
for (int iStat = 0; iStat < m_NumStats; ++iStat)
{
Stat_t & stat = m_pStats[iStat];
switch (stat.m_eStatType)
{
case STAT_INT:
SteamUserStats()->SetStat(stat.m_pchStatName, stat.m_iValue);
break;
case STAT_FLOAT:
SteamUserStats()->SetStat(stat.m_pchStatName, stat.m_flValue);
break;
case STAT_AVGRATE:
SteamUserStats()->UpdateAvgRateStat(stat.m_pchStatName, stat.m_flAvgNumerator, stat.m_flAvgDenominator);
// Media calculada pela API
SteamUserStats()->GetStat(stat.m_pchStatName, & stat.m_flValue);
break;
default:
break;
}
}
return SteamUserStats()->StoreStats();
}
}
OnUserStatsReceived()
Parâmetros — Nenhum
Retorna — Nada
O que faz — Este método é um retorno de chamada que é chamado sempre que tenta recuperar estatísticas. Estatísticas são recuperadas por meio da função
RequestStats()
. O método atualiza a variável-membro m_Stats para apontar aos dados de estatísticas mais recentes do Steam.
void CSteamStats::OnUserStatsReceived( UserStatsReceived_t *pCallback )
{
// Podemos receber retornos de chamada de estatisticas de outros jogos, ignore-os
if ( m_iAppID == pCallback->m_nGameID )
{
if ( k_EResultOK == pCallback->m_eResult )
{
OutputDebugString("Recebeu estatisticas e conquistas do Steam\\n");
// Carregar estatísticas
for ( int iStat = 0; iStat < m_iNumStats; ++iStat )
{
Stat_t &stat = m_Stats[iStat];
switch (stat.m_eStatType)
{
case STAT_INT:
SteamUserStats()->GetStat(stat.m_pchStatName, &stat.m_iValue);
break;
case STAT_FLOAT:
case STAT_AVGRATE:
SteamUserStats()->GetStat(stat.m_pchStatName, &stat.m_flValue);
break;
default:
break;
}
}
m_bInitialized = true;
}
else
{
char buffer[128];
_snprintf( buffer, 128, "RequestStats - failed, %d\\n", pCallback->m_eResult );
OutputDebugString( buffer );
}
}
}
OnUserStatsStored()
Parâmetros — Nenhum
Retorna — Nada
O que faz — Este método é um retorno de chamada disparado sempre que tenta armazenar estatísticas no Steam. Se uma ou mais estatísticas definidas quebraram alguma regra, elas serão revertidas aos valores originais, então precisamos recarregá-las.
void CSteamStats::OnUserStatsStored( UserStatsStored_t *pCallback )
{
// Podemos receber retornos de chamada de estatisticas de outros jogos, ignore-os
if ( m_iAppID == pCallback->m_nGameID )
{
if ( k_EResultOK == pCallback->m_eResult )
{
OutputDebugString( "StoreStats - sucesso\\n" );
}
else if ( k_EResultInvalidParam == pCallback->m_eResult )
{
// Uma ou mais estatisticas quebraram uma regra. Elas foram revertidas,
// logo temos que reiterar pelos valores para sincronizar.
OutputDebugString( "StoreStats - algumas falharam na validacao\\n" );
// Forcaremos um retorno de chamada para recarregar os dados.
UserStatsReceived_t callback;
callback.m_eResult = k_EResultOK;
callback.m_nGameID = m_iAppID;
OnUserStatsReceived( &callback );
}
else
{
char buffer[128];
_snprintf( buffer, 128, "StoreStats - falha, %d\\n", pCallback->m_eResult );
OutputDebugString( buffer );
}
}
}
3º passo — Integração com o jogo
A seguir, encontra-se uma listagem completa de trechos de códigos que precisam ser integrados ao jogo nos locais apropriados.
Defines e globais
A seguir, está uma lista de
include
s necessários para a construção com conquistas, um vetor com as estatísticas específicas do jogo e um ponteiro global ao nosso objeto auxiliar. Por favor, observe que as estatísticas devem ter o mesmo nome que o "Nome na API" na página de administração do Steamworks.
...
#include "steam_api.h"
#include "isteamuserstats.h"
#include "SteamStats.h"
// Vetor de estatisticas, para armazenar dados e o estado das estatisticas
Stat_t g_Stats[] =
{
_STAT_ID( 1, STAT_INT, "QtdPartidas"),
_STAT_ID( 2, STAT_INT, "QtdVitorias"),
_STAT_ID( 3, STAT_INT, "QtdDerrotas"),
_STAT_ID( 4, STAT_FLOAT, "DistanciaPercorrida"),
_STAT_ID( 5, STAT_AVGRATE, "VelMedia"),
_STAT_ID( 7, STAT_FLOAT, "MaiorDistanciaPercorrida"),
};
// Acesso global ao objeto auxiliar de conquistas
CSteamStats* g_SteamStats = NULL;
...
Inicialização
Esta chamada à função
SteamAPI_Init inicializará a base do Steam e deve ser chamada antes de qualquer outra função da API. Se for bem-sucedida, então criamos o objeto auxiliar passando o vetor de estatísticas e o tamanho do vetor.
...
// Inicializacao do Steam
bool bRet = SteamAPI_Init();
// Criacao do objeto SteamStats se o Steam foi inicializado com sucesso
if (bRet)
{
g_SteamStats = new CSteamStats(g_Stats, 6);
}
...
Processamento de retornos de chamada
Para garantir o processamento de todos os retornos de chamada do Steam, precisamos sempre verificar se há novas mensagens. Para tal, adicionamos a chamada a seguir ao ciclo de vida do jogo.
...
SteamAPI_RunCallbacks();
...
Armazenamento de estatísticas
Estatísticas são salvas por meio de uma única chamada à função
StoreStats()
.
...
if (g_SteamStats)
g_SteamStats->StoreStats();
...
Encerramento
O seu código já deve ter uma chamada à função
SteamAPI_Shutdown. Ela encerra o Steam e deve ser chamada antes do encerramento do aplicativo. Por último, excluímos o objeto auxiliar criado.
...
// Encerramento do Steam
SteamAPI_Shutdown();
// Exclusao do objeto SteamStats
if (g_SteamStats)
delete g_SteamStats;
...
4º passo — Testes e solução de problemas
Este código de amostra imprime dados de depuração ao console de depuração, facilitando a compreensão de quais chamadas estão funcionando e quais estão falhando. A seguir, apresentamos algumas mensagens de falha comuns, assim como correções:
This application has failed to start because steam_api.dll was not found. Re-installing the application may fix this problem.(
O aplicativo falhou em iniciar porque steam_api.dll não foi encontrado. Reinstalar o aplicativo pode corrigir este problema.)
Confirme que o arquivo steam_api.dll está no mesmo diretório do executável.
[S_API FAIL] SteamAPI_Init() failed; unable to locate a running instance of Steam, or a local steamclient.dll(
[S_API FAIL] SteamAPI_Init() falhou; não foi possível encontrar uma instância local do Steam ou um arquivo steamclient.dll local)
É possível que não haja um cliente Steam em execução. Inicie o Steam e inicie a sessão.
[S_API FAIL] SteamAPI_Init() failed; no appID found.(
[S_API FAIL] SteamAPI_Init() falhou; nenhum AppID encontrado.)
É possível que o arquivo steam_appid.txt não esteja no lugar certo. Coloque-o na pasta de códigos-fonte e certifique-se que ele contém o número do AppID.