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//-------------------------------------------------------------------------------------------------------------------