//! Minimal ECS app wrapper to replace Bevy's App use bevy_ecs::message::{Message, Messages}; use bevy_ecs::prelude::*; use bevy_ecs::schedule::{IntoScheduleConfigs, ScheduleLabel, Schedules}; use bevy_ecs::system::ScheduleSystem; use std::fmt::Debug; #[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)] pub struct Startup; #[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)] pub struct Update; #[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)] pub struct Last; #[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)] pub struct Fixed; pub struct App { world: World, } impl App { pub fn new() -> Self { let mut world = World::new(); // Initialize schedules let mut schedules = Schedules::new(); schedules.insert(Schedule::new(Startup)); schedules.insert(Schedule::new(Update)); schedules.insert(Schedule::new(Last)); schedules.insert(Schedule::new(Fixed)); world.insert_resource(schedules); Self { world } } pub fn world(&self) -> &World { &self.world } pub fn world_mut(&mut self) -> &mut World { &mut self.world } pub fn insert_resource(&mut self, resource: R) -> &mut Self { self.world.insert_resource(resource); self } pub fn init_resource(&mut self) -> &mut Self { self.world.init_resource::(); self } pub fn insert_non_send_resource(&mut self, resource: R) -> &mut Self { self.world.insert_non_send_resource(resource); self } pub fn add_message(&mut self) -> &mut Self { if !self.world.contains_resource::>() { self.world.init_resource::>(); // Add system to update this message type each frame self.add_systems(Last, |mut messages: ResMut>| { messages.update(); }); } self } pub fn add_systems(&mut self, schedule: impl ScheduleLabel, systems: impl IntoScheduleConfigs) -> &mut Self { let mut schedules = self.world.resource_mut::(); if let Some(schedule_inst) = schedules.get_mut(schedule) { schedule_inst.add_systems(systems); } self } pub fn update(&mut self) { // Remove schedules temporarily to avoid resource_scope conflicts let mut schedules = self.world.remove_resource::().unwrap(); // Run Update schedule if let Some(schedule) = schedules.get_mut(Update) { schedule.run(&mut self.world); } // Run Last schedule (includes event updates) if let Some(schedule) = schedules.get_mut(Last) { schedule.run(&mut self.world); } // Re-insert schedules self.world.insert_resource(schedules); } pub fn run_startup(&mut self) { let _span = tracing::trace_span!("run_startup_schedule").entered(); // Remove schedules temporarily to avoid resource_scope conflicts let mut schedules = self.world.remove_resource::().unwrap(); // Run Startup schedule if let Some(schedule) = schedules.get_mut(Startup) { schedule.run(&mut self.world); } // Re-insert schedules self.world.insert_resource(schedules); } pub fn finish(&mut self) { // Finalize schedules let mut schedules = self.world.remove_resource::().unwrap(); let system_count: usize = schedules.iter().map(|(_, schedule)| schedule.systems().map(|iter| iter.count()).unwrap_or(0)).sum(); let _span = tracing::trace_span!("finish_schedules", system_count = system_count).entered(); for (_, schedule) in schedules.iter_mut() { schedule.graph_mut().initialize(&mut self.world); } self.world.insert_resource(schedules); } pub fn cleanup(&mut self) { // Any cleanup needed before running } } impl Default for App { fn default() -> Self { Self::new() } } /// Plugin trait for modular setup pub trait Plugin { fn build(&self, app: &mut App); }