1use std::any::TypeId;
4use std::ops::{Deref, DerefMut};
5
6use crate::component::Component;
7use crate::entity::Entity;
8use crate::query::{Mut, QueryIter, QueryIterMut};
9use crate::world::UnsafeWorldCell;
10
11#[derive(Clone, Debug, PartialEq, Eq)]
17pub enum Access {
18 ResRead(TypeId),
20 ResWrite(TypeId),
22 CompRead(TypeId),
24 CompWrite(TypeId),
26}
27
28impl Access {
29 pub fn conflicts_with(&self, other: &Access) -> bool {
38 match (self, other) {
39 (Access::ResRead(a), Access::ResWrite(b))
40 | (Access::ResWrite(a), Access::ResRead(b))
41 | (Access::ResWrite(a), Access::ResWrite(b)) => a == b,
42
43 (Access::CompRead(a), Access::CompWrite(b))
44 | (Access::CompWrite(a), Access::CompRead(b))
45 | (Access::CompWrite(a), Access::CompWrite(b)) => a == b,
46
47 _ => false,
48 }
49 }
50}
51
52pub fn has_conflicts(a: &[Access], b: &[Access]) -> bool {
54 a.iter().any(|x| b.iter().any(|y| x.conflicts_with(y)))
55}
56
57pub unsafe trait SystemParam {
74 type Item<'w>;
76
77 fn access() -> Vec<Access>;
79
80 unsafe fn fetch<'w>(world: UnsafeWorldCell) -> Self::Item<'w>;
90}
91
92pub struct Res<'w, T: Send + 'static> {
98 value: &'w T,
99}
100
101impl<T: Send + 'static> Deref for Res<'_, T> {
102 type Target = T;
103 fn deref(&self) -> &T {
104 self.value
105 }
106}
107
108unsafe impl<T: Send + 'static> SystemParam for Res<'_, T> {
110 type Item<'w> = Res<'w, T>;
111
112 fn access() -> Vec<Access> {
113 vec![Access::ResRead(TypeId::of::<T>())]
114 }
115
116 unsafe fn fetch<'w>(world: UnsafeWorldCell) -> Res<'w, T> {
117 Res {
118 value: unsafe { world.get_resource::<T>() },
119 }
120 }
121}
122
123pub struct ResMut<'w, T: Send + 'static> {
129 value: &'w mut T,
130}
131
132impl<T: Send + 'static> Deref for ResMut<'_, T> {
133 type Target = T;
134 fn deref(&self) -> &T {
135 self.value
136 }
137}
138
139impl<T: Send + 'static> DerefMut for ResMut<'_, T> {
140 fn deref_mut(&mut self) -> &mut T {
141 self.value
142 }
143}
144
145unsafe impl<T: Send + 'static> SystemParam for ResMut<'_, T> {
147 type Item<'w> = ResMut<'w, T>;
148
149 fn access() -> Vec<Access> {
150 vec![Access::ResWrite(TypeId::of::<T>())]
151 }
152
153 unsafe fn fetch<'w>(world: UnsafeWorldCell) -> ResMut<'w, T> {
154 ResMut {
155 value: unsafe { world.get_resource_mut::<T>() },
156 }
157 }
158}
159
160pub struct Query<'w, T: Component> {
166 results: Vec<(Entity, &'w T)>,
167}
168
169impl<'w, T: Component> Query<'w, T> {
170 pub fn iter(&self) -> impl Iterator<Item = (Entity, &T)> {
172 self.results.iter().map(|&(e, v)| (e, v))
173 }
174
175 pub fn is_empty(&self) -> bool {
177 self.results.is_empty()
178 }
179
180 pub fn len(&self) -> usize {
182 self.results.len()
183 }
184}
185
186unsafe impl<T: Component> SystemParam for Query<'_, T> {
190 type Item<'w> = Query<'w, T>;
191
192 fn access() -> Vec<Access> {
193 vec![Access::CompRead(TypeId::of::<T>())]
194 }
195
196 unsafe fn fetch<'w>(world: UnsafeWorldCell) -> Query<'w, T> {
197 Query {
198 results: unsafe { QueryIter::<'w, &T>::new(world.archetypes()).collect() },
199 }
200 }
201}
202
203pub struct QueryMut<'w, T: Component> {
209 results: Vec<(Entity, Mut<'w, T>)>,
210}
211
212impl<'w, T: Component> QueryMut<'w, T> {
213 pub fn iter_mut(&mut self) -> impl Iterator<Item = (Entity, &mut Mut<'w, T>)> + '_ {
219 self.results.iter_mut().map(|(e, v)| (*e, v))
220 }
221
222 pub fn is_empty(&self) -> bool {
224 self.results.is_empty()
225 }
226
227 pub fn len(&self) -> usize {
229 self.results.len()
230 }
231}
232
233unsafe impl<T: Component> SystemParam for QueryMut<'_, T> {
240 type Item<'w> = QueryMut<'w, T>;
241
242 fn access() -> Vec<Access> {
243 vec![Access::CompWrite(TypeId::of::<T>())]
244 }
245
246 unsafe fn fetch<'w>(world: UnsafeWorldCell) -> QueryMut<'w, T> {
247 QueryMut {
248 results: unsafe {
249 let tick = world.change_tick();
250 QueryIterMut::<'w, &mut T>::new_from_ptr(world.archetypes_mut_ptr(), tick).collect()
251 },
252 }
253 }
254}
255
256unsafe impl SystemParam for () {
262 type Item<'w> = ();
263
264 fn access() -> Vec<Access> {
265 Vec::new()
266 }
267
268 unsafe fn fetch<'w>(_world: UnsafeWorldCell) -> Self::Item<'w> {}
269}
270
271macro_rules! impl_system_param_tuple {
276 ($($P:ident),+) => {
277 unsafe impl<$($P: SystemParam),+> SystemParam for ($($P,)+) {
279 type Item<'w> = ($($P::Item<'w>,)+);
280
281 fn access() -> Vec<Access> {
282 let mut acc = Vec::new();
283 $(acc.extend($P::access());)+
284 acc
285 }
286
287 unsafe fn fetch<'w>(world: UnsafeWorldCell) -> Self::Item<'w> {
288 ($(unsafe { $P::fetch(world) },)+)
289 }
290 }
291 };
292}
293
294impl_system_param_tuple!(P0);
295impl_system_param_tuple!(P0, P1);
296impl_system_param_tuple!(P0, P1, P2);
297impl_system_param_tuple!(P0, P1, P2, P3);
298impl_system_param_tuple!(P0, P1, P2, P3, P4);
299impl_system_param_tuple!(P0, P1, P2, P3, P4, P5);
300impl_system_param_tuple!(P0, P1, P2, P3, P4, P5, P6);
301impl_system_param_tuple!(P0, P1, P2, P3, P4, P5, P6, P7);
302
303#[cfg(test)]
308mod tests {
309 use super::*;
310 use crate::component::Component;
311 use crate::world::World;
312
313 fn type_id<T: 'static>() -> TypeId {
314 TypeId::of::<T>()
315 }
316
317 #[derive(Debug, PartialEq)]
318 struct Pos {
319 x: f32,
320 }
321 impl Component for Pos {}
322
323 #[test]
326 fn read_read_same_type_no_conflict() {
327 let a = Access::ResRead(type_id::<u32>());
328 let b = Access::ResRead(type_id::<u32>());
329 assert!(!a.conflicts_with(&b));
330 }
331
332 #[test]
333 fn read_write_same_type_conflicts() {
334 let a = Access::ResRead(type_id::<u32>());
335 let b = Access::ResWrite(type_id::<u32>());
336 assert!(a.conflicts_with(&b));
337 assert!(b.conflicts_with(&a));
338 }
339
340 #[test]
341 fn write_write_same_type_conflicts() {
342 let a = Access::ResWrite(type_id::<u32>());
343 let b = Access::ResWrite(type_id::<u32>());
344 assert!(a.conflicts_with(&b));
345 }
346
347 #[test]
348 fn read_write_different_type_no_conflict() {
349 let a = Access::ResRead(type_id::<u32>());
350 let b = Access::ResWrite(type_id::<f32>());
351 assert!(!a.conflicts_with(&b));
352 }
353
354 #[test]
355 fn comp_read_write_conflicts() {
356 let a = Access::CompRead(type_id::<u64>());
357 let b = Access::CompWrite(type_id::<u64>());
358 assert!(a.conflicts_with(&b));
359 assert!(b.conflicts_with(&a));
360 }
361
362 #[test]
363 fn res_and_comp_same_type_no_conflict() {
364 let a = Access::ResWrite(type_id::<u32>());
365 let b = Access::CompWrite(type_id::<u32>());
366 assert!(!a.conflicts_with(&b));
367 }
368
369 #[test]
370 fn has_conflicts_finds_conflict_in_sets() {
371 let set_a = vec![
372 Access::ResRead(type_id::<u32>()),
373 Access::CompRead(type_id::<f32>()),
374 ];
375 let set_b = vec![
376 Access::ResWrite(type_id::<u32>()),
377 Access::CompRead(type_id::<f32>()),
378 ];
379 assert!(has_conflicts(&set_a, &set_b));
380 }
381
382 #[test]
383 fn has_conflicts_empty_sets_no_conflict() {
384 assert!(!has_conflicts(&[], &[]));
385 assert!(!has_conflicts(&[Access::ResRead(type_id::<u32>())], &[]));
386 assert!(!has_conflicts(&[], &[Access::ResWrite(type_id::<u32>())]));
387 }
388
389 #[test]
392 fn res_fetches_resource() {
393 let mut world = World::new();
394 world.insert_resource(42_i32);
395 let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
396 unsafe {
397 let res: Res<'_, i32> = <Res<'_, i32> as SystemParam>::fetch(cell);
398 assert_eq!(*res, 42);
399 }
400 }
401
402 #[test]
403 fn res_access_is_read() {
404 let access = <Res<'_, i32> as SystemParam>::access();
405 assert_eq!(access, vec![Access::ResRead(TypeId::of::<i32>())]);
406 }
407
408 #[test]
409 fn res_mut_fetches_and_mutates() {
410 let mut world = World::new();
411 world.insert_resource(10_u32);
412 let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
413 unsafe {
414 let mut res: ResMut<'_, u32> = <ResMut<'_, u32> as SystemParam>::fetch(cell);
415 *res = 20;
416 }
417 assert_eq!(*world.resource::<u32>(), 20);
418 }
419
420 #[test]
421 fn res_mut_access_is_write() {
422 let access = <ResMut<'_, u32> as SystemParam>::access();
423 assert_eq!(access, vec![Access::ResWrite(TypeId::of::<u32>())]);
424 }
425
426 #[test]
427 fn res_and_res_mut_different_types_no_conflict() {
428 let a = <Res<'_, i32> as SystemParam>::access();
429 let b = <ResMut<'_, u32> as SystemParam>::access();
430 assert!(!has_conflicts(&a, &b));
431 }
432
433 #[test]
434 fn res_and_res_mut_same_type_conflicts() {
435 let a = <Res<'_, i32> as SystemParam>::access();
436 let b = <ResMut<'_, i32> as SystemParam>::access();
437 assert!(has_conflicts(&a, &b));
438 }
439
440 #[test]
443 fn query_fetches_matching_entities() {
444 let mut world = World::new();
445 world.spawn((Pos { x: 1.0 },));
446 world.spawn((Pos { x: 2.0 },));
447 let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
448 unsafe {
449 let q: Query<'_, Pos> = <Query<'_, Pos> as SystemParam>::fetch(cell);
450 assert_eq!(q.len(), 2);
451 }
452 }
453
454 #[test]
455 fn query_access_is_comp_read() {
456 let access = <Query<'_, Pos> as SystemParam>::access();
457 assert_eq!(access, vec![Access::CompRead(TypeId::of::<Pos>())]);
458 }
459
460 #[test]
461 fn query_mut_allows_mutation() {
462 let mut world = World::new();
463 let e = world.spawn((Pos { x: 5.0 },));
464 let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
465 unsafe {
466 let mut q: QueryMut<'_, Pos> = <QueryMut<'_, Pos> as SystemParam>::fetch(cell);
467 for (_, pos) in q.iter_mut() {
468 pos.x += 10.0;
469 }
470 }
471 assert_eq!(world.get::<Pos>(e).unwrap().x, 15.0);
472 }
473
474 #[test]
475 fn query_mut_access_is_comp_write() {
476 let access = <QueryMut<'_, Pos> as SystemParam>::access();
477 assert_eq!(access, vec![Access::CompWrite(TypeId::of::<Pos>())]);
478 }
479
480 #[test]
481 fn query_empty_world() {
482 let mut world = World::new();
483 let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
484 unsafe {
485 let q: Query<'_, Pos> = <Query<'_, Pos> as SystemParam>::fetch(cell);
486 assert!(q.is_empty());
487 }
488 }
489
490 #[test]
493 fn unit_tuple_has_no_access() {
494 let access = <() as SystemParam>::access();
495 assert!(access.is_empty());
496 }
497
498 #[test]
499 fn pair_tuple_aggregates_access() {
500 let access = <(Res<'_, i32>, ResMut<'_, u32>) as SystemParam>::access();
501 assert_eq!(access.len(), 2);
502 assert!(access.contains(&Access::ResRead(TypeId::of::<i32>())));
503 assert!(access.contains(&Access::ResWrite(TypeId::of::<u32>())));
504 }
505
506 #[test]
507 fn triple_tuple_aggregates_access() {
508 let access = <(Res<'_, i32>, ResMut<'_, u32>, Query<'_, Pos>) as SystemParam>::access();
509 assert_eq!(access.len(), 3);
510 }
511
512 #[test]
515 #[should_panic(expected = "resource not found")]
516 fn res_fetch_panics_on_missing_resource() {
517 let mut world = World::new();
518 let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
520 unsafe {
521 let _: Res<'_, i32> = <Res<'_, i32> as SystemParam>::fetch(cell);
522 }
523 }
524
525 #[test]
528 fn query_mut_empty_world() {
529 let mut world = World::new();
530 let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
531 unsafe {
532 let q: QueryMut<'_, Pos> = <QueryMut<'_, Pos> as SystemParam>::fetch(cell);
533 assert!(q.is_empty());
534 }
535 }
536
537 #[derive(Debug, PartialEq)]
540 struct Vel {
541 y: f32,
542 }
543 impl Component for Vel {}
544
545 struct TimeRes(f32);
546 struct GravRes(f32);
547
548 #[test]
549 fn four_arity_tuple_access_and_fetch() {
550 let mut world = World::new();
551 world.insert_resource(TimeRes(1.0));
552 world.insert_resource(GravRes(9.8));
553 world.spawn((Pos { x: 0.0 },));
554 world.spawn((Vel { y: 0.0 },));
555
556 let access = <(
558 Res<'_, TimeRes>,
559 Res<'_, GravRes>,
560 Query<'_, Pos>,
561 Query<'_, Vel>,
562 ) as SystemParam>::access();
563 assert_eq!(access.len(), 4);
564
565 let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
567 unsafe {
568 let (time, grav, positions, velocities) = <(
569 Res<'_, TimeRes>,
570 Res<'_, GravRes>,
571 Query<'_, Pos>,
572 Query<'_, Vel>,
573 ) as SystemParam>::fetch(cell);
574 assert!((time.0 - 1.0).abs() < f32::EPSILON);
575 assert!((grav.0 - 9.8).abs() < f32::EPSILON);
576 assert_eq!(positions.len(), 1);
577 assert_eq!(velocities.len(), 1);
578 }
579 }
580
581 #[test]
584 fn query_read_and_query_mut_different_types() {
585 let mut world = World::new();
586 world.spawn((Pos { x: 1.0 }, Vel { y: 2.0 }));
587 world.spawn((Pos { x: 3.0 }, Vel { y: 4.0 }));
588
589 let a = <Query<'_, Pos> as SystemParam>::access();
591 let b = <QueryMut<'_, Vel> as SystemParam>::access();
592 assert!(!has_conflicts(&a, &b));
593
594 let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
596 unsafe {
597 let positions: Query<'_, Pos> = <Query<'_, Pos> as SystemParam>::fetch(cell);
598 let mut velocities: QueryMut<'_, Vel> = <QueryMut<'_, Vel> as SystemParam>::fetch(cell);
599
600 assert_eq!(positions.len(), 2);
601 assert_eq!(velocities.len(), 2);
602
603 for (_, v) in velocities.iter_mut() {
605 v.y += 10.0;
606 }
607 }
608
609 let ys: Vec<f32> = world.query::<&Vel>().map(|(_, v)| v.y).collect();
611 assert!(ys.iter().all(|&y| y > 10.0));
612 }
613}