use crate::UiContext; use bevy::{ ecs::{ component::ComponentId, event::{Event, EventIterator, Events, ManualEventReader}, query::{QueryState, ReadOnlyWorldQuery}, system::{Query, Resource}, world::{unsafe_world_cell::UnsafeWorldCell, World}, }, utils::{HashMap, HashSet}, }; use dioxus::{ core::{ScopeId, ScopeState}, hooks::use_on_destroy, }; use std::any::TypeId; #[derive(Default)] pub(crate) struct EcsSubscriptions { pub resources: Box>>, #[allow(clippy::type_complexity)] pub events: Box bool>, HashSet)>>, pub world_and_queries: Box>, } #[derive(Clone)] pub(crate) struct EcsContext { pub world: *mut World, } impl EcsContext { #[allow(clippy::mut_from_ref)] pub fn get_world(cx: &ScopeState) -> &mut World { unsafe { &mut *cx .consume_context::() .expect("Must be used from a dioxus component within a DioxusUiRoot bevy component") .world } } } pub fn use_world(cx: &ScopeState) -> &World { let world = EcsContext::get_world(cx); let scope_id = cx.scope_id(); let subscription_manager = *cx.use_hook(|| { let subscription_manager = &mut world .non_send_resource_mut::() .subscriptions .world_and_queries; subscription_manager.insert(scope_id); Box::as_mut(subscription_manager) as *mut HashSet }); use_on_destroy(cx, move || { unsafe { &mut *subscription_manager }.remove(&scope_id); }); world } pub fn use_resource(cx: &ScopeState) -> &T { let world = EcsContext::get_world(cx); let resource_id = world.components().resource_id::().unwrap(); let scope_id = cx.scope_id(); let subscription_manager = *cx.use_hook(|| { let subscription_manager = &mut world .non_send_resource_mut::() .subscriptions .resources; subscription_manager .entry(resource_id) .or_default() .insert(scope_id); Box::as_mut(subscription_manager) as *mut HashMap> }); use_on_destroy(cx, move || { let subscription_manager = &mut unsafe { &mut *subscription_manager }; let resource_subscriptions = subscription_manager.get_mut(&resource_id).unwrap(); resource_subscriptions.remove(&scope_id); if resource_subscriptions.is_empty() { subscription_manager.remove(&resource_id); } }); world.resource() } pub fn use_query(cx: &ScopeState) -> UseQuery<'_, Q, ()> where Q: ReadOnlyWorldQuery, { use_query_filtered(cx) } pub fn use_query_filtered(cx: &ScopeState) -> UseQuery<'_, Q, F> where Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery, { let world = EcsContext::get_world(cx); let scope_id = cx.scope_id(); let subscription_manager = *cx.use_hook(|| { let subscription_manager = &mut world .non_send_resource_mut::() .subscriptions .world_and_queries; subscription_manager.insert(scope_id); Box::as_mut(subscription_manager) as *mut HashSet }); use_on_destroy(cx, move || { unsafe { &mut *subscription_manager }.remove(&scope_id); }); UseQuery { query_state: QueryState::new(world), world_cell: world.as_unsafe_world_cell(), } } pub fn use_event_reader(cx: &ScopeState) -> EventIterator<'_, E> { // TODO: Register the subscription let event_reader = cx.use_hook(|| ManualEventReader::default()); let events = EcsContext::get_world(cx).resource::>(); event_reader.read(events) } pub struct UseQuery<'a, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery> { query_state: QueryState, world_cell: UnsafeWorldCell<'a>, } impl<'a, Q, F> UseQuery<'a, Q, F> where Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery, { pub fn query(&self) -> Query { unsafe { Query::new( self.world_cell, &self.query_state, self.world_cell.last_change_tick(), self.world_cell.change_tick(), true, ) } } }