use std::any::type_name;
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>, wasmtime::Error>
where
for<'a> &'a I: Into<Entity>,
F: From<Entity> + Send,
{
let State::RunSystem { table, .. } = host.access() else {
let ty = type_name::<I>();
return Err(wasmtime::Error::msg(format!(
"{ty} resource is only accessible when running systems"
)));
};
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>, wasmtime::Error>
where
F: From<Entity> + Send,
{
let State::RunSystem {
commands,
table,
insert_despawn_component,
access,
..
} = host.access()
else {
return Err(wasmtime::Error::msg(
"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<(), wasmtime::Error>
where
for<'a> &'a T: Into<Entity>,
{
if bundle.is_empty() {
return Ok(());
}
let State::RunSystem {
commands,
table,
type_registry,
codec,
..
} = host.access()
else {
let ty = type_name::<T>();
return Err(wasmtime::Error::msg(format!(
"{ty} resource is only accessible when running systems"
)));
};
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,
)
.map_err(wasmtime::Error::msg)?;
}
Ok(())
}
pub(crate) fn remove<T>(
host: &mut WasmHost,
input: Resource<T>,
bundle: BundleTypes,
) -> Result<(), wasmtime::Error>
where
for<'a> &'a T: Into<Entity>,
{
if bundle.is_empty() {
return Ok(());
}
let State::RunSystem {
commands,
table,
wasm_registry,
..
} = host.access()
else {
let ty = type_name::<T>();
return Err(wasmtime::Error::msg(format!(
"{ty} resource is only accessible when running systems"
)));
};
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>, wasmtime::Error>
where
T: From<Entity> + Send,
{
let output = T::from(entity);
let output = table.push(output)?;
Ok(output)
}