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

Add custom configs and custom loaders #41

Merged
merged 6 commits into from
May 11, 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
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ serde_yaml = { version = "0.9", optional = true, default-features = false }

[dev-dependencies]
ron = "0.8"
serde_yaml = "0.9"
bevy = "0.10.1"
bevy_prototype_lyon = "0.8.0"
trybuild = "1.0.71"
Expand All @@ -119,6 +120,16 @@ name = "basic_schematic"
path = "examples/basic_schematic.rs"
required-features = ["ron", "auto_name", "custom_schematics", "bevy_sprite"]

[[example]]
name = "custom_config"
path = "examples/custom_config.rs"
required-features = ["ron", "auto_name"]

[[example]]
name = "custom_loader"
path = "examples/custom_loader.rs"
required-features = ["ron", "auto_name"]

[[example]]
name = "custom_schematic"
path = "examples/custom_schematic.rs"
Expand Down
3 changes: 3 additions & 0 deletions assets/examples/custom_config/Player.prototype.ron
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(
name: "Player"
)
11 changes: 11 additions & 0 deletions assets/examples/custom_loader/Player.custom.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: Player
schematics:
custom_loader::Player:
# Comment out the `Health` component below to have this prototype
# be rejected by our custom loader.
custom_loader::Health:
- 100
# Comment out the `Mana` component below to have a default instance
# of it be inserted by our custom loader.
custom_loader::Mana:
- 100
36 changes: 17 additions & 19 deletions bevy_proto_backend/src/children/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,61 @@ use std::path::Path;

use bevy::asset::{AssetIo, Handle, LoadedAsset};

use crate::load::ProtoLoadContext;
use crate::load::{Loader, ProtoLoadContext};
use crate::path::{ProtoPath, ProtoPathContext};
use crate::proto::Prototypical;

/// A helper struct for properly building out a [prototype's] children.
///
/// [prototype's]: Prototypical
pub struct ProtoChildBuilder<'ctx, 'load_ctx, T: Prototypical> {
pub(crate) context: ProtoLoadContext<'ctx, 'load_ctx, T>,
child_count: usize,
pub struct ProtoChildBuilder<'ctx, 'load_ctx, T: Prototypical, L: Loader<T>> {
pub(crate) context: ProtoLoadContext<'ctx, 'load_ctx, T, L>,
}

impl<'ctx, 'load_ctx, T: Prototypical> ProtoChildBuilder<'ctx, 'load_ctx, T> {
pub(crate) fn new(context: ProtoLoadContext<'ctx, 'load_ctx, T>) -> Self {
Self {
context,
child_count: 0,
}
impl<'ctx, 'load_ctx, T: Prototypical, L: Loader<T>> ProtoChildBuilder<'ctx, 'load_ctx, T, L> {
pub(crate) fn new(context: ProtoLoadContext<'ctx, 'load_ctx, T, L>) -> Self {
Self { context }
}

/// Add the given child to the parent.
pub fn add_child(&mut self, mut child: T) -> Result<Handle<T>, T::Error> {
let deps = self.context.preprocess_proto(&mut child)?;
pub fn add_child(&mut self, child: T) -> Result<Handle<T>, L::Error> {
let (child, meta, deps) = self.context.preprocess_proto(child)?;

let child_handle = self.context.set_labeled_asset(
&format!("{:0>3}--{:0>3}", self.context.depth(), self.child_count),
meta.path.label().expect("child should have an asset label"),
LoadedAsset::new(child).with_dependencies(deps),
);

self.child_count += 1;
self.context.increment_index();

Ok(child_handle)
}

/// Add the child with the given path to the parent.
pub fn add_child_path(&mut self, child_path: ProtoPath) -> Result<Handle<T>, T::Error> {
pub fn add_child_path(&mut self, child_path: ProtoPath) -> Result<Handle<T>, L::Error> {
self.context
.child_paths_mut()
.push(child_path.asset_path().to_owned());

self.child_count += 1;
self.context.increment_index();

Ok(self.context.get_handle(child_path))
}

/// Access the current [`ProtoLoadContext`].
pub fn context(&self) -> &ProtoLoadContext<'ctx, 'load_ctx, T> {
pub fn context(&self) -> &ProtoLoadContext<'ctx, 'load_ctx, T, L> {
&self.context
}

/// Access the current [`ProtoLoadContext`] mutably.
pub fn context_mut(&mut self) -> &mut ProtoLoadContext<'ctx, 'load_ctx, T> {
pub fn context_mut(&mut self) -> &mut ProtoLoadContext<'ctx, 'load_ctx, T, L> {
&mut self.context
}
}

impl<'ctx, 'load_ctx, T: Prototypical> ProtoPathContext for ProtoChildBuilder<'ctx, 'load_ctx, T> {
impl<'ctx, 'load_ctx, T: Prototypical, L: Loader<T>> ProtoPathContext
for ProtoChildBuilder<'ctx, 'load_ctx, T, L>
{
fn base_path(&self) -> &Path {
self.context.base_path()
}
Expand Down
67 changes: 67 additions & 0 deletions bevy_proto_backend/src/load/asset_loader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use std::marker::PhantomData;
use std::sync::Arc;

use bevy::app::AppTypeRegistry;
use bevy::asset::{AssetLoader, AssetPath, BoxedFuture, LoadContext, LoadedAsset};
use bevy::prelude::{Handle, World};
use parking_lot::RwLock;

use crate::load::{Loader, ProtoLoadContext};
use crate::proto::{Config, Prototypical};
use crate::registration::{LoadQueue, ProtoRegistry};

pub(crate) struct ProtoAssetLoader<T: Prototypical, L: Loader<T>, C: Config<T>> {
registry: AppTypeRegistry,
proto_registry: Arc<RwLock<LoadQueue<T>>>,
loader: L,
_phantom: PhantomData<C>,
}

impl<T: Prototypical, L: Loader<T>, C: Config<T>> ProtoAssetLoader<T, L, C> {
pub fn new(loader: L, world: &mut World) -> Self {
world.init_resource::<AppTypeRegistry>();
world.init_resource::<ProtoRegistry<T, C>>();

Self {
registry: world.resource::<AppTypeRegistry>().clone(),
proto_registry: world.resource::<ProtoRegistry<T, C>>().load_queue().clone(),
loader,
_phantom: Default::default(),
}
}
}

impl<T: Prototypical, L: Loader<T>, C: Config<T>> AssetLoader for ProtoAssetLoader<T, L, C> {
fn load<'a>(
&'a self,
bytes: &'a [u8],
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, anyhow::Result<(), anyhow::Error>> {
Box::pin(async {
let registry = self.registry.read();
let mut ctx = ProtoLoadContext::<T, L>::new(&registry, &self.loader, load_context);

// 1. Deserialize the prototype
let prototype = L::deserialize(bytes, &mut ctx)?;
let (prototype, _, mut dependency_paths) = ctx.preprocess_proto(prototype)?;
dependency_paths.append(ctx.child_paths_mut());

// 2. Register
let asset_handle: Handle<T> =
load_context.get_handle(AssetPath::new_ref(load_context.path(), None));
self.proto_registry
.write()
.queue(prototype.id().clone(), &asset_handle);

// 3. Finish!
let asset = LoadedAsset::new(prototype).with_dependencies(dependency_paths);
load_context.set_default_asset(asset);

Ok(())
})
}

fn extensions(&self) -> &[&str] {
self.loader.extensions()
}
}
Loading