diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 62808e38a42c2..1d2a278767e18 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -11,6 +11,7 @@ mod info; mod io; mod loader; mod path; +mod ser; pub mod prelude { #[doc(hidden)] @@ -25,6 +26,7 @@ pub use info::*; pub use io::*; pub use loader::*; pub use path::*; +pub use ser::*; use bevy_app::{prelude::Plugin, AppBuilder}; use bevy_ecs::{ diff --git a/crates/bevy_asset/src/ser.rs b/crates/bevy_asset/src/ser.rs new file mode 100644 index 0000000000000..b134570013b7c --- /dev/null +++ b/crates/bevy_asset/src/ser.rs @@ -0,0 +1,90 @@ +use std::cell::Cell; + +use bevy_reflect::Uuid; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::{Asset, AssetServer, Handle, HandleId, HandleUntyped}; + +/////////////////////////////////////////////////////////////////////////////// + +thread_local! { + static ASSET_SERVER: Cell> = Cell::new(None); +} + +#[derive(Serialize, Deserialize)] +enum AssetRef { + Default, + /// Used for static handles like the `PBR_PIPELINE_HANDLE` or a embedded assets + Local(Uuid, u64), + /// Loads from a file + External(String), +} + +impl Serialize for Handle { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let path = ASSET_SERVER + .with(|cell| { + let server = cell.replace(None); + let path = server.as_ref().and_then(|server| { + // TODO: `get_handle_path` does absolutely nothing issue #1290 + server.get_handle_path(self).map(|asset_path| { + let mut path = asset_path.path().to_string_lossy().to_string(); + if let Some(label) = asset_path.label() { + path.push('#'); + path.push_str(label); + } + AssetRef::External(path) + }) + }); + cell.replace(server); + path + }) + .unwrap_or_else(|| match &self.id { + HandleId::Id(type_uuid, id) => AssetRef::Local(*type_uuid, *id), + HandleId::AssetPathId(_) => AssetRef::Default, + }); + + path.serialize(serializer) + } +} + +impl<'de, T: Asset> Deserialize<'de> for Handle { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + match AssetRef::deserialize(deserializer)? { + AssetRef::Default => Ok(Handle::default()), + AssetRef::Local(type_uuid, id) => { + Ok(HandleUntyped::weak_from_u64(type_uuid, id).typed()) + } + AssetRef::External(path) => ASSET_SERVER.with(|cell| { + let server = cell.replace(None); + let handle = server + .as_ref() + .map(|server| server.load(path.as_str())) + .unwrap_or_default(); + cell.replace(server); + Ok(handle) + }), + } + } +} + +impl AssetServer { + /// Enables asset references to be serialized or deserialized + pub fn with_asset_refs_serialization(&self, f: F) -> T + where + F: FnOnce() -> T, + { + ASSET_SERVER.with(|key| { + key.replace(Some(self.clone())); + let result = (f)(); + key.replace(None); + result + }) + } +}