khora_data/ecs/systems/audio_playback_writeback.rs
1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Drains the [`AudioPlaybackWriteback`](crate::flow::AudioPlaybackWriteback)
16//! slot the `SpatialMixingLane` wrote into during the CLAD descent and
17//! applies the per-source playback-state updates back to `AudioSource`
18//! components.
19//!
20//! This system is the canonical Lane → Deck → DataSystem writeback
21//! pattern: the lane is a pure strategy that publishes into the typed
22//! `OutputDeck`; the engine threads the same deck through to the
23//! `Maintenance`-phase `DataSystem`s; this system drains the slot and
24//! mutates the World. No subsystem-specific code lives in the engine.
25
26use khora_core::lane::OutputDeck;
27use khora_core::Runtime;
28
29use crate::ecs::{AudioSource, DataSystemRegistration, TickPhase, World};
30use crate::flow::AudioPlaybackWriteback;
31
32fn audio_playback_writeback(world: &mut World, _runtime: &Runtime, deck: &mut OutputDeck) {
33 let drained = deck.take::<AudioPlaybackWriteback>();
34 if drained.updates.is_empty() {
35 return;
36 }
37
38 // Apply each update to its source. The lane uses the entity's index
39 // (its position in the projected `AudioView::sources`) as the
40 // writeback key, matching the order of `world.query::<&AudioSource>`
41 // — so we match by linear order here too.
42 let mut iter = world.query_mut::<&mut AudioSource>();
43 for update in drained.updates {
44 if let Some(source) = iter.next() {
45 source.state = update.new_state;
46 }
47 }
48}
49
50inventory::submit! {
51 DataSystemRegistration {
52 name: "audio_playback_writeback",
53 phase: TickPhase::Maintenance,
54 run: audio_playback_writeback,
55 order_hint: 10, // After physics_world_writeback (default 0).
56 runs_after: &[],
57 }
58}