Lighting share: upstream benilla + the dynamic light and shadow system only
Drops the Everwood-specific fork bits (realmd split-build constant, fork catalogue, local
ignore rules) so this branch is upstream 66388278 plus lighting, and adds LIGHTING.md.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
7048e5b34f
commit
3e2292a1b7
@@ -19,7 +19,3 @@ trace-*.json
|
||||
.DS_Store
|
||||
*.swp
|
||||
/.idea/
|
||||
|
||||
# MONKEY: per-run client state written by benilla (saved variables, camera) — not source.
|
||||
/benilla-config/
|
||||
/benilla.bat
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
# benilla-lighting
|
||||
|
||||
A dynamic light and shadow system for [benilla](https://github.com/samwhosung/benilla), the Rust +
|
||||
Bevy reimplementation of the 1.12.1 client. This branch is upstream benilla (merged at
|
||||
`66388278`) plus the lighting work, and nothing else. It needs no data changes: everything is
|
||||
derived from what a 1.12 install already carries (WMO `MOLT` lights, `MOCV` vertex colour, portals,
|
||||
M2 particle emitters, `Light.dbc`).
|
||||
|
||||
Everything here is a departure from the reference client's look, so every lane has a live cvar and
|
||||
most can be switched off. Code is tagged `MONKEY (<topic>)` with the rationale and the measurements
|
||||
behind each constant.
|
||||
|
||||
## What it does
|
||||
|
||||
| Lane | What you see | Main switch |
|
||||
|---|---|---|
|
||||
| Sun shadows, characters | real silhouettes for units instead of the oval blob | `characterShadows` |
|
||||
| Sun shadows, world | trees, buildings and alpha-tested foliage cast; baked terrain shadows step aside | `worldShadows` |
|
||||
| Dynamic interiors | WMO rooms are lit per fragment by their own fixtures with a soft falloff, gated per room so light does not leak through walls | `interiorLight`, `interiorRoomGate` |
|
||||
| Synthesised lights | torches, braziers, lanterns, campfires and lampposts emit a light in the colour of their flame, with flicker | `fireLightGain`, `fireFlicker` |
|
||||
| Torch shadows | cube-map shadows from point lights onto buildings, models and terrain, with a contact-hardening penumbra and a shadow floor | `interiorShadows`, `exteriorShadows`, `torchShadowStrength`, `interiorShadowSoft` |
|
||||
| Daylight and doorways | calibrated fixtures at doors, windows and open boundaries carry daylight into a room, and doorways between rooms bleed light | `interiorDaylight`, env `WOW_DAYLIGHT`, `WOW_BLEED` |
|
||||
| Spell and firework lights | fire, holy and fel effects light their surroundings for their lifetime; frost, nature, arcane and shadow do not | `spellLightGain`, env `WOW_SPELL_LIGHT=0` |
|
||||
| Night and interior level | global dimming of the night sky term and of interior ambient | `nightGain`, `interiorGain`, `interiorBakeFloor` |
|
||||
|
||||
The dev build has a panel for all of it: **Ctrl+Shift+D → Lighting & shadows**, with Dim / Default /
|
||||
Bright presets. The Video options page gains Real Character Shadows, World Shadows and Shadow
|
||||
Distance.
|
||||
|
||||
## Where the code lives
|
||||
|
||||
- **Shaders**: `crates/benilla-assets/src/shaders/shadow_hook.wgsl` (shared shadow sampling),
|
||||
`crates/benilla-world/src/shaders/torch_depth.wgsl`, and the lighting lanes inside
|
||||
`static_gx.wgsl`, `wow_model.wgsl` and `terrain.wgsl`. The three receivers mirror each other; the
|
||||
comments say where.
|
||||
- **World** (`crates/benilla-world/src`): `lighting/global_light.rs` (light table packer, lanes,
|
||||
room claims, gains), `lighting/daylight.rs`, `lighting/flicker.rs`, `static_gx/torch_depth.rs`
|
||||
(cube-map cache), `static_gx/shadow.rs`, and the light spawning in `terrain_stream/spawn/`.
|
||||
- **Data rules** (`crates/benilla-formats/src`): `fire_light.rs` (which models emit light, their
|
||||
colour and reach, the spell school rules) and `room_claim.rs` (which rooms a light may light).
|
||||
- **App** (`crates/benilla-app/src`): `torch_shadow.rs` (caster selection), `shadow_core.rs`,
|
||||
`character_shadow.rs`, `world_shadow.rs`, `blob_shadow.rs`, `entities/carried_light.rs`,
|
||||
`entities/spell_fx/lifecycle.rs`, `dynamic_interior.rs`, `debug_panel/lighting_controls.rs`, and
|
||||
the cvars in `cvars.rs` / `video.rs`.
|
||||
- **Tools**: `benilla-extract <Data> wmolights <wmo> [--verts <group>]`, `wmolamps`, `m2firescan`
|
||||
print the inputs the system works from (groups, batch classes, portals, claims, flame emitters).
|
||||
|
||||
## Invariants worth knowing before editing
|
||||
|
||||
- The shared light buffer stays 8528 bytes and the torch table 6416 bytes; both are mirrored in
|
||||
three shaders and pinned by tests.
|
||||
- World lights use upstream's `WorldPointLight`, never Bevy's `PointLight`.
|
||||
- WGSL only fails at pipeline creation, so a shader edit is verified by running a world scene, not
|
||||
by `cargo check`.
|
||||
- Exterior point lights are selected per draw unit (12 slots, ranked against the chunk's box); the
|
||||
cube-map occlusion is evaluated for the nearest three.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
cargo build --release -p benilla
|
||||
```
|
||||
|
||||
Debug tracing: `WOW_TORCH_TRACE=1`, `WOW_POINTS_DUMP=1`, `WOW_SHADOW_TRACE=1`, and the cvar
|
||||
`interiorDebug` 1..4.
|
||||
|
||||
## Licence
|
||||
|
||||
Same as upstream: MIT OR Apache-2.0.
|
||||
@@ -1,94 +0,0 @@
|
||||
# MONKEY-FORK — benilla-everwood
|
||||
|
||||
Our fork of **[samwhosung/benilla](https://github.com/samwhosung/benilla)** (a from-scratch Rust +
|
||||
Bevy reimplementation of the 1.12.1 / 5875 client). Goal: run the Everwood / "Monkey WoW" custom
|
||||
content — the features currently delivered on the stock Blizzard client via **injected `mapi.dll` /
|
||||
`phys.dll`** — as **native code in an open client** instead.
|
||||
|
||||
Upstream is a solo project, published as **squashed snapshots from a private tree, with issues and
|
||||
PRs closed**. Nothing we do here can ever land upstream — we carry our changes forever. So this file
|
||||
is the contract that keeps re-applying them across upstream drops cheap.
|
||||
|
||||
## Branch topology
|
||||
|
||||
| branch | role | rule |
|
||||
|---|---|---|
|
||||
| `vendor` | pristine mirror of `upstream/main` | **never commit here** — only `reset --hard` |
|
||||
| `everwood` | our work (this fork's default) | the `benilla-monkey` crate + the catalogued edits below |
|
||||
|
||||
Remotes: `upstream` = samwhosung/benilla · `origin` = pkuzic/benilla-everwood.
|
||||
|
||||
## Updating to a new upstream drop
|
||||
|
||||
```bash
|
||||
git fetch upstream
|
||||
git checkout vendor && git reset --hard upstream/main # works even if upstream re-squashed / force-pushed
|
||||
git checkout everwood && git rebase vendor # replays our small commit series on top
|
||||
```
|
||||
|
||||
Conflicts can only occur in the few upstream files we edit (see catalogue). `Cargo.lock` conflict →
|
||||
`git checkout --theirs Cargo.lock` then `cargo build` to regenerate. After every rebase, re-run the
|
||||
addon harness as a regression gate (below).
|
||||
|
||||
## Design rule: keep our surface tiny
|
||||
|
||||
All custom logic lives in a **new crate `crates/benilla-monkey/`** (a Bevy plugin). The workspace is
|
||||
`members = ["crates/*"]`, so a new crate is auto-included and **cannot merge-conflict**. benilla
|
||||
already registers Lua globals from Bevy plugins (its `ProbeLuaPlugin` does
|
||||
`script.lua().create_function()` + `globals().set(...)`); `UiScript` exposes `.lua()` /
|
||||
`.register_bindings()`; rig/pose data is public (`RigPose`, `RigPalettes`, `RigSkin`, `PosePost`).
|
||||
When a refactor breaks us it surfaces as a **compile error in our crate** pointing at the renamed
|
||||
symbol — not a silent wipe of edits scattered through theirs.
|
||||
|
||||
## Catalogue of edits to UPSTREAM files
|
||||
|
||||
Every edit outside `crates/benilla-monkey/` goes here and is marked in-source with `// MONKEY:`.
|
||||
Keep this list short; prefer moving logic into the crate.
|
||||
|
||||
| file | edit | why | status |
|
||||
|---|---|---|---|
|
||||
| `crates/benilla-protocol/src/lib.rs` | **split build:** `CLIENT_BUILD` = 5875 (world/mangosd + login-screen), new `AUTH_BUILD` = 7272 (realmd challenge, `lib.rs` call site) | the two servers demand different builds — realmd `FindBuildInfo` needs ≥ 7272, mangosd `IsAcceptableClientBuild` needs exactly 5875 (matches the twmoa client: reports 1.18.1 to realmd, 5875 engine to world). | **LANDED** |
|
||||
| `crates/benilla-app/src/lib.rs` (`run`) | `app.add_plugins(MonkeyPlugin)` | wire our plugin in (no public plugin-group entry yet) | **planned** |
|
||||
| `crates/benilla-ui` / `assets/ui` | publish `TargetLevelText` / `PlayerLevelText` regions | MonkeyCharsheet hooks these stock globals | **planned** |
|
||||
| (register `PlayerModel` frame type) | `CreateFrame("PlayerModel", …)` | BuildUI + ObjectBrowser 3D previews | **planned** |
|
||||
| `crates/benilla-protocol/src/auth.rs` | treat proof reply `0x00`+`0x09` as version-invalid | so a build/version mismatch reads clearly, not "got 0x0" | **idea** |
|
||||
|
||||
## Server-side requirements (Everwood realmd, not benilla edits)
|
||||
|
||||
To let benilla (or any non-original client) authenticate against `bin/Debug/realmd.conf`:
|
||||
- **`StrictVersionCheck = 0`** (default 1) + restart realmd — otherwise realmd checks the client
|
||||
integrity hash, which benilla (not the original binary) can't reproduce → "modified client".
|
||||
- Log in with a **non-GM account** (rank < `ForcePinAccountRank`, default 7) — GM-rank accounts are
|
||||
forced through a PIN benilla doesn't implement.
|
||||
- benilla must present **build 7272** (the `CLIENT_BUILD` patch above), which realmd's
|
||||
`ExpectedRealmdClientBuilds` requires.
|
||||
|
||||
## The ~10 functions to reimplement natively
|
||||
|
||||
Currently injected by `mapi.dll` (`custom/client/mapi/dllmain.cpp` `g_api[]`). Only 4 addons use
|
||||
them; the other 10 Monkey addons are pure FrameXML.
|
||||
|
||||
- **Projection / camera** (easy): `WorldToScreen`, `MonkeyProj`, `MonkeyCamLive`
|
||||
- **Model spawn / transform** (cleaner than the DLL hacks — we own the loader): `MonkeyCreateModel`,
|
||||
`MonkeySetModelTransform`, `MonkeyDestroyModel`; `MonkeyPathByte` becomes unnecessary
|
||||
- **GO model reuse** (obsolete — just spawn our own entity): `MonkeyHijackGO`, `MonkeyRestoreGO`,
|
||||
`MonkeyDriveGO`
|
||||
- **Bone override** (deepest — via `RigPose` around `PosePost`): `MonkeySetScratchBone`, `MonkeyBendBone`
|
||||
|
||||
## De-risk plan (staged)
|
||||
|
||||
1. Plugin PoC — register `WorldToScreen` from `benilla-monkey`, confirm a Lua call returns real coords.
|
||||
2. FrameXML fixes — `PlayerModel` + `TargetLevelText`/`PlayerLevelText`; re-harness (10/14 → ~13/14).
|
||||
3. Live UI smoke test vs the Everwood server (real pixels + interaction).
|
||||
4. Native `MonkeySetScratchBone` via `RigPose` — unblocks MonkeyBodyShape end-to-end.
|
||||
|
||||
## Regression harness
|
||||
|
||||
benilla ships a headless addon corpus harness. Point it at a folder of our addons:
|
||||
|
||||
```bash
|
||||
WOW_DATA=G:/twmoa_1181/Data cargo run -q -p benilla-app --example addon_harness -- <AddOns folder> --verbose
|
||||
```
|
||||
|
||||
Baseline (2026-08-23, before any fix): **12/14 load, 10/14 survive a session**; read the custom
|
||||
MPQ/DBC patch chain without crashing.
|
||||
@@ -35,18 +35,8 @@ use benilla_srp::{NormalizedString, PublicKey, SrpClientChallenge, SESSION_KEY_L
|
||||
/// The realmd (auth/login) server port — the stock one a vmangos `realmd` listens on, which our
|
||||
/// deploy maps straight through (`3724:3724` in the compose file at `vmangos-deploy`).
|
||||
pub const AUTH_PORT: u16 = 3724;
|
||||
/// The engine build we present to the WORLD server (mangosd) and show on the login screen.
|
||||
// MONKEY: the two Everwood servers demand DIFFERENT builds. mangosd's IsAcceptableClientBuild
|
||||
// (src/game/Database/DBCStores.cpp) accepts ONLY 5875, and the twmoa client itself displays
|
||||
// "1.12.1 (5875)" — so the world/engine build stays 5875 (sending 7272 here → mangosd
|
||||
// "HandleAuthSession: version mismatch"). Upstream default was 5875 (unchanged).
|
||||
/// The 1.12.1 client build we present to the server.
|
||||
pub const CLIENT_BUILD: u16 = 5875;
|
||||
/// The build we present to the AUTH server (realmd) in the logon challenge.
|
||||
// MONKEY: realmd's FindBuildInfo (src/realmd/RealmList.cpp) accepts only build >= 7272 (twmoa
|
||||
// 1.18.1). Sending 5875 to realmd → CMD_AUTH_LOGON_CHALLENGE(0x00)+WOW_FAIL_VERSION_INVALID
|
||||
// ("got 0x0"). The twmoa client reports its 1.18.1 version to realmd but the 5875 engine build to
|
||||
// the world, so these two builds MUST differ.
|
||||
pub const AUTH_BUILD: u16 = 7272;
|
||||
/// How many logon challenges [`logon`] will ask for while looking for a `B` both serialization
|
||||
/// conventions read the same way (see the redial comment there). One dial in ~137 comes back
|
||||
/// ambiguous, so eight is already a probability of about 10⁻¹⁷ of running out.
|
||||
@@ -250,8 +240,7 @@ pub fn logon(host: &str, username: &str, password: &str) -> Result<Logon> {
|
||||
let mut dialed = None;
|
||||
for _ in 0..MAX_CHALLENGE_DIALS {
|
||||
let mut stream = dial(host, port)?;
|
||||
// MONKEY: AUTH_BUILD (7272) to realmd, not CLIENT_BUILD (5875) — see the const docs.
|
||||
auth::write_logon_challenge(&mut stream, &username.to_uppercase(), AUTH_BUILD)
|
||||
auth::write_logon_challenge(&mut stream, &username.to_uppercase(), CLIENT_BUILD)
|
||||
.context("sending logon challenge")?;
|
||||
let reply =
|
||||
auth::read_challenge_reply(&mut stream).context("reading logon challenge reply")?;
|
||||
|
||||
Reference in New Issue
Block a user