Cree aplicaciones web multijugador instantáneas, no se requiere servidor
¿PRUEBA LA DEMOSTRACIÓN ?
trystero gestiona una red de mensajería clandestina que permite a los usuarios de su aplicación hablar directamente entre sí, de forma cifrada y sin intermediarios en el servidor.
La red está llena de canales de comunicación abiertos y descentralizados: rastreadores de torrents, intermediarios de dispositivos IoT, protocolos de archivos boutique y redes sociales especializadas.
trystero aprovecha estas redes para establecer automáticamente conexiones p2p seguras y privadas entre los usuarios de su aplicación sin ningún esfuerzo de su parte.
¿Los compañeros pueden conectarse a través de? BitTorrent, ? Nostr, ? MQTT, ⚡️ Supabase, Firebase o ? IPFS: todos usan la misma API.
Además de hacer que la coincidencia entre pares sea automática, trystero ofrece algunas abstracciones interesantes además de WebRTC:
Puedes ver lo que la gente está construyendo con trystero aquí.
Si solo quieres probar trystero , puedes saltarte esta explicación y comenzar a usarlo.
Para establecer una conexión directa de igual a igual con WebRTC, se necesita un canal de señalización para intercambiar información de pares (SDP). Por lo general, esto implica ejecutar su propio servidor de emparejamiento, pero trystero lo abstrae y ofrece múltiples estrategias "sin servidor" para conectar pares (actualmente BitTorrent, Nostr, MQTT, Supabase, Firebase e IPFS).
El punto importante a recordar es este:
Más allá del descubrimiento de pares, los datos de su aplicación nunca tocan el medio de la estrategia y se envían directamente de igual a igual y cifrados de extremo a extremo entre los usuarios.
?
Puedes comparar estrategias aquí.
Puedes instalar con npm ( npm i trystero
) e importar así:
import { joinRoom } from ' trystero '
¿O tal vez prefieres una etiqueta de script simple? Descargue un archivo JS prediseñado de la última versión e impórtelo localmente:
< script type =" module " >
import { joinRoom } from './ trystero -torrent.min.js'
</ script >
De forma predeterminada, se utiliza la estrategia Nostr. Para usar uno diferente, simplemente realice una importación profunda de esta manera (su paquete debe incluir solo el código relevante):
import { joinRoom } from ' trystero /mqtt' // ( trystero -mqtt.min.js with a local file)
// or
import { joinRoom } from ' trystero /torrent' // ( trystero -torrent.min.js)
// or
import { joinRoom } from ' trystero /supabase' // ( trystero -supabase.min.js)
// or
import { joinRoom } from ' trystero /firebase' // ( trystero -firebase.min.js)
// or
import { joinRoom } from ' trystero /ipfs' // ( trystero -ipfs.min.js)
A continuación, únete al usuario a una sala con una identificación:
const config = { appId : 'san_narciso_3d' }
const room = joinRoom ( config , 'yoyodyne' )
El primer argumento es un objeto de configuración que requiere un appId
. Este debería ser un identificador completamente único para su aplicación¹. El segundo argumento es el ID de la habitación.
¿Por qué habitaciones? Los navegadores solo pueden manejar una cantidad limitada de conexiones WebRTC a la vez, por lo que se recomienda diseñar su aplicación de manera que los usuarios estén divididos en grupos (o salas, espacios de nombres o canales... como quiera llamarlos).
¹ Cuando usa Firebase, appId
debe ser la URL de su databaseURL
y cuando usa Supabase, debe ser la URL de su proyecto.
Escuche a los compañeros que se unen a la sala:
room . onPeerJoin ( peerId => console . log ( ` ${ peerId } joined` ) )
Escuche a sus compañeros que salen del salón:
room . onPeerLeave ( peerId => console . log ( ` ${ peerId } left` ) )
Escuche a sus compañeros enviando sus transmisiones de audio/vídeo:
room . onPeerStream (
( stream , peerId ) => ( peerElements [ peerId ] . video . srcObject = stream )
)
Para darse de baja de eventos, abandone la sala:
room . leave ( )
Puede acceder al ID del par del usuario local importando selfId
de esta manera:
import { selfId } from ' trystero '
console . log ( `my peer ID is ${ selfId } ` )
Envíe a sus compañeros su transmisión de video:
room . addStream (
await navigator . mediaDevices . getUserMedia ( { audio : true , video : true } )
)
Envía y suscríbete a acciones P2P personalizadas:
const [ sendDrink , getDrink ] = room . makeAction ( 'drink' )
// buy drink for a friend
sendDrink ( { drink : 'negroni' , withIce : true } , friendId )
// buy round for the house (second argument omitted)
sendDrink ( { drink : 'mezcal' , withIce : false } )
// listen for drinks sent to you
getDrink ( ( data , peerId ) =>
console . log (
`got a ${ data . drink } with ${ data . withIce ? '' : 'out' } ice from ${ peerId } `
)
)
También puedes usar acciones para enviar datos binarios, como imágenes:
const [ sendPic , getPic ] = room . makeAction ( 'pic' )
// blobs are automatically handled, as are any form of TypedArray
canvas . toBlob ( blob => sendPic ( blob ) )
// binary data is received as raw ArrayBuffers so your handling code should
// interpret it in a way that makes sense
getPic (
( data , peerId ) => ( imgs [ peerId ] . src = URL . createObjectURL ( new Blob ( [ data ] ) ) )
)
Digamos que queremos que los usuarios puedan nombrarse a sí mismos:
const idsToNames = { }
const [ sendName , getName ] = room . makeAction ( 'name' )
// tell other peers currently in the room our name
sendName ( 'Oedipa' )
// tell newcomers
room . onPeerJoin ( peerId => sendName ( 'Oedipa' , peerId ) )
// listen for peers naming themselves
getName ( ( name , peerId ) => ( idsToNames [ peerId ] = name ) )
room . onPeerLeave ( peerId =>
console . log ( ` ${ idsToNames [ peerId ] || 'a weird stranger' } left` )
)
Las acciones son inteligentes y manejan la serialización y la fragmentación detrás de escena. Esto significa que puede enviar archivos muy grandes y cualquier dato que envíe se recibirá en el otro lado como del mismo tipo (un número como un número, una cadena como una cadena, un objeto como un objeto, binario como binario, etc.) .
A continuación se muestra un ejemplo sencillo de cómo se puede crear una sala de chat de audio:
// this object can store audio instances for later
const peerAudios = { }
// get a local audio stream from the microphone
const selfStream = await navigator . mediaDevices . getUserMedia ( {
audio : true ,
video : false
} )
// send stream to peers currently in the room
room . addStream ( selfStream )
// send stream to peers who join later
room . onPeerJoin ( peerId => room . addStream ( selfStream , peerId ) )
// handle streams from other peers
room . onPeerStream ( ( stream , peerId ) => {
// create an audio instance and set the incoming stream
const audio = new Audio ( )
audio . srcObject = stream
audio . autoplay = true
// add the audio to peerAudio object if you want to address it for something
// later (volume, etc.)
peerAudios [ peerId ] = audio
} )
Hacer lo mismo con el video es similar, solo asegúrese de agregar transmisiones entrantes a los elementos de video en el DOM:
const peerVideos = { }
const videoContainer = document . getElementById ( 'videos' )
room . onPeerStream ( ( stream , peerId ) => {
let video = peerVideos [ peerId ]
// if this peer hasn't sent a stream before, create a video element
if ( ! video ) {
video = document . createElement ( 'video' )
video . autoplay = true
// add video element to the DOM
videoContainer . appendChild ( video )
}
video . srcObject = stream
peerVideos [ peerId ] = video
} )
Supongamos que su aplicación admite el envío de varios tipos de archivos y desea anotar los bytes sin procesar que se envían con metadatos sobre cómo deben interpretarse. En lugar de agregar manualmente bytes de metadatos al búfer, simplemente puedes pasar un argumento de metadatos en la acción del remitente para tu carga útil binaria:
const [ sendFile , getFile ] = makeAction ( 'file' )
getFile ( ( data , peerId , metadata ) =>
console . log (
`got a file ( ${ metadata . name } ) from ${ peerId } with type ${ metadata . type } ` ,
data
)
)
// to send metadata, pass a third argument
// to broadcast to the whole room, set the second peer ID argument to null
sendFile ( buffer , null , { name : 'The Courierʼs Tragedy' , type : 'application/pdf' } )
Las funciones de remitente de acciones devuelven una promesa que se resuelve cuando terminan de enviar. Opcionalmente, puede utilizar esto para indicarle al usuario cuándo se realiza una transferencia grande.
await sendFile ( amplePayload )
console . log ( 'done sending to all peers' )
Las funciones del remitente de acciones también toman una función de devolución de llamada opcional que se llamará continuamente a medida que avanza la transmisión. Esto se puede utilizar para mostrar una barra de progreso al remitente para transferencias grandes. La devolución de llamada se llama con un valor porcentual entre 0 y 1 y el ID del par receptor:
sendFile (
payload ,
// notice the peer target argument for any action sender can be a single peer
// ID, an array of IDs, or null (meaning send to all peers in the room)
[ peerIdA , peerIdB , peerIdC ] ,
// metadata, which can also be null if you're only interested in the
// progress handler
{ filename : 'paranoids.flac' } ,
// assuming each peer has a loading bar added to the DOM, its value is
// updated here
( percent , peerId ) => ( loadingBars [ peerId ] . value = percent )
)
De manera similar, puedes escuchar eventos de progreso como receptor de esta manera:
const [ sendFile , getFile , onFileProgress ] = room . makeAction ( 'file' )
onFileProgress ( ( percent , peerId , metadata ) =>
console . log (
` ${ percent * 100 } % done receiving ${ metadata . filename } from ${ peerId } `
)
)
Tenga en cuenta que todos los metadatos se envían con eventos de progreso para que pueda mostrarle al usuario receptor que hay una transferencia en curso, quizás con el nombre del archivo entrante.
Dado que un igual puede enviar múltiples transmisiones en paralelo, también puede utilizar metadatos para diferenciarlas, por ejemplo, enviando una identificación única.
Una vez que los pares están conectados entre sí, todas sus comunicaciones se cifran de extremo a extremo. Durante el proceso inicial de conexión/descubrimiento, los SDP de los pares se envían a través del medio de estrategia de peering elegido. De forma predeterminada, el SDP se cifra utilizando una clave derivada del ID de su aplicación y del ID de la sala para evitar que aparezcan datos de sesión en texto sin formato en los registros. Esto está bien para la mayoría de los casos de uso; sin embargo, un operador de estrategia de retransmisión puede aplicar ingeniería inversa a la clave utilizando las ID de la sala y la aplicación. Una opción más segura es pasar un parámetro password
en el objeto de configuración de la aplicación que se utilizará para derivar la clave de cifrado:
joinRoom ( { appId : 'kinneret' , password : 'MuchoMaa$' } , 'w_a_s_t_e__v_i_p' )
Este es un secreto compartido que debe conocerse con anticipación y la contraseña debe coincidir con todos los compañeros en la sala para que puedan conectarse. Un caso de uso de ejemplo podría ser una sala de chat privada donde los usuarios aprenden la contraseña a través de medios externos.
Las funciones trystero son idempotentes, por lo que ya funcionan de inmediato como ganchos de React.
Aquí hay un componente de ejemplo simple en el que cada compañero sincroniza su color favorito con el de los demás:
import { joinRoom } from ' trystero '
import { useState } from 'react'
const trystero Config = { appId : 'thurn-und-taxis' }
export default function App ( { roomId } ) {
const room = joinRoom ( trystero Config , roomId )
const [ sendColor , getColor ] = room . makeAction ( 'color' )
const [ myColor , setMyColor ] = useState ( '#c0ffee' )
const [ peerColors , setPeerColors ] = useState ( { } )
// whenever new peers join the room, send my color to them:
room . onPeerJoin ( peer => sendColor ( myColor , peer ) )
// listen for peers sending their colors and update the state accordingly:
getColor ( ( color , peer ) =>
setPeerColors ( peerColors => ( { ... peerColors , [ peer ] : color } ) )
)
const updateColor = e => {
const { value } = e . target
// when updating my own color, broadcast it to all peers:
sendColor ( value )
setMyColor ( value )
}
return (
< >
< h1 > trystero + React </ h1 >
< h2 > My color: </ h2 >
< input type = "color" value = { myColor } onChange = { updateColor } />
< h2 > Peer colors: </ h2 >
< ul >
{ Object . entries ( peerColors ) . map ( ( [ peerId , color ] ) => (
< li key = { peerId } style = { { backgroundColor : color } } >
{ peerId } : { color }
</ li >
) ) }
</ ul >
</ >
)
}
Los lectores astutos pueden notar que el ejemplo anterior es simple y no considera si queremos cambiar la ID de la habitación del componente o desmontarlo. Para esos escenarios, puedes usar este simple gancho useRoom()
que cancela la suscripción a los eventos de la sala en consecuencia:
import { joinRoom } from ' trystero '
import { useEffect , useRef } from 'react'
export const useRoom = ( roomConfig , roomId ) => {
const roomRef = useRef ( joinRoom ( roomConfig , roomId ) )
const lastRoomIdRef = useRef ( roomId )
useEffect ( ( ) => {
if ( roomId !== lastRoomIdRef . current ) {
roomRef . current . leave ( )
roomRef . current = joinRoom ( roomConfig , roomId )
lastRoomIdRef . current = roomId
}
return ( ) => roomRef . current . leave ( )
} , [ roomConfig , roomId ] )
return roomRef . current
}
Para utilizar la estrategia Supabase:
appId
en la configuración trystero , copie la clave API anon public
y configúrela como supabaseKey
en la configuración de trysteroSi deseas utilizar la estrategia de Firebase y no tienes un proyecto existente:
databaseURL
y úsela como appId
en su configuración trystero{
"rules" : {
".read" : false ,
".write" : false ,
"__ trystero __" : {
".read" : false ,
".write" : false ,
"$room_id" : {
".read" : true ,
".write" : true
}
}
}
}
Estas reglas garantizan que la presencia de los pares de la sala solo sea legible si el espacio de nombres de la sala se conoce de antemano.
joinRoom(config, roomId, [onError])
Agrega un usuario local a la sala mediante la cual otros pares en el mismo espacio de nombres abrirán canales de comunicación y enviarán eventos. Llamar joinRoom()
varias veces con el mismo espacio de nombres devolverá la misma instancia de sala.
config
: objeto de configuración que contiene las siguientes claves:
appId
: (obligatorio) una cadena única que identifica su aplicación. Cuando utilice Supabase, esto debe configurarse en la URL de su proyecto (consulte las instrucciones de configuración de Supabase). Si usa Firebase, esta debería ser la URL de databaseURL
de su configuración de Firebase (consulte también firebaseApp
a continuación para conocer una forma alternativa de configurar la estrategia de Firebase).
password
: (opcional) una cadena para cifrar las descripciones de las sesiones a través de AES-GCM a medida que pasan a través del medio de intercambio de tráfico. Si no se configura, las descripciones de las sesiones se cifrarán con una clave derivada del ID de la aplicación y el nombre de la sala. Una contraseña personalizada debe coincidir entre todos los compañeros de la sala para que puedan conectarse. Consulte cifrado para obtener más detalles.
rtcConfig
: (opcional) especifica una RTCConfiguration
personalizada para todas las conexiones de pares.
relayUrls
: (opcional, ? BitTorrent, ? Nostr, ? MQTT únicamente) Lista personalizada de URL para que la estrategia utilice para iniciar conexiones P2P. Estos serían los rastreadores BitTorrent, los retransmisores Nostr y los corredores MQTT, respectivamente. Deben admitir conexiones WebSocket seguras.
relayRedundancy
: (opcional, ? BitTorrent, ? Nostr, ? MQTT únicamente) Entero que especifica a cuántos rastreadores de torrents conectarse simultáneamente en caso de que alguno falle. Pasar una opción relayUrls
hará que esta opción se ignore ya que se utilizará toda la lista.
supabaseKey
: (obligatorio, ⚡️ solo Supabase) la clave API anon public
de su proyecto Supabase.
firebaseApp
: (opcional, solo Firebase) Puede pasar una instancia de aplicación Firebase ya inicializada en lugar de un appId
. Normalmente, trystero inicializará una aplicación de Firebase según el appId
pero esto fallará si ya la ha inicializado para usarla en otro lugar.
rootPath
: (opcional, solo Firebase) Cadena que especifica la ruta donde trystero escribe sus datos de emparejamiento en su base de datos ( '__ trystero __'
de forma predeterminada). Cambiar esto es útil si desea ejecutar varias aplicaciones utilizando la misma base de datos y no quiere preocuparse por las colisiones de espacios de nombres.
libp2pConfig
: (opcional, solo IPFS) Libp2pOptions
donde puede especificar una lista de pares estáticos para el arranque.
roomId
: una cadena para nombrar pares del espacio y eventos dentro de una sala.
onError(details)
: (opcional) Una función de devolución de llamada que se llamará si no se puede unir a la sala debido a una contraseña incorrecta. details
es un objeto que contiene appId
, roomId
, peerId
y error
que describe el error.
Devuelve un objeto con los siguientes métodos:
leave()
Elimine al usuario local de la sala y cancele su suscripción a los eventos de la sala.
getPeers()
Devuelve un mapa de RTCPeerConnection
para los pares presentes en la sala (sin incluir al usuario local). Las claves de este objeto son los ID de los respectivos pares.
addStream(stream, [targetPeers], [metadata])
Transmite flujo de medios a otros pares.
stream
: un MediaStream
con audio y/o vídeo para enviar a los compañeros de la sala.
targetPeers
: (opcional) si se especifica, la secuencia se envía solo al ID del par de destino (cadena) o a la lista de ID del par (matriz).
metadata
: (opcional) metadatos adicionales (cualquier tipo serializable) que se enviarán con la transmisión. Esto resulta útil cuando se envían varias secuencias para que los destinatarios sepan cuál es cuál (por ejemplo, una cámara web frente a una captura de pantalla). Si desea transmitir una transmisión a todos los pares en la sala con un argumento de metadatos, pase null
como segundo argumento.
removeStream(stream, [targetPeers])
Deja de enviar secuencias de medios enviadas previamente a otros pares.
stream
: un MediaStream
enviado previamente para detener el envío.
targetPeers
: (opcional) si se especifica, la secuencia se elimina solo del ID del par de destino (cadena) o de la lista de ID del par (matriz).
addTrack(track, stream, [targetPeers], [metadata])
Agrega una nueva pista multimedia a una transmisión.
track
: un MediaStreamTrack
para agregar a una transmisión existente.
stream
: el MediaStream
de destino al que adjuntar la nueva pista.
targetPeers
: (opcional) si se especifica, la pista se envía solo al ID del par de destino (cadena) o a la lista de ID de pares (matriz).
metadata
: (opcional) metadatos adicionales (cualquier tipo serializable) que se enviarán con la pista. Consulte las notas metadata
de addStream()
arriba para obtener más detalles.
removeTrack(track, stream, [targetPeers])
Elimina una pista multimedia de una transmisión.
track
: el MediaStreamTrack
que se va a eliminar.
stream
: el MediaStream
al que está adjunta la pista.
targetPeers
: (opcional) si se especifica, la pista se elimina solo del ID del par de destino (cadena) o de la lista de ID del par (matriz).
replaceTrack(oldTrack, newTrack, stream, [targetPeers])
Reemplaza una pista multimedia por una nueva.
oldTrack
: el MediaStreamTrack
que se va a eliminar.
newTrack
: un MediaStreamTrack
para adjuntar.
stream
: el MediaStream
al que está conectado el oldTrack
.
targetPeers
: (opcional) si se especifica, la pista se reemplaza solo para el ID del par de destino (cadena) o la lista de ID de pares (matriz).
onPeerJoin(callback)
Registra una función de devolución de llamada que se llamará cuando un par se una a la sala. Si se llama más de una vez, solo se llama a la última devolución de llamada registrada.
callback(peerId)
: función que se ejecuta cada vez que un par se une, llamada con el ID del par.Ejemplo:
onPeerJoin ( peerId => console . log ( ` ${ peerId } joined` ) )
onPeerLeave(callback)
Registra una función de devolución de llamada que se llamará cuando un par abandone la sala. Si se llama más de una vez, solo se llama a la última devolución de llamada registrada.
callback(peerId)
: función que se ejecuta cada vez que un par se va, llamada con el ID del par.Ejemplo:
onPeerLeave ( peerId => console . log ( ` ${ peerId } left` ) )
onPeerStream(callback)
Registra una función de devolución de llamada que se llamará cuando un par envíe un flujo de medios. Si se llama más de una vez, solo se llama a la última devolución de llamada registrada.
callback(stream, peerId, metadata)
: función que se ejecuta cada vez que un par envía un flujo de medios, llamado con el flujo, el ID y los metadatos opcionales del par (consulte addStream()
arriba para obtener más detalles).Ejemplo:
onPeerStream ( ( stream , peerId ) =>
console . log ( `got stream from ${ peerId } ` , stream )
)
onPeerTrack(callback)
Registra una función de devolución de llamada que se llamará cuando un par envíe una pista multimedia. Si se llama más de una vez, solo se llama a la última devolución de llamada registrada.
callback(track, stream, peerId, metadata)
: función que se ejecuta cada vez que un interlocutor envía una pista multimedia, llamada con la pista del interlocutor, la secuencia adjunta, el ID y los metadatos opcionales (consulte addTrack()
más arriba para obtener más detalles).Ejemplo:
onPeerTrack ( ( track , stream , peerId ) =>
console . log ( `got track from ${ peerId } ` , track )
)
makeAction(actionId)
Escuche y envíe acciones de datos personalizadas.
actionId
: una cadena para registrar esta acción de forma coherente entre todos los pares.Devuelve una matriz de tres funciones:
Envía datos a los pares y devuelve una promesa que se resuelve cuando todos los pares objetivo hayan terminado de recibir datos.
(data, [targetPeers], [metadata], [onProgress])
data
: cualquier valor para enviar (primitivo, objeto, binario). La serialización y la fragmentación se manejan automáticamente. Los datos binarios (por ejemplo, Blob
, TypedArray
) son recibidos por otro par como un ArrayBuffer
agnóstico.
targetPeers
: (opcional) un ID de par (cadena), una matriz de ID de pares o null
(que indica enviar a todos los pares en la sala).
metadata
: (opcional) si los datos son binarios, puede enviar un objeto de metadatos opcional que los describa (consulte Metadatos binarios).
onProgress
: (opcional) una función de devolución de llamada que se llamará cuando se transmita cada fragmento para cada par. La función se llamará con un valor entre 0 y 1 y un ID de igual. Consulte Actualizaciones de progreso para ver un ejemplo.
Registra una función de devolución de llamada que se ejecuta cuando se reciben datos para esta acción de otros pares.
(data, peerId, metadata)
data
: el valor transmitido por el par emisor. La deserialización se maneja automáticamente, es decir, un número se recibirá como un número, un objeto como un objeto, etc.
peerId
: la cadena de ID del par emisor.
metadata
: (opcional) objeto de metadatos opcional proporcionado por el remitente si data
son binarios, por ejemplo, un nombre de archivo.
Registra una función de devolución de llamada que se ejecuta cuando se reciben datos parciales de los pares. Puede utilizar esto para rastrear grandes transferencias binarias. Consulte Actualizaciones de progreso para ver un ejemplo.
(percent, peerId, metadata)
percent
: un número entre 0 y 1 que indica el porcentaje completado de la transferencia.
peerId
: la cadena de ID del par emisor.
metadata
: (opcional) objeto de metadatos opcional proporcionado por el remitente.
Ejemplo:
const [ sendCursor , getCursor ] = room . makeAction ( 'cursormove' )
window . addEventListener ( 'mousemove' , e => sendCursor ( [ e . clientX , e . clientY ] ) )
getCursor ( ( [ x , y ] , peerId ) => {
const peerCursor = cursorMap [ peerId ]
peerCursor . style . left = x + 'px'
peerCursor . style . top = y + 'px'
} )
ping(peerId)
Toma un ID de par y devuelve una promesa que se resuelve en los milisegundos que tomó el viaje de ida y vuelta a ese par. Úselo para medir la latencia.
peerId
: cadena de ID del par objetivo.Ejemplo:
// log round-trip time every 2 seconds
room . onPeerJoin ( peerId =>
setInterval (
async ( ) => console . log ( `took ${ await room . ping ( peerId ) } ms` ) ,
2000
)
)
selfId
Una cadena de identificación única que otros pares conocerán como usuario local globalmente en todas las salas.
getRelaySockets()
(? BitTorrent, ? Nostr, ? MQTT únicamente) Devuelve un objeto de claves URL de retransmisión asignadas a sus conexiones WebSocket. Esto puede ser útil para determinar el estado de la conexión del usuario a los relés y manejar cualquier falla de conexión.
Ejemplo:
console . log ( trystero . getRelaySockets ( ) )
// => Object {
// "wss://tracker.webtorrent.dev": WebSocket,
// "wss://tracker.openwebtorrent.com": WebSocket
// }
getOccupants(config, roomId)
(Solo Firebase) Devuelve una promesa que se resuelve en una lista de ID de usuario presentes en el espacio de nombres determinado. Esto es útil para comprobar cuántos usuarios hay en una sala sin unirse a ella.
config
: un objeto de configuraciónroomId
: una cadena de espacio de nombres que pasarías a joinRoom()
.Ejemplo:
console . log ( ( await trystero . getOccupants ( config , 'the_scope' ) ) . length )
// => 3
configuración única¹ | tamaño del paquete² | hora de conectarse³ | |
---|---|---|---|
? Nostr | ninguno ? | 54K | ⏱️⏱️ |
? MQTT | ninguno ? | 332K | ⏱️⏱️ |
? BitTorrent | ninguno ? | ¿25K? | ⏱️⏱️ |
⚡️ Supabase | ~5 minutos | 150K | ⏱️ ? |
base de fuego | ~5 minutos | 177K | ⏱️ ? |
? IPFS | ninguno ? | 945K | ⏱️⏱️ |
¹ Todas las estrategias, excepto Firebase, no requieren configuración. Firebase es una estrategia administrada que requiere configurar una cuenta.
² Calculado mediante agrupación Rollup + compresión Terser.
³ Velocidad relativa de los compañeros que se conectan entre sí al unirse a una sala. Firebase es casi instantáneo, mientras que las otras estrategias son un poco más lentas para intercambiar información de intercambio de tráfico.
La ventaja única de trystero es que no requiere configuración de backend y utiliza infraestructura descentralizada en la mayoría de los casos. Esto permite una experimentación sin fricciones y ningún punto único de falla. Un posible inconveniente es que es difícil garantizar que la infraestructura pública que utiliza siempre estará altamente disponible, incluso con las técnicas de redundancia que utiliza trystero . Si bien las otras estrategias están descentralizadas, las estrategias Supabase y Firebase son un enfoque más administrado con mayor control y un SLA, que podría ser más apropiado para aplicaciones de "producción".
trystero hace que sea trivial cambiar entre estrategias: simplemente cambie una única línea de importación y experimente rápidamente:
import { joinRoom } from ' trystero /[torrent|nostr|mqtt|supabase|firebase|ipfs]'
trystero de Dan Motzenbecker