use anyhow::anyhow;
use bevy::{
ecs::{
event::ManualEventReader,
query::WorldQuery,
system::{SystemParam, SystemState},
},
prelude::*,
};
use wasmer::{imports, Imports, Instance, Module};
use crate::{WasmScript, WasmerStore, WorldPointer};
fn instantiate_with_imports(
wasmer_store: &mut WasmerStore,
module: &Module,
imports: &Imports,
) -> Result<Instance, anyhow::Error> {
let instance = Instance::new(&mut wasmer_store.0, module, imports)?;
Ok(instance)
}
fn instantiate_if_compiled(
world: &mut World,
wasm_script_handle: Handle<WasmScript>,
get_imports: &impl Fn(&mut WasmerStore, &mut WorldPointer) -> Imports,
) -> Result<bool, anyhow::Error> {
unsafe {
let mut world_pointer = WorldPointer::new(world).clone();
let mut state =
SystemState::<(ResMut<Assets<WasmScript>>, ResMut<WasmerStore>)>::new(world);
let (mut wasm_assets, mut wasmer_store) = state.get_mut(world);
let wasm_script = wasm_assets
.get_mut(&wasm_script_handle)
.ok_or(anyhow!("Asset not properly loaded?"))?;
if let WasmScript::Compiled(module) = wasm_script {
let imports = &get_imports(&mut wasmer_store, &mut world_pointer);
match instantiate_with_imports(&mut wasmer_store, module, imports) {
Ok(instance) => {
let name = module.name().unwrap_or("").to_string();
*wasm_script = WasmScript::Instantiated(name, instance);
Ok(true)
}
Err(err) => {
Err(anyhow!(err))
}
}
} else {
Ok(false)
}
}
}
fn is_script_asset_modified(world: &mut World, resource_handle: &Handle<WasmScript>) -> bool {
let ev_asset_loaded = world
.get_resource_mut::<Events<AssetEvent<WasmScript>>>()
.unwrap();
let mut event_reader = ManualEventReader::<AssetEvent<WasmScript>>::default();
for asset in event_reader.iter(&ev_asset_loaded) {
if let AssetEvent::Modified { handle } = asset {
if handle == resource_handle {
return true;
}
}
}
false
}
pub trait WasmScriptResource: Resource {
type ImportQueriedComponents: WorldQuery;
type ImportResources: SystemParam;
fn get_handle(&self) -> Option<&Handle<WasmScript>>;
fn get_imports(_wasmer_store: &mut WasmerStore, _world_pointer: &mut WorldPointer) -> Imports {
imports! {}
}
}
pub fn instantiate_resource_script<R: Resource>(
get_handle: impl Fn(&R) -> Option<Handle<WasmScript>>,
get_imports: impl Fn(&mut WasmerStore, &mut WorldPointer) -> Imports,
) -> impl Fn(&mut World) {
move |mut world| {
if let Some(resource_handle) = world
.get_resource::<R>()
.and_then(|resource| get_handle(resource))
{
if is_script_asset_modified(&mut world, &resource_handle) {
instantiate_if_compiled(&mut world, resource_handle, &get_imports);
}
}
}
}
pub fn instantiate_wasm_resource_scripts<R: WasmScriptResource>(mut world: &mut World) {
if let Some(resource_handle) = world
.get_resource::<R>()
.and_then(|resource| resource.get_handle())
.cloned()
{
if is_script_asset_modified(&mut world, &resource_handle) {
instantiate_if_compiled(&mut world, resource_handle, &R::get_imports);
}
}
}