Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix asset mixup when two asset files have the same contents in different locations. #234

Merged
merged 1 commit into from
Oct 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion framework_crates/bones_asset/src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ pub struct LoadedAsset {
/// The pack and path the asset was loaded from.
pub loc: AssetLoc,
/// The content IDs of any assets needed by this asset as a dependency.
pub dependencies: Arc<AppendOnlyVec<UntypedHandle>>,
pub dependencies: Vec<UntypedHandle>,
/// The loaded data of the asset.
#[deref]
pub data: SchemaBox,
Expand Down
2 changes: 1 addition & 1 deletion framework_crates/bones_asset/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl<T> Handle<T> {
}

/// An untyped handle to an asset.
#[derive(Default, Clone, Debug, Hash, PartialEq, Eq, Copy)]
#[derive(Default, Clone, Debug, Hash, PartialEq, Eq, Copy, PartialOrd, Ord)]
#[repr(C)]
pub struct UntypedHandle {
/// The runtime ID of the handle
Expand Down
8 changes: 5 additions & 3 deletions framework_crates/bones_asset/src/io.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::path::PathBuf;

use anyhow::Context;
use async_channel::Sender;
use bones_utils::{default, futures::future::Boxed as BoxedFuture, HashMap};

Expand Down Expand Up @@ -105,7 +106,7 @@ impl AssetIo for FileAssetIo {
loc.path
};
let path = base_dir.join(path);
Ok(std::fs::read(path)?)
std::fs::read(&path).with_context(|| format!("Could not load file: {path:?}"))
})
}

Expand Down Expand Up @@ -188,15 +189,16 @@ impl AssetIo for WebAssetIo {
}
let url = format!("{}{}", asset_url, loc.path.to_str().unwrap());
let (sender, receiver) = async_channel::bounded(1);
let req = ehttp::Request::get(url);
let req = ehttp::Request::get(&url);
ehttp::fetch(req, move |resp| {
sender.send_blocking(resp.map(|resp| resp.bytes)).unwrap();
});
let result = receiver
.recv()
.await
.unwrap()
.map_err(|e| anyhow::format_err!("{e}"))?;
.map_err(|e| anyhow::format_err!("{e}"))
.with_context(|| format!("Could not download file: {url}"))?;

Ok(result)
})
Expand Down
89 changes: 59 additions & 30 deletions framework_crates/bones_asset/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,8 @@ impl AssetServer {
path: Path::new("pack.yaml"),
pack: None,
})
.await?;
.await
.context("Could not load pack file")?;
let meta: CorePackfileMeta = serde_yaml::from_slice(&packfile_contents)?;

if !path_is_metadata(&meta.root) {
Expand Down Expand Up @@ -441,10 +442,10 @@ impl AssetServer {
let handle = *self
.store
.path_handles
.entry(loc.to_owned())
.entry(loc.clone())
// If we've already loaded this asset before
.and_modify(|handle| {
// If there is no asset data yet, but we already have a handle, we don't need to
// If we aren't forcing a reload and we already have asset data, we don't need to
// trigger another load.
if self.store.asset_ids.get(handle).is_some() && !force {
should_load = false;
Expand Down Expand Up @@ -479,7 +480,7 @@ impl AssetServer {
// it has a schema not found error, try to load a data asset for the same path, if that
// doesn't work and it is an extension not found error, return the metadata error message.
let partial = if path_is_metadata(&loc.path) {
let data = match server.load_metadata_asset(loc.as_ref(), &data) {
match server.load_metadata_asset(loc.as_ref(), &data).await {
Err(meta_err) => {
if meta_err.downcast_ref::<LoaderNotFound>().is_some() {
match server.load_data_asset(loc.as_ref(), &data).await {
Expand All @@ -497,8 +498,7 @@ impl AssetServer {
}
}
ok => ok,
};
data
}
} else {
server.load_data_asset(loc.as_ref(), &data).await
}?;
Expand Down Expand Up @@ -580,9 +580,9 @@ impl AssetServer {
handle
}

fn load_metadata_asset(
&self,
loc: AssetLocRef,
async fn load_metadata_asset<'a>(
&'a self,
loc: AssetLocRef<'a>,
contents: &[u8],
) -> anyhow::Result<PartialAsset> {
// Get the schema for the asset
Expand Down Expand Up @@ -613,15 +613,19 @@ impl AssetServer {
.ok_or_else(|| LoaderNotFound {
name: schema_name.into(),
})?;
let dependencies = Arc::new(AppendOnlyVec::new());
let mut dependencies = Vec::new();

let mut cid = Cid::default();
// Use the schema name and the file contents to create a unique, content-addressed ID for
// the asset.
cid.update(schema.full_name.as_bytes());
cid.update(contents);

let loader = MetaAssetLoadCtx {
server: self,
loc,
schema,
dependencies: dependencies.clone(),
dependencies: &mut dependencies,
};
let data = if loc.path.extension().unwrap().to_str().unwrap() == "json" {
let mut deserializer = serde_json::Deserializer::from_slice(contents);
Expand All @@ -631,13 +635,19 @@ impl AssetServer {
loader.deserialize(deserializer)?
};

// TODO: Figure out whether or not to update CID with dep cids.

// // Update the Cid
// dependencies.sort();
// for dep in &dependencies {
// cid.update(&dep.0);
// }
// Update Cid with the Cids of it's dependencies
dependencies.sort();
for dep in &dependencies {
let dep_cid = loop {
let listener = self.load_progress.listen();
let Some(cid) = self.store.asset_ids.get(dep) else {
listener.await;
continue;
};
break *cid;
};
cid.update(dep_cid.0.as_slice());
}

Ok(PartialAsset {
cid,
Expand All @@ -659,7 +669,7 @@ impl AssetServer {
.to_str()
.ok_or_else(|| anyhow::format_err!("Invalid unicode in filename"))?;
let (_name, extension) = filename.split_once('.').unwrap();
let loader = SCHEMA_REGISTRY
let (loader, schema) = SCHEMA_REGISTRY
.schemas
.iter()
.find_map(|schema| {
Expand All @@ -670,7 +680,7 @@ impl AssetServer {
.iter()
.any(|ext| ext == extension || ext == filename)
{
Some(loader)
Some((loader, schema))
} else {
None
}
Expand All @@ -686,7 +696,11 @@ impl AssetServer {
})?;

let dependencies = Arc::new(AppendOnlyVec::new());

let mut cid = Cid::default();
// Use the schema name and the file contents to create a unique, content-addressed ID for
// the asset.
cid.update(schema.full_name.as_bytes());
cid.update(contents);

let ctx = AssetLoadCtx {
Expand All @@ -696,6 +710,21 @@ impl AssetServer {
};
let sbox = loader.load(ctx, contents).await?;

// Update Cid with the Cids of it's dependencies
let mut dependencies = dependencies.iter().cloned().collect::<Vec<_>>();
dependencies.sort();
for dep in &dependencies {
let dep_cid = loop {
let listener = self.load_progress.listen();
let Some(cid) = self.store.asset_ids.get(dep) else {
listener.await;
continue;
};
break *cid;
};
cid.update(dep_cid.0.as_slice());
}

Ok(PartialAsset {
cid,
data: sbox,
Expand Down Expand Up @@ -802,7 +831,7 @@ impl AssetServer {
struct PartialAsset {
pub cid: Cid,
pub data: SchemaBox,
pub dependencies: Arc<AppendOnlyVec<UntypedHandle>>,
pub dependencies: Vec<UntypedHandle>,
}

const NO_ASSET_MSG: &str = "Asset not loaded";
Expand All @@ -825,7 +854,7 @@ mod metadata {
pub server: &'srv AssetServer,
/// The dependency list of this asset. This should be updated by asset loaders as
/// dependencies are added.
pub dependencies: Arc<AppendOnlyVec<UntypedHandle>>,
pub dependencies: &'srv mut Vec<UntypedHandle>,
/// The location that the asset is being loaded from.
pub loc: AssetLocRef<'srv>,
/// The schema of the asset being loaded.
Expand Down Expand Up @@ -988,8 +1017,8 @@ mod metadata {
// what data it's expecting.
write!(
formatter,
"asset metadata matching the schema: {:#?}",
self.ptr.schema()
"asset metadata matching the schema: {}",
self.ptr.schema().full_name
)
}

Expand Down Expand Up @@ -1066,8 +1095,8 @@ mod metadata {
// TODO: Write very verbose error messages for metadata asset deserializers.
write!(
formatter,
"asset metadata matching the schema: {:#?}",
self.ptr.schema()
"asset metadata matching the schema: {}",
self.ptr.schema().full_name
)
}

Expand Down Expand Up @@ -1109,8 +1138,8 @@ mod metadata {
// TODO: Write very verbose error messages for metadata asset deserializers.
write!(
formatter,
"asset metadata matching the schema: {:#?}",
self.ptr.schema()
"asset metadata matching the schema: {}",
self.ptr.schema().full_name
)
}

Expand Down Expand Up @@ -1156,8 +1185,8 @@ mod metadata {
// TODO: Write very verbose error messages for metadata asset deserializers.
write!(
formatter,
"asset metadata matching the schema: {:#?}",
self.ptr.schema()
"asset metadata matching the schema: {}",
self.ptr.schema().full_name
)
}

Expand Down
8 changes: 4 additions & 4 deletions framework_crates/bones_bevy_renderer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,10 +502,10 @@ mod storage {
else {
error!(
"\n\nCannot find schema registration for `{}` while loading persisted \
storage. This means you that you need to call \
`{}::schema()` to register your persisted storage type before \
creating the `BonesBevyRenderer` or that there is data from an old \
version of the app inside of the persistent storage file.\n\n",
storage. This means you that you need to call \
`{}::schema()` to register your persisted storage type before \
creating the `BonesBevyRenderer` or that there is data from an old \
version of the app inside of the persistent storage file.\n\n",
type_name, type_name,
);
continue;
Expand Down
Loading