- Treffer-Toleranz: Ziel wird geometrisch + bevorzugt getroffen, auch wenn verdeckt - Zwei Ranglisten: Hall of Fame (Allzeit-Top-5) + Heute (Top-10) - Anti-Cheat: server-autoritative Sessions (start/hit), Score = Server-Zahl, Mindestabstand gegen Auto-Klicker, Deckelung nach Spielzeit; Score ohne Session -> 400 - CLI (server/admin.js): list / delete <name> / clear-today / clear-all - Zwei Betriebsmodi per CLI (live, ohne Neustart): public (/ = Spiel) und private (/ gesperrt, Spiel unter geheimem /pfad) + "not allowed to play"-Seite - Board-Icons von Emojis auf echte SVG-Icons, Board heisst "Hall of Fame" Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
44 lines
1.4 KiB
JavaScript
44 lines
1.4 KiB
JavaScript
// Betriebsmodus des Spiels, per CLI umschaltbar, persistent in data/mode.json.
|
|
// public -> "/" ist das Spiel (Default)
|
|
// private -> "/" zeigt "nicht erlaubt", Spiel nur unter "/<path>" erreichbar (zum Testen)
|
|
// Wird pro Request frisch gelesen -> CLI-Umschaltung wirkt SOFORT, ohne Serverneustart.
|
|
|
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const DATA_DIR = join(__dirname, '..', 'data');
|
|
const MODE_FILE = join(DATA_DIR, 'mode.json');
|
|
|
|
const DEFAULT = { mode: 'public', path: 'play' };
|
|
|
|
function sanitizeSlug(slug) {
|
|
const s = String(slug || '').trim().replace(/^\/+/, '').replace(/[^a-zA-Z0-9_-]/g, '');
|
|
return s || 'play';
|
|
}
|
|
|
|
export function getMode() {
|
|
try {
|
|
if (!existsSync(MODE_FILE)) return { ...DEFAULT };
|
|
const data = JSON.parse(readFileSync(MODE_FILE, 'utf8'));
|
|
return {
|
|
mode: data.mode === 'private' ? 'private' : 'public',
|
|
path: sanitizeSlug(data.path),
|
|
};
|
|
} catch {
|
|
return { ...DEFAULT };
|
|
}
|
|
}
|
|
|
|
export function setMode(mode, path) {
|
|
if (!existsSync(DATA_DIR)) mkdirSync(DATA_DIR, { recursive: true });
|
|
const current = getMode();
|
|
const next = {
|
|
mode: mode === 'private' ? 'private' : 'public',
|
|
path: sanitizeSlug(path || current.path),
|
|
};
|
|
writeFileSync(MODE_FILE, JSON.stringify(next, null, 2));
|
|
return next;
|
|
}
|