import {json} from "@remix-run/node"; import {useEffect, useState} from 'react'; import {useOutletContext, useParams} from 'react-router-dom'; import AssetsContext from '@/context/assets.js'; import ClientContext from '@/context/client.js'; import DebugContext from '@/context/debug.js'; import EcsContext from '@/context/ecs.js'; import MainEntityContext from '@/context/main-entity.js'; import Ecs from '@/ecs/ecs.js'; import Components from '@/ecs-components/index.js'; import Systems from '@/ecs-systems/index.js'; import Ui from '@/react-components/ui.jsx'; import {juggleSession} from '@/session.server'; import {LRUCache} from 'lru-cache'; export const cache = new LRUCache({ max: 128, }); class ClientEcs extends Ecs { readAsset(uri) { if (!cache.has(uri)) { let promise, resolve, reject; promise = new Promise((res, rej) => { resolve = res; reject = rej; }); cache.set(uri, promise); fetch(new URL(uri, window.location.origin)) .then(async (response) => { resolve(response.ok ? response.arrayBuffer() : new ArrayBuffer(0)); }) .catch(reject); } return cache.get(uri); } } export async function loader({request}) { await juggleSession(request); return json({}); } export default function PlaySpecific() { const Client = useOutletContext(); const assetsTuple = useState({}); const [client, setClient] = useState(); const mainEntityTuple = useState(); const debugTuple = useState(false); const ecsTuple = useState(new ClientEcs({Components, Systems})); const [disconnected, setDisconnected] = useState(false); const params = useParams(); const [type, url] = params['*'].split('/'); useEffect(() => { if (!Client) { return; } const client = new Client(); async function connect() { await client.connect(url); setClient(client); } connect(); return () => { client.disconnect(); }; }, [Client, url]); // Sneakily use beforeunload to snag some time to save. useEffect(() => { if ('local' !== type) { return; } async function onBeforeUnload() { client.disconnect(); function waitForSave() { return new Promise((resolve) => setTimeout(resolve, 0)); } while (client.worker) { await waitForSave(); } } addEventListener('beforeunload', onBeforeUnload); return () => { removeEventListener('beforeunload', onBeforeUnload); }; }); useEffect(() => { if (!client) { return; } function onConnectionStatus(status) { switch (status) { case 'aborted': { setDisconnected(true); break; } case 'connected': { setDisconnected(false); break; } } } client.addPacketListener('ConnectionStatus', onConnectionStatus); return () => { client.removePacketListener('ConnectionStatus', onConnectionStatus); }; }, [client]); useEffect(() => { if (!disconnected) { return; } async function reconnect() { await client.connect(url); } reconnect(); const handle = setInterval(reconnect, 1000); return () => { clearInterval(handle); }; }, [client, disconnected, url]); useEffect(() => { let source = true; async function play() { const ctx = new AudioContext(); const response = await fetch(new URL('/assets/yuff.wav', window.location.origin)); const buffer = await ctx.decodeAudioData(await response.arrayBuffer()); if (!source) { return; } source = ctx.createBufferSource(); source.buffer = buffer; source.connect(ctx.destination); source.loop = true; source.start(); } setTimeout(play, 1000); return () => { if (true !== source) { source.stop(); } source = false; }; }, []) return ( ); }