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
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.
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.
{
"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.
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.
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.
{
"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:
{ "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 party | um esquadrão, uma equipe ou um grupo |
server | 4/100 players | um servidor de jogo (FiveM, um servidor da comunidade) |
lobby | 4/100 players | um lobby antes de a partida começar |
match | 4/100 players | uma 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:
{
"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.
-- 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)
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.
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:
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:
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:
{
"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=…" }
}
}
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:
{ "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 comvia: "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 UNAUTHORIZED | Token ausente ou desconhecido; autorize primeiro. |
403 MISSING_SCOPE | O jogador não concedeu essa permissão. Talvez ele a tenha desmarcado. |
403 ORIGIN_NOT_ALLOWED | A requisição trazia um Origin de navegador. Veja "Só jogos nativos" abaixo. |
409 NOT_SIGNED_IN | O mssgs está rodando, mas ninguém está conectado. |
429 RATE_LIMITED | Mais de 120 requisições num minuto vindas de um jogo. |
400 INVALID_GAME_ID | game_id só pode ter letras, dígitos, ponto, hífen ou sublinhado. |
400 TOO_MANY_GUIDS | No 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_KEY | Chave desconhecida, ou uma que saiu de rotação há mais de 24 horas. |
403 SCOPE_NOT_GRANTED | O seu registro não tem o scope de que essa rota precisa. |
400 INVALID_PAYLOAD | Corpo malformado, mais de 100 itens no batch, ou um join.url cujo host não é um dos seus backends registrados. |
400 INVALID_ACTIVITY | Não sobrou nenhum name utilizável depois da normalização. |
410 LINK_REVOKED | O vínculo foi encerrado, de um lado ou do outro. Descarte-o e ofereça "Connect mssgs" de novo. |
429 SLOW_DOWN | Você consultou link/poll mais rápido que interval. |
429 RATE_LIMITED | Uma 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_UNAVAILABLE | Problema 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.