Sari la conținutul principal
Dezvoltatori Game SDK

Arată la ce se joacă cineva

Lasă-ți jocul să-i spună lui mssgs ce face un jucător. Prietenii văd „Playing” sub numele lui, deschid detaliile și apasă Join now ca să intre în același joc. Jocul tău poate verifica și dacă un jucător e în comunitatea ta.

Ce poți face

  • Publică un status de jocJocul, ce face jucătorul, rolul lui și cât de plin e grupul.
  • Adaugă un buton Join nowPrietenii intră în același joc, server sau lobby dintr-o singură apăsare.
  • Verifică apartenențaÎntreabă dacă un jucător e în comunitatea ta și cu ce roluri.
  • Desktop, browser sau telefonJocurile native folosesc puntea locală; cele din browser și de pe telefon trec prin backend-ul tău.

În aplicație

daniPlaying Space Raiders
Space RaidersPlayed by dani
Sector 7
Într-un raid
3 of 4 in the party · for 12 min
Tunar

Statusul de sub nume și detaliile pe care le deschide. Jocul a publicat un singur bloc JSON; restul îl face aplicația.

Prezentare generală

Aplicația desktop mssgs rulează o mică punte HTTP locală cu care vorbește un joc de pe același computer. Jocul tău nu vorbește niciodată cu serverele noastre, nu vede niciodată parola sau tokenul unui cont și nu poate posta niciodată în numele jucătorului. Vorbește cu instanța mssgs în care jucătorul e deja conectat, iar ea decide ce răspunde.

Ce poți face cu ea:

  • Să detectezi că mssgs e instalat și că e cineva conectat.
  • Să afli cine e jucătorul: user_guid, username, avatar.
  • Să întrebi „e jucătorul acesta în comunitatea X?” și ce rol are acolo.
  • Să publici un status „Playing …” cu un buton Join now pentru ceilalți.
  • Să primești datele de alăturare când cineva apasă acel buton.

Două căi de intrare

Un joc desktop nativ vorbește cu puntea locală; despre asta sunt secțiunile următoare. Un joc în browser sau pe telefon nu poate ajunge la această punte. Pentru el publică propriul tău backend, pentru jucătorii care și-au conectat contul mssgs printr-un cod QR sau un cod din opt caractere: vezi Jocuri în browser și pe telefon, cu CozyCity ca prim exemplu. Statusul de joc în sine e același bloc în ambele cazuri.

Dezvăluire minimă, implicit

Scope-urile sunt inegale intenționat. Dacă tot ce îți trebuie e „e persoana asta în comunitatea noastră”, ceri membership.query și numești tu server_guid-ul: primești da/nu plus rolurile ei acolo și nu afli nimic despre restul comunităților ei. Lista completă stă în spatele unui scope distinct, de nivel mai înalt, pe care jucătorul trebuie să-l aprobe separat.

Găsirea clientului

Puntea ascultă doar pe 127.0.0.1, pe primul port liber dintr-un interval mic. Încearcă-le pe rând până răspunde unul: 7440, 7441, 7442, 7443. Versiunile de dezvoltare ale mssgs ascultă în schimb pe 7540–7543, așa că o versiune de test nu răspunde niciodată la apelurile unui joc real.

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

Nu e nevoie de token, iar răspunsul nu spune nimic despre jucător, doar că mssgs e aici și dacă e cineva conectat.

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

Verifică product === "mssgs" și api înainte să mergi mai departe. Dacă niciunul dintre cele patru porturi nu răspunde, mssgs nu rulează. Oferă pur și simplu experiența obișnuită, în loc să-l pui pe jucător să aștepte.

Cererea permisiunii

Tot în afară de /hello are nevoie de un token, iar un token există doar după ce jucătorul ți-a aprobat jocul într-un dialog din aplicație. Cere doar scope-urile pe care le folosești cu adevărat: jucătorul le vede pe fiecare separat, cu o explicație, și le poate debifa individual.

1. Cere permisiunea
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"]
  }'

Primești înapoi {"status":"pending","request_id":"…","poll_after_ms":1000}, iar jucătorul vede dialogul. Apoi interoghează periodic până răspunde (cererea expiră după 3 minute):

2. Interoghează pentru răspuns
curl http://127.0.0.1:7440/mssgs/v1/authorize/<request_id>

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

Verifică mereu ce ai primit de fapt

Lista scopes din răspuns poate fi mai scurtă decât ce ai cerut: jucătorul e liber să debifeze unele dintre ele. În exemplul de mai sus, membership.query a fost refuzat. Ramifică logica după ce spune răspunsul, nu după ce ai cerut, altfel dai peste un 403 MISSING_SCOPE pe care nu l-ai prevăzut.

Salvează tokenul și trimite-l ca Authorization: Bearer <token>. Rezistă la reporniri, așa că jucătorul îți aprobă jocul o singură dată, nu la fiecare sesiune. Dacă mai târziu reautorizezi cu scope-uri deja acordate, primești imediat același token, fără dialog.

Scope-uri și confidențialitate

Cele cinci scope-uri dezvăluie cantități foarte diferite de date. Nu e întâmplător; pe asta se bazează tot designul. Cere cât mai puțin, pornind de sus în acest tabel.

Scope Ce permite La ce renunță jucătorul
presence.write Arată la ce se joacă Nimic. Acest scope doar scrie; nu citește deloc date din cont.
identity Cine e jucătorul user_guid, username, nume afișat, URL-ul avatarului.
staff Indicatori de staff / moderator Două valori booleene, pe lângă identity. Separat, pentru că un joc care afișează un nume n-are de ce să știe că jucătorul moderează comunități.
membership.query Verifică o comunitate pe care o știi deja Pentru un server_guid pe care îl numești: da/nu, numele ei și rolurile pe care le are jucătorul acolo. Nimic despre vreo altă comunitate.
servers.list Toate comunitățile din care face parte Lista completă: guid-uri, nume, pictograme și roluri. Acesta e cel scump: cere-l doar dacă chiar ai nevoie de el.

Majorității jocurilor le ajung două

identity și presence.write acoperă „cine ești” și „arată la ce te joci”, adică aproape orice integrare. Adaugă membership.query dacă vrei să legi o recompensă de apartenența la comunitatea ta. Aproape niciodată nu ai nevoie de servers.list, iar jucătorul îl vede evidențiat cu roșu.

Verificarea apartenenței

Aceasta e alternativa la „dă-mi toată lista”. Numești server_guid-ul propriei comunități (pe care îl știi deja) și primești un răspuns doar despre ea.

O singură comunitate
curl -H "Authorization: Bearer $TOKEN" \
  "http://127.0.0.1:7440/mssgs/v1/membership?server_guid=ca94ecc…f4g02"

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

# nu e membru, și nimic altceva:
# {"server_guid":"…","member":false}

Un „nu” înseamnă exact atât și nimic mai mult. Poți trimite până la 10 guid-uri per apel (repetă server_guid sau separă-le prin virgulă), iar atunci primești un array results. Grupul @everyone nu apare niciodată în roles: e valabil pentru fiecare membru, deci nu-ți spune nimic.

Publicarea unui status de joc

Un singur PUT pune rândul „Playing …” sub numele jucătorului, oriunde îl văd comunitățile lui.

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

Doar name e obligatoriu. Răspunsul îți spune cât trăiește statusul și cât de des să trimiți heartbeat:

Răspuns
{ "ok": true, "expires_in_ms": 90000, "heartbeat_every_ms": 30000 }

Heartbeat, altfel statusul dispare

Un status fără niciun semn de viață timp de 90 de secunde e șters automat. E intenționat: dacă jocul se blochează, jucătorul nu rămâne „în joc” ore întregi. Trimite un POST /mssgs/v1/activity/heartbeat la fiecare 30 de secunde și DELETE /mssgs/v1/activity la o închidere curată.

Număr de jucători și rol

party.kind decide ce propoziție se afișează, pentru că aceleași două numere nu înseamnă același lucru. O echipă de patru nu e un server cu patru jucători.

kind Se afișează ca Pentru
party (implicit)3 of 4 in the partyo echipă, un clan sau un grup
server4/100 playersun server de joc (FiveM, un server de comunitate)
lobby4/100 playersun lobby înainte să înceapă meciul
match4/100 playersun meci sau o rundă în desfășurare

role (până la 48 de caractere) spune în ce rol joacă jucătorul: o meserie, o clasă sau un personaj. Are propriul câmp în loc de încă o propoziție în state, pentru că apare ca etichetă lângă numărul de jucători.

details și state au maximum 128 de caractere fiecare, name 64. Trecerile la rând nou și caracterele de control sunt eliminate. Un URL de pictogramă nu e acceptat, intenționat: l-ar descărca fiecare client care afișează rândul, iar asta ar transforma statusul într-un far care raportează serverului tău fiecare membru din fiecare comunitate în care e jucătorul.

Butonul Join now

Pune un bloc join în activitate, iar ceilalți membri primesc un buton Join now lângă status. Există două variante și le poți combina.

1. Un secret (pentru jocuri native)

Setează {"join":{"secret":"raid-42"}}. Când cineva apasă Join now, secretul ajunge la propria lui copie a jocului tău, pe propriul lui computer, potrivită după același game_id. Nu se deschide niciun URL și nu e apelat niciun handler de schemă. Jocul tău îl preia cu:

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

Interoghează cu ?since=<cursor> ca să vezi fiecare eveniment o singură dată. Dacă jocul celui care a apăsat nu rulează, nu se livrează nimic, ceea ce e un motiv bun să oferi și un URL.

2. Un URL https (pentru jocuri web și linkuri de lobby)

Setează {"join":{"url":"https://play.example.com/s/abc"}} și butonul deschide acel link. Se acceptă doar https. O schemă personalizată (steam://, mygame://, file://) e refuzată: blocul ajunge pe ecranul fiecărui membru, iar un astfel de URL e o cale de a face computerul altcuiva să apeleze un handler local cu argumente alese de tine.

Tot ce e în join e public

Blocul join e difuzat tuturor celor care pot vedea statusul jucătorului; asta e tot rostul unui buton Join now. Așa că tratează-l ca pe un cod de lobby, nu ca pe o credențială. Nu pune niciodată în el ceva ce trebuie să rămână secret și fă-ți codurile să expire.

FiveM

FiveM nu are HTTP în runtime-ul Lua de pe partea de client, așa că o resursă vorbește cu puntea prin NUI, o vizualizare CEF care trimite un Origin. Puntea acceptă explicit aceste origini: https://cfx-nui-<resource> și mai vechiul nui://<resource>. Paginile web obișnuite rămân refuzate, iar o pagină de pe internetul deschis nu poate pretinde acest origin; îl setează browserul însuși.

client.lua: cere NUI-ului să publice
-- Pagina NUI face HTTP-ul; Lua doar îi trimite datele.
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: statusul expiră după 90 s
  end
end)
nui.js
const BASE = 'http://127.0.0.1:7440/mssgs/v1';   // încearcă 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']          // aici nu e nevoie de nimic altceva
    })
  });
  const started = await res.json();
  if (started.status === 'approved') { return started.token; }

  // Jucătorul vede acum dialogul de permisiune în 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' } // linkul tău cfx.re
    })
  });
});

Rezultatul: Playing FiveM · Los Santos Roleplay · 4/100 players · Police, cu un buton Join now care deschide linkul tău cfx.re.

Cere doar presence.write

Un status de joc nu are nevoie de nimic altceva: acest scope nu citește absolut nimic. Dacă vrei să legi o recompensă din joc de apartenența la comunitatea ta mssgs, adaugă membership.query și numește propriul server_guid; tot nu afli nimic despre celelalte comunități ale jucătorului.

Un server pe care joci nu e automat de încredere

Orice server FiveM poate rula resurse pe client, deci orice server pe care intră cineva poate cere permisiunea. Exact de aceea stă la mijloc un dialog care numește resursa: decide jucătorul, nu serverul.

Jocuri în browser și pe telefon: conectare prin backend-ul tău

Un joc dintr-un tab de browser sau de pe telefon nu poate ajunge la puntea de mai sus. Ea rulează pe desktopul jucătorului, iar între ele stau trei ziduri: puntea refuză orice cerere care poartă un Origin de browser, Chrome afișează o cerere de permisiune înainte ca o pagină publică să acceseze 127.0.0.1, Safari refuză din start, iar un telefon nu are nicio cale către loopback-ul unui desktop.

Așa că direcția se inversează. Backend-ul tău știe deja cine joacă și îi spune lui mssgs, pentru jucătorii care și-au conectat contul mssgs la jocul tău. Conectarea se aprobă în aplicația mssgs, niciodată în jocul tău, și creează o legătură, niciodată o sesiune: nimic din ce urmează nu poate autentifica pe cineva și nici nu poate acționa în numele jucătorului. Clientul jocului tău nu vede niciodată o cheie și nu vorbește niciodată cu mss.gs. Primul joc pe această cale este CozyCity, un city builder livrat ca pagină WebGL și ca aplicație de iPhone, fără versiune desktop; exemplele de mai jos sunt chiar ale lui.

1. Înregistrează-ți jocul

Înregistrează jocul pe pagina de înregistrare Game SDK: game_id (de exemplu com.deverence.cozycity), numele și pictograma pe care jucătorul le vede în fereastra de aprobare și hostname-urile backend-ului tău. Îl verificăm acolo, iar după aprobare cheia de backend te așteaptă pe aceeași pagină, afișată o singură dată; noi păstrăm doar un hash al ei. Cheia trebuie să stea pe serverul tău și nicăieri altundeva. O poți roti acolo oricând, iar cea veche rămâne valabilă 24 de ore, ca un deploy să se poată derula treptat.

Înregistrează-ți jocul

Numele și pictograma din fereastră vin întotdeauna din înregistrare, niciodată din cerere. Altfel, un link de phishing ar putea deghiza o cerere de conectare în orice joc ar vrea. Hostname-urile limitează unde poate duce un join.url, vezi mai jos.

2. Conectează un jucător

Jucătorul alege Connect mssgs în jocul tău. Jocul îți întreabă backend-ul, iar backend-ul ne întreabă pe 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 e propriul tău id stabil pentru acel jucător (până la 128 de caractere), nu pentru o sesiune sau un meci; player_name (până la 64) e ce afișează fereastra ca „Player: …”. Dă clientului jocului doar link_code, qr_url și deep_link. device_code e handle-ul tău de interogare și rămâne pe server.

Apoi jocul afișează trei lucruri deodată, pentru că jucătorul poate fi oriunde:

  • Codul QR pentru qr_url. Un telefon cu mssgs îl deschide direct în fereastra de aprobare din aplicație. Fără aplicație, ajunge pe o pagină de pe mss.gs care arată codul și oferă descărcarea.
  • Un buton „Open in mssgs” cu deep_link, pentru un browser de desktop aflat pe același computer cu aplicația desktop. E singurul URL extern pe care jocul tău trebuie să-l deschidă vreodată.
  • Codul în sine, în două grupe de câte patru, de tastat în Settings → Game Activity → Link a game. Alfabetul nu are 0/O sau 1/I, așa că rareori se greșește la tastare.

Ce vede jucătorul în mssgs, în fereastra pe care aplicația o construiește din înregistrare:

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

Între timp, backend-ul tău interoghează la fiecare interval secunde (dacă interoghează mai des, primește 429 SLOW_DOWN) până se schimbă statusul. Un cod funcționează o singură dată și expiră după zece minute:

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"}      # jucătorul a ales Not now
# {"status":"expired"}
# {"status":"linked","link_guid":"…","user":{"user_guid":"62e377…"}}

Salvează link_guid la jucătorul tău; de acum înainte e adresa pentru care publici. Răspunsul conține doar user_guid; un username se adaugă doar când înregistrarea ta are scope-ul identity.link, și nimic în plus. O a doua aprobare pentru același player_ref înlocuiește legătura anterioară, deci un jucător al jocului tău înseamnă un singur cont mssgs. Același cont mssgs se poate conecta la mai multe jocuri și la mai multe player_ref ale aceluiași joc (un iPad de familie).

3. Publică statusul de joc

Același bloc ca la punte, cu aceleași reguli și aceleași limite, doar că acum per legătură și cu cheia ta 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=…" }
  }
}
Răspunsuri
200 {"published":true,"changed":true}     # blocul s-a schimbat și a fost difuzat
200 {"published":true,"changed":false}    # identic cu ce era salvat; s-a reîmprospătat doar TTL-ul
204                                        # salvat, dar jucătorul nu e online în mssgs acum
410 {"error":"LINK_REVOKED"}               # jucătorul s-a deconectat: renunță la legătură

Tratează 200 și 204 la fel: salvat. { "activity": null } șterge blocul; trimite-l când jucătorul pleacă. O diferență față de punte: hostul din join.url trebuie să fie unul dintre backend-urile tale înregistrate (sau un subdomeniu al unuia), altfel primești 400 INVALID_PAYLOAD. Așa, un backend nu poate pune pe statusul unui jucător un buton Join now care duce undeva unde jucătorul acela n-a jucat niciodată.

Heartbeat la fiecare 60 de secunde, TTL 120

Un status publicat trăiește 120 de secunde fără un mesaj nou și apoi dispare singur. Așa că retrimite același bloc la fiecare 60 de secunde; un bloc neschimbat nu costă nimic și doar reîmprospătează TTL-ul. Dacă heartbeat-ul se oprește, se oprește și rândul „Playing …”, exact cum trebuie.

Cu sute de jucători online, trimite heartbeat-ul într-un singur apel, până la 100 de elemente odată. Fiecare element primește propriul status, așa că un jucător care s-a deconectat din mssgs nu-i oprește niciodată pe ceilalți nouăzeci și nouă:

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

Cum se afișează statusul

  • Identic cu un status de pe punte: Playing CozyCity · Lantern Hollow · 6/40 players, cu Join now când există un join.url. Serverul marchează blocul cu via: "backend", așa că un client poate adăuga „Shared by the game's server”.
  • Doar cât timp jucătorul e online în mssgs. Fără un client mssgs deschis, contul e offline și rămâne offline; backend-ul tău nu poate face pe cineva să pară prezent. Asta împiedică totodată ruta aceasta să devină un far de tipul „e René la calculator?”.
  • Prioritate: un joc din aplicație > un joc pe punte > backend-ul tău. Dacă jucătorul se apucă de șah în mssgs în timp ce backend-ul tău continuă să trimită heartbeat, câștigă șahul, nu cine a scris ultimul.

Deconectarea

Jucătorul vede fiecare legătură în Settings → Game Activity → Linked games, cu pictograma și numele tău, numele jucătorului din jocul tău, data conectării și a ultimei publicări, plus un buton Disconnect. După asta, următoarea ta publicare primește 410 LINK_REVOKED; așa află jocul tău. Renunță la link_guid și oferă din nou „Connect mssgs”. Din partea ta, închei o legătură cu DELETE /game-sdk/v1/links/{link_guid}.

Limite

Per legătură, o schimbare contează cel mult o dată la 2 secunde; un heartbeat neschimbat e gratuit. Per cheie sunt 600 de cereri pe minut, iar elementele dintr-un batch se numără individual: heartbeat pentru 300 de jucători la fiecare 60 de secunde consumă 5 din cele 600.

Referință endpoint-uri: puntea

URL de bază http://127.0.0.1:<port>. Toate, în afară de primele trei, necesită Authorization: Bearer <token>.

Metodă Cale Scope Ce face
GET /mssgs/v1/hello niciunul E mssgs aici, ce API vorbește și e cineva conectat. Singura rută care nu cere token, iar ea nu spune nimic despre jucător.
POST /mssgs/v1/authorize niciunul Cere permisiunea jucătorului. Deschide un dialog în aplicație și returnează un request_id de interogat.
GET /mssgs/v1/authorize/:request_id niciunul pending, approved (cu tokenul), denied sau expired.
GET /mssgs/v1/me identity Jucătorul conectat. Adaugă is_staff / is_moderator doar cu scope-ul staff.
GET /mssgs/v1/membership membership.query Apartenența la valorile server_guid pe care le trimiți (până la 10, repetate sau separate prin virgulă).
GET /mssgs/v1/servers servers.list Toate comunitățile din care face parte jucătorul, cu rolurile lui. Mesajele directe nu sunt incluse niciodată.
PUT /mssgs/v1/activity presence.write Publică blocul „Playing …”. Returnează TTL-ul și cât de des să trimiți heartbeat.
POST /mssgs/v1/activity/heartbeat presence.write Menține activitatea publicată fără s-o retrimiți.
DELETE /mssgs/v1/activity presence.write O șterge imediat, la o închidere curată.
GET /mssgs/v1/events presence.write Datele de alăturare destinate jocului tău. Interoghează cu ?since=<cursor>.
GET /mssgs/v1/session niciunul Ce conține acest token: game_id, scope-urile acordate, dacă e cineva conectat.
DELETE /mssgs/v1/session niciunul Renunță la permisiune. Același efect ca atunci când jucătorul o revocă din Settings.

Referință endpoint-uri: backend-uri conectate

URL de bază https://ams1-gateway.mss.gs. Fiecare rută necesită Authorization: Bearer <backend key> și scope-ul activity.write în înregistrarea ta; răspunsurile pleacă cu Cache-Control: no-store. Apelează-le de pe serverul tău, niciodată din clientul jocului.

Metodă Cale Ce face
POST /game-sdk/v1/link/start Pornește o conectare pentru unul dintre jucătorii tăi ({ player_ref, player_name? }). Returnează link_code, device_code, qr_url, deep_link, expires_in și interval.
POST /game-sdk/v1/link/poll { device_code } → pending, denied, expired sau linked cu link_guid și user.
DELETE /game-sdk/v1/links/{link_guid} Încheie o legătură din partea ta. Jucătorul poate face același lucru din Settings.
PUT /game-sdk/v1/links/{link_guid}/activity Publică blocul „Playing …” pentru un jucător; { "activity": null } îl șterge.
POST /game-sdk/v1/activity/batch Același lucru, pentru până la 100 de jucători într-un singur apel. Fiecare element primește propriul răspuns.

Coduri de eroare

Erorile vin înapoi ca {"error":"CODE","message":"…"}, cu statusul HTTP corespunzător.

Cod Semnificație
401 UNAUTHORIZEDToken lipsă sau necunoscut; autorizează-te mai întâi.
403 MISSING_SCOPEJucătorul nu a acordat această permisiune. Poate a debifat-o.
403 ORIGIN_NOT_ALLOWEDCererea purta un Origin de browser. Vezi „Doar jocuri native” mai jos.
409 NOT_SIGNED_INmssgs rulează, dar nu e nimeni conectat.
429 RATE_LIMITEDPeste 120 de cereri pe minut de la un singur joc.
400 INVALID_GAME_IDgame_id poate conține doar litere, cifre, punct, cratimă sau underscore.
400 TOO_MANY_GUIDSCel mult 10 valori server_guid per apel membership.

Backend-uri conectate

Rutele de backend folosesc același format. Într-un batch, statusul vine pentru fiecare element în parte în results, așa că o legătură încheiată nu face niciodată să eșueze tot apelul.

Cod Semnificație
401 INVALID_BACKEND_KEYCheie necunoscută sau una rotită acum mai bine de 24 de ore.
403 SCOPE_NOT_GRANTEDÎnregistrarea ta nu are scope-ul de care are nevoie ruta.
400 INVALID_PAYLOADCorp malformat, peste 100 de elemente în batch sau un join.url al cărui host nu e unul dintre backend-urile tale înregistrate.
400 INVALID_ACTIVITYDupă normalizare nu a mai rămas niciun nume utilizabil.
410 LINK_REVOKEDLegătura s-a încheiat, de o parte sau de cealaltă. Renunță la ea și oferă din nou „Connect mssgs”.
429 SLOW_DOWNAi interogat link/poll mai des decât interval.
429 RATE_LIMITEDO schimbare la aceeași legătură la mai puțin de 2 secunde după ultima sau peste 600 de cereri pe minut pe cheia ta.
503 LINK_STORE_UNAVAILABLEProblemă temporară la noi. Reîncearcă la următorul heartbeat.

Securitate

Doar jocuri native

Cererile care poartă un Origin de pagină web sunt refuzate cu 403 ORIGIN_NOT_ALLOWED. Dacă orice pagină web ar putea detecta că folosești mssgs și ar putea deschide un dialog de permisiune, asta ar fi o suprafață de atac pentru fingerprinting și phishing, nu o funcție. Un joc nativ nu trimite deloc Origin, deci nu e afectat, iar browserul încorporat al unui joc e permis explicit, pe nume, vezi FiveM. Dacă construiești un joc pentru browser sau telefon, nu vorbești cu puntea: propriul tău backend publică pentru jucătorii conectați, vezi Jocuri în browser și pe telefon.

Ce rămâne sub controlul jucătorului

  • Jucătorul poate opri puntea în Settings → Game Activity, după care niciun joc nu mai poate vedea mssgs deloc.
  • Fiecare joc aprobat apare acolo exact cu permisiunile pe care le are, cu momentul ultimei activități și cu un buton Remove. Eliminarea e imediată: tokenul moare pe loc.
  • Un joc conectat din browser sau de pe telefon apare la Linked games cu un buton Disconnect. Și deconectarea e imediată: următoarea publicare a acelui backend primește un 410.
  • Puntea ascultă doar pe 127.0.0.1, niciodată în rețea.
  • Mesajele directe nu sunt dezvăluite niciodată, nici măcar cu servers.list.
  • Fiecare joc are un buget de 120 de cereri pe minut.

Bune practici

  • Cere scope-urile atunci când ai nevoie de ele, nu pe toate deodată la prima pornire.
  • Funcționează și fără mssgs: jucătorul nu e obligat să-l aibă.
  • Șterge-ți statusul când se termină jocul, în loc să aștepți TTL-ul.
  • Tratează un scope refuzat ca pe un rezultat normal, nu ca pe o eroare.

Construiește mai departe