immutable_components/
immutable_components.rs1use bevy::{
4 ecs::{
5 component::{ComponentCloneBehavior, ComponentDescriptor, ComponentId, StorageType},
6 lifecycle::HookContext,
7 world::DeferredWorld,
8 },
9 platform::collections::HashMap,
10 prelude::*,
11 ptr::OwningPtr,
12};
13use core::alloc::Layout;
14
15#[derive(Component)]
18pub struct MyMutableComponent(bool);
19
20#[derive(Component)]
27#[component(immutable)]
28pub struct MyImmutableComponent(bool);
29
30fn demo_1(world: &mut World) {
31 let mut entity = world.spawn((MyMutableComponent(false), MyImmutableComponent(false)));
33
34 let mut my_mutable_component = entity.get_mut::<MyMutableComponent>().unwrap();
36 my_mutable_component.0 = true;
37
38 let mut my_immutable_component = entity.take::<MyImmutableComponent>().unwrap();
44 my_immutable_component.0 = true;
45 entity.insert(my_immutable_component);
46}
47
48#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Component, Reflect)]
50#[reflect(Hash, Component)]
51#[component(
52 immutable,
53 on_insert = on_insert_name,
57 on_replace = on_replace_name,
58)]
59pub struct Name(pub &'static str);
60
61#[derive(Resource, Default)]
63struct NameIndex {
64 name_to_entity: HashMap<Name, Entity>,
65}
66
67impl NameIndex {
68 fn get_entity(&self, name: &'static str) -> Option<Entity> {
69 self.name_to_entity.get(&Name(name)).copied()
70 }
71}
72
73fn on_insert_name(mut world: DeferredWorld<'_>, HookContext { entity, .. }: HookContext) {
78 let Some(&name) = world.entity(entity).get::<Name>() else {
79 unreachable!("Insert hook guarantees `Name` is available on entity")
80 };
81 let Some(mut index) = world.get_resource_mut::<NameIndex>() else {
82 return;
83 };
84
85 index.name_to_entity.insert(name, entity);
86}
87
88fn on_replace_name(mut world: DeferredWorld<'_>, HookContext { entity, .. }: HookContext) {
93 let Some(&name) = world.entity(entity).get::<Name>() else {
94 unreachable!("Replace hook guarantees `Name` is available on entity")
95 };
96 let Some(mut index) = world.get_resource_mut::<NameIndex>() else {
97 return;
98 };
99
100 index.name_to_entity.remove(&name);
101}
102
103fn demo_2(world: &mut World) {
104 world.init_resource::<NameIndex>();
106
107 let alyssa = world.spawn(Name("Alyssa")).id();
109 let javier = world.spawn(Name("Javier")).id();
110
111 let index = world.resource::<NameIndex>();
113
114 assert_eq!(index.get_entity("Alyssa"), Some(alyssa));
115 assert_eq!(index.get_entity("Javier"), Some(javier));
116
117 world.entity_mut(javier).insert(Name("Steven"));
119
120 let steven = javier;
122
123 let index = world.resource::<NameIndex>();
125
126 assert_eq!(index.get_entity("Javier"), None);
127 assert_eq!(index.get_entity("Steven"), Some(steven));
128}
129
130#[expect(
132 unsafe_code,
133 reason = "Unsafe code is needed to work with dynamic components"
134)]
135fn demo_3(world: &mut World) {
136 let my_dynamic_components = [("Foo", 1), ("Bar", 2), ("Baz", 4)];
140
141 let my_registered_components = my_dynamic_components
144 .into_iter()
145 .map(|(name, size)| {
146 let descriptor = unsafe {
150 ComponentDescriptor::new_with_layout(
151 name.to_string(),
152 StorageType::Table,
153 Layout::array::<u8>(size).unwrap(),
154 None,
155 false,
156 ComponentCloneBehavior::Default,
157 )
158 };
159
160 (name, size, descriptor)
161 })
162 .map(|(name, size, descriptor)| {
163 let component_id = world.register_component_with_descriptor(descriptor);
164
165 (name, size, component_id)
166 })
167 .collect::<Vec<(&str, usize, ComponentId)>>();
168
169 let mut entity = world.spawn_empty();
171
172 for (_name, size, component_id) in &my_registered_components {
173 let data = core::iter::repeat_n(0, *size).collect::<Vec<u8>>();
175
176 OwningPtr::make(data, |ptr| {
177 unsafe {
181 entity.insert_by_id(*component_id, ptr);
182 }
183 });
184 }
185
186 for (_name, _size, component_id) in &my_registered_components {
187 assert!(entity.get_by_id(*component_id).is_ok());
189
190 assert!(entity.get_mut_by_id(*component_id).is_err());
192
193 }
195}
196
197fn main() {
198 App::new()
199 .add_systems(Startup, demo_1)
200 .add_systems(Startup, demo_2)
201 .add_systems(Startup, demo_3)
202 .run();
203}