silphius/app/server/websocket.js
2024-09-21 06:24:26 -05:00

106 lines
3.1 KiB
JavaScript

import {mkdir, readFile, unlink, writeFile} from 'node:fs/promises';
import {dirname, join} from 'node:path';
import {WebSocketServer} from 'ws';
import Server from '@/net/server.js';
import {getSession} from '@/server/session.server.js';
import Engine from './engine.js';
const {
RESOURCES_PATH = [process.cwd(), 'resources'].join('/'),
} = process.env;
global.__silphiusWebsocket = null;
class SocketServer extends Server {
async ensurePath(path) {
await mkdir(path, {recursive: true});
}
static qualify(path) {
return join(import.meta.dirname, '..', '..', 'data', 'remote', 'UNIVERSE', path);
}
async readAsset(path) {
const {pathname} = new URL(path, 'http://example.org');
const resourcePath = pathname.slice('/resources/'.length);
try {
const {buffer} = await readFile([RESOURCES_PATH, resourcePath].join('/'));
return buffer;
}
catch (error) {
if ('ENOENT' !== error.code) {
throw error;
}
return new ArrayBuffer(0);
}
}
async readData(path) {
const qualified = this.constructor.qualify(path);
await this.ensurePath(dirname(qualified));
return readFile(qualified);
}
async removeData(path) {
await unlink(path);
}
async writeData(path, view) {
const qualified = this.constructor.qualify(path);
await this.ensurePath(dirname(qualified));
await writeFile(qualified, view);
}
transmit(ws, packed) { ws.send(packed); }
}
export async function handleUpgrade(request, socket, head) {
if (!global.__silphiusWebsocket) {
const engine = new Engine(SocketServer);
await engine.load();
engine.start();
const handleConnection = async (ws, request) => {
ws.on('close', async () => {
await engine.disconnectPlayer(ws);
})
ws.on('message', (packed) => {
engine.server.accept(ws, new DataView(packed.buffer, packed.byteOffset, packed.length));
});
const session = await getSession(request.headers['cookie']);
await engine.connectPlayer(ws, session.get('id'));
};
const wss = new WebSocketServer({
noServer: true,
});
wss.on('connection', handleConnection);
global.__silphiusWebsocket = {engine, handleConnection, wss};
}
const {pathname} = new URL(request.url, 'wss://base.url');
if (pathname === '/ws') {
const {wss} = global.__silphiusWebsocket;
wss.handleUpgrade(request, socket, head, function done(ws) {
wss.emit('connection', ws, request);
});
}
else {
socket.destroy();
}
}
if (import.meta.hot) {
import.meta.hot.on('vite:beforeUpdate', async () => {
if (global.__silphiusWebsocket) {
const {engine, handleConnection, wss} = global.__silphiusWebsocket;
wss.off('connection', handleConnection);
const connections = [];
for (const [connection] of engine.connectedPlayers) {
engine.server.send(connection, {type: 'EcsChange'});
connections.push(connection);
}
await engine.stop();
for (const connection of connections) {
connection.close();
}
global.__silphiusWebsocket = null;
}
});
import.meta.hot.accept();
}