1use std::any::Any;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum StorageType {
5 Table,
6 SparseSet,
7}
8
9pub trait Component: 'static + Any + Send + Sync + Clone {
10 fn storage_type() -> StorageType {
11 StorageType::Table
12 }
13}
14
15#[macro_export]
16macro_rules! impl_component {
17 ($($t:ty),+ $(,)?) => {
18 $(
19 impl $crate::Component for $t {}
20 )+
21 };
22 ($($t:ty),+ ; $storage:expr) => {
23 $(
24 impl $crate::Component for $t {
25 fn storage_type() -> $crate::component::StorageType {
26 $storage
27 }
28 }
29 )+
30 };
31}
32
33#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
35pub struct Parent(pub u32);
36
37#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
38pub struct Children(pub Vec<u32>);
39
40#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
41pub struct EntityName(pub String);
42
43impl EntityName {
44 pub fn new(name: &str) -> Self {
45 Self(name.to_string())
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
50pub struct IsHidden;
51
52#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
53pub struct IsDeleted;
54
55#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
56pub struct PrefabRequest(pub String);
57
58impl PrefabRequest {
59 pub fn new(name: &str) -> Self {
60 Self(name.to_string())
61 }
62 pub fn name(&self) -> &str {
63 &self.0
64 }
65}
66
67impl std::fmt::Display for EntityName {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 write!(f, "{}", self.0)
70 }
71}
72
73#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
74pub struct MeshSource(pub String);
75
76#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
77pub struct MaterialSource {
78 pub albedo: [f32; 4],
79 pub roughness: f32,
80 pub metallic: f32,
81 pub unlit: f32,
82 pub texture_source: Option<String>,
83}
84
85impl_component!(Parent, Children, EntityName, IsHidden, PrefabRequest, IsDeleted, MeshSource, MaterialSource);
86
87pub trait Bundle {
92 fn get_infos() -> Vec<crate::archetype::ComponentInfo>;
93 unsafe fn write_to_archetype(self, arch: &mut crate::archetype::Archetype, _row: usize, tick: u32);
98 fn apply(self, _world: &mut crate::world::World, _entity: crate::entity::Entity) where Self: Sized {}
99}
100
101pub struct DynamicBundle<B: Bundle, C: Component> {
102 pub bundle: B,
103 pub component: C,
104}
105
106impl<B: Bundle, C: Component> Bundle for DynamicBundle<B, C> {
107 fn get_infos() -> Vec<crate::archetype::ComponentInfo> {
108 let mut infos = B::get_infos();
109 infos.push(crate::archetype::ComponentInfo::of::<C>());
110 infos
111 }
112
113 unsafe fn write_to_archetype(self, arch: &mut crate::archetype::Archetype, row: usize, tick: u32) {
114 self.bundle.write_to_archetype(arch, row, tick);
115 let col = arch.get_column_mut(std::any::TypeId::of::<C>()).unwrap();
116 if col.len() <= row {
117 col.push_raw(&self.component as *const _ as *const u8, tick);
118 std::mem::forget(self.component);
119 } else {
120 let ptr = col.get_mut_ptr(row) as *mut C;
121 std::ptr::write(ptr, self.component);
122 *col.ticks_ptr_mut().add(row) = crate::archetype::ComponentTicks::new(tick);
123 }
124 }
125}
126
127pub trait BundleExt: Bundle + Sized {
128 fn with<C: Component>(self, component: C) -> DynamicBundle<Self, C> {
129 DynamicBundle { bundle: self, component }
130 }
131}
132
133impl<T: Bundle> BundleExt for T {}
134
135impl<T: Component> Bundle for T {
136 fn get_infos() -> Vec<crate::archetype::ComponentInfo> {
137 vec![crate::archetype::ComponentInfo::of::<T>()]
138 }
139
140 fn apply(self, world: &mut crate::world::World, entity: crate::entity::Entity) {
141 world.add_component(entity, self);
142 }
143
144 unsafe fn write_to_archetype(self, arch: &mut crate::archetype::Archetype, row: usize, tick: u32) {
145 let col = arch.get_column_mut(std::any::TypeId::of::<T>()).unwrap_or_else(|| {
146 panic!(
147 "Component column for `{}` missing in Archetype. The bundle fast-path \
148 (write_to_archetype) only handles Table-storage components; SparseSet \
149 components must be routed via World::add_component. spawn_batch already \
150 falls back for sparse bundles — reaching here means another bundle path \
151 wrote a sparse component into the archetype.",
152 std::any::type_name::<T>()
153 )
154 });
155 if col.len() <= row {
156 col.push_raw(&self as *const _ as *const u8, tick);
157 std::mem::forget(self);
158 } else {
159 let ptr = col.get_mut_ptr(row) as *mut T;
160 std::ptr::write(ptr, self);
161 *col.ticks_ptr_mut().add(row) = crate::archetype::ComponentTicks::new(tick);
162 }
163 }
164}
165
166macro_rules! impl_bundle_tuple {
167 ($($name:ident),*) => {
168 #[allow(non_snake_case)]
169 impl<$($name: crate::component::Bundle),*> Bundle for ($($name,)*) {
170 fn get_infos() -> Vec<crate::archetype::ComponentInfo> {
171 let mut infos = Vec::new();
172 $(
173 infos.extend(<$name as crate::component::Bundle>::get_infos());
174 )*
175 infos
176 }
177
178 fn apply(self, world: &mut crate::world::World, entity: crate::entity::Entity) {
179 let ($($name,)*) = self;
180 $(
181 $name.apply(world, entity);
182 )*
183 }
184
185 unsafe fn write_to_archetype(self, arch: &mut crate::archetype::Archetype, row: usize, tick: u32) {
186 let ($($name,)*) = self;
187 $(
188 $name.write_to_archetype(arch, row, tick);
189 )*
190 }
191 }
192 };
193}
194
195impl_bundle_tuple!(A);
196impl_bundle_tuple!(A, B);
197impl_bundle_tuple!(A, B, C);
198impl_bundle_tuple!(A, B, C, D);
199impl_bundle_tuple!(A, B, C, D, E);
200impl_bundle_tuple!(A, B, C, D, E, F);
201impl_bundle_tuple!(A, B, C, D, E, F, G);
202impl_bundle_tuple!(A, B, C, D, E, F, G, H);
203impl_bundle_tuple!(A, B, C, D, E, F, G, H, I);
204impl_bundle_tuple!(A, B, C, D, E, F, G, H, I, J);
205impl_bundle_tuple!(A, B, C, D, E, F, G, H, I, J, K);
206impl_bundle_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
207impl_bundle_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
208impl_bundle_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
209impl_bundle_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
210impl_bundle_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);