comments: the manifests say why a dependency or a profile setting is there

One line per dependency where the reason is not obvious, measurements
kept as the number and the unit. Parsed with tomllib before and after,
every manifest is identical.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sam
2026-09-23 19:23:36 +07:00
co-authored by Claude Opus 5.5
parent a1d81a3373
commit b3e3adca69
13 changed files with 92 additions and 277 deletions
+24 -83
View File
@@ -8,33 +8,18 @@ edition = "2021"
license = "MIT OR Apache-2.0"
authors = ["sam <samuv.eth@gmail.com>"]
# Shared pins live here; promote a dep the moment two crates pin the same version (the policy
# since day one, enacted at the 0530 debt sweep). Per-crate feature additions stay in the crate.
# `image` deliberately stays per-crate: its default-features/feature sets differ per consumer,
# and a workspace default-features choice can't be subtracted from downstream.
# **Lint policy** (decision 2266 §C4). `too_many_arguments` is a readability lint for plain
# functions; on a Bevy system the parameter list IS the scheduler's dependency declaration, and
# a system that reads eight resources has eight parameters by construction. The lint had fired
# 364 times in this workspace and been allowed 364 times, 281 of them on systems — a lint whose
# every verdict is overridden is not a gate, it is a tax on adding a system. Allowed once here;
# the per-site attributes are gone. `type_complexity` stays on: it fires on shapes a type alias
# or a `SystemParam` bundle genuinely fixes.
# A version two crates share is pinned in `[workspace.dependencies]`; feature additions stay per
# crate. `image` stays per-crate: its consumers differ on default features, and a member cannot
# turn off default features that a workspace entry enables.
# `too_many_arguments` is off: a Bevy system's parameter list is its dependency declaration.
[workspace.lints.clippy]
too_many_arguments = "allow"
[workspace.dependencies]
anyhow = "1.0.102"
# bevy's `default` is `["2d", "3d", "ui"]`, and every one of those three pulls `audio`
# (= `bevy_audio` + `vorbis`) — so a default bevy compiles rodio, a second cpal, a second ALSA
# binding and an ogg/vorbis decoder into a client that mixes through kira and has had bevy's
# `AudioPlugin` disabled at boot since 0530. This spells the same `default` out with `audio`
# taken away: `2d`/`3d`/`ui` decomposed into the parts they are made of, which is the whole
# union of the three minus that one entry (decision 1932 has the expansion, verified against a
# before/after feature tree). Nothing else in the graph asks for it — avian3d, bevy_egui and
# bevy_transform_interpolation all take bevy with `default-features = false`.
#
# If a future bevy needs another member, add it here by name; do not reach back for `2d`/`3d`/
# `ui`, which would quietly bring the audio stack with it.
# bevy's default features spelled out without `audio`: `2d`, `3d` and `ui` each pull `bevy_audio`
# and `vorbis`, and the client mixes through kira. Add a feature by name, never `2d`/`3d`/`ui`; a
# plugin that takes bevy with its default features brings the audio stack back too.
bevy = { version = "0.18.1", default-features = false, features = [
"default_app",
"default_platform",
@@ -45,88 +30,44 @@ bevy = { version = "0.18.1", default-features = false, features = [
"scene",
"picking",
] }
# The render app's raw GPU handles (the WOW_GPU_MS meter's query set): bevy 0.18 does not wrap
# wgpu's QuerySet family, so the meter reaches for the same wgpu the workspace already builds.
# For the `WOW_GPU_MS` meter's query set, which bevy 0.18 does not wrap; must match bevy's wgpu.
wgpu = "27"
clap = { version = "4.6.1", features = ["derive"] }
sha1 = "0.10.6"
# The WoW format + protocol stack is fully in-repo (decision 0021), so there are no warcraft-rs /
# wow_messages crates (or forks of them) anywhere in the tree. That is still true and is why this
# section stood empty for the project's whole life.
#
# The one entry below is not a fork of a WoW crate at all. `lua-src` compiles the Lua the client's
# addon VM runs on, and benilla's Lua has to accept EXACTLY the grammar the 1.12.1 client's Lua
# accepts — no more and no less. The hunks — `third_party/lua-src/BENILLA.md` lists them — all byte-read out of the client's own parser and VM:
# they restore what 5.1 removed (the iterator-less generic-for `for k, v in someTable do`, which
# 183 of 218 corpus addons are reached by; nesting long strings; the constructor's compat
# semicolon) and delete what 5.1 added (`...` as an expression, `#`, `%`). Both directions are
# below the reach of any layer above the VM — an opcode and a grammar are not a library function,
# a global, or a metamethod — and the corpus ASKS about the second direction directly: 170
# `loadstring` sites compile a vararg expression purely to find out which interpreter they are on.
#
# The fork landed BYTE-IDENTICAL to upstream's 5.1.5 sources on purpose: the pipeline was proven
# transparent before any behavioural payload went into it, so when the suite moved, the patch was
# the only thing that could have moved it. `third_party/lua-src/BENILLA.md` has the current state,
# the per-hunk byte evidence and the verifying command (decisions 1215, 1315, 2101).
# lua-src builds the addon VM's Lua 5.1.5, patched to accept exactly the 1.12 client's grammar;
# the hunks and the command that checks them: `third_party/lua-src/BENILLA.md`.
[patch.crates-io]
lua-src = { path = "third_party/lua-src" }
# kira 0.12.1 with one patch: spatial gains and the volume tween evaluated per CHUNK and
# interpolated, not per frame (decision 1945; `third_party/kira/BENILLA.md`).
# kira 0.12.1, patched to compute spatial gains and the volume tween per output chunk and
# interpolate them, not per sample frame (`third_party/kira/BENILLA.md`).
kira = { path = "third_party/kira" }
# Optimize *dependencies* even in dev builds. avian/parry collision math (and wgpu/bevy render) is
# unusably slow at opt-level 0 — a plain `cargo run` crawls to <1 fps once physics is doing real work.
# opt-level 3 on deps (the standard Bevy fix) makes debug builds playable while our own crates stay
# lightly optimized for fast incremental rebuilds. One-time cost: deps recompile optimized the first time.
# Dependencies at opt-level 3 in dev: at opt-level 0, avian/parry collision drops a run to <1 fps.
[profile.dev.package."*"]
opt-level = 3
# Our own crates at opt-level 1, not 0: Bevy's generic hot paths (material specialization scans,
# query par_for_each closures) monomorphize INTO our crate, so at opt 0 they run at debug codegen
# speed over every resident entity — measured at Stormwind as the difference between ~45 fps and a
# pinned 60 (decision 0362). Warm incremental rebuild cost measured at ~zero. NB: FPS probe numbers
# in decisions ≤ 0361 were taken at opt 0 and are not comparable with post-0362 probes.
# Our crates at opt-level 1: Bevy's generic hot paths monomorphize into them, and at opt-level 0
# Stormwind drops from a pinned 60 fps to ~45; the warm incremental rebuild cost is ~0.
[profile.dev]
opt-level = 1
# No debuginfo trimming: `debug = "line-tables-only"` was A/B-measured (decision 0192) at zero
# recurring win on this stack (macOS unpacked split-debuginfo; sccache; fast ld) — not worth
# invalidating every warm target and the sccache cache for.
# No debuginfo trimming: `debug = "line-tables-only"` measures no recurring win on macOS + sccache.
# Shipping-grade optimization, A/B-graded at the pin (decision 0736): −1.0 cpu_ms (−5 %) vs
# default release, flat at the empty-map and bare-Bevy floors. The campaign keeps grading on
# default `release` — same-day A/B is the law, and fat LTO's link time would tax every
# iteration for a constant offset. Build with `--profile ship` for actual releases (add
# `RUSTFLAGS=-Ctarget-cpu=native` for a machine-local binary; not set here — it isn't portable).
# The shipping build (`--profile ship`): fat LTO measures -1.0 cpu_ms (-5 %) against `release`,
# which stays the measured profile because fat LTO's link time would tax every iteration.
# `-Ctarget-cpu=native` is not set: it is not portable.
[profile.ship]
inherits = "release"
lto = "fat"
codegen-units = 1
# The director's play loop (decision 1157): `release` plus incremental compilation. `benilla-app` is one crate of
# hundreds of thousands of lines (243k when this was measured), and release is non-incremental by cargo's default, so a one-line edit re-optimizes
# all of it — measured 2m47s (43s frontend, ~121s LLVM at opt-level 3) against a 1.8s stamp rebuild
# and a 2.5s no-op. Incremental caches both halves per codegen unit, so an edit pays for what it
# touched. Cargo applies `incremental` to workspace members ONLY (verified: `target/debug/incremental`
# holds the workspace members and zero registry deps), so the ~760 dependencies stay non-incremental and
# sccache-cacheable — 0192's wrapper and the cold-slot build are untouched.
#
# Why a separate profile rather than flipping `release`: incremental raises the codegen-unit count
# and drops cross-unit inlining, which is a real codegen difference. The FPS campaign grades on
# default `release` (0736) and same-day A/B is the law, so the graded profile must not quietly
# change under it. Run it as `cargo play` (the alias in `.cargo/config.toml`).
# `release` plus incremental, run as `cargo play`: under `release` a one-line edit to
# `benilla-app` costs 2m47s. Incremental applies to workspace members only, so dependencies stay
# sccache-cacheable; it changes codegen, so it lives here and not in the measured `release`.
[profile.play]
inherits = "release"
incremental = true
# ThinLTO OFF, and it is not a performance choice -- it is the fix for rustc #86049. `release`
# implies thin-LOCAL LTO (ThinLTO across a crate's own codegen units), which promotes each CGU's
# internal anonymous constants to external symbols named `anon.<hash>.N.llvm.<moduleid>`. Under
# `incremental` some CGUs come back from the cache carrying names minted in an earlier compile,
# so a promoted reference finds no definition and `cc` fails with "symbol(s) not found for
# architecture arm64" -- an intermittent link failure with no source-level cause, cleared only by
# a full rebuild. Measured on the 2026-08-29 occurrence: 7 poisoned CGUs in `benilla-formats`, 48
# unresolved symbols. `lto = "off"` disables the promotion step entirely (verified: the play rlib
# goes from 381 undefined `anon.*.llvm.*` symbols to 0). `release` and `ship` are UNTOUCHED -- they
# are non-incremental, cannot hit this, and stay the graded profiles (0736). Decision 1698.
# Not for speed: under `incremental`, thin-local LTO leaves promoted `anon.*.llvm.*` symbols
# unresolved and the link fails intermittently (rustc #86049).
lto = "off"
+37 -120
View File
@@ -9,154 +9,79 @@ description = "The Bevy app as a library: everything but the build-id stamp, whi
workspace = true
[features]
# **The dev/player seam** (decisions 0026 + 1173). Default-ON, so every ordinary `cargo run`,
# capture, probe and gate builds exactly what it built before. `--no-default-features` compiles
# the instruments — the debug panel, the perf HUD, the inspector, the capture harness and the
# probe fleet — physically OUT: not dormant behind a runtime flag, absent. That is the whole
# requirement a player client has of them, and a runtime toggle cannot meet it.
#
# The rule the seam enforces is one-directional: **dev may see anything; nothing may depend on
# dev.** It is enforced by `cargo build -p benilla --no-default-features` in `scripts/gates.sh`,
# not by anyone remembering it — 0026 adopted the rule in June 2026 with no failing build behind
# it, and twenty-four couplings had accumulated by August (1173).
# `dev` compiles in the instruments: the debug panel, perf HUD, inspector, capture harness and
# probes. Nothing may depend on it; `scripts/gates.sh` builds `benilla` with no default features.
default = ["dev"]
#
# The three benilla crates below are taken with `default-features = false` and their `dev`
# re-exported here (1175). Their `dev` is default-on too, so without the round trip a
# `--no-default-features` build gets the feature back through unification — and the binary
# carries the build machine's source-tree path after all. Same trap as the shim's, one level down.
# `benilla-assets`, `-formats` and `-world` come in without default features and their `dev` is
# forwarded here: it is on by default in each, so unification would put it back in a player build.
dev = ["benilla-assets/dev", "benilla-formats/dev", "benilla-world/dev", "dep:bevy_egui"]
# Opt-in Tracy profiling: `cargo run --features tracy` streams per-system `info_span!` markers (on the
# hot systems) + render-pass CPU timings to a Tracy capture (decision 0026 — the dev/player seam;
# a `docs/howto/running.md` this once cited never landed).
tracy = ["bevy/trace_tracy"]
# Opt-in Chrome-trace profiling: `cargo run --features trace_chrome` writes a `trace-<ts>.json` of
# every system's per-run span next to the working dir. Tracy's offline twin — it needs no GUI and no
# live capture, so a per-system cost question is answerable from a terminal (and from a run someone
# else recorded). Its readers: `scripts/tracesum.py` (per-span totals), `scripts/framecost.py`
# (the per-frame cost ledger, 0718) and `scripts/tracebands.py` (a window band-mapped into
# replaceable units, 1413/1414).
# `--features trace_chrome` writes every system's per-run span to `trace-<ts>.json` in the cwd.
trace_chrome = ["bevy/trace_chrome"]
[dependencies]
anyhow.workspace = true
# OS clipboard access for the UI EditBox copy/cut/paste chords (chat input). Already in the tree via
# bevy_egui; pinned to the same 3.x so there's one copy. Read on the main thread (the UI key feed is
# a NonSend system) — required for the macOS NSPasteboard path. Covers macOS, Windows, and Linux/BSD
# under X11; the Wayland backend is `smithay-clipboard` below (decision 0702).
# The EditBox clipboard chords, read on the main thread as macOS NSPasteboard requires; Wayland
# goes through `smithay-clipboard`. The 3.x bevy_egui uses, so the graph has one copy.
arboard = "3"
benilla-ui = { path = "../benilla-ui" }
bevy.workspace = true
wgpu.workspace = true
# Optional: the dev overlays are its only consumers (the panel, the perf pill, the inspector
# card), so a player build does not link it at all (`dev` feature, decision 1173).
bevy_egui = { version = "0.39", optional = true }
# Zero-copy POD→bytes for the shared light storage buffer (one per-frame write_buffer). Already in
# the tree transitively; pinned to the same version as the dev-dep below.
bytemuck = { version = "1", features = ["derive"] }
# `f16` decode for the booth-target dump (`WOW_BOOTH_DUMP`): the booth render targets are
# `Rgba16Float` (decision 0804) and the PNG wants display bytes. Already in the tree via
# wgpu/bevy_image; declared so the call site is an honest dependency.
# `f16` decode for the `WOW_BOOTH_DUMP` dump of the `Rgba16Float` booth render targets.
half = "2"
# The shipped FrameXML tree, compiled into the binary (decision 1175 §2): 61 files, ~2.8 MB, so the
# client cannot boot without its interface on a machine that has never seen our source. A proc
# macro plus a no-dependency runtime shim — it adds nothing to the graph at runtime, and it is the
# crate everyone uses for this. `assets/ui` is OUR content (MIT/Apache), not Blizzard's.
# Compiles `assets/ui` into the binary, so the interface loads on a machine without the source.
include_dir = "0.7"
# The 1.12 chat filters are PCRE over shipped DBCs (decision 2077; `text_filter`): 2289 profanity
# expressions and 28 spam ones, compiled once at startup and run over every chat line. `RegexSet` is
# exactly the shape that needs — one automaton over the whole list for the "can anything match at
# all" pass — and the crate is already in the tree transitively.
# The 1.12 chat filters (`text_filter`): the DBCs' 2289 profanity and 28 spam patterns, with one
# `RegexSet` for the any-match pass over every chat line.
regex = "1"
# `getrusage` — whole-process CPU time (all threads), the perf probes' `cpu_ms`/`cpu_pct` metric
# (`perf::process_cpu_secs`). Wall-clock fps is unusable as a regression instrument on a machine
# running parallel sessions' builds; CPU-per-frame is, and it is also the number the Mac report
# is about ("250 % CPU at 59 fps"). Was dev-only; since 1857 the output backend needs it in every
# build too (`mach_absolute_time`, the host clock its meters read; `thread_policy_set`, the render
# thread's realtime policy).
# `getrusage` for the perf probes' process CPU time; `mach_absolute_time` and `thread_policy_set`
# for the audio output's meter clock and its render thread's realtime policy.
libc = "0.2"
# Resample varying-size layer BLPs to a uniform size for the terrain texture array; `png` so we can
# encode the cursor image into data an NSImage can load (macOS hardware cursor).
# PNG encoding only: the macOS hardware cursor's NSImage data and screenshots.
image = { version = "0.25", default-features = false, features = ["png"] }
# The net↔ECS bridge: a background IO thread streams typed `SessionEvent`s to the ECS over a
# channel, and outbound `ClientCommand`s back. crossbeam's Sender/Receiver are `Send + Sync + Clone`
# (std's mpsc Sender is `!Sync`, so it can't be a Bevy resource without a mutex — the very thing this
# bridge deletes). The canonical Bevy background-thread→channel→system idiom. See decision 0006.
# Thread-to-ECS channels such as the net bridge's: crossbeam's `Receiver` is `Sync`, so either end
# can be a Bevy resource without a mutex.
crossbeam-channel = "0.5"
benilla-assets = { path = "../benilla-assets", default-features = false }
benilla-formats = { path = "../benilla-formats", default-features = false }
benilla-protocol = { path = "../benilla-protocol" }
benilla-world = { path = "../benilla-world", default-features = false }
# The CVar config diff (`benilla/config.toml`, decision 0954): serde derive on the persisted
# shape, toml as its format. (serde_json left with the era manifest — 0978.)
# The CVar diff persisted in `benilla-config/config.toml`.
serde = { version = "1", features = ["derive"] }
toml = "0.8"
# ECS-native physics: collider storage + broadphase BVH + shape-casts, with a thin custom kinematic
# character controller on top (decision 0009). Pinned 0.6: it targets Bevy 0.18 (verified single,
# unified bevy 0.18.1 in the tree); 0.7.0 jumps to Bevy 0.19 and would duplicate bevy. avian wraps
# parry under the hood, so adopting it lets us drop the *direct* parry3d dep once the bespoke
# CollisionWorld is gone. See docs… (decision 0007 superseded by 0009).
# Collision for our kinematic character controller; 0.6 targets Bevy 0.18, 0.7 moves to 0.19.
avian3d = "0.6"
# The glyph shaper + rasterizer for `FontString` text (decision 0068 §2, probe-verified in
# `probes/text-glyph`). Pinned to 0.16 — the exact version already in the tree transitively
# (`bevy_text`'s own dependency, see `Cargo.lock`) — so this is the *same* `cosmic-text` the decision
# already commits to, not a second copy. Used as a standalone shaper/rasterizer (`FontSystem` +
# `Buffer` + `SwashCache`) in `ui_text.rs`, never `bevy_text`'s `Text2d`/ECS integration (that path
# doesn't feed our custom z-ordered `UiQuad` stream).
# Shapes and rasterizes `FontString` text standalone (`ui_text`), at bevy_text's own 0.16.
cosmic-text = "0.16"
# The font database `cosmic-text` 0.16 uses (its `Source`/`ID`/`FaceInfo` appear in `ui_text.rs`'s
# public-in-crate types). Pinned to the exact version `cosmic-text` 0.16 depends on so there's one
# `fontdb` in the graph.
# `cosmic-text`'s font database, whose types `ui_text` names; must stay the version it depends on.
fontdb = "0.23"
# kira renders the mix; the device it plays through is OURS (`sound::output`, decision 1857) —
# kira's own `cpal` backend is not enabled on any target. On macOS that took cpal out of the graph
# entirely: it could neither report a lost default device nor rebuild on one, and it discarded the
# IO-cycle timestamp the deadline meters need. Off macOS cpal is back (1920) but as OUR device
# layer under the same backend, not as kira's — the ring, the render thread and the meters are the
# same code on every platform.
# The mixer. Its `cpal` backend stays off on every target: `sound::output` is our own backend.
kira = { version = "0.12", default-features = false, features = ["mp3", "wav", "pcm", "adpcm"] }
# The realtime-audio allocation tripwire (decision 1857): `assert_no_alloc(|| …)` around the output
# IO callback and the render pass aborts a DEBUG build on any allocation inside them, so a
# realtime-safety regression fails a test or a smoke instead of reaching an ear. Release builds
# compile it out (`disable_release`, the crate's default) and carry no allocator wrapper.
# Aborts a debug build on an allocation in the audio IO callback or render pass; off in release.
assert_no_alloc = "1.1"
# kira's own decoder, named directly for one trait: the mixer feeds streams through its own
# `MediaSource` so kira's per-stream decode thread runs OUR code and can promote its QoS (decision
# 1109 — a bare-spawned decode thread starves under the world-entry burst and zero-fills the mix).
# No codecs of its own (kira's features enable those); must stay the version kira resolves — one
# symphonia in the graph, or the trait impl won't line up.
# For `MediaSource`: kira's decode thread reads through our source, which raises the thread's QoS
# so the world-entry burst cannot starve it. Must stay kira's version, or the trait won't match.
symphonia = { version = "0.5", default-features = false }
# The mix tap's audio-thread → writer-thread ring (decision 1112): the tap effect must never
# block or allocate on the render path. kira's own SPSC ring crate; kept on the version kira
# resolves so the graph carries one rtrb.
# The audio threads' lock-free SPSC rings (output, mix tap); kira's version, so the graph has one.
rtrb = "0.3"
mint = "0.5"
# The gossip greeting is a weighted random DRAW per menu-open, not a fixed pick (wow-re
# `gossip-npctext-law.md` — the reference re-rolls in its gossip handler), so the app needs a uniform
# source. Pinned to 0.8: the version `benilla-srp` already depends on, so this adds no crate.
# The gossip greeting's weighted draw, re-rolled per open as in the reference; `benilla-srp`'s 0.8.
rand = "0.8"
# Linux/BSD: the Wayland clipboard (decision 0702). We build with bevy's `wayland` feature, so on a
# Wayland session winit gives us a native Wayland window and `arboard` — X11-only without its
# non-default `wayland-data-control` feature — finds no X11 CLIPBOARD owner and every paste fails.
# smithay-clipboard is the right crate for a windowed app: it drives the *standard* `wl_data_device`
# off our own `wl_display`/seat, which every compositor supports. arboard's own Wayland feature would
# not do: it speaks `wlr-data-control`, a privileged manager protocol GNOME/Mutter deliberately
# doesn't implement. `raw-window-handle` (pinned to the 0.6 already in the tree via bevy_window, so
# this adds no crate) is only needed to read the `wl_display` out of bevy's `RawHandleWrapper`.
# Linux/BSD Wayland clipboard: smithay-clipboard drives the standard `wl_data_device` on our own
# `wl_display`, read through raw-window-handle (bevy's 0.6). arboard's Wayland feature speaks
# `wlr-data-control`, which GNOME does not implement.
[target.'cfg(all(unix, not(any(target_os = "macos", target_os = "android"))))'.dependencies]
raw-window-handle = "0.6"
smithay-clipboard = "0.7"
# macOS: drive a native NSCursor for a crisp hardware cursor. winit drives the cursor via AppKit
# cursor-rects, which our continuous Metal render loop doesn't honor on mouse-move (custom cursor /
# hide revert to the arrow); we bypass it with NSCursor + NSWindow::disableCursorRects
# (`video::cursor`).
# macOS: AppKit through objc2 drives a native NSCursor with the window's cursor rects disabled;
# winit's cursor rects revert to the arrow under our continuous render loop (`cursor.rs`).
[target.'cfg(target_os = "macos")'.dependencies]
# The audio device layer (decision 1857, `sound::output::coreaudio`): the HAL output unit through
# coreaudio-rs's `AudioUnit`, and the AudioObject property/listener API through the objc2 CoreAudio
# bindings — the two crates cpal itself stood on, now used directly. The versions are the ones the
# graph already carries; the feature sets are the ones cpal proved to compile against.
# The audio device layer (`sound::output::coreaudio`): the HAL output unit through coreaudio-rs,
# the AudioObject property API through objc2-core-audio, at the versions and features cpal uses.
coreaudio-rs = { version = "0.13", default-features = false, features = ["audio_unit", "core_audio"] }
objc2-core-audio = { version = "0.3", default-features = false, features = ["std", "AudioHardware"] }
objc2-core-audio-types = { version = "0.3", default-features = false, features = ["std", "CoreAudioBaseTypes"] }
@@ -174,20 +99,12 @@ objc2-app-kit = { version = "0.2", features = [
] }
objc2-foundation = { version = "0.2", features = ["NSArray", "NSData", "NSGeometry"] }
# Everywhere else: the same device layer over cpal (decision 1920, `sound::output::cpal`). 1857
# owns the mix-ahead ring, the render thread and the meters on every platform; only the last
# hop — find the default device, open a stream on it, hand back one buffer per cycle — is
# platform code, and off macOS that hop is ALSA/PipeWire, WASAPI or the host's own API. cpal is
# the layer that already speaks all of them; on macOS it is still not in the graph, because
# there its defects (a no-op error callback on the default device, a pinned `DefaultOutput`
# unit, the discarded IO-cycle timestamp) are exactly what 1857 went direct to escape. On an
# unsupported target cpal compiles to its Null host, which reports no device — the same silent
# posture the old `stub.rs` had, without a second file to rot.
# Off macOS the device layer's last hop runs over cpal (`sound::output::cpal`). macOS goes direct:
# there cpal cannot report or rebuild on a lost default device and drops the IO-cycle timestamp.
# An unsupported target gets cpal's Null host, which reports no device.
[target.'cfg(not(target_os = "macos"))'.dependencies]
cpal = "0.17"
# Windows: the journal's and the probes' CPU clocks (decision 2219, `perf::clock`) —
# `GetProcessTimes`, `GetThreadTimes` and `GetSystemTimes`, the twins of the `getrusage`,
# `clock_gettime` and `host_statistics64` arms. The version is one the graph already carries.
# Windows CPU clocks for `perf::clock`: `GetProcessTimes`, `GetThreadTimes`, `GetSystemTimes`.
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_System_Threading"] }
+4 -6
View File
@@ -9,10 +9,8 @@ description = "Bevy AssetSource + AssetLoaders over the WoW 1.12.1 MPQ patch cha
workspace = true
[features]
# Forwarded dev/player seam (0026 · 1173/1174 · 1175). Default-on. `benilla-formats` must be taken
# with `default-features = false` and its `dev` re-exported here: it is default-on there too, so
# without this a `--no-default-features` player build gets the feature back through unification and
# the binary carries the build machine's source-tree path after all.
# The dev/player seam, on by default. `benilla-formats` comes in without default features and its
# `dev` is forwarded here, or unification would put it back in a player build.
default = ["dev"]
dev = ["benilla-formats/dev"]
@@ -20,7 +18,7 @@ dev = ["benilla-formats/dev"]
anyhow.workspace = true
bevy.workspace = true
benilla-formats = { path = "../benilla-formats", default-features = false }
# Nearest-neighbour resize for off-Elwynn layer BLPs authored below LAYER_TEX_SIZE (texel
# replication in gamma byte space — no averaging, so the C2 mid-tone darkening can't creep in).
# Nearest-neighbour resize of layer BLPs smaller than `LAYER_TEX_SIZE`: texel replication in gamma
# bytes, with no averaging to darken mid-tones.
image = { version = "0.25", default-features = false }
serde = { version = "1", features = ["derive"] }
+1 -2
View File
@@ -10,6 +10,5 @@ workspace = true
[dependencies]
benilla-bytes = { path = "../benilla-bytes" }
# S3TC/DXT block decompression — the same fixed-point codec wow-blp uses, so DXT output is
# byte-identical. Decompression is a deterministic algorithm (no quantization), unlike encode.
# S3TC/DXT block decompression, a deterministic fixed-point decode.
texpresso = "2"
+1 -2
View File
@@ -9,6 +9,5 @@ description = "The git build-id stamp emitter shared by the launcher shims' buil
workspace = true
[lib]
# A build-dependency only. No tests, no bevy, no app deps — it runs inside cargo's build-script
# pass and must stay trivially cheap to compile.
# A build-dependency only: it runs in cargo's build-script pass, so it must stay cheap to compile.
path = "src/lib.rs"
+4 -15
View File
@@ -9,12 +9,9 @@ description = "WoW 1.12.1 asset-format adapters (MPQ/BLP/DBC/ADT/WDT) and the be
workspace = true
[features]
# The dev/player seam (0026, built by 1173/1174). Default-on, so every normal build keeps the
# instruments; `--no-default-features` is the player build. Here it gates exactly one thing:
# `install::candidates`' project-folder probe, which bakes THIS source tree's absolute path
# (decision 1175). EVERY dependent must take this crate with `default-features = false` and
# re-export `dev`, or feature unification puts `/Users/…` back into a player binary —
# `scripts/gates.sh`'s `--no-default-features` line is what catches getting that wrong.
# `dev` gates one thing, `install::candidates`' project-folder probe, which bakes this source
# tree's path into the binary. Every dependent takes this crate without default features and
# forwards `dev`, or unification puts the path back into a player build.
default = ["dev"]
dev = []
@@ -27,15 +24,7 @@ benilla-dbc = { path = "../benilla-dbc" }
benilla-m2 = { path = "../benilla-m2" }
benilla-mpq = { path = "../benilla-mpq" }
benilla-blp = { path = "../benilla-blp" }
# PNG only, matching every sibling crate. `image`'s DEFAULT features enable `avif`, which drags
# in ravif -> rav1e -> av-scenechange: a complete AV1 video ENCODER, monomorphized into this
# crate, for the two calls below that both write a PNG. Cutting it takes this crate's graph from
# 112 to 48 dependencies and the workspace's from 499 to 466.
#
# It is NOT why `cargo play` used to fail to link -- that was rustc #86049, fixed by `lto = "off"`
# in `[profile.play]` (decision 1698). The tempting story is that rav1e's monomorphized bulk is
# what tripped it; the evidence says otherwise, since 2 of the 7 poisoned codegen units held only
# our own DBC catalog loaders. Two independent wins, and this is the smaller one. Decision 1698.
# PNG only: the default `avif` feature would pull in rav1e, a whole AV1 encoder, to write PNGs.
image = { version = "0.25", default-features = false, features = ["png"] }
benilla-wdt = { path = "../benilla-wdt" }
benilla-wmo = { path = "../benilla-wmo" }
+2 -6
View File
@@ -9,11 +9,7 @@ description = "Read-only MPQ archive reader for WoW 1.12.1 (build 5875) — in-r
workspace = true
[dependencies]
# Bounds-checked byte readers + the allocation-cap guard (decision 0064). MPQ migrates minimally: the
# hand-rolled rd_u16/rd_u32 move to ByteExt and the header/block-table/sector allocations get capped;
# nothing else here changes (this crate is StormLib-pinned and deliberately not churned further).
# Bounds-checked byte readers and the allocation cap on the header, block table and sectors.
benilla-bytes = { path = "../benilla-bytes" }
# zlib/inflate for the ZLIB (0x02) sector codec. miniz_oxide backend (pure Rust); no C, no archive
# writing. Any other codec the differential pass turns up in real 1.12.1 data (e.g. PKWARE-DCL 0x08)
# gets vendored alongside; `decompress` errors loudly on an unknown method so nothing is missed.
# Inflate for the ZLIB (0x02) sector codec, on its pure-Rust miniz_oxide backend.
flate2 = "1.1"
+2 -3
View File
@@ -14,11 +14,10 @@ clap.workspace = true
benilla-srp = { path = "../benilla-srp" }
flate2 = "1.0"
sha1.workspace = true
# `SessionEventKind` — the fieldless twin of `SessionEvent`, the key the app's packet-handler
# table dispatches on (decision 2305). A derive, so the two can never drift apart.
# Derives `SessionEventKind`, `SessionEvent`'s fieldless twin that the handler table keys on.
strum = { version = "0.26", features = ["derive"] }
[dev-dependencies]
# The SRP probes re-derive the handshake by hand (to vary what `benilla-srp` fixes) — examples only.
# The SRP probe example re-derives the handshake by hand, to vary what `benilla-srp` fixes.
num-bigint = "0.4.6"
rand = "0.8.6"
+1 -3
View File
@@ -10,7 +10,5 @@ workspace = true
[dependencies]
roxmltree = "0.20"
# The embedded Lua runtime for the script host (decision 0068). `lua51` = Classic Era's Lua
# version; `vendored` builds PUC-Rio Lua from source (no system dependency). 0.11 matches the
# runtime that decision 0068's probe A verified (mlua + the LUAI_MAXCSTACK=8000 finding).
# The script host's Lua 5.1; `vendored` builds it from the workspace's patched `lua-src`.
mlua = { version = "0.11", features = ["lua51", "vendored"] }
+1 -2
View File
@@ -10,6 +10,5 @@ workspace = true
[dependencies]
anyhow.workspace = true
# PNG-only image IO + the RgbImage buffer the metrics run over. No Bevy — this is the pure-math half of
# the harness, so it compiles fast and runs under `cargo test --workspace`.
# PNG IO and the `RgbImage` buffer the metrics run over; no Bevy, so the crate stays fast to test.
image = { version = "0.25", default-features = false, features = ["png"] }
+9 -21
View File
@@ -9,42 +9,31 @@ description = "The world renderer: terrain, water, sky, weather, models, lightin
workspace = true
[features]
# Forwarded dev/player seam (0026 · 1173/1174 · 1175). Default-on. `benilla-formats` must be taken
# with `default-features = false` and its `dev` re-exported here: it is default-on there too, so
# without this a `--no-default-features` player build gets the feature back through unification and
# the binary carries the build machine's source-tree path after all.
# The dev/player seam, on by default. `benilla-assets` and `-formats` come in without default
# features and their `dev` is forwarded here, or unification would put it back in a player build.
default = ["dev"]
#
# `bevy/debug` rides with it (decision 1451): without it every ECS diagnostic — a panicking system,
# a parameter that will not validate, the enforcer's whole fault list — reads `<Enable the debug
# feature to see the name>`, which is what the frame-one crash it fixes was reported as. It stores
# `&'static str` names on systems and params; it is diagnostics, not an instrument, so nothing
# depends on it and the player build simply says less. Enabled here rather than in `benilla-app`
# so `benilla-worldview` — the enforcer, whose entire output is these names — carries it too.
# `bevy/debug` names systems and parameters in ECS diagnostics (else "Enable the debug feature to
# see the name"); set here so `benilla-worldview`, which reports those names, gets it too.
dev = ["benilla-formats/dev", "benilla-assets/dev", "bevy/debug"]
[dependencies]
avian3d = "0.6"
benilla-assets = { path = "../benilla-assets", default-features = false }
benilla-formats = { path = "../benilla-formats", default-features = false }
# The wire crate (`benilla_protocol`) is deliberately NOT here. The engine renders a world; it
# does not speak a protocol. Decision 1177 took the last crossing out — `WorldUnit.kind:
# EntityKind` became `WorldUnit.wades: bool` — and the absence of that dependency line IS the
# record's gate: the crate graph enforces it, so no test has to.
# The WMO group parser — the portal audit re-parses a group to pin its own decode against the file.
# No `benilla-protocol`: the engine renders a world and does not speak the protocol.
# For the portal audit, which re-parses a WMO group to check its decode against the file.
benilla-wmo = { path = "../benilla-wmo" }
bevy.workspace = true
bytemuck = { version = "1", features = ["derive"] }
image = { version = "0.25", default-features = false, features = ["png"] }
# macOS: `bgwin` demotes the app to Accessory for a background run and orders the window out of
# the way. Same pins as `benilla-app`'s — one objc2 in the graph.
# macOS AppKit calls, at `benilla-app`'s pins so the graph has one objc2.
[target.'cfg(target_os = "macos")'.dependencies]
objc2-app-kit = { version = "0.2", features = [
# `bgwin`'s Accessory demotion and window ordering, and `modkeys`' modifier read.
"NSApplication",
"NSEvent",
# `mac_quit` walks the default menu bar to re-point the Quit item (decision 1528).
# `mac_quit` walks the default menu bar to re-point the Quit item.
"NSMenu",
"NSMenuItem",
"NSResponder",
@@ -53,6 +42,5 @@ objc2-app-kit = { version = "0.2", features = [
] }
# `MainThreadMarker`, for the AppKit calls above.
objc2-foundation = "0.2"
# `sel!`, for the two selectors `mac_quit` swaps. The pin objc2-app-kit 0.2 itself builds against,
# so this adds a name to the manifest and nothing to the graph.
# `sel!`, for the two selectors `mac_quit` swaps; the objc2 that objc2-app-kit 0.2 builds against.
objc2 = "0.5"
+2 -4
View File
@@ -9,8 +9,7 @@ description = "The world viewer: the engine with no game attached — and the wa
workspace = true
[dependencies]
# **Only the engine.** This is the edge the binary exists to hold: if a world concept
# ever needs the game, this line is what refuses to let it.
# Only the engine, never a game crate: a world concept that needs the game fails to build here.
benilla-world = { path = "../benilla-world" }
[build-dependencies]
@@ -19,6 +18,5 @@ benilla-buildstamp = { path = "../benilla-buildstamp" }
[[bin]]
name = "benilla-worldview"
path = "src/main.rs"
# Same reason as the `benilla` shim: test-building this bin would relink the app in the test
# profile on every sha change, which is the tax the shims exist to kill (decision 0993).
# Test-building this bin would relink it on every commit, since the build stamp changes.
test = false
+4 -10
View File
@@ -9,20 +9,15 @@ description = "The launcher: build.rs stamps the git build id, main() hands it t
workspace = true
[features]
# The dev/player seam, forwarded (decisions 0026 + 1173): this shim is the binary a player would
# run, so `cargo build -p benilla --no-default-features` is the player build — and the gate.
# The dev/player seam: `cargo build -p benilla --no-default-features` is the player build.
default = ["dev"]
dev = ["benilla-app/dev"]
# Forwarded profiling switches, so `cargo run --features tracy -p benilla` keeps working.
# Profiling switches, forwarded so `cargo run -p benilla --features tracy` works.
tracy = ["benilla-app/tracy"]
trace_chrome = ["benilla-app/trace_chrome"]
[dependencies]
# `default-features = false` is LOAD-BEARING, not tidiness: without it `cargo build -p benilla
# --no-default-features` still builds the library with ITS defaults (a path dep gets its own),
# so the player-build gate would compile the full dev tree and pass no matter what. Measured on
# 1174's first green run — `cargo tree` listed bevy_egui either way. The `dev` feature above is
# what turns the instruments back on, and it is on by default here.
# `default-features = false`, or `--no-default-features` here still builds benilla-app with `dev`.
benilla-app = { path = "../benilla-app", default-features = false }
[build-dependencies]
@@ -31,6 +26,5 @@ benilla-buildstamp = { path = "../benilla-buildstamp" }
[[bin]]
name = "benilla"
path = "src/main.rs"
# No tests live here — and test-building this bin would relink the whole app in the test profile
# on every sha change, which is the exact tax the shim exists to kill (decision 0993).
# No tests here, and test-building this bin would relink the whole app on every commit.
test = false