Files
BT411/context/wintesla-port.md
T
arcattackandClaude Opus 5 d39227ef39 Gitea #51 ROOT CAUSE: LoopAtWill was mapped to "loop forever", arming 224 of 603 samples as endless OpenAL sources
SAURON's "coolant flush sound glitched and perma" is one instance of a systemic
defect.  SetupPatch (L4AUDLVL.cpp) decided looping from the SAMPLE flag alone:

    AL_LOOPING = (info.loop != ForceStatic)

That armed every non-ForceStatic sample -- 224 of the 603 authored zones,
including unmistakable one-shots -- as an OpenAL source that never ends on its
own.  Any such sound whose note-off never arrives plays forever.  Measured 1946
LoopAtWill x Transient arming events in one short session.

The sample flag expresses PERMISSION; the SOURCE decides.  Three facts [T1]:

  * the enum's own comments (L4AUDLVL.h:14-19) -- LoopAtWill = "will play once
    OR LOOP AS DESIRED", ForceStatic = "plays only once EVEN IF LOOPED" (a
    veto), LoopAlways = "ramp up and then down";
  * LoopAtWill is enum value 0, i.e. the DEFAULT an unauthored sample receives
    (WTPresets.cpp:37) -- it cannot mean "loop forever";
  * LoopAlways, the real always-loop, is used by ZERO shipped samples.

"As desired" is the source's authored AudioRenderType (Transient=one-shot vs
Sustained=held), streamed from AUDIO*.RES (AUDLVL.cpp:28) and already consulted
by the renderer (L4AUDRND.cpp:567, AUDREND.cpp:184).  SetupPatch now takes it,
threaded from all three L4AudioSource call sites (Direct/Dynamic3D/Static3D).
New rule: LoopAlways->1, ForceStatic->0, LoopAtWill->the source's render type.

MEASURED before changing behaviour (BT_LOOP_AUDIT=1, scratchpad/loopaudit.py):

    LoopAtWill  x Transient -> loop=0  (was 1)   1946 events
    LoopAtWill  x Sustained -> loop=1  unchanged    65 events
    ForceStatic x Transient -> loop=0  unchanged   292 events

The engine loops (EngineAccel07_z0..z2, EngineMotor01, EnginePower01) are all
LoopAtWill/Sustained and PRESERVED -- no regression there.  All 24 reclassified
samples are genuine one-shots: laser charge/fire/explosion/loaded, missile
loading, engine shift, coolant pressure inc/dec.

A/B PROOF (scratchpad/loopab.py, same session both arms, only BT_LOOP_LEGACY
differs), asking the engine's own BT_AUDIO_DUMP what is still playing with
loop=1 long after every release:

    [legacy] EnginePower01, CoolantPresInc03_z2, CoolantPresDcr03_z2
    [fixed]  EnginePower01

The two stuck coolant sources are the reported symptom.  They were eventually
reclaimed when a later trigger reused the source, which is why the bug was
intermittent -- the "perma" case is when nothing else retriggers it.  The fix
removes the mechanism.

BT_LOOP_LEGACY=1 restores the old rule for a field A/B without a rebuild.  The
layers to listen to are the beam sustains: LaserA/CSustain* are authored
LoopAtWill on a TRANSIENT source, so they now end with the sample instead of
looping until note-off.  The render type is T1 authored data; the interpretation
that LoopAtWill defers to it is a strong reading of the enum + defaults rather
than a disassembled statement, and is flagged as such in the KB.

Plausibly bears on #32 (audio cutting in/out late in a match): a stuck looping
source holds its pool slot for the rest of the round.

Rigs: scratchpad/flushsnd.py (flush start/stop pairing), flushsnd2.py (stuck-
source hunt), loopaudit.py (the classification matrix), loopab.py (the A/B).
KB: context/wintesla-port.md audio section, context/decomp-reference.md env
table (BT_LOOP_AUDIT / BT_LOOP_LEGACY / BT_AUDIO_DUMP / BT_AUD_TAIL),
docs/AUDIO_FIDELITY.md F38.  checkctx.py CLEAN.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 13:20:19 -05:00

220 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: wintesla-port
title: "The WinTesla Windows Port — the base everything builds on"
status: established
source_sections: "PROGRESS_LOG.md §5b, §8"
related_topics: [source-completeness, rendering, build-and-run, multiplayer]
key_terms: [WinTesla, L4D3D, MUNGA, RP, DPL]
---
# The WinTesla Windows Port
The `Elsewhen RP411` archive is a **working Windows port of the MUNGA engine** (VS2005,
`WinTesla.sln`). It means the hardest infrastructure is **already solved** — the bt411 build sits
on top of it. Full detail: `docs/PROGRESS_LOG.md §5b, §8`.
## What WinTesla already did (do NOT rebuild)
- **Renderer bypass: DONE** — `MUNGA_L4/L4D3D.cpp` + `DXUtils` replace libDPL / the IG board with
Direct3D9. No `libdpl.lib` in the tree. (The early from-scratch D3D9 viewer was retired and removed.) [T1]
- **Audio: BACKEND now REAL + soundbank loaded; only game-triggering remains** [T2, updated
2026-07-15]. OpenAL replaces the HMI "SOS" engine and the renderer→device→buffer→source→play chain
IS all implemented (`L4AUDRND`/`L4AUDRES`/`L4AUDLVL`; `PatchLevelOfDetail::PlayNote` really calls
`alSourcePlay`). The "no sound, ever" root cause was DEEPER than a gate — **both audio backend DLLs
shipped in `engine/lib/` were no-op STUBS**: (a) `libsndfile-1.dll` exports 15 funcs BY ORDINAL
ONLY (no names), `sf_open()` always returns NULL; (b) `OpenAL32.dll` (72KB) imported **only
KERNEL32** — no dsound/wasapi/winmm — so it returned fake handles (`ctx=0x00000001`,
`alGenSources→0`) and never touched the hardware. So the chain ran clean and silent. FIXES (commit
5b46655): **OpenAL32.dll → real OpenAL Soft 1.25.2 Win32** (drop-in: the exe imports the 25 AL funcs
by NAME; `alSourcePlay` now reaches `AL_PLAYING`). **libsndfile DROPPED** — replaced by `LoadWavPCM()`
in `L4AUDRES` (a tiny in-tree RIFF/WAVE fmt+data reader, no external dep). Gates fixed earlier: the
`AWE_FRONT`/`AWE_REAR` env-var gate in `MakeAudioRenderer` was **relaxed to default-on** (`BT_NO_AUDIO=1`
disables). **Soundbank recovered**: 241 samples cracked from `AUDIO1/2.RES` (SF2 v1.0) by
`tools/sf2extract.py``content/AUDIO/*.wav` + the `allPresets[2][128]` table (`audiopresets.cpp`,
replacing the zero-init btstubs stub); all 241 load (`alErr=0`). **In-game triggering now WORKS** (commit 7b1e469): enabling audio unlocked
`L4AudioRenderer::CreateEntityAudioObjects`, which binds `AudioStateWatcher`s to entity/subsystem
state attrs BY NAME (the **attribute-pointer** databinding, same as gauges) and calls
`AddAudioWatcher` on the resolved `StateIndicator`. The reconstruction had pointed the Mech's state
attrs at the scalar read-pad `attrPad` (fine for a gauge that READs a value, fatal for a state watcher
that calls a METHOD → AV in `SChainOf::Add` on 0xCDCDCDCD). FIXED: the Mech's `AnimationState`/
`ReplicantAnimationState` (driven from `SetBodyAnimation`) and `CollisionState` (driven from
`ProcessCollision` via `collisionTemporaryState`) are now real `StateIndicator` members — **verified:
walking fires `SetupPatch`+`PlayNote`/`alSourcePlay` (footstep/gait patches 78/80/116)**. So mech
movement/animation/collision audio is live. **Subsystem STATE audio DONE** (commits 5ba541e/0e05a22/dcdd7dd):
the audio watcher TYPE per attribute was recovered via a `MakeObjectImplementation` ClassID trace
(class 28 = `AudioStateTrigger`/StateIndicator). KEY: the binary's 0x54-byte subsystem alarm
(`FUN_0041b9ec`, reconstructed as `GaugeAlarm54`) **IS a StateIndicator** — its "three sub-indicators"
@0x18/0x2c/0x40 are the audio/video/gauge watcher SChains, `level@0x14` = currentState. The
reconstruction had modeled that tail as an opaque `_tail` (never constructed → AddAudioWatcher AV'd on
0xCDCDCDCD). FIX: `GaugeAlarm54` now has three REAL constructed sockets + `AddAudioWatcher`; `SetLevel`
fires them on change (empty sockets = no-op, so other alarms/weapons unaffected; `levelB`/`LevelCountB`
untouched; sizeof stays 0x54). Then registered each state attr → its subsystem's own alarm (own
`AttributePointers[]` chained to the parent): `Generator.GeneratorState`→stateAlarm,
`Condenser.CondenserState`→condenserAlarm, `Reservoir.ReservoirState`→reservoirAlarm,
`AmmoBin.AmmoState`→ammoAlarm (+ chained AmmoBin's bare index so `SimulationState` resolves too).
`GeneratorOn`→generatorOn. **audiostate skips 85 → 0** — every StateIndicator audio attribute across
the mech + all subsystems binds to a real indicator, and the alarms are already `SetLevel`'d by the
sims, so generator/condenser/reservoir/ammo state sounds fire on transition. **Still inert** (read 0,
silent, non-crashing — no modeled backing member in the size-locked layouts, a polish follow-up):
`ReportLeak` (Logical), `Torso.MotionState` (Enum), `Torso.SpeedOfTorsoHorizontal` (Scalar).
NOW REAL (fidelity Phase 1, commit fc7f311): `ConfigureActivePress` published at the **MechSubsystem
BASE** (binary attr id 2, descriptor @0x50de5c → member @+0x110 — the field previously misnamed
`vitalSubsystemIndex`; idle 1, driven 0/1 by `MechWeapon::ConfigureMappablesMessageHandler`),
`AmmoBin.FireCountdownStarted``cookOffArmed`@0x18C, `L4MechControlsMapper.TargetRangeExponent`
`targetRangeExponent`@0x1a4. The **bring-up guards**
[T3, self-clearing] stay: a NULL subsystem attr → inert pad (was a fatal `Fail`); an
`AudioStateWatcher` on an unconstructed indicator (chain@+0x18 null/0xCDCDCDCD) → skip. Both pass
automatically once a subsystem is built. Diagnostics: `BT_AUDIO_LOG` (chain + skips/redirects), `BT_ATTRBIND_LOG` (every
attr bind), `BT_AUDIO_TEST` (proof-of-life buffer0).
- **Audio FIDELITY pass (2026-07-16, commits fc7f311 + 691e885; the full audit ledger is
`docs/AUDIO_FIDELITY.md`, 37 findings).** Settled facts [T1 unless noted]:
- **Patch numbering = SF2 `wPreset`.** Any tooling must key presets on `wPreset`, never on phdr
record order. `AUDIO1/2.RES` are SF2 **v1.0** (16-byte shdr, NO name/rate/pitch field); pitch
metadata rides generators: 55 = SBK samplePitch (`root*100+cents`, on every zone), 58 =
overridingRootKey, 51/52 = coarse/fineTune. **EMU8000 v1 base rate = 44100 Hz** → a zone's true
data rate is baked into its WAV: `rate = round(44100·2^(((6000rootCents)+tune)/1200))`
(`tools/sf2extract.py`); the engine's note-60 pitch model (`BTNotePitchFactor`) then reproduces
original playback at every note. SBK `initialAttenuation`(48) is **INVERTED** (127 = full
volume; 0.375 dB/step [T3 exact curve], baked into the PCM).
- **Full-zone table**: 603 zones / 241 presets in `audiopresets.cpp`; the authored MIDI note
SELECTS zones by keyRange in `SetupPatch/PlayNote` (key-splits: Loaded/Jam family low-36
clunk vs high-84 blip; Warnings01 8-way klaxon split), layers attach together,
`MAX_PRESET_SAMPLES=25` (AllExplosion). Authored loop REGIONS apply via `AL_SOFT_loop_points`;
authored `releaseVolEnv` (1.13.9 s on ~20 looping presets) fades on Stop
(`PRESET_serviceReleaseFades`, serviced from `AudioHead::Execute`).
- **⚠ THE LOOP FLAG IS NOT THE LOOP DECISION (Gitea #51, 2026-07-25).** `SetupPatch`
(`L4AUDLVL.cpp`) used to map `AL_LOOPING = (sample != ForceStatic)`, which armed **224 of the
603 authored samples** as OpenAL sources that never end — any whose note-off never arrives plays
**forever** (SAURON: "coolant flush sound glitched and perma"). Three facts say the sample flag
only expresses PERMISSION, and the SOURCE decides [T1]:
(1) the enum's own comments (`L4AUDLVL.h:14-19`) — `LoopAtWill` = "will play once **or loop as
desired**", `ForceStatic` = "plays only once **even if looped**" (a veto), `LoopAlways` =
always; (2) `LoopAtWill` is enum value **0**, i.e. the DEFAULT an unauthored sample receives
(`WTPresets.cpp:37`) — it cannot mean "loop forever"; (3) `LoopAlways`, the real always-loop, is
used by **zero** shipped samples. "As desired" is the source's authored **`AudioRenderType`**
(`Transient`=one-shot vs `Sustained`=held), streamed from `AUDIO*.RES` (`AUDLVL.cpp:28`) and
already consulted by the renderer (`L4AUDRND.cpp:567`). New rule: `LoopAlways`→1,
`ForceStatic`→0, `LoopAtWill`→ the source's render type. **Measured** (`BT_LOOP_AUDIT=1`,
`scratchpad/loopaudit.py`): `LoopAtWill×Transient` 1946 events flip 1→0, `LoopAtWill×Sustained`
65 events stay 1, `ForceStatic×Transient` 292 unchanged — the engine loops
(EngineAccel07_z0-z2/EngineMotor01/EnginePower01) are all Sustained and **preserved**; all 24
reclassified samples are genuine one-shots (laser charge/fire/explosion, missile loading,
coolant pressure inc/dec). **A/B proof** (`scratchpad/loopab.py`): under the old rule
`CoolantPresInc03_z2` + `CoolantPresDcr03_z2` are still playing with `loop=1` after every
release; under the new rule zero coolant sources survive, engine loop intact. `BT_LOOP_LEGACY=1`
restores the old rule for a field A/B — listen to the beam-sustain layers (`LaserA/CSustain*` are
`LoopAtWill` on a Transient source, so they now end with the sample). A stuck looping source also
holds a pool slot for the rest of the match, so this plausibly contributed to **#32**'s dropouts.
- **Distance/volume/doppler**: OpenAL distance model is `AL_NONE` — the AUTHORED
`GetDistanceVolumeScale()` curve multiplies in `CalculateSourceVolumeScale` (Dynamic3D +
Static3D); per-source `AL_GAIN = volume_scale²` (GM CC7 squared law); `alDopplerFactor(0)`
the authored `GetDopplerCents()` (AUDIO.INI model, consumer decomp part_008.c:7466) adds into
the Dynamic3D pitch chain.
- **Audio clock**: `FUN_0044e19c` is `ApplicationManager::GetFrameRate` — the original audio
"frames" were SYSTEM CLOCK TICKS; `MakeAudioRenderer` calibrates at
`SystemClock::GetTicksPerSecond()` (fallback 1000), never the old garbage
`divisionParameters+0x10` read.
- **Published `localVelocity` smoothing is authentic** [T1]: `Mech::Execute` (FUN_004ab430) runs
the localVelocity components through `AverageOf` running-average filters
(part_012.c:15169-15179); the port's ~0.25 s exponential filter in `mech4.cpp` reconstructs it.
- **Phases 3+4 SHIPPED (2026-07-16, commits 0ca7d26..0e2401f).** Phase 3 EFX: `L4AUDEFX`
bridge — per-source lowpass (Dyn3D = highFreqCutoffScale×brightness UNGATED, Static/Direct =
brightness; AWE 1008000 Hz → 5 kHz-ref gainhf [T3 curve]), one EAXReverb slot at
global_reverb_scale 0.3 (3D wet / cockpit Direct dry), Direct position-enum placement.
Phase 4 attributes (all [T1] from binary attribute tables, walked as 16-byte
{id,name,off+1,pad} rows): **HeatSink table @0x50e438 (ids 3..12)** confirms every heat
binding + adds ReportLeak id 12 @0x138 = coolantActive (serves all 19 leak watchers via
PoweredSubsystem⊂HeatSink); **Mech table @0x50c0xx (ids 21..56)**: IncomingLock@0x3fc /
DistanceToMissile@0x400 (real members, Missile::MoveAndCollide reports, PerformAndWatch
latches; old `maxSpeed@0x400` was a misread of the FLT_MAX far default),
CollisionSpeed@0x4B4 (=|impact velocity| at contact-arm), ReduceButton@0x340,
FootStep@0x394, RearFiring@0x410 (old `stateFlags` misread); **Torso table (ids 3..15)**:
full publish onto existing members (SpeedOfTorsoHorizontal=twistVelocity@0x1E8 abs'd,
MotionState=statusFlags@0x20C, ==2 = limit-hit frame). **F5 footStep = CONTACT LEVEL**:
(jointlocal.y <= *ANI hdr[2] threshold) per frame, states 0/1 retain (SelectSequence now
captures hdr[2]; the 150 ms pulse is retired). **F19**: the authored footstep volume =
LocalAcceleration |lin| [0,10]→ctl100 + LocalVelocity |lin| [0,0.6]→ctl101 (ALL motion
watchers extract |linearMotion|, motionValue=3); localAcceleration.linear = d(published
velocity)/dt (binary part_012.c:15186-15195); the mech2.cpp step broadcast is REMOVED.
**attrnull count = 0** — every authored audio attribute binds a real member.
- **Post-Phase-4 closures (2026-07-16):** UnstablePercentage is LIVE (the instability model
@0x3F0 byte-decoded + reconstructed — accel-sway² + gun-the-engine + turn-in-place terms,
feeds gyro swayBias + the audio alarm; commit c51d64a). The F14 per-zone static resonant
low-pass is BAKED (RBJ biquad at the AWE 1008000 Hz curve, float pipeline + peak
normalization; 232 zones incl the LaserLoaded charge hum fc=57 Q=87; c581553). The
**footstep 1020 s warm-up bug** is fixed (a11a697): an idle source's watcher chain executes
only at Start attempts and each hop is audio-frame-gated, so the authored fill-0 N=30
smoothers starved behind the 0.3 transient drop gate — fixes: AudioScaleOf sends EVERY poll
(the bitwise change-gate froze on our deterministic gait floats), recursive gate-free
Component::PrimeWatchers(30) on every transient Start request (AUDREND), and
localAcceleration via the binary's exact 15-sample ring buffers. LESSON: trace caps
saturate early and fake "per-stride" rates — always timestamp before inferring cadence.
- Still deferred: F21 attack velocity/time (all captured events were default 1), F22 HRTF
(user has 2 speakers; enable via alsoft.ini `hrtf=true` for headphones).
- **Energy-weapon post-beam buzz = AUTHENTIC (Gitea #5 verdict, 2026-07-20)** [T2, measured]:
the reported "buzz/electrical sustaining 12 s past the beam" is the AUTHORED charge loop
(`LaserACharge01-04`, looped, release 1.309 s), started by the WeaponState==3 (Loading) watcher
at beam-off, volume authored to track PercentDone (crescendo to ready), stopped on 3→2 with the
authored 1.309 s release fade. Measured live (BT_AUD_TAIL=1 + BT_FIRE_PULSE single shots):
beam 203207 ms (authored DischargeTime 0.2 s); sustain loops cut ≤2 ms after leave-Firing
(authored release 0); charge StopNote ≤2 ms after Loading→Loaded; fades 1.3111.326 s vs
authored 1.309 s (≤ 1 frame over); laser recharge 2.33.8 s, PPC 8.816.3 s under multi-weapon
generator load (authentic electrical model). NOT the #12 source overflow — no energy preset
exceeds 5 zones (max 3; >5 is only the death/UI families: AllExplosion 25, MechExplosion01/
Death01 16, AllButtons 14, AllCoolant/AllProjectile 9, Warnings01/AuxExplosion01/AllHits 8,
AllWarning 7). Caveat: the reporter's 2026-07-18 build predated 2edde29 (Emitter::SetDirty
DelayWatchersFlag poison retirement — PerformAndWatch skips ExecuteWatchers while set,
SIMULATE.cpp:461; cleared only by the encore path, UPDATE.cpp:215), which made emitter audio
watcher timing encore-erratic that session. Harness kept (env-gated, additive): `[aud-tail]`
logging in `L4AUDLVL.cpp` (PLAY/STOP/fade) + `emitter.cpp` (state transitions), and
`BT_FIRE_PULSE="on,off"` single-shot trigger pulsing in `mech4.cpp`.
- **Audio-dropout fix (field 2026-07-23: "cutting in and out toward the end of the match")** [T2,
code-verified vs the AL spec; awaiting live playtest confirm]: three structural defects in the
port's OpenAL source-pool lifecycle (`L4AUDRND.cpp` / `L4AUDIO.cpp` / `L4AUDHDW.h`):
(1) `SourceSet.sources[]` was **uninitialized heap garbage** until first acquisition — AL names
are small ints, so a recycled-heap slot could alias a LIVE source owned by another sound;
`alIsSource()` then skipped generation, two sounds shared one source, and whichever released
first deleted the other's source mid-play (random sound deaths worsening with heap churn).
Fix: `SourceSet` ctor zero-inits (0 is never a valid name). (2) `ReleaseSourceSet` used bulk
`alDeleteSources(count, sources)`, which is **ATOMIC on invalid names** — one empty/-1 slot
(partial set, double release) and NOTHING deleted; after the first pool exhaustion every partial
release leaked and the pool never recovered. Fix: per-slot guarded delete, slots parked at 0.
(3) Failed acquisitions stranded partial sets forever — dropped transients are never released by
anything (the dtor didn't release; maintenance only touches running sources). Fix: handback via
`ReleaseChannels()` at both failure sites (StartRequest + dormant-resume) + a dtor backstop.
Context: the port's `RequestAudioChannels` has NO channel budget (the 1995 per-card budget is
the commented-out block) — it alGenSources until OpenAL Soft's ~256 cap; on failure a transient
is silently dropped (StartRequest early-return), a sustained goes dormant — which is exactly the
"cutting in and out" presentation. Priority-steal (`RequestAudioResources`) only engages at cap.
PERMANENT diagnostics: 30 s `[audio] source census: live/acquireFails` + an always-on
`[audio] ACQUIRE FAILED` line. Baseline (2-node soak): live ≈ 21 peak under fire, 0 at mission
end; exhaustion needs 7-player scale or the leak spiral, aliasing needs neither.
- **L4 HAL on Windows + the VS build system: DONE.** [T1]
- **Red Planet game logic: COMPLETE & buildable** — `VTV/VTVMPPR/WEAPSYS/...`. RP is at/near playable. [T1]
- **Multiplayer: the DOS `NETNUB` driver is already replaced by a ~3.5k-line WinSock2 TCP
reimplementation** (`MUNGA_L4/L4NET.CPP`) + the master/replicant distributed-sim core. See
[[multiplayer]]. [T1]
## What BT still needs (the only remaining work below the game layer is NONE)
Everything below the game layer — engine, renderer, audio, HAL, build — exists and is shared. The
ONLY thing BT needs is its **game-logic source**, which is gone (see [[source-completeness]]) → it
must be **reconstructed**, with the three anchors (BT headers + the WinTesla engine to compile
against + RP's parallel code + the binary oracle). [T2]
## Build implications
- `WinTesla.sln` is **VS2005** and hard-depends on the **legacy DirectX SDK (June 2010)** (`d3dx9`,
`dinput`, `dxerr` — all removed from the modern Windows SDK). The DXSDK is installed at
`C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\`. [T2]
- The shared engine builds GREEN to `munga_engine.lib` via CMake/MSVC Win32 (see [[build-and-run]]).
Recipe gotchas (include-path shadowing, the CAMMGR/time.h fixes, the ATL shim) are in
`docs/PROGRESS_LOG.md §8`. [T2]
## Roadmap position
Old plan ("build a libDPL shim + port the engine") is **superseded** — WinTesla did it. New plan:
reuse the WinTesla engine (shared) + reconstruct the BT game library (`BT410_L4` + game logic). The
from-scratch libDPL shim is no longer needed. Phases 4-8 (BT game layer + pod bring-up) still apply.
[T2]
## Key Relationships
- Solves: the infrastructure that [[source-completeness]] would otherwise require.
- Feeds: [[build-and-run]], [[rendering]], [[multiplayer]].