Files
fuesse-sexy/README.md
Guido 221cba417f Anti-Cheat: server-autoritative Runden (blindes Treffer-Spam wirkungslos)
Der Server bestimmt pro Runde Ziel + alle Items (mit zufaelligen IDs) und validiert
jeden Treffer gegen die Ziel-ID der aktuellen Runde (nonce + itemId). Blindes Spammen
von /api/game/hit bringt damit 0 Punkte. Zusaetzlich Reaktionszeit-Untergrenze und
Mindestabstand; Score weiterhin nach Spielzeit gedeckelt.

- Server: makeRound() + /api/game/start liefert erste Runde, /api/game/hit erwartet
  {nonce,itemId} und gibt bei korrektem Treffer die naechste Runde zurueck
- Client: rendert server-gelieferte Runden, sendet geklickte itemId, Vorschub nur bei
  Server-Bestaetigung (Sofort-Flash fuers Gefuehl)
- README: Ban-Befehle dokumentiert, Anti-Cheat-Abschnitt aktualisiert

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-05 12:39:19 +02:00

109 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 23 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"
```
(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.
## 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
```