import {del, get, set} from 'idb-keyval'; import {encode} from '@/net/packets/index.js'; import Server from '@/net/server.js'; import {withResolvers} from '@/util/promise.js'; import {readAsset} from '@/util/resources.js'; import createEcs from './create/ecs.js'; import './create/forest.js'; import './create/homestead.js'; import './create/player.js'; import './create/town.js'; import Engine from './engine.js'; class WorkerServer extends Server { constructor() { super(); this.fs = {}; } static qualify(path) { return ['UNIVERSE', path].join('/'); } async readAsset(path) { const resource = await readAsset(path); return resource ? resource : new ArrayBuffer(0); } async readData(path) { const data = await get(this.constructor.qualify(path)); if ('undefined' !== typeof data) { return data; } const error = new Error(); error.code = 'ENOENT'; throw error; } async removeData(path) { await del(this.constructor.qualify(path)); } async writeData(path, view) { await set(this.constructor.qualify(path), view); } transmit(connection, packed) { postMessage(packed); } } const engine = new Engine(WorkerServer); onmessage = async (event) => { if (0 === event.data) { await engine.disconnectPlayer(0); engine.stop(); await engine.saveEcses(); postMessage(0); return; } engine.server.accept(0, event.data); }; (async () => { await engine.load(); engine.start(); await engine.connectPlayer(0, 0); postMessage(encode({type: 'ConnectionStatus', payload: 'connected'})); })(); if (import.meta.hot) { const before = withResolvers(); const promises = [before.promise]; const accepted = [ '/app/server/engine.js', '/app/server/create/player.js', '/app/server/create/forest.js', '/app/server/create/town.js', '/app/server/create/homestead.js', ]; let isAccepted = false; import.meta.hot.on('vite:beforeUpdate', async ({updates}) => { isAccepted = !!updates.find(({acceptedPath}) => accepted.includes(acceptedPath)); if (!isAccepted) { return; } await engine.disconnectPlayer(0); engine.stop(); await engine.saveEcses(); before.resolve(); }); import.meta.hot.accept('./engine.js'); import.meta.hot.accept('./create/player.js', async ({default: createPlayer}) => { const {promise, resolve} = withResolvers(); promises.push(promise); await before.promise; const oldPlayer = await engine.loadPlayer(0); const player = await createPlayer(0); // Less jarring player.Ecs = oldPlayer.Ecs; player.Direction = oldPlayer.Direction; player.Position = oldPlayer.Position; await engine.savePlayer(0, player); resolve(); }); import.meta.hot.accept('./create/forest.js', async ({default: createForest}) => { const {promise, resolve} = withResolvers(); promises.push(promise); await before.promise; delete engine.ecses['forests/0']; await engine.server.removeData('forests/0'); const forest = createEcs(engine.Ecs); for (const entity of await createForest()) { await forest.create(entity); } await engine.saveEcs('forests/0', forest); resolve(); }); import.meta.hot.accept('./create/homestead.js', async ({default: createHomestead}) => { const {promise, resolve} = withResolvers(); promises.push(promise); await before.promise; delete engine.ecses['homesteads/0']; await engine.server.removeData('homesteads/0'); const homestead = createEcs(engine.Ecs); for (const entity of await createHomestead('0')) { await homestead.create(entity); } await engine.saveEcs('homesteads/0', homestead); resolve(); }); import.meta.hot.accept('./create/town.js', async ({default: createTown}) => { const {promise, resolve} = withResolvers(); promises.push(promise); await before.promise; delete engine.ecses['town']; await engine.server.removeData('town'); const town = createEcs(engine.Ecs); for (const entity of await createTown()) { await town.create(entity); } await engine.saveEcs('town', town); resolve(); }); import.meta.hot.on('vite:afterUpdate', async () => { if (!isAccepted) { return; } await Promise.all(promises); postMessage(encode({type: 'ConnectionStatus', payload: 'aborted'})); postMessage(encode({type: 'EcsChange'})); close(); }); import.meta.hot.accept(); }