237 lines
10 KiB
Rust
Executable File
237 lines
10 KiB
Rust
Executable File
use anyhow::anyhow;
|
|
use std::marker::PhantomData;
|
|
use std::sync::Arc;
|
|
|
|
use anyhow_ext::Result;
|
|
use parking_lot::RwLock;
|
|
use paste::paste;
|
|
|
|
use crate::dynamic_data::{Battle, DynamicLibrary, ExecutingMove, Pokemon, TurnChoice};
|
|
use crate::script_implementations::wasm::export_registry::WasmVoidResult;
|
|
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
|
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData;
|
|
use crate::static_data::{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 {
|
|
(
|
|
$(
|
|
$prefix:ident, $name:ident($($par_type:ty),*$(,)?)
|
|
)*
|
|
) => {
|
|
#[derive(Default)]
|
|
#[allow(unused_parens)]
|
|
pub(super) struct ScriptFunctionCache {
|
|
script_get_name: RwLock<Option<TypedFunction<(u32), u32>>>,
|
|
dealloc_cstring: RwLock<Option<TypedFunction<(u32), ()>>>,
|
|
$(
|
|
$name: RwLock<Option<TypedFunction<(u32$(, $par_type)*), WasmVoidResult>>>,
|
|
)*
|
|
}
|
|
|
|
impl ScriptFunctionCache {
|
|
$(
|
|
paste! {
|
|
#[cold]
|
|
#[allow(unused_parens)]
|
|
fn [<initialize_ $name>](&self, env: &Arc<WebAssemblyEnvironmentData>) {
|
|
let exported = env.exported_functions();
|
|
let f = exported.get::<StringKey>(&stringify!([< $prefix $name >]).into());
|
|
if let Some(f) = f {
|
|
let func: TypedFunction<(u32 $(,$par_type)*), WasmVoidResult> = f.typed(&env.store_ref()).unwrap();
|
|
let _ = self.$name.write().insert(func);
|
|
}
|
|
}
|
|
|
|
|
|
#[allow(unused_parens)]
|
|
pub(crate) fn [<$name>](
|
|
&self,
|
|
env: &Arc<WebAssemblyEnvironmentData>,
|
|
) -> Option<TypedFunction<(u32 $(,$par_type)*), WasmVoidResult>> {
|
|
{
|
|
let read_lock = self.$name.read();
|
|
if let Some(f) = read_lock.as_ref() {
|
|
return Some(TypedFunction::clone(f));
|
|
}
|
|
}
|
|
self.[<initialize_ $name>](env);
|
|
match self.$name.read().as_ref() {
|
|
Some(f) => Some(TypedFunction::clone(f)),
|
|
None => None,
|
|
}
|
|
}
|
|
}
|
|
)*
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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<T> {
|
|
/// The memory size
|
|
v: u32,
|
|
/// Phantom data to store type.
|
|
_phantom: PhantomData<T>,
|
|
}
|
|
|
|
impl<T> Clone for WasmPtr<T> {
|
|
fn clone(&self) -> Self {
|
|
*self
|
|
}
|
|
}
|
|
|
|
impl<T> Copy for WasmPtr<T> {}
|
|
|
|
impl<T> From<u32> for WasmPtr<T> {
|
|
fn from(v: u32) -> Self {
|
|
Self {
|
|
v,
|
|
_phantom: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> From<WasmPtr<T>> for u32 {
|
|
fn from(v: WasmPtr<T>) -> Self {
|
|
v.v
|
|
}
|
|
}
|
|
|
|
unsafe impl<T> FromToNativeWasmType for WasmPtr<T> {
|
|
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! {
|
|
script_, stack()
|
|
script_, on_remove()
|
|
script_, on_initialize(ExternRef<dyn DynamicLibrary>, u64)
|
|
script_, on_before_turn(ExternRef<TurnChoice>)
|
|
script_, change_speed(ExternRef<TurnChoice>, WasmPtr<u32>)
|
|
script_, change_priority(ExternRef<TurnChoice>, WasmPtr<i8>)
|
|
script_, change_move(ExternRef<TurnChoice>, WasmPtr<ExternRef<StringKey>>)
|
|
script_, change_number_of_hits(ExternRef<TurnChoice>, WasmPtr<u8>)
|
|
script_, prevent_move(ExternRef<ExecutingMove>, WasmPtr<bool>)
|
|
script_, fail_move(ExternRef<ExecutingMove>, WasmPtr<bool>)
|
|
script_, stop_before_move(ExternRef<ExecutingMove>, WasmPtr<bool>)
|
|
script_, on_before_move(ExternRef<ExecutingMove>)
|
|
script_, fail_incoming_move(ExternRef<ExecutingMove>, ExternRef<Pokemon>, WasmPtr<bool>)
|
|
script_, is_invulnerable(ExternRef<ExecutingMove>, ExternRef<Pokemon>, WasmPtr<bool>)
|
|
script_, on_move_miss(ExternRef<ExecutingMove>, ExternRef<Pokemon>)
|
|
script_, change_move_type(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<TypeIdentifier>)
|
|
script_, change_effectiveness(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
|
|
script_, block_critical(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<bool>)
|
|
script_, block_incoming_critical(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<bool>)
|
|
script_, change_accuracy(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u8>)
|
|
script_, change_critical_stage(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u8>)
|
|
script_, change_critical_modifier(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
|
|
script_, change_stab_modifier(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
|
|
script_, change_base_power(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u8>)
|
|
script_, bypass_defensive_stat_boost(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<bool>)
|
|
script_, bypass_offensive_stat_boost(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<bool>)
|
|
script_, change_offensive_stat_value(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u32>)
|
|
script_, change_defensive_stat_value(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u32>)
|
|
script_, change_damage_stat_modifier(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
|
|
script_, change_damage_modifier(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
|
|
script_, change_damage(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u32>)
|
|
script_, change_incoming_damage(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u32>)
|
|
script_, on_incoming_hit(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8)
|
|
script_, on_opponent_faints(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8)
|
|
script_, prevent_stat_boost_change(ExternRef<Pokemon>, u8, i8, u8, WasmPtr<bool>)
|
|
script_, prevent_secondary_effect(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<bool>)
|
|
script_, change_effect_chance(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
|
|
script_, change_incoming_effect_chance(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
|
|
script_, on_secondary_effect(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8)
|
|
script_, on_after_hits(ExternRef<ExecutingMove>, ExternRef<Pokemon>)
|
|
script_, prevent_self_switch(ExternRef<TurnChoice>, WasmPtr<bool>)
|
|
script_, prevent_opponent_switch(ExternRef<TurnChoice>, WasmPtr<bool>)
|
|
script_, on_fail(ExternRef<Pokemon>)
|
|
script_, on_opponent_fail(ExternRef<Pokemon>)
|
|
script_, prevent_self_run_away(ExternRef<TurnChoice>, WasmPtr<bool>)
|
|
script_, prevent_opponent_run_away(ExternRef<TurnChoice>, WasmPtr<bool>)
|
|
script_, on_end_turn()
|
|
script_, on_damage(ExternRef<Pokemon>, u8, u32, u32)
|
|
script_, on_faint(ExternRef<Pokemon>, u8)
|
|
script_, on_switch_in(ExternRef<Pokemon>)
|
|
script_, on_after_held_item_consume(ExternRef<Pokemon>, ExternRef<dyn Item>)
|
|
script_, change_experience_gained(ExternRef<Pokemon>, ExternRef<Pokemon>, WasmPtr<u32>)
|
|
script_, share_experience(ExternRef<Pokemon>, ExternRef<Pokemon>, WasmPtr<bool>)
|
|
script_, block_weather(ExternRef<Battle>, WasmPtr<bool>)
|
|
script_, change_capture_rate_bonus(ExternRef<Pokemon>, ExternRef<dyn Item>, WasmPtr<u8>)
|
|
|
|
// Item script functions
|
|
item_script_, item_on_initialize(u64)
|
|
item_script_, item_is_usable(WasmPtr<bool>)
|
|
item_script_, item_requires_target(WasmPtr<bool>)
|
|
item_script_, item_is_target_valid(ExternRef<Pokemon>, WasmPtr<bool>)
|
|
item_script_, item_is_holdable(WasmPtr<bool>)
|
|
item_script_, item_can_target_hold(ExternRef<Pokemon>, WasmPtr<bool>)
|
|
item_script_, item_on_use()
|
|
item_script_, item_on_use_with_target(ExternRef<Pokemon>)
|
|
}
|
|
|
|
impl ScriptFunctionCache {
|
|
/// Get the name of a script.
|
|
pub(crate) fn script_get_name(&self, env: &Arc<WebAssemblyEnvironmentData>) -> Result<TypedFunction<u32, u32>> {
|
|
{
|
|
let read_lock = self.script_get_name.read();
|
|
if let Some(f) = read_lock.as_ref() {
|
|
return Ok(f.clone());
|
|
}
|
|
}
|
|
{
|
|
let exported = env.exported_functions();
|
|
let f = exported.get::<StringKey>(&"script_get_name".into());
|
|
if let Some(f) = f {
|
|
let func: TypedFunction<u32, u32> = f.typed(&env.store_ref())?;
|
|
let _ = self.script_get_name.write().insert(func);
|
|
}
|
|
}
|
|
Ok(self
|
|
.script_get_name
|
|
.read()
|
|
.as_ref()
|
|
.ok_or(anyhow!("Couldn't find script function `script_get_name`"))?
|
|
.clone())
|
|
}
|
|
|
|
/// Drop the memory of a CString inside the WASM memory.
|
|
pub(crate) fn dealloc_cstring(&self, env: &Arc<WebAssemblyEnvironmentData>, ptr: u32) -> Result<()> {
|
|
{
|
|
let read_lock = self.dealloc_cstring.read();
|
|
if read_lock.is_none() {
|
|
drop(read_lock);
|
|
let exported = env.exported_functions();
|
|
let f = exported.get::<StringKey>(&"dealloc_cstring".into());
|
|
if let Some(f) = f {
|
|
let func: TypedFunction<u32, ()> = f.typed(&env.store_ref())?;
|
|
let _ = self.dealloc_cstring.write().insert(func);
|
|
}
|
|
}
|
|
};
|
|
let func = self
|
|
.dealloc_cstring
|
|
.read()
|
|
.as_ref()
|
|
.ok_or(anyhow!("Couldn't find script function `dealloc_cstring`"))?
|
|
.clone();
|
|
func.call(&mut env.store_mut(), ptr)?;
|
|
Ok(())
|
|
}
|
|
}
|