bevy_cobweb/react/
system_command_spawning.rs

1//local shortcuts
2use crate::prelude::*;
3
4//third-party shortcuts
5use bevy::prelude::*;
6
7//standard shortcuts
8
9
10//-------------------------------------------------------------------------------------------------------------------
11
12/// Records a cleanup callback that can be injected into system commands for cleanup after the system command
13/// runs but before its `apply_deferred` is called.
14///
15/// For efficiency, only function pointer callbacks are supported.
16#[derive(Debug, Default, Copy, Clone)]
17pub struct SystemCommandCleanup
18{
19    cleanup: Option<fn(&mut World)>,
20}
21
22impl SystemCommandCleanup
23{
24    /// Makes a new system cleanup.
25    pub fn new(cleanup: fn(&mut World)) -> Self
26    {
27        Self{ cleanup: Some(cleanup) }
28    }
29
30    /// Runs the system cleanup on the world.
31    ///
32    /// Does nothing if no callback is stored.
33    pub(crate) fn run(self, world: &mut World)
34    {
35        let Some(cleanup) = self.cleanup else { return; };
36        (cleanup)(world);
37    }
38}
39
40//-------------------------------------------------------------------------------------------------------------------
41
42/// Owns a system command callback.
43///
44/// The callback should own the actual system that you want to run. The [`SystemCommandCleanup`] callback must be invoked
45/// between running your system and calling `apply_deferred` on that system.
46pub struct SystemCommandCallback
47{
48    inner: Box<dyn FnMut(&mut World, SystemCommandCleanup) + Send + Sync + 'static>,
49}
50
51impl SystemCommandCallback
52{
53    /// Makes a new system command callback from a system.
54    pub fn new<S, R: CobwebResult, M>(system: S) -> Self
55    where
56        S: IntoSystem<(), R, M> + Send + Sync + 'static
57    {
58        let mut callback = RawCallbackSystem::new(system);
59        let command = move |world: &mut World, cleanup: SystemCommandCleanup|
60        {
61            let result = callback.run_with_cleanup(world, (), move |world: &mut World| cleanup.run(world));
62            result.handle(world);
63        };
64        Self::with(command)
65    }
66
67    /// Makes a new system command callback from a pre-defined callback.
68    pub fn with(callback: impl FnMut(&mut World, SystemCommandCleanup) + Send + Sync + 'static) -> Self
69    {
70        Self{ inner: Box::new(callback) }
71    }
72
73    /// Runs the system command callback.
74    ///
75    /// The `cleanup` should be invoked between running the callback's inner system and
76    /// calling `apply_deferred` on the inner system.
77    pub(crate) fn run(&mut self, world: &mut World, cleanup: SystemCommandCleanup)
78    {
79        (self.inner)(world, cleanup);
80    }
81}
82
83//-------------------------------------------------------------------------------------------------------------------
84
85/// Stores a system command's callback.
86///
87/// We store the callback in an option in order to avoid archetype moves when taking/reinserting the callback in order to
88/// call it.
89#[derive(Component)]
90pub(crate) struct SystemCommandStorage
91{
92    callback: Option<SystemCommandCallback>,
93}
94
95impl SystemCommandStorage
96{
97    pub(crate) fn new(callback: SystemCommandCallback) -> Self
98    {
99        Self{ callback: Some(callback) }
100    }
101
102    pub(crate) fn insert(&mut self, callback: SystemCommandCallback)
103    {
104        self.callback = Some(callback);
105    }
106
107    pub(crate) fn take(&mut self) -> Option<SystemCommandCallback>
108    {
109        self.callback.take()
110    }
111}
112
113//-------------------------------------------------------------------------------------------------------------------
114
115/// Spawns a system as a [`SystemCommand`].
116///
117/// Systems are not initialized until they are first run.
118pub fn spawn_system_command<S, R: CobwebResult, M>(world: &mut World, system: S) -> SystemCommand
119where
120    S: IntoSystem<(), R, M> + Send + Sync + 'static,
121{
122    spawn_system_command_from(world, SystemCommandCallback::new(system))
123}
124
125//-------------------------------------------------------------------------------------------------------------------
126
127/// Spawns a [`SystemCommand`] from a pre-defined callback.
128pub fn spawn_system_command_from(world: &mut World, callback: SystemCommandCallback) -> SystemCommand
129{
130    SystemCommand(world.spawn(SystemCommandStorage::new(callback)).id())
131}
132
133//-------------------------------------------------------------------------------------------------------------------
134
135//todo: allow overwriting an existing command's callback
136
137//-------------------------------------------------------------------------------------------------------------------
138
139/// Spawns a ref-counted [`SystemCommand`] from a given raw system.
140///
141/// Systems are not initialized until they are first run.
142///
143/// Returns a cleanup handle. The system will be dropped when the last copy of the handle is dropped.
144///
145/// Panics if [`setup_auto_despawn()`](AutoDespawnAppExt::setup_auto_despawn) was not added to your app.
146pub fn spawn_rc_system_command<S, R: CobwebResult, M>(world: &mut World, system: S) -> AutoDespawnSignal
147where
148    S: IntoSystem<(), R, M> + Send + Sync + 'static,
149{
150    let system_command = spawn_system_command(world, system);
151    world.resource::<AutoDespawner>().prepare(*system_command)
152}
153
154//-------------------------------------------------------------------------------------------------------------------
155
156/// Spawns a ref-counted [`SystemCommand`] from a pre-defined callback.
157///
158/// Returns a cleanup handle. The system will be dropped when the last copy of the handle is dropped.
159///
160/// Panics if [`setup_auto_despawn()`](AutoDespawnAppExt::setup_auto_despawn) was not added to your app.
161pub fn spawn_rc_system_command_from(world: &mut World, callback: SystemCommandCallback) -> AutoDespawnSignal
162{
163    let system_command = spawn_system_command_from(world, callback);
164    world.resource::<AutoDespawner>().prepare(*system_command)
165}
166
167//-------------------------------------------------------------------------------------------------------------------