11use 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 ,
44} ;
55pub use bevy_derive:: AppLabel ;
66use bevy_ecs:: {
@@ -64,6 +64,8 @@ pub struct App {
6464 pub sub_apps : SubApps ,
6565 #[ doc( hidden) ]
6666 pub tls : ThreadLocalStorage ,
67+ send : AppEventSender ,
68+ recv : AppEventReceiver ,
6769 /// The function that will manage the app's lifecycle.
6870 ///
6971 /// Bevy provides the [`WinitPlugin`] and [`ScheduleRunnerPlugin`] for windowed and headless
@@ -114,65 +116,96 @@ impl App {
114116 ///
115117 /// Use this constructor if you want to customize scheduling, exit handling, cleanup, etc.
116118 pub fn empty ( ) -> App {
119+ let ( send, recv) = app_thread_channel ( ) ;
117120 Self {
118121 sub_apps : SubApps :: new ( ) ,
119122 tls : ThreadLocalStorage :: new ( ) ,
123+ send,
124+ recv,
120125 runner : Some ( Box :: new ( run_once) ) ,
121126 }
122127 }
123128
124129 /// 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+ ) {
126140 let Self {
127141 sub_apps,
128142 tls,
143+ send,
144+ recv,
129145 runner,
130146 } = self ;
131147
132- ( sub_apps, tls, runner)
148+ ( sub_apps, tls, send , recv , runner)
133149 }
134150
135151 /// Returns an [`App`] assembled from the given individual parts.
152+ #[ doc( hidden) ]
136153 pub fn from_parts (
137154 sub_apps : SubApps ,
138155 tls : ThreadLocalStorage ,
156+ send : AppEventSender ,
157+ recv : AppEventReceiver ,
139158 runner : Option < RunnerFn > ,
140159 ) -> Self {
141160 App {
142161 sub_apps,
143162 tls,
163+ send,
164+ recv,
144165 runner,
145166 }
146167 }
147168
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+
148186 /// Runs the default schedules of all sub-apps (starting with the "main" app) once.
149187 pub fn update ( & mut self ) {
150188 if self . is_building_plugins ( ) {
151189 panic ! ( "App::update() was called while a plugin was building." ) ;
152190 }
153191
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 ( ) ;
159193
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 ( ) ;
164196
165197 #[ cfg( not( target_arch = "wasm32" ) ) ]
166198 {
167199 // Move sub-apps to another thread and run an event loop in this thread.
200+ let thread_send = send. clone ( ) ;
168201 let thread = std:: thread:: spawn ( move || {
169202 let result = catch_unwind ( AssertUnwindSafe ( || {
170203 sub_apps. update ( ) ;
171- send . send ( AppEvent :: Exit ( sub_apps) ) . unwrap ( ) ;
204+ thread_send . send ( AppEvent :: Exit ( sub_apps) ) . unwrap ( ) ;
172205 } ) ) ;
173206
174207 if let Some ( payload) = result. err ( ) {
175- send . send ( AppEvent :: Error ( payload) ) . unwrap ( ) ;
208+ thread_send . send ( AppEvent :: Error ( payload) ) . unwrap ( ) ;
176209 }
177210 } ) ;
178211
@@ -200,13 +233,10 @@ impl App {
200233 sub_apps. update ( ) ;
201234 }
202235
203- // remove channel
204- sub_apps
205- . iter_mut ( )
206- . for_each ( |sub_app| tls. remove_channel ( sub_app. world_mut ( ) ) ) ;
207-
208236 // 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 ( ) ;
210240 }
211241
212242 /// Runs the [`App`] by calling its [runner](Self::set_runner).
@@ -239,6 +269,10 @@ impl App {
239269 panic ! ( "App::run() was called while a plugin was building." ) ;
240270 }
241271
272+ // Insert channel here because some sub-apps are moved to a different thread during
273+ // plugin build.
274+ self . insert_tls_channel ( ) ;
275+
242276 if self . plugins_state ( ) == PluginsState :: Ready {
243277 // If we're already ready, we finish up now and advance one frame.
244278 // This prevents black frames during the launch transition on iOS.
@@ -893,14 +927,6 @@ impl App {
893927type RunnerFn = Box < dyn FnOnce ( App ) > ;
894928
895929fn 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-
904930 // wait for plugins to finish setting up
905931 let plugins_state = app. plugins_state ( ) ;
906932 if plugins_state != PluginsState :: Cleaned {
@@ -912,13 +938,13 @@ fn run_once(mut app: App) {
912938 app. cleanup ( ) ;
913939 }
914940
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
916942 if plugins_state == PluginsState :: Cleaned {
917943 return ;
918944 }
919945
920946 // disassemble
921- let ( mut sub_apps, _ , _) = app. into_parts ( ) ;
947+ let ( mut sub_apps, mut tls , send , recv , _) = app. into_parts ( ) ;
922948
923949 #[ cfg( not( target_arch = "wasm32" ) ) ]
924950 {
@@ -955,6 +981,8 @@ fn run_once(mut app: App) {
955981 {
956982 sub_apps. update ( ) ;
957983 }
984+
985+ tls. clear ( ) ;
958986}
959987
960988/// An event that indicates the [`App`] should exit. If one or more of these are present at the
0 commit comments