Saltar al contenido
Nelkano Emulador multisistema

SDK de Nelkano

Multijugador: GB/GBC, GBA, NDS y módulos custom

Multijugador y Amigos vienen incluidos con Nelkano. Para conectar jugadores, abre un juego GB/GBC, GBA o NDS y entra en Multijugador desde los ajustes de la partida o su botón de sistema. Cada motor y adaptador determina los modos disponibles.

El Editor de controles permite mover, escalar u ocultar ese botón. Para añadir un modo de conexión, utiliza los ganchos de Multijugador y comprueba primero las capacidades de la sesión.

Elegir jugadores y conectar

  1. Iniciad sesión con cuentas que sean amigas aceptadas y abrid juegos compatibles.
  2. En los ajustes de la partida, entra en CONEXIONES → Multijugador.
  3. El anfitrión elige Crear partida y selecciona a sus amigos. Los invitados eligen Unirse y seleccionan al anfitrión.
  4. Pulsad la acción del modo que vais a utilizar. Elegir los amigos y el rol todavía no establece la conexión.

Las acciones de conexión están al pie. Desconectar aparece si hay una conexión o una recuperación pendiente; la cruz superior cierra el panel.

Crear o unirse a una partida y conectar con cable link (ejemplo).
Crear o unirse a una partida y conectar con cable link (ejemplo).

Cable link de GBA

Conectar con cable link requiere dos jugadores con la misma ROM exacta. Conectad primero y entrad después al multijugador del propio juego.

La sesión es temporal: al terminar se recupera la partida anterior y se descarta el progreso de la conexión.

El host negocia WebRTC entre cuentas amigas. La conexión entre redes depende de ICE, NAT y las redes de ambos jugadores. Studio simula el flujo.

Puerto localLink del SDK

Comprueba que exista host.localLink y que capabilities(session) devuelva supported:true.

  • capabilities y status requieren emulation.read.
  • connect(session,{friendUid,role,players:2}) requiere emulation.handoff y network.multiplayer.
  • stop(session) requiere emulation.handoff.
  • role es host o guest; friendUid identifica a un amigo aceptado.

capabilities incluye systemId, players, protocol, stateFormat, maxStateBytes y transport:webrtc-friends. host y join por código ya no forman parte del puerto público. JavaScript no recibe rutas privadas, estados completos, credenciales ni handles nativos.

connect devuelve phase:connecting mientras prepara el cable. Consulta status mediante ticks de runtime, incluso con el panel cerrado:

  • phase: idle, connecting, playing o error.
  • Datos: active, role, friendUid, players, delayFrames y message.
  • playing: la conexión nativa está establecida y el estado inicial coincide.
  • restored:true: se ha recuperado la partida original al cerrar.
  • Un error con restored:false exige reintentar la recuperación o reabrir la partida.

Cada llamada valida propietario, permisos y referencia id/generation. La negociación comprueba cuenta, amistad, roles, ROM, sesión y nonce; cada copia inicial admite hasta 4 MiB. Studio simula el contrato sin conexiones físicas.

Crear online para otro juego

Crea un módulo que consuma game-adapters 1.0.0 de nelkano.multiplayer y registre el adaptador:

await host.hooks.contribute("nelkano.multiplayer", "game-adapters", "mi-juego", {label: "Mi juego"});

Implementa estas operaciones en onHook:

  • identify: recibe sesión, gameId y systemId. Solo si reconoces el juego, devuelve {compatible:true, compatibilityKey:"mi-protocolo-v1", transportSystem:"gba"}. Los jugadores compatibles deben compartir la clave.
  • capture: devuelve los datos públicos del juego.
  • render: recibe peers para representarlos.
  • messages: recibe los paquetes de tu protocolo.

Consume sessions para consultar snapshot, conectar con {friendUid, role}, desconectar o enviar con send y {type, payload, channel?}. Multijugador añade identidad de sesión y secuencia.

channel sustituye mensajes pendientes del mismo canal, útil para posiciones. Omítelo cuando debas conservar cada mensaje.

Contribuye a events para recibir changed con type, state, sessionId, peers y packets. No llames de vuelta al proveedor dentro del callback: crearías un ciclo. Amigos proporciona la cuenta y su lista; el host conserva la conexión autenticada y los tokens.

Añadir una acción al formulario

Contribuye a connection-actions 1.0.0 con strategy:presence, adapterId, systems, minPlayers y maxPlayers. La acción y el adaptador deben pertenecer al mismo módulo y autor.

Las salas de presencia admiten de dos a cuatro participantes, según el adaptador. El anfitrión selecciona a sus amigos y cada invitado elige al anfitrión. Cable link sigue limitado a dos jugadores.

sessions.connect acepta friendUids. Si usas host.online.connect directamente como anfitrión en ONLINE_LAYER, puedes indicar hasta tres destinatarios. send acepta peerUid para elegir uno.

Cada enlace tiene su sessionId. El protocolo de presencia utiliza además el identificador y los miembros de la sala.

Mantener la partida activa en presencia

En presencia, la partida sigue avanzando desde online.connect hasta desconectar, fallar o retirar el módulo. El host aplica esta regla sin otro permiso. Durante Online se evita el apagado por inactividad; en Android, girar el dispositivo conserva pantalla y sesión.

El panel muestra los errores de red. Los registros de transporte guardan contadores y motivos de cierre, sin contraseñas, tokens ni cuerpos de paquetes. Puedes añadir un motivo corto con online.disconnect({reason: "local-disconnect"}).

Estado y reconexión

El estado se muestra al principio del panel para anfitrión e invitado:

  • Conectado: han llegado datos del amigo en los últimos ocho segundos.
  • Reconectando: no llegan datos, pero la sesión permanece abierta.

Una sesión HTTP abierta no confirma que el otro jugador esté activo.

Multijugador envía una señal de vida cada segundo y medio, también durante transiciones en las que el adaptador no puede leer la presencia. Cada remitente tiene su propia secuencia para que un jugador nuevo no herede la del anterior.

Reconectar capa online cierra la conexión y abre otra; el amigo debe entrar de nuevo. sessions.connect reutiliza una conexión con la misma configuración, por lo que la reconexión explícita se hace desde ese botón.

sessions.snapshot incluye state, transportState, peerActive y peerLastSeenAt. Los avisos events entregan el mismo estado comprobado.

Partir del ejemplo de adaptador

.\nelkmod.cmd example multiplayer-adapter C:/mis-modulos/mi-adaptador

El ejemplo usa un CHIP-8 ficticio para enseñar game-adapters, sessions y events. No sincroniza la CPU ni convierte un juego en online por sí solo.

  • identify puede indicar connectionKind:presence y transportSystem.
  • shared-console necesita un core/host compatible.
  • sessions.send mantiene los mensajes públicos separados de los paquetes internos de tarjetas y mandos.
  • events incluye mode y systemId; snapshot identifica nelkano.multiplayer como proveedor.

Cada adaptador define su protocolo mediante los ganchos de nelkano.multiplayer. Un gancho no añade soporte de CPU, cable o consola compartida a un core que no lo anuncie.

Crear una integración de consola compartida

host.sharedConsole es opcional y no añade un modo al menú. Con emulation.read, consulta capabilities(session): devuelve supported, sistema, tamaño/formato de tarjetas, códigos de entrada y límites.

El adaptador disponible es PS1 Android; Studio lo simula. Comprueba supported antes de usarlo.

Para compartir tarjetas, solicita emulation.handoff:

  • exportCard(session,{slot,index}): exporta desde index 0 en fragmentos de 16 KiB.
  • stageCard(session,{index,data}): prepara los fragmentos para tu módulo/autor/sesión; caducan a los diez segundos.
  • beginTemporary(session) y endTemporary(session): inician y terminan la sesión temporal.

El adaptador valida el formato antes de montarlo. Tu módulo define el protocolo y los juegos compatibles.

Con emulation.input puedes llamar a input(session,{sequence,keys}), controller(session,{enabled,message}) y controllerEvents(session).

  • Las secuencias deben aumentar y los códigos pertenecer al core; no se admiten duplicados.
  • El anfitrión libera botones tras 1200 ms sin una entrada válida.
  • La superficie del invitado pausa su propia emulación, conserva el orden de pulsaciones y libera botones al perder el foco.
  • Esa superficie se cierra si no se consulta durante cinco segundos.

Cada llamada comprueba permisos, propietario y generación.

Esta estrategia admite online.connect con continuous:false, una política local que no se envía al servidor. GBA mantiene continuous:true por defecto. La tarjeta temporal conserva intactos los guardados permanentes de ambos jugadores.