- Passwortgeschütztes Panel unter /admin (Auto-Passwort 20 Zeichen, per CLI abrufbar: `node server/admin.js password` / `password reset`), In-Memory-Sessions + Cookie, Login-Rate-Limit. - Spieler-Tracking: pro Score IP (CF/XFF/req.ip), Gerät/Browser (ua-parser-js), grober Ort (geoip-lite, offline) -> data/players.json; Aggregation pro Name. - Live-Tuning: configStore (data/config.json) für Timer + Schwierigkeitskurve; Server (makeRound) und Client (/api/config) lesen daraus -> Änderungen ohne Neustart. - Bild-Verwaltung: Upload/Löschen von Füßen & Deko (multer) + rescanLibrary(). - Panel-Aktionen: Spieler bannen/entbannen, Rangliste heute/alles leeren. - Fix: .item object-fit:contain -> nicht-quadratische Bilder werden nicht mehr verzerrt. - trust proxy für korrekte Client-IP hinter Reverse-Proxy/Cloudflare. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
131 lines
5.8 KiB
Markdown
131 lines
5.8 KiB
Markdown
# fuesse.sexy — „Wanted!" 🦶
|
||
|
||
Ein kleines Arcade-Spiel nach dem Vorbild des **„Wanted!"-Minispiels aus New Super Mario
|
||
Bros.**: Oben ein Fahndungsplakat mit *einem* gesuchten Fuß, unten wuseln viele Füße (in
|
||
verschiedenen Farben) + etwas Deko herum. **Schnapp dir den gesuchten Fuß, bevor die Zeit
|
||
abläuft** — dann kommt sofort ein neues Plakat mit einer anderen Farbe.
|
||
|
||
- **Zeitdruck:** Countdown; jeder Treffer gibt Zeit dazu, Fehlklick zieht Zeit ab. Bei 0 → Game-Over.
|
||
- **Wird schwerer:** erst ordentliches **Raster**, dann **wild verteilt**, dann **Bewegung**
|
||
(erst in 2–3 Gruppen, später einzeln) — und je weiter, desto **schneller** und **mehr** Füße.
|
||
- **Gesucht ist immer ein Fuß, aber die Farbe wechselt jede Runde** — nicht auf Autopilot klicken!
|
||
- **Rangliste:** die Top 10 auf der Startseite, serverseitig gespeichert. Nach dem Spiel
|
||
wird **einmal** nach dem Namen gefragt (danach gecacht).
|
||
|
||
## Installation
|
||
|
||
```sh
|
||
./install.sh
|
||
```
|
||
|
||
Das Skript installiert alles Nötige, startet den Server **im Hintergrund** und zeigt die
|
||
URL. Dann im Browser öffnen: **http://<server-ip>:3000** (lokal: http://localhost:3000).
|
||
|
||
Manuell: `npm install && npm start`.
|
||
|
||
## Die Füße & Deko erweitern
|
||
|
||
Alle Bildchen liegen in `library/`:
|
||
|
||
```
|
||
library/
|
||
feet/ ← die Füße (= die gesuchten Figuren). Farbe = Dateiname.
|
||
props/ ← Deko ("andere Bildchen") als Zusatz-Ablenker.
|
||
```
|
||
|
||
Neue Bildchen einfach in den passenden Ordner legen und **Server neu starten** — fertig.
|
||
Formate: `.svg`, `.png`, `.webp`, `.jpg`. Für Füße gilt: **mind. 2** in `library/feet/`
|
||
(mehr = mehr Farb-Vielfalt). Am besten freigestellt (transparenter Hintergrund), grob
|
||
quadratisch.
|
||
|
||
### Woher Bildchen nehmen?
|
||
openclipart.org (CC0), svgrepo.com (CC0-Filter), kenney.nl (CC0), pixabay.com. Bei
|
||
Flaticon/Freepik Namensnennung beachten.
|
||
|
||
## Feintuning
|
||
|
||
In `public/js/app.js` oben:
|
||
- `START_TIME`, `MAX_TIME`, `BONUS`, `PENALTY` — Timer/Balance.
|
||
- `difficulty(score)` — Anzahl, Bewegungs-Stufen (`grid`/`scatter`/`groups`/`individual`)
|
||
und Tempo je Score.
|
||
|
||
## Rangliste
|
||
|
||
- Zwei Boards: **Hall of Fame** (Allzeit-Top-5) + **Heute** (Top-10). Gespeichert in
|
||
`data/leaderboard.json` (wird automatisch angelegt, überlebt Neustarts).
|
||
- **Anti-Cheat:** **server-autoritative Runden.** Der Server bestimmt pro Runde das Ziel
|
||
und alle Items (mit zufälligen IDs) und validiert jeden Treffer gegen die Ziel-ID der
|
||
*aktuellen* Runde (`/api/game/hit` mit `nonce` + `itemId`). Blindes „Treffer-Spam"
|
||
bringt dadurch nichts mehr; zusätzlich Reaktionszeit-Untergrenze, Mindestabstand und
|
||
Deckelung nach echter Spielzeit. Beim Eintragen (`/api/score`) zählt **nur** die
|
||
server-gezählte Trefferzahl. Namen werden beim Anzeigen escaped (kein XSS).
|
||
> Hinweis: In einem Browserspiel bekommt der Client zwangsläufig die Renderdaten —
|
||
> ein eigens gebauter Solver-Bot bleibt theoretisch möglich, 100 % dicht geht nicht.
|
||
|
||
## Verwaltung per CLI
|
||
|
||
Auf dem Server (im Projektordner):
|
||
|
||
```sh
|
||
# Rangliste
|
||
node server/admin.js list # beide Boards anzeigen
|
||
node server/admin.js delete "Name" # einen bestimmten Spieler löschen
|
||
node server/admin.js clear-today # nur die heutige Rangliste leeren
|
||
node server/admin.js clear-all # komplette Rangliste leeren
|
||
|
||
# Bannen (namensbasiert, case-insensitiv)
|
||
node server/admin.js ban "Name" # sperren (+ vorhandene Einträge entfernen)
|
||
node server/admin.js unban "Name" # Sperre aufheben
|
||
node server/admin.js bans # gebannte Namen anzeigen
|
||
|
||
# Betriebsmodus (wirkt SOFORT, ohne Neustart)
|
||
node server/admin.js mode # aktuellen Modus anzeigen
|
||
node server/admin.js mode public # Spiel öffentlich unter "/"
|
||
node server/admin.js mode private geheim # "/" gesperrt, Spiel nur unter "/geheim"
|
||
|
||
# Admin-Panel-Passwort
|
||
node server/admin.js password # Passwort anzeigen (Login unter /admin)
|
||
node server/admin.js password reset # neues Passwort erzeugen
|
||
```
|
||
|
||
(Kurzform via npm: `npm run lb -- <befehl>`.)
|
||
|
||
- **public** (Default): `/` ist das Spiel.
|
||
- **private**: `/` zeigt „You are not allowed to play", das Spiel läuft nur unter dem
|
||
gewählten Pfad (`/<pfad>`) — praktisch zum Testen im Live-Betrieb.
|
||
|
||
## Admin-Panel (`/admin`)
|
||
|
||
Passwortgeschütztes Web-Panel. Passwort wird beim ersten Start automatisch generiert
|
||
(20 Zeichen) und liegt in `data/admin.json` — anzeigen mit `node server/admin.js password`.
|
||
|
||
Funktionen:
|
||
- **Spieler**: approximative Zuordnung (IP, grober Ort via `geoip-lite` offline, Gerät, Browser)
|
||
aus den Spiel-Logs; einzelne Spieler direkt **bannen/entbannen**.
|
||
- **Einstellungen**: Timer (Start/Max/Bonus/Strafe) **und** Schwierigkeitskurve (Objekt-Anzahl,
|
||
Stufen, Tempo) **live** einstellen — wirkt sofort beim nächsten Spielstart.
|
||
- **Bilder**: Füße & Deko **hochladen/löschen** (kein Neustart). Empfehlung: **quadratische**
|
||
Bilder, 256×256 oder 512×512 PNG (transparent) bzw. SVG mit quadratischem `viewBox`;
|
||
nicht-quadratische werden per `object-fit: contain` eingepasst statt verzerrt.
|
||
- **Rangliste**: heute/alles zurücksetzen.
|
||
|
||
> Datenschutz: IP + grober Ort + Gerät/Browser sind personenbezogen (nur für Moderation).
|
||
> Für den öffentlichen Betrieb einen kurzen Datenschutz-Hinweis ergänzen.
|
||
|
||
## Deployment (Ausblick)
|
||
|
||
Normaler Node-Server. Auf einen Node-Host bringen (VPS/LXC/Railway/Render), `./install.sh`,
|
||
Domain **fuesse.sexy** per Reverse-Proxy (z.B. Nginx Proxy Manager) darauf zeigen. Für
|
||
HTTPS hinter Cloudflare: DNS-Challenge (Cloudflare-Token) im Proxy nutzen.
|
||
|
||
## Projektstruktur
|
||
|
||
```
|
||
server/index.js schlanker Express-Server (Bildchen-Liste + Rangliste)
|
||
public/ Frontend (Startseite, Spiel, Game-Over) — Vanilla JS
|
||
library/feet/ die Füße (gesuchte Figuren)
|
||
library/props/ Deko-Ablenker
|
||
data/ Rangliste (leaderboard.json, zur Laufzeit)
|
||
install.sh Ein-Befehl-Setup + detached Start
|
||
```
|