Vista geral
A API do Steamworks permite que o seu jogo tire partido do Steam ao aceder a todos os sistemas oferecidos pela API, como a possibilidade de pausar o jogo quando o utilizador abre o
Painel Steam, convidar amigos para jogar, alcançar
proezas no Steam, competir nas
tabelas de liderança no Steam e muito mais.
O
Guia de referência da API do Steamworks cataloga e documenta cada interface, função, callback e tipo disponibilizados na API.
Não é obrigatório integrar a API do Steamworks para lançar um produto no Steam, mas recomendamos fazê-lo para possibilitar muitas das interações de que os utilizadores do Steam estão à espera.
Primeiros passos
ATENÇÃO: a API do Steamworks foi concebida para uso com C++ e é oficialmente compatível com Microsoft Visual Studio 2008+ no Microsoft Windows e com GCC 4.6+ e Clang 3.0+ no macOS e no SteamOS/Linux. Se estiver a usar um engine de terceiros ou uma linguagem de programação que não seja C++, consulte a secção
Compatibilidade com engines comerciais e linguagens que não sejam C++ para confirmar se existem instruções específicas para o engine ou linguagem que escolheu. Em alguns casos, poderá ignorar muitos dos seguintes passos.
- Se ainda não o fez, descarregue o SDK do Steamworks e extraia o conteúdo.
- Copie a pasta de cabeçalhos da API do Steamworks (
public/steam
) para o diretório-base da sua aplicação.
- Copie os ficheiros redistribuíveis relevantes de
redistributable_bin
para um local apropriado na pasta do projeto.
Inicialização e encerramento
SteamAPI_Init
Depois de configurar a API do Steamworks no seu projeto, pode começar a usá-la ao chamar a função
SteamAPI_Init para iniciar a API. Isto irá configurar o estado global e popular os ponteiros das interfaces, que são acessíveis através das funções globais com o mesmo nome das interfaces. Esta função
TEM de ser chamada e de retornar com sucesso antes de aceder a qualquer uma das
Interfaces do Steamworks!
A API do Steamworks não será inicializada se não souber o AppID do seu jogo. Quando iniciar a aplicação pelo Steam, o AppID será informado automaticamente. Durante o desenvolvimento do jogo, terá de passar manualmente estas informações ao Steam. Crie um ficheiro de texto com apenas o AppID e guarde-o com o nome
steam_appid.txt
na pasta do executável. Este ficheiro terá prioridade sobre o valor fornecido pelo Steam. Não inclua este ficheiro em versões públicas da aplicação. Por exemplo:
480
Se
false for retornado, isso indica uma das seguintes condições:
- A aplicação Steam não está em execução. Para que seja possível a implementação das várias interfaces do Steamworks, uma aplicação Steam terá de estar em execução.
- A aplicação Steam não pôde determinar o AppID do jogo. Se estiver a executar a aplicação diretamente a partir do executável ou do depurador, terá de incluir um ficheiro
steam_appid.txt
na pasta do executável. O ficheiro deve conter apenas o AppID da aplicação. O Steam irá procurar este ficheiro no diretório atual do executável. Se estiver a executar o executável a partir de um diretório diferente, poderá ter de mover o ficheiro steam_appid.txt
.
- A aplicação não está a ser executada no mesmo contexto de utilizador do sistema operativo que a aplicação Steam, como um utilizador ou nível de acesso administrativo diferente.
- Assegure-se de que tem uma licença para o AppID na conta Steam usada no momento. O jogo tem de estar disponível na sua Biblioteca Steam.
- O seu AppID não foi completamente configurado (por exemplo,
Estado de lançamento: Indisponível
ou pode não ter pacotes padrão).
Se estiver a ter problemas de inicialização, consulte o artigo
Depuração da API do Steamworks para saber mais sobre as várias formas de depurar API do Steamworks.
SteamAPI_RestartAppIfNecessary
SteamAPI_RestartAppIfNecessary verifica se o seu executável foi iniciado através do Steam. Caso contrário, o executável voltará a ser iniciado, mas através do Steam.
Esta função é opcional, mas recomendada, já que o contexto do Steam associado à sua aplicação (incluindo o seu AppID) não será configurado se o utilizador iniciar diretamente o executável. Se isto ocorrer,
SteamAPI_Init
irá falhar e não será possível usar a API do Steamworks.
Caso a queira usar, SteamAPI_RestartAppIfNecessary deverá ser a primeira função chamada, mesmo antes de
SteamAPI_Init.
Se
true for retornado, a aplicação Steam será iniciada (se necessário) e o seu jogo será iniciado através do Steam. O processo atual deve ser encerrado assim que possível. Como esta função basicamente executa
steam://run/<AppID>
, poderá não iniciar o mesmo executável que chamou a função (por exemplo, se estiver a executar por um depurador). A inicialização será sempre a partir da versão instalada na pasta da Biblioteca Steam.
Caso contrário, se
false for retornado, isso significa que o jogo foi iniciado pela aplicação Steam e não é necessário realizar nenhuma ação. Uma exceção é se um ficheiro
steam_appid.txt
estiver presente. Nesse caso, será sempre retornado
false. Isto permite-lhe desenvolver e testar sem iniciar o jogo através da aplicação Steam. Não se esqueça de remover o ficheiro
steam_appid.txt
ao enviar o jogo para o depot do Steam!
ATENÇÃO: se usar o wrapper de DRM do Steam no ficheiro executável principal, esta verificação é desnecessária, uma vez que o wrapper de DRM irá realizá-la internamente.
SteamAPI_Shutdown
Quando terminar de usar a API do Steamworks, deve chamar a função
SteamAPI_Shutdown para libertar os recursos usados pela aplicação internamente no Steam. Chame esta função durante o encerramento do processo, se possível.
Esta função não irá remover o hook entre o
Painel Steam e o seu jogo, pois não existe a garantia de que a sua API de renderização terminou de o usar.
Interfaces do Steamworks
A API do Steamworks é composta de várias interfaces que expõem uma quantidade limitada e específica de funcionalidades.
Após o Steamworks ter sido iniciado com sucesso, poderá aceder às interfaces através das respetivas funções globais. As funções terão sempre o mesmo nome que a respetiva interface. Como tal, pode aceder à interface
ISteamApps através do acessor
SteamApps()
e à interface
ISteamFriends através do acessor
SteamFriends()
.
Pode usar estas interfaces para realizar chamadas, como no seguinte exemplo:
const char *name = SteamFriends()->GetPersonaName();
Pode encontrar a lista completa de interfaces no
Guia de referência da API do Steamworks ou ao consultar os ficheiros de cabeçalho da API do Steamworks.
Callbacks
Os callbacks são um dos aspetos mais importantes do Steamworks, pois permitem-lhe obter dados do Steam, de modo assíncrono, sem que o jogo seja interrompido. O objetivo dos callbacks é proporcionar um método simples, leve e seguro a nível de tipo e thread para comunicar eventos assíncronos a qualquer objeto registado como objeto de escuta (listener).
Os callbacks normalmente são desencadeados por um evento que ocorre no Steam, como quando um amigo inicia ou termina sessão, ou pelo resultado assíncrono de algumas funções da API. Cada callback consiste numa struct que contém um identificador único e um pequeno conjunto de dados. Os callbacks são declarados nos ficheiros de cabeçalho
ISteam*.h
, agrupados com a interface mais relacionada.
Para estar à escuta de um callback, uma struct ou classe têm de usar o macro
STEAM_CALLBACK( :classname, :functionname, :callbackname )
na declaração.
-
:classname
refere-se ao nome da struct ou classe onde o "listener" está a ser definido (por exemplo, CGameManager
).
-
:functionname
será o nome da função que recebe este callback (por exemplo, OnGameOverlayActivated
).
-
:callbackname
é o nome do callback (por exemplo, GameOverlayActivated_t
).
Estes passos definem uma função de membro local para a classe, que é prototipada automaticamente como
void :functionname( :callbackname *pCallback )
. Criar uma nova instância do objeto fará com que esta função membro se registe como "listener" na API do Steamworks. Destruir o objeto irá remover o registo.
ATENÇÃO: certifique-se sempre de que o Steam foi inicializado antes de criar objetos que ficam à escuta de callbacks.
Para que os callbacks sejam enviados aos "listeners" registados, chame a função
SteamAPI_RunCallbacks. O ideal é chamar esta função frequentemente, pois quanto mais tempo se passar entre chamadas, maior será a possível latência entre a receção de eventos ou resultados da API do Steamworks. A maioria dos jogos chama a função uma vez por frame renderizada; recomendamos que a chame, pelo menos, uma vez por segundo. Todas as funções "listener" registadas serão invocadas durante esta chamada, no contexto de thread de onde a função
SteamAPI_RunCallbacks
foi chamada.
Exemplo
Um callback bastante comum é
ISteamFriends::GameOverlayActivated_t. Como o nome indica, é enviado um callback sempre que o utilizador ativar ou desativar o
Painel Steam.
class CGameManager
{
private:
STEAM_CALLBACK( CGameManager, OnGameOverlayActivated, GameOverlayActivated_t );
};
void CGameManager::OnGameOverlayActivated( GameOverlayActivated_t* pCallback )
{
if ( pCallback->m_bActive )
printf( "Steam overlay now active\n" );
else
printf( "Steam overlay now inactive\n" );
}
Um caso de uso popular e recomendado para o callback
ISteamFriends::GameOverlayActivated_t é pausar o jogo quando o Painel Steam é aberto.
Resultados de chamada (CallResults)
Muitos métodos do Steamworks usam resultados de chamada em vez de funções callback para retornar resultados, de modo assíncrono, da chamada a uma função. A diferença entre callbacks e resultados de chamada é que callbacks são escutados por todos os "listeners", enquanto os resultados de chamada são destinados a um "listener" específico. Tal como acontece com callbacks, o seu jogo terá de chamar
SteamAPI_RunCallbacks para passar resultados de chamada ao "listener".
Para identificar uma função que retorna um resultado de chamada, inspecione o valor retornado; se for do tipo
SteamAPICall_t e tiver um atributo
CALL_RESULT()
, terá de registar um "listener" para receber o resultado de chamada. Atenção: um callback registado para um CCallResult só irá ocorrer para o
SteamAPICall_t atribuído mais recentemente. Esteja ciente disto se reutilizar um.
ATENÇÃO: callbacks e resultados de chamada não são intercambiáveis. Um evento só resultará num callback ou num resultado de chamada, não em ambos. Certifique-se de que está a registar para o tipo correto de evento!
Resultados de chamada devem ser criados como um membro de uma struct/classe e com o tipo CCallResult. Também terá de criar uma função de membro para receber a função callback.
void func( :callresultname *pCallback, bool bIOFailure );
CCallResult< :classname, :callresultname > m_callresultname;
-
:classname
refere-se ao nome da struct ou classe onde está a definir isto (por exemplo, CGameManager
).
-
:callresultname
refere-se ao nome do resultado de chamada (por exemplo, NumberOfCurrentPlayers_t
).
Pode atribuir o nome que desejar à função, aos parâmetros e ao tipo CCallResult.
Exemplo
Segue-se um exemplo de como usar a API
ISteamUserStats::GetNumberOfCurrentPlayers, que produz um resultado de chamada
ISteamUserStats::NumberOfCurrentPlayers_t.
class CGameManager
{
public:
void GetNumberOfCurrentPlayers();
private:
void OnGetNumberOfCurrentPlayers( NumberOfCurrentPlayers_t *pCallback, bool bIOFailure );
CCallResult< CGameManager, NumberOfCurrentPlayers_t > m_NumberOfCurrentPlayersCallResult;
};
void CGameManager::GetNumberOfCurrentPlayers()
{
printf( "Getting Number of Current Players\n" );
SteamAPICall_t hSteamAPICall = SteamUserStats()->GetNumberOfCurrentPlayers();
m_NumberOfCurrentPlayersCallResult.Set( hSteamAPICall, this, &CGameManager::OnGetNumberOfCurrentPlayers );
}
void CGameManager::OnGetNumberOfCurrentPlayers( NumberOfCurrentPlayers_t *pCallback, bool bIOFailure )
{
if ( bIOFailure || !pCallback->m_bSuccess )
{
printf( "NumberOfCurrentPlayers_t failed!\n" );
return;
}
printf( "Number of players currently playing: %d\n", pCallback->m_cPlayers );
}
ATENÇÃO: se não puder usar o sistema CCallResult, é possível usar
ISteamUtils::IsAPICallCompleted,
ISteamUtils::GetAPICallResult e
ISteamUtils::GetAPICallFailureReason para acompanhar o estado de um resultado de chamada.
Envio manual de callbacks
As classes e os macros usados para registar callbacks são convenientes em código C++. Porém, também existe um mecanismo de nível mais baixo para lidar com callbacks. Este mecanismo funciona de forma semelhante a um loop de eventos do Windows: em vez de ter uma única função que envia todos os callbacks e resultados de chamadas para os "listeners" ativos, terá de obter o próximo callback disponível num loop e de o enviar utilizando os mecanismos que desejar. O modo de envio manual é especialmente útil para uma layer intermediária que expõe o SDK do Steamworks a linguagens que não sejam C++. Verifique a função
SteamAPI_ManualDispatch_Init()
no ficheiro
steam_api.h
para mais informações.
Servidores de jogos do Steam
A API do Steamworks permite a gestão de servidores de jogos, assim como de clientes comuns. Um servidor de jogos, em termos da API do Steamworks, é uma entidade no sistema à qual os utilizadores normais estabelecem ligação para jogar jogos multijogador. A ligação pode ser tanto a um servidor remoto como a um servidor local, no mesmo processo que o cliente. Cada servidor tem um conjunto próprio de funções da API que pode usar, assim como um SteamID único que os outros utilizadores podem referenciar.
Para usar a API de servidores de jogos no Steam, deve incluir o ficheiro de cabeçalho
steam_gameserver.h
em vez de
steam_api.h
.
A inicialização e utilização da API de servidores de jogos é muito semelhante às da API normal:
Depois de iniciar um servidor, terá acesso a duas interfaces exclusivas:
ISteamGameServer e
ISteamGameServerStats.
Também pode aceder às seguintes interfaces comuns a partir do servidor:
- ISteamClient, acessível através da interface global:
SteamGameServerClient()
- ISteamUtils, acessível através da interface global:
SteamGameServerUtils()
- ISteamNetworking, acessível através da interface global:
SteamGameServerNetworking()
- ISteamHTTP, acessível através da interface global:
SteamGameServerHTTP()
- ISteamUGC, acessível através da interface global:
SteamGameServerUGC()
- ISteamApps, acessível através da interface global:
SteamGameServerApps()
Se tiver um servidor de jogo dedicado (sem componente de cliente), só terá de iniciar as APIs de servidores de jogos. Não é necessário iniciar a API para utilizadores comuns.
Consulte o artigo
Aplicação de exemplo da API do Steamworks (Spacewar) para um exemplo detalhado de como usar a API de servidores de jogos.
Ao contrário de jogos, um servidor dedicado é normalmente executado num ambiente sem uma aplicação Steam instalada para fornecer os binários mais recentes do Steamworks. Para que o servidor dedicado os tenha, terá de incluir os redistribuíveis de servidor dedicado na sua aplicação. Inicie sessão em
partner.steamgames.com e aceda às definições técnicas da aplicação. De seguida, no separador "Instalação", clique em "Redistribuíveis" e assinale a caixa "Dedicated Server Redistributables".
Compatibilidade com engines comerciais e linguagens que não sejam C++
Se estiver a usar um engine de jogos comercial ou uma linguagem de programação que não seja C ou C++, recomendamos que se informe acerca do nível de compatibilidade com a API do Steamworks.
Alguns engines já têm compatibilidade nativa, mas para outros poderá precisar de uma solução de terceiros.
Caso o seu engine não tenha compatibilidade nativa, pode usar a
Web API do Steam para aceder a muitas das funcionalidades do Steam.
Independentemente de como o SDK do Steamworks foi implementado no seu engine, irá precisar da versão mais recente do SDK do Steamworks de forma a
enviar a sua aplicação para o Steam.
ATENÇÃO: se alguma parte do software que pretende lançar no Steam estiver disponível sob uma licença open source restritiva, consulte
Distribuição de aplicações open source no Steam.
Seguem-se abaixo alguns engines usados por muitos jogos no Steam e a documentação relevante sobre como utilizar o SDK do Steamworks com esses engines.
ATENÇÃO: a Valve não apoia oficialmente estes engines nem as soluções de terceiros. A seguinte lista está organizada por ordem alfabética, não é completa e deve servir apenas como um guia. Engines só serão listados se tiverem compatibilidade nativa ou se existir uma solução de terceiros que se enquadre nas diretrizes. Soluções de terceiros só serão listadas se forem razoavelmente atualizadas com o SDK do Steamworks, estiverem disponíveis de forma gratuita sob uma licença permissiva (consulte:
Distribuição de aplicações open source no Steam) e tiverem um tópico no
fórum de discussão do Steamworks. Recomendamos que consulte a comunidade para descobrir a opção que poderá funcionar melhor para o seu caso específico.
Engines:
Linguagens de programação:
- ActionScript (Adobe Flash, AIR)
- C#
- D
- Java
- JavaScript
- Python
- Rust
Interface plana como intermediária com outras linguagens
O SDK tem algumas funcionalidades para facilitar a criação de camadas intermediárias com outras linguagens.
-
steam_api_flat.h
declara um conjunto de funções "planas" que correspondem às funções de interface do SDK. Não contém código C puro, mas usa as convenções de associação e chamada dessa linguagem, o que facilita a interoperabilidade com outras linguagens. Estas funções são exportadas por steam_api[64][.dll/.so/dylib]
.
-
steam_api.json
descreve (quase todas) as interfaces, tipos e funções no SDK. Pretende-se que este ficheiro seja usado por um processo automatizado para gerar a camada intermediária. Esperamos que, com este ficheiro, 95% do trabalho possa ser automatizado, mas ainda existem alguns casos especiais que têm de ser geridos manualmente. Em particular, é provável que os tipos CSteamID e CGameID precisem de uma gestão especial por parte da sua camada intermediária para que sejam eficazes.
Detalhes técnicos
O Steam usa várias técnicas para expor funcionalidades à sua aplicação. Não é crucial compreender exatamente como a API do Steamworks funciona, mas não é difícil e pode ser útil para decidir como programar o seu jogo com a integração do Steam em mente.
Ao associar o seu jogo a
steam_api[64][.dll/.so/dylib]
, terá acesso a um
pequeno conjunto de funções C, prefixadas com o macro
S_API
. Estas funções são todas expostas nos ficheiros de cabeçalho
steam_api.h
e
steam_gameserver.h
. O módulo
steam_api
em si é muito pequeno e apenas expõe as funcionalidades base para iniciar e encerrar a API do Steamworks.
A API do Steamworks, quando inicializada, identifica o processo da aplicação Steam em execução e carrega
steamclient.dll
a partir do caminho desse processo. O ficheiro
steamclient.dll
é essencialmente o pilar da aplicação Steam, que contém e mantém as informações necessárias para a execução do Steam. A interface da aplicação Steam utiliza um conjunto semelhante de funções para permitir o acesso aos dados fornecidos por
steamclient.dll
. Como estes dados estão no processo do Steam, todas as chamadas da API do Steam são organizadas e enviadas automaticamente por um mecanismo RPC/IPC (um pipe interprocessos,
ISteamClient::HSteamPipe).
O processo do Steam, via
steamclient.dll
, mantém uma ligação constante aos servidores de back‑end do Steam. É através desta ligação que ocorrem todas as comunicações relacionadas com autenticação, matchmaking, listas de amigos e o sistema VAC. A ligação pode ser perdida se o utilizador tiver um problema na rede ou se o servidor do Steam ao qual estiver ligado receber uma atualização. Callbacks serão enviados para todas as aplicações em execução se a ligação for perdida ou restabelecida. A aplicação Steam tentará restabelecer a ligação automaticamente (aplicações Steam que estejam a executar uma aplicação terão prioridade; este processo costuma demora menos de 10 segundos, embora possa chegar a até 5 minutos).
O versionamento da API do Steamworks é realizado através de um mecanismo do tipo COM, no qual strings com o nome e a versão de uma interface são passadas para
steamclient.dll
, que depois retorna a versão correta da interface ao autor da chamada. O ficheiro
steamclient.dll
contém um conjunto de adaptadores para todas as versões lançadas de uma interface, que encaminham ou reinterpretam as chamadas antigas no contexto da interface mais recente. Os adaptadores encontram-se dentro da aplicação Steam para que sejam fáceis de atualizar na eventualidade de qualquer problema.