Saltar al contenido
Nelkano Emulador multisistema

SDK de Nelkano

Permisos y datos

Declara en capabilities los permisos que necesita tu módulo. Por ejemplo, storage.private.cas para guardar un ajuste y library.read para consultar juegos. La app los muestra al instalar y rechaza las llamadas no autorizadas.

Permisos básicos

  • library.read: Consultar juegos.
  • library.delete: Pedir un borrado con confirmación del usuario.
  • library.launch: Abrir o continuar un juego de la biblioteca; puedes indicar un slot de guardado.
  • library.cover: Usar la captura de un guardado como carátula o restaurar la portada.
  • saves.read: Consultar los guardados.
  • saves.write: Guardar la partida actual en un slot manual.
  • saves.load: Cargar un guardado en la partida actual.
  • saves.autosave: Solicitar autoguardados.
  • emulation.read: Consultar la partida y recibir sus eventos.
  • emulation.control: Pausar, reanudar y reiniciar.
  • presentation.player: Cambiar pantalla completa, estiramiento, pantalla NDS/3DS y slot elegido.
  • storage.private.cas: Guardar preferencias propias.
  • modules.hooks: Usar y ofrecer ganchos entre módulos.
  • environment.display: Leer tamaño y densidad de pantalla.
  • presentation.control-layouts: Publicar posiciones, tamaños e iconos de controles.
  • presentation.appearance: Ofrecer estilos de apariencia.
Autor, compatibilidad y permisos antes de instalar (ejemplo).
Autor, compatibilidad y permisos antes de instalar (ejemplo).

Guardar una preferencia

const doc = await host.storage.read('preferences'); const saved = await host.storage.compareAndSwap( 'preferences', doc.revision, { enabled: true } ); if (!saved) throw new Error('El ajuste cambió. Vuelve a intentarlo.');

La revisión evita sobrescribir un cambio más reciente. Los datos pertenecen a tu módulo y autor: desactivar los conserva; al desinstalar, el usuario puede borrarlos.

Consultar o borrar juegos

const games = await host.library.list(); if (games.length === 0) throw new Error('La biblioteca está vacía.'); await host.library.requestDelete([games[0].id]);

requestDelete pide confirmación y permite cancelar. En Android elimina la copia privada del juego y sus guardados, conservando el archivo original importado. Studio sólo elimina datos ficticios.

Permisos de Colecciones en la consola de Studio.
Permisos de Colecciones en la consola de Studio.

Acceder a la cuenta

  • account.session: identidad, inicio de sesión, registro y cierre de sesión.
  • network.account: peticiones a los endpoints de cuenta aprobados de nelkano.com.
  • resources.image: referencia de una foto elegida por el usuario, sin revelar su ruta privada.

Android utiliza una sesión cifrada y permisos firmados. Studio utiliza cuentas ficticias. Para conectar con otro servidor necesitas network.external.

Usar funciones del dispositivo

  • input.gamepad: detectar mandos y aplicar o capturar asignaciones.
  • media.second-screen: buscar y conectar un receptor local.
  • media.streaming: iniciar y detener WebRTC con confirmación del usuario.
  • appearance.locale: aplicar un diccionario. Elegir un tema requiere también presentation.appearance.
  • resources.patch: elegir un parche y crear una copia del juego.

Qué ocurre si falla un módulo

Actualizar la app o perder el proceso no cambia la elección de activar o desactivar un módulo opcional. En Android y escritorio, la recuperación en primer plano funciona así:

  • Esenciales: se vuelven a iniciar ante un fallo que detenga su ejecución, sin límite de intentos y con una espera creciente de hasta 30 segundos.
  • Opcionales: hasta tres intentos por ronda ante cierres recuperables. Los errores de código se muestran sin repetirlos indefinidamente.
  • Una acción que falla sin dañar el proceso muestra el error y mantiene el módulo.

Los cinco esenciales permanecen habilitados: Apariencia, Nelkano básicos, Editor de controles, Amigos y Multijugador. Si una versión anterior los dejó apagados o desinstalados, la app los recupera con los datos que existan.

Trabajar con memoria, recursos y multijugador

  • emulation.memory.read: Leer rangos de memoria GBA permitidos.
  • emulation.memory.write: Cambiar RAM con comprobación de los bytes anteriores.
  • emulation.resources: Leer o buscar en la ROM y obtener una imagen reducida de la emulación.
  • emulation.handoff: Crear y restaurar checkpoints, preparar una copia privada de un proyecto y usar su puente RGSS.
  • presentation.overlay: Dibujar imágenes y etiquetas sobre la pantalla del juego.
  • network.multiplayer: Conectar con un amigo aceptado usando la cuenta de Nelkano y su relay.
  • runtime.events: Suscribirse al aviso runtime-tick de la sesión activa.

Consulta las capacidades antes de ofrecer una acción que dependa del motor. Usa la sesión actual: una referencia antigua se rechaza. El SDK entrega datos y referencias limitadas, sin revelar al JavaScript rutas privadas ni tokens de cuenta.

Modificar un juego durante la sesión

host.gameMod requiere emulation.game-mod y el permiso de cada operación:

  • emulation.read: consultar capacidades.
  • emulation.memory.read: leer y listar reglas.
  • emulation.memory.write: escribir o retirar una regla.
  • Lectura y escritura: instalar reglas.

Los hosts antiguos rechazan el permiso nuevo; no ejecutan el paquete parcialmente. Consulta los límites y la retirada de cambios en Modificar un juego. Estos permisos no dan acceso a archivos privados ni envían datos a servidores.

Guardar registros y consultarlos

Con storage.entities, define los tipos mediante entities.define({name,version,schema,indexes,relations}). Después utiliza get, query, transact y migrate.

Admite relaciones dentro del módulo, filtros de igualdad, índices, ordenación y páginas. Los lotes usan expectedRevision y se aplican completos; las migraciones son transaccionales.

  • 64 tipos y 10.000 registros por tipo.
  • 32 KiB por registro y 16 MiB por identidad.
  • 128 cambios por lote; respuestas de consultas y lotes de hasta 96 KiB.

Los datos pertenecen al módulo y autor. Se guardan localmente en SQLite en Android/escritorio y en JSON en Studio; no se suben a Nelkano. Una actualización fallida recupera las entidades anteriores. Las preferencias CAS mantienen su formato.

Conectar con otro servidor

  1. Declara network.external y los destinos en origins, con origen HTTP o HTTPS, servidor y puerto concretos.
  2. Usa connections.request/socket/read/send/close. Un origen autoriza también su WebSocket WS o WSS correspondiente.
  3. Para añadir un destino, solicita requestOrigin. La decisión se conserva para el módulo y autor.

Android admite HTTPS/WSS; escritorio y Studio también HTTP/WS. Las peticiones no heredan cookies ni claves de la cuenta Nelkano y no siguen redirecciones.

Hay hasta 12 trabajos simultáneos: 8 HTTP y 4 sockets. Los cuerpos y mensajes admiten 64 KiB. onEvent recibe connection-changed y connection-origin-reviewed; desactivar cancela las conexiones. Studio sí utiliza la red: usa datos de prueba.

Programar una tarea

Con runtime.tasks puedes consultar host.tasks.capabilities() y utilizar:

  • schedule({id,delayMs,intervalMs?,payload?,background?}): programar.
  • read(id): consultar el estado.
  • update(id,{state?,progress?,message?,result?}): publicar avances y resultado.
  • cancel(id): cancelar.

Las tareas normales entregan task-due a onEvent. Admiten 32 registros por módulo, intervalos de 100 ms a 24 h y datos de 32 KiB. Comprueba persistent y closedApp antes de ofrecer trabajo con la interfaz cerrada.

Continuar una tarea con la interfaz cerrada

Declara también runtime.tasks.background y programa con background:true. El planificador nativo conserva el registro y ejecuta onBackground(event). Puede reutilizar la instancia activa o crear otra sin llamar a activate().

  • Retraso mínimo: un minuto. Intervalo periódico mínimo: quince minutos.
  • El sistema decide cuándo ejecutar y puede repetir un intento interrumpido. Diseña la tarea para que repetirla no duplique sus efectos y publica el progreso con update.
  • Cerrar la interfaz conserva el registro; desactivar o desinstalar lo cancela.

Android utiliza JobScheduler. En escritorio se utiliza Task Scheduler del usuario o systemd --user, con la sesión iniciada y acceso exclusivo a los datos. Antes de ejecutar se comprueban paquete, firma, habilitación y permisos.

En esa ejecución puedes usar tareas, transferencias, secretos, CAS, listados de biblioteca y guardados, y HTTP a destinos autorizados. No abre juegos, ventanas ni sockets, ni solicita permisos. Las transferencias requieren la partida cerrada.

Guardar archivos privados

Declara storage.files. Puedes listar, leer y eliminar con files.list/read/remove; para escribir:

  1. begin recibe name, expectedRevision y, opcionalmente, mimeType.
  2. write añade tramos consecutivos de hasta 64 KiB.
  3. commit publica el archivo completo. Si otra revisión ha cambiado, devuelve un conflicto. abort descarta la escritura.

Límites: 16 archivos, 1 MiB por archivo, 4 MiB por módulo/autor y dos escritores abiertos por módulo. Desactivar descarta escrituras sin publicar y conserva los archivos terminados. Una actualización fallida recupera los anteriores.

Importar y exportar archivos del usuario

files.import/export requiere también resources.documents. Abre el selector de Android o escritorio para que el usuario elija origen o destino; el módulo no recibe rutas ni URI.

Studio guarda archivos locales, pero anuncia importExport:false para el selector nativo. Una página puede usar un PNG privado con resource: file:imagen.png, ligado al registro activo y de hasta 1024×1024.

Guardar claves de servicios propios

Con storage.secrets, usa secrets.get/put/remove/capabilities. Admite 64 claves, 4 KiB por valor y 64 KiB por módulo/autor.

  • Android: clave Keystore separada y almacenamiento excluido de las copias de seguridad.
  • Windows: cifrado DPAPI.
  • Linux: Secret Service si está disponible; si falla, los cambios permanecen sólo en memoria.
  • Studio: sólo memoria; utiliza claves de prueba.

Consulta persistent después de guardar. La recuperación de actualizaciones utiliza copias cifradas o referencias opacas, sin escribir secretos en texto plano en el registro de recuperación. Este servicio no comparte la sesión de Nelkano.

Transferir juegos y partidas

library.transfer y saves.transfer permiten importar y exportar mediante host.transfers, con referencias privadas del módulo y autor. La app verifica los archivos antes de incorporarlos.

Reemplazar guardados exige la revisión actual del destino: si cambió, se rechaza. La importación sustituye el conjunto completo de progreso y elimina los archivos de progreso que no estén en la copia recibida. La categoría de juegos no sustituye silenciosamente otro contenido con la misma identidad.

Para transferir por red necesitas sus permisos y orígenes. Consulta Emulación y transferencias para el contrato completo.

Traducir, capturar y preparar recursos

  • translation.local: analizar, editar y reconstruir traducciones con los proveedores anunciados por el host.
  • media.capture: capturar la imagen del juego.
  • media.record: grabar clips.
  • resources.packs: preparar capas de archivos propias del módulo.

Incluir preferencias en el perfil

  1. Declara profile.provide y registra los documentos CAS con host.profile.register({version:1,keys}).
  2. Implementa onProfile({operation:'validate',version,documents}). Devuelve {documents} normalizados con las mismas claves; el host valida un documento por llamada y no permite llamadas al SDK durante ella.
  3. Al recibir profile-restored, recarga el estado publicado.

Exportar y restaurar el perfil

Declara profile.backup. Consulta host.profile.capabilities() y list(); después:

  • Exportar: export({moduleIds?}) devuelve ID, tamaño y SHA-256. Lee con read({id,offset,length}).
  • Importar: beginImport({size,sha256}), write({id,offset,dataBase64}) y preview({id}). La vista previa devuelve la revisión agregada del destino.
  • Confirmar: commit({id,expectedRevision}).
  • Descartar o liberar una referencia: abort({id}) o release({id}).

La restauración requiere proveedores activos con la misma identidad y autor. Valida todos los documentos y publica el lote completo con revisiones locales nuevas. El módulo importador no puede restaurarse a sí mismo durante esa llamada.

Básicos, Editor y Apariencia registran preferencias, colecciones/favoritos y controles. Restaurar no reconecta el streaming. El perfil excluye secretos, archivos arbitrarios, entidades y partidas; para el progreso utiliza host.transfers. Tampoco incluye claves que no se hayan registrado.

Límites: 32 proveedores, 16 claves por proveedor, 96 KiB por documento y 64 MiB por contenedor. Tramos de 64 KiB, dos referencias abiertas por módulo y ocho globales; caducan tras diez minutos sin actividad.

Procesar imagen y audio

  • graphics.shaders: Postprocesar en GPU la imagen presentada del juego, separado de controles y texto superpuesto.
  • graphics.options: Cambiar opciones gráficas que anuncie el core, inicialmente raster GameCube 1×/2×/4×.
  • graphics.textures: Cargar PNG privados y sustituir texturas observadas por su hash dentro del core compatible.
  • media.microphone: Capturar PCM del micrófono después del permiso nativo; se detiene al pasar a segundo plano.
  • audio.dsp: Procesar el PCM del core antes de reproducirlo mediante ganancias, biquads y FIR.
  • runtime.tasks.background: Junto con runtime.tasks, conservar y ejecutar registros background:true con la interfaz cerrada.

Consulta capabilities antes de ofrecer la función. Los permisos gráficos no entregan contextos GPU, JNI ni rutas y rechazan sesiones antiguas. Grabar el juego no autoriza el micrófono.

Rebobinado, sonido y cámara

  • emulation.timeline: historial, rebobinado, runahead, avance y repeticiones editables cuando el core lo admite; inicialmente CHIP-8.
  • audio.mix: clips PCM16LE privados, sincronizados con la partida antes del DSP y la reproducción.
  • media.camera: captura Camera2 en Android con permiso nativo. Entregar la imagen a CAM 3DS necesita también emulation.input.

Cada servicio requiere su permiso. Las tareas con la interfaz cerrada mantienen sus restricciones: estos permisos no les permiten abrir juegos, mostrar interfaz, solicitar permisos ni usar cámara, reproducción o un core.

Instalar y ejecutar un motor externo

native.execute autoriza los artefactos nativos declarados y revisados de un paquete V2; native.gpu, su integración gráfica. El JavaScript no recibe punteros, JNI, contextos GL ni rutas privadas.

Las operaciones de host.engines requieren además emulation.read, emulation.control, emulation.input o emulation.resources, según la acción. engines.importGame necesita también library.transfer y resources.documents: abre el selector con motor y sistema explícitos.

El código nativo se ejecuta fuera de QuickJS y requiere confiar en su autor. Desactivar retira su proveedor y recursos, y cierra las sesiones que dependían de él. Declarar una capacidad no demuestra que el motor implemente memoria, cámara o timeline.

Comprobar si puedes publicarlo en el catálogo

El catálogo web acepta V1 de hasta 32 MiB con los siguientes permisos:

  • Juegos y guardados: library.read, library.delete, library.launch, library.cover, saves.read, saves.autosave, saves.write, saves.load.
  • Emulación: emulation.game-mod, emulation.memory.read, emulation.memory.write, emulation.resources, emulation.handoff, emulation.read, emulation.control.
  • Interfaz: presentation.overlay, presentation.player, presentation.appearance, presentation.control-layouts, environment.display, appearance.locale.
  • Datos e integración: runtime.events, storage.private.cas, modules.hooks.
  • Cuenta y conexiones: account.session, network.account, network.multiplayer.
  • Dispositivo y recursos: resources.image, resources.patch, input.gamepad, media.second-screen, media.streaming.

Los demás permisos de esta página requieren un paquete compatible instalado mediante Importar módulo. Ser V1 no basta para entrar en el catálogo. V2 se distribuye por importación local.