khora_agents/asset_agent/
agent.rs1use anyhow::{anyhow, Context, Result};
18use khora_core::{
19 asset::{Asset, AssetHandle, AssetUUID},
20 vfs::VirtualFileSystem,
21};
22use khora_data::assets::Assets;
23use khora_lanes::asset_lane::{AssetLoaderLane, PackLoadingLane};
24use khora_telemetry::MetricsRegistry;
25use std::{
26 any::{Any, TypeId},
27 collections::HashMap,
28 fs::File,
29 sync::Arc,
30};
31
32use crate::asset_agent::loader::AssetLoaderLaneRegistry;
33
34pub struct AssetAgent {
36 vfs: VirtualFileSystem,
38 loading_lane: PackLoadingLane,
40 loaders: AssetLoaderLaneRegistry,
42 storages: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
45}
46
47impl AssetAgent {
48 pub fn new(
50 index_bytes: &[u8],
51 data_file: File,
52 metrics_registry: Arc<MetricsRegistry>,
53 ) -> Result<Self> {
54 let vfs = VirtualFileSystem::new(index_bytes)
55 .context("Failed to initialize VirtualFileSystem from index bytes")?;
56
57 let loading_lane = PackLoadingLane::new(data_file);
58
59 Ok(Self {
60 vfs,
61 loading_lane,
62 loaders: AssetLoaderLaneRegistry::new(metrics_registry),
63 storages: HashMap::new(),
64 })
65 }
66
67 pub fn register_loader<A: Asset>(
72 &mut self,
73 type_name: &str,
74 loader: impl AssetLoaderLane<A> + Send + Sync + 'static,
75 ) {
76 self.loaders.register::<A>(type_name, loader);
77 }
78
79 pub fn load<A: Asset>(&mut self, uuid: &AssetUUID) -> Result<AssetHandle<A>> {
83 let type_id = TypeId::of::<A>();
84
85 let storage = self
87 .storages
88 .entry(type_id)
89 .or_insert_with(|| Box::new(Assets::<A>::new()));
90
91 let assets = storage
93 .downcast_mut::<Assets<A>>()
94 .ok_or_else(|| anyhow!("Mismatched asset storage type"))?;
95
96 if let Some(handle) = assets.get(uuid) {
98 return Ok(handle.clone()); }
100
101 let metadata = self
103 .vfs
104 .get_metadata(uuid)
105 .ok_or_else(|| anyhow!("Asset with UUID {:?} not found in VFS", uuid))?;
106
107 let source = metadata
108 .variants
109 .get("default")
110 .ok_or_else(|| anyhow!("Asset {:?} has no 'default' variant", uuid))?;
111
112 let bytes = self.loading_lane.load_asset_bytes(source)?;
113
114 let asset: A = self.loaders.load::<A>(&metadata.asset_type_name, &bytes)?;
115
116 let handle = AssetHandle::new(asset);
118 assets.insert(*uuid, handle.clone());
119
120 Ok(handle)
121 }
122}