use bevy::{
ecs::{
component::{ComponentCloneBehavior, ComponentDescriptor, ComponentId, StorageType},
lifecycle::HookContext,
world::DeferredWorld,
},
platform::collections::HashMap,
prelude::*,
ptr::OwningPtr,
};
use core::alloc::Layout;
#[derive(Component)]
pub struct MyMutableComponent(bool);
#[derive(Component)]
#[component(immutable)]
pub struct MyImmutableComponent(bool);
fn demo_1(world: &mut World) {
let mut entity = world.spawn((MyMutableComponent(false), MyImmutableComponent(false)));
let mut my_mutable_component = entity.get_mut::<MyMutableComponent>().unwrap();
my_mutable_component.0 = true;
let mut my_immutable_component = entity.take::<MyImmutableComponent>().unwrap();
my_immutable_component.0 = true;
entity.insert(my_immutable_component);
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Component, Reflect)]
#[reflect(Hash, Component)]
#[component(
immutable,
on_insert = on_insert_name,
on_replace = on_replace_name,
)]
pub struct Name(pub &'static str);
#[derive(Resource, Default)]
struct NameIndex {
name_to_entity: HashMap<Name, Entity>,
}
impl NameIndex {
fn get_entity(&self, name: &'static str) -> Option<Entity> {
self.name_to_entity.get(&Name(name)).copied()
}
}
fn on_insert_name(mut world: DeferredWorld<'_>, HookContext { entity, .. }: HookContext) {
let Some(&name) = world.entity(entity).get::<Name>() else {
unreachable!("Insert hook guarantees `Name` is available on entity")
};
let Some(mut index) = world.get_resource_mut::<NameIndex>() else {
return;
};
index.name_to_entity.insert(name, entity);
}
fn on_replace_name(mut world: DeferredWorld<'_>, HookContext { entity, .. }: HookContext) {
let Some(&name) = world.entity(entity).get::<Name>() else {
unreachable!("Replace hook guarantees `Name` is available on entity")
};
let Some(mut index) = world.get_resource_mut::<NameIndex>() else {
return;
};
index.name_to_entity.remove(&name);
}
fn demo_2(world: &mut World) {
world.init_resource::<NameIndex>();
let alyssa = world.spawn(Name("Alyssa")).id();
let javier = world.spawn(Name("Javier")).id();
let index = world.resource::<NameIndex>();
assert_eq!(index.get_entity("Alyssa"), Some(alyssa));
assert_eq!(index.get_entity("Javier"), Some(javier));
world.entity_mut(javier).insert(Name("Steven"));
let steven = javier;
let index = world.resource::<NameIndex>();
assert_eq!(index.get_entity("Javier"), None);
assert_eq!(index.get_entity("Steven"), Some(steven));
}
#[expect(
unsafe_code,
reason = "Unsafe code is needed to work with dynamic components"
)]
fn demo_3(world: &mut World) {
let my_dynamic_components = [("Foo", 1), ("Bar", 2), ("Baz", 4)];
let my_registered_components = my_dynamic_components
.into_iter()
.map(|(name, size)| {
let descriptor = unsafe {
ComponentDescriptor::new_with_layout(
name.to_string(),
StorageType::Table,
Layout::array::<u8>(size).unwrap(),
None,
false,
ComponentCloneBehavior::Default,
)
};
(name, size, descriptor)
})
.map(|(name, size, descriptor)| {
let component_id = world.register_component_with_descriptor(descriptor);
(name, size, component_id)
})
.collect::<Vec<(&str, usize, ComponentId)>>();
let mut entity = world.spawn_empty();
for (_name, size, component_id) in &my_registered_components {
let data = core::iter::repeat_n(0, *size).collect::<Vec<u8>>();
OwningPtr::make(data, |ptr| {
unsafe {
entity.insert_by_id(*component_id, ptr);
}
});
}
for (_name, _size, component_id) in &my_registered_components {
assert!(entity.get_by_id(*component_id).is_ok());
assert!(entity.get_mut_by_id(*component_id).is_err());
}
}
fn main() {
App::new()
.add_systems(Startup, demo_1)
.add_systems(Startup, demo_2)
.add_systems(Startup, demo_3)
.run();
}