From ba5992e9257ad072553fa35d665e2a9f92641d92 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sat, 27 Aug 2022 18:04:56 +0200 Subject: [PATCH] Implements every script function --- src/dynamic_data/flow/turn_runner.rs | 8 +- src/dynamic_data/models/battle.rs | 1 + src/dynamic_data/models/executing_move.rs | 1 + src/dynamic_data/script_handling/script.rs | 6 +- .../dynamic_data/learned_move.rs | 4 +- .../wasm/export_registry/dynamic_data/mod.rs | 2 +- .../export_registry/dynamic_data/pokemon.rs | 16 +- .../dynamic_data/turn_choice.rs | 10 +- .../wasm/export_registry/mod.rs | 14 +- .../wasm/export_registry/static_data/mod.rs | 12 +- .../wasm/export_registry/static_data/moves.rs | 26 +- .../export_registry/static_data/species.rs | 14 +- src/script_implementations/wasm/extern_ref.rs | 12 +- src/script_implementations/wasm/mod.rs | 53 +- src/script_implementations/wasm/script.rs | 770 ++++++++++++++++-- .../wasm/script_function_cache.rs | 114 ++- .../wasm/script_resolver.rs | 6 +- .../wasm/temp_wasm_allocator.rs | 7 +- src/static_data/items.rs | 1 + 19 files changed, 919 insertions(+), 158 deletions(-) diff --git a/src/dynamic_data/flow/turn_runner.rs b/src/dynamic_data/flow/turn_runner.rs index 6bca850..81f3c92 100644 --- a/src/dynamic_data/flow/turn_runner.rs +++ b/src/dynamic_data/flow/turn_runner.rs @@ -92,8 +92,8 @@ impl Battle { /// Executes a move choice. fn execute_move_choice<'func>(&'func self, choice: &'func TurnChoice) -> PkmnResult<()> { - let choice = choice.get_move_turn_data(); - let used_move = choice.used_move(); + let move_choice = choice.get_move_turn_data(); + let used_move = move_choice.used_move(); let move_data = { let move_data_lock = used_move; let move_data = move_data_lock.move_data(); @@ -103,7 +103,7 @@ impl Battle { }; // FIXME: also change the script on the choice if changed; let target_type = move_data.target(); - let targets = resolve_targets(choice.target_side(), choice.target_index(), target_type, self); + let targets = resolve_targets(move_choice.target_side(), move_choice.target_index(), target_type, self); let mut number_of_hits: u8 = 1; script_hook!(change_number_of_hits, choice, choice, &mut number_of_hits); @@ -116,7 +116,7 @@ impl Battle { choice.user().clone(), used_move.clone(), move_data.clone(), - choice.script().clone(), + move_choice.script().clone(), ); let mut prevented = false; script_hook!(prevent_move, executing_move, &executing_move, &mut prevented); diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index 0e92842..195c8a9 100644 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -22,6 +22,7 @@ use crate::{script_hook, PkmnResult, StringKey}; /// A pokemon battle, with any amount of sides and pokemon per side. #[derive(Debug)] +#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct Battle { /// The library the battle uses for handling. library: Arc, diff --git a/src/dynamic_data/models/executing_move.rs b/src/dynamic_data/models/executing_move.rs index 8237a3d..41cee96 100644 --- a/src/dynamic_data/models/executing_move.rs +++ b/src/dynamic_data/models/executing_move.rs @@ -84,6 +84,7 @@ impl HitData { /// An executing move is the data of the move for while it is executing. #[derive(Debug)] +#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct ExecutingMove { /// The number of hits this move has. number_of_hits: u8, diff --git a/src/dynamic_data/script_handling/script.rs b/src/dynamic_data/script_handling/script.rs index 56db1ce..f4eaa72 100644 --- a/src/dynamic_data/script_handling/script.rs +++ b/src/dynamic_data/script_handling/script.rs @@ -7,7 +7,7 @@ use std::thread::JoinHandle; use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard}; -use crate::dynamic_data::choices::{MoveChoice, TurnChoice}; +use crate::dynamic_data::choices::TurnChoice; use crate::dynamic_data::DamageSource; use crate::dynamic_data::ExecutingMove; use crate::dynamic_data::Pokemon; @@ -79,11 +79,11 @@ pub trait Script: Send + Sync { /// This function allows you to change the move that is used during execution. This is useful for /// moves such as metronome, where the move chosen actually differs from the move used. - fn change_move(&self, _choice: &MoveChoice, _move_name: &mut StringKey) {} + fn change_move(&self, _choice: &TurnChoice, _move_name: &mut StringKey) {} /// This function allows you to change a move into a multi-hit move. The number of hits set here /// gets used as the number of hits. If set to 0, this will behave as if the move missed on its /// first hit. - fn change_number_of_hits(&self, _choice: &MoveChoice, _number_of_hits: &mut u8) {} + fn change_number_of_hits(&self, _choice: &TurnChoice, _number_of_hits: &mut u8) {} /// This function allows you to prevent a move from running. If this gets set to true, the move /// ends execution here. No PP will be decreased in this case. diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs b/src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs index b28e498..6c4c535 100644 --- a/src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs @@ -13,7 +13,7 @@ register! { turn_choice: ExternRef, ) -> u8 { unsafe { - transmute(turn_choice.value(&env).unwrap().learn_method()) + transmute(turn_choice.value_func(&env).unwrap().learn_method()) } } @@ -21,6 +21,6 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> ExternRef { - ExternRef::func_new(&env, turn_choice.value(&env).unwrap().move_data()) + ExternRef::func_new(&env, turn_choice.value_func(&env).unwrap().move_data()) } } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs b/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs index 6ae6ed5..ea0a804 100644 --- a/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs @@ -18,7 +18,7 @@ register! { env: FunctionEnvMut, dynamic_lib: ExternRef, ) -> ExternRef { - ExternRef::func_new(&env, dynamic_lib.value(&env).unwrap().static_data()) + ExternRef::func_new(&env, dynamic_lib.value_func(&env).unwrap().static_data()) } manual manual_register } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs b/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs index 85a9736..e878424 100644 --- a/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs @@ -13,7 +13,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef { - let lib = pokemon.value(&env).unwrap().library(); + let lib = pokemon.value_func(&env).unwrap().library(); ExternRef::func_new(&env, lib) } @@ -21,7 +21,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef> { - let statistic_set = pokemon.value(&env).unwrap().boosted_stats(); + let statistic_set = pokemon.value_func(&env).unwrap().boosted_stats(); ExternRef::func_new(&env, statistic_set) } @@ -29,7 +29,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef> { - let statistic_set = pokemon.value(&env).unwrap().flat_stats(); + let statistic_set = pokemon.value_func(&env).unwrap().flat_stats(); ExternRef::func_new(&env, statistic_set) } @@ -37,7 +37,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef> { - let statistic_set = pokemon.value(&env).unwrap().stat_boosts(); + let statistic_set = pokemon.value_func(&env).unwrap().stat_boosts(); ExternRef::func_new(&env, statistic_set) } @@ -45,7 +45,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef> { - let statistic_set = pokemon.value(&env).unwrap().individual_values(); + let statistic_set = pokemon.value_func(&env).unwrap().individual_values(); ExternRef::func_new(&env, statistic_set) } @@ -53,7 +53,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef> { - let statistic_set = pokemon.value(&env).unwrap().effort_values(); + let statistic_set = pokemon.value_func(&env).unwrap().effort_values(); ExternRef::func_new(&env, statistic_set) } @@ -61,7 +61,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef { - let species = pokemon.value(&env).unwrap().species(); + let species = pokemon.value_func(&env).unwrap().species(); ExternRef::func_new(&env, species) } @@ -72,7 +72,7 @@ register! { source: u8 ) { unsafe{ - pokemon.value(&env).unwrap().damage(damage, transmute(source)); + pokemon.value_func(&env).unwrap().damage(damage, transmute(source)); } } } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs b/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs index 25ba52d..84c8574 100644 --- a/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs @@ -12,7 +12,7 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> ExternRef { - let turn_choice = turn_choice.value(&env).unwrap(); + let turn_choice = turn_choice.value_func(&env).unwrap(); ExternRef::func_new(&env, turn_choice.user().as_ref().deref()) } @@ -20,7 +20,7 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> u8 { - match turn_choice.value(&env).unwrap() { + match turn_choice.value_func(&env).unwrap() { TurnChoice::Move(_) => 0, TurnChoice::Item(_) => 1, TurnChoice::Switch(_) => 2, @@ -33,7 +33,7 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> ExternRef { - if let TurnChoice::Move(d) = turn_choice.value(&env).unwrap() { + if let TurnChoice::Move(d) = turn_choice.value_func(&env).unwrap() { return ExternRef::func_new(&env, d.used_move().as_ref()); } panic!("Invalid turn choice"); @@ -43,7 +43,7 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> u8 { - if let TurnChoice::Move(d) = turn_choice.value(&env).unwrap() { + if let TurnChoice::Move(d) = turn_choice.value_func(&env).unwrap() { return d.target_side(); } panic!("Invalid turn choice"); @@ -53,7 +53,7 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> u8 { - if let TurnChoice::Move(d) = turn_choice.value(&env).unwrap() { + if let TurnChoice::Move(d) = turn_choice.value_func(&env).unwrap() { return d.target_index(); } panic!("Invalid turn choice"); diff --git a/src/script_implementations/wasm/export_registry/mod.rs b/src/script_implementations/wasm/export_registry/mod.rs index a5a1df4..490a0d8 100644 --- a/src/script_implementations/wasm/export_registry/mod.rs +++ b/src/script_implementations/wasm/export_registry/mod.rs @@ -119,13 +119,13 @@ fn _vec_extern_ref_get_value(env: FunctionEnvMut, reference: u32 /// Gets the hash value of a StringKey. fn string_key_get_hash(env: FunctionEnvMut, string_key: ExternRef) -> u32 { - string_key.value(&env).unwrap().hash() + string_key.value_func(&env).unwrap().hash() } /// Get a null-terminated C string from a StringKey. Note that this involves a copy into WASM /// memory, so this is relatively heavy. fn string_key_get_str(env: FunctionEnvMut, string_key: ExternRef) -> u32 { - let string_key = string_key.value(&env).unwrap().str(); + let string_key = string_key.value_func(&env).unwrap().str(); let wasm_string_ptr = env .data() .data() @@ -141,7 +141,7 @@ fn string_key_get_str(env: FunctionEnvMut, string_key: ExternRef /// Gets the type of an EffectParameter fn effect_parameter_get_type(env: FunctionEnvMut, parameter: ExternRef) -> u8 { - let v = parameter.value(&env).unwrap(); + let v = parameter.value_func(&env).unwrap(); match v { EffectParameter::Bool(_) => 1, EffectParameter::Int(_) => 2, @@ -152,7 +152,7 @@ fn effect_parameter_get_type(env: FunctionEnvMut, parameter: Ext /// Gets the inner bool data of an EffectParameter. Panics if it's not a bool. fn effect_parameter_as_bool(env: FunctionEnvMut, parameter: ExternRef) -> u8 { - let v = parameter.value(&env).unwrap(); + let v = parameter.value_func(&env).unwrap(); match v { EffectParameter::Bool(b) => { if *b { @@ -167,7 +167,7 @@ fn effect_parameter_as_bool(env: FunctionEnvMut, parameter: Exte /// Gets the inner int data of an EffectParameter. Panics if it's not an int. fn effect_parameter_as_int(env: FunctionEnvMut, parameter: ExternRef) -> i64 { - let v = parameter.value(&env).unwrap(); + let v = parameter.value_func(&env).unwrap(); match v { EffectParameter::Int(i) => *i, _ => panic!("Unexpected parameter type!"), @@ -176,7 +176,7 @@ fn effect_parameter_as_int(env: FunctionEnvMut, parameter: Exter /// Gets the inner float data of an EffectParameter. Panics if it's not a float. fn effect_parameter_as_float(env: FunctionEnvMut, parameter: ExternRef) -> f32 { - let v = parameter.value(&env).unwrap(); + let v = parameter.value_func(&env).unwrap(); match v { EffectParameter::Float(f) => *f, _ => panic!("Unexpected parameter type!"), @@ -188,7 +188,7 @@ fn effect_parameter_as_string( env: FunctionEnvMut, parameter: ExternRef, ) -> ExternRef { - let v = parameter.value(&env).unwrap(); + let v = parameter.value_func(&env).unwrap(); match v { EffectParameter::String(s) => ExternRef::func_new(&env, s), _ => panic!("Unexpected parameter type!"), diff --git a/src/script_implementations/wasm/export_registry/static_data/mod.rs b/src/script_implementations/wasm/export_registry/static_data/mod.rs index a0ee609..699c58a 100644 --- a/src/script_implementations/wasm/export_registry/static_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/static_data/mod.rs @@ -13,33 +13,33 @@ mod species; register! { fn static_data_get_move_library(env: FunctionEnvMut, data_library: ExternRef) -> ExternRef { - ExternRef::func_new(&env, data_library.value(&env).unwrap().moves()) + ExternRef::func_new(&env, data_library.value_func(&env).unwrap().moves()) } fn static_data_get_species_library( env: FunctionEnvMut, data_library: ExternRef, ) -> ExternRef { - ExternRef::func_new(&env, data_library.value(&env).unwrap().species()) + ExternRef::func_new(&env, data_library.value_func(&env).unwrap().species()) } fn static_data_get_item_library(env: FunctionEnvMut, data_library: ExternRef) -> ExternRef { - ExternRef::func_new(&env, data_library.value(&env).unwrap().items()) + ExternRef::func_new(&env, data_library.value_func(&env).unwrap().items()) } fn static_data_get_type_library(env: FunctionEnvMut, data_library: ExternRef) -> ExternRef { - ExternRef::func_new(&env, data_library.value(&env).unwrap().types()) + ExternRef::func_new(&env, data_library.value_func(&env).unwrap().types()) } fn static_data_get_library_settings( env: FunctionEnvMut, data_library: ExternRef, ) -> ExternRef { - ExternRef::func_new(&env, data_library.value(&env).unwrap().settings()) + ExternRef::func_new(&env, data_library.value_func(&env).unwrap().settings()) } fn library_settings_get_maximum_level(env: FunctionEnvMut, data_library: ExternRef) -> LevelInt { - data_library.value(&env).unwrap().maximum_level() + data_library.value_func(&env).unwrap().maximum_level() } manual manual_registration diff --git a/src/script_implementations/wasm/export_registry/static_data/moves.rs b/src/script_implementations/wasm/export_registry/static_data/moves.rs index a0a64a9..3d2ebda 100644 --- a/src/script_implementations/wasm/export_registry/static_data/moves.rs +++ b/src/script_implementations/wasm/export_registry/static_data/moves.rs @@ -11,8 +11,8 @@ fn move_library_get_move( lib: ExternRef, string_key: ExternRef, ) -> ExternRef { - let lib = lib.value(&env).unwrap(); - let m = lib.get(string_key.value(&env).unwrap()); + let lib = lib.value_func(&env).unwrap(); + let m = lib.get(string_key.value_func(&env).unwrap()); if let Some(v) = m { ExternRef::func_new(&env, v) } else { @@ -21,7 +21,7 @@ fn move_library_get_move( } fn move_library_get_move_by_hash(env: FunctionEnvMut, lib: ExternRef, hash: u32) -> ExternRef { - let lib = lib.value(&env).unwrap(); + let lib = lib.value_func(&env).unwrap(); let m = lib.get_by_hash(hash); if let Some(v) = m { ExternRef::func_new(&env, v) @@ -31,39 +31,39 @@ fn move_library_get_move_by_hash(env: FunctionEnvMut, lib: Exter } fn move_data_get_name(env: FunctionEnvMut, move_data: ExternRef) -> ExternRef { - ExternRef::func_new(&env, move_data.value(&env).unwrap().name()) + ExternRef::func_new(&env, move_data.value_func(&env).unwrap().name()) } fn move_data_get_type(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value(&env).unwrap().move_type().into() + move_data.value_func(&env).unwrap().move_type().into() } fn move_data_get_category(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value(&env).unwrap().category() as u8 + move_data.value_func(&env).unwrap().category() as u8 } fn move_data_get_base_power(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value(&env).unwrap().base_power() + move_data.value_func(&env).unwrap().base_power() } fn move_data_get_accuracy(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value(&env).unwrap().accuracy() + move_data.value_func(&env).unwrap().accuracy() } fn move_data_get_base_usages(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value(&env).unwrap().base_usages() + move_data.value_func(&env).unwrap().base_usages() } fn move_data_get_target(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value(&env).unwrap().target() as u8 + move_data.value_func(&env).unwrap().target() as u8 } fn move_data_get_priority(env: FunctionEnvMut, move_data: ExternRef) -> i8 { - move_data.value(&env).unwrap().priority() + move_data.value_func(&env).unwrap().priority() } fn move_data_has_flag(env: FunctionEnvMut, move_data: ExternRef, flag: ExternRef) -> u8 { - if move_data.value(&env).unwrap().has_flag(flag.value(&env).unwrap()) { + if move_data.value_func(&env).unwrap().has_flag(flag.value_func(&env).unwrap()) { 1 } else { 0 } } fn move_data_has_flag_by_hash(env: FunctionEnvMut, move_data: ExternRef, flag_hash: u32) -> u8 { - if move_data.value(&env).unwrap().has_flag_by_hash(flag_hash) { + if move_data.value_func(&env).unwrap().has_flag_by_hash(flag_hash) { 1 } else { 0 diff --git a/src/script_implementations/wasm/export_registry/static_data/species.rs b/src/script_implementations/wasm/export_registry/static_data/species.rs index b72779f..5b18814 100644 --- a/src/script_implementations/wasm/export_registry/static_data/species.rs +++ b/src/script_implementations/wasm/export_registry/static_data/species.rs @@ -12,8 +12,8 @@ fn species_library_get_species( lib: ExternRef, string_key: ExternRef, ) -> ExternRef { - let lib = lib.value(&env).unwrap(); - let m = lib.get(string_key.value(&env).unwrap()); + let lib = lib.value_func(&env).unwrap(); + let m = lib.get(string_key.value_func(&env).unwrap()); if let Some(v) = m { ExternRef::func_new(&env, v) } else { @@ -25,14 +25,14 @@ fn species_get_capture_rate( env: FunctionEnvMut, species: ExternRef, ) -> u8 { - species.value(&env).unwrap().capture_rate() + species.value_func(&env).unwrap().capture_rate() } fn species_get_growth_rate( env: FunctionEnvMut, species: ExternRef, ) -> ExternRef { - let species = species.value(&env).unwrap(); + let species = species.value_func(&env).unwrap(); ExternRef::func_new(&env, species.growth_rate()) } @@ -40,14 +40,14 @@ fn species_get_gender_rate( env: FunctionEnvMut, species: ExternRef, ) -> f32 { - species.value(&env).unwrap().gender_rate() + species.value_func(&env).unwrap().gender_rate() } fn species_get_name( env: FunctionEnvMut, species: ExternRef, ) -> ExternRef { - let species = species.value(&env).unwrap(); + let species = species.value_func(&env).unwrap(); ExternRef::func_new(&env, species.name()) } @@ -55,7 +55,7 @@ fn species_get_id( env: FunctionEnvMut, species: ExternRef, ) -> u16 { - species.value(&env).unwrap().id() + species.value_func(&env).unwrap().id() } diff --git a/src/script_implementations/wasm/extern_ref.rs b/src/script_implementations/wasm/extern_ref.rs index 5db2d86..48d4efb 100644 --- a/src/script_implementations/wasm/extern_ref.rs +++ b/src/script_implementations/wasm/extern_ref.rs @@ -1,5 +1,6 @@ use std::marker::PhantomData; use std::mem::transmute; +use std::sync::Arc; use crate::script_implementations::wasm::script_resolver::{ WebAssemblyEnv, WebAssemblyEnvironmentData, WebAssemblyScriptResolver, @@ -11,6 +12,7 @@ use wasmer::FunctionEnvMut; /// An Extern Ref allows us to pass objects to WASM without actually passing raw memory, or /// requiring us to make copies. Instead, we pass a simple increment index, that we can then use /// to find the relevant data. +#[derive(Copy, Clone)] pub(crate) struct ExternRef> { /// The lookup index we can use to find the data. index: u32, @@ -55,8 +57,14 @@ impl> ExternRef { /// Returns the real value for a given ExternRef. Note that the requested type must be the same as the type of the /// value when it was passed before. If these types do not match, this will panic. - pub fn value<'a, 'b>(&'a self, env: &'b FunctionEnvMut) -> Option<&'b T> { - let ptr = env.data().data().get_extern_ref_value(self.index) as *const T; + pub fn value_func<'a, 'b>(&'a self, env: &'b FunctionEnvMut) -> Option<&'b T> { + self.value(&env.data().data()) + } + + /// Returns the real value for a given ExternRef. Note that the requested type must be the same as the type of the + /// value when it was passed before. If these types do not match, this will panic. + pub fn value<'a, 'b, 'c>(&'a self, env: &'b Arc) -> Option<&'c T> { + let ptr = env.get_extern_ref_value(self.index) as *const T; unsafe { ptr.as_ref() } } } diff --git a/src/script_implementations/wasm/mod.rs b/src/script_implementations/wasm/mod.rs index df20be9..424fd3a 100644 --- a/src/script_implementations/wasm/mod.rs +++ b/src/script_implementations/wasm/mod.rs @@ -16,61 +16,64 @@ mod temp_wasm_allocator; #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] #[allow(missing_docs)] #[allow(clippy::missing_docs_in_private_items)] +#[repr(u8)] pub enum WebAssemblyScriptCapabilities { None = 0, Initialize = 1, OnStack, OnRemove, OnBeforeTurn, - ChangeAttack, - ModifyNumberOfHits, - PreventAttack, - FailAttack, - StopBeforeAttack, - OnBeforeAttack, - FailIncomingAttack, + ChangeMove, + ChangeNumberOfHits, + PreventMove, + FailMove, + StopBeforeMove, + OnBeforeMove, + FailIncomingMove, IsInvulnerable, - OnAttackMiss, - ChangeAttackType, + OnMoveMiss, + ChangeMoveType, ChangeEffectiveness, BlockCritical, OnIncomingHit, OnFaintingOpponent, PreventStatBoostChange, - ModifyStatBoostChange, + ChangeStatBoostChange, PreventSecondaryEffects, OnSecondaryEffect, OnAfterHits, PreventSelfSwitch, - ModifyEffectChance, - ModifyIncomingEffectChance, - OverrideBasePower, + ChangeEffectChance, + ChangeIncomingEffectChance, + ChangeBasePower, ChangeDamageStatsUser, BypassDefensiveStat, BypassOffensiveStat, - ModifyStatModifier, - ModifyDamageModifier, - OverrideDamage, - OverrideIncomingDamage, + ChangeStatModifier, + ChangeDamageModifier, + ChangeDamage, + ChangeIncomingDamage, ChangeSpeed, ChangePriority, OnFail, OnOpponentFail, - PreventRunAway, + PreventSelfRunAway, PreventOpponentRunAway, PreventOpponentSwitch, OnEndTurn, OnDamage, OnFaint, OnAfterHeldItemConsume, - PreventIncomingCritical, - ModifyCriticalStage, - OverrideCriticalModifier, - OverrideSTABModifier, - ModifyExperienceGain, + BlockIncomingCritical, + ChangeAccuracy, + ChangeCriticalStage, + ChangeCriticalModifier, + ChangeSTABModifier, + ChangeExperienceGain, DoesShareExperience, BlockWeather, OnSwitchIn, - ModifyOffensiveStatValue, - ModifyDefensiveStatValue, + ChangeOffensiveStatValue, + ChangeDefensiveStatValue, + ChangeCaptureRate, } diff --git a/src/script_implementations/wasm/script.rs b/src/script_implementations/wasm/script.rs index f132255..5f4987a 100644 --- a/src/script_implementations/wasm/script.rs +++ b/src/script_implementations/wasm/script.rs @@ -1,14 +1,14 @@ use std::any::Any; use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize}; -use std::sync::{Arc, Weak}; +use std::sync::Arc; use hashbrown::HashSet; -use crate::dynamic_data::{DynamicLibrary, Script, TurnChoice}; +use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, ExecutingMove, Pokemon, Script, TurnChoice}; use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData; use crate::script_implementations::wasm::WebAssemblyScriptCapabilities; -use crate::static_data::EffectParameter; +use crate::static_data::{EffectParameter, Item, Statistic, TypeIdentifier}; use crate::StringKey; /// A WebAssemblyScript is there to implement the Script trait within WebAssemblyScript. @@ -29,7 +29,7 @@ pub struct WebAssemblyScript { /// Capabilities define which functions we actually implement. capabilities: Arc>, /// The global runtime environment data. - environment: Weak, + environment: Arc, } impl WebAssemblyScript { @@ -38,7 +38,7 @@ impl WebAssemblyScript { owner_ptr: *mut u8, self_ptr: u32, capabilities: Arc>, - environment: Weak, + environment: Arc, name: StringKey, ) -> Self { Self { @@ -51,6 +51,33 @@ impl WebAssemblyScript { environment, } } + + /// Check if this script implements a certain capability. + #[inline(always)] + fn has_capability(&self, cap: &WebAssemblyScriptCapabilities) -> bool { + self.capabilities.contains(cap) + } +} + +/// Util macro to reduce function call verbosity. +macro_rules! call_func { + ($func:ident, $env:ident, $self:ident $(, $par_name:expr)*) => { + $func.call(&mut $env.store_mut(), $self.self_ptr $(, $par_name)*).unwrap(); + } +} + +/// Util macro to reduce extern ref instantiation verbosity. +macro_rules! ex_ref { + ($env:ident, $value:expr) => { + ExternRef::new($env.as_ref(), $value) + }; +} + +/// Util macro to reduce vec extern ref instantiation verbosity. +macro_rules! vec_ex_ref { + ($env:ident, $value:expr) => { + VecExternRef::new($env.as_ref(), $value) + }; } impl Script for WebAssemblyScript { @@ -67,104 +94,721 @@ impl Script for WebAssemblyScript { } fn stack(&self) { - if !self.capabilities.contains(&WebAssemblyScriptCapabilities::OnStack) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnStack) { return; } - let env = self.environment.upgrade().unwrap(); - let func = env.script_function_cache().stack(&env); - if let Some(func) = func { - func.call(&mut env.store_mut(), self.self_ptr).unwrap(); + let env = &self.environment; + if let Some(func) = env.script_function_cache().stack(env) { + call_func!(func, env, self); } } fn on_remove(&self) { - if !self.capabilities.contains(&WebAssemblyScriptCapabilities::OnRemove) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnRemove) { return; } - let env = self.environment.upgrade().unwrap(); - let func = env.script_function_cache().on_remove(&env); - if let Some(func) = func { - func.call(&mut env.store_mut(), self.self_ptr).unwrap(); + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_remove(env) { + call_func!(func, env, self); } } fn on_initialize(&self, library: &DynamicLibrary, pars: &[EffectParameter]) { - if !self.capabilities.contains(&WebAssemblyScriptCapabilities::Initialize) { + if !self.has_capability(&WebAssemblyScriptCapabilities::Initialize) { return; } - let env = self.environment.upgrade().unwrap(); - let func = env.script_function_cache().on_initialize(&env); - if let Some(func) = func { - func.call( - &mut env.store_mut(), - self.self_ptr, - ExternRef::new(env.as_ref(), library), - VecExternRef::new(env.as_ref(), pars), - ) - .unwrap(); + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_initialize(env) { + call_func!(func, env, self, ex_ref!(env, library), vec_ex_ref!(env, pars)); } } fn on_before_turn(&self, choice: &TurnChoice) { - if !self.capabilities.contains(&WebAssemblyScriptCapabilities::OnBeforeTurn) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnBeforeTurn) { return; } - let env = self.environment.upgrade().unwrap(); - let func = env.script_function_cache().on_before_turn(&env); - if let Some(func) = func { - func.call( - &mut env.store_mut(), - self.self_ptr, - ExternRef::new(env.as_ref(), choice), - ) - .unwrap(); + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_before_turn(env) { + call_func!(func, env, self, ex_ref!(env, choice)); } } fn change_speed(&self, choice: &TurnChoice, speed: &mut u32) { - if !self.capabilities.contains(&WebAssemblyScriptCapabilities::ChangeSpeed) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeSpeed) { return; } - let env = self.environment.upgrade().unwrap(); - let func = env.script_function_cache().change_speed(&env); - if let Some(func) = func { - let ptr = env.temp_allocate_mem_typed::(*speed); - func.call( - &mut env.store_mut(), - self.self_ptr, - ExternRef::new(env.as_ref(), choice), - ptr.wasm_pointer, - ) - .unwrap(); + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_speed(env) { + let ptr = env.allocate_temp::(*speed); + call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); *speed = *ptr.value(); } } fn change_priority(&self, choice: &TurnChoice, priority: &mut i8) { - if !self - .capabilities - .contains(&WebAssemblyScriptCapabilities::ChangePriority) - { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangePriority) { return; } - let env = self.environment.upgrade().unwrap(); - let func = env.script_function_cache().change_priority(&env); - if let Some(func) = func { - let ptr = env.temp_allocate_mem_typed::(*priority); - func.call( - &mut env.store_mut(), - self.self_ptr, - ExternRef::new(env.as_ref(), choice), - ptr.wasm_pointer, - ) - .unwrap(); + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_priority(env) { + let ptr = env.allocate_temp::(*priority); + call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); *priority = *ptr.value(); } } + fn change_move(&self, choice: &TurnChoice, move_name: &mut StringKey) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeMove) { + return; + } + + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_move(env) { + let move_ref = ex_ref!(env, move_name); + let ptr = env.allocate_temp::>(move_ref); + call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); + *move_name = ptr.value().value(env).unwrap().clone(); + } + } + + fn change_number_of_hits(&self, choice: &TurnChoice, number_of_hits: &mut u8) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeNumberOfHits) { + return; + } + + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_number_of_hits(env) { + let ptr = env.allocate_temp::(*number_of_hits); + call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); + *number_of_hits = *ptr.value(); + } + } + + fn prevent_move(&self, mv: &ExecutingMove, prevent: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::PreventMove) { + return; + } + + let env = &self.environment; + if let Some(func) = env.script_function_cache().prevent_move(env) { + let ptr = env.allocate_temp::(*prevent); + call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr); + *prevent = *ptr.value(); + } + } + + fn fail_move(&self, mv: &ExecutingMove, fail: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::FailMove) { + return; + } + + let env = &self.environment; + if let Some(func) = env.script_function_cache().fail_move(env) { + let ptr = env.allocate_temp::(*fail); + call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr); + *fail = *ptr.value(); + } + } + + fn stop_before_move(&self, mv: &ExecutingMove, stop: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::StopBeforeMove) { + return; + } + + let env = &self.environment; + if let Some(func) = env.script_function_cache().stop_before_move(env) { + let ptr = env.allocate_temp::(*stop); + call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr); + *stop = *ptr.value(); + } + } + + fn on_before_move(&self, mv: &ExecutingMove) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnBeforeMove) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_before_move(env) { + call_func!(func, env, self, ex_ref!(env, mv)); + } + } + + fn fail_incoming_move(&self, mv: &ExecutingMove, target: &Arc, fail: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::FailIncomingMove) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().fail_incoming_move(env) { + let ptr = env.allocate_temp::(*fail); + call_func!( + func, + env, + self, + ex_ref!(env, mv), + ex_ref!(env, target.as_ref()), + ptr.wasm_ptr + ); + *fail = *ptr.value(); + } + } + + fn is_invulnerable(&self, mv: &ExecutingMove, target: &Arc, invulnerable: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::IsInvulnerable) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().is_invulnerable(env) { + let ptr = env.allocate_temp::(*invulnerable); + call_func!( + func, + env, + self, + ex_ref!(env, mv), + ex_ref!(env, target.as_ref()), + ptr.wasm_ptr + ); + *invulnerable = *ptr.value(); + } + } + + fn on_move_miss(&self, mv: &ExecutingMove, target: &Arc) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnMoveMiss) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_move_miss(env) { + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target.as_ref())); + } + } + + fn change_move_type(&self, mv: &ExecutingMove, target: &Arc, hit: u8, move_type: &mut TypeIdentifier) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeMoveType) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_move_type(env) { + let ptr = env.allocate_temp::(*move_type); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *move_type = *ptr.value(); + } + } + + fn change_effectiveness(&self, mv: &ExecutingMove, target: &Arc, hit: u8, effectiveness: &mut f32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeEffectiveness) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_effectiveness(env) { + let ptr = env.allocate_temp(*effectiveness); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *effectiveness = *ptr.value(); + } + } + + fn block_critical(&self, mv: &ExecutingMove, target: &Arc, hit: u8, block_critical: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::BlockCritical) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().block_critical(env) { + let ptr = env.allocate_temp(*block_critical); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *block_critical = *ptr.value(); + } + } + + fn block_incoming_critical(&self, mv: &ExecutingMove, target: &Arc, hit: u8, block_critical: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::BlockIncomingCritical) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().block_incoming_critical(env) { + let ptr = env.allocate_temp(*block_critical); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *block_critical = *ptr.value(); + } + } + + fn change_accuracy(&self, mv: &ExecutingMove, target: &Arc, hit: u8, accuracy: &mut u8) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeAccuracy) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_accuracy(env) { + let ptr = env.allocate_temp(*accuracy); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *accuracy = *ptr.value(); + } + } + + fn change_critical_stage(&self, mv: &ExecutingMove, target: &Arc, hit: u8, stage: &mut u8) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCriticalStage) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_critical_stage(env) { + let ptr = env.allocate_temp(*stage); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *stage = *ptr.value(); + } + } + + fn change_critical_modifier(&self, mv: &ExecutingMove, target: &Arc, hit: u8, modifier: &mut f32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCriticalModifier) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_critical_modifier(env) { + let ptr = env.allocate_temp(*modifier); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *modifier = *ptr.value(); + } + } + + fn change_stab_modifier(&self, mv: &ExecutingMove, target: &Arc, hit: u8, modifier: &mut f32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeSTABModifier) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_stab_modifier(env) { + let ptr = env.allocate_temp(*modifier); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *modifier = *ptr.value(); + } + } + + fn change_base_power(&self, mv: &ExecutingMove, target: &Arc, hit: u8, base_power: &mut u8) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeBasePower) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_base_power(env) { + let ptr = env.allocate_temp(*base_power); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *base_power = *ptr.value(); + } + } + + fn bypass_defensive_stat_boost(&self, mv: &ExecutingMove, target: &Arc, hit: u8, bypass: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::BypassDefensiveStat) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().bypass_defensive_stat_boost(env) { + let ptr = env.allocate_temp(*bypass); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *bypass = *ptr.value(); + } + } + + fn bypass_offensive_stat_boost(&self, mv: &ExecutingMove, target: &Arc, hit: u8, bypass: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::BypassOffensiveStat) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().bypass_offensive_stat_boost(env) { + let ptr = env.allocate_temp(*bypass); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *bypass = *ptr.value(); + } + } + + fn change_offensive_stat_value(&self, mv: &ExecutingMove, target: &Arc, hit: u8, amount: &mut u32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeOffensiveStatValue) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_offensive_stat_value(env) { + let ptr = env.allocate_temp(*amount); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *amount = *ptr.value(); + } + } + + fn change_defensive_stat_value(&self, mv: &ExecutingMove, target: &Arc, hit: u8, amount: &mut u32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeOffensiveStatValue) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_defensive_stat_value(env) { + let ptr = env.allocate_temp(*amount); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *amount = *ptr.value(); + } + } + + fn change_damage_stat_modifier(&self, mv: &ExecutingMove, target: &Arc, hit: u8, modifier: &mut f32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeStatModifier) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_damage_stat_modifier(env) { + let ptr = env.allocate_temp(*modifier); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *modifier = *ptr.value(); + } + } + + fn change_damage_modifier(&self, mv: &ExecutingMove, target: &Arc, hit: u8, modifier: &mut f32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeDamageModifier) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_damage_modifier(env) { + let ptr = env.allocate_temp(*modifier); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *modifier = *ptr.value(); + } + } + + fn change_damage(&self, mv: &ExecutingMove, target: &Arc, hit: u8, damage: &mut u32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeDamage) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_damage(env) { + let ptr = env.allocate_temp(*damage); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *damage = *ptr.value(); + } + } + + fn change_incoming_damage(&self, mv: &ExecutingMove, target: &Arc, hit: u8, damage: &mut u32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeIncomingDamage) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_incoming_damage(env) { + let ptr = env.allocate_temp(*damage); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *damage = *ptr.value(); + } + } + + fn on_incoming_hit(&self, mv: &ExecutingMove, target: &Arc, hit: u8) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnIncomingHit) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_incoming_hit(env) { + let target = target.as_ref(); + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit); + } + } + + fn on_opponent_faints(&self, mv: &ExecutingMove, target: &Arc, hit: u8) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaintingOpponent) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_opponent_faints(env) { + let target = target.as_ref(); + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit); + } + } + + fn prevent_stat_boost_change( + &self, + target: &Pokemon, + stat: Statistic, + amount: i8, + self_inflicted: bool, + prevent: &mut bool, + ) { + if !self.has_capability(&WebAssemblyScriptCapabilities::PreventStatBoostChange) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().prevent_stat_boost_change(env) { + let ptr = env.allocate_temp(*prevent); + let self_inflicted = if self_inflicted { 1_u8 } else { 0_u8 }; + call_func!( + func, + env, + self, + ex_ref!(env, target), + stat as u8, + amount, + self_inflicted, + ptr.wasm_ptr + ); + *prevent = *ptr.value(); + } + } + + fn prevent_secondary_effect(&self, mv: &ExecutingMove, target: &Arc, hit: u8, prevent: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSecondaryEffects) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().prevent_secondary_effect(env) { + let ptr = env.allocate_temp(*prevent); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *prevent = *ptr.value(); + } + } + + fn change_effect_chance(&self, mv: &ExecutingMove, target: &Arc, hit: u8, chance: &mut f32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeEffectChance) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_effect_chance(env) { + let ptr = env.allocate_temp(*chance); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *chance = *ptr.value(); + } + } + + fn change_incoming_effect_chance(&self, mv: &ExecutingMove, target: &Arc, hit: u8, chance: &mut f32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeIncomingEffectChance) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_incoming_effect_chance(env) { + let ptr = env.allocate_temp(*chance); + let target = target.as_ref(); + let w_ptr = ptr.wasm_ptr; + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); + *chance = *ptr.value(); + } + } + + fn on_secondary_effect(&self, mv: &ExecutingMove, target: &Arc, hit: u8) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaintingOpponent) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_secondary_effect(env) { + let target = target.as_ref(); + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit); + } + } + + fn on_after_hits(&self, mv: &ExecutingMove, target: &Arc) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnAfterHits) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_after_hits(env) { + let target = target.as_ref(); + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target)); + } + } + + fn prevent_self_switch(&self, choice: &TurnChoice, prevent: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfSwitch) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().prevent_self_switch(env) { + let ptr = env.allocate_temp(*prevent); + call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); + *prevent = *ptr.value(); + } + } + + fn prevent_opponent_switch(&self, choice: &TurnChoice, prevent: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfSwitch) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().prevent_opponent_switch(env) { + let ptr = env.allocate_temp(*prevent); + call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); + *prevent = *ptr.value(); + } + } + + fn on_fail(&self, pokemon: &Pokemon) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnFail) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_fail(env) { + call_func!(func, env, self, ex_ref!(env, pokemon)); + } + } + + fn on_opponent_fail(&self, pokemon: &Pokemon) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnFail) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_opponent_fail(env) { + call_func!(func, env, self, ex_ref!(env, pokemon)); + } + } + + fn prevent_self_run_away(&self, choice: &TurnChoice, prevent: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfRunAway) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().prevent_self_run_away(env) { + let ptr = env.allocate_temp(*prevent); + call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); + *prevent = *ptr.value(); + } + } + + fn prevent_opponent_run_away(&self, choice: &TurnChoice, prevent: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::PreventOpponentRunAway) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().prevent_opponent_run_away(env) { + let ptr = env.allocate_temp(*prevent); + call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); + *prevent = *ptr.value(); + } + } + fn on_end_turn(&self) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnEndTurn) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_end_turn(env) { + call_func!(func, env, self); + } + } + + fn on_damage(&self, pokemon: &Pokemon, source: DamageSource, old_health: u32, new_health: u32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnDamage) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_damage(env) { + let r = ex_ref!(env, pokemon); + call_func!(func, env, self, r, source as u8, old_health, new_health); + } + } + + fn on_faint(&self, pokemon: &Pokemon, source: DamageSource) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaint) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_faint(env) { + call_func!(func, env, self, ex_ref!(env, pokemon), source as u8); + } + } + + fn on_switch_in(&self, pokemon: &Pokemon) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnSwitchIn) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_switch_in(env) { + call_func!(func, env, self, ex_ref!(env, pokemon)); + } + } + + fn on_after_held_item_consume(&self, pokemon: &Pokemon, item: &Item) { + if !self.has_capability(&WebAssemblyScriptCapabilities::OnAfterHeldItemConsume) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().on_after_held_item_consume(env) { + call_func!(func, env, self, ex_ref!(env, pokemon), ex_ref!(env, item)); + } + } + + fn change_experience_gained(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, amount: &mut u32) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeExperienceGain) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_experience_gained(env) { + let ptr = env.allocate_temp(*amount); + let fainted_mon = ex_ref!(env, fainted_mon); + let winning_mon = ex_ref!(env, winning_mon); + call_func!(func, env, self, fainted_mon, winning_mon, ptr.wasm_ptr); + *amount = *ptr.value(); + } + } + + fn share_experience(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, shares: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::DoesShareExperience) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().share_experience(env) { + let ptr = env.allocate_temp(*shares); + let fainted_mon = ex_ref!(env, fainted_mon); + let winning_mon = ex_ref!(env, winning_mon); + call_func!(func, env, self, fainted_mon, winning_mon, ptr.wasm_ptr); + *shares = *ptr.value(); + } + } + + fn block_weather(&self, battle: &Battle, blocked: &mut bool) { + if !self.has_capability(&WebAssemblyScriptCapabilities::BlockWeather) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().block_weather(env) { + let ptr = env.allocate_temp(*blocked); + call_func!(func, env, self, ex_ref!(env, battle), ptr.wasm_ptr); + *blocked = *ptr.value(); + } + } + + fn change_capture_rate_bonus(&self, target: &Pokemon, pokeball: &Item, modifier: &mut u8) { + if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCaptureRate) { + return; + } + let env = &self.environment; + if let Some(func) = env.script_function_cache().change_capture_rate_bonus(env) { + let ptr = env.allocate_temp(*modifier); + let target = ex_ref!(env, target); + let pokeball = ex_ref!(env, pokeball); + call_func!(func, env, self, target, pokeball, ptr.wasm_ptr); + *modifier = *ptr.value(); + } + } + fn as_any(&self) -> &dyn Any { self } diff --git a/src/script_implementations/wasm/script_function_cache.rs b/src/script_implementations/wasm/script_function_cache.rs index 072c2bc..074f592 100644 --- a/src/script_implementations/wasm/script_function_cache.rs +++ b/src/script_implementations/wasm/script_function_cache.rs @@ -1,14 +1,15 @@ +use std::marker::PhantomData; use std::sync::Arc; use parking_lot::RwLock; use paste::paste; -use crate::dynamic_data::{DynamicLibrary, TurnChoice}; +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; +use crate::static_data::{EffectParameter, Item, TypeIdentifier}; use crate::StringKey; -use wasmer::TypedFunction; +use wasmer::{FromToNativeWasmType, TypedFunction}; /// A macro to generate the script function cache a bit easier. macro_rules! script_function_cache { @@ -19,7 +20,7 @@ macro_rules! script_function_cache { ) => { #[derive(Default)] #[allow(unused_parens)] - pub struct ScriptFunctionCache { + pub(super) struct ScriptFunctionCache { $( $name: RwLock>>, )* @@ -60,11 +61,112 @@ macro_rules! script_function_cache { } } +/// 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, u32) - change_priority(ExternRef, u32) + 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) + } diff --git a/src/script_implementations/wasm/script_resolver.rs b/src/script_implementations/wasm/script_resolver.rs index 716ad9f..5382611 100644 --- a/src/script_implementations/wasm/script_resolver.rs +++ b/src/script_implementations/wasm/script_resolver.rs @@ -223,7 +223,7 @@ impl ScriptResolver for WebAssemblyScriptResolver { owner as *mut u8, script, capabilities.clone(), - Arc::downgrade(&self.environment_data), + self.environment_data.clone(), script_key.clone(), )))) } @@ -324,7 +324,7 @@ impl WebAssemblyEnvironmentData { } /// - pub fn script_function_cache(&self) -> &ScriptFunctionCache { + pub(super) fn script_function_cache(&self) -> &ScriptFunctionCache { &self.script_function_cache } @@ -361,7 +361,7 @@ impl WebAssemblyEnvironmentData { /// Allocate a piece of memory inside WASM with a very short lifespan. This is mainly used for /// rapid allocation of script function parameters, where WASM needs to write to a specific /// pointer. - pub fn temp_allocate_mem_typed(&self, value: T) -> AllocatedObject { + pub(super) fn allocate_temp(&self, value: T) -> AllocatedObject { self.temp_allocator.read().as_ref().unwrap().alloc::(value) } diff --git a/src/script_implementations/wasm/temp_wasm_allocator.rs b/src/script_implementations/wasm/temp_wasm_allocator.rs index 1399bdf..e8806ec 100644 --- a/src/script_implementations/wasm/temp_wasm_allocator.rs +++ b/src/script_implementations/wasm/temp_wasm_allocator.rs @@ -1,3 +1,4 @@ +use crate::script_implementations::wasm::script_function_cache::WasmPtr; use std::mem::size_of; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -40,7 +41,7 @@ impl TempWasmAllocator { } AllocatedObject:: { ptr, - wasm_pointer: self.wasm_pointer + ptr_offset as u32, + wasm_ptr: (self.wasm_pointer + ptr_offset as u32).into(), allocator: self as *const TempWasmAllocator, } } @@ -56,11 +57,11 @@ impl TempWasmAllocator { } /// A value allocated within WASM memory. Once this goes out of scope, it gets deallocated, -pub struct AllocatedObject { +pub(super) struct AllocatedObject { /// The pointer in host memory to where the object lives. pub ptr: *mut T, /// The pointer in client memory to where the object lives. - pub wasm_pointer: u32, + pub wasm_ptr: WasmPtr, /// A raw pointer to the allocator we use, so we know where to deallocate to. allocator: *const TempWasmAllocator, } diff --git a/src/static_data/items.rs b/src/static_data/items.rs index c661400..4bb60e6 100644 --- a/src/static_data/items.rs +++ b/src/static_data/items.rs @@ -46,6 +46,7 @@ pub enum BattleItemCategory { /// An item is an object which the player can pick up, keep in their Bag, and use in some manner #[derive(Debug)] +#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct Item { /// The name of the item. name: StringKey,