SDK
@uloggd/arcade-sdk: el cliente, cada comando, evento, error y código de cierre, y el simulador.
npm install @uloggd/arcade-sdk- Licencia MIT, sin dependencias en tiempo de ejecución. Apunta a ES2020 y se puede importar durante el renderizado en el servidor.
- Viene como módulos ES y como CommonJS, ambos con declaraciones de tipos, y como un build para una etiqueta script común (abajo).
@uloggd/arcade-sdkes el cliente,@uloggd/arcade-sdk/mockel simulador, y@uloggd/arcade-sdk/serverla verificación de tokens para Node 20.19 o posterior, Deno, Bun y Workers (consulta Identidad; para servidores en otros lenguajes, consulta Verificación en otros lenguajes).- uloggd nunca sirve el SDK a los juegos: lo empaquetas tú, o lo alojas junto al juego.
| Versión | Qué trae |
|---|---|
0.1.0 | Solo módulos ES. |
0.2.0 | Añade CommonJS, cargado con require, y el build para etiqueta script, dist/uloggd-arcade.min.js. La API y el protocolo no cambian. |
Sin un bundler
El paquete incluye dist/uloggd-arcade.min.js, un build para una etiqueta
<script> común que define una sola variable global, UloggdArcade.
Cópialo de node_modules/@uloggd/arcade-sdk/dist/ a tu juego, junto con
uloggd-arcade.min.js.map si quieres trazas de error legibles, y sírvelo desde
el propio origen del juego.
<script src="./uloggd-arcade.min.js"></script>
<script>
const arcade = UloggdArcade.createArcadeSDK({
appId: "ID_DE_TU_APLICACION",
mockWhenStandalone: true,
});
arcade.ready().then(({ context }) => startGame(context));
</script>UloggdArcadecontienecreateArcadeSDK,ArcadeSDKError,ArcadeSDKMocky las constantes (SDK_VERSION,ERROR_CODES,HostCloseCode,GuestCloseCode,PROTOCOL_VERSION,SUBSCRIBABLE_EVENTSyDEFAULT_HOST_ORIGIN). La verificación de tokens no está en él: le corresponde a tu servidor.- Sírvelo tú mismo. uloggd no lo sirve, y una copia en una CDN pública es un tercero que tu lanzamiento debe declarar, y que puede cambiar lo que ejecuta el juego sin una nueva revisión.
- Sin npm, el mismo archivo está en
https://registry.npmjs.org/@uloggd/arcade-sdk/-/arcade-sdk-0.2.0.tgz, dentro depackage/dist/. - Cárgalo como script clásico, antes de los scripts que lo usan. Como script
de módulo (
type="module"), no define ninguna variable global. - Los módulos ES de
disttambién funcionan desde un script de módulo, que importadist/client.jscon la carpetachunksal lado.
Crear el cliente
import { createArcadeSDK, ArcadeSDKError } from "@uloggd/arcade-sdk";
const arcade = createArcadeSDK({
appId: "ID_DE_TU_APLICACION",
mockWhenStandalone: true,
});
try {
const { context } = await arcade.ready();
// context: { appId, instanceId, mode, locale, platform, layout, game }
startGame(context);
} catch (error) {
if (error instanceof ArcadeSDKError && error.code === "not_embedded") {
showMessage("Juega a este juego en uloggd.com");
}
}| Opción | |
|---|---|
appId | El id de tu aplicación, tal como lo muestra el portal: un uuid en minúsculas. |
hostOrigin | Dónde se ejecuta uloggd. https://uloggd.com por defecto; un origen http:// solo se acepta cuando el propio juego se ejecuta en un origen de loopback. |
mockWhenStandalone | Abierto directamente en un origen de loopback (localhost), fuera de cualquier frame, el cliente es el simulador. En cualquier otro sitio fuera de uloggd, ready() se rechaza con not_embedded, así que un juego desplegado donde no toca falla a la vista. |
createArcadeSDK lanza invalid_args al instante para un appId o un
hostOrigin mal formado.
| Miembro | |
|---|---|
ready() | Se resuelve una vez, tras el handshake, con { protocol, commands, context }. Se rechaza con not_embedded, con timeout cuando uloggd no responde al handshake en 10 segundos, o con closed. |
context | El mismo contexto, o null antes de que ready() se resuelva. |
commands | Los comandos de abajo. |
subscribe(event, listener, onError?) | Escucha un evento y devuelve la función que deja de escucharlo. |
supports(command) | Si uloggd anunció el comando en READY. Compruébalo antes de usar un comando más nuevo que el SDK que publicaste. |
close({ code, message? }) | Termina la sesión, con uno de los códigos de cierre de abajo. |
destroy() | Termina la sesión normalmente, con el código 1000, como haría close, y rechaza todo lo pendiente. Para la limpieza de un efecto o un hot reload. |
Comandos
Todo comando devuelve una promesa y tiene un plazo: 10 segundos, o 5 minutos
para authorize, que espera al jugador.
| Comando | Argumentos | Resultado | Exige |
|---|---|---|---|
authorize | { scopes: ["identify"] } | { scopes } | |
getPlayer | ninguno | { id, nickname } | identify |
getIdentityToken | { nonce } | { token, expiresAt } | identify |
getGame | ninguno | { appId, slug, name } | |
requestFullscreen | ninguno | { fullscreen } | un gesto |
exitFullscreen | ninguno | { fullscreen } |
authorizeabre el diálogo de consentimiento del propio uloggd, fuera del frame, donde el jugador elige el apodo que verá tu juego (uno generado, por defecto). Sin la capacidadpointer-lock, el diálogo se abre directamente, o en cuanto la pestaña vuelva a mostrarse, si está oculta. Con ella, el diálogo no se abre solo, porque uloggd no ve un bloqueo que tu juego mantiene: la barra del jugador muestra la petición con un botón Responder, y el diálogo se abre desde ese clic. Pide solo permisos que el lanzamiento declaró, o falla coninvalid_args. Si el jugador cancela, falla conuser_cancelled, y pedirlo de nuevo sigue fallando hasta que el jugador vuelva a abrir el juego. Pídelo tras un clic tuyo, como "Guardar mi progreso", y no al cargar. A un jugador que ya aceptó tu juego no se le vuelve a preguntar.getPlayerdevuelve el id por juego y el apodo del jugador, sin verificar. Muéstralos; nunca confíes en ellos en tu servidor.getIdentityTokendevuelve un token firmado por uloggd, para que lo verifique tu servidor. El nonce viene de tu servidor: de 16 a 128 caracteres entre letras, dígitos,_y-. Un token cada 3 segundos, y 30 por jugador por hora.getGamedevuelve el nombre aprobado y el slug, la dirección reservada al activar Arcade. Hoy el slug nunca esnull; el tipo admitenullpara versiones futuras.requestFullscreenyexitFullscreenactúan sobre el contenedor de uloggd, con su barra visible. Llama arequestFullscreendesde un clic o una tecla, o falla conrequires_gesture.
document.querySelector("#guardar")!.addEventListener("click", async () => {
await arcade.commands.authorize({ scopes: ["identify"] });
const { nonce } = await fetch("/session/nonce", { method: "POST" }).then(
(r) => r.json(),
);
const { token } = await arcade.commands.getIdentityToken({ nonce });
await fetch("/session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});
});Eventos
const off = arcade.subscribe("LAYOUT_UPDATE", (layout) => {
resize(layout.width, layout.height);
});
// más tarde
off();| Evento | Datos |
|---|---|
READY | { protocol: 1, commands, context }. Se envía una vez, sin suscribirse; ready() se resuelve con él. |
VISIBILITY_UPDATE | { visible } |
LAYOUT_UPDATE | { width, height, fullscreen, safeArea: { top, right, bottom, left } } |
LOCALE_UPDATE | { locale } |
CLOSING | { reason, deadlineMs }, donde reason es user, hidden o suspended. |
GRANT_UPDATE | { scopes } |
- El primer oyente de un evento hace la suscripción, y el último
off()la deshace. Una suscripción que uloggd rechaza quita sus oyentes y llama aonError. - El SDK también escribe el área segura en
:root, como--uloggd-safe-area-inset-top,-right,-bottomy-left. CLOSINGte deja hastadeadlineMspara guardar.GRANT_UPDATEllega cuando el jugador revoca el acceso de tu juego en Ajustes. Deja de llamar agetPlayerygetIdentityToken, y trata al jugador como desconectado de tu servidor.
Errores
Todo rechazo es un ArcadeSDKError, con un code:
| Código | Significa |
|---|---|
not_embedded | Fuera de uloggd, y fuera del simulador. |
unknown_command | uloggd no conoce el comando; comprueba supports() antes. |
invalid_args | Se rechazaron los argumentos: un permiso que el lanzamiento no declaró, un nonce mal formado, una clave de más. |
missing_scope | El jugador no concedió el permiso que necesita el comando. |
user_cancelled | El jugador cerró el diálogo de uloggd. |
requires_gesture | El comando necesita antes un clic o una tecla en el juego. |
rate_limited | Demasiados comandos, o demasiados tokens. |
timeout | Sin respuesta a tiempo. |
closed | La sesión se cerró antes de la respuesta. Cuando la terminó un código de cierre, el error lo lleva en closeCode. |
unavailable | uloggd no puede hacerlo ahora, como emitir tokens de identidad sin una llave de firma disponible. |
internal | Algo falló del lado de uloggd. |
Códigos de cierre
uloggd termina la sesión con uno de estos, y quita el frame:
| Código | Significa |
|---|---|
4000 | Frame o aplicación no válidos. |
4001 | Origen no válido. |
4002 | Demasiados mensajes. |
4003 | El jugador revocó el acceso del juego. |
4004 | Versión de protocolo no admitida. |
4010 | El juego fue suspendido o desactivado. |
El juego puede cerrar con 1000 (normal) o con un código propio de 4500 a
4599. El message, de hasta 200 caracteres, llega a la consola del
navegador solo en modo de prueba: los jugadores solo ven las palabras del
propio uloggd.
Límites
El SDK te mantiene dentro de ellos, pero importan si envías muchos comandos a
la vez: cada mensaje tiene hasta 64 KiB, como mucho 32 comandos esperan
respuesta al mismo tiempo, y los comandos se limitan a 20 por segundo, con
picos de 50. Pasado eso, uloggd termina la sesión con 4002.
El simulador
En localhost, con mockWhenStandalone: true, el cliente es un
ArcadeSDKMock, que tiene la misma interfaz:
- todos los comandos responden con datos fijos.
authorizeconcede al instante, como un jugador que acepta, y hasta entoncesgetPlayerygetIdentityTokenfallan conmissing_scope, como dentro de uloggd; ?mock_player=2en la dirección de la página da un segundo jugador, así que dos pestañas pueden ser dos jugadores;- los tokens de identidad se firman con una llave desechable e
iss: "mock", que los verificadores del servidor rechazan salvo que pasesallowMock: true.
En el build para etiqueta script, el simulador es UloggdArcade.ArcadeSDKMock.
En las pruebas, crea tú el simulador y dirígelo. Además de appId, acepta
mode, locale, platform, player y scopes, para un jugador que ya
aceptó:
import { ArcadeSDKError } from "@uloggd/arcade-sdk";
import { ArcadeSDKMock } from "@uloggd/arcade-sdk/mock";
const arcade = new ArcadeSDKMock({
appId: "ID_DE_TU_APLICACION",
scopes: ["identify"],
});
arcade.setPlayer({
id: "6d0c0d6e-3f7a-4d4b-9d61-2f1f5b0a8c11",
nickname: "Ada",
});
arcade.overrideCommand("authorize", async () => {
throw new ArcadeSDKError("user_cancelled");
});
arcade.emit("VISIBILITY_UPDATE", { visible: false });