尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Open-source browser FPS with peer-to-peer multiplayer co-op over NET and WebRTC. No game server, end-to-end encrypted. three.js, host-authoritative netcode with client-side prediction and lag compensation, a server browser, and an F4 network debug panel.
翻譯準備中
正在載入完整 README。
檢視完整 README ↗An open-source first-person shooter that runs in the browser, with peer-to-peer multiplayer co-op over NET (@net-mesh/browser 0.45.1) and WebRTC. There's no game server: the host's browser runs the match, every player links straight to it, and the traffic is end-to-end encrypted. It's built with three.js and plain JavaScript, and it's meant as a working example to copy from when you add multiplayer to your own web game.
▶ Play it in your browser · NET docs · NET browser quickstart
hostNetcode / joinNetcode).rewind).npm install
npm start
npm start builds the game into dist/ (scripts/build.mjs), rebuilds it on every change, and serves it over HTTPS at https://localhost:8081/ (WebCrypto and WebRTC need a secure context). For co-op, open it in two browsers, or two tabs. In one, go to Play → Co-op → Host game. In the other, open the server browser and join.
Co-op needs a NET anchor, which introduces players' browsers to each other. By default it uses NET's public anchor, https://anchor.ai2070.net, so there's nothing to run for it. The public anchor keeps each site's games apart: players of your page see each other's lobbies, never another site's, even one that also calls its game net-shooter.
One-time setup for npm start: mkcert, for a certificate your browser trusts. winget install FiloSottile.mkcert (or brew install mkcert), then mkcert -install.
To run your own anchor instead, npm start -- --local-anchor starts one on :8444 and prints a page URL with ?anchor= pointing at it. It needs a net-mesh with the anchor feature: download net-mesh-anchor-v0.45.1-<platform> from the NET release (x86_64 Linux and Windows), or build it with cargo install net-cli --features rtc-bootstrap. Put it at .anchor/bin/net-mesh(.exe), or set NET_MESH_BIN. The first run fills .anchor/ with the mesh key, the credential issuer key and the certificate. It's git-ignored; keep it private.
Other options:
npm start -- --host <LAN IP>: play across your LAN. Other machines must trust your mkcert CA.?anchor=https://… on the page URL: use a different anchor than the public one.ANCHOR_STATS=1, ANCHOR_VERBOSE=1|2|3: anchor diagnostics.netDebug() in the devtools console: this player's connection details.Use a regular browser. Embedded editor previews may block WebRTC.
npm run build writes the game to dist/, ready for any static host:
js/main.js, bundled with three.js and NET from node_modules and minified. NET (js/net.js) stays a separate chunk, loaded only when co-op is opened, so solo never loads it.net_leaf.js and net_leaf_bg.wasm), copied next to the chunks. NET imports it at run time from beside its own code, which no bundler can follow.index.html and css/style.css.three.js and NET are pinned npm dependencies (Renovate keeps them current); only the font still comes from a CDN (Google Fonts).
| Action | Input |
|---|---|
| Move | W A S D |
| Look | Mouse |
| Fire | Left click (hold for automatic fire) |
| Aim down sights | Right click |
| Reload | R |
| Sprint | Shift |
| Jump | Space |
| Pause | Esc |
| Network debug panel | F4 |
Solo: a walled arena with crates and cover, plus eight target dummies that patrol, take damage (headshots do double), and respawn three seconds after they go down.
Menus: main menu, mode select (Solo / Co-op), settings (player name, sensitivity, FOV, volume, saved in localStorage), controls, and a pause menu (Resume, Settings, Controls, Quit to main menu). Esc steps back out of sub-menus. Beside the main menu, a card on NET, in NET's own look, with links to its site and its browser quickstart.
Co-op over NET, 2–4 players:
listLobbies, with columns for server, region, map, players and route. Search matches server names; the region dropdown, Hide full and Hide empty filter the list; column headers sort it; Refresh fetches it again. If NET can't be reached, the list says why and the status reads "Not connected to NET".js/sim.js), and the host confirms it. Other players are smoothed between host snapshots.rewind): a shot hits what the shooter saw when they fired.Press F4 at any time, and again to close it. The panel shows NET as it runs, in your own browser, for debugging your connection or your own NET game built from this one. It's the same panel as in Rose & Blade, adapted to this game, where players connect to the host and not to each other.
node.counters(), node.rtcStats()), the ones that aren't zero.How it gets the numbers (js/net.js, under "For the network debug panel"):
openStream and each stream's send are wrapped, and incoming stream_data events are counted per peer. That covers movement and the lobby store alike.stats().clock). The host has no clock of its own, so every player reports its round trip with an input about once a second (twice a second while its panel is open), and the host's panel shows that.peerAttempt, read every half second while the panel is open.net:links entity to its snapshots, with each player's link and round trip, so that player's map shows the whole game. When no panel is open, the host stops sending it, about 3 s later.The panel only reads. Opening it changes nothing in the game, apart from those small reports and the net:links entity.
All of it is in js/net.js, which js/main.js loads only when you open co-op. A NET outage never breaks solo.
| Piece | NET API | What it does here |
|---|---|---|
| Connection | requestCredential, connect | The page asks the anchor for an anonymous credential and becomes a NET node. There's one node per page, with a fresh identity on every load. |
| Staying connected | call (to a service nobody runs) | Before browsing or hosting, and when the tab comes back to the front, the node checks its session with the anchor is still there; if not, it's replaced. |
| Hosting | createLobby | Hosts the game's store: the roster of names, colours and kills. It also publishes the listing the server browser shows. |
| Server browser | listLobbies | Lists open games: name, region, map, players, lock. |
| Joining | joinLobby, then an enlist action | Joins the store. The host checks the password in enlist, so it never leaves the host's page. |
| Movement | hostNetcode / joinNetcode | Host-authoritative tick at 30 Hz. Each client predicts itself and reconciles, and other players are interpolated. |
| Shots | netcode inputs + rewind | Shots ride along with inputs, and the host resolves them against the targets as the shooter saw them. |
| Direct links | peerAttempt, acceptPeer | The host answers joining players, so they connect peer-to-peer instead of staying on the anchor's relay. |
Things worth knowing, found while building it:
liveNode in js/net.js checks the node before it's used for that (at most every 20 s) and when the tab comes back to the front, and makes a new one if the session is gone. Never during a game.connectPeer only returns once the direct attempt has settled, which where none can be had is the whole ICE deadline. The lobby is joined through a node whose connectPeer goes on after 1.5 s, over the relayed session; the direct link, when it comes, takes over underneath. If that fast join fails, it's retried with the full wait. Netcode is handed the node without connectPeer at all, so movement starts on the session the lobby already brought up.connectPeer itself never cancels an attempt under way or re-offers on a direct pair.The game started as a solo shooter whose co-op menus were already shaped around NET: a server browser with Region and Route columns and a Host Game form, but only placeholder data behind them. NET was added in these steps:
Load NET only for co-op. js/main.js loads js/net.js with a dynamic import() the first time co-op is opened, so solo never loads NET. (It first came from the jsDelivr CDN; it's now an npm dependency, bundled into its own chunk.)
Share the movement rules. Player movement and collision moved out of js/game.js into js/sim.js as a pure stepPlayer(state, input) function with no three.js in it, next to the arena layout. Solo, the co-op host and each client's prediction all run the same function, so the host and clients agree on movement (0 prediction corrections in testing).
Write the session layer (js/net.js). hostGame and joinGame return a small session object that the game uses: input(), entities(), roster(), teamKills(), onEnd(), leave(). Everything NET-specific stays behind it: lobby, store, netcode and connection handling.
Add a co-op mode to js/game.js.
resolveShot(), moving targets back to where the shooter saw them (netcode rewind) before raycasting.Connect the menus. The server browser's placeholder generator was replaced with listLobbies. The Join button now joins, with a password box for locked games. The Host Game form now calls createLobby with its settings. Settings gained a player name.
Add a local anchor. scripts/dev.mjs (npm start -- --local-anchor) runs net-mesh anchor serve for the game ID net-shooter, creating the mesh key, issuer key and mkcert certificate on first run.
Test with real browsers. A Playwright script ran the whole flow with separate browser contexts: host with a password, find the game in the browser, wrong and then right password, move, shoot, close the tab, re-host. It found and fixed these issues:
index.html markup for the HUD and every menu screen
css/style.css styling
js/main.js entry point; game state (menu / playing / paused), pointer lock
js/game.js scene, camera, player controls, rifle, targets, co-op rendering
js/sim.js arena layout and player movement (shared by solo, co-op host and prediction)
js/net.js NET: lobbies, host/join, netcode, connection handling (loaded only for co-op)
js/servers.js the server browser
js/ui.js menu screen stack, settings, host form
js/hud.js HUD: ammo, kills, hit marker
js/audio.js synthesized sound effects (Web Audio)
js/toast.js notification toast
js/netdebug.js the network debug panel (F4)
js/netrain.js the NET card's packet rain, beside the main menu
scripts/build.mjs npm run build: the game into dist/
scripts/dev.mjs npm start: build and watch, HTTPS page server, optional local NET anchor
docs/ README screenshots and the social preview image
MIT. Use it, fork it, ship it. three.js and NET keep their own licenses (NET is MIT or Apache-2.0).
How the browser maps to NET:
shot:<id> entities.Some lessons came from an earlier NET test game and were built in from the start:
acceptPeer, so joins go direct instead of waiting about 20 s for the direct attempt to time out.