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,即可验证所有权并自行管理介绍。
一分钟完成验证CRL6Mans website with user accounts
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
Web platform and Discord bot for the CRL West 6mans competitive Rocket League league.
CRL West 6Mans is a competitive collegiate Rocket League pickup league. Players sign in with Discord, and the platform runs the full season lifecycle:查看完整 README ↗Web platform and Discord bot for the CRL West 6mans competitive Rocket League league.
CRL West 6Mans runs drafted Rocket League seasons for college players. Players sign in with Discord, and the platform runs the full season lifecycle:
There are two surfaces:
Staff have three tiers — Moderator → Director → CEO — each able to act on the tiers below it.
Players are ranked by Rank Value, which weighs both 2v2 and 3v3 performance:
RV = (All Time Peak 2v2 + Season Peak 2v2) × 0.3 + (All Time Peak 3v3 + Season Peak 3v3) × 0.2
RV is used for draft ordering, auto-balance team assignment, and sub eligibility limits.
Staff tiers: Moderator → Director → CEO (each tier can act on all tiers below it). Commands marked [Staff] require any staff role. [Director] requires director or CEO. [CEO] requires CEO only.
/totalplayersShows the total number of approved, registered players.
/totalusersShows the total number of users in the system across all statuses (pending, approved, rejected).
/playerinfo <username>Looks up a player's full registration info — peak/current 2v2 and 3v3 MMR, rank value, status, and tracker link.
| Option | Description |
|---|---|
username | The player's Discord username |
/setdraftchannelSets the channel where draft announcements and picks are posted. Run this command inside the target channel.
The bot opens this channel to the event role (/admin settournamentid) when a draft starts and closes it again when the draft ends, so only the current event's players can watch it while it runs. Deny @everyone View Channel on it for that to mean anything — the bot only ever touches the event role's own overwrite.
/pick <player>Picks a player for your team during the snake draft. Only usable by the captain whose turn it is.
| Option | Description |
|---|---|
player | Player to pick from the draft pool |
/openroundManually opens channels for the next round of matches. Usually unnecessary — channels now open automatically for every format: a match's channel is created once both its teams finish their prior-round games (and its category is created lazily if it doesn't exist yet). This command remains as a fallback to create any channel that auto-creation missed.
/setruleschannelSets the channel linked in match messages as the rulebook. Run this command inside the target channel.
/syncroles — [Director]Creates missing Discord roles and syncs them to all players based on current DB state. Ensures:
Registered — given to all approved players, stripped from unapprovedDrafted and Captain — assigned to active team membersRun this after making staff role or registration changes to reconcile Discord with the database. The reply lists exactly which role IDs it reconciled (Registered, Drafted, Captain, each team) and how many players were updated.
/diagrolesDiagnoses why your Discord roles may not be assigned correctly.
/assignrole <user> <role> / /removerole <user> <role>Manually assigns or removes a staff role (moderator, director, ceo) from a player.
/setmoderatorid <role> / /setdirectorid <role> / /setceoid <role>Stores the Discord role ID for each staff tier (saved on league_settings) so the bot can @mention/ping that role — e.g. tagging moderators when a sub request is escalated. These configure which Discord role maps to each tier; they do not grant a person a staff role (use /assignrole for that).
| Option | Description |
|---|---|
role | The Discord role to use for that tier |
/setregisteredrole <role>Stores the Discord role ID granted to players when their registration is approved. If set, /syncroles will use this role; if not set, the bot falls back to resolving a role named Registered by name (creating it if it doesn't exist). Storing the role ID prevents duplicates if the role is ever renamed in Discord.
| Option | Description |
|---|---|
role | The Discord role to grant on registration approval |
/settournamentid <role>Stores the Discord role ID every player on a team holds while an event is running — 1v1, 2v2 and 3v3 alike. It's granted when teams are formed and stripped when the event ends. At 2v2/3v3 players hold it alongside their team role; at 1v1 it replaces the team role entirely, since a 1v1 "team" is one player and per-team roles would mean one role per entrant. If not set, the bot falls back to resolving a role named Tournament by name, creating it if it doesn't exist. Storing the role ID means the role can be renamed in Discord without the bot minting a duplicate.
| Option | Description |
|---|---|
role | The shared role for everyone in the running event |
/setsupporterrole <role>Stores the Discord role ID granted to Patreon supporters (the "Discord role" tier benefit). This command only identifies which role to use — it does not itself grant or remove the role from anyone.
| Option | Description |
|---|---|
role | The Discord role to use for Patreon supporters |
The "bot" is the league's Discord application. It has no always-on gateway connection — it works entirely through HTTP slash-command interactions (Ed25519-verified at /api/discord/interactions) and REST calls made with the bot token from the web app's server actions. From Discord's perspective, every action below is performed by the bot user.
Roles
Registered (on approval), EnteredDraft (on joining the draft pool), Drafted and Captain (on draft/team assignment — the highest-RV player gets Captain), and Kicked. The bot creates any of these that don't exist, then adds/removes them from members as their status changes. /syncroles is authoritative for Registered — use /setregisteredrole to configure which Discord role to use, then run /syncroles to apply it retroactively to all approved players.discord_role_id you assign in the Admin → Team Slots panel. Create a Discord role per team (so you control its name, color, and hierarchy position) and paste its role ID into that team's slot. The bot then assigns/removes that role as players join, move, or leave the team, and renames the Discord role when a team is renamed. If a team has no role ID linked, /syncroles will fall back to creating a role by the team's name./settournamentid, or let the bot auto-create one named Tournament. It's added when teams are formed and stripped when the event ends. At 2v2/3v3 it sits alongside the team role; at team_size = 1 it takes the place of one, since a team is one player there and per-team roles would mean one role per entrant./syncroles reconciles every player's roles to the database (adds correct ones, removes stale ones).Channels & messages
/setdraftchannel.Moderation
Kicked role and applies a Discord timeout. It does not remove the player from the server.Access
Web push notifications (signup/draft/season events, sub requests) use the browser Push API, not Discord — they're independent of the bot.
Invite the bot with scopes bot + applications.commands and these guild permissions:
| Permission | Why it's needed |
|---|---|
| Manage Roles | create/edit/assign/remove team & status roles |
| Manage Channels | create/delete match channels & categories and set per-channel visibility |
| Ban Members | ban / unban players |
| Moderate Members | time out players (kick penalty + ban cleanup) |
| View Channels · Send Messages · Read Message History · Attach Files | read/post in match channels and accept replay uploads |
Role hierarchy matters: drag the bot's role above every role it manages (all team roles, Registered, Captain, Kicked) and above any member it must time out or ban. If the bot's role sits below a target, Discord returns 403 Missing Permissions and the action silently fails (logged server-side).
No privileged gateway intents are required — interactions arrive as signed webhooks and member data is read via REST.
Part 1 — Create the bot and add it to the server
DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, DISCORD_PUBLIC_KEY, DISCORD_BOT_TOKEN, DISCORD_GUILD_ID, DISCORD_REDIRECT_URI, ADMIN_DISCORD_IDS (see Setup below).…/api/auth/discord/callback) to the application.https://<your-domain>/api/discord/interactions (use ngrok locally — see below).node scripts/register-commands.mjs.Part 2 — Configure within Discord
staff_roles (or use an ADMIN_DISCORD_IDS account), then grant others with /assignrole./setmoderatorid, /setdirectorid, /setceoid./setregisteredrole (create a Discord role for Registered first, then link it), then run /syncroles./syncroles, then /setdraftchannel and /setruleschannel inside their target channels.Captain/Drafted are auto-created — only team roles need linking.)/admin settournamentid (optional — the bot auto-creates one named Tournament otherwise). Everyone on a team gets it for the duration of the event, and it is what the draft channel opens to while a draft is running./admin setsupporterrole.Moving an already-running league to a different Discord server is a different job — see SERVER-TRANSFER.md.
Create a .env.local file with the following:
# Discord OAuth
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
DISCORD_REDIRECT_URI=http://localhost:3000/api/auth/discord/callback
# Session
SESSION_SECRET=
# Supabase
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
# Discord Bot
DISCORD_PUBLIC_KEY=
DISCORD_BOT_TOKEN=
DISCORD_GUILD_ID=
# Admin Discord user IDs (comma-separated) — superuser fallback for staff actions
ADMIN_DISCORD_IDS=
# Discord server invite link — shown to users who must join the server before
# entering a draft/tournament. Set both to the same invite URL.
DISCORD_INVITE_URL=
NEXT_PUBLIC_DISCORD_INVITE_URL=
# Web push notifications (VAPID) — generate a key pair with:
# npx web-push generate-vapid-keys
NEXT_PUBLIC_VAPID_PUBLIC_KEY=
VAPID_PRIVATE_KEY=
[email protected]
# Cron auth — shared secret the external pinger sends as
# "Authorization: Bearer <CRON_SECRET>" to the /api/cron/* endpoints
CRON_SECRET=
Vercel deployment: All of these variables must also be added to your Vercel project under Settings → Environment Variables —
.env.localis only used for local development and is never deployed. Set the scope to Production (and Preview/Development as needed).Note: the production build evaluates the push module at build time, so the
VAPID_*keys must be present in the build environment (Vercel) or the build fails withNo key set vapidDetails.publicKey.
The app stores everything in Supabase (Postgres) and uses the service-role key for all server access, so make sure SUPABASE_SERVICE_ROLE_KEY is set.
In the Supabase SQL editor, set up the schema:
scripts/schema.sql — creates all the tables.scripts/*-migration.sql file. These add columns/constraints that were introduced over time and are idempotent (add column if not exists, drop constraint if exists), so it's safe to run them all — on a fresh database or an existing one.If a feature ever errors with "column … does not exist," a migration hasn't been applied — re-run the
scripts/*-migration.sqlfiles.
npm install
npm run dev
node scripts/register-commands.mjs
Run this from the project root. It reads .env.local automatically.
Set your app's Interactions Endpoint URL in the Discord Developer Portal to:
https://your-domain.com/api/discord/interactions
For local development, use ngrok:
ngrok http 3000
# then set: https://<your-ngrok-url>/api/discord/interactions
Deployed on Vercel via vercel --prod.
Both cron jobs (/api/cron/draft-autopick and /api/cron/tournament-scheduler) need to run every minute. The Vercel Hobby plan only allows daily crons, so vercel.json is set to a daily fallback — configure an external service (e.g. cron-job.org) to POST to both endpoints every minute with the header Authorization: Bearer <CRON_SECRET>. The autopick timer is 45 seconds. The client fires it the moment the deadline passes when anyone has the draft page open; the cron is a fallback for when nobody does.