Aller au contenu principal
Développeurs Game SDK

Montrer à quoi quelqu’un joue

Laisse ton jeu dire à mssgs ce que fait un joueur. Ses amis voient « Joue à » sous son nom, ouvrent les détails et appuient sur Rejoindre pour arriver dans la même partie. Ton jeu peut aussi vérifier si un joueur fait partie de ta communauté.

Ce que vous pouvez faire

  • Publier un statut de jeuLe jeu, ce que fait le joueur, son rôle et le remplissage du groupe.
  • Ajouter un bouton RejoindreLes amis rejoignent la même partie, le même serveur ou le même lobby en un appui.
  • Vérifier l’appartenanceDemande si un joueur fait partie de ta communauté, et avec quels rôles.
  • Ordinateur, navigateur ou téléphoneLes jeux natifs passent par le pont local ; les jeux navigateur et mobiles passent par ton backend.

Dans l'app

daniJoue à Space Raiders
Space RaidersJoué par dani
Sector 7
En raid
3 sur 4 dans le groupe · depuis 12 min
Artilleur

Le statut sous un nom, et les détails qu’il ouvre. Le jeu a publié un seul bloc JSON ; le reste, c’est l’application.

Présentation

L’application de bureau mssgs fait tourner un petit pont HTTP local auquel un jeu sur la même machine s’adresse. Ton jeu ne parle jamais à nos serveurs, ne voit jamais le mot de passe ni le jeton d’un compte, et ne peut jamais publier au nom du joueur. Il parle à la copie de mssgs sur laquelle le joueur est déjà connecté, et c’est cette copie qui décide quoi répondre.

Ce que tu peux en faire :

  • Détecter que mssgs est installé et que quelqu’un est connecté.
  • Savoir qui est le joueur : user_guid, nom d’utilisateur, avatar.
  • Demander « ce joueur est-il dans la communauté X ? » et quel rôle il y occupe.
  • Publier un statut « Joue à … » avec un bouton Rejoindre pour les autres.
  • Recevoir une transmission de connexion quand quelqu’un appuie sur ce bouton.

Deux façons d’entrer

Un jeu de bureau natif s’adresse au pont local ; c’est ce que décrivent les sections suivantes. Un jeu dans un navigateur ou sur un téléphone ne peut pas atteindre ce pont. Pour ceux-là, c’est ton propre backend qui publie, pour les joueurs qui ont relié leur compte mssgs via un QR ou un code à huit caractères : voir Jeux navigateur et mobile, avec CozyCity comme premier exemple. Le statut de jeu lui-même est le même bloc dans les deux cas.

Divulgation minimale, par défaut

Les scopes sont volontairement inégaux. Si tout ce dont tu as besoin est « cette personne est-elle dans notre communauté », tu demandes membership.query et tu nommes toi-même le server_guid : tu obtiens oui/non plus ses rôles là-bas, et tu n’apprends rien sur le reste de ses communautés. La liste complète se trouve derrière un scope distinct, plus élevé, que le joueur doit approuver séparément.

Trouver le client

Le pont écoute uniquement sur 127.0.0.1, sur le premier port libre d’une petite plage. Essaie-les dans l’ordre jusqu’à ce que l’un réponde : 7440, 7441, 7442, 7443. Les versions de développement de mssgs écoutent plutôt sur 7540–7543, pour qu’une version de test ne réponde jamais aux appels d’un vrai jeu.

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

Aucun jeton nécessaire, et la réponse ne dit rien du joueur, seulement que mssgs est là et si quelqu’un est connecté.

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

Vérifie product === "mssgs" et api avant d’aller plus loin. Si aucun des quatre ports ne répond, mssgs ne tourne pas. Propose simplement ton expérience normale au lieu de faire attendre le joueur.

Demander la permission

Tout sauf /hello nécessite un jeton, et un jeton n’existe qu’une fois que le joueur a approuvé ton jeu dans une boîte de dialogue de l’application. Ne demande que les scopes que tu utilises vraiment : le joueur voit chacun séparément, expliqué, et peut les décocher un par un.

1. Demander la permission
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"]
  }'

Tu reçois {"status":"pending","request_id":"…","poll_after_ms":1000} et le joueur voit la boîte de dialogue. Interroge ensuite jusqu’à ce qu’il réponde (la demande expire au bout de 3 minutes) :

2. Interroger pour la réponse
curl http://127.0.0.1:7440/mssgs/v1/authorize/<request_id>

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

Vérifie toujours ce que tu as réellement obtenu

Les scopes de la réponse peuvent être moins nombreux que ceux que tu as demandés : le joueur est libre d’en décocher certains. Dans l’exemple ci-dessus, membership.query a été refusé. Décide en fonction de ce que dit la réponse, pas de ce que tu as demandé, sinon tu tomberas sur un 403 MISSING_SCOPE que tu n’avais pas prévu.

Conserve le jeton et envoie-le sous la forme Authorization: Bearer <token>. Il survit aux redémarrages, donc un joueur approuve ton jeu une fois plutôt qu’à chaque session. Si tu redemandes plus tard l’autorisation avec des scopes déjà accordés, tu récupères directement le même jeton, sans boîte de dialogue.

Scopes et confidentialité

Les cinq scopes révèlent des quantités très différentes. Ce n’est pas un hasard, c’est tout le principe. Demande le moins possible, en descendant ce tableau.

Scope Ce qu’il permet Ce que le joueur cède
presence.write Montrer à quoi il joue Rien. Ce scope ne fait qu’écrire ; il ne lit aucune donnée du compte.
identity Qui est le joueur user_guid, nom d’utilisateur, nom affiché, URL de l’avatar.
staff Indicateurs équipe / modérateur Deux booléens, en plus d’identity. À part, parce qu’un jeu qui affiche un nom n’a pas à savoir que le joueur modère des communautés.
membership.query Vérifier une communauté que tu connais déjà Pour un server_guid que tu nommes : oui/non, son nom et les rôles que ce joueur y détient. Rien sur aucune autre communauté.
servers.list Toutes les communautés dont il fait partie La liste complète : guids, noms, icônes et rôles. C’est le scope coûteux : ne le demande que si tu en as vraiment besoin.

La plupart des jeux en ont besoin de deux

identity et presence.write couvrent « qui es-tu » et « montre à quoi tu joues », ce qui correspond à presque toutes les intégrations. Ajoute membership.query si tu veux lier une récompense à l’appartenance à ta communauté. Tu n’as presque jamais besoin de servers.list, et le joueur le voit surligné en rouge.

Vérifier l’appartenance

C’est l’alternative à « donne-moi toute la liste ». Tu nommes le server_guid de ta propre communauté (que tu connais déjà) et tu obtiens une réponse sur celle-là seulement.

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

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

# pas membre, et rien d’autre :
# {"server_guid":"…","member":false}

Un « non » veut dire exactement cela, rien de plus. Tu peux passer jusqu’à 10 guids par appel (répète server_guid ou sépare-les par des virgules), ce qui renvoie un tableau results. Le groupe @everyone n’est jamais dans roles : il vaut pour chaque membre, il ne t’apprend donc rien.

Publier un statut de jeu

Un seul PUT place la ligne « Joue à … » sous le nom du joueur, partout où ses communautés le voient.

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

Seul name est obligatoire. La réponse te dit combien de temps le statut vit et à quelle fréquence envoyer un heartbeat :

Réponse
{ "ok": true, "expires_in_ms": 90000, "heartbeat_every_ms": 30000 }

Un heartbeat, sinon le statut disparaît

Un statut sans signe de vie pendant 90 secondes est effacé automatiquement. C’est voulu : si ton jeu plante, le joueur ne reste pas « en train de jouer » pendant des heures. Envoie un POST /mssgs/v1/activity/heartbeat toutes les 30 secondes, et DELETE /mssgs/v1/activity lors d’un arrêt propre.

Nombre de joueurs et rôle

party.kind décide quelle phrase est affichée, car les deux mêmes nombres ne veulent pas dire la même chose. Une escouade de quatre n’est pas un serveur avec quatre joueurs dessus.

kind S’affiche comme Pour
party (par défaut)3 sur 4 dans le groupeune escouade, une équipe ou un groupe
server4/100 joueursun serveur de jeu (FiveM, un serveur communautaire)
lobby4/100 joueursun lobby avant le début de la partie
match4/100 joueursune partie ou une manche en cours

role (jusqu’à 48 caractères) est ce que le joueur incarne : un métier, une classe ou un personnage. Il a son propre champ plutôt qu’une phrase de plus dans state, car il s’affiche comme une étiquette à côté du nombre de joueurs.

details et state sont limités à 128 caractères chacun, name à 64. Les sauts de ligne et les caractères de contrôle sont supprimés. Une URL d’icône n’est volontairement pas prise en charge : elle serait chargée par chaque client qui affiche la ligne, ce qui transformerait un statut en balise signalant à ton serveur chaque membre de chaque communauté dont le joueur fait partie.

Le bouton Rejoindre

Mets un bloc join dans ton activité et les autres membres voient un bouton Rejoindre à côté du statut. Il y a deux façons de faire, et tu peux les combiner.

1. Un secret (pour les jeux natifs)

Définis {"join":{"secret":"raid-42"}}. Quand quelqu’un appuie sur Rejoindre, ce secret est remis à sa propre copie de ton jeu, sur sa propre machine, identifiée par le même game_id. Aucune URL n’est ouverte et aucun gestionnaire de schéma n’est invoqué. Ton jeu le récupère avec :

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

Interroge avec ?since=<cursor> pour voir chaque événement une seule fois. Si le jeu de la personne qui a appuyé ne tourne pas, rien n’est remis, ce qui est une bonne raison de proposer aussi une URL.

2. Une URL https (pour les jeux web et les liens de lobby)

Définis {"join":{"url":"https://play.example.com/s/abc"}} et le bouton ouvre ce lien. Seul https est accepté. Un schéma personnalisé (steam://, mygame://, file://) est refusé : ce bloc atterrit sur l’écran de chaque membre, et une telle URL permet de faire invoquer à la machine de quelqu’un d’autre un gestionnaire local avec des arguments que tu as choisis.

Tout ce qui est dans join est public

Le bloc join est diffusé à toutes les personnes qui peuvent voir le statut du joueur ; c’est tout l’intérêt d’un bouton Rejoindre. Traite-le donc comme un code de lobby, pas comme un identifiant. N’y mets jamais rien qui doive rester secret, et fais expirer tes codes.

FiveM

FiveM n’a pas de HTTP dans son runtime Lua côté client, donc une ressource s’adresse au pont via NUI, une vue CEF, qui envoie un Origin. Le pont accepte explicitement ces origines : https://cfx-nui-<resource> et l’ancien nui://<resource>. Les pages web ordinaires restent refusées, et une page du web ouvert ne peut pas revendiquer cette origine ; c’est le navigateur qui la définit lui-même.

client.lua : demander à la NUI de publier
-- La page NUI fait le HTTP ; Lua lui envoie seulement les données.
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 : le statut expire après 90 s
  end
end)
nui.js
const BASE = 'http://127.0.0.1:7440/mssgs/v1';   // essaie 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']          // rien de plus n’est nécessaire ici
    })
  });
  const started = await res.json();
  if (started.status === 'approved') { return started.token; }

  // Le joueur voit maintenant la boîte de dialogue de permission dans 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' } // ton lien cfx.re
    })
  });
});

Le résultat : Joue à FiveM · Los Santos Roleplay · 4/100 joueurs · Police, avec un bouton Rejoindre qui ouvre ton lien cfx.re.

Demande seulement presence.write

Un statut de jeu n’a besoin de rien d’autre : ce scope ne lit absolument rien. Si tu veux lier une récompense en jeu à l’appartenance à ta communauté mssgs, ajoute membership.query et nomme ton propre server_guid ; tu n’apprends toujours rien sur les autres communautés du joueur.

Un serveur sur lequel tu joues n’est pas automatiquement de confiance

N’importe quel serveur FiveM peut exécuter des ressources client, donc n’importe quel serveur que quelqu’un rejoint peut demander la permission. C’est justement pour cela qu’une boîte de dialogue s’intercale, en nommant la ressource : c’est le joueur qui décide, pas le serveur.

Jeux navigateur et mobile : la liaison via ton backend

Un jeu dans un onglet de navigateur ou sur un téléphone ne peut pas atteindre le pont décrit plus haut. Le pont tourne sur l’ordinateur du joueur, et trois murs se dressent entre les deux : le pont refuse toute requête qui porte un Origin de navigateur, Chrome place une demande de permission devant une page publique qui interroge 127.0.0.1 et Safari refuse tout net, et un téléphone n’a aucun chemin vers la boucle locale d’un ordinateur.

Le sens s’inverse donc. Ton propre backend sait déjà qui joue, et c’est lui qui le dit à mssgs, pour les joueurs qui ont relié leur compte mssgs à ton jeu. La liaison est approuvée dans l’application mssgs, jamais dans ton jeu, et elle crée un lien, jamais une session : rien de ce qui suit ne peut connecter qui que ce soit ni agir au nom du joueur. Ton client de jeu ne voit jamais de clé et ne parle jamais à mss.gs. Le premier jeu sur cette voie est CozyCity, un city builder livré sous forme de page WebGL et d’app iPhone, sans version de bureau ; les exemples ci-dessous sont les siens.

1. Enregistre ton jeu

Enregistre le jeu sur la page d’enregistrement du Game SDK : ton game_id (par exemple com.deverence.cozycity), le nom et l’icône que le joueur voit sur la fiche d’approbation, et les noms d’hôte de ton backend. Nous l’examinons là-bas, et une fois approuvé, ta clé de backend t’attend sur la même page, affichée une seule fois ; nous n’en gardons qu’une empreinte. La clé a sa place sur ton serveur et nulle part ailleurs. Tu peux la renouveler là-bas à tout moment, et l’ancienne reste valable 24 heures pour qu’un déploiement puisse se faire progressivement.

Enregistrer ton jeu

Le nom et l’icône de la fiche viennent toujours de l’enregistrement, jamais de la requête. Sinon, un lien d’hameçonnage pourrait déguiser une demande de liaison en n’importe quel jeu. Les noms d’hôte délimitent où un join.url peut pointer, voir plus bas.

2. Relie un joueur

Le joueur choisit Connecter mssgs dans ton jeu. Ton jeu demande à ton backend, et ton backend nous demande :

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 est ton propre id stable pour ce joueur (jusqu’à 128 caractères), pas pour une session ni une partie ; player_name (jusqu’à 64) est ce que la fiche affiche après « Joueur : ». Ne transmets à ton client de jeu que link_code, qr_url et deep_link. device_code est ta référence d’interrogation et reste sur le serveur.

Ton jeu affiche alors trois choses à la fois, car le joueur peut se trouver n’importe où :

  • Le QR de qr_url. Un téléphone avec mssgs l’ouvre directement sur la fiche d’approbation de l’application. Sans l’application, il arrive sur une page de mss.gs qui affiche le code et propose le téléchargement.
  • Un bouton « Ouvrir dans mssgs » avec deep_link, pour un navigateur de bureau qui se trouve à côté de l’application de bureau. C’est la seule URL externe que ton jeu doit jamais ouvrir.
  • Le code lui-même, en deux groupes de quatre, à saisir dans Réglages → Activité de jeu → Lier un jeu. L’alphabet n’a ni 0/O ni 1/I, donc la saisie se passe rarement mal.

Ce que le joueur voit dans mssgs, dessiné par l’application à partir de l’enregistrement :

Connecter CozyCity à ton compte mssgs ?

CozyCity pourra montrer à quoi tu joues dans ton statut mssgs. Il ne verra pas tes messages, tes amis ni tes serveurs, et ne pourra pas publier en ton nom.
Joueur : La ville de René · Connecter / Pas maintenant

Pendant ce temps, ton backend interroge toutes les interval secondes (plus vite, la réponse est 429 SLOW_DOWN) jusqu’à ce que le statut change. Un code ne sert qu’une fois et expire au bout de dix minutes :

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"}      # le joueur a choisi Pas maintenant
# {"status":"expired"}
# {"status":"linked","link_guid":"…","user":{"user_guid":"62e377…"}}

Associe link_guid à ton joueur ; c’est désormais l’adresse pour laquelle tu publies. La réponse ne contient que le user_guid ; un username n’est ajouté que si ton enregistrement détient le scope identity.link, et il n’y a rien au-delà. Une deuxième approbation pour le même player_ref remplace la liaison précédente, donc un joueur de ton jeu correspond à un compte mssgs. Un même compte mssgs peut être relié à plusieurs jeux et à plusieurs player_ref d’un même jeu (un iPad familial).

3. Publie le statut de jeu

Le même bloc que sur le pont, avec les mêmes règles et les mêmes limites, mais cette fois par liaison et avec ta clé de backend :

PUT /game-sdk/v1/links/{link_guid}/activity
{
  "activity": {
    "name":       "CozyCity",
    "details":    "Lantern Hollow",
    "state":      "Day 12 · 34 residents",
    "started_at": 1788901000000,
    "party":      { "size": 6, "max": 40, "kind": "server" },
    "join":       { "url": "https://cozycity.net/game/?share=…" }
  }
}
Réponses
200 {"published":true,"changed":true}     # le bloc a changé et a été diffusé
200 {"published":true,"changed":false}    # identique à ce qui était stocké ; seul le TTL a été rafraîchi
204                                        # stocké, mais le joueur n’est pas en ligne dans mssgs en ce moment
410 {"error":"LINK_REVOKED"}               # le joueur s’est déconnecté : abandonne la liaison

Traite 200 et 204 de la même façon : stocké. { "activity": null } efface le bloc, envoie-le quand le joueur s’en va. Une différence avec le pont : l’hôte de join.url doit être l’un de tes backends enregistrés (ou un sous-domaine de l’un d’eux), sinon tu reçois 400 INVALID_PAYLOAD. Un backend ne peut donc pas placer sur le statut d’un joueur un bouton Rejoindre qui mène quelque part où ce joueur n’a jamais joué.

Un heartbeat toutes les 60 secondes, TTL 120

Un statut publié vit 120 secondes sans nouveau message, puis disparaît tout seul. Renvoie donc le même bloc toutes les 60 secondes ; un bloc inchangé ne coûte rien et ne fait que rafraîchir le TTL. Si ton heartbeat s’arrête, la ligne « Joue à … » s’arrête aussi, et c’est exactement le but.

Avec des centaines de joueurs en ligne, envoie le heartbeat en un seul appel, jusqu’à 100 éléments à la fois. Chaque élément reçoit son propre statut, donc un joueur qui s’est déconnecté dans mssgs n’arrête jamais les quatre-vingt-dix-neuf autres :

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

Comment le statut s’affiche

  • Exactement comme un statut du pont : Joue à CozyCity · Lantern Hollow · 6/40 joueurs, avec Rejoindre quand il y a un join.url. Le bloc est marqué via: "backend" côté serveur, donc un client peut ajouter « Partagé par le serveur du jeu ».
  • Seulement tant que le joueur est en ligne dans mssgs. Sans client mssgs ouvert, le compte est hors ligne et le reste ; ton backend ne peut pas faire paraître quelqu’un présent. Cela empêche aussi cette voie de devenir une balise « René est-il devant son ordinateur ».
  • Priorité : un jeu intégré à l’application > un jeu sur le pont > ton backend. Si le joueur se lance dans une partie d’échecs dans mssgs pendant que ton backend continue d’envoyer des heartbeats, ce sont les échecs qui l’emportent, pas le dernier à avoir écrit.

Déconnexion

Le joueur voit chaque liaison dans Réglages → Activité de jeu → Jeux liés, avec ton icône et ton nom, le nom de joueur venant de ton jeu, la date de la liaison et de la dernière publication, et un bouton Déconnecter. Ensuite, ta publication suivante répond 410 LINK_REVOKED ; c’est ainsi que ton jeu l’apprend. Abandonne le link_guid et propose à nouveau « Connecter mssgs ». De ton côté, mets fin à une liaison avec DELETE /game-sdk/v1/links/{link_guid}.

Limites

Par liaison, un changement compte au plus toutes les 2 secondes ; un heartbeat inchangé est gratuit. Par clé, il y a 600 requêtes par minute, les éléments d’un lot étant comptés individuellement : envoyer un heartbeat pour 300 joueurs toutes les 60 secondes en consomme 5 sur 600.

Référence des endpoints : le pont

URL de base http://127.0.0.1:<port>. Tout sauf les trois premiers exige Authorization: Bearer <token>.

Méthode Chemin Scope Ce qu’il fait
GET /mssgs/v1/hello aucun mssgs est-il là, quelle version de l’API parle-t-il, et quelqu’un est-il connecté. La seule route qui ne demande pas de jeton, et elle ne dit rien du joueur.
POST /mssgs/v1/authorize aucun Demande la permission au joueur. Ouvre une boîte de dialogue dans l’application et renvoie un request_id à interroger.
GET /mssgs/v1/authorize/:request_id aucun pending, approved (avec le jeton), denied ou expired.
GET /mssgs/v1/me identity Le joueur connecté. Ajoute is_staff / is_moderator uniquement avec le scope staff.
GET /mssgs/v1/membership membership.query L’appartenance aux server_guid que tu passes (jusqu’à 10, répétés ou séparés par des virgules).
GET /mssgs/v1/servers servers.list Toutes les communautés dont le joueur fait partie, avec ses rôles. Les messages privés ne sont jamais inclus.
PUT /mssgs/v1/activity presence.write Publie le bloc « Joue à … ». Renvoie le TTL et la fréquence du heartbeat.
POST /mssgs/v1/activity/heartbeat presence.write Maintient l’activité publiée en vie sans la renvoyer.
DELETE /mssgs/v1/activity presence.write L’efface immédiatement, pour un arrêt propre.
GET /mssgs/v1/events presence.write Les transmissions Rejoindre destinées à ton jeu. Interroge avec ?since=<cursor>.
GET /mssgs/v1/session aucun Ce que ce jeton détient : game_id, scopes accordés, si quelqu’un est connecté.
DELETE /mssgs/v1/session aucun Rend la permission. Même effet que si le joueur la révoquait dans les Réglages.

Référence des endpoints : backends liés

URL de base https://ams1-gateway.mss.gs. Chaque route exige Authorization: Bearer <backend key> et le scope activity.write sur ton enregistrement ; les réponses partent avec Cache-Control: no-store. Appelle-les depuis ton serveur, jamais depuis le client de jeu.

Méthode Chemin Ce qu’il fait
POST /game-sdk/v1/link/start Lance une liaison pour l’un de tes joueurs ({ player_ref, player_name? }). Renvoie link_code, device_code, qr_url, deep_link, expires_in et interval.
POST /game-sdk/v1/link/poll { device_code } → pending, denied, expired, ou linked avec link_guid et user.
DELETE /game-sdk/v1/links/{link_guid} Met fin à une liaison de ton côté. Le joueur peut faire de même depuis les Réglages.
PUT /game-sdk/v1/links/{link_guid}/activity Publie le bloc « Joue à … » pour un joueur ; { "activity": null } l’efface.
POST /game-sdk/v1/activity/batch La même chose, pour jusqu’à 100 joueurs en un seul appel. Chaque élément répond séparément.

Codes d’erreur

Les erreurs reviennent sous la forme {"error":"CODE","message":"…"} avec le statut HTTP correspondant.

Code Signification
401 UNAUTHORIZEDJeton manquant ou inconnu ; demande d’abord l’autorisation.
403 MISSING_SCOPELe joueur n’a pas accordé cette permission. Il l’a peut-être décochée.
403 ORIGIN_NOT_ALLOWEDLa requête portait un Origin de navigateur. Voir « Jeux natifs uniquement » plus bas.
409 NOT_SIGNED_INmssgs tourne mais personne n’est connecté.
429 RATE_LIMITEDPlus de 120 requêtes en une minute depuis un même jeu.
400 INVALID_GAME_IDgame_id doit être composé de lettres, chiffres, points, tirets ou underscores.
400 TOO_MANY_GUIDSAu maximum 10 valeurs server_guid par appel d’appartenance.

Backends liés

Les routes du backend utilisent la même forme. Dans un lot, le statut revient par élément dans results, donc une liaison terminée ne fait jamais échouer tout l’appel.

Code Signification
401 INVALID_BACKEND_KEYClé inconnue, ou renouvelée il y a plus de 24 heures.
403 SCOPE_NOT_GRANTEDTon enregistrement ne détient pas le scope dont cette route a besoin.
400 INVALID_PAYLOADCorps mal formé, plus de 100 éléments dans un lot, ou un join.url dont l’hôte n’est pas l’un de tes backends enregistrés.
400 INVALID_ACTIVITYPlus aucun name utilisable après normalisation.
410 LINK_REVOKEDLa liaison a pris fin, d’un côté ou de l’autre. Abandonne-la et propose à nouveau « Connecter mssgs ».
429 SLOW_DOWNTu as interrogé link/poll plus vite que interval.
429 RATE_LIMITEDUn changement sur une liaison moins de 2 secondes après le précédent, ou plus de 600 requêtes en une minute sur ta clé.
503 LINK_STORE_UNAVAILABLEProblème temporaire de notre côté. Réessaie au prochain heartbeat.

Sécurité

Jeux natifs uniquement

Les requêtes qui portent un Origin de page web sont refusées avec 403 ORIGIN_NOT_ALLOWED. Qu’une page web quelconque puisse détecter que tu utilises mssgs et faire apparaître une demande de permission serait une surface de fingerprinting et d’hameçonnage, pas une fonctionnalité. Un jeu natif n’envoie aucun Origin, il n’est donc pas concerné, et le navigateur intégré d’un jeu est autorisé nommément, voir FiveM. Si tu développes un jeu navigateur ou mobile, tu ne t’adresses pas au pont : ton propre backend publie pour les joueurs liés, voir Jeux navigateur et mobile.

Ce que le joueur garde en main

  • Le joueur peut désactiver le pont dans Réglages → Activité de jeu, après quoi aucun jeu ne peut plus voir mssgs.
  • Chaque jeu approuvé y est listé avec exactement les permissions qu’il détient, sa dernière activité et un bouton Retirer. Le retrait est immédiat : le jeton meurt aussitôt.
  • Un jeu navigateur ou mobile lié est listé sous Jeux liés avec un bouton Déconnecter. La déconnexion est immédiate aussi : la publication suivante de ce backend reçoit un 410.
  • Le pont écoute uniquement sur 127.0.0.1, jamais sur le réseau.
  • Les messages privés ne sont jamais divulgués, pas même avec servers.list.
  • Chaque jeu dispose d’un budget de 120 requêtes par minute.

Se comporter en bon citoyen

  • Demande les scopes quand tu en as besoin, pas tous d’un coup au premier lancement.
  • Fonctionne sans mssgs : le joueur n’est pas obligé de l’avoir.
  • Efface ton statut quand la partie s’arrête au lieu d’attendre le TTL.
  • Traite un scope refusé comme un résultat normal, pas comme une erreur.

Continuer