1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use bevy_app::{AppBuilder, Plugin};
#[cfg(target_os = "android")]
use bevy_asset::AndroidAssetIo;
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
use bevy_asset::FileAssetIo;
#[cfg(target_arch = "wasm32")]
use bevy_asset::WasmAssetIo;
use bevy_asset::{AssetIo, AssetIoError, AssetServer, AssetServerSettings, HandleUntyped};
use bevy_ecs::Res;
use bevy_tasks::IoTaskPool;
use bevy_utils::BoxedFuture;
use futures::future::TryFutureExt;
use std::collections::HashMap;
use std::path::{Path, PathBuf};

pub struct InlineAssets {
    assets: HashMap<&'static Path, &'static [u8]>,
}

impl InlineAssets {
    pub fn new() -> Self {
        Self {
            assets: HashMap::new(),
        }
    }

    pub fn load_all(
        &self,
        asset_server: Res<AssetServer>,
    ) -> HashMap<&'static Path, HandleUntyped> {
        self.assets
            .keys()
            .map(|&p| (p, asset_server.load_untyped(p)))
            .collect()
    }

    pub fn add(&mut self, path: &'static Path, data: &'static [u8]) -> &mut Self {
        self.assets.insert(path, data);
        self
    }
    fn io<T: AssetIo>(&self, base: T) -> InlineAssetIo {
        InlineAssetIo {
            assets: self.assets.clone(),
            base: Box::new(base),
        }
    }
}

#[macro_export]
macro_rules! inline_assets {
    [$($paths:literal),+,] => {
        inline_assets![$($paths),+]
    };
    [$($paths:literal),+] => {{
        let mut inline_assets = $crate::InlineAssets::new();
        $( inline_assets.add(Path::new($paths), &include_bytes!(concat!("../", $paths))[..]) );+;
        inline_assets
    }};
}

struct InlineAssetIo {
    assets: HashMap<&'static Path, &'static [u8]>,
    base: Box<dyn AssetIo>,
}

impl AssetIo for InlineAssetIo {
    fn load_path<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<Vec<u8>, AssetIoError>> {
        let future = self.base.load_path(path);
        if let Some(&bytes) = self.assets.get(path) {
            Box::pin(future.or_else(move |_| futures::future::ok(bytes.to_owned())))
        } else {
            future
        }
    }
    fn read_directory(
        &self,
        path: &Path,
    ) -> Result<Box<dyn Iterator<Item = PathBuf>>, AssetIoError> {
        self.base.read_directory(path)
    }
    fn is_directory(&self, path: &Path) -> bool {
        self.base.is_directory(path)
    }
    fn watch_path_for_changes(&self, path: &Path) -> Result<(), AssetIoError> {
        self.base.watch_path_for_changes(path)
    }
    fn watch_for_changes(&self) -> Result<(), AssetIoError> {
        self.base.watch_for_changes()
    }
}

#[derive(Default)]
pub struct InlineAssetsPlugin;

impl Plugin for InlineAssetsPlugin {
    fn build(&self, app: &mut AppBuilder) {
        let task_pool: IoTaskPool = (*app
            .resources()
            .get::<IoTaskPool>()
            .expect("IoTaskPool resource not found"))
        .clone();

        let base_asset_io = {
            let settings = app
                .resources_mut()
                .get_or_insert_with(AssetServerSettings::default);

            #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
            let asset_io = FileAssetIo::new(&settings.asset_folder);
            #[cfg(target_arch = "wasm32")]
            let asset_io = WasmAssetIo::new(&settings.asset_folder);
            #[cfg(target_os = "android")]
            let asset_io = AndroidAssetIo::new(&settings.asset_folder);
            asset_io
        };

        let asset_io = app
            .resources()
            .get::<InlineAssets>()
            .expect("InlineAssets resource not found")
            .io(base_asset_io);

        app.add_resource(AssetServer::new(asset_io, task_pool.0));
    }
}