- every build off macOS has been silent since the 09-02 snapshot (B356): the non-macOS device layer was a stub that opened nothing, and nothing had ever compiled the client for another target; that layer is now the same output stream over cpal — one seam, not two stacks: the ring, the render thread and the rebuild-on-device-change loop are one code on every platform, only the device file differs; the audio thread never allocates, a lost or changed default device is noticed at kira's own cadence, and realtime is `SCHED_FIFO` on unix and MMCSS "Pro Audio" on Windows, either refusable, the ring turning a refusal into a degradation; and the two instruments this bug says were missing exist — a Linux and Windows cross-compile under `clippy -D warnings`, and a live output test that opens the real device and asserts the ring never ran dry, because compiling is not sounding (1920) - an emote that requires standing still now refuses on the move: `CheckEmoteEligible`'s `0x4000` arm writes an out-param that `DoEmote` turns into a red "You can't do that while moving!" and aborts; 20 emotes of 78 can raise the line, read off the shipped `Emotes.dbc`; SWIMMING is not in the mask, so a swimmer holding still emotes fine, and `IsSelfControlled` is TRUE for an ordinary player, so a feared or confused player emotes away happily (1904); and the cast path gains its equipped-item rung — the button greyed and pressing it still sent: the reference short-circuits to "fits" on four gates, not one, the miss being a zero `EquippedItemSubClassMask`, which means no item required at all, and the rung sits above the crowd-control validator, so a player both stunned and missing the weapon is told about the weapon; that ladder has six arms, not three — CHARMED, FLEEING and CONFUSED join (1925) - the quest reward's money is the reference's own widget: the tail of the shared `QuestFrameItems_Update` drives `questState.."MoneyFrame"` through `MoneyFrame_Update`, and the two hand-built coin trios become one `MoneyFrameTemplate` instance each at the reference's anchors, which our coins already used; the progress panel stays on coins, being the required-money flow, not the reward one (1928); and the flight master's `!` joins the questgiver sweep, because the green `!` and the gold `!` are the SAME slot, `unit+0xb2c`, torn down once before both queries re-issue; the mouseover re-ask is refuted — the only taxi-status sender has one live caller — and the two queries are not symmetric: the questgiver's goes out on every sweep, the taxi one only when the unit's key moves or on a full sweep, as the wire shows at Dungar Longdrink: 68 questgiver status queries against 1 taxi-node one (1918) - `GetSpellTabInfo` out of range answers `nil, nil, 0, 0` — the reference returns four values on every path that returns (`0x4b3ce0`), and ours answered a single nil copied from another binding's shape; it matters because stock `SpellBookFrame_OnLoad` calls it unconditionally and then does `id > (offset + numSpells)`, which a nil raises on and (0+0) makes true, hiding every button on the page — the behaviour to match; the argument raises the reference's usage string when absent, and the index truncates toward zero THEN decrements (1931); the perf probe gains a `WOW_PROBE_UNCAP=vsync` arm, because the uncapped present mode is a request, not a schedule — measured, it taxes the number it was meant to pin (11.80 `cpu_ms` uncapped against 14.65 under it), so the default stays uncapped; and the last three test temp paths are keyed on the pid, after concurrent runs raced on one fixed shared file Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
184 lines
12 KiB
TOML
184 lines
12 KiB
TOML
[package]
|
|
name = "benilla-app"
|
|
version.workspace = true
|
|
edition.workspace = true
|
|
license.workspace = true
|
|
description = "The Bevy app as a library: everything but the build-id stamp, which lives in the `benilla` launcher shim so a commit dirties ~30 lines instead of this crate (decision 0993)"
|
|
|
|
[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).
|
|
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.
|
|
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). Aggregate it with `scripts/tracesum.py`.
|
|
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).
|
|
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.
|
|
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.
|
|
include_dir = "0.7"
|
|
# `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).
|
|
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).
|
|
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.
|
|
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.)
|
|
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).
|
|
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).
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
rand = "0.8"
|
|
|
|
[dev-dependencies]
|
|
# The WMO portal-cull audit's raw-face instruments (`WOW_PIN_COL`): the pin probe compares the
|
|
# filtered walking-collision set against the group file's FULL face list with per-face MOPY flags,
|
|
# which only the raw parser exposes — benilla-formats' gathers are exactly the filtered views.
|
|
benilla-wmo = { path = "../benilla-wmo" }
|
|
|
|
# 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`.
|
|
[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. See
|
|
# docs/knowledge/camera-control.md.
|
|
[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.
|
|
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"] }
|
|
objc2 = "0.5"
|
|
objc2-app-kit = { version = "0.2", features = [
|
|
"NSApplication",
|
|
"NSResponder",
|
|
# NSApplicationActivationPolicy (bgwin's Accessory demotion) is declared in this header.
|
|
"NSRunningApplication",
|
|
"NSWindow",
|
|
"NSView",
|
|
"NSImage",
|
|
"NSCursor",
|
|
"NSEvent",
|
|
] }
|
|
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.
|
|
[target.'cfg(not(target_os = "macos"))'.dependencies]
|
|
cpal = "0.17"
|