bevy_mod_ffi_guest 0.2.0

FFI utilities for Bevy guests
Documentation
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) };
    }
}