Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Chips on your phone, cards on the table. A realtime chip tracker for live home poker.
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
Chips on your phone. Cards on the table. A realtime chip tracker for live home poker: deal real cards, and everyone bets, raises and scoops pots from their own phone.
poker home-game chip-tracker texas-holdem realtime websockets durable-objects cloudflare-pages react pwa
Somebody always forgets the chips. Somebody else "borrows" a stack. Side pots turn into a group negotiation. So the cards stay physical (shuffling is half the fun) and the chips moved into everyone's pocket.查看完整 README ↗Chips on your phone. Cards on the table. A realtime chip tracker for live home poker: deal real cards, and everyone bets, raises and scoops pots from their own phone.
poker home-game chip-tracker texas-holdem realtime websockets durable-objects cloudflare-pages react pwa
Play it: piss-poker.pages.dev
Somebody always forgets the chips. Somebody else "borrows" a stack. Side pots turn into a group negotiation. So the cards stay physical (shuffling is half the fun) and the chips moved into everyone's pocket.
Before writing a line, we read the code of 22 open source chip trackers and put 9 betting engines through scripted side pot and all-in scenarios. Every tracker had real rule bugs (short all-ins reopening betting, the big blind posted twice when the table shrinks, kicks deleting chips, clients trusted to say who they are), and none of the engines passed every scenario. So this one was built from scratch around a small, heavily tested rules engine and a server that owns the truth.
F fold, C check or call, R raise, arrows to size, Enter to confirm, N next hand) on iPad and desktop. Light and dark follow the device setting. phones, iPads, laptops Cloudflare
┌──────────────────────┐ HTTPS ┌─────────────────────────────────────────────┐
│ React 19 SPA (Vite) │──────────▶│ Pages: static app + /api/* Pages Function │
│ shared/engine.ts │ │ │ service binding │
│ (legal moves, UI) │ WSS │ ▼ │
│ │◀─────────▶│ Worker "piss-poker-api" │
└──────────────────────┘ │ POST /api/rooms create a table │
│ GET /api/rooms/:code peek at a table │
│ GET /api/rooms/:code/ws WebSocket upgrade │
│ │ idFromName(code) │
│ ▼ │
│ Durable Object "Room" (one per table) │
│ shared/engine.ts (authoritative rules) │
│ hibernating WebSockets, SQLite storage │
│ undo history, presence, claims, alarms │
└─────────────────────────────────────────────┘
The server owns the truth. Phones send intentions (act, award, undo); the room's Durable Object validates them against the rules engine and broadcasts the new state to everyone. A client cannot move chips, act out of turn or pretend to be someone else.
Every change is versioned. Each state carries a version v, and every action names the version it was decided on. Double taps, two people pressing "Deal next hand" at once, and actions taken from a stale screen are rejected with STALE. The sender gets the fresh state back.
Identity is a device secret. Each device generates a random 256-bit token. The room stores only a SHA-256 hash of it (salted with the table code) and never broadcasts it. Taking over a seat from a new device needs approval from someone at the table, unless nobody else is around.
Undo rewinds the game, not the guest list. The last 30 game states are kept. Membership lives outside that history, so undoing never removes someone who just sat down or brings back someone who was removed.
Resilient on real phones. iOS freezes sockets when a phone locks, so clients ping every 10 seconds (answered by the runtime without waking the object), reconnect on focus, visibility and network changes, and back off with jitter. Rooms hibernate when idle and delete themselves after 12 quiet hours.
| Path | What lives there |
|---|---|
shared/engine.ts | Pure betting engine: blinds, turn order, raises, side pots, payouts. No dependencies. |
shared/protocol.ts | Wire message types and strict validation of everything a client sends. |
shared/settle.ts | Ledger, cents conversion that always sums to zero, minimal transfers. |
shared/names.ts | Name cleanup: Unicode normalization, invisible and bidi characters stripped, grapheme limits. |
worker/src/room.ts | The Room Durable Object: sockets, permissions, undo, presence, rate limits, expiry. |
worker/src/index.ts | API router, origin allowlist, room creation with per-address rate limiting. |
functions/api/[[path]].ts | Pages Function forwarding /api/* to the worker. |
functions/_middleware.ts | Accept negotiation (text/markdown), real 404s, SPA rewrite for /t/*. |
public/ | Trust pages, llms.txt, markdown mirrors, 404.html, SEO assets. |
src/ | The React app: screens, the action dock, sheets, table display. |
test/ | Engine scenarios, a seeded fuzzer, protocol and settlement tests. |
worker/test/ | Durable Object integration tests running inside workerd. |
e2e/ | Playwright suite with several simulated devices, plus a screenshot walkthrough. |
npm run test:unit # engine scenarios + 1,500 fuzzed games + protocol + settle-up
npm run test:worker # Durable Object and WebSocket protocol inside workerd
npm run test:e2e # Playwright: full hands across simulated phones, iPad and desktop
FUZZ_GAMES=25000 npm run test:unit # the long fuzz run
Scenario tests cover every rule bug found while auditing other projects:
The fuzzer plays random games of 2 to 9 players with short, mixed and deep stacks, random leaves and deliberate illegal moves. After every action it checks:
25,000 games pass clean.
Integration tests cover stale versions, concurrent next and undo, seat claims, kicks, host handoff, oversized and malformed messages, rate limits and room expiry.
End-to-end tests cover:
npm install
npm run dev # worker on :8787 and Vite on :5173 with /api proxied
Open two browser windows (or a normal and a private window) at http://localhost:5173 to play against yourself.
Other scripts: npm run typecheck, npm run build, npm run shots (screenshots of every screen in /tmp/pp-shots), npm run assets (regenerates icons and the OpenGraph image).
Pushes to main ship automatically once typecheck, unit, worker and end-to-end tests pass:
deploy-web job calls a Cloudflare Pages deploy hook (repository secret PAGES_DEPLOY_HOOK). Pages then builds (npm run test:unit && npm run build) and serves piss-poker.pages.dev.deploy-api job runs wrangler deploy. It needs a CLOUDFLARE_API_TOKEN secret with Workers Scripts: Edit permission (CLOUDFLARE_ACCOUNT_ID is already set). The worker rarely changes, so without the token it is skipped and can be shipped with npm run deploy:api.Manual deploy from a logged-in machine: npm run deploy.
React 19, Vite 8, TypeScript 5.9, Cloudflare Pages, Workers, Durable Objects (SQLite-backed, WebSocket hibernation), Vitest 4 with @cloudflare/vitest-pool-workers, Playwright, Geist and Instrument Serif, uqr for QR codes.
MIT © Karthik Subramanian