Skip to content

Commit 03dc086

Browse files
committed
Send asset events for UUID asset IDs (duplicating their entity's asset events).
1 parent 08cad80 commit 03dc086

3 files changed

Lines changed: 103 additions & 12 deletions

File tree

crates/bevy_asset/src/assets.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use bevy_ecs::{
1515
world::{DeferredWorld, Mut, Ref, World},
1616
};
1717
use bevy_reflect::TypePath;
18-
use core::{marker::PhantomData, ops::Deref};
18+
use core::{any::TypeId, marker::PhantomData, ops::Deref};
1919
use derive_more::{Deref, DerefMut};
2020
use thiserror::Error;
2121

@@ -220,6 +220,14 @@ fn write_added_asset_event<A: Asset>(
220220
if let Some(mut changes) = world.get_resource_mut::<AssetChanges<A>>() {
221221
changes.insert(entity, tick);
222222
}
223+
if let Some(uuid_map) = world.get_resource::<AssetUuidMap>()
224+
&& let Some(uuids) = uuid_map.entity_to_uuids(entity, TypeId::of::<A>())
225+
{
226+
for uuid in uuids {
227+
let id = AssetId::<A>::Uuid { uuid };
228+
world.write_message(AssetEvent::<A>::Added { id });
229+
}
230+
}
223231
}
224232

225233
/// Writes [`AssetEvent::Removed`] for this asset.
@@ -237,6 +245,14 @@ fn write_removed_asset_event<A: Asset>(
237245
if let Some(mut changes) = world.get_resource_mut::<AssetChanges<A>>() {
238246
changes.remove(&entity);
239247
}
248+
if let Some(uuid_map) = world.get_resource::<AssetUuidMap>()
249+
&& let Some(uuids) = uuid_map.entity_to_uuids(entity, TypeId::of::<A>())
250+
{
251+
for uuid in uuids {
252+
let id = AssetId::<A>::Uuid { uuid };
253+
world.write_message(AssetEvent::<A>::Removed { id });
254+
}
255+
}
240256
}
241257

242258
/// Writes [`AssetEvent::Modified`] messages for any assets that have changed.
@@ -246,6 +262,7 @@ pub(crate) fn write_modified_asset_events<A: Asset>(
246262
ticks: SystemChangeTick,
247263
mut messages: MessageWriter<AssetEvent<A>>,
248264
mut changes: Option<ResMut<AssetChanges<A>>>,
265+
asset_uuid_map: Option<Res<AssetUuidMap>>,
249266
) {
250267
for (entity, data_ref) in assets.iter() {
251268
let entity = AssetEntity::new_unchecked(entity);
@@ -265,6 +282,27 @@ pub(crate) fn write_modified_asset_events<A: Asset>(
265282
},
266283
});
267284
}
285+
286+
let Some(uuid_map) = asset_uuid_map else {
287+
return;
288+
};
289+
290+
for (_, inner) in uuid_map.read().iter() {
291+
for (&uuid, handle) in inner.uuid_to_handle.iter() {
292+
let Ok((_, data_ref)) = assets.get(handle.entity().raw_entity()) else {
293+
continue;
294+
};
295+
296+
if data_ref.last_changed() == data_ref.added() {
297+
// The change corresponds to new asset data, which would have been handled by
298+
// `AssetData`s hooks.
299+
continue;
300+
}
301+
messages.write(AssetEvent::Modified {
302+
id: AssetId::Uuid { uuid },
303+
});
304+
}
305+
}
268306
}
269307

270308
/// Despawns assets whose handles have been dropped and so are "unused".

crates/bevy_asset/src/direct_access_ext.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use uuid::Uuid;
1313

1414
use crate::{
1515
insert_asset, meta::Settings, setup_asset, Asset, AssetData, AssetEntity,
16-
AssetEntityDoesNotExistError, AssetHandleProvider, AssetId, AssetMut, AssetPath,
16+
AssetEntityDoesNotExistError, AssetEvent, AssetHandleProvider, AssetId, AssetMut, AssetPath,
1717
AssetSelfHandle, AssetServer, AssetUuidMap, EntityHandle, Handle, ResolveUuidError,
1818
UntypedEntityHandle, UntypedHandle,
1919
};
@@ -89,6 +89,13 @@ impl DirectAssetAccessExt for World {
8989
let entity_handle = UntypedEntityHandle::from(EntityHandle::try_from(handle).unwrap());
9090
self.resource_mut::<AssetUuidMap>()
9191
.set_uuid(uuid, entity_handle);
92+
// Send an asset event that this UUID has been added.
93+
// TODO: This isn't quite sufficient, since users can call set_uuid themselves. We should be
94+
// sending a message in that case as well. This also doesn't work correctly if the UUID
95+
// was already set.
96+
self.write_message(AssetEvent::<A>::Added {
97+
id: AssetId::Uuid { uuid },
98+
});
9299
Handle::Uuid(uuid, PhantomData)
93100
}
94101

@@ -350,6 +357,13 @@ impl InternalAssetCommands<'_, '_, '_> {
350357
world
351358
.resource_mut::<AssetUuidMap>()
352359
.set_uuid(uuid, entity_handle);
360+
// Send an asset event that this UUID has been added.
361+
// TODO: This isn't quite sufficient, since users can call set_uuid themselves. We should be
362+
// sending a message in that case as well. This also doesn't work correctly if the UUID
363+
// was already set.
364+
world.write_message(AssetEvent::<A>::Added {
365+
id: AssetId::Uuid { uuid },
366+
});
353367
});
354368
Handle::Uuid(uuid, PhantomData)
355369
}

crates/bevy_asset/src/uuid_map.rs

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use core::any::TypeId;
2+
13
use alloc::sync::Arc;
24

35
use bevy_ecs::resource::Resource;
46
use bevy_platform::{
5-
collections::HashMap,
7+
collections::{hash_map::Entry, HashMap, HashSet},
68
sync::{PoisonError, RwLock, RwLockReadGuard},
79
};
810
use bevy_utils::TypeIdMap;
@@ -15,21 +17,43 @@ use crate::{
1517

1618
/// Maps asset UUIDs to the asset handle assigned to it.
1719
#[derive(Resource, Clone, Default)]
18-
pub struct AssetUuidMap(Arc<RwLock<TypeIdMap<HashMap<Uuid, UntypedEntityHandle>>>>);
20+
pub struct AssetUuidMap(Arc<RwLock<TypeIdMap<AssetUuidMapInner>>>);
21+
22+
#[derive(Default)]
23+
pub(crate) struct AssetUuidMapInner {
24+
pub(crate) uuid_to_handle: HashMap<Uuid, UntypedEntityHandle>,
25+
entity_to_uuids: HashMap<AssetEntity, HashSet<Uuid>>,
26+
}
1927

2028
impl AssetUuidMap {
2129
/// Sets the handle that a UUID refers to.
2230
pub fn set_uuid(&mut self, uuid: Uuid, handle: UntypedEntityHandle) {
23-
self.0
24-
.write()
25-
.unwrap_or_else(PoisonError::into_inner)
26-
.entry(handle.0.type_id)
31+
let mut type_id_map = self.0.write().unwrap_or_else(PoisonError::into_inner);
32+
let inner = type_id_map.entry(handle.0.type_id).or_default();
33+
let new_entity = handle.entity();
34+
match inner.uuid_to_handle.entry(uuid) {
35+
Entry::Vacant(entry) => {
36+
entry.insert(handle);
37+
}
38+
Entry::Occupied(mut entry) => {
39+
let old_entity = entry.get().entity();
40+
inner
41+
.entity_to_uuids
42+
.get_mut(&old_entity)
43+
.unwrap()
44+
.remove(&uuid);
45+
entry.insert(handle);
46+
}
47+
}
48+
inner
49+
.entity_to_uuids
50+
.entry(new_entity)
2751
.or_default()
28-
.insert(uuid, handle);
52+
.insert(uuid);
2953
}
3054

3155
/// Convenience function for accessing the internal uuid map.
32-
fn read(&self) -> RwLockReadGuard<'_, TypeIdMap<HashMap<Uuid, UntypedEntityHandle>>> {
56+
pub(crate) fn read(&self) -> RwLockReadGuard<'_, TypeIdMap<AssetUuidMapInner>> {
3357
self.0.read().unwrap_or_else(PoisonError::into_inner)
3458
}
3559

@@ -46,7 +70,7 @@ impl AssetUuidMap {
4670
UntypedHandle::Uuid { type_id, uuid } => self
4771
.read()
4872
.get(&type_id)
49-
.and_then(|map| map.get(&uuid))
73+
.and_then(|inner| inner.uuid_to_handle.get(&uuid))
5074
.cloned()
5175
.ok_or(ResolveUuidError(uuid)),
5276
}
@@ -79,11 +103,26 @@ impl AssetUuidMap {
79103
UntypedAssetId::Uuid { type_id, uuid } => self
80104
.read()
81105
.get(&type_id)
82-
.and_then(|map| map.get(&uuid))
106+
.and_then(|inner| inner.uuid_to_handle.get(&uuid))
83107
.map(|value| value.0.entity)
84108
.ok_or(ResolveUuidError(uuid)),
85109
}
86110
}
111+
112+
/// Returns a reverse mapping from an entity to all UUIDs that reference it.
113+
pub(crate) fn entity_to_uuids(
114+
&self,
115+
entity: AssetEntity,
116+
type_id: TypeId,
117+
) -> Option<HashSet<Uuid>> {
118+
Some(
119+
self.read()
120+
.get(&type_id)?
121+
.entity_to_uuids
122+
.get(&entity)?
123+
.clone(),
124+
)
125+
}
87126
}
88127

89128
/// An error while resolve a [`Uuid`] in the [`AssetUuidMap`].

0 commit comments

Comments
 (0)