Skip to content

Commit

Permalink
First draft of the example
Browse files Browse the repository at this point in the history
  • Loading branch information
Alice Cecile committed Apr 17, 2024
1 parent bfdd0f8 commit db62bf5
Showing 1 changed file with 173 additions and 1 deletion.
174 changes: 173 additions & 1 deletion examples/custom_asset_lifecycles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,176 @@
//! As this example demonstrates, you can bypass the [`ManifestPlugin`](leafwing_manifest::plugin::ManifestPlugin) entirely, and load your assets however you like,
//! calling the publicly exposed methods yourself to replicate the work it does.
fn main() {}
use bevy::{asset::LoadState, prelude::*};
use leafwing_manifest::manifest::Manifest;
use manifest_definition::{ItemManifest, RawItemManifest};

/// The core data structures and [`Manifest`] implementation is stolen directly from raw.rs:
/// it's not the focus of this example!
mod manifest_definition {
use std::path::PathBuf;

use bevy::{prelude::*, utils::HashMap};
use leafwing_manifest::{
identifier::Id,
manifest::{Manifest, ManifestFormat},
};
use serde::{Deserialize, Serialize};

#[derive(Debug, PartialEq)]
#[allow(dead_code)] // Properties are for demonstration purposes only.
pub struct Item {
name: String,
description: String,
value: i32,
weight: f32,
max_stack: u8,
sprite: Handle<Image>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct RawItem {
name: String,
description: String,
value: i32,
weight: f32,
max_stack: u8,
sprite: PathBuf,
}

#[derive(Debug, Resource, PartialEq)]
pub struct ItemManifest {
items: HashMap<Id<Item>, Item>,
}

#[derive(Debug, Asset, TypePath, Serialize, Deserialize, PartialEq, Clone)]
pub struct RawItemManifest {
items: Vec<RawItem>,
}

impl Manifest for ItemManifest {
type Item = Item;
type RawItem = RawItem;
type RawManifest = RawItemManifest;
type ConversionError = std::convert::Infallible;

const FORMAT: ManifestFormat = ManifestFormat::Ron;

fn get(&self, id: Id<Item>) -> Option<&Self::Item> {
self.items.get(&id)
}

fn from_raw_manifest(
raw_manifest: Self::RawManifest,
world: &mut World,
) -> Result<Self, Self::ConversionError> {
let asset_server = world.resource::<AssetServer>();

let items: HashMap<_, _> = raw_manifest
.items
.into_iter()
.map(|raw_item| {
let sprite_handle = asset_server.load(raw_item.sprite);

let item = Item {
name: raw_item.name,
description: raw_item.description,
value: raw_item.value,
weight: raw_item.weight,
max_stack: raw_item.max_stack,
sprite: sprite_handle,
};

let id = Id::from_name(&item.name);

(id, item)
})
.collect();

Ok(ItemManifest { items })
}
}
}

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(PreUpdate, manage_manifests)
.run();
}

// The same basic workflow applies:
// 1. Start loading the raw manifest
// 2. Wait for the raw manifest to load
// 3. Convert it into a usable form
// 4. Store it as a resource
#[derive(Debug, PartialEq, Default)]
enum ManifestProgress {
#[default]
NotLoaded,
Loading,
Loaded,
Processed,
}

/// Handles the entire lifecycle of the manifest.
///
/// This is a tiny simple systems for simplicity: the core steps can be arranged however you'd like.
/// See the source code of the [`plugin`](leafwing_manifest::plugin) module for further inspiration.
fn manage_manifests(
mut progress: Local<ManifestProgress>,
mut manifest_handle: Local<Option<Handle<RawItemManifest>>>,
mut commands: Commands,
asset_server: Res<AssetServer>,
raw_manifest_assets: Res<Assets<RawItemManifest>>,
) {
match *progress {
// Step 1: Start loading the raw manifest.
ManifestProgress::NotLoaded => {
// Load the raw manifest from disk
let handle = asset_server.load("raw_items.ron");
*manifest_handle = Some(handle);
*progress = ManifestProgress::Loading;
}
// Step 2: Wait for the raw manifest to load.
ManifestProgress::Loading => {
// The handle is always created in the previous step, so this is safe.
let handle = manifest_handle.as_ref().unwrap();
// Check if the asset is loaded
let load_state = asset_server.get_load_state(handle).unwrap();
match load_state {
// We're safely loaded: timie to move on to the next step.
LoadState::Loaded => {
*progress = ManifestProgress::Loaded;
}
// We're still waiting for the asset to load
LoadState::NotLoaded | LoadState::Loading => (),
// Something went wrong: panic!
LoadState::Failed => {
panic!("Failed to load manifest");
}
}
}
// Step 3: Process the raw manifest into a usable form.
// Step 4: Store the usable form as a resource.
ManifestProgress::Loaded => {
let raw_manifest = raw_manifest_assets
.get(manifest_handle.as_ref().unwrap())
.unwrap()
// This process can be done without cloning, but it involves more sophisticated machinery.
.clone();

// We're deferring the actual work with commands to avoid blocking the whole world
// every time this system runs.
commands.add(|mut world: &mut World| {
let item_manifest =
ItemManifest::from_raw_manifest(raw_manifest, &mut world).unwrap();

world.insert_resource(item_manifest);
});
*progress = ManifestProgress::Processed;
}
// All done: no more work to do!
ManifestProgress::Processed => (),
}
}

0 comments on commit db62bf5

Please sign in to comment.