1pub use paste;
427
428#[cfg(not(target_family = "wasm"))]
429pub use rayon;
430
431#[derive(
432 Default, Clone, Copy, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize,
433)]
434pub struct Entity {
435 pub id: u32,
436 pub generation: u32,
437}
438
439impl std::fmt::Display for Entity {
440 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
441 let Self { id, generation } = self;
442 write!(f, "Id: {id} - Generation: {generation}")
443 }
444}
445
446#[derive(Default)]
447pub struct EntityAllocator {
448 pub next_id: u32,
449 pub free_ids: Vec<(u32, u32)>,
450}
451
452impl EntityAllocator {
453 pub fn allocate(&mut self) -> Entity {
454 if let Some((id, next_gen)) = self.free_ids.pop() {
455 Entity {
456 id,
457 generation: next_gen,
458 }
459 } else {
460 let id = self.next_id;
461 self.next_id += 1;
462 Entity { id, generation: 0 }
463 }
464 }
465
466 pub fn deallocate(&mut self, entity: Entity) {
467 self.free_ids
468 .push((entity.id, entity.generation.wrapping_add(1)));
469 }
470}
471
472#[derive(Copy, Clone, Default)]
473pub struct EntityLocation {
474 pub generation: u32,
475 pub table_index: u32,
476 pub array_index: u32,
477 pub allocated: bool,
478}
479
480#[derive(Default)]
481pub struct EntityLocations {
482 pub locations: Vec<EntityLocation>,
483}
484
485impl EntityLocations {
486 pub fn get(&self, id: u32) -> Option<&EntityLocation> {
487 self.locations.get(id as usize)
488 }
489
490 pub fn get_mut(&mut self, id: u32) -> Option<&mut EntityLocation> {
491 self.locations.get_mut(id as usize)
492 }
493
494 pub fn ensure_slot(&mut self, id: u32, generation: u32) {
495 let id_usize = id as usize;
496 if id_usize >= self.locations.len() {
497 self.locations.resize(
498 (self.locations.len() * 2).max(64).max(id_usize + 1),
499 EntityLocation::default(),
500 );
501 }
502 self.locations[id_usize].generation = generation;
503 }
504
505 pub fn insert(&mut self, id: u32, location: EntityLocation) {
506 let id_usize = id as usize;
507 if id_usize >= self.locations.len() {
508 self.locations.resize(
509 (self.locations.len() * 2).max(64).max(id_usize + 1),
510 EntityLocation::default(),
511 );
512 }
513 self.locations[id_usize] = location;
514 }
515
516 pub fn mark_deallocated(&mut self, id: u32) {
517 if let Some(loc) = self.locations.get_mut(id as usize) {
518 loc.allocated = false;
519 }
520 }
521}
522
523#[derive(Clone)]
552pub struct EventQueue<T> {
553 current: Vec<T>,
554 previous: Vec<T>,
555}
556
557impl<T> Default for EventQueue<T> {
558 fn default() -> Self {
559 Self::new()
560 }
561}
562
563impl<T> EventQueue<T> {
564 pub fn new() -> Self {
566 Self {
567 current: Vec::new(),
568 previous: Vec::new(),
569 }
570 }
571
572 pub fn send(&mut self, event: T) {
576 #[cfg(debug_assertions)]
577 {
578 const WARN_THRESHOLD: usize = 10000;
579 let total = self.len();
580 if total > WARN_THRESHOLD {
581 eprintln!(
582 "WARNING: EventQueue has {} events. Did you forget to call update()?",
583 total
584 );
585 }
586 }
587 self.current.push(event);
588 }
589
590 pub fn read(&self) -> impl Iterator<Item = &T> {
594 self.previous.iter().chain(self.current.iter())
595 }
596
597 pub fn peek(&self) -> Option<&T> {
599 self.previous.first().or_else(|| self.current.first())
600 }
601
602 pub fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
606 self.previous.drain(..).chain(self.current.drain(..))
607 }
608
609 pub fn update(&mut self) {
618 self.previous.clear();
619 std::mem::swap(&mut self.current, &mut self.previous);
620 }
621
622 pub fn clear(&mut self) {
626 self.current.clear();
627 self.previous.clear();
628 }
629
630 pub fn len(&self) -> usize {
632 self.current.len() + self.previous.len()
633 }
634
635 pub fn is_empty(&self) -> bool {
637 self.current.is_empty() && self.previous.is_empty()
638 }
639}
640
641struct ScheduleEntry<W> {
642 name: &'static str,
643 system: Box<dyn FnMut(&mut W) + Send>,
644}
645
646pub struct Schedule<W> {
647 entries: Vec<ScheduleEntry<W>>,
648}
649
650impl<W> Schedule<W> {
651 pub fn new() -> Self {
652 Self {
653 entries: Vec::new(),
654 }
655 }
656
657 pub fn push<F>(&mut self, name: &'static str, system: F) -> &mut Self
658 where
659 F: FnMut(&mut W) + Send + 'static,
660 {
661 self.assert_unique(name);
662 self.entries.push(ScheduleEntry {
663 name,
664 system: Box::new(system),
665 });
666 self
667 }
668
669 pub fn push_readonly<F>(&mut self, name: &'static str, mut system: F) -> &mut Self
670 where
671 F: FnMut(&W) + Send + 'static,
672 {
673 self.assert_unique(name);
674 self.entries.push(ScheduleEntry {
675 name,
676 system: Box::new(move |world: &mut W| {
677 system(&*world);
678 }),
679 });
680 self
681 }
682
683 pub fn insert_before<F>(&mut self, target: &str, name: &'static str, system: F) -> &mut Self
684 where
685 F: FnMut(&mut W) + Send + 'static,
686 {
687 self.assert_unique(name);
688 let index = self.index_of_or_panic(target, "insert_before");
689 self.entries.insert(
690 index,
691 ScheduleEntry {
692 name,
693 system: Box::new(system),
694 },
695 );
696 self
697 }
698
699 pub fn insert_after<F>(&mut self, target: &str, name: &'static str, system: F) -> &mut Self
700 where
701 F: FnMut(&mut W) + Send + 'static,
702 {
703 self.assert_unique(name);
704 let index = self.index_of_or_panic(target, "insert_after");
705 self.entries.insert(
706 index + 1,
707 ScheduleEntry {
708 name,
709 system: Box::new(system),
710 },
711 );
712 self
713 }
714
715 pub fn replace<F>(&mut self, name: &str, system: F) -> &mut Self
716 where
717 F: FnMut(&mut W) + Send + 'static,
718 {
719 let index = self
720 .index_of(name)
721 .unwrap_or_else(|| panic!("Schedule::replace: system \"{name}\" not found"));
722 self.entries[index].system = Box::new(system);
723 self
724 }
725
726 pub fn remove(&mut self, name: &str) -> bool {
727 let len_before = self.entries.len();
728 self.entries.retain(|entry| entry.name != name);
729 self.entries.len() != len_before
730 }
731
732 pub fn contains(&self, name: &str) -> bool {
733 self.entries.iter().any(|entry| entry.name == name)
734 }
735
736 pub fn names(&self) -> impl Iterator<Item = &'static str> + '_ {
737 self.entries.iter().map(|entry| entry.name)
738 }
739
740 pub fn len(&self) -> usize {
741 self.entries.len()
742 }
743
744 pub fn is_empty(&self) -> bool {
745 self.entries.is_empty()
746 }
747
748 pub fn run(&mut self, world: &mut W) {
749 for entry in &mut self.entries {
750 (entry.system)(world);
751 }
752 }
753
754 fn index_of(&self, name: &str) -> Option<usize> {
755 self.entries.iter().position(|entry| entry.name == name)
756 }
757
758 fn assert_unique(&self, name: &str) {
759 assert!(
760 !self.contains(name),
761 "Schedule: system \"{name}\" already exists"
762 );
763 }
764
765 fn index_of_or_panic(&self, target: &str, method: &str) -> usize {
766 self.index_of(target)
767 .unwrap_or_else(|| panic!("Schedule::{method}: system \"{target}\" not found"))
768 }
769}
770
771impl<W> Default for Schedule<W> {
772 fn default() -> Self {
773 Self::new()
774 }
775}
776
777#[macro_export]
778macro_rules! ecs {
779 (
780 $world:ident {
781 $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
782 }
783 Tags {
784 $($tag_name:ident => $tag_mask:ident),* $(,)?
785 }
786 Events {
787 $($event_name:ident: $event_type:ty),* $(,)?
788 }
789 $resources:ident {
790 $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
791 }
792 ) => {
793 $crate::ecs_impl! {
794 $world {
795 $($(#[$comp_attr])* $name: $type => $mask),*
796 }
797 Tags {
798 $($tag_name => $tag_mask),*
799 }
800 Events {
801 $($event_name: $event_type),*
802 }
803 $resources {
804 $($(#[$attr])* $resource_name: $resource_type),*
805 }
806 }
807 };
808
809 (
810 $world:ident {
811 $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
812 }
813 Tags {
814 $($tag_name:ident => $tag_mask:ident),* $(,)?
815 }
816 $resources:ident {
817 $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
818 }
819 ) => {
820 $crate::ecs_impl! {
821 $world {
822 $($(#[$comp_attr])* $name: $type => $mask),*
823 }
824 Tags {
825 $($tag_name => $tag_mask),*
826 }
827 Events {}
828 $resources {
829 $($(#[$attr])* $resource_name: $resource_type),*
830 }
831 }
832 };
833
834 (
835 $world:ident {
836 $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
837 }
838 Events {
839 $($event_name:ident: $event_type:ty),* $(,)?
840 }
841 $resources:ident {
842 $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
843 }
844 ) => {
845 $crate::ecs_impl! {
846 $world {
847 $($(#[$comp_attr])* $name: $type => $mask),*
848 }
849 Tags {}
850 Events {
851 $($event_name: $event_type),*
852 }
853 $resources {
854 $($(#[$attr])* $resource_name: $resource_type),*
855 }
856 }
857 };
858
859 (
860 $world:ident {
861 $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
862 }
863 $resources:ident {
864 $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
865 }
866 ) => {
867 $crate::ecs_impl! {
868 $world {
869 $($(#[$comp_attr])* $name: $type => $mask),*
870 }
871 Tags {}
872 Events {}
873 $resources {
874 $($(#[$attr])* $resource_name: $resource_type),*
875 }
876 }
877 };
878
879 (
880 $ecs:ident {
881 $($world_name:ident {
882 $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
883 })+
884 }
885 Tags {
886 $($tag_name:ident => $tag_mask:ident),* $(,)?
887 }
888 Events {
889 $($event_name:ident: $event_type:ty),* $(,)?
890 }
891 $resources:ident {
892 $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
893 }
894 ) => {
895 $crate::ecs_multi_impl! {
896 $ecs {
897 $($world_name {
898 $($(#[$comp_attr])* $name: $type => $mask),*
899 })+
900 }
901 Tags {
902 $($tag_name => $tag_mask),*
903 }
904 Events {
905 $($event_name: $event_type),*
906 }
907 $resources {
908 $($(#[$attr])* $resource_name: $resource_type),*
909 }
910 }
911 };
912
913 (
914 $ecs:ident {
915 $($world_name:ident {
916 $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
917 })+
918 }
919 Tags {
920 $($tag_name:ident => $tag_mask:ident),* $(,)?
921 }
922 $resources:ident {
923 $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
924 }
925 ) => {
926 $crate::ecs_multi_impl! {
927 $ecs {
928 $($world_name {
929 $($(#[$comp_attr])* $name: $type => $mask),*
930 })+
931 }
932 Tags {
933 $($tag_name => $tag_mask),*
934 }
935 Events {}
936 $resources {
937 $($(#[$attr])* $resource_name: $resource_type),*
938 }
939 }
940 };
941
942 (
943 $ecs:ident {
944 $($world_name:ident {
945 $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
946 })+
947 }
948 Events {
949 $($event_name:ident: $event_type:ty),* $(,)?
950 }
951 $resources:ident {
952 $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
953 }
954 ) => {
955 $crate::ecs_multi_impl! {
956 $ecs {
957 $($world_name {
958 $($(#[$comp_attr])* $name: $type => $mask),*
959 })+
960 }
961 Tags {}
962 Events {
963 $($event_name: $event_type),*
964 }
965 $resources {
966 $($(#[$attr])* $resource_name: $resource_type),*
967 }
968 }
969 };
970
971 (
972 $ecs:ident {
973 $($world_name:ident {
974 $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
975 })+
976 }
977 $resources:ident {
978 $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
979 }
980 ) => {
981 $crate::ecs_multi_impl! {
982 $ecs {
983 $($world_name {
984 $($(#[$comp_attr])* $name: $type => $mask),*
985 })+
986 }
987 Tags {}
988 Events {}
989 $resources {
990 $($(#[$attr])* $resource_name: $resource_type),*
991 }
992 }
993 };
994}
995
996#[macro_export]
997macro_rules! ecs_impl {
998 (
999 $world:ident {
1000 $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
1001 }
1002 Tags {
1003 $($tag_name:ident => $tag_mask:ident),* $(,)?
1004 }
1005 Events {
1006 $($event_name:ident: $event_type:ty),* $(,)?
1007 }
1008 $resources:ident {
1009 $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
1010 }
1011 ) => {
1012 #[allow(unused)]
1013 #[derive(Default, Debug, Clone)]
1014 pub struct EntityBuilder {
1015 $($(#[$comp_attr])* $name: Option<$type>,)*
1016 }
1017
1018 #[allow(unused)]
1019 impl EntityBuilder {
1020 pub fn new() -> Self {
1021 Self::default()
1022 }
1023
1024 $(
1025 $(#[$comp_attr])*
1026 $crate::paste::paste! {
1027 pub fn [<with_$name>](mut self, value: $type) -> Self {
1028 self.$name = Some(value);
1029 self
1030 }
1031 }
1032 )*
1033
1034 pub fn spawn(self, world: &mut $world, instances: usize) -> Vec<$crate::Entity> {
1035 let mut mask = 0;
1036 $(
1037 $(#[$comp_attr])*
1038 if self.$name.is_some() {
1039 mask |= $mask;
1040 }
1041 )*
1042 let entities = world.spawn_entities(mask, instances);
1043 let last_entity_index = entities.len().saturating_sub(1);
1044 for (entity_index, entity) in entities.iter().enumerate() {
1045 if entity_index == last_entity_index {
1046 $(
1047 $(#[$comp_attr])*
1048 $crate::paste::paste! {
1049 if let Some(component) = self.$name {
1050 world.[<set_$name>](*entity, component);
1051 }
1052 }
1053 )*
1054 break;
1055 } else {
1056 $(
1057 $(#[$comp_attr])*
1058 $crate::paste::paste! {
1059 if let Some(ref component) = self.$name {
1060 world.[<set_$name>](*entity, component.clone());
1061 }
1062 }
1063 )*
1064 }
1065 }
1066 entities
1067 }
1068 }
1069
1070 #[repr(u64)]
1071 #[allow(clippy::upper_case_acronyms)]
1072 #[allow(non_camel_case_types)]
1073 pub enum Component {
1074 $($(#[$comp_attr])* $mask,)*
1075 $($tag_mask,)*
1076 }
1077
1078 $($(#[$comp_attr])* pub const $mask: u64 = 1 << (Component::$mask as u64);)*
1079 $(pub const $tag_mask: u64 = 1 << (Component::$tag_mask as u64);)*
1080
1081 const ALL_TAGS_MASK: u64 = 0 $(| $tag_mask)*;
1082
1083 pub const COMPONENT_COUNT: usize = {
1084 let mut count = 0;
1085 $($(#[$comp_attr])* { count += 1; let _ = Component::$mask; })*
1086 $(count += 1; let _ = Component::$tag_mask;)*
1087 count
1088 };
1089
1090 $crate::paste::paste! {
1091 pub enum Command {
1092 SpawnEntities { mask: u64, count: usize },
1093 DespawnEntities { entities: Vec<$crate::Entity> },
1094 AddComponents { entity: $crate::Entity, mask: u64 },
1095 RemoveComponents { entity: $crate::Entity, mask: u64 },
1096 $(
1097 $(#[$comp_attr])*
1098 [<Set $mask:camel>] { entity: $crate::Entity, value: $type },
1099 )*
1100 $(
1101 [<Add $tag_mask:camel>] { entity: $crate::Entity },
1102 [<Remove $tag_mask:camel>] { entity: $crate::Entity },
1103 )*
1104 }
1105 }
1106
1107 #[allow(unused)]
1108 pub struct $world {
1109 entity_locations: $crate::EntityLocations,
1110 tables: Vec<ComponentArrays>,
1111 allocator: $crate::EntityAllocator,
1112 pub resources: $resources,
1113 table_edges: Vec<TableEdges>,
1114 table_lookup: std::collections::HashMap<u64, usize>,
1115 query_cache: std::collections::HashMap<u64, Vec<usize>>,
1116 current_tick: u32,
1117 last_tick: u32,
1118 $($tag_name: std::collections::HashSet<$crate::Entity>,)*
1119 command_buffer: Vec<Command>,
1120 $($event_name: $crate::EventQueue<$event_type>,)*
1121 }
1122
1123 impl Default for $world {
1124 fn default() -> Self {
1125 Self {
1126 entity_locations: $crate::EntityLocations::default(),
1127 tables: Vec::default(),
1128 allocator: $crate::EntityAllocator::default(),
1129 resources: $resources::default(),
1130 table_edges: Vec::default(),
1131 table_lookup: std::collections::HashMap::default(),
1132 query_cache: std::collections::HashMap::default(),
1133 current_tick: 0,
1134 last_tick: 0,
1135 $(
1136 $tag_name: std::collections::HashSet::default(),
1137 )*
1138 command_buffer: Vec::default(),
1139 $(
1140 $event_name: $crate::EventQueue::new(),
1141 )*
1142 }
1143 }
1144 }
1145
1146 #[allow(unused)]
1147 impl $world {
1148 fn get_cached_tables(&mut self, mask: u64) -> &[usize] {
1149 if !self.query_cache.contains_key(&mask) {
1150 let matching_tables: Vec<usize> = self.tables
1151 .iter()
1152 .enumerate()
1153 .filter(|(_, table)| table.mask & mask == mask)
1154 .map(|(idx, _)| idx)
1155 .collect();
1156 self.query_cache.insert(mask, matching_tables);
1157 }
1158 &self.query_cache[&mask]
1159 }
1160
1161 fn invalidate_query_cache_for_table(&mut self, new_table_mask: u64, new_table_index: usize) {
1162 self.query_cache.retain(|query_mask, cached_tables| {
1163 if new_table_mask & query_mask == *query_mask {
1164 cached_tables.push(new_table_index);
1165 true
1166 } else {
1167 true
1168 }
1169 });
1170 }
1171
1172 $(
1173 $(#[$comp_attr])*
1174 $crate::paste::paste! {
1175 #[inline]
1176 pub fn [<get_ $name>](&self, entity: $crate::Entity) -> Option<&$type> {
1177 let (table_index, array_index) = get_location(&self.entity_locations, entity)?;
1178 let table = &self.tables[table_index];
1179
1180 if table.mask & $mask == 0 {
1181 return None;
1182 }
1183
1184 Some(&table.$name[array_index])
1185 }
1186
1187 $crate::paste::paste! {
1188 #[inline]
1189 pub fn [<get_ $name _mut>](&mut self, entity: $crate::Entity) -> Option<&mut $type> {
1190 let (table_index, array_index) = get_location(&self.entity_locations, entity)?;
1191 let current_tick = self.current_tick;
1192 let table = &mut self.tables[table_index];
1193
1194 if table.mask & $mask == 0 {
1195 return None;
1196 }
1197
1198 table.[<$name _changed>][array_index] = current_tick;
1199 Some(&mut table.$name[array_index])
1200 }
1201 }
1202
1203 $crate::paste::paste! {
1204 #[inline]
1205 pub fn [<modify_ $name>]<R>(&mut self, entity: $crate::Entity, f: impl FnOnce(&mut $type) -> R) -> Option<R> {
1206 let (table_index, array_index) = get_location(&self.entity_locations, entity)?;
1207 let current_tick = self.current_tick;
1208 let table = &mut self.tables[table_index];
1209
1210 if table.mask & $mask == 0 {
1211 return None;
1212 }
1213
1214 table.[<$name _changed>][array_index] = current_tick;
1215 Some(f(&mut table.$name[array_index]))
1216 }
1217 }
1218
1219 #[inline]
1220 pub fn [<entity_has_ $name>](&self, entity: $crate::Entity) -> bool {
1221 self.entity_has_components(entity, $mask)
1222 }
1223
1224 #[inline]
1225 pub fn [<set_ $name>](&mut self, entity: $crate::Entity, value: $type) {
1226 if let Some((table_index, array_index)) = get_location(&self.entity_locations, entity) {
1227 if self.tables[table_index].mask & $mask != 0 {
1228 self.tables[table_index].$name[array_index] = value;
1229 return;
1230 }
1231 self.add_components_at(entity, $mask, table_index, array_index);
1232 if let Some((new_table_index, new_array_index)) = get_location(&self.entity_locations, entity) {
1233 self.tables[new_table_index].$name[new_array_index] = value;
1234 }
1235 }
1236 }
1237
1238 #[inline]
1239 pub fn [<add_ $name>](&mut self, entity: $crate::Entity) {
1240 self.add_components(entity, $mask);
1241 }
1242
1243 #[inline]
1244 pub fn [<remove_ $name>](&mut self, entity: $crate::Entity) -> bool {
1245 self.remove_components(entity, $mask)
1246 }
1247
1248 #[inline]
1249 pub fn [<query_ $name>](&self) -> [<$mask:camel QueryIter>]<'_> {
1250 [<$mask:camel QueryIter>] {
1251 tables: &self.tables,
1252 table_index: 0,
1253 array_index: 0,
1254 }
1255 }
1256
1257 pub fn [<for_each_ $name _mut>]<F>(&mut self, mut f: F)
1258 where
1259 F: FnMut(&mut $type),
1260 {
1261 let table_indices: Vec<usize> = self.get_cached_tables($mask).to_vec();
1262
1263 for table_index in table_indices {
1264 for component in &mut self.tables[table_index].$name {
1265 f(component);
1266 }
1267 }
1268 }
1269
1270 #[cfg(not(target_family = "wasm"))]
1271 pub fn [<par_for_each_ $name _mut>]<F>(&mut self, f: F)
1272 where
1273 F: Fn(&mut $type) + Send + Sync,
1274 {
1275 use $crate::rayon::prelude::*;
1276
1277 self.tables
1278 .par_iter_mut()
1279 .filter(|table| table.mask & $mask != 0)
1280 .for_each(|table| {
1281 table.$name.par_iter_mut().for_each(|component| f(component));
1282 });
1283 }
1284
1285 pub fn [<iter_ $name _slices>](&self) -> impl Iterator<Item = &[$type]> {
1286 self.tables
1287 .iter()
1288 .filter(|table| table.mask & $mask != 0)
1289 .map(|table| table.$name.as_slice())
1290 }
1291
1292 pub fn [<iter_ $name _slices_mut>](&mut self) -> impl Iterator<Item = &mut [$type]> {
1293 self.tables
1294 .iter_mut()
1295 .filter(|table| table.mask & $mask != 0)
1296 .map(|table| table.$name.as_mut_slice())
1297 }
1298 }
1299 )*
1300
1301 pub fn spawn_entities(&mut self, mask: u64, count: usize) -> Vec<$crate::Entity> {
1302 let mut entities = Vec::with_capacity(count);
1303 let table_index = get_or_create_table(self, mask);
1304 let start_index = self.tables[table_index].entity_indices.len();
1305
1306 self.tables[table_index].entity_indices.reserve(count);
1307 $(
1308 $(#[$comp_attr])*
1309 $crate::paste::paste! {
1310 if mask & $mask != 0 {
1311 self.tables[table_index].$name.reserve(count);
1312 self.tables[table_index].[<$name _changed>].reserve(count);
1313 }
1314 }
1315 )*
1316
1317 for i in 0..count {
1318 let entity = create_entity(self);
1319 entities.push(entity);
1320
1321 self.tables[table_index].entity_indices.push(entity);
1322 $(
1323 $(#[$comp_attr])*
1324 $crate::paste::paste! {
1325 if mask & $mask != 0 {
1326 self.tables[table_index].$name.push(<$type>::default());
1327 self.tables[table_index].[<$name _changed>].push(self.current_tick);
1328 }
1329 }
1330 )*
1331
1332 insert_location(
1333 &mut self.entity_locations,
1334 entity,
1335 (table_index, start_index + i),
1336 );
1337 }
1338
1339 entities
1340 }
1341
1342 pub fn spawn_batch<F>(&mut self, mask: u64, count: usize, mut init: F) -> Vec<$crate::Entity>
1343 where
1344 F: FnMut(&mut ComponentArrays, usize),
1345 {
1346 let table_index = get_or_create_table(self, mask);
1347 let start_index = self.tables[table_index].entity_indices.len();
1348
1349 self.tables[table_index].entity_indices.reserve(count);
1350 $(
1351 $(#[$comp_attr])*
1352 $crate::paste::paste! {
1353 if mask & $mask != 0 {
1354 self.tables[table_index].$name.reserve(count);
1355 self.tables[table_index].[<$name _changed>].reserve(count);
1356 }
1357 }
1358 )*
1359
1360 let mut entities = Vec::with_capacity(count);
1361
1362 for i in 0..count {
1363 let entity = create_entity(self);
1364 entities.push(entity);
1365
1366 self.tables[table_index].entity_indices.push(entity);
1367 $(
1368 $(#[$comp_attr])*
1369 $crate::paste::paste! {
1370 if mask & $mask != 0 {
1371 self.tables[table_index].$name.push(<$type>::default());
1372 self.tables[table_index].[<$name _changed>].push(self.current_tick);
1373 }
1374 }
1375 )*
1376
1377 insert_location(
1378 &mut self.entity_locations,
1379 entity,
1380 (table_index, start_index + i),
1381 );
1382
1383 init(&mut self.tables[table_index], start_index + i);
1384 }
1385
1386 entities
1387 }
1388
1389 pub fn query_entities(&self, mask: u64) -> EntityQueryIter<'_> {
1390 EntityQueryIter {
1391 tables: &self.tables,
1392 mask,
1393 table_index: 0,
1394 array_index: 0,
1395 }
1396 }
1397
1398 pub fn query_entities_changed(&self, mask: u64) -> ChangedEntityQueryIter<'_> {
1399 ChangedEntityQueryIter {
1400 tables: &self.tables,
1401 mask,
1402 since_tick: self.last_tick,
1403 table_index: 0,
1404 array_index: 0,
1405 }
1406 }
1407
1408 pub fn query_first_entity(&self, mask: u64) -> Option<$crate::Entity> {
1409 for table in &self.tables {
1410 if table.mask & mask != mask {
1411 continue;
1412 }
1413 if let Some(&entity) = table.entity_indices.first() {
1414 return Some(entity);
1415 }
1416 }
1417 None
1418 }
1419
1420 pub fn despawn_entities(&mut self, entities: &[$crate::Entity]) -> Vec<$crate::Entity> {
1421 let mut despawned = Vec::with_capacity(entities.len());
1422 let mut tables_to_update = Vec::new();
1423
1424 for &entity in entities {
1425 if let Some(loc) = self.entity_locations.get_mut(entity.id) {
1426 if loc.allocated && loc.generation == entity.generation {
1427 let table_idx = loc.table_index as usize;
1428 let array_idx = loc.array_index as usize;
1429
1430 let next_gen = loc.generation.wrapping_add(1);
1431 self.entity_locations.mark_deallocated(entity.id);
1432 if let Some(loc) = self.entity_locations.get_mut(entity.id) {
1433 loc.generation = next_gen;
1434 }
1435 self.allocator.free_ids.push((entity.id, next_gen));
1436
1437 tables_to_update.push((table_idx, array_idx));
1438 despawned.push(entity);
1439 }
1440 }
1441 }
1442
1443 tables_to_update.sort_by(|a, b| b.cmp(a));
1444
1445 for (table_idx, array_idx) in tables_to_update {
1446 if table_idx >= self.tables.len() {
1447 continue;
1448 }
1449
1450 let table = &mut self.tables[table_idx];
1451 if table.entity_indices.is_empty() {
1452 continue;
1453 }
1454 let last_idx = table.entity_indices.len() - 1;
1455
1456 if array_idx < last_idx {
1457 let moved_entity = table.entity_indices[last_idx];
1458 if let Some(loc) = self.entity_locations.get_mut(moved_entity.id) {
1459 if loc.allocated {
1460 loc.array_index = array_idx as u32;
1461 }
1462 }
1463 }
1464
1465 $(
1466 $(#[$comp_attr])*
1467 $crate::paste::paste! {
1468 if table.mask & $mask != 0 {
1469 table.$name.swap_remove(array_idx);
1470 table.[<$name _changed>].swap_remove(array_idx);
1471 }
1472 }
1473 )*
1474 table.entity_indices.swap_remove(array_idx);
1475 }
1476
1477 $(
1478 for &entity in &despawned {
1479 self.$tag_name.remove(&entity);
1480 }
1481 )*
1482
1483 despawned
1484 }
1485
1486 fn add_components_at(&mut self, entity: $crate::Entity, mask: u64, table_index: usize, array_index: usize) {
1487 let current_mask = self.tables[table_index].mask;
1488 if current_mask & mask == mask {
1489 return;
1490 }
1491
1492 let target_table = if mask.count_ones() == 1 {
1493 get_component_index(mask).and_then(|idx| self.table_edges[table_index].add_edges[idx])
1494 } else {
1495 self.table_edges[table_index].multi_add_cache.get(&mask).copied()
1496 };
1497
1498 let new_table_index = target_table.unwrap_or_else(|| {
1499 let new_idx = get_or_create_table(self, current_mask | mask);
1500 self.table_edges[table_index].multi_add_cache.insert(mask, new_idx);
1501 new_idx
1502 });
1503
1504 move_entity(self, entity, table_index, array_index, new_table_index);
1505 }
1506
1507 pub fn add_components(&mut self, entity: $crate::Entity, mask: u64) -> bool {
1508 if let Some((table_index, array_index)) = get_location(&self.entity_locations, entity) {
1509 self.add_components_at(entity, mask, table_index, array_index);
1510 true
1511 } else {
1512 false
1513 }
1514 }
1515
1516 pub fn remove_components(&mut self, entity: $crate::Entity, mask: u64) -> bool {
1517 if let Some((table_index, array_index)) = get_location(&self.entity_locations, entity) {
1518 let current_mask = self.tables[table_index].mask;
1519 if current_mask & mask == 0 {
1520 return true;
1521 }
1522
1523 let target_table = if mask.count_ones() == 1 {
1524 get_component_index(mask)
1525 .and_then(|idx| self.table_edges[table_index].remove_edges[idx])
1526 } else {
1527 self.table_edges[table_index].multi_remove_cache.get(&mask).copied()
1528 };
1529
1530 let new_table_index = target_table.unwrap_or_else(|| {
1531 let new_idx = get_or_create_table(self, current_mask & !mask);
1532 self.table_edges[table_index].multi_remove_cache.insert(mask, new_idx);
1533 new_idx
1534 });
1535
1536 move_entity(self, entity, table_index, array_index, new_table_index);
1537 true
1538 } else {
1539 false
1540 }
1541 }
1542
1543 pub fn component_mask(&self, entity: $crate::Entity) -> Option<u64> {
1544 get_location(&self.entity_locations, entity)
1545 .map(|(table_index, _)| self.tables[table_index].mask)
1546 }
1547
1548 pub fn get_all_entities(&self) -> Vec<$crate::Entity> {
1549 let mut result = Vec::with_capacity(self.entity_count());
1550 for table in &self.tables {
1551 result.extend(table.entity_indices.iter().copied());
1552 }
1553 result
1554 }
1555
1556 pub fn entity_count(&self) -> usize {
1557 self.tables.iter().map(|table| table.entity_indices.len()).sum()
1558 }
1559
1560 pub fn entity_has_components(&self, entity: $crate::Entity, components: u64) -> bool {
1561 self.component_mask(entity).unwrap_or(0) & components == components
1562 }
1563
1564 pub fn increment_tick(&mut self) {
1565 self.last_tick = self.current_tick;
1566 self.current_tick = self.current_tick.wrapping_add(1);
1567 }
1568
1569 pub fn current_tick(&self) -> u32 {
1570 self.current_tick
1571 }
1572
1573 pub fn last_tick(&self) -> u32 {
1574 self.last_tick
1575 }
1576
1577 $(
1578 $crate::paste::paste! {
1579 pub fn [<add_ $tag_name>](&mut self, entity: $crate::Entity) {
1580 if get_location(&self.entity_locations, entity).is_some() {
1581 self.$tag_name.insert(entity);
1582 }
1583 }
1584
1585 pub fn [<remove_ $tag_name>](&mut self, entity: $crate::Entity) -> bool {
1586 self.$tag_name.remove(&entity)
1587 }
1588
1589 pub fn [<has_ $tag_name>](&self, entity: $crate::Entity) -> bool {
1590 self.$tag_name.contains(&entity)
1591 }
1592
1593 pub fn [<query_ $tag_name>](&self) -> impl Iterator<Item = $crate::Entity> + '_ {
1594 self.$tag_name.iter().copied()
1595 }
1596 }
1597 )*
1598
1599 fn entity_matches_tags(&self, entity: $crate::Entity, include_tags: u64, exclude_tags: u64) -> bool {
1600 $(
1601 if include_tags & $tag_mask != 0 && !self.$tag_name.contains(&entity) {
1602 return false;
1603 }
1604 if exclude_tags & $tag_mask != 0 && self.$tag_name.contains(&entity) {
1605 return false;
1606 }
1607 )*
1608 true
1609 }
1610
1611 $(
1612 $crate::paste::paste! {
1613 pub fn [<send_ $event_name>](&mut self, event: $event_type) {
1614 self.$event_name.send(event);
1615 }
1616
1617 pub fn [<read_ $event_name>](&self) -> impl Iterator<Item = &$event_type> {
1618 self.$event_name.read()
1619 }
1620
1621 pub fn [<drain_ $event_name>](&mut self) -> impl Iterator<Item = $event_type> + '_ {
1622 self.$event_name.drain()
1623 }
1624
1625 pub fn [<clear_ $event_name>](&mut self) {
1626 self.$event_name.clear();
1627 }
1628
1629 pub fn [<update_ $event_name>](&mut self) {
1630 self.$event_name.update();
1631 }
1632
1633 pub fn [<len_ $event_name>](&self) -> usize {
1634 self.$event_name.len()
1635 }
1636
1637 pub fn [<is_empty_ $event_name>](&self) -> bool {
1638 self.$event_name.is_empty()
1639 }
1640
1641 pub fn [<peek_ $event_name>](&self) -> Option<&$event_type> {
1642 self.$event_name.peek()
1643 }
1644
1645 pub fn [<collect_ $event_name>](&self) -> Vec<$event_type>
1646 where
1647 $event_type: Clone,
1648 {
1649 self.$event_name.read().cloned().collect()
1650 }
1651 }
1652 )*
1653
1654 fn update_events(&mut self) {
1655 $(
1656 self.$event_name.update();
1657 )*
1658 }
1659
1660 pub fn step(&mut self) {
1661 self.update_events();
1662 self.last_tick = self.current_tick;
1663 self.current_tick += 1;
1664 }
1665
1666 pub fn queue_spawn_entities(&mut self, mask: u64, count: usize) {
1667 self.command_buffer.push(Command::SpawnEntities { mask, count });
1668 }
1669
1670 pub fn queue_despawn_entities(&mut self, entities: Vec<$crate::Entity>) {
1671 self.command_buffer.push(Command::DespawnEntities { entities });
1672 }
1673
1674 pub fn queue_despawn_entity(&mut self, entity: $crate::Entity) {
1675 self.command_buffer.push(Command::DespawnEntities { entities: vec![entity] });
1676 }
1677
1678 pub fn queue_add_components(&mut self, entity: $crate::Entity, mask: u64) {
1679 self.command_buffer.push(Command::AddComponents { entity, mask });
1680 }
1681
1682 pub fn queue_remove_components(&mut self, entity: $crate::Entity, mask: u64) {
1683 self.command_buffer.push(Command::RemoveComponents { entity, mask });
1684 }
1685
1686 $(
1687 $crate::paste::paste! {
1688 pub fn [<queue_set_ $name>](&mut self, entity: $crate::Entity, value: $type) {
1689 self.command_buffer.push(Command::[<Set $mask:camel>] { entity, value });
1690 }
1691 }
1692 )*
1693
1694 $(
1695 $crate::paste::paste! {
1696 pub fn [<queue_add_ $tag_name>](&mut self, entity: $crate::Entity) {
1697 self.command_buffer.push(Command::[<Add $tag_mask:camel>] { entity });
1698 }
1699
1700 pub fn [<queue_remove_ $tag_name>](&mut self, entity: $crate::Entity) {
1701 self.command_buffer.push(Command::[<Remove $tag_mask:camel>] { entity });
1702 }
1703 }
1704 )*
1705
1706 pub fn apply_commands(&mut self) {
1707 let commands = std::mem::take(&mut self.command_buffer);
1708
1709 $crate::paste::paste! {
1710 for command in commands {
1711 match command {
1712 Command::SpawnEntities { mask, count } => {
1713 self.spawn_entities(mask, count);
1714 }
1715 Command::DespawnEntities { entities } => {
1716 self.despawn_entities(&entities);
1717 }
1718 Command::AddComponents { entity, mask } => {
1719 self.add_components(entity, mask);
1720 }
1721 Command::RemoveComponents { entity, mask } => {
1722 self.remove_components(entity, mask);
1723 }
1724 $(
1725 Command::[<Set $mask:camel>] { entity, value } => {
1726 self.[<set_ $name>](entity, value);
1727 }
1728 )*
1729 $(
1730 Command::[<Add $tag_mask:camel>] { entity } => {
1731 self.[<add_ $tag_name>](entity);
1732 }
1733 Command::[<Remove $tag_mask:camel>] { entity } => {
1734 self.[<remove_ $tag_name>](entity);
1735 }
1736 )*
1737 }
1738 }
1739 }
1740 }
1741
1742 pub fn command_count(&self) -> usize {
1743 self.command_buffer.len()
1744 }
1745
1746 pub fn clear_commands(&mut self) {
1747 self.command_buffer.clear();
1748 }
1749
1750
1751 $(
1752 $crate::paste::paste! {
1753 pub fn [<query_ $name _mut>]<F>(&mut self, mask: u64, mut f: F)
1754 where
1755 F: FnMut($crate::Entity, &mut $type),
1756 {
1757 let table_indices: Vec<usize> = self.get_cached_tables(mask).to_vec();
1758
1759 for &table_index in &table_indices {
1760 let table = &mut self.tables[table_index];
1761 if table.mask & $mask == 0 {
1762 continue;
1763 }
1764
1765 for idx in 0..table.entity_indices.len() {
1766 let entity = table.entity_indices[idx];
1767 f(entity, &mut table.$name[idx]);
1768 }
1769 }
1770 }
1771 }
1772 )*
1773 }
1774
1775 #[allow(unused)]
1776 impl $world {
1777 #[inline]
1778 pub fn for_each<F>(&self, include: u64, exclude: u64, mut f: F)
1779 where
1780 F: FnMut($crate::Entity, &ComponentArrays, usize),
1781 {
1782 let component_include = include & !ALL_TAGS_MASK;
1783 let component_exclude = exclude & !ALL_TAGS_MASK;
1784 let tag_include = include & ALL_TAGS_MASK;
1785 let tag_exclude = exclude & ALL_TAGS_MASK;
1786
1787 if let Some(cached) = self.query_cache.get(&component_include) {
1788 for &table_index in cached {
1789 let table = &self.tables[table_index];
1790 if table.mask & component_exclude != 0 {
1791 continue;
1792 }
1793 if tag_include == 0 && tag_exclude == 0 {
1794 for (idx, &entity) in table.entity_indices.iter().enumerate() {
1795 f(entity, table, idx);
1796 }
1797 } else {
1798 for (idx, &entity) in table.entity_indices.iter().enumerate() {
1799 if self.entity_matches_tags(entity, tag_include, tag_exclude) {
1800 f(entity, table, idx);
1801 }
1802 }
1803 }
1804 }
1805 return;
1806 }
1807
1808 for table in &self.tables {
1809 if table.mask & component_include != component_include || table.mask & component_exclude != 0 {
1810 continue;
1811 }
1812
1813 if tag_include == 0 && tag_exclude == 0 {
1814 for (idx, &entity) in table.entity_indices.iter().enumerate() {
1815 f(entity, table, idx);
1816 }
1817 } else {
1818 for (idx, &entity) in table.entity_indices.iter().enumerate() {
1819 if self.entity_matches_tags(entity, tag_include, tag_exclude) {
1820 f(entity, table, idx);
1821 }
1822 }
1823 }
1824 }
1825 }
1826
1827 #[inline]
1828 pub fn for_each_mut<F>(&mut self, include: u64, exclude: u64, mut f: F)
1829 where
1830 F: FnMut($crate::Entity, &mut ComponentArrays, usize),
1831 {
1832 let component_include = include & !ALL_TAGS_MASK;
1833 let component_exclude = exclude & !ALL_TAGS_MASK;
1834 let tag_include = include & ALL_TAGS_MASK;
1835 let tag_exclude = exclude & ALL_TAGS_MASK;
1836
1837 let table_indices: Vec<usize> = self.get_cached_tables(component_include).to_vec();
1838
1839 if tag_include == 0 && tag_exclude == 0 {
1840 for &table_index in &table_indices {
1841 let table = &mut self.tables[table_index];
1842 if table.mask & component_exclude != 0 {
1843 continue;
1844 }
1845
1846 for idx in 0..table.entity_indices.len() {
1847 let entity = table.entity_indices[idx];
1848 f(entity, table, idx);
1849 }
1850 }
1851 } else {
1852 for &table_index in &table_indices {
1853 let table = &mut self.tables[table_index];
1854 if table.mask & component_exclude != 0 {
1855 continue;
1856 }
1857
1858 for idx in 0..table.entity_indices.len() {
1859 let entity = table.entity_indices[idx];
1860 let mut tag_match = true;
1861 $(
1862 if tag_include & $tag_mask != 0 && !self.$tag_name.contains(&entity) {
1863 tag_match = false;
1864 }
1865 if tag_match && tag_exclude & $tag_mask != 0 && self.$tag_name.contains(&entity) {
1866 tag_match = false;
1867 }
1868 )*
1869 if tag_match {
1870 f(entity, table, idx);
1871 }
1872 }
1873 }
1874 }
1875 }
1876
1877 #[cfg(not(target_family = "wasm"))]
1878 #[inline]
1879 pub fn par_for_each_mut<F>(&mut self, include: u64, exclude: u64, f: F)
1880 where
1881 F: Fn($crate::Entity, &mut ComponentArrays, usize) + Send + Sync,
1882 {
1883 use $crate::rayon::prelude::*;
1884
1885 let component_include = include & !ALL_TAGS_MASK;
1886 let component_exclude = exclude & !ALL_TAGS_MASK;
1887 let tag_include = include & ALL_TAGS_MASK;
1888 let tag_exclude = exclude & ALL_TAGS_MASK;
1889
1890 if tag_include == 0 && tag_exclude == 0 {
1891 self.tables
1892 .par_iter_mut()
1893 .filter(|table| table.mask & component_include == component_include && table.mask & component_exclude == 0)
1894 .for_each(|table| {
1895 for idx in 0..table.entity_indices.len() {
1896 let entity = table.entity_indices[idx];
1897 f(entity, table, idx);
1898 }
1899 });
1900 } else {
1901 $(let $tag_name = &self.$tag_name;)*
1902 self.tables
1903 .par_iter_mut()
1904 .filter(|table| table.mask & component_include == component_include && table.mask & component_exclude == 0)
1905 .for_each(|table| {
1906 for idx in 0..table.entity_indices.len() {
1907 let entity = table.entity_indices[idx];
1908 let mut tag_match = true;
1909 $(
1910 if tag_include & $tag_mask != 0 && !$tag_name.contains(&entity) {
1911 tag_match = false;
1912 }
1913 if tag_match && tag_exclude & $tag_mask != 0 && $tag_name.contains(&entity) {
1914 tag_match = false;
1915 }
1916 )*
1917 if tag_match {
1918 f(entity, table, idx);
1919 }
1920 }
1921 });
1922 }
1923 }
1924
1925 #[inline]
1926 pub fn for_each_mut_changed<F>(&mut self, include: u64, exclude: u64, mut f: F)
1927 where
1928 F: FnMut($crate::Entity, &mut ComponentArrays, usize),
1929 {
1930 let component_include = include & !ALL_TAGS_MASK;
1931 let component_exclude = exclude & !ALL_TAGS_MASK;
1932 let tag_include = include & ALL_TAGS_MASK;
1933 let tag_exclude = exclude & ALL_TAGS_MASK;
1934
1935 let table_indices: Vec<usize> = self.get_cached_tables(component_include).to_vec();
1936 let since_tick = self.last_tick;
1937
1938 if tag_include == 0 && tag_exclude == 0 {
1939 for &table_index in &table_indices {
1940 let table = &mut self.tables[table_index];
1941 if table.mask & component_exclude != 0 {
1942 continue;
1943 }
1944
1945 for idx in 0..table.entity_indices.len() {
1946 let entity = table.entity_indices[idx];
1947
1948 let mut changed = false;
1949 $(
1950 $crate::paste::paste! {
1951 if component_include & $mask != 0 && table.mask & $mask != 0 && table.[<$name _changed>][idx] > since_tick {
1952 changed = true;
1953 }
1954 }
1955 )*
1956
1957 if changed {
1958 f(entity, table, idx);
1959 }
1960 }
1961 }
1962 } else {
1963 for &table_index in &table_indices {
1964 let table = &mut self.tables[table_index];
1965 if table.mask & component_exclude != 0 {
1966 continue;
1967 }
1968
1969 for idx in 0..table.entity_indices.len() {
1970 let entity = table.entity_indices[idx];
1971 let mut tag_match = true;
1972 $(
1973 if tag_include & $tag_mask != 0 && !self.$tag_name.contains(&entity) {
1974 tag_match = false;
1975 }
1976 if tag_match && tag_exclude & $tag_mask != 0 && self.$tag_name.contains(&entity) {
1977 tag_match = false;
1978 }
1979 )*
1980 if !tag_match {
1981 continue;
1982 }
1983
1984 let mut changed = false;
1985 $(
1986 $crate::paste::paste! {
1987 if component_include & $mask != 0 && table.mask & $mask != 0 && table.[<$name _changed>][idx] > since_tick {
1988 changed = true;
1989 }
1990 }
1991 )*
1992
1993 if changed {
1994 f(entity, table, idx);
1995 }
1996 }
1997 }
1998 }
1999 }
2000
2001 }
2002
2003
2004 #[derive(Default)]
2005 pub struct $resources {
2006 $($(#[$attr])* pub $resource_name: $resource_type,)*
2007 }
2008
2009 $crate::paste::paste! {
2010 #[derive(Default)]
2011 pub struct ComponentArrays {
2012 $($(#[$comp_attr])* pub $name: Vec<$type>,)*
2013 $($(#[$comp_attr])* pub [<$name _changed>]: Vec<u32>,)*
2014 pub entity_indices: Vec<$crate::Entity>,
2015 pub mask: u64,
2016 }
2017 }
2018
2019
2020 pub struct QueryBuilder<'a> {
2021 world: &'a $world,
2022 include: u64,
2023 exclude: u64,
2024 }
2025
2026 impl<'a> QueryBuilder<'a> {
2027 pub fn new(world: &'a $world) -> Self {
2028 Self {
2029 world,
2030 include: 0,
2031 exclude: 0,
2032 }
2033 }
2034
2035 pub fn with(mut self, mask: u64) -> Self {
2036 self.include |= mask;
2037 self
2038 }
2039
2040 pub fn without(mut self, mask: u64) -> Self {
2041 self.exclude |= mask;
2042 self
2043 }
2044
2045 pub fn iter<F>(self, f: F)
2046 where
2047 F: FnMut($crate::Entity, &ComponentArrays, usize),
2048 {
2049 self.world.for_each(self.include, self.exclude, f);
2050 }
2051 }
2052
2053 pub struct QueryBuilderMut<'a> {
2054 world: &'a mut $world,
2055 include: u64,
2056 exclude: u64,
2057 }
2058
2059 impl<'a> QueryBuilderMut<'a> {
2060 pub fn new(world: &'a mut $world) -> Self {
2061 Self {
2062 world,
2063 include: 0,
2064 exclude: 0,
2065 }
2066 }
2067
2068 pub fn with(mut self, mask: u64) -> Self {
2069 self.include |= mask;
2070 self
2071 }
2072
2073 pub fn without(mut self, mask: u64) -> Self {
2074 self.exclude |= mask;
2075 self
2076 }
2077
2078 pub fn iter<F>(self, f: F)
2079 where
2080 F: FnMut($crate::Entity, &mut ComponentArrays, usize),
2081 {
2082 self.world.for_each_mut(self.include, self.exclude, f);
2083 }
2084 }
2085
2086 impl $world {
2087 pub fn query(&self) -> QueryBuilder<'_> {
2088 QueryBuilder::new(self)
2089 }
2090
2091 pub fn query_mut(&mut self) -> QueryBuilderMut<'_> {
2092 QueryBuilderMut::new(self)
2093 }
2094
2095 $(
2096 $(#[$comp_attr])*
2097 $crate::paste::paste! {
2098 pub fn [<iter_ $name>]<F>(&self, mut f: F)
2099 where
2100 F: FnMut($crate::Entity, &$type),
2101 {
2102 self.for_each($mask, 0, |entity, table, idx| {
2103 f(entity, &table.$name[idx]);
2104 });
2105 }
2106
2107 pub fn [<iter_ $name _mut>]<F>(&mut self, mut f: F)
2108 where
2109 F: FnMut($crate::Entity, &mut $type),
2110 {
2111 self.for_each_mut($mask, 0, |entity, table, idx| {
2112 f(entity, &mut table.$name[idx]);
2113 });
2114 }
2115 }
2116 )*
2117 }
2118
2119 pub struct EntityQueryIter<'a> {
2120 tables: &'a [ComponentArrays],
2121 mask: u64,
2122 table_index: usize,
2123 array_index: usize,
2124 }
2125
2126 impl<'a> Iterator for EntityQueryIter<'a> {
2127 type Item = $crate::Entity;
2128
2129 fn next(&mut self) -> Option<Self::Item> {
2130 loop {
2131 if self.table_index >= self.tables.len() {
2132 return None;
2133 }
2134
2135 let table = &self.tables[self.table_index];
2136
2137 if table.mask & self.mask != self.mask {
2138 self.table_index += 1;
2139 self.array_index = 0;
2140 continue;
2141 }
2142
2143 if self.array_index >= table.entity_indices.len() {
2144 self.table_index += 1;
2145 self.array_index = 0;
2146 continue;
2147 }
2148
2149 let entity = table.entity_indices[self.array_index];
2150 self.array_index += 1;
2151 return Some(entity);
2152 }
2153 }
2154
2155 fn size_hint(&self) -> (usize, Option<usize>) {
2156 let mut remaining = 0;
2157 for table_idx in self.table_index..self.tables.len() {
2158 let table = &self.tables[table_idx];
2159 if table.mask & self.mask != self.mask {
2160 continue;
2161 }
2162 if table_idx == self.table_index {
2163 remaining += table.entity_indices.len().saturating_sub(self.array_index);
2164 } else {
2165 remaining += table.entity_indices.len();
2166 }
2167 }
2168 (remaining, Some(remaining))
2169 }
2170 }
2171
2172 pub struct ChangedEntityQueryIter<'a> {
2173 tables: &'a [ComponentArrays],
2174 mask: u64,
2175 since_tick: u32,
2176 table_index: usize,
2177 array_index: usize,
2178 }
2179
2180 impl<'a> Iterator for ChangedEntityQueryIter<'a> {
2181 type Item = $crate::Entity;
2182
2183 fn next(&mut self) -> Option<Self::Item> {
2184 loop {
2185 if self.table_index >= self.tables.len() {
2186 return None;
2187 }
2188
2189 let table = &self.tables[self.table_index];
2190
2191 if table.mask & self.mask != self.mask {
2192 self.table_index += 1;
2193 self.array_index = 0;
2194 continue;
2195 }
2196
2197 if self.array_index >= table.entity_indices.len() {
2198 self.table_index += 1;
2199 self.array_index = 0;
2200 continue;
2201 }
2202
2203 let idx = self.array_index;
2204 self.array_index += 1;
2205
2206 let mut changed = false;
2207 $(
2208 $crate::paste::paste! {
2209 if self.mask & $mask != 0 && table.mask & $mask != 0 && table.[<$name _changed>][idx] > self.since_tick {
2210 changed = true;
2211 }
2212 }
2213 )*
2214
2215 if changed {
2216 return Some(table.entity_indices[idx]);
2217 }
2218 }
2219 }
2220 }
2221
2222 $(
2223 $(#[$comp_attr])*
2224 $crate::paste::paste! {
2225 pub struct [<$mask:camel QueryIter>]<'a> {
2226 tables: &'a [ComponentArrays],
2227 table_index: usize,
2228 array_index: usize,
2229 }
2230
2231 impl<'a> Iterator for [<$mask:camel QueryIter>]<'a> {
2232 type Item = &'a $type;
2233
2234 fn next(&mut self) -> Option<Self::Item> {
2235 loop {
2236 if self.table_index >= self.tables.len() {
2237 return None;
2238 }
2239
2240 let table = &self.tables[self.table_index];
2241
2242 if table.mask & $mask == 0 {
2243 self.table_index += 1;
2244 self.array_index = 0;
2245 continue;
2246 }
2247
2248 if self.array_index >= table.$name.len() {
2249 self.table_index += 1;
2250 self.array_index = 0;
2251 continue;
2252 }
2253
2254 let component = &table.$name[self.array_index];
2255 self.array_index += 1;
2256 return Some(component);
2257 }
2258 }
2259
2260 fn size_hint(&self) -> (usize, Option<usize>) {
2261 let mut remaining = 0;
2262 for table_idx in self.table_index..self.tables.len() {
2263 let table = &self.tables[table_idx];
2264 if table.mask & $mask == 0 {
2265 continue;
2266 }
2267 if table_idx == self.table_index {
2268 remaining += table.$name.len().saturating_sub(self.array_index);
2269 } else {
2270 remaining += table.$name.len();
2271 }
2272 }
2273 (remaining, Some(remaining))
2274 }
2275 }
2276
2277 }
2278 )*
2279
2280 #[derive(Clone)]
2281 struct TableEdges {
2282 add_edges: [Option<usize>; COMPONENT_COUNT],
2283 remove_edges: [Option<usize>; COMPONENT_COUNT],
2284 multi_add_cache: std::collections::HashMap<u64, usize>,
2285 multi_remove_cache: std::collections::HashMap<u64, usize>,
2286 }
2287
2288 impl Default for TableEdges {
2289 fn default() -> Self {
2290 Self {
2291 add_edges: [None; COMPONENT_COUNT],
2292 remove_edges: [None; COMPONENT_COUNT],
2293 multi_add_cache: std::collections::HashMap::default(),
2294 multi_remove_cache: std::collections::HashMap::default(),
2295 }
2296 }
2297 }
2298
2299 fn get_component_index(mask: u64) -> Option<usize> {
2300 match mask {
2301 $($(#[$comp_attr])* $mask => Some(Component::$mask as _),)*
2302 _ => None,
2303 }
2304 }
2305
2306 fn remove_from_table(arrays: &mut ComponentArrays, index: usize) -> Option<$crate::Entity> {
2307 let last_index = arrays.entity_indices.len() - 1;
2308 let mut swapped_entity = None;
2309
2310 if index < last_index {
2311 swapped_entity = Some(arrays.entity_indices[last_index]);
2312 }
2313
2314 $(
2315 $(#[$comp_attr])*
2316 $crate::paste::paste! {
2317 if arrays.mask & $mask != 0 {
2318 arrays.$name.swap_remove(index);
2319 arrays.[<$name _changed>].swap_remove(index);
2320 }
2321 }
2322 )*
2323 arrays.entity_indices.swap_remove(index);
2324
2325 swapped_entity
2326 }
2327
2328 fn move_entity(
2329 world: &mut $world,
2330 entity: $crate::Entity,
2331 from_table: usize,
2332 from_index: usize,
2333 to_table: usize,
2334 ) {
2335 let tick = world.current_tick;
2336 let components = {
2337 let from_table_ref = &mut world.tables[from_table];
2338 (
2339 $(
2340 $(#[$comp_attr])*
2341 {
2342 if from_table_ref.mask & $mask != 0 {
2343 Some(std::mem::take(&mut from_table_ref.$name[from_index]))
2344 } else {
2345 None
2346 }
2347 },
2348 )*
2349 )
2350 };
2351
2352 add_to_table(&mut world.tables[to_table], entity, components, tick);
2353 let new_index = world.tables[to_table].entity_indices.len() - 1;
2354 insert_location(&mut world.entity_locations, entity, (to_table, new_index));
2355
2356 if let Some(swapped) = remove_from_table(&mut world.tables[from_table], from_index) {
2357 insert_location(
2358 &mut world.entity_locations,
2359 swapped,
2360 (from_table, from_index),
2361 );
2362 }
2363 }
2364
2365 fn get_location(locations: &$crate::EntityLocations, entity: $crate::Entity) -> Option<(usize, usize)> {
2366 let location = locations.get(entity.id)?;
2367 if !location.allocated || location.generation != entity.generation {
2368 return None;
2369 }
2370
2371 Some((location.table_index as usize, location.array_index as usize))
2372 }
2373
2374 fn insert_location(
2375 locations: &mut $crate::EntityLocations,
2376 entity: $crate::Entity,
2377 location: (usize, usize),
2378 ) {
2379 locations.insert(entity.id, $crate::EntityLocation {
2380 generation: entity.generation,
2381 table_index: location.0 as u32,
2382 array_index: location.1 as u32,
2383 allocated: true,
2384 });
2385 }
2386
2387 fn create_entity(world: &mut $world) -> $crate::Entity {
2388 let entity = world.allocator.allocate();
2389 world.entity_locations.ensure_slot(entity.id, entity.generation);
2390 entity
2391 }
2392
2393 fn add_to_table(
2394 arrays: &mut ComponentArrays,
2395 entity: $crate::Entity,
2396 components: ( $(Option<$type>,)* ),
2397 tick: u32,
2398 ) {
2399 let ($($name,)*) = components;
2400 $(
2401 $(#[$comp_attr])*
2402 $crate::paste::paste! {
2403 if arrays.mask & $mask != 0 {
2404 if let Some(component) = $name {
2405 arrays.$name.push(component);
2406 } else {
2407 arrays.$name.push(<$type>::default());
2408 }
2409 arrays.[<$name _changed>].push(tick);
2410 }
2411 }
2412 )*
2413 arrays.entity_indices.push(entity);
2414 }
2415
2416 fn get_or_create_table(world: &mut $world, mask: u64) -> usize {
2417 if let Some(&index) = world.table_lookup.get(&mask) {
2418 return index;
2419 }
2420
2421 let table_index = world.tables.len();
2422 world.tables.push(ComponentArrays {
2423 mask,
2424 ..Default::default()
2425 });
2426 world.table_edges.push(TableEdges::default());
2427 world.table_lookup.insert(mask, table_index);
2428
2429 world.invalidate_query_cache_for_table(mask, table_index);
2430
2431 for comp_mask in [$($(#[$comp_attr])* $mask,)*] {
2432 if let Some(comp_idx) = get_component_index(comp_mask) {
2433 for (idx, table) in world.tables.iter().enumerate() {
2434 if table.mask | comp_mask == mask {
2435 world.table_edges[idx].add_edges[comp_idx] = Some(table_index);
2436 }
2437 if table.mask & !comp_mask == mask {
2438 world.table_edges[idx].remove_edges[comp_idx] = Some(table_index);
2439 }
2440 }
2441 }
2442 }
2443
2444 table_index
2445 }
2446 };
2447}
2448
2449#[macro_export]
2450macro_rules! ecs_world_impl {
2451 (
2452 $world:ident {
2453 $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
2454 }
2455 ) => {
2456 $crate::paste::paste! {
2457 #[repr(u64)]
2458 #[allow(clippy::upper_case_acronyms)]
2459 #[allow(non_camel_case_types)]
2460 pub enum [<$world Component>] {
2461 $($(#[$comp_attr])* $mask,)*
2462 }
2463
2464 $($(#[$comp_attr])* pub const $mask: u64 = 1 << ([<$world Component>]::$mask as u64);)*
2465
2466 pub const [<$world:snake:upper _COMPONENT_COUNT>]: usize = {
2467 let mut count = 0;
2468 $($(#[$comp_attr])* { count += 1; let _ = [<$world Component>]::$mask; })*
2469 count
2470 };
2471
2472 #[derive(Default)]
2473 pub struct [<$world ComponentArrays>] {
2474 $($(#[$comp_attr])* pub $name: Vec<$type>,)*
2475 $($(#[$comp_attr])* pub [<$name _changed>]: Vec<u32>,)*
2476 pub entity_indices: Vec<$crate::Entity>,
2477 pub mask: u64,
2478 }
2479
2480 #[derive(Clone)]
2481 struct [<$world TableEdges>] {
2482 add_edges: [Option<usize>; [<$world:snake:upper _COMPONENT_COUNT>]],
2483 remove_edges: [Option<usize>; [<$world:snake:upper _COMPONENT_COUNT>]],
2484 multi_add_cache: std::collections::HashMap<u64, usize>,
2485 multi_remove_cache: std::collections::HashMap<u64, usize>,
2486 }
2487
2488 impl Default for [<$world TableEdges>] {
2489 fn default() -> Self {
2490 Self {
2491 add_edges: [None; [<$world:snake:upper _COMPONENT_COUNT>]],
2492 remove_edges: [None; [<$world:snake:upper _COMPONENT_COUNT>]],
2493 multi_add_cache: std::collections::HashMap::default(),
2494 multi_remove_cache: std::collections::HashMap::default(),
2495 }
2496 }
2497 }
2498
2499 #[allow(unused)]
2500 pub struct $world {
2501 pub entity_locations: $crate::EntityLocations,
2502 tables: Vec<[<$world ComponentArrays>]>,
2503 table_edges: Vec<[<$world TableEdges>]>,
2504 table_lookup: std::collections::HashMap<u64, usize>,
2505 query_cache: std::collections::HashMap<u64, Vec<usize>>,
2506 current_tick: u32,
2507 last_tick: u32,
2508 }
2509
2510 impl Default for $world {
2511 fn default() -> Self {
2512 Self {
2513 entity_locations: $crate::EntityLocations::default(),
2514 tables: Vec::default(),
2515 table_edges: Vec::default(),
2516 table_lookup: std::collections::HashMap::default(),
2517 query_cache: std::collections::HashMap::default(),
2518 current_tick: 0,
2519 last_tick: 0,
2520 }
2521 }
2522 }
2523
2524 #[allow(unused)]
2525 impl $world {
2526 fn get_cached_tables(&mut self, mask: u64) -> &[usize] {
2527 if !self.query_cache.contains_key(&mask) {
2528 let matching_tables: Vec<usize> = self.tables
2529 .iter()
2530 .enumerate()
2531 .filter(|(_, table)| table.mask & mask == mask)
2532 .map(|(idx, _)| idx)
2533 .collect();
2534 self.query_cache.insert(mask, matching_tables);
2535 }
2536 &self.query_cache[&mask]
2537 }
2538
2539 fn invalidate_query_cache_for_table(&mut self, new_table_mask: u64, new_table_index: usize) {
2540 self.query_cache.retain(|query_mask, cached_tables| {
2541 if new_table_mask & query_mask == *query_mask {
2542 cached_tables.push(new_table_index);
2543 true
2544 } else {
2545 true
2546 }
2547 });
2548 }
2549
2550 $(
2551 $(#[$comp_attr])*
2552 $crate::paste::paste! {
2553 #[inline]
2554 pub fn [<get_ $name>](&self, entity: $crate::Entity) -> Option<&$type> {
2555 let (table_index, array_index) = [<get_location_ $world:snake>](&self.entity_locations, entity)?;
2556 let table = &self.tables[table_index];
2557 if table.mask & $mask == 0 {
2558 return None;
2559 }
2560 Some(&table.$name[array_index])
2561 }
2562
2563 #[inline]
2564 pub fn [<get_ $name _mut>](&mut self, entity: $crate::Entity) -> Option<&mut $type> {
2565 let (table_index, array_index) = [<get_location_ $world:snake>](&self.entity_locations, entity)?;
2566 let current_tick = self.current_tick;
2567 let table = &mut self.tables[table_index];
2568 if table.mask & $mask == 0 {
2569 return None;
2570 }
2571 table.[<$name _changed>][array_index] = current_tick;
2572 Some(&mut table.$name[array_index])
2573 }
2574
2575 #[inline]
2576 pub fn [<modify_ $name>]<R>(&mut self, entity: $crate::Entity, f: impl FnOnce(&mut $type) -> R) -> Option<R> {
2577 let (table_index, array_index) = [<get_location_ $world:snake>](&self.entity_locations, entity)?;
2578 let current_tick = self.current_tick;
2579 let table = &mut self.tables[table_index];
2580 if table.mask & $mask == 0 {
2581 return None;
2582 }
2583 table.[<$name _changed>][array_index] = current_tick;
2584 Some(f(&mut table.$name[array_index]))
2585 }
2586
2587 #[inline]
2588 pub fn [<entity_has_ $name>](&self, entity: $crate::Entity) -> bool {
2589 self.entity_has_components(entity, $mask)
2590 }
2591
2592 #[inline]
2593 pub fn [<set_ $name>](&mut self, entity: $crate::Entity, value: $type) {
2594 if let Some((table_index, array_index)) = [<get_location_ $world:snake>](&self.entity_locations, entity) {
2595 let table = &mut self.tables[table_index];
2596 if table.mask & $mask != 0 {
2597 table.$name[array_index] = value;
2598 return;
2599 }
2600 }
2601 self.add_components(entity, $mask);
2602 if let Some((table_index, array_index)) = [<get_location_ $world:snake>](&self.entity_locations, entity) {
2603 self.tables[table_index].$name[array_index] = value;
2604 }
2605 }
2606
2607 #[inline]
2608 pub fn [<add_ $name>](&mut self, entity: $crate::Entity) {
2609 self.add_components(entity, $mask);
2610 }
2611
2612 #[inline]
2613 pub fn [<remove_ $name>](&mut self, entity: $crate::Entity) -> bool {
2614 self.remove_components(entity, $mask)
2615 }
2616
2617 #[inline]
2618 pub fn [<query_ $name>](&self) -> [<$mask:camel QueryIter>]<'_> {
2619 [<$mask:camel QueryIter>] {
2620 tables: &self.tables,
2621 table_index: 0,
2622 array_index: 0,
2623 }
2624 }
2625
2626 pub fn [<for_each_ $name _mut>]<F>(&mut self, mut f: F)
2627 where
2628 F: FnMut(&mut $type),
2629 {
2630 let table_indices: Vec<usize> = self.get_cached_tables($mask).to_vec();
2631 for table_index in table_indices {
2632 for component in &mut self.tables[table_index].$name {
2633 f(component);
2634 }
2635 }
2636 }
2637
2638 #[cfg(not(target_family = "wasm"))]
2639 pub fn [<par_for_each_ $name _mut>]<F>(&mut self, f: F)
2640 where
2641 F: Fn(&mut $type) + Send + Sync,
2642 {
2643 use $crate::rayon::prelude::*;
2644 self.tables
2645 .par_iter_mut()
2646 .filter(|table| table.mask & $mask != 0)
2647 .for_each(|table| {
2648 table.$name.par_iter_mut().for_each(|component| f(component));
2649 });
2650 }
2651
2652 pub fn [<iter_ $name _slices>](&self) -> impl Iterator<Item = &[$type]> {
2653 self.tables
2654 .iter()
2655 .filter(|table| table.mask & $mask != 0)
2656 .map(|table| table.$name.as_slice())
2657 }
2658
2659 pub fn [<iter_ $name _slices_mut>](&mut self) -> impl Iterator<Item = &mut [$type]> {
2660 self.tables
2661 .iter_mut()
2662 .filter(|table| table.mask & $mask != 0)
2663 .map(|table| table.$name.as_mut_slice())
2664 }
2665 }
2666 )*
2667
2668 pub fn spawn_entities(&mut self, allocator: &mut $crate::EntityAllocator, mask: u64, count: usize) -> Vec<$crate::Entity> {
2669 let mut entities = Vec::with_capacity(count);
2670 let table_index = [<get_or_create_table_ $world:snake>](self, mask);
2671 let start_index = self.tables[table_index].entity_indices.len();
2672
2673 self.tables[table_index].entity_indices.reserve(count);
2674 $(
2675 $(#[$comp_attr])*
2676 {
2677 if mask & $mask != 0 {
2678 self.tables[table_index].$name.reserve(count);
2679 self.tables[table_index].[<$name _changed>].reserve(count);
2680 }
2681 }
2682 )*
2683
2684 for local_index in 0..count {
2685 let entity = allocator.allocate();
2686 self.entity_locations.ensure_slot(entity.id, entity.generation);
2687 entities.push(entity);
2688
2689 self.tables[table_index].entity_indices.push(entity);
2690 $(
2691 $(#[$comp_attr])*
2692 {
2693 if mask & $mask != 0 {
2694 self.tables[table_index].$name.push(<$type>::default());
2695 self.tables[table_index].[<$name _changed>].push(self.current_tick);
2696 }
2697 }
2698 )*
2699
2700 [<insert_location_ $world:snake>](
2701 &mut self.entity_locations,
2702 entity,
2703 (table_index, start_index + local_index),
2704 );
2705 }
2706
2707 entities
2708 }
2709
2710 pub fn spawn_batch<F>(&mut self, allocator: &mut $crate::EntityAllocator, mask: u64, count: usize, mut init: F) -> Vec<$crate::Entity>
2711 where
2712 F: FnMut(&mut [<$world ComponentArrays>], usize),
2713 {
2714 let table_index = [<get_or_create_table_ $world:snake>](self, mask);
2715 let start_index = self.tables[table_index].entity_indices.len();
2716
2717 self.tables[table_index].entity_indices.reserve(count);
2718 $(
2719 $(#[$comp_attr])*
2720 {
2721 if mask & $mask != 0 {
2722 self.tables[table_index].$name.reserve(count);
2723 self.tables[table_index].[<$name _changed>].reserve(count);
2724 }
2725 }
2726 )*
2727
2728 let mut entities = Vec::with_capacity(count);
2729 for local_index in 0..count {
2730 let entity = allocator.allocate();
2731 self.entity_locations.ensure_slot(entity.id, entity.generation);
2732 entities.push(entity);
2733
2734 self.tables[table_index].entity_indices.push(entity);
2735 $(
2736 $(#[$comp_attr])*
2737 {
2738 if mask & $mask != 0 {
2739 self.tables[table_index].$name.push(<$type>::default());
2740 self.tables[table_index].[<$name _changed>].push(self.current_tick);
2741 }
2742 }
2743 )*
2744
2745 [<insert_location_ $world:snake>](
2746 &mut self.entity_locations,
2747 entity,
2748 (table_index, start_index + local_index),
2749 );
2750
2751 init(&mut self.tables[table_index], start_index + local_index);
2752 }
2753
2754 entities
2755 }
2756
2757 pub fn query_entities(&self, mask: u64) -> [<$world EntityQueryIter>]<'_> {
2758 [<$world EntityQueryIter>] {
2759 tables: &self.tables,
2760 mask,
2761 table_index: 0,
2762 array_index: 0,
2763 }
2764 }
2765
2766 pub fn query_entities_changed(&self, mask: u64) -> [<$world ChangedEntityQueryIter>]<'_> {
2767 [<$world ChangedEntityQueryIter>] {
2768 tables: &self.tables,
2769 mask,
2770 since_tick: self.last_tick,
2771 table_index: 0,
2772 array_index: 0,
2773 }
2774 }
2775
2776 pub fn query_first_entity(&self, mask: u64) -> Option<$crate::Entity> {
2777 for table in &self.tables {
2778 if table.mask & mask != mask {
2779 continue;
2780 }
2781 if let Some(&entity) = table.entity_indices.first() {
2782 return Some(entity);
2783 }
2784 }
2785 None
2786 }
2787
2788 pub fn remove_entity(&mut self, entity: $crate::Entity) -> bool {
2789 if let Some(loc) = self.entity_locations.get_mut(entity.id) {
2790 if loc.allocated && loc.generation == entity.generation {
2791 let table_idx = loc.table_index as usize;
2792 let array_idx = loc.array_index as usize;
2793
2794 self.entity_locations.mark_deallocated(entity.id);
2795
2796 if table_idx < self.tables.len() {
2797 let table = &mut self.tables[table_idx];
2798 if !table.entity_indices.is_empty() {
2799 let last_idx = table.entity_indices.len() - 1;
2800
2801 if array_idx < last_idx {
2802 let moved_entity = table.entity_indices[last_idx];
2803 if let Some(moved_loc) = self.entity_locations.get_mut(moved_entity.id) {
2804 if moved_loc.allocated {
2805 moved_loc.array_index = array_idx as u32;
2806 }
2807 }
2808 }
2809
2810 $(
2811 $(#[$comp_attr])*
2812 {
2813 if table.mask & $mask != 0 {
2814 table.$name.swap_remove(array_idx);
2815 table.[<$name _changed>].swap_remove(array_idx);
2816 }
2817 }
2818 )*
2819 table.entity_indices.swap_remove(array_idx);
2820 }
2821 }
2822 return true;
2823 }
2824 }
2825 false
2826 }
2827
2828 pub fn add_components(&mut self, entity: $crate::Entity, mask: u64) -> bool {
2829 if let Some((table_index, array_index)) = [<get_location_ $world:snake>](&self.entity_locations, entity) {
2830 let current_mask = self.tables[table_index].mask;
2831 if current_mask & mask == mask {
2832 return true;
2833 }
2834
2835 let target_table = if mask.count_ones() == 1 {
2836 [<get_component_index_ $world:snake>](mask).and_then(|idx| self.table_edges[table_index].add_edges[idx])
2837 } else {
2838 self.table_edges[table_index].multi_add_cache.get(&mask).copied()
2839 };
2840
2841 let new_table_index = target_table.unwrap_or_else(|| {
2842 let new_idx = [<get_or_create_table_ $world:snake>](self, current_mask | mask);
2843 self.table_edges[table_index].multi_add_cache.insert(mask, new_idx);
2844 new_idx
2845 });
2846
2847 [<move_entity_ $world:snake>](self, entity, table_index, array_index, new_table_index);
2848 true
2849 } else {
2850 if let Some(loc) = self.entity_locations.get(entity.id) {
2851 if loc.allocated {
2852 return false;
2853 }
2854 }
2855
2856 let table_index = [<get_or_create_table_ $world:snake>](self, mask);
2857 let start_index = self.tables[table_index].entity_indices.len();
2858
2859 self.tables[table_index].entity_indices.push(entity);
2860 $(
2861 $(#[$comp_attr])*
2862 {
2863 if mask & $mask != 0 {
2864 self.tables[table_index].$name.push(<$type>::default());
2865 self.tables[table_index].[<$name _changed>].push(self.current_tick);
2866 }
2867 }
2868 )*
2869
2870 self.entity_locations.insert(entity.id, $crate::EntityLocation {
2871 generation: entity.generation,
2872 table_index: table_index as u32,
2873 array_index: start_index as u32,
2874 allocated: true,
2875 });
2876 true
2877 }
2878 }
2879
2880 pub fn remove_components(&mut self, entity: $crate::Entity, mask: u64) -> bool {
2881 if let Some((table_index, array_index)) = [<get_location_ $world:snake>](&self.entity_locations, entity) {
2882 let current_mask = self.tables[table_index].mask;
2883 if current_mask & mask == 0 {
2884 return true;
2885 }
2886
2887 let target_table = if mask.count_ones() == 1 {
2888 [<get_component_index_ $world:snake>](mask)
2889 .and_then(|idx| self.table_edges[table_index].remove_edges[idx])
2890 } else {
2891 self.table_edges[table_index].multi_remove_cache.get(&mask).copied()
2892 };
2893
2894 let new_table_index = target_table.unwrap_or_else(|| {
2895 let new_idx = [<get_or_create_table_ $world:snake>](self, current_mask & !mask);
2896 self.table_edges[table_index].multi_remove_cache.insert(mask, new_idx);
2897 new_idx
2898 });
2899
2900 [<move_entity_ $world:snake>](self, entity, table_index, array_index, new_table_index);
2901 true
2902 } else {
2903 false
2904 }
2905 }
2906
2907 pub fn component_mask(&self, entity: $crate::Entity) -> Option<u64> {
2908 [<get_location_ $world:snake>](&self.entity_locations, entity)
2909 .map(|(table_index, _)| self.tables[table_index].mask)
2910 }
2911
2912 pub fn get_all_entities(&self) -> Vec<$crate::Entity> {
2913 let mut result = Vec::with_capacity(self.entity_count());
2914 for table in &self.tables {
2915 result.extend(table.entity_indices.iter().copied());
2916 }
2917 result
2918 }
2919
2920 pub fn entity_count(&self) -> usize {
2921 self.tables.iter().map(|table| table.entity_indices.len()).sum()
2922 }
2923
2924 pub fn entity_has_components(&self, entity: $crate::Entity, components: u64) -> bool {
2925 self.component_mask(entity).unwrap_or(0) & components == components
2926 }
2927
2928 pub fn increment_tick(&mut self) {
2929 self.last_tick = self.current_tick;
2930 self.current_tick = self.current_tick.wrapping_add(1);
2931 }
2932
2933 pub fn current_tick(&self) -> u32 {
2934 self.current_tick
2935 }
2936
2937 pub fn last_tick(&self) -> u32 {
2938 self.last_tick
2939 }
2940
2941 $(
2942 $(#[$comp_attr])*
2943 $crate::paste::paste! {
2944 pub fn [<query_ $name _mut>]<F>(&mut self, mask: u64, mut f: F)
2945 where
2946 F: FnMut($crate::Entity, &mut $type),
2947 {
2948 let table_indices: Vec<usize> = self.get_cached_tables(mask).to_vec();
2949 for &table_index in &table_indices {
2950 let table = &mut self.tables[table_index];
2951 if table.mask & $mask == 0 {
2952 continue;
2953 }
2954 for idx in 0..table.entity_indices.len() {
2955 let entity = table.entity_indices[idx];
2956 f(entity, &mut table.$name[idx]);
2957 }
2958 }
2959 }
2960 }
2961 )*
2962 }
2963
2964 #[allow(unused)]
2965 impl $world {
2966 #[inline]
2967 pub fn for_each<F>(&self, include: u64, exclude: u64, mut f: F)
2968 where
2969 F: FnMut($crate::Entity, &[<$world ComponentArrays>], usize),
2970 {
2971 for table in &self.tables {
2972 if table.mask & include != include || table.mask & exclude != 0 {
2973 continue;
2974 }
2975 for (idx, &entity) in table.entity_indices.iter().enumerate() {
2976 f(entity, table, idx);
2977 }
2978 }
2979 }
2980
2981 #[inline]
2982 pub fn for_each_mut<F>(&mut self, include: u64, exclude: u64, mut f: F)
2983 where
2984 F: FnMut($crate::Entity, &mut [<$world ComponentArrays>], usize),
2985 {
2986 let table_indices: Vec<usize> = self.get_cached_tables(include).to_vec();
2987
2988 for &table_index in &table_indices {
2989 let table = &mut self.tables[table_index];
2990 if table.mask & exclude != 0 {
2991 continue;
2992 }
2993 for idx in 0..table.entity_indices.len() {
2994 let entity = table.entity_indices[idx];
2995 f(entity, table, idx);
2996 }
2997 }
2998 }
2999
3000 #[cfg(not(target_family = "wasm"))]
3001 #[inline]
3002 pub fn par_for_each_mut<F>(&mut self, include: u64, exclude: u64, f: F)
3003 where
3004 F: Fn($crate::Entity, &mut [<$world ComponentArrays>], usize) + Send + Sync,
3005 {
3006 use $crate::rayon::prelude::*;
3007
3008 self.tables
3009 .par_iter_mut()
3010 .filter(|table| table.mask & include == include && table.mask & exclude == 0)
3011 .for_each(|table| {
3012 for idx in 0..table.entity_indices.len() {
3013 let entity = table.entity_indices[idx];
3014 f(entity, table, idx);
3015 }
3016 });
3017 }
3018
3019 #[inline]
3020 pub fn for_each_mut_changed<F>(&mut self, include: u64, exclude: u64, mut f: F)
3021 where
3022 F: FnMut($crate::Entity, &mut [<$world ComponentArrays>], usize),
3023 {
3024 let table_indices: Vec<usize> = self.get_cached_tables(include).to_vec();
3025 let since_tick = self.last_tick;
3026
3027 for &table_index in &table_indices {
3028 let table = &mut self.tables[table_index];
3029 if table.mask & exclude != 0 {
3030 continue;
3031 }
3032
3033 for idx in 0..table.entity_indices.len() {
3034 let entity = table.entity_indices[idx];
3035
3036 let mut changed = false;
3037 $(
3038 $(#[$comp_attr])*
3039 {
3040 if include & $mask != 0 && table.mask & $mask != 0 && table.[<$name _changed>][idx] > since_tick {
3041 changed = true;
3042 }
3043 }
3044 )*
3045
3046 if changed {
3047 f(entity, table, idx);
3048 }
3049 }
3050 }
3051 }
3052 }
3053
3054 #[allow(unused)]
3055 impl $world {
3056 #[inline]
3057 pub fn for_each_with_tags<F>(
3058 &self,
3059 include: u64,
3060 exclude: u64,
3061 include_tags: &[&std::collections::HashSet<$crate::Entity>],
3062 exclude_tags: &[&std::collections::HashSet<$crate::Entity>],
3063 mut f: F,
3064 )
3065 where
3066 F: FnMut($crate::Entity, &[<$world ComponentArrays>], usize),
3067 {
3068 let has_tag_filter = !include_tags.is_empty() || !exclude_tags.is_empty();
3069
3070 for table in &self.tables {
3071 if table.mask & include != include || table.mask & exclude != 0 {
3072 continue;
3073 }
3074
3075 if has_tag_filter {
3076 for (idx, &entity) in table.entity_indices.iter().enumerate() {
3077 if include_tags.iter().all(|tag_set| tag_set.contains(&entity))
3078 && !exclude_tags.iter().any(|tag_set| tag_set.contains(&entity))
3079 {
3080 f(entity, table, idx);
3081 }
3082 }
3083 } else {
3084 for (idx, &entity) in table.entity_indices.iter().enumerate() {
3085 f(entity, table, idx);
3086 }
3087 }
3088 }
3089 }
3090
3091 #[inline]
3092 pub fn for_each_mut_with_tags<F>(
3093 &mut self,
3094 include: u64,
3095 exclude: u64,
3096 include_tags: &[&std::collections::HashSet<$crate::Entity>],
3097 exclude_tags: &[&std::collections::HashSet<$crate::Entity>],
3098 mut f: F,
3099 )
3100 where
3101 F: FnMut($crate::Entity, &mut [<$world ComponentArrays>], usize),
3102 {
3103 let has_tag_filter = !include_tags.is_empty() || !exclude_tags.is_empty();
3104 let table_indices: Vec<usize> = self.get_cached_tables(include).to_vec();
3105
3106 if has_tag_filter {
3107 let matching_entities: std::collections::HashSet<$crate::Entity> = table_indices
3108 .iter()
3109 .filter_map(|&idx| self.tables.get(idx))
3110 .filter(|table| table.mask & exclude == 0)
3111 .flat_map(|table| table.entity_indices.iter().copied())
3112 .filter(|entity| {
3113 include_tags.iter().all(|tag_set| tag_set.contains(entity))
3114 && !exclude_tags.iter().any(|tag_set| tag_set.contains(entity))
3115 })
3116 .collect();
3117
3118 for &table_index in &table_indices {
3119 let table = &mut self.tables[table_index];
3120 if table.mask & exclude != 0 {
3121 continue;
3122 }
3123 for idx in 0..table.entity_indices.len() {
3124 let entity = table.entity_indices[idx];
3125 if matching_entities.contains(&entity) {
3126 f(entity, table, idx);
3127 }
3128 }
3129 }
3130 } else {
3131 for &table_index in &table_indices {
3132 let table = &mut self.tables[table_index];
3133 if table.mask & exclude != 0 {
3134 continue;
3135 }
3136 for idx in 0..table.entity_indices.len() {
3137 let entity = table.entity_indices[idx];
3138 f(entity, table, idx);
3139 }
3140 }
3141 }
3142 }
3143
3144 #[cfg(not(target_family = "wasm"))]
3145 #[inline]
3146 pub fn par_for_each_mut_with_tags<F>(
3147 &mut self,
3148 include: u64,
3149 exclude: u64,
3150 include_tags: &[&std::collections::HashSet<$crate::Entity>],
3151 exclude_tags: &[&std::collections::HashSet<$crate::Entity>],
3152 f: F,
3153 )
3154 where
3155 F: Fn($crate::Entity, &mut [<$world ComponentArrays>], usize) + Send + Sync,
3156 {
3157 use $crate::rayon::prelude::*;
3158
3159 let table_indices: Vec<usize> = self.get_cached_tables(include).to_vec();
3160 let table_index_set: std::collections::HashSet<usize> = table_indices.iter().copied().collect();
3161 let has_tag_filter = !include_tags.is_empty() || !exclude_tags.is_empty();
3162
3163 if has_tag_filter {
3164 let matching_entities: std::collections::HashSet<$crate::Entity> = table_indices
3165 .iter()
3166 .filter_map(|&idx| self.tables.get(idx))
3167 .filter(|table| table.mask & exclude == 0)
3168 .flat_map(|table| table.entity_indices.iter().copied())
3169 .filter(|entity| {
3170 include_tags.iter().all(|tag_set| tag_set.contains(entity))
3171 && !exclude_tags.iter().any(|tag_set| tag_set.contains(entity))
3172 })
3173 .collect();
3174
3175 self.tables
3176 .par_iter_mut()
3177 .enumerate()
3178 .filter(|(idx, table)| table_index_set.contains(idx) && table.mask & exclude == 0)
3179 .for_each(|(_, table)| {
3180 for idx in 0..table.entity_indices.len() {
3181 let entity = table.entity_indices[idx];
3182 if matching_entities.contains(&entity) {
3183 f(entity, table, idx);
3184 }
3185 }
3186 });
3187 } else {
3188 self.tables
3189 .par_iter_mut()
3190 .enumerate()
3191 .filter(|(idx, table)| table_index_set.contains(idx) && table.mask & exclude == 0)
3192 .for_each(|(_, table)| {
3193 for idx in 0..table.entity_indices.len() {
3194 let entity = table.entity_indices[idx];
3195 f(entity, table, idx);
3196 }
3197 });
3198 }
3199 }
3200 }
3201
3202 #[allow(unused)]
3203 pub struct [<$world QueryBuilder>]<'a> {
3204 world: &'a $world,
3205 include: u64,
3206 exclude: u64,
3207 }
3208
3209 #[allow(unused)]
3210 impl<'a> [<$world QueryBuilder>]<'a> {
3211 pub fn new(world: &'a $world) -> Self {
3212 Self {
3213 world,
3214 include: 0,
3215 exclude: 0,
3216 }
3217 }
3218
3219 pub fn with(mut self, mask: u64) -> Self {
3220 self.include |= mask;
3221 self
3222 }
3223
3224 pub fn without(mut self, mask: u64) -> Self {
3225 self.exclude |= mask;
3226 self
3227 }
3228
3229 pub fn iter<F>(self, f: F)
3230 where
3231 F: FnMut($crate::Entity, &[<$world ComponentArrays>], usize),
3232 {
3233 self.world.for_each(self.include, self.exclude, f);
3234 }
3235 }
3236
3237 #[allow(unused)]
3238 pub struct [<$world QueryBuilderMut>]<'a> {
3239 world: &'a mut $world,
3240 include: u64,
3241 exclude: u64,
3242 }
3243
3244 #[allow(unused)]
3245 impl<'a> [<$world QueryBuilderMut>]<'a> {
3246 pub fn new(world: &'a mut $world) -> Self {
3247 Self {
3248 world,
3249 include: 0,
3250 exclude: 0,
3251 }
3252 }
3253
3254 pub fn with(mut self, mask: u64) -> Self {
3255 self.include |= mask;
3256 self
3257 }
3258
3259 pub fn without(mut self, mask: u64) -> Self {
3260 self.exclude |= mask;
3261 self
3262 }
3263
3264 pub fn iter<F>(self, f: F)
3265 where
3266 F: FnMut($crate::Entity, &mut [<$world ComponentArrays>], usize),
3267 {
3268 self.world.for_each_mut(self.include, self.exclude, f);
3269 }
3270 }
3271
3272 #[allow(unused)]
3273 impl $world {
3274 pub fn query(&self) -> [<$world QueryBuilder>]<'_> {
3275 [<$world QueryBuilder>]::new(self)
3276 }
3277
3278 pub fn query_mut(&mut self) -> [<$world QueryBuilderMut>]<'_> {
3279 [<$world QueryBuilderMut>]::new(self)
3280 }
3281
3282 $(
3283 $(#[$comp_attr])*
3284 $crate::paste::paste! {
3285 pub fn [<iter_ $name>]<F>(&self, mut f: F)
3286 where
3287 F: FnMut($crate::Entity, &$type),
3288 {
3289 self.for_each($mask, 0, |entity, table, idx| {
3290 f(entity, &table.$name[idx]);
3291 });
3292 }
3293
3294 pub fn [<iter_ $name _mut>]<F>(&mut self, mut f: F)
3295 where
3296 F: FnMut($crate::Entity, &mut $type),
3297 {
3298 self.for_each_mut($mask, 0, |entity, table, idx| {
3299 f(entity, &mut table.$name[idx]);
3300 });
3301 }
3302 }
3303 )*
3304 }
3305
3306 pub struct [<$world EntityQueryIter>]<'a> {
3307 tables: &'a [[<$world ComponentArrays>]],
3308 mask: u64,
3309 table_index: usize,
3310 array_index: usize,
3311 }
3312
3313 impl<'a> Iterator for [<$world EntityQueryIter>]<'a> {
3314 type Item = $crate::Entity;
3315
3316 fn next(&mut self) -> Option<Self::Item> {
3317 loop {
3318 if self.table_index >= self.tables.len() {
3319 return None;
3320 }
3321 let table = &self.tables[self.table_index];
3322 if table.mask & self.mask != self.mask {
3323 self.table_index += 1;
3324 self.array_index = 0;
3325 continue;
3326 }
3327 if self.array_index >= table.entity_indices.len() {
3328 self.table_index += 1;
3329 self.array_index = 0;
3330 continue;
3331 }
3332 let entity = table.entity_indices[self.array_index];
3333 self.array_index += 1;
3334 return Some(entity);
3335 }
3336 }
3337
3338 fn size_hint(&self) -> (usize, Option<usize>) {
3339 let mut remaining = 0;
3340 for table_idx in self.table_index..self.tables.len() {
3341 let table = &self.tables[table_idx];
3342 if table.mask & self.mask != self.mask {
3343 continue;
3344 }
3345 if table_idx == self.table_index {
3346 remaining += table.entity_indices.len().saturating_sub(self.array_index);
3347 } else {
3348 remaining += table.entity_indices.len();
3349 }
3350 }
3351 (remaining, Some(remaining))
3352 }
3353 }
3354
3355 pub struct [<$world ChangedEntityQueryIter>]<'a> {
3356 tables: &'a [[<$world ComponentArrays>]],
3357 mask: u64,
3358 since_tick: u32,
3359 table_index: usize,
3360 array_index: usize,
3361 }
3362
3363 impl<'a> Iterator for [<$world ChangedEntityQueryIter>]<'a> {
3364 type Item = $crate::Entity;
3365
3366 fn next(&mut self) -> Option<Self::Item> {
3367 loop {
3368 if self.table_index >= self.tables.len() {
3369 return None;
3370 }
3371 let table = &self.tables[self.table_index];
3372 if table.mask & self.mask != self.mask {
3373 self.table_index += 1;
3374 self.array_index = 0;
3375 continue;
3376 }
3377 if self.array_index >= table.entity_indices.len() {
3378 self.table_index += 1;
3379 self.array_index = 0;
3380 continue;
3381 }
3382 let idx = self.array_index;
3383 self.array_index += 1;
3384
3385 let mut changed = false;
3386 $(
3387 $(#[$comp_attr])*
3388 {
3389 if self.mask & $mask != 0 && table.mask & $mask != 0 && table.[<$name _changed>][idx] > self.since_tick {
3390 changed = true;
3391 }
3392 }
3393 )*
3394
3395 if changed {
3396 return Some(table.entity_indices[idx]);
3397 }
3398 }
3399 }
3400 }
3401
3402 $(
3403 $(#[$comp_attr])*
3404 $crate::paste::paste! {
3405 pub struct [<$mask:camel QueryIter>]<'a> {
3406 tables: &'a [[<$world ComponentArrays>]],
3407 table_index: usize,
3408 array_index: usize,
3409 }
3410
3411 impl<'a> Iterator for [<$mask:camel QueryIter>]<'a> {
3412 type Item = &'a $type;
3413
3414 fn next(&mut self) -> Option<Self::Item> {
3415 loop {
3416 if self.table_index >= self.tables.len() {
3417 return None;
3418 }
3419 let table = &self.tables[self.table_index];
3420 if table.mask & $mask == 0 {
3421 self.table_index += 1;
3422 self.array_index = 0;
3423 continue;
3424 }
3425 if self.array_index >= table.$name.len() {
3426 self.table_index += 1;
3427 self.array_index = 0;
3428 continue;
3429 }
3430 let component = &table.$name[self.array_index];
3431 self.array_index += 1;
3432 return Some(component);
3433 }
3434 }
3435
3436 fn size_hint(&self) -> (usize, Option<usize>) {
3437 let mut remaining = 0;
3438 for table_idx in self.table_index..self.tables.len() {
3439 let table = &self.tables[table_idx];
3440 if table.mask & $mask == 0 {
3441 continue;
3442 }
3443 if table_idx == self.table_index {
3444 remaining += table.$name.len().saturating_sub(self.array_index);
3445 } else {
3446 remaining += table.$name.len();
3447 }
3448 }
3449 (remaining, Some(remaining))
3450 }
3451 }
3452 }
3453 )*
3454
3455 fn [<get_component_index_ $world:snake>](mask: u64) -> Option<usize> {
3456 match mask {
3457 $($(#[$comp_attr])* $mask => Some([<$world Component>]::$mask as _),)*
3458 _ => None,
3459 }
3460 }
3461
3462 fn [<remove_from_table_ $world:snake>](arrays: &mut [<$world ComponentArrays>], index: usize) -> Option<$crate::Entity> {
3463 let last_index = arrays.entity_indices.len() - 1;
3464 let mut swapped_entity = None;
3465 if index < last_index {
3466 swapped_entity = Some(arrays.entity_indices[last_index]);
3467 }
3468 $(
3469 $(#[$comp_attr])*
3470 {
3471 if arrays.mask & $mask != 0 {
3472 arrays.$name.swap_remove(index);
3473 arrays.[<$name _changed>].swap_remove(index);
3474 }
3475 }
3476 )*
3477 arrays.entity_indices.swap_remove(index);
3478 swapped_entity
3479 }
3480
3481 fn [<move_entity_ $world:snake>](
3482 world: &mut $world,
3483 entity: $crate::Entity,
3484 from_table: usize,
3485 from_index: usize,
3486 to_table: usize,
3487 ) {
3488 let tick = world.current_tick;
3489 let components = {
3490 let from_table_ref = &mut world.tables[from_table];
3491 (
3492 $(
3493 $(#[$comp_attr])*
3494 {
3495 if from_table_ref.mask & $mask != 0 {
3496 Some(std::mem::take(&mut from_table_ref.$name[from_index]))
3497 } else {
3498 None
3499 }
3500 },
3501 )*
3502 )
3503 };
3504
3505 [<add_to_table_ $world:snake>](&mut world.tables[to_table], entity, components, tick);
3506 let new_index = world.tables[to_table].entity_indices.len() - 1;
3507 [<insert_location_ $world:snake>](&mut world.entity_locations, entity, (to_table, new_index));
3508
3509 if let Some(swapped) = [<remove_from_table_ $world:snake>](&mut world.tables[from_table], from_index) {
3510 [<insert_location_ $world:snake>](
3511 &mut world.entity_locations,
3512 swapped,
3513 (from_table, from_index),
3514 );
3515 }
3516 }
3517
3518 fn [<get_location_ $world:snake>](locations: &$crate::EntityLocations, entity: $crate::Entity) -> Option<(usize, usize)> {
3519 let location = locations.get(entity.id)?;
3520 if !location.allocated || location.generation != entity.generation {
3521 return None;
3522 }
3523 Some((location.table_index as usize, location.array_index as usize))
3524 }
3525
3526 fn [<insert_location_ $world:snake>](
3527 locations: &mut $crate::EntityLocations,
3528 entity: $crate::Entity,
3529 location: (usize, usize),
3530 ) {
3531 locations.insert(entity.id, $crate::EntityLocation {
3532 generation: entity.generation,
3533 table_index: location.0 as u32,
3534 array_index: location.1 as u32,
3535 allocated: true,
3536 });
3537 }
3538
3539 fn [<add_to_table_ $world:snake>](
3540 arrays: &mut [<$world ComponentArrays>],
3541 entity: $crate::Entity,
3542 components: ( $($(#[$comp_attr])* Option<$type>,)* ),
3543 tick: u32,
3544 ) {
3545 let ($($name,)*) = components;
3546 $(
3547 $(#[$comp_attr])*
3548 {
3549 if arrays.mask & $mask != 0 {
3550 if let Some(component) = $name {
3551 arrays.$name.push(component);
3552 } else {
3553 arrays.$name.push(<$type>::default());
3554 }
3555 arrays.[<$name _changed>].push(tick);
3556 }
3557 }
3558 )*
3559 arrays.entity_indices.push(entity);
3560 }
3561
3562 fn [<get_or_create_table_ $world:snake>](world: &mut $world, mask: u64) -> usize {
3563 if let Some(&index) = world.table_lookup.get(&mask) {
3564 return index;
3565 }
3566
3567 let table_index = world.tables.len();
3568 world.tables.push([<$world ComponentArrays>] {
3569 mask,
3570 ..Default::default()
3571 });
3572 world.table_edges.push([<$world TableEdges>]::default());
3573 world.table_lookup.insert(mask, table_index);
3574
3575 world.invalidate_query_cache_for_table(mask, table_index);
3576
3577 for comp_mask in [$($(#[$comp_attr])* $mask,)*] {
3578 if let Some(comp_idx) = [<get_component_index_ $world:snake>](comp_mask) {
3579 for (idx, table) in world.tables.iter().enumerate() {
3580 if table.mask | comp_mask == mask {
3581 world.table_edges[idx].add_edges[comp_idx] = Some(table_index);
3582 }
3583 if table.mask & !comp_mask == mask {
3584 world.table_edges[idx].remove_edges[comp_idx] = Some(table_index);
3585 }
3586 }
3587 }
3588 }
3589
3590 table_index
3591 }
3592 }
3593 };
3594}
3595
3596#[macro_export]
3597macro_rules! ecs_multi_impl {
3598 (
3599 $ecs:ident {
3600 $($world_name:ident {
3601 $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
3602 })+
3603 }
3604 Tags {
3605 $($tag_name:ident => $tag_mask:ident),* $(,)?
3606 }
3607 Events {
3608 $($event_name:ident: $event_type:ty),* $(,)?
3609 }
3610 $resources:ident {
3611 $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
3612 }
3613 ) => {
3614 $(
3615 $crate::ecs_world_impl! {
3616 $world_name {
3617 $($(#[$comp_attr])* $name: $type => $mask),*
3618 }
3619 }
3620 )+
3621
3622 #[derive(Default)]
3623 pub struct $resources {
3624 $($(#[$attr])* pub $resource_name: $resource_type,)*
3625 }
3626
3627 $crate::paste::paste! {
3628 #[allow(unused)]
3629 #[derive(Default, Debug, Clone)]
3630 pub struct EntityBuilder {
3631 $($(
3632 $(#[$comp_attr])* $name: Option<$type>,
3633 )*)+
3634 }
3635
3636 #[allow(unused)]
3637 impl EntityBuilder {
3638 pub fn new() -> Self {
3639 Self::default()
3640 }
3641
3642 $($(
3643 $(#[$comp_attr])*
3644 pub fn [<with_ $name>](mut self, value: $type) -> Self {
3645 self.$name = Some(value);
3646 self
3647 }
3648 )*)+
3649
3650 pub fn spawn(self, ecs: &mut $ecs, instances: usize) -> Vec<$crate::Entity> {
3651 let mut entities = Vec::with_capacity(instances);
3652 for _ in 0..instances {
3653 entities.push(ecs.allocator.allocate());
3654 }
3655
3656 $(
3657 {
3658 let mut mask: u64 = 0;
3659 $(
3660 $(#[$comp_attr])*
3661 if self.$name.is_some() {
3662 mask |= $mask;
3663 }
3664 )*
3665 if mask != 0 {
3666 let last_entity_index = entities.len().saturating_sub(1);
3667 for (entity_index, entity) in entities.iter().enumerate() {
3668 ecs.[<$world_name:snake>].add_components(*entity, mask);
3669 if entity_index == last_entity_index {
3670 $(
3671 $(#[$comp_attr])*
3672 if let Some(component) = self.$name {
3673 ecs.[<$world_name:snake>].[<set_ $name>](*entity, component);
3674 }
3675 )*
3676 break;
3677 } else {
3678 $(
3679 $(#[$comp_attr])*
3680 if let Some(ref component) = self.$name {
3681 ecs.[<$world_name:snake>].[<set_ $name>](*entity, component.clone());
3682 }
3683 )*
3684 }
3685 }
3686 }
3687 }
3688 )+
3689
3690 entities
3691 }
3692 }
3693
3694 pub enum Command {
3695 Spawn { count: usize },
3696 Despawn { entities: Vec<$crate::Entity> },
3697 $($(
3698 $(#[$comp_attr])*
3699 [<$world_name Set $mask:camel>] { entity: $crate::Entity, value: $type },
3700 $(#[$comp_attr])*
3701 [<$world_name AddComponents $mask:camel>] { entity: $crate::Entity },
3702 $(#[$comp_attr])*
3703 [<$world_name RemoveComponents $mask:camel>] { entity: $crate::Entity },
3704 )*)+
3705 $(
3706 [<Add $tag_mask:camel>] { entity: $crate::Entity },
3707 [<Remove $tag_mask:camel>] { entity: $crate::Entity },
3708 )*
3709 }
3710
3711 #[allow(unused)]
3712 pub struct $ecs {
3713 $(pub [<$world_name:snake>]: $world_name,)+
3714 allocator: $crate::EntityAllocator,
3715 pub resources: $resources,
3716 $(pub $tag_name: std::collections::HashSet<$crate::Entity>,)*
3717 command_buffer: Vec<Command>,
3718 $($event_name: $crate::EventQueue<$event_type>,)*
3719 }
3720
3721 impl Default for $ecs {
3722 fn default() -> Self {
3723 Self {
3724 $([<$world_name:snake>]: $world_name::default(),)+
3725 allocator: $crate::EntityAllocator::default(),
3726 resources: $resources::default(),
3727 $(
3728 $tag_name: std::collections::HashSet::default(),
3729 )*
3730 command_buffer: Vec::default(),
3731 $(
3732 $event_name: $crate::EventQueue::new(),
3733 )*
3734 }
3735 }
3736 }
3737
3738 #[allow(unused)]
3739 impl $ecs {
3740 pub fn spawn(&mut self) -> $crate::Entity {
3741 self.allocator.allocate()
3742 }
3743
3744 pub fn spawn_count(&mut self, count: usize) -> Vec<$crate::Entity> {
3745 let mut entities = Vec::with_capacity(count);
3746 for _ in 0..count {
3747 entities.push(self.allocator.allocate());
3748 }
3749 entities
3750 }
3751
3752 pub fn despawn(&mut self, entity: $crate::Entity) {
3753 $(self.[<$world_name:snake>].remove_entity(entity);)+
3754 $(self.$tag_name.remove(&entity);)*
3755 self.allocator.deallocate(entity);
3756 }
3757
3758 pub fn despawn_entities(&mut self, entities: &[$crate::Entity]) {
3759 for &entity in entities {
3760 self.despawn(entity);
3761 }
3762 }
3763
3764 $(
3765 pub fn [<add_ $tag_name>](&mut self, entity: $crate::Entity) {
3766 self.$tag_name.insert(entity);
3767 }
3768
3769 pub fn [<remove_ $tag_name>](&mut self, entity: $crate::Entity) -> bool {
3770 self.$tag_name.remove(&entity)
3771 }
3772
3773 pub fn [<has_ $tag_name>](&self, entity: $crate::Entity) -> bool {
3774 self.$tag_name.contains(&entity)
3775 }
3776
3777 pub fn [<query_ $tag_name>](&self) -> impl Iterator<Item = $crate::Entity> + '_ {
3778 self.$tag_name.iter().copied()
3779 }
3780 )*
3781
3782 $(
3783 pub fn [<send_ $event_name>](&mut self, event: $event_type) {
3784 self.$event_name.send(event);
3785 }
3786
3787 pub fn [<read_ $event_name>](&self) -> impl Iterator<Item = &$event_type> {
3788 self.$event_name.read()
3789 }
3790
3791 pub fn [<drain_ $event_name>](&mut self) -> impl Iterator<Item = $event_type> + '_ {
3792 self.$event_name.drain()
3793 }
3794
3795 pub fn [<clear_ $event_name>](&mut self) {
3796 self.$event_name.clear();
3797 }
3798
3799 pub fn [<update_ $event_name>](&mut self) {
3800 self.$event_name.update();
3801 }
3802
3803 pub fn [<len_ $event_name>](&self) -> usize {
3804 self.$event_name.len()
3805 }
3806
3807 pub fn [<is_empty_ $event_name>](&self) -> bool {
3808 self.$event_name.is_empty()
3809 }
3810
3811 pub fn [<peek_ $event_name>](&self) -> Option<&$event_type> {
3812 self.$event_name.peek()
3813 }
3814
3815 pub fn [<collect_ $event_name>](&self) -> Vec<$event_type>
3816 where
3817 $event_type: Clone,
3818 {
3819 self.$event_name.read().cloned().collect()
3820 }
3821 )*
3822
3823 fn update_events(&mut self) {
3824 $(
3825 self.$event_name.update();
3826 )*
3827 }
3828
3829 pub fn step(&mut self) {
3830 self.update_events();
3831 $(
3832 self.[<$world_name:snake>].last_tick = self.[<$world_name:snake>].current_tick;
3833 self.[<$world_name:snake>].current_tick += 1;
3834 )+
3835 }
3836
3837 pub fn queue_spawn(&mut self, count: usize) {
3838 self.command_buffer.push(Command::Spawn { count });
3839 }
3840
3841 pub fn queue_despawn_entity(&mut self, entity: $crate::Entity) {
3842 self.command_buffer.push(Command::Despawn { entities: vec![entity] });
3843 }
3844
3845 pub fn queue_despawn_entities(&mut self, entities: Vec<$crate::Entity>) {
3846 self.command_buffer.push(Command::Despawn { entities });
3847 }
3848
3849 $($(
3850 $(#[$comp_attr])*
3851 pub fn [<queue_set_ $name>](&mut self, entity: $crate::Entity, value: $type) {
3852 self.command_buffer.push(Command::[<$world_name Set $mask:camel>] { entity, value });
3853 }
3854
3855 $(#[$comp_attr])*
3856 pub fn [<queue_add_ $name>](&mut self, entity: $crate::Entity) {
3857 self.command_buffer.push(Command::[<$world_name AddComponents $mask:camel>] { entity });
3858 }
3859
3860 $(#[$comp_attr])*
3861 pub fn [<queue_remove_ $name>](&mut self, entity: $crate::Entity) {
3862 self.command_buffer.push(Command::[<$world_name RemoveComponents $mask:camel>] { entity });
3863 }
3864 )*)+
3865
3866 $(
3867 pub fn [<queue_add_ $tag_name>](&mut self, entity: $crate::Entity) {
3868 self.command_buffer.push(Command::[<Add $tag_mask:camel>] { entity });
3869 }
3870
3871 pub fn [<queue_remove_ $tag_name>](&mut self, entity: $crate::Entity) {
3872 self.command_buffer.push(Command::[<Remove $tag_mask:camel>] { entity });
3873 }
3874 )*
3875
3876 pub fn apply_commands(&mut self) {
3877 let commands = std::mem::take(&mut self.command_buffer);
3878 for command in commands {
3879 match command {
3880 Command::Spawn { count } => {
3881 self.spawn_count(count);
3882 }
3883 Command::Despawn { entities } => {
3884 self.despawn_entities(&entities);
3885 }
3886 $($(
3887 $(#[$comp_attr])*
3888 Command::[<$world_name Set $mask:camel>] { entity, value } => {
3889 self.[<$world_name:snake>].[<set_ $name>](entity, value);
3890 }
3891 $(#[$comp_attr])*
3892 Command::[<$world_name AddComponents $mask:camel>] { entity } => {
3893 self.[<$world_name:snake>].add_components(entity, $mask);
3894 }
3895 $(#[$comp_attr])*
3896 Command::[<$world_name RemoveComponents $mask:camel>] { entity } => {
3897 self.[<$world_name:snake>].remove_components(entity, $mask);
3898 }
3899 )*)+
3900 $(
3901 Command::[<Add $tag_mask:camel>] { entity } => {
3902 self.[<add_ $tag_name>](entity);
3903 }
3904 Command::[<Remove $tag_mask:camel>] { entity } => {
3905 self.[<remove_ $tag_name>](entity);
3906 }
3907 )*
3908 }
3909 }
3910 }
3911
3912 pub fn command_count(&self) -> usize {
3913 self.command_buffer.len()
3914 }
3915
3916 pub fn clear_commands(&mut self) {
3917 self.command_buffer.clear();
3918 }
3919 }
3920 }
3921 };
3922}
3923
3924#[macro_export]
3925macro_rules! table_has_components {
3926 ($table:expr, $mask:expr) => {
3927 $table.mask & $mask == $mask
3928 };
3929}
3930
3931#[cfg(test)]
3932mod tests {
3933 use super::*;
3934 use std::collections::HashSet;
3935
3936 ecs! {
3937 World {
3938 position: Position => POSITION,
3939 velocity: Velocity => VELOCITY,
3940 health: Health => HEALTH,
3941 parent: Parent => PARENT,
3942 node: Node => NODE,
3943 }
3944 Tags {
3945 player => PLAYER,
3946 enemy => ENEMY,
3947 active => ACTIVE,
3948 }
3949 Resources {
3950 _delta_time: f32,
3951 }
3952 }
3953
3954 use components::*;
3955 mod components {
3956 use super::*;
3957
3958 #[derive(Default, Debug, Copy, Clone, PartialEq)]
3959 pub struct Parent(pub Entity);
3960
3961 #[derive(Default, Debug, Clone, PartialEq)]
3962 pub struct Node {
3963 pub id: Entity,
3964 pub parent: Option<Entity>,
3965 pub children: Vec<Entity>,
3966 }
3967
3968 #[derive(Default, Debug, Clone)]
3969 pub struct Position {
3970 pub x: f32,
3971 pub y: f32,
3972 }
3973
3974 #[derive(Default, Debug, Clone)]
3975 pub struct Velocity {
3976 pub x: f32,
3977 pub y: f32,
3978 }
3979
3980 #[derive(Default, Debug, Clone)]
3981 pub struct Health {
3982 pub value: f32,
3983 }
3984 }
3985
3986 mod systems {
3987 use super::*;
3988
3989 pub fn run_systems(world: &mut World, dt: f32) {
3990 world.tables.iter_mut().for_each(|table| {
3991 if super::table_has_components!(table, POSITION | VELOCITY | HEALTH) {
3992 update_positions_system(&mut table.position, &table.velocity, dt);
3993 }
3994 if super::table_has_components!(table, HEALTH) {
3995 health_system(&mut table.health);
3996 }
3997 });
3998 }
3999
4000 #[inline]
4001 pub fn update_positions_system(
4002 positions: &mut [Position],
4003 velocities: &[Velocity],
4004 dt: f32,
4005 ) {
4006 positions
4007 .iter_mut()
4008 .zip(velocities.iter())
4009 .for_each(|(pos, vel)| {
4010 pos.x += vel.x * dt;
4011 pos.y += vel.y * dt;
4012 });
4013 }
4014
4015 #[inline]
4016 pub fn health_system(health: &mut [Health]) {
4017 health.iter_mut().for_each(|health| {
4018 health.value *= 0.98;
4019 });
4020 }
4021 }
4022
4023 fn setup_test_world() -> (World, Entity) {
4024 let mut world = World::default();
4025 let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4026
4027 if let Some(pos) = world.get_position_mut(entity) {
4028 pos.x = 1.0;
4029 pos.y = 2.0;
4030 }
4031 if let Some(vel) = world.get_velocity_mut(entity) {
4032 vel.x = 3.0;
4033 vel.y = 4.0;
4034 }
4035
4036 (world, entity)
4037 }
4038
4039 #[test]
4040 fn test_spawn_entities() {
4041 let mut world = World::default();
4042 let entities = world.spawn_entities(POSITION | VELOCITY, 3);
4043
4044 assert_eq!(entities.len(), 3);
4045 assert_eq!(world.get_all_entities().len(), 3);
4046
4047 for entity in entities {
4048 assert!(world.get_position(entity).is_some());
4049 assert!(world.get_velocity(entity).is_some());
4050 assert!(world.get_health(entity).is_none());
4051 }
4052 }
4053
4054 #[test]
4055 fn test_component_access() {
4056 let (mut world, entity) = setup_test_world();
4057
4058 let pos = world.get_position(entity).unwrap();
4059 assert_eq!(pos.x, 1.0);
4060 assert_eq!(pos.y, 2.0);
4061
4062 if let Some(pos) = world.get_position_mut(entity) {
4063 pos.x = 5.0;
4064 }
4065
4066 let pos = world.get_position(entity).unwrap();
4067 assert_eq!(pos.x, 5.0);
4068 }
4069
4070 #[test]
4071 fn test_modify_component() {
4072 let (mut world, entity) = setup_test_world();
4073
4074 let pos = world.get_position(entity).unwrap();
4075 assert_eq!(pos.x, 1.0);
4076 assert_eq!(pos.y, 2.0);
4077
4078 let old_x = world.modify_position(entity, |pos| {
4079 let old = pos.x;
4080 pos.x = 10.0;
4081 pos.y = 20.0;
4082 old
4083 });
4084 assert_eq!(old_x, Some(1.0));
4085
4086 let pos = world.get_position(entity).unwrap();
4087 assert_eq!(pos.x, 10.0);
4088 assert_eq!(pos.y, 20.0);
4089
4090 let invalid_entity = Entity {
4091 id: 9999,
4092 generation: 0,
4093 };
4094 let result = world.modify_position(invalid_entity, |pos| pos.x = 100.0);
4095 assert!(result.is_none());
4096
4097 let entity_no_health = world.spawn_entities(POSITION, 1)[0];
4098 let result = world.modify_health(entity_no_health, |h| h.value = 50.0);
4099 assert!(result.is_none());
4100 }
4101
4102 #[test]
4103 fn test_add_remove_components() {
4104 let (mut world, entity) = setup_test_world();
4105
4106 assert!(world.get_health(entity).is_none());
4107
4108 world.add_components(entity, HEALTH);
4109 assert!(world.get_health(entity).is_some());
4110
4111 world.remove_components(entity, HEALTH);
4112 assert!(world.get_health(entity).is_none());
4113 }
4114
4115 #[test]
4116 fn test_component_mask() {
4117 let (mut world, entity) = setup_test_world();
4118
4119 let mask = world.component_mask(entity).unwrap();
4120 assert_eq!(mask, POSITION | VELOCITY);
4121
4122 world.add_components(entity, HEALTH);
4123 let mask = world.component_mask(entity).unwrap();
4124 assert_eq!(mask, POSITION | VELOCITY | HEALTH);
4125 }
4126
4127 #[test]
4128 fn test_query_entities() {
4129 let mut world = World::default();
4130
4131 let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4132 let _e2 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4133 let e3 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4134
4135 let pos_vel: Vec<_> = world.query_entities(POSITION | VELOCITY).collect();
4136 let pos_health: Vec<_> = world.query_entities(POSITION | HEALTH).collect();
4137 let all: Vec<_> = world.query_entities(POSITION | VELOCITY | HEALTH).collect();
4138
4139 assert_eq!(pos_vel.len(), 2);
4140 assert_eq!(pos_health.len(), 2);
4141 assert_eq!(all.len(), 1);
4142
4143 let pos_vel: HashSet<_> = pos_vel.into_iter().collect();
4144 assert!(pos_vel.contains(&e1));
4145 assert!(pos_vel.contains(&e3));
4146
4147 assert_eq!(all[0], e3);
4148 }
4149
4150 #[test]
4151 fn test_query_first_entity() {
4152 let mut world = World::default();
4153
4154 let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4155 let e2 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4156
4157 let first = world.query_first_entity(POSITION | VELOCITY).unwrap();
4158 assert!(first == e1 || first == e2);
4159
4160 assert!(world.query_first_entity(HEALTH).is_some());
4161 assert!(
4162 world
4163 .query_first_entity(POSITION | VELOCITY | HEALTH)
4164 .is_some()
4165 );
4166 }
4167
4168 #[test]
4169 fn test_despawn_entities() {
4170 let mut world = World::default();
4171
4172 let entities = world.spawn_entities(POSITION | VELOCITY, 3);
4173 assert_eq!(world.get_all_entities().len(), 3);
4174
4175 let despawned = world.despawn_entities(&[entities[1]]);
4176 assert_eq!(despawned.len(), 1);
4177 assert_eq!(world.get_all_entities().len(), 2);
4178
4179 assert!(world.get_position(entities[1]).is_none());
4180
4181 assert!(world.get_position(entities[0]).is_some());
4182 assert!(world.get_position(entities[2]).is_some());
4183 }
4184
4185 #[test]
4186 fn test_parallel_systems() {
4187 let mut world = World::default();
4188
4189 let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4190
4191 if let Some(pos) = world.get_position_mut(entity) {
4192 pos.x = 0.0;
4193 pos.y = 0.0;
4194 }
4195 if let Some(vel) = world.get_velocity_mut(entity) {
4196 vel.x = 1.0;
4197 vel.y = 1.0;
4198 }
4199 if let Some(health) = world.get_health_mut(entity) {
4200 health.value = 100.0;
4201 }
4202
4203 systems::run_systems(&mut world, 1.0);
4204
4205 let pos = world.get_position(entity).unwrap();
4206 let health = world.get_health(entity).unwrap();
4207
4208 assert_eq!(pos.x, 1.0);
4209 assert_eq!(pos.y, 1.0);
4210 assert!(health.value < 100.0);
4211 }
4212
4213 #[test]
4214 fn test_add_components() {
4215 let (mut world, entity) = setup_test_world();
4216
4217 assert!(world.get_health(entity).is_none());
4218
4219 world.add_components(entity, HEALTH);
4220 assert!(world.get_health(entity).is_some());
4221
4222 world.remove_components(entity, HEALTH);
4223 assert!(world.get_health(entity).is_none());
4224 }
4225
4226 #[test]
4227 fn test_multiple_component_addition() {
4228 let mut world = World::default();
4229 let entity = world.spawn_entities(POSITION, 1)[0];
4230
4231 world.add_components(entity, VELOCITY | HEALTH);
4232
4233 assert!(world.get_position(entity).is_some());
4234 assert!(world.get_velocity(entity).is_some());
4235 assert!(world.get_health(entity).is_some());
4236
4237 if let Some(pos) = world.get_position_mut(entity) {
4238 pos.x = 1.0;
4239 }
4240 world.add_components(entity, VELOCITY);
4241 assert_eq!(world.get_position(entity).unwrap().x, 1.0);
4242 }
4243
4244 #[test]
4245 fn test_component_chain_addition() {
4246 let mut world = World::default();
4247 let entity = world.spawn_entities(POSITION, 1)[0];
4248
4249 if let Some(pos) = world.get_position_mut(entity) {
4250 pos.x = 1.0;
4251 }
4252
4253 world.add_components(entity, VELOCITY);
4254 world.add_components(entity, HEALTH);
4255
4256 assert_eq!(world.get_position(entity).unwrap().x, 1.0);
4257 }
4258
4259 #[test]
4260 fn test_component_removal_order() {
4261 let mut world = World::default();
4262 let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4263
4264 world.remove_components(entity, VELOCITY);
4265 world.remove_components(entity, HEALTH);
4266 assert!(world.get_position(entity).is_some());
4267 assert!(world.get_velocity(entity).is_none());
4268 assert!(world.get_health(entity).is_none());
4269 }
4270
4271 #[test]
4272 fn test_edge_cases() {
4273 let mut world = World::default();
4274
4275 let empty = world.spawn_entities(0, 1)[0];
4276
4277 world.add_components(empty, POSITION);
4278 assert!(world.get_position(empty).is_some());
4279
4280 world.add_components(empty, POSITION);
4281 world.add_components(empty, POSITION);
4282
4283 world.remove_components(empty, VELOCITY);
4284
4285 world.remove_components(empty, POSITION);
4286 assert_eq!(world.component_mask(empty).unwrap(), 0);
4287
4288 let invalid = Entity {
4289 id: 9999,
4290 generation: 0,
4291 };
4292 assert!(!world.add_components(invalid, POSITION));
4293 }
4294
4295 #[test]
4296 fn test_component_data_integrity() {
4297 let mut world = World::default();
4298 let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4299
4300 {
4301 let pos = world.get_position_mut(entity).unwrap();
4302 pos.x = 1.0;
4303 pos.y = 2.0;
4304 let vel = world.get_velocity_mut(entity).unwrap();
4305 vel.x = 3.0;
4306 vel.y = 4.0;
4307 }
4308
4309 world.add_components(entity, HEALTH);
4310 world.remove_components(entity, HEALTH);
4311 world.add_components(entity, HEALTH);
4312
4313 let pos = world.get_position(entity).unwrap();
4314 let vel = world.get_velocity(entity).unwrap();
4315 assert_eq!(pos.x, 1.0);
4316 assert_eq!(pos.y, 2.0);
4317 assert_eq!(vel.x, 3.0);
4318 assert_eq!(vel.y, 4.0);
4319 }
4320
4321 #[test]
4322 fn test_entity_references_through_moves() {
4323 let mut world = World::default();
4324
4325 let entity1 = world.spawn_entities(POSITION, 1)[0];
4326 let entity2 = world.spawn_entities(POSITION, 1)[0];
4327
4328 world.add_components(entity1, VELOCITY);
4329 if let Some(vel) = world.get_velocity_mut(entity1) {
4330 vel.x = entity2.id as f32;
4331 }
4332
4333 world.add_components(entity2, VELOCITY | HEALTH);
4334
4335 let stored_id = world.get_velocity(entity1).unwrap().x as u32;
4336 let entity2_loc = get_location(&world.entity_locations, entity2);
4337 assert!(entity2_loc.is_some());
4338 assert_eq!(stored_id, entity2.id);
4339 }
4340
4341 #[test]
4342 fn test_table_cleanup_after_despawn() {
4343 let mut world = World::default();
4344
4345 let e1 = world.spawn_entities(POSITION, 1)[0];
4346 let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4347
4348 let initial_tables = world.tables.len();
4349 assert_eq!(initial_tables, 2, "Should have two tables initially");
4350
4351 world.despawn_entities(&[e2]);
4352
4353 assert!(world.get_position(e2).is_none());
4354 assert!(world.get_velocity(e2).is_none());
4355
4356 assert!(world.get_position(e1).is_some());
4357
4358 let remaining: Vec<_> = world.query_entities(POSITION).collect();
4359 assert_eq!(remaining.len(), 1);
4360 assert!(remaining.contains(&e1));
4361
4362 assert!(
4363 world.tables.len() <= initial_tables,
4364 "Should not have more tables than initial state"
4365 );
4366
4367 for table in &world.tables {
4368 for &entity in &table.entity_indices {
4369 assert!(
4370 get_location(&world.entity_locations, entity).is_some(),
4371 "Entity location should be valid for remaining entities"
4372 );
4373 }
4374 }
4375 }
4376
4377 #[test]
4378 fn test_concurrent_entity_references() {
4379 let mut world = World::default();
4380
4381 let entity1 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4382 let entity2 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4383
4384 if let Some(pos) = world.get_position_mut(entity1) {
4385 pos.x = 1.0;
4386 }
4387 if let Some(health) = world.get_health_mut(entity1) {
4388 health.value = 100.0;
4389 }
4390
4391 let id1 = entity1.id;
4392
4393 world.despawn_entities(&[entity1]);
4394
4395 let entity3 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4396 assert_eq!(entity3.id, id1, "Should reuse entity1's ID");
4397 assert_eq!(
4398 entity3.generation,
4399 entity1.generation + 1,
4400 "Should have incremented generation"
4401 );
4402
4403 if let Some(pos) = world.get_position_mut(entity3) {
4404 pos.x = 3.0;
4405 }
4406 if let Some(health) = world.get_health_mut(entity3) {
4407 health.value = 50.0;
4408 }
4409
4410 if let Some(pos) = world.get_position(entity2) {
4411 assert_eq!(pos.x, 0.0, "Entity2's data should be unchanged");
4412 }
4413
4414 if let Some(pos) = world.get_position(entity3) {
4415 assert_eq!(pos.x, 3.0, "Should get entity3's data, not entity1's");
4416 }
4417 assert!(
4418 world.get_position(entity1).is_none(),
4419 "Should not be able to access entity1's old data"
4420 );
4421 }
4422
4423 #[test]
4424 fn test_generational_indices_aba() {
4425 let mut world = World::default();
4426
4427 let entity_a1 = world.spawn_entities(POSITION, 1)[0];
4428 assert_eq!(
4429 entity_a1.generation, 0,
4430 "First use of ID should have generation 0"
4431 );
4432
4433 if let Some(pos) = world.get_position_mut(entity_a1) {
4434 pos.x = 1.0;
4435 pos.y = 1.0;
4436 }
4437
4438 let id = entity_a1.id;
4439
4440 world.despawn_entities(&[entity_a1]);
4441
4442 let entity_a2 = world.spawn_entities(POSITION, 1)[0];
4443 assert_eq!(entity_a2.id, id, "Should reuse the same ID");
4444 assert_eq!(
4445 entity_a2.generation, 1,
4446 "Second use of ID should have generation 1"
4447 );
4448
4449 if let Some(pos) = world.get_position_mut(entity_a2) {
4450 pos.x = 2.0;
4451 pos.y = 2.0;
4452 }
4453
4454 assert!(
4455 world.get_position(entity_a1).is_none(),
4456 "Old reference to entity should be invalid"
4457 );
4458
4459 world.despawn_entities(&[entity_a2]);
4460
4461 let entity_a3 = world.spawn_entities(POSITION, 1)[0];
4462 assert_eq!(entity_a3.id, id, "Should reuse the same ID again");
4463 assert_eq!(
4464 entity_a3.generation, 2,
4465 "Third use of ID should have generation 2"
4466 );
4467
4468 if let Some(pos) = world.get_position_mut(entity_a3) {
4469 pos.x = 3.0;
4470 pos.y = 3.0;
4471 }
4472
4473 assert!(
4474 world.get_position(entity_a1).is_none(),
4475 "First generation reference should be invalid"
4476 );
4477 assert!(
4478 world.get_position(entity_a2).is_none(),
4479 "Second generation reference should be invalid"
4480 );
4481
4482 let pos = world.get_position(entity_a3);
4483 assert!(
4484 pos.is_some(),
4485 "Current generation reference should be valid"
4486 );
4487 let pos = pos.unwrap();
4488 assert_eq!(pos.x, 3.0, "Should have the current generation's data");
4489 assert_eq!(pos.y, 3.0, "Should have the current generation's data");
4490 }
4491
4492 #[test]
4493 fn test_all_entities() {
4494 let mut world = World::default();
4495
4496 let e1 = world.spawn_entities(POSITION, 1)[0];
4497 let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4498 let e3 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4499 let e4 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4500
4501 let all = world.get_all_entities();
4502
4503 assert_eq!(all.len(), 4, "Should have 4 total entities");
4504
4505 assert!(all.contains(&e1), "Missing entity 1");
4506 assert!(all.contains(&e2), "Missing entity 2");
4507 assert!(all.contains(&e3), "Missing entity 3");
4508 assert!(all.contains(&e4), "Missing entity 4");
4509
4510 world.despawn_entities(&[e2, e3]);
4511 let remaining = world.get_all_entities();
4512
4513 assert_eq!(remaining.len(), 2, "Should have 2 entities after despawn");
4514
4515 assert!(remaining.contains(&e1), "Missing entity 1 after despawn");
4516 assert!(remaining.contains(&e4), "Missing entity 4 after despawn");
4517 assert!(!remaining.contains(&e2), "Entity 2 should be despawned");
4518 assert!(!remaining.contains(&e3), "Entity 3 should be despawned");
4519 }
4520
4521 #[test]
4522 fn test_all_entities_empty_world() {
4523 assert!(
4524 World::default().get_all_entities().is_empty(),
4525 "Empty world should return empty vector"
4526 );
4527 }
4528
4529 #[test]
4530 fn test_all_entities_after_table_merges() {
4531 let mut world = World::default();
4532
4533 let e1 = world.spawn_entities(POSITION, 1)[0];
4534 let e2 = world.spawn_entities(VELOCITY, 1)[0];
4535
4536 world.add_components(e1, VELOCITY);
4537 world.add_components(e2, POSITION);
4538
4539 let all = world.get_all_entities();
4540 assert_eq!(
4541 all.len(),
4542 2,
4543 "Should maintain all entities through table merges"
4544 );
4545 assert!(all.contains(&e1), "Should contain first entity after merge");
4546 assert!(
4547 all.contains(&e2),
4548 "Should contain second entity after merge"
4549 );
4550 }
4551
4552 #[test]
4553 fn test_table_transitions() {
4554 let mut world = World::default();
4555
4556 let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4557
4558 println!("Initial mask: {:b}", world.component_mask(entity).unwrap());
4559
4560 let (old_table_idx, _) = get_location(&world.entity_locations, entity).unwrap();
4561
4562 world.add_components(entity, POSITION);
4563
4564 let final_mask = world.component_mask(entity).unwrap();
4565 println!("Final mask: {:b}", final_mask);
4566 let (new_table_idx, _) = get_location(&world.entity_locations, entity).unwrap();
4567
4568 println!(
4569 "Old table index: {}, New table index: {}",
4570 old_table_idx, new_table_idx
4571 );
4572 println!("Tables after operation:");
4573 for (index, table) in world.tables.iter().enumerate() {
4574 println!("Table {}: mask={:b}", index, table.mask);
4575 }
4576
4577 assert_eq!(
4578 final_mask & (POSITION | VELOCITY | HEALTH),
4579 POSITION | VELOCITY | HEALTH,
4580 "Entity should still have all original components"
4581 );
4582 }
4583
4584 #[test]
4585 fn test_real_camera_scenario() {
4586 let mut world = World::default();
4587
4588 let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4589
4590 let query_results: Vec<_> = world.query_entities(POSITION | VELOCITY).collect();
4591 assert!(
4592 query_results.contains(&entity),
4593 "Initial query should match\n\
4594 Entity mask: {:b}\n\
4595 Query mask: {:b}",
4596 world.component_mask(entity).unwrap(),
4597 POSITION | VELOCITY
4598 );
4599
4600 world.add_components(entity, HEALTH);
4601
4602 let query_results: Vec<_> = world.query_entities(POSITION | VELOCITY).collect();
4603 assert!(
4604 query_results.contains(&entity),
4605 "Query should still match after adding component\n\
4606 Entity mask: {:b}\n\
4607 Query mask: {:b}",
4608 world.component_mask(entity).unwrap(),
4609 POSITION | VELOCITY
4610 );
4611 }
4612
4613 #[test]
4614 fn test_query_consistency() {
4615 let mut world = World::default();
4616
4617 let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4618
4619 let query_mask = POSITION | VELOCITY;
4620
4621 let query_results: Vec<_> = world.query_entities(query_mask).collect();
4622 assert!(
4623 query_results.contains(&entity),
4624 "query_entities should find entity with mask {:b} when querying for {:b}",
4625 world.component_mask(entity).unwrap(),
4626 query_mask
4627 );
4628
4629 let first_result = world.query_first_entity(query_mask);
4630 assert!(
4631 first_result.is_some(),
4632 "query_first_entity should find entity with mask {:b} when querying for {:b}",
4633 world.component_mask(entity).unwrap(),
4634 query_mask
4635 );
4636 assert_eq!(
4637 first_result.unwrap(),
4638 entity,
4639 "query_first_entity should find same entity as query_entities"
4640 );
4641
4642 world.add_components(entity, HEALTH);
4643
4644 let query_results: Vec<_> = world.query_entities(query_mask).collect();
4645 assert!(
4646 query_results.contains(&entity),
4647 "query_entities should still find entity after adding component\n\
4648 Entity mask: {:b}\n\
4649 Query mask: {:b}",
4650 world.component_mask(entity).unwrap(),
4651 query_mask
4652 );
4653
4654 let first_result = world.query_first_entity(query_mask);
4655 assert!(
4656 first_result.is_some(),
4657 "query_first_entity should still find entity after adding component\n\
4658 Entity mask: {:b}\n\
4659 Query mask: {:b}",
4660 world.component_mask(entity).unwrap(),
4661 query_mask
4662 );
4663 }
4664
4665 #[test]
4666 fn entity_has_components_test() {
4667 let mut world = World::default();
4668 let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4669 assert!(world.entity_has_components(entity, POSITION | VELOCITY));
4670 assert!(!world.entity_has_components(entity, HEALTH));
4671 }
4672
4673 #[test]
4674 fn test_set_component() {
4675 let mut world = World::default();
4676 let entity = world.spawn_entities(POSITION, 1)[0];
4677 world.set_position(entity, Position { x: 1.0, y: 2.0 });
4678 assert_eq!(world.get_position(entity).unwrap().x, 1.0);
4679 assert_eq!(world.get_position(entity).unwrap().y, 2.0);
4680
4681 world.set_position(entity, Position { x: 3.0, y: 4.0 });
4682 assert_eq!(world.get_position(entity).unwrap().x, 3.0);
4683 assert_eq!(world.get_position(entity).unwrap().y, 4.0);
4684 }
4685
4686 #[test]
4687 fn test_entity_builder() {
4688 let mut world = World::default();
4689 let entities = EntityBuilder::new()
4690 .with_position(Position { x: 1.0, y: 2.0 })
4691 .spawn(&mut world, 2);
4692 assert_eq!(world.get_position(entities[0]).unwrap().x, 1.0);
4693 assert_eq!(world.get_position(entities[1]).unwrap().y, 2.0);
4694 }
4695
4696 #[test]
4697 fn test_query_composition_exclude() {
4698 let mut world = World::default();
4699
4700 let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4701 let e2 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4702 let e3 = world.spawn_entities(POSITION, 1)[0];
4703
4704 let mut count = 0;
4705 world.for_each_mut(POSITION | VELOCITY, HEALTH, |_entity, _table, _idx| {
4706 count += 1;
4707 });
4708
4709 assert_eq!(count, 1);
4710
4711 let mut found_entities = Vec::new();
4712 world.for_each_mut(POSITION | VELOCITY, HEALTH, |entity, _table, _idx| {
4713 found_entities.push(entity);
4714 });
4715
4716 assert!(found_entities.contains(&e1));
4717 assert!(!found_entities.contains(&e2));
4718 assert!(!found_entities.contains(&e3));
4719 }
4720
4721 #[test]
4722 fn test_query_composition_include_only() {
4723 let mut world = World::default();
4724
4725 let e1 = world.spawn_entities(POSITION, 1)[0];
4726 let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4727 let e3 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4728
4729 let mut count = 0;
4730 world.for_each_mut(POSITION | VELOCITY, 0, |_entity, _table, _idx| {
4731 count += 1;
4732 });
4733
4734 assert_eq!(count, 2);
4735
4736 let mut found_entities = Vec::new();
4737 world.for_each_mut(POSITION | VELOCITY, 0, |entity, _table, _idx| {
4738 found_entities.push(entity);
4739 });
4740
4741 assert!(!found_entities.contains(&e1));
4742 assert!(found_entities.contains(&e2));
4743 assert!(found_entities.contains(&e3));
4744 }
4745
4746 #[test]
4747 fn test_change_detection_basic() {
4748 let mut world = World::default();
4749 let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4750
4751 world.step();
4752
4753 world.get_position_mut(entity).unwrap().x = 10.0;
4754
4755 let mut changed_count = 0;
4756 world.for_each_mut_changed(POSITION, 0, |_entity, _table, _idx| {
4757 changed_count += 1;
4758 });
4759
4760 assert_eq!(changed_count, 1);
4761 }
4762
4763 #[test]
4764 fn test_change_detection_unchanged() {
4765 let mut world = World::default();
4766 let e1 = world.spawn_entities(POSITION, 1)[0];
4767 let e2 = world.spawn_entities(POSITION, 1)[0];
4768
4769 world.step();
4770
4771 world.get_position_mut(e1).unwrap().x = 5.0;
4772
4773 let mut changed_entities = Vec::new();
4774 world.for_each_mut_changed(POSITION, 0, |entity, _table, _idx| {
4775 changed_entities.push(entity);
4776 });
4777
4778 assert_eq!(changed_entities.len(), 1);
4779 assert!(changed_entities.contains(&e1));
4780 assert!(!changed_entities.contains(&e2));
4781 }
4782
4783 #[test]
4784 fn test_change_detection_tick_tracking() {
4785 let mut world = World::default();
4786 let entity = world.spawn_entities(POSITION, 1)[0];
4787
4788 assert_eq!(world.current_tick(), 0);
4789 assert_eq!(world.last_tick(), 0);
4790
4791 world.step();
4792 assert_eq!(world.current_tick(), 1);
4793 assert_eq!(world.last_tick(), 0);
4794
4795 world.step();
4796 assert_eq!(world.current_tick(), 2);
4797 assert_eq!(world.last_tick(), 1);
4798
4799 world.step();
4800 world.get_position_mut(entity).unwrap().x = 10.0;
4801
4802 let mut count = 0;
4803 world.for_each_mut_changed(POSITION, 0, |_entity, _table, _idx| {
4804 count += 1;
4805 });
4806 assert_eq!(count, 1);
4807
4808 world.step();
4809 count = 0;
4810 world.for_each_mut_changed(POSITION, 0, |_entity, _table, _idx| {
4811 count += 1;
4812 });
4813 assert_eq!(count, 0);
4814 }
4815
4816 #[test]
4817 fn test_change_detection_multiple_components() {
4818 let mut world = World::default();
4819 let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4820 let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4821
4822 world.step();
4823
4824 world.get_position_mut(e1).unwrap().x = 5.0;
4825 world.get_velocity_mut(e2).unwrap().x = 10.0;
4826
4827 let mut changed_entities = Vec::new();
4828 world.for_each_mut_changed(POSITION | VELOCITY, 0, |entity, _table, _idx| {
4829 changed_entities.push(entity);
4830 });
4831
4832 assert_eq!(changed_entities.len(), 2);
4833 assert!(changed_entities.contains(&e1));
4834 assert!(changed_entities.contains(&e2));
4835 }
4836
4837 #[test]
4838 fn test_change_detection_with_exclude() {
4839 let mut world = World::default();
4840 let e1 = world.spawn_entities(POSITION, 1)[0];
4841 let e2 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4842
4843 world.step();
4844
4845 world.get_position_mut(e1).unwrap().x = 5.0;
4846 world.get_position_mut(e2).unwrap().x = 10.0;
4847
4848 let mut changed_entities = Vec::new();
4849 world.for_each_mut_changed(POSITION, HEALTH, |entity, _table, _idx| {
4850 changed_entities.push(entity);
4851 });
4852
4853 assert_eq!(changed_entities.len(), 1);
4854 assert!(changed_entities.contains(&e1));
4855 assert!(!changed_entities.contains(&e2));
4856 }
4857
4858 #[test]
4859 fn test_simd_slice_iteration_read() {
4860 let mut world = World::default();
4861 world.spawn_entities(POSITION, 3);
4862
4863 let mut total_count = 0;
4864 for slice in world.iter_position_slices() {
4865 total_count += slice.len();
4866 for pos in slice {
4867 assert_eq!(pos.x, 0.0);
4868 assert_eq!(pos.y, 0.0);
4869 }
4870 }
4871
4872 assert_eq!(total_count, 3);
4873 }
4874
4875 #[test]
4876 fn test_simd_slice_iteration_write() {
4877 let mut world = World::default();
4878 let entities = world.spawn_entities(POSITION, 5);
4879
4880 for slice in world.iter_position_slices_mut() {
4881 for pos in slice {
4882 pos.x = 10.0;
4883 pos.y = 20.0;
4884 }
4885 }
4886
4887 for entity in entities {
4888 let pos = world.get_position(entity).unwrap();
4889 assert_eq!(pos.x, 10.0);
4890 assert_eq!(pos.y, 20.0);
4891 }
4892 }
4893
4894 #[test]
4895 fn test_simd_slice_iteration_multiple_archetypes() {
4896 let mut world = World::default();
4897 world.spawn_entities(POSITION, 2);
4898 world.spawn_entities(POSITION | VELOCITY, 3);
4899 world.spawn_entities(POSITION | HEALTH, 4);
4900
4901 let mut slice_count = 0;
4902 let mut total_entities = 0;
4903
4904 for slice in world.iter_position_slices() {
4905 slice_count += 1;
4906 total_entities += slice.len();
4907 }
4908
4909 assert_eq!(slice_count, 3);
4910 assert_eq!(total_entities, 9);
4911 }
4912
4913 #[test]
4914 fn test_simd_slice_vectorizable_operation() {
4915 let mut world = World::default();
4916 world.spawn_entities(POSITION | VELOCITY, 1000);
4917
4918 for pos in world.iter_position_slices_mut() {
4919 for p in pos {
4920 p.x += 1.0;
4921 p.y += 2.0;
4922 }
4923 }
4924
4925 for vel in world.iter_velocity_slices_mut() {
4926 for v in vel {
4927 v.x *= 0.99;
4928 v.y *= 0.99;
4929 }
4930 }
4931
4932 let mut checked = 0;
4933 for slice in world.iter_position_slices() {
4934 for pos in slice {
4935 assert_eq!(pos.x, 1.0);
4936 assert_eq!(pos.y, 2.0);
4937 checked += 1;
4938 }
4939 }
4940 assert_eq!(checked, 1000);
4941 }
4942
4943 #[test]
4944 fn test_simd_slice_empty_world() {
4945 let world = World::default();
4946
4947 let mut count = 0;
4948 for _ in world.iter_position_slices() {
4949 count += 1;
4950 }
4951 assert_eq!(count, 0);
4952 }
4953
4954 #[test]
4955 fn test_simd_slice_no_matching_archetype() {
4956 let mut world = World::default();
4957 world.spawn_entities(VELOCITY, 5);
4958
4959 let mut count = 0;
4960 for _ in world.iter_position_slices() {
4961 count += 1;
4962 }
4963 assert_eq!(count, 0);
4964 }
4965
4966 #[test]
4967 fn test_sparse_set_add_remove_has() {
4968 let mut world = World::default();
4969 let entity = world.spawn_entities(POSITION, 1)[0];
4970
4971 assert!(!world.has_player(entity));
4972
4973 world.add_player(entity);
4974 assert!(world.has_player(entity));
4975
4976 world.remove_player(entity);
4977 assert!(!world.has_player(entity));
4978 }
4979
4980 #[test]
4981 fn test_sparse_set_query() {
4982 let mut world = World::default();
4983 let e1 = world.spawn_entities(POSITION, 1)[0];
4984 let e2 = world.spawn_entities(POSITION, 1)[0];
4985 let e3 = world.spawn_entities(POSITION, 1)[0];
4986
4987 world.add_player(e1);
4988 world.add_player(e3);
4989
4990 let players: Vec<Entity> = world.query_player().collect();
4991 assert_eq!(players.len(), 2);
4992 assert!(players.contains(&e1));
4993 assert!(players.contains(&e3));
4994 assert!(!players.contains(&e2));
4995 }
4996
4997 #[test]
4998 fn test_sparse_set_no_archetype_fragmentation() {
4999 let mut world = World::default();
5000 let e1 = world.spawn_entities(POSITION, 1)[0];
5001 let e2 = world.spawn_entities(POSITION, 1)[0];
5002 let e3 = world.spawn_entities(POSITION, 1)[0];
5003
5004 world.add_player(e1);
5005 world.add_enemy(e2);
5006
5007 let mut count = 0;
5008 world.for_each(POSITION, 0, |_, _, _| {
5009 count += 1;
5010 });
5011
5012 assert_eq!(count, 3);
5013
5014 let mask1 = world.component_mask(e1).unwrap();
5015 let mask2 = world.component_mask(e2).unwrap();
5016 let mask3 = world.component_mask(e3).unwrap();
5017
5018 assert_eq!(mask1, mask2);
5019 assert_eq!(mask2, mask3);
5020 }
5021
5022 #[test]
5023 fn test_sparse_set_tag_component_query() {
5024 let mut world = World::default();
5025 let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5026 let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5027 let e3 = world.spawn_entities(POSITION, 1)[0];
5028 let e4 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5029
5030 world.add_player(e1);
5031 world.add_player(e2);
5032
5033 let mut count = 0;
5034 world.for_each(POSITION | VELOCITY | PLAYER, 0, |_, _, _| {
5035 count += 1;
5036 });
5037 assert_eq!(count, 2);
5038
5039 let mut found = Vec::new();
5040 world.for_each(POSITION | VELOCITY | PLAYER, 0, |entity, _, _| {
5041 found.push(entity);
5042 });
5043 assert!(found.contains(&e1));
5044 assert!(found.contains(&e2));
5045 assert!(!found.contains(&e3));
5046 assert!(!found.contains(&e4));
5047 }
5048
5049 #[test]
5050 fn test_sparse_set_exclude_tag() {
5051 let mut world = World::default();
5052 let e1 = world.spawn_entities(POSITION, 1)[0];
5053 let e2 = world.spawn_entities(POSITION, 1)[0];
5054 let e3 = world.spawn_entities(POSITION, 1)[0];
5055
5056 world.add_player(e1);
5057 world.add_player(e2);
5058
5059 let mut count = 0;
5060 world.for_each(POSITION, PLAYER, |_, _, _| {
5061 count += 1;
5062 });
5063 assert_eq!(count, 1);
5064
5065 let mut found = Vec::new();
5066 world.for_each(POSITION, PLAYER, |entity, _, _| {
5067 found.push(entity);
5068 });
5069 assert_eq!(found.len(), 1);
5070 assert!(found.contains(&e3));
5071 }
5072
5073 #[test]
5074 fn test_sparse_set_cleanup_on_despawn() {
5075 let mut world = World::default();
5076 let e1 = world.spawn_entities(POSITION, 1)[0];
5077 let e2 = world.spawn_entities(POSITION, 1)[0];
5078
5079 world.add_player(e1);
5080 world.add_enemy(e2);
5081
5082 assert_eq!(world.query_player().count(), 1);
5083 assert_eq!(world.query_enemy().count(), 1);
5084
5085 world.despawn_entities(&[e1]);
5086
5087 assert_eq!(world.query_player().count(), 0);
5088 assert_eq!(world.query_enemy().count(), 1);
5089
5090 world.despawn_entities(&[e2]);
5091 assert_eq!(world.query_enemy().count(), 0);
5092 }
5093
5094 #[test]
5095 fn test_sparse_set_multiple_tags() {
5096 let mut world = World::default();
5097 let entity = world.spawn_entities(POSITION, 1)[0];
5098
5099 world.add_player(entity);
5100 world.add_active(entity);
5101
5102 assert!(world.has_player(entity));
5103 assert!(world.has_active(entity));
5104 assert!(!world.has_enemy(entity));
5105
5106 let mut count = 0;
5107 world.for_each(POSITION | PLAYER | ACTIVE, 0, |_, _, _| {
5108 count += 1;
5109 });
5110 assert_eq!(count, 1);
5111
5112 world.remove_active(entity);
5113 assert!(world.has_player(entity));
5114 assert!(!world.has_active(entity));
5115
5116 count = 0;
5117 world.for_each(POSITION | PLAYER | ACTIVE, 0, |_, _, _| {
5118 count += 1;
5119 });
5120 assert_eq!(count, 0);
5121 }
5122
5123 #[test]
5124 fn test_sparse_set_for_each_mut() {
5125 let mut world = World::default();
5126 let e1 = world.spawn_entities(POSITION, 1)[0];
5127 let e2 = world.spawn_entities(POSITION, 1)[0];
5128 let e3 = world.spawn_entities(POSITION, 1)[0];
5129
5130 world.add_player(e1);
5131 world.add_player(e3);
5132
5133 world.for_each_mut(POSITION | PLAYER, 0, |_, table, idx| {
5134 table.position[idx].x = 100.0;
5135 });
5136
5137 assert_eq!(world.get_position(e1).unwrap().x, 100.0);
5138 assert_ne!(world.get_position(e2).unwrap().x, 100.0);
5139 assert_eq!(world.get_position(e3).unwrap().x, 100.0);
5140 }
5141
5142 #[test]
5143 #[cfg(not(target_family = "wasm"))]
5144 fn test_sparse_set_par_for_each_mut() {
5145 let mut world = World::default();
5146 let entities = world.spawn_entities(POSITION, 100);
5147
5148 for &entity in &entities[0..50] {
5149 world.add_player(entity);
5150 }
5151
5152 world.par_for_each_mut(POSITION | PLAYER, 0, |_, table, idx| {
5153 table.position[idx].x = 200.0;
5154 });
5155
5156 for &entity in entities.iter().take(50) {
5157 assert_eq!(world.get_position(entity).unwrap().x, 200.0);
5158 }
5159 for &entity in entities.iter().skip(50).take(50) {
5160 assert_ne!(world.get_position(entity).unwrap().x, 200.0);
5161 }
5162 }
5163
5164 #[test]
5165 fn test_sparse_set_complex_query() {
5166 let mut world = World::default();
5167 let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5168 let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5169 let e3 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5170 let _e4 = world.spawn_entities(POSITION, 1)[0];
5171
5172 world.add_player(e1);
5173 world.add_enemy(e2);
5174 world.add_enemy(e3);
5175 world.add_active(e1);
5176 world.add_active(e2);
5177
5178 let mut count = 0;
5179 world.for_each(POSITION | VELOCITY | ENEMY | ACTIVE, 0, |_, _, _| {
5180 count += 1;
5181 });
5182 assert_eq!(count, 1);
5183
5184 let mut found = Vec::new();
5185 world.for_each(POSITION | VELOCITY | ENEMY | ACTIVE, 0, |entity, _, _| {
5186 found.push(entity);
5187 });
5188 assert_eq!(found.len(), 1);
5189 assert!(found.contains(&e2));
5190 }
5191
5192 #[test]
5193 fn test_command_buffer_spawn() {
5194 let mut world = World::default();
5195
5196 world.queue_spawn_entities(POSITION, 3);
5197 world.queue_spawn_entities(VELOCITY, 2);
5198
5199 assert_eq!(world.command_count(), 2);
5200 assert_eq!(world.get_all_entities().len(), 0);
5201
5202 world.apply_commands();
5203
5204 assert_eq!(world.command_count(), 0);
5205 assert_eq!(world.get_all_entities().len(), 5);
5206
5207 let mut pos_count = 0;
5208 world.for_each(POSITION, 0, |_, _, _| pos_count += 1);
5209 assert_eq!(pos_count, 3);
5210
5211 let mut vel_count = 0;
5212 world.for_each(VELOCITY, 0, |_, _, _| vel_count += 1);
5213 assert_eq!(vel_count, 2);
5214 }
5215
5216 #[test]
5217 fn test_command_buffer_despawn() {
5218 let mut world = World::default();
5219 let entities = world.spawn_entities(POSITION, 5);
5220
5221 world.queue_despawn_entity(entities[0]);
5222 world.queue_despawn_entity(entities[2]);
5223 world.queue_despawn_entity(entities[4]);
5224
5225 assert_eq!(world.command_count(), 3);
5226 assert_eq!(world.get_all_entities().len(), 5);
5227
5228 world.apply_commands();
5229
5230 assert_eq!(world.command_count(), 0);
5231 assert_eq!(world.get_all_entities().len(), 2);
5232
5233 let remaining = world.get_all_entities();
5234 assert!(remaining.contains(&entities[1]));
5235 assert!(remaining.contains(&entities[3]));
5236 }
5237
5238 #[test]
5239 fn test_command_buffer_add_remove_components() {
5240 let mut world = World::default();
5241 let entity = world.spawn_entities(POSITION, 1)[0];
5242
5243 world.queue_add_components(entity, VELOCITY);
5244 assert_eq!(world.command_count(), 1);
5245 assert!(world.get_velocity(entity).is_none());
5246
5247 world.apply_commands();
5248 assert!(world.get_velocity(entity).is_some());
5249
5250 world.queue_remove_components(entity, VELOCITY);
5251 world.apply_commands();
5252 assert!(world.get_velocity(entity).is_none());
5253 }
5254
5255 #[test]
5256 fn test_command_buffer_set_component() {
5257 let mut world = World::default();
5258 let entity = world.spawn_entities(POSITION, 1)[0];
5259
5260 world.queue_set_position(entity, Position { x: 100.0, y: 200.0 });
5261 assert_eq!(world.command_count(), 1);
5262 assert_ne!(world.get_position(entity).unwrap().x, 100.0);
5263
5264 world.apply_commands();
5265
5266 assert_eq!(world.command_count(), 0);
5267 let pos = world.get_position(entity).unwrap();
5268 assert_eq!(pos.x, 100.0);
5269 assert_eq!(pos.y, 200.0);
5270 }
5271
5272 #[test]
5273 fn test_command_buffer_tags() {
5274 let mut world = World::default();
5275 let entity = world.spawn_entities(POSITION, 1)[0];
5276
5277 world.queue_add_player(entity);
5278 world.queue_add_active(entity);
5279
5280 assert_eq!(world.command_count(), 2);
5281 assert!(!world.has_player(entity));
5282 assert!(!world.has_active(entity));
5283
5284 world.apply_commands();
5285
5286 assert!(world.has_player(entity));
5287 assert!(world.has_active(entity));
5288
5289 world.queue_remove_player(entity);
5290 world.apply_commands();
5291
5292 assert!(!world.has_player(entity));
5293 assert!(world.has_active(entity));
5294 }
5295
5296 #[test]
5297 fn test_command_buffer_mixed_operations() {
5298 let mut world = World::default();
5299 let e1 = world.spawn_entities(POSITION, 1)[0];
5300
5301 world.queue_spawn_entities(VELOCITY, 2);
5302 world.queue_add_components(e1, VELOCITY);
5303 world.queue_set_position(e1, Position { x: 50.0, y: 75.0 });
5304 world.queue_add_player(e1);
5305
5306 assert_eq!(world.command_count(), 4);
5307
5308 world.apply_commands();
5309
5310 assert_eq!(world.command_count(), 0);
5311 assert_eq!(world.get_all_entities().len(), 3);
5312 assert!(world.get_velocity(e1).is_some());
5313 assert_eq!(world.get_position(e1).unwrap().x, 50.0);
5314 assert!(world.has_player(e1));
5315 }
5316
5317 #[test]
5318 fn test_command_buffer_clear() {
5319 let mut world = World::default();
5320
5321 world.queue_spawn_entities(POSITION, 5);
5322 world.queue_spawn_entities(VELOCITY, 3);
5323
5324 assert_eq!(world.command_count(), 2);
5325
5326 world.clear_commands();
5327
5328 assert_eq!(world.command_count(), 0);
5329 assert_eq!(world.get_all_entities().len(), 0);
5330 }
5331
5332 #[test]
5333 fn test_command_buffer_multiple_batches() {
5334 let mut world = World::default();
5335
5336 world.queue_spawn_entities(POSITION, 2);
5337 world.apply_commands();
5338 assert_eq!(world.get_all_entities().len(), 2);
5339
5340 world.queue_spawn_entities(VELOCITY, 3);
5341 world.apply_commands();
5342 assert_eq!(world.get_all_entities().len(), 5);
5343
5344 world.queue_spawn_entities(POSITION | VELOCITY, 1);
5345 world.apply_commands();
5346 assert_eq!(world.get_all_entities().len(), 6);
5347 }
5348
5349 #[test]
5350 fn test_command_buffer_despawn_batch() {
5351 let mut world = World::default();
5352 let entities = world.spawn_entities(POSITION, 10);
5353
5354 let to_despawn = vec![entities[0], entities[3], entities[5], entities[9]];
5355 world.queue_despawn_entities(to_despawn.clone());
5356
5357 assert_eq!(world.command_count(), 1);
5358 world.apply_commands();
5359
5360 assert_eq!(world.get_all_entities().len(), 6);
5361
5362 let remaining = world.get_all_entities();
5363 for &entity in &to_despawn {
5364 assert!(!remaining.contains(&entity));
5365 }
5366 }
5367
5368 #[test]
5369 fn test_command_buffer_parallel_safety() {
5370 let mut world = World::default();
5371 let _ = world.spawn_entities(POSITION | VELOCITY, 100);
5372
5373 let mut to_despawn = Vec::new();
5374 world.for_each(POSITION | VELOCITY, 0, |entity, table, idx| {
5375 if table.position[idx].x < 0.0 {
5376 to_despawn.push(entity);
5377 }
5378 });
5379
5380 for entity in to_despawn {
5381 world.queue_despawn_entity(entity);
5382 }
5383
5384 let command_count_before = world.command_count();
5385 world.apply_commands();
5386
5387 assert_eq!(world.get_all_entities().len(), 100 - command_count_before);
5388 }
5389
5390 #[test]
5391 fn test_ergonomic_query_builder() {
5392 let mut world = World::default();
5393 world.spawn_entities(POSITION | VELOCITY, 3);
5394 world.spawn_entities(POSITION, 2);
5395
5396 let mut count = 0;
5397 world
5398 .query()
5399 .with(POSITION)
5400 .with(VELOCITY)
5401 .iter(|_entity, _table, _idx| {
5402 count += 1;
5403 });
5404 assert_eq!(count, 3);
5405
5406 count = 0;
5407 world
5408 .query()
5409 .with(POSITION)
5410 .without(VELOCITY)
5411 .iter(|_entity, _table, _idx| {
5412 count += 1;
5413 });
5414 assert_eq!(count, 2);
5415 }
5416
5417 #[test]
5418 fn test_ergonomic_query_builder_mut() {
5419 let mut world = World::default();
5420 let entities = world.spawn_entities(POSITION, 3);
5421
5422 world
5423 .query_mut()
5424 .with(POSITION)
5425 .iter(|_entity, table, idx| {
5426 table.position[idx].x = 100.0;
5427 });
5428
5429 for &entity in &entities {
5430 assert_eq!(world.get_position(entity).unwrap().x, 100.0);
5431 }
5432 }
5433
5434 #[test]
5435 fn test_ergonomic_single_component_iter() {
5436 let mut world = World::default();
5437 world.spawn_entities(POSITION, 5);
5438
5439 let mut count = 0;
5440 world.iter_position(|_entity, pos| {
5441 assert_eq!(pos.x, 0.0);
5442 count += 1;
5443 });
5444 assert_eq!(count, 5);
5445 }
5446
5447 #[test]
5448 fn test_ergonomic_single_component_iter_mut() {
5449 let mut world = World::default();
5450 let entities = world.spawn_entities(POSITION, 5);
5451
5452 world.iter_position_mut(|_entity, pos| {
5453 pos.x = 42.0;
5454 });
5455
5456 for &entity in &entities {
5457 assert_eq!(world.get_position(entity).unwrap().x, 42.0);
5458 }
5459 }
5460
5461 #[test]
5462 fn test_ergonomic_iter_with_entity() {
5463 let mut world = World::default();
5464 let e1 = world.spawn_entities(POSITION, 1)[0];
5465 let e2 = world.spawn_entities(POSITION, 1)[0];
5466
5467 let mut found = vec![];
5468 world.iter_position(|entity, _pos| {
5469 found.push(entity);
5470 });
5471
5472 assert_eq!(found.len(), 2);
5473 assert!(found.contains(&e1));
5474 assert!(found.contains(&e2));
5475 }
5476
5477 #[test]
5478 fn test_ergonomic_batch_spawn() {
5479 let mut world = World::default();
5480
5481 let entities = world.spawn_batch(POSITION | VELOCITY, 100, |table, idx| {
5482 table.position[idx] = Position {
5483 x: idx as f32,
5484 y: idx as f32 * 2.0,
5485 };
5486 table.velocity[idx] = Velocity { x: 1.0, y: -1.0 };
5487 });
5488
5489 assert_eq!(entities.len(), 100);
5490
5491 for (i, &entity) in entities.iter().enumerate() {
5492 let pos = world.get_position(entity).unwrap();
5493 assert_eq!(pos.x, i as f32);
5494 assert_eq!(pos.y, i as f32 * 2.0);
5495
5496 let vel = world.get_velocity(entity).unwrap();
5497 assert_eq!(vel.x, 1.0);
5498 assert_eq!(vel.y, -1.0);
5499 }
5500 }
5501
5502 #[test]
5503 fn test_ergonomic_query_with_tags() {
5504 let mut world = World::default();
5505 let e1 = world.spawn_entities(POSITION, 1)[0];
5506 let e2 = world.spawn_entities(POSITION, 1)[0];
5507 let _e3 = world.spawn_entities(POSITION, 1)[0];
5508
5509 world.add_player(e1);
5510 world.add_player(e2);
5511
5512 let mut count = 0;
5513 world
5514 .query()
5515 .with(POSITION | PLAYER)
5516 .iter(|_entity, _table, _idx| {
5517 count += 1;
5518 });
5519 assert_eq!(count, 2);
5520
5521 count = 0;
5522 world
5523 .query()
5524 .with(POSITION)
5525 .without(PLAYER)
5526 .iter(|_entity, _table, _idx| {
5527 count += 1;
5528 });
5529 assert_eq!(count, 1);
5530 }
5531
5532 #[test]
5533 fn test_query_builder_mut_without() {
5534 let mut world = World::default();
5535 let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5536 let e2 = world.spawn_entities(POSITION, 1)[0];
5537
5538 world.set_position(e1, Position { x: 1.0, y: 2.0 });
5539 world.set_position(e2, Position { x: 3.0, y: 4.0 });
5540
5541 let mut count = 0;
5542 world
5543 .query_mut()
5544 .with(POSITION)
5545 .without(VELOCITY)
5546 .iter(|_entity, _table, _idx| {
5547 count += 1;
5548 });
5549 assert_eq!(count, 1);
5550 }
5551
5552 #[test]
5553 fn test_iter_methods() {
5554 let mut world = World::default();
5555 let e1 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
5556 let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5557
5558 world.set_position(e1, Position { x: 1.0, y: 2.0 });
5559 world.set_position(e2, Position { x: 3.0, y: 4.0 });
5560 world.set_velocity(e1, Velocity { x: 1.0, y: 1.0 });
5561 world.set_velocity(e2, Velocity { x: 2.0, y: 2.0 });
5562 world.set_health(e1, Health { value: 100.0 });
5563
5564 let mut sum_x = 0.0;
5565 world.iter_position(|_entity, pos| {
5566 sum_x += pos.x;
5567 });
5568 assert_eq!(sum_x, 4.0);
5569
5570 world.iter_position_mut(|_entity, pos| {
5571 pos.x *= 2.0;
5572 });
5573
5574 let mut count = 0;
5575 world.iter_velocity(|_entity, _vel| {
5576 count += 1;
5577 });
5578 assert_eq!(count, 2);
5579
5580 world.iter_velocity_mut(|_entity, vel| {
5581 vel.x *= 2.0;
5582 });
5583
5584 world.iter_health_mut(|_entity, health| {
5585 health.value += 10.0;
5586 });
5587
5588 let mut health_sum = 0.0;
5589 world.iter_health(|_entity, health| {
5590 health_sum += health.value;
5591 });
5592 assert_eq!(health_sum, 110.0);
5593
5594 let e3 = world.spawn_entities(PARENT | NODE, 1)[0];
5595 world.set_parent(e3, Parent(e1));
5596 world.set_node(
5597 e3,
5598 Node {
5599 id: e3,
5600 parent: Some(e1),
5601 children: Vec::new(),
5602 },
5603 );
5604
5605 let mut parent_count = 0;
5606 world.iter_parent(|_entity, _parent| {
5607 parent_count += 1;
5608 });
5609 assert_eq!(parent_count, 1);
5610
5611 let mut node_count = 0;
5612 world.iter_node(|_entity, _node| {
5613 node_count += 1;
5614 });
5615 assert_eq!(node_count, 1);
5616
5617 world.iter_parent_mut(|_entity, parent| {
5618 parent.0 = e2;
5619 });
5620
5621 world.iter_node_mut(|_entity, _node| {});
5622 }
5623
5624 #[test]
5625 fn test_schedule_basic() {
5626 let mut world = World::default();
5627 world.resources._delta_time = 0.016;
5628
5629 let mut schedule = Schedule::new();
5630 schedule.push("tick", |world: &mut World| {
5631 world.resources._delta_time += 0.016;
5632 });
5633
5634 schedule.run(&mut world);
5635 assert_eq!(world.resources._delta_time, 0.032);
5636
5637 schedule.run(&mut world);
5638 assert_eq!(world.resources._delta_time, 0.048);
5639 }
5640
5641 #[test]
5642 fn test_schedule_multiple_systems() {
5643 let mut world = World::default();
5644 let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5645 world.set_position(entity, Position { x: 0.0, y: 0.0 });
5646 world.set_velocity(entity, Velocity { x: 10.0, y: 5.0 });
5647 world.resources._delta_time = 0.1;
5648
5649 let mut schedule = Schedule::new();
5650
5651 schedule.push("physics", |world: &mut World| {
5652 let dt = world.resources._delta_time;
5653 let updates: Vec<(Entity, Velocity)> = world
5654 .query_entities(POSITION | VELOCITY)
5655 .filter_map(|entity| world.get_velocity(entity).map(|vel| (entity, vel.clone())))
5656 .collect();
5657
5658 for (entity, vel) in updates {
5659 if let Some(pos) = world.get_position_mut(entity) {
5660 pos.x += vel.x * dt;
5661 pos.y += vel.y * dt;
5662 }
5663 }
5664 });
5665
5666 schedule.push("double_dt", |world: &mut World| {
5667 world.resources._delta_time *= 2.0;
5668 });
5669
5670 schedule.run(&mut world);
5671
5672 let pos = world.get_position(entity).unwrap();
5673 assert_eq!(pos.x, 1.0);
5674 assert_eq!(pos.y, 0.5);
5675 assert_eq!(world.resources._delta_time, 0.2);
5676 }
5677
5678 #[test]
5679 fn test_schedule_builder_pattern() {
5680 let mut world = World::default();
5681 world.resources._delta_time = 1.0;
5682
5683 let mut schedule = Schedule::new();
5684 schedule
5685 .push("add", |world: &mut World| {
5686 world.resources._delta_time += 1.0;
5687 })
5688 .push("multiply", |world: &mut World| {
5689 world.resources._delta_time *= 2.0;
5690 });
5691
5692 schedule.run(&mut world);
5693 assert_eq!(world.resources._delta_time, 4.0);
5694 }
5695
5696 #[test]
5697 fn test_schedule_system_order() {
5698 let mut world = World::default();
5699 let entity = world.spawn_entities(POSITION, 1)[0];
5700 world.set_position(entity, Position { x: 0.0, y: 0.0 });
5701
5702 let mut schedule = Schedule::new();
5703
5704 schedule.push("set_x", |world: &mut World| {
5705 let entities: Vec<Entity> = world.query_entities(POSITION).collect();
5706 if let Some(pos) = world.get_position_mut(entities[0]) {
5707 pos.x = 10.0;
5708 }
5709 });
5710
5711 schedule.push("double_x", |world: &mut World| {
5712 let entities: Vec<Entity> = world.query_entities(POSITION).collect();
5713 if let Some(pos) = world.get_position_mut(entities[0]) {
5714 pos.x *= 2.0;
5715 }
5716 });
5717
5718 schedule.run(&mut world);
5719
5720 let pos = world.get_position(entity).unwrap();
5721 assert_eq!(pos.x, 20.0);
5722 }
5723
5724 #[test]
5725 fn test_schedule_with_components() {
5726 let mut world = World::default();
5727 let entity = world.spawn_entities(HEALTH, 1)[0];
5728 world.set_health(entity, Health { value: 100.0 });
5729
5730 let mut schedule = Schedule::new();
5731
5732 schedule.push("damage", |world: &mut World| {
5733 let entities: Vec<Entity> = world.query_entities(HEALTH).collect();
5734 for entity in entities {
5735 if let Some(health) = world.get_health_mut(entity) {
5736 health.value -= 10.0;
5737 }
5738 }
5739 });
5740
5741 schedule.push("decay", |world: &mut World| {
5742 let entities: Vec<Entity> = world.query_entities(HEALTH).collect();
5743 for entity in entities {
5744 if let Some(health) = world.get_health_mut(entity) {
5745 health.value *= 0.9;
5746 }
5747 }
5748 });
5749
5750 schedule.run(&mut world);
5751
5752 let health = world.get_health(entity).unwrap();
5753 assert_eq!(health.value, 81.0);
5754 }
5755
5756 #[test]
5757 fn test_schedule_insert_before() {
5758 let mut world = World::default();
5759 world.resources._delta_time = 1.0;
5760
5761 let mut schedule = Schedule::new();
5762 schedule.push("first", |world: &mut World| {
5763 world.resources._delta_time += 10.0;
5764 });
5765 schedule.push("third", |world: &mut World| {
5766 world.resources._delta_time *= 3.0;
5767 });
5768 schedule.insert_before("third", "second", |world: &mut World| {
5769 world.resources._delta_time *= 2.0;
5770 });
5771
5772 schedule.run(&mut world);
5773 assert_eq!(world.resources._delta_time, 66.0);
5774 }
5775
5776 #[test]
5777 fn test_schedule_insert_after() {
5778 let mut world = World::default();
5779 world.resources._delta_time = 1.0;
5780
5781 let mut schedule = Schedule::new();
5782 schedule.push("first", |world: &mut World| {
5783 world.resources._delta_time += 10.0;
5784 });
5785 schedule.push("third", |world: &mut World| {
5786 world.resources._delta_time *= 3.0;
5787 });
5788 schedule.insert_after("first", "second", |world: &mut World| {
5789 world.resources._delta_time *= 2.0;
5790 });
5791
5792 schedule.run(&mut world);
5793 assert_eq!(world.resources._delta_time, 66.0);
5794 }
5795
5796 #[test]
5797 fn test_schedule_remove() {
5798 let mut world = World::default();
5799 world.resources._delta_time = 1.0;
5800
5801 let mut schedule = Schedule::new();
5802 schedule.push("add", |world: &mut World| {
5803 world.resources._delta_time += 10.0;
5804 });
5805 schedule.push("multiply", |world: &mut World| {
5806 world.resources._delta_time *= 5.0;
5807 });
5808
5809 schedule.remove("multiply");
5810 schedule.run(&mut world);
5811 assert_eq!(world.resources._delta_time, 11.0);
5812 }
5813
5814 #[test]
5815 fn test_schedule_remove_nonexistent() {
5816 let mut schedule: Schedule<World> = Schedule::new();
5817 schedule.push("a", |_world: &mut World| {});
5818 schedule.remove("nonexistent");
5819 assert!(schedule.contains("a"));
5820 }
5821
5822 #[test]
5823 fn test_schedule_contains() {
5824 let mut schedule: Schedule<World> = Schedule::new();
5825 assert!(!schedule.contains("physics"));
5826
5827 schedule.push("physics", |_world: &mut World| {});
5828 assert!(schedule.contains("physics"));
5829 assert!(!schedule.contains("render"));
5830
5831 schedule.remove("physics");
5832 assert!(!schedule.contains("physics"));
5833 }
5834
5835 #[test]
5836 #[should_panic(expected = "system \"nonexistent\" not found")]
5837 fn test_schedule_insert_before_panics() {
5838 let mut schedule: Schedule<World> = Schedule::new();
5839 schedule.insert_before("nonexistent", "new", |_world: &mut World| {});
5840 }
5841
5842 #[test]
5843 #[should_panic(expected = "system \"nonexistent\" not found")]
5844 fn test_schedule_insert_after_panics() {
5845 let mut schedule: Schedule<World> = Schedule::new();
5846 schedule.insert_after("nonexistent", "new", |_world: &mut World| {});
5847 }
5848
5849 #[test]
5850 fn test_schedule_ordering_verification() {
5851 let order = std::sync::Arc::new(std::sync::Mutex::new(Vec::<&'static str>::new()));
5852
5853 let mut schedule: Schedule<World> = Schedule::new();
5854 let order_clone = order.clone();
5855 schedule.push("a", move |_world: &mut World| {
5856 order_clone.lock().unwrap().push("a");
5857 });
5858 let order_clone = order.clone();
5859 schedule.push("c", move |_world: &mut World| {
5860 order_clone.lock().unwrap().push("c");
5861 });
5862 let order_clone = order.clone();
5863 schedule.insert_before("c", "b", move |_world: &mut World| {
5864 order_clone.lock().unwrap().push("b");
5865 });
5866 let order_clone = order.clone();
5867 schedule.insert_after("c", "d", move |_world: &mut World| {
5868 order_clone.lock().unwrap().push("d");
5869 });
5870
5871 let mut world = World::default();
5872 schedule.run(&mut world);
5873 assert_eq!(*order.lock().unwrap(), vec!["a", "b", "c", "d"]);
5874 }
5875
5876 #[test]
5877 fn test_schedule_readonly_wrapper() {
5878 let mut world = World::default();
5879 world.resources._delta_time = 42.0;
5880
5881 fn read_system(world: &World) -> f32 {
5882 world.resources._delta_time
5883 }
5884
5885 let observed = std::sync::Arc::new(std::sync::Mutex::new(0.0_f32));
5886 let observed_clone = observed.clone();
5887
5888 let mut schedule = Schedule::new();
5889 schedule.push("reader", move |w: &mut World| {
5890 *observed_clone.lock().unwrap() = read_system(w);
5891 });
5892
5893 schedule.run(&mut world);
5894 assert_eq!(*observed.lock().unwrap(), 42.0);
5895 }
5896
5897 #[test]
5898 #[should_panic(expected = "already exists")]
5899 fn test_schedule_push_duplicate_panics() {
5900 let mut schedule: Schedule<World> = Schedule::new();
5901 schedule.push("a", |_w: &mut World| {});
5902 schedule.push("a", |_w: &mut World| {});
5903 }
5904
5905 #[test]
5906 #[should_panic(expected = "already exists")]
5907 fn test_schedule_insert_before_duplicate_panics() {
5908 let mut schedule: Schedule<World> = Schedule::new();
5909 schedule.push("a", |_w: &mut World| {});
5910 schedule.push("b", |_w: &mut World| {});
5911 schedule.insert_before("b", "a", |_w: &mut World| {});
5912 }
5913
5914 #[test]
5915 #[should_panic(expected = "already exists")]
5916 fn test_schedule_insert_after_duplicate_panics() {
5917 let mut schedule: Schedule<World> = Schedule::new();
5918 schedule.push("a", |_w: &mut World| {});
5919 schedule.insert_after("a", "a", |_w: &mut World| {});
5920 }
5921
5922 #[test]
5923 fn test_schedule_replace() {
5924 let mut world = World::default();
5925 world.resources._delta_time = 1.0;
5926
5927 let mut schedule = Schedule::new();
5928 schedule.push("add", |world: &mut World| {
5929 world.resources._delta_time += 10.0;
5930 });
5931
5932 schedule.run(&mut world);
5933 assert_eq!(world.resources._delta_time, 11.0);
5934
5935 schedule.replace("add", |world: &mut World| {
5936 world.resources._delta_time += 100.0;
5937 });
5938
5939 schedule.run(&mut world);
5940 assert_eq!(world.resources._delta_time, 111.0);
5941 }
5942
5943 #[test]
5944 #[should_panic(expected = "system \"nonexistent\" not found")]
5945 fn test_schedule_replace_panics_on_missing() {
5946 let mut schedule: Schedule<World> = Schedule::new();
5947 schedule.replace("nonexistent", |_w: &mut World| {});
5948 }
5949
5950 #[test]
5951 fn test_schedule_replace_preserves_order() {
5952 let mut world = World::default();
5953 world.resources._delta_time = 0.0;
5954
5955 let mut schedule = Schedule::new();
5956 schedule.push("first", |world: &mut World| {
5957 world.resources._delta_time += 1.0;
5958 });
5959 schedule.push("second", |world: &mut World| {
5960 world.resources._delta_time *= 10.0;
5961 });
5962 schedule.push("third", |world: &mut World| {
5963 world.resources._delta_time += 5.0;
5964 });
5965
5966 schedule.replace("second", |world: &mut World| {
5967 world.resources._delta_time *= 100.0;
5968 });
5969
5970 schedule.run(&mut world);
5971 assert_eq!(world.resources._delta_time, 105.0);
5972 }
5973
5974 #[test]
5975 fn test_schedule_names() {
5976 let mut schedule: Schedule<World> = Schedule::new();
5977 schedule.push("a", |_w: &mut World| {});
5978 schedule.push("b", |_w: &mut World| {});
5979 schedule.push("c", |_w: &mut World| {});
5980
5981 let names: Vec<&str> = schedule.names().collect();
5982 assert_eq!(names, vec!["a", "b", "c"]);
5983 }
5984
5985 #[test]
5986 fn test_schedule_len_and_is_empty() {
5987 let mut schedule: Schedule<World> = Schedule::new();
5988 assert!(schedule.is_empty());
5989 assert_eq!(schedule.len(), 0);
5990
5991 schedule.push("a", |_w: &mut World| {});
5992 assert!(!schedule.is_empty());
5993 assert_eq!(schedule.len(), 1);
5994
5995 schedule.push("b", |_w: &mut World| {});
5996 assert_eq!(schedule.len(), 2);
5997
5998 schedule.remove("a");
5999 assert_eq!(schedule.len(), 1);
6000
6001 schedule.remove("b");
6002 assert!(schedule.is_empty());
6003 }
6004
6005 #[test]
6006 fn test_schedule_remove_returns_bool() {
6007 let mut schedule: Schedule<World> = Schedule::new();
6008 schedule.push("a", |_w: &mut World| {});
6009
6010 assert!(schedule.remove("a"));
6011 assert!(!schedule.remove("a"));
6012 assert!(!schedule.remove("nonexistent"));
6013 }
6014
6015 #[test]
6016 fn test_schedule_remove_then_push_reuses_name() {
6017 let mut world = World::default();
6018 world.resources._delta_time = 0.0;
6019
6020 let mut schedule = Schedule::new();
6021 schedule.push("sys", |world: &mut World| {
6022 world.resources._delta_time += 1.0;
6023 });
6024
6025 schedule.remove("sys");
6026 schedule.push("sys", |world: &mut World| {
6027 world.resources._delta_time += 100.0;
6028 });
6029
6030 schedule.run(&mut world);
6031 assert_eq!(world.resources._delta_time, 100.0);
6032 }
6033
6034 #[test]
6035 fn test_schedule_insert_before_first() {
6036 let mut schedule: Schedule<World> = Schedule::new();
6037 schedule.push("b", |_w: &mut World| {});
6038 schedule.insert_before("b", "a", |_w: &mut World| {});
6039
6040 let names: Vec<&str> = schedule.names().collect();
6041 assert_eq!(names, vec!["a", "b"]);
6042 }
6043
6044 #[test]
6045 fn test_schedule_insert_after_last() {
6046 let mut schedule: Schedule<World> = Schedule::new();
6047 schedule.push("a", |_w: &mut World| {});
6048 schedule.insert_after("a", "b", |_w: &mut World| {});
6049
6050 let names: Vec<&str> = schedule.names().collect();
6051 assert_eq!(names, vec!["a", "b"]);
6052 }
6053
6054 #[test]
6055 fn test_schedule_push_readonly() {
6056 let mut world = World::default();
6057 world.resources._delta_time = 42.0;
6058
6059 let observed = std::sync::Arc::new(std::sync::Mutex::new(0.0_f32));
6060 let obs = observed.clone();
6061
6062 let mut schedule = Schedule::new();
6063 schedule.push_readonly("reader", move |world: &World| {
6064 *obs.lock().unwrap() = world.resources._delta_time;
6065 });
6066
6067 schedule.run(&mut world);
6068 assert_eq!(*observed.lock().unwrap(), 42.0);
6069 }
6070
6071 #[test]
6072 fn test_query_cache_persistence_on_new_archetype() {
6073 let mut world = World::default();
6074
6075 world.spawn_entities(POSITION, 5);
6076
6077 let mut count1 = 0;
6078 world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6079 count1 += 1;
6080 });
6081 assert_eq!(count1, 5);
6082
6083 let cache_size_before = world.query_cache.len();
6084 assert_eq!(cache_size_before, 1);
6085
6086 world.spawn_entities(POSITION | VELOCITY, 3);
6087
6088 let cache_size_after = world.query_cache.len();
6089 assert_eq!(cache_size_after, 1);
6090
6091 let mut count2 = 0;
6092 world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6093 count2 += 1;
6094 });
6095 assert_eq!(count2, 8);
6096
6097 world.spawn_entities(POSITION | HEALTH, 2);
6098
6099 let mut count3 = 0;
6100 world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6101 count3 += 1;
6102 });
6103 assert_eq!(count3, 10);
6104 }
6105
6106 #[test]
6107 fn test_multi_component_add_performance() {
6108 let mut world = World::default();
6109 let entity = world.spawn_entities(POSITION, 1)[0];
6110 let source_table_index =
6111 world.entity_locations.get(entity.id).unwrap().table_index as usize;
6112
6113 world.add_components(entity, VELOCITY | HEALTH);
6114
6115 assert!(world.get_position(entity).is_some());
6116 assert!(world.get_velocity(entity).is_some());
6117 assert!(world.get_health(entity).is_some());
6118
6119 let cache_entry_in_source = world.table_edges[source_table_index]
6120 .multi_add_cache
6121 .get(&(VELOCITY | HEALTH))
6122 .copied();
6123 assert!(cache_entry_in_source.is_some());
6124
6125 let entity2 = world.spawn_entities(POSITION, 1)[0];
6126 let source_table_index2 =
6127 world.entity_locations.get(entity2.id).unwrap().table_index as usize;
6128
6129 assert_eq!(source_table_index, source_table_index2);
6130
6131 world.add_components(entity2, VELOCITY | HEALTH);
6132
6133 let cache_hit = world.table_edges[source_table_index2]
6134 .multi_add_cache
6135 .get(&(VELOCITY | HEALTH))
6136 .copied();
6137 assert_eq!(cache_hit, cache_entry_in_source);
6138 }
6139
6140 #[test]
6141 fn test_multi_component_remove_performance() {
6142 let mut world = World::default();
6143 let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
6144 let source_table_index =
6145 world.entity_locations.get(entity.id).unwrap().table_index as usize;
6146
6147 world.remove_components(entity, VELOCITY | HEALTH);
6148
6149 assert!(world.get_position(entity).is_some());
6150 assert!(world.get_velocity(entity).is_none());
6151 assert!(world.get_health(entity).is_none());
6152
6153 let cache_entry_in_source = world.table_edges[source_table_index]
6154 .multi_remove_cache
6155 .get(&(VELOCITY | HEALTH))
6156 .copied();
6157 assert!(cache_entry_in_source.is_some());
6158
6159 let entity2 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
6160 let source_table_index2 =
6161 world.entity_locations.get(entity2.id).unwrap().table_index as usize;
6162
6163 assert_eq!(source_table_index, source_table_index2);
6164
6165 world.remove_components(entity2, VELOCITY | HEALTH);
6166
6167 let cache_hit = world.table_edges[source_table_index2]
6168 .multi_remove_cache
6169 .get(&(VELOCITY | HEALTH))
6170 .copied();
6171 assert_eq!(cache_hit, cache_entry_in_source);
6172 }
6173
6174 #[test]
6175 fn test_query_cache_invalidation_selective() {
6176 let mut world = World::default();
6177
6178 world.spawn_entities(POSITION, 10);
6179 world.spawn_entities(VELOCITY, 5);
6180
6181 let mut pos_count1 = 0;
6182 world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6183 pos_count1 += 1;
6184 });
6185 let mut vel_count1 = 0;
6186 world.for_each_mut(VELOCITY, 0, |_entity, _table, _idx| {
6187 vel_count1 += 1;
6188 });
6189 assert_eq!(pos_count1, 10);
6190 assert_eq!(vel_count1, 5);
6191
6192 let cache_size_before = world.query_cache.len();
6193 assert_eq!(cache_size_before, 2);
6194
6195 world.spawn_entities(POSITION | VELOCITY, 3);
6196
6197 let cache_size_after_archetype = world.query_cache.len();
6198 assert_eq!(cache_size_after_archetype, 2);
6199
6200 let mut pos_count2 = 0;
6201 world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6202 pos_count2 += 1;
6203 });
6204 let mut vel_count2 = 0;
6205 world.for_each_mut(VELOCITY, 0, |_entity, _table, _idx| {
6206 vel_count2 += 1;
6207 });
6208 assert_eq!(pos_count2, 13);
6209 assert_eq!(vel_count2, 8);
6210
6211 world.spawn_entities(HEALTH, 2);
6212
6213 let cache_size_after_health = world.query_cache.len();
6214 assert_eq!(cache_size_after_health, 2);
6215
6216 let mut pos_count3 = 0;
6217 world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6218 pos_count3 += 1;
6219 });
6220 let mut vel_count3 = 0;
6221 world.for_each_mut(VELOCITY, 0, |_entity, _table, _idx| {
6222 vel_count3 += 1;
6223 });
6224 let mut health_count = 0;
6225 world.for_each_mut(HEALTH, 0, |_entity, _table, _idx| {
6226 health_count += 1;
6227 });
6228 assert_eq!(pos_count3, 13);
6229 assert_eq!(vel_count3, 8);
6230 assert_eq!(health_count, 2);
6231
6232 let final_cache_size = world.query_cache.len();
6233 assert_eq!(final_cache_size, 3);
6234 }
6235
6236 #[test]
6237 fn test_entity_has_components_requires_all() {
6238 let mut world = World::default();
6239 let entity = world.spawn_entities(POSITION, 1)[0];
6240
6241 assert!(
6242 !world.entity_has_components(entity, POSITION | VELOCITY),
6243 "Should return false when entity only has POSITION but query asks for POSITION | VELOCITY"
6244 );
6245
6246 assert!(
6247 world.entity_has_components(entity, POSITION),
6248 "Should return true when entity has the single queried component"
6249 );
6250
6251 world.add_components(entity, VELOCITY);
6252 assert!(
6253 world.entity_has_components(entity, POSITION | VELOCITY),
6254 "Should return true when entity has all queried components"
6255 );
6256
6257 assert!(
6258 !world.entity_has_components(entity, POSITION | VELOCITY | HEALTH),
6259 "Should return false when entity is missing one of three queried components"
6260 );
6261 }
6262
6263 #[test]
6264 fn test_entity_has_generated_methods_consistency() {
6265 let mut world = World::default();
6266 let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
6267
6268 assert!(world.entity_has_position(entity));
6269 assert!(world.entity_has_velocity(entity));
6270 assert!(!world.entity_has_health(entity));
6271
6272 assert!(world.entity_has_components(entity, POSITION));
6273 assert!(world.entity_has_components(entity, VELOCITY));
6274 assert!(world.entity_has_components(entity, POSITION | VELOCITY));
6275 assert!(!world.entity_has_components(entity, HEALTH));
6276 assert!(!world.entity_has_components(entity, POSITION | HEALTH));
6277 }
6278
6279 #[test]
6280 fn test_despawn_preserves_change_vec_consistency() {
6281 let mut world = World::default();
6282 let entities = world.spawn_entities(POSITION, 5);
6283
6284 world.step();
6285
6286 world.get_position_mut(entities[0]).unwrap().x = 10.0;
6287 world.get_position_mut(entities[4]).unwrap().x = 40.0;
6288
6289 world.despawn_entities(&[entities[2]]);
6290
6291 for table in &world.tables {
6292 if table.mask & POSITION != 0 {
6293 let entity_count = table.entity_indices.len();
6294 assert_eq!(
6295 table.position.len(),
6296 entity_count,
6297 "Position vec length should match entity count after despawn"
6298 );
6299 assert_eq!(
6300 table.position_changed.len(),
6301 entity_count,
6302 "Position changed vec length should match entity count after despawn"
6303 );
6304 }
6305 }
6306 }
6307
6308 #[test]
6309 fn test_change_detection_after_despawn() {
6310 let mut world = World::default();
6311 let e1 = world.spawn_entities(POSITION, 1)[0];
6312 let e2 = world.spawn_entities(POSITION, 1)[0];
6313 let e3 = world.spawn_entities(POSITION, 1)[0];
6314
6315 world.set_position(e1, Position { x: 1.0, y: 0.0 });
6316 world.set_position(e2, Position { x: 2.0, y: 0.0 });
6317 world.set_position(e3, Position { x: 3.0, y: 0.0 });
6318
6319 world.step();
6320
6321 world.get_position_mut(e3).unwrap().x = 30.0;
6322
6323 world.despawn_entities(&[e2]);
6324
6325 let mut changed_entities = Vec::new();
6326 world.for_each_mut_changed(POSITION, 0, |entity, _table, _idx| {
6327 changed_entities.push(entity);
6328 });
6329
6330 assert!(
6331 changed_entities.contains(&e3),
6332 "e3 was modified and should be detected as changed"
6333 );
6334 assert!(
6335 !changed_entities.contains(&e1),
6336 "e1 was not modified and should not be detected as changed"
6337 );
6338 }
6339
6340 #[test]
6341 fn test_change_detection_only_checks_queried_components() {
6342 let mut world = World::default();
6343 let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
6344
6345 world.step();
6346
6347 world.get_velocity_mut(entity).unwrap().x = 99.0;
6348
6349 let mut pos_changed_count = 0;
6350 world.for_each_mut_changed(POSITION, 0, |_entity, _table, _idx| {
6351 pos_changed_count += 1;
6352 });
6353 assert_eq!(
6354 pos_changed_count, 0,
6355 "Changing velocity should not trigger position change detection"
6356 );
6357
6358 let mut vel_changed_count = 0;
6359 world.for_each_mut_changed(VELOCITY, 0, |_entity, _table, _idx| {
6360 vel_changed_count += 1;
6361 });
6362 assert_eq!(
6363 vel_changed_count, 1,
6364 "Changing velocity should trigger velocity change detection"
6365 );
6366 }
6367
6368 #[test]
6369 fn test_change_detection_multi_component_query_only_relevant() {
6370 let mut world = World::default();
6371 let e1 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
6372 let e2 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
6373
6374 world.step();
6375
6376 world.get_position_mut(e1).unwrap().x = 5.0;
6377 world.get_health_mut(e2).unwrap().value = 50.0;
6378
6379 let mut changed_entities = Vec::new();
6380 world.for_each_mut_changed(POSITION | VELOCITY, 0, |entity, _table, _idx| {
6381 changed_entities.push(entity);
6382 });
6383
6384 assert!(
6385 changed_entities.contains(&e1),
6386 "e1 had position changed, which is in the query"
6387 );
6388 assert!(
6389 !changed_entities.contains(&e2),
6390 "e2 only had health changed, which is NOT in the query"
6391 );
6392 }
6393
6394 #[test]
6395 fn test_entity_count() {
6396 let mut world = World::default();
6397 assert_eq!(world.entity_count(), 0);
6398
6399 world.spawn_entities(POSITION, 5);
6400 assert_eq!(world.entity_count(), 5);
6401
6402 world.spawn_entities(VELOCITY, 3);
6403 assert_eq!(world.entity_count(), 8);
6404
6405 let entities = world.spawn_entities(POSITION | VELOCITY, 2);
6406 assert_eq!(world.entity_count(), 10);
6407
6408 world.despawn_entities(&[entities[0]]);
6409 assert_eq!(world.entity_count(), 9);
6410 }
6411
6412 #[test]
6413 fn test_entity_count_matches_get_all_entities() {
6414 let mut world = World::default();
6415
6416 world.spawn_entities(POSITION, 10);
6417 world.spawn_entities(VELOCITY, 5);
6418 world.spawn_entities(POSITION | VELOCITY | HEALTH, 3);
6419
6420 assert_eq!(world.entity_count(), world.get_all_entities().len());
6421 }
6422
6423 #[test]
6424 fn test_entity_query_iter_size_hint() {
6425 let mut world = World::default();
6426
6427 world.spawn_entities(POSITION | VELOCITY, 5);
6428 world.spawn_entities(POSITION, 3);
6429 world.spawn_entities(VELOCITY | HEALTH, 2);
6430
6431 let iter = world.query_entities(POSITION);
6432 let (lower, upper) = iter.size_hint();
6433 assert_eq!(lower, 8);
6434 assert_eq!(upper, Some(8));
6435
6436 let count = world.query_entities(POSITION).count();
6437 assert_eq!(count, 8);
6438
6439 let iter = world.query_entities(POSITION | VELOCITY);
6440 let (lower, upper) = iter.size_hint();
6441 assert_eq!(lower, 5);
6442 assert_eq!(upper, Some(5));
6443 }
6444
6445 #[test]
6446 fn test_entity_query_iter_size_hint_decreases() {
6447 let mut world = World::default();
6448 world.spawn_entities(POSITION, 3);
6449
6450 let mut iter = world.query_entities(POSITION);
6451 assert_eq!(iter.size_hint(), (3, Some(3)));
6452
6453 iter.next();
6454 assert_eq!(iter.size_hint(), (2, Some(2)));
6455
6456 iter.next();
6457 assert_eq!(iter.size_hint(), (1, Some(1)));
6458
6459 iter.next();
6460 assert_eq!(iter.size_hint(), (0, Some(0)));
6461 }
6462
6463 #[test]
6464 fn test_component_query_iter_size_hint() {
6465 let mut world = World::default();
6466 world.spawn_entities(POSITION, 4);
6467 world.spawn_entities(POSITION | VELOCITY, 3);
6468
6469 let iter = world.query_position();
6470 let (lower, upper) = iter.size_hint();
6471 assert_eq!(lower, 7);
6472 assert_eq!(upper, Some(7));
6473
6474 let count = world.query_position().count();
6475 assert_eq!(count, 7);
6476 }
6477
6478 #[test]
6479 fn test_query_entities_collect_preallocates() {
6480 let mut world = World::default();
6481 world.spawn_entities(POSITION | VELOCITY, 100);
6482
6483 let entities: Vec<Entity> = world.query_entities(POSITION | VELOCITY).collect();
6484 assert_eq!(entities.len(), 100);
6485 }
6486
6487 #[test]
6488 fn test_despawn_multiple_same_table_change_vecs() {
6489 let mut world = World::default();
6490 let entities = world.spawn_entities(POSITION | VELOCITY, 6);
6491
6492 world.step();
6493
6494 world.get_position_mut(entities[0]).unwrap().x = 10.0;
6495 world.get_position_mut(entities[5]).unwrap().x = 50.0;
6496
6497 world.despawn_entities(&[entities[1], entities[3]]);
6498
6499 for table in &world.tables {
6500 if table.mask & POSITION != 0 {
6501 let entity_count = table.entity_indices.len();
6502 assert_eq!(table.position.len(), entity_count);
6503 assert_eq!(table.position_changed.len(), entity_count);
6504 }
6505 if table.mask & VELOCITY != 0 {
6506 let entity_count = table.entity_indices.len();
6507 assert_eq!(table.velocity.len(), entity_count);
6508 assert_eq!(table.velocity_changed.len(), entity_count);
6509 }
6510 }
6511
6512 let remaining = world.get_all_entities();
6513 assert_eq!(remaining.len(), 4);
6514 assert!(world.get_position(entities[0]).is_some());
6515 assert!(world.get_position(entities[5]).is_some());
6516 }
6517
6518 mod cfg_test {
6519 #[derive(Default, Debug, Clone)]
6520 pub struct BaseComponent {
6521 pub value: i32,
6522 }
6523
6524 #[derive(Default, Debug, Clone)]
6525 pub struct DebugComponent {
6526 pub debug_value: i32,
6527 }
6528
6529 crate::ecs! {
6530 CfgWorld {
6531 base: BaseComponent => BASE,
6532 #[cfg(debug_assertions)]
6533 debug_only: DebugComponent => DEBUG_ONLY,
6534 }
6535 CfgResources {
6536 counter: i32,
6537 }
6538 }
6539
6540 #[test]
6541 fn test_cfg_attribute_on_components() {
6542 let mut world = CfgWorld::default();
6543
6544 let entities = world.spawn_entities(BASE, 3);
6545 assert_eq!(entities.len(), 3);
6546
6547 world.set_base(entities[0], BaseComponent { value: 10 });
6548 assert_eq!(world.get_base(entities[0]).unwrap().value, 10);
6549
6550 world.resources.counter = 42;
6551 assert_eq!(world.resources.counter, 42);
6552
6553 let mut count = 0;
6554 world.query().with(BASE).iter(|_entity, _table, _idx| {
6555 count += 1;
6556 });
6557 assert_eq!(count, 3);
6558
6559 world.query_mut().with(BASE).iter(|_entity, table, idx| {
6560 table.base[idx].value += 1;
6561 });
6562 assert_eq!(world.get_base(entities[0]).unwrap().value, 11);
6563
6564 let mut without_count = 0;
6565 world
6566 .query()
6567 .with(BASE)
6568 .without(0)
6569 .iter(|_entity, _table, _idx| {
6570 without_count += 1;
6571 });
6572 assert_eq!(without_count, 3);
6573
6574 let mut without_mut_count = 0;
6575 world
6576 .query_mut()
6577 .with(BASE)
6578 .without(0)
6579 .iter(|_entity, _table, _idx| {
6580 without_mut_count += 1;
6581 });
6582 assert_eq!(without_mut_count, 3);
6583
6584 let mut iter_count = 0;
6585 world.iter_base(|_entity, _base| {
6586 iter_count += 1;
6587 });
6588 assert_eq!(iter_count, 3);
6589
6590 world.iter_base_mut(|_entity, base| {
6591 base.value *= 2;
6592 });
6593 assert_eq!(world.get_base(entities[0]).unwrap().value, 22);
6594
6595 #[cfg(debug_assertions)]
6596 {
6597 world.set_debug_only(entities[0], DebugComponent { debug_value: 42 });
6598 assert_eq!(world.get_debug_only(entities[0]).unwrap().debug_value, 42);
6599
6600 let debug_entities = world.spawn_entities(DEBUG_ONLY, 2);
6601 assert_eq!(debug_entities.len(), 2);
6602
6603 let mut debug_count = 0;
6604 world.iter_debug_only(|_entity, _debug| {
6605 debug_count += 1;
6606 });
6607 assert_eq!(debug_count, 3);
6608
6609 world.iter_debug_only_mut(|_entity, debug| {
6610 debug.debug_value += 1;
6611 });
6612 assert_eq!(world.get_debug_only(entities[0]).unwrap().debug_value, 43);
6613 }
6614 }
6615 }
6616
6617 mod multi_world_test {
6618 use super::*;
6619
6620 #[derive(Default, Debug, Clone, PartialEq)]
6621 pub struct Position {
6622 pub x: f32,
6623 pub y: f32,
6624 }
6625
6626 #[derive(Default, Debug, Clone, PartialEq)]
6627 pub struct Velocity {
6628 pub x: f32,
6629 pub y: f32,
6630 }
6631
6632 #[derive(Default, Debug, Clone, PartialEq)]
6633 pub struct Sprite {
6634 pub id: u32,
6635 }
6636
6637 #[derive(Default, Debug, Clone, PartialEq)]
6638 pub struct Color {
6639 pub r: f32,
6640 pub g: f32,
6641 pub b: f32,
6642 }
6643
6644 #[derive(Debug, Clone)]
6645 #[allow(dead_code)]
6646 pub struct CollisionEvent {
6647 pub entity_a: Entity,
6648 pub entity_b: Entity,
6649 }
6650
6651 crate::ecs! {
6652 GameEcs {
6653 CoreWorld {
6654 position: Position => MW_POSITION,
6655 velocity: Velocity => MW_VELOCITY,
6656 }
6657 RenderWorld {
6658 sprite: Sprite => MW_SPRITE,
6659 color: Color => MW_COLOR,
6660 }
6661 }
6662 Tags {
6663 player => MW_PLAYER,
6664 }
6665 Events {
6666 collision: CollisionEvent,
6667 }
6668 GameResources {
6669 delta_time: f32,
6670 }
6671 }
6672
6673 #[test]
6674 fn test_multi_world_spawn() {
6675 let mut ecs = GameEcs::default();
6676 let entity = ecs.spawn();
6677 assert_eq!(entity.id, 0);
6678 assert_eq!(entity.generation, 0);
6679 }
6680
6681 #[test]
6682 fn test_multi_world_per_world_components() {
6683 let mut ecs = GameEcs::default();
6684 let entity = ecs.spawn();
6685
6686 ecs.core_world
6687 .add_components(entity, MW_POSITION | MW_VELOCITY);
6688 ecs.core_world
6689 .set_position(entity, Position { x: 1.0, y: 2.0 });
6690
6691 assert_eq!(ecs.core_world.get_position(entity).unwrap().x, 1.0);
6692 assert_eq!(ecs.core_world.get_position(entity).unwrap().y, 2.0);
6693 assert!(ecs.core_world.get_velocity(entity).is_some());
6694 }
6695
6696 #[test]
6697 fn test_multi_world_cross_world_access() {
6698 let mut ecs = GameEcs::default();
6699 let entity = ecs.spawn();
6700
6701 ecs.core_world
6702 .set_position(entity, Position { x: 5.0, y: 10.0 });
6703 ecs.render_world.set_sprite(entity, Sprite { id: 42 });
6704
6705 assert_eq!(ecs.core_world.get_position(entity).unwrap().x, 5.0);
6706 assert_eq!(ecs.render_world.get_sprite(entity).unwrap().id, 42);
6707 }
6708
6709 #[test]
6710 fn test_multi_world_split_borrow() {
6711 let mut ecs = GameEcs::default();
6712 let entity = ecs.spawn();
6713
6714 ecs.core_world
6715 .set_position(entity, Position { x: 1.0, y: 2.0 });
6716 ecs.render_world.set_sprite(entity, Sprite { id: 1 });
6717
6718 let GameEcs {
6719 core_world,
6720 render_world,
6721 ..
6722 } = &mut ecs;
6723 core_world.for_each_mut(MW_POSITION, 0, |entity, table, idx| {
6724 if let Some(sprite) = render_world.get_sprite(entity) {
6725 table.position[idx].x += sprite.id as f32;
6726 }
6727 });
6728
6729 assert_eq!(ecs.core_world.get_position(entity).unwrap().x, 2.0);
6730 }
6731
6732 #[test]
6733 fn test_multi_world_despawn_cascades() {
6734 let mut ecs = GameEcs::default();
6735 let entity = ecs.spawn();
6736
6737 ecs.core_world
6738 .set_position(entity, Position { x: 1.0, y: 2.0 });
6739 ecs.render_world.set_sprite(entity, Sprite { id: 1 });
6740 ecs.add_player(entity);
6741
6742 assert!(ecs.core_world.get_position(entity).is_some());
6743 assert!(ecs.render_world.get_sprite(entity).is_some());
6744 assert!(ecs.has_player(entity));
6745
6746 ecs.despawn(entity);
6747
6748 assert!(ecs.core_world.get_position(entity).is_none());
6749 assert!(ecs.render_world.get_sprite(entity).is_none());
6750 assert!(!ecs.has_player(entity));
6751 }
6752
6753 #[test]
6754 fn test_multi_world_entity_in_one_world_only() {
6755 let mut ecs = GameEcs::default();
6756 let entity = ecs.spawn();
6757
6758 ecs.core_world
6759 .set_position(entity, Position { x: 1.0, y: 2.0 });
6760
6761 assert!(ecs.core_world.get_position(entity).is_some());
6762 assert!(ecs.render_world.get_sprite(entity).is_none());
6763 assert!(ecs.render_world.get_color(entity).is_none());
6764 }
6765
6766 #[test]
6767 fn test_multi_world_entity_in_no_world() {
6768 let mut ecs = GameEcs::default();
6769 let entity = ecs.spawn();
6770
6771 assert!(ecs.core_world.get_position(entity).is_none());
6772 assert!(ecs.render_world.get_sprite(entity).is_none());
6773 }
6774
6775 #[test]
6776 fn test_multi_world_entity_builder() {
6777 let mut ecs = GameEcs::default();
6778 let entities = EntityBuilder::new()
6779 .with_position(Position { x: 1.0, y: 2.0 })
6780 .with_sprite(Sprite { id: 42 })
6781 .spawn(&mut ecs, 2);
6782
6783 assert_eq!(entities.len(), 2);
6784 assert_eq!(ecs.core_world.get_position(entities[0]).unwrap().x, 1.0);
6785 assert_eq!(ecs.core_world.get_position(entities[1]).unwrap().x, 1.0);
6786 assert_eq!(ecs.render_world.get_sprite(entities[0]).unwrap().id, 42);
6787 assert_eq!(ecs.render_world.get_sprite(entities[1]).unwrap().id, 42);
6788 }
6789
6790 #[test]
6791 fn test_multi_world_tags() {
6792 let mut ecs = GameEcs::default();
6793 let entity = ecs.spawn();
6794
6795 ecs.add_player(entity);
6796 assert!(ecs.has_player(entity));
6797
6798 let players: Vec<Entity> = ecs.query_player().collect();
6799 assert_eq!(players.len(), 1);
6800
6801 ecs.remove_player(entity);
6802 assert!(!ecs.has_player(entity));
6803 }
6804
6805 #[test]
6806 fn test_multi_world_tag_split_borrow() {
6807 let mut ecs = GameEcs::default();
6808 let e1 = ecs.spawn();
6809 let e2 = ecs.spawn();
6810
6811 ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
6812 ecs.core_world.set_position(e2, Position { x: 2.0, y: 0.0 });
6813 ecs.add_player(e1);
6814
6815 let GameEcs {
6816 core_world, player, ..
6817 } = &mut ecs;
6818 let mut player_positions = Vec::new();
6819 core_world.for_each_mut(MW_POSITION, 0, |entity, table, idx| {
6820 if player.contains(&entity) {
6821 player_positions.push(table.position[idx].clone());
6822 }
6823 });
6824
6825 assert_eq!(player_positions.len(), 1);
6826 assert_eq!(player_positions[0].x, 1.0);
6827 }
6828
6829 #[test]
6830 fn test_multi_world_events() {
6831 let mut ecs = GameEcs::default();
6832 let e1 = ecs.spawn();
6833 let e2 = ecs.spawn();
6834
6835 ecs.send_collision(CollisionEvent {
6836 entity_a: e1,
6837 entity_b: e2,
6838 });
6839
6840 let events: Vec<_> = ecs.collect_collision();
6841 assert_eq!(events.len(), 1);
6842 assert_eq!(events[0].entity_a, e1);
6843 }
6844
6845 #[test]
6846 fn test_multi_world_resources() {
6847 let mut ecs = GameEcs::default();
6848 ecs.resources.delta_time = 0.016;
6849 assert_eq!(ecs.resources.delta_time, 0.016);
6850 }
6851
6852 #[test]
6853 fn test_multi_world_schedule() {
6854 let mut ecs = GameEcs::default();
6855 let entity = ecs.spawn();
6856 ecs.core_world
6857 .set_position(entity, Position { x: 0.0, y: 0.0 });
6858 ecs.core_world
6859 .set_velocity(entity, Velocity { x: 1.0, y: 2.0 });
6860 ecs.resources.delta_time = 0.1;
6861
6862 let mut schedule: Schedule<GameEcs> = Schedule::new();
6863 schedule.push("physics", |ecs: &mut GameEcs| {
6864 let dt = ecs.resources.delta_time;
6865 ecs.core_world
6866 .for_each_mut(MW_POSITION | MW_VELOCITY, 0, |_entity, table, idx| {
6867 table.position[idx].x += table.velocity[idx].x * dt;
6868 table.position[idx].y += table.velocity[idx].y * dt;
6869 });
6870 });
6871
6872 schedule.run(&mut ecs);
6873
6874 let pos = ecs.core_world.get_position(entity).unwrap();
6875 assert!((pos.x - 0.1).abs() < f32::EPSILON);
6876 assert!((pos.y - 0.2).abs() < f32::EPSILON);
6877 }
6878
6879 #[test]
6880 fn test_multi_world_mask_independence() {
6881 assert_eq!(MW_POSITION, 1 << 0);
6882 assert_eq!(MW_VELOCITY, 1 << 1);
6883 assert_eq!(MW_SPRITE, 1 << 0);
6884 assert_eq!(MW_COLOR, 1 << 1);
6885 }
6886
6887 #[test]
6888 fn test_multi_world_command_buffer() {
6889 let mut ecs = GameEcs::default();
6890 let entity = ecs.spawn();
6891
6892 ecs.core_world
6893 .set_position(entity, Position { x: 0.0, y: 0.0 });
6894
6895 ecs.queue_set_position(entity, Position { x: 10.0, y: 20.0 });
6896 ecs.queue_add_player(entity);
6897
6898 assert_eq!(ecs.core_world.get_position(entity).unwrap().x, 0.0);
6899 assert!(!ecs.has_player(entity));
6900
6901 ecs.apply_commands();
6902
6903 assert_eq!(ecs.core_world.get_position(entity).unwrap().x, 10.0);
6904 assert!(ecs.has_player(entity));
6905 }
6906
6907 #[test]
6908 fn test_multi_world_step() {
6909 let mut ecs = GameEcs::default();
6910
6911 assert_eq!(ecs.core_world.current_tick(), 0);
6912 assert_eq!(ecs.render_world.current_tick(), 0);
6913
6914 ecs.step();
6915
6916 assert_eq!(ecs.core_world.current_tick(), 1);
6917 assert_eq!(ecs.render_world.current_tick(), 1);
6918 }
6919
6920 #[test]
6921 fn test_multi_world_for_each_within_world() {
6922 let mut ecs = GameEcs::default();
6923 let e1 = ecs.spawn();
6924 let e2 = ecs.spawn();
6925
6926 ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
6927 ecs.core_world.set_position(e2, Position { x: 2.0, y: 0.0 });
6928 ecs.core_world
6929 .set_velocity(e2, Velocity { x: 10.0, y: 0.0 });
6930
6931 let mut count = 0;
6932 ecs.core_world
6933 .for_each(MW_POSITION, 0, |_entity, _table, _idx| {
6934 count += 1;
6935 });
6936 assert_eq!(count, 2);
6937
6938 count = 0;
6939 ecs.core_world
6940 .for_each(MW_POSITION | MW_VELOCITY, 0, |_entity, _table, _idx| {
6941 count += 1;
6942 });
6943 assert_eq!(count, 1);
6944 }
6945
6946 #[test]
6947 fn test_multi_world_query_builder() {
6948 let mut ecs = GameEcs::default();
6949 let e1 = ecs.spawn();
6950 let e2 = ecs.spawn();
6951
6952 ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
6953 ecs.core_world.set_position(e2, Position { x: 2.0, y: 0.0 });
6954 ecs.core_world
6955 .set_velocity(e2, Velocity { x: 10.0, y: 0.0 });
6956
6957 let mut count = 0;
6958 ecs.core_world
6959 .query()
6960 .with(MW_POSITION)
6961 .without(MW_VELOCITY)
6962 .iter(|_entity, _table, _idx| {
6963 count += 1;
6964 });
6965 assert_eq!(count, 1);
6966 }
6967
6968 #[test]
6969 fn test_multi_world_generational_reuse() {
6970 let mut ecs = GameEcs::default();
6971
6972 let e1 = ecs.spawn();
6973 ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
6974
6975 ecs.despawn(e1);
6976 assert!(ecs.core_world.get_position(e1).is_none());
6977
6978 let e2 = ecs.spawn();
6979 assert_eq!(e2.id, e1.id);
6980 assert_eq!(e2.generation, e1.generation + 1);
6981
6982 assert!(ecs.core_world.get_position(e1).is_none());
6983 }
6984
6985 #[test]
6986 fn test_multi_world_ghost_entity_guard() {
6987 let mut ecs = GameEcs::default();
6988 let e1 = ecs.spawn();
6989 ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
6990 ecs.despawn(e1);
6991
6992 let e2 = ecs.spawn();
6993 assert_eq!(e2.id, e1.id);
6994
6995 ecs.core_world.set_position(e2, Position { x: 5.0, y: 5.0 });
6996 assert_eq!(ecs.core_world.get_position(e2).unwrap().x, 5.0);
6997
6998 assert!(ecs.core_world.get_position(e1).is_none());
6999 }
7000
7001 #[test]
7002 fn test_multi_world_for_each_with_tags() {
7003 let mut ecs = GameEcs::default();
7004 let e1 = ecs.spawn();
7005 let e2 = ecs.spawn();
7006 let e3 = ecs.spawn();
7007
7008 ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
7009 ecs.core_world.set_position(e2, Position { x: 2.0, y: 0.0 });
7010 ecs.core_world.set_position(e3, Position { x: 3.0, y: 0.0 });
7011
7012 ecs.add_player(e1);
7013 ecs.add_player(e3);
7014
7015 let GameEcs {
7016 core_world, player, ..
7017 } = &ecs;
7018
7019 let mut count = 0;
7020 core_world.for_each_with_tags(
7021 MW_POSITION,
7022 0,
7023 &[player],
7024 &[],
7025 |_entity, _table, _idx| {
7026 count += 1;
7027 },
7028 );
7029 assert_eq!(count, 2);
7030
7031 count = 0;
7032 core_world.for_each_with_tags(
7033 MW_POSITION,
7034 0,
7035 &[],
7036 &[player],
7037 |_entity, _table, _idx| {
7038 count += 1;
7039 },
7040 );
7041 assert_eq!(count, 1);
7042 }
7043
7044 #[test]
7045 fn test_multi_world_for_each_mut_with_tags() {
7046 let mut ecs = GameEcs::default();
7047 let e1 = ecs.spawn();
7048 let e2 = ecs.spawn();
7049
7050 ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
7051 ecs.core_world.set_position(e2, Position { x: 2.0, y: 0.0 });
7052
7053 ecs.add_player(e1);
7054
7055 let player_set = ecs.player.clone();
7056 ecs.core_world.for_each_mut_with_tags(
7057 MW_POSITION,
7058 0,
7059 &[&player_set],
7060 &[],
7061 |_entity, table, idx| {
7062 table.position[idx].x += 100.0;
7063 },
7064 );
7065
7066 assert_eq!(ecs.core_world.get_position(e1).unwrap().x, 101.0);
7067 assert_eq!(ecs.core_world.get_position(e2).unwrap().x, 2.0);
7068 }
7069 }
7070}