A local RDR2 map using React/TypeScript/Leaflet, Express/SQLite and a Python extractor. Category symbols and waypoint photographs are extracted from RDR2, stored locally, and displayed in filters, canvas markers and a keyboard/touch gallery. Found progress is independent of imports. Existing pan/zoom, category filters and found visibility remain.
Use Node 22.22.3+ within Node 22 and Python 3.11 or 3.12. The pipeline dependencies are pinned separately from the optional legacy tile downloader. From the repo root:
npm ci
npm run install:all
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r rdr2_extractor/requirements-pipeline.txt
npx --prefix frontend playwright install chromiumA normal Chrome/Chromium installation is needed only for live extraction. Offline
fixtures and checks do not contact RDR2. To use an existing Chrome for Playwright,
export CHROME_PATH=/absolute/path/to/chrome instead of downloading its browser.
npm run demo -- data/demo
npm run import -- data/demo/dataset.json --db data/demo/map.sqlite --data-root data/demo
DB_PATH="$PWD/data/demo/map.sqlite" DATA_ROOT="$PWD/data/demo" npm run dev:apiIn another terminal:
VITE_TILE_SOURCE=none npm run dev:web -- --host 127.0.0.1Open http://localhost:5173. Demo pictures/symbols are generated geometry, not game assets. An empty installation also starts with zero points; extraction/import supplies data.
The populated legacy DB is normally backend/data/rdr2.db, version 0. Stop the old
app. Follow migration to test a SQLite backup copy first, then:
npm run migrate -- --db backend/data/rdr2.db
npm run import -- data/all-public/dataset.json --db backend/data/rdr2.db --data-root data
DB_PATH="$PWD/backend/data/rdr2.db" DATA_ROOT="$PWD/data" npm run dev:apiCreate data/all-public/dataset.json using the full extraction below before importing.
Migration creates a consistent backup and verifies all legacy values. Import requires
an explicit dataset path; it never resets progress or removes absent points/categories.
A later partial capture does not erase previously downloaded media. Progress reset
is a separate explicit API operation. See the migration document for restoration.
With the Python environment active, capture the public source and download a small sample (Blazing Star, a two-photo bone, a point without photos and map controls):
python -m rdr2_extractor.pipeline --output data/sample --sample 6
python -m rdr2_extractor.pipeline --output data/sample --resume
python -m rdr2_extractor.pipeline --output data/sample --phase validateOutput includes dataset.json, report.json, local icons/ and images/, plus
an ignored source capture. Resume keeps the original selection and verifies existing
files. To use all public points, choose a new output and --sample 0. Coverage stays
partial when the source omits premium categories. --phase normalize exports without
media downloads; --phase discover captures without normalization/downloads.
For all public points and their photographs, extract and import the same output:
./rdr2_extractor/run.sh --output data/all-public --sample 0
./rdr2_extractor/run.sh --output data/all-public --phase validate
npm run import -- data/all-public/dataset.json --db backend/data/rdr2.db --data-root dataThe default is --sample 6, regardless of the output directory name. A report with
waypoints: 6 and mediaComplete: true means media is complete for those six points
only. Importing it leaves other existing points unchanged, so they can still show
“Photographs have not been inspected yet.” This means discovery is unknown, not that
a photograph download failed. none means the inspected source has no photographs.
If data/all-public already contains a six-point sample, rerun the first command
without --resume to normalize the full capture. To continue an interrupted full
download, use ./rdr2_extractor/run.sh --output data/all-public --resume.
Resume preserves the saved selection even when combined with --sample 0.
After importing, reload the map to fetch the updated detail data.
Reuse an allowed offline capture, without recapturing the site:
python -m rdr2_extractor.pipeline --capture data/sample/capture.json --output data/enriched --sample 6For an old extraction whose raw_data.mapData contains source locations/media,
combine its data with the current sprite discovery:
python -m rdr2_extractor.pipeline --capture data/sample/capture.json --legacy-capture /absolute/path/to/extracted_data.json --output data/legacy-enriched --sample 0
npm run import -- data/legacy-enriched/dataset.json --db backend/data/rdr2.db --data-root dataNo tile download is required for enrichment. Existing tiles remain in
rdr2_extractor/data/tiles/zoom_Z/X_Y.jpg. TILES_DIR can point elsewhere.
The retained optional tile command is documented in extractor README.
All downloaded assets and captures are excluded from Git; see third-party notices.
npm run dev:api
npm run dev:web -- --host 127.0.0.1
# Or start both local processes:
./start.shBackend defaults: port 3001, DB backend/data/rdr2.db, media data/, existing
extractor tiles. Export HOST, PORT, DB_PATH, DATA_ROOT, TILES_DIR, STATIC_ROOT to override; use
absolute filesystem paths when starting from a different directory. Backend does not
implicitly load .env. Frontend defaults to relative /api URLs and VITE_TILE_SOURCE=local|none.
Vite proxies /api to API_TARGET (default http://127.0.0.1:3001);
WEB_HOST and WEB_PORT configure its development listener. Production serves
the compiled frontend from STATIC_ROOT through the API, with no Vite process.
With the Python environment active:
npm run contracts:check
npm run lint
npm run types
npm test
npm run build
npm run test:e2enpm run check runs the same sequence. E2E creates temporary data/DB and local
servers on 3903/5177, blocks external requests and never touches personal progress.
CI uses only synthetic assets. npm run contracts regenerates TypeScript declarations
from the JSON schema. QA evidence distinguishes offline tests,
real-source inspection, performance measurements and remaining external limitations.
The supported homeserver path, including rootless Podman, Docker compatibility,
dataset import, GHCR publication, systemd user startup, backups and rollback is
documented in docs/deployment.md. It defaults to the
separate RDR2 installation on LAN port 8081 via SSH alias baphomet; keep
.env.docker local and use an
IP address or local DNS name when opening the map from a phone.
GET /api/markerssupports existing bbox/category_ids/limit/offset queries.GET /api/markers/categoriesreturns counts and local category asset metadata.GET /api/markers/:idreturns the ordered local gallery and discovery state.GET /api/assets/:idserves verified PNG/JPEG/WebP media.GET /api/tiles/:z/:x/:y.jpgserves existing local tiles.GET /api/progress,POST /api/progress/:idread/update found status.POST /api/progress/resetexplicitly resets progress.
See architecture, data contract, source discovery, reference decisions, and contributing. Agent instructions are centralized in AGENTS.md; CLAUDE.md imports them, and other Markdown agents use the same workflow.
Public source coverage excludes premium locations; no access bypass is attempted. Uninspected/failed discovery, pending/failed download and missing local files remain visible distinct states. This repository distributes code and synthetic fixtures, not downloaded game media, a deployed site or cloud services.
The source code is licensed under MIT. The license does not grant rights to third-party game assets; see third-party notices.