BeamNG.Earth
BeamNG.drive용 1:1 비율 실시간 지구, 실제 고도·위성 이미지·부동 원점·보조 서버에서 LOD 스트리밍
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Play N64 games online with friends in your browser — no install needed. WebRTC netplay powered by EmulatorJS.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Play retro games online with friends — no downloads, no emulator setup. Visit the URL, drop your ROM, and play.
kaillera-next is a browser-based netplay platform built on EmulatorJS (mupen64plus-next WASM core). Players connect through WebRTC for low-latency peer-to-peer gameplay, with the server handling only room management and signaling.README 전문 보기 ↗Play retro games online with friends — no downloads, no emulator setup. Visit the URL, drop your ROM, and play.
kaillera-next is a browser-based netplay platform built on EmulatorJS (mupen64plus-next WASM core). Players connect through WebRTC for low-latency peer-to-peer gameplay, with the server handling only room management and signaling.
Rollback — All players run the emulator in sync. Inputs are exchanged every frame over WebRTC DataChannels in a full mesh (up to 4 players, 6 connections). Uses a patched mupen64plus-next WASM core with deterministic timing (_kn_set_deterministic, _kn_set_frame_time) and a C-level GGPO-style rollback engine (_kn_pre_tick / _kn_post_tick) that predicts remote inputs and replays on misprediction, keeping responsive play under ~150 ms RTT. Strict lockstep is the silent stall fallback inside the same engine (boot convergence, menu phase-lock gate, and stock cores without kn_pre_tick). Delay is symmetrically negotiated via host-authoritative broadcast at game start and works mobile↔mobile with bit-identical state across iOS Safari and desktop Chrome.
Streaming — Host runs the only emulator and streams the canvas as video to guests via WebRTC MediaStream. Guests send controller input back over a DataChannel. Zero desync by design — only one emulator instance exists. SDP is optimized for low-latency gaming (H264/VP9 preference, high bitrate floor, minimal jitter buffer).
Both modes support:
Gamepads work alongside keyboard controls; phones and tablets also get on-screen touch controls.
If a controller's buttons land in the wrong place, open Configure Controller in the room, before the game starts, and press Quick Setup to remap them one at a time. Mappings are saved in your browser, and the same panel has stick deadzone and sensitivity settings.
To use a Switch 2 Pro Controller:
You only do this once. After that first permission grant the controller is re-enabled automatically each time it is plugged in, and the Connect button hides itself while a granted controller is plugged in; press any button to wake it, as with any controller.
Limits: this needs a browser with WebUSB (Chrome; Edge should work), so Safari and Firefox are out. Bluetooth is not supported (the controller uses a custom BLE protocol). On Windows the browser may need the WinUSB driver to open the controller (for example installed with Zadig), and on Linux it may need a udev rule; neither is tested.
Requires Python 3.11+, uv, and just.
# Install dependencies
just setup
# Start the server (HTTP, no Redis)
just serve
# → http://localhost:27888/
This is the fastest path to a running server. Over plain HTTP you can explore the lobby, create rooms, and use streaming mode. Rollback mode requires HTTPS — see below.
| Feature | HTTP | HTTPS |
|---|---|---|
| Lobby, rooms, Socket.IO | works | works |
| Streaming mode | works | works |
| Rollback mode | broken (needs SharedArrayBuffer) | works |
| High-res frame timing | degraded (~1ms vs ~5μs) | works |
Browsers gate SharedArrayBuffer and performance.now() precision behind
cross-origin isolation,
which requires a secure context (HTTPS). The server sends the required
COOP/COEP headers automatically — you just need HTTPS.
# One-time: generate HTTPS certs (see HTTPS setup below)
just certs
# Start dev server (Redis + HTTPS)
just dev
# → https://<your-domain>:27888/
HTTPS is required — browsers need crossOriginIsolated (SharedArrayBuffer, high-res timers) which only works over secure contexts. just certs issues a real Let's Encrypt certificate via lego using the Cloudflare DNS-01 challenge, so the cert is trusted by every device (including mobile) without installing a local CA. The subdomain never needs to resolve publicly — point it at your LAN IP via local DNS (AdGuard, Pi-hole, /etc/hosts, etc.).
brew install lego # macOS
# or download a release: https://github.com/go-acme/lego/releases
In the Cloudflare dashboard, create a token with Zone:DNS:Edit scoped to the zone you'll use.
Add to server/.env:
[email protected]
LEGO_DOMAIN=kn-test.example.com
CLOUDFLARE_DNS_API_TOKEN=your-scoped-token-here
Then:
just certs
Certs land in certs/. Re-run just certs to renew (Let's Encrypt certs expire every ~90 days).
mkcert works for localhost but requires installing its CA on every mobile device. Prefer the lego flow for cross-device testing.
docker build -t kaillera-next .
docker run -p 27888:27888 -e ALLOWED_ORIGIN="https://yourdomain.com" kaillera-next
The Docker image runs as a non-root user with a health check on /health.
docker stack deploy -c docker-compose.prod.yml kaillera-next
Includes Redis for session persistence (blue-green deploys, reconnect survival) and persistent log volumes.
┌──────────────────┐ ┌──────────────────┐
│ Browser A │ │ Browser B │
│ EmulatorJS + │◄──P2P──►│ EmulatorJS + │
│ WebRTC mesh │ WebRTC │ WebRTC mesh │
└────────┬─────────┘ └────────┬─────────┘
│ Socket.IO │
└────────────┬───────────────┘
▼
┌─────────────────────┐
│ kaillera-next │
│ FastAPI + Socket.IO │
│ + Redis │
│ :27888 │
└─────────────────────┘
The server handles room creation, player coordination, and WebRTC signaling. Once peers are connected, game data flows directly between browsers — the server is idle during rollback gameplay. Redis persists room state across deploys and reconnects.
All frontend assets are self-hosted (EmulatorJS, Socket.IO) — zero CDN dependencies. The server sends Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp headers to enable crossOriginIsolated on all browsers, unlocking SharedArrayBuffer and high-resolution performance.now().
server/ Python signaling server (FastAPI + Socket.IO + uvloop)
src/
main.py Entry point — serves API, Socket.IO, and static frontend
state.py Redis-backed room persistence
ratelimit.py Per-IP rolling-window rate limiting
db.py Database: SQLite or Cloudflare D1 backend, SQL migrations in server/migrations/
api/
app.py REST endpoints, security headers (COOP/COEP/CSP), admin API
signaling.py Socket.IO events — rooms, WebRTC relay, ROM sharing, game data
payloads.py Pydantic v2 payload models + @validated decorator
og.py Open Graph meta-tag injection + build-time-rendered OG cards (no runtime browser)
web/ Static frontend (HTML + JS, served by FastAPI)
index.html Lobby — create/join rooms, invite links
join.html Invite page (/join?room=CODE) — who invited you, Join vs Watch, waking
play.html Game page — overlay, EmulatorJS embed, toolbar
admin.html Admin dashboard (session logs, events, feedback, screenshots)
privacy.html Privacy notice — what's collected (diagnostics, screenshots), opt-out
demo.html Interactive rollback demo (user-supplied ROM required)
lag-test.html Standalone dev harness for lagviz.js
error.html Error/fallback page
static/
play.js Play page orchestrator (Socket.IO, overlay, ROM handling, engine dispatch)
landing.js Front-page board controller (room creation, open-rooms board)
landing.css Front-page styles (direction A tokens)
join.js Invite-page controller (room lookup, join/watch states)
lagviz.js Waking-state lag visualizer (front page + invite page)
admin.js Admin dashboard logic (sessions, events, feedback, screenshots)
admin.css Admin dashboard styles
play.css Play page styles (room overlay tokens, direction A)
netplay-rollback.js Primary engine: GGPO-style C-level rollback (4P mesh WebRTC, prediction + replay); strict lockstep is the silent stall fallback inside this file
rollback-shadow-worker.js Shadow worker: presentation overlay, plus a Mode 2 replay coprocessor whose corrected state main adopts
netplay-lockstep.js Deprecated compat shim (re-fetches netplay-rollback.js for cached play.html pages that still request the old filename)
netplay-streaming.js Streaming engine (host video → guests via WebRTC MediaStream)
gamepad-manager.js True analog gamepad input (3-stage pipeline: deadzone → scale → N64 quantize)
switch2-usb.js WebUSB handshake for the Switch 2 Pro Controller
controller-settings.js In-game controller settings panel
virtual-gamepad.js On-screen touch controls for mobile
shared.js Input encoding/decoding, wire format, input application to WASM
kn-audio.js AudioWorklet pump + rollback audio capture
kn-capture.js Diagnostic gameplay-canvas capture opt-out policy (localStorage flag)
kn-desync-detector.js Field-granular cross-peer desync detection (RDRAM probes)
kn-diagnostics.js Rollback diagnostics ring buffer (RB-CHECK, audit log)
kn-vision-client.js Claude vision client for screenshot-diff desync analysis
audio-worklet-processor.js AudioWorklet ring buffer for rollback audio
core-redirector.js Redirect EJS core download to patched WASM, IDB cache management
api-sandbox.js Browser API interception (rAF, performance.now, getGamepads)
storage.js Safe localStorage/sessionStorage wrapper
kn-state.js Shared cross-module state
feedback.js In-app feedback collection
version.js Version display + changelog modal
version-guard.js Cross-tab version mismatch guard (forces reload after deploys)
ejs-loader.js EmulatorJS loader shim (boot + script setup hooks)
demo.js Demo page orchestrator (fake peer + menu autopilot)
fake-peer.js Synthetic P2 for the demo page (simulated input/jitter)
ssb64-menu-autopilot.js Closed-loop menu driver for the demo (reads RDRAM menu state)
rollback-timeline.js Demo page's live rollback-metrics HUD
socket.io.min.js Self-hosted Socket.IO client (v4.8.4)
vendor/fflate.min.js Self-hosted compression lib used to extract ROM ZIP archives
ejs/ Self-hosted EmulatorJS runtime, compression libs, localization
ejs/cores/ Patched mupen64plus-next WASM core
build/ WASM core build system (Docker + C patches)
tests/ E2E tests (pytest + Playwright) + VGP visual regression
docs/ Roadmap, MVP plan, design specs
certs/ TLS certificates for HTTPS dev (gitignored)
The pre-built patched core is included at web/static/ejs/cores/. You only need to rebuild if you modify the C patches.
The build compiles a patched mupen64plus-next core with:
_kn_set_deterministic, _kn_set_frame_time)_kn_sync_hash, _kn_sync_read, _kn_sync_write, _kn_sync_hash_regions)_kn_get_audio_ptr, _kn_get_audio_samples, _kn_reset_audio, _kn_get_audio_rate)-fno-fast-math, -ffp-contract=off)wasm-opt --denan (injected before --asyncify)srand(0)) and RTC# 1. Build the Docker image with Emscripten SDK (one-time, ~10 min)
docker build -t emulatorjs-builder build/
# 2. Compile the patched core (~5-15 min depending on CPU)
docker run --rm -v $(pwd)/build:/build emulatorjs-builder bash /build/build.sh
# 3. Deploy to web/static/ejs/cores/ (all three must come from the same build)
cp build/output/mupen64plus_next-wasm.data web/static/ejs/cores/
cp build/output/mupen64plus_next_libretro.js web/static/ejs/cores/
cp build/output/mupen64plus_next_libretro.wasm web/static/ejs/cores/
The build clones EmulatorJS's forks of mupen64plus-libretro-nx and RetroArch, applies patches from build/patches/, compiles to LLVM bitcode, links through RetroArch with asyncify, and packages into a 7z .data archive that EmulatorJS loads at runtime.
| Patch | Description |
|---|---|
mupen64plus-kn-all.patch | Core exports: deterministic timing, resync hash/read/write, audio capture |
mupen64plus-deterministic-timing.patch | features_cpu.c and profile.c timing fixes |
mupen64plus-wasm-determinism.patch | Strict FP compile flags, FPU NaN canonicalization, srand(0), deterministic RTC |
mupen64plus-ai-determinism.patch | AI DMA deterministic audio interface timing |
mupen64plus-fpu-trace.patch | FPU operation tracing for cross-platform determinism verification |
mupen64plus-softfloat.patch | SoftFloat FPU for bit-exact cross-platform floating point |
retroarch-deterministic-timing.patch | RetroArch _emscripten_get_now() override + AUDIO_FLAG_SUSPENDED bypass |
| Variable | Default | Description |
|---|---|---|
ALLOWED_ORIGIN | * | CORS origin — set to your domain in production |
PORT | 27888 | Server listen port |
MAX_ROOMS | 100 | Maximum concurrent rooms |
MAX_SPECTATORS | 20 | Maximum spectators per room |
REDIS_URL | — | Redis connection URL (required for session persistence) |
REDIS_HOST | redis | Redis host (used if REDIS_URL not set) |
REDIS_PORT | 6379 | Redis port |
REDIS_PASSWORD | — | Redis password |
ADMIN_KEY | — | Admin API key for management endpoints |
LOG_RETENTION_DAYS | 7 | Auto-delete old session data after N days |
FLAGGED_STALE_DAYS | 180 | Auto-delete unresolved flagged matches after N days |
DB_PATH | data/kn.db | SQLite database file path |
D1_DATABASE_ID, D1_API_TOKEN, CF_ACCOUNT_ID | — | Switch to the Cloudflare D1 backend instead of SQLite |
IP_HASH_SALT | random | Salt for IP address hashing (random per restart if unset) |
KN_PROXY_SECRET | — | Shared secret trusting the landing Worker's forwarded client IP |
PUBLIC_ORIGINS | — | Extra origins allowed to read /health, /list, /room/*, /api/stats/public |
TRUSTED_PROXY_IPS | — | Comma-separated trusted proxy IPs for X-Forwarded-For |
Public:
| Endpoint | Description |
|---|---|
GET /health | Health check |
GET /list?game_id=... | Room listing (EmulatorJS-Netplay compatible) |
GET /room/{room_id} | Room info (rate-limited) |
GET /ice-servers | ICE/TURN server configuration |
GET /api/cached-state/{rom_hash} | Retrieve cached save state |
POST /api/cache-state/{rom_hash} | Upload save state to cache |
POST /api/session-log | HTTP fallback for session log flush |
POST /api/client-event | Client event logging |
POST /api/feedback | Feedback submission |
GET /api/rom-hashes | Known ROM hashes |
GET /api/core-info | Content-hashed WASM core build info |
GET /api/version | Static asset version |
GET /api/landing-build | Content-hash build id shared with the landing Worker |
GET /api/stats/public | Matches-started-this-week counter for the front page |
GET /room/{room_id}/card.jpg | Invite card image (room name + live frame) for social sharing |
GET /room/{room_id}/frame.jpg | Latest board frame for a listed, in-game room |
GET /privacy, GET /privacy.html | Privacy notice page |
Admin (requires ADMIN_KEY):
| Endpoint | Description |
|---|---|
GET /admin/api/stats | Server statistics |
GET /admin/api/session-logs | List session logs |
GET /admin/api/session-logs/{log_id} | Download a session log |
GET /admin/api/client-events | List client events |
GET /admin/api/client-events/{event_id} | Client event detail |
GET /admin/api/feedback | List feedback |
GET /admin/api/feedback/{feedback_id} | Feedback detail |
GET /admin/api/screenshots/{match_id} | List screenshots for a match |
GET /admin/api/screenshots/img/{screenshot_id} | Serve screenshot image |
GET /admin/api/session-logs/{log_id}/export | Stream a session log's entries as newline-delimited JSON |
GET /admin/api/matches | List recently-rotated matches with precomputed metrics |
GET /admin/api/matches/{match_id} | One match's precomputed metrics row |
GET /admin/api/input-audit/{match_id} | Per-slot input audit for a match |
GET /admin/api/session-timeline | Session timeline; requires match_id or room query parameter |
GET /admin/api/desync-events | List vision-validated desync events; requires match_id query parameter |
V1 is feature-complete and deployment-ready:
GPL-3.0
TURN_SECRET| — |
| HMAC secret for time-limited TURN credentials |
TURN_SERVERS | — | Comma-separated TURN server URLs |
ICE_SERVERS | — | Legacy static ICE server config (JSON) |
CF_TURN_KEY_ID, CF_TURN_API_TOKEN | — | Cloudflare Realtime TURN credentials for /ice-servers |
PER_IP_SPECTATOR_CAP | 3 | Max spectator slots one IP can hold in a room |
ROM_SHARING_ENABLED | true | Enable host ROM P2P sharing |
GAME_IMAGES_ENABLED | true | Read by og.py but not currently wired to anything — OG-card game images are always on |
ANTHROPIC_API_KEY, DESYNC_VISION_ENABLED | — | Enable the Claude desync-vision endpoint (DESYNC_VISION_ENABLED=1) |
DISABLE_HTTPS | — | Set to disable HTTPS even when certs are present |
DISABLE_RATE_LIMIT | — | Set to 1 to disable per-IP rate limiting (dev only) |
DEBUG_MODE | — | Enables debug-sync and local log files; debug-logs writes to stdout even when unset |
GIT_VERSION | — | Override version string (set automatically in Docker) |
KEEPALIVE_SECONDS | 0 (off) | How often play pages ping /health |
RENDER_EXTERNAL_URL, RENDER | — | Render.com-provided external URL / platform flag (CORS origin, trusted-proxy behavior) |
BUDGET_AUTO_FLAGGED_BYTES | 200 MiB | Byte budget for auto-flagged match blob retention |
PARQUET_DIR | data/parquet | Output dir for rolled-up match-log Parquet files |