aboutsummaryrefslogtreecommitdiff
path: root/azalea-ecs/src
diff options
context:
space:
mode:
Diffstat (limited to 'azalea-ecs/src')
-rw-r--r--azalea-ecs/src/lib.rs144
1 files changed, 144 insertions, 0 deletions
diff --git a/azalea-ecs/src/lib.rs b/azalea-ecs/src/lib.rs
new file mode 100644
index 00000000..44579c5d
--- /dev/null
+++ b/azalea-ecs/src/lib.rs
@@ -0,0 +1,144 @@
+#![feature(trait_alias)]
+
+//! Re-export important parts of `bevy_ecs` and `bevy_app` and make them more
+//! compatible with Azalea.
+//!
+//! This is completely compatible with `bevy_ecs`, so it won't cause issues if
+//! you use plugins meant for Bevy.
+//!
+//! Changes:
+//! - Add [`TickPlugin`], [`TickStage`] and [`AppTickExt`]
+//! - Change the macros to use azalea/azalea_ecs instead of bevy/bevy_ecs
+//! - Rename bevy_ecs::world::World to azalea_ecs::ecs::Ecs
+//! - Re-export `bevy_app` in the `app` module.
+
+use std::time::{Duration, Instant};
+
+pub mod ecs {
+ pub use bevy_ecs::world::World as Ecs;
+ pub use bevy_ecs::world::{EntityMut, EntityRef, Mut};
+}
+pub mod component {
+ pub use azalea_ecs_macros::Component;
+ pub use bevy_ecs::component::{ComponentId, ComponentStorage, Components, TableStorage};
+
+ // we do this because re-exporting Component would re-export the macro as well,
+ // which is bad (since we have our own Component macro)
+ // instead, we have to do this so Component is a trait alias and the original
+ // impl-able trait is still available as BevyComponent
+ pub trait Component = bevy_ecs::component::Component;
+ pub use bevy_ecs::component::Component as BevyComponent;
+}
+pub mod bundle {
+ pub use azalea_ecs_macros::Bundle;
+ pub trait Bundle = bevy_ecs::bundle::Bundle;
+ pub use bevy_ecs::bundle::Bundle as BevyBundle;
+}
+pub mod system {
+ pub use azalea_ecs_macros::Resource;
+ pub use bevy_ecs::system::{
+ Command, Commands, EntityCommands, Query, Res, ResMut, SystemState,
+ };
+ pub trait Resource = bevy_ecs::system::Resource;
+ pub use bevy_ecs::system::Resource as BevyResource;
+}
+pub use bevy_app as app;
+pub use bevy_ecs::{entity, event, ptr, query, schedule, storage};
+
+use app::{App, CoreStage, Plugin};
+use bevy_ecs::schedule::*;
+use ecs::Ecs;
+
+pub struct TickPlugin {
+ /// How often a tick should happen. 50 milliseconds by default. Set to 0 to
+ /// tick every update.
+ pub tick_interval: Duration,
+}
+impl Plugin for TickPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_stage_before(
+ CoreStage::Update,
+ TickLabel,
+ TickStage {
+ interval: self.tick_interval,
+ next_tick: Instant::now(),
+ stage: Box::new(SystemStage::parallel()),
+ },
+ );
+ }
+}
+impl Default for TickPlugin {
+ fn default() -> Self {
+ Self {
+ tick_interval: Duration::from_millis(50),
+ }
+ }
+}
+
+#[derive(StageLabel)]
+struct TickLabel;
+
+/// A [`Stage`] that runs every 50 milliseconds.
+pub struct TickStage {
+ pub interval: Duration,
+ pub next_tick: Instant,
+ stage: Box<dyn Stage>,
+}
+
+impl Stage for TickStage {
+ fn run(&mut self, ecs: &mut Ecs) {
+ // if the interval is 0, that means it runs every tick
+ if self.interval.is_zero() {
+ self.stage.run(ecs);
+ return;
+ }
+ // keep calling run until it's caught up
+ // TODO: Minecraft bursts up to 10 ticks and then skips, we should too (but
+ // check the source so we do it right)
+ while Instant::now() > self.next_tick {
+ self.next_tick += self.interval;
+ self.stage.run(ecs);
+ }
+ }
+}
+
+pub trait AppTickExt {
+ fn add_tick_system_set(&mut self, system_set: SystemSet) -> &mut App;
+ fn add_tick_system<Params>(&mut self, system: impl IntoSystemDescriptor<Params>) -> &mut App;
+}
+
+impl AppTickExt for App {
+ /// Adds a set of ECS systems that will run every 50 milliseconds.
+ ///
+ /// Note that you should NOT have `EventReader`s in tick systems, as this
+ /// will make them sometimes be missed.
+ fn add_tick_system_set(&mut self, system_set: SystemSet) -> &mut App {
+ let tick_stage = self
+ .schedule
+ .get_stage_mut::<TickStage>(TickLabel)
+ .expect("Tick Stage not found");
+ let stage = tick_stage
+ .stage
+ .downcast_mut::<SystemStage>()
+ .expect("Fixed Timestep sub-stage is not a SystemStage");
+ stage.add_system_set(system_set);
+ self
+ }
+
+ /// Adds a new ECS system that will run every 50 milliseconds.
+ ///
+ /// Note that you should NOT have `EventReader`s in tick systems, as this
+ /// will make them sometimes be missed.
+ fn add_tick_system<Params>(&mut self, system: impl IntoSystemDescriptor<Params>) -> &mut App {
+ let tick_stage = self
+ .schedule
+ .get_stage_mut::<TickStage>(TickLabel)
+ .expect("Tick Stage not found");
+ let stage = tick_stage
+ .stage
+ .downcast_mut::<SystemStage>()
+ .expect("Fixed Timestep sub-stage is not a SystemStage");
+ stage.add_system(system);
+ self
+ }
+}