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() } }