สร้างเว็บแอปที่มีผู้เล่นหลายคนได้ทันที โดยไม่ต้องใช้เซิร์ฟเวอร์
ลองเดโม่ดู ไหม?
trystero จัดการเครือข่ายจัดส่งลับที่ช่วยให้ผู้ใช้แอปพลิเคชันของคุณพูดคุยกันโดยตรง มีการเข้ารหัสและไม่ต้องมีคนกลางเซิร์ฟเวอร์
อินเทอร์เน็ตเต็มไปด้วยช่องทางการสื่อสารแบบเปิดและกระจายอำนาจ: ตัวติดตามทอร์เรนต์ นายหน้าอุปกรณ์ IoT โปรโตคอลไฟล์บูติก และโซเชียลเน็ตเวิร์กเฉพาะกลุ่ม
trystero piggybacks บนเครือข่ายเหล่านี้เพื่อสร้างการเชื่อมต่อ p2p ที่ปลอดภัย เป็นส่วนตัวระหว่างผู้ใช้แอปของคุณโดยอัตโนมัติ โดยไม่ต้องใช้ความพยายามในส่วนของคุณ
เพื่อนๆ สามารถเชื่อมต่อผ่าน ? บิททอร์เรนต์, ? นอสตร์, ? MQTT, ⚡️ Supabase, Firebase หรือ ? IPFS – ทั้งหมดใช้ API เดียวกัน
นอกจากการทำให้การจับคู่แบบเพียร์แมตช์เป็นแบบอัตโนมัติแล้ว trystero ยังนำเสนอนามธรรมที่ดีนอกเหนือจาก WebRTC:
คุณสามารถดูสิ่งที่ผู้คนกำลังสร้างด้วย trystero ได้ที่นี่
หากคุณเพียงต้องการลองใช้ trystero คุณสามารถข้ามคำอธิบายนี้และไปใช้ได้เลย
ในการสร้างการเชื่อมต่อแบบเพียร์ทูเพียร์โดยตรงกับ WebRTC จำเป็นต้องมีช่องทางการส่งสัญญาณเพื่อแลกเปลี่ยนข้อมูลเพียร์ (SDP) โดยทั่วไปสิ่งนี้เกี่ยวข้องกับการเรียกใช้เซิร์ฟเวอร์การค้นหาแมตช์ของคุณเอง แต่ trystero จะสรุปสิ่งนี้ให้คุณ และเสนอกลยุทธ์ "ไร้เซิร์ฟเวอร์" มากมายสำหรับการเชื่อมต่อเพียร์ (ปัจจุบันคือ BitTorrent, Nostr, MQTT, Supabase, Firebase และ IPFS)
จุดสำคัญที่ต้องจำคือ:
นอกเหนือจากการค้นพบแบบ peer-to-peer ข้อมูลของแอปของคุณไม่เคยแตะต้องสื่อกลยุทธ์และจะถูกส่งโดยตรงแบบ peer-to-peer และการเข้ารหัสจากต้นทางถึงปลายทางระหว่างผู้ใช้
-
คุณสามารถเปรียบเทียบกลยุทธ์ได้ที่นี่
คุณสามารถติดตั้งด้วย npm ( npm i trystero
) และนำเข้าดังนี้:
import { joinRoom } from ' trystero '
หรือบางทีคุณอาจต้องการแท็กสคริปต์ธรรมดา? ดาวน์โหลดไฟล์ JS ที่สร้างไว้ล่วงหน้าจากรีลีสล่าสุดและนำเข้าในเครื่อง:
< script type =" module " >
import { joinRoom } from './ trystero -torrent.min.js'
</ script >
ตามค่าเริ่มต้น จะใช้กลยุทธ์ Nostr หากต้องการใช้อันอื่นเพียงแค่นำเข้าเชิงลึกเช่นนั้น (บันเดิลของคุณควรจัดการรวมเฉพาะโค้ดที่เกี่ยวข้องเท่านั้น):
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)
ถัดไป ให้ผู้ใช้เข้าร่วมห้องที่มีรหัส:
const config = { appId : 'san_narciso_3d' }
const room = joinRoom ( config , 'yoyodyne' )
อาร์กิวเมนต์แรกคือออบเจ็กต์การกำหนดค่าที่ต้องมี appId
นี่ควรเป็นตัวระบุที่ไม่ซ้ำกันโดยสมบูรณ์สำหรับแอปของคุณ¹ อาร์กิวเมนต์ที่สองคือรหัสห้อง
ทำไมต้องมีห้อง? เบราว์เซอร์สามารถรองรับการเชื่อมต่อ WebRTC ในจำนวนที่จำกัดในแต่ละครั้ง ดังนั้นจึงแนะนำให้ออกแบบแอปของคุณโดยแบ่งผู้ใช้ออกเป็นกลุ่ม (หรือห้อง หรือเนมสเปซ หรือช่อง... สิ่งที่คุณต้องการเรียก)
¹ เมื่อใช้ Firebase appId
ควรเป็น databaseURL
ของคุณ และเมื่อใช้ Supabase ควรเป็น URL โปรเจ็กต์ของคุณ
ฟังเพื่อนที่เข้าร่วมห้อง:
room . onPeerJoin ( peerId => console . log ( ` ${ peerId } joined` ) )
ฟังเพื่อนออกจากห้อง:
room . onPeerLeave ( peerId => console . log ( ` ${ peerId } left` ) )
ฟังเพื่อนที่ส่งสตรีมเสียง/วิดีโอ:
room . onPeerStream (
( stream , peerId ) => ( peerElements [ peerId ] . video . srcObject = stream )
)
หากต้องการยกเลิกการสมัครรับกิจกรรม ให้ออกจากห้อง:
room . leave ( )
คุณสามารถเข้าถึงเพียร์ ID ของผู้ใช้ภายในเครื่องได้โดยการนำเข้า selfId
ดังนี้:
import { selfId } from ' trystero '
console . log ( `my peer ID is ${ selfId } ` )
ส่งสตรีมวิดีโอของคุณให้กับเพื่อน:
room . addStream (
await navigator . mediaDevices . getUserMedia ( { audio : true , video : true } )
)
ส่งและสมัครรับการดำเนินการ P2P แบบกำหนดเอง:
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 } `
)
)
คุณยังสามารถใช้การดำเนินการเพื่อส่งข้อมูลไบนารี เช่น รูปภาพ:
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 ] ) ) )
)
สมมติว่าเราต้องการให้ผู้ใช้สามารถตั้งชื่อตัวเองได้:
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` )
)
การดำเนินการเป็นไปอย่างชาญฉลาดและจัดการการซีเรียลไลซ์และการแบ่งส่วนให้กับคุณในเบื้องหลัง ซึ่งหมายความว่าคุณสามารถส่งไฟล์ที่มีขนาดใหญ่มากได้ และข้อมูลใดก็ตามที่คุณส่งจะได้รับในอีกด้านหนึ่งเป็นประเภทเดียวกัน (ตัวเลขเป็นตัวเลข สตริงเป็นสตริง อ็อบเจ็กต์เป็นอ็อบเจ็กต์ ไบนารีเป็นไบนารี ฯลฯ) .
นี่คือตัวอย่างง่ายๆ ของวิธีการสร้างห้องสนทนาแบบเสียง:
// 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
} )
การทำเช่นเดียวกันกับวิดีโอก็คล้ายกัน เพียงอย่าลืมเพิ่มสตรีมขาเข้าให้กับองค์ประกอบวิดีโอใน 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
} )
สมมติว่าแอปของคุณรองรับการส่งไฟล์ประเภทต่างๆ และคุณต้องการใส่คำอธิบายประกอบไบต์ดิบที่ถูกส่งพร้อมกับข้อมูลเมตาเกี่ยวกับวิธีการตีความไฟล์เหล่านั้น แทนที่จะเพิ่มไบต์ข้อมูลเมตาลงในบัฟเฟอร์ด้วยตนเอง คุณสามารถส่งอาร์กิวเมนต์ข้อมูลเมตาในการดำเนินการของผู้ส่งสำหรับเพย์โหลดไบนารีของคุณ:
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' } )
ฟังก์ชันผู้ส่งการดำเนินการส่งคืนสัญญาที่จะแก้ไขเมื่อส่งเสร็จแล้ว คุณสามารถเลือกใช้สิ่งนี้เพื่อระบุให้ผู้ใช้ทราบเมื่อการถ่ายโอนจำนวนมากเสร็จสิ้น
await sendFile ( amplePayload )
console . log ( 'done sending to all peers' )
ฟังก์ชันผู้ส่งการดำเนินการยังใช้ฟังก์ชันการโทรกลับเสริมซึ่งจะถูกเรียกอย่างต่อเนื่องในขณะที่การส่งข้อมูลดำเนินไป สามารถใช้เพื่อแสดงแถบความคืบหน้าแก่ผู้ส่งสำหรับการโอนเงินจำนวนมาก การโทรกลับถูกเรียกด้วยค่าเปอร์เซ็นต์ระหว่าง 0 ถึง 1 และ ID ของผู้รับ:
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 )
)
ในทำนองเดียวกัน คุณสามารถฟังเหตุการณ์ความคืบหน้าในฐานะผู้รับได้ดังนี้:
const [ sendFile , getFile , onFileProgress ] = room . makeAction ( 'file' )
onFileProgress ( ( percent , peerId , metadata ) =>
console . log (
` ${ percent * 100 } % done receiving ${ metadata . filename } from ${ peerId } `
)
)
โปรดสังเกตว่าข้อมูลเมตาใดๆ จะถูกส่งไปพร้อมกับเหตุการณ์ความคืบหน้า เพื่อให้คุณสามารถแสดงให้ผู้ใช้ที่รับทราบว่ามีการถ่ายโอนอยู่ระหว่างดำเนินการ โดยอาจเป็นชื่อของไฟล์ที่เข้ามา
เนื่องจากเพียร์สามารถส่งข้อมูลหลายรายการพร้อมกันได้ คุณจึงสามารถใช้ข้อมูลเมตาเพื่อแยกความแตกต่างระหว่างข้อมูลเหล่านั้นได้ เช่น โดยการส่ง ID ที่ไม่ซ้ำกัน
เมื่อเพื่อนเชื่อมต่อถึงกัน การสื่อสารทั้งหมดของพวกเขาจะถูกเข้ารหัสจากต้นทางถึงปลายทาง ในระหว่างขั้นตอนการเชื่อมต่อ / การค้นพบครั้งแรก SDP ของเพียร์จะถูกส่งผ่านสื่อกลยุทธ์การเพียร์ที่เลือก ตามค่าเริ่มต้น SDP จะถูกเข้ารหัสโดยใช้คีย์ที่ได้รับจากรหัสแอปและรหัสห้องของคุณ เพื่อป้องกันไม่ให้ข้อมูลเซสชันข้อความธรรมดาปรากฏในบันทึก ซึ่งเป็นเรื่องปกติสำหรับกรณีการใช้งานส่วนใหญ่ อย่างไรก็ตาม ผู้ดำเนินการกลยุทธ์การถ่ายทอดสามารถวิศวกรรมย้อนกลับคีย์ได้โดยใช้รหัสห้องและแอป ตัวเลือกที่ปลอดภัยกว่าคือการส่งพารามิเตอร์ password
ผ่านในออบเจ็กต์การกำหนดค่าแอปซึ่งจะใช้ในการรับคีย์การเข้ารหัส:
joinRoom ( { appId : 'kinneret' , password : 'MuchoMaa$' } , 'w_a_s_t_e__v_i_p' )
นี่เป็นความลับที่ใช้ร่วมกันที่ต้องทราบล่วงหน้า และรหัสผ่านจะต้องตรงกันกับเพื่อนทุกคนในห้องเพื่อให้สามารถเชื่อมต่อได้ กรณีการใช้งานตัวอย่างอาจเป็นห้องสนทนาส่วนตัวที่ผู้ใช้เรียนรู้รหัสผ่านผ่านวิธีการภายนอก
ฟังก์ชัน trystero เป็น idempotent ดังนั้นจึงทำงานนอกกรอบเป็น React hooks แล้ว
ต่อไปนี้เป็นองค์ประกอบตัวอย่างง่ายๆ ที่แต่ละเพียร์จะซิงค์สีโปรดของตนกับคนอื่นๆ:
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 >
</ >
)
}
ผู้อ่านที่ชาญฉลาดอาจสังเกตเห็นตัวอย่างข้างต้นนั้นเรียบง่าย และไม่ได้พิจารณาว่าเราต้องการเปลี่ยนรหัสห้องของส่วนประกอบหรือยกเลิกการต่อเชื่อม สำหรับสถานการณ์เหล่านั้น คุณสามารถใช้ตะขอ useRoom()
แบบธรรมดาที่จะยกเลิกการสมัครจากกิจกรรมในห้องตามลำดับ:
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
}
วิธีใช้กลยุทธ์ Supabase:
appId
ในการกำหนดค่า trystero คัดลอกคีย์ API anon public
และตั้งค่าเป็น supabaseKey
ในการกำหนดค่า trysteroหากคุณต้องการใช้กลยุทธ์ Firebase และไม่มีโปรเจ็กต์ที่มีอยู่ ให้ทำดังนี้
databaseURL
และใช้เป็น appId
ในการกำหนดค่า trystero ของคุณ{
"rules" : {
".read" : false ,
".write" : false ,
"__ trystero __" : {
".read" : false ,
".write" : false ,
"$room_id" : {
".read" : true ,
".write" : true
}
}
}
}
กฎเหล่านี้ช่วยให้แน่ใจว่าการมีอยู่ของเพื่อนร่วมห้องจะสามารถอ่านได้ก็ต่อเมื่อทราบเนมสเปซของห้องล่วงหน้าเท่านั้น
joinRoom(config, roomId, [onError])
เพิ่มผู้ใช้ภายในห้องโดยที่เพื่อนคนอื่นๆ ในเนมสเปซเดียวกันจะเปิดช่องทางการสื่อสารและส่งกิจกรรม การเรียก joinRoom()
หลายครั้งด้วยเนมสเปซเดียวกันจะส่งคืนอินสแตนซ์ห้องเดียวกัน
config
- ออบเจ็กต์การกำหนดค่าที่มีคีย์ต่อไปนี้:
appId
- (จำเป็น) สตริงเฉพาะที่ระบุแอปของคุณ เมื่อใช้ Supabase ควรตั้งค่านี้เป็น URL โปรเจ็กต์ของคุณ (ดูคำแนะนำในการตั้งค่า Supabase) หากใช้ Firebase นี่ควรเป็น databaseURL
จากการกำหนดค่า Firebase ของคุณ (โปรดดู firebaseApp
ด้านล่างเพื่อดูวิธีอื่นในการกำหนดค่ากลยุทธ์ Firebase)
password
- (เป็นทางเลือก) สตริงสำหรับเข้ารหัสคำอธิบายเซสชันผ่าน AES-GCM เมื่อส่งผ่านสื่อการเพียร์ หากไม่ได้ตั้งค่า คำอธิบายเซสชันจะถูกเข้ารหัสด้วยคีย์ที่ได้มาจากรหัสแอปและชื่อห้อง รหัสผ่านที่กำหนดเองจะต้องตรงกันระหว่างเพื่อนในห้องเพื่อให้เชื่อมต่อได้ ดูการเข้ารหัสสำหรับรายละเอียดเพิ่มเติม
rtcConfig
- (เป็นทางเลือก) ระบุ RTCConfiguration
แบบกำหนดเองสำหรับการเชื่อมต่อแบบเพียร์ทั้งหมด
relayUrls
- (เป็นทางเลือก, ? BitTorrent, ? Nostr, ? MQTT เท่านั้น) รายการ URL ที่กำหนดเองสำหรับกลยุทธ์ที่จะใช้เพื่อบูตการเชื่อมต่อ P2P สิ่งเหล่านี้คือเครื่องมือติดตาม BitTorrent, รีเลย์ Nostr และโบรกเกอร์ MQTT ตามลำดับ ต้องรองรับการเชื่อมต่อ WebSocket ที่ปลอดภัย
relayRedundancy
- (เป็นทางเลือก, ? BitTorrent, ? Nostr, ? MQTT เท่านั้น) จำนวนเต็มระบุจำนวนตัวติดตาม torrent ที่จะเชื่อมต่อพร้อมกันในกรณีที่บางตัวล้มเหลว การส่งผ่านตัวเลือก relayUrls
จะทำให้ตัวเลือกนี้ถูกละเว้น เนื่องจากระบบจะใช้รายการทั้งหมด
supabaseKey
- (จำเป็น ⚡️ Supabase เท่านั้น) คีย์ API anon public
ของโปรเจ็กต์ Supabase ของคุณ
firebaseApp
- (ไม่บังคับ, Firebase เท่านั้น) คุณสามารถส่งอินสแตนซ์แอป Firebase ที่เริ่มต้นแล้วแทน appId
ได้ โดยปกติแล้ว trystero จะเริ่มต้นแอป Firebase ตาม appId
แต่จะล้มเหลวหากคุณได้เริ่มต้นเพื่อใช้ที่อื่นแล้ว
rootPath
- (เป็นทางเลือก, Firebase เท่านั้น) สตริงที่ระบุเส้นทางโดยที่ trystero เขียนข้อมูลการจับคู่ในฐานข้อมูลของคุณ ( '__ trystero __'
เป็นค่าเริ่มต้น) การเปลี่ยนแปลงนี้จะมีประโยชน์หากคุณต้องการเรียกใช้หลายแอปโดยใช้ฐานข้อมูลเดียวกัน และไม่ต้องการกังวลเกี่ยวกับการชนกันของเนมสเปซ
libp2pConfig
- (เป็นทางเลือก, ? IPFS เท่านั้น) Libp2pOptions
ที่คุณสามารถระบุรายการเพียร์แบบคงที่สำหรับการบูตสแตรปปิ้ง
roomId
- สตริงสำหรับเนมสเปซเพียร์และกิจกรรมภายในห้อง
onError(details)
- (เป็นทางเลือก) ฟังก์ชั่นการโทรกลับที่จะถูกเรียกหากไม่สามารถเข้าร่วมห้องได้เนื่องจากรหัสผ่านไม่ถูกต้อง details
เป็นออบเจ็กต์ที่มี appId
, roomId
, peerId
และ error
ที่อธิบายข้อผิดพลาด
ส่งกลับวัตถุด้วยวิธีต่อไปนี้:
leave()
ลบผู้ใช้ในพื้นที่ออกจากห้องและยกเลิกการสมัครจากกิจกรรมในห้อง
getPeers()
ส่งคืนแผนที่ของ RTCPeerConnection
สำหรับเพียร์ที่อยู่ในห้อง (ไม่รวมผู้ใช้ในเครื่อง) คีย์ของออบเจ็กต์นี้คือ ID ของเพียร์ที่เกี่ยวข้อง
addStream(stream, [targetPeers], [metadata])
ออกอากาศสื่อสตรีมไปยังเพื่อนคนอื่นๆ
stream
- MediaStream
พร้อมเสียงและ/หรือวิดีโอเพื่อส่งให้เพื่อนในห้อง
targetPeers
- (ไม่บังคับ) หากระบุไว้ สตรีมจะถูกส่งไปยัง ID เพียร์เป้าหมาย (สตริง) หรือรายการ ID เพียร์ (อาร์เรย์) เท่านั้น
metadata
- (ไม่บังคับ) ข้อมูลเมตาเพิ่มเติม (ประเภทที่สามารถซีเรียลไลซ์ได้) ที่จะส่งพร้อมกับสตรีม สิ่งนี้มีประโยชน์เมื่อส่งสตรีมหลายรายการ เพื่อให้ผู้รับรู้ว่าอันไหน (เช่น เว็บแคม กับ การจับภาพหน้าจอ) หากคุณต้องการออกอากาศสตรีมไปยังเพื่อนทุกคนในห้องด้วยอาร์กิวเมนต์ข้อมูลเมตา ให้ส่งค่า null
เป็นอาร์กิวเมนต์ที่สอง
removeStream(stream, [targetPeers])
หยุดส่งสตรีมสื่อที่ส่งก่อนหน้านี้ไปยังเพื่อนคนอื่นๆ
stream
- MediaStream
ที่ส่งไปก่อนหน้านี้เพื่อหยุดการส่ง
targetPeers
- (เป็นทางเลือก) หากระบุไว้ สตรีมจะถูกลบออกจาก ID เพียร์เป้าหมาย (สตริง) หรือรายการ ID เพียร์ (อาร์เรย์) เท่านั้น
addTrack(track, stream, [targetPeers], [metadata])
เพิ่มแทร็กสื่อใหม่ให้กับสตรีม
track
- MediaStreamTrack
เพื่อเพิ่มลงในสตรีมที่มีอยู่
stream
- เป้าหมาย MediaStream
ที่จะแนบแทร็กใหม่
targetPeers
- (ไม่บังคับ) หากระบุ แทร็กจะถูกส่งไปยัง ID เพียร์เป้าหมาย (สตริง) หรือรายการ ID เพียร์ (อาร์เรย์) เท่านั้น
metadata
- (ไม่บังคับ) ข้อมูลเมตาเพิ่มเติม (ประเภทที่สามารถซีเรียลไลซ์ได้) ที่จะส่งพร้อมกับแทร็ก ดูบันทึก metadata
สำหรับ addStream()
ด้านบนสำหรับรายละเอียดเพิ่มเติม
removeTrack(track, stream, [targetPeers])
ลบแทร็กสื่อออกจากสตรีม
track
- MediaStreamTrack
ที่จะลบ
stream
- MediaStream
ที่แนบแทร็กอยู่
targetPeers
- (ไม่บังคับ) หากระบุไว้ แทร็กจะถูกลบออกจาก ID เพียร์เป้าหมาย (สตริง) หรือรายการ ID เพียร์ (อาร์เรย์) เท่านั้น
replaceTrack(oldTrack, newTrack, stream, [targetPeers])
แทนที่แทร็กสื่อด้วยแทร็กใหม่
oldTrack
- MediaStreamTrack
ที่จะลบ
newTrack
- MediaStreamTrack
ที่จะแนบ
stream
- MediaStream
ที่แนบ oldTrack
ไว้
targetPeers
- (เป็นทางเลือก) หากระบุไว้ แทร็กจะถูกแทนที่เฉพาะสำหรับ ID เพียร์เป้าหมาย (สตริง) หรือรายการ ID เพียร์ (อาร์เรย์)
onPeerJoin(callback)
ลงทะเบียนฟังก์ชันการโทรกลับที่จะถูกเรียกเมื่อเพื่อนเข้าร่วมห้อง หากมีการโทรมากกว่าหนึ่งครั้ง จะมีการโทรเฉพาะการโทรกลับล่าสุดที่ลงทะเบียนไว้เท่านั้น
callback(peerId)
- ฟังก์ชั่นให้ทำงานเมื่อใดก็ตามที่เพียร์เข้าร่วม เรียกด้วย ID ของเพียร์ตัวอย่าง:
onPeerJoin ( peerId => console . log ( ` ${ peerId } joined` ) )
onPeerLeave(callback)
ลงทะเบียนฟังก์ชันการโทรกลับที่จะถูกเรียกเมื่อเพื่อนออกจากห้อง หากมีการโทรมากกว่าหนึ่งครั้ง จะมีการโทรเฉพาะการโทรกลับล่าสุดที่ลงทะเบียนไว้เท่านั้น
callback(peerId)
- ฟังก์ชั่นให้ทำงานทุกครั้งที่เพียร์ออก โดยถูกเรียกด้วย ID ของเพียร์ตัวอย่าง:
onPeerLeave ( peerId => console . log ( ` ${ peerId } left` ) )
onPeerStream(callback)
ลงทะเบียนฟังก์ชันการโทรกลับที่จะถูกเรียกเมื่อเพียร์ส่งสตรีมสื่อ หากมีการโทรมากกว่าหนึ่งครั้ง จะมีการโทรเฉพาะการโทรกลับล่าสุดที่ลงทะเบียนไว้เท่านั้น
callback(stream, peerId, metadata)
- ฟังก์ชั่นให้ทำงานทุกครั้งที่เพียร์ส่งสตรีมสื่อ เรียกด้วยสตรีมของเพียร์ ID และข้อมูลเมตาเสริม (ดู addStream()
ด้านบนสำหรับรายละเอียด)ตัวอย่าง:
onPeerStream ( ( stream , peerId ) =>
console . log ( `got stream from ${ peerId } ` , stream )
)
onPeerTrack(callback)
ลงทะเบียนฟังก์ชันการโทรกลับที่จะถูกเรียกเมื่อเพียร์ส่งแทร็กมีเดีย หากมีการโทรมากกว่าหนึ่งครั้ง จะมีการโทรเฉพาะการโทรกลับล่าสุดที่ลงทะเบียนไว้เท่านั้น
callback(track, stream, peerId, metadata)
- ฟังก์ชั่นให้ทำงานทุกครั้งที่เพียร์ส่งแทร็กมีเดีย เรียกพร้อมกับแทร็กของเพียร์ สตรีมที่แนบ ID และข้อมูลเมตาเสริม (ดู addTrack()
ด้านบนสำหรับรายละเอียด)ตัวอย่าง:
onPeerTrack ( ( track , stream , peerId ) =>
console . log ( `got track from ${ peerId } ` , track )
)
makeAction(actionId)
รับฟังและส่งการดำเนินการกับข้อมูลที่กำหนดเอง
actionId
- สตริงสำหรับลงทะเบียนการดำเนินการนี้อย่างสม่ำเสมอในหมู่เพียร์ทั้งหมดส่งกลับอาร์เรย์ของฟังก์ชันสามรายการ:
ส่งข้อมูลไปยังเพียร์และส่งคืนสัญญาที่จะแก้ไขเมื่อเพียร์เป้าหมายทั้งหมดรับข้อมูลเสร็จแล้ว
(data, [targetPeers], [metadata], [onProgress])
data
- ค่าใด ๆ ที่จะส่ง (ดั้งเดิม, วัตถุ, ไบนารี) การจัดลำดับและการแบ่งส่วนได้รับการจัดการโดยอัตโนมัติ ข้อมูลไบนารี (เช่น Blob
, TypedArray
) ได้รับจากเพียร์อื่นในฐานะ ArrayBuffer
ที่ไม่เชื่อเรื่องพระเจ้า
targetPeers
- (ไม่บังคับ) ไม่ว่าจะเป็นรหัสเพียร์ (สตริง) อาร์เรย์ของรหัสเพียร์ หรือ null
(ระบุว่าจะส่งให้เพื่อนทั้งหมดในห้อง)
metadata
- (เป็นทางเลือก) หากข้อมูลเป็นแบบไบนารี คุณสามารถส่งออบเจ็กต์ข้อมูลเมตาเผื่อเลือกเพื่ออธิบายข้อมูลนั้นได้ (ดูข้อมูลเมตาไบนารี)
onProgress
- (เป็นทางเลือก) ฟังก์ชั่นการโทรกลับที่จะถูกเรียกเมื่อทุก ๆ ก้อนของเพียร์ทุกคนถูกส่ง ฟังก์ชันนี้จะถูกเรียกด้วยค่าระหว่าง 0 ถึง 1 และรหัสเพียร์ ดูตัวอย่างการอัปเดตความคืบหน้า
ลงทะเบียนฟังก์ชันการโทรกลับที่ทำงานเมื่อได้รับข้อมูลสำหรับการดำเนินการนี้จากเพียร์อื่น
(data, peerId, metadata)
data
- ค่าที่ส่งโดยเพียร์ผู้ส่ง การดีซีเรียลไลซ์จะถูกจัดการโดยอัตโนมัติ เช่น จะได้รับตัวเลขเป็นตัวเลข วัตถุเป็นวัตถุ ฯลฯ
peerId
- สตริง ID ของเพียร์ที่ส่ง
metadata
- (เป็นทางเลือก) ออบเจ็กต์ข้อมูลเมตาเสริมที่ผู้ส่งจัดหาให้ หาก data
เป็นแบบไบนารี เช่น ชื่อไฟล์
ลงทะเบียนฟังก์ชันการโทรกลับที่ทำงานเมื่อได้รับข้อมูลบางส่วนจากเพียร์ คุณสามารถใช้สิ่งนี้เพื่อติดตามการถ่ายโอนไบนารี่ขนาดใหญ่ ดูตัวอย่างการอัปเดตความคืบหน้า
(percent, peerId, metadata)
percent
- ตัวเลขระหว่าง 0 ถึง 1 ระบุเปอร์เซ็นต์การโอนเสร็จสมบูรณ์
peerId
- สตริง ID ของเพียร์ที่ส่ง
metadata
- (เป็นทางเลือก) ออบเจ็กต์ข้อมูลเมตาเสริมที่จัดทำโดยผู้ส่ง
ตัวอย่าง:
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)
รับ ID เพียร์และส่งกลับสัญญาที่แก้ไขเป็นมิลลิวินาทีที่การเดินทางไปกลับไปยังเพียร์นั้น ใช้สิ่งนี้เพื่อวัดเวลาแฝง
peerId
- สตริง ID เพียร์ของเพียร์เป้าหมายตัวอย่าง:
// log round-trip time every 2 seconds
room . onPeerJoin ( peerId =>
setInterval (
async ( ) => console . log ( `took ${ await room . ping ( peerId ) } ms` ) ,
2000
)
)
selfId
สตริง ID ที่ไม่ซ้ำที่เพื่อนคนอื่นๆ จะรู้จักผู้ใช้ภายในเครื่องจากทั่วโลกในห้องต่างๆ
getRelaySockets()
(? BitTorrent, ? Nostr, ? MQTT เท่านั้น) ส่งคืนออบเจ็กต์ของคีย์ URL รีเลย์ที่แมปกับการเชื่อมต่อ WebSocket สิ่งนี้มีประโยชน์ในการกำหนดสถานะการเชื่อมต่อของผู้ใช้กับรีเลย์ และการจัดการความล้มเหลวในการเชื่อมต่อ
ตัวอย่าง:
console . log ( trystero . getRelaySockets ( ) )
// => Object {
// "wss://tracker.webtorrent.dev": WebSocket,
// "wss://tracker.openwebtorrent.com": WebSocket
// }
getOccupants(config, roomId)
( Firebase เท่านั้น) ส่งคืนสัญญาที่แก้ไขรายการรหัสผู้ใช้ที่มีอยู่ในเนมสเปซที่กำหนด สิ่งนี้มีประโยชน์ในการตรวจสอบว่ามีผู้ใช้กี่คนในห้องโดยไม่ต้องเข้าร่วม
config
- วัตถุการกำหนดค่าroomId
- สตริงเนมสเปซที่คุณจะส่งไปยัง joinRoom()
ตัวอย่าง:
console . log ( ( await trystero . getOccupants ( config , 'the_scope' ) ) . length )
// => 3
การตั้งค่าเพียงครั้งเดียว¹ | ขนาดมัด² | ถึงเวลาเชื่อมต่อ³ | |
---|---|---|---|
- นสท | ไม่มี ? | 54K | |
- มคต | ไม่มี ? | 332K | |
- บิตทอร์เรนต์ | ไม่มี ? | 25K ? | |
⚡️ ซูปาเบส | ~5 นาที | 150K | ? |
ฐานไฟ | ~5 นาที | 177K | ? |
- IPFS | ไม่มี ? | 945K |
¹ กลยุทธ์ทั้งหมดยกเว้น Firebase ไม่จำเป็นต้องตั้งค่าใดๆ Firebase เป็นกลยุทธ์ที่ได้รับการจัดการซึ่งต้องมีการตั้งค่าบัญชี
² คำนวณผ่านการรวมกลุ่ม Rollup + การบีบอัด Terser
³ ความเร็วสัมพัทธ์ของผู้ร่วมงานที่เชื่อมต่อถึงกันเมื่อเข้าร่วมห้อง Firebase เกือบจะเกิดขึ้นทันที ในขณะที่กลยุทธ์อื่นๆ จะช้ากว่าเล็กน้อยในการแลกเปลี่ยนข้อมูลการเพียร์
ข้อได้เปรียบเฉพาะของ trystero คือไม่จำเป็นต้องตั้งค่าแบ็กเอนด์และใช้โครงสร้างพื้นฐานแบบกระจายอำนาจในกรณีส่วนใหญ่ ซึ่งช่วยให้การทดลองเป็นไปอย่างราบรื่นและไม่มีจุดล้มเหลวแม้แต่จุดเดียว ข้อเสียเปรียบประการหนึ่งที่อาจเกิดขึ้นคือ เป็นการยากที่จะรับประกันว่าโครงสร้างพื้นฐานสาธารณะที่ใช้จะมีความพร้อมใช้งานสูงอยู่เสมอ แม้ว่าจะมีเทคนิคการสำรอง trystero ก็ตาม แม้ว่ากลยุทธ์อื่นๆ จะได้รับการกระจายอำนาจ แต่กลยุทธ์ Supabase และ Firebase นั้นเป็นแนวทางที่มีการจัดการมากกว่าพร้อมการควบคุมที่มากกว่าและมี SLA ซึ่งอาจเหมาะสมกว่าสำหรับแอป "ที่ใช้งานจริง"
trystero ทำให้การสลับระหว่างกลยุทธ์เป็นเรื่องเล็กน้อย — เพียงเปลี่ยนบรรทัดการนำเข้าเพียงบรรทัดเดียวและทดลองอย่างรวดเร็ว:
import { joinRoom } from ' trystero /[torrent|nostr|mqtt|supabase|firebase|ipfs]'
trystero โดย Dan Motzenbecker