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