52 lines
2.2 KiB
JavaScript
52 lines
2.2 KiB
JavaScript
'use strict';
|
|
|
|
const assert=require('assert/strict');
|
|
|
|
async function connectRealtime(base,session,{timeout=4000}={}){
|
|
assert(session?.playerId&&session?.token,'Realtime session is required');
|
|
const url=base.replace(/^http/,'ws')+'/api/realtime';
|
|
const socket=new WebSocket(url);
|
|
const queue=[];
|
|
const waiters=[];
|
|
let closed=false;
|
|
function dispatch(message){
|
|
for(let index=0;index<waiters.length;index++){
|
|
const waiter=waiters[index];
|
|
if(waiter.predicate(message)){
|
|
waiters.splice(index,1);clearTimeout(waiter.timer);waiter.resolve(message);return;
|
|
}
|
|
}
|
|
queue.push(message);
|
|
}
|
|
socket.addEventListener('message',event=>{
|
|
try{dispatch(JSON.parse(String(event.data)))}catch(error){dispatch({type:'test-parse-error',error})}
|
|
});
|
|
socket.addEventListener('close',()=>{closed=true;for(const waiter of waiters.splice(0)){clearTimeout(waiter.timer);waiter.reject(new Error('Realtime socket closed'))}});
|
|
await new Promise((resolve,reject)=>{
|
|
const timer=setTimeout(()=>reject(new Error('Realtime socket open timed out')),timeout);
|
|
socket.addEventListener('open',()=>{clearTimeout(timer);resolve()},{once:true});
|
|
socket.addEventListener('error',()=>{clearTimeout(timer);reject(new Error('Realtime socket failed'))},{once:true});
|
|
});
|
|
socket.send(JSON.stringify({type:'hello',playerId:session.playerId,token:session.token}));
|
|
async function waitFor(predicateOrType,waitTimeout=timeout){
|
|
const predicate=typeof predicateOrType==='function'?predicateOrType:message=>message?.type===predicateOrType;
|
|
const found=queue.findIndex(predicate);
|
|
if(found>=0)return queue.splice(found,1)[0];
|
|
if(closed)throw new Error('Realtime socket is closed');
|
|
return new Promise((resolve,reject)=>{
|
|
const waiter={predicate,resolve,reject,timer:null};
|
|
waiter.timer=setTimeout(()=>{const index=waiters.indexOf(waiter);if(index>=0)waiters.splice(index,1);reject(new Error('Realtime message timed out'))},waitTimeout);
|
|
waiters.push(waiter);
|
|
});
|
|
}
|
|
const ready=await waitFor('ready');
|
|
return{
|
|
socket,ready,
|
|
send(message){socket.send(JSON.stringify(message))},
|
|
waitFor,
|
|
close(){try{socket.close()}catch(_){}},
|
|
queued:queue
|
|
};
|
|
}
|
|
|
|
module.exports={connectRealtime};
|