use crate::{
system::{
IntoEntityObserverSystem, OnEntity, ParamBuilder, ParamCursor, SharedEvent, System,
SystemParam,
},
world::World,
};
use bevy_ecs::{
component::ComponentId,
entity::Entity,
ptr::{Ptr, PtrMut},
};
use bevy_mod_ffi_core::{entity_world_mut, filtered_entity_mut};
use bevy_mod_ffi_guest_sys::{self, system::ObserverClosure};
use std::{ffi::CString, marker::PhantomData, ptr::NonNull};
pub struct EntityWorldMut<'w> {
id: Entity,
ptr: *mut entity_world_mut,
world: &'w mut World,
}
impl<'w> EntityWorldMut<'w> {
pub(crate) unsafe fn from_ptr(
id: Entity,
ptr: *mut entity_world_mut,
world: &'w mut World,
) -> Self {
Self { id, ptr, world }
}
pub fn id(&self) -> Entity {
self.id
}
pub fn observe<E, Marker, S>(self, observer: S) -> Self
where
E: SharedEvent + 'static,
S: IntoEntityObserverSystem<E, Marker>,
S::System: 'static,
<S::System as System>::Param: 'static,
{
let mut system = observer.into_system();
let mut builder = ParamBuilder::new();
let mut state =
<<S::System as System>::Param as SystemParam>::build(self.world, &mut builder);
let state_ptr = builder.build(self.world);
let event_name = E::type_path();
let event_name_cstring = CString::new(event_name).unwrap();
let event_name_bytes = event_name_cstring.as_bytes_with_nul();
let observer_boxed: ObserverClosure = Box::new(move |params, event_ptr| {
let mut param_cursor = ParamCursor::new(params);
let params = unsafe {
<<S::System as System>::Param as SystemParam>::get_param(
&mut state,
&mut param_cursor,
)
};
let event = unsafe { &*(event_ptr as *const E) };
system.run(
OnEntity {
entity: self.id,
event,
},
params,
);
});
let success = unsafe {
bevy_mod_ffi_guest_sys::world::bevy_entity_world_mut_observe(
self.ptr,
state_ptr,
event_name_bytes.as_ptr(),
event_name_bytes.len(),
Box::into_raw(Box::new(observer_boxed)) as _,
bevy_mod_ffi_guest_sys::system::bevy_guest_run_observer,
)
};
assert!(
success,
"Failed to add entity observer for event: {}",
event_name
);
self
}
pub fn trigger<E: SharedEvent>(self, event: E) -> Self {
let event_name = E::type_path();
let event_name_cstring = CString::new(event_name).unwrap();
let event_name_bytes = event_name_cstring.as_bytes_with_nul();
let event_bytes = bytemuck::bytes_of(&event);
let success = unsafe {
bevy_mod_ffi_guest_sys::world::bevy_entity_world_mut_trigger(
self.ptr,
event_name_bytes.as_ptr(),
event_name_bytes.len(),
event_bytes.as_ptr(),
event_bytes.len(),
)
};
assert!(
success,
"Failed to trigger event for entity: {}",
event_name
);
self
}
}
impl Drop for EntityWorldMut<'_> {
fn drop(&mut self) {
unsafe { bevy_mod_ffi_guest_sys::world::entity::bevy_world_entity_mut_drop(self.ptr) }
}
}
pub struct FilteredEntityMut<'w> {
id: Entity,
ptr: *mut filtered_entity_mut,
_marker: PhantomData<&'w mut World>,
}
impl<'w> FilteredEntityMut<'w> {
pub(crate) unsafe fn from_ptr(id: Entity, ptr: *mut filtered_entity_mut) -> Self {
Self {
id,
ptr,
_marker: PhantomData,
}
}
pub fn id(&self) -> Entity {
self.id
}
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
let mut out_ptr = std::ptr::null_mut();
let success = unsafe {
bevy_mod_ffi_guest_sys::world::entity::bevy_filtered_entity_mut_get_component(
self.ptr,
component_id.index(),
&mut out_ptr,
)
};
if !success {
return None;
}
let ptr = NonNull::new(out_ptr)?;
Some(unsafe { Ptr::new(ptr) })
}
pub fn get_mut_by_id(&mut self, component_id: ComponentId) -> Option<PtrMut<'w>> {
let mut out_ptr = std::ptr::null_mut();
let success = unsafe {
bevy_mod_ffi_guest_sys::world::entity::bevy_filtered_entity_mut_get_component_mut(
self.ptr,
component_id.index(),
&mut out_ptr,
)
};
if !success || out_ptr.is_null() {
return None;
}
let ptr = NonNull::new(out_ptr)?;
Some(unsafe { PtrMut::new(ptr) })
}
}
impl Drop for FilteredEntityMut<'_> {
fn drop(&mut self) {
unsafe { bevy_mod_ffi_guest_sys::world::entity::bevy_filtered_entity_mut_drop(self.ptr) };
}
}