Multiplayer-Spiel 8-Ball für zwei Spieler im Internet. Backend: JSON und SQL. Frontend: JavaScript ES5 + Canvas 2D.
Der Server ist die Zustandsautorität (Anti-Cheat).
Abb. 1 — Spielbildschirm. Von links: Spielerleiste über dem Tisch (Avatar + Login + Pool),
Tisch mit aufgebautem Billarddreieck und weißer Kugel auf der Anspiellinie, rechtes Panel: TREFFPUNKT
(große weiße Kugel zum Einstellen des Effekts), VERSENKTE KUGELN (Reihenfolge) und RUNDE.
Unter dem Tisch die Statusmeldung „Warten auf Spieler" und die Schaltfläche „Am Tisch Platz nehmen".
Abb. 2 — Schuss-Engine-Tests (A–H). Für jeden Schuss meldet die Engine:
Treffer/Fehlschuss in die Tasche, Endposition und Endgeschwindigkeit sowie die vollständige Kugelbahn.
Getestete Szenarien: Versenken in Eck- und Mitteltaschen (A, F, G, H), Bandenabpraller (B),
Rollen entlang der Bande ohne hineinzufallen (C, D) und Trajektorie am Loch vorbei (E).
Aufbau: Weiße Kugel auf der Anspiellinie (x=260), Dreieck aus 15 Kugeln mit der Spitze bei x=700;
die Schwarze (8) in der Mitte des Dreiecks, an den Ecken jeweils eine volle und eine halbe Kugel.
Anstoß: Erster Schuss von der Weißen ins Dreieck; der Anstoßer wird vom Server per Zufall ermittelt (mt_rand).
Gruppen: Der Spieler, der im Zustand playing als Erster eine Kugel versenkt, erhält diese Gruppe —
volle (1–7) oder halbe (9–15); der Gegner erhält die andere Gruppe.
Zugreihenfolge: Eigene Kugel versenkt = Schuss geht weiter; Fehlschuss oder Foul = Zug geht über
an den Gegner.
Schwarze (8): Erst nach dem Versenken der eigenen Gruppe erlaubt; muss in die Tasche fallen,
die der Tasche der zuletzt versenkten Kugel gegenüberliegt (diagonal) (durch goldenen
Ring markiert). Schwarze zu früh oder in die falsche Tasche = sofortiger Verlust.
Fouls (Folge: Gegner bekommt die Weiße in die Hand)
Erster Kontakt der Weißen mit einer Kugel des Gegners oder der Schwarzen (wenn diese ungültig ist)
Die Weiße hat keine Kugel berührt und ist von weniger als 3 verschiedenen Banden abgeprallt
Versenken der Weißen oder einer gegnerischen Kugel (gegnerische Kugel zählt für diesen)
Überschreitung der Zugzeit / des Zeitlimits für die Ausführung des Schusses
Spielende
Sieg: Schwarze in die erforderliche Tasche → Gewinner kassiert den Pool (2× Einsatz)
WA (W.O. / Aufgabe): Gegner hat den Tisch verlassen oder ist nicht innerhalb von 30 Sek. zurückgekehrt → Pool geht an den Anwesenden
Revanche: Nach dem Zustand finished zieht die Schaltfläche „Revanche" das Startgeld erneut ab
4. Tischgeometrie und Taschen
Physik-Koordinaten befinden sich immer im logischen System 1000×620 px (Tuch von (53,53) bis (947,547));
Bildschirmskalierung ändert die Physik nicht. Jede der 6 Taschen ist ein Führungskorridor mit trichterförmigen
Backen — kein Punkt. Eine Kugel im Korridor rollt über eine Rinne in das dunkle Loch (Radius ~25 px)
und verschwindet erst dort; Kanalwände prallen sie zur Mitte hin ab (keine „unsichtbaren Banden").
Abb. 4 — Tischgeometrie: Tuch, 6 nummerierte Taschen und Diagonaltaschenregel für die Schwarze.
Achse entlang PAXIS; Ecken haben eine „Schürze" bis t=34
Taschendiagonalen
0↔4, 1↔3, 2↔5
erforderliche Tasche für die Schwarze
Sperre für Weiße-Platzierung
PCAP + Radius ≈ cueR+15
Darf nicht im Taschenbereich / auf einer anderen Kugel platziert werden
5. Physik-Engine (Client, deterministisch)
Konstante
Wert
Bedeutung
SIM_DT
1/240 s
fester Simulationsschritt — Determinismus auf beiden Clients
FRICTION
0.997 / Schritt
Rollreibung
RAIL_REST
0.8
Banden-Elastizitätskoeffizient
STOP_V
8 px/s (skaliert mit Ø)
Kugel-Stoppschwelle
MAX_SPEED
1600 px/s (skaliert mit Ø)
Schusskraft = 1.0
Kugel-Kollisionen: elastisch, mit Positions-Separation — die Separation drückt eine Kugel niemals
aus dem Taschenkorridor zurück auf die Bande.
Effekt (Spin): Seitlicher Spin (x) krümmt die Bahn der Weißen vor dem ersten Kontakt;
oben/unten (y) modifiziert das Verhalten nach dem Kontakt (Follow/Draw). Wird nach jedem Schuss zurückgesetzt.
Skalierung: Schusskraft und Stoppschwelle skalieren mit dem Kugeldurchmesser
(Basis Ø28 px) — Kugeln jeder Größe bewegen sich natürlich.
Kugelrollen: Drehung = Weg/Radius; Nummern und Streifen rollen über die Kugel
entsprechend der Bewegungsrichtung (volle 360°).
Simulationsereignisse:pocketed (Kugel+Tasche, in der Reihenfolge),
firstHit (erster Kontakt der Weißen), rails (Menge der berührten Banden).
Ereignisse + Endpositionen werden als Schussergebnis an den Server gemeldet.
6. Steuerung und Treffpunkt
Aktion
PC
Smartphone (Touch)
Zielen
Maus legt Richtung fest; Ziellinie + Geisterkugel + Rückprallbahn
Ziehen legt Richtung und Kraft in einer Geste fest („Zwille")
Schusskraft
Halten und entlang des Queues nach hinten ziehen (nach vorne nimmt sie ab); loslassen = Schuss
wie beim Zielen
Treffpunkt
große weiße Kugel im Panel: oben = hinter der Kugel herziehen (Follow), unten = zurückziehen (Draw), Seiten = Bogen und Effet an der Bande
Weiße in der Hand
Klick platziert sofort
Geist ziehen und Finger loslassen platziert
Servalidierung der Position der Weißen (place_cue): innerhalb des Tuchs, nicht zu nah an der Tasche,
überlappt keine andere Kugel.
7. Multiplayer — Zustandsautomat und Zeit
Abb. 5 — Tisch-Zustandsautomat.
Limit
Konstante / Konfiguration
Folge
Zugzeit
turn_time pro Tisch (5–300 s, Std. 30)
Foul: Zug + Weiße in der Hand des Gegners
Rückkehr an den Tisch
SEAT_TIMEOUT = 30 s
Platz 30 s lang gehalten; danach freigegeben, im Spiel = W.O. (Aufgabe)
Schussergebnis
SHOT_TIMEOUT = 150 s
Schuss läuft ab → Zeitfoul
Heartbeat
Pollt get_status alle ~1,2 s
aktualisiert last_activity des Spielers
Die Zugzeit wird erst nach dem Stillstand aller Kugeln gezählt (Phase aim) —
die Zeitachse erscheint nach der Simulation. Alle Zustandsoperationen erfolgen atomar unter flock
(withState) — keine Schreibkonflikte.
Hinsetzen erfordert min_balance; start_game zieht von beiden Spielern
blind (Startgeld) ein → Pool = 2× Einsatz.
Der Gewinner kassiert den gesamten Pool; beim Löschen eines Tisches während des Spiels wird der Pool hälftig zurückerstattet.
Guthaben in baza/<uid>/stankonta.txt; Schreibvorgänge unter LOCK_EX.
10. Ränge und VIP-Rechner
Rang
Wird vergeben
Privileg
normal
automatisch bei der Registrierung
—
silver / gold
Admin (Spielerliste)
Abzeichen am Login
VIP
Admin
dynamische %-Trefferchance beim Zielen
VIP-Rechner (Client, nur Vorschau): Berechnet für die Anspielkugel die Bahn der getroffenen Kugel —
direkt, nach Bandenabpraller und in Kombination (Kugel wird von der getroffenen angestoßen). Der Prozentsatz ergibt sich
aus dem physischen Schnittpunkt der Bahn mit dem Taschenhals, dem Einfallswinkel und Hindernissen auf dem Weg. Berechnet
werden nur legale Kugeln (Gruppe / beliebige, wenn keine Gruppen zugewiesen sind). Grün ≥ 50%, rot darunter;
waagerechter Text auch im Hochformat.
11. Admin-Panel (admin.php)
Tische
Parameter
Bereich
Hinweise
Startgeld (Blind)
≥ 1 Währung
Pool = 2× Einsatz
Mindestguthaben
≥ Startgeld
Sperre für Hinsetzen unterhalb des Schwellenwerts
Tuchfarbe
HEX
Tuch, Rinnen und Schatten werden aus dieser Farbe berechnet
Kugeldurchmesser
18–44 px
Physik skaliert proportional
Zugzeit
5–300 s
„X s/Zug"
Tischgröße
320–1600 × 280–1400 px
nur der Tisch — Kugeln rund, UI feste Größe (Letterbox)
Eine frische Installation erstellt 1 Tisch. Warnung beim Standard-Admin-Passwort.
Spieloberfläche
Spielerleiste oben: Avatar + Login + Gruppe + Kugelminiaturen; der aktive Spieler
hat einen blinkenden goldenen Avatar-Rahmen; in der Mitte der Pool und die Schaltfläche SPIELREGELN.
Panel unter dem Tisch: Treffpunkt, versenkte Kugeln in der Reihenfolge, Runde/Zug.
Infoleiste: Zugreihenfolge, Fouls, Weiße in der Hand, Kugelbewegung, Spielstatus.
„Spiel starten" — Schaltfläche in der Tischmitte; nach Abschluss = „Revanche".
Hochformat (Smartphone): Wenn der Bildschirm auf einem mobilen Gerät höher als breit ist,
wird der Tisch um −90° gedreht und an den Bildschirm angepasst. Schatten/Reflexionen kompensieren die Drehung
(das Licht bleibt bildschirmorientiert), Beschriftungen und VIP-% sind gegenkonvertiert. Auf dem PC immer waagerecht.
12. Sicherheitsschicht
(plik6).php gemeinsames Modul, das von jeder öffentlichen PHP-Datei eingebunden wird:
Domänenlizenz (Diebstahlschutz): Skript funktioniert nur auf Domänen aus der Liste
SEC_LICENSED_HOSTS; Eintrag .domäne schließt alle Subdomains ein.
Nicht lizensierte Domäne → HTTP 403.
Sitzung: HttpOnly-Cookie + SameSite=Lax + secure (HTTPS),
use_strict_mode, ID-Regenerierung nach dem Login.
CSRF: Sitzungstoken erforderlich bei allen zustandsändernden Aktionen
(Formulare, Admin-API, Spiel-API).
Rate Limiting: Admin-Login 5/5 Min., Spieler-Login 8/5 Min.,
Registrierung 5/10 Min. — Zähler pro IP in baza/_ratelimit/.
Header: X-Frame-Options, nosniff, Referrer-Policy (auch in .htaccess).
.htaccess: Sperrung von Listing, versteckten Dateien, Datenerweiterungen
(.txt .json .hash .bak .zip …) und Dateiverzeichnissen der Datei specyfikacja.html .
Passwörter: Spieler — password_hash (bcrypt); Admin — Hash in
.hash (außerhalb des Codes).
Login: Whitelist [A-Za-z0-9_-]{2,20} — eliminiert XSS durch Login
in Panels und Meldungen.
Autorisierung: Guthaben nur das eigene, Tische nur aus der Admin-Konfiguration,
Admin-Vorschau nur im Nur-Lese-Modus.
13. Technische Hinweise
PHP 5.6, JSON/TXT oder SQL als Speicher; Kompatibilität mit einfachem Apache-Hosting.
Client: reines JS + Canvas 2D, ES5; eine einzige Datei 8.php ohne externe Abhängigkeiten.
Deterministische Simulation (SIM_DT = 1/240) — beide Spieler berechnen identische Bewegungen;
das Ergebnis kann von jedem gemeldet werden, der Server validiert es.
Physik-Koordinaten immer im System 1000×620; TABLE_SCALE ist lediglich der Zeichenmaßstab.
Alle Zustandsschreibvorgänge erfolgen atomar unter flock; fehlgeschlagenes json_encode →
vorheriger Zustand bleibt erhalten (ohne die Datei zu leeren).
Zum vollständigen Schutz des Quellcodes vor Diebstahl: ionCube Encoder / SourceGuardian
(Domänensperre ist eine Barriere, keine Verschlüsselung).