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。 以下为已保存的摘录。
A small web app for our CS2 friend group (~15 players) to run 10-man mixes: pick 10 players,
get 3 balanced 5v5 team proposals, vote on one in real time, and track everyone's
performance across mixes (popflash-style stats from demos).
Status: early development — project scaffold in place; work follows the roadmap.查看完整 README ↗A small web app for our CS2 friend group (~15 players) to run 10-man mixes: pick 10 players, get 3 balanced 5v5 team proposals, vote on one in real time, and track everyone's performance across mixes (popflash-style stats from demos).
Status: early development — project scaffold in place; work follows the roadmap.
Mixes are played on our private FACEIT Club queue, which handles servers, anti-cheat and map veto. The app does the balancing and voting, and pulls per-map stats from the FACEIT API afterwards.
| Doc | Contents |
|---|---|
| Product spec | Goals, users, features, user stories, out of scope |
| Architecture | Stack, components, auth, realtime, hosting limits |
| Data model | Database tables and relations |
| Team balancing | Skill score, form, pairing constraints, 3-variant selection |
| Demo pipeline | Getting demos (2 paths), parsing, stat formulas |
| External APIs | Steam, FACEIT, Leetify — endpoints and usage rules |
| Roadmap | Phases, spikes to run first, open questions |
| Decisions | Decision log (why X and not Y) |
Requires Node.js 22+.
npm ci
cp .env.example .env.local # fill in keys (see comments in the file)
npm run dev # http://localhost:3000
Only the Supabase URL and publishable key are browser-safe; keep the other environment keys server-side.
| Command | What it does |
|---|---|
npm run dev | Dev server |
npm run build | Production build |
npm test | Unit tests (Vitest, lib/**/*.test.ts) |
npm run lint | ESLint |
npm run typecheck | Generate Next route types + tsc |
npm run format | Prettier (write) |
npm run format:check | Check formatting without writing |
CI runs format check, lint, typecheck, tests and build on every PR.
The existing Vercel Git integration builds previews for pull requests and Production from main.
vercel.json schedules GET /api/cron/keepalive daily at 05:00 UTC. Set a random CRON_SECRET
in the Vercel Production environment before deploying; Vercel sends it as a bearer token.
The endpoint performs a small, uncached database read, returns 200 {"ok":true} on success,
401 without the correct secret (including when the variable is unset), and 503 when the DB
is unavailable. It never returns database rows. No new tables or migrations are needed.
After deployment, check the cron in Vercel Project → Settings → Cron Jobs and run it once; verify the function log returns 200. Cron jobs run on Production only, so previews and local development do not run the schedule. This and the bearer header follow the Vercel cron setup and cron management guide.
On /g/<slug>, group admins can add a player by SteamID64, Steam profile URL or vanity name.
Steam fills the name and avatar; FACEIT is linked when available. Repeating an add is safe:
it refreshes the profile without changing an active member's role or fallback ELO. If FACEIT
is temporarily unavailable, adding still succeeds with a warning; repeat the add to retry.
Closed memberships stay closed; a membership-reopening policy is outside M1-3.
Fallback ELO is an optional integer from 600 to 2500, scoped to the group and used only when FACEIT ELO is missing. Clearing the field removes it. Remove closes membership without deleting history; the existing last-admin protection still applies. The first Steam login claims the same player identity, preserving group membership. Guests see the roster without edit controls.
UI is built from the VGUI primitives in components/vgui/ (Tailwind v4, rules in DESIGN.md).
Create one Discord application with a bot, then set DISCORD_CLIENT_ID,
DISCORD_CLIENT_SECRET, and DISCORD_BOT_TOKEN in the server environment. In the Discord
Developer Portal, add https://YOUR_HOST/auth/discord/guild/callback to OAuth2 redirect URIs
(and https://YOUR_HOST/auth/discord/player/callback for player accounts). For local testing,
register both paths with http://localhost:3000. Each preview host
needs its own registered redirect URI. Keep the client secret and bot token out of the browser.
A group admin opens Group settings → Discord server → Connect Server. Discord asks them to select a server and approve the bot permissions (View Channels, Send Messages, Connect, Move Members). The callback confirms that the Discord account can manage that server and that the bot joined it. The admin then selects three distinct voice channels and one text channel and saves them. Reconnecting another server clears the old channel selection. Group admins can move the locked 5v5 lineup to separate team voice channels after the knife round and return it to the lobby from the mix page. Players must already be connected to voice in that server. The result lists players who were offline, unlinked, or could not be moved. A live connection requires the owner to create the Discord application and add its credentials. The bot posts to the selected text channel once when a mix is created, when its lineup locks (or on the first page view after an automatic close) and when results are saved. Posts never mention anyone.
Automatic return to the lobby: in FACEIT App Studio, add a webhook to
https://YOUR_HOST/api/webhooks/faceit with header X-Mixer-Secret set to FACEIT_WEBHOOK_SECRET,
subscribed to match_status_finished for the Club's organizer. When a room finishes and at least 8
of a recently locked mix's 10 players were in it, that lineup goes back to the lobby once per match.
Until a real Club match confirms delivery (S6), the manual button stays the reliable path.
Signed-in players can link or unlink their own Discord account from the group roster. Linking uses
OAuth2 identify, stores only the verified Discord user ID, and requires the same Steam session on
return. A Discord account can belong to only one player. Active mixes list participants who have
not linked Discord yet when the group has a connected server.
Next.js (TypeScript) on Vercel · Supabase (Postgres + Realtime) · Steam OpenID login ·
demoparser2 (WASM, parsed in the browser) · FACEIT Data API · Leetify Public API.
Everything on free tiers.