- Python 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| bf6sim | ||
| docs | ||
| .gitattributes | ||
| .gitignore | ||
| bf6_extract.py | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
bf6-datamine
Battlefield 6 weapons research toolkit: a live game-file extractor and a Python port of the raymdl BF6 Weapon Analyzer simulation, plus the research docs produced with them.
Python 3.13, uv-managed, stdlib only.
Layout
bf6_extract.py EBX datamine extractor (toc_bf6 driver + GameRemixer/parser)
bf6sim/ weapon sim package (attachments, recoil, spread, damage, ballistics, TTK)
docs/
gunplay-research.md build research + verified in-game tables (master report)
mechanics.md game mechanics tech reference (patch 1.4.3.0 baseline)
raymdl-model-notes.md condensed compilation of the analyzer's model docs
extracts/ output of the extractor, per date (gitignored, regenerable)
tools/ ResHax BF6 TOC tools (gitignored, see below)
Setup
uv sync
tools/ needs id-daemon's ResHax BF6 tools (toc_bf6.exe + DLLs). If missing, the extractor
says where it looked; pass --tools <dir> to override.
Extractor
Dumps EBX from the installed game (Steam, Data\ root required — layout.toc anchor), then
parses GameRemixer live-tuning values, killswitches, attachment wrappers, damage curves, WB
raws. Reading game files is passive and anticheat-safe; never write game files or install mods.
uv run bf6_extract.py --out extracts/<patch> # full run: dump + parse (~5-10 min)
uv run bf6_extract.py --skip-dump --dump-dir <dir> ... # reparse existing dumps
uv run bf6_extract.py --skip-dump --dump-dir <d> --site-dir <analyzer-data> # + site merge
Outputs per extract: weapons.json (all live grx/kst fields + site layer + ingame_name
from game files), attachments.json (name/slug/points cost/modifier GUIDs),
damage_curves.json (per-caliber StartDamage/EndDamage/falloff/drag/gravity),
hit_zone_constants.json, generic_tweakables.json, meta.json, site/ (raymdl data JSONs).
Simulator
uv run bf6sim weapons <filter> --class ar,smg # class aliases: ar, carbine, smg, lmg, dmr, sr, sg, sidearm
uv run bf6sim atts tr7 # per-slot attachments: pts + stat deltas vs stock (+ legend)
uv run bf6sim atts tr7 --slot muzzle # single slot
uv run bf6sim stats tr7
uv run bf6sim build tr7 --muzzle long_supp --grip bcg_light --mag 20_rnd # applied stats + pts
uv run bf6sim ttk tr7 --ranges 0,21,50 --zone head --headshots 0
uv run bf6sim spread tr7 --shots 20 --move
uv run bf6sim recoil tr7 --seed 0 --comp 50 --hip
uv run bf6sim cadence tr7 --mode tap --tap auto --range 15 --target head --runs 2000
Every command takes --json for machine-readable output (full applied stat vector included).
Unknown attachment ids fail closed with nearest-match suggestions and exit code 1.
Cadence, compensation and hit probability (cadence)
cadence fires a string under a controlled cadence and scores it against a target, so recovery
packages, grip rows and accuracy bloom can be judged per playstyle instead of per stat:
- Modes:
--mode spray(continuous),--mode burst --burst N --gap MS(N-shot bursts with a pause),--mode tap --tap MS(paced single shots) or--tap auto— a solver that binary-searches the smallest tap interval holding the steady-state pre-shot recoil residual under--tol(default 0.05 deg); the solved interval and residual are printed, and the per-shot table carries cumulativet(ms)timestamps. --comp PCTmodels open-loop pull compensation (same math as the analyzer; >100 = overpull). Default is 100 — full pattern mastery, the CS-spray-pattern doctrine: the sim models the machine ceiling and treats the learned pattern as a skill assumption, not a random variable. At--comp 100with--perfectevery shot lands on the same point — muzzle choice stops mattering for placement.--perfectzeroes recoil direction variance only.- Bullet dispersion is still sampled (
--runs NMonte Carlo,u^distExpradius + uniform angle from the game's spread model) — even a 100% macro misses "like a real shooting range". - Target geometry:
--target torso|head(r = 0.20/0.10 m, override with--radius) +--range Mprints per-shot and overall hit% at that range plus the full-cadence range — the max range where every shot of the string stays on target. Target radii are screenshot-calibrated against the in-game range silhouettes (110 deg FOV, 35 m lane): silhouette torso 0.39 m wide ≈ sim r 0.20; in-game arm capsules push the effective body radius to ~0.30–0.35 m. Tap cadence fully recovers dispersion between shots; spray/burst climb with bloom, which is where barreladsSpread*multipliers (e.g. Cryogenic) show up.
Delta-row signs (atts legend): recoil/hiprecoil +N = less kick, cone/hipcone +N = tighter,
ADS/mADS +N = faster/tighter, hip +N = wider, strafe −N = slower, sprint/draw −N = faster.
Deltas are vs the stock gun (default mag/ammo/barrel, iron sights) and come from the sim's own
tier-mod sums, so hidden stock bonuses (e.g. TR-7's default mag −1 sprint/draw tier, Basic
barrel's +1 ADS tier on most weapons) show up as losses when swapped away. Continuous
attachment multipliers display as x ratios in atts (e.g. Cryogenic's
adsbloom -0.136 · adsfiredec x1.837 · adsfireoff x0.667); multiplier fields the sim models
are adsSpreadIncMult (ADS bloom per shot) and the adsSpreadFiring/NotFiringDec* recovery
multipliers.
Data resolution: --data <dir> or auto (cwd data/, then newest extracts/<date>/).
Loads either an extract dir (site layer merged per weapon) or the analyzer repo data/ directly.
Numerical parity with the analyzer's JS is exact (RNG bit-exact, same rounding/clamping); the
JS _ctx globals are explicit parameters instead. Sim requires the weapon's site layer
(derived fields) — all 63 analyzer weapons merge (2026-09-19, codename drift resolved in
SITE_ALIASES via the damageSource <Name>_WB ↔ grx-key-prefix match); leftover extract
codenames (gadgets, portal assets, KSG) have no analyzer record and list as unsupported.
Known source conflict: the in-game ADS-move penalty for Variable High / Thermal optics
(−1 strafe row, e.g. 0.67→0.60 and 0.475→0.42 mult, verified 2026-09-19) is absent from both
the analyzer data and the datamine (noEffect: true). atts prints a warning on those sights;
the sim does not model the penalty.
Patch workflow
- Full extractor run into
extracts/<patch>/with--site-dirpointing at a fresh clone of the analyzer repo'sdata/. - Diff
weapons.jsongrx fields vs the previous extract = what DICE changed (ground truth). - Diff
site/= what the analyzer models changed. - Unmatched weapons (codename drift) — add to
SITE_ALIASESinbf6_extract.py.
Tools & credits
- ResHax — game reverse-engineering forum; the BF6 TOC tools (
toc_bf6.exe+ DLLs) are id-daemon's work, distributed through ResHax's tools section. Vendored locally intools/(gitignored — grab your own copy there). - raymdl/BF6-Weapon-Analyzer — community datamine site; source of
the
site/data layers, the sim this package ports, and the model docs summarized indocs/raymdl-model-notes.md(live site). - sym.gg — community weapon-data baseline the analyzer builds on.
- Frosty Tool Suite — Frostbite EBX export tooling; the analyzer uses a BF6 fork with a scriptable FrostyCmd.
- Python 3.13, uv package manager.
Provenance
- Live values: GameRemixer (grx_weapons.ebx) + killswitch layer, current installed patch.
- Attachment costs: decoded from
attachment_<weapon>_<slug>.ebx(+0x4c), matches in-game. - Site layers: raymdl analyzer (sym.gg baseline + Frosty export + tier-ladder modeling).
- Per-attachment effect magnitudes,
protectionIndex, ballistics remain analyzer-side (CAS-partition layer); seedocs/raymdl-model-notes.md.