Skip to content

Commit

Permalink
Migrate to nested systems
Browse files Browse the repository at this point in the history
  • Loading branch information
spectria-limina committed Dec 12, 2024
1 parent 4c9ede5 commit 84f9c87
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 33 deletions.
6 changes: 6 additions & 0 deletions src/ecs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,9 @@ where S: System
fn validate_param(&mut self, world: &World) -> bool { self.sys.validate_param(world) }
fn default_system_sets(&self) -> Vec<InternedSystemSet> {self.sys.default_system_sets() }
}

pub struct EcsExtensionsPlugin;
impl Plugin for EcsExtensionsPlugin {
fn build(&self, app: &mut App) { app.init_resource::<NestedSystemRegistry>(); }
}
pub fn plugin() -> EcsExtensionsPlugin { EcsExtensionsPlugin }
6 changes: 1 addition & 5 deletions src/ecs/nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ impl<Data, Arg: SystemInput> HasInnerArg for NestedWith<'_, Data, Arg> {
type InnerArg = Arg;
}


struct SystemWithData<Sys, Data, Arg> {
sys: Sys,
data: Data,
Expand Down Expand Up @@ -126,9 +125,6 @@ where
// SAFETY: This is guaranteed safe by our only caller
let input: SystemIn<Sys> = (nested, self.data.clone(), unsafe { inner_arg.read() });
let out = unsafe { self.sys.run_unsafe(input, world) };
unsafe {
self.sys.queue_deferred(world.into_deferred());
}
Box::new(out)
})
}
Expand Down Expand Up @@ -277,7 +273,7 @@ impl NestedSystem<'_> {
}
self.registry.store[s.0] = Cached::Stored(sys);
// FIXME: Do we need to poison/abort if a panic comes through here? Figure that out.
// self.accesses.pop();
self.accesses.pop();
match out.downcast::<Out>() {
Ok(out) => *out,
Err(_) => panic!(
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ fn start(args: Args, primary_window: Window) -> eyre::Result<()> {
.add_plugins(arena::plugin())
.add_plugins(color::plugin())
.add_plugins(drag::plugin())
.add_plugins(ecs::plugin())
.add_plugins(player::plugin())
.add_plugins(player::window::plugin())
.add_plugins(waymark::plugin())
Expand Down
23 changes: 16 additions & 7 deletions src/player/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use bevy::{
prelude::*,
};
use bevy_egui::egui;
use itertools::Itertools;

use super::{job::Job, Player, PlayerSprite};
use crate::{
ecs::{EntityWorldExts, NestedSystemExts},
spawner::{self, panel::SpawnerPanel, Spawnable, Spawner},
widget::{egui_context, WidgetSystemId},
widget::{egui_context, Widget, WidgetSystemId},
};

const SIZE: f32 = 35.0;
Expand All @@ -35,6 +36,7 @@ impl Spawnable for PlayerSprite {

/// A window with controls to manipulate the waymarks.
#[derive(Debug, Default, Copy, Clone, Component, Reflect)]
#[component(on_add = Self::on_add)]
pub struct PlayerWindow;

impl PlayerWindow {
Expand All @@ -43,17 +45,24 @@ impl PlayerWindow {
/// Will panic if there is more than one camera.
pub fn show(world: &mut World) {
let ctx = egui_context(world);
let mut state =
SystemState::<(Query<Entity, With<PlayerWindow>>, Query<&Children>)>::new(world);
let mut state = SystemState::<(
Query<Entity, With<PlayerWindow>>,
Query<&Widget, With<SpawnerPanel<PlayerSprite>>>,
Query<&Children>,
)>::new(world);

let ewin = egui::Window::new("Players")
.default_width(4.0 * (PlayerSprite::size() + PlayerSprite::sep()).x);
ewin.show(&ctx, |ui| {
let (mut win_q, _parent_q) = state.get_mut(world);
let (mut win_q, panel_q, parent_q) = state.get_mut(world);
let win_id = win_q.single_mut();
let panel_sys_id: WidgetSystemId = todo!();

world.run_nested_with(panel_sys_id, ui);
let panel = panel_q
.iter_many(parent_q.children(win_id))
.copied()
.exactly_one()
.unwrap();
panel.show_world(world, ui);

state.apply(world);
});
Expand All @@ -72,7 +81,7 @@ impl PlayerWindow {
Job::Dragoon,
];

world.commands().queue(move |mut world: &mut World| {
world.commands().queue(move |world: &mut World| {
world.resource_scope(move |world: &mut World, asset_server: Mut<AssetServer>| {
world.entity_mut(id).with_children(move |window| {
window
Expand Down
23 changes: 12 additions & 11 deletions src/spawner/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{borrow::Cow, fmt::Debug, marker::PhantomData};
use std::{any::type_name, borrow::Cow, fmt::Debug, marker::PhantomData};

use bevy::{
ecs::{component::ComponentId, system::EntityCommands, world::DeferredWorld},
Expand All @@ -13,7 +13,7 @@ use itertools::Itertools;

use crate::{
ecs::{EntityExts, EntityExtsOf, NestedSystemExts},
widget::{WidgetCtx, WidgetSystemId},
widget::{widget, InitWidget, WidgetCtx, WidgetSystemId},
};

pub mod panel;
Expand Down Expand Up @@ -41,6 +41,7 @@ pub trait Spawnable: Component + Reflect + TypePath + Clone + PartialEq + Debug
#[derive(Debug, Clone, Component)]
#[component(on_add = Spawner::<T>::on_add)]
#[component(on_remove = Spawner::<T>::on_remove)]
#[require(InitWidget(|| widget!()))]
pub struct Spawner<T: Spawnable> {
pub target: T,
pub image: Handle<Image>,
Expand Down Expand Up @@ -147,8 +148,11 @@ impl<T: Spawnable> Spawner<T> {
WidgetCtx { ns: _ns, id, ui }: WidgetCtx,
spawner_q: Query<(&Spawner<T>, &SpawnerTextureId)>,
mut pointer_ev: EventWriter<PointerHits>,
) -> egui::Response {
let (spawner, texture_id) = spawner_q.get(id).unwrap();
) {
let (spawner, texture_id) = spawner_q
.get(id)
.expect("Spawner::show called without a Spawner");
debug!("Drawing Spawner<{:?}>: {:?}", type_name::<T>(), spawner);
let resp = ui.add(
egui::Image::new((
texture_id.0,
Expand All @@ -171,8 +175,6 @@ impl<T: Spawnable> Spawner<T> {
1_000_001.0,
));
}

resp
}
}

Expand Down Expand Up @@ -235,7 +237,7 @@ mod test {
ecs::{EntityWorldExts, NestedSystemExts},
testing::*,
waymark::Waymark,
widget::{egui_context, WidgetSystemId},
widget::{egui_context, Widget, WidgetSystemId},
};

#[derive(Default, Resource)]
Expand All @@ -249,10 +251,9 @@ mod test {
egui::Area::new("test".into())
.fixed_pos(pos)
.show(&ctx, |ui| {
let mut q = world.query_filtered::<Entity, With<Spawner<Waymark>>>();
let id = q.single(world);
let panel_sys_id: WidgetSystemId = todo!();
world.run_nested_with(panel_sys_id, ui);
let mut state = world.query_filtered::<&Widget, With<Spawner<Waymark>>>();
let widget = *state.single(world);
widget.show_world(world, ui);
});
}

Expand Down
16 changes: 10 additions & 6 deletions src/spawner/panel.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::marker::PhantomData;
use std::{any::type_name, marker::PhantomData};

use bevy::prelude::*;
use bevy_egui::egui;

use super::{Spawnable, Spawner};
use crate::{
ecs::{EntityWorldExts as _, NestedSystemExts},
widget::{widget, InitWidget, WidgetCtx, WidgetSystemId},
widget::{widget, InitWidget, Widget, WidgetCtx, WidgetSystemId},
};

#[derive(Component, derive_more::Debug, Reflect)]
Expand All @@ -19,7 +19,12 @@ pub struct SpawnerPanel<T: Spawnable> {
impl<T: Spawnable> SpawnerPanel<T> {
pub fn new() -> Self { Self { _ph: PhantomData } }

pub fn show(WidgetCtx { ns: _ns, id, ui }: WidgetCtx, world: &mut World) {
pub fn show(
WidgetCtx { ns, id, ui }: WidgetCtx,
spawner_q: Query<&Widget, With<Spawner<T>>>,
children_q: Query<&Children>,
) {
debug!("Drawing SpawnerPanel<{:?}>", type_name::<T>());
ui.add_space(T::sep().y);
let frame = egui::Frame {
outer_margin: egui::Margin::symmetric(T::sep().x, T::sep().y) / 2.0,
Expand All @@ -32,9 +37,8 @@ impl<T: Spawnable> SpawnerPanel<T> {
.with_main_align(egui::Align::Center),
|ui| {
ui.spacing_mut().item_spacing = egui::Vec2::new(T::sep().x, T::sep().y);
let spawners: Vec<WidgetSystemId> = todo!();
for id in spawners {
world.run_nested_with(id, ui);
for spawner in spawner_q.iter_many(children_q.children(id)) {
spawner.show(ns, ui);
}
},
)
Expand Down
16 changes: 12 additions & 4 deletions src/waymark/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ use bevy::{
prelude::*,
};
use bevy_egui::{egui, egui::TextEdit, EguiClipboard};
use itertools::Itertools;

use super::{insert_waymark, Preset, Waymark};
use crate::{
arena::Arena,
ecs::{EntityWorldExts, NestedSystemExts},
spawner::{self, panel::SpawnerPanel, Spawnable, Spawner},
widget::{egui_context, WidgetSystemId},
widget::{egui_context, Widget, WidgetSystemId},
};

const SPAWNER_SIZE: f32 = 40.0;
Expand All @@ -36,6 +37,7 @@ impl Spawnable for Waymark {

/// A window with controls to manipulate the waymarks.
#[derive(Debug, Default, Clone, Component, Reflect)]
#[component(on_add = Self::on_add)]
pub struct WaymarkWindow {
preset_name: String,
}
Expand All @@ -46,6 +48,7 @@ impl WaymarkWindow {
let ctx = egui_context(world);
let mut state = SystemState::<(
Query<(Entity, &mut WaymarkWindow)>,
Query<&Widget, With<SpawnerPanel<Waymark>>>,
Query<&Children>,
Commands,
ResMut<EguiClipboard>,
Expand All @@ -54,7 +57,8 @@ impl WaymarkWindow {
let ewin =
egui::Window::new("Waymarks").default_width(4.0 * (Waymark::size() + Waymark::sep()).x);
ewin.show(&ctx, |ui| {
let (mut win_q, _parent_q, mut commands, mut clipboard) = state.get_mut(world);
let (mut win_q, panel_q, children_q, mut commands, mut clipboard) =
state.get_mut(world);
let (win_id, mut win) = win_q.single_mut();

ui.horizontal(|ui| {
Expand Down Expand Up @@ -83,8 +87,12 @@ impl WaymarkWindow {
);
ui.separator();

let panel_sys_id: WidgetSystemId = todo!();
world.run_nested_with(panel_sys_id, ui);
let panel = panel_q
.iter_many(children_q.children(win_id))
.copied()
.exactly_one()
.unwrap();
panel.show_world(world, ui);

state.apply(world);
});
Expand Down
119 changes: 119 additions & 0 deletions src/widget/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use bevy::{
ecs::{
component::ComponentId,
system::{SystemParamItem, SystemState},
world::DeferredWorld,
},
prelude::*,
};
use bevy_egui::{
egui::{self, Ui},
EguiContexts,
};

use crate::ecs::{HasInnerArg, NestedSystem, NestedSystemExts, NestedSystemId};

// TODO: TEST TEST TEST
pub fn egui_contexts_scope<U, F: FnOnce(SystemParamItem<EguiContexts>) -> U>(
world: &mut World,
f: F,
) -> U {
let mut state = SystemState::<EguiContexts>::new(world);
f(state.get_mut(world))
}

pub fn egui_context(world: &mut World) -> egui::Context {
egui_contexts_scope(world, |mut contexts| contexts.ctx_mut().clone())
}

pub struct WidgetCtx<'a> {
pub ns: &'a mut NestedSystem<'a>,
pub id: Entity,
pub ui: &'a mut Ui,
}

impl SystemInput for WidgetCtx<'_> {
type Param<'i> = WidgetCtx<'i>;
type Inner<'i> = (&'i mut NestedSystem<'i>, Entity, &'i mut Ui);

fn wrap((ns, id, ui): Self::Inner<'_>) -> Self::Param<'_> { WidgetCtx { ns, id, ui } }
}
impl HasInnerArg for WidgetCtx<'_> {
type InnerArg = InMut<'static, Ui>;
}

pub type WidgetSystemId = NestedSystemId<InMut<'static, Ui>>;

#[derive(Debug, Copy, Clone)]
#[derive(Component)]
pub struct Widget(WidgetSystemId);

impl Widget {
pub fn show(&self, nested: &mut NestedSystem, ui: &mut Ui) {
nested.run_nested_with(self.0, ui)
}
pub fn show_world(&self, world: &mut World, ui: &mut Ui) { world.run_nested_with(self.0, ui) }
}

#[derive(Debug, Copy, Clone, Component)]
#[component(storage = "SparseSet")]
#[component(on_add = Self::init)]

pub struct InitWidget(pub fn(&mut World, Entity) -> WidgetSystemId);

impl InitWidget {
pub fn init(mut world: DeferredWorld, id: Entity, _: ComponentId) {
let this = *world.get::<Self>(id).unwrap();
world.commands().queue(move |world: &mut World| {
let nested_system_id = this.0(world, id);
world
.entity_mut(id)
.insert(Widget(nested_system_id))
.remove::<Self>();
});
}
}

#[macro_export]
macro_rules! widget {
() => {
$crate::widget!(Self::show)
};
($show:path) => {
$crate::widget::InitWidget(
|world: &mut World, id: Entity| -> $crate::widget::WidgetSystemId {
debug!(
"Registering widget {:?} {:?} with show function {:?}",
id,
::std::any::type_name::<Self>(),
::std::any::type_name_of_val(&$show)
);
$crate::ecs::NestedSystemRegistry::register_with_data(world, $show, id)
},
)
};
}
#[allow(unused)]
pub use crate::widget;

#[cfg(test)]
mod test {
use super::*;
use crate::ecs::NestedWith;

#[derive(Component)]
#[require(InitWidget(|| widget!()))]
struct Test;

impl Test {
pub fn show(NestedWith(_ns, _id, InMut(_ui)): NestedWith<Entity, InMut<Ui>>) {
// do ui stuff here i guess
}
}
}

pub struct WidgetPlugin;
impl Plugin for WidgetPlugin {
fn build(&self, _app: &mut App) { let _ = (); }
}
pub fn plugin() -> WidgetPlugin { WidgetPlugin }

0 comments on commit 84f9c87

Please sign in to comment.