Skip to content

Instantly share code, notes, and snippets.

@stepancheg
Created November 27, 2023 08:09
Show Gist options
  • Save stepancheg/64d76e78599a5dec32595cf18195bf45 to your computer and use it in GitHub Desktop.
Save stepancheg/64d76e78599a5dec32595cf18195bf45 to your computer and use it in GitHub Desktop.
use std::collections::HashMap;
use std::ops::Deref;
use std::ops::DerefMut;
use bevy::app::App;
use bevy::ecs::schedule::InternedScheduleLabel;
use bevy::ecs::schedule::ScheduleLabel;
use bevy::prelude::{IntoSystem, IntoSystemConfigs};
use crate::bevy_util::auto_deps::res::{read_in_system, write_out_system};
use crate::bevy_util::auto_deps::value::AutoSystemValue;
use crate::bevy_util::auto_deps::value::AutoSystemValueId;
#[derive(Default)]
struct AutoSystemValueStatus {
in_filled: bool,
out_filled: bool,
schedule: Option<InternedScheduleLabel>,
}
impl AutoSystemValueStatus {
fn set_schedule_label(&mut self, schedule: InternedScheduleLabel) {
if let Some(schedule_old) = self.schedule {
assert_eq!(schedule_old, schedule);
} else {
self.schedule = Some(schedule);
}
}
}
pub(crate) struct AppBuilder {
pub(crate) app: App,
auto_deps: HashMap<AutoSystemValueId, AutoSystemValueStatus>,
}
impl AppBuilder {
pub(crate) fn new() -> Self {
Self::with_app(App::new())
}
pub(crate) fn with_app(app: App) -> Self {
AppBuilder {
app,
auto_deps: HashMap::new(),
}
}
pub(crate) fn add_system_with_deps<In, Out, Marker>(
&mut self,
schedule: impl ScheduleLabel,
system: impl IntoSystem<In, Out, Marker>,
) where
In: AutoSystemValue,
Out: AutoSystemValue,
{
let schedule = schedule.intern();
In::init_resource(&mut self.app);
if let Some(in_t) = In::make_label() {
let status = self.auto_deps.entry(in_t).or_default();
status.set_schedule_label(schedule);
assert!(!status.in_filled);
status.in_filled = true;
}
if let Some(out_t) = Out::make_label() {
let status = self.auto_deps.entry(out_t).or_default();
status.set_schedule_label(schedule);
assert!(!status.out_filled);
status.out_filled = true;
}
let mut system = IntoSystemConfigs::into_configs(read_in_system.pipe(system).pipe(write_out_system));
if let Some(in_t) = In::make_label() {
system = system.after(in_t.label);
}
if let Some(out_t) = Out::make_label() {
system = system.before(out_t.label);
}
self.app.add_systems(schedule, system);
}
pub(crate) fn build(self) -> App {
for (_t, status) in self.auto_deps {
assert!(status.in_filled);
assert!(status.out_filled);
}
self.app
}
pub(crate) fn run(self) {
self.build().run();
}
}
impl Deref for AppBuilder {
type Target = App;
fn deref(&self) -> &Self::Target {
&self.app
}
}
impl DerefMut for AppBuilder {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.app
}
}
#[cfg(test)]
mod tests {
use bevy::app::Startup;
use bevy::prelude::In;
use crate::bevy_util::auto_deps::builder::AppBuilder;
use crate::bevy_util::auto_deps::value::AutoSystemValueHolder;
#[test]
fn test() {
fn system1() -> AutoSystemValueHolder<u32> {
println!("system1");
AutoSystemValueHolder(1)
}
fn system2(In(AutoSystemValueHolder(x)): In<AutoSystemValueHolder<u32>>) -> AutoSystemValueHolder<String> {
println!("system2");
assert_eq!(1, x);
AutoSystemValueHolder("hello".to_owned())
}
fn system3(
In(AutoSystemValueHolder(x)): In<AutoSystemValueHolder<String>>,
) {
println!("system3");
assert_eq!("hello", x);
}
let mut app = AppBuilder::new();
app.add_system_with_deps(Startup, system3);
app.add_system_with_deps(Startup, system1);
app.add_system_with_deps(Startup, system2);
app.run();
}
}
use std::any::TypeId;
use std::mem;
use bevy::prelude::In;
use bevy::prelude::ResMut;
use bevy::prelude::Resource;
use crate::bevy_util::auto_deps::value::AutoSystemValue;
#[derive(Resource)]
pub(crate) struct AutoRes<T: AutoSystemValue>(pub(crate) Option<T>);
impl<T: AutoSystemValue> Default for AutoRes<T> {
fn default() -> Self {
AutoRes(None)
}
}
pub(crate) fn write_out_system<V: AutoSystemValue>(In(v): In<V>, res: Option<ResMut<AutoRes<V>>>) {
if V::is_unit() {
return;
} else {
let mut res = res.unwrap();
assert!(res.0.is_none());
res.0 = Some(v);
}
}
pub(crate) fn read_in_system<V: AutoSystemValue>(res: Option<ResMut<AutoRes<V>>>) -> V {
if TypeId::of::<V>() == TypeId::of::<()>() {
// We could avoid it with some metaprogramming, but this is cheaper to write.
unsafe { mem::transmute_copy(&()) }
} else {
mem::take(&mut res.unwrap().0).unwrap()
}
}
use bevy::app::App;
use bevy::ecs::schedule::InternedSystemSet;
use bevy::prelude::SystemSet;
use crate::bevy_util::auto_deps::res::AutoRes;
use crate::bevy_util::system_set::system_type_set::ForkSystemTypeSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct AutoSystemValueId {
pub(crate) label: InternedSystemSet,
}
pub(crate) trait AutoSystemValue: Send + Sync + 'static {
fn is_unit() -> bool;
fn make_label() -> Option<AutoSystemValueId>;
fn init_resource(app: &mut App);
}
impl AutoSystemValue for () {
fn is_unit() -> bool {
true
}
fn make_label() -> Option<AutoSystemValueId> {
None
}
fn init_resource(_app: &mut App) {}
}
pub(crate) struct AutoSystemValueHolder<T>(pub(crate) T);
impl<T: Send + Sync + 'static> AutoSystemValue for AutoSystemValueHolder<T> {
fn is_unit() -> bool {
false
}
fn make_label() -> Option<AutoSystemValueId> {
Some(AutoSystemValueId {
label: ForkSystemTypeSet::<T>::new().intern(),
})
}
fn init_resource(app: &mut App) {
app.init_resource::<AutoRes<Self>>();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment