Przejdź do treści głównej
Deweloperzy Game SDK

Pokaż, w co ktoś gra

Niech twoja gra przekazuje mssgs, co robi gracz. Znajomi widzą „Playing” pod nazwą gracza, otwierają szczegóły i przyciskiem Join now dołączają do tej samej gry. Twoja gra może też sprawdzić, czy gracz jest w twojej społeczności.

Co możesz z tym zrobić

  • Publikuj status gryGra, co gracz właśnie robi, jego rola i jak zapełniona jest drużyna.
  • Dodaj przycisk Join nowZnajomi jednym kliknięciem dołączają do tej samej gry, serwera albo lobby.
  • Sprawdzaj członkostwoZapytaj, czy gracz jest w twojej społeczności i z jakimi rolami.
  • Komputer, przeglądarka albo telefonGry natywne korzystają z lokalnego mostka; gry w przeglądarce i na telefonie przechodzą przez twój backend.

W aplikacji

daniPlaying Space Raiders
Space RaidersPlayed by dani
Sector 7
W rajdzie
3 of 4 in the party · for 12 min
Strzelec

Status pod nazwą i szczegóły, które otwiera. Gra opublikowała jeden blok JSON; resztą zajmuje się aplikacja.

Przegląd

Aplikacja desktopowa mssgs uruchamia mały lokalny mostek HTTP, z którym rozmawia gra na tym samym komputerze. Twoja gra nigdy nie łączy się z naszymi serwerami, nigdy nie widzi hasła ani tokenu konta i nigdy nie może publikować w imieniu gracza. Rozmawia z kopią mssgs, w której gracz jest już zalogowany, a ta kopia decyduje, co odpowiedzieć.

Co możesz z nim zrobić:

  • Wykryć, że mssgs jest zainstalowany i ktoś jest zalogowany.
  • Odczytać, kim jest gracz: user_guid, username, awatar.
  • Zapytać „czy ten gracz jest w społeczności X?” i jaką rolę tam ma.
  • Opublikować status „Playing …” z przyciskiem Join now dla innych.
  • Odebrać dane do dołączenia, gdy ktoś naciśnie ten przycisk.

Dwie drogi

Natywna gra desktopowa rozmawia z lokalnym mostkiem; to opisują kolejne sekcje. Gra w przeglądarce albo na telefonie nie może połączyć się z tym mostkiem. W jej przypadku publikuje twój własny backend, dla graczy, którzy połączyli swoje konto mssgs kodem QR albo ośmioznakowym kodem: zobacz Gry w przeglądarce i na telefonie, z CozyCity jako pierwszym przykładem. Sam status gry to w obu przypadkach ten sam blok.

Domyślnie minimum ujawnianych danych

Zakresy celowo nie są równe. Jeśli potrzebujesz tylko wiedzieć, „czy ta osoba jest w naszej społeczności”, prosisz o membership.query i podajesz konkretny server_guid: dostajesz tak/nie oraz role tej osoby w tej społeczności i nie dowiadujesz się niczego o pozostałych jej społecznościach. Pełna lista jest za osobnym, wyższym zakresem, który gracz musi zatwierdzić oddzielnie.

Wyszukiwanie klienta

Mostek nasłuchuje tylko na 127.0.0.1, na pierwszym wolnym porcie z niewielkiego zakresu. Próbuj ich po kolei, aż któryś odpowie: 7440, 7441, 7442, 7443. Wersje deweloperskie mssgs nasłuchują zamiast tego na 7540–7543, więc wersja testowa nigdy nie odpowiada na wywołania prawdziwej gry.

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

Token nie jest potrzebny, a odpowiedź nie mówi nic o graczu, tylko to, że mssgs tu jest i czy ktoś jest zalogowany.

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

Sprawdź product === "mssgs" i api, zanim pójdziesz dalej. Jeśli żaden z czterech portów nie odpowiada, mssgs nie działa. Wtedy po prostu działaj jak zwykle, zamiast kazać graczowi czekać.

Prośba o zgodę

Wszystko poza /hello wymaga tokenu, a token istnieje dopiero wtedy, gdy gracz zatwierdzi twoją grę w oknie dialogowym w aplikacji. Proś tylko o zakresy, których naprawdę używasz: gracz widzi każdy z nich osobno, z wyjaśnieniem, i może odznaczyć każdy z osobna.

1. Poproś o zgodę
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"]
  }'

Dostajesz z powrotem {"status":"pending","request_id":"…","poll_after_ms":1000}, a gracz widzi okno dialogowe. Potem odpytuj, aż odpowie (prośba wygasa po 3 minutach):

2. Odpytaj o odpowiedź
curl http://127.0.0.1:7440/mssgs/v1/authorize/<request_id>

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

Zawsze sprawdzaj, co faktycznie dostajesz

Lista scopes w odpowiedzi może być krótsza niż ta, o którą prosisz: gracz może odznaczyć poszczególne zakresy. W przykładzie powyżej membership.query został odrzucony. Uzależniaj dalsze kroki od tego, co mówi odpowiedź, a nie od tego, o co prosisz, bo inaczej trafisz na 403 MISSING_SCOPE, którego nikt nie przewidział.

Zapisz token i wysyłaj go jako Authorization: Bearer <token>. Przetrwa restarty, więc gracz zatwierdza twoją grę raz, a nie w każdej sesji. Jeśli później ponownie autoryzujesz grę z zakresami, które zostały już przyznane, od razu dostajesz ten sam token, bez okna dialogowego.

Zakresy i prywatność

Pięć zakresów ujawnia bardzo różne ilości danych. To nie przypadek, na tym polega cały projekt. Proś o jak najmniej, zaczynając od góry tej tabeli.

Zakres Na co pozwala Co oddaje gracz
presence.write Pokazywanie, w co gra Nic. Ten zakres tylko zapisuje; nie odczytuje żadnych danych konta.
identity Kim jest gracz user_guid, username, nazwa wyświetlana, URL awatara.
staff Flagi personelu / moderatora Dwie wartości logiczne, oprócz identity. Osobno, bo gra, która wyświetla nazwę, nie ma powodu wiedzieć, że gracz moderuje społeczności.
membership.query Sprawdzenie znanej ci społeczności Dla podanego przez ciebie server_guid: tak/nie, jej nazwa i role, które ma tam gracz. Nic o żadnej innej społeczności.
servers.list Wszystkie społeczności gracza Pełna lista: guidy, nazwy, ikony i role. To ten kosztowny zakres: proś o niego tylko wtedy, gdy naprawdę go potrzebujesz.

Większości gier wystarczą dwa

identity i presence.write obejmują „kim jesteś” i „pokaż, w co grasz”, czyli prawie każdą integrację. Dodaj membership.query, jeśli chcesz powiązać nagrodę z członkostwem w twojej społeczności. servers.list prawie nigdy nie jest potrzebny, a gracz widzi go wyróżniony na czerwono.

Sprawdzanie członkostwa

To alternatywa dla „daj mi całą listę”. Podajesz server_guid własnej społeczności (który już znasz) i dostajesz odpowiedź wyłącznie o niej.

Jedna społeczność
curl -H "Authorization: Bearer $TOKEN" \
  "http://127.0.0.1:7440/mssgs/v1/membership?server_guid=ca94ecc…f4g02"

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

# nie jest członkiem i nic poza tym:
# {"server_guid":"…","member":false}

„Nie” znaczy dokładnie tyle i nic więcej. W jednym wywołaniu możesz podać do 10 guidów (powtórz server_guid albo rozdziel je przecinkami), a wtedy dostajesz tablicę results. Grupy @everyone nigdy nie ma w roles: dotyczy każdego członka, więc nic ci nie mówi.

Publikowanie statusu gry

Jedno żądanie PUT umieszcza linię „Playing …” pod nazwą gracza, wszędzie tam, gdzie widzą go jego społeczności.

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

Wymagane jest tylko name. Odpowiedź mówi, jak długo status żyje i jak często wysyłać heartbeat:

Odpowiedź
{ "ok": true, "expires_in_ms": 90000, "heartbeat_every_ms": 30000 }

Heartbeat, inaczej status znika

Status bez oznak życia przez 90 sekund jest usuwany automatycznie. To celowe: jeśli gra się zawiesi, gracz nie wisi potem godzinami jako „grający”. Wysyłaj POST /mssgs/v1/activity/heartbeat co 30 sekund, a DELETE /mssgs/v1/activity przy czystym zamknięciu gry.

Liczba graczy i rola

party.kind decyduje, jakie zdanie się wyświetli, bo te same dwie liczby nie znaczą tego samego. Czteroosobowa drużyna to nie serwer z czterema graczami.

kind Wyświetla się jako Dla
party (domyślnie)3 of 4 in the partydrużyna, ekipa albo grupa
server4/100 playersserwer gry (FiveM, serwer społeczności)
lobby4/100 playerslobby przed rozpoczęciem meczu
match4/100 playerstrwający mecz albo runda

role (do 48 znaków) to to, kim gra gracz: zawód, klasa albo postać. Ma własne pole zamiast kolejnego zdania w state, bo wyświetla się jako etykieta obok liczby graczy.

details i state mają limit 128 znaków każde, name 64. Podziały wiersza i znaki sterujące są usuwane. Adres URL ikony celowo nie jest obsługiwany: pobierałby go każdy klient, który wyświetla tę linię, a to zmieniłoby status w piksel śledzący, który zgłasza twojemu serwerowi każdego członka każdej społeczności, w której jest gracz.

Przycisk Join now

Umieść w aktywności blok join, a inni członkowie dostaną obok statusu przycisk Join now. Są dwa sposoby i można je łączyć.

1. Sekret (dla gier natywnych)

Ustaw {"join":{"secret":"raid-42"}}. Gdy ktoś naciśnie Join now, ten sekret trafia do jego własnej kopii twojej gry, na jego własnym komputerze, dopasowanej po tym samym game_id. Żaden URL nie jest otwierany i żaden handler schematu nie jest wywoływany. Twoja gra odbiera go tak:

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

Odpytuj z ?since=<cursor>, żeby każde zdarzenie zobaczyć tylko raz. Jeśli gra osoby, która nacisnęła przycisk, nie jest uruchomiona, nic nie zostaje dostarczone, i to dobry powód, żeby podać też URL.

2. URL https (dla gier webowych i linków do lobby)

Ustaw {"join":{"url":"https://play.example.com/s/abc"}}, a przycisk otworzy ten link. Akceptowany jest tylko https. Własny schemat (steam://, mygame://, file://) jest odrzucany: ten blok trafia na ekran każdego członka, a taki URL to sposób, by zmusić cudzy komputer do wywołania lokalnego handlera z wybranymi przez ciebie argumentami.

Wszystko w join jest publiczne

Blok join jest rozsyłany do wszystkich, którzy widzą status gracza; na tym polega cały sens przycisku Join now. Traktuj go więc jak kod lobby, a nie jak dane uwierzytelniające. Nigdy nie umieszczaj w nim niczego, co musi pozostać tajne, i ustawiaj wygasanie kodów.

FiveM

FiveM nie ma HTTP w swoim środowisku Lua po stronie klienta, więc zasób rozmawia z mostkiem przez NUI, widok CEF, który wysyła nagłówek Origin. Mostek jawnie akceptuje te originy: https://cfx-nui-<resource> i starszy nui://<resource>. Zwykłe strony internetowe nadal są odrzucane, a strona w otwartym internecie nie może podszyć się pod ten origin; ustawia go sama przeglądarka.

client.lua: poproś NUI o publikację
-- Strona NUI wykonuje HTTP; Lua tylko przekazuje jej dane.
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: status wygasa po 90 s
  end
end)
nui.js
const BASE = 'http://127.0.0.1:7440/mssgs/v1';   // próbuj 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']          // nic więcej nie jest tu potrzebne
    })
  });
  const started = await res.json();
  if (started.status === 'approved') { return started.token; }

  // Gracz widzi teraz w mssgs okno z prośbą o zgodę.
  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' } // twój link cfx.re
    })
  });
});

Efekt: Playing FiveM · Los Santos Roleplay · 4/100 players · Police, z przyciskiem Join now, który otwiera twój link cfx.re.

Proś tylko o presence.write

Status gry nie potrzebuje niczego więcej: ten zakres niczego nie odczytuje. Jeśli chcesz powiązać nagrodę w grze z członkostwem w twojej społeczności mssgs, dodaj membership.query i podaj server_guid swojej społeczności; nadal nie dowiadujesz się niczego o innych społecznościach gracza.

Serwer, na którym grasz, nie jest automatycznie zaufany

Każdy serwer FiveM może uruchamiać zasoby klienckie, więc każdy serwer, do którego ktoś dołączy, może poprosić o zgodę. Właśnie dlatego pośrodku stoi okno dialogowe z nazwą zasobu: decyduje gracz, nie serwer.

Gry w przeglądarce i na telefonie: połączenie przez twój backend

Gra w karcie przeglądarki albo na telefonie nie może połączyć się z opisanym wyżej mostkiem. Mostek działa na komputerze gracza, a po drodze stoją trzy przeszkody: mostek odrzuca każde żądanie z nagłówkiem Origin przeglądarki, Chrome pyta o zgodę, zanim publiczna strona pobierze coś z 127.0.0.1, a Safari od razu odmawia, telefon zaś w ogóle nie ma drogi do loopbacku komputera.

Dlatego kierunek się odwraca. Twój własny backend już wie, kto gra, i to on informuje mssgs, w przypadku graczy, którzy połączyli swoje konto mssgs z twoją grą. Zgodę na połączenie gracz daje w aplikacji mssgs, nigdy w twojej grze, i powstaje w ten sposób połączenie, nigdy sesja: nic poniżej nie może nikogo zalogować ani działać w imieniu gracza. Klient twojej gry nigdy nie widzi klucza i nigdy nie łączy się z mss.gs. Pierwszą grą na tej ścieżce jest CozyCity, gra o budowaniu miasta dostępna jako strona WebGL i aplikacja na iPhone’a, bez wersji desktopowej; poniższe przykłady pochodzą właśnie z niej.

1. Zarejestruj grę

Zarejestruj grę na stronie rejestracji Game SDK: podaj swój game_id (na przykład com.deverence.cozycity), nazwę i ikonę, które gracz zobaczy w oknie zatwierdzania, oraz nazwy hostów swojego backendu. Sprawdzamy ją na miejscu, a po zatwierdzeniu na tej samej stronie czeka twój klucz backendu, pokazany tylko raz; my przechowujemy wyłącznie jego skrót. Klucz powinien być na twoim serwerze i nigdzie indziej. Możesz go tam w każdej chwili zrotować, a stary pozostaje ważny przez 24 godziny, żeby wdrożenie mogło spokojnie przejść.

Zarejestruj grę

Nazwa i ikona w tym oknie zawsze pochodzą z rejestracji, nigdy z żądania. Inaczej link phishingowy mógłby przebrać prośbę o połączenie za dowolną grę. Nazwy hostów wyznaczają, dokąd może prowadzić join.url, zobacz niżej.

2. Połącz gracza

Gracz wybiera w twojej grze Connect mssgs. Gra pyta twój backend, a backend pyta nas:

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 to twój własny stały identyfikator tego gracza (do 128 znaków), a nie sesji czy meczu; player_name (do 64) to to, co okno pokazuje jako „Player: …”. Klientowi gry przekaż tylko link_code, qr_url i deep_link. device_code służy do odpytywania i zostaje na serwerze.

Następnie gra pokazuje jednocześnie trzy rzeczy, bo gracz może być gdziekolwiek:

  • Kod QR z qr_url. Telefon z mssgs otwiera go od razu w oknie zatwierdzania w aplikacji. Bez aplikacji trafia na stronę w mss.gs, która pokazuje kod i proponuje pobranie.
  • Przycisk „Open in mssgs” z deep_link, dla przeglądarki na komputerze, na którym działa aplikacja desktopowa. To jedyny zewnętrzny URL, jaki twoja gra kiedykolwiek musi otworzyć.
  • Sam kod, w dwóch grupach po cztery znaki, do wpisania w Settings → Game Activity → Link a game. W alfabecie nie ma 0/O ani 1/I, więc przy przepisywaniu rzadko zdarzają się pomyłki.

Co gracz widzi w mssgs, w oknie, które aplikacja buduje na podstawie rejestracji:

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

W tym czasie twój backend odpytuje co interval sekund (na częstsze zapytania odpowiedzią jest 429 SLOW_DOWN), aż status się zmieni. Kod działa raz i wygasa po dziesięciu minutach:

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"}      # gracz wybrał Not now
# {"status":"expired"}
# {"status":"linked","link_guid":"…","user":{"user_guid":"62e377…"}}

Zapisz link_guid przy swoim graczu; od teraz to adres, pod który publikujesz. Odpowiedź zawiera tylko user_guid; username dochodzi tylko wtedy, gdy twoja rejestracja ma zakres identity.link, i nic ponad to. Drugie zatwierdzenie dla tego samego player_ref zastępuje wcześniejsze połączenie, więc jeden gracz twojej gry to jedno konto mssgs. To samo konto mssgs może być połączone z kilkoma grami i z kilkoma player_ref jednej gry (rodzinny iPad).

3. Opublikuj status gry

Ten sam blok co przy mostku, z tymi samymi zasadami i limitami, tylko teraz dla każdego połączenia osobno i z twoim kluczem backendu:

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=…" }
  }
}
Odpowiedzi
200 {"published":true,"changed":true}     # blok się zmienił i został rozesłany
200 {"published":true,"changed":false}    # identyczny z zapisanym; odświeżono tylko TTL
204                                        # zapisany, ale gracz nie jest teraz online w mssgs
410 {"error":"LINK_REVOKED"}               # gracz się rozłączył: usuń połączenie

Traktuj 200 i 204 tak samo: zapisano. { "activity": null } czyści blok, wyślij to, gdy gracz wychodzi. Jedna różnica względem mostka: host w join.url musi być jednym z twoich zarejestrowanych backendów (albo jego subdomeną), inaczej dostajesz 400 INVALID_PAYLOAD. Dzięki temu backend nie może dodać do statusu gracza przycisku Join now, który prowadzi tam, gdzie ten gracz nigdy nie grał.

Heartbeat co 60 sekund, TTL 120

Opublikowany status żyje 120 sekund bez nowej wiadomości, a potem sam znika. Wysyłaj więc ten sam blok co 60 sekund; niezmieniony blok nic nie kosztuje i tylko odświeża TTL. Jeśli heartbeat ustanie, zniknie też linia „Playing …”, i właśnie o to chodzi.

Przy setkach graczy online wysyłaj heartbeat jednym wywołaniem, do 100 pozycji naraz. Każda pozycja dostaje własny status, więc jeden gracz, który rozłączył się w mssgs, nigdy nie zatrzyma pozostałych dziewięćdziesięciu dziewięciu:

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

Jak wyświetla się status

  • Tak samo jak status z mostka: Playing CozyCity · Lantern Hollow · 6/40 players, z Join now, gdy jest join.url. Serwer oznacza blok jako via: "backend", więc klient może dopisać „Shared by the game's server”.
  • Tylko wtedy, gdy gracz jest online w mssgs. Bez otwartego klienta mssgs konto jest offline i takie pozostaje; twój backend nie może sprawić, że ktoś wygląda na obecnego. Dzięki temu ta droga nie staje się też sygnałem „czy René siedzi przy komputerze”.
  • Pierwszeństwo: gra w aplikacji > gra przez mostek > twój backend. Jeśli gracz zasiądzie do szachów w mssgs, a twój backend dalej wysyła heartbeat, wygrywają szachy, a nie ten, kto zapisał ostatni.

Rozłączanie

Gracz widzi każde połączenie w Settings → Game Activity → Linked games, z twoją ikoną i nazwą, nazwą gracza z twojej gry, datą połączenia i ostatniej publikacji oraz przyciskiem Disconnect. Potem na twoją następną publikację odpowiedzią jest 410 LINK_REVOKED; tak twoja gra się o tym dowiaduje. Usuń link_guid i znów zaproponuj „Connect mssgs”. Ze swojej strony kończysz połączenie przez DELETE /game-sdk/v1/links/{link_guid}.

Limity

Na jedno połączenie zmiana liczy się najwyżej co 2 sekundy; niezmieniony heartbeat jest darmowy. Na klucz przypada 600 żądań na minutę, a pozycje żądania zbiorczego liczą się osobno: heartbeat dla 300 graczy co 60 sekund zużywa 5 z 600.

Referencja endpointów: mostek

Bazowy URL http://127.0.0.1:<port>. Wszystko poza pierwszymi trzema wymaga Authorization: Bearer <token>.

Metoda Ścieżka Scope Co robi
GET /mssgs/v1/hello brak Czy mssgs tu jest, co obsługuje i czy ktoś jest zalogowany. Jedyna trasa, która nie wymaga tokenu, i nie mówi nic o graczu.
POST /mssgs/v1/authorize brak Poproś gracza o zgodę. Wyświetla okno dialogowe w aplikacji i zwraca request_id do odpytywania.
GET /mssgs/v1/authorize/:request_id brak pending, approved (z tokenem), denied albo expired.
GET /mssgs/v1/me identity Zalogowany gracz. Dodaje is_staff / is_moderator tylko z zakresem staff.
GET /mssgs/v1/membership membership.query Członkostwo w podanych wartościach server_guid (do 10, powtórzonych albo rozdzielonych przecinkami).
GET /mssgs/v1/servers servers.list Wszystkie społeczności gracza z jego rolami. Wiadomości prywatne nigdy nie są uwzględniane.
PUT /mssgs/v1/activity presence.write Opublikuj blok „Playing …”. Zwraca TTL i to, jak często wysyłać heartbeat.
POST /mssgs/v1/activity/heartbeat presence.write Utrzymaj opublikowaną aktywność bez ponownego wysyłania.
DELETE /mssgs/v1/activity presence.write Wyczyść ją natychmiast, przy czystym zamknięciu.
GET /mssgs/v1/events presence.write Zdarzenia dołączenia skierowane do twojej gry. Odpytuj z ?since=<cursor>.
GET /mssgs/v1/session brak Co ma ten token: game_id, przyznane zakresy, czy ktoś jest zalogowany.
DELETE /mssgs/v1/session brak Oddaj zgodę. Ten sam efekt, co cofnięcie jej przez gracza w Settings.

Referencja endpointów: połączone backendy

Bazowy URL https://ams1-gateway.mss.gs. Każda trasa wymaga Authorization: Bearer <backend key> i zakresu activity.write w twojej rejestracji; odpowiedzi wychodzą z Cache-Control: no-store. Wywołuj je ze swojego serwera, nigdy z klienta gry.

Metoda Ścieżka Co robi
POST /game-sdk/v1/link/start Rozpocznij połączenie dla jednego z twoich graczy ({ player_ref, player_name? }). Zwraca link_code, device_code, qr_url, deep_link, expires_in i interval.
POST /game-sdk/v1/link/poll { device_code } → pending, denied, expired albo linked z link_guid i user.
DELETE /game-sdk/v1/links/{link_guid} Zakończ połączenie ze swojej strony. Gracz może zrobić to samo w Settings.
PUT /game-sdk/v1/links/{link_guid}/activity Opublikuj blok „Playing …” dla jednego gracza; { "activity": null } go czyści.
POST /game-sdk/v1/activity/batch To samo dla maksymalnie 100 graczy w jednym wywołaniu. Każda pozycja dostaje własną odpowiedź.

Kody błędów

Błędy wracają jako {"error":"CODE","message":"…"} z odpowiednim statusem HTTP.

Kod Znaczenie
401 UNAUTHORIZEDBrak tokenu albo nieznany token; najpierw autoryzacja.
403 MISSING_SCOPETo uprawnienie nie zostało przyznane. Gracz mógł je odznaczyć.
403 ORIGIN_NOT_ALLOWEDŻądanie miało nagłówek Origin przeglądarki. Zobacz „Tylko gry natywne” niżej.
409 NOT_SIGNED_INmssgs działa, ale nikt nie jest zalogowany.
429 RATE_LIMITEDPonad 120 żądań na minutę od jednej gry.
400 INVALID_GAME_IDgame_id może zawierać tylko litery, cyfry, kropkę, myślnik albo podkreślnik.
400 TOO_MANY_GUIDSNajwyżej 10 wartości server_guid na jedno wywołanie membership.

Połączone backendy

Trasy backendu używają tego samego formatu. W żądaniu zbiorczym status wraca dla każdej pozycji osobno w results, więc jedno zakończone połączenie nigdy nie powoduje błędu całego wywołania.

Kod Znaczenie
401 INVALID_BACKEND_KEYNieznany klucz albo klucz zrotowany ponad 24 godziny temu.
403 SCOPE_NOT_GRANTEDTwoja rejestracja nie ma zakresu, którego wymaga ta trasa.
400 INVALID_PAYLOADNieprawidłowa treść żądania, ponad 100 pozycji w żądaniu zbiorczym albo join.url, którego host nie jest jednym z twoich zarejestrowanych backendów.
400 INVALID_ACTIVITYPo normalizacji nie została żadna użyteczna nazwa.
410 LINK_REVOKEDPołączenie zostało zakończone, po jednej lub drugiej stronie. Usuń je i znów zaproponuj „Connect mssgs”.
429 SLOW_DOWNOdpytujesz link/poll częściej niż co interval.
429 RATE_LIMITEDZmiana jednego połączenia w ciągu 2 sekund od poprzedniej albo ponad 600 żądań na minutę na twój klucz.
503 LINK_STORE_UNAVAILABLEChwilowy problem po naszej stronie. Spróbuj ponownie przy następnym heartbeacie.

Bezpieczeństwo

Tylko gry natywne

Żądania z nagłówkiem Origin strony internetowej są odrzucane z 403 ORIGIN_NOT_ALLOWED. Gdyby dowolna strona mogła wykryć, że używasz mssgs, i wywołać okno z prośbą o zgodę, byłaby to furtka do fingerprintingu i phishingu, a nie funkcja. Gra natywna w ogóle nie wysyła Origin, więc to jej nie dotyczy, a wbudowana przeglądarka gry jest dopuszczona z nazwy, zobacz FiveM. Jeśli budujesz grę w przeglądarce albo na telefon, nie rozmawiasz z mostkiem: twój własny backend publikuje dla połączonych graczy, zobacz Gry w przeglądarce i na telefonie.

Nad czym gracz zachowuje kontrolę

  • Gracz może wyłączyć mostek w Settings → Game Activity, a wtedy żadna gra w ogóle nie widzi mssgs.
  • Każda zatwierdzona gra jest tam wymieniona dokładnie z uprawnieniami, które ma, datą ostatniej aktywności i przyciskiem Remove. Usunięcie działa natychmiast: token od razu przestaje działać.
  • Połączona gra w przeglądarce albo na telefonie jest wymieniona w Linked games z przyciskiem Disconnect. Rozłączenie też działa natychmiast: następna publikacja tego backendu dostaje 410.
  • Mostek nasłuchuje tylko na 127.0.0.1, nigdy w sieci.
  • Wiadomości prywatne nigdy nie są ujawniane, nawet przy servers.list.
  • Każda gra ma limit 120 żądań na minutę.

Dobre praktyki

  • Proś o zakresy wtedy, gdy ich potrzebujesz, a nie o wszystkie naraz przy pierwszym uruchomieniu.
  • Działaj bez mssgs: gracz nie musi go mieć.
  • Czyść status, gdy gra się kończy, zamiast czekać na TTL.
  • Traktuj odrzucony zakres jako normalny wynik, a nie błąd.

Buduj dalej