1use core::fmt::Display;
2
3use crate::{
4 component::{ComponentId, Components},
5 query::{Access, QueryData},
6};
7use bevy_utils::BloomFilter;
8
9const USE_FILTER_THRESHOLD: usize = 4;
15
16#[inline(never)]
20pub fn has_conflicts<Q: QueryData>(components: &Components) -> Result<(), QueryAccessError> {
21 let Some(state) = Q::get_state(components) else {
22 return Err(QueryAccessError::ComponentNotRegistered);
23 };
24
25 let result = if let Some(size) = Q::iter_access(&state).size_hint().1
26 && size <= USE_FILTER_THRESHOLD
27 {
28 has_conflicts_small::<Q>(&state)
29 } else {
30 has_conflicts_large::<Q>(&state)
31 };
32 if let Err(e) = result {
33 panic!("{e}");
34 }
35
36 Ok(())
37}
38
39fn has_conflicts_small<'a, Q: QueryData>(
44 state: &'a Q::State,
45) -> Result<(), AccessConflictError<'a>> {
46 let mut inner_access = [EcsAccessType::Empty; USE_FILTER_THRESHOLD];
48 for (i, access) in Q::iter_access(state).enumerate() {
49 for access_other in inner_access.iter().take(i) {
50 if access.is_compatible(*access_other).is_err() {
51 return Err(AccessConflictError(access, *access_other));
52 }
53 }
54 inner_access[i] = access;
55 }
56
57 Ok(())
58}
59
60fn has_conflicts_large<'a, Q: QueryData>(
65 state: &'a Q::State,
66) -> Result<(), AccessConflictError<'a>> {
67 let mut filter = BloomFilter::<8, 2>::new();
69 for (i, access) in Q::iter_access(state).enumerate() {
70 let needs_check = match access {
71 EcsAccessType::Component(EcsAccessLevel::Read(component_id))
72 | EcsAccessType::Component(EcsAccessLevel::Write(component_id)) => {
73 filter.check_insert(&component_id.index())
74 }
75 EcsAccessType::Component(EcsAccessLevel::ReadAll)
76 | EcsAccessType::Component(EcsAccessLevel::WriteAll) => true,
77 EcsAccessType::Access(access) => {
78 if let Ok(component_iter) = access.try_iter_access() {
79 let mut needs_check = false;
80 for kind in component_iter {
81 let index = match kind {
82 crate::query::ComponentAccessKind::Shared(id)
83 | crate::query::ComponentAccessKind::Exclusive(id)
84 | crate::query::ComponentAccessKind::Archetypal(id) => id.index(),
85 };
86 if filter.check_insert(&index) {
87 needs_check = true;
88 }
89 }
90 needs_check
91 } else {
92 true
93 }
94 }
95 EcsAccessType::Empty => continue,
96 };
97 if needs_check {
98 for (j, access_other) in Q::iter_access(state).enumerate() {
100 if i == j {
101 continue;
102 }
103 if access.is_compatible(access_other).is_err() {
104 return Err(AccessConflictError(access, access_other));
105 }
106 }
107 }
108 }
109 Ok(())
110}
111
112#[derive(Copy, Clone, Debug, PartialEq)]
114pub enum EcsAccessType<'a> {
115 Component(EcsAccessLevel),
117 Access(&'a Access),
119 Empty,
121}
122
123impl<'a> EcsAccessType<'a> {
124 #[inline(never)]
126 pub fn is_compatible(&self, other: Self) -> Result<(), AccessConflictError<'_>> {
127 use EcsAccessLevel::*;
128 use EcsAccessType::*;
129
130 match (*self, other) {
131 (Component(ReadAll), Component(Write(_)))
132 | (Component(Write(_)), Component(ReadAll))
133 | (Component(_), Component(WriteAll))
134 | (Component(WriteAll), Component(_)) => Err(AccessConflictError(*self, other)),
135
136 (Empty, _)
137 | (_, Empty)
138 | (Component(Read(_)), Component(Read(_)))
140 | (Component(ReadAll), Component(Read(_)))
141 | (Component(Read(_)), Component(ReadAll))
142 | (Component(ReadAll), Component(ReadAll))
143 => {
144 Ok(())
145 }
146
147 (Component(Read(id)), Component(Write(id_other)))
148 | (Component(Write(id)), Component(Read(id_other)))
149 | (Component(Write(id)), Component(Write(id_other)))
150 => if id == id_other {
151 Err(AccessConflictError(*self, other))
152 } else {
153 Ok(())
154 },
155
156 (Component(Read(component_id)), Access(access))
158 | (Access(access), Component(Read(component_id))) => if access.has_write(component_id) {
159 Err(AccessConflictError(*self, other))
160 } else {
161 Ok(())
162 },
163
164 (Component(Write(component_id)), Access(access))
165 | (Access(access), Component(Write(component_id))) => if access.has_read(component_id) {
166 Err(AccessConflictError(*self, other))
167 } else {
168 Ok(())
169 },
170
171 (Component(ReadAll), Access(access))
172 | (Access(access), Component(ReadAll)) => if access.has_any_write() {
173 Err(AccessConflictError(*self, other))
174 } else {
175 Ok(())
176 },
177
178 (Component(WriteAll), Access(access))
179 | (Access(access), Component(WriteAll))=> if access.has_any_read() {
180 Err(AccessConflictError(*self, other))
181 } else {
182 Ok(())
183 },
184
185 (Access(access), Access(other_access)) => if access.is_compatible(other_access) {
186 Ok(())
187 } else {
188 Err(AccessConflictError(*self, other))
189 },
190 }
191 }
192}
193
194#[derive(Clone, Copy, Debug, PartialEq, Hash)]
197pub enum EcsAccessLevel {
198 Read(ComponentId),
200 Write(ComponentId),
202 ReadAll,
204 WriteAll,
206}
207
208pub struct AccessConflictError<'a>(EcsAccessType<'a>, EcsAccessType<'a>);
210
211impl Display for AccessConflictError<'_> {
212 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
213 use EcsAccessLevel::*;
214 use EcsAccessType::*;
215
216 let AccessConflictError(a, b) = self;
217 match (a, b) {
218 (Component(ReadAll), Component(Write(id)))
220 | (Component(Write(id)), Component(ReadAll)) => {
221 write!(
222 f,
223 "Component read all access conflicts with component {id:?} write."
224 )
225 }
226 (Component(WriteAll), Component(Write(id)))
227 | (Component(Write(id)), Component(WriteAll)) => {
228 write!(
229 f,
230 "Component write all access conflicts with component {id:?} write."
231 )
232 }
233 (Component(WriteAll), Component(Read(id)))
234 | (Component(Read(id)), Component(WriteAll)) => {
235 write!(
236 f,
237 "Component write all access conflicts with component {id:?} read."
238 )
239 }
240 (Component(WriteAll), Component(ReadAll))
241 | (Component(ReadAll), Component(WriteAll)) => {
242 write!(f, "Component write all conflicts with component read all.")
243 }
244 (Component(WriteAll), Component(WriteAll)) => {
245 write!(f, "Component write all conflicts with component write all.")
246 }
247
248 (Component(Read(id)), Component(Write(id_other)))
250 | (Component(Write(id_other)), Component(Read(id))) => write!(
251 f,
252 "Component {id:?} read conflicts with component {id_other:?} write."
253 ),
254 (Component(Write(id)), Component(Write(id_other))) => write!(
255 f,
256 "Component {id:?} write conflicts with component {id_other:?} write."
257 ),
258
259 (Access(_), Component(Read(id))) | (Component(Read(id)), Access(_)) => write!(
261 f,
262 "Access has a write that conflicts with component {id:?} read."
263 ),
264 (Access(_), Component(Write(id))) | (Component(Write(id)), Access(_)) => write!(
265 f,
266 "Access has a read that conflicts with component {id:?} write."
267 ),
268 (Access(_), Component(ReadAll)) | (Component(ReadAll), Access(_)) => write!(
269 f,
270 "Access has a write that conflicts with component read all"
271 ),
272 (Access(_), Component(WriteAll)) | (Component(WriteAll), Access(_)) => write!(
273 f,
274 "Access has a read that conflicts with component write all"
275 ),
276 (Access(_), Access(_)) => write!(f, "Access conflicts with other Access"),
277
278 _ => {
279 unreachable!("Other accesses should be compatible");
280 }
281 }
282 }
283}
284
285#[derive(Clone, Copy, Debug, PartialEq)]
287pub enum QueryAccessError {
288 ComponentNotRegistered,
290 EntityDoesNotMatch,
292}
293
294impl core::error::Error for QueryAccessError {}
295
296impl Display for QueryAccessError {
297 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
298 match *self {
299 QueryAccessError::ComponentNotRegistered => {
300 write!(
301 f,
302 "At least one component in Q was not registered in world.
303 Consider calling `World::register_component`"
304 )
305 }
306 QueryAccessError::EntityDoesNotMatch => {
307 write!(f, "Entity does not match Q")
308 }
309 }
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316 use crate::{
317 prelude::Component,
318 query::WorldQuery,
319 world::{EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, World},
320 };
321
322 #[derive(Component)]
323 struct C1;
324
325 #[derive(Component)]
326 struct C2;
327
328 fn setup_world() -> World {
329 let world = World::new();
330 let mut world = world;
331 world.register_component::<C1>();
332 world.register_component::<C2>();
333 world
334 }
335
336 fn assert_compatible<Q: WorldQuery + QueryData>() {
337 let world = setup_world();
338 let c = world.components();
339
340 let state = <Q>::get_state(c).unwrap();
341 assert!(has_conflicts_small::<Q>(&state).is_ok());
342 assert!(has_conflicts_large::<Q>(&state).is_ok());
343 assert!(has_conflicts::<Q>(c).is_ok());
344 }
345
346 fn assert_conflicted<Q: WorldQuery + QueryData>() {
347 let world = setup_world();
348 let c = world.components();
349
350 let state = <Q>::get_state(c).unwrap();
351 assert!(has_conflicts_small::<Q>(&state).is_err());
352 assert!(has_conflicts_large::<Q>(&state).is_err());
353 let _ = has_conflicts::<Q>(c);
354 }
355
356 #[test]
357 fn simple_compatible() {
358 assert_compatible::<&mut C1>();
359 assert_compatible::<&C1>();
360 assert_compatible::<(&C1, &C1)>();
361 }
362
363 #[test]
364 #[should_panic(expected = "conflicts")]
365 fn conflict_component_read_conflicts_write() {
366 assert_conflicted::<(&C1, &mut C1)>();
367 }
368
369 #[test]
370 #[should_panic(expected = "conflicts")]
371 fn conflict_component_write_conflicts_read() {
372 assert_conflicted::<(&mut C1, &C1)>();
373 }
374
375 #[test]
376 #[should_panic(expected = "conflicts")]
377 fn conflict_component_write_conflicts_write() {
378 assert_conflicted::<(&mut C1, &mut C1)>();
379 }
380
381 #[test]
382 fn entity_ref_compatible() {
383 assert_compatible::<(EntityRef, &C1)>();
384 assert_compatible::<(&C1, EntityRef)>();
385 assert_compatible::<(EntityRef, EntityRef)>();
386 }
387
388 #[test]
389 #[should_panic(expected = "conflicts")]
390 fn entity_ref_conflicts_component_write() {
391 assert_conflicted::<(EntityRef, &mut C1)>();
392 }
393
394 #[test]
395 #[should_panic(expected = "conflicts")]
396 fn component_write_conflicts_entity_ref() {
397 assert_conflicted::<(&mut C1, EntityRef)>();
398 }
399
400 #[test]
401 #[should_panic(expected = "conflicts")]
402 fn entity_mut_conflicts_component_read() {
403 assert_conflicted::<(EntityMut, &C1)>();
404 }
405
406 #[test]
407 #[should_panic(expected = "conflicts")]
408 fn component_read_conflicts_entity_mut() {
409 assert_conflicted::<(&C1, EntityMut)>();
410 }
411
412 #[test]
413 #[should_panic(expected = "conflicts")]
414 fn entity_mut_conflicts_component_write() {
415 assert_conflicted::<(EntityMut, &mut C1)>();
416 }
417
418 #[test]
419 #[should_panic(expected = "conflicts")]
420 fn component_write_conflicts_entity_mut() {
421 assert_conflicted::<(&mut C1, EntityMut)>();
422 }
423
424 #[test]
425 #[should_panic(expected = "conflicts")]
426 fn entity_mut_conflicts_entity_ref() {
427 assert_conflicted::<(EntityMut, EntityRef)>();
428 }
429
430 #[test]
431 #[should_panic(expected = "conflicts")]
432 fn entity_ref_conflicts_entity_mut() {
433 assert_conflicted::<(EntityRef, EntityMut)>();
434 }
435
436 #[test]
437 fn entity_ref_except_compatible() {
438 assert_compatible::<(EntityRefExcept<C1>, &mut C1)>();
439 assert_compatible::<(&mut C1, EntityRefExcept<C1>)>();
440 assert_compatible::<(&C2, EntityRefExcept<C1>)>();
441 assert_compatible::<(&mut C1, EntityRefExcept<(C1, C2)>)>();
442 assert_compatible::<(EntityRefExcept<(C1, C2)>, &mut C1)>();
443 assert_compatible::<(&mut C1, &mut C2, EntityRefExcept<(C1, C2)>)>();
444 assert_compatible::<(&mut C1, EntityRefExcept<(C1, C2)>, &mut C2)>();
445 assert_compatible::<(EntityRefExcept<(C1, C2)>, &mut C1, &mut C2)>();
446 }
447
448 #[test]
449 #[should_panic(expected = "conflicts")]
450 fn entity_ref_except_conflicts_component_write() {
451 assert_conflicted::<(EntityRefExcept<C1>, &mut C2)>();
452 }
453
454 #[test]
455 #[should_panic(expected = "conflicts")]
456 fn component_write_conflicts_entity_ref_except() {
457 assert_conflicted::<(&mut C2, EntityRefExcept<C1>)>();
458 }
459
460 #[test]
461 fn entity_mut_except_compatible() {
462 assert_compatible::<(EntityMutExcept<C1>, &mut C1)>();
463 assert_compatible::<(&mut C1, EntityMutExcept<C1>)>();
464 assert_compatible::<(&mut C1, EntityMutExcept<(C1, C2)>)>();
465 assert_compatible::<(EntityMutExcept<(C1, C2)>, &mut C1)>();
466 assert_compatible::<(&mut C1, &mut C2, EntityMutExcept<(C1, C2)>)>();
467 assert_compatible::<(&mut C1, EntityMutExcept<(C1, C2)>, &mut C2)>();
468 assert_compatible::<(EntityMutExcept<(C1, C2)>, &mut C1, &mut C2)>();
469 }
470
471 #[test]
472 #[should_panic(expected = "conflicts")]
473 fn entity_mut_except_conflicts_component_read() {
474 assert_conflicted::<(EntityMutExcept<C1>, &C2)>();
475 }
476
477 #[test]
478 #[should_panic(expected = "conflicts")]
479 fn component_read_conflicts_entity_mut_except() {
480 assert_conflicted::<(&C2, EntityMutExcept<C1>)>();
481 }
482
483 #[test]
484 #[should_panic(expected = "conflicts")]
485 fn entity_mut_except_conflicts_component_write() {
486 assert_conflicted::<(EntityMutExcept<C1>, &mut C2)>();
487 }
488
489 #[test]
490 #[should_panic(expected = "conflicts")]
491 fn component_write_conflicts_entity_mut_except() {
492 assert_conflicted::<(&mut C2, EntityMutExcept<C1>)>();
493 }
494}