Online Durak. Fast, browser-based, free to play.
Work in progress.
- Client: https://durak-icfred.web.app
- Worker: deployed at the Cloudflare workers.dev subdomain configured locally
(set
VITE_WORKER_BASE_URLfor the client build).
pnpm install
pnpm typecheck
pnpm lint
pnpm test
# client (vite :5173) + server (wrangler + durable objects :8787) in parallel
pnpm dev
# firebase emulators (auth :9099, firestore :8080, hosting :5000, ui :4000)
pnpm exec firebase emulators:startTo run a 2-browser networked test locally, pnpm dev, open two tabs at
http://localhost:5173, host a match in one and join its code in the other.
Worker (Cloudflare):
export CLOUDFLARE_API_TOKEN=...
export CLOUDFLARE_ACCOUNT_ID=604e6aa04390dad3b7b1d2255079ef67
pnpm --filter @durak/server exec wrangler deployClient (Firebase Hosting):
pnpm --filter @durak/client build
pnpm exec firebase deploy --only hostingFirestore rules:
pnpm exec firebase deploy --only firestore:rulesThe client reads VITE_WORKER_BASE_URL (e.g.
wss://durak-server.<your-subdomain>.workers.dev) at build time. The hook
constructs ${base}/match/<matchId>/ws for game sockets and
${base}/lobbies (over http(s)) for the lobby list.
- Pure-TS rules engine in
@durak/shared/rules— no UI / network deps. - Wire protocol in
@durak/shared/protocol—ClientMessage/ServerMessagediscriminated unions, JSON over WSS. - Cloudflare Durable Objects:
MatchRoom— one DO per match (idFromName(matchCode)). Holds the authoritativeGameState, validates moves, broadcasts per-playerPlayerViewso hidden hands stay hidden. Owns shot clock + grace.LobbyRegistry— single DO (idFromName('main')) holding open lobbies in memory; match rooms call/openand/closeas their joinability changes; worker exposesGET /lobbies.
- React + Pixi (via
@pixi/react) for the client. GSAP for animation. Local-vs-bot mode (useLocalGame) and networked mode (useNetworkedGame) share the sameGameTablepresentational component.