use std::any::type_name;
use anyhow::{Result, bail};
use bevy_ecs::prelude::*;
use bevy_log::prelude::*;
use wasmtime::component::Resource;
use wasmtime_wasi::ResourceTable;
use crate::{
access::ModAccess,
bindings::wasvy::ecs::app::{Bundle, BundleTypes},
cleanup::DespawnModEntity,
component::{insert_component, remove_component},
host::WasmHost,
runner::State,
};
pub(crate) fn map_entity<I, F>(host: &mut WasmHost, input: Resource<I>) -> Result<Resource<F>>
where
for<'a> &'a I: Into<Entity>,
F: From<Entity> + Send,
{
let State::RunSystem { table, .. } = host.access() else {
bail!(
"{} resource is only accessible when running systems",
type_name::<I>()
)
};
let input = table.get(&input)?;
let entity = input.into();
entity_resource(entity, table)
}
pub(crate) fn spawn_empty<F>(host: &mut WasmHost) -> Result<Resource<F>>
where
F: From<Entity> + Send,
{
let State::RunSystem {
commands,
table,
insert_despawn_component,
access,
..
} = host.access()
else {
bail!("Commands resource is only accessible when running systems",)
};
let mut entity_commands = commands.spawn_empty();
if let ModAccess::Sandbox(entity) = access {
entity_commands.insert(ChildOf(*entity));
};
if let Some(mod_id) = insert_despawn_component.0 {
entity_commands.insert(DespawnModEntity(mod_id));
}
let entity = entity_commands.id();
trace!("Spawn empty ({entity})");
entity_resource(entity, table)
}
pub(crate) fn insert<T>(host: &mut WasmHost, input: &Resource<T>, bundle: Bundle) -> Result<()>
where
for<'a> &'a T: Into<Entity>,
{
if bundle.is_empty() {
return Ok(());
}
let State::RunSystem {
commands,
table,
type_registry,
codec,
..
} = host.access()
else {
bail!(
"{} resource is only accessible when running systems",
type_name::<T>()
)
};
let input = table.get(input)?;
let entity = input.into();
trace!("Insert components to ({entity})");
for (type_path, serialized_component) in bundle {
#[cfg(feature = "serde_json")]
trace!(
"- {type_path}: {}",
String::from_utf8_lossy(&serialized_component)
);
#[cfg(not(feature = "serde_json"))]
trace!("- {type_path}: {:?}", serialized_component);
insert_component(
commands,
type_registry,
codec,
entity,
type_path,
serialized_component,
)?;
}
Ok(())
}
pub(crate) fn remove<T>(host: &mut WasmHost, input: Resource<T>, bundle: BundleTypes) -> Result<()>
where
for<'a> &'a T: Into<Entity>,
{
if bundle.is_empty() {
return Ok(());
}
let State::RunSystem {
commands,
table,
wasm_registry,
..
} = host.access()
else {
bail!(
"{} resource is only accessible when running systems",
type_name::<T>()
)
};
let input = table.get(&input)?;
let entity = input.into();
trace!("Remove components from ({entity})");
for type_path in bundle {
trace!("- {type_path}");
remove_component(commands, wasm_registry, entity, type_path)?;
}
Ok(())
}
fn entity_resource<T>(entity: Entity, table: &mut ResourceTable) -> Result<Resource<T>>
where
T: From<Entity> + Send,
{
let output = T::from(entity);
let output = table.push(output)?;
Ok(output)
}