Multiplayer game 8-ball two players on the internet. Backend: json and sql. Frontend: JavaScript ES5 + Canvas 2D.
The server is the state authority server (anti-cheat).
Fig. 1 — Game screen. From the left: player bar above the table (avatar + login + pool),
table with racked balls and the cue ball on the break line, right panel: HIT POINT
(large white ball for setting spin), POTTED BALLS (order) and ROUND.
Below the table, the status message "Waiting for players" and the "Sit at the table" button.
Fig. 2 — Shot engine tests (A–H). For each shot, the engine reports:
pocket hit/miss, end position, end velocity, and full ball trajectory.
Tested scenarios: potting into corner and middle pockets (A, F, G, H), cushion rebound (B),
rolling along the cushion without dropping (C, D), and trajectory missing the pocket (E).
Racking: cue ball on the break line (x=260), 15-ball triangle with the apex at x=700;
the black ball (8) in the center of the triangle, solid and stripe on the corners.
Break: first shot from the cue ball into the triangle; the breaker is chosen randomly by the server (mt_rand).
Groups: the player who first pots a ball in the playing state gets that group —
solids (1–7) or stripes (9–15); the opponent gets the other group.
Turn: potting your own ball = turn continues; miss or foul = turn passes
to the opponent.
Black ball (8): legal only after clearing your entire group; must go into the pocket
diagonal to the pocket where the last ball was potted (highlighted with a gold
ring). Black ball too early or into the wrong pocket = immediate loss.
Fouls (consequence: opponent gets cue ball in hand)
First contact of the cue ball with the opponent's ball or the black ball (when illegal)
Cue ball hit no ball and bounced off fewer than 3 different cushions
Pocketing the cue ball or opponent's ball (opponent's ball counts for them)
Exceeding move time / shot time limit
Game end
Win: black ball into the required pocket → winner takes the pool (2× stake)
Walkover: opponent left the table or did not return within 30 s → pool goes to the present player
Rematch: after the finished state, the "Rematch" button deducts the entry fee again
4. Table geometry and pockets
Physics coordinates are always in the logical system 1000×620 px (cloth from (53,53) to (947,547));
screen scaling does not change physics. Each of the 6 pockets is a guide corridor with funnel-shaped
jaws — not a point. A ball in the corridor rolls down a gutter to a dark hole (radius ~25 px)
and only disappears there; channel walls bounce it towards the center (no "invisible cushions").
Fig. 4 — Table geometry: cloth, 6 numbered pockets, and diagonal pocket rule for the black ball.
axis along PAXIS; corners have an "apron" up to t=34
Pocket diagonals
0↔4, 1↔3, 2↔5
required pocket for the black ball
Cue ball placement lock
PCAP + radius ≈ cueR+15
forbidden to place near a pocket / on another ball
5. Physics engine (client, deterministic)
Constant
Value
Meaning
SIM_DT
1/240 s
fixed simulation step — determinism on both clients
FRICTION
0.997 / step
rolling friction
RAIL_REST
0.8
cushion restitution coefficient
STOP_V
8 px/s (scaled with Ø)
ball stop threshold
MAX_SPEED
1600 px/s (scaled with Ø)
shot power = 1.0
Ball collisions: elastic, with position separation — separation never pushes a ball
out of the pocket corridor back onto the cushion.
Spin (effect): side spin (x) curves the cue ball path before the first contact;
top/bottom spin (y) modifies post-contact behavior (follow/draw). Reset after each shot.
Scaling: shot power and stop threshold scale with ball diameter
(base Ø28 px) — balls of any size move naturally.
Ball rolling: rotation = distance/radius; numbers and stripes roll across the sphere
according to movement direction (full 360°).
Simulation events:pocketed (ball+pocket, in order),
firstHit (first contact of the cue ball), rails (set of hit cushions).
Events + end positions reported to the server as the shot result.
drag sets direction and power in a single gesture ("slingshot")
Shot power
hold and pull back along the cue (forward decreases); release = shot
same as aiming
Hit point
large white ball in panel: top = follow, bottom = draw, sides = curve and cushion spin
Cue ball in hand
click places immediately
drag ghost and release finger to place
Server validation of cue ball position (place_cue): within cloth, not too close to a pocket,
does not overlap another ball.
7. Multiplayer — state machine and time
Fig. 5 — Table state machine.
Limit
Constant / configuration
Consequence
Move time
turn_time per table (5–300 s, def. 30)
foul: turn + opponent gets cue ball in hand
Return to table
SEAT_TIMEOUT = 30 s
seat held for 30 s; then released, in-game = walkover
Shot result
SHOT_TIMEOUT = 150 s
shot expires → time foul
Heartbeat
polling get_status every ~1.2 s
refreshes player's last_activity
Move time counted only after all balls stop moving (aim phase) —
the timer bar appears after simulation. All state operations atomic under flock
(withState) — no write race conditions.
8. API — complete list of actions
bilard_api.php?table=<id>&action=<action> (requires session; POST actions require CSRF token)
Action
Method
Effect
get_status
GET
Full state: balls, seats (+rank/balance/idle), turn, phase, pool, time_left/time_limit, pending_shot, potted_order, moves/round, config (felt, ball_r, cue_r, table_w/h), caller balance and rank
test_session
GET
Diagnostics: user, userId, balance
sit
POST
Take a seat (checks min. balance, table lock, free seat)
leave_table
POST
Release seat; during a game = surrender (walkover)
start_game
POST
Start when both are seated; collects entry fee, pool = 2× stake, draws breaker
place_cue
POST
Place cue ball in hand; position validation (cloth, pockets, collisions)
Sitting down requires min_balance; start_game collects blind
(entry fee) from both players → pool = 2× stake.
Winner takes the whole pool; if a table is deleted during a game, the pool is refunded 50/50.
Balance in baza/<uid>/stankonta.txt; writes under LOCK_EX.
10. Ranks and VIP calculator
Rank
Assigned
Privilege
normal
automatically upon registration
—
silver / gold
admin (player list)
badge next to login
VIP
admin
dynamic % pocketing chance while aiming
VIP calculator (client, preview only): calculates the path of the targeted ball —
directly, after cushion rebound, and in combination (ball knocked by the targeted one). Percentage results
from the physical intersection of the path with the pocket throat, entry angle, and obstacles on the way. Calculated
only for legal balls (group / any when groups not assigned). Green ≥ 50%, red below;
horizontal text also in portrait mode.
11. Admin panel (admin.php)
Tables
Parameter
Range
Notes
Entry fee (blind)
≥ 1 currency
pool = 2× stake
Min. balance
≥ entry fee
blocks sitting below threshold
Cloth color
HEX
cloth, gutters, and shadows calculated from this color
Ball diameter
18–44 px
physics scales proportionally
Move time
5–300 s
"X s/move"
Table size
320–1600 × 280–1400 px
table only — round balls, UI fixed size (letterbox)
Statistics: registered players, online players (activity < 120 s), number of tables.
Player list: login, balance, current table, rank dropdown.
Fresh installation creates 1 table. Warning on default admin password.
Game interface
Player bar at the top: avatar + login + group + ball thumbnails; active player
has a flashing gold avatar border; in the center, the pool and the GAME RULES button.
Panel below the table: hit point, potted balls in order, round/move.
Info bar: turn, fouls, cue ball in hand, ball movement, game state.
"Start Game" — button in the middle of the table; after completion = "Rematch".
Portrait mode (mobile): when the screen is taller than it is wide on a mobile device,
the table renders rotated by −90°, fitted to the screen. Shadows/reflections compensate for rotation
(lighting remains screen-oriented), labels and VIP % are counter-rotated. Always horizontal on PC.
12. Security layer
(plik6).php shared module included by every public PHP file:
Domain license (anti-theft): script only works on domains from the list
SEC_LICENSED_HOSTS; entry .domain covers all subdomains.
Unlicensed domain → HTTP 403.
Session: HttpOnly cookie + SameSite=Lax + secure (HTTPS),
use_strict_mode, ID regeneration after login.
CSRF: session token required in all state-changing actions
(forms, admin API, game API).
Rate limiting: admin login 5/5 min, player login 8/5 min,
registration 5/10 min — counters per IP in baza/_ratelimit/.
Headers: X-Frame-Options, nosniff, Referrer-Policy (also in .htaccess).
.htaccess: blocking listing, hidden files, data extensions
(.txt .json .hash .bak .zip …) and file directories specyfikacja.html .