bevy_map_scatter/
events.rs

1use bevy::prelude::*;
2use crossbeam_channel::{Receiver, Sender};
3use map_scatter::prelude::{EventSink, ScatterEvent, ScatterEventKind};
4
5/// Delivery priority for scatter events in the bus.
6#[derive(Debug, Copy, Clone, PartialEq, Eq)]
7pub enum ScatterEventPriority {
8    High,
9    Low,
10}
11
12/// Filters which scatter events are forwarded onto the bus.
13#[derive(Debug, Clone)]
14pub struct ScatterEventFilter {
15    /// Emit run start/finish events.
16    pub emit_run_events: bool,
17    /// Emit layer start/finish events.
18    pub emit_layer_events: bool,
19    /// Emit per-position evaluation events.
20    pub emit_position_evaluated: bool,
21    /// Emit per-placement events.
22    pub emit_placement_made: bool,
23    /// Emit overlay generation events.
24    pub emit_overlay_generated: bool,
25    /// Emit warnings.
26    pub emit_warnings: bool,
27}
28
29impl ScatterEventFilter {
30    /// High-level events only (no per-position or per-placement spam).
31    pub fn high_level() -> Self {
32        Self {
33            emit_run_events: true,
34            emit_layer_events: true,
35            emit_position_evaluated: false,
36            emit_placement_made: false,
37            emit_overlay_generated: true,
38            emit_warnings: true,
39        }
40    }
41
42    /// Emit all events.
43    pub fn verbose() -> Self {
44        Self {
45            emit_run_events: true,
46            emit_layer_events: true,
47            emit_position_evaluated: true,
48            emit_placement_made: true,
49            emit_overlay_generated: true,
50            emit_warnings: true,
51        }
52    }
53
54    pub fn wants(&self, kind: ScatterEventKind) -> bool {
55        match kind {
56            ScatterEventKind::RunStarted | ScatterEventKind::RunFinished => self.emit_run_events,
57            ScatterEventKind::LayerStarted | ScatterEventKind::LayerFinished => {
58                self.emit_layer_events
59            }
60            ScatterEventKind::PositionEvaluated => self.emit_position_evaluated,
61            ScatterEventKind::PlacementMade => self.emit_placement_made,
62            ScatterEventKind::OverlayGenerated => self.emit_overlay_generated,
63            ScatterEventKind::Warning => self.emit_warnings,
64        }
65    }
66
67    pub fn priority(kind: ScatterEventKind) -> ScatterEventPriority {
68        match kind {
69            ScatterEventKind::PositionEvaluated | ScatterEventKind::PlacementMade => {
70                ScatterEventPriority::Low
71            }
72            _ => ScatterEventPriority::High,
73        }
74    }
75}
76
77impl Default for ScatterEventFilter {
78    fn default() -> Self {
79        Self::high_level()
80    }
81}
82
83/// Configuration for the scatter bus.
84#[derive(Resource, Debug, Clone)]
85pub struct ScatterBusConfig {
86    /// Maximum number of messages buffered before backpressure applies.
87    pub capacity: usize,
88    /// Event filtering rules.
89    pub filter: ScatterEventFilter,
90}
91
92impl ScatterBusConfig {
93    pub fn new(capacity: usize, filter: ScatterEventFilter) -> Self {
94        Self { capacity, filter }
95    }
96
97    pub fn with_capacity(mut self, capacity: usize) -> Self {
98        self.capacity = capacity;
99        self
100    }
101
102    pub fn with_filter(mut self, filter: ScatterEventFilter) -> Self {
103        self.filter = filter;
104        self
105    }
106}
107
108impl Default for ScatterBusConfig {
109    fn default() -> Self {
110        Self {
111            capacity: 1024,
112            filter: ScatterEventFilter::high_level(),
113        }
114    }
115}
116
117/// Bevy message containing the originating scatter request entity and the underlying [`ScatterEvent`].
118#[non_exhaustive]
119#[derive(Message, Debug, Clone)]
120pub struct ScatterMessage {
121    /// Entity that initiated the scatter request.
122    pub request_entity: Entity,
123    /// Scatter event emitted by the runner.
124    pub event: ScatterEvent,
125}
126
127/// Global bus for streaming scatter events from async tasks to the main thread.
128#[derive(Resource, Clone)]
129pub struct ScatterBus {
130    /// Sender used by async tasks to publish scatter messages.
131    tx: Sender<ScatterMessage>,
132    /// Receiver drained on the main thread to dispatch messages.
133    rx: Receiver<ScatterMessage>,
134    config: ScatterBusConfig,
135}
136
137impl ScatterBus {
138    pub fn new(config: ScatterBusConfig) -> Self {
139        let (tx, rx) = crossbeam_channel::bounded(config.capacity);
140        Self { tx, rx, config }
141    }
142
143    pub fn sender(&self) -> &Sender<ScatterMessage> {
144        &self.tx
145    }
146
147    pub fn receiver(&self) -> &Receiver<ScatterMessage> {
148        &self.rx
149    }
150
151    pub fn config(&self) -> &ScatterBusConfig {
152        &self.config
153    }
154
155    pub fn filter(&self) -> &ScatterEventFilter {
156        &self.config.filter
157    }
158}
159
160impl FromWorld for ScatterBus {
161    fn from_world(world: &mut World) -> Self {
162        let config = world
163            .get_resource::<ScatterBusConfig>()
164            .cloned()
165            .unwrap_or_default();
166        Self::new(config)
167    }
168}
169
170/// Event sink that forwards events to the global scatter bus, tagging each event with the request entity.
171pub struct ChannelSink {
172    /// Request entity associated with the emitted events.
173    pub request: Entity,
174    /// Sender used to forward messages onto the bus.
175    pub tx: Sender<ScatterMessage>,
176    /// Filter applied to outgoing events.
177    pub filter: ScatterEventFilter,
178}
179
180impl EventSink for ChannelSink {
181    #[inline]
182    fn send(&mut self, event: ScatterEvent) {
183        let kind = event.kind();
184        if !self.filter.wants(kind) {
185            return;
186        }
187        let message = ScatterMessage {
188            request_entity: self.request,
189            event,
190        };
191        match ScatterEventFilter::priority(kind) {
192            ScatterEventPriority::High => {
193                let _ = self.tx.send(message);
194            }
195            ScatterEventPriority::Low => {
196                let _ = self.tx.try_send(message);
197            }
198        }
199    }
200
201    fn wants(&self, kind: ScatterEventKind) -> bool {
202        self.filter.wants(kind)
203    }
204}