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
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.
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é.
{
"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.
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.
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.
{
"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 :
{ "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 groupe | une escouade, une équipe ou un groupe |
server | 4/100 joueurs | un serveur de jeu (FiveM, un serveur communautaire) |
lobby | 4/100 joueurs | un lobby avant le début de la partie |
match | 4/100 joueurs | une 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 :
{
"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.
-- 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)
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.
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 :
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 :
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 :
{
"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} # 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 :
{ "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 UNAUTHORIZED | Jeton manquant ou inconnu ; demande d’abord l’autorisation. |
403 MISSING_SCOPE | Le joueur n’a pas accordé cette permission. Il l’a peut-être décochée. |
403 ORIGIN_NOT_ALLOWED | La requête portait un Origin de navigateur. Voir « Jeux natifs uniquement » plus bas. |
409 NOT_SIGNED_IN | mssgs tourne mais personne n’est connecté. |
429 RATE_LIMITED | Plus de 120 requêtes en une minute depuis un même jeu. |
400 INVALID_GAME_ID | game_id doit être composé de lettres, chiffres, points, tirets ou underscores. |
400 TOO_MANY_GUIDS | Au 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_KEY | Clé inconnue, ou renouvelée il y a plus de 24 heures. |
403 SCOPE_NOT_GRANTED | Ton enregistrement ne détient pas le scope dont cette route a besoin. |
400 INVALID_PAYLOAD | Corps 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_ACTIVITY | Plus aucun name utilisable après normalisation. |
410 LINK_REVOKED | La liaison a pris fin, d’un côté ou de l’autre. Abandonne-la et propose à nouveau « Connecter mssgs ». |
429 SLOW_DOWN | Tu as interrogé link/poll plus vite que interval. |
429 RATE_LIMITED | Un 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_UNAVAILABLE | Problè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.