---
title: "Game SDK: rodyk, ką žaidėjai žaidžia mssgs platformoje"
description: "Iš savo žaidimo skelbk „Playing“ būseną su mygtuku Join now ir tikrink narystę bendruomenėje. mssgs Game SDK natyviniams, naršyklės ir telefono žaidimams."
canonical: https://docs.mss.gs/lt/game-sdk
language: lt
---

# Parodyk, ką kas nors žaidžia

Leisk savo žaidimui pranešti mssgs, ką veikia žaidėjas. Draugai po jo vardu mato „Playing“, atidaro išsamią informaciją ir mygtuku Join now prisijungia prie to paties žaidimo. Tavo žaidimas taip pat gali patikrinti, ar žaidėjas yra tavo bendruomenėje.

## Ką gali padaryti

- **Skelbk žaidimo būseną** Žaidimas, ką žaidėjas daro, jo vaidmuo ir kiek užpildyta komanda.

- **Pridėk mygtuką Join now** Draugai vienu paspaudimu prisijungia prie to paties žaidimo, serverio ar lobby.

- **Tikrink narystę** Paklausk, ar žaidėjas yra tavo bendruomenėje ir kokius vaidmenis ten turi.

- **Kompiuteris, naršyklė ar telefonas** Natyviniai žaidimai naudoja vietinį tiltą; naršyklės ir telefono žaidimai eina per tavo backend.

Programėlėje

Būsena po vardu ir informacija, kurią ji atveria. Žaidimas paskelbė vieną JSON bloką; visa kita padaro programėlė.

- [Apžvalga](#overview)

- [Kliento paieška](#discover)

- [Leidimo prašymas](#authorize)

- [Žaidimo būsena](#activity)

- [Join now](#join)

- [Naršyklė ir telefonas](#linked)

- [Žinynas](#reference)

## Apžvalga

mssgs kompiuterio programėlė paleidžia nedidelį **vietinį HTTP tiltą**, su kuriuo bendrauja tame pačiame kompiuteryje esantis žaidimas. Tavo žaidimas niekada nesijungia prie mūsų serverių, niekada nemato paskyros slaptažodžio ar tokeno ir niekada negali skelbti žaidėjo vardu. Jis bendrauja su ta mssgs kopija, prie kurios žaidėjas jau prisijungęs, o ta kopija sprendžia, ką atsakyti.

Ką su juo gali daryti:

- Aptikti, kad mssgs įdiegta ir kas nors yra prisijungęs.

- Perskaityti, kas yra žaidėjas: user_guid, username, avataras.

- Paklausti „ar šis žaidėjas yra bendruomenėje X?“ ir kokį vaidmenį jis ten turi.

- Paskelbti būseną „Playing …“ su mygtuku **Join now** kitiems.

- Gauti prisijungimo duomenis, kai kas nors paspaudžia tą mygtuką.

### Du keliai

**Natyvinis kompiuterio žaidimas** bendrauja su vietiniu tiltu; tai aprašo kiti skyriai. Žaidimas **naršyklėje ar telefone** to tilto pasiekti negali. Tokiems žaidimams skelbia tavo paties backend, tiems žaidėjams, kurie susiejo savo mssgs paskyrą QR kodu arba aštuonių simbolių kodu: žr. [Naršyklės ir telefono žaidimai](#linked), o pirmasis pavyzdys yra [CozyCity](https://cozycity.net). Pati žaidimo būsena abiem atvejais yra tas pats blokas.

### Pagal numatytuosius nustatymus atskleidžiama minimaliai

Aprėptys tyčia nelygios. Jei tau reikia tik žinoti, „ar šis žmogus yra mūsų bendruomenėje“, prašai membership.query ir pats nurodai server_guid: gauni taip/ne ir jo vaidmenis toje bendruomenėje, o apie kitas jo bendruomenes nesužinai nieko. Visas sąrašas slypi už atskiros, aukštesnės aprėpties, kurią žaidėjas turi patvirtinti atskirai.

## Kliento paieška

Tiltas klauso tik 127.0.0.1 , pirmame laisvame nedidelio intervalo prievade. Bandyk juos iš eilės, kol kuris nors atsakys: **7440, 7441, 7442, 7443**. Kūrimo (development) mssgs versijos vietoj to klauso **7540–7543**, todėl bandomoji versija niekada neatsako į tikro žaidimo užklausas.

Tokeno nereikia, o atsakymas nieko nesako apie žaidėją, tik tai, kad mssgs čia yra ir ar kas nors prisijungęs.

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

Prieš eidamas toliau, patikrink product === "mssgs" ir api . Jei neatsako nė vienas iš keturių prievadų, mssgs neveikia. Tiesiog pasiūlyk įprastą patirtį, užuot vertęs žaidėją laukti.

## Leidimo prašymas

Viskam, išskyrus /hello , reikia tokeno, o tokenas atsiranda tik tada, kai žaidėjas patvirtina tavo žaidimą programėlės dialogo lange. Prašyk tik tų aprėpčių, kurias tikrai naudoji: žaidėjas mato kiekvieną atskirai, su paaiškinimu, ir gali kiekvieną atžymėti atskirai.

```bash
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"]
  }'
```

Atgal gauni {"status":"pending","request_id":"…","poll_after_ms":1000} , o žaidėjas mato dialogo langą. Tada periodiškai tikrink (polling), kol jis atsakys (prašymas nustoja galioti po 3 minučių):

```bash
curl http://127.0.0.1:7440/mssgs/v1/authorize/<request_id>

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

### Visada tikrink, ką iš tikrųjų gavai

Atsakyme esantis scopes sąrašas gali būti **trumpesnis** nei tas, kurio prašei: žaidėjas gali atžymėti atskiras aprėptis. Aukščiau esančiame pavyzdyje membership.query buvo atmesta. Tolesnius veiksmus grįsk tuo, ką sako atsakymas, o ne tuo, ko prašei, antraip susidursi su 403 MISSING_SCOPE , kurio nenumatei.

Išsaugok tokeną ir siųsk jį kaip Authorization: Bearer <token> . Jis išlieka po paleidimų iš naujo, todėl žaidėjas tavo žaidimą patvirtina vieną kartą, o ne kiekvienos sesijos metu. Jei vėliau autorizuosi iš naujo su jau suteiktomis aprėptimis, iškart gausi tą patį tokeną be jokio dialogo lango.

## Aprėptys ir privatumas

Penkios aprėptys atskleidžia labai skirtingą kiekį duomenų. Tai ne atsitiktinumas; tuo ir grindžiamas visas sumanymas. Prašyk kuo mažiau, eidamas šia lentele iš viršaus žemyn.

| Aprėptis | Ką leidžia | Ką atiduoda žaidėjas |
| --- | --- | --- |
| presence.write | **Rodyti, ką žaidžia** | Nieko. Ši aprėptis tik rašo; ji visai neskaito paskyros duomenų. |
| identity | **Kas yra žaidėjas** | user_guid, username, rodomas vardas, avataro URL. |
| staff | **Personalo / moderatoriaus žymos** | Dvi loginės reikšmės, papildomai prie identity. Atskirai, nes žaidimui, rodančiam vardą, nėra reikalo žinoti, kad žaidėjas moderuoja bendruomenes. |
| membership.query | **Patikrinti jau žinomą bendruomenę** | Tavo nurodytam server_guid: taip/ne, jos pavadinimas ir žaidėjo vaidmenys joje. Nieko apie jokią kitą bendruomenę. |
| servers.list | **Visos bendruomenės, kuriose jis yra** | Visas sąrašas: guid, pavadinimai, ikonos ir vaidmenys. Tai brangiausia aprėptis: prašyk jos tik tada, kai tikrai reikia. |

### Daugumai žaidimų užtenka dviejų

identity ir presence.write apima „kas tu esi“ ir „parodyk, ką žaidi“, o to reikia beveik kiekvienai integracijai. Pridėk membership.query , jei nori susieti apdovanojimą su naryste tavo bendruomenėje. servers.list beveik niekada neprireiks, o žaidėjas mato ją paryškintą raudonai.

## Narystės tikrinimas

Tai alternatyva prašymui „duok man visą sąrašą“. Nurodai savo bendruomenės server_guid (kurį jau žinai) ir gauni atsakymą tik apie ją.

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

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

# ne narys, ir nieko daugiau:
# {"server_guid":"…","member":false}
```

„Ne“ reiškia būtent tai ir nieko daugiau. Viename iškvietime gali perduoti iki 10 guid (pakartok server_guid arba atskirk juos kableliais), tada gausi masyvą results . Grupės @everyone niekada nėra roles sąraše: ji galioja kiekvienam nariui, todėl nieko tau nepasako.

## Žaidimo būsenos skelbimas

Vienas PUT įdeda eilutę „Playing …“ po žaidėjo vardu visur, kur jį mato jo bendruomenės.

```json
{
  "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" }
}
```

Privalomas tik name . Atsakymas nurodo, kiek laiko būsena galioja ir kaip dažnai siųsti heartbeat:

```json
{ "ok": true, "expires_in_ms": 90000, "heartbeat_every_ms": 30000 }
```

### Heartbeat, kitaip būsena dingsta

Būsena, kuri 90 sekundžių nerodo gyvybės ženklų, išvaloma automatiškai. Tai tyčia: jei tavo žaidimas nulūš, žaidėjas neliks „žaidžiantis“ ištisas valandas. Siųsk POST /mssgs/v1/activity/heartbeat kas 30 sekundžių, o tvarkingai išjungdamas žaidimą siųsk DELETE /mssgs/v1/activity .

### Žaidėjų skaičius ir vaidmuo

party.kind lemia, kuris sakinys rodomas, nes tie patys du skaičiai reiškia ne tą patį. Keturių žmonių komanda nėra serveris su keturiais žaidėjais.

| kind | Rodoma kaip | Kam |
| --- | --- | --- |
| party (numatytasis) | 3 of 4 in the party | būrys, komanda ar grupė |
| server | 4/100 players | žaidimo serveris (FiveM, bendruomenės serveris) |
| lobby | 4/100 players | lobby prieš prasidedant mačui |
| match | 4/100 players | vykstantis mačas ar raundas |

role (iki 48 simbolių) yra tai, *kuo* žaidėjas žaidžia: profesija, klasė ar personažas. Jam skirtas atskiras laukas, o ne dar vienas sakinys state , nes jis rodomas kaip etiketė šalia žaidėjų skaičiaus.

details ir state ribojami iki 128 simbolių kiekvienas, name iki 64. Eilučių lūžiai ir valdymo simboliai pašalinami. **Ikonos URL tyčia nepalaikomas**: jį parsisiųstų kiekvienas klientas, rodantis šią eilutę, o tai paverstų būseną švyturiu, pranešančiu tavo serveriui apie kiekvieną kiekvienos bendruomenės, kurioje yra žaidėjas, narį.

## Mygtukas Join now

Įdėk į savo veiklą (activity) bloką join , ir kiti nariai šalia būsenos gaus mygtuką **Join now**. Yra du būdai, ir juos galima derinti.

### 1. Paslaptis (natyviniams žaidimams)

Nustatyk {"join":{"secret":"raid-42"}} . Kai kas nors paspaudžia Join now, ši paslaptis pristatoma *jo paties* tavo žaidimo kopijai, jo paties kompiuteryje, susietai pagal tą patį game_id . Joks URL neatidaromas ir jokia schemos tvarkyklė nekviečiama. Tavo žaidimas ją pasiima taip:

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

Tikrink su ?since=<cursor> , kad kiekvieną įvykį pamatytum tik kartą. Jei paspaudusiojo žaidimas neveikia, niekas nepristatoma, ir tai gera priežastis pasiūlyti ir URL.

### 2. https URL (naršyklės žaidimams ir lobby nuorodoms)

Nustatyk {"join":{"url":"https://play.example.com/s/abc"}} , ir mygtukas atidarys tą nuorodą. **Priimamas tik https .** Savita schema ( steam:// , mygame:// , file:// ) atmetama: šis blokas atsiduria kiekvieno nario ekrane, o toks URL yra būdas priversti kieno nors kito kompiuterį iškviesti vietinę tvarkyklę su tavo pasirinktais argumentais.

### Viskas, kas yra join, yra vieša

Blokas join transliuojamas visiems, kurie mato žaidėjo būseną; tame ir yra mygtuko Join now esmė. Todėl laikyk jį lobby kodu, o ne prisijungimo duomenimis. Niekada nedėk į jį nieko, kas turi likti paslaptyje, ir nustatyk savo kodams galiojimo pabaigą.

## FiveM

FiveM kliento pusės Lua aplinkoje neturi HTTP, todėl resursas bendrauja su tiltu per **NUI**, CEF rodinį, kuris siunčia Origin. Tiltas šiuos origin priima aiškiai: https://cfx-nui-<resource> ir senesnį nui://<resource> . Įprasti tinklalapiai ir toliau atmetami, o puslapis atvirame internete negali apsimesti šiuo origin; jį nustato pati naršyklė.

```lua
-- NUI puslapis atlieka HTTP; Lua tik perduoda jam duomenis.
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: būsena nustoja galioti po 90 s
  end
end)
```

```javascript
const BASE = 'http://127.0.0.1:7440/mssgs/v1';   // bandyk 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']          // čia daugiau nieko nereikia
    })
  });
  const started = await res.json();
  if (started.status === 'approved') { return started.token; }

  // Dabar žaidėjas mssgs mato leidimo dialogo langą.
  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' } // tavo cfx.re nuoroda
    })
  });
});
```

Rezultatas: **Playing FiveM · Los Santos Roleplay · 4/100 players · Police**, su mygtuku Join now, kuris atidaro tavo cfx.re nuorodą.

### Prašyk tik presence.write

Žaidimo būsenai daugiau nieko nereikia: ši aprėptis nieko neskaito. Jei nori susieti apdovanojimą žaidime su naryste tavo mssgs bendruomenėje, pridėk membership.query ir nurodyk savo server_guid; apie kitas žaidėjo bendruomenes vis tiek nieko nesužinai.

### Serveris, kuriame žaidi, nėra automatiškai patikimas

Bet kuris FiveM serveris gali paleisti kliento resursus, todėl bet kuris serveris, prie kurio kas nors prisijungia, gali prašyti leidimo. Būtent todėl tarpe yra dialogo langas, kuriame nurodomas resursas: sprendžia žaidėjas, ne serveris.

## Naršyklės ir telefono žaidimai: susiejimas per tavo backend

Žaidimas naršyklės skirtuke ar telefone negali pasiekti aukščiau aprašyto tilto. Tiltas veikia žaidėjo kompiuteryje, o tarp jų stovi trys sienos: tiltas atmeta kiekvieną užklausą su naršyklės Origin , Chrome, prieš viešam puslapiui kreipiantis į 127.0.0.1 , parodo leidimo užklausą, Safari iškart atsisako, o telefonas apskritai neturi kelio į kompiuterio loopback.

Todėl kryptis apsiverčia. **Tavo paties backend jau žino, kas žaidžia, ir jis praneša mssgs** apie žaidėjus, kurie susiejo savo mssgs paskyrą su tavo žaidimu. Susiejimas patvirtinamas *mssgs programėlėje*, niekada ne tavo žaidime, ir jis sukuria ryšį, niekada ne sesiją: niekas toliau aprašyta negali nieko prijungti ar veikti žaidėjo vardu. Tavo žaidimo klientas niekada nemato rakto ir niekada nesikreipia į mss.gs. Pirmasis žaidimas šiuo keliu yra [CozyCity](https://cozycity.net), miesto kūrimo žaidimas, išleistas kaip WebGL puslapis ir iPhone programėlė be kompiuterio versijos; toliau pateikti pavyzdžiai yra būtent iš jo.

### 1. Užregistruok žaidimą

Užregistruok žaidimą [Game SDK registracijos puslapyje](https://mss.gs/lt/docs/game-sdk/register): savo game_id (pavyzdžiui, com.deverence.cozycity ), pavadinimą ir ikoną, kuriuos žaidėjas mato patvirtinimo lange, ir savo backend serverių pavadinimus (hostnames). Peržiūrime ją ten pat, o kai ji patvirtinama, tame pačiame puslapyje tavęs laukia **backend raktas**, parodomas vieną kartą; mes saugome tik jo santrauką (digest). Raktas turi būti tavo serveryje ir niekur kitur. Ten pat gali jį bet kada pakeisti nauju, o senasis galioja dar 24 valandas, kad diegimas spėtų pereiti.

[Užregistruok savo žaidimą](https://mss.gs/lt/docs/game-sdk/register)

Pavadinimas ir ikona lange **visada imami iš registracijos**, niekada iš užklausos. Kitaip sukčiavimo (phishing) nuoroda galėtų paversti susiejimo prašymą bet kokiu žaidimu. Serverių pavadinimai riboja, kur gali vesti join.url , žr. toliau.

### 2. Susiek žaidėją

Žaidėjas tavo žaidime pasirenka **Connect mssgs**. Tavo žaidimas klausia tavo backend, o backend klausia mūsų:

```bash
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 yra tavo paties nekintamas to žaidėjo ID (iki 128 simbolių), ne sesijos ar mačo; player_name (iki 64) yra tai, ką langas rodo kaip „Player: …“. Savo žaidimo klientui perduok tik link_code , qr_url ir deep_link . device_code yra tavo tikrinimo (polling) rankena ir lieka serveryje.

Tada tavo žaidimas iš karto parodo tris dalykus, nes žaidėjas gali būti bet kur:

- **QR kodą** iš qr_url . Telefonas su mssgs jį atidaro tiesiai programėlės patvirtinimo lange. Be programėlės jis patenka į mss.gs puslapį, kuris parodo kodą ir pasiūlo atsisiųsti programėlę.

- **Mygtuką „Open in mssgs“** su deep_link , skirtą kompiuterio naršyklei, šalia kurios veikia kompiuterio programėlė. Tai vienintelis išorinis URL, kurį tavo žaidimui kada nors reikės atidaryti.

- **Patį kodą**, dviem grupėmis po keturis simbolius, kurį reikia įvesti skiltyje **Settings → Game Activity → Link a game**. Abėcėlėje nėra 0/O ar 1/I, todėl jį įvedant retai suklystama.

Ką žaidėjas mato mssgs, langą, kurį programėlė sudaro pagal registraciją:

### 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**

Tuo metu tavo backend tikrina kas interval sekundžių (į dažnesnes užklausas atsakoma 429 SLOW_DOWN ), kol būsena pasikeičia. Kodas veikia vieną kartą ir nustoja galioti po dešimties minučių:

```bash
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"}      # žaidėjas pasirinko Not now
# {"status":"expired"}
# {"status":"linked","link_guid":"…","user":{"user_guid":"62e377…"}}
```

Išsaugok link_guid prie savo žaidėjo; nuo šiol tai adresas, kuriam skelbi. Atsakyme yra tik user_guid ; username pridedamas tik tada, kai tavo registracija turi aprėptį identity.link , ir nieko daugiau. Antras patvirtinimas tam pačiam player_ref **pakeičia** ankstesnį susiejimą, todėl vienas tavo žaidimo žaidėjas yra viena mssgs paskyra. Ta pati mssgs paskyra gali būti susieta su keliais žaidimais ir su keliais vieno žaidimo player_ref (šeimos iPad).

### 3. Paskelbk žaidimo būseną

Tas pats blokas kaip ir [tilte](#activity), su tomis pačiomis taisyklėmis ir ribomis, tik dabar kiekvienam susiejimui atskirai ir su tavo backend raktu:

```json
{
  "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=…" }
  }
}
```

```bash
200 {"published":true,"changed":true}     # blokas pasikeitė ir buvo transliuotas
200 {"published":true,"changed":false}    # toks pat kaip išsaugotas; atnaujintas tik TTL
204                                        # išsaugota, bet žaidėjas dabar nėra prisijungęs prie mssgs
410 {"error":"LINK_REVOKED"}               # žaidėjas atsijungė: pašalink susiejimą
```

Į 200 ir 204 reaguok vienodai: išsaugota. { "activity": null } išvalo bloką, siųsk tai, kai žaidėjas išeina. Vienas skirtumas nuo tilto: join.url serveris turi būti vienas iš tavo užregistruotų backend serverių (arba jo subdomenas), kitaip gausi 400 INVALID_PAYLOAD . Taigi backend negali pridėti prie žaidėjo būsenos mygtuko Join now, vedančio ten, kur tas žaidėjas niekada nežaidė.

### Heartbeat kas 60 sekundžių, TTL 120

Paskelbta būsena be naujos žinutės galioja **120 sekundžių** ir tada pati išnyksta. Todėl tą patį bloką siųsk kas 60 sekundžių; nepasikeitęs blokas nieko nekainuoja ir tik atnaujina TTL. Jei tavo heartbeat sustoja, sustoja ir eilutė „Playing …“, ir būtent to ir siekiama.

Kai prisijungę šimtai žaidėjų, siųsk heartbeat vienu iškvietimu, iki 100 elementų vienu metu. Kiekvienas elementas gauna savo būseną, todėl vienas žaidėjas, atsijungęs mssgs, niekada nesustabdo kitų devyniasdešimt devynių:

```json
{ "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 } ] }
```

### Kaip rodoma būsena

- Lygiai taip pat kaip būsena iš tilto: **Playing CozyCity · Lantern Hollow · 6/40 players**, su Join now, kai yra join.url . Serverio pusėje blokas pažymimas via: "backend" , todėl klientas gali pridėti „Shared by the game's server“.

- **Tik kol žaidėjas prisijungęs prie mssgs.** Kai neatidarytas joks mssgs klientas, paskyra yra neprisijungusi ir tokia lieka; tavo backend negali padaryti, kad kas nors atrodytų esantis. Tai taip pat neleidžia šiam keliui tapti švyturiu „ar René prie kompiuterio“.

- Pirmenybė: **žaidimas programėlėje > žaidimas per tiltą > tavo backend**. Jei žaidėjas mssgs viduje sėda žaisti šachmatų, o tavo backend toliau siunčia heartbeat, laimi šachmatai, o ne tas, kuris rašė paskutinis.

### Atsijungimas

Žaidėjas mato kiekvieną susiejimą skiltyje **Settings → Game Activity → Linked games**, su tavo ikona ir pavadinimu, žaidėjo vardu iš tavo žaidimo, susiejimo ir paskutinio skelbimo laiku bei mygtuku **Disconnect**. Po to į tavo kitą skelbimą atsakoma 410 LINK_REVOKED ; taip tavo žaidimas apie tai sužino. Pašalink link_guid ir vėl pasiūlyk „Connect mssgs“. Iš savo pusės susiejimą užbaigsi su DELETE /game-sdk/v1/links/{link_guid} .

### Apribojimai

Vienam susiejimui *pakeitimas* skaičiuojamas daugiausia kas 2 sekundes; nepasikeitęs heartbeat nemokamas. Vienam raktui tenka 600 užklausų per minutę, o paketo elementai skaičiuojami atskirai: heartbeat 300 žaidėjų kas 60 sekundžių sunaudoja 5 iš 600.

## Endpoint’ų žinynas: tiltas

Bazinis URL http://127.0.0.1:<port> . Visiems, išskyrus pirmus tris, reikia Authorization: Bearer <token> .

| Metodas | Kelias | Scope | Ką daro |
| --- | --- | --- | --- |
| GET | /mssgs/v1/hello | nėra | Ar mssgs čia, ką ji palaiko ir ar kas nors prisijungęs. Vienintelis maršrutas, kuriam nereikia tokeno, ir jis nieko nesako apie žaidėją. |
| POST | /mssgs/v1/authorize | nėra | Paprašyk žaidėjo leidimo. Programėlėje atidaro dialogo langą ir grąžina request_id, kurį reikia tikrinti. |
| GET | /mssgs/v1/authorize/:request_id | nėra | pending, approved (su tokenu), denied arba expired. |
| GET | /mssgs/v1/me | identity | Prisijungęs žaidėjas. is_staff / is_moderator prideda tik su aprėptimi staff. |
| GET | /mssgs/v1/membership | membership.query | Narystė perduotose server_guid reikšmėse (iki 10, pakartotose arba atskirtose kableliais). |
| GET | /mssgs/v1/servers | servers.list | Visos žaidėjo bendruomenės su jo vaidmenimis. Asmeninės žinutės niekada neįtraukiamos. |
| PUT | /mssgs/v1/activity | presence.write | Paskelbk bloką „Playing …“. Grąžina TTL ir kaip dažnai siųsti heartbeat. |
| POST | /mssgs/v1/activity/heartbeat | presence.write | Palaikyk paskelbtą veiklą gyvą, nesiųsdamas jos iš naujo. |
| DELETE | /mssgs/v1/activity | presence.write | Išvalyk ją iškart, tvarkingai išjungiant. |
| GET | /mssgs/v1/events | presence.write | Prisijungimo perdavimai, skirti tavo žaidimui. Tikrink su ?since=<cursor>. |
| GET | /mssgs/v1/session | nėra | Ką turi šis tokenas: game_id, suteiktos aprėptys, ar kas nors prisijungęs. |
| DELETE | /mssgs/v1/session | nėra | Grąžink leidimą. Tas pats poveikis, kaip žaidėjui jį atšaukus skiltyje Settings. |

## Endpoint’ų žinynas: susieti backend serveriai

Bazinis URL https://ams1-gateway.mss.gs . Kiekvienam maršrutui reikia Authorization: Bearer <backend key> ir aprėpties activity.write tavo registracijoje; atsakymai siunčiami su Cache-Control: no-store . Kviesk juos iš savo serverio, niekada iš žaidimo kliento.

| Metodas | Kelias | Ką daro |
| --- | --- | --- |
| POST | /game-sdk/v1/link/start | Pradėk susiejimą vienam iš savo žaidėjų ({ player_ref, player_name? }). Grąžina link_code, device_code, qr_url, deep_link, expires_in ir interval. |
| POST | /game-sdk/v1/link/poll | { device_code } → pending, denied, expired arba linked su link_guid ir user. |
| DELETE | /game-sdk/v1/links/{link_guid} | Užbaik susiejimą iš savo pusės. Žaidėjas gali padaryti tą patį skiltyje Settings. |
| PUT | /game-sdk/v1/links/{link_guid}/activity | Paskelbk bloką „Playing …“ vienam žaidėjui; { "activity": null } jį išvalo. |
| POST | /game-sdk/v1/activity/batch | Tas pats iki 100 žaidėjų vienu iškvietimu. Kiekvienas elementas atsako atskirai. |

## Klaidų kodai

Klaidos grąžinamos kaip {"error":"CODE","message":"…"} su atitinkamu HTTP būsenos kodu.

| Kodas | Reikšmė |
| --- | --- |
| 401 UNAUTHORIZED | Tokeno nėra arba jis nežinomas; pirmiausia autorizuokis. |
| 403 MISSING_SCOPE | Žaidėjas nesuteikė šio leidimo. Galbūt jį atžymėjo. |
| 403 ORIGIN_NOT_ALLOWED | Užklausoje buvo naršyklės Origin. Žr. „Tik natyviniai žaidimai“ toliau. |
| 409 NOT_SIGNED_IN | mssgs veikia, bet niekas neprisijungęs. |
| 429 RATE_LIMITED | Daugiau nei 120 užklausų per minutę iš vieno žaidimo. |
| 400 INVALID_GAME_ID | game_id gali sudaryti tik raidės, skaitmenys, taškas, brūkšnelis ar pabraukimo brūkšnys. |
| 400 TOO_MANY_GUIDS | Daugiausia 10 server_guid reikšmių viename membership iškvietime. |

### Susieti backend serveriai

Backend maršrutai naudoja tą pačią formą. Paketo užklausoje būsena grąžinama kiekvienam elementui atskirai lauke results , todėl vienas užbaigtas susiejimas niekada nesugadina viso iškvietimo.

| Kodas | Reikšmė |
| --- | --- |
| 401 INVALID_BACKEND_KEY | Nežinomas raktas arba raktas, pakeistas daugiau nei prieš 24 valandas. |
| 403 SCOPE_NOT_GRANTED | Tavo registracija neturi aprėpties, kurios reikia šiam maršrutui. |
| 400 INVALID_PAYLOAD | Netinkamas užklausos turinys, daugiau nei 100 paketo elementų arba join.url, kurio serveris nėra vienas iš tavo užregistruotų backend serverių. |
| 400 INVALID_ACTIVITY | Po normalizavimo neliko tinkamo pavadinimo. |
| 410 LINK_REVOKED | Susiejimas baigtas, vienoje ar kitoje pusėje. Pašalink jį ir vėl pasiūlyk „Connect mssgs“. |
| 429 SLOW_DOWN | link/poll tikrinai dažniau nei kas interval. |
| 429 RATE_LIMITED | Vieno susiejimo pakeitimas per 2 sekundes nuo ankstesnio arba daugiau nei 600 užklausų per minutę tavo raktu. |
| 503 LINK_STORE_UNAVAILABLE | Laikina problema mūsų pusėje. Bandyk dar kartą su kitu heartbeat. |

## Saugumas

### Tik natyviniai žaidimai

Užklausos su tinklalapio Origin atmetamos su 403 ORIGIN_NOT_ALLOWED . Jei bet kuris tinklalapis galėtų aptikti, kad naudoji mssgs, ir iškviesti leidimo dialogo langą, tai būtų spraga sekimui (fingerprinting) ir sukčiavimui, o ne funkcija. Natyvinis žaidimas visai nesiunčia Origin, todėl jo tai neliečia, o paties žaidimo įterpta naršyklė leidžiama pagal pavadinimą, žr. [FiveM](#fivem). Jei kuri naršyklės ar telefono žaidimą, su tiltu nebendrauji: susietų žaidėjų būsenas skelbia tavo paties backend, žr. [Naršyklės ir telefono žaidimai](#linked).

### Ką žaidėjas išlaiko savo rankose

- Žaidėjas gali išjungti tiltą skiltyje **Settings → Game Activity**, ir tada joks žaidimas apskritai nemato mssgs.

- Kiekvienas patvirtintas žaidimas ten išvardytas su tiksliais jo turimais leidimais, paskutinio aktyvumo laiku ir mygtuku **Remove**. Pašalinimas veikia iškart: tokenas iš karto nustoja galioti.

- Susietas naršyklės ar telefono žaidimas išvardytas skiltyje **Linked games** su mygtuku **Disconnect**. Atsijungimas taip pat veikia iškart: kitas to backend skelbimas gauna 410 .

- Tiltas klauso tik 127.0.0.1, niekada tinkle.

- Asmeninės žinutės niekada neatskleidžiamos, net su servers.list.

- Kiekvienas žaidimas turi 120 užklausų per minutę limitą.

### Geroji praktika

- Prašyk aprėpčių tada, kai jų reikia, o ne visų iš karto per pirmą paleidimą.

- Veik ir be mssgs: žaidėjas neprivalo jos turėti.

- Išvalyk būseną, kai žaidimas baigiasi, užuot laukęs TTL.

- Atmestą aprėptį laikyk įprastu rezultatu, o ne klaida.

## Kurk toliau
