Перейти к основному содержанию
Разработчикам Game SDK

Показывайте, во что играет человек

Пусть ваша игра сообщает mssgs, чем занят игрок. Друзья видят «Играет» под его именем, открывают подробности и нажимают «Присоединиться», чтобы попасть в ту же игру. Ваша игра также может проверить, состоит ли игрок в вашем сообществе.

Что можно сделать

  • Публикуйте игровой статусИгра, чем игрок занят, его роль и насколько заполнена группа.
  • Добавьте кнопку «Присоединиться»Друзья одним нажатием попадают в ту же игру, на тот же сервер или в то же лобби.
  • Проверяйте членствоУзнайте, состоит ли игрок в вашем сообществе и с какими ролями.
  • Компьютер, браузер или телефонНативные игры используют локальный мост; браузерные и мобильные игры работают через ваш бэкенд.

В приложении

daniИграет в Space Raiders
Space RaidersИграет dani
Sector 7
В рейде
В группе: 3 из 4 · 12 мин
Стрелок

Статус под именем и подробности, которые он открывает. Игра опубликовала один блок JSON; всё остальное делает приложение.

Обзор

Настольное приложение mssgs запускает небольшой локальный HTTP-мост, с которым общается игра на том же компьютере. Ваша игра никогда не обращается к нашим серверам, никогда не видит пароль или токен аккаунта и никогда не может писать от имени игрока. Она общается с копией mssgs, в которой игрок уже вошёл в аккаунт, и эта копия решает, что отвечать.

Что с его помощью можно делать:

  • Определить, что mssgs установлен и кто-то вошёл в аккаунт.
  • Узнать, кто игрок: user_guid, имя пользователя, аватар.
  • Спросить «состоит ли этот игрок в сообществе X?» и какая у него там роль.
  • Опубликовать статус «Играет в …» с кнопкой «Присоединиться» для остальных.
  • Получить передачу приглашения, когда кто-то нажимает эту кнопку.

Два способа подключения

Нативная настольная игра общается с локальным мостом; именно это описывают следующие разделы. Игра в браузере или на телефоне до этого моста добраться не может. За такие игры публикует ваш собственный бэкенд, для игроков, которые связали свой аккаунт mssgs через QR-код или восьмисимвольный код: см. Браузерные и мобильные игры, где первым примером служит CozyCity. Сам игровой статус в обоих случаях представляет собой один и тот же блок.

Минимальное раскрытие по умолчанию

Области доступа намеренно неравноценны. Если вам нужно знать только «состоит ли этот человек в нашем сообществе», запросите membership.query и сами укажите server_guid: вы получите «да» или «нет» и роли игрока там, и ничего не узнаете об остальных его сообществах. Полный список скрыт за отдельной, более высокой областью доступа, которую игрок должен одобрить отдельно.

Как найти клиент

Мост слушает только 127.0.0.1, на первом свободном порту из небольшого диапазона. Перебирайте их по порядку, пока какой-нибудь не ответит: 7440, 7441, 7442, 7443. Сборки mssgs для разработки слушают вместо этого 7540–7543, так что тестовая сборка никогда не отвечает на вызовы настоящей игры.

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

Токен не нужен, и ответ ничего не говорит об игроке, только о том, что mssgs здесь и вошёл ли кто-нибудь в аккаунт.

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

Прежде чем идти дальше, проверьте product === "mssgs" и api. Если ни один из четырёх портов не отвечает, mssgs не запущен. Просто предложите обычный сценарий игры, не заставляя игрока ждать.

Запрос разрешения

Всё, кроме /hello, требует токена, а токен появляется только после того, как игрок одобрил вашу игру в диалоге внутри приложения. Запрашивайте только те области доступа, которые действительно используете: игрок видит каждую отдельно, с пояснением, и может снять галочку с любой из них.

1. Запрос разрешения
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"]
  }'

Вы получите {"status":"pending","request_id":"…","poll_after_ms":1000}, а игрок увидит диалог. Затем опрашивайте, пока он не ответит (запрос истекает через 3 минуты):

2. Опрос ответа
curl http://127.0.0.1:7440/mssgs/v1/authorize/<request_id>

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

Всегда проверяйте, что вы на самом деле получили

Список scopes в ответе может быть короче, чем вы запросили: игрок вправе снять галочку с отдельных областей. В примере выше в membership.query было отказано. Опирайтесь на то, что сказано в ответе, а не на то, что вы запросили, иначе столкнётесь с незапланированным 403 MISSING_SCOPE.

Сохраните токен и отправляйте его как Authorization: Bearer <token>. Он переживает перезапуски, так что игрок одобряет вашу игру один раз, а не в каждой сессии. Если позже вы снова пройдёте авторизацию с уже выданными областями доступа, вы сразу получите тот же токен без диалога.

Области доступа и приватность

Пять областей доступа раскрывают очень разный объём данных. Это не случайность, а весь замысел. Запрашивайте как можно меньше, двигаясь по этой таблице сверху вниз.

Область доступа Что она разрешает Чем игрок за это платит
presence.write Показывать, во что играет игрок Ничем. Эта область доступа только пишет; она вообще не читает данных аккаунта.
identity Кто игрок user_guid, имя пользователя, отображаемое имя, URL аватара.
staff Флаги сотрудника и модератора Два логических значения поверх identity. Отдельно, потому что игре, которая показывает имя, незачем знать, что игрок модерирует сообщества.
membership.query Проверять сообщество, которое вы уже знаете Для указанного вами server_guid: да или нет, его название и роли игрока в нём. Ничего о других сообществах.
servers.list Все сообщества игрока Полный список: guid, названия, значки и роли. Это самая дорогая область: запрашивайте её, только если она вам действительно нужна.

Большинству игр нужны две из них

identity и presence.write покрывают «кто вы» и «покажите, во что вы играете», а это почти любая интеграция. Добавьте membership.query, если хотите привязать награду к членству в вашем сообществе. servers.list вам почти никогда не понадобится, и игрок видит его выделенным красным.

Проверка членства

Это альтернатива подходу «дайте мне весь список». Вы указываете server_guid своего собственного сообщества (который вы и так знаете) и получаете ответ только о нём.

Одно сообщество
curl -H "Authorization: Bearer $TOKEN" \
  "http://127.0.0.1:7440/mssgs/v1/membership?server_guid=ca94ecc…f4g02"

# участник:
# {"server_guid":"ca94ecc…","member":true,"name":"Acme Fans","is_owner":false,
#  "roles":[{"guid":"0aa32…","name":"Pro"}]}

# не участник, и больше ничего:
# {"server_guid":"…","member":false}

«Нет» означает ровно это и ничего больше. За один вызов можно передать до 10 guid (повторите server_guid или перечислите их через запятую), и тогда вернётся массив results. Группа @everyone никогда не попадает в roles: она верна для каждого участника, поэтому ни о чём не говорит.

Публикация игрового статуса

Один PUT помещает строку «Играет в …» под именем игрока везде, где его видят его сообщества.

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

Обязательно только поле name. Ответ сообщает, сколько живёт статус и как часто отправлять heartbeat:

Ответ
{ "ok": true, "expires_in_ms": 90000, "heartbeat_every_ms": 30000 }

Heartbeat, иначе статус исчезнет

Статус без признаков жизни в течение 90 секунд удаляется автоматически. Это сделано намеренно: если игра упадёт, игрок не останется «играющим» несколько часов. Отправляйте POST /mssgs/v1/activity/heartbeat каждые 30 секунд и DELETE /mssgs/v1/activity при штатном завершении.

Число игроков и роль

party.kind определяет, какая фраза будет показана, потому что одни и те же два числа означают разное. Отряд из четырёх человек не то же самое, что сервер, на котором четыре игрока.

kind Как отображается Для чего
party (по умолчанию)В группе: 3 из 4отряд, команда или группа
server4/100 игроковигровой сервер (FiveM, сервер сообщества)
lobby4/100 игроковлобби до начала матча
match4/100 игроковидущий матч или раунд

role (до 48 символов) означает, кем играет игрок: профессия, класс или персонаж. Для неё есть отдельное поле, а не ещё одна фраза в state, потому что она показывается как метка рядом с числом игроков.

details и state ограничены 128 символами каждое, name ограничено 64. Переводы строк и управляющие символы удаляются. URL значка намеренно не поддерживается: его загружал бы каждый клиент, отображающий эту строку, и статус превратился бы в маячок, сообщающий вашему серверу о каждом участнике каждого сообщества, где состоит игрок.

Кнопка «Присоединиться»

Добавьте в активность блок join, и другие участники увидят рядом со статусом кнопку «Присоединиться». Есть два способа, и их можно сочетать.

1. Секрет (для нативных игр)

Задайте {"join":{"secret":"raid-42"}}. Когда кто-то нажимает «Присоединиться», этот секрет доставляется его собственной копии вашей игры, на его собственном компьютере, с сопоставлением по тому же game_id. Никакой URL не открывается, и никакой обработчик схемы не вызывается. Игра забирает секрет так:

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

Опрашивайте с ?since=<cursor>, чтобы видеть каждое событие один раз. Если игра нажавшего не запущена, ничего не доставляется, и это хороший повод дополнительно предложить URL.

2. URL с https (для веб-игр и ссылок на лобби)

Задайте {"join":{"url":"https://play.example.com/s/abc"}}, и кнопка откроет эту ссылку. Принимается только https. Собственная схема (steam://, mygame://, file://) отклоняется: этот блок попадает на экран каждого участника, а такой URL позволил бы заставить чужой компьютер вызвать локальный обработчик с выбранными вами аргументами.

Всё в join публично

Блок join рассылается всем, кто может видеть статус игрока; в этом и состоит смысл кнопки «Присоединиться». Поэтому относитесь к нему как к коду лобби, а не как к учётным данным. Никогда не кладите туда ничего, что должно оставаться секретом, и ограничивайте срок действия своих кодов.

FiveM

В клиентской среде Lua у FiveM нет HTTP, поэтому ресурс общается с мостом через NUI, представление CEF, которое отправляет Origin. Мост явно принимает такие источники: https://cfx-nui-<resource> и более старый nui://<resource>. Обычные веб-страницы по-прежнему отклоняются, и страница в открытом интернете не может выдать себя за такой источник: браузер выставляет его сам.

client.lua: попросить NUI опубликовать статус
-- HTTP выполняет страница NUI; Lua только передаёт ей данные.
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: статус истекает через 90 с
  end
end)
nui.js
const BASE = 'http://127.0.0.1:7440/mssgs/v1';   // перебираем 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']          // больше здесь ничего не нужно
    })
  });
  const started = await res.json();
  if (started.status === 'approved') { return started.token; }

  // Теперь игрок видит в 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' } // ваша ссылка cfx.re
    })
  });
});

Результат: Играет в FiveM · Los Santos Roleplay · Игроков: 4/100 · Police, с кнопкой «Присоединиться», которая открывает вашу ссылку cfx.re.

Запрашивайте только presence.write

Игровому статусу больше ничего не нужно: эта область доступа вообще ничего не читает. Если хотите привязать внутриигровую награду к членству в вашем сообществе mssgs, добавьте membership.query и укажите свой server_guid; о других сообществах игрока вы по-прежнему ничего не узнаете.

Сервер, на котором вы играете, не становится доверенным автоматически

Любой сервер FiveM может запускать клиентские ресурсы, поэтому любой сервер, на который кто-то зашёл, может запросить разрешение. Именно поэтому между ними стоит диалог с названием ресурса: решает игрок, а не сервер.

Браузерные и мобильные игры: связывание через ваш бэкенд

Игра во вкладке браузера или на телефоне не может достучаться до описанного выше моста. Он работает на компьютере игрока, и между ними три стены: мост отклоняет каждый запрос с браузерным Origin, Chrome показывает запрос разрешения, когда публичная страница обращается к 127.0.0.1, а Safari отказывает сразу, у телефона же вообще нет пути к loopback-интерфейсу компьютера.

Поэтому направление меняется. Ваш собственный бэкенд уже знает, кто играет, и сообщает об этом mssgs для игроков, которые связали свой аккаунт mssgs с вашей игрой. Связь одобряется в приложении mssgs, а не в вашей игре, и создаёт именно связь, а не сессию: ничто из описанного ниже не может никого авторизовать или действовать от имени игрока. Клиент вашей игры никогда не видит ключа и никогда не обращается к mss.gs. Первая игра на этом пути: CozyCity, градостроительный симулятор, который выходит как страница WebGL и приложение для iPhone без настольной версии; примеры ниже взяты из неё.

1. Зарегистрируйте игру

Зарегистрируйте игру на странице регистрации Game SDK: свой game_id (например, com.deverence.cozycity), название и значок, которые игрок видит в окне подтверждения, и имена хостов своего бэкенда. Мы проверим её там, и после одобрения ваш ключ бэкенда будет ждать на той же странице, показанный один раз; мы храним только его дайджест. Ключ должен лежать на вашем сервере и больше нигде. Там же его можно заменить в любой момент, а старый остаётся действительным 24 часа, чтобы деплой успел пройти.

Зарегистрировать игру

Название и значок в окне всегда берутся из регистрации, а не из запроса. Иначе фишинговая ссылка могла бы выдать запрос на связывание за любую игру. Имена хостов ограничивают, куда может вести join.url, см. ниже.

2. Свяжите игрока

Игрок выбирает в вашей игре «Подключить mssgs». Игра обращается к вашему бэкенду, а бэкенд к нам:

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 представляет собой ваш собственный стабильный id этого игрока (до 128 символов), а не сессии или матча; player_name (до 64) показывается в окне как «Игрок: …». Передавайте клиенту игры только link_code, qr_url и deep_link. device_code служит вашим дескриптором для опроса и остаётся на сервере.

Затем игра показывает сразу три вещи, потому что игрок может находиться где угодно:

  • QR-код для qr_url. Телефон с mssgs открывает его сразу в окне подтверждения приложения. Без приложения он попадает на страницу mss.gs, которая показывает код и предлагает скачать приложение.
  • Кнопку «Открыть в mssgs» с deep_link для браузера на компьютере, где рядом установлено настольное приложение. Это единственный внешний URL, который вашей игре когда-либо нужно открыть.
  • Сам код, двумя группами по четыре символа, чтобы ввести его в разделе Настройки → Игровая активность → Связать игру. В алфавите нет 0/O и 1/I, так что ошибиться при вводе трудно.

Что игрок видит в mssgs; приложение строит это окно по данным регистрации:

Подключить CozyCity к твоему аккаунту mssgs?

CozyCity сможет показывать в твоём статусе mssgs, во что ты играешь. Игра не увидит твои сообщения, друзей и серверы и не сможет писать от твоего имени.
Игрок: René's city · Подключить / Не сейчас

Тем временем ваш бэкенд опрашивает каждые interval секунд (на более частые запросы приходит 429 SLOW_DOWN), пока статус не сменится. Код работает один раз и истекает через десять минут:

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"}      # игрок выбрал «Не сейчас»
# {"status":"expired"}
# {"status":"linked","link_guid":"…","user":{"user_guid":"62e377…"}}

Сохраните link_guid для своего игрока; с этого момента это адрес, для которого вы публикуете. В ответе есть только user_guid; username добавляется, только если в вашей регистрации есть область доступа identity.link, и больше ничего. Повторное одобрение для того же player_ref заменяет прежнюю связь, так что одному игроку вашей игры соответствует один аккаунт mssgs. Один аккаунт mssgs может быть связан с несколькими играми и с несколькими player_ref одной игры (семейный iPad).

3. Публикуйте игровой статус

Тот же блок, что и на мосту, с теми же правилами и ограничениями, только теперь для каждой связи и с вашим ключом бэкенда:

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=…" }
  }
}
Ответы
200 {"published":true,"changed":true}     # блок изменился и был разослан
200 {"published":true,"changed":false}    # совпадает с сохранённым; обновлён только TTL
204                                        # сохранён, но игрок сейчас не в сети в mssgs
410 {"error":"LINK_REVOKED"}               # игрок отключил игру: удалите связь

Обрабатывайте 200 и 204 одинаково: сохранено. { "activity": null } очищает блок, отправьте его, когда игрок уходит. Одно отличие от моста: хост в join.url должен быть одним из ваших зарегистрированных бэкендов (или их поддоменом), иначе вы получите 400 INVALID_PAYLOAD. Так бэкенд не может поставить в статус игрока кнопку «Присоединиться», ведущую туда, где этот игрок никогда не играл.

Heartbeat каждые 60 секунд, TTL 120

Опубликованный статус живёт 120 секунд без нового сообщения, а затем исчезает сам. Поэтому повторяйте тот же блок каждые 60 секунд; неизменённый блок ничего не стоит и только обновляет TTL. Если heartbeat прекращается, пропадает и строка «Играет в …», в этом весь смысл.

Когда в сети сотни игроков, отправляйте heartbeat одним вызовом, до 100 элементов за раз. Каждый элемент получает собственный статус, так что один игрок, отключивший игру в mssgs, никогда не остановит остальных девяносто девять:

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

Как показывается статус

  • Так же, как статус с моста: Играет в CozyCity · Lantern Hollow · Игроков: 6/40, с кнопкой «Присоединиться», если есть join.url. Сервер помечает блок как via: "backend", так что клиент может добавить «Передаёт сервер игры».
  • Только пока игрок в сети в mssgs. Если ни один клиент mssgs не открыт, аккаунт не в сети и остаётся не в сети; ваш бэкенд не может создать видимость присутствия. Это же не даёт этому пути превратиться в маячок «сидит ли René за компьютером».
  • Приоритет: игра внутри приложения > игра через мост > ваш бэкенд. Если игрок садится за шахматы внутри mssgs, пока ваш бэкенд продолжает отправлять heartbeat, побеждают шахматы, а не тот, кто написал последним.

Отключение

Игрок видит каждую связь в разделе Настройки → Игровая активность → Связанные игры: ваш значок и название, имя игрока из вашей игры, когда связь была создана и когда игра последний раз публиковала статус, а также кнопку «Отключить». После этого ваша следующая публикация получит ответ 410 LINK_REVOKED; так ваша игра об этом узнаёт. Удалите link_guid и снова предложите «Подключить mssgs». Со своей стороны связь можно завершить через DELETE /game-sdk/v1/links/{link_guid}.

Ограничения

Для одной связи изменение учитывается не чаще раза в 2 секунды; неизменённый heartbeat бесплатен. На один ключ приходится 600 запросов в минуту, элементы пакета считаются по отдельности: heartbeat для 300 игроков каждые 60 секунд расходует 5 из 600.

Справочник эндпоинтов: мост

Базовый URL http://127.0.0.1:<port>. Всё, кроме первых трёх, требует Authorization: Bearer <token>.

Метод Путь Scope Что делает
GET /mssgs/v1/hello нет Есть ли здесь mssgs, какую версию API он поддерживает и вошёл ли кто-нибудь в аккаунт. Единственный маршрут без токена, и он ничего не сообщает об игроке.
POST /mssgs/v1/authorize нет Запросить разрешение у игрока. Открывает диалог в приложении и возвращает request_id для опроса.
GET /mssgs/v1/authorize/:request_id нет pending, approved (с токеном), denied или expired.
GET /mssgs/v1/me identity Игрок, вошедший в аккаунт. Добавляет is_staff / is_moderator только при области доступа staff.
GET /mssgs/v1/membership membership.query Членство в переданных значениях server_guid (до 10, повторяющимся параметром или через запятую).
GET /mssgs/v1/servers servers.list Все сообщества игрока с его ролями. Личные сообщения никогда не включаются.
PUT /mssgs/v1/activity presence.write Опубликовать блок «Играет в …». Возвращает TTL и частоту heartbeat.
POST /mssgs/v1/activity/heartbeat presence.write Поддерживать опубликованную активность, не отправляя её заново.
DELETE /mssgs/v1/activity presence.write Сразу очистить её при штатном завершении.
GET /mssgs/v1/events presence.write Передачи приглашений, адресованные вашей игре. Опрашивайте с ?since=<cursor>.
GET /mssgs/v1/session нет Что содержит этот токен: game_id, выданные области доступа, вошёл ли кто-нибудь в аккаунт.
DELETE /mssgs/v1/session нет Вернуть разрешение. Тот же эффект, что и отзыв игроком в Настройках.

Справочник эндпоинтов: связанные бэкенды

Базовый URL https://ams1-gateway.mss.gs. Каждый маршрут требует Authorization: Bearer <backend key> и области доступа activity.write в вашей регистрации; ответы отправляются с Cache-Control: no-store. Вызывайте их со своего сервера, никогда из клиента игры.

Метод Путь Что делает
POST /game-sdk/v1/link/start Начать связывание для одного из ваших игроков ({ player_ref, player_name? }). Возвращает link_code, device_code, qr_url, deep_link, expires_in и interval.
POST /game-sdk/v1/link/poll { device_code } → pending, denied, expired или linked с link_guid и user.
DELETE /game-sdk/v1/links/{link_guid} Завершить связь со своей стороны. Игрок может сделать то же самое в Настройках.
PUT /game-sdk/v1/links/{link_guid}/activity Опубликовать блок «Играет в …» для одного игрока; { "activity": null } очищает его.
POST /game-sdk/v1/activity/batch То же самое для до 100 игроков одним вызовом. Каждый элемент получает собственный ответ.

Коды ошибок

Ошибки возвращаются как {"error":"CODE","message":"…"} с соответствующим HTTP-статусом.

Код Значение
401 UNAUTHORIZEDТокен отсутствует или неизвестен; сначала пройдите авторизацию.
403 MISSING_SCOPEИгрок не выдал это разрешение. Возможно, он снял с него галочку.
403 ORIGIN_NOT_ALLOWEDЗапрос содержал браузерный Origin. См. «Только нативные игры» ниже.
409 NOT_SIGNED_INmssgs запущен, но никто не вошёл в аккаунт.
429 RATE_LIMITEDБольше 120 запросов в минуту от одной игры.
400 INVALID_GAME_IDgame_id может содержать только буквы, цифры, точку, дефис или подчёркивание.
400 TOO_MANY_GUIDSНе больше 10 значений server_guid за один вызов membership.

Связанные бэкенды

Маршруты бэкенда используют тот же формат. Внутри пакета статус возвращается для каждого элемента в results, так что одна завершённая связь никогда не проваливает весь вызов.

Код Значение
401 INVALID_BACKEND_KEYНеизвестный ключ или ключ, заменённый больше 24 часов назад.
403 SCOPE_NOT_GRANTEDВ вашей регистрации нет области доступа, нужной этому маршруту.
400 INVALID_PAYLOADНекорректное тело запроса, больше 100 элементов пакета или join.url, хост которого не входит в ваши зарегистрированные бэкенды.
400 INVALID_ACTIVITYПосле нормализации не осталось пригодного name.
410 LINK_REVOKEDСвязь завершена, с одной из сторон. Удалите её и снова предложите «Подключить mssgs».
429 SLOW_DOWNВы опрашивали link/poll чаще, чем interval.
429 RATE_LIMITEDИзменение одной связи в течение 2 секунд после предыдущего или больше 600 запросов в минуту на ваш ключ.
503 LINK_STORE_UNAVAILABLEВременная проблема на нашей стороне. Повторите при следующем heartbeat.

Безопасность

Только нативные игры

Запросы с Origin веб-страницы отклоняются с 403 ORIGIN_NOT_ALLOWED. Если бы любая веб-страница могла обнаружить, что у вас запущен mssgs, и вызвать диалог разрешения, это была бы поверхность для фингерпринтинга и фишинга, а не возможность. Нативная игра вообще не отправляет Origin, поэтому её это не касается, а встроенный браузер игры разрешён явно по имени, см. FiveM. Если вы делаете браузерную или мобильную игру, вы не обращаетесь к мосту: за связанных игроков публикует ваш собственный бэкенд, см. Браузерные и мобильные игры.

Что остаётся под контролем игрока

  • Игрок может выключить мост в разделе Настройки → Игровая активность, после чего ни одна игра вообще не видит mssgs.
  • Там же перечислена каждая одобренная игра с точным списком её разрешений, временем последней активности и кнопкой «Убрать». Удаление срабатывает сразу: токен немедленно перестаёт действовать.
  • Связанная браузерная или мобильная игра указана в разделе Связанные игры с кнопкой «Отключить». Отключение тоже срабатывает сразу: следующая публикация этого бэкенда получает 410.
  • Мост слушает только 127.0.0.1 и никогда не слушает сеть.
  • Личные сообщения никогда не раскрываются, даже с servers.list.
  • На каждую игру действует лимит 120 запросов в минуту.

Как вести себя корректно

  • Запрашивайте области доступа, когда они нужны, а не все сразу при первом запуске.
  • Работайте и без mssgs: он не обязан быть у игрока.
  • Очищайте статус, когда игра заканчивается, не дожидаясь TTL.
  • Считайте отклонённую область доступа нормальным исходом, а не ошибкой.

Что дальше