Spaces:
Running
Running
Download net.js from CyberMax-tools/hushfox: direct link, hf CLI and curl.
- Browser
- Download file 2.67 kB
-
https://huggingface.co/spaces/CyberMax-tools/hushfox/resolve/main/net.js
- Command line
-
hf download hf://spaces/CyberMax-tools/hushfox/net.js
-
curl -L -o net.js https://huggingface.co/spaces/CyberMax-tools/hushfox/resolve/main/net.js
2.67 kB
| // Hushfox room client: one WebSocket to the room's Durable Object, auto-reconnect with the same player id (the seat | |
| // and score survive a locked phone for 15 s), server clock offset for the shared timer. (c) 2026 CyberMax. | |
| export const API_DEFAULT = 'https://hushfox.cybermaxtools.com'; | |
| /** The rooms API base. Local test pages may pass ?api=http://127.0.0.1:PORT (only honoured on localhost). */ | |
| export function apiBase() { | |
| try { | |
| const q = new URLSearchParams(location.search).get('api'); | |
| if (q && /^(localhost|127\.0\.0\.1)$/.test(location.hostname) && /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(q)) return q; | |
| } catch { /* */ } | |
| return window.HUSHFOX_API || API_DEFAULT; | |
| } | |
| export async function createRoom(api = apiBase()) { | |
| const r = await fetch(`${api}/api/rooms`, { method: 'POST' }); | |
| if (!r.ok) throw new Error('Could not create a room'); | |
| return (await r.json()).code; | |
| } | |
| export async function roomInfo(code, api = apiBase()) { | |
| const r = await fetch(`${api}/api/rooms/${encodeURIComponent(code)}`); | |
| return r.json(); | |
| } | |
| /** Connect to a room. handlers: { state(s), error(msg), status('open'|'retry'|'gone') }. Returns { send, close }. */ | |
| export function connect(code, pid, name, handlers, api = apiBase()) { | |
| let ws = null, closed = false, tries = 0, timer = 0, ping = 0; | |
| const open = () => { | |
| ws = new WebSocket(`${api.replace(/^http/, 'ws')}/ws/${code}`); | |
| ws.addEventListener('open', () => { | |
| tries = 0; | |
| ws.send(JSON.stringify({ t: 'hello', pid, name })); | |
| handlers.status && handlers.status('open'); | |
| clearInterval(ping); | |
| ping = setInterval(() => { try { ws.send('{"t":"ping"}'); } catch { /* */ } }, 25000); | |
| }); | |
| ws.addEventListener('message', (e) => { | |
| let m; try { m = JSON.parse(e.data); } catch { return; } | |
| if (m.t === 'state') { m.s.offset = m.s.now - Date.now(); handlers.state(m.s); } | |
| else if (m.t === 'error') handlers.error && handlers.error(m.error); | |
| else if (m.t === 'gone') { closed = true; handlers.status && handlers.status('gone'); } | |
| }); | |
| ws.addEventListener('close', (e) => { | |
| clearInterval(ping); | |
| if (closed || e.code === 4000 || e.code === 4001) { if (e.code !== 4000) handlers.status && handlers.status('gone'); return; } | |
| handlers.status && handlers.status('retry'); | |
| tries++; | |
| timer = setTimeout(open, Math.min(8000, 400 * 2 ** Math.min(tries, 5))); | |
| }); | |
| }; | |
| open(); | |
| return { | |
| send(o) { if (ws && ws.readyState === 1) { ws.send(JSON.stringify(o)); return true; } return false; }, | |
| close() { closed = true; clearTimeout(timer); clearInterval(ping); try { ws.close(); } catch { /* */ } }, | |
| }; | |
| } | |