Initial commit — Pokemon Doors Randomiser
This commit is contained in:
+104
@@ -0,0 +1,104 @@
|
||||
import express from 'express'
|
||||
import http from 'http'
|
||||
import { WebSocketServer } from 'ws'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const app = express()
|
||||
const server = http.createServer(app)
|
||||
const PORT = process.env.PORT || 3001
|
||||
|
||||
const MAPS_DIR = path.join(__dirname, '..', 'maps')
|
||||
const DOORS_FILE = path.join(__dirname, '..', 'src', 'data', 'door-positions.json')
|
||||
const BASEMAP_FILE = path.join(__dirname, '..', 'src', 'data', 'basemap.json')
|
||||
const SIZES_FILE = path.join(__dirname, '..', 'src', 'data', 'room-sizes.json')
|
||||
|
||||
// ── WebSocket (admin collab) ────────────────────────────────────────────────
|
||||
const wss = new WebSocketServer({ server, path: '/ws/admin' })
|
||||
const adminClients = new Set()
|
||||
|
||||
function readJSON(file, fallback) {
|
||||
try { return JSON.parse(fs.readFileSync(file, 'utf8')) } catch { return fallback }
|
||||
}
|
||||
|
||||
function broadcast(sender, msg) {
|
||||
const str = JSON.stringify(msg)
|
||||
for (const client of adminClients) {
|
||||
if (client !== sender && client.readyState === 1) client.send(str)
|
||||
}
|
||||
}
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
adminClients.add(ws)
|
||||
ws.send(JSON.stringify({
|
||||
type: 'init',
|
||||
doors: readJSON(DOORS_FILE, {}),
|
||||
basemap: readJSON(BASEMAP_FILE, []),
|
||||
sizes: readJSON(SIZES_FILE, {}),
|
||||
}))
|
||||
|
||||
ws.on('message', (raw) => {
|
||||
try {
|
||||
const msg = JSON.parse(raw)
|
||||
if (msg.type === 'doors-save') {
|
||||
fs.writeFileSync(DOORS_FILE, JSON.stringify(msg.data, null, 2))
|
||||
broadcast(ws, { type: 'doors-update', data: msg.data })
|
||||
}
|
||||
if (msg.type === 'basemap-save') {
|
||||
fs.writeFileSync(BASEMAP_FILE, JSON.stringify(msg.data, null, 2))
|
||||
broadcast(ws, { type: 'basemap-update', data: msg.data })
|
||||
}
|
||||
if (msg.type === 'sizes-save') {
|
||||
fs.writeFileSync(SIZES_FILE, JSON.stringify(msg.data, null, 2))
|
||||
broadcast(ws, { type: 'sizes-update', data: msg.data })
|
||||
}
|
||||
if (msg.type === 'basemap-position-save') {
|
||||
const current = readJSON(BASEMAP_FILE, [])
|
||||
const next = current.map(entry => entry.id === msg.id ? { ...entry, position: msg.position } : entry)
|
||||
fs.writeFileSync(BASEMAP_FILE, JSON.stringify(next, null, 2))
|
||||
broadcast(ws, { type: 'basemap-update', data: next })
|
||||
}
|
||||
} catch {}
|
||||
})
|
||||
|
||||
ws.on('close', () => adminClients.delete(ws))
|
||||
})
|
||||
|
||||
// ── REST ────────────────────────────────────────────────────────────────────
|
||||
function formatName(f) {
|
||||
return f.replace(/_/g,' ').replace(/\b\w/g,c=>c.toUpperCase())
|
||||
.replace(/\b1f\b/gi,'1F').replace(/\b2f\b/gi,'2F')
|
||||
.replace(/\bB1f\b/gi,'B1F').replace(/\bB2f\b/gi,'B2F')
|
||||
.replace(/\bRt\b/g,'Route').replace(/\bPokecenter\b/g,'Pokémon Center')
|
||||
}
|
||||
|
||||
function scanRooms(dir, rooms = []) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name)
|
||||
if (entry.isDirectory()) scanRooms(full, rooms)
|
||||
else if (entry.name.toLowerCase().endsWith('.png')) {
|
||||
const base = path.basename(entry.name, path.extname(entry.name)).toLowerCase()
|
||||
const rel = path.relative(MAPS_DIR, full).replace(/\\/g, '/')
|
||||
const cat = rel.split('/').slice(-2,-1)[0] ?? 'other'
|
||||
rooms.push({ id: base, name: formatName(base), image: `/maps/${rel}`, category: cat })
|
||||
}
|
||||
}
|
||||
return rooms
|
||||
}
|
||||
|
||||
app.get('/api/rooms', (_, res) => { try { res.json(scanRooms(MAPS_DIR)) } catch(e) { res.status(500).json({error:e.message}) } })
|
||||
app.get('/api/doors', (_, res) => res.json(readJSON(DOORS_FILE, {})))
|
||||
app.get('/api/basemap', (_, res) => res.json(readJSON(BASEMAP_FILE, [])))
|
||||
app.get('/api/room-sizes', (_, res) => res.json(readJSON(SIZES_FILE, {})))
|
||||
|
||||
app.post('/api/doors', express.json(), (req, res) => { try { fs.writeFileSync(DOORS_FILE, JSON.stringify(req.body,null,2)); res.json({ok:true}) } catch(e){res.status(500).json({error:e.message})} })
|
||||
app.post('/api/basemap', express.json(), (req, res) => { try { fs.writeFileSync(BASEMAP_FILE, JSON.stringify(req.body,null,2)); res.json({ok:true}) } catch(e){res.status(500).json({error:e.message})} })
|
||||
app.post('/api/room-sizes', express.json(), (req, res) => { try { fs.writeFileSync(SIZES_FILE, JSON.stringify(req.body,null,2)); res.json({ok:true}) } catch(e){res.status(500).json({error:e.message})} })
|
||||
|
||||
app.use('/maps', express.static(MAPS_DIR))
|
||||
app.use(express.static(path.join(__dirname, '..', 'dist')))
|
||||
app.get('/{*path}', (_, res) => res.sendFile(path.join(__dirname, '..', 'dist', 'index.html')))
|
||||
|
||||
server.listen(PORT, () => console.log(`Pokemon Doors running on http://localhost:${PORT}`))
|
||||
Reference in New Issue
Block a user