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,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
React + TypeScript + Vite single-page app for LoveyDovey, a real-time game app for couples and friend groups: synced two-player games, AI-generated party games for group lobbies, XP/streaks/leaderboards, and an admin dashboard.
This is the frontend only. It talks to the lovey-dovey-api Laravel backend over REST + Pusher-backed WebSockets.
• React 19 + TypeScript + Vite 7
• React Router v7 (client-side routing, BrowserRouter)
• Tailwind CSS v3 (darkMode: 'class') + Framer Motion for animation
• Axios for REST calls, Laravel Echo + Pusher JS for real-time
• lucide-react for icons檢視完整 README ↗React + TypeScript + Vite single-page app for LoveyDovey, a real-time game app for couples and friend groups: synced two-player games, AI-generated party games for group lobbies, XP/streaks/leaderboards, and an admin dashboard.
This is the frontend only. It talks to the lovey-dovey-api Laravel backend over REST + Pusher-backed WebSockets.
BrowserRouter)darkMode: 'class') + Framer Motion for animationNo server-side rendering, no state management library beyond React context — auth and partner state live in small hand-rolled contexts/hooks (see below).
npm install
cp .env.example .env # then fill in the values below
npm run dev # http://localhost:5173
npm run build # tsc -b && vite build → dist/
npm run preview # serve the production build locally
npm run lint # eslint .
| Variable | Example | Notes |
|---|---|---|
VITE_API_BASE_URL | http://127.0.0.1:8000/api | Axios base URL — includes the /api prefix. Read in src/libs/axios.ts. |
VITE_API_URL | http://127.0.0.1:8000 | Base URL without /api — used only to build the /broadcasting/auth URL in src/libs/echo.ts. |
VITE_PUSHER_APP_KEY | abc123 | Required. If empty, new Pusher(...) throws synchronously at import time and the whole app fails to boot — see Troubleshooting. |
VITE_PUSHER_APP_CLUSTER | mt1 | Pusher cluster. |
VITE_PUSHER_SCHEME | https | Anything other than http forces TLS for the socket connection. |
There is no build-time distinction between environments beyond these variables — point them at whichever backend you're running (local php artisan serve, staging, production) and rebuild.
src/
pages/ top-level routed screens (see Routes below)
pages/admin/ admin dashboard screens, behind RequireAdmin
games/ the actual game UIs — one file per game, plus two "runner"
wrappers that add real-time sync on top
components/ shared UI (cards, modals, toasts, feedback widget, footer…)
components/lobbies/ lobby-specific widgets (create-lobby modal, upcoming list)
components/partners/ partner-linking UI
context/ AuthContext (session/token), ToastContext (toast queue)
hooks/ usePartner (active partner + pair status)
libs/ axios client, Echo client, and thin per-domain API wrappers
(auth, partner, daily, leaderboard, notification, progress, streaks)
| Path | Page | Notes |
|---|---|---|
/ | Landing | Marketing/landing page, public |
/onboarding | Onboarding | Sign-up flow |
/signin | SignIn | Login |
/forgot-password, /password-reset/:token | ForgotPassword, ResetPassword | Password reset flow |
/games | GamesDashboard | Main authenticated home — game catalog, quick actions, progress, leaderboard |
/settings | Settings | Profile, notification prefs, dark mode toggle, sessions/devices, partner unpair |
/lobby/:code | LobbyRoom | Group lobby: presence, chat, host-started games |
/session/:code | Session | A live couple game session (see Real-time below) |
/admin/* | AdminLayout + children | Admin dashboard, gated by RequireAdmin (checks user.is_admin) |
vercel.json adds a catch-all SPA rewrite (/(.*) → /index.html) — required on Vercel (or any static host) so a hard refresh or a deep link (an emailed invite link, a bookmark) doesn't 404 before React Router even boots.
The app has two distinct multiplayer models, each with its own sync strategy — knowing which one a given game uses matters a lot when debugging or extending it.
Used by all "couple" games (Truth or Dare, Truth or Dare Plus/Erotic, Spice Dice, Emoji-Only Chat, Memory Match). Rendered by src/pages/Session.tsx at /session/:code.
GameSession.state, a JSON blob) and validates every move (whose turn it is, legal actions).POST /sessions/{code}/action and re-renders whatever state comes back.couple-session.{code}, receiving .session.created / .session.updated events — plus a 3-second polling fallback (Session.tsx) in case a broadcast is missed.Used by Trivia, Charades (AI), Hot Seat, Would You Rather, and Spice Dice in a group lobby (a different code path from the couple version of Spice Dice). Rendered inside src/pages/LobbyRoom.tsx via src/games/SyncedLobbyGameRunner.tsx.
broadcast(type, payload) → POST /lobbies/{code}/games/{id}/action → relayed over Pusher channel lobby-game.{id} as .LobbyGameUpdate.sendAction(type, payload) (e.g. a vote, a buzz) goes over the same channel; only the host's client applies it (onHostReceive) and re-broadcasts the resulting state.seq so a client can dedupe an event it's already applied.src/libs/echo.ts configures a laravel-echo client with a custom authorizer that POSTs the bearer token (from localStorage.auth_token) to ${VITE_API_URL}/broadcasting/auth. Channel authorization rules live server-side in the backend's routes/channels.php.
| Kind | Title | Category | Mode | Notes |
|---|---|---|---|---|
truth_dare | Truth or Dare – Romantic | Romantic | Couple | Curated prompt bank (TruthDarePrompts on the backend), turn-based |
truth_dare_erotic | Truth or Dare – Erotic | Erotic | Couple | Plus-only; same engine, spicier prompt pool |
spice_dice | Spice Dice | Spicy | Couple and Lobby | Always draws a dare, never a truth |
emoji_chat | Emoji-Only Chat | Playful | Couple | No turns, timed, emoji-only messages (server-validated) |
memory_match | Memory Match – Couple Edition | Challenge | Couple | Server-dealt deck, match keeps your turn |
trivia | Trivia Night: Duo vs Duo | Challenge | Lobby | AI-generated questions, cached & rotated (see backend README) |
charades_ai | Charades with AI Prompts | Playful | Lobby | AI-generated prompt cards |
hot_seat, would_you_rather | — | — | Lobby only | Not in the seeded catalog as standalone tiles; started from within a lobby |
"Plus" (Spicy/Erotic categories) requires user.is_plus, which the backend computes as paid subscriber OR still inside their 14-day free trial — the frontend never needs to know which; it just reads user.is_plus.
/admin)A separate, non-dark-mode-by-default (toggleable) shell (AdminLayout) gated by RequireAdmin. Responsive: sidebar collapses into a slide-in drawer below md.
| Page | Purpose |
|---|---|
| Overview | Platform stats (users, revenue, games played, signups) |
| Users | Search/filter, view profile, promote/demote admin, (de)activate, delete, bulk actions, last-login |
| Games | CRUD on the seeded game catalog; recent/all lobby sessions |
| Feedback | In-app user feedback inbox (bug/idea/praise/other), mark reviewed |
| Features & Tips | Compose an email announcement to every user who opted into "New features & tips"; history of past sends |
| Reports | Signups, games-by-kind, revenue, Plus conversions, top lobbies |
| Settings | Platform-wide toggles (maintenance mode, registration open, Plus pricing, announcement banner) |
The user-facing feedback widget (src/components/FeedbackModal.tsx) is available from the main dashboard's top bar for any signed-in user, not just admins.
/history; lobby games are credited when the host ends the session (every current lobby member gets the same XP, not just the host).StreakService on the backend).DailyChallengeCard).WeeklySummaryCard) and an opt-in email digest, covering both couple games and lobby games.Set in /settings (email_news, email_reminders, weekly_summary, private_profile on the user). email_news ("New features & tips") is the audience for the admin's Features & Tips announcements.
Toggled via localStorage.theme ('light' | 'dark') and a dark class on <html> (Tailwind's class strategy). Set from Settings.tsx for the main app and independently from the admin header (AdminLayout.tsx) — both read/write the same localStorage key so the preference is shared, but each surface applies the class itself on mount rather than relying on a single global bootstrap.
VITE_PUSHER_APP_KEY is empty. echo.ts constructs the Pusher client at module import time, so this crashes the entire app before React even renders, not just real-time features. Any non-empty placeholder value is enough to boot locally if you don't need working real-time (the socket just won't connect).vercel.json isn't deployed yet, or you're on a different static host without an equivalent catch-all rewrite configured.403 on /broadcasting/auth — the bearer token isn't reaching the request; check localStorage.auth_token and that VITE_API_URL doesn't include a stray /api suffix (see the table above).CoupleSessionController::invite) is up to date.Static build, deployable anywhere that serves an SPA with a catch-all rewrite (Vercel config included). Set all VITE_* variables in the hosting provider's environment settings and rebuild — they're baked in at build time, not read at runtime.