1
1
use crate :: {
2
- app_thread_channel, AppEvent , Main , MainSchedulePlugin , PlaceholderPlugin , Plugin , Plugins ,
3
- PluginsState , SubApp , SubApps ,
2
+ app_thread_channel, AppEvent , AppEventReceiver , AppEventSender , Main , MainSchedulePlugin ,
3
+ PlaceholderPlugin , Plugin , Plugins , PluginsState , SubApp , SubApps ,
4
4
} ;
5
5
pub use bevy_derive:: AppLabel ;
6
6
use bevy_ecs:: {
@@ -64,6 +64,8 @@ pub struct App {
64
64
pub sub_apps : SubApps ,
65
65
#[ doc( hidden) ]
66
66
pub tls : ThreadLocalStorage ,
67
+ send : AppEventSender ,
68
+ recv : AppEventReceiver ,
67
69
/// The function that will manage the app's lifecycle.
68
70
///
69
71
/// Bevy provides the [`WinitPlugin`] and [`ScheduleRunnerPlugin`] for windowed and headless
@@ -114,65 +116,96 @@ impl App {
114
116
///
115
117
/// Use this constructor if you want to customize scheduling, exit handling, cleanup, etc.
116
118
pub fn empty ( ) -> App {
119
+ let ( send, recv) = app_thread_channel ( ) ;
117
120
Self {
118
121
sub_apps : SubApps :: new ( ) ,
119
122
tls : ThreadLocalStorage :: new ( ) ,
123
+ send,
124
+ recv,
120
125
runner : Some ( Box :: new ( run_once) ) ,
121
126
}
122
127
}
123
128
124
129
/// Disassembles the [`App`] and returns its individual parts.
125
- pub fn into_parts ( self ) -> ( SubApps , ThreadLocalStorage , Option < RunnerFn > ) {
130
+ #[ doc( hidden) ]
131
+ pub fn into_parts (
132
+ self ,
133
+ ) -> (
134
+ SubApps ,
135
+ ThreadLocalStorage ,
136
+ AppEventSender ,
137
+ AppEventReceiver ,
138
+ Option < RunnerFn > ,
139
+ ) {
126
140
let Self {
127
141
sub_apps,
128
142
tls,
143
+ send,
144
+ recv,
129
145
runner,
130
146
} = self ;
131
147
132
- ( sub_apps, tls, runner)
148
+ ( sub_apps, tls, send , recv , runner)
133
149
}
134
150
135
151
/// Returns an [`App`] assembled from the given individual parts.
152
+ #[ doc( hidden) ]
136
153
pub fn from_parts (
137
154
sub_apps : SubApps ,
138
155
tls : ThreadLocalStorage ,
156
+ send : AppEventSender ,
157
+ recv : AppEventReceiver ,
139
158
runner : Option < RunnerFn > ,
140
159
) -> Self {
141
160
App {
142
161
sub_apps,
143
162
tls,
163
+ send,
164
+ recv,
144
165
runner,
145
166
}
146
167
}
147
168
169
+ /// Inserts the channel to [`ThreadLocals`] into all sub-apps.
170
+ #[ doc( hidden) ]
171
+ pub fn insert_tls_channel ( & mut self ) {
172
+ self . sub_apps . iter_mut ( ) . for_each ( |sub_app| {
173
+ self . tls
174
+ . insert_channel ( sub_app. world_mut ( ) , self . send . clone ( ) ) ;
175
+ } ) ;
176
+ }
177
+
178
+ /// Removes the channel to [`ThreadLocals`] from all sub-apps.
179
+ #[ doc( hidden) ]
180
+ pub fn remove_tls_channel ( & mut self ) {
181
+ self . sub_apps
182
+ . iter_mut ( )
183
+ . for_each ( |sub_app| self . tls . remove_channel ( sub_app. world_mut ( ) ) ) ;
184
+ }
185
+
148
186
/// Runs the default schedules of all sub-apps (starting with the "main" app) once.
149
187
pub fn update ( & mut self ) {
150
188
if self . is_building_plugins ( ) {
151
189
panic ! ( "App::update() was called while a plugin was building." ) ;
152
190
}
153
191
154
- // disassemble
155
- let ( mut sub_apps, tls, runner) = std:: mem:: take ( self ) . into_parts ( ) ;
156
-
157
- // create channel
158
- let ( send, recv) = app_thread_channel ( ) ;
192
+ self . insert_tls_channel ( ) ;
159
193
160
- // insert channel
161
- sub_apps
162
- . iter_mut ( )
163
- . for_each ( |sub_app| tls. insert_channel ( sub_app. world_mut ( ) , send. clone ( ) ) ) ;
194
+ // disassemble
195
+ let ( mut sub_apps, tls, send, recv, runner) = std:: mem:: take ( self ) . into_parts ( ) ;
164
196
165
197
#[ cfg( not( target_arch = "wasm32" ) ) ]
166
198
{
167
199
// Move sub-apps to another thread and run an event loop in this thread.
200
+ let thread_send = send. clone ( ) ;
168
201
let thread = std:: thread:: spawn ( move || {
169
202
let result = catch_unwind ( AssertUnwindSafe ( || {
170
203
sub_apps. update ( ) ;
171
- send . send ( AppEvent :: Exit ( sub_apps) ) . unwrap ( ) ;
204
+ thread_send . send ( AppEvent :: Exit ( sub_apps) ) . unwrap ( ) ;
172
205
} ) ) ;
173
206
174
207
if let Some ( payload) = result. err ( ) {
175
- send . send ( AppEvent :: Error ( payload) ) . unwrap ( ) ;
208
+ thread_send . send ( AppEvent :: Error ( payload) ) . unwrap ( ) ;
176
209
}
177
210
} ) ;
178
211
@@ -200,13 +233,10 @@ impl App {
200
233
sub_apps. update ( ) ;
201
234
}
202
235
203
- // remove channel
204
- sub_apps
205
- . iter_mut ( )
206
- . for_each ( |sub_app| tls. remove_channel ( sub_app. world_mut ( ) ) ) ;
207
-
208
236
// reassemble
209
- * self = App :: from_parts ( sub_apps, tls, runner) ;
237
+ * self = App :: from_parts ( sub_apps, tls, send, recv, runner) ;
238
+
239
+ self . remove_tls_channel ( ) ;
210
240
}
211
241
212
242
/// Runs the [`App`] by calling its [runner](Self::set_runner).
@@ -239,6 +269,10 @@ impl App {
239
269
panic ! ( "App::run() was called while a plugin was building." ) ;
240
270
}
241
271
272
+ // Insert channel here because some sub-apps are moved to a different thread during
273
+ // plugin build.
274
+ self . insert_tls_channel ( ) ;
275
+
242
276
if self . plugins_state ( ) == PluginsState :: Ready {
243
277
// If we're already ready, we finish up now and advance one frame.
244
278
// This prevents black frames during the launch transition on iOS.
@@ -893,14 +927,6 @@ impl App {
893
927
type RunnerFn = Box < dyn FnOnce ( App ) > ;
894
928
895
929
fn run_once ( mut app : App ) {
896
- // TODO: rework app setup
897
- // create channel
898
- let ( send, recv) = app_thread_channel ( ) ;
899
- // insert channel
900
- app. sub_apps
901
- . iter_mut ( )
902
- . for_each ( |sub_app| app. tls . insert_channel ( sub_app. world_mut ( ) , send. clone ( ) ) ) ;
903
-
904
930
// wait for plugins to finish setting up
905
931
let plugins_state = app. plugins_state ( ) ;
906
932
if plugins_state != PluginsState :: Cleaned {
@@ -912,13 +938,13 @@ fn run_once(mut app: App) {
912
938
app. cleanup ( ) ;
913
939
}
914
940
915
- // if plugins where cleaned before the runner start, an update already ran
941
+ // If plugins where cleaned before the runner start, an update already ran
916
942
if plugins_state == PluginsState :: Cleaned {
917
943
return ;
918
944
}
919
945
920
946
// disassemble
921
- let ( mut sub_apps, _ , _) = app. into_parts ( ) ;
947
+ let ( mut sub_apps, mut tls , send , recv , _) = app. into_parts ( ) ;
922
948
923
949
#[ cfg( not( target_arch = "wasm32" ) ) ]
924
950
{
@@ -955,6 +981,8 @@ fn run_once(mut app: App) {
955
981
{
956
982
sub_apps. update ( ) ;
957
983
}
984
+
985
+ tls. clear ( ) ;
958
986
}
959
987
960
988
/// An event that indicates the [`App`] should exit. If one or more of these are present at the
0 commit comments