From e69ad01e700e80fa959dfee08cd556ca0d777ffd Mon Sep 17 00:00:00 2001 From: skylar Date: Sun, 1 Nov 2026 05:43:24 -0800 Subject: Yesod: self-hosted WebXR rooms Start a room in a world and hang out in VR from the headset's browser: A-Frame + Networked-A-Frame over EasyRTC, with a coturn relay that can hide everyone's IP address. Ships with the Campfire, Temple, Cabin, Balcony and Test room worlds. Co-Authored-By: Claude Opus 5.5 --- tools/portraits/render.html | 85 +++++++++++++++++++++++++++++++++++++++++++++ tools/portraits/server.ts | 27 ++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 tools/portraits/render.html create mode 100644 tools/portraits/server.ts (limited to 'tools/portraits') diff --git a/tools/portraits/render.html b/tools/portraits/render.html new file mode 100644 index 0000000..8e0a26e --- /dev/null +++ b/tools/portraits/render.html @@ -0,0 +1,85 @@ + + +render + + + diff --git a/tools/portraits/server.ts b/tools/portraits/server.ts new file mode 100644 index 0000000..8af80f5 --- /dev/null +++ b/tools/portraits/server.ts @@ -0,0 +1,27 @@ +// Renders avatar portraits for the lobby. Serves render.html plus the project's +// public files, and saves the PNGs the page POSTs back into ./out. +// +// deno run --allow-net=127.0.0.1:8097 --allow-read --allow-write=tools/portraits tools/portraits/server.ts +// then open http://localhost:8097/ (or ?slugs=name1,name2) and wait for the title "done". +const HERE = new URL('.', import.meta.url).pathname; +const PUBLIC = new URL('../../public', import.meta.url).pathname; +await Deno.mkdir(`${HERE}out`, { recursive: true }); + +const TYPES: Record = { html: 'text/html', js: 'text/javascript', vrm: 'application/octet-stream' }; + +Deno.serve({ port: 8097, hostname: '127.0.0.1' }, async (req) => { + const url = new URL(req.url); + if (url.pathname.includes('..')) return new Response('no', { status: 400 }); + if (req.method === 'POST' && url.pathname.startsWith('/save/')) { + const slug = url.pathname.slice(6).replace(/[^a-z0-9-]/g, ''); + await Deno.writeFile(`${HERE}out/${slug}.png`, new Uint8Array(await req.arrayBuffer())); + return new Response('ok'); + } + const file = url.pathname === '/' ? `${HERE}render.html` : PUBLIC + url.pathname; + try { + const ext = file.split('.').pop() || ''; + return new Response(await Deno.readFile(file), { headers: { 'content-type': TYPES[ext] || 'application/octet-stream' } }); + } catch { + return new Response('not found', { status: 404 }); + } +}); -- cgit v1.2.3