Muestra a qué está jugando alguien
Deja que tu juego le diga a mssgs qué está haciendo un jugador. Sus amigos ven “Playing” bajo su nombre, abren los detalles y pulsan Join now para entrar en la misma partida. Tu juego también puede comprobar si un jugador está en tu comunidad.
Qué puedes hacer
- Publica un estado de juegoEl juego, lo que está haciendo el jugador, su rol y lo lleno que está el grupo.
- Añade un botón Join nowLos amigos se unen al mismo juego, servidor o lobby con una sola pulsación.
- Comprueba la pertenenciaPregunta si un jugador está en tu comunidad y con qué roles.
- Escritorio, navegador o móvilLos juegos nativos usan el puente local; los juegos de navegador y de móvil pasan por tu backend.
En la app
El estado bajo un nombre y los detalles que abre. El juego publicó un solo bloque JSON; el resto lo pone la app.
Descripción general
La app de escritorio de mssgs ejecuta un pequeño puente HTTP local con el que habla un juego en el mismo equipo. Tu juego nunca habla con nuestros servidores, nunca ve la contraseña ni el token de una cuenta y nunca puede publicar en nombre del jugador. Habla con la copia de mssgs en la que el jugador ya ha iniciado sesión, y es esa copia la que decide qué responder.
Lo que puedes hacer con él:
- Detectar que mssgs está instalado y que alguien ha iniciado sesión.
- Leer quién es el jugador: user_guid, username, avatar.
- Preguntar “¿está este jugador en la comunidad X?” y qué rol tiene allí.
- Publicar un estado “Playing …” con un botón Join now para los demás.
- Recibir los datos para unirse cuando alguien pulsa ese botón.
Dos formas de entrar
Un juego nativo de escritorio habla con el puente local; eso es lo que describen las secciones siguientes. Un juego en un navegador o en un móvil no puede llegar a ese puente. En esos casos publica tu propio backend, para los jugadores que han vinculado su cuenta de mssgs con un QR o con un código de ocho caracteres: consulta Juegos de navegador y móvil, con CozyCity como primer ejemplo. El estado de juego en sí es el mismo bloque en ambos casos.
Lo mínimo imprescindible, por defecto
Los scopes son desiguales a propósito. Si lo único que necesitas saber es “¿está esta persona en nuestra comunidad?”, pides membership.query e indicas tú mismo el server_guid: obtienes sí/no más sus roles allí, y no averiguas nada sobre el resto de sus comunidades. La lista completa está detrás de un scope aparte y superior, que el jugador tiene que aprobar por separado.
Encontrar el cliente
El puente solo escucha en 127.0.0.1, en el primer puerto libre de un pequeño rango. Pruébalos en orden hasta que uno responda: 7440, 7441, 7442, 7443. Las compilaciones de desarrollo de mssgs escuchan en cambio en 7540–7543, para que una versión de prueba nunca responda a las llamadas de un juego real.
http://127.0.0.1:7440/mssgs/v1/hello
No hace falta token, y la respuesta no dice nada del jugador, solo que mssgs está ahí y si alguien ha iniciado sesión.
{
"product": "mssgs",
"api": 1,
"client": "desktop",
"version": "14.2.20015",
"platform": "darwin",
"signed_in": true,
"scopes": ["identity", "staff", "membership.query", "servers.list", "presence.write"]
}
Comprueba product === "mssgs" y api antes de seguir. Si ninguno de los cuatro puertos responde, mssgs no se está ejecutando. En ese caso ofrece tu experiencia normal en lugar de hacer esperar al jugador.
Scopes y privacidad
Los cinco scopes revelan cantidades de datos muy distintas. No es casualidad; es todo el diseño. Pide lo menos posible, empezando por arriba de esta tabla.
| Scope | Qué permite | Qué cede el jugador |
|---|---|---|
presence.write |
Mostrar a qué está jugando | Nada. Este scope solo escribe; no lee ningún dato de la cuenta. |
identity |
Quién es el jugador | user_guid, username, nombre visible, URL del avatar. |
staff |
Indicadores de staff / moderador | Dos booleanos, además de identity. Va aparte porque un juego que muestra un nombre no tiene por qué saber que el jugador modera comunidades. |
membership.query |
Comprobar una comunidad que ya conoces | Para un server_guid que indicas tú: sí/no, su nombre y los roles que tiene allí ese jugador. Nada sobre ninguna otra comunidad. |
servers.list |
Todas las comunidades en las que está | La lista completa: guids, nombres, iconos y roles. Este es el caro: pídelo solo si de verdad lo necesitas. |
A la mayoría de los juegos les bastan dos
identity y presence.write cubren “quién eres” y “muestra a qué juegas”, que es casi cualquier integración. Añade membership.query si quieres vincular una recompensa a la pertenencia a tu comunidad. Casi nunca necesitas servers.list, y el jugador lo ve resaltado en rojo.
Comprobar la pertenencia
Esta es la alternativa a “dame la lista entera”. Indicas el server_guid de tu propia comunidad (que ya conoces) y obtienes una respuesta solo sobre ella.
curl -H "Authorization: Bearer $TOKEN" \
"http://127.0.0.1:7440/mssgs/v1/membership?server_guid=ca94ecc…f4g02"
# es miembro:
# {"server_guid":"ca94ecc…","member":true,"name":"Acme Fans","is_owner":false,
# "roles":[{"guid":"0aa32…","name":"Pro"}]}
# no es miembro, y nada más:
# {"server_guid":"…","member":false}
Un “no” es exactamente eso y nada más. Puedes pasar hasta 10 guids por llamada (repite server_guid o sepáralos con comas), lo que devuelve un array results. El grupo @everyone nunca aparece en roles: se aplica a todos los miembros, así que no te dice nada.
Publicar un estado de juego
Un solo PUT pone la línea “Playing …” bajo el nombre del jugador, en todos los sitios donde lo ven sus comunidades.
{
"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" }
}
Solo name es obligatorio. La respuesta te dice cuánto dura el estado y cada cuánto enviar un heartbeat:
{ "ok": true, "expires_in_ms": 90000, "heartbeat_every_ms": 30000 }
Heartbeat, o el estado desaparece
Un estado que no da señales de vida durante 90 segundos se borra automáticamente. Es a propósito: si tu juego se cuelga, el jugador no se queda “jugando” durante horas. Envía un POST /mssgs/v1/activity/heartbeat cada 30 segundos y un DELETE /mssgs/v1/activity al cerrar el juego limpiamente.
Número de jugadores y rol
party.kind decide qué frase se muestra, porque los mismos dos números no significan lo mismo. Una escuadra de cuatro no es un servidor con cuatro jugadores.
kind |
Se muestra como | Para |
|---|---|---|
party (por defecto) | 3 of 4 in the party | una escuadra, un equipo o un grupo |
server | 4/100 players | un servidor de juego (FiveM, un servidor de la comunidad) |
lobby | 4/100 players | un lobby antes de que empiece la partida |
match | 4/100 players | una partida o una ronda en curso |
role (hasta 48 caracteres) es como qué juega el jugador: un oficio, una clase o un personaje. Tiene su propio campo en lugar de otra frase en state, porque se muestra como etiqueta junto al número de jugadores.
details y state tienen un límite de 128 caracteres cada uno, y name de 64. Los saltos de línea y los caracteres de control se eliminan. Una URL de icono no se admite a propósito: la descargaría cada cliente que muestre la línea, lo que convertiría un estado en una baliza que informa a tu servidor de cada miembro de cada comunidad en la que está el jugador.
El botón Join now
Pon un bloque join en tu actividad y los demás miembros verán un botón Join now junto al estado. Hay dos formas, y puedes combinarlas.
1. Un secreto (para juegos nativos)
Define {"join":{"secret":"raid-42"}}. Cuando alguien pulsa Join now, ese secreto se entrega a su propia copia de tu juego, en su propio equipo, emparejada por el mismo game_id. No se abre ninguna URL ni se invoca ningún manejador de esquema. Tu juego lo recoge con:
{
"events": [
{ "seq": 1, "type": "join", "secret": "raid-42",
"from": { "user_guid": "62e377…", "username": "mssgs-test-1" } }
],
"cursor": 1
}
Consulta con ?since=<cursor> para ver cada evento una sola vez. Si el juego de quien pulsó el botón no se está ejecutando, no se entrega nada, lo que es un buen motivo para ofrecer también una URL.
2. Una URL https (para juegos web y enlaces a lobbies)
Define {"join":{"url":"https://play.example.com/s/abc"}} y el botón abre ese enlace. Solo se acepta https. Un esquema personalizado (steam://, mygame://, file://) se rechaza: ese bloque aparece en la pantalla de todos los miembros, y una URL así es una forma de hacer que el equipo de otra persona invoque un manejador local con argumentos elegidos por ti.
Todo lo que va en join es público
El bloque join se difunde a todos los que pueden ver el estado del jugador; ese es precisamente el sentido de un botón Join now. Así que trátalo como un código de lobby, no como una credencial. No pongas nunca en él nada que deba seguir siendo secreto, y haz que tus códigos caduquen.
FiveM
FiveM no tiene HTTP en su runtime Lua del lado del cliente, así que un recurso habla con el puente a través de NUI, una vista CEF, que envía un Origin. El puente acepta esos orígenes de forma explícita: https://cfx-nui-<resource> y el antiguo nui://<resource>. Las páginas web normales siguen rechazadas, y una página de la web abierta no puede reclamar ese origen; lo fija el propio navegador.
-- La página NUI hace el HTTP; Lua solo le envía los datos.
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: el estado caduca a los 90 s
end
end)
const BASE = 'http://127.0.0.1:7440/mssgs/v1'; // prueba 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'] // aquí no hace falta nada más
})
});
const started = await res.json();
if (started.status === 'approved') { return started.token; }
// Ahora el jugador ve el diálogo de permiso en 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' } // tu enlace de cfx.re
})
});
});
El resultado: Playing FiveM · Los Santos Roleplay · 4/100 players · Police, con un botón Join now que abre tu enlace de cfx.re.
Pide solo presence.write
Un estado de juego no necesita nada más: ese scope no lee absolutamente nada. Si quieres vincular una recompensa del juego a la pertenencia a tu comunidad de mssgs, añade membership.query e indica tu propio server_guid; sigues sin saber nada de las demás comunidades del jugador.
Un servidor en el que juegas no es de confianza automáticamente
Cualquier servidor de FiveM puede ejecutar recursos de cliente, así que cualquier servidor al que alguien se une puede pedir permiso. Precisamente por eso hay un diálogo de por medio que nombra el recurso: decide el jugador, no el servidor.
Juegos de navegador y móvil: vinculación a través de tu backend
Un juego en una pestaña del navegador o en un móvil no puede llegar al puente de arriba. El puente se ejecuta en el ordenador del jugador, y hay tres muros de por medio: el puente rechaza cualquier petición que lleve un Origin de navegador, Chrome muestra una solicitud de permiso antes de que una página pública acceda a 127.0.0.1 y Safari lo rechaza directamente, y un móvil no tiene ningún camino hasta el loopback de un ordenador.
Así que la dirección se invierte. Tu propio backend ya sabe quién está jugando, y es él quien se lo dice a mssgs, para los jugadores que han vinculado su cuenta de mssgs a tu juego. La vinculación se aprueba en la app de mssgs, nunca en tu juego, y crea un vínculo, nunca una sesión: nada de lo que sigue puede iniciar la sesión de nadie ni actuar como el jugador. El cliente de tu juego nunca ve una clave y nunca habla con mss.gs. El primer juego que usa esta vía es CozyCity, un juego de construir ciudades que se publica como página WebGL y como app para iPhone, sin versión de escritorio; los ejemplos de abajo son los suyos.
1. Registra tu juego
Registra el juego en la página de registro del Game SDK: tu game_id (por ejemplo com.deverence.cozycity), el nombre y el icono que ve el jugador en la hoja de aprobación y los nombres de host de tu backend. Lo revisamos allí, y una vez aprobado, tu clave de backend te espera en esa misma página, mostrada una sola vez; nosotros solo guardamos un hash. La clave va en tu servidor y en ningún otro sitio. Puedes rotarla allí en cualquier momento, y la antigua sigue siendo válida durante 24 horas para que un despliegue pueda completarse.
El nombre y el icono de la hoja siempre salen del registro, nunca de la petición. Si no, un enlace de phishing podría disfrazar una solicitud de vinculación del juego que quisiera. Los nombres de host delimitan adónde puede apuntar un join.url, ver más abajo.
2. Vincula a un jugador
El jugador elige Connect mssgs en tu juego. Tu juego se lo pide a tu backend, y tu backend nos lo pide a nosotros:
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 es tu propio id estable para ese jugador (hasta 128 caracteres), no para una sesión ni una partida; player_name (hasta 64) es lo que la hoja muestra como “Player: …”. Pasa al cliente de tu juego solo link_code, qr_url y deep_link. device_code es tu identificador para consultar y se queda en el servidor.
Después tu juego muestra tres cosas a la vez, porque el jugador podría estar en cualquier sitio:
- El QR de
qr_url. Un móvil con mssgs lo abre directamente en la hoja de aprobación de la app. Sin la app, lleva a una página de mss.gs que muestra el código y ofrece la descarga. - Un botón “Open in mssgs” con
deep_link, para un navegador de escritorio que está junto a la app de escritorio. Es la única URL externa que tu juego necesita abrir. - El propio código, en dos grupos de cuatro, para escribirlo en Settings → Game Activity → Link a game. El alfabeto no tiene 0/O ni 1/I, así que rara vez hay errores al escribirlo.
Lo que ve el jugador en mssgs, dibujado por la app a partir del registro:
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
Mientras tanto, tu backend consulta cada interval segundos (si vas más rápido, la respuesta es 429 SLOW_DOWN) hasta que cambie el estado. Un código funciona una vez y caduca a los diez minutos:
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"} # el jugador eligió Not now
# {"status":"expired"}
# {"status":"linked","link_guid":"…","user":{"user_guid":"62e377…"}}
Guarda link_guid junto a tu jugador; a partir de ahora es la dirección para la que publicas. La respuesta solo lleva el user_guid; solo se añade un username cuando tu registro tiene el scope identity.link, y no hay nada más allá de eso. Una segunda aprobación para el mismo player_ref sustituye al vínculo anterior, así que un jugador de tu juego es una cuenta de mssgs. La misma cuenta de mssgs puede vincularse a varios juegos y a varios player_ref de un mismo juego (el iPad de la familia).
3. Publica el estado de juego
El mismo bloque que en el puente, con las mismas reglas y los mismos límites, solo que ahora por vínculo y con tu clave 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} # el bloque ha cambiado y se ha difundido
200 {"published":true,"changed":false} # idéntico a lo guardado; solo se ha renovado el TTL
204 # guardado, pero el jugador no está conectado en mssgs ahora mismo
410 {"error":"LINK_REVOKED"} # el jugador se ha desconectado: descarta el vínculo
Trata 200 y 204 igual: guardado. { "activity": null } borra el bloque; envíalo cuando el jugador se vaya. Una diferencia con el puente: el host de join.url debe ser uno de tus backends registrados (o un subdominio de uno), o recibirás 400 INVALID_PAYLOAD. Así un backend no puede poner en el estado de un jugador un botón Join now que lleve a un sitio en el que ese jugador nunca ha jugado.
Heartbeat cada 60 segundos, TTL 120
Un estado publicado vive 120 segundos sin un mensaje nuevo y luego desaparece solo. Así que reenvía el mismo bloque cada 60 segundos; un bloque sin cambios no cuesta nada y solo renueva el TTL. Si tu heartbeat se detiene, la línea “Playing …” desaparece, que es justo lo que se busca.
Con cientos de jugadores conectados, envía el heartbeat en una sola llamada, hasta 100 elementos a la vez. Cada elemento recibe su propio estado, así que un jugador que se ha desconectado en mssgs nunca frena a los otros noventa y nueve:
{ "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 } ] }
Cómo se muestra el estado
- Idéntico a un estado del puente: Playing CozyCity · Lantern Hollow · 6/40 players, con Join now cuando hay un
join.url. El servidor marca el bloque convia: "backend", así que un cliente puede añadir “Shared by the game's server”. - Solo mientras el jugador está conectado en mssgs. Sin ningún cliente de mssgs abierto, la cuenta está desconectada y sigue desconectada; tu backend no puede hacer que alguien parezca presente. Eso también impide que esta vía se convierta en una baliza de “¿está René delante del ordenador?”.
- Prioridad: un juego dentro de la app > un juego en el puente > tu backend. Si el jugador se pone a jugar al ajedrez dentro de mssgs mientras tu backend sigue enviando heartbeats, gana el ajedrez, no quien haya escrito el último.
Desconexión
El jugador ve cada vínculo en Settings → Game Activity → Linked games, con tu icono y tu nombre, el nombre de jugador de tu juego, cuándo se vinculó y cuándo publicó por última vez, y un botón Disconnect. A partir de ahí, tu siguiente publicación responde 410 LINK_REVOKED; así se entera tu juego. Descarta el link_guid y vuelve a ofrecer “Connect mssgs”. Desde tu lado, termina un vínculo con DELETE /game-sdk/v1/links/{link_guid}.
Límites
Por vínculo, un cambio cuenta como máximo cada 2 segundos; un heartbeat sin cambios es gratis. Por clave hay 600 peticiones por minuto, y los elementos de un lote cuentan por separado: enviar el heartbeat de 300 jugadores cada 60 segundos gasta 5 de las 600.
Referencia de endpoints: el puente
URL base http://127.0.0.1:<port>. Todo menos los tres primeros requiere Authorization: Bearer <token>.
| Método | Ruta | Scope | Qué hace |
|---|---|---|---|
| GET | /mssgs/v1/hello |
ninguno | ¿Está mssgs aquí, qué versión de la API habla y hay alguien con la sesión iniciada? La única ruta que no necesita token, y no dice nada del jugador. |
| POST | /mssgs/v1/authorize |
ninguno | Pide permiso al jugador. Abre un diálogo en la app y devuelve un request_id para consultar. |
| GET | /mssgs/v1/authorize/:request_id |
ninguno | pending, approved (con el token), denied o expired. |
| GET | /mssgs/v1/me |
identity |
El jugador con la sesión iniciada. Añade is_staff / is_moderator solo con el scope staff. |
| GET | /mssgs/v1/membership |
membership.query |
La pertenencia a los valores de server_guid que pases (hasta 10, repetidos o separados por comas). |
| GET | /mssgs/v1/servers |
servers.list |
Todas las comunidades en las que está el jugador, con sus roles. Los mensajes directos nunca se incluyen. |
| PUT | /mssgs/v1/activity |
presence.write |
Publica el bloque “Playing …”. Devuelve el TTL y cada cuánto enviar el heartbeat. |
| POST | /mssgs/v1/activity/heartbeat |
presence.write |
Mantiene viva la actividad publicada sin reenviarla. |
| DELETE | /mssgs/v1/activity |
presence.write |
La borra de inmediato, para un cierre limpio. |
| GET | /mssgs/v1/events |
presence.write |
Los datos para unirse dirigidos a tu juego. Consulta con ?since=<cursor>. |
| GET | /mssgs/v1/session |
ninguno | Lo que tiene este token: game_id, scopes concedidos y si hay alguien con la sesión iniciada. |
| DELETE | /mssgs/v1/session |
ninguno | Devuelve el permiso. El mismo efecto que si el jugador lo revoca en Settings. |
Referencia de endpoints: backends vinculados
URL base https://ams1-gateway.mss.gs. Todas las rutas requieren Authorization: Bearer <backend key> y el scope activity.write en tu registro; las respuestas salen con Cache-Control: no-store. Llámalas desde tu servidor, nunca desde el cliente del juego.
| Método | Ruta | Qué hace |
|---|---|---|
| POST | /game-sdk/v1/link/start |
Inicia un vínculo para uno de tus jugadores ({ player_ref, player_name? }). Devuelve link_code, device_code, qr_url, deep_link, expires_in e interval. |
| POST | /game-sdk/v1/link/poll |
{ device_code } → pending, denied, expired, o linked con link_guid y user. |
| DELETE | /game-sdk/v1/links/{link_guid} |
Termina un vínculo desde tu lado. El jugador puede hacer lo mismo desde Settings. |
| PUT | /game-sdk/v1/links/{link_guid}/activity |
Publica el bloque “Playing …” de un jugador; { "activity": null } lo borra. |
| POST | /game-sdk/v1/activity/batch |
Lo mismo, para hasta 100 jugadores en una llamada. Cada elemento responde por su cuenta. |
Códigos de error
Los errores se devuelven como {"error":"CODE","message":"…"} con el estado HTTP correspondiente.
| Código | Significado |
|---|---|
401 UNAUTHORIZED | Token ausente o desconocido; autoriza primero. |
403 MISSING_SCOPE | El jugador no concedió ese permiso. Puede que lo desmarcara. |
403 ORIGIN_NOT_ALLOWED | La petición llevaba un Origin de navegador. Consulta “Solo juegos nativos” más abajo. |
409 NOT_SIGNED_IN | mssgs se está ejecutando, pero nadie ha iniciado sesión. |
429 RATE_LIMITED | Más de 120 peticiones en un minuto desde un mismo juego. |
400 INVALID_GAME_ID | game_id solo puede contener letras, dígitos, punto, guion o guion bajo. |
400 TOO_MANY_GUIDS | Como máximo 10 valores de server_guid por llamada a membership. |
Backends vinculados
Las rutas del backend usan el mismo formato. Dentro de un lote, el estado se devuelve por elemento en results, así que un vínculo terminado nunca hace fallar toda la llamada.
| Código | Significado |
|---|---|
401 INVALID_BACKEND_KEY | Clave desconocida, o una que se rotó hace más de 24 horas. |
403 SCOPE_NOT_GRANTED | Tu registro no tiene el scope que necesita esa ruta. |
400 INVALID_PAYLOAD | Cuerpo mal formado, más de 100 elementos en el lote o un join.url cuyo host no es uno de tus backends registrados. |
400 INVALID_ACTIVITY | No queda ningún nombre utilizable tras la normalización. |
410 LINK_REVOKED | El vínculo terminó, por cualquiera de los dos lados. Descártalo y vuelve a ofrecer “Connect mssgs”. |
429 SLOW_DOWN | Has consultado link/poll más rápido que interval. |
429 RATE_LIMITED | Un cambio en un vínculo a menos de 2 segundos del anterior, o más de 600 peticiones en un minuto con tu clave. |
503 LINK_STORE_UNAVAILABLE | Problema temporal por nuestra parte. Vuelve a intentarlo en tu próximo heartbeat. |
Seguridad
Solo juegos nativos
Las peticiones que llevan el Origin de una página web se rechazan con 403 ORIGIN_NOT_ALLOWED. Que cualquier página web pudiera detectar que usas mssgs y abrir un diálogo de permiso sería una vía para el fingerprinting y el phishing, no una función. Un juego nativo no envía ningún Origin, así que no le afecta, y el navegador integrado de un juego se permite por nombre, ver FiveM. Si estás creando un juego de navegador o de móvil, no hablas con el puente: tu propio backend publica para los jugadores vinculados, ver Juegos de navegador y móvil.
Lo que el jugador sigue controlando
- El jugador puede desactivar el puente en Settings → Game Activity, y a partir de ahí ningún juego puede ver mssgs en absoluto.
- Cada juego aprobado aparece allí con exactamente los permisos que tiene, cuándo estuvo activo por última vez y un botón Remove. Quitarlo es inmediato: el token deja de valer al instante.
- Un juego de navegador o de móvil vinculado aparece en Linked games con un botón Disconnect. Desconectarlo también es inmediato: la siguiente publicación de ese backend recibe un
410. - El puente solo escucha en 127.0.0.1, nunca en la red.
- Los mensajes directos nunca se revelan, ni siquiera con servers.list.
- Cada juego tiene un límite de 120 peticiones por minuto.
Buenas prácticas
- Pide los scopes cuando los necesites, no todos a la vez en el primer arranque.
- Funciona sin mssgs: el jugador no tiene por qué tenerlo.
- Borra tu estado cuando termine la partida en lugar de esperar al TTL.
- Trata un scope rechazado como un resultado normal, no como un error.