use std::marker::PhantomData;
use std::sync::Arc;
use parking_lot::RwLock;
use paste::paste;
use crate::dynamic_data::{Battle, DynamicLibrary, ExecutingMove, Pokemon, TurnChoice};
use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef};
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData;
use crate::static_data::{EffectParameter, Item, TypeIdentifier};
use crate::StringKey;
use wasmer::{FromToNativeWasmType, TypedFunction};
/// A macro to generate the script function cache a bit easier.
macro_rules! script_function_cache {
(
$(
$name:ident($($par_type:ty),*$(,)?)
)*
) => {
#[derive(Default)]
#[allow(unused_parens)]
pub(super) struct ScriptFunctionCache {
script_get_name: RwLock>>,
dealloc_cstring: RwLock >>,
$(
$name: RwLock >>,
)*
}
impl ScriptFunctionCache {
$(
paste! {
#[cold]
#[allow(unused_parens)]
fn [](&self, env: &Arc) {
let exported = env.exported_functions();
let f = exported.get::(&stringify!([< script_ $name >]).into());
if let Some(f) = f {
let func: TypedFunction<(u32 $(,$par_type)*), ()> = f.typed(&env.store_ref()).unwrap();
let _ = self.$name.write().insert(func);
}
}
#[allow(unused_parens)]
pub(crate) fn [<$name>](
&self,
env: &Arc,
) -> Option> {
{
let read_lock = self.$name.read();
if let Some(f) = read_lock.as_ref() {
return Some(f.clone());
}
}
self.[](env);
self.$name.read().as_ref().cloned()
}
}
)*
}
}
}
/// Helper struct to indicate WASM client pointer type. This makes it harder to pass the wrong type
/// to a function by accident.
#[derive(Ord, PartialOrd, Eq, PartialEq)]
pub(super) struct WasmPtr {
/// The memory size
v: u32,
/// Phantom data to store type.
_phantom: PhantomData,
}
impl Clone for WasmPtr {
fn clone(&self) -> Self {
Self {
v: self.v,
_phantom: Default::default(),
}
}
}
impl Copy for WasmPtr {}
impl From for WasmPtr {
fn from(v: u32) -> Self {
Self {
v,
_phantom: Default::default(),
}
}
}
impl From> for u32 {
fn from(v: WasmPtr) -> Self {
v.v
}
}
unsafe impl FromToNativeWasmType for WasmPtr {
type Native = i32;
fn from_native(native: Self::Native) -> Self {
Self {
v: native as u32,
_phantom: Default::default(),
}
}
fn to_native(self) -> Self::Native {
self.v as i32
}
}
script_function_cache! {
stack()
on_remove()
on_initialize(ExternRef, VecExternRef)
on_before_turn(ExternRef)
change_speed(ExternRef, WasmPtr)
change_priority(ExternRef, WasmPtr)
change_move(ExternRef, WasmPtr>)
change_number_of_hits(ExternRef, WasmPtr)
prevent_move(ExternRef, WasmPtr)
fail_move(ExternRef, WasmPtr)
stop_before_move(ExternRef, WasmPtr)
on_before_move(ExternRef)
fail_incoming_move(ExternRef, ExternRef, WasmPtr)
is_invulnerable(ExternRef, ExternRef, WasmPtr)
on_move_miss(ExternRef, ExternRef)
change_move_type(ExternRef, ExternRef, u8, WasmPtr)
change_effectiveness(ExternRef, ExternRef, u8, WasmPtr)
block_critical(ExternRef, ExternRef, u8, WasmPtr)
block_incoming_critical(ExternRef, ExternRef, u8, WasmPtr)
change_accuracy(ExternRef, ExternRef, u8, WasmPtr)
change_critical_stage(ExternRef, ExternRef, u8, WasmPtr)
change_critical_modifier(ExternRef, ExternRef, u8, WasmPtr)
change_stab_modifier(ExternRef, ExternRef, u8, WasmPtr)
change_base_power(ExternRef, ExternRef, u8, WasmPtr)
bypass_defensive_stat_boost(ExternRef, ExternRef, u8, WasmPtr)
bypass_offensive_stat_boost(ExternRef, ExternRef, u8, WasmPtr)
change_offensive_stat_value(ExternRef, ExternRef, u8, WasmPtr)
change_defensive_stat_value(ExternRef, ExternRef, u8, WasmPtr)
change_damage_stat_modifier(ExternRef, ExternRef, u8, WasmPtr)
change_damage_modifier(ExternRef, ExternRef, u8, WasmPtr)
change_damage(ExternRef, ExternRef, u8, WasmPtr)
change_incoming_damage(ExternRef, ExternRef, u8, WasmPtr)
on_incoming_hit(ExternRef, ExternRef, u8)
on_opponent_faints(ExternRef, ExternRef, u8)
prevent_stat_boost_change(ExternRef, u8, i8, u8, WasmPtr)
prevent_secondary_effect(ExternRef, ExternRef, u8, WasmPtr)
change_effect_chance(ExternRef, ExternRef, u8, WasmPtr)
change_incoming_effect_chance(ExternRef, ExternRef, u8, WasmPtr)
on_secondary_effect(ExternRef, ExternRef, u8)
on_after_hits(ExternRef, ExternRef)
prevent_self_switch(ExternRef, WasmPtr)
prevent_opponent_switch(ExternRef, WasmPtr)
on_fail(ExternRef)
on_opponent_fail(ExternRef)
prevent_self_run_away(ExternRef, WasmPtr)
prevent_opponent_run_away(ExternRef, WasmPtr)
on_end_turn()
on_damage(ExternRef, u8, u32, u32)
on_faint(ExternRef, u8)
on_switch_in(ExternRef)
on_after_held_item_consume(ExternRef, ExternRef- )
change_experience_gained(ExternRef
, ExternRef, WasmPtr)
share_experience(ExternRef, ExternRef, WasmPtr)
block_weather(ExternRef, WasmPtr)
change_capture_rate_bonus(ExternRef, ExternRef- , WasmPtr
)
}
impl ScriptFunctionCache {
pub(crate) fn script_get_name(&self, env: &Arc) -> Option> {
{
let read_lock = self.script_get_name.read();
if let Some(f) = read_lock.as_ref() {
return Some(f.clone());
}
}
{
let exported = env.exported_functions();
let f = exported.get::(&"script_get_name".into());
if let Some(f) = f {
let func: TypedFunction = f.typed(&env.store_ref()).unwrap();
let _ = self.script_get_name.write().insert(func);
}
}
self.script_get_name.read().as_ref().cloned()
}
pub(crate) fn dealloc_cstring(&self, env: &Arc) -> Option> {
{
let read_lock = self.dealloc_cstring.read();
if let Some(f) = read_lock.as_ref() {
return Some(f.clone());
}
}
{
let exported = env.exported_functions();
let f = exported.get::(&"dealloc_cstring".into());
if let Some(f) = f {
let func: TypedFunction = f.typed(&env.store_ref()).unwrap();
let _ = self.dealloc_cstring.write().insert(func);
}
}
self.dealloc_cstring.read().as_ref().cloned()
}
}