Skip to main content

AssetServer

Struct AssetServer 

Source
pub struct AssetServer { /* private fields */ }
Expand description

Central coordinator for asset loading and caching.

The AssetServer manages:

  • Asset loaders (registered by file extension)
  • Asset storage (cached loaded assets)
  • Loading queue (assets being loaded)
  • Hot reloading (watching for file changes)

§Thread Safety

AssetServer is Send but NOT Sync - it should be accessed from a single thread (typically the main thread). For multi-threaded asset loading, use async handles and check loading state from the main thread.

§Example

use goud_engine::assets::{Asset, AssetServer};

struct MyAsset { data: String }
impl Asset for MyAsset {}

let mut server = AssetServer::new();

// Load returns a handle immediately
let handle = server.load::<MyAsset>("data/config.json");

// Asset loads in background, check state
match server.get_load_state(&handle) {
    Some(state) => println!("Loading: {}", state),
    None => println!("Not found"),
}

Implementations§

Source§

impl AssetServer

Source

pub fn load_async<A: Asset>(&mut self, path: impl AsRef<Path>) -> AssetHandle<A>

Loads an asset asynchronously using a background thread.

Returns a handle immediately in the Loading state. The actual file I/O and parsing happen on a rayon thread pool. Call [process_loads] each frame to drain completed results and transition handles to Loaded or Failed.

§Deduplication

If an asset with the same path is already loaded or loading, the existing handle is returned without spawning a new background task.

§Arguments
  • path - Path to the asset file (relative to asset root)
§Example
let handle = server.load_async::<MyAsset>("data/config.test");
// ... later, in the game loop:
server.process_loads();
if server.is_loaded(&handle) {
    let asset = server.get(&handle).unwrap();
}
Source§

impl AssetServer

Source

pub fn process_loads(&mut self) -> usize

Drains completed async load results and applies them to asset storage.

This must be called from the main thread (typically once per frame) to transition async-loaded assets from Loading to Loaded or Failed.

§Returns

The number of load results processed in this call.

Source§

impl AssetServer

Source

pub fn new() -> Self

Creates a new asset server with the default asset root (“assets/”).

§Example
use goud_engine::assets::AssetServer;

let server = AssetServer::new();
Source

pub fn with_root(root: impl AsRef<Path>) -> Self

Creates a new asset server with a custom asset root directory.

§Arguments
  • root - Base directory for asset files (relative or absolute)
§Example
use goud_engine::assets::AssetServer;

let server = AssetServer::with_root("game_assets");
Source

pub fn asset_root(&self) -> &Path

Returns the asset root directory.

§Example
use goud_engine::assets::AssetServer;

let server = AssetServer::with_root("game_assets");
assert_eq!(server.asset_root().to_str().unwrap(), "game_assets");
Source

pub fn set_asset_root(&mut self, root: impl AsRef<Path>)

Sets the asset root directory.

§Arguments
  • root - New base directory for asset files
Source§

impl AssetServer

Source

pub fn register_loader<L: AssetLoader>(&mut self, loader: L)

Registers an asset loader for specific file extensions.

Loaders are matched by file extension. If multiple loaders support the same extension, the most recently registered one is used.

§Arguments
  • loader - The asset loader to register
§Example
use goud_engine::assets::{Asset, AssetServer, AssetLoader, LoadContext, AssetLoadError};

#[derive(Clone)]
struct TextAsset { content: String }
impl Asset for TextAsset {}

#[derive(Clone)]
struct TextLoader;
impl AssetLoader for TextLoader {
    type Asset = TextAsset;
    type Settings = ();

    fn extensions(&self) -> &[&str] {
        &["txt"]
    }

    fn load<'a>(
        &'a self,
        bytes: &'a [u8],
        _settings: &'a Self::Settings,
        _context: &'a mut LoadContext,
    ) -> Result<Self::Asset, AssetLoadError> {
        let content = String::from_utf8(bytes.to_vec())
            .map_err(|e| AssetLoadError::decode_failed(e.to_string()))?;
        Ok(TextAsset { content })
    }
}

let mut server = AssetServer::new();
server.register_loader(TextLoader);
Source

pub fn register_loader_with_settings<L: AssetLoader>( &mut self, loader: L, settings: L::Settings, )

Registers an asset loader with custom settings.

§Arguments
  • loader - The asset loader to register
  • settings - Custom settings for this loader
Source

pub fn has_loader_for_extension(&self, extension: &str) -> bool

Returns true if a loader is registered for the given extension.

§Example
use goud_engine::assets::AssetServer;

let server = AssetServer::new();
// Assuming TextLoader is registered for "txt"
// assert!(server.has_loader_for_extension("txt"));
assert!(!server.has_loader_for_extension("unknown"));
Source

pub fn has_loader_for_type<A: Asset>(&self) -> bool

Returns true if a loader is registered for the given asset type.

Source

pub fn loader_count(&self) -> usize

Returns the number of registered loaders.

Source§

impl AssetServer

Source

pub fn load<A: Asset>(&mut self, path: impl AsRef<Path>) -> AssetHandle<A>

Loads an asset from a path (relative to asset root), returning a handle immediately.

The asset loads synchronously (blocking). The handle is valid even if loading fails — check with get_load_state().

Source

pub fn load_from_bytes<A: Asset>( &mut self, path: impl AsRef<Path>, bytes: &[u8], ) -> AssetHandle<A>

Loads an asset from pre-fetched bytes, returning a handle.

This is the platform-agnostic entry point for loading assets when you already have the raw bytes (e.g., from a web fetch, embedded resource, or custom I/O layer). The bytes are run through the registered loader matched by the path’s file extension.

Returns an existing handle if an asset with the same path is already loaded.

§Arguments
  • path - Logical asset path (used for loader lookup and deduplication)
  • bytes - Raw asset bytes to parse
§Example
use goud_engine::assets::{Asset, AssetServer, AssetLoader, LoadContext, AssetLoadError};

#[derive(Clone)]
struct JsonAsset { data: String }
impl Asset for JsonAsset {}

#[derive(Clone)]
struct JsonLoader;
impl AssetLoader for JsonLoader {
    type Asset = JsonAsset;
    type Settings = ();
    fn extensions(&self) -> &[&str] { &["json"] }
    fn load<'a>(
        &'a self, bytes: &'a [u8], _: &'a (), _: &'a mut LoadContext,
    ) -> Result<Self::Asset, AssetLoadError> {
        Ok(JsonAsset { data: String::from_utf8_lossy(bytes).into_owned() })
    }
}

let mut server = AssetServer::new();
server.register_loader(JsonLoader);

let bytes = br#"{"key": "value"}"#;
let handle = server.load_from_bytes::<JsonAsset>("config.json", bytes);
assert!(server.is_loaded(&handle));
Source

pub fn get<A: Asset>(&self, handle: &AssetHandle<A>) -> Option<&A>

Gets a reference to a loaded asset.

Returns None if the asset is not loaded or the handle is invalid.

§Example
use goud_engine::assets::{Asset, AssetServer};

struct MyAsset { value: i32 }
impl Asset for MyAsset {}

let mut server = AssetServer::new();
let handle = server.load::<MyAsset>("data/config.json");

if let Some(asset) = server.get(&handle) {
    println!("Asset value: {}", asset.value);
}
Source

pub fn get_mut<A: Asset>(&mut self, handle: &AssetHandle<A>) -> Option<&mut A>

Gets a mutable reference to a loaded asset.

Returns None if the asset is not loaded or the handle is invalid.

Source

pub fn is_loaded<A: Asset>(&self, handle: &AssetHandle<A>) -> bool

Returns true if the handle points to a valid, loaded asset.

§Example
use goud_engine::assets::{Asset, AssetServer, AssetHandle};

struct MyAsset;
impl Asset for MyAsset {}

let server = AssetServer::new();
let handle = AssetHandle::<MyAsset>::INVALID;

assert!(!server.is_loaded(&handle));
Source

pub fn get_load_state<A: Asset>( &self, handle: &AssetHandle<A>, ) -> Option<AssetState>

Returns the loading state for a handle, or None if not tracked.

Source

pub fn unload<A: Asset>(&mut self, handle: &AssetHandle<A>) -> Option<A>

Unloads an asset, freeing its memory.

The handle becomes invalid after this call.

§Returns

The unloaded asset if it was loaded, otherwise None.

§Example
use goud_engine::assets::{Asset, AssetServer};

struct Texture { width: u32 }
impl Asset for Texture {}

let mut server = AssetServer::new();
let handle = server.load::<Texture>("texture.png");

// Later, unload to free memory
let texture = server.unload(&handle);
assert!(!server.is_loaded(&handle));
Source

pub fn dependency_graph(&self) -> &DependencyGraph

Returns a reference to the dependency graph.

Useful for inspecting asset relationships or computing cascade reload orders externally.

Source

pub fn dependency_graph_mut(&mut self) -> &mut DependencyGraph

Returns a mutable reference to the dependency graph.

Source

pub fn reload_by_path(&mut self, path: &str) -> bool

Reloads an asset from disk by its path, using the type-erased loader.

This is used by the hot-reload watcher to reload assets without knowing their concrete type at compile time.

§Returns

true if the asset was successfully reloaded, false if the path has no registered loader or the reload failed.

Source

pub fn get_cascade_order(&self, changed_path: &str) -> Vec<String>

Returns the cascade reload order for a changed asset path.

This delegates to [DependencyGraph::get_cascade_order] and returns the list of asset paths that should be reloaded, in the correct order, when changed_path changes.

Source

pub fn loaded_count<A: Asset>(&self) -> usize

Returns the number of loaded assets of a specific type.

§Example
use goud_engine::assets::{Asset, AssetServer};

struct Texture;
impl Asset for Texture {}

let server = AssetServer::new();
assert_eq!(server.loaded_count::<Texture>(), 0);
Source

pub fn total_loaded_count(&self) -> usize

Returns the total number of loaded assets across all types.

Source

pub fn registered_type_count(&self) -> usize

Returns the number of registered asset types.

Source

pub fn clear_type<A: Asset>(&mut self)

Clears all loaded assets of a specific type.

This frees memory but does not affect registered loaders.

Source

pub fn clear(&mut self)

Clears all loaded assets from all types.

This frees memory but does not affect registered loaders.

Source

pub fn handles<A: Asset>(&self) -> impl Iterator<Item = AssetHandle<A>> + '_

Returns an iterator over all loaded asset handles of a specific type.

Source

pub fn iter<A: Asset>(&self) -> impl Iterator<Item = (AssetHandle<A>, &A)>

Returns an iterator over all loaded assets of a specific type.

Source

pub fn create_hot_reload_watcher(&self) -> Result<HotReloadWatcher>

Creates a hot reload watcher for this asset server.

The watcher will detect file changes in the asset root directory and automatically reload modified assets.

§Errors

Returns an error if the file watcher cannot be initialized.

§Example
use goud_engine::assets::AssetServer;

let mut server = AssetServer::new();
let mut watcher = server.create_hot_reload_watcher().unwrap();

// In game loop
loop {
    watcher.process_events(&mut server);
    // ... rest of game loop
}
Source

pub fn create_hot_reload_watcher_with_config( &self, config: HotReloadConfig, ) -> Result<HotReloadWatcher>

Creates a hot reload watcher with custom configuration.

§Errors

Returns an error if the file watcher cannot be initialized.

§Example
use goud_engine::assets::{AssetServer, HotReloadConfig};
use std::time::Duration;

let mut server = AssetServer::new();
let config = HotReloadConfig::new()
    .with_debounce(Duration::from_millis(200))
    .watch_extension("png")
    .watch_extension("json");

let mut watcher = server.create_hot_reload_watcher_with_config(config).unwrap();

// In game loop
loop {
    watcher.process_events(&mut server);
    // ... rest of game loop
}

Trait Implementations§

Source§

impl Debug for AssetServer

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for AssetServer

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<R, P> ReadPrimitive<R> for P
where R: Read + ReadEndian<P>, P: Default,

Source§

fn read_from_little_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_little_endian().
Source§

fn read_from_big_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_big_endian().
Source§

fn read_from_native_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_native_endian().
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,