PkmnLib_rs/src/script_implementations/wasm/item_script.rs

168 lines
5.9 KiB
Rust

use crate::dynamic_data::{ItemScript, Pokemon};
use crate::script_implementations::wasm::export_registry::WasmVoidResultExtension;
use crate::script_implementations::wasm::extern_ref::ExternRef;
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData;
use crate::static_data::Parameter;
use crate::StringKey;
use std::sync::Arc;
/// A web assembly implementation of the item script.
pub struct WebAssemblyItemScript {
/// The unique identifier of the script.
name: StringKey,
/// Pointer inside WebAssembly memory where the data is for this script.
self_ptr: u32,
/// Cached value for the is_usable function.
is_usable: Option<bool>,
/// Cached value for the requires_target function.
requires_target: Option<bool>,
/// Cached value for the is_holdable function.
is_holdable: Option<bool>,
/// The global runtime environment data.
environment: Arc<WebAssemblyEnvironmentData>,
}
impl WebAssemblyItemScript {
/// Create a new instance of the script.
pub fn new(name: StringKey, self_ptr: u32, environment: Arc<WebAssemblyEnvironmentData>) -> Self {
Self {
name,
self_ptr,
is_usable: None,
requires_target: None,
is_holdable: None,
environment,
}
}
/// Get the name of the script.
pub fn name(&self) -> &StringKey {
&self.name
}
}
/// Util macro to reduce function call verbosity.
macro_rules! call_func {
($func:ident, $env:ident, $self:ident $(, $par_name:expr)*) => {
match $func.call(&mut $env.store_mut(), $self.self_ptr $(, $par_name)*) {
Ok(res) => res.into_result_env($env)?,
Err(e) => return Err(e.into()),
};
}
}
/// Util macro to reduce extern ref instantiation verbosity.
macro_rules! ex_ref {
($env:ident, $value:expr) => {
ExternRef::new($env.as_ref(), $value.into())
};
}
impl ItemScript for WebAssemblyItemScript {
fn on_initialize(&self, pars: Vec<Arc<Parameter>>) -> anyhow_ext::Result<()> {
let env = &self.environment;
if let Some(func) = env.script_function_cache().item_on_initialize(env) {
let pars = pars
.into_iter()
.map(|p| ExternRef::<Parameter>::new(env, (&p).into()).index() as u32)
.collect::<Vec<_>>();
let wasm_ptr = env.copy_value_vec_to_wasm(&pars)?;
let r: u64 = unsafe { std::mem::transmute((wasm_ptr, pars.len() as u32)) };
call_func!(func, env, self, r);
}
Ok(())
}
fn is_item_usable(&self) -> anyhow_ext::Result<bool> {
if let Some(is_usable) = self.is_usable {
return Ok(is_usable);
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().item_is_usable(env) {
let res_ptr = env.allocate_temp::<bool>(false);
call_func!(func, env, self, res_ptr.wasm_ptr);
let res = *res_ptr.value();
let self_mut = unsafe { (self as *const Self as *mut Self).as_mut().unwrap_unchecked() };
self_mut.is_usable = Some(res);
return Ok(res);
}
Ok(true)
}
fn requires_target(&self) -> anyhow_ext::Result<bool> {
if let Some(requires_target) = self.requires_target {
return Ok(requires_target);
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().item_requires_target(env) {
let res_ptr = env.allocate_temp::<bool>(false);
call_func!(func, env, self, res_ptr.wasm_ptr);
let res = *res_ptr.value();
let self_mut = unsafe { (self as *const Self as *mut Self).as_mut().unwrap_unchecked() };
self_mut.requires_target = Some(res);
return Ok(res);
}
Ok(false)
}
fn is_target_valid(&self, target: &Pokemon) -> anyhow::Result<bool> {
let env = &self.environment;
if let Some(func) = env.script_function_cache().item_is_target_valid(env) {
let target = ex_ref!(env, target);
let res_ptr = env.allocate_temp::<bool>(false);
call_func!(func, env, self, target, res_ptr.wasm_ptr);
return Ok(*res_ptr.value());
}
Ok(false)
}
fn is_holdable(&self) -> anyhow::Result<bool> {
if let Some(is_holdable) = self.is_holdable {
return Ok(is_holdable);
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().item_is_holdable(env) {
let res_ptr = env.allocate_temp::<bool>(false);
call_func!(func, env, self, res_ptr.wasm_ptr);
let res = *res_ptr.value();
let self_mut = unsafe { (self as *const Self as *mut Self).as_mut().unwrap_unchecked() };
self_mut.is_holdable = Some(res);
return Ok(res);
}
Ok(false)
}
fn can_target_hold(&self, target: &Pokemon) -> anyhow::Result<bool> {
let env = &self.environment;
if let Some(func) = env.script_function_cache().item_can_target_hold(env) {
let target = ex_ref!(env, target);
let res_ptr = env.allocate_temp::<bool>(false);
call_func!(func, env, self, target, res_ptr.wasm_ptr);
return Ok(*res_ptr.value());
}
Ok(false)
}
fn on_use(&self) -> anyhow::Result<()> {
let env = &self.environment;
if let Some(func) = env.script_function_cache().item_on_use(env) {
call_func!(func, env, self);
}
Ok(())
}
fn on_use_with_target(&self, target: &Pokemon) -> anyhow::Result<()> {
let env = &self.environment;
if let Some(func) = env.script_function_cache().item_on_use_with_target(env) {
let target = ex_ref!(env, target);
call_func!(func, env, self, target);
}
Ok(())
}
}