Introdução
O presente artigo é um guia passo a passo rápido para integrar estatísticas básicas do Steam na sua aplicação em menos de 10 minutos e com menos de 10 linhas de código integradas na sua base de código. O
SDK do Steamworks vem com uma boa aplicação de exemplo, intitulada
Spacewar, que demonstra toda a gama de funcionalidades do Steam e que deve ser o seu ponto de referência para ver todas as funcionalidades do Steam em ação. Este tutorial tenta ser o mais direto possível ao resumir ao necessário as informações encontradas no Spacewar e na API de estatísticas e proezas para estatísticas do Steam. Tenha em atenção que estatísticas e proezas têm muito em comum. Por isso, se estiver a integrar ambas, esteja ciente que muitas das chamadas poderão ser consolidadas.
Passo 1 – Definição das estatísticas do jogo
As estatísticas são específicas para cada aplicação e configuradas na página de
configuração de estatísticas, situada na página de administração da aplicação no Steamworks. Segue-se abaixo uma lista de estatísticas do Spacewar, a aplicação de exemplo da API do Steamworks:

Passo 2 – Encapsulamento do funcionamento das estatísticas
O seguinte código não depende de nenhum jogo e pode ser adicionado ao seu jogo como pretender. A classe é completamente funcional, mas pode ser expandida facilmente para atender a quaisquer outras necessidades. Este código foi retirado diretamente dos ficheiros de exemplo do Spacewar
StatsAndAchievements.cpp/h
.
Ficheiro de cabeçalho
Primeiro, definimos uma estrutura para armazenar os dados das estatísticas recebidos do Steam, os tipos de estatísticas numa enumeração e um macro para criar objetos desse tipo. Estes dados são mapeados diretamente para os 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;
};
De seguida, definimos uma classe de programa auxiliar, que irá encapsular todas as chamadas à API de estatísticas do Steam assim como criar todos os
callbacks do Steam.
class CSteamStats
{
private:
int64 m_iAppID; // O nosso AppID atual
Stat_t *m_pStats; // Dados das estatísticas
int m_iNumStats; // O número de estatísticas
bool m_bInitialized; // Chamámos RequestStats e recebemos o callback?
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 );
};
Ficheiro de código
Construtor
Parâmetros – O construtor recebe um ponteiro para uma matriz de estatísticas, juntamente com o tamanho da matriz. O formato da matriz será abordado mais adiante no código principal do jogo.
Retorna – Não aplicável
O que faz – O construtor inicializa alguns membros e obtém o AppID que está a ser executado atualmente. Além disso, o construtor associa os métodos de chamada de retorno para gerir chamadas assíncronas realizadas ao Steam. Por último, realiza uma chamada inicial à função
RequestStats()
para obter as estatísticas e as proezas do utilizador 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 valor booleano que representa o resultado da chamada. Se a chamada falhou, é provável que o Steam não tenha sido inicializado. Assegure-se de que tem uma aplicação Steam aberta quando tentar realizar esta chamada e que a função
SteamAPI_Init foi chamada antes disso.
O que faz – Este método basicamente encapsula uma chamada à função
ISteamUserStats::RequestCurrentStats, que é uma chamada assíncrona ao Steam a pedir as estatísticas do utilizador atual. Esta chamada precisa de ser realizada antes de poder definir qualquer estatística ou proeza. A chamada inicial a este método é realizada no construtor. Pode realizar a chamada mais tarde, se quiser verificar estatísticas ou proezas atualizadas.
bool CSteamStats::RequestStats()
{
// O Steam está disponível? Se não estiver, não poderemos obter estatísticas.
if ( NULL == SteamUserStats() || NULL == SteamUser() )
{
return false;
}
// O utilizador está com sessão iniciada? Se não estiver, não poderemos obter estatísticas.
if ( !SteamUser()->BLoggedOn() )
{
return false;
}
// Pedir estatísticas do utilizador.
return SteamUserStats()->RequestCurrentStats();
}
StoreStats()
Parâmetros – Nenhum
Retorna – Um valor booleano que representa o resultado da chamada. Se a chamada falhou, é provável que o Steam não tenha sido inicializado. Assegure-se de que tem uma aplicação Steam aberta quando tentar realizar esta chamada e que a função
SteamAPI_Init foi chamada antes disso.
O que faz – Este método basicamente encapsula uma chamada à função
ISteamUserStats::StoreStats, que é uma chamada assíncrona ao Steam que armazena as estatísticas do utilizador atual no servidor. Esta chamada precisa de ser realizada sempre que quiser atualizar as estatísticas do utilizador.
bool CSteamStats::StoreStats()
{
if ( m_bInitialized )
{
// Carregar estatísticas
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 );
// O resultado médio é calculado por nós
SteamUserStats()->GetStat(stat.m_pchStatName, &stat.m_flValue );
break;
default:
break;
}
}
return SteamUserStats()->StoreStats();
}
}
OnUserStatsReceived()
Parâmetros – Não aplicável
Retorna – Nada a retornar
O que faz – Este método é uma chamada de retorno que é realizada sempre que tentar pedir estatísticas. Estatísticas são pedidas através da função
RequestStats()
. O método atualiza a variável membro m_Stats para refletir os dados de estatísticas mais recentes do Steam.
void CSteamStats::OnUserStatsReceived( UserStatsReceived_t *pCallback )
{
// Podemos receber callbacks de estatísticas de outros jogos, ignore-os
if ( m_iAppID == pCallback->m_nGameID )
{
if ( k_EResultOK == pCallback->m_eResult )
{
OutputDebugString( "Recebeu estatísticas e proezas 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 – Não aplicável
Retorna – Nada a retornar
O que faz – Este método é uma chamada de retorno que é realizada sempre que tentar armazenar estatísticas no Steam. Se uma ou mais estatísticas definidas violarem uma restrição, serão revertidas para os valores originais e terão de ser recarregadas.
void CSteamStats::OnUserStatsStored( UserStatsStored_t *pCallback )
{
// Podemos receber chamadas de retorno de estatísticas de outros jogos. Ignore-os
if ( m_iAppID == pCallback->m_nGameID )
{
if ( k_EResultOK == pCallback->m_eResult )
{
OutputDebugString( "StoreStats - success\n" );
}
else if ( k_EResultInvalidParam == pCallback->m_eResult )
{
// Uma ou mais estatísticas definidas violaram uma restrição. Foram revertidas
// e temos de reiterar pelos valores para garantir a sincronização.
OutputDebugString( "StoreStats - algumas falharam na validação\n" );
// Forçar uma chamada de retorno 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 );
}
}
}
Passo 3 – Integração no jogo
Segue-se uma listagem completa de excertos de código que precisam de ser integrados nos locais apropriados do jogo.
Definições e variáveis globais
Segue uma lista de "inclusões" necessárias para a criação com estatísticas, uma matriz com as estatísticas específicas do jogo e um ponteiro global para o nosso objeto auxiliar. Note que as estatísticas devem ter o mesmo nome presente na página de administração no Steamworks.
...
#include "steam_api.h"
#include "isteamuserstats.h"
#include "SteamStats.h"
// Matriz de estatísticas que armazena os dados e o estado das estatísticas
Stat_t g_Stats[] =
{
_STAT_ID( 1, STAT_INT, "NumGames"),
_STAT_ID( 2, STAT_INT, "NumWins"),
_STAT_ID( 3, STAT_INT, "NumLosses"),
_STAT_ID( 4, STAT_FLOAT, "FeetTraveled"),
_STAT_ID( 5, STAT_AVGRATE, "AverageSpeed"),
_STAT_ID( 7, STAT_FLOAT, "MaxFeetTraveled"),
};
// Acesso global ao objeto de estatísticas
CSteamStats* g_SteamStats = NULL;
...
Inicialização
A chamada à função
SteamAPI_Init inicializa o Steam e deve ser chamada antes de qualquer outra função. Se a chamada for bem-sucedida, criamos o objeto auxiliar ao passar a matriz de estatísticas e o tamanho da matriz.
...
// Inicialização do Steam
bool bRet = SteamAPI_Init();
// Criação do objeto SteamStats se o Steam foi inicializado com sucesso
if (bRet)
{
g_SteamStats = new CSteamStats(g_Stats, 6);
}
...
Processamento de chamadas de retorno
De forma a garantir o processamento de todos as chamadas de retorno do Steam, precisamos de verificar regularmente se há novas mensagens. Para tal, adicionamos a seguinte chamada ao ciclo do jogo.
...
SteamAPI_RunCallbacks();
...
Armazenamento de estatísticas
Estatísticas são armazenadas ao realizar uma única chamada à função
StoreStats()
.
...
if (g_SteamStats)
g_SteamStats->StoreStats();
...
Encerramento
Provavelmente, já deve ter uma chamada à função
SteamAPI_Shutdown no seu código. Esta função encerra o Steam e deve ser chamada antes do encerramento da sua aplicação. Por último, eliminamos o objeto auxiliar que criámos.
...
// Encerramento do Steam
SteamAPI_Shutdown();
// Eliminação do objeto SteamStats
if (g_SteamStats)
delete g_SteamStats;
...
Passo 4 – Testes e resolução de problemas
Este código de amostra envia informações de depuração para a consola de depuração, ajudando assim a compreender quais as chamadas que estão a funcionar ou a falhar. Seguem-se algumas mensagens de erro 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. – "Esta aplicação não pôde ser iniciada porque steam_api.dll não foi encontrado. Voltar a instalar a aplicação pode corrigir o problema."Certifique-se de que o ficheiro steam_api.dll está no diretório do executável.
[S_API FAIL] SteamAPI_Init() failed; unable to locate a running instance of Steam, or a local steamclient.dll – "SteamAPI_Init() falhou; não foi possível encontrar uma instância ativa do Steam nem um ficheiro steamclient.dll local."É possível que não tenha uma aplicação Steam em execução. Inicie o Steam e inicie sessão.
[S_API FAIL] SteamAPI_Init() failed; no appID found. – "SteamAPI_Init() falhou; nenhum AppID encontrado."É provável que não tenha o ficheiro steam_appid.txt no diretório correto. Coloque-o na pasta de origem e certifique-se de que contém o número do seu AppID.