Ana içeriğe geç
Geliştiriciler Game SDK

Birinin ne oynadığını göster

Oyunun, bir oyuncunun ne yaptığını mssgs’e bildirsin. Arkadaşları adının altında “Playing” görür, ayrıntıları açar ve Join now’a basarak aynı oyuna atlar. Oyunun ayrıca bir oyuncunun topluluğunda olup olmadığını da kontrol edebilir.

Neler yapabilirsin

  • Oynama durumu yayınlaOyun, oyuncunun ne yaptığı, rolü ve grubun ne kadar dolu olduğu.
  • Join now düğmesi ekleArkadaşlar tek basışta aynı oyuna, sunucuya ya da lobiye katılır.
  • Üyeliği kontrol etBir oyuncunun topluluğunda olup olmadığını ve hangi rollere sahip olduğunu sor.
  • Masaüstü, tarayıcı ya da telefonNative oyunlar yerel köprüyü kullanır; tarayıcı ve telefon oyunları senin backend’inden geçer.

Uygulamada

daniPlaying Space Raiders
Space RaidersPlayed by dani
Sector 7
Baskında
3 of 4 in the party · for 12 min
Topçu

Bir adın altındaki durum ve açtığı ayrıntılar. Oyun tek bir JSON bloğu yayınladı; gerisini uygulama yapar.

Genel bakış

mssgs masaüstü uygulaması, aynı makinedeki bir oyunun konuştuğu küçük bir yerel HTTP köprüsü çalıştırır. Oyunun sunucularımızla hiç konuşmaz, bir hesap parolasını ya da token’ını hiç görmez ve oyuncu adına asla mesaj gönderemez. Oyuncunun zaten giriş yapmış olduğu mssgs kopyasıyla konuşur ve ne yanıt verileceğine o kopya karar verir.

Onunla neler yapabilirsin:

  • mssgs’in kurulu olduğunu ve birinin giriş yapmış olduğunu algıla.
  • Oyuncunun kim olduğunu oku: user_guid, username, avatar.
  • “Bu oyuncu X topluluğunda mı?” diye ve orada hangi role sahip olduğunu sor.
  • Başkaları için Join now düğmeli bir “Playing …” durumu yayınla.
  • Biri o düğmeye bastığında katılma bilgisini al.

İki giriş yolu

Native bir masaüstü oyunu yerel köprüyle konuşur; sonraki bölümler bunu anlatır. Tarayıcıdaki ya da telefondaki bir oyun o köprüye ulaşamaz. Bu oyunlarda, mssgs hesaplarını bir QR ya da sekiz karakterlik bir kodla bağlamış oyuncular adına kendi backend’in yayın yapar: bkz. Tarayıcı ve telefon oyunları, ilk örnek olarak CozyCity ile. Oynama durumunun kendisi her iki yolda da aynı bloktur.

Varsayılan olarak en az bilgi

Kapsamlar bilerek eşit değil. Tek ihtiyacın “bu kişi topluluğumuzda mı” ise membership.query istersin ve server_guid’i kendin belirtirsin: evet/hayır ile oradaki rollerini alırsın ve diğer toplulukları hakkında hiçbir şey öğrenmezsin. Tam liste, oyuncunun ayrıca onaylaması gereken ayrı ve daha yüksek bir kapsamın arkasındadır.

İstemciyi bulmak

Köprü yalnızca 127.0.0.1 üzerinde, küçük bir aralıktaki ilk boş portta dinler. Biri yanıt verene kadar sırayla dene: 7440, 7441, 7442, 7443. mssgs’in geliştirme sürümleri bunun yerine 7540–7543 üzerinde dinler, böylece bir test sürümü gerçek bir oyunun çağrılarına asla yanıt vermez.

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

Token gerekmez ve yanıt oyuncu hakkında hiçbir şey söylemez; yalnızca mssgs’in burada olduğunu ve birinin giriş yapıp yapmadığını söyler.

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

Devam etmeden önce product === "mssgs" ve api değerlerini kontrol et. Dört porttan hiçbiri yanıt vermezse mssgs çalışmıyordur. Oyuncuyu bekletmek yerine normal deneyimini sun.

İzin istemek

/hello dışındaki her şey bir token gerektirir ve token ancak oyuncu, uygulamanın içindeki bir iletişim kutusunda oyununu onayladıktan sonra var olur. Yalnızca gerçekten kullandığın kapsamları iste: oyuncu her birini ayrı ayrı, açıklamasıyla görür ve tek tek işaretini kaldırabilir.

1. İzin iste
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"]
  }'

Geriye {"status":"pending","request_id":"…","poll_after_ms":1000} döner ve oyuncu iletişim kutusunu görür. Ardından oyuncu yanıt verene kadar yokla (istek 3 dakika sonra geçersiz olur):

2. Yanıtı yokla
curl http://127.0.0.1:7440/mssgs/v1/authorize/<request_id>

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

Gerçekte ne aldığını her zaman kontrol et

Yanıttaki scopes, istediğinden kısa olabilir: oyuncu tek tek kapsamların işaretini kaldırmakta özgürdür. Yukarıdaki örnekte membership.query reddedildi. Ne istediğine göre değil, yanıtın ne dediğine göre dallan; yoksa hesaba katmadığın bir 403 MISSING_SCOPE ile karşılaşırsın.

Token’ı sakla ve Authorization: Bearer <token> olarak gönder. Yeniden başlatmalardan sonra da geçerlidir, yani oyuncu oyununu her oturumda değil, bir kez onaylar. Daha sonra zaten verilmiş kapsamlarla yeniden yetkilendirme yaparsan aynı token, iletişim kutusu olmadan hemen geri gelir.

Kapsamlar ve gizlilik

Beş kapsam çok farklı miktarlarda bilgi verir. Bu tesadüf değil; tasarımın ta kendisi. Bu tabloda yukarıdan aşağı inerek olabildiğince az şey iste.

Kapsam Neye izin verir Oyuncunun verdiği
presence.write Ne oynadığını göstermek Hiçbir şey. Bu kapsam yalnızca yazar; hiçbir hesap verisi okumaz.
identity Oyuncunun kim olduğu user_guid, username, görünen ad, avatar URL’si.
staff Personel / moderatör işaretleri identity’ye ek olarak iki boolean değer. Ayrıdır, çünkü bir ad gösteren oyunun, oyuncunun toplulukları moderatör olarak yönettiğini bilmesine gerek yoktur.
membership.query Zaten bildiğin bir topluluğu kontrol etmek Belirttiğin bir server_guid için: evet/hayır, topluluğun adı ve oyuncunun oradaki rolleri. Başka hiçbir topluluk hakkında hiçbir şey.
servers.list Bulunduğu tüm topluluklar Tam liste: guid’ler, adlar, simgeler ve roller. Pahalı olan bu: yalnızca gerçekten ihtiyacın varsa iste.

Çoğu oyuna ikisi yeter

identity ve presence.write, “sen kimsin” ve “ne oynadığını göster” işlerini karşılar; bu da neredeyse her entegrasyon demek. Bir ödülü topluluğuna üyeliğe bağlamak istersen membership.query ekle. servers.list’e neredeyse hiç ihtiyacın olmaz ve oyuncu onu kırmızıyla vurgulanmış görür.

Üyeliği kontrol etmek

Bu, “bana tüm listeyi ver”in alternatifi. Kendi topluluğunun server_guid’ini (onu zaten biliyorsun) belirtirsin ve yalnızca o topluluk hakkında bir yanıt alırsın.

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

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

# üye değil, başka hiçbir bilgi yok:
# {"server_guid":"…","member":false}

“Hayır” tam olarak bu demektir, fazlası değil. Tek çağrıda en fazla 10 guid gönderebilirsin (server_guid’i tekrarla ya da virgülle ayır); bu durumda bir results dizisi döner. @everyone grubu roles içinde asla yer almaz: her üye için geçerlidir, yani sana hiçbir şey söylemez.

Oynama durumu yayınlamak

Tek bir PUT, oyuncunun adının altına, topluluklarının onu gördüğü her yerde “Playing …” satırını koyar.

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

Yalnızca name zorunludur. Yanıt, durumun ne kadar yaşadığını ve ne sıklıkla heartbeat göndermen gerektiğini söyler:

Yanıt
{ "ok": true, "expires_in_ms": 90000, "heartbeat_every_ms": 30000 }

Heartbeat gönder, yoksa durum kaybolur

90 saniye boyunca yaşam belirtisi göstermeyen bir durum otomatik olarak temizlenir. Bu bilerek böyle: oyunun çökerse oyuncu saatlerce “oynuyor” görünmez. Her 30 saniyede bir POST /mssgs/v1/activity/heartbeat, düzgün bir kapanışta da DELETE /mssgs/v1/activity gönder.

Oyuncu sayısı ve rol

Hangi cümlenin gösterileceğine party.kind karar verir, çünkü aynı iki sayı aynı anlama gelmez. Dört kişilik bir ekip, üzerinde dört oyuncu olan bir sunucu değildir.

kind Görünümü Şunun için
party (varsayılan)3 of 4 in the partybir ekip, tayfa ya da grup
server4/100 playersbir oyun sunucusu (FiveM, bir topluluk sunucusu)
lobby4/100 playersmaç başlamadan önceki bir lobi
match4/100 playersdevam eden bir maç ya da tur

role (en fazla 48 karakter), oyuncunun ne olarak oynadığıdır: bir meslek, sınıf ya da karakter. state içinde bir cümle daha olmak yerine kendi alanına sahiptir, çünkü oyuncu sayısının yanında bir etiket olarak gösterilir.

details ve state en fazla 128’er karakter, name en fazla 64 karakter olabilir. Satır sonları ve kontrol karakterleri çıkarılır. Simge URL’si bilerek desteklenmez: satırı gösteren her istemci onu indirirdi; bu da durumu, oyuncunun bulunduğu her topluluğun her üyesini senin sunucuna bildiren bir işarete dönüştürürdü.

Join now düğmesi

Etkinliğine bir join bloğu koy, diğer üyeler durumun yanında bir Join now düğmesi görsün. İki yolu var ve ikisini birleştirebilirsin.

1. Gizli bir değer (native oyunlar için)

{"join":{"secret":"raid-42"}} ayarla. Biri Join now’a bastığında bu değer, aynı game_id ile eşleşen ve onun kendi makinesinde çalışan oyun kopyasına iletilir. Hiçbir URL açılmaz ve hiçbir şema işleyicisi çağrılmaz. Oyunun onu şöyle alır:

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

Her olayı bir kez görmek için ?since=<cursor> ile yokla. Düğmeye basan kişinin oyunu çalışmıyorsa hiçbir şey iletilmez; bu da ayrıca bir URL sunmak için iyi bir neden.

2. Bir https URL’si (web oyunları ve lobi bağlantıları için)

{"join":{"url":"https://play.example.com/s/abc"}} ayarla, düğme o bağlantıyı açar. Yalnızca https kabul edilir. Özel bir şema (steam://, mygame://, file://) reddedilir: o blok her üyenin ekranına düşer ve böyle bir URL, başka birinin makinesine senin seçtiğin argümanlarla yerel bir işleyici çağırtmanın bir yoludur.

join içindeki her şey herkese açıktır

join bloğu, oyuncunun durumunu görebilen herkese yayınlanır; bir Join now düğmesinin bütün amacı da bu. Bu yüzden onu bir kimlik bilgisi gibi değil, bir lobi kodu gibi düşün. İçine gizli kalması gereken hiçbir şey koyma ve kodlarının süresinin dolmasını sağla.

FiveM

FiveM’in istemci tarafındaki Lua çalışma ortamında HTTP yoktur, bu yüzden bir kaynak köprüyle, Origin gönderen bir CEF görünümü olan NUI üzerinden konuşur. Köprü bu origin’leri açıkça kabul eder: https://cfx-nui-<resource> ve eski nui://<resource>. Sıradan web sayfaları reddedilmeye devam eder ve açık web’deki bir sayfa bu origin’i sahiplenemez; onu tarayıcının kendisi belirler.

client.lua: NUI’dan yayınlamasını iste
-- HTTP’yi NUI sayfası yapar; Lua ona yalnızca veriyi gönderir.
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: durum 90 sn sonra sona erer
  end
end)
nui.js
const BASE = 'http://127.0.0.1:7440/mssgs/v1';   // 7440-7443’ü dene
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']          // burada başka bir şey gerekmez
    })
  });
  const started = await res.json();
  if (started.status === 'approved') { return started.token; }

  // Oyuncu şimdi mssgs’te izin iletişim kutusunu görür.
  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' } // cfx.re bağlantın
    })
  });
});

Sonuç: cfx.re bağlantını açan bir Join now düğmesiyle Playing FiveM · Los Santos Roleplay · 4/100 players · Police.

Yalnızca presence.write iste

Oynama durumu başka hiçbir şeye ihtiyaç duymaz: bu kapsam hiçbir şey okumaz. Oyun içi bir ödülü mssgs topluluğuna üyeliğe bağlamak istersen membership.query ekle ve kendi server_guid’ini belirt; oyuncunun diğer toplulukları hakkında yine hiçbir şey öğrenmezsin.

Oynadığın bir sunucu otomatik olarak güvenilir değildir

Her FiveM sunucusu istemci kaynakları çalıştırabilir, yani birinin katıldığı her sunucu izin isteyebilir. Tam da bu yüzden arada, kaynağın adını gösteren bir iletişim kutusu vardır: karar sunucunun değil, oyuncunundur.

Tarayıcı ve telefon oyunları: backend’in üzerinden bağlamak

Bir tarayıcı sekmesindeki ya da telefondaki bir oyun yukarıdaki köprüye ulaşamaz. Köprü oyuncunun masaüstünde çalışır ve arada üç engel vardır: köprü, tarayıcı Origin’i taşıyan her isteği reddeder; Chrome, herkese açık bir sayfanın 127.0.0.1 adresine istek atmasının önüne bir izin istemi koyar, Safari ise doğrudan reddeder; bir telefonun da masaüstünün loopback adresine ulaşmasının hiçbir yolu yoktur.

Bu yüzden yön tersine döner. Kendi backend’in kimin oynadığını zaten bilir ve bunu mssgs’e o söyler; mssgs hesaplarını oyununa bağlamış oyuncular için. Bağlantı oyununda değil, her zaman mssgs uygulamasında onaylanır ve bir oturum değil, yalnızca bir bağlantı oluşturur: aşağıdaki hiçbir şey kimsenin oturumunu açamaz ya da oyuncu adına hareket edemez. Oyun istemcin hiçbir anahtar görmez ve mss.gs ile hiç konuşmaz. Bu yoldaki ilk oyun, masaüstü sürümü olmadan bir WebGL sayfası ve bir iPhone uygulaması olarak yayımlanan şehir kurma oyunu CozyCity; aşağıdaki örnekler de ondan.

1. Oyununu kaydet

Oyunu Game SDK kayıt sayfasında kaydet: game_id değerin (örneğin com.deverence.cozycity), oyuncunun onay ekranında göreceği ad ve simge ve backend’inin ana bilgisayar adları. Başvuruyu orada inceleriz; onaylandığında backend anahtarın aynı sayfada seni bekler ve yalnızca bir kez gösterilir; biz yalnızca özetini saklarız. Anahtarın yeri senin sunucundur, başka hiçbir yer değil. Anahtarı orada istediğin zaman yenileyebilirsin; eski anahtar 24 saat geçerli kalır, böylece bir dağıtım sorunsuz tamamlanabilir.

Oyununu kaydet

Ekrandaki ad ve simge her zaman kayıttan gelir, asla istekten değil. Aksi halde bir oltalama bağlantısı, bir bağlantı isteğini istediği oyunun kılığına sokabilirdi. Ana bilgisayar adları, bir join.url’nin nereye gidebileceğini sınırlar, aşağıya bak.

2. Bir oyuncuyu bağla

Oyuncu oyununda Connect mssgs seçeneğini seçer. Oyunun backend’ine sorar, backend’in de bize sorar:

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, o oyuncu için senin kendi kalıcı kimliğindir (en fazla 128 karakter); bir oturumun ya da maçın kimliği değil. player_name (en fazla 64) ekranın “Player: …” olarak gösterdiği addır. Oyun istemcine yalnızca link_code, qr_url ve deep_link ver. device_code senin yoklama tutamacındır ve sunucuda kalır.

Ardından oyunun aynı anda üç şey gösterir, çünkü oyuncu herhangi bir yerde olabilir:

  • qr_url değerinin QR kodu. mssgs yüklü bir telefon onu doğrudan uygulamanın onay ekranında açar. Uygulama yoksa, kodu gösteren ve indirmeyi öneren mss.gs’teki bir sayfaya gider.
  • deep_link ile bir “Open in mssgs” düğmesi, masaüstü uygulamasının çalıştığı bilgisayardaki bir tarayıcı için. Oyununun açması gereken tek harici URL budur.
  • Kodun kendisi, dörder karakterlik iki grup halinde, Settings → Game Activity → Link a game altına yazılmak üzere. Alfabede 0/O ya da 1/I yoktur, bu yüzden yazarken nadiren hata olur.

Oyuncunun mssgs’te gördüğü, uygulamanın kayda göre çizdiği ekran:

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

Bu arada backend’in, durum değişene kadar her interval saniyede bir yoklar (daha sık yoklarsan yanıt 429 SLOW_DOWN olur). Bir kod bir kez çalışır ve on dakika sonra geçersiz olur:

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"}      # oyuncu Not now seçeneğini seçti
# {"status":"expired"}
# {"status":"linked","link_guid":"…","user":{"user_guid":"62e377…"}}

link_guid’i oyuncunla birlikte sakla; artık yayın yaptığın adres budur. Yanıt yalnızca user_guid’i taşır; kaydın identity.link kapsamına sahipse yalnızca bir username eklenir, bunun ötesinde hiçbir şey yoktur. Aynı player_ref için ikinci bir onay önceki bağlantının yerini alır, yani oyununun bir oyuncusu tek bir mssgs hesabıdır. Aynı mssgs hesabı birkaç oyuna ve bir oyunun birkaç player_ref’ine bağlanabilir (bir aile iPad’i).

3. Oynama durumunu yayınla

Köprüdekiyle aynı blok, aynı kurallar ve aynı sınırlarla; yalnızca artık bağlantı başına ve backend anahtarınla:

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=…" }
  }
}
Yanıtlar
200 {"published":true,"changed":true}     # blok değişti ve yayınlandı
200 {"published":true,"changed":false}    # saklananla aynı; yalnızca TTL yenilendi
204                                        # saklandı, ama oyuncu şu anda mssgs’te çevrimiçi değil
410 {"error":"LINK_REVOKED"}               # oyuncu bağlantıyı kesti: bağlantıyı bırak

200 ve 204’ü aynı şekilde ele al: saklandı. { "activity": null } bloğu temizler; oyuncu ayrıldığında bunu gönder. Köprüden bir farkı var: join.url’nin ana bilgisayarı, kayıtlı backend’lerinden biri (ya da onun bir alt alan adı) olmalıdır, yoksa 400 INVALID_PAYLOAD alırsın. Böylece bir backend, bir oyuncunun durumuna o oyuncunun hiç oynamadığı bir yere götüren bir Join now düğmesi koyamaz.

Her 60 saniyede heartbeat, TTL 120

Yayınlanan bir durum, yeni bir mesaj gelmezse 120 saniye yaşar ve sonra kendiliğinden düşer. Bu yüzden aynı bloğu her 60 saniyede bir yeniden gönder; değişmemiş bir blok hiçbir şeye mal olmaz ve yalnızca TTL’yi yeniler. Heartbeat’in durursa “Playing …” satırı da kaybolur; amaç da tam olarak bu.

Yüzlerce oyuncu çevrimiçiyken heartbeat’i tek çağrıda, bir seferde en fazla 100 öğeyle gönder. Her öğe kendi durumunu alır, böylece mssgs’te bağlantısını kesmiş bir oyuncu diğer doksan dokuzunu asla durdurmaz:

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

Durum nasıl gösterilir

  • Köprü durumuyla aynı: Playing CozyCity · Lantern Hollow · 6/40 players, bir join.url varsa Join now ile. Blok sunucu tarafında via: "backend" olarak işaretlenir, böylece bir istemci “Shared by the game's server” ekleyebilir.
  • Yalnızca oyuncu mssgs’te çevrimiçiyken. Açık bir mssgs istemcisi yoksa hesap çevrimdışıdır ve öyle kalır; backend’in birini oradaymış gibi gösteremez. Bu, bu yolun bir “René bilgisayarının başında mı” işaretine dönüşmesini de engeller.
  • Öncelik: uygulama içi bir oyun > köprüdeki bir oyun > senin backend’in. Oyuncu mssgs’in içinde satranca otururken backend’in heartbeat göndermeye devam ederse, en son yazan değil, satranç kazanır.

Bağlantıyı kesmek

Oyuncu her bağlantıyı Settings → Game Activity → Linked games altında görür: simgen ve adın, oyunundaki oyuncu adı, ne zaman bağlandığı, en son ne zaman yayın yaptığı ve bir Disconnect düğmesiyle. Bundan sonra bir sonraki yayının 410 LINK_REVOKED yanıtını alır; oyunun bunu böyle öğrenir. link_guid’i bırak ve yeniden “Connect mssgs” sun. Kendi tarafından bir bağlantıyı DELETE /game-sdk/v1/links/{link_guid} ile sonlandırırsın.

Sınırlar

Bağlantı başına bir değişiklik en fazla 2 saniyede bir sayılır; değişmemiş bir heartbeat ücretsizdir. Anahtar başına dakikada 600 istek hakkın vardır ve toplu çağrıdaki öğeler tek tek sayılır: 300 oyuncu için her 60 saniyede bir heartbeat göndermek 600’ün 5’ini harcar.

Uç nokta referansı: köprü

Temel URL http://127.0.0.1:<port>. İlk üçü dışındaki her şey Authorization: Bearer <token> gerektirir.

Yöntem Yol Scope Ne yapar
GET /mssgs/v1/hello yok mssgs burada mı, hangi API’yi konuşuyor ve giriş yapmış biri var mı. Token gerektirmeyen tek rota; oyuncu hakkında hiçbir şey söylemez.
POST /mssgs/v1/authorize yok Oyuncudan izin iste. Uygulamada bir iletişim kutusu açar ve yoklaman için bir request_id döndürür.
GET /mssgs/v1/authorize/:request_id yok pending, approved (token ile), denied ya da expired.
GET /mssgs/v1/me identity Giriş yapmış oyuncu. is_staff / is_moderator yalnızca staff kapsamıyla eklenir.
GET /mssgs/v1/membership membership.query Gönderdiğin server_guid değerlerindeki üyelik (en fazla 10, tekrarlanmış ya da virgülle ayrılmış).
GET /mssgs/v1/servers servers.list Oyuncunun bulunduğu tüm topluluklar, rolleriyle birlikte. Özel mesajlar asla dahil edilmez.
PUT /mssgs/v1/activity presence.write “Playing …” bloğunu yayınla. TTL’yi ve ne sıklıkla heartbeat gönderileceğini döndürür.
POST /mssgs/v1/activity/heartbeat presence.write Yayınlanan etkinliği yeniden göndermeden canlı tut.
DELETE /mssgs/v1/activity presence.write Düzgün bir kapanış için hemen temizle.
GET /mssgs/v1/events presence.write Oyununa yönelik katılma olayları. ?since=<cursor> ile yokla.
GET /mssgs/v1/session yok Bu token’da neler olduğu: game_id, verilen kapsamlar, giriş yapmış biri olup olmadığı.
DELETE /mssgs/v1/session yok İzni geri ver. Oyuncunun izni Settings’ten geri almasıyla aynı etkiye sahiptir.

Uç nokta referansı: bağlı backend’ler

Temel URL https://ams1-gateway.mss.gs. Her rota Authorization: Bearer <backend key> ve kaydında activity.write kapsamını gerektirir; yanıtlar Cache-Control: no-store ile gönderilir. Bunları oyun istemcisinden değil, her zaman kendi sunucundan çağır.

Yöntem Yol Ne yapar
POST /game-sdk/v1/link/start Oyuncularından biri için bir bağlantı başlat ({ player_ref, player_name? }). link_code, device_code, qr_url, deep_link, expires_in ve interval döndürür.
POST /game-sdk/v1/link/poll { device_code } → pending, denied, expired ya da link_guid ve user ile linked.
DELETE /game-sdk/v1/links/{link_guid} Bir bağlantıyı kendi tarafından sonlandır. Oyuncu da aynısını Settings’ten yapabilir.
PUT /game-sdk/v1/links/{link_guid}/activity Bir oyuncu için “Playing …” bloğunu yayınla; { "activity": null } bloğu temizler.
POST /game-sdk/v1/activity/batch Aynısı, tek çağrıda en fazla 100 oyuncu için. Her öğe ayrı yanıtlanır.

Hata kodları

Hatalar, eşleşen bir HTTP durum koduyla {"error":"CODE","message":"…"} olarak döner.

Kod Anlamı
401 UNAUTHORIZEDToken eksik ya da bilinmiyor; önce yetkilendir.
403 MISSING_SCOPEOyuncu bu izni vermedi. İşaretini kaldırmış olabilir.
403 ORIGIN_NOT_ALLOWEDİstek bir tarayıcı Origin’i taşıyordu. Aşağıdaki “Yalnızca native oyunlar” bölümüne bak.
409 NOT_SIGNED_INmssgs çalışıyor ama kimse giriş yapmamış.
429 RATE_LIMITEDBir oyundan bir dakikada 120’den fazla istek.
400 INVALID_GAME_IDgame_id yalnızca harf, rakam, nokta, tire ya da alt çizgiden oluşabilir.
400 TOO_MANY_GUIDSÜyelik çağrısı başına en fazla 10 server_guid değeri.

Bağlı backend’ler

Backend rotaları aynı yapıyı kullanır. Toplu bir çağrıda durum, results içinde her öğe için ayrı döner; böylece sona ermiş tek bir bağlantı tüm çağrıyı asla başarısız kılmaz.

Kod Anlamı
401 INVALID_BACKEND_KEYBilinmeyen bir anahtar ya da 24 saatten daha önce yenilenerek devreden çıkmış bir anahtar.
403 SCOPE_NOT_GRANTEDKaydın, o rotanın gerektirdiği kapsama sahip değil.
400 INVALID_PAYLOADHatalı biçimlendirilmiş gövde, 100’den fazla toplu öğe ya da ana bilgisayarı kayıtlı backend’lerinden biri olmayan bir join.url.
400 INVALID_ACTIVITYNormalleştirmeden sonra kullanılabilir bir ad kalmadı.
410 LINK_REVOKEDBağlantı, taraflardan biri tarafından sonlandırıldı. Bağlantıyı bırak ve yeniden “Connect mssgs” sun.
429 SLOW_DOWNlink/poll çağrısını interval değerinden daha sık yaptın.
429 RATE_LIMITEDBir bağlantıda bir öncekinden sonraki 2 saniye içinde bir değişiklik ya da anahtarında dakikada 600’den fazla istek.
503 LINK_STORE_UNAVAILABLEBizim tarafımızda geçici bir sorun. Bir sonraki heartbeat’inde yeniden dene.

Güvenlik

Yalnızca native oyunlar

Bir web sayfası Origin’i taşıyan istekler 403 ORIGIN_NOT_ALLOWED ile reddedilir. Herhangi bir web sayfasının mssgs kullandığını algılayıp bir izin iletişim kutusu açabilmesi bir özellik değil, parmak izi çıkarma ve oltalama için bir saldırı yüzeyi olurdu. Native bir oyun hiç Origin göndermez, bu yüzden etkilenmez; bir oyunun kendi gömülü tarayıcısına da adıyla izin verilir, bkz. FiveM. Bir tarayıcı ya da telefon oyunu geliştiriyorsan köprüyle konuşmazsın: bağlı oyuncular adına kendi backend’in yayın yapar, bkz. Tarayıcı ve telefon oyunları.

Oyuncunun elinde kalanlar

  • Oyuncu köprüyü Settings → Game Activity altında kapatabilir; bundan sonra hiçbir oyun mssgs’i göremez.
  • Onaylanan her oyun orada, sahip olduğu izinlerin tam listesi, en son ne zaman etkin olduğu ve bir Remove düğmesiyle listelenir. Kaldırma anında gerçekleşir: token hemen geçersiz olur.
  • Bağlı bir tarayıcı ya da telefon oyunu Linked games altında bir Disconnect düğmesiyle listelenir. Bağlantıyı kesmek de anında gerçekleşir: o backend’in bir sonraki yayını 410 alır.
  • Köprü yalnızca 127.0.0.1 üzerinde dinler, asla ağda değil.
  • Özel mesajlar, servers.list ile bile asla paylaşılmaz.
  • Oyun başına dakikada 120 isteklik bir bütçe vardır.

İyi uygulamalar

  • Kapsamları ilk açılışta hepsini birden değil, ihtiyacın olduğunda iste.
  • mssgs olmadan da çalış: oyuncunun mssgs kullanması şart değil.
  • Oyun bittiğinde TTL’yi beklemek yerine durumunu temizle.
  • Reddedilen bir kapsamı hata değil, olağan bir sonuç olarak ele al.

Geliştirmeye devam et