Vai al contenuto principale
Sviluppatori Game SDK

Mostra a cosa sta giocando qualcuno

Fai dire al tuo gioco a mssgs cosa sta facendo un giocatore. Gli amici vedono “Sta giocando a” sotto il suo nome, aprono i dettagli e premono Unisciti ora per entrare nella stessa partita. Il tuo gioco può anche verificare se un giocatore fa parte della tua community.

Cosa puoi fare

  • Pubblica uno stato di giocoIl gioco, cosa sta facendo il giocatore, il suo ruolo e quanto è pieno il gruppo.
  • Aggiungi un pulsante Unisciti oraGli amici entrano nella stessa partita, server o lobby con una sola pressione.
  • Verifica l’appartenenzaChiedi se un giocatore fa parte della tua community, e con quali ruoli.
  • Desktop, browser o telefonoI giochi nativi usano il bridge locale; i giochi browser e per telefono passano dal tuo backend.

Nell'app

daniSta giocando a Space Raiders
Space RaidersGiocato da dani
Sector 7
In un raid
3 di 4 nel gruppo · da 12 min
Artigliere

Lo stato sotto un nome, e i dettagli che apre. Il gioco ha pubblicato un solo blocco JSON; il resto è l’app.

Panoramica

L’app desktop di mssgs esegue un piccolo bridge HTTP locale con cui dialoga un gioco sulla stessa macchina. Il tuo gioco non parla mai con i nostri server, non vede mai la password o un token dell’account e non può mai pubblicare al posto del giocatore. Parla con la copia di mssgs in cui il giocatore ha già fatto l’accesso, ed è quella copia a decidere cosa rispondere.

Cosa puoi farci:

  • Rilevare che mssgs è installato e che qualcuno ha fatto l’accesso.
  • Leggere chi è il giocatore: user_guid, nome utente, avatar.
  • Chiedere “questo giocatore fa parte della community X?” e quale ruolo ha lì.
  • Pubblicare uno stato “Sta giocando a …” con un pulsante Unisciti ora per gli altri.
  • Ricevere il passaggio di un ingresso quando qualcuno preme quel pulsante.

Due modi di entrare

Un gioco desktop nativo dialoga con il bridge locale; è ciò che descrivono le sezioni seguenti. Un gioco nel browser o su un telefono non può raggiungere quel bridge. In quel caso è il tuo backend a pubblicare, per i giocatori che hanno collegato il proprio account mssgs tramite un QR o un codice di otto caratteri: vedi Giochi browser e per telefono, con CozyCity come primo esempio. Lo stato di gioco in sé è lo stesso blocco in entrambi i casi.

Divulgazione minima, per impostazione predefinita

Gli scope sono volutamente diseguali. Se tutto ciò che ti serve è “questa persona fa parte della nostra community”, chiedi membership.query e indichi tu il server_guid: ottieni sì/no più i suoi ruoli lì, e non vieni a sapere nulla delle sue altre community. L’elenco completo sta dietro uno scope separato e più alto, che il giocatore deve approvare a parte.

Trovare il client

Il bridge è in ascolto solo su 127.0.0.1, sulla prima porta libera di un piccolo intervallo. Provale in ordine finché una risponde: 7440, 7441, 7442, 7443. Le build di sviluppo di mssgs sono invece in ascolto su 7540–7543, così una build di test non risponde mai alle chiamate di un gioco vero.

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

Non serve un token, e la risposta non dice nulla sul giocatore, solo che mssgs è qui e se qualcuno ha fatto l’accesso.

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

Controlla product === "mssgs" e api prima di andare avanti. Se nessuna delle quattro porte risponde, mssgs non è in esecuzione. Offri semplicemente la tua esperienza normale invece di far aspettare il giocatore.

Chiedere il permesso

Tutto tranne /hello richiede un token, e un token esiste solo dopo che il giocatore ha approvato il tuo gioco in una finestra di dialogo dentro l’app. Chiedi solo gli scope che usi davvero: il giocatore li vede uno per uno, spiegati, e può deselezionarli singolarmente.

1. Chiedi il permesso
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"]
  }'

Ricevi {"status":"pending","request_id":"…","poll_after_ms":1000} e il giocatore vede la finestra di dialogo. Poi interroga finché non risponde (la richiesta scade dopo 3 minuti):

2. Interroga per la risposta
curl http://127.0.0.1:7440/mssgs/v1/authorize/<request_id>

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

Controlla sempre cosa hai ottenuto davvero

Gli scopes nella risposta possono essere meno di quelli che hai chiesto: il giocatore è libero di deselezionarne alcuni. Nell’esempio qui sopra, membership.query è stato rifiutato. Decidi in base a cosa dice la risposta, non a cosa hai richiesto, altrimenti incontrerai un 403 MISSING_SCOPE che non avevi previsto.

Salva il token e invialo come Authorization: Bearer <token>. Sopravvive ai riavvii, quindi il giocatore approva il tuo gioco una volta sola e non a ogni sessione. Se in seguito ripeti l’autorizzazione con scope già concessi, ricevi subito lo stesso token, senza finestra di dialogo.

Scope e privacy

I cinque scope cedono quantità molto diverse di informazioni. Non è un caso; è l’intero progetto. Chiedi il meno possibile, scendendo lungo questa tabella.

Scope Cosa permette Cosa cede il giocatore
presence.write Mostrare a cosa sta giocando Niente. Questo scope scrive soltanto; non legge alcun dato dell’account.
identity Chi è il giocatore user_guid, nome utente, nome visualizzato, URL dell’avatar.
staff Flag di staff / moderatore Due booleani, in aggiunta a identity. Separato perché un gioco che mostra un nome non ha motivo di sapere che il giocatore modera delle community.
membership.query Verificare una community che conosci già Per un server_guid che indichi tu: sì/no, il suo nome e i ruoli che quel giocatore ha lì. Nulla su nessun’altra community.
servers.list Tutte le community di cui fa parte L’elenco completo: guid, nomi, icone e ruoli. È quello costoso: chiedilo solo se ti serve davvero.

Alla maggior parte dei giochi ne servono due

identity e presence.write coprono “chi sei” e “mostra a cosa stai giocando”, cioè quasi ogni integrazione. Aggiungi membership.query se vuoi legare una ricompensa all’appartenenza alla tua community. Non ti serve quasi mai servers.list, e il giocatore lo vede evidenziato in rosso.

Verificare l’appartenenza

È l’alternativa a “dammi l’elenco completo”. Indichi il server_guid della tua community (che conosci già) e ricevi una risposta solo su quella.

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

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

# non è membro, e nient’altro:
# {"server_guid":"…","member":false}

Un “no” è esattamente questo e niente di più. Puoi passare fino a 10 guid per chiamata (ripeti server_guid o separali con virgole), e ricevi un array results. Il gruppo @everyone non è mai in roles: vale per ogni membro, quindi non ti dice nulla.

Pubblicare uno stato di gioco

Un solo PUT mette la riga “Sta giocando a …” sotto il nome del giocatore, ovunque le sue community lo vedano.

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" }
}

Solo name è obbligatorio. La risposta ti dice quanto dura lo stato e ogni quanto inviare l’heartbeat:

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

Heartbeat, o lo stato sparisce

Uno stato senza segni di vita per 90 secondi viene cancellato automaticamente. È voluto: se il tuo gioco va in crash, il giocatore non resta “in gioco” per ore. Invia un POST /mssgs/v1/activity/heartbeat ogni 30 secondi, e DELETE /mssgs/v1/activity alla chiusura regolare.

Numero di giocatori e ruolo

party.kind decide quale frase viene mostrata, perché gli stessi due numeri non significano la stessa cosa. Una squadra di quattro non è un server con quattro giocatori.

kind Appare come Per
party (predefinito)3 di 4 nel gruppouna squadra, una crew o un gruppo
server4/100 giocatoriun server di gioco (FiveM, un server della community)
lobby4/100 giocatoriuna lobby prima che la partita inizi
match4/100 giocatoriuna partita o un round in corso

role (fino a 48 caratteri) è nei panni di chi sta giocando il giocatore: un mestiere, una classe o un personaggio. Ha un campo tutto suo invece di un’altra frase in state, perché viene mostrato come etichetta accanto al numero di giocatori.

details e state sono limitati a 128 caratteri ciascuno, name a 64. Gli a capo e i caratteri di controllo vengono rimossi. Un URL per l’icona volutamente non è supportato: verrebbe scaricato da ogni client che mostra la riga, trasformando uno stato in un segnale che riferisce al tuo server ogni membro di ogni community in cui è il giocatore.

Il pulsante Unisciti ora

Metti un blocco join nella tua attività e gli altri membri vedono un pulsante Unisciti ora accanto allo stato. Ci sono due modi, e puoi combinarli.

1. Un secret (per i giochi nativi)

Imposta {"join":{"secret":"raid-42"}}. Quando qualcuno preme Unisciti ora, quel secret viene consegnato alla sua copia del tuo gioco, sulla sua macchina, abbinata tramite lo stesso game_id. Non viene aperto nessun URL e non viene invocato nessun gestore di schemi. Il tuo gioco lo raccoglie con:

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

Interroga con ?since=<cursor> così vedi ogni evento una sola volta. Se il gioco di chi ha premuto non è in esecuzione, non viene consegnato nulla: un buon motivo per offrire anche un URL.

2. Un URL https (per giochi web e link alle lobby)

Imposta {"join":{"url":"https://play.example.com/s/abc"}} e il pulsante apre quel link. È accettato solo https. Uno schema personalizzato (steam://, mygame://, file://) viene rifiutato: quel blocco finisce sullo schermo di ogni membro, e un URL del genere è un modo per far invocare alla macchina di qualcun altro un gestore locale con argomenti scelti da te.

Tutto ciò che sta in join è pubblico

Il blocco join viene trasmesso a chiunque possa vedere lo stato del giocatore; è proprio lo scopo di un pulsante Unisciti ora. Trattalo quindi come un codice di lobby, non come una credenziale. Non metterci mai nulla che debba restare segreto, e fai scadere i tuoi codici.

FiveM

FiveM non ha HTTP nel suo runtime Lua lato client, quindi una resource dialoga con il bridge tramite NUI, una vista CEF, che invia un Origin. Il bridge accetta esplicitamente queste origini: https://cfx-nui-<resource> e il più vecchio nui://<resource>. Le normali pagine web restano rifiutate, e una pagina sul web aperto non può rivendicare quell’origine; la imposta il browser stesso.

client.lua: chiedi alla NUI di pubblicare
-- La pagina NUI fa l’HTTP; Lua le manda solo i dati.
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: lo stato scade dopo 90 s
  end
end)
nui.js
const BASE = 'http://127.0.0.1:7440/mssgs/v1';   // prova 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']          // qui non serve altro
    })
  });
  const started = await res.json();
  if (started.status === 'approved') { return started.token; }

  // Ora il giocatore vede la finestra dei permessi in 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' } // il tuo link cfx.re
    })
  });
});

Il risultato: Sta giocando a FiveM · Los Santos Roleplay · 4/100 giocatori · Police, con un pulsante Unisciti ora che apre il tuo link cfx.re.

Chiedi solo presence.write

Uno stato di gioco non ha bisogno d’altro: quello scope non legge assolutamente nulla. Se vuoi legare una ricompensa nel gioco all’appartenenza alla tua community mssgs, aggiungi membership.query e indica il tuo server_guid; continui a non sapere nulla delle altre community del giocatore.

Un server su cui giochi non è automaticamente affidabile

Qualsiasi server FiveM può eseguire resource lato client, quindi qualsiasi server in cui qualcuno entra può chiedere il permesso. È proprio per questo che in mezzo c’è una finestra di dialogo che indica la resource: decide il giocatore, non il server.

Giochi browser e per telefono: il collegamento tramite il tuo backend

Un gioco in una scheda del browser o su un telefono non può raggiungere il bridge qui sopra. Il bridge gira sul desktop del giocatore, e in mezzo ci sono tre muri: il bridge rifiuta ogni richiesta che porta un Origin del browser, Chrome mette una richiesta di permesso davanti a una pagina pubblica che contatta 127.0.0.1 e Safari la rifiuta del tutto, e un telefono non ha alcuna strada verso il loopback di un desktop.

Quindi la direzione si inverte. Il tuo backend sa già chi sta giocando, e lo dice a mssgs, per i giocatori che hanno collegato il proprio account mssgs al tuo gioco. Il collegamento viene approvato nell’app mssgs, mai nel tuo gioco, e crea un collegamento, mai una sessione: niente di quanto segue può far accedere qualcuno o agire al posto del giocatore. Il client del tuo gioco non vede mai una chiave e non parla mai con mss.gs. Il primo gioco su questa strada è CozyCity, un city builder distribuito come pagina WebGL e come app per iPhone, senza build desktop; gli esempi qui sotto sono i suoi.

1. Registra il tuo gioco

Registra il gioco nella pagina di registrazione del Game SDK: il tuo game_id (per esempio com.deverence.cozycity), il nome e l’icona che il giocatore vede nella schermata di approvazione e gli hostname del tuo backend. Lo esaminiamo lì, e dopo l’approvazione la tua chiave backend ti aspetta sulla stessa pagina, mostrata una sola volta; noi ne conserviamo solo un digest. La chiave va sul tuo server e da nessun’altra parte. Puoi ruotarla lì in qualsiasi momento, e la vecchia resta valida per 24 ore così un deploy può completarsi.

Registra il tuo gioco

Il nome e l’icona nella schermata vengono sempre dalla registrazione, mai dalla richiesta. Altrimenti un link di phishing potrebbe travestire una richiesta di collegamento da qualsiasi gioco volesse. Gli hostname delimitano dove può puntare un join.url, vedi più avanti.

2. Collega un giocatore

Il giocatore sceglie Connetti mssgs nel tuo gioco. Il tuo gioco chiede al tuo backend, e il tuo backend chiede a noi:

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 è il tuo id stabile per quel giocatore (fino a 128 caratteri), non per una sessione o una partita; player_name (fino a 64) è ciò che la schermata mostra come “Giocatore: …”. Al client del tuo gioco passa solo link_code, qr_url e deep_link. device_code è il tuo riferimento per il polling e resta sul server.

Il tuo gioco mostra poi tre cose insieme, perché il giocatore potrebbe trovarsi ovunque:

  • Il QR di qr_url. Un telefono con mssgs lo apre direttamente nella schermata di approvazione dell’app. Senza l’app arriva su una pagina di mss.gs che mostra il codice e propone il download.
  • Un pulsante “Apri in mssgs” con deep_link, per un browser desktop accanto all’app desktop. È l’unico URL esterno che il tuo gioco debba mai aprire.
  • Il codice stesso, in due gruppi di quattro, da digitare in Impostazioni → Attività di gioco → Collega un gioco. L’alfabeto non contiene 0/O né 1/I, quindi digitarlo raramente va storto.

Cosa vede il giocatore in mssgs, disegnato dall’app a partire dalla registrazione:

Collegare CozyCity al tuo account mssgs?

CozyCity potrà mostrare a cosa stai giocando come stato mssgs. Non vedrà i tuoi messaggi, i tuoi amici o i tuoi server e non potrà pubblicare a tuo nome.
Giocatore: La città di René · Collega / Non ora

Nel frattempo il tuo backend interroga ogni interval secondi (se va più veloce riceve 429 SLOW_DOWN) finché lo stato non cambia. Un codice funziona una volta sola e scade dopo dieci minuti:

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"}      # il giocatore ha scelto Non ora
# {"status":"expired"}
# {"status":"linked","link_guid":"…","user":{"user_guid":"62e377…"}}

Salva link_guid insieme al tuo giocatore; da qui in poi è l’indirizzo per cui pubblichi. La risposta contiene solo lo user_guid; uno username viene aggiunto solo se la tua registrazione ha lo scope identity.link, e oltre a questo non c’è nulla. Una seconda approvazione per lo stesso player_ref sostituisce il collegamento precedente, quindi un giocatore del tuo gioco è un account mssgs. Lo stesso account mssgs può collegarsi a più giochi e a più player_ref di uno stesso gioco (un iPad di famiglia).

3. Pubblica lo stato di gioco

Lo stesso blocco del bridge, con le stesse regole e gli stessi limiti, solo che ora è per collegamento e con la tua chiave 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=…" }
  }
}
Risposte
200 {"published":true,"changed":true}     # il blocco è cambiato ed è stato trasmesso
200 {"published":true,"changed":false}    # identico a quello salvato; è stato solo rinnovato il TTL
204                                        # salvato, ma il giocatore ora non è online in mssgs
410 {"error":"LINK_REVOKED"}               # il giocatore si è scollegato: elimina il collegamento

Tratta 200 e 204 allo stesso modo: salvato. { "activity": null } cancella il blocco, invialo quando il giocatore esce. Una differenza rispetto al bridge: l’host di join.url deve essere uno dei tuoi backend registrati (o un suo sottodominio), altrimenti ricevi 400 INVALID_PAYLOAD. Così un backend non può mettere sullo stato di un giocatore un pulsante Unisciti ora che porta dove quel giocatore non ha mai giocato.

Heartbeat ogni 60 secondi, TTL 120

Uno stato pubblicato vive 120 secondi senza un nuovo messaggio e poi scade da solo. Quindi rinvia lo stesso blocco ogni 60 secondi; un blocco invariato non costa nulla e rinnova solo il TTL. Se il tuo heartbeat si ferma, si ferma anche la riga “Sta giocando a …”, ed è proprio questo lo scopo.

Con centinaia di giocatori online, invia l’heartbeat in una sola chiamata, fino a 100 elementi alla volta. Ogni elemento riceve il proprio stato, così un giocatore che si è scollegato in mssgs non blocca mai gli altri novantanove:

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 } ] }

Come viene mostrato lo stato

  • Identico a uno stato dal bridge: Sta giocando a CozyCity · Lantern Hollow · 6/40 giocatori, con Unisciti ora quando c’è un join.url. Il blocco viene marcato via: "backend" lato server, così un client può aggiungere “Condiviso dal server del gioco”.
  • Solo mentre il giocatore è online in mssgs. Senza un client mssgs aperto l’account è offline e resta offline; il tuo backend non può far sembrare presente qualcuno. Questo impedisce anche che questa strada diventi un segnale “René è al computer”.
  • Precedenza: un gioco dentro l’app > un gioco sul bridge > il tuo backend. Se il giocatore si mette a giocare a scacchi dentro mssgs mentre il tuo backend continua a inviare heartbeat, vincono gli scacchi, non chi ha scritto per ultimo.

Scollegare

Il giocatore vede ogni collegamento in Impostazioni → Attività di gioco → Giochi collegati, con la tua icona e il tuo nome, il nome del giocatore dal tuo gioco, quando è stato collegato e quando ha pubblicato l’ultima volta, e un pulsante Scollega. Dopo di che la tua pubblicazione successiva risponde 410 LINK_REVOKED; è così che il tuo gioco lo scopre. Elimina il link_guid e proponi di nuovo “Connetti mssgs”. Dalla tua parte, chiudi un collegamento con DELETE /game-sdk/v1/links/{link_guid}.

Limiti

Per collegamento, una modifica conta al massimo ogni 2 secondi; un heartbeat invariato è gratuito. Per chiave ci sono 600 richieste al minuto, con gli elementi di un batch contati singolarmente: un heartbeat per 300 giocatori ogni 60 secondi ne consuma 5 su 600.

Riferimento degli endpoint: il bridge

URL di base http://127.0.0.1:<port>. Tutto tranne i primi tre richiede Authorization: Bearer <token>.

Metodo Percorso Scope Cosa fa
GET /mssgs/v1/hello nessuno mssgs è qui, che versione dell’API parla, e qualcuno ha fatto l’accesso? L’unica route che non richiede un token, e non dice nulla sul giocatore.
POST /mssgs/v1/authorize nessuno Chiede il permesso al giocatore. Apre una finestra di dialogo nell’app e restituisce un request_id da interrogare.
GET /mssgs/v1/authorize/:request_id nessuno pending, approved (con il token), denied o expired.
GET /mssgs/v1/me identity Il giocatore con accesso effettuato. Aggiunge is_staff / is_moderator solo con lo scope staff.
GET /mssgs/v1/membership membership.query Appartenenza ai valori server_guid che passi (fino a 10, ripetuti o separati da virgole).
GET /mssgs/v1/servers servers.list Ogni community di cui fa parte il giocatore, con i suoi ruoli. I messaggi diretti non sono mai inclusi.
PUT /mssgs/v1/activity presence.write Pubblica il blocco “Sta giocando a …”. Restituisce il TTL e ogni quanto inviare l’heartbeat.
POST /mssgs/v1/activity/heartbeat presence.write Tiene viva l’attività pubblicata senza rinviarla.
DELETE /mssgs/v1/activity presence.write La cancella subito, per una chiusura regolare.
GET /mssgs/v1/events presence.write Passaggi di ingresso destinati al tuo gioco. Interroga con ?since=<cursor>.
GET /mssgs/v1/session nessuno Cosa contiene questo token: game_id, scope concessi, se qualcuno ha fatto l’accesso.
DELETE /mssgs/v1/session nessuno Restituisce il permesso. Stesso effetto del giocatore che lo revoca nelle Impostazioni.

Riferimento degli endpoint: backend collegati

URL di base https://ams1-gateway.mss.gs. Ogni route richiede Authorization: Bearer <backend key> e lo scope activity.write sulla tua registrazione; le risposte escono con Cache-Control: no-store. Chiamale dal tuo server, mai dal client del gioco.

Metodo Percorso Cosa fa
POST /game-sdk/v1/link/start Avvia un collegamento per uno dei tuoi giocatori ({ player_ref, player_name? }). Restituisce link_code, device_code, qr_url, deep_link, expires_in e interval.
POST /game-sdk/v1/link/poll { device_code } → pending, denied, expired, oppure linked con link_guid e user.
DELETE /game-sdk/v1/links/{link_guid} Chiude un collegamento dalla tua parte. Il giocatore può fare lo stesso dalle Impostazioni.
PUT /game-sdk/v1/links/{link_guid}/activity Pubblica il blocco “Sta giocando a …” per un giocatore; { "activity": null } lo cancella.
POST /game-sdk/v1/activity/batch Lo stesso, per fino a 100 giocatori in una chiamata. Ogni elemento risponde per conto suo.

Codici di errore

Gli errori tornano come {"error":"CODE","message":"…"} con lo stato HTTP corrispondente.

Codice Significato
401 UNAUTHORIZEDToken mancante o sconosciuto; autorizza prima.
403 MISSING_SCOPEIl giocatore non ha concesso quel permesso. Potrebbe averlo deselezionato.
403 ORIGIN_NOT_ALLOWEDLa richiesta portava un Origin del browser. Vedi “Solo giochi nativi” più avanti.
409 NOT_SIGNED_INmssgs è in esecuzione ma nessuno ha fatto l’accesso.
429 RATE_LIMITEDPiù di 120 richieste in un minuto da un solo gioco.
400 INVALID_GAME_IDgame_id deve contenere solo lettere, cifre, punto, trattino o trattino basso.
400 TOO_MANY_GUIDSAl massimo 10 valori server_guid per chiamata di appartenenza.

Backend collegati

Le route del backend usano la stessa forma. Dentro un batch lo stato torna per ciascun elemento in results, così un collegamento chiuso non fa mai fallire l’intera chiamata.

Codice Significato
401 INVALID_BACKEND_KEYChiave sconosciuta, o ruotata da più di 24 ore.
403 SCOPE_NOT_GRANTEDLa tua registrazione non ha lo scope richiesto da quella route.
400 INVALID_PAYLOADBody non valido, più di 100 elementi nel batch, o un join.url il cui host non è uno dei tuoi backend registrati.
400 INVALID_ACTIVITYDopo la normalizzazione non resta un name utilizzabile.
410 LINK_REVOKEDIl collegamento è stato chiuso, da una delle due parti. Eliminalo e proponi di nuovo “Connetti mssgs”.
429 SLOW_DOWNHai interrogato link/poll più spesso di interval.
429 RATE_LIMITEDUna modifica a un collegamento entro 2 secondi dalla precedente, o più di 600 richieste in un minuto sulla tua chiave.
503 LINK_STORE_UNAVAILABLEProblema temporaneo da parte nostra. Riprova al prossimo heartbeat.

Sicurezza

Solo giochi nativi

Le richieste che portano l’Origin di una pagina web vengono rifiutate con 403 ORIGIN_NOT_ALLOWED. Che qualsiasi pagina web possa rilevare che usi mssgs e far comparire una finestra dei permessi è una superficie per fingerprinting e phishing, non una funzione. Un gioco nativo non invia alcun Origin, quindi non ne è toccato, e il browser integrato di un gioco è ammesso per nome, vedi FiveM. Se stai costruendo un gioco browser o per telefono non dialoghi con il bridge: è il tuo backend a pubblicare per i giocatori collegati, vedi Giochi browser e per telefono.

Cosa resta nelle mani del giocatore

  • Il giocatore può spegnere il bridge in Impostazioni → Attività di gioco, dopo di che nessun gioco può più vedere mssgs.
  • Ogni gioco approvato è elencato lì con esattamente i permessi che ha, quando è stato attivo l’ultima volta e un pulsante Rimuovi. La rimozione è immediata: il token smette subito di valere.
  • Un gioco browser o per telefono collegato è elencato in Giochi collegati con un pulsante Scollega. Anche lo scollegamento è immediato: la pubblicazione successiva di quel backend riceve un 410.
  • Il bridge è in ascolto solo su 127.0.0.1, mai sulla rete.
  • I messaggi diretti non vengono mai rivelati, nemmeno con servers.list.
  • C’è un budget di 120 richieste al minuto per gioco.

Comportarsi bene

  • Chiedi gli scope quando ti servono, non tutti insieme al primo avvio.
  • Funziona anche senza mssgs: il giocatore non è obbligato ad averlo.
  • Cancella il tuo stato quando il gioco si ferma, invece di aspettare il TTL.
  • Tratta uno scope rifiutato come un esito normale, non come un errore.

Continua a costruire