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,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Human, Animal, Plant, Object - Discord Activity word game
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Human, Animal, Plant, Object — Discord Activity إنسان، حيوان، نبات، جماد. A multiplayer word game that runs inside a Discord voice channel as an Activity. Built with Next.js (App Router) on Vercel, Supabase (Postgres + Realtime), and the Discord Embedded App SDK. The UI comes in English and Arabic, with full right-to-left layout for Arabic.檢視完整 README ↗
إنسان، حيوان، نبات، جماد. A multiplayer word game that runs inside a Discord voice channel as an Activity. Built with Next.js (App Router) on Vercel, Supabase (Postgres + Realtime), and the Discord Embedded App SDK. The UI comes in English and Arabic, with full right-to-left layout for Arabic.
src/lib/scoring.ts)| Answer | Points |
|---|---|
| Valid, and nobody else wrote the same word in that category | 10 |
| Valid, but another player wrote the same word | 5 |
| Invalid, blank, or wrong starting letter | 0 |
The server decides whether an answer is valid, in this order:
Matching ignores case, accents, and Arabic diacritics. It treats أ/إ/آ/ا as the same letter, and likewise ى/ي and ة/ه. A leading "ال" or "the " is also allowed. So "الأسد" counts for أ, and "The Rock" counts for R.
Discord client ──iframe──▶ https://<client_id>.discordsays.com (Discord proxy)
│ / → your Vercel app
│ /supabase → <ref>.supabase.co (Realtime websocket)
│ /discord-cdn → cdn.discordapp.com (avatars)
▼
Next.js on Vercel
/api/auth/discord trades the OAuth code for a token, checks the user with Discord, returns a signed session (JWT)
/api/game/[action] join · state · settings · ready · start · answers · endRound · vote · verdict · tally · next · lobby
│ service-role key
▼
Supabase Postgres ── Realtime (postgres_changes on `games`) ──▶ every client refetches state
/api/game/*, which checks the Discord session before touching the database. Picking letters, round deadlines, closing rounds, and scoring all happen on the server. Race-prone steps run as atomic SQL functions (start_round, end_round, apply_round_scores).games.version. Clients subscribe to that row through Supabase Realtime and refetch a view of the game tailored to them. Clients also poll state every 5 seconds. That poll doubles as a heartbeat (who's online, host handover) and as a fallback if the websocket drops.rounds.ends_at is set in Postgres. Clients measure how far their clock is from the server's on each response and count down to the server's deadline. The server accepts answers up to 2 seconds after the deadline to catch the last auto-save, then closes the round itself.games row, and no table accepts writes from the client.supabase/migrations/…_init.sql tables, RLS, triggers, RPCs, realtime publication
src/app/api/auth/discord OAuth2 code exchange → session token
src/app/api/auth/guest local test mode (ALLOW_GUEST_MODE=true only)
src/app/api/game/[action] game action router
src/server/game.ts all game rules and state projection
src/server/session.ts session JWT (jose)
src/lib/letters.ts alphabets, normalization, letter check
src/lib/scoring.ts validity + points (shared by server and live preview)
src/lib/discord.ts Embedded App SDK boot + URL mapping patches
src/hooks/useGame.ts realtime subscription, polling, clock sync
src/components/ Lobby, RoundPlay, Voting, RoundResults, FinalLeaderboard
src/i18n/en.json, ar.json every UI string (CLDR plural forms for Arabic)
tests/ node:test unit tests (scoring, letters, i18n key parity)
supabase link --project-ref <ref> and then supabase db push, orsupabase/migrations/20260915000000_init.sql into the SQL editor.sb_publishable_… key)sb_secret_… key)pg_cron and schedule the cleanup:
select cron.schedule('cleanup-games', '0 * * * *', 'select public.cleanup_stale_games()');
Go to discord.com/developers/applications and click New Application.
On the OAuth2 page:
https://127.0.0.1. Discord requires one, but Activities never actually use it.On Activities → Getting Started, enable Activities for the app.
On Activities → URL Mappings, add these mappings. Targets have no https:// and no trailing slash.
| Prefix | Target |
|---|---|
/ | your-app.vercel.app |
/supabase | <project-ref>.supabase.co |
/discord-cdn | cdn.discordapp.com |
The app calls patchUrlMappings() on startup. That rewrites Supabase and avatar URLs to go through these prefixes, because Discord's content security policy (CSP) blocks every host that isn't mapped.
On Installation, enable Guild Install and User Install.
On Activities → Settings, choose the supported platforms: Web, iOS, and Android. The layout is responsive and works on phones.
OAuth scopes requested at runtime: identify and guilds.
To launch it, join a voice channel, open the 🚀 Activities menu, and pick your app. While the app is unverified, only you, your team members, and users you add under App Testers can launch it.
Import the repo into Vercel. The framework preset is detected automatically.
Add these environment variables (see .env.example):
| Variable | Where it's used |
|---|---|
NEXT_PUBLIC_DISCORD_CLIENT_ID | client + server |
DISCORD_CLIENT_SECRET | server only |
NEXT_PUBLIC_SUPABASE_URL | client + server |
NEXT_PUBLIC_SUPABASE_ANON_KEY | client (Realtime only) |
SUPABASE_SERVICE_ROLE_KEY | server only |
SESSION_SECRET | server only. Generate with openssl rand -base64 32 |
ALLOW_GUEST_MODE | set false in production |
Turn off Deployment Protection for the environment Discord points at (Project → Settings → Deployment Protection). Otherwise Discord's proxy gets a Vercel login page instead of the game.
Framing headers: next.config.ts sends Content-Security-Policy: frame-ancestors with Discord's domains (discord.com, *.discord.com, *.discordsays.com). It doesn't send X-Frame-Options, so the page can be embedded. No vercel.json is needed.
Point the / URL mapping at your production domain.
cp .env.example .env.local # fill in the values
npm install
npm run dev
Without Discord (quickest). With ALLOW_GUEST_MODE=true, open http://localhost:3000, pick a name and a room code, then open more tabs with the same ?room= to play against yourself. Each tab gets its own guest identity. Guest rooms are kept separate from real Discord instances.
Inside Discord. Expose your dev server over HTTPS and point the / URL mapping at it:
npx cloudflared tunnel --url http://localhost:3000
If hot reload complains about the tunnel host, add it to allowedDevOrigins in next.config.ts.
| Command | What it does |
|---|---|
npm run dev | Next.js dev server |
npm run build | production build |
npm run lint | TypeScript type-check |
npm test | unit tests for scoring, letter normalization, and that en/ar have matching keys and all used keys exist |
{ id, emoji } to src/lib/categories.ts.categories.<id> to both en.json and ar.json.npm test. It fails if a label is missing in either language.t("section.key", { vars }). For counts, pass count. Arabic plural forms (_zero, _one, _two, _few, _many, _other) are picked automatically.join. Call GET /applications/{id}/activity-instances/{instance_id} with a bot token. Today the instance id is treated as a secret you can only get by being in the call./api/game/*, for example with Vercel Firewall rules.