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
실시간 비대칭 멀티플레이 강도극, 한 명은 시야 제한, 팀이 시설을 보고 도둑을 안내해 탈출
A real-time asymmetric multiplayer heist. One player can’t see the whole picture. Your crew sees the facility, guides the thief, and finds the way out.
게임TypeScript · CSSGitHub @abhishiv17온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
An asymmetric multiplayer heist built with React Three Fiber + Rapier, out of nothing but
boxes, cylinders and cones. One player walks the building blind; everyone else can see the
security layer, but each of them is stuck watching a single room.
A room fills up, a ten second countdown runs, then the roles are drawn:
• one thief, first person, starting on the street outside the entrance;
• everyone else a spectator, each posted to exactly one room — lobby, security or vault — and
they cannot leave it. Three spectators covers the building; fewer means blind spots.README 전문 보기 ↗An asymmetric multiplayer heist built with React Three Fiber + Rapier, out of nothing but boxes, cylinders and cones. One player walks the building blind; everyone else can see the security layer, but each of them is stuck watching a single room.
npm run dev
| Route | What it is |
|---|---|
/ | What the game is, and how a run goes. |
/rooms | Create a room (2–4 players) or join with a code. |
/room/[code] | Name gate → lobby → the run. Share this link; anyone who opens it joins. |
/play | Solo sandbox: you drive the thief and can look through all three layers. |
A room fills up, a ten second countdown runs, then the roles are drawn:
Rooms fill in a fixed order — lobby, then security, then vault — so the set covered is always the sensible one; only who gets which is random. The exception is a crew of one: a single spectator would be stuck in one room while the run needs another, so they roam with the thief instead and are always on the command channel.
The draw is deterministic from a seed on the room record, so every client lands on the same result the moment the clock hits zero — nothing waits on the host's tab being awake.
Every view carries a 2D floorplan in the top right with a live dot for the thief. Spectators also see guards in their own room on it; the thief does not get guard positions.
4712.
The alarm panel in here can be disabled with E, blinding every camera.E at the keypad. Without the code
relayed from a spectator, it refuses.Cameras sweep, two guards patrol and give chase once the alarm is up, floor traps hurt, and the health pack and bandages patch you up. None of that is visible to the thief.
| View | Who | What it shows |
|---|---|---|
| Thief | the thief | A normal facility. No labels, no cones, no traps. |
| Spectator ("Watch") | spectators, solo | Their room as a cutaway: camera cones, the guard, the keypad, the keycard. |
| Discovery | spectators, solo | Same room plus pulsing blips over what nobody has found yet. Click to scan. |
Anything a spectator scans stays tagged for everyone in the Watch layer — found things become shared knowledge. A room a spectator is not posted to stays sealed to them.
Desktop:
WASD move, Shift run, Space jump, E interact1 / 2 / 3 switch viewPhone (anything with a coarse pointer):
E and JUMP on the right. The jump
button becomes EXIT when they are standing in the extraction vent.E). That opens the vault and
releases the extraction vent.A spectator posted to the vault can scan the vent open early, before the thief ever reaches the keypad. The 4-digit code on the note in the security room is a second way to satisfy the keypad, not a requirement - a run is always finishable with the keycard alone.
The authoritative Colyseus path receives thief input, steps physics, guards and detection on the
match server, and publishes role-scoped world snapshots 12 times a second. Spectators never step
the physics world; they fold each snapshot back into the same runtime the renderers already read,
and send validated discover and command messages the other way. Colyseus rooms retain a
disconnected player for twenty seconds so a normal network drop can rejoin the same session.
Transports sit behind one small interface (app/game/net/types.ts):
spacetime (default) — rooms and world snapshots use the published SpacetimeDB module.
This remains available for compatibility with the published module.colyseus (authoritative) — set NEXT_PUBLIC_MATCH_TRANSPORT=colyseus and point
NEXT_PUBLIC_COLYSEUS_URL at the long-lived match server. This is the production path for
server-owned Crew matches; it must run as a WebSocket-capable service, not as a serverless
request function.The module in spacetime/src/index.ts is written for exactly this flow and typechecks
against [email protected]:
game_room, player, thief_state, discovered_item, game_eventcreate_room, join_room, leave_room, start_run, draw_roles, publish_world,
discover_item, log_event, end_rundraw_roles uses the same seeded shuffle as the web client, so client-side prediction and the
server agree on who gets to be the thief.The browser-side adapter is wired in app/game/net/spacetimeNet.ts, with the generated-compatible
client schema in app/game/net/spacetime.ts. That file has to mirror the published module
exactly — SpacetimeDB addresses tables and reducers by index, so one extra column or reducer
shifts every id after it and the first subscription update fails to decode. Check it against the
live schema before changing it:
curl https://maincloud.spacetimedb.com/v1/database/one-heist-spacetime/schema?version=9
spacetime/src/index.ts is currently ahead of what is deployed (it adds spectator
disconnect-grace bookkeeping); the client bindings track the deployed module, not this source.
The deployed client uses the module named by NEXT_PUBLIC_SPACETIME_MODULE_NAME (default: one-heist-spacetime) at
NEXT_PUBLIC_SPACETIME_HOST (default: wss://maincloud.spacetimedb.com).
Spectator commands use the existing log_event reducer with a reserved command:* tone, so the
command deck works with the published module without a schema migration. These events are filtered
out of the normal game log and delivered to the thief as live command messages.
app/
page.tsx landing
rooms/page.tsx create / join
room/[code]/ name gate, lobby, countdown, then the run
play/page.tsx solo sandbox
api/rooms/ room registry + SSE stream
lib/roomStore.ts in-memory rooms, join / start / broadcast
spacetime/ SpacetimeDB module (tables + reducers)
packages/ shared contracts, layout and deterministic simulation
server/ authoritative Colyseus room and production entrypoint
docs/, HACKATHON_SPEC.md the original design notes
game/
GameShell.tsx HUD: role, layer switch, minimap, discovery panel, log
GameCanvas.tsx <Canvas> + <Physics> (paused on spectators) + keyboard map
store.ts game state, and who is allowed to see which room
session.ts rooms, players, countdown, the seeded role draw
runtime.ts per-frame world state that must not re-render React
level.ts floorplan: rooms, wall runs, doors, markers, patrols, cameras
net/ transport interface + SSE server transport
components/
Building.tsx walls cut around openings, floors, ceilings, doors, room fog
Exterior.tsx ground, sky, plaza, lamps, skyline, extraction pad
Rooms.tsx per-room furniture, the round vault door
Furniture.tsx shared props
Interactables.tsx keycard, keypad, alarm panel, traps, vent, med kits, loot
Markers.tsx neon outlines, labels, clickable discovery blips
Thief.tsx local (physics) and remote (streamed) thief
Guard.tsx local patrol AI and remote guards
SecurityCameras.tsx sweeping cameras and cones
Systems.tsx line of sight, alarm, damage, room tracking, prompts (host only)
NetSync.tsx snapshot publish / apply
ViewRig.tsx cameras, controls and per-view lighting
Minimap.tsx the 2D floorplan
Three rules keep the layers honest:
level.ts is the single source of truth. Every marker carries a room and a
reveal: "spectator" | "discovery"; useRoomVisible is the one place that answers "can this
client see inside this room".The production Colyseus topology has two services:
npm run build and npm run start.npm run server:start.NEXT_PUBLIC_COLYSEUS_URL to the public HTTPS origin of the match service.
The Colyseus client upgrades that endpoint to secure WebSockets in production.HOST=0.0.0.0 and a platform-provided PORT.GET /healthz; it returns a JSON ok response only
after the Colyseus HTTP/WebSocket service is listening.Do not use the local http://localhost:2567 fallback in production. The client intentionally
fails with a clear configuration error when NEXT_PUBLIC_COLYSEUS_URL is missing from a production
build. Copy .env.example to the deployment platform's environment settings and provide secrets
there; never commit .env or provider keys.
Production Google sign-in requires these public Vercel variables:
NEXT_PUBLIC_SPACETIME_AUTH_CLIENT_ID=your-spacetimeauth-client-id
NEXT_PUBLIC_SITE_URL=https://your-production-domain
Enable Google in the SpacetimeAuth project and allow https://your-production-domain/ as the
client redirect and post-logout URI. Publish the module with an account that owns or collaborates
on one-heist-spacetime before deploying the frontend, because the counter and profile tables are
part of the client schema.