Steamworks

Documentação do Steamworks
Vista geral da API do Steamworks

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.

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:
// Obtém o nome do utilizador do Steam atual. 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.
// Na definição de classe class CGameManager { public: void GetNumberOfCurrentPlayers(); private: void OnGetNumberOfCurrentPlayers( NumberOfCurrentPlayers_t *pCallback, bool bIOFailure ); CCallResult< CGameManager, NumberOfCurrentPlayers_t > m_NumberOfCurrentPlayersCallResult; }; // Fazer pedido assíncrono para receber o número atual de jogadores. void CGameManager::GetNumberOfCurrentPlayers() { printf( "Getting Number of Current Players\n" ); SteamAPICall_t hSteamAPICall = SteamUserStats()->GetNumberOfCurrentPlayers(); m_NumberOfCurrentPlayersCallResult.Set( hSteamAPICall, this, &CGameManager::OnGetNumberOfCurrentPlayers ); } // Chamado quando SteamUserStats()->GetNumberOfCurrentPlayers() retornar de forma assíncrona, após uma chamada à função SteamAPI_RunCallbacks(). 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:

EngineCompatibilidade nativa?Informações
CRYENGINE ✔️
GameMaker Studio 2 ✔️ Utilização do SDK do Steamworks com o GameMaker: Studio
Godot Página externa: GodotSteam | Tópico nos fóruns de discussão de developers Steamworks
Haxe Página externa: SteamWrap | Tópico nos fóruns de discussão de developers Steamworks
Leadwerks Game Engine ✔️ Guia de referência da API do Leadwerks › Steamworks
RPG Maker MV Página externa: Greenworks | Tópico nos fóruns de discussão de developers Steamworks
Source 2013 ✔️ Distribuição de jogos e mods que usam o engine Source
Unity Página externa: Facepunch.Steamworks - Tópico nos fóruns de discussão de developers Steamworks
Página externa: http://steamworks.github.io - Tópico nos fóruns de discussão de developers Steamworks
Unreal Engine 4 ✔️ Online Subsystem Steam
Visionaire Studio ✔️ Publicar no Steam

Linguagens de programação:

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.