1
1
#[ cfg( feature = "filesystem_watcher" ) ]
2
2
use crate :: { filesystem_watcher:: FilesystemWatcher , AssetServer } ;
3
- use crate :: { AssetIo , AssetIoError , Metadata } ;
3
+ use crate :: { AssetIo , AssetIoError , ChangeWatcher , Metadata } ;
4
4
use anyhow:: Result ;
5
5
#[ cfg( feature = "filesystem_watcher" ) ]
6
- use bevy_ecs:: system:: Res ;
6
+ use bevy_ecs:: system:: { Local , Res } ;
7
7
use bevy_utils:: BoxedFuture ;
8
8
#[ cfg( feature = "filesystem_watcher" ) ]
9
- use bevy_utils:: { default, HashSet } ;
9
+ use bevy_utils:: { default, HashMap , Instant } ;
10
10
#[ cfg( feature = "filesystem_watcher" ) ]
11
11
use crossbeam_channel:: TryRecvError ;
12
12
use fs:: File ;
@@ -35,13 +35,13 @@ impl FileAssetIo {
35
35
/// watching for changes.
36
36
///
37
37
/// See `get_base_path` below.
38
- pub fn new < P : AsRef < Path > > ( path : P , watch_for_changes : bool ) -> Self {
38
+ pub fn new < P : AsRef < Path > > ( path : P , watch_for_changes : & Option < ChangeWatcher > ) -> Self {
39
39
let file_asset_io = FileAssetIo {
40
40
#[ cfg( feature = "filesystem_watcher" ) ]
41
41
filesystem_watcher : default ( ) ,
42
42
root_path : Self :: get_base_path ( ) . join ( path. as_ref ( ) ) ,
43
43
} ;
44
- if watch_for_changes {
44
+ if let Some ( configuration ) = watch_for_changes {
45
45
#[ cfg( any(
46
46
not( feature = "filesystem_watcher" ) ,
47
47
target_arch = "wasm32" ,
@@ -52,7 +52,7 @@ impl FileAssetIo {
52
52
wasm32 / android targets"
53
53
) ;
54
54
#[ cfg( feature = "filesystem_watcher" ) ]
55
- file_asset_io. watch_for_changes ( ) . unwrap ( ) ;
55
+ file_asset_io. watch_for_changes ( configuration ) . unwrap ( ) ;
56
56
}
57
57
file_asset_io
58
58
}
@@ -143,10 +143,10 @@ impl AssetIo for FileAssetIo {
143
143
Ok ( ( ) )
144
144
}
145
145
146
- fn watch_for_changes ( & self ) -> Result < ( ) , AssetIoError > {
146
+ fn watch_for_changes ( & self , configuration : & ChangeWatcher ) -> Result < ( ) , AssetIoError > {
147
147
#[ cfg( feature = "filesystem_watcher" ) ]
148
148
{
149
- * self . filesystem_watcher . write ( ) = Some ( default ( ) ) ;
149
+ * self . filesystem_watcher . write ( ) = Some ( FilesystemWatcher :: new ( configuration ) ) ;
150
150
}
151
151
#[ cfg( not( feature = "filesystem_watcher" ) ) ]
152
152
bevy_log:: warn!( "Watching for changes is not supported when the `filesystem_watcher` feature is disabled" ) ;
@@ -174,22 +174,26 @@ impl AssetIo for FileAssetIo {
174
174
feature = "filesystem_watcher" ,
175
175
all( not( target_arch = "wasm32" ) , not( target_os = "android" ) )
176
176
) ) ]
177
- pub fn filesystem_watcher_system ( asset_server : Res < AssetServer > ) {
177
+ pub fn filesystem_watcher_system (
178
+ asset_server : Res < AssetServer > ,
179
+ mut changed : Local < HashMap < PathBuf , Instant > > ,
180
+ ) {
178
181
let asset_io =
179
182
if let Some ( asset_io) = asset_server. server . asset_io . downcast_ref :: < FileAssetIo > ( ) {
180
183
asset_io
181
184
} else {
182
185
return ;
183
186
} ;
184
187
let watcher = asset_io. filesystem_watcher . read ( ) ;
188
+
185
189
if let Some ( ref watcher) = * watcher {
186
- let mut changed = HashSet :: < & PathBuf > :: default ( ) ;
187
190
loop {
188
191
let event = match watcher. receiver . try_recv ( ) {
189
192
Ok ( result) => result. unwrap ( ) ,
190
193
Err ( TryRecvError :: Empty ) => break ,
191
194
Err ( TryRecvError :: Disconnected ) => panic ! ( "FilesystemWatcher disconnected." ) ,
192
195
} ;
196
+
193
197
if let notify:: event:: Event {
194
198
kind : notify:: event:: EventKind :: Modify ( _) ,
195
199
paths,
@@ -199,13 +203,22 @@ pub fn filesystem_watcher_system(asset_server: Res<AssetServer>) {
199
203
for path in & paths {
200
204
let Some ( set) = watcher. path_map . get ( path) else { continue } ;
201
205
for to_reload in set {
202
- if !changed. contains ( to_reload) {
203
- changed. insert ( to_reload) ;
204
- let _ = asset_server. load_untracked ( to_reload. as_path ( ) . into ( ) , true ) ;
205
- }
206
+ // When an asset is modified, note down the timestamp (overriding any previous modification events)
207
+ changed. insert ( to_reload. to_owned ( ) , Instant :: now ( ) ) ;
206
208
}
207
209
}
208
210
}
209
211
}
212
+
213
+ // Reload all assets whose last modification was at least 50ms ago.
214
+ //
215
+ // When changing and then saving a shader, several modification events are sent in short succession.
216
+ // Unless we wait until we are sure the shader is finished being modified (and that there will be no more events coming),
217
+ // we will sometimes get a crash when trying to reload a partially-modified shader.
218
+ for ( to_reload, _) in
219
+ changed. drain_filter ( |_, last_modified| last_modified. elapsed ( ) >= watcher. delay )
220
+ {
221
+ let _ = asset_server. load_untracked ( to_reload. as_path ( ) . into ( ) , true ) ;
222
+ }
210
223
}
211
224
}
0 commit comments