Pular para o conteúdo principal
Desenvolvedores Game SDK

Mostre o que alguém está jogando

Deixe o seu jogo contar ao mssgs o que o jogador está fazendo. Os amigos veem "Playing" embaixo do nome dele, abrem os detalhes e apertam Join now para entrar no mesmo jogo. O seu jogo também pode verificar se um jogador está na sua comunidade.

O que você pode fazer

  • Publique um status de jogoO jogo, o que o jogador está fazendo, o papel dele e quão cheio está o grupo.
  • Adicione um botão Join nowOs amigos entram no mesmo jogo, servidor ou lobby com um toque.
  • Verifique a participaçãoPergunte se um jogador está na sua comunidade e com quais cargos.
  • Desktop, navegador ou celularJogos nativos usam a ponte local; jogos no navegador e no celular passam pelo seu backend.

No app

daniPlaying Space Raiders
Space RaidersPlayed by dani
Sector 7
Numa raid
3 of 4 in the party · for 12 min
Artilheiro

O status embaixo de um nome e os detalhes que ele abre. O jogo publicou um bloco JSON; o resto é o app.

Visão geral

O app para computador do mssgs roda uma pequena ponte HTTP local com a qual um jogo na mesma máquina conversa. O seu jogo nunca fala com os nossos servidores, nunca vê a senha ou o token de uma conta e nunca pode publicar como o jogador. Ele conversa com a cópia do mssgs em que o jogador já está conectado, e essa cópia decide o que responder.

O que você pode fazer com ela:

  • Detectar que o mssgs está instalado e que alguém está conectado.
  • Ler quem é o jogador: user_guid, username, avatar.
  • Perguntar "este jogador está na comunidade X?" e qual cargo ele tem lá.
  • Publicar um status "Playing …" com um botão Join now para os outros.
  • Receber um repasse de entrada quando alguém aperta esse botão.

Dois caminhos

Um jogo nativo de desktop conversa com a ponte local; é isso que as próximas seções descrevem. Um jogo no navegador ou no celular não consegue alcançar essa ponte. Nesses casos, é o seu próprio backend que publica, para os jogadores que vincularam a conta do mssgs por QR code ou por um código de oito caracteres: veja Jogos de navegador e celular, com o CozyCity como primeiro exemplo. O status de jogo em si é o mesmo bloco nos dois casos.

Exposição mínima, por padrão

Os scopes são desiguais de propósito. Se tudo o que você precisa saber é "esta pessoa está na nossa comunidade", você pede membership.query e informa você mesmo o server_guid: recebe sim/não mais os cargos dela ali e não fica sabendo nada sobre o resto das comunidades dela. A lista completa fica atrás de um scope separado e mais alto, que o jogador precisa aprovar à parte.

Encontrando o cliente

A ponte escuta só em 127.0.0.1, na primeira porta livre de uma faixa pequena. Tente-as em ordem até uma responder: 7440, 7441, 7442, 7443. As builds de desenvolvimento do mssgs escutam em 7540–7543, para que uma build de teste nunca responda às chamadas de um jogo real.

GET http://127.0.0.1:7440/mssgs/v1/hello

Não precisa de token, e a resposta não diz nada sobre o jogador, só que o mssgs está ali e se alguém está conectado.

Resposta
{
  "product": "mssgs",
  "api": 1,
  "client": "desktop",
  "version": "14.2.20015",
  "platform": "darwin",
  "signed_in": true,
  "scopes": ["identity", "staff", "membership.query", "servers.list", "presence.write"]
}

Verifique product === "mssgs" e api antes de seguir. Se nenhuma das quatro portas responder, o mssgs não está rodando. Simplesmente ofereça a experiência normal do jogo em vez de deixar o jogador esperando.

Pedindo permissão

Tudo, exceto /hello, precisa de um token, e um token só existe depois que o jogador aprovou o seu jogo numa janela dentro do app. Peça só os scopes que você realmente usa: o jogador vê cada um separadamente, com explicação, e pode desmarcá-los um a um.

1. Pedir permissão
curl -X POST http://127.0.0.1:7440/mssgs/v1/authorize \
  -H "Content-Type: application/json" \
  -d '{
    "game_id": "com.acme.spacegame",
    "name": "Space Raiders",
    "scopes": ["identity", "membership.query", "presence.write"]
  }'

Você recebe de volta {"status":"pending","request_id":"…","poll_after_ms":1000} e o jogador vê a janela. Depois, consulte até ele responder (o pedido expira depois de 3 minutos):

2. Consultar a resposta
curl http://127.0.0.1:7440/mssgs/v1/authorize/<request_id>

# {"status":"approved","token":"…","scopes":["identity","presence.write"],"game_id":"com.acme.spacegame"}

Sempre verifique o que você recebeu de fato

Os scopes da resposta podem ser menos do que você pediu: o jogador é livre para desmarcar alguns. No exemplo acima, membership.query foi recusado. Decida com base no que a resposta diz, não no que você pediu, senão você vai dar de cara com um 403 MISSING_SCOPE que não estava nos seus planos.

Guarde o token e envie-o como Authorization: Bearer <token>. Ele sobrevive a reinicializações, então o jogador aprova o seu jogo uma vez, e não a cada sessão. Se mais tarde você autorizar de novo com scopes que já foram concedidos, recebe o mesmo token na hora, sem janela.

Scopes e privacidade

Os cinco scopes revelam quantidades de dados bem diferentes. Isso não é por acaso; é o próprio design. Peça o mínimo possível, descendo por esta tabela.

Scope O que permite Do que o jogador abre mão
presence.write Mostrar o que está jogando Nada. Este scope só escreve; ele não lê nenhum dado da conta.
identity Quem é o jogador user_guid, username, nome de exibição, URL do avatar.
staff Flags de equipe / moderador Dois booleanos, além de identity. Separado porque um jogo que mostra um nome não tem por que saber que o jogador modera comunidades.
membership.query Verificar uma comunidade que você já conhece Para um server_guid que você informa: sim/não, o nome dela e os cargos que o jogador tem ali. Nada sobre nenhuma outra comunidade.
servers.list Todas as comunidades em que ele está A lista completa: guids, nomes, ícones e cargos. Este é o caro: peça só se realmente precisar.

A maioria dos jogos precisa de dois

identity e presence.write cobrem "quem é você" e "mostre o que você está jogando", que é quase toda integração. Adicione membership.query se quiser vincular uma recompensa à participação na sua comunidade. Você quase nunca precisa de servers.list, e o jogador o vê destacado em vermelho.

Verificando a participação

Esta é a alternativa a "me dá a lista inteira". Você informa o server_guid da sua própria comunidade (que você já conhece) e recebe uma resposta só sobre ela.

Uma comunidade
curl -H "Authorization: Bearer $TOKEN" \
  "http://127.0.0.1:7440/mssgs/v1/membership?server_guid=ca94ecc…f4g02"

# um membro:
# {"server_guid":"ca94ecc…","member":true,"name":"Acme Fans","is_owner":false,
#  "roles":[{"guid":"0aa32…","name":"Pro"}]}

# não é membro, e nada mais:
# {"server_guid":"…","member":false}

Um "não" é exatamente isso e nada mais. Você pode passar até 10 guids por chamada (repita server_guid ou separe por vírgulas), o que retorna um array results. O grupo @everyone nunca aparece em roles: ele vale para todo membro, então não diz nada.

Publicando um status de jogo

Um único PUT coloca a linha "Playing …" embaixo do nome do jogador, em todo lugar em que as comunidades dele o veem.

PUT /mssgs/v1/activity
{
  "name":    "Space Raiders",
  "details": "Sector 7",
  "state":   "In a raid",
  "role":    "Gunner",
  "started_at": 1755859200000,
  "party":   { "size": 3, "max": 4, "kind": "party" },
  "join":    { "secret": "raid-42" }
}

Só name é obrigatório. A resposta diz quanto tempo o status vive e com que frequência enviar o heartbeat:

Resposta
{ "ok": true, "expires_in_ms": 90000, "heartbeat_every_ms": 30000 }

Heartbeat, ou o status some

Um status sem sinal de vida por 90 segundos é removido automaticamente. É de propósito: se o seu jogo travar, o jogador não fica "jogando" por horas. Envie um POST /mssgs/v1/activity/heartbeat a cada 30 segundos e um DELETE /mssgs/v1/activity num encerramento normal.

Número de jogadores e papel

party.kind decide qual frase é exibida, porque os mesmos dois números não significam a mesma coisa. Um esquadrão de quatro não é um servidor com quatro jogadores.

kind Aparece como Para
party (padrão)3 of 4 in the partyum esquadrão, uma equipe ou um grupo
server4/100 playersum servidor de jogo (FiveM, um servidor da comunidade)
lobby4/100 playersum lobby antes de a partida começar
match4/100 playersuma partida ou rodada em andamento

role (até 48 caracteres) é o que o jogador está jogando como: uma profissão, classe ou personagem. Ele tem um campo próprio em vez de mais uma frase em state, porque aparece como um rótulo ao lado do número de jogadores.

details e state têm limite de 128 caracteres cada, name de 64. Quebras de linha e caracteres de controle são removidos. Uma URL de ícone não é suportada, de propósito: ela seria buscada por todo cliente que exibe a linha, o que transformaria um status num rastreador que informa ao seu servidor cada membro de cada comunidade em que o jogador está.

O botão Join now

Coloque um bloco join na sua atividade e os outros membros ganham um botão Join now ao lado do status. Há duas formas, e você pode combiná-las.

1. Um segredo (para jogos nativos)

Defina {"join":{"secret":"raid-42"}}. Quando alguém aperta Join now, esse segredo é entregue à cópia dessa pessoa do seu jogo, na máquina dela, identificada pelo mesmo game_id. Nenhuma URL é aberta e nenhum handler de esquema é acionado. O seu jogo o recebe com:

GET /mssgs/v1/events
{
  "events": [
    { "seq": 1, "type": "join", "secret": "raid-42",
      "from": { "user_guid": "62e377…", "username": "mssgs-test-1" } }
  ],
  "cursor": 1
}

Consulte com ?since=<cursor> para ver cada evento uma única vez. Se o jogo de quem apertou não estiver rodando, nada é entregue, o que é um bom motivo para oferecer também uma URL.

2. Uma URL https (para jogos web e links de lobby)

Defina {"join":{"url":"https://play.example.com/s/abc"}} e o botão abre esse link. Só https é aceito. Um esquema personalizado (steam://, mygame://, file://) é recusado: esse bloco aparece na tela de todos os membros, e uma URL assim é uma forma de fazer a máquina de outra pessoa acionar um handler local com argumentos escolhidos por você.

Tudo em join é público

O bloco join é transmitido para todos que podem ver o status do jogador; esse é o propósito de um botão Join now. Então trate-o como um código de lobby, não como uma credencial. Nunca coloque nele nada que precise continuar secreto, e faça seus códigos expirarem.

FiveM

O FiveM não tem HTTP no runtime Lua do lado do cliente, então um resource conversa com a ponte através da NUI, uma view CEF, que envia um Origin. A ponte aceita essas origens explicitamente: https://cfx-nui-<resource> e a mais antiga nui://<resource>. Páginas web comuns continuam recusadas, e uma página na web aberta não pode alegar essa origem; é o próprio navegador que a define.

client.lua: pedir à NUI que publique
-- A página NUI faz o HTTP; o Lua só envia os dados para ela.
CreateThread(function()
  while true do
    SendNUIMessage({
      action  = 'mssgs:publish',
      players = GetActivePlayers and #GetActivePlayers() or 0,
      maxPlayers = GetConvarInt('sv_maxclients', 100),
      job     = exports['qb-core'] and 'Police' or nil
    })
    Wait(30000) -- heartbeat: o status expira depois de 90s
  end
end)
nui.js
const BASE = 'http://127.0.0.1:7440/mssgs/v1';   // tente 7440-7443
let token = null;

async function authorize () {
  const res = await fetch(`${BASE}/authorize`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      game_id: 'fivem.lossantos.rp',
      name: 'Los Santos Roleplay',
      scopes: ['presence.write']          // não é preciso mais nada aqui
    })
  });
  const started = await res.json();
  if (started.status === 'approved') { return started.token; }

  // Agora o jogador vê a janela de permissão no mssgs.
  for (let i = 0; i < 180; i += 1) {
    await new Promise((r) => { setTimeout(r, 1000); });
    const poll = await (await fetch(`${BASE}/authorize/${started.request_id}`)).json();
    if (poll.status === 'approved') { return poll.token; }
    if (poll.status !== 'pending') { return null; }
  }
  return null;
}

window.addEventListener('message', async (event) => {
  if (event.data.action !== 'mssgs:publish') { return; }
  if (!token) { token = await authorize(); }
  if (!token) { return; }

  await fetch(`${BASE}/activity`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
    body: JSON.stringify({
      name: 'FiveM',
      details: 'Los Santos Roleplay',
      role: event.data.job,                       // "Police"
      party: { size: event.data.players, max: event.data.maxPlayers, kind: 'server' },
      join: { url: 'https://cfx.re/join/abc123' } // o seu link cfx.re
    })
  });
});

O resultado: Playing FiveM · Los Santos Roleplay · 4/100 players · Police, com um botão Join now que abre o seu link cfx.re.

Peça só presence.write

Um status de jogo não precisa de mais nada: esse scope não lê absolutamente nada. Se quiser vincular uma recompensa no jogo à participação na sua comunidade do mssgs, adicione membership.query e informe o seu próprio server_guid; mesmo assim você não fica sabendo nada sobre as outras comunidades do jogador.

Um servidor em que você joga não é confiável automaticamente

Qualquer servidor FiveM pode rodar resources no cliente, então qualquer servidor em que alguém entra pode pedir permissão. É exatamente por isso que há uma janela no meio, com o nome do resource: quem decide é o jogador, não o servidor.

Jogos de navegador e celular: vinculando pelo seu backend

Um jogo numa aba do navegador ou num celular não consegue alcançar a ponte descrita acima. Ela roda no desktop do jogador, e há três barreiras no caminho: a ponte recusa toda requisição que traz um Origin de navegador, o Chrome coloca um pedido de permissão na frente de uma página pública que acessa 127.0.0.1 e o Safari recusa de vez, e um celular não tem nenhum caminho até o loopback de um desktop.

Então a direção se inverte. O seu próprio backend já sabe quem está jogando, e é ele que avisa o mssgs, para os jogadores que vincularam a conta do mssgs ao seu jogo. O vínculo é aprovado no app do mssgs, nunca no seu jogo, e ele cria um vínculo, nunca uma sessão: nada abaixo pode conectar ninguém nem agir como o jogador. O cliente do seu jogo nunca vê uma chave e nunca fala com mss.gs. O primeiro jogo nesse caminho é o CozyCity, um jogo de construir cidades que é lançado como página WebGL e como app de iPhone, sem versão de desktop; os exemplos abaixo são dele.

1. Registre o seu jogo

Registre o jogo na página de registro do Game SDK: o seu game_id (por exemplo com.deverence.cozycity), o nome e o ícone que o jogador vê na tela de aprovação e os hostnames do seu backend. Nós o analisamos ali e, depois de aprovado, a sua chave de backend fica esperando na mesma página, mostrada uma única vez; nós guardamos só um digest. A chave pertence ao seu servidor e a nenhum outro lugar. Você pode fazer a rotação dela ali a qualquer momento, e a antiga continua válida por 24 horas para que um deploy possa ser concluído.

Registre o seu jogo

O nome e o ícone na tela sempre vêm do registro, nunca da requisição. Do contrário, um link de phishing poderia disfarçar um pedido de vínculo como qualquer jogo que quisesse. Os hostnames limitam para onde um join.url pode apontar, veja abaixo.

2. Vincule um jogador

O jogador escolhe Connect mssgs no seu jogo. O seu jogo pergunta ao seu backend, e o seu backend pergunta a nós:

POST /game-sdk/v1/link/start
curl -X POST https://ams1-gateway.mss.gs/game-sdk/v1/link/start \
  -H "Authorization: Bearer $BACKEND_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "player_ref": "player-8812", "player_name": "René\u2019s city" }'

# {"link_code":"K7PQ2XM4","device_code":"…","qr_url":"https://mss.gs/gl/K7PQ2XM4",
#  "deep_link":"mssgs://link-game/K7PQ2XM4","expires_in":600,"interval":5}

player_ref é o seu próprio id estável para aquele jogador (até 128 caracteres), não para uma sessão ou uma partida; player_name (até 64) é o que a tela mostra como "Player: …". Entregue ao cliente do seu jogo só link_code, qr_url e deep_link. device_code é o seu identificador de polling e fica no servidor.

O seu jogo então mostra três coisas ao mesmo tempo, porque o jogador pode estar em qualquer lugar:

  • O QR code de qr_url. Um celular com o mssgs o abre direto na tela de aprovação do app. Sem o app, ele cai numa página em mss.gs que mostra o código e oferece o download.
  • Um botão "Open in mssgs" com deep_link, para um navegador de desktop que está ao lado do app para computador. É a única URL externa que o seu jogo precisa abrir.
  • O próprio código, em dois grupos de quatro, para digitar em Settings → Game Activity → Link a game. O alfabeto não tem 0/O nem 1/I, então é raro errar na digitação.

O que o jogador vê no mssgs, desenhado pelo app a partir do registro:

Connect CozyCity to your mssgs account?

CozyCity will be able to show what you are playing as your mssgs status. It will not see your messages, your friends or your servers, and it cannot post as you.
Player: René's city · Connect / Not now

Enquanto isso, o seu backend consulta a cada interval segundos (mais rápido que isso recebe 429 SLOW_DOWN) até o status mudar. Um código funciona uma vez e expira depois de dez minutos:

POST /game-sdk/v1/link/poll
curl -X POST https://ams1-gateway.mss.gs/game-sdk/v1/link/poll \
  -H "Authorization: Bearer $BACKEND_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "device_code": "…" }'

# {"status":"pending"}
# {"status":"denied"}      # o jogador escolheu Not now
# {"status":"expired"}
# {"status":"linked","link_guid":"…","user":{"user_guid":"62e377…"}}

Guarde o link_guid junto do seu jogador; de agora em diante, é o endereço para o qual você publica. A resposta traz só o user_guid; um username só é adicionado quando o seu registro tem o scope identity.link, e não há nada além disso. Uma segunda aprovação para o mesmo player_ref substitui o vínculo anterior, então um jogador do seu jogo é uma conta do mssgs. A mesma conta do mssgs pode se vincular a vários jogos e a vários player_refs de um mesmo jogo (um iPad da família).

3. Publique o status de jogo

O mesmo bloco da ponte, com as mesmas regras e os mesmos limites, só que agora por vínculo e com a sua chave de backend:

PUT /game-sdk/v1/links/{link_guid}/activity
{
  "activity": {
    "name":       "CozyCity",
    "details":    "Lantern Hollow",
    "state":      "Day 12 · 34 residents",
    "started_at": 1788901000000,
    "party":      { "size": 6, "max": 40, "kind": "server" },
    "join":       { "url": "https://cozycity.net/game/?share=…" }
  }
}
Respostas
200 {"published":true,"changed":true}     # o bloco mudou e foi transmitido
200 {"published":true,"changed":false}    # idêntico ao que estava guardado; só o TTL foi renovado
204                                        # guardado, mas o jogador não está online no mssgs agora
410 {"error":"LINK_REVOKED"}               # o jogador desconectou: descarte o vínculo

Trate 200 e 204 da mesma forma: guardado. { "activity": null } limpa o bloco; envie isso quando o jogador sair. Uma diferença em relação à ponte: o host de join.url precisa ser um dos seus backends registrados (ou um subdomínio de um deles), senão você recebe 400 INVALID_PAYLOAD. Assim, um backend não consegue colocar no status de um jogador um botão Join now que leva a um lugar onde esse jogador nunca jogou.

Heartbeat a cada 60 segundos, TTL 120

Um status publicado vive 120 segundos sem uma nova mensagem e depois cai sozinho. Então reenvie o mesmo bloco a cada 60 segundos; um bloco sem mudanças não custa nada e só renova o TTL. Se o seu heartbeat parar, a linha "Playing …" some, e é exatamente essa a ideia.

Com centenas de jogadores online, envie o heartbeat numa só chamada, até 100 itens por vez. Cada item recebe o seu próprio status, então um jogador que desconectou no mssgs nunca trava os outros noventa e nove:

POST /game-sdk/v1/activity/batch
{ "items": [ { "link_guid": "…", "activity": { "name": "CozyCity", "details": "Lantern Hollow" } },
             { "link_guid": "…", "activity": null } ] }

// → 200 { "results": [ { "link_guid": "…", "status": 200, "changed": false },
//                      { "link_guid": "…", "status": 410 } ] }

Como o status aparece

  • Idêntico a um status da ponte: Playing CozyCity · Lantern Hollow · 6/40 players, com Join now quando há um join.url. O bloco é marcado com via: "backend" no servidor, então um cliente pode adicionar "Shared by the game's server".
  • Só enquanto o jogador estiver online no mssgs. Sem nenhum cliente do mssgs aberto, a conta fica offline e continua offline; o seu backend não consegue fazer alguém parecer presente. Isso também impede que este caminho vire um sinalizador de "o René está no computador".
  • Precedência: um jogo dentro do app > um jogo na ponte > o seu backend. Se o jogador senta para jogar xadrez dentro do mssgs enquanto o seu backend continua enviando heartbeats, o xadrez vence, não quem escreveu por último.

Desconectando

O jogador vê todos os vínculos em Settings → Game Activity → Linked games, com o seu ícone e nome, o nome de jogador do seu jogo, quando foi vinculado e quando publicou pela última vez, e um botão Disconnect. Depois disso, a sua próxima publicação responde 410 LINK_REVOKED; é assim que o seu jogo fica sabendo. Descarte o link_guid e ofereça "Connect mssgs" de novo. Do seu lado, encerre um vínculo com DELETE /game-sdk/v1/links/{link_guid}.

Limites

Por vínculo, uma mudança conta no máximo a cada 2 segundos; um heartbeat sem mudanças é grátis. Por chave, são 600 requisições por minuto, com os itens de um batch contados individualmente: enviar heartbeat para 300 jogadores a cada 60 segundos gasta 5 das 600.

Referência de endpoints: a ponte

URL base http://127.0.0.1:<port>. Tudo, exceto as três primeiras, exige Authorization: Bearer <token>.

Método Caminho Scope O que faz
GET /mssgs/v1/hello nenhum Se o mssgs está aqui, que API ele fala e se alguém está conectado. A única rota que não precisa de token, e não diz nada sobre o jogador.
POST /mssgs/v1/authorize nenhum Pede permissão ao jogador. Abre uma janela no app e retorna um request_id para consultar.
GET /mssgs/v1/authorize/:request_id nenhum pending, approved (com o token), denied ou expired.
GET /mssgs/v1/me identity O jogador conectado. Adiciona is_staff / is_moderator só com o scope staff.
GET /mssgs/v1/membership membership.query Participação nos valores de server_guid que você passar (até 10, repetidos ou separados por vírgula).
GET /mssgs/v1/servers servers.list Todas as comunidades em que o jogador está, com os cargos dele. Mensagens diretas nunca são incluídas.
PUT /mssgs/v1/activity presence.write Publica o bloco "Playing …". Retorna o TTL e com que frequência enviar heartbeat.
POST /mssgs/v1/activity/heartbeat presence.write Mantém a atividade publicada viva sem reenviá-la.
DELETE /mssgs/v1/activity presence.write Limpa na hora, para um encerramento normal.
GET /mssgs/v1/events presence.write Repasses de entrada destinados ao seu jogo. Consulte com ?since=<cursor>.
GET /mssgs/v1/session nenhum O que este token tem: game_id, scopes concedidos, se alguém está conectado.
DELETE /mssgs/v1/session nenhum Devolve a permissão. Mesmo efeito que o jogador revogá-la em Settings.

Referência de endpoints: backends vinculados

URL base https://ams1-gateway.mss.gs. Toda rota exige Authorization: Bearer <backend key> e o scope activity.write no seu registro; as respostas saem com Cache-Control: no-store. Chame-as do seu servidor, nunca do cliente do jogo.

Método Caminho O que faz
POST /game-sdk/v1/link/start Inicia um vínculo para um dos seus jogadores ({ player_ref, player_name? }). Retorna link_code, device_code, qr_url, deep_link, expires_in e interval.
POST /game-sdk/v1/link/poll { device_code } → pending, denied, expired, ou linked com link_guid e user.
DELETE /game-sdk/v1/links/{link_guid} Encerra um vínculo do seu lado. O jogador pode fazer o mesmo em Settings.
PUT /game-sdk/v1/links/{link_guid}/activity Publica o bloco "Playing …" para um jogador; { "activity": null } o limpa.
POST /game-sdk/v1/activity/batch O mesmo, para até 100 jogadores numa chamada. Cada item responde por si.

Códigos de erro

Os erros voltam como {"error":"CODE","message":"…"} com o status HTTP correspondente.

Código Significado
401 UNAUTHORIZEDToken ausente ou desconhecido; autorize primeiro.
403 MISSING_SCOPEO jogador não concedeu essa permissão. Talvez ele a tenha desmarcado.
403 ORIGIN_NOT_ALLOWEDA requisição trazia um Origin de navegador. Veja "Só jogos nativos" abaixo.
409 NOT_SIGNED_INO mssgs está rodando, mas ninguém está conectado.
429 RATE_LIMITEDMais de 120 requisições num minuto vindas de um jogo.
400 INVALID_GAME_IDgame_id só pode ter letras, dígitos, ponto, hífen ou sublinhado.
400 TOO_MANY_GUIDSNo máximo 10 valores de server_guid por chamada de membership.

Backends vinculados

As rotas de backend usam o mesmo formato. Dentro de um batch, o status volta por item em results, então um vínculo encerrado nunca faz a chamada inteira falhar.

Código Significado
401 INVALID_BACKEND_KEYChave desconhecida, ou uma que saiu de rotação há mais de 24 horas.
403 SCOPE_NOT_GRANTEDO seu registro não tem o scope de que essa rota precisa.
400 INVALID_PAYLOADCorpo malformado, mais de 100 itens no batch, ou um join.url cujo host não é um dos seus backends registrados.
400 INVALID_ACTIVITYNão sobrou nenhum name utilizável depois da normalização.
410 LINK_REVOKEDO vínculo foi encerrado, de um lado ou do outro. Descarte-o e ofereça "Connect mssgs" de novo.
429 SLOW_DOWNVocê consultou link/poll mais rápido que interval.
429 RATE_LIMITEDUma mudança num vínculo menos de 2 segundos depois da anterior, ou mais de 600 requisições num minuto na sua chave.
503 LINK_STORE_UNAVAILABLEProblema temporário do nosso lado. Tente de novo no próximo heartbeat.

Segurança

Só jogos nativos

Requisições que trazem o Origin de uma página web são recusadas com 403 ORIGIN_NOT_ALLOWED. Qualquer página web poder detectar que você usa o mssgs e abrir uma janela de permissão seria uma porta para fingerprinting e phishing, não um recurso. Um jogo nativo não envia Origin nenhum, então não é afetado, e o navegador embutido de um jogo é permitido pelo nome, veja FiveM. Se você está criando um jogo de navegador ou de celular, você não fala com a ponte: o seu próprio backend publica para os jogadores vinculados, veja Jogos de navegador e celular.

O que continua nas mãos do jogador

  • O jogador pode desligar a ponte em Settings → Game Activity, e a partir daí nenhum jogo consegue ver o mssgs.
  • Todo jogo aprovado aparece ali com exatamente as permissões que tem, quando esteve ativo pela última vez e um botão Remove. A remoção é imediata: o token morre na hora.
  • Um jogo de navegador ou de celular vinculado aparece em Linked games com um botão Disconnect. Desconectar também é imediato: a próxima publicação desse backend recebe um 410.
  • A ponte escuta só em 127.0.0.1, nunca na rede.
  • As mensagens diretas nunca são reveladas, nem com servers.list.
  • Há um limite de 120 requisições por minuto por jogo.

Boas práticas

  • Peça os scopes quando precisar deles, não todos de uma vez no primeiro uso.
  • Funcione sem o mssgs: o jogador não é obrigado a tê-lo.
  • Limpe o seu status quando o jogo parar em vez de esperar o TTL.
  • Trate um scope recusado como um resultado normal, não como um erro.

Continue criando