PUBG drop map: live squad map with WebSocket rooms
All checks were successful
Deploy production / deploy (push) Successful in 1m15s
All checks were successful
Deploy production / deploy (push) Successful in 1m15s
- server.js wraps Bun.serve to add /ws/<channel> upgrades (same server and port) and serve the map tiles from the host mount; Dockerfile now runs bun ./server.js - ws hub (channel registry, origin check) + pubg-map channel: rooms by unguessable code, validated + rate-limited messages, per-room write queue, presence - games.pubg_rooms / pubg_matches / pubg_markers; per-match sessions with history - /trackers/games/pubg/map hub (owner: rooms, landings over time, history) and /trackers/games/pubg/map/<code> fullscreen Leaflet map (all 11 maps) - deploy-remote.sh bind-mounts ~/pubg-maps; scripts/build-pubg-tiles.py rebuilds it
This commit is contained in:
74
server.js
Normal file
74
server.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/* global Bun */
|
||||
// Production entry point: `bun ./server.js` instead of `bun ./build`. The adapter-bun build
|
||||
// starts Bun.serve itself without WebSocket handlers, so this wraps Bun.serve before loading
|
||||
// it and adds, on the same server and port:
|
||||
// /ws/<channel> WebSocket upgrades, handled by #lib/server/ws/hub (registered in
|
||||
// hooks.server.ts init, published on globalThis.__siteWs)
|
||||
// /pubg-tiles/… the PUBG map tiles, straight from the bind-mounted PUBG_TILES_ROOT
|
||||
// (immutable, so Cloudflare and browsers cache them; no session lookup)
|
||||
// Everything else goes to SvelteKit unchanged.
|
||||
import { resolve, sep } from 'node:path';
|
||||
|
||||
const TILE_RE = /^\/pubg-tiles\/([a-z-]{3,20})\/(?:(\d)\/(\d{1,2})\/(\d{1,2})|thumb)\.webp$/;
|
||||
const tilesRoot = resolve(process.env.PUBG_TILES_ROOT || '/pubg-maps');
|
||||
|
||||
function clientIp(request, server) {
|
||||
const h = request.headers;
|
||||
return (
|
||||
h.get('cf-connecting-ip') ||
|
||||
h.get('x-forwarded-for')?.split(',')[0]?.trim() ||
|
||||
server.requestIP(request)?.address ||
|
||||
'unknown'
|
||||
);
|
||||
}
|
||||
|
||||
async function tile(pathname) {
|
||||
const m = TILE_RE.exec(pathname);
|
||||
if (!m) return new Response('Not Found', { status: 404 });
|
||||
const rel = m[2] === undefined ? `${m[1]}/thumb.webp` : `${m[1]}/${m[2]}/${m[3]}/${m[4]}.webp`;
|
||||
const path = resolve(tilesRoot, rel);
|
||||
if (!path.startsWith(tilesRoot + sep)) return new Response('Not Found', { status: 404 });
|
||||
const file = Bun.file(path);
|
||||
if (!(await file.exists())) return new Response('Not Found', { status: 404 });
|
||||
return new Response(file, {
|
||||
headers: {
|
||||
'content-type': 'image/webp',
|
||||
'cache-control': 'public, max-age=31536000, immutable',
|
||||
'access-control-allow-origin': '*'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const serve = Bun.serve.bind(Bun);
|
||||
Bun.serve = (options) => {
|
||||
const app = options.fetch;
|
||||
options.fetch = async (request, server) => {
|
||||
const { pathname } = new URL(request.url);
|
||||
if (pathname.startsWith('/pubg-tiles/')) return tile(pathname);
|
||||
if (pathname.startsWith('/ws/')) {
|
||||
const hub = globalThis.__siteWs;
|
||||
if (!hub) return new Response('Service Unavailable', { status: 503 });
|
||||
if (request.headers.get('upgrade')?.toLowerCase() !== 'websocket')
|
||||
return new Response('Upgrade Required', { status: 426 });
|
||||
const data = await hub.authorize(request, new URL(request.url), clientIp(request, server));
|
||||
if (data instanceof Response) return data;
|
||||
if (server.upgrade(request, { data })) return undefined;
|
||||
return new Response('Upgrade Required', { status: 426 });
|
||||
}
|
||||
return app(request, server);
|
||||
};
|
||||
options.websocket = {
|
||||
// Messages are small JSON; Bun pings idle sockets so proxies keep them open.
|
||||
maxPayloadLength: 16 * 1024,
|
||||
idleTimeout: 60,
|
||||
sendPings: true,
|
||||
backpressureLimit: 1024 * 1024,
|
||||
closeOnBackpressureLimit: true,
|
||||
open: (ws) => globalThis.__siteWs?.open(ws),
|
||||
message: (ws, message) => globalThis.__siteWs?.message(ws, message),
|
||||
close: (ws) => globalThis.__siteWs?.close(ws)
|
||||
};
|
||||
return serve(options);
|
||||
};
|
||||
|
||||
await import('./build/index.js');
|
||||
Reference in New Issue
Block a user