From eb689772907845ee2d5418be221960e5fb12859e Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Fri, 21 Apr 2023 10:35:46 +0200 Subject: [PATCH] Support for errors from scripts through separate script error handling. --- src/dynamic_data/models/battle.rs | 2 +- src/dynamic_data/models/pokemon.rs | 11 +- src/dynamic_data/script_handling/mod.rs | 99 +++-- src/dynamic_data/script_handling/script.rs | 354 +++++++++++++---- .../script_handling/script_set.rs | 40 +- .../script_handling/volatile_scripts_owner.rs | 2 +- src/ffi/dynamic_data/mod.rs | 11 + src/script_implementations/wasm/script.rs | 370 ++++++++++++------ tests/integration.rs | 10 +- 9 files changed, 677 insertions(+), 222 deletions(-) diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index f248387..a35df64 100755 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -353,7 +353,7 @@ impl Battle { if let Some(script) = self.weather.get() { let lock = script.read(); Ok(Some( - lock.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.name().clone(), + lock.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.name()?.clone(), )) } else { Ok(None) diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index 6fb7eea..2a9091a 100755 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -535,11 +535,18 @@ impl Pokemon { .library .load_script(self.into(), ScriptCategory::Ability, ability.name())?; if let Some(ability_script) = ability_script { - self.ability_script + let script_result = self + .ability_script .set(ability_script) .as_ref() // Ensure the ability script gets initialized with the parameters for the ability. - .on_initialize(&self.library, ability.parameters().to_vec()) + .on_initialize(&self.library, ability.parameters().to_vec()); + match script_result { + Ok(_) => (), + Err(e) => { + crate::dynamic_data::script_handling::handle_script_error(&e); + } + } } else { self.ability_script.clear(); } diff --git a/src/dynamic_data/script_handling/mod.rs b/src/dynamic_data/script_handling/mod.rs index 20d75db..95ab715 100755 --- a/src/dynamic_data/script_handling/mod.rs +++ b/src/dynamic_data/script_handling/mod.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, Result}; -use std::sync::{Arc, Weak}; +use std::sync::{Arc, LazyLock, Weak}; use parking_lot::RwLock; @@ -33,7 +33,12 @@ macro_rules! script_hook { if let Some(script) = script { if let Some(script) = script.read().as_deref() { if !script.is_suppressed() { - script.$hook_name($($parameters),*); + match script.$hook_name($($parameters),*) { + Ok(_) => {} + Err(e) => { + $crate::dynamic_data::script_handling::handle_script_error(&e); + } + } } } } @@ -53,7 +58,12 @@ macro_rules! run_scripts { let s = s.read(); if let Some(s) = s.deref() { if !s.is_suppressed() { - s.$hook_name($($parameters),*); + match s.$hook_name($($parameters),*) { + Ok(_) => {} + Err(e) => { + $crate::dynamic_data::script_handling::handle_script_error(&e); + } + } } } } @@ -65,8 +75,13 @@ macro_rules! run_scripts { if let Some(s) = s.get() { let s = s.read(); if let Some(s) = s.deref() { - if !s.is_suppressed() && set.has(s.name()) { - s.$hook_name($($parameters),*); + if !s.is_suppressed() && set.has(s.name()?) { + match s.$hook_name($($parameters),*) { + Ok(_) => {} + Err(e) => { + $crate::dynamic_data::script_handling::handle_script_error(&e); + } + } } } } @@ -78,6 +93,35 @@ macro_rules! run_scripts { }; } +/// When a script encounters an error, this function should be called. This allows the implementation +/// to define how to handle script errors. +pub(crate) fn handle_script_error(e: &anyhow_ext::Error) { + unsafe { + (HANDLE_SCRIPT_ERROR_IMPL.read())(e); + } +} + +/// The type of the function that is called when a script encounters an error. +type ScriptErrorFunction = Box; + +/// The currently set script error handler. +static mut HANDLE_SCRIPT_ERROR_IMPL: LazyLock> = + LazyLock::new(|| RwLock::new(Box::new(default_script_error_handler))); + +/// The default script error handler. This will panic with the error message. As this can (and should) +/// be changed by the implementation, we allow panics here. +#[allow(clippy::panic)] +fn default_script_error_handler(e: &anyhow_ext::Error) { + panic!("Script error: {}", e); +} + +/// Sets the script error handler to the given function. +pub fn set_script_error_handler(f: ScriptErrorFunction) { + unsafe { + *HANDLE_SCRIPT_ERROR_IMPL.write() = f; + } +} + /// The script source data is the basic data required for any script source. #[derive(Default, Debug)] pub struct ScriptSourceData { @@ -279,8 +323,8 @@ mod tests { } impl Script for TestScript { - fn name(&self) -> &StringKey { - &self.name + fn name(&self) -> Result<&StringKey> { + Ok(&self.name) } fn get_marked_for_deletion(&self) -> &AtomicBool { @@ -295,8 +339,9 @@ mod tests { fn remove_suppression(&self) {} - fn stack(&self) { + fn stack(&self) -> Result<()> { self.test_count.fetch_add(1, Ordering::SeqCst); + Ok(()) } fn as_any(&self) -> &dyn Any { @@ -314,7 +359,7 @@ mod tests { let scripts = vec![ScriptWrapper::from(&script)]; let mut aggregator = ScriptIterator::new(&scripts as *const Vec); while let Some(v) = aggregator.get_next().unwrap() { - v.get().unwrap().read().as_ref().unwrap().stack(); + v.get().unwrap().read().as_ref().unwrap().stack().unwrap(); } let a = script.get_as::().unwrap(); assert_eq!(a.test_count.load(Ordering::Relaxed), 1); @@ -328,7 +373,7 @@ mod tests { for i in 1..11 { aggregator.reset(); while let Some(v) = aggregator.get_next().unwrap() { - v.get().unwrap().read().as_ref().unwrap().stack(); + v.get().unwrap().read().as_ref().unwrap().stack().unwrap(); } let a = script.get_as::().unwrap(); assert_eq!(a.test_count.load(Ordering::Relaxed), i); @@ -347,7 +392,7 @@ mod tests { ]; let mut aggregator = ScriptIterator::new(&scripts as *const Vec); while let Some(v) = aggregator.get_next().unwrap() { - v.get().unwrap().read().as_ref().unwrap().stack(); + v.get().unwrap().read().as_ref().unwrap().stack().unwrap(); } let a = script1.get_as::().unwrap(); assert_eq!(a.test_count.load(Ordering::Relaxed), 1); @@ -371,7 +416,7 @@ mod tests { for i in 1..11 { aggregator.reset(); while let Some(v) = aggregator.get_next().unwrap() { - v.get().unwrap().read().as_ref().unwrap().stack(); + v.get().unwrap().read().as_ref().unwrap().stack().unwrap(); } let a = script1.get_as::().unwrap(); assert_eq!(a.test_count.load(Ordering::Relaxed), i); @@ -385,38 +430,38 @@ mod tests { #[test] fn script_aggregator_property_iterates_script_set() { let set = Arc::new(ScriptSet::default()); - set.add(Arc::new(TestScript::new_with_name("test_a"))); - set.add(Arc::new(TestScript::new_with_name("test_b"))); - set.add(Arc::new(TestScript::new_with_name("test_c"))); + set.add(Arc::new(TestScript::new_with_name("test_a"))).unwrap(); + set.add(Arc::new(TestScript::new_with_name("test_b"))).unwrap(); + set.add(Arc::new(TestScript::new_with_name("test_c"))).unwrap(); let scripts = vec![ScriptWrapper::from(&set)]; let mut aggregator = ScriptIterator::new(&scripts as *const Vec); for i in 1..11 { aggregator.reset(); while let Some(v) = aggregator.get_next().unwrap() { - v.get().unwrap().read().as_ref().unwrap().stack(); + v.get().unwrap().read().as_ref().unwrap().stack().unwrap(); } let s = set.at(0).unwrap(); let s = s.get_as::().unwrap(); assert_eq!(s.test_count.load(Ordering::Relaxed), i); - assert_eq!(s.name().str(), "test_a"); + assert_eq!(s.name().unwrap().str(), "test_a"); let s = set.at(1).unwrap(); let s = s.get_as::().unwrap(); assert_eq!(s.test_count.load(Ordering::Relaxed), i); - assert_eq!(s.name().str(), "test_b"); + assert_eq!(s.name().unwrap().str(), "test_b"); let s = set.at(2).unwrap(); let s = s.get_as::().unwrap(); assert_eq!(s.test_count.load(Ordering::Relaxed), i); - assert_eq!(s.name().str(), "test_c"); + assert_eq!(s.name().unwrap().str(), "test_c"); } } #[test] fn script_aggregator_property_iterates_script_set_when_removing_last() { let set = Arc::new(ScriptSet::default()); - set.add(Arc::new(TestScript::new_with_name("test_a"))); - set.add(Arc::new(TestScript::new_with_name("test_b"))); - set.add(Arc::new(TestScript::new_with_name("test_c"))); + set.add(Arc::new(TestScript::new_with_name("test_a"))).unwrap(); + set.add(Arc::new(TestScript::new_with_name("test_b"))).unwrap(); + set.add(Arc::new(TestScript::new_with_name("test_c"))).unwrap(); let scripts = vec![ScriptWrapper::from(&set)]; let mut aggregator = ScriptIterator::new(&scripts as *const Vec); @@ -431,6 +476,7 @@ mod tests { .as_ref() .unwrap() .name() + .unwrap() .str(), "test_a" ); @@ -445,6 +491,7 @@ mod tests { .as_ref() .unwrap() .name() + .unwrap() .str(), "test_b" ); @@ -456,9 +503,9 @@ mod tests { #[test] fn script_aggregator_property_iterates_script_set_when_removing_middle() { let set = Arc::new(ScriptSet::default()); - set.add(Arc::new(TestScript::new_with_name("test_a"))); - set.add(Arc::new(TestScript::new_with_name("test_b"))); - set.add(Arc::new(TestScript::new_with_name("test_c"))); + set.add(Arc::new(TestScript::new_with_name("test_a"))).unwrap(); + set.add(Arc::new(TestScript::new_with_name("test_b"))).unwrap(); + set.add(Arc::new(TestScript::new_with_name("test_c"))).unwrap(); let scripts = vec![ScriptWrapper::from(&set)]; let mut aggregator = ScriptIterator::new(&scripts as *const Vec); @@ -473,6 +520,7 @@ mod tests { .as_ref() .unwrap() .name() + .unwrap() .str(), "test_a" ); @@ -490,6 +538,7 @@ mod tests { .as_ref() .unwrap() .name() + .unwrap() .str(), "test_c" ); diff --git a/src/dynamic_data/script_handling/script.rs b/src/dynamic_data/script_handling/script.rs index df296e1..3890cdd 100755 --- a/src/dynamic_data/script_handling/script.rs +++ b/src/dynamic_data/script_handling/script.rs @@ -23,7 +23,7 @@ use crate::StringKey; pub trait Script: Send + Sync { /// The name of a script is its unique identifier. This should generally be set on load, and be /// the same as the key that was used to load it. - fn name(&self) -> &StringKey; + fn name(&self) -> Result<&StringKey>; /// Returns an atomic bool for internal marking of deletion. This is currently only specifically /// used for deletion of a script while we are holding a reference to it (i.e. executing a script ///hook on it). @@ -61,50 +61,80 @@ pub trait Script: Send + Sync { /// This function is ran when a volatile effect is added while that volatile effect already is /// in place. Instead of adding the volatile effect twice, it will execute this function instead. - fn stack(&self) {} + fn stack(&self) -> Result<()> { + Ok(()) + } /// This function is ran when this script stops being in effect, and is removed from its owner. - fn on_remove(&self) {} + fn on_remove(&self) -> Result<()> { + Ok(()) + } /// This function is ran when this script starts being in effect. - fn on_initialize(&self, _library: &Arc, _pars: Vec) {} + fn on_initialize(&self, _library: &Arc, _pars: Vec) -> Result<()> { + Ok(()) + } /// This function is ran just before the start of the turn. Everyone has made its choices here, /// and the turn is about to start. This is a great place to initialize data if you need to know /// something has happened during a turn. - fn on_before_turn(&self, _choice: &TurnChoice) {} + fn on_before_turn(&self, _choice: &TurnChoice) -> Result<()> { + Ok(()) + } /// This function allows you to modify the effective speed of the Pokemon. This is ran before /// turn ordering, so overriding here will allow you to put certain Pokemon before others. - fn change_speed(&self, _choice: &TurnChoice, _speed: &mut u32) {} + fn change_speed(&self, _choice: &TurnChoice, _speed: &mut u32) -> Result<()> { + Ok(()) + } /// This function allows you to modify the effective priority of the Pokemon. This is ran before /// turn ordering, so overriding here will allow you to put certain Pokemon before others. Note /// that this is only relevant on move choices, as other turn choice types do not have a priority. - fn change_priority(&self, _choice: &TurnChoice, _priority: &mut i8) {} + fn change_priority(&self, _choice: &TurnChoice, _priority: &mut i8) -> Result<()> { + Ok(()) + } /// 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: &TurnChoice, _move_name: &mut StringKey) {} + fn change_move(&self, _choice: &TurnChoice, _move_name: &mut StringKey) -> Result<()> { + Ok(()) + } /// 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: &TurnChoice, _number_of_hits: &mut u8) {} + fn change_number_of_hits(&self, _choice: &TurnChoice, _number_of_hits: &mut u8) -> Result<()> { + Ok(()) + } /// 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. - fn prevent_move(&self, _move: &ExecutingMove, _prevent: &mut bool) {} + fn prevent_move(&self, _move: &ExecutingMove, _prevent: &mut bool) -> Result<()> { + Ok(()) + } /// This function makes the move fail. If the fail field gets set to true, the move ends execution, /// and fail events get triggered. - fn fail_move(&self, _move: &ExecutingMove, _fail: &mut bool) {} + fn fail_move(&self, _move: &ExecutingMove, _fail: &mut bool) -> Result<()> { + Ok(()) + } /// Similar to [`Self::prevent_move`]. This function will also stop execution, but PP will be /// decreased. - fn stop_before_move(&self, _move: &ExecutingMove, _stop: &mut bool) {} + fn stop_before_move(&self, _move: &ExecutingMove, _stop: &mut bool) -> Result<()> { + Ok(()) + } /// This function runs just before the move starts its execution. - fn on_before_move(&self, _move: &ExecutingMove) {} + fn on_before_move(&self, _move: &ExecutingMove) -> Result<()> { + Ok(()) + } /// This function allows a script to prevent a move that is targeted at its owner. If set to true /// the move fails, and fail events get triggered. - fn fail_incoming_move(&self, _move: &ExecutingMove, _target: &Arc, _fail: &mut bool) {} + fn fail_incoming_move(&self, _move: &ExecutingMove, _target: &Arc, _fail: &mut bool) -> Result<()> { + Ok(()) + } /// This function allows a script to make its owner invulnerable to an incoming move. - fn is_invulnerable(&self, _move: &ExecutingMove, _target: &Arc, _invulnerable: &mut bool) {} + fn is_invulnerable(&self, _move: &ExecutingMove, _target: &Arc, _invulnerable: &mut bool) -> Result<()> { + Ok(()) + } /// This function occurs when a move gets missed. This runs on the scripts belonging to the executing /// move, which include the scripts that are attached to the owner of the script. - fn on_move_miss(&self, _move: &ExecutingMove, _target: &Arc) {} + fn on_move_miss(&self, _move: &ExecutingMove, _target: &Arc) -> Result<()> { + Ok(()) + } /// This function allows the script to change the actual type that is used for the move on a target. fn change_move_type( &self, @@ -112,12 +142,29 @@ pub trait Script: Send + Sync { _target: &Arc, _hit: u8, _move_type: &mut TypeIdentifier, - ) { + ) -> Result<()> { + Ok(()) } /// This function allows the script to change how effective a move is on a target. - fn change_effectiveness(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _effectiveness: &mut f32) {} + fn change_effectiveness( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _effectiveness: &mut f32, + ) -> Result<()> { + Ok(()) + } /// This function allows a script to block an outgoing move from being critical. - fn block_critical(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _block_critical: &mut bool) {} + fn block_critical( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _block_critical: &mut bool, + ) -> Result<()> { + Ok(()) + } /// This function allows a script to block an incoming move from being critical. fn block_incoming_critical( &self, @@ -125,33 +172,104 @@ pub trait Script: Send + Sync { _target: &Arc, _hit: u8, _block_critical: &mut bool, - ) { + ) -> Result<()> { + Ok(()) } /// This function allows a script to modify the accuracy of a move used. This value represents /// the percentage accuracy, so anything above 100% will make it always hit. - fn change_accuracy(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _accuracy: &mut u8) {} + fn change_accuracy( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _accuracy: &mut u8, + ) -> Result<()> { + Ok(()) + } /// This function allows a script to change the critical stage of the move used. - fn change_critical_stage(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _stage: &mut u8) {} + fn change_critical_stage( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _stage: &mut u8, + ) -> Result<()> { + Ok(()) + } /// This function allows a script to change the damage modifier of a critical hit. This will only /// run when a hit is critical. - fn change_critical_modifier(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _modifier: &mut f32) {} + fn change_critical_modifier( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _modifier: &mut f32, + ) -> Result<()> { + Ok(()) + } /// This function allows a script to change the damage modifier of a Same Type Attack Bonus, which /// occurs when the user has the move type as one of its own types. - fn change_stab_modifier(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _modifier: &mut f32) {} + fn change_stab_modifier( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _modifier: &mut f32, + ) -> Result<()> { + Ok(()) + } /// This function allows a script to change the effective base power of a move hit. - fn change_base_power(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _base_power: &mut u8) {} + fn change_base_power( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _base_power: &mut u8, + ) -> Result<()> { + Ok(()) + } /// This function allows a script to bypass defensive stat boosts for a move hit. - fn bypass_defensive_stat_boost(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _bypass: &mut bool) { + fn bypass_defensive_stat_boost( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _bypass: &mut bool, + ) -> Result<()> { + Ok(()) } /// This function allows a script to bypass offensive stat boosts for a move hit. - fn bypass_offensive_stat_boost(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _bypass: &mut bool) { + fn bypass_offensive_stat_boost( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _bypass: &mut bool, + ) -> Result<()> { + Ok(()) } /// This function allows a script to change the actual offensive stat values used when calculating damage - fn change_offensive_stat_value(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _amount: &mut u32) {} + fn change_offensive_stat_value( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _amount: &mut u32, + ) -> Result<()> { + Ok(()) + } /// This function allows a script to change the actual defensive stat values used when calculating damage. - fn change_defensive_stat_value(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _amount: &mut u32) {} + fn change_defensive_stat_value( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _amount: &mut u32, + ) -> Result<()> { + Ok(()) + } /// This function allows a script to change the raw modifier we retrieved from the stats of the /// defender and attacker. @@ -161,19 +279,42 @@ pub trait Script: Send + Sync { _target: &Arc, _hit: u8, _modifier: &mut f32, - ) { + ) -> Result<()> { + Ok(()) } /// This function allows a script to apply a raw multiplier to the damage done by a move. - fn change_damage_modifier(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _modifier: &mut f32) {} + fn change_damage_modifier( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _modifier: &mut f32, + ) -> Result<()> { + Ok(()) + } /// This function allows a script to modify the outgoing damage done by a move. - fn change_damage(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _damage: &mut u32) {} + fn change_damage(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _damage: &mut u32) -> Result<()> { + Ok(()) + } /// This function allows a script to modify the incoming damage done by a move. - fn change_incoming_damage(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _damage: &mut u32) {} + fn change_incoming_damage( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _damage: &mut u32, + ) -> Result<()> { + Ok(()) + } /// This function triggers when an incoming hit happens. This triggers after the damage is done, /// but before the secondary effect of the move happens. - fn on_incoming_hit(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8) {} + fn on_incoming_hit(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8) -> Result<()> { + Ok(()) + } /// This function triggers when an opponent on the field faints. - fn on_opponent_faints(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8) {} + fn on_opponent_faints(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8) -> Result<()> { + Ok(()) + } /// This function allows a script attached to a Pokemon or its parents to prevent stat boost /// changes on that Pokemon. fn prevent_stat_boost_change( @@ -183,21 +324,46 @@ pub trait Script: Send + Sync { _amount: i8, _self_inflicted: bool, _prevent: &mut bool, - ) { + ) -> Result<()> { + Ok(()) } /// This function allows a script attached to a Pokemon or its parents to modify the amount by /// which the stat boost will change. If the stat boost is done by the user itself, self /// inflicted will be true, otherwise it will be false. - fn change_stat_boost_change(&self, _target: &Pokemon, _stat: Statistic, _self_inflicted: bool, _amount: &mut i8) {} + fn change_stat_boost_change( + &self, + _target: &Pokemon, + _stat: Statistic, + _self_inflicted: bool, + _amount: &mut i8, + ) -> Result<()> { + Ok(()) + } /// This function allows a script attached to a Pokemon or its parents to prevent an incoming /// secondary effect. This means the move will still hit and do damage, but not trigger its /// secondary effect. Note that this function is not called for status moves. - fn prevent_secondary_effect(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _prevent: &mut bool) {} + fn prevent_secondary_effect( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _prevent: &mut bool, + ) -> Result<()> { + Ok(()) + } /// This function allows a script attached to a move or its parents to change the chance the /// secondary effect of a move will trigger. The chance is depicted in percentage here, so /// changing this to above or equal to 100 will make it always hit, while setting it to equal or /// below 0 will make it never hit. - fn change_effect_chance(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _chance: &mut f32) {} + fn change_effect_chance( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _chance: &mut f32, + ) -> Result<()> { + Ok(()) + } /// This function allows a script attached to a Pokemon or its parents to change the chance the /// secondary effect of an incoming move will trigger. The chance is depicted in percentage here, /// so changing this to above or equal to 100 will make it always hit, while setting it to equal @@ -208,53 +374,93 @@ pub trait Script: Send + Sync { _target: &Arc, _hit: u8, _chance: &mut f32, - ) { + ) -> Result<()> { + Ok(()) } /// This function triggers when the move uses its secondary effect. Moves should implement their /// secondary effects here. Status moves should implement their actual functionality in this /// function as well, as status moves effects are defined as secondary effects for simplicity. - fn on_secondary_effect(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8) {} + fn on_secondary_effect(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8) -> Result<()> { + Ok(()) + } /// This function triggers on a move or its parents when all hits on a target are finished. - fn on_after_hits(&self, _move: &ExecutingMove, _target: &Arc) {} + fn on_after_hits(&self, _move: &ExecutingMove, _target: &Arc) -> Result<()> { + Ok(()) + } /// This function prevents the Pokemon it is attached to from being able to switch out. - fn prevent_self_switch(&self, _choice: &TurnChoice, _prevent: &mut bool) {} + fn prevent_self_switch(&self, _choice: &TurnChoice, _prevent: &mut bool) -> Result<()> { + Ok(()) + } /// This function allows the prevention of switching for any opponent. - fn prevent_opponent_switch(&self, _choice: &TurnChoice, _prevent: &mut bool) {} + fn prevent_opponent_switch(&self, _choice: &TurnChoice, _prevent: &mut bool) -> Result<()> { + Ok(()) + } /// This function is called on a move and its parents when the move fails. - fn on_fail(&self, _target: &Pokemon) {} + fn on_fail(&self, _target: &Pokemon) -> Result<()> { + Ok(()) + } /// This function is called on a script when an opponent fails. - fn on_opponent_fail(&self, _target: &Pokemon) {} + fn on_opponent_fail(&self, _target: &Pokemon) -> Result<()> { + Ok(()) + } /// This function allows preventing the running away of the Pokemon its attached to - fn prevent_self_run_away(&self, _choice: &TurnChoice, _prevent: &mut bool) {} + fn prevent_self_run_away(&self, _choice: &TurnChoice, _prevent: &mut bool) -> Result<()> { + Ok(()) + } /// This function prevents a Pokemon on another side than where its attached to from running away. - fn prevent_opponent_run_away(&self, _choice: &TurnChoice, _prevent: &mut bool) {} + fn prevent_opponent_run_away(&self, _choice: &TurnChoice, _prevent: &mut bool) -> Result<()> { + Ok(()) + } /// This function id triggered on all scripts active in the battle after all choices have finished /// running. Note that choices are not active anymore here, so their scripts do not call this /// function. - fn on_end_turn(&self) {} + fn on_end_turn(&self) -> Result<()> { + Ok(()) + } /// This function is triggered on a Pokemon and its parents when the given Pokemon takes damage. - fn on_damage(&self, _pokemon: &Pokemon, _source: DamageSource, _old_health: u32, _new_health: u32) {} + fn on_damage(&self, _pokemon: &Pokemon, _source: DamageSource, _old_health: u32, _new_health: u32) -> Result<()> { + Ok(()) + } /// This function is triggered on a Pokemon and its parents when the given Pokemon faints. - fn on_faint(&self, _pokemon: &Pokemon, _source: DamageSource) {} + fn on_faint(&self, _pokemon: &Pokemon, _source: DamageSource) -> Result<()> { + Ok(()) + } /// This function is triggered on a Pokemon and its parents when the given Pokemon is switched into /// the battlefield. - fn on_switch_in(&self, _pokemon: &Pokemon) {} + fn on_switch_in(&self, _pokemon: &Pokemon) -> Result<()> { + Ok(()) + } /// This function is triggered on a Pokemon and its parents when the given Pokemon consumes the /// held item it had. - fn on_after_held_item_consume(&self, _pokemon: &Pokemon, _item: &dyn Item) {} + fn on_after_held_item_consume(&self, _pokemon: &Pokemon, _item: &dyn Item) -> Result<()> { + Ok(()) + } /// This function is triggered on a Pokemon and its parents when the given Pokemon gains experience, /// and allows for changing this amount of experience. - fn change_experience_gained(&self, _fainted_mon: &Pokemon, _winning_mon: &Pokemon, _amount: &mut u32) {} + fn change_experience_gained( + &self, + _fainted_mon: &Pokemon, + _winning_mon: &Pokemon, + _amount: &mut u32, + ) -> Result<()> { + Ok(()) + } /// This function is triggered on a Pokemon and its parents when the given Pokemon gains experience, /// and allows for making the experience be shared across multiple Pokemon. - fn share_experience(&self, _fainted_mon: &Pokemon, _winning_mon: &Pokemon, _shares: &mut bool) {} + fn share_experience(&self, _fainted_mon: &Pokemon, _winning_mon: &Pokemon, _shares: &mut bool) -> Result<()> { + Ok(()) + } /// This function is triggered on a battle and its parents when something attempts to change the /// weather, and allows for blocking the weather change. - fn block_weather(&self, _battle: &Battle, _blocked: &mut bool) {} + fn block_weather(&self, _battle: &Battle, _blocked: &mut bool) -> Result<()> { + Ok(()) + } /// This function is called when a Pokeball is thrown at a Pokemon, and allows modifying the catch /// rate of this attempt. Pokeball modifier effects should be implemented here, as well as for /// example status effects that change capture rates. - fn change_capture_rate_bonus(&self, _target: &Pokemon, _pokeball: &dyn Item, _modifier: &mut u8) {} + fn change_capture_rate_bonus(&self, _target: &Pokemon, _pokeball: &dyn Item, _modifier: &mut u8) -> Result<()> { + Ok(()) + } /// Helper function to turn the self into an Any for downcasting. fn as_any(&self) -> &dyn Any; @@ -319,7 +525,13 @@ impl ScriptContainer { /// separate thread, and be replaced immediately after the script stops being active. pub fn set(&self, script: Arc) -> Arc { if let Some(v) = self.script.read().deref() { - v.on_remove(); + let script_result = v.on_remove(); + match script_result { + Ok(_) => {} + Err(e) => { + crate::dynamic_data::script_handling::handle_script_error(&e); + } + } v.mark_for_deletion(); } // If we have a lock on the script, we can't replace it now. Wait until we can get the lock, @@ -342,7 +554,13 @@ impl ScriptContainer { /// we just mark it as deleted. If we do this we remove the script later on. pub fn clear(&self) { if let Some(v) = self.script.read().deref() { - v.on_remove(); + let script_result = v.on_remove(); + match script_result { + Ok(_) => {} + Err(e) => { + crate::dynamic_data::script_handling::handle_script_error(&e); + } + } v.mark_for_deletion(); } if !self.script.is_locked() { @@ -418,8 +636,8 @@ mod tests { unsafe impl Send for TestScript {} impl Script for TestScript { - fn name(&self) -> &StringKey { - &self.name + fn name(&self) -> Result<&StringKey> { + Ok(&self.name) } fn get_marked_for_deletion(&self) -> &AtomicBool { @@ -430,8 +648,9 @@ mod tests { &self.suppressed_count } - fn stack(&self) { + fn stack(&self) -> Result<()> { unsafe { self.container.load(Ordering::Relaxed).as_ref().unwrap().clear() } + Ok(()) } fn as_any(&self) -> &dyn Any { @@ -458,7 +677,7 @@ mod tests { drop(w); // Initialize with the script being taken as read lock. This prevents the script from actually // removing itself, as it's still doing things. - container.script.read().as_ref().unwrap().stack(); + container.script.read().as_ref().unwrap().stack().unwrap(); // If we now try and get the script, it will be none the first time. This has the side effect // of actually disposing of the script. assert!(container.get().is_none()); @@ -497,8 +716,8 @@ mod tests { unsafe impl Send for ReplaceTestScript {} impl Script for ReplaceTestScript { - fn name(&self) -> &StringKey { - &self.name + fn name(&self) -> Result<&StringKey> { + Ok(&self.name) } fn get_marked_for_deletion(&self) -> &AtomicBool { @@ -544,7 +763,10 @@ mod tests { h.join().unwrap(); } - assert_eq!(container.script.read().as_ref().unwrap().name(), &"script2".into()); + assert_eq!( + container.script.read().as_ref().unwrap().name().unwrap(), + &"script2".into() + ); } } diff --git a/src/dynamic_data/script_handling/script_set.rs b/src/dynamic_data/script_handling/script_set.rs index 9ab9014..ac2ab7a 100755 --- a/src/dynamic_data/script_handling/script_set.rs +++ b/src/dynamic_data/script_handling/script_set.rs @@ -19,22 +19,22 @@ pub struct ScriptSet { impl ScriptSet { /// Adds a script to the set. If the script with that name already exists in this set, this /// makes that script stack instead. The return value here is that script. - pub fn add(&self, script: Arc) -> ScriptContainer { - if let Some(lock) = self.scripts.read().get(script.name()) { + pub fn add(&self, script: Arc) -> Result { + if let Some(lock) = self.scripts.read().get(script.name()?) { if let Some(existing) = lock.get() { let existing = existing.read(); if let Some(v) = &*existing { - v.stack(); - return lock.clone(); + v.stack()?; + return Ok(lock.clone()); } } } // If the existing script does not exist, or is not a script, we can just add the new one. - let name = script.name().clone(); + let name = script.name()?.clone(); let container = ScriptContainer::new(script); self.scripts.write().insert(name, container.clone()); - container + Ok(container) } /// Adds a script with a name to the set. If the script with that name already exists in this @@ -47,14 +47,20 @@ impl ScriptSet { if let Some(existing) = lock.get() { let existing = existing.read(); if let Some(v) = &*existing { - v.stack(); + let script_result = v.stack(); + match script_result { + Ok(_) => (), + Err(e) => { + crate::dynamic_data::script_handling::handle_script_error(&e); + } + } return Ok(Some(lock.clone())); } } } let script = instantiation()?; if let Some(script) = script { - let name = script.name().clone(); + let name = script.name()?.clone(); let arc = ScriptContainer::new(script); self.scripts.write().insert(name, arc.clone()); Ok(Some(arc)) @@ -74,7 +80,14 @@ impl ScriptSet { if let Some(script) = value { if let Some(script) = script.get() { let script = script.read(); - script.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.on_remove(); + let script_result = script.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.on_remove(); + match script_result { + Ok(_) => (), + Err(e) => { + crate::dynamic_data::script_handling::handle_script_error(&e); + } + } + script .as_ref() .ok_or(PkmnError::UnableToAcquireLock)? @@ -90,7 +103,14 @@ impl ScriptSet { if let Some(script) = script.1.get() { let script = script.read(); if let Some(script) = &*script { - script.on_remove(); + let script_result = script.on_remove(); + match script_result { + Ok(_) => (), + Err(e) => { + crate::dynamic_data::script_handling::handle_script_error(&e); + } + } + script.mark_for_deletion(); } } diff --git a/src/dynamic_data/script_handling/volatile_scripts_owner.rs b/src/dynamic_data/script_handling/volatile_scripts_owner.rs index 367b642..17a70d0 100755 --- a/src/dynamic_data/script_handling/volatile_scripts_owner.rs +++ b/src/dynamic_data/script_handling/volatile_scripts_owner.rs @@ -31,7 +31,7 @@ pub trait VolatileScriptsOwner { /// Adds a volatile script by name. fn add_volatile_script_with_script(&self, script: Arc) -> Result> { self.volatile_scripts() - .stack_or_add(&script.name().clone(), &|| Ok(Some(script.clone()))) + .stack_or_add(&script.name()?.clone(), &|| Ok(Some(script.clone()))) } /// Removes a volatile script by name. diff --git a/src/ffi/dynamic_data/mod.rs b/src/ffi/dynamic_data/mod.rs index 044a575..fd5323b 100644 --- a/src/ffi/dynamic_data/mod.rs +++ b/src/ffi/dynamic_data/mod.rs @@ -1,6 +1,17 @@ +use std::ffi::{c_char, CString}; + /// The foreign function interfaces for the turn choices mod choices; /// The foreign function interfaces for the dynamic data libraries mod libraries; /// The foreign function interfaces for the dynamic data models mod models; + +/// The library the battle uses for handling. +#[no_mangle] +#[allow(clippy::unwrap_used)] +extern "C" fn set_script_error_handler(func: unsafe extern "C" fn(*mut c_char)) { + crate::dynamic_data::set_script_error_handler(Box::new(move |e: &anyhow_ext::Error| unsafe { + func(CString::new(e.to_string()).unwrap().into_raw()) + })); +} diff --git a/src/script_implementations/wasm/script.rs b/src/script_implementations/wasm/script.rs index 1896eb7..9204bd6 100755 --- a/src/script_implementations/wasm/script.rs +++ b/src/script_implementations/wasm/script.rs @@ -1,3 +1,4 @@ +use anyhow_ext::Result; use std::any::Any; use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::Arc; @@ -93,8 +94,8 @@ macro_rules! vec_ex_ref { } impl Script for WebAssemblyScript { - fn name(&self) -> &StringKey { - &self.name + fn name(&self) -> Result<&StringKey> { + Ok(&self.name) } fn get_marked_for_deletion(&self) -> &AtomicBool { @@ -105,50 +106,54 @@ impl Script for WebAssemblyScript { &self.suppressed_count } - fn stack(&self) { + fn stack(&self) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnStack) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().stack(env) { call_func!(func, env, self); } + Ok(()) } - fn on_remove(&self) { + fn on_remove(&self) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnRemove) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().on_remove(env) { call_func!(func, env, self); } + Ok(()) } - fn on_initialize(&self, library: &Arc, pars: Vec) { + fn on_initialize(&self, library: &Arc, pars: Vec) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::Initialize) { - return; + return Ok(()); } 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)); } + Ok(()) } - fn on_before_turn(&self, choice: &TurnChoice) { + fn on_before_turn(&self, choice: &TurnChoice) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnBeforeTurn) { - return; + return Ok(()); } 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)); } + Ok(()) } - fn change_speed(&self, choice: &TurnChoice, speed: &mut u32) { + fn change_speed(&self, choice: &TurnChoice, speed: &mut u32) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeSpeed) { - return; + return Ok(()); } let env = &self.environment; @@ -157,11 +162,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); *speed = *ptr.value(); } + Ok(()) } - fn change_priority(&self, choice: &TurnChoice, priority: &mut i8) { + fn change_priority(&self, choice: &TurnChoice, priority: &mut i8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangePriority) { - return; + return Ok(()); } let env = &self.environment; @@ -170,11 +176,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); *priority = *ptr.value(); } + Ok(()) } - fn change_move(&self, choice: &TurnChoice, move_name: &mut StringKey) { + fn change_move(&self, choice: &TurnChoice, move_name: &mut StringKey) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeMove) { - return; + return Ok(()); } let env = &self.environment; @@ -184,11 +191,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); *move_name = ptr.value().value(env).unwrap().clone(); } + Ok(()) } - fn change_number_of_hits(&self, choice: &TurnChoice, number_of_hits: &mut u8) { + fn change_number_of_hits(&self, choice: &TurnChoice, number_of_hits: &mut u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeNumberOfHits) { - return; + return Ok(()); } let env = &self.environment; @@ -197,11 +205,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); *number_of_hits = *ptr.value(); } + Ok(()) } - fn prevent_move(&self, mv: &ExecutingMove, prevent: &mut bool) { + fn prevent_move(&self, mv: &ExecutingMove, prevent: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventMove) { - return; + return Ok(()); } let env = &self.environment; @@ -210,11 +219,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr); *prevent = *ptr.value(); } + Ok(()) } - fn fail_move(&self, mv: &ExecutingMove, fail: &mut bool) { + fn fail_move(&self, mv: &ExecutingMove, fail: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::FailMove) { - return; + return Ok(()); } let env = &self.environment; @@ -223,11 +233,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr); *fail = *ptr.value(); } + Ok(()) } - fn stop_before_move(&self, mv: &ExecutingMove, stop: &mut bool) { + fn stop_before_move(&self, mv: &ExecutingMove, stop: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::StopBeforeMove) { - return; + return Ok(()); } let env = &self.environment; @@ -236,21 +247,23 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr); *stop = *ptr.value(); } + Ok(()) } - fn on_before_move(&self, mv: &ExecutingMove) { + fn on_before_move(&self, mv: &ExecutingMove) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnBeforeMove) { - return; + return Ok(()); } 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)); } + Ok(()) } - fn fail_incoming_move(&self, mv: &ExecutingMove, target: &Arc, fail: &mut bool) { + fn fail_incoming_move(&self, mv: &ExecutingMove, target: &Arc, fail: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::FailIncomingMove) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().fail_incoming_move(env) { @@ -265,11 +278,12 @@ impl Script for WebAssemblyScript { ); *fail = *ptr.value(); } + Ok(()) } - fn is_invulnerable(&self, mv: &ExecutingMove, target: &Arc, invulnerable: &mut bool) { + fn is_invulnerable(&self, mv: &ExecutingMove, target: &Arc, invulnerable: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::IsInvulnerable) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().is_invulnerable(env) { @@ -284,21 +298,29 @@ impl Script for WebAssemblyScript { ); *invulnerable = *ptr.value(); } + Ok(()) } - fn on_move_miss(&self, mv: &ExecutingMove, target: &Arc) { + fn on_move_miss(&self, mv: &ExecutingMove, target: &Arc) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnMoveMiss) { - return; + return Ok(()); } 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())); } + Ok(()) } - fn change_move_type(&self, mv: &ExecutingMove, target: &Arc, hit: u8, move_type: &mut TypeIdentifier) { + fn change_move_type( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + move_type: &mut TypeIdentifier, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeMoveType) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_move_type(env) { @@ -308,11 +330,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *move_type = *ptr.value(); } + Ok(()) } - fn change_effectiveness(&self, mv: &ExecutingMove, target: &Arc, hit: u8, effectiveness: &mut f32) { + fn change_effectiveness( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + effectiveness: &mut f32, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeEffectiveness) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_effectiveness(env) { @@ -322,11 +351,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *effectiveness = *ptr.value(); } + Ok(()) } - fn block_critical(&self, mv: &ExecutingMove, target: &Arc, hit: u8, block_critical: &mut bool) { + fn block_critical( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + block_critical: &mut bool, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::BlockCritical) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().block_critical(env) { @@ -336,11 +372,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *block_critical = *ptr.value(); } + Ok(()) } - fn block_incoming_critical(&self, mv: &ExecutingMove, target: &Arc, hit: u8, block_critical: &mut bool) { + fn block_incoming_critical( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + block_critical: &mut bool, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::BlockIncomingCritical) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().block_incoming_critical(env) { @@ -350,11 +393,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *block_critical = *ptr.value(); } + Ok(()) } - fn change_accuracy(&self, mv: &ExecutingMove, target: &Arc, hit: u8, accuracy: &mut u8) { + fn change_accuracy(&self, mv: &ExecutingMove, target: &Arc, hit: u8, accuracy: &mut u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeAccuracy) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_accuracy(env) { @@ -364,11 +408,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *accuracy = *ptr.value(); } + Ok(()) } - fn change_critical_stage(&self, mv: &ExecutingMove, target: &Arc, hit: u8, stage: &mut u8) { + fn change_critical_stage(&self, mv: &ExecutingMove, target: &Arc, hit: u8, stage: &mut u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCriticalStage) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_critical_stage(env) { @@ -378,11 +423,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *stage = *ptr.value(); } + Ok(()) } - fn change_critical_modifier(&self, mv: &ExecutingMove, target: &Arc, hit: u8, modifier: &mut f32) { + fn change_critical_modifier( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + modifier: &mut f32, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCriticalModifier) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_critical_modifier(env) { @@ -392,11 +444,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *modifier = *ptr.value(); } + Ok(()) } - fn change_stab_modifier(&self, mv: &ExecutingMove, target: &Arc, hit: u8, modifier: &mut f32) { + fn change_stab_modifier( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + modifier: &mut f32, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeSTABModifier) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_stab_modifier(env) { @@ -406,11 +465,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *modifier = *ptr.value(); } + Ok(()) } - fn change_base_power(&self, mv: &ExecutingMove, target: &Arc, hit: u8, base_power: &mut u8) { + fn change_base_power(&self, mv: &ExecutingMove, target: &Arc, hit: u8, base_power: &mut u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeBasePower) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_base_power(env) { @@ -420,11 +480,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *base_power = *ptr.value(); } + Ok(()) } - fn bypass_defensive_stat_boost(&self, mv: &ExecutingMove, target: &Arc, hit: u8, bypass: &mut bool) { + fn bypass_defensive_stat_boost( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + bypass: &mut bool, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::BypassDefensiveStat) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().bypass_defensive_stat_boost(env) { @@ -434,11 +501,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *bypass = *ptr.value(); } + Ok(()) } - fn bypass_offensive_stat_boost(&self, mv: &ExecutingMove, target: &Arc, hit: u8, bypass: &mut bool) { + fn bypass_offensive_stat_boost( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + bypass: &mut bool, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::BypassOffensiveStat) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().bypass_offensive_stat_boost(env) { @@ -448,11 +522,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *bypass = *ptr.value(); } + Ok(()) } - fn change_offensive_stat_value(&self, mv: &ExecutingMove, target: &Arc, hit: u8, amount: &mut u32) { + fn change_offensive_stat_value( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + amount: &mut u32, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeOffensiveStatValue) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_offensive_stat_value(env) { @@ -462,11 +543,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *amount = *ptr.value(); } + Ok(()) } - fn change_defensive_stat_value(&self, mv: &ExecutingMove, target: &Arc, hit: u8, amount: &mut u32) { + fn change_defensive_stat_value( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + amount: &mut u32, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeOffensiveStatValue) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_defensive_stat_value(env) { @@ -476,11 +564,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *amount = *ptr.value(); } + Ok(()) } - fn change_damage_stat_modifier(&self, mv: &ExecutingMove, target: &Arc, hit: u8, modifier: &mut f32) { + fn change_damage_stat_modifier( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + modifier: &mut f32, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeStatModifier) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_damage_stat_modifier(env) { @@ -490,11 +585,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *modifier = *ptr.value(); } + Ok(()) } - fn change_damage_modifier(&self, mv: &ExecutingMove, target: &Arc, hit: u8, modifier: &mut f32) { + fn change_damage_modifier( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + modifier: &mut f32, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeDamageModifier) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_damage_modifier(env) { @@ -504,11 +606,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *modifier = *ptr.value(); } + Ok(()) } - fn change_damage(&self, mv: &ExecutingMove, target: &Arc, hit: u8, damage: &mut u32) { + fn change_damage(&self, mv: &ExecutingMove, target: &Arc, hit: u8, damage: &mut u32) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeDamage) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_damage(env) { @@ -518,11 +621,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *damage = *ptr.value(); } + Ok(()) } - fn change_incoming_damage(&self, mv: &ExecutingMove, target: &Arc, hit: u8, damage: &mut u32) { + fn change_incoming_damage( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + damage: &mut u32, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeIncomingDamage) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_incoming_damage(env) { @@ -532,28 +642,31 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *damage = *ptr.value(); } + Ok(()) } - fn on_incoming_hit(&self, mv: &ExecutingMove, target: &Arc, hit: u8) { + fn on_incoming_hit(&self, mv: &ExecutingMove, target: &Arc, hit: u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnIncomingHit) { - return; + return Ok(()); } 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); } + Ok(()) } - fn on_opponent_faints(&self, mv: &ExecutingMove, target: &Arc, hit: u8) { + fn on_opponent_faints(&self, mv: &ExecutingMove, target: &Arc, hit: u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaintingOpponent) { - return; + return Ok(()); } 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); } + Ok(()) } fn prevent_stat_boost_change( @@ -563,9 +676,9 @@ impl Script for WebAssemblyScript { amount: i8, self_inflicted: bool, prevent: &mut bool, - ) { + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventStatBoostChange) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().prevent_stat_boost_change(env) { @@ -583,11 +696,18 @@ impl Script for WebAssemblyScript { ); *prevent = *ptr.value(); } + Ok(()) } - fn prevent_secondary_effect(&self, mv: &ExecutingMove, target: &Arc, hit: u8, prevent: &mut bool) { + fn prevent_secondary_effect( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + prevent: &mut bool, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSecondaryEffects) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().prevent_secondary_effect(env) { @@ -597,11 +717,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *prevent = *ptr.value(); } + Ok(()) } - fn change_effect_chance(&self, mv: &ExecutingMove, target: &Arc, hit: u8, chance: &mut f32) { + fn change_effect_chance(&self, mv: &ExecutingMove, target: &Arc, hit: u8, chance: &mut f32) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeEffectChance) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_effect_chance(env) { @@ -611,11 +732,18 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *chance = *ptr.value(); } + Ok(()) } - fn change_incoming_effect_chance(&self, mv: &ExecutingMove, target: &Arc, hit: u8, chance: &mut f32) { + fn change_incoming_effect_chance( + &self, + mv: &ExecutingMove, + target: &Arc, + hit: u8, + chance: &mut f32, + ) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeIncomingEffectChance) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_incoming_effect_chance(env) { @@ -625,33 +753,36 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr); *chance = *ptr.value(); } + Ok(()) } - fn on_secondary_effect(&self, mv: &ExecutingMove, target: &Arc, hit: u8) { + fn on_secondary_effect(&self, mv: &ExecutingMove, target: &Arc, hit: u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaintingOpponent) { - return; + return Ok(()); } 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); } + Ok(()) } - fn on_after_hits(&self, mv: &ExecutingMove, target: &Arc) { + fn on_after_hits(&self, mv: &ExecutingMove, target: &Arc) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnAfterHits) { - return; + return Ok(()); } 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)); } + Ok(()) } - fn prevent_self_switch(&self, choice: &TurnChoice, prevent: &mut bool) { + fn prevent_self_switch(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfSwitch) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().prevent_self_switch(env) { @@ -659,11 +790,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); *prevent = *ptr.value(); } + Ok(()) } - fn prevent_opponent_switch(&self, choice: &TurnChoice, prevent: &mut bool) { + fn prevent_opponent_switch(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfSwitch) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().prevent_opponent_switch(env) { @@ -671,31 +803,34 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); *prevent = *ptr.value(); } + Ok(()) } - fn on_fail(&self, pokemon: &Pokemon) { + fn on_fail(&self, pokemon: &Pokemon) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnFail) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().on_fail(env) { call_func!(func, env, self, ex_ref!(env, pokemon)); } + Ok(()) } - fn on_opponent_fail(&self, pokemon: &Pokemon) { + fn on_opponent_fail(&self, pokemon: &Pokemon) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnFail) { - return; + return Ok(()); } 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)); } + Ok(()) } - fn prevent_self_run_away(&self, choice: &TurnChoice, prevent: &mut bool) { + fn prevent_self_run_away(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfRunAway) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().prevent_self_run_away(env) { @@ -703,11 +838,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); *prevent = *ptr.value(); } + Ok(()) } - fn prevent_opponent_run_away(&self, choice: &TurnChoice, prevent: &mut bool) { + fn prevent_opponent_run_away(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventOpponentRunAway) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().prevent_opponent_run_away(env) { @@ -715,61 +851,67 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr); *prevent = *ptr.value(); } + Ok(()) } - fn on_end_turn(&self) { + fn on_end_turn(&self) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnEndTurn) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().on_end_turn(env) { call_func!(func, env, self); } + Ok(()) } - fn on_damage(&self, pokemon: &Pokemon, source: DamageSource, old_health: u32, new_health: u32) { + fn on_damage(&self, pokemon: &Pokemon, source: DamageSource, old_health: u32, new_health: u32) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnDamage) { - return; + return Ok(()); } 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); } + Ok(()) } - fn on_faint(&self, pokemon: &Pokemon, source: DamageSource) { + fn on_faint(&self, pokemon: &Pokemon, source: DamageSource) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaint) { - return; + return Ok(()); } 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); } + Ok(()) } - fn on_switch_in(&self, pokemon: &Pokemon) { + fn on_switch_in(&self, pokemon: &Pokemon) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnSwitchIn) { - return; + return Ok(()); } 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)); } + Ok(()) } - fn on_after_held_item_consume(&self, pokemon: &Pokemon, item: &dyn Item) { + fn on_after_held_item_consume(&self, pokemon: &Pokemon, item: &dyn Item) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnAfterHeldItemConsume) { - return; + return Ok(()); } 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)); } + Ok(()) } - fn change_experience_gained(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, amount: &mut u32) { + fn change_experience_gained(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, amount: &mut u32) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeExperienceGain) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_experience_gained(env) { @@ -779,11 +921,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, fainted_mon, winning_mon, ptr.wasm_ptr); *amount = *ptr.value(); } + Ok(()) } - fn share_experience(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, shares: &mut bool) { + fn share_experience(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, shares: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::DoesShareExperience) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().share_experience(env) { @@ -793,11 +936,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, fainted_mon, winning_mon, ptr.wasm_ptr); *shares = *ptr.value(); } + Ok(()) } - fn block_weather(&self, battle: &Battle, blocked: &mut bool) { + fn block_weather(&self, battle: &Battle, blocked: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::BlockWeather) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().block_weather(env) { @@ -805,11 +949,12 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, ex_ref!(env, battle), ptr.wasm_ptr); *blocked = *ptr.value(); } + Ok(()) } - fn change_capture_rate_bonus(&self, target: &Pokemon, pokeball: &dyn Item, modifier: &mut u8) { + fn change_capture_rate_bonus(&self, target: &Pokemon, pokeball: &dyn Item, modifier: &mut u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCaptureRate) { - return; + return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_capture_rate_bonus(env) { @@ -819,6 +964,7 @@ impl Script for WebAssemblyScript { call_func!(func, env, self, target, pokeball, ptr.wasm_ptr); *modifier = *ptr.value(); } + Ok(()) } fn as_any(&self) -> &dyn Any { diff --git a/tests/integration.rs b/tests/integration.rs index 7c00414..6dfe6d7 100755 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -119,7 +119,7 @@ fn validate_assurance() { let mv = p1.learned_moves().read()[0].as_ref().unwrap().clone(); let choice = TurnChoice::Move(MoveChoice::new(p1.clone(), mv.clone(), 1, 0)); - script.on_before_turn(&choice); + script.on_before_turn(&choice).unwrap(); assert!(battle.sides()[1].has_volatile_script(&"assurance_data".into())); let executing_move = ExecutingMove::new( @@ -131,19 +131,19 @@ fn validate_assurance() { ScriptContainer::default(), ); let mut v = 20_u8; - script.change_base_power(&executing_move, &p2, 0, &mut v); + script.change_base_power(&executing_move, &p2, 0, &mut v).unwrap(); assert_eq!(v, 20_u8); let s = battle.sides()[1].get_volatile_script(&"assurance_data".into()); let binding = s.as_ref().unwrap().get().unwrap().read(); let data_script = binding.as_ref().unwrap(); - data_script.on_damage(&p2, DamageSource::Misc, 100, 50); + data_script.on_damage(&p2, DamageSource::Misc, 100, 50).unwrap(); let mut v = 20_u8; - script.change_base_power(&executing_move, &p2, 0, &mut v); + script.change_base_power(&executing_move, &p2, 0, &mut v).unwrap(); assert_eq!(v, 40_u8); - data_script.on_end_turn(); + data_script.on_end_turn().unwrap(); assert!(!battle.sides()[1].has_volatile_script(&"assurance_data".into())); }