bevy_mod_ffi_host_sys 0.2.0

Low-level FFI bindings for bevy_mod_ffi hosts
Documentation
use crate::{SharedRegistry, SharedSystemState};
use bevy::{
    ecs::{
        entity::Entity, event::Event, observer::On, prelude::*, system::DynSystemParam,
        world::World,
    },
    prelude::*,
    reflect::TypePath,
};
use bevy_mod_ffi_core::{dyn_system_param, system_state, world, RunObserverFn};
use std::{any::Any, ffi::CStr, marker::PhantomData, slice, sync::Arc};

#[derive(Clone)]
pub struct LibraryHandle(pub Arc<dyn Any + Send + Sync>);

#[derive(EntityEvent, Clone, Copy)]
pub struct EntityEventWrapper<E> {
    pub entity: Entity,
    pub inner: E,
}

pub trait Observable: Send + Sync + 'static {
    fn type_path(&self) -> &'static str;

    fn observe(
        &self,
        world: &mut World,
        state: Box<SharedSystemState>,
        f_ptr: usize,
        run_observer_fn: RunObserverFn,
        library_handle: LibraryHandle,
    ) -> Entity;

    fn observe_entity(
        &self,
        entity: EntityWorldMut,
        state: Box<SharedSystemState>,
        f_ptr: usize,
        run_observer_fn: RunObserverFn,
        library_handle: LibraryHandle,
    );

    fn trigger(&self, world: &mut World, event_data: &[u8]);

    fn trigger_for_entity(&self, entity: EntityWorldMut, event_data: &[u8]);
}

pub struct ObservableOf<E> {
    _marker: PhantomData<E>,
}

impl<E> ObservableOf<E> {
    pub fn new() -> Self {
        Self {
            _marker: PhantomData,
        }
    }
}

impl<E> Default for ObservableOf<E> {
    fn default() -> Self {
        Self::new()
    }
}

impl<E: Event + Clone + Copy + TypePath> Observable for ObservableOf<E>
where
    for<'a> E::Trigger<'a>: Default,
{
    fn type_path(&self) -> &'static str {
        E::type_path()
    }

    fn observe(
        &self,
        world: &mut World,
        state: Box<SharedSystemState>,
        f_ptr: usize,
        run_observer_fn: RunObserverFn,
        library_handle: LibraryHandle,
    ) -> Entity {
        let observer_system =
            state.build_any_system(move |on: On<E>, params: Vec<DynSystemParam>| {
                let _ = &library_handle;

                let mut param_ptrs: Vec<*mut dyn_system_param> = Vec::new();
                for param in params {
                    let boxed = Box::new(param);
                    param_ptrs.push(Box::into_raw(boxed) as *mut dyn_system_param);
                }
                let len = param_ptrs.len();
                let pointers_ptr = param_ptrs.as_ptr();

                let event_ptr = on.event() as *const E as *const u8;

                unsafe {
                    run_observer_fn(f_ptr as _, pointers_ptr, len, event_ptr as _);
                };
            });

        world.add_observer(observer_system).id()
    }

    fn trigger(&self, world: &mut World, event_data: &[u8]) {
        let event = unsafe { *(event_data.as_ptr() as *const E) };
        world.trigger(event);
    }

    fn observe_entity(
        &self,
        mut entity: EntityWorldMut,
        state: Box<SharedSystemState>,
        f_ptr: usize,
        run_observer_fn: RunObserverFn,
        library_handle: LibraryHandle,
    ) {
        let observer_system = state.build_any_system(
            move |on: On<EntityEventWrapper<E>>, params: Vec<DynSystemParam>| {
                let _ = &library_handle;

                let mut param_ptrs: Vec<*mut dyn_system_param> = Vec::new();
                for param in params {
                    param_ptrs.push(Box::into_raw(Box::new(param)) as *mut dyn_system_param);
                }

                let len = param_ptrs.len();
                let params_ptr = param_ptrs.into_boxed_slice();

                let event_ptr = &on.event().inner as *const E as *const u8;
                unsafe {
                    run_observer_fn(f_ptr as _, params_ptr.as_ptr(), len, event_ptr as _);
                };
            },
        );
        entity.observe(observer_system);
    }

    fn trigger_for_entity(&self, mut entity: EntityWorldMut, event_data: &[u8]) {
        let inner = unsafe { *(event_data.as_ptr() as *const E) };
        entity.trigger(|entity| EntityEventWrapper { entity, inner });
    }
}

#[derive(Resource, Clone)]
pub struct CurrentLibraryHandle(pub Option<LibraryHandle>);

#[unsafe(no_mangle)]
pub unsafe extern "C" fn bevy_system_state_build_on(
    world_ptr: *mut world,
    state_ptr: *mut system_state,
    event_name_ptr: *const u8,
    event_name_len: usize,
    f_ptr: *mut (),
    run_observer_fn: RunObserverFn,
) -> bool {
    let world = unsafe { &mut *(world_ptr as *mut World) };
    let state: Box<SharedSystemState> = unsafe { Box::from_raw(state_ptr as _) };

    let event_name_bytes = unsafe { slice::from_raw_parts(event_name_ptr, event_name_len) };
    let event_name = match CStr::from_bytes_with_nul(event_name_bytes) {
        Ok(cstr) => match cstr.to_str() {
            Ok(s) => s,
            Err(_) => return false,
        },
        Err(_) => return false,
    };

    let Some(library_handle) = world
        .get_resource::<CurrentLibraryHandle>()
        .and_then(|h| h.0.clone())
    else {
        return false;
    };

    let mut registry = match world.remove_resource::<SharedRegistry>() {
        Some(r) => r,
        None => return false,
    };

    if let Some(event_ops) = registry.events.remove(event_name) {
        let observer_entity = event_ops.observe(
            world,
            state,
            f_ptr as usize,
            run_observer_fn,
            library_handle,
        );
        registry.register_observer(observer_entity);

        let key = event_ops.type_path();
        registry.events.insert(key, event_ops);
        world.insert_resource(registry);

        true
    } else {
        world.insert_resource(registry);
        false
    }
}