From 46195d30423e1a0ff12dbd923348be546efc797e Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Thu, 22 Jun 2023 15:43:41 +0200 Subject: [PATCH] Major rework of extern ref system for WASM, fixes most possible panics in WASM handling --- .cargo/config.toml | 4 - src/dynamic_data/choices.rs | 83 ++-- src/dynamic_data/event_hooks.rs | 4 +- src/dynamic_data/flow/choice_queue.rs | 164 ++++--- src/dynamic_data/flow/target_resolver.rs | 3 +- src/dynamic_data/flow/turn_runner.rs | 16 +- src/dynamic_data/libraries/damage_library.rs | 32 +- src/dynamic_data/libraries/dynamic_library.rs | 48 +- src/dynamic_data/libraries/misc_library.rs | 4 +- src/dynamic_data/models/battle.rs | 182 ++++--- src/dynamic_data/models/battle_party.rs | 2 +- src/dynamic_data/models/battle_random.rs | 4 +- src/dynamic_data/models/battle_side.rs | 200 +++++--- src/dynamic_data/models/executing_move.rs | 23 +- src/dynamic_data/models/pokemon.rs | 339 ++++++++----- src/dynamic_data/models/pokemon_party.rs | 13 +- src/dynamic_data/script_handling/mod.rs | 2 +- src/dynamic_data/script_handling/script.rs | 147 +++--- src/ffi/dynamic_data/choices.rs | 8 +- .../libraries/battle_stat_calculator.rs | 9 +- .../dynamic_data/libraries/dynamic_library.rs | 32 +- src/ffi/dynamic_data/models/battle.rs | 21 +- src/ffi/dynamic_data/models/battle_party.rs | 2 +- src/ffi/dynamic_data/models/event.rs | 3 +- src/ffi/dynamic_data/models/learned_move.rs | 3 +- src/ffi/dynamic_data/models/pokemon.rs | 97 ++-- src/ffi/dynamic_data/models/pokemon_party.rs | 14 +- src/ffi/mod.rs | 35 ++ src/ffi/static_data/ability.rs | 10 +- src/ffi/static_data/form.rs | 4 +- src/ffi/static_data/libraries/static_data.rs | 85 ++-- src/ffi/static_data/move_data.rs | 6 +- .../export_registry/dynamic_data/battle.rs | 57 ++- .../dynamic_data/battle_random.rs | 13 +- .../dynamic_data/battle_side.rs | 13 +- .../dynamic_data/choice_queue.rs | 6 +- .../dynamic_data/executing_move.rs | 35 +- .../export_registry/dynamic_data/hit_data.rs | 29 +- .../dynamic_data/learned_move.rs | 15 +- .../wasm/export_registry/dynamic_data/mod.rs | 24 +- .../export_registry/dynamic_data/party.rs | 13 +- .../export_registry/dynamic_data/pokemon.rs | 120 ++--- .../dynamic_data/turn_choice.rs | 44 +- .../wasm/export_registry/mod.rs | 51 +- .../export_registry/static_data/ability.rs | 30 +- .../wasm/export_registry/static_data/form.rs | 62 ++- .../wasm/export_registry/static_data/item.rs | 52 +- .../wasm/export_registry/static_data/mod.rs | 46 +- .../wasm/export_registry/static_data/moves.rs | 77 +-- .../export_registry/static_data/nature.rs | 23 +- .../export_registry/static_data/species.rs | 64 +-- .../wasm/export_registry/static_data/types.rs | 22 +- .../wasm/export_registry/wasm_object.rs | 448 ++++++++++++++++++ .../wasm/export_registry/wasm_result.rs | 62 +-- src/script_implementations/wasm/extern_ref.rs | 86 ++-- src/script_implementations/wasm/script.rs | 213 ++++----- .../wasm/script_function_cache.rs | 6 +- .../wasm/script_resolver.rs | 108 +++-- src/static_data/libraries/item_library.rs | 4 +- src/static_data/libraries/move_library.rs | 4 +- src/static_data/libraries/species_library.rs | 6 +- src/static_data/libraries/static_data.rs | 140 ++---- src/static_data/moves/secondary_effect.rs | 11 +- src/static_data/species_data/ability.rs | 11 +- src/static_data/species_data/form.rs | 11 +- tests/common/data_getter.rs | 3 +- tests/common/library_loader.rs | 68 +-- tests/common/test_case.rs | 4 +- tests/common/test_step.rs | 5 +- tests/data/gen7_scripts.wasm | Bin 1397015 -> 1356252 bytes tests/integration.rs | 45 +- 71 files changed, 2142 insertions(+), 1488 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 803e058..c658a16 100755 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,2 @@ -[target.x86_64-unknown-linux-gnu] -linker = "/usr/bin/clang" -rustflags = ["-Clink-arg=-fuse-ld=/usr/bin/mold"] - [rust] debuginfo-level = 1 \ No newline at end of file diff --git a/src/dynamic_data/choices.rs b/src/dynamic_data/choices.rs index 2efb6cd..2b42c3b 100755 --- a/src/dynamic_data/choices.rs +++ b/src/dynamic_data/choices.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Result}; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicI8, AtomicU32, Ordering}; use std::sync::Arc; use parking_lot::RwLock; @@ -16,12 +16,12 @@ struct CommonChoiceData { /// A unique identifier so we know what value this is. identifier: ValueIdentifier, /// The user of the turn choice - user: Arc, + user: Pokemon, /// The speed of the user at the beginning of the turn. - speed: u32, + speed: AtomicU32, /// This random value is set at the beginning of the turn. It is used for tie breaking of the /// turn order in a predictable way, regardless of implementation and hardware. - random_value: u32, + random_value: AtomicU32, /// Whether or not the choice has failed. A failed choice will stop running, and execute special /// fail handling during turn execution. has_failed: AtomicBool, @@ -57,32 +57,22 @@ impl TurnChoice { TurnChoice::Pass(data) => &data.choice_data, } } - /// The shared choice data between each of the different turn choices. - fn choice_data_mut(&mut self) -> &mut Box { - match self { - TurnChoice::Move(data) => &mut data.choice_data, - TurnChoice::Item(data) => &mut data.choice_data, - TurnChoice::Switch(data) => &mut data.choice_data, - TurnChoice::Flee(data) => &mut data.choice_data, - TurnChoice::Pass(data) => &mut data.choice_data, - } - } /// Get the user of the given choice. - pub fn user(&self) -> &Arc { + pub fn user(&self) -> &Pokemon { &self.choice_data().user } /// Get the speed of the user for the choice. Note that this speed is the speed of the Pokemon /// at the start of the turn! pub fn speed(&self) -> u32 { - self.choice_data().speed + self.choice_data().speed.load(Ordering::Relaxed) } - /// Get the mutable speed of the user for the choice. Note that this speed is the speed of the Pokemon - /// at the start of the turn! - pub fn speed_mut(&mut self) -> &mut u32 { - &mut self.choice_data_mut().speed + /// Sets the speed of user for the choice. Note that this speed is the speed of the Pokemon at + /// the start of the turn! + pub fn set_speed(&self, value: u32) { + self.choice_data().speed.store(value, Ordering::Relaxed); } /// Gets whether or not the choice has failed. If we notice this when we execute the choice, we @@ -101,12 +91,12 @@ impl TurnChoice { /// breaking of turn executions. This means that choices get executed with a predictable order, /// regardless of implementation details. pub(crate) fn random_value(&self) -> u32 { - self.choice_data().random_value + self.choice_data().random_value.load(Ordering::Relaxed) } /// This sets the above random value. - pub(crate) fn set_random_value(&mut self, val: u32) { - self.choice_data_mut().random_value = val; + pub(crate) fn set_random_value(&self, val: u32) { + self.choice_data().random_value.store(val, Ordering::Relaxed) } /// Helper function to get the move choice data from a turn. Note that this will panic if not @@ -174,26 +164,26 @@ pub struct MoveChoice { /// The move script. script: ScriptContainer, /// The priority of the move choice at the beginning of the turn. - priority: i8, + priority: AtomicI8, /// The common turn choice data. choice_data: Box, } impl MoveChoice { /// Initializes the data for a new move choice. - pub fn new(user: Arc, used_move: Arc, target_side: u8, target_index: u8) -> Self { + pub fn new(user: Pokemon, used_move: Arc, target_side: u8, target_index: u8) -> Self { let speed = user.boosted_stats().speed(); Self { used_move, target_side, target_index, script: Default::default(), - priority: 0, + priority: AtomicI8::new(0), choice_data: Box::new(CommonChoiceData { identifier: Default::default(), user, - speed, - random_value: 0, + speed: AtomicU32::new(speed), + random_value: AtomicU32::new(0), has_failed: Default::default(), script_source_data: Default::default(), }), @@ -215,14 +205,14 @@ impl MoveChoice { } /// The priority of the move choice at the beginning of the turn. pub fn priority(&self) -> i8 { - self.priority + self.priority.load(Ordering::Relaxed) } /// The priority of the move choice at the beginning of the turn. - pub fn priority_mut(&mut self) -> &mut i8 { - &mut self.priority + pub fn set_priority(&self, value: i8) { + self.priority.store(value, Ordering::Relaxed) } /// The user of the choice. - pub fn user(&self) -> &Arc { + pub fn user(&self) -> &Pokemon { &self.choice_data.user } /// The move script of the choice. @@ -259,14 +249,14 @@ pub struct ItemChoice { impl ItemChoice { /// Initialised a new item choice. - pub fn new(user: Arc) -> Self { + pub fn new(user: Pokemon) -> Self { let speed = user.boosted_stats().speed(); Self { choice_data: Box::new(CommonChoiceData { identifier: Default::default(), user, - speed, - random_value: 0, + speed: AtomicU32::new(speed), + random_value: AtomicU32::new(0), has_failed: Default::default(), script_source_data: Default::default(), }), @@ -299,14 +289,14 @@ pub struct SwitchChoice { impl SwitchChoice { /// Initialise the turn choice data. - pub fn new(user: Arc) -> Self { + pub fn new(user: Pokemon) -> Self { let speed = user.boosted_stats().speed(); Self { choice_data: Box::new(CommonChoiceData { identifier: Default::default(), user, - speed, - random_value: 0, + speed: AtomicU32::new(speed), + random_value: AtomicU32::new(0), has_failed: Default::default(), script_source_data: Default::default(), }), @@ -339,13 +329,13 @@ pub struct FleeChoice { impl FleeChoice { /// Initialises a new flee choice. - pub fn new(user: Arc) -> Self { + pub fn new(user: Pokemon) -> Self { Self { choice_data: Box::new(CommonChoiceData { identifier: Default::default(), user, - speed: 0, - random_value: 0, + speed: AtomicU32::new(0), + random_value: AtomicU32::new(0), has_failed: Default::default(), script_source_data: Default::default(), }), @@ -378,14 +368,14 @@ pub struct PassChoice { impl PassChoice { /// Initialised a new pass choice. - pub fn new(user: Arc) -> Self { + pub fn new(user: Pokemon) -> Self { let speed = user.boosted_stats().speed(); Self { choice_data: Box::new(CommonChoiceData { identifier: Default::default(), user, - speed, - random_value: 0, + speed: AtomicU32::new(speed), + random_value: AtomicU32::new(0), has_failed: Default::default(), script_source_data: Default::default(), }), @@ -428,7 +418,10 @@ impl Ord for TurnChoice { match self { TurnChoice::Move(data) => { if let TurnChoice::Move(other_data) = other { - let priority_compare = data.priority.cmp(&other_data.priority); + let priority_compare = data + .priority + .load(Ordering::Relaxed) + .cmp(&other_data.priority.load(Ordering::Relaxed)); if priority_compare != std::cmp::Ordering::Equal { return priority_compare; } diff --git a/src/dynamic_data/event_hooks.rs b/src/dynamic_data/event_hooks.rs index 7f80222..3b1b980 100755 --- a/src/dynamic_data/event_hooks.rs +++ b/src/dynamic_data/event_hooks.rs @@ -29,6 +29,7 @@ impl EventHook { /// listeners can exist at the same time. Note that for these functions the event will be disposed /// of after the event is finished being sent. pub fn register_listener(&self, func: EvtHookFn) { + #[allow(clippy::unwrap_used)] // This should never fail. self.evt_hook_function.write().unwrap().push(func); } @@ -36,6 +37,7 @@ impl EventHook { /// dispose of the event afterwards. pub fn trigger(&self, evt: Event) { let b = Box::new(&evt); + #[allow(clippy::unwrap_used)] // This should never fail. let read_lock = self.evt_hook_function.read().unwrap(); for f in read_lock.iter() { f(&b); @@ -59,7 +61,7 @@ pub enum Event<'own> { /// The index of the Pokemon that got switched in/out on its side index: u8, /// The new Pokemon that will be on the spot. If none, the spot will now be empty. - pokemon: Option>, + pokemon: Option, }, /// A swap event happens when two Pokemon on a side swap positions. Note that this is rare. Swap { diff --git a/src/dynamic_data/flow/choice_queue.rs b/src/dynamic_data/flow/choice_queue.rs index 67e2700..54288fe 100755 --- a/src/dynamic_data/flow/choice_queue.rs +++ b/src/dynamic_data/flow/choice_queue.rs @@ -6,6 +6,8 @@ use anyhow::Result; use anyhow_ext::anyhow; use parking_lot::lock_api::MappedRwLockReadGuard; use parking_lot::{RawRwLock, RwLock, RwLockReadGuard}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; /// The ChoiceQueue is used to run choices one by one. /// @@ -14,51 +16,50 @@ use parking_lot::{RawRwLock, RwLock, RwLockReadGuard}; /// helper functions to change the turn order while doing the execution. This is needed, as several /// moves in Pokemon actively mess with this order. #[derive(Debug)] - pub struct ChoiceQueue { /// A unique identifier so we know what value this is. identifier: ValueIdentifier, /// Our storage of turn choices. Starts out completely filled, then slowly empties as turns get /// executed. - queue: RwLock>>, + queue: RwLock>>>, /// The current index of the turn we need to execute next. - current: usize, + current: AtomicUsize, } impl ChoiceQueue { /// Initializes a ChoiceQueue, and sort the choices. - pub(crate) fn new(mut queue: Vec>) -> Self { + pub(crate) fn new(mut queue: Vec>>) -> Self { queue.sort_unstable_by(|a, b| b.cmp(a)); Self { identifier: Default::default(), queue: RwLock::new(queue), - current: 0, + current: AtomicUsize::new(0), } } /// Dequeues the next turn choice to be executed. This gives ownership to the callee, and replaces /// our own reference to the turn choice with an empty spot. It also increments the current position /// by one. - pub fn dequeue(&mut self) -> Result> { + pub fn dequeue(&self) -> Result>> { let mut write_lock = self.queue.write(); - if self.current >= write_lock.len() { + if self.current.load(Ordering::Relaxed) >= write_lock.len() { return Ok(None); } let c = write_lock - .get_mut(self.current) + .get_mut(self.current.load(Ordering::Relaxed)) .ok_or(anyhow!("Unable to get current turn choice"))? .take(); - self.current += 1; + self.current.fetch_add(1, Ordering::Relaxed); Ok(c) } /// This reads what the next choice to execute will be, without modifying state. - pub fn peek(&self) -> Result>> { + pub fn peek(&self) -> Result>>> { let read_lock = self.queue.read(); - if self.current >= read_lock.len() { + if self.current.load(Ordering::Relaxed) >= read_lock.len() { Ok(None) } else { - let v = RwLockReadGuard::try_map(read_lock, |a| match a.get(self.current) { + let v = RwLockReadGuard::try_map(read_lock, |a| match a.get(self.current.load(Ordering::Relaxed)) { Some(Some(v)) => Some(v), _ => None, }); @@ -71,7 +72,7 @@ impl ChoiceQueue { /// Check if we have any choices remaining. pub fn has_next(&self) -> bool { - self.current < self.queue.read().len() + self.current.load(Ordering::Relaxed) < self.queue.read().len() } /// This resorts the yet to be executed choices. This can be useful for dealing with situations @@ -80,19 +81,19 @@ impl ChoiceQueue { pub fn resort(&mut self) -> Result<()> { let len = self.queue.read().len(); let mut write_lock = self.queue.write(); - for index in self.current..len { + for index in self.current.load(Ordering::Relaxed)..len { let choice = &mut write_lock.get_mut_res(index)?; if let Some(choice) = choice { let mut speed = choice.user().boosted_stats().speed(); script_hook!(change_speed, (*choice), choice, &mut speed); - *choice.speed_mut() = speed; + choice.set_speed(speed) } } write_lock - .get_mut(self.current..len) + .get_mut(self.current.load(Ordering::Relaxed)..len) .ok_or(PkmnError::IndexOutOfBounds { - index: self.current, + index: self.current.load(Ordering::Relaxed), len, })? .sort_unstable_by(|a, b| b.cmp(a)); @@ -104,7 +105,7 @@ impl ChoiceQueue { let mut queue_lock = self.queue.write(); let mut desired_index = None; // Find the index for the choice we want to move up. - for index in self.current..queue_lock.len() { + for index in self.current.load(Ordering::Relaxed)..queue_lock.len() { if let Some(Some(choice)) = &queue_lock.get(index) { if pokemon.value_identifier() == choice.user().value_identifier() { desired_index = Some(index); @@ -115,7 +116,7 @@ impl ChoiceQueue { let result = match desired_index { Some(desired_index) => { // If the choice we want to move up is already the next choice, just return. - if desired_index == self.current { + if desired_index == self.current.load(Ordering::Relaxed) { return Ok(true); } @@ -126,11 +127,14 @@ impl ChoiceQueue { .ok_or(anyhow!("Choice was already taken"))?; // Iterate backwards from the spot before the choice we want to move up, push them all back // by 1 spot. - for index in (self.current..desired_index).rev() { + let current = self.current.load(Ordering::Relaxed); + for index in (current..desired_index).rev() { queue_lock.swap(index, index + 1); } // Place the choice that needs to be next in the next to be executed position. - let _ = queue_lock.get_mut_res(self.current)?.insert(choice); + let _ = queue_lock + .get_mut_res(self.current.load(Ordering::Relaxed))? + .insert(choice); true } None => false, @@ -139,12 +143,14 @@ impl ChoiceQueue { } /// Internal helper function to be easily able to iterate over the yet to be executed choices. - pub(crate) fn get_queue(&self) -> Result]>> { + pub(crate) fn get_queue(&self) -> Result>]>> { let read_lock = self.queue.read(); - match RwLockReadGuard::try_map(read_lock, |a| a.get(self.current..self.queue.read().len())) { + match RwLockReadGuard::try_map(read_lock, |a| { + a.get(self.current.load(Ordering::Relaxed)..self.queue.read().len()) + }) { Ok(v) => Ok(v), Err(_) => Err(PkmnError::IndexOutOfBounds { - index: self.current, + index: self.current.load(Ordering::Relaxed), len: self.queue.read().len(), } .into()), @@ -176,7 +182,7 @@ mod tests { #[test] fn dequeue_from_empty_queue() { - let mut queue = ChoiceQueue::new(Vec::new()); + let queue = ChoiceQueue::new(Vec::new()); assert!(queue.dequeue().unwrap().is_none()); } @@ -204,9 +210,9 @@ mod tests { #[test] fn create_queue_with_single_item() { - let user = Arc::new(get_user(10)); + let user = get_user(10); - let queue = ChoiceQueue::new(vec![Some(TurnChoice::Pass(PassChoice::new(user)))]); + let queue = ChoiceQueue::new(vec![Some(Arc::new(TurnChoice::Pass(PassChoice::new(user))))]); assert!(queue.has_next()); assert!(queue.peek().unwrap().is_some()); assert_eq!(7, queue.peek().unwrap().unwrap().speed()); @@ -214,9 +220,9 @@ mod tests { #[test] fn dequeue_from_queue_with_single_item() { - let user = Arc::new(get_user(10)); + let user = get_user(10); - let mut queue = ChoiceQueue::new(vec![Some(TurnChoice::Pass(PassChoice::new(user)))]); + let queue = ChoiceQueue::new(vec![Some(Arc::new(TurnChoice::Pass(PassChoice::new(user))))]); assert!(queue.has_next()); assert_eq!(7, queue.dequeue().unwrap().unwrap().speed()); assert!(!queue.has_next()); @@ -225,12 +231,12 @@ mod tests { #[test] fn create_queue_with_two_items_with_equal_order() { - let user1 = Arc::new(get_user(10)); - let user2 = Arc::new(get_user(10)); + let user1 = get_user(10); + let user2 = get_user(10); let queue = ChoiceQueue::new(vec![ - Some(TurnChoice::Pass(PassChoice::new(user1))), - Some(TurnChoice::Pass(PassChoice::new(user2))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user1)))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user2)))), ]); assert!(queue.has_next()); assert!(queue.peek().unwrap().is_some()); @@ -239,12 +245,12 @@ mod tests { #[test] fn create_queue_with_two_items_get_queue() { - let user1 = Arc::new(get_user(10)); - let user2 = Arc::new(get_user(5)); + let user1 = get_user(10); + let user2 = get_user(5); let queue = ChoiceQueue::new(vec![ - Some(TurnChoice::Pass(PassChoice::new(user1.clone()))), - Some(TurnChoice::Pass(PassChoice::new(user2))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user1.clone())))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user2)))), ]); let inner_queue = queue.get_queue().unwrap(); assert_eq!( @@ -255,12 +261,12 @@ mod tests { #[test] fn create_queue_with_two_items_in_wrong_order_sorts_correctly() { - let user1 = Arc::new(get_user(5)); - let user2 = Arc::new(get_user(100)); + let user1 = get_user(5); + let user2 = get_user(100); - let mut queue = ChoiceQueue::new(vec![ - Some(TurnChoice::Pass(PassChoice::new(user1))), - Some(TurnChoice::Pass(PassChoice::new(user2))), + let queue = ChoiceQueue::new(vec![ + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user1)))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user2)))), ]); assert_eq!(25, queue.dequeue().unwrap().unwrap().speed()); assert_eq!(6, queue.dequeue().unwrap().unwrap().speed()); @@ -268,12 +274,12 @@ mod tests { #[test] fn resort_with_two_choices() { - let user1 = Arc::new(get_user(50)); - let user2 = Arc::new(get_user(1)); + let user1 = get_user(50); + let user2 = get_user(1); let mut queue = ChoiceQueue::new(vec![ - Some(TurnChoice::Pass(PassChoice::new(user1.clone()))), - Some(TurnChoice::Pass(PassChoice::new(user2.clone()))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user1.clone())))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user2.clone())))), ]); user2.change_level_by(60).unwrap(); @@ -290,18 +296,18 @@ mod tests { #[test] fn move_pokemon_choice_first_with_two_choices() { - let user1 = Arc::new(get_user(100)); - let user2 = Arc::new(get_user(1)); + let user1 = get_user(100); + let user2 = get_user(1); - let mut queue = ChoiceQueue::new(vec![ - Some(TurnChoice::Pass(PassChoice::new(user1.clone()))), - Some(TurnChoice::Pass(PassChoice::new(user2.clone()))), + let queue = ChoiceQueue::new(vec![ + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user1.clone())))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user2.clone())))), ]); assert_eq!( user1.value_identifier(), queue.peek().unwrap().unwrap().user().value_identifier() ); - assert!(queue.move_pokemon_choice_next(user2.as_ref()).unwrap()); + assert!(queue.move_pokemon_choice_next(&user2).unwrap()); assert_eq!( user2.value_identifier(), @@ -315,18 +321,18 @@ mod tests { #[test] fn move_pokemon_choice_first_when_choice_has_already_been() { - let user1 = Arc::new(get_user(10)); - let user2 = Arc::new(get_user(100)); + let user1 = get_user(10); + let user2 = get_user(100); - let mut queue = ChoiceQueue::new(vec![ - Some(TurnChoice::Pass(PassChoice::new(user1.clone()))), - Some(TurnChoice::Pass(PassChoice::new(user2.clone()))), + let queue = ChoiceQueue::new(vec![ + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user1.clone())))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user2.clone())))), ]); assert_eq!( user2.value_identifier(), queue.dequeue().unwrap().unwrap().user().value_identifier() ); - assert!(!queue.move_pokemon_choice_next(user2.as_ref()).unwrap()); + assert!(!queue.move_pokemon_choice_next(&user2).unwrap()); assert_eq!( user1.value_identifier(), @@ -337,18 +343,18 @@ mod tests { #[test] fn move_pokemon_choice_first_when_choice_is_next() { - let user1 = Arc::new(get_user(100)); - let user2 = Arc::new(get_user(10)); + let user1 = get_user(100); + let user2 = get_user(10); let queue = ChoiceQueue::new(vec![ - Some(TurnChoice::Pass(PassChoice::new(user1.clone()))), - Some(TurnChoice::Pass(PassChoice::new(user2))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user1.clone())))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(user2)))), ]); assert_eq!( user1.value_identifier(), queue.peek().unwrap().unwrap().user().value_identifier() ); - assert!(queue.move_pokemon_choice_next(user1.as_ref()).unwrap()); + assert!(queue.move_pokemon_choice_next(&user1).unwrap()); assert_eq!( user1.value_identifier(), queue.peek().unwrap().unwrap().user().value_identifier() @@ -358,29 +364,29 @@ mod tests { #[test] fn move_pokemon_choice_first_with_seven_choices() { let users = [ - Arc::new(get_user(100)), - Arc::new(get_user(90)), - Arc::new(get_user(80)), - Arc::new(get_user(70)), - Arc::new(get_user(60)), - Arc::new(get_user(50)), - Arc::new(get_user(40)), + get_user(100), + get_user(90), + get_user(80), + get_user(70), + get_user(60), + get_user(50), + get_user(40), ]; - let mut queue = ChoiceQueue::new(vec![ - Some(TurnChoice::Pass(PassChoice::new(users[0].clone()))), - Some(TurnChoice::Pass(PassChoice::new(users[1].clone()))), - Some(TurnChoice::Pass(PassChoice::new(users[2].clone()))), - Some(TurnChoice::Pass(PassChoice::new(users[3].clone()))), - Some(TurnChoice::Pass(PassChoice::new(users[4].clone()))), - Some(TurnChoice::Pass(PassChoice::new(users[5].clone()))), - Some(TurnChoice::Pass(PassChoice::new(users[6].clone()))), + let queue = ChoiceQueue::new(vec![ + Some(Arc::new(TurnChoice::Pass(PassChoice::new(users[0].clone())))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(users[1].clone())))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(users[2].clone())))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(users[3].clone())))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(users[4].clone())))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(users[5].clone())))), + Some(Arc::new(TurnChoice::Pass(PassChoice::new(users[6].clone())))), ]); assert_eq!( users[0].value_identifier(), queue.peek().unwrap().unwrap().user().value_identifier() ); - assert!(queue.move_pokemon_choice_next(users[4].as_ref()).unwrap()); + assert!(queue.move_pokemon_choice_next(&users[4]).unwrap()); assert_eq!( users[4].value_identifier(), diff --git a/src/dynamic_data/flow/target_resolver.rs b/src/dynamic_data/flow/target_resolver.rs index 99b1dfb..7020e63 100755 --- a/src/dynamic_data/flow/target_resolver.rs +++ b/src/dynamic_data/flow/target_resolver.rs @@ -1,6 +1,5 @@ use anyhow::Result; use std::ops::Deref; -use std::sync::Arc; use num_traits::abs; @@ -10,7 +9,7 @@ use crate::static_data::MoveTarget; use crate::VecExt; /// Helper type for the vector of targ ets we will return. -pub type TargetList = Vec>>; +pub type TargetList = Vec>; /// This returns all Pokemon in the battle. fn get_all_targets(battle: &Battle) -> TargetList { diff --git a/src/dynamic_data/flow/turn_runner.rs b/src/dynamic_data/flow/turn_runner.rs index 010f503..903ab71 100755 --- a/src/dynamic_data/flow/turn_runner.rs +++ b/src/dynamic_data/flow/turn_runner.rs @@ -77,9 +77,9 @@ impl Battle { } /// Executes a single choice. - fn execute_choice(&self, choice: &TurnChoice) -> Result<()> { + fn execute_choice(&self, choice: &Arc) -> Result<()> { // A pass turn choice means the user does not intend to do anything. As such, return. - if let TurnChoice::Pass(..) = choice { + if let TurnChoice::Pass(..) = choice.deref() { return Ok(()); } if self.has_ended() { @@ -95,7 +95,7 @@ impl Battle { if !self.can_use(choice) { return Ok(()); } - match choice { + match choice.deref() { TurnChoice::Move(..) => self.execute_move_choice(choice)?, TurnChoice::Item(_) => {} TurnChoice::Switch(_) => {} @@ -106,7 +106,7 @@ impl Battle { } /// Executes a move choice. - fn execute_move_choice<'func>(&'func self, choice: &'func TurnChoice) -> Result<()> { + fn execute_move_choice<'func>(&'func self, choice: &'func Arc) -> Result<()> { let move_choice = choice.get_move_turn_data()?; let used_move = move_choice.used_move(); let move_data = { @@ -129,14 +129,14 @@ impl Battle { if number_of_hits == 0 { return Ok(()); } - let mut executing_move = ExecutingMove::new( + let executing_move = Arc::new(ExecutingMove::new( targets.clone(), number_of_hits, choice.user().clone(), used_move.clone(), move_data, move_choice.script().clone(), - ); + )); let mut prevented = false; script_hook!(prevent_move, executing_move, &executing_move, &mut prevented); if prevented { @@ -161,13 +161,13 @@ impl Battle { } script_hook!(on_before_move, executing_move, &executing_move); for target in targets.iter().flatten() { - self.handle_move_for_target(&mut executing_move, target)?; + self.handle_move_for_target(&executing_move, target)?; } Ok(()) } /// Executes a move turn choice on a single target. - fn handle_move_for_target(&self, executing_move: &mut ExecutingMove, target: &Arc) -> Result<()> { + fn handle_move_for_target(&self, executing_move: &Arc, target: &Pokemon) -> Result<()> { { let mut fail = false; script_hook!(fail_incoming_move, target, executing_move, target, &mut fail); diff --git a/src/dynamic_data/libraries/damage_library.rs b/src/dynamic_data/libraries/damage_library.rs index c632df0..964e286 100755 --- a/src/dynamic_data/libraries/damage_library.rs +++ b/src/dynamic_data/libraries/damage_library.rs @@ -14,8 +14,8 @@ pub trait DamageLibrary: std::fmt::Debug + ValueIdentifiable { /// Calculate the damage for a given hit on a Pokemon. fn get_damage( &self, - executing_move: &ExecutingMove, - target: &Arc, + executing_move: &Arc, + target: &Pokemon, hit_number: u8, hit_data: &HitData, ) -> Result; @@ -23,8 +23,8 @@ pub trait DamageLibrary: std::fmt::Debug + ValueIdentifiable { /// Calculate the base power for a given hit on a Pokemon. fn get_base_power( &self, - executing_move: &ExecutingMove, - target: &Arc, + executing_move: &Arc, + target: &Pokemon, hit_number: u8, hit_data: &HitData, ) -> Result; @@ -33,8 +33,8 @@ pub trait DamageLibrary: std::fmt::Debug + ValueIdentifiable { fn is_critical( &self, battle: &Battle, - executing_move: &ExecutingMove, - target: &Arc, + executing_move: &Arc, + target: &Pokemon, hit_number: u8, ) -> Result; } @@ -61,8 +61,8 @@ impl Gen7DamageLibrary { /// Calculates the modifier applied to damage from the statistics of the relevant Pokemon. fn get_stat_modifier( &self, - executing_move: &ExecutingMove, - target: &Arc, + executing_move: &Arc, + target: &Pokemon, hit_number: u8, hit_data: &HitData, ) -> Result { @@ -151,8 +151,8 @@ impl Gen7DamageLibrary { /// to apply a raw modifier to the damage. fn get_damage_modifier( &self, - executing_move: &ExecutingMove, - target: &Arc, + executing_move: &Arc, + target: &Pokemon, hit_number: u8, _hit_data: &HitData, ) -> Result { @@ -172,8 +172,8 @@ impl Gen7DamageLibrary { impl DamageLibrary for Gen7DamageLibrary { fn get_damage( &self, - executing_move: &ExecutingMove, - target: &Arc, + executing_move: &Arc, + target: &Pokemon, hit_number: u8, hit_data: &HitData, ) -> Result { @@ -260,8 +260,8 @@ impl DamageLibrary for Gen7DamageLibrary { fn get_base_power( &self, - executing_move: &ExecutingMove, - target: &Arc, + executing_move: &Arc, + target: &Pokemon, hit_number: u8, _hit_data: &HitData, ) -> Result { @@ -284,8 +284,8 @@ impl DamageLibrary for Gen7DamageLibrary { fn is_critical( &self, battle: &Battle, - executing_move: &ExecutingMove, - target: &Arc, + executing_move: &Arc, + target: &Pokemon, hit_number: u8, ) -> Result { // Status moves can't be critical. diff --git a/src/dynamic_data/libraries/dynamic_library.rs b/src/dynamic_data/libraries/dynamic_library.rs index 828b0f0..bb386e0 100755 --- a/src/dynamic_data/libraries/dynamic_library.rs +++ b/src/dynamic_data/libraries/dynamic_library.rs @@ -16,15 +16,15 @@ use crate::{StringKey, ValueIdentifiable, ValueIdentifier}; /// calculators that might be customized between different generations and implementations. pub trait DynamicLibrary: Debug + ValueIdentifiable { /// The static data is the immutable storage data for this library. - fn static_data(&self) -> &Box; + fn static_data(&self) -> &Arc; /// The stat calculator deals with the calculation of flat and boosted stats, based on the /// Pokemons attributes. - fn stat_calculator(&self) -> &Box; + fn stat_calculator(&self) -> &Arc; /// The damage calculator deals with the calculation of things relating to damage. - fn damage_calculator(&self) -> &Box; + fn damage_calculator(&self) -> &Arc; /// The Misc Library holds minor functions that do not fall in any of the other libraries and /// calculators. - fn misc_library(&self) -> &Box; + fn misc_library(&self) -> &Arc; /// Loads a standard script with a given unique combination of category and key. If no script /// can be created with this combination, returns None. @@ -47,15 +47,15 @@ pub struct DynamicLibraryImpl { /// A unique identifier so we know what value this is. identifier: ValueIdentifier, /// The static data is the immutable storage data for this library. - static_data: Box, + static_data: Arc, /// The stat calculator deals with the calculation of flat and boosted stats, based on the /// Pokemons attributes. - stat_calculator: Box, + stat_calculator: Arc, /// The damage calculator deals with the calculation of things relating to damage. - damage_calculator: Box, + damage_calculator: Arc, /// The Misc Library holds minor functions that do not fall in any of the other libraries and /// calculators. - misc_library: Box, + misc_library: Arc, /// The script resolver deals with how to resolve the scripts from specific unique key combinations. script_resolver: Box, @@ -64,10 +64,10 @@ pub struct DynamicLibraryImpl { impl DynamicLibraryImpl { /// Instantiates a new DynamicLibrary with given parameters. pub fn new( - static_data: Box, - stat_calculator: Box, - damage_calculator: Box, - misc_library: Box, + static_data: Arc, + stat_calculator: Arc, + damage_calculator: Arc, + misc_library: Arc, script_resolver: Box, ) -> Self { Self { @@ -83,21 +83,21 @@ impl DynamicLibraryImpl { impl DynamicLibrary for DynamicLibraryImpl { /// The static data is the immutable storage data for this library. - fn static_data(&self) -> &Box { + fn static_data(&self) -> &Arc { &self.static_data } /// The stat calculator deals with the calculation of flat and boosted stats, based on the /// Pokemons attributes. - fn stat_calculator(&self) -> &Box { + fn stat_calculator(&self) -> &Arc { &self.stat_calculator } /// The damage calculator deals with the calculation of things relating to damage. - fn damage_calculator(&self) -> &Box { + fn damage_calculator(&self) -> &Arc { &self.damage_calculator } /// The Misc Library holds minor functions that do not fall in any of the other libraries and /// calculators. - fn misc_library(&self) -> &Box { + fn misc_library(&self) -> &Arc { &self.misc_library } @@ -139,10 +139,10 @@ pub mod test { #[derive(Debug)] pub DynamicLibrary{} impl DynamicLibrary for DynamicLibrary { - fn static_data(&self) -> &Box; - fn stat_calculator(&self) -> &Box; - fn damage_calculator(&self) -> &Box; - fn misc_library(&self) -> &Box; + fn static_data(&self) -> &Arc; + fn stat_calculator(&self) -> &Arc; + fn damage_calculator(&self) -> &Arc; + fn misc_library(&self) -> &Arc; fn load_script( &self, owner: ScriptOwnerData, @@ -161,10 +161,10 @@ pub mod test { pub fn build() -> DynamicLibraryImpl { DynamicLibraryImpl { identifier: Default::default(), - static_data: Box::new(crate::static_data::libraries::static_data::test::build()), - stat_calculator: Box::new(Gen7BattleStatCalculator::new()), - damage_calculator: Box::new(Gen7DamageLibrary::new(false)), - misc_library: Box::new(Gen7MiscLibrary::new()), + static_data: Arc::new(crate::static_data::libraries::static_data::test::build()), + stat_calculator: Arc::new(Gen7BattleStatCalculator::new()), + damage_calculator: Arc::new(Gen7DamageLibrary::new(false)), + misc_library: Arc::new(Gen7MiscLibrary::new()), script_resolver: Box::new(EmptyScriptResolver { identifier: Default::default(), }), diff --git a/src/dynamic_data/libraries/misc_library.rs b/src/dynamic_data/libraries/misc_library.rs index adf9d76..8997ac8 100755 --- a/src/dynamic_data/libraries/misc_library.rs +++ b/src/dynamic_data/libraries/misc_library.rs @@ -14,7 +14,7 @@ pub trait MiscLibrary: Debug + ValueIdentifiable { /// Returns whether or not a Pokemon is allowed to flee or switch out. fn can_flee(&self, choice: &TurnChoice) -> bool; /// Returns the move we need to use if we can't use another move. Typically Struggle. - fn replacement_move(&self, user: &Arc, target_side: u8, target_index: u8) -> TurnChoice; + fn replacement_move(&self, user: &Pokemon, target_side: u8, target_index: u8) -> TurnChoice; // TODO: can evolve from level up? // TODO: get time } @@ -66,7 +66,7 @@ impl MiscLibrary for Gen7MiscLibrary { todo!() } - fn replacement_move(&self, user: &Arc, target_side: u8, target_index: u8) -> TurnChoice { + fn replacement_move(&self, user: &Pokemon, target_side: u8, target_index: u8) -> TurnChoice { self.struggle_learned_move.restore_all_uses(); TurnChoice::Move(MoveChoice::new( user.clone(), diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index 8a6c5d7..b750b7f 100755 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -1,6 +1,7 @@ +use std::ffi::c_void; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, Weak}; use anyhow::Result; use anyhow_ext::anyhow; @@ -22,15 +23,15 @@ use crate::dynamic_data::{ChoiceQueue, ScriptContainer}; use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData}; use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier, VecExt}; -/// A pokemon battle, with any amount of sides and pokemon per side. +/// The data of a battle. #[derive(Debug)] -pub struct Battle { +struct BattleData { /// A unique identifier so we know what value this is. identifier: ValueIdentifier, /// The library the battle uses for handling. library: Arc, /// A list of all different parties in the battle. - parties: Vec, + parties: Vec>, /// Whether or not Pokemon can flee from the battle. can_flee: bool, /// The number of sides in the battle. Typically 2. @@ -40,9 +41,9 @@ pub struct Battle { /// A list of all sides in the battle. sides: Vec, /// The RNG used for the battle. - random: BattleRandom, + random: Arc, /// A queue of the yet to be executed choices in a turn. - current_turn_queue: RwLock>, + current_turn_queue: RwLock>>, /// Whether or not the battle has ended. has_ended: AtomicBool, /// The eventual result of the battle. Inconclusive until the battle is ended. @@ -61,16 +62,30 @@ pub struct Battle { script_source_data: RwLock, } +/// A pokemon battle, with any amount of sides and pokemon per side. +#[derive(Clone, Debug)] +pub struct Battle { + /// The actual data of the battle. + data: Arc, +} + +/// A weak reference to a battle. +#[derive(Clone, Debug, Default)] +pub struct WeakBattleReference { + /// A weak reference to the actual data of the battle. + data: Weak, +} + impl Battle { /// Initializes a new battle. pub fn new( library: Arc, - parties: Vec, + parties: Vec>, can_flee: bool, number_of_sides: u8, pokemon_per_side: u8, random_seed: Option, - ) -> Arc { + ) -> Self { // If no seed was passed, we use the current time as seed for the RNG, otherwise we use the // seed. let random = if let Some(seed) = random_seed { @@ -83,7 +98,7 @@ impl Battle { sides.push(BattleSide::new(i, pokemon_per_side)); } - let battle = Self { + let battle = BattleData { identifier: Default::default(), library, parties, @@ -91,7 +106,7 @@ impl Battle { number_of_sides, pokemon_per_side, sides, - random, + random: Arc::new(random), current_turn_queue: RwLock::new(None), has_ended: AtomicBool::new(false), result: RwLock::new(BattleResult::Inconclusive), @@ -104,73 +119,77 @@ impl Battle { }; let battle_arc = Arc::new(battle); - - let battle_ptr = Arc::as_ptr(&battle_arc) as *mut Battle; - unsafe { - for side in &mut battle_ptr.as_mut().unwrap().sides { - side.set_battle(Arc::downgrade(&battle_arc)); - } + let battle = Self { data: battle_arc }; + for side in &battle.data.sides { + side.set_battle(battle.weak()); } - battle_arc + battle + } + + /// Returns a weak reference to the battle. + pub fn weak(&self) -> WeakBattleReference { + WeakBattleReference { + data: Arc::downgrade(&self.data), + } } /// The library the battle uses for handling. pub fn library(&self) -> &Arc { - &self.library + &self.data.library } /// A list of all different parties in the battle. - pub fn parties(&self) -> &Vec { - &self.parties + pub fn parties(&self) -> &Vec> { + &self.data.parties } /// Whether or not Pokemon can flee from the battle. pub fn can_flee(&self) -> bool { - self.can_flee + self.data.can_flee } /// The number of sides in the battle. Typically 2. pub fn number_of_sides(&self) -> u8 { - self.number_of_sides + self.data.number_of_sides } /// The number of Pokemon that can be on each side. pub fn pokemon_per_side(&self) -> u8 { - self.pokemon_per_side + self.data.pokemon_per_side } /// A list of all sides in the battle. pub fn sides(&self) -> &Vec { - &self.sides + &self.data.sides } /// The RNG used for the battle. - pub fn random(&self) -> &BattleRandom { - &self.random + pub fn random(&self) -> &Arc { + &self.data.random } /// Whether or not the battle has ended. pub fn has_ended(&self) -> bool { - self.has_ended.load(Ordering::Relaxed) + self.data.has_ended.load(Ordering::Relaxed) } /// The eventual result of the battle. Inconclusive until the battle is ended. pub fn result(&self) -> BattleResult { - *self.result.read() + *self.data.result.read() } /// The handler to send all events to. pub fn event_hook(&self) -> &EventHook { - &self.event_hook + &self.data.event_hook } /// The index of the current turn. 0 until all choices pub fn current_turn(&self) -> u32 { - self.current_turn.load(Ordering::Relaxed) + self.data.current_turn.load(Ordering::Relaxed) } /// The time in nanoseconds the last turn took to run. Defaults to 0. pub fn last_turn_time(&self) -> u64 { - self.last_turn_time.load(Ordering::Relaxed) + self.data.last_turn_time.load(Ordering::Relaxed) } /// A queue of the yet to be executed choices in a turn. - pub fn current_turn_queue(&self) -> &RwLock> { - &self.current_turn_queue + pub fn current_turn_queue(&self) -> &RwLock>> { + &self.data.current_turn_queue } /// Get a Pokemon on the battlefield, on a specific side and an index on that side. - pub fn get_pokemon(&self, side: u8, index: u8) -> Option> { - let side = self.sides.get(side as usize); + pub fn get_pokemon(&self, side: u8, index: u8) -> Option { + let side = self.data.sides.get(side as usize); let pokemon_read_lock = side?.pokemon(); let pokemon = pokemon_read_lock.get(index as usize); pokemon?.clone() @@ -180,7 +199,7 @@ impl Battle { /// for that slot, or a party is responsible, but has no remaining Pokemon to throw out anymore, /// this returns false. pub fn can_slot_be_filled(&self, side: u8, index: u8) -> bool { - for party in &self.parties { + for party in &self.data.parties { if party.is_responsible_for_index(side, index) && party.has_pokemon_not_in_field() { return true; } @@ -197,12 +216,12 @@ impl Battle { } let mut surviving_side_exists = false; let mut winning_side = None; - for (side_index, side) in self.sides.iter().enumerate() { + for (side_index, side) in self.data.sides.iter().enumerate() { // If any side has fled, the battle end. if side.has_fled_battle() { - let mut w = self.result.write(); + let mut w = self.data.result.write(); *w = BattleResult::Inconclusive; - self.has_ended.store(true, Ordering::SeqCst); + self.data.has_ended.store(true, Ordering::SeqCst); return Ok(()); } // If the side is not defeated @@ -217,15 +236,15 @@ impl Battle { } // Everyone died :( if !surviving_side_exists { - let mut w = self.result.write(); + let mut w = self.data.result.write(); *w = BattleResult::Inconclusive; } // Someone survived, they won! else { - let mut w = self.result.write(); + let mut w = self.data.result.write(); *w = BattleResult::Conclusive(winning_side.ok_or(anyhow!("Winning side was not set"))?); } - self.has_ended.store(true, Ordering::SeqCst); + self.data.has_ended.store(true, Ordering::SeqCst); Ok(()) } @@ -263,7 +282,7 @@ impl Battle { let side = choice.user().get_battle_side_index(); match side { Some(side) => { - self.sides.get_res(side as usize)?.set_choice(choice)?; + self.data.sides.get_res(side as usize)?.set_choice(choice)?; self.check_choices_set_and_run()?; Ok(true) } @@ -274,7 +293,7 @@ impl Battle { /// Checks to see whether all Pokemon on the field have set their choices. If so, we then run /// the turn. fn check_choices_set_and_run(&self) -> Result<()> { - for side in &self.sides { + for side in &self.data.sides { if !side.all_choices_set() { return Ok(()); } @@ -283,20 +302,19 @@ impl Battle { } } let start_time = chrono::Utc::now(); - let mut choices = Vec::with_capacity(self.number_of_sides as usize * self.pokemon_per_side as usize); - for side in &self.sides { + let mut choices = Vec::with_capacity(self.data.number_of_sides as usize * self.data.pokemon_per_side as usize); + for side in &self.data.sides { let mut side_choices = side.choices().write(); for choice_opt in side_choices.deref_mut() { - let mut choice = choice_opt + let choice = choice_opt .as_mut() .ok_or(anyhow!("Choice was none, but all choices were set? Logic error."))?; - let c = choice.deref(); - if let TurnChoice::Move(data) = c { + if let TurnChoice::Move(data) = choice.clone().deref() { let mut change_priority = data.priority(); - script_hook!(change_priority, c, c, &mut change_priority); + script_hook!(change_priority, choice, choice, &mut change_priority); if change_priority != data.priority() { - if let TurnChoice::Move(data) = choice.deref_mut() { - *data.priority_mut() = change_priority; + if let TurnChoice::Move(data) = choice.clone().deref() { + data.set_priority(change_priority); } } } @@ -304,31 +322,34 @@ impl Battle { let mut speed = choice.user().boosted_stats().speed(); let c = choice.deref(); script_hook!(change_speed, c, c, &mut speed); - *choice.speed_mut() = speed; + choice.set_speed(speed); - choice.set_random_value(self.random.get()? as u32); + choice.set_random_value(self.data.random.get()? as u32); choices.push(choice_opt.take()); } // Drop the lock guard, as we need to write into it in reset_choices. drop(side_choices); side.reset_choices(); } - self.current_turn.fetch_add(1, Ordering::SeqCst); + self.data.current_turn.fetch_add(1, Ordering::SeqCst); - self.current_turn_queue.write().replace(ChoiceQueue::new(choices)); + self.data + .current_turn_queue + .write() + .replace(Arc::new(ChoiceQueue::new(choices))); { self.run_turn()?; } - self.current_turn_queue.write().take(); - self.event_hook.trigger(Event::EndTurn); + self.data.current_turn_queue.write().take(); + self.data.event_hook.trigger(Event::EndTurn); let end_time = chrono::Utc::now(); let time = end_time - start_time; match time.num_nanoseconds() { None => {} Some(v) => { - self.last_turn_time.store(v as u64, Ordering::SeqCst); + self.data.last_turn_time.store(v as u64, Ordering::SeqCst); } } Ok(()) @@ -341,16 +362,16 @@ impl Battle { .library() .load_script(self.into(), ScriptCategory::Weather, &weather)? .ok_or(anyhow!("Couldn't find weather script by name {}", weather))?; - self.weather.set(script); + self.data.weather.set(script); } else { - self.weather.clear(); + self.data.weather.clear(); } Ok(()) } /// Gets the current weather of the battle. If no weather is present, this returns None. pub fn weather_name(&self) -> Result> { - if let Some(script) = self.weather.get() { + if let Some(script) = self.data.weather.get() { let lock = script.read(); Ok(Some( lock.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.name()?.clone(), @@ -361,13 +382,38 @@ impl Battle { } } +impl WeakBattleReference { + /// Attempts to upgrade the weak reference to a strong reference. If the strong reference has + /// been dropped, this returns None. + pub fn upgrade(&self) -> Option { + self.data.upgrade().map(|battle| Battle { data: battle }) + } + + /// Gets the inner pointer to the reference counted data. + pub(crate) fn as_ptr(&self) -> *const c_void { + self.data.as_ptr() as *const c_void + } +} + +unsafe impl Send for WeakBattleReference {} + +unsafe impl Sync for WeakBattleReference {} + +impl PartialEq for WeakBattleReference { + fn eq(&self, other: &Self) -> bool { + self.data.ptr_eq(&other.data) + } +} + +impl Eq for WeakBattleReference {} + impl VolatileScriptsOwner for Battle { fn volatile_scripts(&self) -> &Arc { - &self.volatile_scripts + &self.data.volatile_scripts } fn load_volatile_script(&self, key: &StringKey) -> Result>> { - self.library.load_script(self.into(), ScriptCategory::Battle, key) + self.data.library.load_script(self.into(), ScriptCategory::Battle, key) } } @@ -377,12 +423,12 @@ impl ScriptSource for Battle { } fn get_script_source_data(&self) -> &RwLock { - &self.script_source_data + &self.data.script_source_data } fn get_own_scripts(&self, scripts: &mut Vec) { - scripts.push((&self.weather).into()); - scripts.push((&self.volatile_scripts).into()); + scripts.push((&self.data.weather).into()); + scripts.push((&self.data.volatile_scripts).into()); } fn collect_scripts(&self, scripts: &mut Vec) -> Result<()> { @@ -393,7 +439,7 @@ impl ScriptSource for Battle { impl ValueIdentifiable for Battle { fn value_identifier(&self) -> ValueIdentifier { - self.identifier + self.data.identifier } } diff --git a/src/dynamic_data/models/battle_party.rs b/src/dynamic_data/models/battle_party.rs index 7cb6514..d61c84c 100755 --- a/src/dynamic_data/models/battle_party.rs +++ b/src/dynamic_data/models/battle_party.rs @@ -49,7 +49,7 @@ impl BattleParty { } /// Gets a Pokemon at an index. - pub fn get_pokemon(&self, index: usize) -> Option> { + pub fn get_pokemon(&self, index: usize) -> Option { self.party.at(index) } diff --git a/src/dynamic_data/models/battle_random.rs b/src/dynamic_data/models/battle_random.rs index 9a88b61..0d2c5dc 100755 --- a/src/dynamic_data/models/battle_random.rs +++ b/src/dynamic_data/models/battle_random.rs @@ -60,8 +60,8 @@ impl BattleRandom { pub fn effect_chance( &self, mut chance: f32, - executing_move: &ExecutingMove, - target: &Arc, + executing_move: &Arc, + target: &Pokemon, hit_number: u8, ) -> Result { script_hook!( diff --git a/src/dynamic_data/models/battle_side.rs b/src/dynamic_data/models/battle_side.rs index 8d878ba..635d2b3 100755 --- a/src/dynamic_data/models/battle_side.rs +++ b/src/dynamic_data/models/battle_side.rs @@ -1,3 +1,4 @@ +use std::ffi::c_void; use std::ops::Deref; use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use std::sync::{Arc, Weak}; @@ -11,14 +12,14 @@ use crate::dynamic_data::event_hooks::Event; use crate::dynamic_data::models::battle::Battle; use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; -use crate::dynamic_data::Script; use crate::dynamic_data::ScriptSet; use crate::dynamic_data::VolatileScriptsOwner; +use crate::dynamic_data::{Script, WeakBattleReference}; use crate::{script_hook, StringKey, ValueIdentifiable, ValueIdentifier, VecExt}; -/// A side on a battle. +/// The data that is stored for a battle side. #[derive(Debug)] -pub struct BattleSide { +struct BattleSideData { /// A unique identifier so we know what value this is. identifier: ValueIdentifier, /// The index of the side on the battle. @@ -26,18 +27,18 @@ pub struct BattleSide { /// The number of Pokemon that can be on the side. pokemon_per_side: u8, /// A list of pokemon currently on the battlefield. - pokemon: RwLock>>>, + pokemon: RwLock>>, /// The currently set choices for all Pokemon on the battlefield. Cleared when the turn starts. - choices: RwLock>>, + choices: RwLock>>>, /// The slots on the side that can still be filled. Once all slots are set to false, this side /// has lost the battle. fillable_slots: Vec, /// The number of choices that are set. choices_set: AtomicU8, /// A reference to the battle we're part of. - battle: Weak, + battle: WeakBattleReference, /// Whether or not this side has fled. - has_fled_battle: bool, + has_fled_battle: AtomicBool, /// The volatile scripts that are attached to the side. volatile_scripts: Arc, @@ -45,6 +46,20 @@ pub struct BattleSide { script_source_data: RwLock, } +/// A side on a battle. +#[derive(Debug)] +pub struct BattleSide { + /// The data that is stored for this side. + data: Arc, +} + +/// A non owning reference to a battle side. +#[derive(Debug, Clone)] +pub struct WeakBattleSideReference { + /// A weak reference to the data of the battle side. + data: Weak, +} + impl BattleSide { /// Instantiates a battle side. pub fn new(index: u8, pokemon_per_side: u8) -> Self { @@ -61,83 +76,92 @@ impl BattleSide { let pokemon = RwLock::new(pokemon); Self { - identifier: Default::default(), - index, - pokemon_per_side, - pokemon, - choices, - fillable_slots, - choices_set: AtomicU8::new(0), - battle: Weak::new(), - has_fled_battle: false, - volatile_scripts: Default::default(), - script_source_data: Default::default(), + data: Arc::new(BattleSideData { + identifier: Default::default(), + index, + pokemon_per_side, + pokemon, + choices, + fillable_slots, + choices_set: AtomicU8::new(0), + battle: WeakBattleReference::default(), + has_fled_battle: AtomicBool::new(false), + volatile_scripts: Default::default(), + script_source_data: Default::default(), + }), } } /// Set the battle this side belongs to. - pub(crate) fn set_battle(&mut self, battle: Weak) { - self.battle = battle; + pub(crate) fn set_battle(&self, battle: WeakBattleReference) { + #[allow(clippy::unwrap_used)] // Can only be Some() + unsafe { + (self.data.deref() as *const BattleSideData as *mut BattleSideData) + .as_mut() + .unwrap() + .battle = battle; + } } /// The index of the side on the battle. pub fn index(&self) -> u8 { - self.index + self.data.index } /// The number of Pokemon that can be on the side. pub fn pokemon_per_side(&self) -> u8 { - self.pokemon_per_side + self.data.pokemon_per_side } /// A list of pokemon currently on the battlefield. - pub fn pokemon(&self) -> RwLockReadGuard<'_, RawRwLock, Vec>>> { - self.pokemon.read() + pub fn pokemon(&self) -> RwLockReadGuard<'_, RawRwLock, Vec>> { + self.data.pokemon.read() } /// The currently set choices for all Pokemon on the battlefield. Cleared when the turn starts. - pub fn choices(&self) -> &RwLock>> { - &self.choices + pub fn choices(&self) -> &RwLock>>> { + &self.data.choices } /// The slots on the side that can still be filled. Once all slots are set to false, this side /// has lost the battle. pub fn fillable_slots(&self) -> &Vec { - &self.fillable_slots + &self.data.fillable_slots } /// The number of choices that are set. pub fn choices_set(&self) -> u8 { - self.choices_set.load(Ordering::SeqCst) + self.data.choices_set.load(Ordering::SeqCst) } /// A reference to the battle we're part of. - pub fn battle(&self) -> Result> { - self.battle + pub fn battle(&self) -> Result { + self.data + .battle .upgrade() .ok_or(anyhow!("Battle was not set, but requested")) } /// Whether or not this side has fled. pub fn has_fled_battle(&self) -> bool { - self.has_fled_battle + self.data.has_fled_battle.load(Ordering::SeqCst) } /// The volatile scripts that are attached to the side. pub fn volatile_scripts(&self) -> &Arc { - &self.volatile_scripts + &self.data.volatile_scripts } /// Whether every Pokemon on this side has its choices pub fn all_choices_set(&self) -> bool { - self.choices_set() == self.pokemon_per_side + self.choices_set() == self.data.pokemon_per_side } /// Returns true if there are slots that need to be filled with a new pokemon, that have parties /// responsible for them. Returns false if all slots are filled with usable pokemon, or slots are /// empty, but can't be filled by any party anymore. pub fn all_slots_filled(&self) -> Result { - for (i, pokemon) in self.pokemon.read().iter().enumerate() { + for (i, pokemon) in self.data.pokemon.read().iter().enumerate() { match pokemon { Some(pokemon) => { - if !pokemon.is_usable() && self.battle()?.can_slot_be_filled(self.index, i as u8) { + if !pokemon.is_usable() && self.battle()?.can_slot_be_filled(self.data.index, i as u8) { return Ok(false); } } None => { - if self.battle()?.can_slot_be_filled(self.index, i as u8) { + if self.battle()?.can_slot_be_filled(self.data.index, i as u8) { return Ok(false); } } @@ -148,11 +172,11 @@ impl BattleSide { /// Sets a choice for a Pokemon on this side. pub(crate) fn set_choice(&self, choice: TurnChoice) -> Result<()> { - for (index, pokemon_slot) in self.pokemon.read().iter().enumerate() { + for (index, pokemon_slot) in self.data.pokemon.read().iter().enumerate() { if let Some(pokemon) = pokemon_slot { - if std::ptr::eq(pokemon.deref(), choice.user().deref()) { - self.choices.write().get_mut_res(index)?.replace(choice); - self.choices_set.fetch_add(1, Ordering::SeqCst); + if Pokemon::eq(pokemon, choice.user()) { + self.data.choices.write().get_mut_res(index)?.replace(choice.into()); + self.data.choices_set.fetch_add(1, Ordering::SeqCst); return Ok(()); } } @@ -162,21 +186,21 @@ impl BattleSide { /// Resets all choices on this side. pub fn reset_choices(&self) { - let len = self.choices.read().len(); + let len = self.data.choices.read().len(); for i in 0..len { - self.choices.write().get_mut(i).take(); + self.data.choices.write().get_mut(i).take(); } } /// Forcibly removes a Pokemon from the field. pub fn force_clear_pokemon(&mut self, index: u8) { - self.pokemon.write().get_mut(index as usize).take(); + self.data.pokemon.write().get_mut(index as usize).take(); } /// Switches out a spot on the field for a different Pokemon. - pub fn set_pokemon(&self, index: u8, pokemon: Option>) -> Result<()> { + pub fn set_pokemon(&self, index: u8, pokemon: Option) -> Result<()> { { - let mut write_lock = self.pokemon.write(); + let mut write_lock = self.data.pokemon.write(); let old = write_lock.get_mut_res(index as usize)?; let old = match pokemon { Some(pokemon) => old.replace(pokemon), @@ -192,33 +216,33 @@ impl BattleSide { } let pokemon = { - let read_lock = self.pokemon.read(); + let read_lock = self.data.pokemon.read(); &read_lock.get_res(index as usize)?.clone() }; if let Some(pokemon) = pokemon { - pokemon.set_battle_data(self.battle.clone(), self.index); + pokemon.set_battle_data(self.data.battle.clone(), self.data.index); pokemon.set_on_battlefield(true)?; pokemon.set_battle_index(index); let battle = self.battle()?; for side in battle.sides() { - if side.index() == self.index { + if side.index() == self.data.index { continue; } for opponent in side.pokemon().iter().flatten() { - opponent.mark_opponent_as_seen(Arc::downgrade(pokemon)); - pokemon.mark_opponent_as_seen(Arc::downgrade(opponent)); + opponent.mark_opponent_as_seen(pokemon.weak()); + pokemon.mark_opponent_as_seen(opponent.weak()); } } battle.event_hook().trigger(Event::Switch { - side_index: self.index, + side_index: self.data.index, index, pokemon: Some(pokemon.clone()), }); script_hook!(on_switch_in, pokemon, pokemon); } else { self.battle()?.event_hook().trigger(Event::Switch { - side_index: self.index, + side_index: self.data.index, index, pokemon: None, }); @@ -227,9 +251,9 @@ impl BattleSide { } /// Checks whether a Pokemon is on the field in this side. - pub fn is_pokemon_on_side(&self, pokemon: Arc) -> bool { - for p in self.pokemon.read().iter().flatten() { - if Arc::ptr_eq(p, &pokemon) { + pub fn is_pokemon_on_side(&self, pokemon: Pokemon) -> bool { + for p in self.data.pokemon.read().iter().flatten() { + if Pokemon::eq(p, &pokemon) { return true; } } @@ -239,18 +263,19 @@ impl BattleSide { /// Marks a slot as unfillable. This happens when no parties are able to fill the slot anymore. /// If this happens, the slot can not be used again. pub(crate) fn mark_slot_as_unfillable(&self, index: u8) -> Result<()> { - self.fillable_slots + self.data + .fillable_slots .get_res(index as usize)? .store(false, Ordering::SeqCst); Ok(()) } /// Checks whether a slot is unfillable or not. - pub fn is_slot_unfillable(&self, pokemon: Arc) -> Result { - for (i, slot) in self.pokemon.read().iter().enumerate() { + pub fn is_slot_unfillable(&self, pokemon: Pokemon) -> Result { + for (i, slot) in self.data.pokemon.read().iter().enumerate() { if let Some(p) = slot { - if Arc::ptr_eq(p, &pokemon) { - return Ok(self.fillable_slots.get_res(i)?.load(Ordering::Relaxed)); + if Pokemon::eq(p, &pokemon) { + return Ok(self.data.fillable_slots.get_res(i)?.load(Ordering::Relaxed)); } } } @@ -259,7 +284,7 @@ impl BattleSide { /// Checks whether the side has been defeated. pub fn is_defeated(&self) -> bool { - for fillable_slot in &self.fillable_slots { + for fillable_slot in &self.data.fillable_slots { if fillable_slot.load(Ordering::Relaxed) { return false; } @@ -269,19 +294,20 @@ impl BattleSide { /// Mark the side as fled. pub fn mark_as_fled(&mut self) { - self.has_fled_battle = true; + self.data.has_fled_battle.store(true, Ordering::SeqCst); } /// Gets a random Pokemon on the given side. pub fn get_random_creature_index(&self) -> Result { // TODO: Consider adding parameter to only get index for available creatures. - Ok(self.battle()?.random().get_max(self.pokemon_per_side as i32)? as u8) + Ok(self.battle()?.random().get_max(self.data.pokemon_per_side as i32)? as u8) } /// Swap two Pokemon on a single side around. pub fn swap_positions(&mut self, a: u8, b: u8) -> Result { + let data = &self.data; // If out of range, don't allow swapping. - if a >= self.pokemon_per_side || b >= self.pokemon_per_side { + if a >= data.pokemon_per_side || b >= data.pokemon_per_side { return Ok(false); } // If the two indices are the same, don't allow swapping. @@ -294,10 +320,10 @@ impl BattleSide { let mut party_b = None; let battle = self.battle()?; for party in battle.parties() { - if party.is_responsible_for_index(self.index, a) { + if party.is_responsible_for_index(data.index, a) { party_a = Some(party); } - if party.is_responsible_for_index(self.index, b) { + if party.is_responsible_for_index(data.index, b) { party_b = Some(party); } } @@ -315,19 +341,49 @@ impl BattleSide { return Ok(false); } - self.pokemon.write().swap(a as usize, b as usize); + data.pokemon.write().swap(a as usize, b as usize); self.battle()?.event_hook().trigger(Event::Swap { - side_index: self.index, + side_index: data.index, index_a: a, index_b: b, }); Ok(true) } + + /// Gets a weak reference to the side. + pub fn weak(&self) -> WeakBattleSideReference { + WeakBattleSideReference { + data: Arc::downgrade(&self.data), + } + } +} + +impl WeakBattleSideReference { + /// Upgrades the weak reference to a strong reference, returning `None` if the side has been + /// dropped. + pub fn upgrade(&self) -> Option { + self.data.upgrade().map(|data| BattleSide { data }) + } + + /// Gets the underlying pointer to the data of the side. + pub(crate) fn as_ptr(&self) -> *const c_void { + self.data.as_ptr() as *const c_void + } +} + +unsafe impl Send for WeakBattleSideReference {} + +unsafe impl Sync for WeakBattleSideReference {} + +impl PartialEq for WeakBattleSideReference { + fn eq(&self, other: &Self) -> bool { + self.data.ptr_eq(&other.data) + } } impl VolatileScriptsOwner for BattleSide { fn volatile_scripts(&self) -> &Arc { - &self.volatile_scripts + &self.data.volatile_scripts } fn load_volatile_script(&self, key: &StringKey) -> Result>> { @@ -343,11 +399,11 @@ impl ScriptSource for BattleSide { } fn get_script_source_data(&self) -> &RwLock { - &self.script_source_data + &self.data.script_source_data } fn get_own_scripts(&self, scripts: &mut Vec) { - scripts.push((&self.volatile_scripts).into()); + scripts.push((&self.data.volatile_scripts).into()); } fn collect_scripts(&self, scripts: &mut Vec) -> Result<()> { @@ -358,6 +414,6 @@ impl ScriptSource for BattleSide { impl ValueIdentifiable for BattleSide { fn value_identifier(&self) -> ValueIdentifier { - self.identifier + self.data.identifier } } diff --git a/src/dynamic_data/models/executing_move.rs b/src/dynamic_data/models/executing_move.rs index aa060d1..47a7caa 100755 --- a/src/dynamic_data/models/executing_move.rs +++ b/src/dynamic_data/models/executing_move.rs @@ -16,7 +16,6 @@ use crate::{PkmnError, ValueIdentifiable, ValueIdentifier}; /// A hit data is the data for a single hit, on a single target. #[derive(Default, Debug)] - pub struct HitData { /// A unique identifier so we know what value this is. identifier: ValueIdentifier, @@ -96,9 +95,9 @@ pub struct ExecutingMove { number_of_hits: u8, /// A list of hits for this move. For multi target multi hit moves, this stores the hits linearly, /// for example: (target1, hit1), (target1, hit2), (target2, hit1), (target2, hit2), etc. - hits: Vec, + hits: Vec>, /// The user of the move. - user: Arc, + user: Pokemon, /// The move the user has actually chosen to do. chosen_move: Arc, /// The move that the user is actually going to do. @@ -116,7 +115,7 @@ impl ExecutingMove { pub fn new( targets: TargetList, number_of_hits: u8, - user: Arc, + user: Pokemon, chosen_move: Arc, use_move: Arc, script: ScriptContainer, @@ -124,7 +123,7 @@ impl ExecutingMove { let total_hits = number_of_hits as usize * targets.len(); let mut hits = Vec::with_capacity(total_hits); for _i in 0..total_hits { - hits.push(HitData::default()) + hits.push(Arc::new(HitData::default())) } Self { identifier: Default::default(), @@ -148,7 +147,7 @@ impl ExecutingMove { self.number_of_hits } /// The user of the move. - pub fn user(&self) -> &Arc { + pub fn user(&self) -> &Pokemon { &self.user } /// The move the user has actually chosen to do. @@ -165,10 +164,10 @@ impl ExecutingMove { } /// Gets a hit data for a target, with a specific index. - pub fn get_hit_data(&self, for_target: &Arc, hit: u8) -> Result<&HitData> { + pub fn get_hit_data(&self, for_target: &Pokemon, hit: u8) -> Result<&Arc> { for (index, target) in self.targets.iter().enumerate() { if let Some(target) = target { - if Arc::ptr_eq(target, for_target) { + if Pokemon::eq(target, for_target) { let i = index * self.number_of_hits as usize + hit as usize; return match self.hits.get(i) { Some(hit) => Ok(hit), @@ -185,9 +184,9 @@ impl ExecutingMove { } /// Checks whether a Pokemon is a target for this move. - pub fn is_pokemon_target(&self, pokemon: &Arc) -> bool { + pub fn is_pokemon_target(&self, pokemon: &Pokemon) -> bool { for target in self.targets.iter().flatten() { - if Arc::ptr_eq(target, pokemon) { + if Pokemon::eq(target, pokemon) { return true; } } @@ -195,10 +194,10 @@ impl ExecutingMove { } /// Gets the index of the hits in this move where the hits for a specific target start. - pub(crate) fn get_index_of_target(&self, for_target: &Arc) -> Result { + pub(crate) fn get_index_of_target(&self, for_target: &Pokemon) -> Result { for (index, target) in self.targets.iter().enumerate() { if let Some(target) = target { - if Arc::ptr_eq(target, for_target) { + if Pokemon::eq(target, for_target) { let i = index * self.number_of_hits as usize; return Ok(i); } diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index 20e6cc4..79751a9 100755 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -1,3 +1,4 @@ +use std::ffi::c_void; use std::fmt::{Debug, Formatter}; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU8, Ordering}; @@ -12,7 +13,9 @@ use crate::dynamic_data::event_hooks::Event; use crate::dynamic_data::models::battle::Battle; use crate::dynamic_data::models::learned_move::{LearnedMove, MoveLearnMethod}; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; -use crate::dynamic_data::{DynamicLibrary, Script, ScriptCategory, ScriptContainer, ScriptSet, VolatileScriptsOwner}; +use crate::dynamic_data::{ + DynamicLibrary, Script, ScriptCategory, ScriptContainer, ScriptSet, VolatileScriptsOwner, WeakBattleReference, +}; use crate::static_data::AbilityIndex; use crate::static_data::Form; use crate::static_data::Gender; @@ -26,8 +29,8 @@ use crate::utils::Random; use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier, VecExt}; use anyhow::{anyhow, bail, Result}; -/// An individual Pokemon as we know and love them. -pub struct Pokemon { +/// The data of a Pokemon. +struct PokemonData { /// A unique identifier so we know what value this is. identifier: ValueIdentifier, /// The library data of the Pokemon. @@ -67,16 +70,16 @@ pub struct Pokemon { height: Atomic, /// The stats of the Pokemon when disregarding any stat boosts. - flat_stats: StatisticSet, + flat_stats: Arc>, /// The statistics boosts of the Pokemon. Will prevent the value from going above 6, and below /// -6. - stat_boost: ClampedStatisticSet, + stat_boost: Arc>, /// The stats of the Pokemon including the stat boosts - boosted_stats: StatisticSet, + boosted_stats: Arc>, /// The [individual values](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon. - individual_values: ClampedStatisticSet, + individual_values: Arc>, /// The [effort values](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon. - effort_values: ClampedStatisticSet, + effort_values: Arc>, /// The [nature](https://bulbapedia.bulbagarden.net/wiki/Nature) of the Pokemon. nature: Arc, @@ -118,6 +121,24 @@ pub struct Pokemon { script_source_data: RwLock, } +/// An individual Pokemon. +#[derive(Clone)] +pub struct Pokemon { + /// The data of the Pokemon. + data: Arc, +} + +/// A non-owning reference to a Pokemon. +#[derive(Debug, Clone)] +pub struct WeakPokemonReference { + /// The weak reference to the data. + data: Weak, +} + +unsafe impl Send for WeakPokemonReference {} + +unsafe impl Sync for WeakPokemonReference {} + impl Pokemon { /// Instantiates a new Pokemon. pub fn new( @@ -143,7 +164,7 @@ impl Pokemon { .natures() .get_nature(nature) .ok_or(PkmnError::InvalidNatureName { nature: nature.clone() })?; - let mut pokemon = Self { + let pokemon_data = PokemonData { identifier: Default::default(), library, species: RwLock::new(species), @@ -180,28 +201,32 @@ impl Pokemon { volatile: Default::default(), script_source_data: Default::default(), }; + + let pokemon = Self { + data: Arc::new(pokemon_data), + }; pokemon.recalculate_flat_stats()?; let health = pokemon.flat_stats().hp(); - pokemon.current_health = AtomicU32::new(health); + pokemon.data.current_health.store(health, Ordering::Relaxed); Ok(pokemon) } /// The library data of the Pokemon. pub fn library(&self) -> &Arc { - &self.library + &self.data.library } /// The species of the Pokemon. pub fn species(&self) -> Arc { - self.species.read().clone() + self.data.species.read().clone() } /// The form of the Pokemon. pub fn form(&self) -> Arc { - self.form.read().clone() + self.data.form.read().clone() } /// The species that should be displayed to the user. This handles stuff like the Illusion ability. pub fn display_species(&self) -> Arc { - if let Some(v) = &self.display_species { + if let Some(v) = &self.data.display_species { v.clone() } else { self.species() @@ -209,7 +234,7 @@ impl Pokemon { } /// The form that should be displayed to the user. This handles stuff like the Illusion ability. pub fn display_form(&self) -> Arc { - if let Some(v) = &self.display_form { + if let Some(v) = &self.data.display_form { v.clone() } else { self.form() @@ -217,53 +242,57 @@ impl Pokemon { } /// The current level of the Pokemon. pub fn level(&self) -> LevelInt { - self.level.load(Ordering::Relaxed) + self.data.level.load(Ordering::Relaxed) } /// The amount of experience of the Pokemon. pub fn experience(&self) -> u32 { - self.experience.load(Ordering::Relaxed) + self.data.experience.load(Ordering::Relaxed) } /// A unique random number for this Pokemon. pub fn unique_identifier(&self) -> u32 { - self.unique_identifier + self.data.unique_identifier } /// The gender of the Pokemon. pub fn gender(&self) -> Gender { - *self.gender.read() + *self.data.gender.read() } /// The coloring of the Pokemon. Value 0 is the default, value 1 means shiny. Other values are /// currently not used, and can be used for other implementations. pub fn coloring(&self) -> u8 { - self.coloring + self.data.coloring } /// Gets the held item of a Pokemon pub fn held_item(&self) -> &RwLock>> { - &self.held_item + &self.data.held_item } /// Checks whether the Pokemon is holding a specific item. pub fn has_held_item(&self, name: &StringKey) -> bool { // Only true if we have an item, and the item name is the same as the requested item. - if let Some(v) = self.held_item.read().deref() { + if let Some(v) = self.data.held_item.read().deref() { return v.name() == name; } false } /// Changes the held item of the Pokemon. Returns the previously held item. pub fn set_held_item(&self, item: &Arc) -> Option> { - self.held_item.write().replace(item.clone()) + self.data.held_item.write().replace(item.clone()) } /// Removes the held item from the Pokemon. Returns the previously held item. pub fn remove_held_item(&self) -> Option> { - self.held_item.write().take() + self.data.held_item.write().take() } /// Makes the Pokemon uses its held item. pub fn consume_held_item(&self) -> Result { - if self.held_item.read().is_none() { + if self.data.held_item.read().is_none() { return Ok(false); } - let script = self - .library - .load_item_script(self.held_item.read().as_ref().ok_or(PkmnError::UnableToAcquireLock)?)?; + let script = self.data.library.load_item_script( + self.data + .held_item + .read() + .as_ref() + .ok_or(PkmnError::UnableToAcquireLock)?, + )?; if script.is_none() { return Ok(false); } @@ -274,60 +303,60 @@ impl Pokemon { /// The remaining health points of the Pokemon. pub fn current_health(&self) -> u32 { - self.current_health.load(Ordering::Relaxed) + self.data.current_health.load(Ordering::Relaxed) } /// The max health points of the Pokemon. pub fn max_health(&self) -> u32 { - self.boosted_stats.hp() + self.data.boosted_stats.hp() } /// The weight of the Pokemon in kilograms. pub fn weight(&self) -> f32 { - self.weight.load(Ordering::Relaxed) + self.data.weight.load(Ordering::Relaxed) } /// Sets the weight of the Pokemon in kilograms. pub fn set_weight(&self, weight: f32) { - self.weight.store(weight, Ordering::Relaxed) + self.data.weight.store(weight, Ordering::Relaxed) } /// The height of the Pokemon in meters. pub fn height(&self) -> f32 { - self.height.load(Ordering::Relaxed) + self.data.height.load(Ordering::Relaxed) } /// An optional nickname of the Pokemon. pub fn nickname(&self) -> &Option { - &self.nickname + &self.data.nickname } /// An index of the ability to find the actual ability on the form. pub fn real_ability(&self) -> &AbilityIndex { - &self.ability_index + &self.data.ability_index } /// The current types of the Pokemon. pub fn types(&self) -> RwLockReadGuard<'_, RawRwLock, Vec> { - self.types.read() + self.data.types.read() } /// The moves the Pokemon has learned. This is of a set length of [`MAX_MOVES`]. Empty move slots /// are defined by None. pub fn learned_moves(&self) -> &RwLock<[Option>; MAX_MOVES]> { - &self.moves + &self.data.moves } /// The stats of the Pokemon when disregarding any stat boosts. - pub fn flat_stats(&self) -> &StatisticSet { - &self.flat_stats + pub fn flat_stats(&self) -> &Arc> { + &self.data.flat_stats } /// The amount of boosts on a specific stat. - pub fn stat_boosts(&self) -> &ClampedStatisticSet { - &self.stat_boost + pub fn stat_boosts(&self) -> &Arc> { + &self.data.stat_boost } /// The stats of the Pokemon including the stat boosts - pub fn boosted_stats(&self) -> &StatisticSet { - &self.boosted_stats + pub fn boosted_stats(&self) -> &Arc> { + &self.data.boosted_stats } /// Get the stat boosts for a specific stat. pub fn stat_boost(&self, stat: Statistic) -> i8 { - self.stat_boost.get_stat(stat) + self.data.stat_boost.get_stat(stat) } /// Change a boosted stat by a certain amount. pub fn change_stat_boost(&self, stat: Statistic, mut diff_amount: i8, self_inflicted: bool) -> Result { @@ -357,13 +386,13 @@ impl Pokemon { } let mut changed = false; - let old_value = self.stat_boost.get_stat(stat); + let old_value = self.data.stat_boost.get_stat(stat); match diff_amount.cmp(&0_i8) { std::cmp::Ordering::Less => { - changed = self.stat_boost.decrease_stat(stat, -diff_amount); + changed = self.data.stat_boost.decrease_stat(stat, -diff_amount); } std::cmp::Ordering::Greater => { - changed = self.stat_boost.increase_stat(stat, -diff_amount); + changed = self.data.stat_boost.increase_stat(stat, -diff_amount); } _ => {} } @@ -383,17 +412,17 @@ impl Pokemon { } /// The [individual values](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon. - pub fn individual_values(&self) -> &ClampedStatisticSet { - &self.individual_values + pub fn individual_values(&self) -> &Arc> { + &self.data.individual_values } /// The [effort values](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon. - pub fn effort_values(&self) -> &ClampedStatisticSet { - &self.effort_values + pub fn effort_values(&self) -> &Arc> { + &self.data.effort_values } /// Gets the battle the battle is currently in. - pub fn get_battle(&self) -> Option> { - let r = self.battle_data.read(); + pub fn get_battle(&self) -> Option { + let r = self.data.battle_data.read(); if let Some(data) = &r.deref() { data.battle.upgrade() } else { @@ -403,26 +432,31 @@ impl Pokemon { /// Get the index of the side of the battle the Pokemon is in. Only returns a value if the Pokemon /// is on the battlefield. pub fn get_battle_side_index(&self) -> Option { - self.battle_data.read().as_ref().map(|data| data.battle_side_index()) + self.data + .battle_data + .read() + .as_ref() + .map(|data| data.battle_side_index()) } /// Get the index of the slot on the side of the battle the Pokemon is in. Only returns a value /// if the Pokemon is on the battlefield. pub fn get_battle_index(&self) -> Option { - self.battle_data.read().as_ref().map(|data| data.index()) + self.data.battle_data.read().as_ref().map(|data| data.index()) } /// Returns whether something overrides the ability. pub fn is_ability_overriden(&self) -> bool { - self.override_ability.is_some() + self.data.override_ability.is_some() } /// Returns the currently active ability. pub fn active_ability(&self) -> Result> { - if let Some(v) = &self.override_ability { + if let Some(v) = &self.data.override_ability { return Ok(v.clone()); } let form = self.form(); - let ability = form.get_ability(self.ability_index)?; + let ability = form.get_ability(self.data.ability_index)?; Ok(self + .data .library .static_data() .abilities() @@ -434,68 +468,70 @@ impl Pokemon { /// The script for the status. pub fn status(&self) -> &ScriptContainer { - &self.status_script + &self.data.status_script } /// Returns the script for the currently active ability. pub fn ability_script(&self) -> &ScriptContainer { - &self.ability_script + &self.data.ability_script } /// Whether or not the Pokemon is allowed to gain experience. pub fn allowed_experience_gain(&self) -> bool { - self.allowed_experience + self.data.allowed_experience } /// The [nature](https://bulbapedia.bulbagarden.net/wiki/Nature) of the Pokemon. pub fn nature(&self) -> &Arc { - &self.nature + &self.data.nature } /// Calculates the flat stats on the Pokemon. This should be called when for example the base /// stats, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted /// stats, as those depend on the flat stats. pub fn recalculate_flat_stats(&self) -> Result<()> { - self.library + self.data + .library .stat_calculator() - .calculate_flat_stats(self, &self.flat_stats)?; + .calculate_flat_stats(self, &self.data.flat_stats)?; self.recalculate_boosted_stats()?; Ok(()) } /// Calculates the boosted stats on the Pokemon, _without_ recalculating the flat stats. /// This should be called when a stat boost changes. pub fn recalculate_boosted_stats(&self) -> Result<()> { - self.library + self.data + .library .stat_calculator() - .calculate_boosted_stats(self, &self.boosted_stats) + .calculate_boosted_stats(self, &self.data.boosted_stats) } /// Change the species of the Pokemon. pub fn change_species(&self, species: Arc, form: Arc) -> Result<()> { - *self.species.write() = species.clone(); - *self.form.write() = form.clone(); + *self.data.species.write() = species.clone(); + *self.data.form.write() = form.clone(); // If the pokemon is genderless, but it's new species is not, we want to set its gender if self.gender() != Gender::Genderless && species.gender_rate() < 0.0 { // If we're in battle, use the battle random for predictability - let r = self.battle_data.read(); + let r = self.data.battle_data.read(); if let Some(data) = r.deref() { let battle = data.battle().ok_or(anyhow!("Battle not set"))?; let mut random = match battle.random().get_rng().lock() { Ok(v) => v, Err(_) => return Err(PkmnError::UnableToAcquireLock.into()), }; - *self.gender.write() = species.get_random_gender(random.deref_mut()); + *self.data.gender.write() = species.get_random_gender(random.deref_mut()); } else { // If we're not in battle, just use a new random. - *self.gender.write() = species.get_random_gender(&mut Random::default()); + *self.data.gender.write() = species.get_random_gender(&mut Random::default()); } } // Else if the new species is genderless, but the pokemon has a gender, make the creature genderless. else if species.gender_rate() < 0.0 && self.gender() != Gender::Genderless { - *self.gender.write() = Gender::Genderless; + *self.data.gender.write() = Gender::Genderless; } - let r = self.battle_data.read(); + let r = self.data.battle_data.read(); if let Some(battle_data) = &r.deref() { if let Some(battle) = battle_data.battle() { battle.event_hook().trigger(Event::SpeciesChange { @@ -513,29 +549,31 @@ impl Pokemon { if self.form().value_identifier() == form.value_identifier() { return Ok(()); } - *self.form.write() = form.clone(); + *self.data.form.write() = form.clone(); { - let mut type_lock = self.types.write(); + let mut type_lock = self.data.types.write(); type_lock.clear(); for t in form.types() { type_lock.push(*t); } } - self.weight.store(form.weight(), Ordering::SeqCst); - self.height.store(form.height(), Ordering::SeqCst); + self.data.weight.store(form.weight(), Ordering::SeqCst); + self.data.height.store(form.height(), Ordering::SeqCst); let ability = self.active_ability()?; let ability_script = self + .data .library .load_script(self.into(), ScriptCategory::Ability, ability.name())?; if let Some(ability_script) = ability_script { let script_result = self + .data .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.data.library, ability.parameters().to_vec()); match script_result { Ok(_) => (), Err(e) => { @@ -543,21 +581,23 @@ impl Pokemon { } } } else { - self.ability_script.clear(); + self.data.ability_script.clear(); } let old_health = self.max_health(); self.recalculate_flat_stats()?; let diff_health = (self.max_health() - old_health) as i32; if self.current_health() == 0 && (self.current_health() as i32) < -diff_health { - self.current_health.store(0, Ordering::SeqCst); + self.data.current_health.store(0, Ordering::SeqCst); } else if diff_health < 0 { - self.current_health.fetch_sub(-diff_health as u32, Ordering::SeqCst); + self.data + .current_health + .fetch_sub(-diff_health as u32, Ordering::SeqCst); } else { - self.current_health.fetch_add(diff_health as u32, Ordering::SeqCst); + self.data.current_health.fetch_add(diff_health as u32, Ordering::SeqCst); } // TODO: consider form specific attacks? - let r = self.battle_data.read(); + let r = self.data.battle_data.read(); if let Some(battle_data) = r.deref() { if let Some(battle) = battle_data.battle() { battle.event_hook().trigger(Event::FormChange { @@ -571,7 +611,7 @@ impl Pokemon { /// Whether or not the Pokemon is useable in a battle. pub fn is_usable(&self) -> bool { - !self.is_caught && !self.is_egg && !self.is_fainted() + !self.data.is_caught && !self.data.is_egg && !self.is_fainted() } /// Returns whether the Pokemon is fainted. @@ -580,8 +620,8 @@ impl Pokemon { } /// Sets the current battle the Pokemon is in. - pub fn set_battle_data(&self, battle: Weak, battle_side_index: u8) { - let mut w = self.battle_data.write(); + pub fn set_battle_data(&self, battle: WeakBattleReference, battle_side_index: u8) { + let mut w = self.data.battle_data.write(); if let Some(battle_data) = w.deref_mut() { battle_data.battle = battle; battle_data.battle_side_index.store(battle_side_index, Ordering::SeqCst); @@ -598,13 +638,13 @@ impl Pokemon { /// Sets whether or not the Pokemon is on the battlefield. pub fn set_on_battlefield(&self, value: bool) -> Result<()> { - let r = self.battle_data.read(); + let r = self.data.battle_data.read(); if let Some(data) = &mut r.deref() { data.on_battle_field.store(value, Ordering::SeqCst); if !value { - self.volatile.clear()?; - self.weight.store(self.form().weight(), Ordering::SeqCst); - self.height.store(self.form().height(), Ordering::SeqCst); + self.data.volatile.clear()?; + self.data.weight.store(self.form().weight(), Ordering::SeqCst); + self.data.height.store(self.form().height(), Ordering::SeqCst); } } Ok(()) @@ -612,7 +652,7 @@ impl Pokemon { /// Sets the index of the slot of the side the Pokemon is on. pub fn set_battle_index(&self, index: u8) { - let r = self.battle_data.read(); + let r = self.data.battle_data.read(); if let Some(data) = r.deref() { data.index.store(index, Ordering::SeqCst) } @@ -620,16 +660,20 @@ impl Pokemon { /// Whether or not the Pokemon is on the battlefield. pub fn is_on_battlefield(&self) -> bool { - self.battle_data.read().as_ref().is_some_and(|a| a.on_battle_field()) + self.data + .battle_data + .read() + .as_ref() + .is_some_and(|a| a.on_battle_field()) } /// Marks an opponent as seen, for use in experience gain. - pub fn mark_opponent_as_seen(&self, pokemon: Weak) { - let r = self.battle_data.read(); - if let Some(battle_data) = &r.deref() { + pub fn mark_opponent_as_seen(&self, pokemon: WeakPokemonReference) { + let r = self.data.battle_data.read(); + if let Some(battle_data) = r.deref() { let mut opponents = battle_data.seen_opponents().write(); for seen_opponent in opponents.deref() { - if seen_opponent.ptr_eq(&pokemon) { + if seen_opponent.eq(&pokemon) { return; } } @@ -646,7 +690,7 @@ impl Pokemon { return Ok(()); } let new_health = self.current_health() - damage; - if let Some(battle_data) = &self.battle_data.read().deref() { + if let Some(battle_data) = &self.data.battle_data.read().deref() { if let Some(battle) = battle_data.battle() { battle.event_hook().trigger(Event::Damage { pokemon: self, @@ -656,11 +700,17 @@ impl Pokemon { }); } } - if self.battle_data.read().as_ref().is_some_and(|a| a.on_battle_field()) { + if self + .data + .battle_data + .read() + .as_ref() + .is_some_and(|a| a.on_battle_field()) + { script_hook!(on_damage, self, self, source, self.current_health(), new_health); } - self.current_health.store(new_health, Ordering::SeqCst); + self.data.current_health.store(new_health, Ordering::SeqCst); if self.is_fainted() && damage > 0 { self.on_faint(source)?; } @@ -669,7 +719,7 @@ impl Pokemon { /// Triggers when the Pokemon faints. fn on_faint(&self, source: DamageSource) -> Result<()> { - let r = self.battle_data.read(); + let r = self.data.battle_data.read(); if let Some(battle_data) = r.deref() { if let Some(battle) = battle_data.battle() { battle.event_hook().trigger(Event::Faint { pokemon: self }); @@ -703,7 +753,7 @@ impl Pokemon { return false; } let new_health = self.current_health() + max_amount; - if let Some(battle_data) = &self.battle_data.read().deref() { + if let Some(battle_data) = &self.data.battle_data.read().deref() { if let Some(battle) = battle_data.battle() { battle.event_hook().trigger(Event::Heal { pokemon: self, @@ -712,7 +762,7 @@ impl Pokemon { }); } } - self.current_health.store(new_health, Ordering::SeqCst); + self.data.current_health.store(new_health, Ordering::SeqCst); true } @@ -727,6 +777,7 @@ impl Pokemon { } }; let move_data = self + .data .library .static_data() .moves() @@ -742,12 +793,13 @@ impl Pokemon { /// Removes the current non-volatile status from the Pokemon. pub fn clear_status(&self) { - self.status_script.clear() + self.data.status_script.clear() } /// Increases the level by a certain amount pub fn change_level_by(&self, amount: LevelInt) -> Result<()> { - self.level + self.data + .level .fetch_update(Ordering::SeqCst, Ordering::Relaxed, |x| { let max_level = self.library().static_data().settings().maximum_level(); if x + amount > max_level { @@ -759,13 +811,50 @@ impl Pokemon { .ok(); self.recalculate_flat_stats() } + + /// Take a weak reference to the Pokemon. + pub fn weak(&self) -> WeakPokemonReference { + WeakPokemonReference { + data: Arc::downgrade(&self.data), + } + } +} + +impl PartialEq for Pokemon { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.data, &other.data) + } +} + +impl Eq for Pokemon {} + +impl PartialEq for WeakPokemonReference { + fn eq(&self, other: &Self) -> bool { + Weak::ptr_eq(&self.data, &other.data) + } +} + +impl Eq for WeakPokemonReference {} + +impl WeakPokemonReference { + /// Attempts to upgrade the weak reference to a strong reference. + pub fn upgrade(&self) -> Option { + Some(Pokemon { + data: self.data.upgrade()?, + }) + } + + /// Gets the pointer to the underlying data. + pub(crate) fn as_ptr(&self) -> *const c_void { + self.data.as_ptr() as *const c_void + } } /// The data of the Pokemon related to being in a battle. #[derive(Debug)] pub struct PokemonBattleData { /// The battle data of the Pokemon - battle: Weak, + battle: WeakBattleReference, /// The index of the side of the Pokemon battle_side_index: AtomicU8, /// The index of the slot on the side of the Pokemon. @@ -773,16 +862,12 @@ pub struct PokemonBattleData { /// Whether or not the Pokemon is on the battlefield. on_battle_field: AtomicBool, /// A list of opponents the Pokemon has seen this battle. - seen_opponents: RwLock>>, + seen_opponents: RwLock>, } impl PokemonBattleData { /// The battle data of the Pokemon - pub fn battle_mut(&mut self) -> Option> { - self.battle.upgrade() - } - /// The battle data of the Pokemon - pub fn battle(&self) -> Option> { + pub fn battle(&self) -> Option { self.battle.upgrade() } @@ -799,7 +884,7 @@ impl PokemonBattleData { self.on_battle_field.load(Ordering::Relaxed) } /// A list of opponents the Pokemon has seen this battle. - pub fn seen_opponents(&self) -> &RwLock>> { + pub fn seen_opponents(&self) -> &RwLock> { &self.seen_opponents } } @@ -807,7 +892,7 @@ impl PokemonBattleData { impl ScriptSource for Pokemon { fn get_script_count(&self) -> Result { let mut c = 3; - if let Some(battle_data) = &self.battle_data.read().deref() { + if let Some(battle_data) = &self.data.battle_data.read().deref() { if let Some(battle) = battle_data.battle() { c += battle .sides() @@ -819,19 +904,19 @@ impl ScriptSource for Pokemon { } fn get_script_source_data(&self) -> &RwLock { - &self.script_source_data + &self.data.script_source_data } fn get_own_scripts(&self, scripts: &mut Vec) { - scripts.push((&self.held_item_trigger_script).into()); - scripts.push((&self.ability_script).into()); - scripts.push((&self.status_script).into()); - scripts.push((&self.volatile).into()); + scripts.push((&self.data.held_item_trigger_script).into()); + scripts.push((&self.data.ability_script).into()); + scripts.push((&self.data.status_script).into()); + scripts.push((&self.data.volatile).into()); } fn collect_scripts(&self, scripts: &mut Vec) -> Result<()> { self.get_own_scripts(scripts); - if let Some(battle_data) = &self.battle_data.read().deref() { + if let Some(battle_data) = &self.data.battle_data.read().deref() { if let Some(battle) = battle_data.battle() { battle .sides() @@ -845,17 +930,17 @@ impl ScriptSource for Pokemon { impl VolatileScriptsOwner for Pokemon { fn volatile_scripts(&self) -> &Arc { - &self.volatile + &self.data.volatile } fn load_volatile_script(&self, key: &StringKey) -> Result>> { - self.library.load_script(self.into(), ScriptCategory::Pokemon, key) + self.data.library.load_script(self.into(), ScriptCategory::Pokemon, key) } } impl ValueIdentifiable for Pokemon { fn value_identifier(&self) -> ValueIdentifier { - self.identifier + self.data.identifier } } @@ -921,7 +1006,7 @@ pub mod test { }); let mut static_lib = MockStaticData::new(); - static_lib.expect_species().return_const(Box::new(species_lib)); + static_lib.expect_species().return_const(Arc::new(species_lib)); let mut growth_rate_lib = MockGrowthRateLibrary::new(); growth_rate_lib @@ -934,8 +1019,8 @@ pub mod test { Some(Arc::new(n)) }); - static_lib.expect_growth_rates().return_const(Box::new(growth_rate_lib)); - static_lib.expect_natures().return_const(Box::new(nature_lib)); + static_lib.expect_growth_rates().return_const(Arc::new(growth_rate_lib)); + static_lib.expect_natures().return_const(Arc::new(nature_lib)); let mut stat_calculator = MockBattleStatCalculator::new(); stat_calculator.expect_calculate_flat_stats().returning(|_, _| Ok(())); @@ -944,8 +1029,8 @@ pub mod test { .returning(|_, _| Ok(())); let mut lib = MockDynamicLibrary::new(); - lib.expect_static_data().return_const(Box::new(static_lib)); - lib.expect_stat_calculator().return_const(Box::new(stat_calculator)); + lib.expect_static_data().return_const(Arc::new(static_lib)); + lib.expect_stat_calculator().return_const(Arc::new(stat_calculator)); Arc::new(lib) } diff --git a/src/dynamic_data/models/pokemon_party.rs b/src/dynamic_data/models/pokemon_party.rs index bbe1307..98a7e6e 100755 --- a/src/dynamic_data/models/pokemon_party.rs +++ b/src/dynamic_data/models/pokemon_party.rs @@ -1,7 +1,6 @@ use anyhow::Result; use parking_lot::lock_api::RwLockReadGuard; use parking_lot::{RawRwLock, RwLock}; -use std::sync::Arc; use crate::dynamic_data::models::pokemon::Pokemon; use crate::{ValueIdentifiable, ValueIdentifier, VecExt}; @@ -12,7 +11,7 @@ pub struct PokemonParty { /// A unique identifier so we know what value this is. identifier: ValueIdentifier, /// The underlying list of Pokemon. - pokemon: RwLock>>>, + pokemon: RwLock>>, } impl PokemonParty { @@ -29,7 +28,7 @@ impl PokemonParty { } /// Instantiates a party with a list. - pub fn new_from_vec(pokemon: Vec>>) -> Self { + pub fn new_from_vec(pokemon: Vec>) -> Self { Self { identifier: Default::default(), pokemon: RwLock::new(pokemon), @@ -37,7 +36,7 @@ impl PokemonParty { } /// Gets a Pokemon at an index in the party. - pub fn at(&self, index: usize) -> Option> { + pub fn at(&self, index: usize) -> Option { let read_lock = self.pokemon.read(); let opt = read_lock.get(index); if let Some(v) = opt { @@ -53,7 +52,7 @@ impl PokemonParty { } /// Sets the Pokemon at an index to a Pokemon, returning the old Pokemon. - pub fn swap_into(&self, index: usize, pokemon: Option>) -> Result>> { + pub fn swap_into(&self, index: usize, pokemon: Option) -> Result> { let mut party = self.pokemon.write(); if index >= party.len() { return Ok(pokemon); @@ -82,7 +81,7 @@ impl PokemonParty { } /// Gets the underlying list of Pokemon. - pub fn pokemon(&self) -> RwLockReadGuard<'_, RawRwLock, Vec>>> { + pub fn pokemon(&self) -> RwLockReadGuard<'_, RawRwLock, Vec>> { self.pokemon.read() } @@ -112,7 +111,7 @@ impl PokemonParty { /// Checks if the party contains a given pokemon. pub fn has_pokemon(&self, pokemon: &Pokemon) -> bool { for p in self.pokemon.read().iter().flatten() { - if std::ptr::eq(p.as_ref(), pokemon) { + if Pokemon::eq(p, pokemon) { return true; } } diff --git a/src/dynamic_data/script_handling/mod.rs b/src/dynamic_data/script_handling/mod.rs index 95ab715..6723053 100755 --- a/src/dynamic_data/script_handling/mod.rs +++ b/src/dynamic_data/script_handling/mod.rs @@ -236,7 +236,7 @@ impl ScriptIterator { } } else if let ScriptWrapper::Script(script) = wrapper { if let Some(v) = script.upgrade() { - if let Some(..) = v.read().as_ref() { + if v.read().as_ref().is_some() { return Ok(true); } } diff --git a/src/dynamic_data/script_handling/script.rs b/src/dynamic_data/script_handling/script.rs index 2f2a7b7..da88d33 100755 --- a/src/dynamic_data/script_handling/script.rs +++ b/src/dynamic_data/script_handling/script.rs @@ -2,17 +2,17 @@ use anyhow::{anyhow, Result}; use std::any::Any; use std::fmt::{Debug, Formatter}; use std::ops::Deref; -use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; use std::thread::JoinHandle; use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard}; use crate::dynamic_data::choices::TurnChoice; -use crate::dynamic_data::ExecutingMove; -use crate::dynamic_data::Pokemon; use crate::dynamic_data::{Battle, DynamicLibrary}; use crate::dynamic_data::{BattleSide, DamageSource}; +use crate::dynamic_data::{ExecutingMove, WeakBattleReference, WeakPokemonReference}; +use crate::dynamic_data::{Pokemon, WeakBattleSideReference}; use crate::static_data::{EffectParameter, TypeIdentifier}; use crate::static_data::{Item, Statistic}; use crate::StringKey; @@ -69,77 +69,77 @@ pub trait Script: Send + Sync { Ok(()) } /// This function is ran when this script starts being in effect. - fn on_initialize(&self, _library: &Arc, _pars: Vec) -> Result<()> { + 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) -> Result<()> { + fn on_before_turn(&self, _choice: &Arc) -> 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) -> Result<()> { + fn change_speed(&self, _choice: &Arc, _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) -> Result<()> { + fn change_priority(&self, _choice: &Arc, _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) -> Result<()> { + fn change_move(&self, _choice: &Arc, _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) -> Result<()> { + fn change_number_of_hits(&self, _choice: &Arc, _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) -> Result<()> { + fn prevent_move(&self, _move: &Arc, _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) -> Result<()> { + fn fail_move(&self, _move: &Arc, _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) -> Result<()> { + fn stop_before_move(&self, _move: &Arc, _stop: &mut bool) -> Result<()> { Ok(()) } /// This function runs just before the move starts its execution. - fn on_before_move(&self, _move: &ExecutingMove) -> Result<()> { + fn on_before_move(&self, _move: &Arc) -> 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) -> Result<()> { + fn fail_incoming_move(&self, _move: &Arc, _target: &Pokemon, _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) -> Result<()> { + fn is_invulnerable(&self, _move: &Arc, _target: &Pokemon, _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) -> Result<()> { + fn on_move_miss(&self, _move: &Arc, _target: &Pokemon) -> 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, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _move_type: &mut TypeIdentifier, ) -> Result<()> { @@ -148,8 +148,8 @@ pub trait Script: Send + Sync { /// This function allows the script to change how effective a move is on a target. fn change_effectiveness( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _effectiveness: &mut f32, ) -> Result<()> { @@ -158,8 +158,8 @@ pub trait Script: Send + Sync { /// This function allows a script to block an outgoing move from being critical. fn block_critical( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _block_critical: &mut bool, ) -> Result<()> { @@ -168,8 +168,8 @@ pub trait Script: Send + Sync { /// This function allows a script to block an incoming move from being critical. fn block_incoming_critical( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _block_critical: &mut bool, ) -> Result<()> { @@ -179,8 +179,8 @@ pub trait Script: Send + Sync { /// the percentage accuracy, so anything above 100% will make it always hit. fn change_accuracy( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _accuracy: &mut u8, ) -> Result<()> { @@ -190,8 +190,8 @@ pub trait Script: Send + Sync { /// This function allows a script to change the critical stage of the move used. fn change_critical_stage( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _stage: &mut u8, ) -> Result<()> { @@ -201,8 +201,8 @@ pub trait Script: Send + Sync { /// run when a hit is critical. fn change_critical_modifier( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _modifier: &mut f32, ) -> Result<()> { @@ -212,8 +212,8 @@ pub trait Script: Send + Sync { /// occurs when the user has the move type as one of its own types. fn change_stab_modifier( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _modifier: &mut f32, ) -> Result<()> { @@ -223,8 +223,8 @@ pub trait Script: Send + Sync { /// This function allows a script to change the effective base power of a move hit. fn change_base_power( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _base_power: &mut u8, ) -> Result<()> { @@ -233,8 +233,8 @@ pub trait Script: Send + Sync { /// This function allows a script to bypass defensive stat boosts for a move hit. fn bypass_defensive_stat_boost( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _bypass: &mut bool, ) -> Result<()> { @@ -243,8 +243,8 @@ pub trait Script: Send + Sync { /// This function allows a script to bypass offensive stat boosts for a move hit. fn bypass_offensive_stat_boost( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _bypass: &mut bool, ) -> Result<()> { @@ -253,8 +253,8 @@ pub trait Script: Send + Sync { /// 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, + _move: &Arc, + _target: &Pokemon, _hit: u8, _amount: &mut u32, ) -> Result<()> { @@ -263,8 +263,8 @@ pub trait Script: Send + Sync { /// 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, + _move: &Arc, + _target: &Pokemon, _hit: u8, _amount: &mut u32, ) -> Result<()> { @@ -275,8 +275,8 @@ pub trait Script: Send + Sync { /// defender and attacker. fn change_damage_stat_modifier( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _modifier: &mut f32, ) -> Result<()> { @@ -285,22 +285,22 @@ pub trait Script: Send + Sync { /// 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, + _move: &Arc, + _target: &Pokemon, _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) -> Result<()> { + fn change_damage(&self, _move: &Arc, _target: &Pokemon, _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, + _move: &Arc, + _target: &Pokemon, _hit: u8, _damage: &mut u32, ) -> Result<()> { @@ -308,11 +308,11 @@ pub trait Script: Send + Sync { } /// 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) -> Result<()> { + fn on_incoming_hit(&self, _move: &Arc, _target: &Pokemon, _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) -> Result<()> { + fn on_opponent_faints(&self, _move: &Arc, _target: &Pokemon, _hit: u8) -> Result<()> { Ok(()) } /// This function allows a script attached to a Pokemon or its parents to prevent stat boost @@ -344,8 +344,8 @@ pub trait Script: Send + Sync { /// secondary effect. Note that this function is not called for status moves. fn prevent_secondary_effect( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _prevent: &mut bool, ) -> Result<()> { @@ -357,8 +357,8 @@ pub trait Script: Send + Sync { /// below 0 will make it never hit. fn change_effect_chance( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _chance: &mut f32, ) -> Result<()> { @@ -370,8 +370,8 @@ pub trait Script: Send + Sync { /// or below 0 will make it never hit. fn change_incoming_effect_chance( &self, - _move: &ExecutingMove, - _target: &Arc, + _move: &Arc, + _target: &Pokemon, _hit: u8, _chance: &mut f32, ) -> Result<()> { @@ -380,19 +380,19 @@ pub trait Script: Send + Sync { /// 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) -> Result<()> { + fn on_secondary_effect(&self, _move: &Arc, _target: &Pokemon, _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) -> Result<()> { + fn on_after_hits(&self, _move: &Arc, _target: &Pokemon) -> 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) -> Result<()> { + fn prevent_self_switch(&self, _choice: &Arc, _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) -> Result<()> { + fn prevent_opponent_switch(&self, _choice: &Arc, _prevent: &mut bool) -> Result<()> { Ok(()) } /// This function is called on a move and its parents when the move fails. @@ -404,11 +404,11 @@ pub trait Script: Send + Sync { 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) -> Result<()> { + fn prevent_self_run_away(&self, _choice: &Arc, _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) -> Result<()> { + fn prevent_opponent_run_away(&self, _choice: &Arc, _prevent: &mut bool) -> Result<()> { Ok(()) } /// This function id triggered on all scripts active in the battle after all choices have finished @@ -432,7 +432,7 @@ pub trait Script: Send + Sync { } /// 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) -> Result<()> { + fn on_after_held_item_consume(&self, _pokemon: &Pokemon, _item: &Arc) -> Result<()> { Ok(()) } /// This function is triggered on a Pokemon and its parents when the given Pokemon gains experience, @@ -458,7 +458,12 @@ pub trait Script: Send + Sync { /// 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) -> Result<()> { + fn change_capture_rate_bonus( + &self, + _target: &Pokemon, + _pokeball: &Arc, + _modifier: &mut u8, + ) -> Result<()> { Ok(()) } @@ -778,29 +783,29 @@ mod tests { /// Data to store references to their owning objects on scripts. pub enum ScriptOwnerData { /// A script attached to a Pokemon has a reference to that Pokemon. - Pokemon(AtomicPtr), + Pokemon(WeakPokemonReference), /// A script attached to a Battle Side has a reference to that Battle Side. - BattleSide(AtomicPtr), + BattleSide(WeakBattleSideReference), /// A script attached to a Battle has a reference to that Battle. - Battle(AtomicPtr), + Battle(WeakBattleReference), /// A script also can have no owner. None, } impl From<&Pokemon> for ScriptOwnerData { fn from(p: &Pokemon) -> Self { - ScriptOwnerData::Pokemon(AtomicPtr::new(p as *const Pokemon as *mut Pokemon)) + ScriptOwnerData::Pokemon(p.weak()) } } impl From<&BattleSide> for ScriptOwnerData { fn from(p: &BattleSide) -> Self { - ScriptOwnerData::BattleSide(AtomicPtr::new(p as *const BattleSide as *mut BattleSide)) + ScriptOwnerData::BattleSide(p.weak()) } } impl From<&Battle> for ScriptOwnerData { fn from(p: &Battle) -> Self { - ScriptOwnerData::Battle(AtomicPtr::new(p as *const Battle as *mut Battle)) + ScriptOwnerData::Battle(p.weak()) } } diff --git a/src/ffi/dynamic_data/choices.rs b/src/ffi/dynamic_data/choices.rs index b497362..5a19a0c 100644 --- a/src/ffi/dynamic_data/choices.rs +++ b/src/ffi/dynamic_data/choices.rs @@ -12,7 +12,7 @@ extern "C" fn turn_choice_drop(choice: OwnedPtr) { /// Get the user of the given choice. #[no_mangle] -extern "C" fn turn_choice_user(choice: ExternPointer) -> IdentifiablePointer> { +extern "C" fn turn_choice_user(choice: ExternPointer) -> IdentifiablePointer { choice.as_ref().user().clone().into() } @@ -40,7 +40,7 @@ extern "C" fn turn_choice_fail(choice: ExternPointer) { /// Creates a new Turn Choice with a move to use. #[no_mangle] extern "C" fn turn_choice_move_new( - user: ExternPointer>, + user: ExternPointer, learned_move: ExternPointer>, target_side: u8, target_index: u8, @@ -94,12 +94,12 @@ extern "C" fn turn_choice_move_priority(choice: ExternPointer) -> Na /// Creates a new Turn Choice for the user to flee. #[no_mangle] -extern "C" fn turn_choice_flee_new(user: ExternPointer>) -> IdentifiablePointer { +extern "C" fn turn_choice_flee_new(user: ExternPointer) -> IdentifiablePointer { Box::new(TurnChoice::Flee(FleeChoice::new(user.as_ref().clone()))).into() } /// Creates a new Turn Choice for the user to pass the turn. #[no_mangle] -extern "C" fn turn_choice_pass_new(user: ExternPointer>) -> IdentifiablePointer { +extern "C" fn turn_choice_pass_new(user: ExternPointer) -> IdentifiablePointer { Box::new(TurnChoice::Pass(PassChoice::new(user.as_ref().clone()))).into() } diff --git a/src/ffi/dynamic_data/libraries/battle_stat_calculator.rs b/src/ffi/dynamic_data/libraries/battle_stat_calculator.rs index a9836b8..16bd6e5 100644 --- a/src/ffi/dynamic_data/libraries/battle_stat_calculator.rs +++ b/src/ffi/dynamic_data/libraries/battle_stat_calculator.rs @@ -2,7 +2,6 @@ use crate::dynamic_data::{BattleStatCalculator, Gen7BattleStatCalculator, Pokemo use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr}; use crate::static_data::{Statistic, StatisticSet}; use std::ptr::drop_in_place; -use std::sync::Arc; /// Creates a new Gen 7 battle stat calculator #[no_mangle] @@ -23,7 +22,7 @@ extern "C" fn battle_stat_calculator_drop(ptr: OwnedPtr>, - pokemon: ExternPointer>, + pokemon: ExternPointer, mut stats: ExternPointer>>, ) -> NativeResult<()> { ptr.as_ref() @@ -35,7 +34,7 @@ extern "C" fn battle_stat_calculator_calculate_flat_stats( #[no_mangle] extern "C" fn battle_stat_calculator_calculate_flat_stat( ptr: ExternPointer>, - pokemon: ExternPointer>, + pokemon: ExternPointer, stat: Statistic, ) -> NativeResult { ptr.as_ref().calculate_flat_stat(pokemon.as_ref(), stat).into() @@ -45,7 +44,7 @@ extern "C" fn battle_stat_calculator_calculate_flat_stat( #[no_mangle] extern "C" fn battle_stat_calculator_calculate_boosted_stats( ptr: ExternPointer>, - pokemon: ExternPointer>, + pokemon: ExternPointer, mut stats: ExternPointer>>, ) -> NativeResult<()> { ptr.as_ref() @@ -57,7 +56,7 @@ extern "C" fn battle_stat_calculator_calculate_boosted_stats( #[no_mangle] extern "C" fn battle_stat_calculator_calculate_boosted_stat( ptr: ExternPointer>, - pokemon: ExternPointer>, + pokemon: ExternPointer, stat: Statistic, ) -> NativeResult { ptr.as_ref().calculate_boosted_stat(pokemon.as_ref(), stat).into() diff --git a/src/ffi/dynamic_data/libraries/dynamic_library.rs b/src/ffi/dynamic_data/libraries/dynamic_library.rs index d6a168a..5729101 100644 --- a/src/ffi/dynamic_data/libraries/dynamic_library.rs +++ b/src/ffi/dynamic_data/libraries/dynamic_library.rs @@ -9,18 +9,18 @@ use std::sync::Arc; /// Instantiates a new DynamicLibrary with given parameters. #[no_mangle] extern "C" fn dynamic_library_new( - static_data: OwnedPtr>, - stat_calculator: OwnedPtr>, - damage_library: OwnedPtr>, - misc_library: OwnedPtr>, + static_data: OwnedPtr>, + stat_calculator: OwnedPtr>, + damage_library: OwnedPtr>, + misc_library: OwnedPtr>, script_resolver: OwnedPtr>, ) -> IdentifiablePointer> { unsafe { let a: Arc = Arc::new(DynamicLibraryImpl::new( - *Box::from_raw(static_data), - *Box::from_raw(stat_calculator), - *Box::from_raw(damage_library), - *Box::from_raw(misc_library), + static_data.read(), + stat_calculator.read(), + damage_library.read(), + misc_library.read(), *Box::from_raw(script_resolver), )); a.into() @@ -37,8 +37,8 @@ extern "C" fn dynamic_library_drop(ptr: OwnedPtr>) { #[no_mangle] extern "C" fn dynamic_library_get_static_data( ptr: ExternPointer>, -) -> IdentifiablePointer> { - ptr.as_ref().static_data().into() +) -> IdentifiablePointer> { + ptr.as_ref().static_data().clone().into() } /// The stat calculator deals with the calculation of flat and boosted stats, based on the @@ -46,16 +46,16 @@ extern "C" fn dynamic_library_get_static_data( #[no_mangle] extern "C" fn dynamic_library_get_stat_calculator( ptr: ExternPointer>, -) -> IdentifiablePointer> { - ptr.as_ref().stat_calculator().into() +) -> IdentifiablePointer> { + ptr.as_ref().stat_calculator().clone().into() } /// The damage calculator deals with the calculation of things relating to damage. #[no_mangle] extern "C" fn dynamic_library_get_damage_calculator( ptr: ExternPointer>, -) -> IdentifiablePointer> { - ptr.as_ref().damage_calculator().into() +) -> IdentifiablePointer> { + ptr.as_ref().damage_calculator().clone().into() } /// The Misc Library holds minor functions that do not fall in any of the other libraries and @@ -63,6 +63,6 @@ extern "C" fn dynamic_library_get_damage_calculator( #[no_mangle] extern "C" fn dynamic_library_get_misc_library( ptr: ExternPointer>, -) -> IdentifiablePointer> { - ptr.as_ref().misc_library().into() +) -> IdentifiablePointer> { + ptr.as_ref().misc_library().clone().into() } diff --git a/src/ffi/dynamic_data/models/battle.rs b/src/ffi/dynamic_data/models/battle.rs index 8f03fb8..df22307 100644 --- a/src/ffi/dynamic_data/models/battle.rs +++ b/src/ffi/dynamic_data/models/battle.rs @@ -11,7 +11,7 @@ use std::sync::Arc; #[no_mangle] extern "C" fn battle_new( library: ExternPointer>, - parties: *const OwnedPtr, + parties: *const OwnedPtr>, parties_length: usize, can_flee: u8, number_of_sides: u8, @@ -19,7 +19,7 @@ extern "C" fn battle_new( // NOTE: Split into two due to u128 not being ABI safe: https://github.com/rust-lang/rust/issues/54341 random_seed_1: u64, random_seed_2: u64, -) -> IdentifiablePointer> { +) -> IdentifiablePointer { let parties = unsafe { std::slice::from_raw_parts(parties, parties_length) .iter() @@ -59,9 +59,12 @@ extern "C" fn battle_parties_length(ptr: ExternPointer>) -> usize { /// Get a party in the battle. #[no_mangle] -extern "C" fn battle_parties_get(ptr: ExternPointer>, index: usize) -> IdentifiablePointer { +extern "C" fn battle_parties_get( + ptr: ExternPointer>, + index: usize, +) -> IdentifiablePointer> { if let Some(v) = ptr.as_ref().parties().get(index) { - (v as *const BattleParty).into() + v.clone().into() } else { IdentifiablePointer::none() } @@ -103,8 +106,8 @@ extern "C" fn battle_sides_get(ptr: ExternPointer>, index: usize) -> /// The RNG used for the battle. #[no_mangle] -extern "C" fn battle_random(ptr: ExternPointer>) -> IdentifiablePointer { - (ptr.as_ref().random() as *const BattleRandom).into() +extern "C" fn battle_random(ptr: ExternPointer>) -> IdentifiablePointer> { + ptr.as_ref().random().clone().into() } /// Whether or not the battle has ended. @@ -150,11 +153,7 @@ extern "C" fn battle_last_turn_time(ptr: ExternPointer>) -> u64 { /// Get a Pokemon on the battlefield, on a specific side and an index on that side. #[no_mangle] -extern "C" fn battle_get_pokemon( - ptr: ExternPointer>, - side: u8, - index: u8, -) -> IdentifiablePointer> { +extern "C" fn battle_get_pokemon(ptr: ExternPointer>, side: u8, index: u8) -> IdentifiablePointer { if let Some(v) = ptr.as_ref().get_pokemon(side, index) { v.into() } else { diff --git a/src/ffi/dynamic_data/models/battle_party.rs b/src/ffi/dynamic_data/models/battle_party.rs index 98bda5f..750af6a 100644 --- a/src/ffi/dynamic_data/models/battle_party.rs +++ b/src/ffi/dynamic_data/models/battle_party.rs @@ -58,7 +58,7 @@ extern "C" fn battle_party_has_pokemon_not_in_field(ptr: ExternPointer>, index: usize, -) -> IdentifiablePointer> { +) -> IdentifiablePointer { if let Some(v) = ptr.as_ref().get_pokemon(index) { v.into() } else { diff --git a/src/ffi/dynamic_data/models/event.rs b/src/ffi/dynamic_data/models/event.rs index 4052073..c266465 100644 --- a/src/ffi/dynamic_data/models/event.rs +++ b/src/ffi/dynamic_data/models/event.rs @@ -1,6 +1,5 @@ use crate::dynamic_data::{Event, Pokemon}; use crate::ffi::{ExternPointer, IdentifiablePointer}; -use std::sync::Arc; /// The kind of the event. #[no_mangle] @@ -67,7 +66,7 @@ event_ffi!( /// The index of the Pokemon that got switched in/out on its side index: u8, /// The new Pokemon that will be on the spot. If none, the spot will be null. - pokemon: IdentifiablePointer>, + pokemon: IdentifiablePointer, }, { return SwitchData{ diff --git a/src/ffi/dynamic_data/models/learned_move.rs b/src/ffi/dynamic_data/models/learned_move.rs index f1ee004..6c3866d 100644 --- a/src/ffi/dynamic_data/models/learned_move.rs +++ b/src/ffi/dynamic_data/models/learned_move.rs @@ -61,5 +61,6 @@ extern "C" fn learned_move_restore_all_uses(learned_move: ExternPointer>, amount: u8) -> NativeResult<()> { - learned_move.as_ref().restore_uses(amount).into() + learned_move.as_ref().restore_uses(amount); + NativeResult::ok(()) } diff --git a/src/ffi/dynamic_data/models/pokemon.rs b/src/ffi/dynamic_data/models/pokemon.rs index dd3eb01..fb5454a 100644 --- a/src/ffi/dynamic_data/models/pokemon.rs +++ b/src/ffi/dynamic_data/models/pokemon.rs @@ -22,7 +22,7 @@ extern "C" fn pokemon_new( gender: Gender, coloring: u8, nature: *const c_char, -) -> NativeResult>> { +) -> NativeResult> { let nature = unsafe { CStr::from_ptr(nature) }.into(); let pokemon = Pokemon::new( library.as_ref().clone(), @@ -39,44 +39,44 @@ extern "C" fn pokemon_new( &nature, ); match pokemon { - Ok(pokemon) => NativeResult::ok(Arc::new(pokemon).into()), + Ok(pokemon) => NativeResult::ok(pokemon.into()), Err(err) => NativeResult::err(err), } } /// Drops an Arc reference held by the FFI. #[no_mangle] -unsafe extern "C" fn pokemon_drop(ptr: OwnedPtr>) { +unsafe extern "C" fn pokemon_drop(ptr: OwnedPtr) { drop_in_place(ptr); } /// The library data of the Pokemon. #[no_mangle] -extern "C" fn pokemon_library(ptr: ExternPointer>) -> IdentifiablePointer> { +extern "C" fn pokemon_library(ptr: ExternPointer) -> IdentifiablePointer> { ptr.as_ref().library().clone().into() } /// The species of the Pokemon. #[no_mangle] -extern "C" fn pokemon_species(ptr: ExternPointer>) -> IdentifiablePointer> { +extern "C" fn pokemon_species(ptr: ExternPointer) -> IdentifiablePointer> { ptr.as_ref().species().into() } /// The form of the Pokemon. #[no_mangle] -extern "C" fn pokemon_form(ptr: ExternPointer>) -> IdentifiablePointer> { +extern "C" fn pokemon_form(ptr: ExternPointer) -> IdentifiablePointer> { ptr.as_ref().form().into() } /// The species that should be displayed to the user. This handles stuff like the Illusion ability. #[no_mangle] -extern "C" fn pokemon_display_species(ptr: ExternPointer>) -> IdentifiablePointer> { +extern "C" fn pokemon_display_species(ptr: ExternPointer) -> IdentifiablePointer> { ptr.as_ref().display_species().into() } /// The form that should be displayed to the user. This handles stuff like the Illusion ability. #[no_mangle] -extern "C" fn pokemon_display_form(ptr: ExternPointer>) -> IdentifiablePointer> { +extern "C" fn pokemon_display_form(ptr: ExternPointer) -> IdentifiablePointer> { ptr.as_ref().display_form().into() } @@ -88,7 +88,7 @@ ffi_arc_getter!(Pokemon, coloring, u8); /// Gets the held item of a Pokemon #[no_mangle] -extern "C" fn pokemon_held_item(ptr: ExternPointer>) -> IdentifiablePointer> { +extern "C" fn pokemon_held_item(ptr: ExternPointer) -> IdentifiablePointer> { if let Some(v) = ptr.as_ref().held_item().read().as_ref() { v.clone().into() } else { @@ -98,7 +98,7 @@ extern "C" fn pokemon_held_item(ptr: ExternPointer>) -> Identifiabl /// Checks whether the Pokemon is holding a specific item. #[no_mangle] -extern "C" fn pokemon_has_held_item(ptr: ExternPointer>, name: *const c_char) -> u8 { +extern "C" fn pokemon_has_held_item(ptr: ExternPointer, name: *const c_char) -> u8 { let name = unsafe { CStr::from_ptr(name) }.into(); u8::from(ptr.as_ref().has_held_item(&name)) } @@ -106,7 +106,7 @@ extern "C" fn pokemon_has_held_item(ptr: ExternPointer>, name: *con /// Changes the held item of the Pokemon. Returns the previously held item. #[no_mangle] extern "C" fn pokemon_set_held_item( - ptr: ExternPointer>, + ptr: ExternPointer, item: ExternPointer>, ) -> IdentifiablePointer> { if let Some(v) = ptr.as_ref().set_held_item(item.as_ref()) { @@ -118,7 +118,7 @@ extern "C" fn pokemon_set_held_item( /// Removes the held item from the Pokemon. Returns the previously held item. #[no_mangle] -extern "C" fn pokemon_remove_held_item(ptr: ExternPointer>) -> IdentifiablePointer> { +extern "C" fn pokemon_remove_held_item(ptr: ExternPointer) -> IdentifiablePointer> { if let Some(v) = ptr.as_ref().remove_held_item() { v.into() } else { @@ -128,7 +128,7 @@ extern "C" fn pokemon_remove_held_item(ptr: ExternPointer>) -> Iden /// Makes the Pokemon uses its held item. #[no_mangle] -extern "C" fn pokemon_consume_held_item(ptr: ExternPointer>) -> NativeResult { +extern "C" fn pokemon_consume_held_item(ptr: ExternPointer) -> NativeResult { match ptr.as_ref().consume_held_item() { Ok(v) => NativeResult::ok(u8::from(v)), Err(err) => NativeResult::err(err), @@ -142,7 +142,7 @@ ffi_arc_getter!(Pokemon, height, f32); /// An optional nickname of the Pokemon. #[no_mangle] -extern "C" fn pokemon_nickname(ptr: ExternPointer>) -> NativeResult<*mut c_char> { +extern "C" fn pokemon_nickname(ptr: ExternPointer) -> NativeResult<*mut c_char> { let name = ptr.as_ref().nickname(); if let Some(v) = name { match CString::new(v.as_str()) { @@ -156,13 +156,13 @@ extern "C" fn pokemon_nickname(ptr: ExternPointer>) -> NativeResult /// Whether the actual ability on the form is a hidden ability. #[no_mangle] -extern "C" fn pokemon_real_ability_is_hidden(ptr: ExternPointer>) -> u8 { +extern "C" fn pokemon_real_ability_is_hidden(ptr: ExternPointer) -> u8 { u8::from(ptr.as_ref().real_ability().hidden) } /// The index of the actual ability on the form. #[no_mangle] -extern "C" fn pokemon_real_ability_index(ptr: ExternPointer>) -> u8 { +extern "C" fn pokemon_real_ability_index(ptr: ExternPointer) -> u8 { ptr.as_ref().real_ability().index } @@ -172,7 +172,7 @@ ffi_vec_value_getters!(Pokemon, Pokemon, types, TypeIdentifier); /// a null pointer otherwise. #[no_mangle] extern "C" fn pokemon_learned_move_get( - ptr: ExternPointer>, + ptr: ExternPointer, index: usize, ) -> IdentifiablePointer> { if let Some(Some(v)) = ptr.as_ref().learned_moves().read().get(index) { @@ -184,26 +184,26 @@ extern "C" fn pokemon_learned_move_get( /// The stats of the Pokemon when disregarding any stat boosts. #[no_mangle] -extern "C" fn pokemon_flat_stats(ptr: ExternPointer>) -> IdentifiablePointer> { - (ptr.as_ref().flat_stats() as *const StatisticSet).into() +extern "C" fn pokemon_flat_stats(ptr: ExternPointer) -> IdentifiablePointer>> { + ptr.as_ref().flat_stats().clone().into() } /// The stats of the Pokemon including the stat boosts. #[no_mangle] -extern "C" fn pokemon_boosted_stats(ptr: ExternPointer>) -> IdentifiablePointer> { - (ptr.as_ref().boosted_stats() as *const StatisticSet).into() +extern "C" fn pokemon_boosted_stats(ptr: ExternPointer) -> IdentifiablePointer>> { + ptr.as_ref().boosted_stats().clone().into() } /// Get the stat boosts for a specific stat. #[no_mangle] -extern "C" fn pokemon_get_stat_boost(ptr: ExternPointer>, statistic: Statistic) -> i8 { +extern "C" fn pokemon_get_stat_boost(ptr: ExternPointer, statistic: Statistic) -> i8 { ptr.as_ref().stat_boost(statistic) } /// Change a boosted stat by a certain amount. #[no_mangle] extern "C" fn pokemon_change_stat_boost( - ptr: ExternPointer>, + ptr: ExternPointer, stat: Statistic, diff_amount: i8, self_inflicted: u8, @@ -216,14 +216,14 @@ extern "C" fn pokemon_change_stat_boost( /// Gets a [individual value](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon. #[no_mangle] -extern "C" fn pokemon_get_individual_value(ptr: ExternPointer>, stat: Statistic) -> u8 { +extern "C" fn pokemon_get_individual_value(ptr: ExternPointer, stat: Statistic) -> u8 { ptr.as_ref().individual_values().get_stat(stat) } /// Modifies a [individual value](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon. #[no_mangle] extern "C" fn pokemon_set_individual_value( - ptr: ExternPointer>, + ptr: ExternPointer, stat: Statistic, value: u8, ) -> NativeResult<()> { @@ -233,17 +233,13 @@ extern "C" fn pokemon_set_individual_value( /// Gets a [effort value](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon. #[no_mangle] -extern "C" fn pokemon_get_effort_value(ptr: ExternPointer>, stat: Statistic) -> u8 { +extern "C" fn pokemon_get_effort_value(ptr: ExternPointer, stat: Statistic) -> u8 { ptr.as_ref().effort_values().get_stat(stat) } /// Modifies a [effort value](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon. #[no_mangle] -extern "C" fn pokemon_set_effort_value( - ptr: ExternPointer>, - stat: Statistic, - value: u8, -) -> NativeResult<()> { +extern "C" fn pokemon_set_effort_value(ptr: ExternPointer, stat: Statistic, value: u8) -> NativeResult<()> { ptr.as_ref().effort_values().set_stat(stat, value); ptr.as_ref().recalculate_flat_stats().into() } @@ -251,7 +247,7 @@ extern "C" fn pokemon_set_effort_value( /// Gets the data for the battle the Pokemon is currently in. If the Pokemon is not in a battle, this /// returns null. #[no_mangle] -extern "C" fn pokemon_get_battle(ptr: ExternPointer>) -> IdentifiablePointer> { +extern "C" fn pokemon_get_battle(ptr: ExternPointer) -> IdentifiablePointer { if let Some(v) = ptr.as_ref().get_battle() { v.into() } else { @@ -262,27 +258,27 @@ extern "C" fn pokemon_get_battle(ptr: ExternPointer>) -> Identifiab /// Get the index of the side of the battle the Pokemon is in. If the Pokemon /// is not on the battlefield, this always returns 0. #[no_mangle] -extern "C" fn pokemon_get_battle_side_index(ptr: ExternPointer>) -> u8 { +extern "C" fn pokemon_get_battle_side_index(ptr: ExternPointer) -> u8 { ptr.as_ref().get_battle_side_index().unwrap_or_default() } /// Get the index of the slot on the side of the battle the Pokemon is in. If the Pokemon /// is not on the battlefield, this always returns 0. #[no_mangle] -extern "C" fn pokemon_get_battle_index(ptr: ExternPointer>) -> u8 { +extern "C" fn pokemon_get_battle_index(ptr: ExternPointer) -> u8 { ptr.as_ref().get_battle_index().unwrap_or_default() } /// Returns whether something overrides the ability. #[no_mangle] -extern "C" fn pokemon_is_ability_overriden(ptr: ExternPointer>) -> u8 { +extern "C" fn pokemon_is_ability_overriden(ptr: ExternPointer) -> u8 { u8::from(ptr.as_ref().is_ability_overriden()) } /// Returns the currently active ability. #[no_mangle] extern "C" fn pokemon_active_ability( - ptr: ExternPointer>, + ptr: ExternPointer, ) -> NativeResult>> { match ptr.as_ref().active_ability() { Ok(v) => NativeResult::ok(v.clone().into()), @@ -292,13 +288,13 @@ extern "C" fn pokemon_active_ability( /// Whether or not the Pokemon is allowed to gain experience. #[no_mangle] -extern "C" fn pokemon_allowed_experience_gain(ptr: ExternPointer>) -> u8 { +extern "C" fn pokemon_allowed_experience_gain(ptr: ExternPointer) -> u8 { u8::from(ptr.as_ref().allowed_experience_gain()) } /// The [nature](https://bulbapedia.bulbagarden.net/wiki/Nature) of the Pokemon. #[no_mangle] -extern "C" fn pokemon_nature(ptr: ExternPointer>) -> IdentifiablePointer> { +extern "C" fn pokemon_nature(ptr: ExternPointer) -> IdentifiablePointer> { ptr.as_ref().nature().clone().into() } @@ -306,20 +302,20 @@ extern "C" fn pokemon_nature(ptr: ExternPointer>) -> IdentifiablePo /// stats, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted /// stats, as those depend on the flat stats. #[no_mangle] -extern "C" fn pokemon_recalculate_flat_stats(ptr: ExternPointer>) -> NativeResult<()> { +extern "C" fn pokemon_recalculate_flat_stats(ptr: ExternPointer) -> NativeResult<()> { ptr.as_ref().recalculate_flat_stats().into() } /// Calculates the boosted stats on the Pokemon. This should be called when a stat boost changes. #[no_mangle] -extern "C" fn pokemon_recalculate_boosted_stats(ptr: ExternPointer>) -> NativeResult<()> { +extern "C" fn pokemon_recalculate_boosted_stats(ptr: ExternPointer) -> NativeResult<()> { ptr.as_ref().recalculate_boosted_stats().into() } /// Change the species of the Pokemon. #[no_mangle] extern "C" fn pokemon_change_species( - ptr: ExternPointer>, + ptr: ExternPointer, species: ExternPointer>, form: ExternPointer>, ) -> NativeResult<()> { @@ -330,48 +326,45 @@ extern "C" fn pokemon_change_species( /// Change the form of the Pokemon. #[no_mangle] -extern "C" fn pokemon_change_form( - ptr: ExternPointer>, - form: ExternPointer>, -) -> NativeResult<()> { +extern "C" fn pokemon_change_form(ptr: ExternPointer, form: ExternPointer>) -> NativeResult<()> { ptr.as_ref().change_form(form.as_ref()).into() } /// Whether or not the Pokemon is useable in a battle. #[no_mangle] -extern "C" fn pokemon_is_usable(ptr: ExternPointer>) -> u8 { +extern "C" fn pokemon_is_usable(ptr: ExternPointer) -> u8 { u8::from(ptr.as_ref().is_usable()) } /// Returns whether the Pokemon is fainted. #[no_mangle] -extern "C" fn pokemon_is_fainted(ptr: ExternPointer>) -> u8 { +extern "C" fn pokemon_is_fainted(ptr: ExternPointer) -> u8 { u8::from(ptr.as_ref().is_fainted()) } /// Whether or not the Pokemon is on the battlefield. #[no_mangle] -extern "C" fn pokemon_is_on_battlefield(ptr: ExternPointer>) -> u8 { +extern "C" fn pokemon_is_on_battlefield(ptr: ExternPointer) -> u8 { u8::from(ptr.as_ref().is_on_battlefield()) } /// Damages the Pokemon by a certain amount of damage, from a damage source. #[no_mangle] -extern "C" fn pokemon_damage(ptr: ExternPointer>, damage: u32, source: DamageSource) -> NativeResult<()> { +extern "C" fn pokemon_damage(ptr: ExternPointer, damage: u32, source: DamageSource) -> NativeResult<()> { ptr.as_ref().damage(damage, source).into() } /// Heals the Pokemon by a specific amount. Unless allow_revive is set to true, this will not /// heal if the Pokemon has 0 health. If the amount healed is 0, this will return false. #[no_mangle] -extern "C" fn pokemon_heal(ptr: ExternPointer>, amount: u32, allow_revive: u8) -> bool { +extern "C" fn pokemon_heal(ptr: ExternPointer, amount: u32, allow_revive: u8) -> bool { ptr.as_ref().heal(amount, allow_revive == 1) } /// Learn a move. #[no_mangle] extern "C" fn pokemon_learn_move( - ptr: ExternPointer>, + ptr: ExternPointer, move_name: *const c_char, learn_method: MoveLearnMethod, ) -> NativeResult<()> { @@ -384,6 +377,6 @@ extern "C" fn pokemon_learn_move( /// Removes the current non-volatile status from the Pokemon. #[no_mangle] -extern "C" fn pokemon_clear_status(ptr: ExternPointer>) { +extern "C" fn pokemon_clear_status(ptr: ExternPointer) { ptr.as_ref().clear_status() } diff --git a/src/ffi/dynamic_data/models/pokemon_party.rs b/src/ffi/dynamic_data/models/pokemon_party.rs index 958c400..723428a 100644 --- a/src/ffi/dynamic_data/models/pokemon_party.rs +++ b/src/ffi/dynamic_data/models/pokemon_party.rs @@ -10,10 +10,7 @@ extern "C" fn pokemon_party_new(capacity: usize) -> IdentifiablePointer>, - index: usize, -) -> IdentifiablePointer> { +extern "C" fn pokemon_party_at(ptr: ExternPointer>, index: usize) -> IdentifiablePointer { if let Some(v) = ptr.as_ref().at(index) { v.into() } else { @@ -32,8 +29,8 @@ extern "C" fn pokemon_party_switch(ptr: ExternPointer>, a: usi extern "C" fn pokemon_party_swap_into( ptr: ExternPointer>, index: usize, - pokemon: ExternPointer>, -) -> NativeResult>> { + pokemon: ExternPointer, +) -> NativeResult> { let pokemon = if pokemon.ptr.is_null() { None } else { @@ -66,9 +63,6 @@ extern "C" fn pokemon_party_pack_party(ptr: ExternPointer>) -> /// Checks if the party contains a given pokemon. #[no_mangle] -extern "C" fn pokemon_party_has_pokemon( - ptr: ExternPointer>, - pokemon: ExternPointer>, -) -> u8 { +extern "C" fn pokemon_party_has_pokemon(ptr: ExternPointer>, pokemon: ExternPointer) -> u8 { u8::from(ptr.as_ref().has_pokemon(pokemon.as_ref())) } diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 5dc53db..5f4f45f 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -87,6 +87,7 @@ macro_rules! ffi_vec_stringkey_getters { }; } +use crate::dynamic_data::{Battle, Pokemon}; use crate::{ValueIdentifiable, ValueIdentifier}; pub(self) use ffi_arc_dyn_getter; pub(self) use ffi_arc_getter; @@ -250,6 +251,40 @@ impl From<*const T> for IdentifiablePointer { } } +impl From for IdentifiablePointer { + fn from(v: Pokemon) -> Self { + let id = v.value_identifier().value(); + Self { + ptr: Box::into_raw(Box::new(v)), + id, + } + } +} + +impl From> for IdentifiablePointer { + fn from(v: Option) -> Self { + if let Some(v) = v { + let id = unsafe { transmute(v.value_identifier()) }; + Self { + ptr: Box::into_raw(Box::new(v)), + id, + } + } else { + IdentifiablePointer::none() + } + } +} + +impl From for IdentifiablePointer { + fn from(v: Battle) -> Self { + let id = v.value_identifier().value(); + Self { + ptr: Box::into_raw(Box::new(v)), + id, + } + } +} + impl IdentifiablePointer { /// Returns an identifiable pointer with null as pointer, and 0 as identifier. pub fn none() -> Self { diff --git a/src/ffi/static_data/ability.rs b/src/ffi/static_data/ability.rs index 7a44063..146718a 100644 --- a/src/ffi/static_data/ability.rs +++ b/src/ffi/static_data/ability.rs @@ -11,13 +11,13 @@ use std::sync::Arc; unsafe extern "C" fn ability_new( name: *const c_char, effect: *const c_char, - parameters: *const OwnedPtr, + parameters: *const OwnedPtr>, parameters_length: usize, ) -> NativeResult>> { let parameters = std::slice::from_raw_parts(parameters, parameters_length); - let mut parameters_vec: Vec = Vec::with_capacity(parameters_length); + let mut parameters_vec: Vec> = Vec::with_capacity(parameters_length); for parameter in parameters { - parameters_vec.push(*Box::from_raw(*parameter)); + parameters_vec.push(parameter.read()); } let name: StringKey = match CStr::from_ptr(name).to_str() { @@ -68,9 +68,9 @@ unsafe extern "C" fn ability_parameter_length(ptr: ExternPointer>, index: usize, -) -> IdentifiablePointer { +) -> IdentifiablePointer> { if let Some(p) = ptr.as_ref().parameters().get(index) { - (p as *const EffectParameter).into() + p.clone().into() } else { IdentifiablePointer::none() } diff --git a/src/ffi/static_data/form.rs b/src/ffi/static_data/form.rs index e3c8044..0e34eae 100644 --- a/src/ffi/static_data/form.rs +++ b/src/ffi/static_data/form.rs @@ -95,8 +95,8 @@ ffi_vec_value_getters!(Form, dyn Form, types, TypeIdentifier); #[no_mangle] unsafe extern "C" fn form_base_stats( ptr: ExternPointer>, -) -> IdentifiablePointer> { - (ptr.as_ref().base_stats() as *const StaticStatisticSet).into() +) -> IdentifiablePointer>> { + ptr.as_ref().base_stats().clone().into() } ffi_vec_stringkey_getters!(Form, abilities); diff --git a/src/ffi/static_data/libraries/static_data.rs b/src/ffi/static_data/libraries/static_data.rs index 370bb68..9532aab 100644 --- a/src/ffi/static_data/libraries/static_data.rs +++ b/src/ffi/static_data/libraries/static_data.rs @@ -4,28 +4,29 @@ use crate::static_data::{ StaticData, StaticDataImpl, TypeLibrary, }; use std::ptr::drop_in_place; +use std::sync::Arc; /// Instantiates a new data collection. #[no_mangle] unsafe extern "C" fn static_data_new( - settings: OwnedPtr>, - species: OwnedPtr>, - moves: OwnedPtr>, - items: OwnedPtr>, - growth_rates: OwnedPtr>, - types: OwnedPtr>, - natures: OwnedPtr>, - abilities: OwnedPtr>, -) -> IdentifiablePointer> { - let b: Box = Box::new(StaticDataImpl::new( - *Box::from_raw(settings), - *Box::from_raw(species), - *Box::from_raw(moves), - *Box::from_raw(items), - *Box::from_raw(growth_rates), - *Box::from_raw(types), - *Box::from_raw(natures), - *Box::from_raw(abilities), + settings: OwnedPtr>, + species: OwnedPtr>, + moves: OwnedPtr>, + items: OwnedPtr>, + growth_rates: OwnedPtr>, + types: OwnedPtr>, + natures: OwnedPtr>, + abilities: OwnedPtr>, +) -> IdentifiablePointer> { + let b: Arc = Arc::new(StaticDataImpl::new( + settings.read(), + species.read(), + moves.read(), + items.read(), + growth_rates.read(), + types.read(), + natures.read(), + abilities.read(), )); b.into() } @@ -39,63 +40,63 @@ unsafe extern "C" fn static_data_drop(ptr: OwnedPtr>) { /// Several misc settings for the library. #[no_mangle] unsafe extern "C" fn static_data_settings( - mut data: ExternPointer>, -) -> IdentifiablePointer> { - data.as_mut().settings().into() + mut data: ExternPointer>, +) -> IdentifiablePointer> { + data.as_mut().settings().clone().into() } /// All data for Pokemon species. #[no_mangle] unsafe extern "C" fn static_data_species( - mut data: ExternPointer>, -) -> IdentifiablePointer> { - data.as_mut().species().into() + mut data: ExternPointer>, +) -> IdentifiablePointer> { + data.as_mut().species().clone().into() } /// All data for the moves. #[no_mangle] unsafe extern "C" fn static_data_moves( - mut data: ExternPointer>, -) -> IdentifiablePointer> { - data.as_mut().moves().into() + mut data: ExternPointer>, +) -> IdentifiablePointer> { + data.as_mut().moves().clone().into() } /// All data for the items. #[no_mangle] unsafe extern "C" fn static_data_items( - mut data: ExternPointer>, -) -> IdentifiablePointer> { - (data.as_mut().items()).into() + mut data: ExternPointer>, +) -> IdentifiablePointer> { + (data.as_mut().items()).clone().into() } /// All data for growth rates. #[no_mangle] unsafe extern "C" fn static_data_growth_rates( - mut data: ExternPointer>, -) -> IdentifiablePointer> { - data.as_mut().growth_rates().into() + mut data: ExternPointer>, +) -> IdentifiablePointer> { + data.as_mut().growth_rates().clone().into() } /// All data related to types and type effectiveness. #[no_mangle] unsafe extern "C" fn static_data_types( - mut data: ExternPointer>, -) -> IdentifiablePointer> { - data.as_mut().types().into() + mut data: ExternPointer>, +) -> IdentifiablePointer> { + data.as_mut().types().clone().into() } /// All data related to natures. #[no_mangle] unsafe extern "C" fn static_data_natures( - data: ExternPointer>, -) -> IdentifiablePointer> { - data.as_ref().natures().into() + data: ExternPointer>, +) -> IdentifiablePointer> { + data.as_ref().natures().clone().into() } /// All data related to abilities. #[no_mangle] unsafe extern "C" fn static_data_abilities( - mut data: ExternPointer>, -) -> IdentifiablePointer> { - (data.as_mut().abilities()).into() + mut data: ExternPointer>, +) -> IdentifiablePointer> { + (data.as_mut().abilities()).clone().into() } diff --git a/src/ffi/static_data/move_data.rs b/src/ffi/static_data/move_data.rs index d0be0ab..53f8e1e 100644 --- a/src/ffi/static_data/move_data.rs +++ b/src/ffi/static_data/move_data.rs @@ -102,7 +102,7 @@ unsafe extern "C" fn move_data_has_flag(ptr: ExternPointer>, f unsafe extern "C" fn secondary_effect_new( chance: f32, effect_name: BorrowedPtr, - parameters: *mut OwnedPtr, + parameters: *mut OwnedPtr>, parameters_length: usize, ) -> IdentifiablePointer> { let parameter_slice = std::slice::from_raw_parts(parameters, parameters_length); @@ -156,9 +156,9 @@ unsafe extern "C" fn secondary_effect_parameter_length(ptr: ExternPointer>, index: usize, -) -> IdentifiablePointer { +) -> IdentifiablePointer> { if let Some(v) = ptr.as_ref().parameters().get(index) { - (v as *const EffectParameter).into() + v.clone().into() } else { IdentifiablePointer::none() } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/battle.rs b/src/script_implementations/wasm/export_registry/dynamic_data/battle.rs index 7597b10..6e885e7 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/battle.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/battle.rs @@ -5,10 +5,9 @@ use crate::script_implementations::wasm::export_registry::wasm_result::{ get_value, get_value_arc, get_value_call_getter, try_wasm, }; use crate::script_implementations::wasm::export_registry::{register, wasm_ok, WasmResult}; -use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; +use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::StringKey; -use anyhow_ext::Context; use wasmer::FunctionEnvMut; register! { @@ -23,19 +22,27 @@ register! { fn battle_get_parties( env: FunctionEnvMut, battle: ExternRef, - ) -> WasmResult> { - let parties = get_value_call_getter!(battle.parties(), env); - wasm_ok(VecExternRef::new(env.data().data().as_ref(), parties)) + ) -> WasmResult { + let battle = get_value!(battle, env); + let parties = battle.parties(); + let mut vec : Vec = Vec::with_capacity(parties.len()); + for party in parties { + vec.push(ExternRef::::func_new(&env, party.into()).index() as u32); + } + let wasm_ptr = try_wasm!(env.data().data().copy_value_vec_to_wasm(&vec), env); + let r: u64 = unsafe { std::mem::transmute((wasm_ptr, vec.len() as u32)) }; + wasm_ok(r) } fn battle_get_choice_queue( env: FunctionEnvMut, battle: ExternRef, ) -> WasmResult> { - let value = get_value_call_getter!(battle.current_turn_queue(), env); + let battle = get_value!(battle, env); + let value = battle.current_turn_queue(); let queue = value.read(); wasm_ok(if let Some(queue) = queue.as_ref() { - ExternRef::::func_new(&env, queue) + ExternRef::::func_new(&env, queue.into()) } else { ExternRef::null() }) @@ -45,24 +52,34 @@ register! { env: FunctionEnvMut, battle: ExternRef, ) -> WasmResult> { - let value = get_value_call_getter!(battle.library(), env); - wasm_ok(ExternRef::::func_new(&env, &value.clone())) + let battle = get_value!(battle, env); + let value = battle.library(); + wasm_ok(ExternRef::func_new(&env, value.into())) } fn battle_get_sides( env: FunctionEnvMut, battle: ExternRef, - ) -> WasmResult> { - let value = get_value_arc!(battle, env); - wasm_ok(VecExternRef::new(env.data().data().as_ref(), value.sides())) + ) -> WasmResult { + let value = get_value!(battle, env); + let sides = value.sides(); + let mut vec : Vec = Vec::with_capacity(sides.len()); + for side in sides { + vec.push(ExternRef::::func_new(&env, side.into()).index() as u32); + } + let wasm_ptr = try_wasm!(env.data().data().copy_value_vec_to_wasm(&vec), env); + let r: u64 = unsafe { std::mem::transmute((wasm_ptr, vec.len() as u32)) }; + wasm_ok(r) + } fn battle_get_random( env: FunctionEnvMut, battle: ExternRef, ) -> WasmResult> { - let random = get_value_call_getter!(battle.random(), env); - wasm_ok(ExternRef::::func_new(&env, random)) + let battle = get_value!(battle, env); + let random = battle.random(); + wasm_ok(ExternRef::::func_new(&env, random.into())) } fn battle_get_weather_name( @@ -72,7 +89,7 @@ register! { let weather = get_value_call_getter!(battle.weather_name(), env); let weather = try_wasm!(weather, env); wasm_ok(if let Some(weather) = weather { - ExternRef::::func_new(&env, &weather) + ExternRef::::func_new(&env, weather.into()) } else { ExternRef::null() }) @@ -86,8 +103,8 @@ register! { let battle = get_value!(battle, env); let pokemon = get_value!(pokemon, env); for party in battle.parties() { - if party.party().has_pokemon(pokemon) { - return wasm_ok(ExternRef::::func_new(&env, party)); + if party.party().has_pokemon(&pokemon) { + return wasm_ok(ExternRef::::func_new(&env, party.into())); } } wasm_ok(ExternRef::::null()) @@ -101,7 +118,7 @@ register! { let battle = get_value!(battle, env); let pokemon = battle.get_pokemon(side, index); wasm_ok(if let Some(pokemon) = pokemon { - ExternRef::::func_new(&env, &pokemon) + ExternRef::::func_new(&env, (&pokemon).into()) } else { ExternRef::null() }) @@ -159,7 +176,7 @@ register! { env: FunctionEnvMut, battle_party: ExternRef, ) -> WasmResult> { - let value = get_value_call_getter!(battle_party.party(), env); - wasm_ok(ExternRef::::func_new(&env, value.as_ref())) + let battle_party = get_value_arc!(battle_party, env); + wasm_ok(ExternRef::::func_new(&env, battle_party.party().into())) } } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/battle_random.rs b/src/script_implementations/wasm/export_registry/dynamic_data/battle_random.rs index fa1894e..a0ed4db 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/battle_random.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/battle_random.rs @@ -1,21 +1,18 @@ use crate::dynamic_data::BattleRandom; -use crate::script_implementations::wasm::export_registry::wasm_result::{ - get_value, get_value_call_getter, try_wasm, wasm_ok, -}; +use crate::script_implementations::wasm::export_registry::wasm_result::{get_value_arc, try_wasm, wasm_ok}; use crate::script_implementations::wasm::export_registry::{register, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use wasmer::FunctionEnvMut; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use anyhow_ext::Context; register! { fn battle_random_get( env: FunctionEnvMut, battle_random: ExternRef, ) -> WasmResult { - let v = get_value_call_getter!(battle_random.get(), env); - let v = try_wasm!(v, env); + let v = get_value_arc!(battle_random, env); + let v = try_wasm!(v.get(), env); wasm_ok(v) } fn battle_random_get_max( @@ -23,7 +20,7 @@ register! { battle_random: ExternRef, max: i32 ) -> WasmResult { - let battle_random = get_value!(battle_random, env); + let battle_random = get_value_arc!(battle_random, env); let v = try_wasm!(battle_random.get_max(max), env); wasm_ok(v) } @@ -33,7 +30,7 @@ register! { min: i32, max: i32 ) -> WasmResult { - let battle_random = get_value!(battle_random, env); + let battle_random = get_value_arc!(battle_random, env); let v = try_wasm!(battle_random.get_between(min, max), env); wasm_ok(v) } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/battle_side.rs b/src/script_implementations/wasm/export_registry/dynamic_data/battle_side.rs index 8089084..9226130 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/battle_side.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/battle_side.rs @@ -7,7 +7,6 @@ use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script::WebAssemblyScript; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use anyhow::anyhow; -use anyhow_ext::Context; use std::ffi::CStr; use wasmer::FunctionEnvMut; @@ -46,7 +45,7 @@ register! { ) -> WasmResult> { let value = get_value_call_getter!(side.battle(), env); let value = try_wasm!(value, env); - wasm_ok(ExternRef::func_new(&env, &value)) + wasm_ok(ExternRef::func_new(&env, (&value).into())) } fn battleside_get_pokemon( @@ -55,11 +54,11 @@ register! { index: u32 ) -> WasmResult> { let side = get_value!(side, env); - wasm_ok(if let Some(Some(p)) = side.pokemon().get(index as usize) { - ExternRef::::func_new(&env, p.as_ref()) + let x = wasm_ok(if let Some(Some(p)) = side.pokemon().get(index as usize) { + ExternRef::::func_new(&env, p.into()) } else { ExternRef::null() - }) + }); x } fn battle_side_get_has_fled_battle( @@ -99,13 +98,13 @@ register! { side: ExternRef, script_ptr: u32 ) -> WasmResult { - let side : &BattleSide = get_value!(side, env); + let side = get_value!(side, env); unsafe { let e = env.data().data(); let name_func = try_wasm!(e.script_function_cache().script_get_name(&e), env); let name_ptr = try_wasm!(name_func.call(&mut e.store_mut(), script_ptr), env); let c_name: &CStr = CStr::from_ptr(e.get_raw_pointer(name_ptr)); - let script = try_wasm!(e.setup_script(script_ptr, ScriptCategory::Side, &c_name.as_ref().into(), side.into()), env); + let script = try_wasm!(e.setup_script(script_ptr, ScriptCategory::Side, &c_name.as_ref().into(), (&side).into()), env); try_wasm!(e.script_function_cache().dealloc_cstring(&e, name_ptr), env); if let Some(script) = script { diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/choice_queue.rs b/src/script_implementations/wasm/export_registry/dynamic_data/choice_queue.rs index 2b5a57e..c2de197 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/choice_queue.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/choice_queue.rs @@ -1,8 +1,8 @@ use crate::dynamic_data::{ChoiceQueue, Pokemon}; +use crate::script_implementations::wasm::export_registry::wasm_result::get_value_arc; use crate::script_implementations::wasm::export_registry::{get_value, register, try_wasm, wasm_ok, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use anyhow_ext::Context; use wasmer::FunctionEnvMut; register! { @@ -11,9 +11,9 @@ register! { battle_random: ExternRef, pokemon: ExternRef ) -> WasmResult { - let battle_random = get_value!(battle_random, env); + let battle_random = get_value_arc!(battle_random, env); let pokemon = get_value!(pokemon, env); - let res = try_wasm!(battle_random.move_pokemon_choice_next(pokemon), env); + let res = try_wasm!(battle_random.move_pokemon_choice_next(&pokemon), env); wasm_ok(u8::from(res)) } } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/executing_move.rs b/src/script_implementations/wasm/export_registry/dynamic_data/executing_move.rs index 68aa902..8ed40f8 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/executing_move.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/executing_move.rs @@ -1,13 +1,10 @@ use crate::dynamic_data::{ExecutingMove, HitData, LearnedMove, Pokemon}; use crate::script_implementations::wasm::export_registry::wasm_result::get_value_arc; -use crate::script_implementations::wasm::export_registry::{ - get_value, get_value_call_getter, register, try_wasm, wasm_ok, WasmResult, -}; +use crate::script_implementations::wasm::export_registry::{get_value, register, try_wasm, wasm_ok, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script::WebAssemblyScript; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::MoveData; -use anyhow_ext::Context; use wasmer::FunctionEnvMut; register! { @@ -15,31 +12,32 @@ register! { env: FunctionEnvMut, executing_move: ExternRef, ) -> WasmResult> { - let user = get_value_call_getter!(executing_move.user(), env); - wasm_ok(ExternRef::::func_new(&env, user.as_ref())) + let executing_move = get_value_arc!(executing_move, env); + wasm_ok(ExternRef::::func_new(&env, executing_move.user().into())) } fn executing_move_get_use_move( env: FunctionEnvMut, executing_move: ExternRef, ) -> WasmResult> { - let use_move = get_value_call_getter!(executing_move.use_move(), env); - wasm_ok(ExternRef::::func_new(&env, use_move)) + let executing_move = get_value_arc!(executing_move, env); + wasm_ok(ExternRef::::func_new(&env, executing_move.use_move().into())) } fn executing_move_get_chosen_move( env: FunctionEnvMut, executing_move: ExternRef, ) -> WasmResult> { - let chosen_move = get_value_call_getter!(executing_move.chosen_move(), env); - wasm_ok(ExternRef::::func_new(&env, chosen_move.as_ref())) + let executing_move = get_value_arc!(executing_move, env); + wasm_ok(ExternRef::::func_new(&env, executing_move.chosen_move().into())) } fn executing_move_get_number_of_hits( env: FunctionEnvMut, executing_move: ExternRef, ) -> WasmResult { - wasm_ok(get_value_call_getter!(executing_move.number_of_hits(), env)) + let executing_move = get_value_arc!(executing_move, env); + wasm_ok(executing_move.number_of_hits()) } fn executing_move_get_hit_data( @@ -48,17 +46,18 @@ register! { target: ExternRef, hit: u8 ) -> WasmResult> { - let executing_move = get_value!(executing_move, env); - let target = get_value_arc!(target, env); + let executing_move = get_value_arc!(executing_move, env); + let target = get_value!(target, env); let hit_data = try_wasm!(executing_move.get_hit_data(&target, hit), env); - wasm_ok(ExternRef::::func_new(&env, hit_data)) + wasm_ok(ExternRef::::func_new(&env, hit_data.into())) } fn executing_move_get_number_of_targets( env: FunctionEnvMut, executing_move: ExternRef, ) -> WasmResult { - wasm_ok(get_value_call_getter!(executing_move.target_count(), env) as u32) + let executing_move = get_value_arc!(executing_move, env); + wasm_ok(executing_move.target_count() as u32) } fn executing_move_is_pokemon_target( @@ -66,8 +65,8 @@ register! { executing_move: ExternRef, pokemon: ExternRef ) -> WasmResult { - let executing_move = get_value!(executing_move, env); - let pokemon = get_value_arc!(pokemon, env); + let executing_move = get_value_arc!(executing_move, env); + let pokemon = get_value!(pokemon, env); wasm_ok(if executing_move.is_pokemon_target(&pokemon) { 1 } else { 0 }) } @@ -77,7 +76,7 @@ register! { env: FunctionEnvMut, executing_move: ExternRef, ) -> WasmResult { - let executing_move = get_value!(executing_move, env); + let executing_move = get_value_arc!(executing_move, env); let script = executing_move.script(); if script.is_any() { let script = try_wasm!(script.get_as::(), env); diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/hit_data.rs b/src/script_implementations/wasm/export_registry/dynamic_data/hit_data.rs index c42a37d..5d74e87 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/hit_data.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/hit_data.rs @@ -1,11 +1,10 @@ use crate::dynamic_data::HitData; use crate::script_implementations::wasm::export_registry::wasm_result::{ - get_value_call_getter, get_value_void, wasm_ok, WasmVoidResultExtension, + get_value_arc, get_value_arc_void, wasm_ok, WasmVoidResultExtension, }; use crate::script_implementations::wasm::export_registry::{register, WasmResult, WasmVoidResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use anyhow_ext::Context; use wasmer::FunctionEnvMut; register! { @@ -13,22 +12,23 @@ register! { env: FunctionEnvMut, hit: ExternRef, ) -> WasmResult { - wasm_ok(get_value_call_getter!(hit.damage(), env)) + let hit = get_value_arc!(hit, env); + wasm_ok(hit.damage()) } fn hit_data_is_critical( env: FunctionEnvMut, hit: ExternRef, ) -> WasmResult { - let is_critical = get_value_call_getter!(hit.is_critical(), env); - wasm_ok(u8::from(is_critical)) + let hit = get_value_arc!(hit, env); + wasm_ok(u8::from(hit.is_critical())) } fn hit_data_fail( env: FunctionEnvMut, hit: ExternRef, ) -> WasmVoidResult { - let hit = get_value_void!(hit, env); + let hit = get_value_arc_void!(hit, env); hit.fail(); WasmVoidResult::ok() } @@ -37,21 +37,24 @@ register! { env: FunctionEnvMut, hit: ExternRef, ) -> WasmResult { - wasm_ok(get_value_call_getter!(hit.base_power(), env)) + let hit = get_value_arc!(hit, env); + wasm_ok(hit.base_power()) } fn hit_data_get_effectiveness( env: FunctionEnvMut, hit: ExternRef, ) -> WasmResult { - wasm_ok(get_value_call_getter!(hit.effectiveness(), env)) + let hit = get_value_arc!(hit, env); + wasm_ok(hit.effectiveness()) } fn hit_data_get_move_type( env: FunctionEnvMut, hit: ExternRef, ) -> WasmResult { - wasm_ok(get_value_call_getter!(hit.move_type(), env).into()) + let hit = get_value_arc!(hit, env); + wasm_ok(hit.move_type().into()) } fn hit_data_set_critical( @@ -59,7 +62,7 @@ register! { hit: ExternRef, value: u8 ) -> WasmVoidResult { - let hit = get_value_void!(hit, env); + let hit = get_value_arc_void!(hit, env); hit.set_critical(value == 1); WasmVoidResult::ok() } @@ -69,7 +72,7 @@ register! { hit: ExternRef, effectiveness: f32 ) -> WasmVoidResult { - let hit = get_value_void!(hit, env); + let hit = get_value_arc_void!(hit, env); hit.set_effectiveness(effectiveness); WasmVoidResult::ok() } @@ -79,7 +82,7 @@ register! { hit: ExternRef, damage: u32 ) -> WasmVoidResult { - let hit = get_value_void!(hit, env); + let hit = get_value_arc_void!(hit, env); hit.set_damage(damage); WasmVoidResult::ok() } @@ -89,7 +92,7 @@ register! { hit: ExternRef, move_type: u8 ) -> WasmVoidResult { - let hit = get_value_void!(hit, env); + let hit = get_value_arc_void!(hit, env); hit.set_move_type(move_type.into()); WasmVoidResult::ok() } 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 3f1e39f..f593ddb 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs @@ -2,13 +2,12 @@ use std::intrinsics::transmute; use crate::dynamic_data::{LearnedMove, MoveLearnMethod}; use crate::script_implementations::wasm::export_registry::wasm_result::{ - get_value_call_getter, get_value_void, wasm_ok, WasmVoidResult, WasmVoidResultExtension, + get_value_arc, get_value_arc_void, wasm_ok, WasmVoidResult, WasmVoidResultExtension, }; use crate::script_implementations::wasm::export_registry::{register, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::MoveData; -use anyhow_ext::Context; use wasmer::FunctionEnvMut; register! { @@ -17,8 +16,8 @@ register! { turn_choice: ExternRef, ) -> WasmResult { unsafe { - let learn_method = get_value_call_getter!(turn_choice.learn_method(), &env); - wasm_ok(transmute::(learn_method)) + let learned_move = get_value_arc!(turn_choice, env); + wasm_ok(transmute::(learned_move.learn_method())) } } @@ -26,15 +25,15 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmResult> { - let move_data = get_value_call_getter!(turn_choice.move_data(), &env); - wasm_ok(ExternRef::::func_new(&env, move_data)) + let turn_choice = get_value_arc!(turn_choice, env); + wasm_ok(ExternRef::func_new(&env, turn_choice.move_data().into())) } fn learned_move_restore_all_uses( env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmVoidResult { - let turn_choice = get_value_void!(turn_choice, env); + let turn_choice = get_value_arc_void!(turn_choice, env); turn_choice.restore_all_uses(); WasmVoidResult::ok() } @@ -44,7 +43,7 @@ register! { turn_choice: ExternRef, amount: u8, ) -> WasmVoidResult { - let turn_choice = get_value_void!(turn_choice, env); + let turn_choice = get_value_arc_void!(turn_choice, env); turn_choice.restore_uses(amount); WasmVoidResult::ok() } 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 33db355..f617755 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs @@ -2,12 +2,10 @@ use crate::dynamic_data::{DynamicLibrary, ScriptOwnerData}; use crate::script_implementations::wasm::export_registry::register; use crate::script_implementations::wasm::export_registry::wasm_result::{try_wasm, wasm_ok, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; -use std::sync::atomic::Ordering; use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut}; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::StaticData; -use anyhow_ext::Context; /// The battle registration mod battle; @@ -37,25 +35,23 @@ register! { ) -> WasmResult> { let dynamic_lib = try_wasm!(dynamic_lib.value_func_arc(&env), env); let static_data = dynamic_lib.static_data(); - wasm_ok(ExternRef::::func_new(&env, static_data)) + wasm_ok(ExternRef::::func_new(&env, static_data.into())) } fn script_get_owner( env: FunctionEnvMut, script: u32, ) -> u32 { - unsafe { - let script = env.data().data().get_loaded_script(script); - if let Some(script) = script { - match script.get_owner() { - ScriptOwnerData::Pokemon(p) => env.data().data().get_extern_ref_index::(p.load(Ordering::Relaxed).as_ref().unwrap()) as u32, - ScriptOwnerData::BattleSide(p) => env.data().data().get_extern_ref_index::(p.load(Ordering::Relaxed).as_ref().unwrap()) as u32, - ScriptOwnerData::Battle(p) => env.data().data().get_extern_ref_index::(p.load(Ordering::Relaxed).as_ref().unwrap()) as u32, - ScriptOwnerData::None => 0, - } - } else { - 0 + let script = env.data().data().get_loaded_script(script); + if let Some(script) = script { + match script.get_owner() { + ScriptOwnerData::Pokemon(p) => env.data().data().get_extern_ref_index(p.into()) as u32, + ScriptOwnerData::BattleSide(p) => env.data().data().get_extern_ref_index(p.into()) as u32, + ScriptOwnerData::Battle(p) => env.data().data().get_extern_ref_index(p.into()) as u32, + ScriptOwnerData::None => 0, } + } else { + 0 } } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/party.rs b/src/script_implementations/wasm/export_registry/dynamic_data/party.rs index 797d3ab..742f5f8 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/party.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/party.rs @@ -1,9 +1,8 @@ use crate::dynamic_data::{Pokemon, PokemonParty}; use crate::script_implementations::wasm::export_registry::register; -use crate::script_implementations::wasm::export_registry::wasm_result::{get_value, wasm_ok, WasmResult}; +use crate::script_implementations::wasm::export_registry::wasm_result::{get_value_arc, wasm_ok, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use anyhow_ext::Context; use wasmer::FunctionEnvMut; register! { @@ -12,20 +11,20 @@ register! { party: ExternRef, index: u32 ) -> WasmResult> { - let party = get_value!(party, env); - wasm_ok( + let party = get_value_arc!(party, env); + let x = wasm_ok( if let Some(Some(v)) = &party.pokemon().get(index as usize) { - ExternRef::func_new(&env, v.as_ref()) + ExternRef::func_new(&env, v.into()) } else { ExternRef::null() - }) + }); x } fn party_get_length( env: FunctionEnvMut, party: ExternRef, ) -> WasmResult { - let party = get_value!(party, env); + let party = get_value_arc!(party, env); wasm_ok(party.length() as u32) } } 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 5a7cc12..5c4730b 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs @@ -3,8 +3,8 @@ use std::mem::transmute; use crate::defines::LevelInt; use crate::dynamic_data::{Battle, DynamicLibrary, LearnedMove, Pokemon, VolatileScriptsOwner}; use crate::script_implementations::wasm::export_registry::{ - get_value_arc, get_value_arc_void, get_value_call_getter, get_value_void, register, try_wasm, wasm_err, wasm_ok, - WasmResult, WasmVoidResult, WasmVoidResultExtension, + get_value, get_value_arc, get_value_arc_void, get_value_call_getter, get_value_void, register, try_wasm, wasm_err, + wasm_ok, WasmResult, WasmVoidResult, WasmVoidResultExtension, }; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script::WebAssemblyScript; @@ -13,7 +13,6 @@ use crate::static_data::{Ability, ClampedStatisticSet, Form, Nature, Species}; use crate::static_data::{Item, StatisticSet}; use crate::{ScriptCategory, VecExt}; use anyhow::anyhow; -use anyhow_ext::Context; use std::ffi::{c_char, CStr, CString}; use wasmer::FunctionEnvMut; @@ -22,56 +21,62 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult> { - let lib = get_value_call_getter!(pokemon.library(), env).clone(); - wasm_ok(ExternRef::::func_new(&env, &lib)) + let pokemon = get_value!(pokemon, env); + let lib = pokemon.library(); + wasm_ok(ExternRef::::func_new(&env, lib.into())) } fn pokemon_get_boosted_stats( env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult>> { - let statistic_set = get_value_call_getter!(pokemon.boosted_stats(), env).clone(); - wasm_ok(ExternRef::>::func_new(&env, statistic_set)) + let pokemon = get_value!(pokemon, env); + let statistic_set = pokemon.boosted_stats(); + wasm_ok(ExternRef::>::func_new(&env, statistic_set.into())) } fn pokemon_get_flat_stats( env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult>> { - let statistic_set = get_value_call_getter!(pokemon.flat_stats(), env).clone(); - wasm_ok(ExternRef::>::func_new(&env, statistic_set)) + let pokemon = get_value!(pokemon, env); + let statistic_set = pokemon.flat_stats(); + wasm_ok(ExternRef::>::func_new(&env, statistic_set.into())) } fn pokemon_get_stat_boosts( env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult>> { - let statistic_set = get_value_call_getter!(pokemon.stat_boosts(), env).clone(); - wasm_ok(ExternRef::>::func_new(&env, statistic_set)) + let pokemon = get_value!(pokemon, env); + let statistic_set = pokemon.stat_boosts(); + wasm_ok(ExternRef::>::func_new(&env, statistic_set.into())) } fn pokemon_get_individual_values( env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult>> { - let statistic_set = get_value_call_getter!(pokemon.individual_values(), env).clone(); - wasm_ok(ExternRef::>::func_new(&env, statistic_set)) + let pokemon = get_value!(pokemon, env); + let statistic_set = pokemon.individual_values(); + wasm_ok(ExternRef::>::func_new(&env, statistic_set.into())) } fn pokemon_get_effort_values( env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult>> { - let statistic_set = get_value_call_getter!(pokemon.effort_values(), env).clone(); - wasm_ok(ExternRef::>::func_new(&env, statistic_set)) + let pokemon = get_value!(pokemon, env); + let statistic_set = pokemon.effort_values(); + wasm_ok(ExternRef::>::func_new(&env, statistic_set.into())) } fn pokemon_get_species( env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult> { - let species = get_value_call_getter!(pokemon.species(), env).clone(); - wasm_ok(ExternRef::::func_new(&env, &species)) + let species = get_value_call_getter!(pokemon.species(), env); + wasm_ok(ExternRef::::func_new(&env, (&species).into())) } fn pokemon_get_weight( @@ -134,11 +139,11 @@ register! { pokemon: ExternRef, index: u32 ) -> WasmResult> { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let read_lock = pokemon.learned_moves().read(); let mv = read_lock.get(index as usize); wasm_ok(if let Some(Some(mv)) = mv { - ExternRef::::func_new(&env, mv) + ExternRef::::func_new(&env, mv.into()) } else{ ExternRef::::null() @@ -153,7 +158,7 @@ register! { self_inflicted: u8 ) -> WasmResult { unsafe{ - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let res = try_wasm!(pokemon.change_stat_boost(transmute(stat), amount, self_inflicted == 1), env); wasm_ok(u8::from(res)) } @@ -163,11 +168,9 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult> { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); wasm_ok(if let Some(battle) = pokemon.get_battle() { - let r = ExternRef::func_new(&env, &battle); - std::mem::forget(battle); - r + ExternRef::func_new(&env, (&battle).into()) } else { ExternRef::null() }) @@ -177,7 +180,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); wasm_ok(if let Some(i) = pokemon.get_battle_index() { i } else { @@ -189,7 +192,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); wasm_ok(if let Some(i) = pokemon.get_battle_side_index() { i } else { @@ -201,10 +204,10 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult> { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let read_lock = pokemon.held_item().read(); wasm_ok(if let Some(item) = read_lock.as_ref() { - ExternRef::::func_new(&env, item.as_ref()) + ExternRef::::func_new(&env, item.into()) } else { ExternRef::::null() }) @@ -217,7 +220,7 @@ register! { ) -> WasmResult { let name : *mut c_char = env.data().data().get_raw_pointer(name); let name = unsafe { CStr::from_ptr(name) }; - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); wasm_ok(u8::from(pokemon.has_held_item(&name.into()))) } @@ -227,7 +230,7 @@ register! { amount: u32, allow_revive: u8 ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); wasm_ok(u8::from(pokemon.heal(amount, allow_revive == 1))) } @@ -248,14 +251,16 @@ register! { pokemon: ExternRef, ) -> WasmResult> { let active_ability = get_value_call_getter!(pokemon.active_ability(), env); - wasm_ok(ExternRef::::func_new(&env, &active_ability)) + let active_ability = try_wasm!(active_ability, env); + wasm_ok(ExternRef::::func_new(&env, (&active_ability).into())) } fn pokemon_get_real_ability( env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult { - let index = get_value_call_getter!(pokemon.real_ability(), env); + let pokemon = get_value!(pokemon, env); + let index = pokemon.real_ability(); let t: (u8, u8) = (if index.hidden { 1 } else { 0 }, index.index); let r: u16 = unsafe { transmute(t) }; wasm_ok(r) @@ -265,7 +270,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); wasm_ok(if pokemon.is_ability_overriden() { 1 } else { 0 }) } @@ -273,7 +278,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); wasm_ok(if pokemon.allowed_experience_gain() { 1 } else { 0 }) } @@ -281,7 +286,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); wasm_ok(if pokemon.is_usable() { 1 } else { 0 }) } @@ -292,10 +297,10 @@ register! { item: ExternRef ) -> WasmResult> { let item = get_value_arc!(item, env); - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let old_item = pokemon.set_held_item(&item); wasm_ok(if let Some(old_item) = old_item { - ExternRef::::func_new(&env, &old_item) + ExternRef::::func_new(&env, (&old_item).into()) } else { ExternRef::null() }) @@ -305,10 +310,10 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult> { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let old_item = pokemon.remove_held_item(); wasm_ok(if let Some(old_item) = old_item { - ExternRef::::func_new(&env, &old_item) + ExternRef::::func_new(&env, (&old_item).into()) } else { ExternRef::::null() }) @@ -318,7 +323,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let res = try_wasm!(pokemon.consume_held_item(), env); wasm_ok(if res { 1 } else { 0 }) } @@ -327,7 +332,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let len = pokemon.types().len(); wasm_ok(len as u32) } @@ -337,7 +342,7 @@ register! { pokemon: ExternRef, index: u32 ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let types = pokemon.types(); let type_v = types.get_res(index as usize); match type_v { @@ -351,7 +356,7 @@ register! { pokemon: ExternRef, t: u8 ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); wasm_ok(if pokemon.types().contains(&t.into()) { 1 } else { 0 }) } @@ -398,8 +403,9 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult> { - let nature = get_value_call_getter!(pokemon.nature(), env); - wasm_ok(ExternRef::::func_new(&env, nature)) + let pokemon = get_value!(pokemon, env); + let nature = pokemon.nature(); + wasm_ok(ExternRef::::func_new(&env, nature.into())) } fn pokemon_get_form( @@ -407,7 +413,7 @@ register! { pokemon: ExternRef, ) -> WasmResult> { let form = get_value_call_getter!(pokemon.form(), env); - wasm_ok(ExternRef::::func_new(&env, &form)) + wasm_ok(ExternRef::::func_new(&env, (&form).into())) } fn pokemon_get_display_species( @@ -415,7 +421,7 @@ register! { pokemon: ExternRef, ) -> WasmResult> { let display_species = get_value_call_getter!(pokemon.display_species(), env); - wasm_ok(ExternRef::::func_new(&env, &display_species)) + wasm_ok(ExternRef::::func_new(&env, (&display_species).into())) } fn pokemon_get_display_form( @@ -423,7 +429,7 @@ register! { pokemon: ExternRef, ) -> WasmResult> { let display_form = get_value_call_getter!(pokemon.display_form(), env); - wasm_ok(ExternRef::::func_new(&env, &display_form)) + wasm_ok(ExternRef::::func_new(&env, (&display_form).into())) } fn pokemon_get_level( @@ -458,7 +464,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let nickname = pokemon.nickname(); if let Some(nickname) = nickname { let nickname: CString = match CString::new(nickname.as_str()) { @@ -482,7 +488,7 @@ register! { name_ptr: u32 ) -> WasmResult { unsafe { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let c_name = CStr::from_ptr(env.data().data().get_raw_pointer(name_ptr)); let script = try_wasm!(pokemon.add_volatile_script(&c_name.as_ref().into()), env); if let Some(script) = script { @@ -499,7 +505,7 @@ register! { pokemon: ExternRef, script_ptr: u32 ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); unsafe{ let env_data = env.data().data(); let name_ptr = match try_wasm!(env_data.script_function_cache().script_get_name(&env_data), env).call(&mut env_data.store_mut(), script_ptr){ @@ -507,14 +513,14 @@ register! { Err(e) => return wasm_err::(e.into(), &env) }; let c_name: &CStr = CStr::from_ptr(env_data.get_raw_pointer(name_ptr)); - let script = try_wasm!(env_data.setup_script(script_ptr, ScriptCategory::Pokemon, &c_name.as_ref().into(), pokemon.as_ref().into()), env); + let script = try_wasm!(env_data.setup_script(script_ptr, ScriptCategory::Pokemon, &c_name.as_ref().into(), (&pokemon).into()), env); try_wasm!(env_data.script_function_cache().dealloc_cstring(&env_data, name_ptr), env); if let Some(script) = script { let script = try_wasm!(pokemon.add_volatile_script_with_script(script), env); let s = match script.as_ref() { Some(s) => s, - None => return wasm_err::(anyhow!("Unable to get script").into(), &env) + None => return wasm_err::(anyhow!("Unable to get script"), &env) }; let s = try_wasm!(s.get_as::(), env); wasm_ok(s.get_wasm_pointer()) @@ -531,7 +537,7 @@ register! { ) -> WasmResult { unsafe { let c_name = CStr::from_ptr(env.data().data().get_raw_pointer(name_ptr)); - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); wasm_ok(u8::from(pokemon.has_volatile_script(&c_name.as_ref().into()))) } } @@ -543,7 +549,7 @@ register! { ) -> WasmResult { unsafe { let c_name = CStr::from_ptr(env.data().data().get_raw_pointer(name_ptr)); - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let script = pokemon.get_volatile_script(&c_name.as_ref().into()); wasm_ok(if let Some(script) = script { let script = try_wasm!(script.get_as::(), env); @@ -564,7 +570,7 @@ register! { let pokemon = get_value_void!(pokemon, env); match pokemon.remove_volatile_script(&c_name.as_ref().into()) { Ok(_) => WasmVoidResult::ok(), - Err(e) => WasmVoidResult::err(e.into(), &env) + Err(e) => WasmVoidResult::err(e, &env) } } } @@ -573,7 +579,7 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, ) -> WasmResult { - let pokemon = get_value_arc!(pokemon, env); + let pokemon = get_value!(pokemon, env); let script = pokemon.ability_script(); if script.is_any() { let script = try_wasm!(script.get_as::(), env); 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 ac73c72..565b220 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs @@ -1,14 +1,12 @@ use crate::dynamic_data::{LearnedMove, Pokemon, TurnChoice}; -use crate::script_implementations::wasm::export_registry::wasm_result::{ - get_value, get_value_call_getter, get_value_void, wasm_err, -}; +use crate::script_implementations::wasm::export_registry::wasm_result::{get_value_arc, get_value_arc_void, wasm_err}; use crate::script_implementations::wasm::export_registry::{ register, wasm_ok, WasmResult, WasmVoidResult, WasmVoidResultExtension, }; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script::WebAssemblyScript; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use anyhow_ext::Context; +use std::ops::Deref; use wasmer::FunctionEnvMut; register! { @@ -17,16 +15,16 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmResult> { - let user = get_value_call_getter!(turn_choice.user(), &env); - wasm_ok(ExternRef::func_new(&env, user)) + let turn_choice = get_value_arc!(turn_choice, env); + wasm_ok(ExternRef::func_new(&env, turn_choice.user().into())) } fn turn_choice_get_kind( env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmResult { - let turn_choice = get_value!(turn_choice, env); - wasm_ok(match turn_choice { + let turn_choice = get_value_arc!(turn_choice, env); + wasm_ok(match turn_choice.deref() { TurnChoice::Move(_) => 0, TurnChoice::Item(_) => 1, TurnChoice::Switch(_) => 2, @@ -39,21 +37,23 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmResult { - wasm_ok(get_value_call_getter!(turn_choice.speed(), &env)) + let turn_choice = get_value_arc!(turn_choice, env); + wasm_ok(turn_choice.speed()) } fn turn_choice_has_failed( env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmResult { - wasm_ok(if get_value_call_getter!(turn_choice.has_failed(), &env) { 1 } else { 0 }) + let turn_choice = get_value_arc!(turn_choice, env); + wasm_ok(if turn_choice.has_failed() { 1 } else { 0 }) } fn turn_choice_fail( env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmVoidResult { - let turn_choice = get_value_void!(turn_choice, env); + let turn_choice = get_value_arc_void!(turn_choice, env); turn_choice.fail(); WasmVoidResult::ok() } @@ -63,9 +63,9 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmResult { - return match turn_choice.value_func(&env) { + return match turn_choice.value_func_arc(&env) { Ok(v) => { - match v { + match v.deref() { TurnChoice::Move(m) => wasm_ok(m.priority()), _ => wasm_err::(anyhow_ext::anyhow!("Invalid turn choice"), &env) } @@ -78,10 +78,10 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmResult> { - return match turn_choice.value_func(&env) { + return match turn_choice.value_func_arc(&env) { Ok(v) => { - match v { - TurnChoice::Move(m) => wasm_ok(ExternRef::::func_new(&env, m.used_move())), + match v.deref() { + TurnChoice::Move(m) => wasm_ok(ExternRef::func_new(&env, m.used_move().into())), _ => wasm_err::>(anyhow_ext::anyhow!("Invalid turn choice"), &env) } }, @@ -93,9 +93,9 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmResult { - return match turn_choice.value_func(&env) { + return match turn_choice.value_func_arc(&env) { Ok(v) => { - match v { + match v.deref() { TurnChoice::Move(m) => wasm_ok(m.target_side()), _ => wasm_err::(anyhow_ext::anyhow!("Invalid turn choice"), &env) } @@ -108,9 +108,9 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmResult { - return match turn_choice.value_func(&env) { + return match turn_choice.value_func_arc(&env) { Ok(v) => { - match v { + match v.deref() { TurnChoice::Move(m) => wasm_ok(m.target_index()), _ => wasm_err::(anyhow_ext::anyhow!("Invalid turn choice"), &env) } @@ -123,9 +123,9 @@ register! { env: FunctionEnvMut, turn_choice: ExternRef, ) -> WasmResult { - return match turn_choice.value_func(&env) { + return match turn_choice.value_func_arc(&env) { Ok(v) => { - match v { + match v.deref() { TurnChoice::Move(d) => { if let Some(script) = d.script().get() { let read_lock = script.read(); diff --git a/src/script_implementations/wasm/export_registry/mod.rs b/src/script_implementations/wasm/export_registry/mod.rs index 97d89ce..b611054 100755 --- a/src/script_implementations/wasm/export_registry/mod.rs +++ b/src/script_implementations/wasm/export_registry/mod.rs @@ -1,21 +1,25 @@ use anyhow_ext::anyhow; use std::ffi::{c_char, CStr, CString}; use std::mem::{align_of, forget}; +use std::ops::Deref; use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::EffectParameter; use crate::StringKey; -use anyhow_ext::Context; /// Dynamic data registration mod dynamic_data; /// Static data registration mod static_data; +/// Handling for opaque handles passed to WASM mod wasm_object; +/// Result types for WASM mod wasm_result; +#[doc(inline)] +pub(super) use wasm_object::*; #[doc(inline)] pub use wasm_result::*; @@ -71,7 +75,6 @@ pub(crate) fn register_webassembly_funcs( ) { register_func_with_env!(imports, store, _print, env); register_func_with_env!(imports, store, _error, env); - register_func_with_env!(imports, store, _vec_extern_ref_get_value, env); static_data::register(imports, store, env); dynamic_data::register(imports, store, env); @@ -90,7 +93,9 @@ fn _print(env: FunctionEnvMut, p: u32) { unsafe { let mem: *mut c_char = env.data().data().get_raw_pointer(p); let s = CStr::from_ptr(mem); - println!("{}", s.to_str().unwrap()); + if let Ok(v) = s.to_str() { + println!("{}", v); + } } } @@ -100,21 +105,13 @@ fn _error(env: FunctionEnvMut, message: u32) { unsafe { let mem: *const c_char = env.data().data().get_raw_pointer(message); let message_str = CStr::from_ptr(mem); - panic!("WASM Error: {}", message_str.to_str().unwrap()); + #[allow(clippy::panic)] + if let Ok(v) = message_str.to_str() { + panic!("WASM Error: {}", v); + } } } -/// Get a single item from an earlier passed VecExternRef -fn _vec_extern_ref_get_value(env: FunctionEnvMut, reference: u32, index: u32) -> WasmResult { - let res = try_wasm!( - env.data() - .data() - .get_extern_vec_ref_extern_ref(reference as usize, index as usize), - env - ); - wasm_ok(res as u32) -} - /// Gets the hash value of a StringKey. fn string_key_get_hash(env: FunctionEnvMut, string_key: ExternRef) -> WasmResult { let value = get_value!(string_key, env); @@ -123,7 +120,7 @@ fn string_key_get_hash(env: FunctionEnvMut, string_key: ExternRe /// 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, u32) { +fn string_key_get_str(env: FunctionEnvMut, string_key: ExternRef) -> WasmResult { let value = get_value!(string_key, env); let string_key = value.str(); let wasm_string_ptr = try_wasm!( @@ -147,8 +144,8 @@ fn effect_parameter_get_type( env: FunctionEnvMut, parameter: ExternRef, ) -> WasmResult { - let value = get_value!(parameter, env); - wasm_ok(match value { + let value = get_value_arc!(parameter, env); + wasm_ok(match value.deref() { EffectParameter::Bool(_, _) => 1, EffectParameter::Int(_, _) => 2, EffectParameter::Float(_, _) => 3, @@ -161,8 +158,8 @@ fn effect_parameter_as_bool( env: FunctionEnvMut, parameter: ExternRef, ) -> WasmResult { - let value = get_value!(parameter, env); - match value { + let value = get_value_arc!(parameter, env); + match value.deref() { EffectParameter::Bool(_, b) => wasm_ok(>::from(*b)), _ => wasm_err::(anyhow!("Unexpected parameter type. Expected bool, got {}", value), &env), } @@ -173,8 +170,8 @@ fn effect_parameter_as_int( env: FunctionEnvMut, parameter: ExternRef, ) -> WasmResult { - let value = get_value!(parameter, env); - match value { + let value = get_value_arc!(parameter, env); + match value.deref() { EffectParameter::Int(_, i) => wasm_ok(*i), _ => wasm_err::(anyhow!("Unexpected parameter type. Expected int, got {}", value), &env), } @@ -185,8 +182,8 @@ fn effect_parameter_as_float( env: FunctionEnvMut, parameter: ExternRef, ) -> WasmResult { - let value = get_value!(parameter, env); - match value { + let value = get_value_arc!(parameter, env); + match value.deref() { EffectParameter::Float(_, f) => wasm_ok(*f), _ => wasm_err::( anyhow!("Unexpected parameter type. Expected float, got {}", value), @@ -200,9 +197,9 @@ fn effect_parameter_as_string( env: FunctionEnvMut, parameter: ExternRef, ) -> WasmResult> { - let value = get_value!(parameter, env); - match value { - EffectParameter::Float(_, s) => wasm_ok(ExternRef::::func_new(&env, s)), + let value = get_value_arc!(parameter, env); + match value.deref() { + EffectParameter::String(_, s) => wasm_ok(ExternRef::::func_new(&env, s.clone().into())), _ => wasm_err::>( anyhow!("Unexpected parameter type. Expected string, got {}", value), &env, diff --git a/src/script_implementations/wasm/export_registry/static_data/ability.rs b/src/script_implementations/wasm/export_registry/static_data/ability.rs index 4055a07..0571a32 100644 --- a/src/script_implementations/wasm/export_registry/static_data/ability.rs +++ b/src/script_implementations/wasm/export_registry/static_data/ability.rs @@ -1,6 +1,7 @@ -use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::export_registry::wasm_result::get_value_arc; use crate::script_implementations::wasm::export_registry::FunctionEnvMut; -use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; +use crate::script_implementations::wasm::export_registry::{register, try_wasm, wasm_ok, WasmResult}; +use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::{Ability, EffectParameter}; use crate::StringKey; @@ -10,25 +11,32 @@ register! { fn ability_get_name( env: FunctionEnvMut, ability: ExternRef -) -> ExternRef { - let ability = ability.value_func_arc(&env).unwrap(); - ExternRef::func_new(&env, ability.name()) +) -> WasmResult> { + let ability = get_value_arc!(ability, env); + wasm_ok(ExternRef::func_new(&env, ability.name().clone().into())) } fn ability_get_effect( env: FunctionEnvMut, ability: ExternRef -) -> ExternRef { - let ability = ability.value_func_arc(&env).unwrap(); - ExternRef::func_new(&env, ability.effect()) +) -> WasmResult> { + let ability = get_value_arc!(ability, env); + wasm_ok(ExternRef::func_new(&env, ability.effect().clone().into())) } fn ability_get_parameters( env: FunctionEnvMut, ability: ExternRef -) -> VecExternRef { - let ability = ability.value_func_arc(&env).unwrap(); - VecExternRef::new(env.data().data().as_ref(), ability.parameters()) +) -> WasmResult { + let ability = get_value_arc!(ability, env); + let parameters = ability.parameters(); + let mut vec : Vec = Vec::with_capacity(parameters.len()); + for parameter in parameters { + vec.push(ExternRef::::func_new(&env, parameter.into()).index() as u32); + } + let wasm_ptr = try_wasm!(env.data().data().copy_value_vec_to_wasm(&vec), env); + let r: u64 = unsafe { std::mem::transmute((wasm_ptr, vec.len() as u32)) }; + wasm_ok(r) } } diff --git a/src/script_implementations/wasm/export_registry/static_data/form.rs b/src/script_implementations/wasm/export_registry/static_data/form.rs index cf21193..aa94ffd 100644 --- a/src/script_implementations/wasm/export_registry/static_data/form.rs +++ b/src/script_implementations/wasm/export_registry/static_data/form.rs @@ -1,26 +1,25 @@ -use crate::script_implementations::wasm::export_registry::wasm_result::try_wasm; +use crate::script_implementations::wasm::export_registry::wasm_result::{get_value_arc, try_wasm}; use crate::script_implementations::wasm::export_registry::{register, wasm_ok, FunctionEnvMut, WasmResult}; -use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; +use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::{Form, StatisticSet}; use crate::StringKey; -use anyhow_ext::Context; register! { fn form_get_name( env: FunctionEnvMut, form: ExternRef -) -> ExternRef { - let form = form.value_func_arc(&env).unwrap(); - ExternRef::func_new(&env, form.name()) +) -> WasmResult> { + let form = get_value_arc!(form, env); + wasm_ok(ExternRef::func_new(&env, form.name().clone().into())) } fn form_get_types( env: FunctionEnvMut, form: ExternRef ) -> WasmResult { - let form = form.value_func_arc(&env).unwrap(); + let form = get_value_arc!(form, env); let vec = form.types(); let wasm_ptr = try_wasm!(env.data().data().copy_value_vec_to_wasm(vec), env); let r: u64 = unsafe { std::mem::transmute((wasm_ptr, vec.len() as u32)) }; @@ -30,54 +29,69 @@ fn form_get_types( fn form_get_height( env: FunctionEnvMut, form: ExternRef -) -> f32 { - form.value_func_arc(&env).unwrap().height() +) -> WasmResult { + wasm_ok(get_value_arc!(form, env).height()) } fn form_get_weight( env: FunctionEnvMut, form: ExternRef -) -> f32 { - form.value_func_arc(&env).unwrap().weight() +) -> WasmResult { + wasm_ok(get_value_arc!(form, env).weight()) } fn form_get_base_experience( env: FunctionEnvMut, form: ExternRef -) -> u32 { - form.value_func_arc(&env).unwrap().base_experience() +) -> WasmResult { + wasm_ok(get_value_arc!(form, env).base_experience()) } fn form_get_base_stats( env: FunctionEnvMut, form: ExternRef -) -> ExternRef> { - let form = form.value_func_arc(&env).unwrap(); - ExternRef::func_new(&env, form.base_stats()) +) -> WasmResult>> { + let form = get_value_arc!(form, env); + wasm_ok(ExternRef::func_new(&env, form.base_stats().into())) } fn form_get_abilities( env: FunctionEnvMut, form: ExternRef -) -> VecExternRef { - let form = form.value_func_arc(&env).unwrap(); - VecExternRef::new(env.data().data().as_ref(), form.abilities()) +) -> WasmResult { + let form = get_value_arc!(form, env); + let abilities = form.abilities(); + let mut ability_refs : Vec = Vec::with_capacity(abilities.len()); + for ability in abilities { + ability_refs.push(ExternRef::::func_new(&env, ability.clone().into()).index() as u32); + } + let wasm_ptr = try_wasm!(env.data().data().copy_value_vec_to_wasm(&ability_refs), env); + let r: u64 = unsafe { std::mem::transmute((wasm_ptr, ability_refs.len() as u32)) }; + wasm_ok(r) } fn form_get_hidden_abilities( env: FunctionEnvMut, form: ExternRef -) -> VecExternRef { - let form = form.value_func_arc(&env).unwrap(); - VecExternRef::new(env.data().data().as_ref(), form.hidden_abilities()) +) -> WasmResult { + let form = get_value_arc!(form, env); + let abilities = form.hidden_abilities(); + let mut ability_refs : Vec = Vec::with_capacity(abilities.len()); + for ability in abilities { + ability_refs.push(ExternRef::::func_new(&env, ability.clone().into()).index() as u32); + } + let wasm_ptr = try_wasm!(env.data().data().copy_value_vec_to_wasm(&ability_refs), env); + let r: u64 = unsafe { std::mem::transmute((wasm_ptr, ability_refs.len() as u32)) }; + wasm_ok(r) } fn form_has_flag_by_hash( env: FunctionEnvMut, form: ExternRef, flag_hash: u32, -) -> u8 { - if form.value_func_arc(&env).unwrap().has_flag_by_hash(flag_hash) { 1 } else { 0 } +) -> WasmResult { + let form = get_value_arc!(form, env); + wasm_ok(if form.has_flag_by_hash(flag_hash) { 1 } else { 0 }) } diff --git a/src/script_implementations/wasm/export_registry/static_data/item.rs b/src/script_implementations/wasm/export_registry/static_data/item.rs index be8c750..7cdfd2a 100755 --- a/src/script_implementations/wasm/export_registry/static_data/item.rs +++ b/src/script_implementations/wasm/export_registry/static_data/item.rs @@ -1,4 +1,5 @@ -use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::export_registry::wasm_result::{get_value, get_value_arc, wasm_ok}; +use crate::script_implementations::wasm::export_registry::{register, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::Item; @@ -12,60 +13,67 @@ register! { env: FunctionEnvMut, lib: ExternRef, string_key: ExternRef, - ) -> ExternRef { - let lib = lib.value_func_box(&env).unwrap(); - let m = lib.get(string_key.value_func(&env).unwrap()); - if let Some(v) = m { - ExternRef::func_new(&env, &v) + ) -> WasmResult> { + let lib = get_value_arc!(lib, env); + let string_key = get_value!(string_key, env); + let m = lib.get(&string_key); + wasm_ok(if let Some(v) = m { + ExternRef::func_new(&env, (&v).into()) } else { ExternRef::null() - } + }) } - fn item_library_get_item_by_hash(env: FunctionEnvMut, lib: ExternRef, hash: u32) -> ExternRef { - let lib = lib.value_func_box(&env).unwrap(); + fn item_library_get_item_by_hash(env: FunctionEnvMut, lib: ExternRef, hash: u32) -> WasmResult> { + let lib = get_value_arc!(lib, env); let m = lib.get_by_hash(hash); - if let Some(v) = m { - ExternRef::func_new(&env, &v) + wasm_ok(if let Some(v) = m { + ExternRef::func_new(&env, (&v).into()) } else { ExternRef::null() - } + }) } fn item_get_price( env: FunctionEnvMut, item: ExternRef, - ) -> i32 { - item.value_func_arc(&env).unwrap().price() + ) -> WasmResult { + let item = get_value_arc!(item, env); + wasm_ok(item.price()) } fn item_get_name( env: FunctionEnvMut, item: ExternRef, - ) -> ExternRef { - ExternRef::func_new(&env, item.value_func_arc(&env).unwrap().name()) + ) -> WasmResult> { + let item = get_value_arc!(item, env); + wasm_ok(ExternRef::func_new(&env, item.name().clone().into())) } fn item_get_category( env: FunctionEnvMut, item: ExternRef, - ) -> u8 { - unsafe { transmute(item.value_func_arc(&env).unwrap().category()) } + ) -> WasmResult { + let item = get_value_arc!(item, env); + unsafe { wasm_ok(transmute(item.category())) } } fn item_get_battle_category( env: FunctionEnvMut, item: ExternRef, - ) -> u8 { - unsafe { transmute(item.value_func_arc(&env).unwrap().battle_category()) } + ) -> WasmResult { + let item = get_value_arc!(item, env); + unsafe { wasm_ok(transmute(item.battle_category())) } } fn item_has_flag( env: FunctionEnvMut, item: ExternRef, key: ExternRef - ) -> u8 { - if item.value_func_arc(&env).unwrap().has_flag(key.value_func(&env).unwrap()) { 1 } else { 0 } + ) -> WasmResult { + let item = get_value_arc!(item, env); + let key = get_value!(key, env); + wasm_ok(if item.has_flag(&key) { 1 } else { 0 }) } } 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 769ba13..60d32d7 100755 --- a/src/script_implementations/wasm/export_registry/static_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/static_data/mod.rs @@ -3,7 +3,7 @@ use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut}; use crate::defines::LevelInt; use crate::script_implementations::wasm::export_registry::wasm_result::{ - get_value, get_value_void, wasm_ok, WasmResult, + get_value_arc, get_value_arc_void, wasm_ok, WasmResult, }; use crate::script_implementations::wasm::export_registry::{register, WasmVoidResult, WasmVoidResultExtension}; use crate::script_implementations::wasm::extern_ref::ExternRef; @@ -12,7 +12,6 @@ use crate::static_data::{ ItemLibrary, LibrarySettings, MoveLibrary, SpeciesLibrary, StaticData, StaticStatisticSet, StatisticSet, TypeLibrary, }; -use anyhow_ext::Context; /// Ability data registration mod ability; @@ -30,52 +29,59 @@ mod species; mod types; register! { - fn static_data_get_move_library(env: FunctionEnvMut, data_library: ExternRef) -> ExternRef { - ExternRef::func_new(&env, data_library.value_func_box(&env).unwrap().moves()) + fn static_data_get_move_library(env: FunctionEnvMut, data_library: ExternRef) -> WasmResult> { + let data_library = get_value_arc!(data_library, env); + wasm_ok(ExternRef::func_new(&env, data_library.moves().into())) } fn static_data_get_species_library( env: FunctionEnvMut, data_library: ExternRef, - ) -> ExternRef { - ExternRef::func_new(&env, data_library.value_func_box(&env).unwrap().species()) + ) -> WasmResult> { + let data_library = get_value_arc!(data_library, env); + wasm_ok(ExternRef::func_new(&env, data_library.species().into())) } - fn static_data_get_item_library(env: FunctionEnvMut, data_library: ExternRef) -> ExternRef { - ExternRef::func_new(&env, data_library.value_func_box(&env).unwrap().items()) + fn static_data_get_item_library(env: FunctionEnvMut, data_library: ExternRef) -> WasmResult> { + let data_library = get_value_arc!(data_library, env); + wasm_ok(ExternRef::func_new(&env, data_library.items().into())) } - fn static_data_get_type_library(env: FunctionEnvMut, data_library: ExternRef) -> ExternRef { - ExternRef::func_new(&env, data_library.value_func_box(&env).unwrap().types()) + fn static_data_get_type_library(env: FunctionEnvMut, data_library: ExternRef) -> WasmResult> { + let data_library = get_value_arc!(data_library, env); + wasm_ok(ExternRef::func_new(&env, data_library.types().into())) } fn static_data_get_library_settings( env: FunctionEnvMut, data_library: ExternRef, - ) -> ExternRef { - ExternRef::func_new(&env, data_library.value_func_box(&env).unwrap().settings()) + ) -> WasmResult> { + let data_library = get_value_arc!(data_library, env); + wasm_ok(ExternRef::func_new(&env, data_library.settings().into())) } - fn library_settings_get_maximum_level(env: FunctionEnvMut, data_library: ExternRef) -> LevelInt { - data_library.value_func_box(&env).unwrap().maximum_level() + fn library_settings_get_maximum_level(env: FunctionEnvMut, data_library: ExternRef) -> WasmResult { + let data_library = get_value_arc!(data_library, env); + wasm_ok(data_library.maximum_level()) } - fn static_statistics_set_get_stat(env: FunctionEnvMut, statistics_set: ExternRef>, stat: u8) -> u32 { + fn static_statistics_set_get_stat(env: FunctionEnvMut, statistics_set: ExternRef>, stat: u8) -> WasmResult { unsafe { - statistics_set.value_func(&env).unwrap().get_stat(transmute(stat)) as u32 + let statistics_set = get_value_arc!(statistics_set, env); + wasm_ok(statistics_set.get_stat(transmute(stat)) as u32) } } fn statistic_set_get(env: FunctionEnvMut, statistics_set: ExternRef>, stat: u8) -> WasmResult { unsafe { - let statistics_set = get_value!(statistics_set, env); + let statistics_set = get_value_arc!(statistics_set, env); wasm_ok(statistics_set.get_stat(transmute(stat)) as i64) } } fn statistic_set_set(env: FunctionEnvMut, statistics_set: ExternRef>, stat: u8, value: u64) -> WasmVoidResult { unsafe { - let statistics_set = get_value_void!(statistics_set, env); + let statistics_set = get_value_arc_void!(statistics_set, env); statistics_set.set_stat(transmute(stat), value as u32); WasmVoidResult::ok() } @@ -83,7 +89,7 @@ register! { fn statistic_set_increase_stat(env: FunctionEnvMut, statistics_set: ExternRef>, stat: u8, value: u64) -> WasmVoidResult { unsafe { - let statistics_set = get_value_void!(statistics_set, env); + let statistics_set = get_value_arc_void!(statistics_set, env); statistics_set.increase_stat(transmute(stat), value as u32); WasmVoidResult::ok() } @@ -91,7 +97,7 @@ register! { fn statistic_set_decrease_stat(env: FunctionEnvMut, statistics_set: ExternRef>, stat: u8, value: u64) -> WasmVoidResult { unsafe { - let statistics_set = get_value_void!(statistics_set, env); + let statistics_set = get_value_arc_void!(statistics_set, env); statistics_set.decrease_stat(transmute(stat), value as u32); WasmVoidResult::ok() } 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 aaec1cd..0197d5e 100755 --- a/src/script_implementations/wasm/export_registry/static_data/moves.rs +++ b/src/script_implementations/wasm/export_registry/static_data/moves.rs @@ -1,4 +1,5 @@ -use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::export_registry::wasm_result::{get_value, get_value_arc, wasm_ok}; +use crate::script_implementations::wasm::export_registry::{register, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::{MoveData, MoveLibrary}; @@ -10,55 +11,67 @@ fn move_library_get_move( env: FunctionEnvMut, lib: ExternRef, string_key: ExternRef, -) -> ExternRef { - let lib = lib.value_func_box(&env).unwrap(); - let m = lib.get(string_key.value_func(&env).unwrap()); - if let Some(v) = m { - ExternRef::func_new(&env, &v) +) -> WasmResult> { + let lib = get_value_arc!(lib, env); + let string_key = get_value!(string_key, env); + let m = lib.get(&string_key); + wasm_ok(if let Some(v) = m { + ExternRef::func_new(&env, (&v).into()) } else { ExternRef::null() - } + }) } -fn move_library_get_move_by_hash(env: FunctionEnvMut, lib: ExternRef, hash: u32) -> ExternRef { - let lib = lib.value_func_box(&env).unwrap(); +fn move_library_get_move_by_hash(env: FunctionEnvMut, lib: ExternRef, hash: u32) -> WasmResult> { + let lib = get_value_arc!(lib, env); let m = lib.get_by_hash(hash); - if let Some(v) = m { - ExternRef::func_new(&env, &v) + wasm_ok(if let Some(v) = m { + ExternRef::func_new(&env, (&v).into()) } else { ExternRef::null() - } + }) } -fn move_data_get_name(env: FunctionEnvMut, move_data: ExternRef) -> ExternRef { - ExternRef::func_new(&env, move_data.value_func_arc(&env).unwrap().name()) +fn move_data_get_name(env: FunctionEnvMut, move_data: ExternRef) ->WasmResult> { + let move_data = get_value_arc!(move_data, env); + wasm_ok(ExternRef::func_new(&env, move_data.name().clone().into())) } -fn move_data_get_type(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value_func_arc(&env).unwrap().move_type().into() +fn move_data_get_type(env: FunctionEnvMut, move_data: ExternRef) -> WasmResult { + let move_data = get_value_arc!(move_data, env); + wasm_ok(move_data.move_type().into()) } -fn move_data_get_category(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value_func_arc(&env).unwrap().category() as u8 +fn move_data_get_category(env: FunctionEnvMut, move_data: ExternRef) -> WasmResult { + let move_data = get_value_arc!(move_data, env); + wasm_ok(move_data.category() as u8) } -fn move_data_get_base_power(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value_func_arc(&env).unwrap().base_power() +fn move_data_get_base_power(env: FunctionEnvMut, move_data: ExternRef) -> WasmResult { + let move_data = get_value_arc!(move_data, env); + wasm_ok(move_data.base_power()) } -fn move_data_get_accuracy(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value_func_arc(&env).unwrap().accuracy() +fn move_data_get_accuracy(env: FunctionEnvMut, move_data: ExternRef) -> WasmResult { + let move_data = get_value_arc!(move_data, env); + wasm_ok(move_data.accuracy()) } -fn move_data_get_base_usages(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value_func_arc(&env).unwrap().base_usages() +fn move_data_get_base_usages(env: FunctionEnvMut, move_data: ExternRef) -> WasmResult { + let move_data = get_value_arc!(move_data, env); + wasm_ok(move_data.base_usages()) } -fn move_data_get_target(env: FunctionEnvMut, move_data: ExternRef) -> u8 { - move_data.value_func_arc(&env).unwrap().target() as u8 +fn move_data_get_target(env: FunctionEnvMut, move_data: ExternRef) -> WasmResult { + let move_data = get_value_arc!(move_data, env); + wasm_ok(move_data.target() as u8) } -fn move_data_get_priority(env: FunctionEnvMut, move_data: ExternRef) -> i8 { - move_data.value_func_arc(&env).unwrap().priority() +fn move_data_get_priority(env: FunctionEnvMut, move_data: ExternRef) -> WasmResult { + let move_data = get_value_arc!(move_data, env); + wasm_ok(move_data.priority()) } -fn move_data_has_flag(env: FunctionEnvMut, move_data: ExternRef, flag: ExternRef) -> u8 { - u8::from(move_data.value_func_arc(&env).unwrap().has_flag(flag.value_func(&env).unwrap())) +fn move_data_has_flag(env: FunctionEnvMut, move_data: ExternRef, flag: ExternRef) -> WasmResult { + let move_data = get_value_arc!(move_data, env); + let flag = get_value!(flag, env); + wasm_ok(u8::from(move_data.has_flag(&flag))) } -fn move_data_has_flag_by_hash(env: FunctionEnvMut, move_data: ExternRef, flag_hash: u32) -> u8 { - u8::from(move_data.value_func_arc(&env).unwrap().has_flag_by_hash(flag_hash)) +fn move_data_has_flag_by_hash(env: FunctionEnvMut, move_data: ExternRef, flag_hash: u32) -> WasmResult { + let move_data = get_value_arc!(move_data, env); + wasm_ok(u8::from(move_data.has_flag_by_hash(flag_hash))) } } diff --git a/src/script_implementations/wasm/export_registry/static_data/nature.rs b/src/script_implementations/wasm/export_registry/static_data/nature.rs index f370c49..081cd55 100644 --- a/src/script_implementations/wasm/export_registry/static_data/nature.rs +++ b/src/script_implementations/wasm/export_registry/static_data/nature.rs @@ -1,4 +1,5 @@ -use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::export_registry::wasm_result::{get_value_arc, wasm_ok}; +use crate::script_implementations::wasm::export_registry::{register, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::Nature; @@ -9,25 +10,29 @@ register! { fn nature_get_increase_stat( env: FunctionEnvMut, nature: ExternRef -) -> u8 { - unsafe { transmute(nature.value_func_box(&env).unwrap().increased_stat()) } +) -> WasmResult { + let nature = get_value_arc!(nature, env); + wasm_ok(unsafe { transmute(nature.increased_stat()) }) } fn nature_get_decrease_stat( env: FunctionEnvMut, nature: ExternRef -) -> u8 { - unsafe { transmute(nature.value_func_box(&env).unwrap().decreased_stat()) } +) -> WasmResult { + let nature = get_value_arc!(nature, env); + wasm_ok(unsafe { transmute(nature.decreased_stat()) }) } fn nature_get_increase_modifier( env: FunctionEnvMut, nature: ExternRef -) -> f32 { - nature.value_func_box(&env).unwrap().increased_modifier() +) -> WasmResult { + let nature = get_value_arc!(nature, env); + wasm_ok(nature.increased_modifier()) } fn nature_get_decrease_modifier( env: FunctionEnvMut, nature: ExternRef -) -> f32 { - nature.value_func_box(&env).unwrap().decreased_modifier() +) -> WasmResult { + let nature = get_value_arc!(nature, env); + wasm_ok(nature.decreased_modifier()) } } 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 5793c16..01d5447 100755 --- a/src/script_implementations/wasm/export_registry/static_data/species.rs +++ b/src/script_implementations/wasm/export_registry/static_data/species.rs @@ -1,4 +1,5 @@ -use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::export_registry::wasm_result::{get_value, get_value_arc, wasm_ok}; +use crate::script_implementations::wasm::export_registry::{register, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::SpeciesLibrary; @@ -12,73 +13,78 @@ fn species_library_get_species( env: FunctionEnvMut, lib: ExternRef, string_key: ExternRef, -) -> ExternRef { - let lib = lib.value_func_box(&env).unwrap(); - let m = lib.get(string_key.value_func(&env).unwrap()); - if let Some(v) = m { - ExternRef::func_new(&env, &v) +) -> WasmResult> { + let lib = get_value_arc!(lib, env); + let string_key = get_value!(string_key, env); + let m = lib.get(&string_key); + wasm_ok(if let Some(v) = m { + ExternRef::func_new(&env, (&v).into()) } else { ExternRef::null() - } + }) } fn species_get_capture_rate( env: FunctionEnvMut, species: ExternRef, -) -> u8 { - species.value_func_arc(&env).unwrap().capture_rate() +) -> WasmResult { + let species = get_value_arc!(species, env); + wasm_ok(species.capture_rate()) } fn species_get_growth_rate( env: FunctionEnvMut, species: ExternRef, -) -> ExternRef { - let species = species.value_func_arc(&env).unwrap(); - ExternRef::func_new(&env, species.growth_rate()) +) -> WasmResult> { + let species = get_value_arc!(species, env); + wasm_ok(ExternRef::func_new(&env, species.growth_rate().clone().into())) } fn species_get_gender_rate( env: FunctionEnvMut, species: ExternRef, -) -> f32 { - species.value_func_arc(&env).unwrap().gender_rate() +) -> WasmResult { + let species = get_value_arc!(species, env); + wasm_ok(species.gender_rate()) } fn species_get_name( env: FunctionEnvMut, species: ExternRef, -) -> ExternRef { - let species = species.value_func_arc(&env).unwrap(); - ExternRef::func_new(&env, species.name()) +) -> WasmResult> { + let species = get_value_arc!(species, env); + wasm_ok(ExternRef::func_new(&env, species.name().clone().into())) } fn species_get_id( env: FunctionEnvMut, species: ExternRef, -) -> u16 { - species.value_func_arc(&env).unwrap().id() +) -> WasmResult { + let species = get_value_arc!(species, env); + wasm_ok(species.id()) } fn species_get_form_by_hash( env: FunctionEnvMut, species: ExternRef, form_hash: u32 -) -> ExternRef { - let species = species.value_func_arc(&env).unwrap(); - let form = species.get_form_by_hash(form_hash); - if let Some(form) = form { - ExternRef::func_new(&env, &form) - } else { - ExternRef::null() - } +) -> WasmResult> { + let species = get_value_arc!(species, env); + let form = species.get_form_by_hash(form_hash); + wasm_ok(if let Some(form) = form { + ExternRef::func_new(&env, (&form).into()) + } else { + ExternRef::null() + }) } fn species_has_flag_by_hash( env: FunctionEnvMut, species: ExternRef, flag_hash: u32 -) -> u8 { - if species.value_func_arc(&env).unwrap().has_flag_by_hash(flag_hash) { 1 } else { 0 } +) -> WasmResult { + let species = get_value_arc!(species, env); + wasm_ok(if species.has_flag_by_hash(flag_hash) { 1 } else { 0 }) } diff --git a/src/script_implementations/wasm/export_registry/static_data/types.rs b/src/script_implementations/wasm/export_registry/static_data/types.rs index 7e0cc9d..9323a6d 100644 --- a/src/script_implementations/wasm/export_registry/static_data/types.rs +++ b/src/script_implementations/wasm/export_registry/static_data/types.rs @@ -1,4 +1,7 @@ use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::export_registry::wasm_result::{ + get_value, get_value_arc, try_wasm, wasm_ok, WasmResult, +}; use crate::script_implementations::wasm::export_registry::FunctionEnvMut; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; @@ -11,15 +14,15 @@ register! { env: FunctionEnvMut, lib: ExternRef, name: ExternRef - ) -> u8 { - let lib = lib.value_func_arc(&env).unwrap(); - let name = name.value_func(&env).unwrap(); - let type_id = lib.get_type_id(name); - if let Some(type_id) = type_id { + ) -> WasmResult { + let lib = get_value_arc!(lib, env); + let name = get_value!(name, env); + let type_id = lib.get_type_id(&name); + wasm_ok(if let Some(type_id) = type_id { type_id.into() } else { 0 - } + }) } fn type_library_get_single_effectiveness( @@ -27,9 +30,10 @@ register! { lib: ExternRef, attacking: u8, defending: u8 - ) -> f32 { - let lib = lib.value_func_arc(&env).unwrap(); - lib.get_single_effectiveness(attacking.into(), defending.into()).unwrap() + ) -> WasmResult { + let lib = get_value_arc!(lib, env); + let effectiveness = try_wasm!(lib.get_single_effectiveness(attacking.into(), defending.into()), env); + wasm_ok(effectiveness) } } diff --git a/src/script_implementations/wasm/export_registry/wasm_object.rs b/src/script_implementations/wasm/export_registry/wasm_object.rs index 8b13789..f098f5a 100644 --- a/src/script_implementations/wasm/export_registry/wasm_object.rs +++ b/src/script_implementations/wasm/export_registry/wasm_object.rs @@ -1 +1,449 @@ +use crate::dynamic_data::{Battle, Pokemon, WeakBattleReference, WeakBattleSideReference, WeakPokemonReference}; +use anyhow::anyhow; +use anyhow_ext::Result; +use std::hash::{Hash, Hasher}; +use std::sync::{Arc, Weak}; +#[derive(Clone)] +#[allow(clippy::missing_docs_in_private_items)] +pub(crate) enum WasmObject { + // Static data + StringKey(crate::StringKey), + EffectParameter(Weak), + + MoveData(Weak), + Species(Weak), + Form(Weak), + Item(Weak), + Ability(Weak), + Nature(Weak), + + StaticStatisticSetU16(Weak>), + StatisticSetU32(Weak>), + StatChangeStatisticSet(Weak>), + EVStatisticSet(Weak>), + IVStatisticSet(Weak>), + + // Static data libraries + MoveLibrary(Weak), + SpeciesLibrary(Weak), + ItemLibrary(Weak), + TypeLibrary(Weak), + LibrarySettings(Weak), + AbilityLibrary(Weak), + StaticData(Weak), + + // Dynamic data + Pokemon(WeakPokemonReference), + PokemonParty(Weak), + LearnedMove(Weak), + + // Dynamic data libraries + DynamicLibrary(Weak), + + // Battle data + Battle(WeakBattleReference), + ChoiceQueue(Weak), + BattleRandom(Weak), + BattleSide(WeakBattleSideReference), + BattleParty(Weak), + TurnChoice(Weak), + ExecutingMove(Weak), + HitData(Weak), +} + +/// Trait for converting from a [`WasmObject`] to a Rust object. +pub(crate) trait FromWasmObj +where + Self: Sized, +{ + /// Converts from a [`WasmObject`] to a Rust object. + fn from_wasm_obj(obj: WasmObject) -> Result; +} + +impl PartialEq for WasmObject { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (WasmObject::StringKey(s1), WasmObject::StringKey(s2)) => s1 == s2, + (WasmObject::EffectParameter(s1), WasmObject::EffectParameter(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::MoveData(s1), WasmObject::MoveData(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::Species(s1), WasmObject::Species(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::Form(s1), WasmObject::Form(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::Item(s1), WasmObject::Item(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::Ability(s1), WasmObject::Ability(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::Nature(s1), WasmObject::Nature(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::StaticStatisticSetU16(s1), WasmObject::StaticStatisticSetU16(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::StatisticSetU32(s1), WasmObject::StatisticSetU32(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::StatChangeStatisticSet(s1), WasmObject::StatChangeStatisticSet(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::EVStatisticSet(s1), WasmObject::EVStatisticSet(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::IVStatisticSet(s1), WasmObject::IVStatisticSet(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::MoveLibrary(s1), WasmObject::MoveLibrary(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::SpeciesLibrary(s1), WasmObject::SpeciesLibrary(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::ItemLibrary(s1), WasmObject::ItemLibrary(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::TypeLibrary(s1), WasmObject::TypeLibrary(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::LibrarySettings(s1), WasmObject::LibrarySettings(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::AbilityLibrary(s1), WasmObject::AbilityLibrary(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::StaticData(s1), WasmObject::StaticData(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::Pokemon(s1), WasmObject::Pokemon(s2)) => WeakPokemonReference::eq(s1, s2), + (WasmObject::PokemonParty(s1), WasmObject::PokemonParty(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::LearnedMove(s1), WasmObject::LearnedMove(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::DynamicLibrary(s1), WasmObject::DynamicLibrary(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::Battle(s1), WasmObject::Battle(s2)) => WeakBattleReference::eq(s1, s2), + (WasmObject::ChoiceQueue(s1), WasmObject::ChoiceQueue(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::BattleRandom(s1), WasmObject::BattleRandom(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::BattleSide(s1), WasmObject::BattleSide(s2)) => WeakBattleSideReference::eq(s1, s2), + (WasmObject::BattleParty(s1), WasmObject::BattleParty(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::TurnChoice(s1), WasmObject::TurnChoice(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::ExecutingMove(s1), WasmObject::ExecutingMove(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::HitData(s1), WasmObject::HitData(s2)) => Weak::ptr_eq(s1, s2), + + _ => false, + } + } +} + +impl Eq for WasmObject {} + +impl Hash for WasmObject { + fn hash(&self, state: &mut H) { + match self { + WasmObject::StringKey(s) => state.write_u32(s.hash()), + WasmObject::EffectParameter(m) => m.as_ptr().hash(state), + WasmObject::MoveData(m) => m.as_ptr().hash(state), + WasmObject::Species(m) => m.as_ptr().hash(state), + WasmObject::Form(m) => m.as_ptr().hash(state), + WasmObject::Item(m) => m.as_ptr().hash(state), + WasmObject::Ability(m) => m.as_ptr().hash(state), + WasmObject::Nature(m) => m.as_ptr().hash(state), + WasmObject::StaticStatisticSetU16(m) => m.as_ptr().hash(state), + WasmObject::StatisticSetU32(m) => m.as_ptr().hash(state), + WasmObject::StatChangeStatisticSet(m) => m.as_ptr().hash(state), + WasmObject::EVStatisticSet(m) => m.as_ptr().hash(state), + WasmObject::IVStatisticSet(m) => m.as_ptr().hash(state), + WasmObject::MoveLibrary(m) => m.as_ptr().hash(state), + WasmObject::SpeciesLibrary(m) => m.as_ptr().hash(state), + WasmObject::ItemLibrary(m) => m.as_ptr().hash(state), + WasmObject::TypeLibrary(m) => m.as_ptr().hash(state), + WasmObject::LibrarySettings(m) => m.as_ptr().hash(state), + WasmObject::AbilityLibrary(m) => m.as_ptr().hash(state), + WasmObject::StaticData(m) => m.as_ptr().hash(state), + WasmObject::Pokemon(m) => m.as_ptr().hash(state), + WasmObject::PokemonParty(m) => m.as_ptr().hash(state), + WasmObject::LearnedMove(m) => m.as_ptr().hash(state), + WasmObject::DynamicLibrary(m) => m.as_ptr().hash(state), + WasmObject::Battle(m) => m.as_ptr().hash(state), + WasmObject::ChoiceQueue(m) => m.as_ptr().hash(state), + WasmObject::BattleRandom(m) => m.as_ptr().hash(state), + WasmObject::BattleSide(m) => m.as_ptr().hash(state), + WasmObject::BattleParty(m) => m.as_ptr().hash(state), + WasmObject::TurnChoice(m) => m.as_ptr().hash(state), + WasmObject::ExecutingMove(m) => m.as_ptr().hash(state), + WasmObject::HitData(m) => m.as_ptr().hash(state), + }; + } +} + +/// Macro to convert a `WasmObject` into a `Result` of the specified type. +macro_rules! get_from_wasm_obj { + ($variant:ident, $obj:expr) => { + match $obj { + WasmObject::$variant(b) => b.upgrade().ok_or(anyhow!("$variant was dropped")), + _ => Err(anyhow!("Expected $variant")), + } + }; +} + +/// Macro to implement `FromWasmObj` for a type. +macro_rules! impl_from_wasm_obj { + ($variant:ident, $ty:ty) => { + impl FromWasmObj for $ty { + fn from_wasm_obj(obj: WasmObject) -> Result { + get_from_wasm_obj!($variant, obj) + } + } + }; +} + +impl From for WasmObject { + fn from(value: crate::StringKey) -> Self { + Self::StringKey(value) + } +} + +impl FromWasmObj for crate::StringKey { + fn from_wasm_obj(obj: WasmObject) -> Result { + match obj { + WasmObject::StringKey(b) => Ok(b), + _ => Err(anyhow!("Expected StringKey")), + } + } +} + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::EffectParameter(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(EffectParameter, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::MoveData(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(MoveData, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::Species(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(Species, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::Form(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(Form, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::Item(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(Item, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::Ability(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(Ability, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::Nature(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(Nature, Arc); + +impl From<&Arc>> for WasmObject { + fn from(value: &Arc>) -> Self { + Self::StaticStatisticSetU16(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(StaticStatisticSetU16, Arc>); + +impl From<&Arc>> for WasmObject { + fn from(value: &Arc>) -> Self { + Self::StatisticSetU32(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(StatisticSetU32, Arc>); + +impl From<&Arc>> for WasmObject { + fn from(value: &Arc>) -> Self { + Self::StatChangeStatisticSet(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!( + StatChangeStatisticSet, + Arc> +); + +impl From<&Arc>> for WasmObject { + fn from(value: &Arc>) -> Self { + Self::EVStatisticSet(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(EVStatisticSet, Arc>); + +impl From<&Arc>> for WasmObject { + fn from(value: &Arc>) -> Self { + Self::IVStatisticSet(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(IVStatisticSet, Arc>); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::MoveLibrary(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(MoveLibrary, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::SpeciesLibrary(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(SpeciesLibrary, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::ItemLibrary(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(ItemLibrary, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::TypeLibrary(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(TypeLibrary, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::LibrarySettings(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(LibrarySettings, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::AbilityLibrary(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(AbilityLibrary, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::StaticData(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(StaticData, Arc); + +impl From<&Pokemon> for WasmObject { + fn from(value: &Pokemon) -> Self { + Self::Pokemon(value.weak()) + } +} + +impl From<&WeakPokemonReference> for WasmObject { + fn from(value: &WeakPokemonReference) -> Self { + Self::Pokemon(value.clone()) + } +} + +impl_from_wasm_obj!(Pokemon, Pokemon); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::PokemonParty(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(PokemonParty, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::LearnedMove(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(LearnedMove, Arc); + +impl From<&Battle> for WasmObject { + fn from(value: &Battle) -> Self { + Self::Battle(value.weak()) + } +} + +impl From<&WeakBattleReference> for WasmObject { + fn from(value: &WeakBattleReference) -> Self { + Self::Battle(value.clone()) + } +} + +impl_from_wasm_obj!(Battle, crate::dynamic_data::Battle); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::ChoiceQueue(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(ChoiceQueue, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::DynamicLibrary(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(DynamicLibrary, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::BattleRandom(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(BattleRandom, Arc); + +impl From<&crate::dynamic_data::BattleSide> for WasmObject { + fn from(value: &crate::dynamic_data::BattleSide) -> Self { + Self::BattleSide(value.weak()) + } +} + +impl From<&WeakBattleSideReference> for WasmObject { + fn from(value: &WeakBattleSideReference) -> Self { + Self::BattleSide(value.clone()) + } +} + +impl_from_wasm_obj!(BattleSide, crate::dynamic_data::BattleSide); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::BattleParty(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(BattleParty, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::TurnChoice(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(TurnChoice, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::ExecutingMove(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(ExecutingMove, Arc); + +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::HitData(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(HitData, Arc); diff --git a/src/script_implementations/wasm/export_registry/wasm_result.rs b/src/script_implementations/wasm/export_registry/wasm_result.rs index d028244..3b30c21 100644 --- a/src/script_implementations/wasm/export_registry/wasm_result.rs +++ b/src/script_implementations/wasm/export_registry/wasm_result.rs @@ -10,14 +10,18 @@ pub type WasmResult = (u32, T); /// A result type that can be given to, or returned from WASM, but does not contain a value. pub type WasmVoidResult = u32; +/// An extension trait for `WasmResult` that provides some convenience methods. pub(crate) trait WasmVoidResultExtension { - fn into_result(self, env: FunctionEnvMut) -> anyhow_ext::Result<()>; - fn into_result_env(self, env: &Arc) -> anyhow_ext::Result<()>; - fn from_result(res: anyhow_ext::Result<()>, env: &FunctionEnvMut) -> Self; + /// Returns a `WasmVoidResult` that indicates success. fn ok() -> Self; + /// Returns a `WasmVoidResult` that indicates failure. fn err(err: anyhow::Error, env: &FunctionEnvMut) -> Self; + /// Converts a `WasmVoidResult` into a `Result<(), anyhow::Error>`. Used specifically for script + /// function calls. + fn into_result_env(self, env: &Arc) -> anyhow::Result<()>; } +/// Returns a success `WasmResult` with the given value. pub(super) fn wasm_ok(value: T) -> WasmResult where T: FromToNativeWasmType, @@ -26,6 +30,8 @@ where (0, value) } +/// Returns a failure `WasmResult` with the given error. +#[allow(clippy::unwrap_used)] pub(super) fn wasm_err(err: Error, env: &FunctionEnvMut) -> WasmResult where T: FromToNativeWasmType + Default, @@ -36,50 +42,38 @@ where } impl WasmVoidResultExtension for WasmVoidResult { - fn into_result(self, env: FunctionEnvMut) -> anyhow::Result<()> { - Self::into_result_env(self, &env.data().data()) + fn ok() -> Self { + 0 } + #[allow(clippy::unwrap_used)] + fn err(err: Error, env: &FunctionEnvMut) -> Self { + let s = CString::new(err.to_string()).unwrap(); + let ptr = env.data().data().copy_value_vec_to_wasm(s.as_bytes()).unwrap(); + ptr + } + + #[allow(clippy::unwrap_used)] fn into_result_env(self, env: &Arc) -> anyhow::Result<()> { if self == 0 { Ok(()) } else { unsafe { let ptr = self; - let mem: *mut c_char = env.get_raw_pointer(ptr as u32); + let mem: *mut c_char = env.get_raw_pointer(ptr); let string = std::ffi::CStr::from_ptr(mem); let e = anyhow_ext::anyhow!("{}", string.to_str().unwrap()); - env.script_function_cache().dealloc_cstring(&env, ptr as u32).unwrap(); + env.script_function_cache().dealloc_cstring(env, ptr).unwrap(); Err(e) } } } - - fn from_result(res: anyhow::Result<()>, env: &FunctionEnvMut) -> Self { - match res { - Ok(_) => 0, - Err(e) => { - let s = CString::new(e.to_string()).unwrap(); - let ptr = env.data().data().copy_value_vec_to_wasm(s.as_bytes()).unwrap(); - ptr as u32 - } - } - } - - fn ok() -> Self { - 0 - } - - fn err(err: Error, env: &FunctionEnvMut) -> Self { - let s = CString::new(err.to_string()).unwrap(); - let ptr = env.data().data().copy_value_vec_to_wasm(s.as_bytes()).unwrap(); - ptr as u32 - } } +/// Macro to try an expression, and return a `WasmResult` if it fails. macro_rules! try_wasm { ($e:expr, $env:expr) => { - match $e.with_context(|| format!("WASM function {}", stdext::function_name!())) { + match $e { Ok(v) => v, Err(e) => { return crate::script_implementations::wasm::export_registry::wasm_err(e.into(), &$env); @@ -88,12 +82,13 @@ macro_rules! try_wasm { }; } +/// Macro to try an expression, and return a `WasmVoidResult` if it fails. macro_rules! try_wasm_void { ($e:expr, $env:expr) => { - match $e.with_context(|| format!("WASM function {}", stdext::function_name!())) { + match $e { Ok(v) => v, Err(e) => { - return WasmVoidResult::from_result(Err(e.into()), &$env); + return WasmVoidResult::err(e.into(), &$env); } } }; @@ -102,24 +97,28 @@ macro_rules! try_wasm_void { pub(super) use try_wasm; pub(super) use try_wasm_void; +/// Resolve an externref to a value. Returns an error if this fails. macro_rules! get_value { ($e:expr, $env:expr) => { crate::script_implementations::wasm::export_registry::try_wasm!($e.value_func(&$env), $env) }; } +/// Resolve an externref of an Arc to a value. Returns an error if this fails. macro_rules! get_value_arc { ($e:expr, $env:expr) => { crate::script_implementations::wasm::export_registry::try_wasm!($e.value_func_arc(&$env), $env) }; } +/// Resolve an externref to a value. Returns an error if this fails. macro_rules! get_value_void { ($e:expr, $env:expr) => { crate::script_implementations::wasm::export_registry::try_wasm_void!($e.value_func(&$env), $env) }; } +/// Resolve an externref of an Arc to a value. Returns an error if this fails. macro_rules! get_value_arc_void { ($e:expr, $env:expr) => { crate::script_implementations::wasm::export_registry::try_wasm_void!($e.value_func_arc(&$env), $env) @@ -131,6 +130,7 @@ pub(super) use get_value_arc; pub(super) use get_value_arc_void; pub(super) use get_value_void; +/// Resolve an externref to a value, and call a getter on it. Returns an error if this fails. macro_rules! get_value_call_getter { ($e:ident.$func:ident(), $env:expr) => {{ let _value = crate::script_implementations::wasm::export_registry::try_wasm!($e.value_func(&$env), $env); diff --git a/src/script_implementations/wasm/extern_ref.rs b/src/script_implementations/wasm/extern_ref.rs index 6b23e0b..503b7fe 100755 --- a/src/script_implementations/wasm/extern_ref.rs +++ b/src/script_implementations/wasm/extern_ref.rs @@ -1,10 +1,10 @@ -use crate::{PkmnError, ValueIdentifiable}; +use crate::ValueIdentifiable; use anyhow_ext::Result; -use std::any::Any; use std::marker::PhantomData; use std::mem::transmute; use std::sync::Arc; +use crate::script_implementations::wasm::export_registry::{FromWasmObj, WasmObject}; use crate::script_implementations::wasm::script_resolver::{ WebAssemblyEnv, WebAssemblyEnvironmentData, WebAssemblyScriptResolver, }; @@ -44,26 +44,26 @@ impl Default for ExternRef { impl ExternRef { /// Instantiates a new ExternRef for a bit of data. If we already have made an Extern Ref for /// this data and type, we use that instead. - pub fn new(env: &WebAssemblyEnvironmentData, value: &dyn Any) -> Self { + pub fn new(env: &WebAssemblyEnvironmentData, value: WasmObject) -> Self { Self { - index: env.get_extern_ref_index::(value), + index: env.get_extern_ref_index(value), _phantom: Default::default(), } } /// Instantiates a new ExternRef for a bit of data using the function environment. If we already /// have made an Extern Ref for this data and type, we use that instead. - pub fn func_new(env: &FunctionEnvMut, value: &dyn Any) -> Self { + pub fn func_new(env: &FunctionEnvMut, value: WasmObject) -> Self { Self { - index: env.data().data().get_extern_ref_index::(value), + index: env.data().data().get_extern_ref_index(value), _phantom: Default::default(), } } /// Creates an ExternRef with a given resolver. This can be used in cases where we do not have an environment variable. - pub(crate) fn new_with_resolver(resolver: &WebAssemblyScriptResolver, value: &dyn Any) -> Self { + pub(crate) fn new_with_resolver(resolver: &WebAssemblyScriptResolver, value: WasmObject) -> Self { Self { - index: resolver.environment_data().get_extern_ref_index::(value), + index: resolver.environment_data().get_extern_ref_index(value), _phantom: Default::default(), } } @@ -78,62 +78,46 @@ 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_func<'a>(&self, env: &'a FunctionEnvMut) -> Result<&'a T> + pub fn value_func(&self, env: &FunctionEnvMut) -> Result where - T: Sized + 'static, + T: FromWasmObj, { - self.value(&env.data().data())?.ok_or(PkmnError::NullReference.into()) + 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_func_arc(&self, env: &FunctionEnvMut) -> Result> where - T: 'static, + Arc: FromWasmObj, { - self.value_arc(&env.data().data())? - .ok_or(PkmnError::NullReference.into()) + self.value_arc(&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_func_box(&self, env: &FunctionEnvMut) -> Result<&Box> + pub fn value(&self, env: &Arc) -> Result where - T: 'static, + T: FromWasmObj, { - self.value_box(&env.data().data()) + let value = env.get_extern_ref_value::(self.index)?; + let o = FromWasmObj::from_wasm_obj(value)?; + Ok(o) } /// 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>(&self, env: &Arc) -> Result> + pub fn value_arc(&self, env: &Arc) -> Result> where - T: Sized + 'static, + Arc: FromWasmObj, { - Ok(env.get_extern_ref_value::(self.index)?.downcast_ref::()) + let wasm_obj = env.get_extern_ref_value::(self.index)?; + FromWasmObj::from_wasm_obj(wasm_obj) } - /// 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_arc(&self, env: &Arc) -> Result>> - where - T: 'static, - { - Ok(env - .get_extern_ref_value::(self.index)? - .downcast_ref::>() - .cloned()) - } - - /// 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_box(&self, env: &Arc) -> Result<&Box> - where - T: 'static, - { - env.get_extern_ref_value::(self.index)? - .downcast_ref::>() - .ok_or(PkmnError::NullReference.into()) + /// Returns the internal index for this ExternRef. + pub(crate) fn index(&self) -> usize { + self.index } } @@ -185,16 +169,16 @@ impl Default for VecExternRef { } } -impl VecExternRef { - /// Instantiates a new VecExternRef for a given slice. - pub fn new(env: &WebAssemblyEnvironmentData, value: &Vec) -> Self { - Self { - index: env.get_extern_vec_ref_index(value), - size: value.len() as u32, - _phantom: Default::default(), - } - } -} +// impl VecExternRef { +// /// Instantiates a new VecExternRef for a given slice. +// pub fn new(env: &WebAssemblyEnvironmentData, value: &Vec) -> Self { +// Self { +// index: env.get_extern_vec_ref_index(value), +// size: value.len() as u32, +// _phantom: Default::default(), +// } +// } +// } unsafe impl FromToNativeWasmType for VecExternRef { type Native = i64; diff --git a/src/script_implementations/wasm/script.rs b/src/script_implementations/wasm/script.rs index 2799b38..4848750 100755 --- a/src/script_implementations/wasm/script.rs +++ b/src/script_implementations/wasm/script.rs @@ -1,4 +1,4 @@ -use anyhow_ext::{anyhow, Result}; +use anyhow_ext::Result; use std::any::Any; use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::Arc; @@ -9,7 +9,7 @@ use crate::dynamic_data::{ Battle, DamageSource, DynamicLibrary, ExecutingMove, Pokemon, Script, ScriptOwnerData, TurnChoice, }; use crate::script_implementations::wasm::export_registry::WasmVoidResultExtension; -use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; +use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData; use crate::script_implementations::wasm::WebAssemblyScriptCapabilities; use crate::static_data::{EffectParameter, Item, Statistic, TypeIdentifier}; @@ -86,14 +86,7 @@ macro_rules! call_func { /// 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) + ExternRef::new($env.as_ref(), $value.into()) }; } @@ -132,19 +125,25 @@ impl Script for WebAssemblyScript { Ok(()) } - fn on_initialize(&self, library: &Arc, pars: Vec) -> Result<()> { + fn on_initialize(&self, library: &Arc, pars: Vec>) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::Initialize) { 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)); + let pars = pars + .into_iter() + .map(|p| ExternRef::::new(env, (&p).into()).index() as u32) + .collect::>(); + let wasm_ptr = env.copy_value_vec_to_wasm(&pars)?; + let r: u64 = unsafe { std::mem::transmute((wasm_ptr, pars.len() as u32)) }; + call_func!(func, env, self, ex_ref!(env, library), r); } Ok(()) } - fn on_before_turn(&self, choice: &TurnChoice) -> Result<()> { + fn on_before_turn(&self, choice: &Arc) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnBeforeTurn) { return Ok(()); } @@ -155,7 +154,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn change_speed(&self, choice: &TurnChoice, speed: &mut u32) -> Result<()> { + fn change_speed(&self, choice: &Arc, speed: &mut u32) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeSpeed) { return Ok(()); } @@ -169,7 +168,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn change_priority(&self, choice: &TurnChoice, priority: &mut i8) -> Result<()> { + fn change_priority(&self, choice: &Arc, priority: &mut i8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangePriority) { return Ok(()); } @@ -183,26 +182,22 @@ impl Script for WebAssemblyScript { Ok(()) } - fn change_move(&self, choice: &TurnChoice, move_name: &mut StringKey) -> Result<()> { + fn change_move(&self, choice: &Arc, move_name: &mut StringKey) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeMove) { return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().change_move(env) { - let move_ref = ex_ref!(env, move_name); + let move_ref = ex_ref!(env, move_name.clone()); 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)? - .ok_or(anyhow!("Unable to get move name"))? - .clone(); + *move_name = ptr.value().value(env)?; } Ok(()) } - fn change_number_of_hits(&self, choice: &TurnChoice, number_of_hits: &mut u8) -> Result<()> { + fn change_number_of_hits(&self, choice: &Arc, number_of_hits: &mut u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeNumberOfHits) { return Ok(()); } @@ -216,7 +211,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn prevent_move(&self, mv: &ExecutingMove, prevent: &mut bool) -> Result<()> { + fn prevent_move(&self, mv: &Arc, prevent: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventMove) { return Ok(()); } @@ -230,7 +225,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn fail_move(&self, mv: &ExecutingMove, fail: &mut bool) -> Result<()> { + fn fail_move(&self, mv: &Arc, fail: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::FailMove) { return Ok(()); } @@ -244,7 +239,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn stop_before_move(&self, mv: &ExecutingMove, stop: &mut bool) -> Result<()> { + fn stop_before_move(&self, mv: &Arc, stop: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::StopBeforeMove) { return Ok(()); } @@ -258,7 +253,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn on_before_move(&self, mv: &ExecutingMove) -> Result<()> { + fn on_before_move(&self, mv: &Arc) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnBeforeMove) { return Ok(()); } @@ -269,61 +264,47 @@ impl Script for WebAssemblyScript { Ok(()) } - fn fail_incoming_move(&self, mv: &ExecutingMove, target: &Arc, fail: &mut bool) -> Result<()> { + fn fail_incoming_move(&self, mv: &Arc, target: &Pokemon, fail: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::FailIncomingMove) { return Ok(()); } 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 - ); + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), ptr.wasm_ptr); *fail = *ptr.value(); } Ok(()) } - fn is_invulnerable(&self, mv: &ExecutingMove, target: &Arc, invulnerable: &mut bool) -> Result<()> { + fn is_invulnerable(&self, mv: &Arc, target: &Pokemon, invulnerable: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::IsInvulnerable) { return Ok(()); } 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 - ); + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), ptr.wasm_ptr); *invulnerable = *ptr.value(); } Ok(()) } - fn on_move_miss(&self, mv: &ExecutingMove, target: &Arc) -> Result<()> { + fn on_move_miss(&self, mv: &Arc, target: &Pokemon) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnMoveMiss) { 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())); + call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target)); } Ok(()) } fn change_move_type( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, move_type: &mut TypeIdentifier, ) -> Result<()> { @@ -333,7 +314,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -343,8 +324,8 @@ impl Script for WebAssemblyScript { fn change_effectiveness( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, effectiveness: &mut f32, ) -> Result<()> { @@ -354,7 +335,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -364,8 +345,8 @@ impl Script for WebAssemblyScript { fn block_critical( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, block_critical: &mut bool, ) -> Result<()> { @@ -375,7 +356,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -385,8 +366,8 @@ impl Script for WebAssemblyScript { fn block_incoming_critical( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, block_critical: &mut bool, ) -> Result<()> { @@ -396,7 +377,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -404,14 +385,14 @@ impl Script for WebAssemblyScript { Ok(()) } - fn change_accuracy(&self, mv: &ExecutingMove, target: &Arc, hit: u8, accuracy: &mut u8) -> Result<()> { + fn change_accuracy(&self, mv: &Arc, target: &Pokemon, hit: u8, accuracy: &mut u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeAccuracy) { return Ok(()); } 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 target = target; 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(); @@ -419,14 +400,14 @@ impl Script for WebAssemblyScript { Ok(()) } - fn change_critical_stage(&self, mv: &ExecutingMove, target: &Arc, hit: u8, stage: &mut u8) -> Result<()> { + fn change_critical_stage(&self, mv: &Arc, target: &Pokemon, hit: u8, stage: &mut u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCriticalStage) { return Ok(()); } 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 target = target; 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(); @@ -436,8 +417,8 @@ impl Script for WebAssemblyScript { fn change_critical_modifier( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, modifier: &mut f32, ) -> Result<()> { @@ -447,7 +428,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -457,8 +438,8 @@ impl Script for WebAssemblyScript { fn change_stab_modifier( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, modifier: &mut f32, ) -> Result<()> { @@ -468,7 +449,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -476,14 +457,14 @@ impl Script for WebAssemblyScript { Ok(()) } - fn change_base_power(&self, mv: &ExecutingMove, target: &Arc, hit: u8, base_power: &mut u8) -> Result<()> { + fn change_base_power(&self, mv: &Arc, target: &Pokemon, hit: u8, base_power: &mut u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeBasePower) { return Ok(()); } 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 target = target; 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(); @@ -493,8 +474,8 @@ impl Script for WebAssemblyScript { fn bypass_defensive_stat_boost( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, bypass: &mut bool, ) -> Result<()> { @@ -504,7 +485,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -514,8 +495,8 @@ impl Script for WebAssemblyScript { fn bypass_offensive_stat_boost( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, bypass: &mut bool, ) -> Result<()> { @@ -525,7 +506,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -535,8 +516,8 @@ impl Script for WebAssemblyScript { fn change_offensive_stat_value( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, amount: &mut u32, ) -> Result<()> { @@ -546,7 +527,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -556,8 +537,8 @@ impl Script for WebAssemblyScript { fn change_defensive_stat_value( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, amount: &mut u32, ) -> Result<()> { @@ -567,7 +548,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -577,8 +558,8 @@ impl Script for WebAssemblyScript { fn change_damage_stat_modifier( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, modifier: &mut f32, ) -> Result<()> { @@ -588,7 +569,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -598,8 +579,8 @@ impl Script for WebAssemblyScript { fn change_damage_modifier( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, modifier: &mut f32, ) -> Result<()> { @@ -609,7 +590,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -617,14 +598,14 @@ impl Script for WebAssemblyScript { Ok(()) } - fn change_damage(&self, mv: &ExecutingMove, target: &Arc, hit: u8, damage: &mut u32) -> Result<()> { + fn change_damage(&self, mv: &Arc, target: &Pokemon, hit: u8, damage: &mut u32) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeDamage) { return Ok(()); } 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 target = target; 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(); @@ -634,8 +615,8 @@ impl Script for WebAssemblyScript { fn change_incoming_damage( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, damage: &mut u32, ) -> Result<()> { @@ -645,7 +626,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -653,25 +634,25 @@ impl Script for WebAssemblyScript { Ok(()) } - fn on_incoming_hit(&self, mv: &ExecutingMove, target: &Arc, hit: u8) -> Result<()> { + fn on_incoming_hit(&self, mv: &Arc, target: &Pokemon, hit: u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnIncomingHit) { return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().on_incoming_hit(env) { - let target = target.as_ref(); + let target = target; 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) -> Result<()> { + fn on_opponent_faints(&self, mv: &Arc, target: &Pokemon, hit: u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaintingOpponent) { return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().on_opponent_faints(env) { - let target = target.as_ref(); + let target = target; call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit); } Ok(()) @@ -709,8 +690,8 @@ impl Script for WebAssemblyScript { fn prevent_secondary_effect( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, prevent: &mut bool, ) -> Result<()> { @@ -720,7 +701,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -728,14 +709,14 @@ impl Script for WebAssemblyScript { Ok(()) } - fn change_effect_chance(&self, mv: &ExecutingMove, target: &Arc, hit: u8, chance: &mut f32) -> Result<()> { + fn change_effect_chance(&self, mv: &Arc, target: &Pokemon, hit: u8, chance: &mut f32) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeEffectChance) { return Ok(()); } 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 target = target; 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(); @@ -745,8 +726,8 @@ impl Script for WebAssemblyScript { fn change_incoming_effect_chance( &self, - mv: &ExecutingMove, - target: &Arc, + mv: &Arc, + target: &Pokemon, hit: u8, chance: &mut f32, ) -> Result<()> { @@ -756,7 +737,7 @@ impl Script for WebAssemblyScript { 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 target = target; 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(); @@ -764,31 +745,31 @@ impl Script for WebAssemblyScript { Ok(()) } - fn on_secondary_effect(&self, mv: &ExecutingMove, target: &Arc, hit: u8) -> Result<()> { + fn on_secondary_effect(&self, mv: &Arc, target: &Pokemon, hit: u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaintingOpponent) { return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().on_secondary_effect(env) { - let target = target.as_ref(); + let target = target; call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit); } Ok(()) } - fn on_after_hits(&self, mv: &ExecutingMove, target: &Arc) -> Result<()> { + fn on_after_hits(&self, mv: &Arc, target: &Pokemon) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnAfterHits) { return Ok(()); } let env = &self.environment; if let Some(func) = env.script_function_cache().on_after_hits(env) { - let target = target.as_ref(); + let target = target; call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target)); } Ok(()) } - fn prevent_self_switch(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> { + fn prevent_self_switch(&self, choice: &Arc, prevent: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfSwitch) { return Ok(()); } @@ -801,7 +782,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn prevent_opponent_switch(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> { + fn prevent_opponent_switch(&self, choice: &Arc, prevent: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfSwitch) { return Ok(()); } @@ -836,7 +817,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn prevent_self_run_away(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> { + fn prevent_self_run_away(&self, choice: &Arc, prevent: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfRunAway) { return Ok(()); } @@ -849,7 +830,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn prevent_opponent_run_away(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> { + fn prevent_opponent_run_away(&self, choice: &Arc, prevent: &mut bool) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::PreventOpponentRunAway) { return Ok(()); } @@ -906,7 +887,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn on_after_held_item_consume(&self, pokemon: &Pokemon, item: &dyn Item) -> Result<()> { + fn on_after_held_item_consume(&self, pokemon: &Pokemon, item: &Arc) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::OnAfterHeldItemConsume) { return Ok(()); } @@ -960,7 +941,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn change_capture_rate_bonus(&self, target: &Pokemon, pokeball: &dyn Item, modifier: &mut u8) -> Result<()> { + fn change_capture_rate_bonus(&self, target: &Pokemon, pokeball: &Arc, modifier: &mut u8) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCaptureRate) { return Ok(()); } diff --git a/src/script_implementations/wasm/script_function_cache.rs b/src/script_implementations/wasm/script_function_cache.rs index a932ad8..2bac161 100755 --- a/src/script_implementations/wasm/script_function_cache.rs +++ b/src/script_implementations/wasm/script_function_cache.rs @@ -8,9 +8,9 @@ use paste::paste; use crate::dynamic_data::{Battle, DynamicLibrary, ExecutingMove, Pokemon, TurnChoice}; use crate::script_implementations::wasm::export_registry::WasmVoidResult; -use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; +use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData; -use crate::static_data::{EffectParameter, Item, TypeIdentifier}; +use crate::static_data::{Item, TypeIdentifier}; use crate::StringKey; use wasmer::{FromToNativeWasmType, TypedFunction}; @@ -123,7 +123,7 @@ unsafe impl FromToNativeWasmType for WasmPtr { script_function_cache! { stack() on_remove() - on_initialize(ExternRef, VecExternRef) + on_initialize(ExternRef, u64) on_before_turn(ExternRef) change_speed(ExternRef, WasmPtr) change_priority(ExternRef, WasmPtr) diff --git a/src/script_implementations/wasm/script_resolver.rs b/src/script_implementations/wasm/script_resolver.rs index 468b20a..92f21a4 100755 --- a/src/script_implementations/wasm/script_resolver.rs +++ b/src/script_implementations/wasm/script_resolver.rs @@ -1,9 +1,8 @@ -use std::any::Any; use std::fmt::{Debug, Formatter}; use std::mem::{align_of, forget, size_of}; use std::sync::{Arc, Weak}; -use anyhow_ext::{anyhow, ensure, Context, Result}; +use anyhow_ext::{anyhow, Context, Result}; use hashbrown::{HashMap, HashSet}; use parking_lot::lock_api::RwLockReadGuard; use parking_lot::{RawRwLock, RwLock}; @@ -13,7 +12,7 @@ use wasmer::{ }; use crate::dynamic_data::{ItemScript, Script, ScriptOwnerData, ScriptResolver}; -use crate::script_implementations::wasm::export_registry::register_webassembly_funcs; +use crate::script_implementations::wasm::export_registry::{register_webassembly_funcs, WasmObject}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script::WebAssemblyScript; use crate::script_implementations::wasm::script_function_cache::ScriptFunctionCache; @@ -192,7 +191,7 @@ impl ScriptResolver for WebAssemblyScriptResolver { .call( &mut self.store_mut(), category as u8, - ExternRef::new_with_resolver(self, script_key), + ExternRef::new_with_resolver(self, WasmObject::StringKey(script_key.clone())), )?; self.environment_data.setup_script(script, category, script_key, owner) } @@ -223,7 +222,7 @@ pub struct WebAssemblyEnvironmentData { /// we instead keep track of all the data we've sent to WASM, and pass the ID of that data to WASM. This allows us /// to only operate on data we know WASM owns. We currently store this data in this continuous Vec, and give the index /// of the data as the ID. - extern_ref_pointers: RwLock>, + extern_ref_pointers: RwLock>, /// To make sure we send the same identifier to WASM when we send the same piece of data multiple times, we have a /// backwards lookup on extern_ref_pointers. This allows us to get the index for a given piece of data. extern_ref_pointers_lookup: RwLock>, @@ -269,9 +268,7 @@ pub struct WebAssemblyEnvironmentData { #[derive(Clone, Eq, PartialEq, Hash)] struct ExternRefLookupKey { /// The raw pointer to the data - pub ptr: *const dyn Any, - /// Whether or not the reference is a Vec - pub is_vec: bool, + pub obj: WasmObject, } impl WebAssemblyEnvironmentData { @@ -387,8 +384,8 @@ impl WebAssemblyEnvironmentData { /// when extern refs get actually properly implemented at compile time we might want to get rid /// of this code. #[inline(always)] - pub fn get_extern_ref_index(&self, value: &dyn Any) -> usize { - self.get_extern_ref_from_identifier(value, false) + pub(crate) fn get_extern_ref_index(&self, value: WasmObject) -> usize { + self.get_extern_ref_from_identifier(value) } /// Get a numeric value from any given value. This is not a true Extern Ref from WASM, as this @@ -397,15 +394,15 @@ impl WebAssemblyEnvironmentData { /// access can be touched through this, and we ensure the value is the correct type. In the future, /// when extern refs get actually properly implemented at compile time we might want to get rid /// of this code. - pub fn get_extern_vec_ref_index(&self, value: &Vec) -> usize { - let mut vec = Vec::with_capacity(value.len()); - for v in value { - vec.push(self.get_extern_ref_index::(v)); - } - let p = self.get_extern_ref_from_identifier(value, true); - self.extern_vec_ref_lookup.write().insert(p, vec); - p - } + // pub fn get_extern_vec_ref_index(&self, value: &Vec) -> usize { + // let mut vec = Vec::with_capacity(value.len()); + // for v in value { + // vec.push(self.get_extern_ref_index(v)); + // } + // let p = self.get_extern_ref_from_identifier(value); + // self.extern_vec_ref_lookup.write().insert(p, vec); + // p + // } /// Get an extern ref belonging to a vector we have passed to WASM. pub fn get_extern_vec_ref_extern_ref(&self, extern_vec_ref: usize, index: usize) -> Result { @@ -417,44 +414,37 @@ impl WebAssemblyEnvironmentData { /// Gets the extern ref index belonging to a specific pointer. If none exists, this will create /// a new one. #[inline(always)] - fn get_extern_ref_from_identifier(&self, value: &dyn Any, is_vec: bool) -> usize { - if let Some(v) = self.extern_ref_pointers_lookup.read().get(&ExternRefLookupKey { - ptr: value as *const dyn Any, - is_vec, - }) { + fn get_extern_ref_from_identifier(&self, value: WasmObject) -> usize { + if let Some(v) = self + .extern_ref_pointers_lookup + .read() + .get(&ExternRefLookupKey { obj: value.clone() }) + { return *v; } let index = { let mut extern_ref_guard = self.extern_ref_pointers.write(); - extern_ref_guard.push(value as *const dyn Any); + extern_ref_guard.push(value.clone()); extern_ref_guard.len() }; - self.extern_ref_pointers_lookup.write().insert( - ExternRefLookupKey { - ptr: value as *const dyn Any, - is_vec, - }, - index, - ); - self.extern_ref_type_lookup.write().insert(ExternRefLookupKey { - ptr: value as *const dyn Any, - is_vec, - }); + self.extern_ref_pointers_lookup + .write() + .insert(ExternRefLookupKey { obj: value.clone() }, index); + self.extern_ref_type_lookup + .write() + .insert(ExternRefLookupKey { obj: value }); index } /// Gets a value from the extern ref lookup. This turns an earlier registered index back into /// its proper value, validates its type, and returns the value. - pub fn get_extern_ref_value<'a, T: ?Sized>(&self, index: usize) -> Result<&'a dyn Any> { + pub(crate) fn get_extern_ref_value(&self, index: usize) -> Result { let read_guard = self.extern_ref_pointers.read(); let ptr = read_guard.get_res(index - 1)?; if self .extern_ref_type_lookup .read() - .get(&ExternRefLookupKey { - ptr: *ptr, - is_vec: false, - }) + .get(&ExternRefLookupKey { obj: ptr.clone() }) .is_none() { #[allow(clippy::panic)] // Allow panic here, as this is a security error. @@ -466,17 +456,23 @@ impl WebAssemblyEnvironmentData { } } - unsafe { Ok(ptr.as_ref().unwrap()) } + Ok(ptr.clone()) } /// The WASM store. pub fn store_ref(&self) -> StoreRef<'_> { - unsafe { self.store.as_ref().unwrap().as_store_ref() } + #[allow(clippy::unwrap_used)] // This should never be None when used. + unsafe { + self.store.as_ref().unwrap().as_store_ref() + } } /// The mutable WASM store. pub fn store_mut(&self) -> StoreMut<'_> { - unsafe { self.store.as_mut().unwrap().as_store_mut() } + #[allow(clippy::unwrap_used)] // This should never be None when used. + unsafe { + self.store.as_mut().unwrap().as_store_mut() + } } /// Find a loaded script based on the pointer in WASM memory. @@ -514,12 +510,16 @@ impl WebAssemblyEnvironmentData { .get::(&"get_script_capabilities".into()) { let res = get_cap.call(&mut self.store_mut(), &[Value::I32(script_ptr as i32)])?; - let ptr = - (self.memory() as *const WebAssemblyScriptCapabilities).offset(res[0].i32().unwrap() as isize); - let length = res[1].i32(); - ensure!(length.is_some(), "Script capabilities length was not a valid i32"); - for i in 0..length.unwrap() as usize { - capabilities.insert(*ptr.add(i)); + let ptr = (self.memory() as *const WebAssemblyScriptCapabilities) + .offset(res.get_res(0)?.unwrap_i32() as isize); + let length = res.get_res(1)?.i32(); + match length { + None => return Err(anyhow_ext::anyhow!("Script capabilities length was not a valid i32")), + Some(length) => { + for i in 0..length as usize { + capabilities.insert(*ptr.add(i)); + } + } } } } @@ -529,13 +529,16 @@ impl WebAssemblyEnvironmentData { } let read_guard = self.script_capabilities.read(); - let capabilities = read_guard.get(&key).unwrap(); + let capabilities = read_guard.get(&key).ok_or(anyhow!("Script capabilities not found"))?; + + #[allow(clippy::unwrap_used)] // This should never be None when used. + let environment = { self.self_arc.read().as_ref().unwrap().upgrade().unwrap() }; let script = Arc::new(WebAssemblyScript::new( owner, script_ptr, capabilities.clone(), - self.self_arc.read().as_ref().unwrap().upgrade().unwrap(), + environment, script_key.clone(), )); @@ -558,6 +561,7 @@ impl WebAssemblyEnv { } /// Get the actual data belonging to the current context. + #[allow(clippy::unwrap_used)] // This should never be None when used. pub fn data(&self) -> Arc { self.data.upgrade().unwrap() } diff --git a/src/static_data/libraries/item_library.rs b/src/static_data/libraries/item_library.rs index 9053b66..0b2d59d 100755 --- a/src/static_data/libraries/item_library.rs +++ b/src/static_data/libraries/item_library.rs @@ -67,7 +67,7 @@ pub mod tests { ) } - pub fn build() -> Box { + pub fn build() -> Arc { let mut lib = ItemLibraryImpl::new(1); let m = build_item(); // Borrow as mut so we can insert @@ -75,6 +75,6 @@ pub mod tests { w.add(&"foo".into(), Arc::new(m)); // Drops borrow as mut - Box::new(lib) + Arc::new(lib) } } diff --git a/src/static_data/libraries/move_library.rs b/src/static_data/libraries/move_library.rs index 16db077..5361bc6 100755 --- a/src/static_data/libraries/move_library.rs +++ b/src/static_data/libraries/move_library.rs @@ -71,7 +71,7 @@ pub mod tests { ) } - pub fn build() -> Box { + pub fn build() -> Arc { let mut lib = MoveLibraryImpl::new(1); let m = build_move(); // Borrow as mut so we can insert @@ -79,6 +79,6 @@ pub mod tests { w.add(&StringKey::new("foo"), Arc::new(m)); // Drops borrow as mut - Box::new(lib) + Arc::new(lib) } } diff --git a/src/static_data/libraries/species_library.rs b/src/static_data/libraries/species_library.rs index c050bd6..2b9c6cd 100755 --- a/src/static_data/libraries/species_library.rs +++ b/src/static_data/libraries/species_library.rs @@ -95,7 +95,7 @@ pub mod tests { )) } - pub fn build() -> Box { + pub fn build() -> Arc { let mut lib = SpeciesLibraryImpl::new(1); let species = build_species(); // Borrow as mut so we can insert @@ -103,7 +103,7 @@ pub mod tests { w.add(&"foo".into(), species); // Drops borrow as mut - Box::new(lib) + Arc::new(lib) } #[test] @@ -124,6 +124,8 @@ pub mod tests { fn add_species_to_library_then_remove() { let mut lib = build(); + let lib = Arc::get_mut(&mut lib).unwrap(); + lib.remove(&"foo".into()); // Borrow as read so we can read diff --git a/src/static_data/libraries/static_data.rs b/src/static_data/libraries/static_data.rs index d83a829..2d3d6dc 100755 --- a/src/static_data/libraries/static_data.rs +++ b/src/static_data/libraries/static_data.rs @@ -8,40 +8,27 @@ use crate::static_data::SpeciesLibrary; use crate::static_data::TypeLibrary; use crate::{ValueIdentifiable, ValueIdentifier}; use std::fmt::Debug; +use std::sync::Arc; /// The storage for all different libraries. pub trait StaticData: Debug + ValueIdentifiable { /// Several misc settings for the library. - fn settings(&self) -> &Box; + fn settings(&self) -> &Arc; /// All data for Pokemon species. - fn species(&self) -> &Box; - /// All data for Pokemon species. - fn species_mut(&mut self) -> &mut Box; + fn species(&self) -> &Arc; /// All data for the moves. - fn moves(&self) -> &Box; - /// All data for the moves. - fn moves_mut(&mut self) -> &mut Box; + fn moves(&self) -> &Arc; /// All data for the items. - fn items(&self) -> &Box; - /// All data for the items. - fn items_mut(&mut self) -> &mut Box; + fn items(&self) -> &Arc; /// All data for growth rates. - fn growth_rates(&self) -> &Box; - /// All data for growth rates. - fn growth_rates_mut(&mut self) -> &mut Box; + fn growth_rates(&self) -> &Arc; /// All data related to types and type effectiveness. - fn types(&self) -> &Box; - /// All data related to types and type effectiveness. - fn types_mut(&mut self) -> &mut Box; + fn types(&self) -> &Arc; /// All data related to natures. - fn natures(&self) -> &Box; - /// All data related to natures. - fn natures_mut(&mut self) -> &mut Box; + fn natures(&self) -> &Arc; /// All data related to abilities. - fn abilities(&self) -> &Box; - /// All data related to abilities. - fn abilities_mut(&mut self) -> &mut Box; + fn abilities(&self) -> &Arc; } /// The storage for all different libraries. @@ -50,34 +37,34 @@ pub struct StaticDataImpl { /// A unique identifier so we know what value this is. identifier: ValueIdentifier, /// Several misc settings for the library. - settings: Box, + settings: Arc, /// All data for Pokemon species. - species: Box, + species: Arc, /// All data for the moves. - moves: Box, + moves: Arc, /// All data for the items. - items: Box, + items: Arc, /// All data for growth rates. - growth_rates: Box, + growth_rates: Arc, /// All data related to types and type effectiveness. - types: Box, + types: Arc, /// All data related to natures. - natures: Box, + natures: Arc, /// All data related to abilities. - abilities: Box, + abilities: Arc, } impl StaticDataImpl { /// Instantiates a new data collection. pub fn new( - settings: Box, - species: Box, - moves: Box, - items: Box, - growth_rates: Box, - types: Box, - natures: Box, - abilities: Box, + settings: Arc, + species: Arc, + moves: Arc, + items: Arc, + growth_rates: Arc, + types: Arc, + natures: Arc, + abilities: Arc, ) -> Self { Self { identifier: Default::default(), @@ -95,66 +82,38 @@ impl StaticDataImpl { impl StaticData for StaticDataImpl { /// Several misc settings for the library. - fn settings(&self) -> &Box { + fn settings(&self) -> &Arc { &self.settings } /// All data for Pokemon species. - fn species(&self) -> &Box { + fn species(&self) -> &Arc { &self.species } - /// All data for Pokemon species. - fn species_mut(&mut self) -> &mut Box { - &mut self.species - } /// All data for the moves. - fn moves(&self) -> &Box { + fn moves(&self) -> &Arc { &self.moves } - /// All data for the moves. - fn moves_mut(&mut self) -> &mut Box { - &mut self.moves - } /// All data for the items. - fn items(&self) -> &Box { + fn items(&self) -> &Arc { &self.items } - /// All data for the items. - fn items_mut(&mut self) -> &mut Box { - &mut self.items - } /// All data for growth rates. - fn growth_rates(&self) -> &Box { + fn growth_rates(&self) -> &Arc { &self.growth_rates } - /// All data for growth rates. - fn growth_rates_mut(&mut self) -> &mut Box { - &mut self.growth_rates - } /// All data related to types and type effectiveness. - fn types(&self) -> &Box { + fn types(&self) -> &Arc { &self.types } - /// All data related to types and type effectiveness. - fn types_mut(&mut self) -> &mut Box { - &mut self.types - } /// All data related to natures. - fn natures(&self) -> &Box { + fn natures(&self) -> &Arc { &self.natures } - /// All data related to natures. - fn natures_mut(&mut self) -> &mut Box { - &mut self.natures - } /// All data related to abilities. - fn abilities(&self) -> &Box { + fn abilities(&self) -> &Arc { &self.abilities } - /// All data related to abilities. - fn abilities_mut(&mut self) -> &mut Box { - &mut self.abilities - } } impl ValueIdentifiable for StaticDataImpl { @@ -174,22 +133,15 @@ pub mod test { #[derive(Debug)] pub StaticData{} impl StaticData for StaticData { - fn settings(&self) -> &Box; - fn species(&self) -> &Box; - fn species_mut(&mut self) -> &mut Box; - fn moves(&self) -> &Box; - fn moves_mut(&mut self) -> &mut Box; - fn items(&self) -> &Box; - fn items_mut(&mut self) -> &mut Box; + fn settings(&self) -> &Arc; + fn species(&self) -> &Arc; + fn moves(&self) -> &Arc; + fn items(&self) -> &Arc; - fn growth_rates(&self) -> & Box; - fn growth_rates_mut(&mut self) -> &mut Box; - fn types(&self) -> &Box; - fn types_mut(&mut self) -> &mut Box; - fn natures(&self) -> &Box; - fn natures_mut(&mut self) -> &mut Box; - fn abilities(&self) -> &Box; - fn abilities_mut(&mut self) -> &mut Box; + fn growth_rates(&self) -> & Arc; + fn types(&self) -> &Arc; + fn natures(&self) -> &Arc; + fn abilities(&self) -> &Arc; } impl ValueIdentifiable for StaticData { fn value_identifier(&self) -> ValueIdentifier{ @@ -201,14 +153,14 @@ pub mod test { pub fn build() -> StaticDataImpl { StaticDataImpl { identifier: Default::default(), - settings: Box::new(LibrarySettingsImpl::new(100, 100).unwrap()), + settings: Arc::new(LibrarySettingsImpl::new(100, 100).unwrap()), species: crate::static_data::libraries::species_library::tests::build(), moves: crate::static_data::libraries::move_library::tests::build(), items: crate::static_data::libraries::item_library::tests::build(), - growth_rates: Box::new(crate::static_data::libraries::growth_rate_library::tests::build()), - types: Box::new(crate::static_data::libraries::type_library::tests::build()), - natures: Box::new(crate::static_data::libraries::nature_library::tests::build()), - abilities: Box::new(crate::static_data::libraries::ability_library::tests::build()), + growth_rates: Arc::new(crate::static_data::libraries::growth_rate_library::tests::build()), + types: Arc::new(crate::static_data::libraries::type_library::tests::build()), + natures: Arc::new(crate::static_data::libraries::nature_library::tests::build()), + abilities: Arc::new(crate::static_data::libraries::ability_library::tests::build()), } } } diff --git a/src/static_data/moves/secondary_effect.rs b/src/static_data/moves/secondary_effect.rs index a1bd59c..8ee083c 100755 --- a/src/static_data/moves/secondary_effect.rs +++ b/src/static_data/moves/secondary_effect.rs @@ -1,6 +1,7 @@ use crate::static_data::EffectParameter; use crate::{StringKey, ValueIdentifiable, ValueIdentifier}; use std::fmt::Debug; +use std::sync::Arc; /// A secondary effect is an effect on a move that happens after it hits. pub trait SecondaryEffect: Debug + ValueIdentifiable { @@ -9,7 +10,7 @@ pub trait SecondaryEffect: Debug + ValueIdentifiable { /// The name of the effect. fn effect_name(&self) -> &StringKey; /// A list of parameters for the effect. - fn parameters(&self) -> &Vec; + fn parameters(&self) -> &Vec>; } /// A secondary effect is an effect on a move that happens after it hits. @@ -22,12 +23,12 @@ pub struct SecondaryEffectImpl { /// The name of the effect. effect_name: StringKey, /// A list of parameters for the effect. - parameters: Vec, + parameters: Vec>, } impl SecondaryEffectImpl { /// Instantiates a new Secondary Effect. - pub fn new(chance: f32, effect_name: StringKey, parameters: Vec) -> Self { + pub fn new(chance: f32, effect_name: StringKey, parameters: Vec>) -> Self { Self { identifier: Default::default(), chance, @@ -47,7 +48,7 @@ impl SecondaryEffect for SecondaryEffectImpl { &self.effect_name } /// A list of parameters for the effect. - fn parameters(&self) -> &Vec { + fn parameters(&self) -> &Vec> { &self.parameters } } @@ -74,7 +75,7 @@ pub(crate) mod tests { impl SecondaryEffect for SecondaryEffect { fn chance(&self) -> f32; fn effect_name(&self) -> &StringKey; - fn parameters(&self) -> &Vec; + fn parameters(&self) -> &Vec>; } impl ValueIdentifiable for SecondaryEffect{ fn value_identifier(&self) -> ValueIdentifier{ diff --git a/src/static_data/species_data/ability.rs b/src/static_data/species_data/ability.rs index 61e31db..425763c 100755 --- a/src/static_data/species_data/ability.rs +++ b/src/static_data/species_data/ability.rs @@ -1,6 +1,7 @@ use crate::static_data::EffectParameter; use crate::{StringKey, ValueIdentifiable, ValueIdentifier}; use std::fmt::Debug; +use std::sync::Arc; /// An ability is a passive effect in battle that is attached to a Pokemon. pub trait Ability: Debug + ValueIdentifiable { @@ -9,7 +10,7 @@ pub trait Ability: Debug + ValueIdentifiable { /// The name of the script effect of the ability. fn effect(&self) -> &StringKey; /// The parameters for the script effect of the ability. - fn parameters(&self) -> &Vec; + fn parameters(&self) -> &Vec>; } /// An ability is a passive effect in battle that is attached to a Pokemon. @@ -22,12 +23,12 @@ pub struct AbilityImpl { /// The name of the script effect of the ability. effect: StringKey, /// The parameters for the script effect of the ability. - parameters: Vec, + parameters: Vec>, } impl AbilityImpl { /// Instantiates a new ability. - pub fn new(name: &StringKey, effect: &StringKey, parameters: Vec) -> Self { + pub fn new(name: &StringKey, effect: &StringKey, parameters: Vec>) -> Self { Self { identifier: Default::default(), name: name.clone(), @@ -47,7 +48,7 @@ impl Ability for AbilityImpl { &self.effect } /// The parameters for the script effect of the ability. - fn parameters(&self) -> &Vec { + fn parameters(&self) -> &Vec> { &self.parameters } } @@ -81,7 +82,7 @@ pub(crate) mod tests { impl Ability for Ability { fn name(&self) -> &StringKey; fn effect(&self) -> &StringKey; - fn parameters(&self) -> &Vec; + fn parameters(&self) -> &Vec>; } impl ValueIdentifiable for Ability { fn value_identifier(&self) -> ValueIdentifier { diff --git a/src/static_data/species_data/form.rs b/src/static_data/species_data/form.rs index 71fbe8b..a5fd7d4 100755 --- a/src/static_data/species_data/form.rs +++ b/src/static_data/species_data/form.rs @@ -1,6 +1,7 @@ use anyhow_ext::{ensure, Result}; use hashbrown::HashSet; use std::fmt::Debug; +use std::sync::Arc; use crate::static_data::Statistic; use crate::static_data::TypeIdentifier; @@ -23,7 +24,7 @@ pub trait Form: ValueIdentifiable + Debug { /// The normal types a Pokemon with this form has. fn types(&self) -> &Vec; /// The inherent values of a form of species that are used for the stats of a Pokemon. - fn base_stats(&self) -> &StaticStatisticSet; + fn base_stats(&self) -> &Arc>; /// The possible abilities a Pokemon with this form can have. fn abilities(&self) -> &Vec; /// The possible hidden abilities a Pokemon with this form can have. @@ -74,7 +75,7 @@ pub struct FormImpl { /// The normal types a Pokemon with this form has. types: Vec, /// The inherent values of a form of species that are used for the stats of a Pokemon. - base_stats: StaticStatisticSet, + base_stats: Arc>, /// The possible abilities a Pokemon with this form can have. abilities: Vec, /// The possible hidden abilities a Pokemon with this form can have. @@ -106,7 +107,7 @@ impl FormImpl { weight, base_experience, types, - base_stats, + base_stats: Arc::new(base_stats), abilities, hidden_abilities, moves, @@ -137,7 +138,7 @@ impl Form for FormImpl { &self.types } /// The inherent values of a form of species that are used for the stats of a Pokemon. - fn base_stats(&self) -> &StaticStatisticSet { + fn base_stats(&self) -> &Arc> { &self.base_stats } /// The possible abilities a Pokemon with this form can have. @@ -242,7 +243,7 @@ pub(crate) mod tests { fn weight(&self) -> f32; fn base_experience(&self) -> u32; fn types(&self) -> &Vec; - fn base_stats(&self) -> &StaticStatisticSet; + fn base_stats(&self) -> &Arc>; fn abilities(&self) -> &Vec; fn hidden_abilities(&self) -> &Vec; fn moves(&self) -> &Box; diff --git a/tests/common/data_getter.rs b/tests/common/data_getter.rs index cf99a99..67c6f7c 100755 --- a/tests/common/data_getter.rs +++ b/tests/common/data_getter.rs @@ -1,5 +1,4 @@ use serde::Deserialize; -use std::sync::Arc; use pkmn_lib::dynamic_data::Battle; @@ -10,7 +9,7 @@ pub enum TestDataGetter { } impl TestDataGetter { - pub fn get(&self, battle: &Arc) -> String { + pub fn get(&self, battle: &Battle) -> String { match self { TestDataGetter::PokemonHealth { index } => battle .get_pokemon(index[0], index[1]) diff --git a/tests/common/library_loader.rs b/tests/common/library_loader.rs index fa060ac..aaf82a0 100755 --- a/tests/common/library_loader.rs +++ b/tests/common/library_loader.rs @@ -65,7 +65,7 @@ pub fn load_library() -> LoadResult { let species_load_time = t2 - t1; let data = StaticDataImpl::new( - Box::new(LibrarySettingsImpl::new(100, 100).unwrap()), + Arc::new(LibrarySettingsImpl::new(100, 100).unwrap()), species, moves, items, @@ -81,10 +81,10 @@ pub fn load_library() -> LoadResult { let wasm_load_time = t2 - t1; let library = Arc::new(DynamicLibraryImpl::new( - Box::new(data), - Box::new(Gen7BattleStatCalculator::new()), - Box::new(Gen7DamageLibrary::new(false)), - Box::new(Gen7MiscLibrary::new()), + Arc::new(data), + Arc::new(Gen7BattleStatCalculator::new()), + Arc::new(Gen7DamageLibrary::new(false)), + Arc::new(Gen7MiscLibrary::new()), script_resolver, )); @@ -101,13 +101,13 @@ pub fn load_library() -> LoadResult { } } -pub fn load_types(path: &String) -> Box { +pub fn load_types(path: &String) -> Arc { let mut reader = csv::ReaderBuilder::new() .delimiter(b'|') .from_path(path.to_string() + "Types.csv") .unwrap(); - let mut type_library = Box::new(TypeLibraryImpl::new(20)); + let mut type_library = TypeLibraryImpl::new(20); let headers = reader.headers().unwrap(); for header in headers.iter().skip(1) { @@ -126,16 +126,16 @@ pub fn load_types(path: &String) -> Box { .unwrap(); } } - type_library + Arc::new(type_library) } -pub fn load_natures(path: &String) -> Box { +pub fn load_natures(path: &String) -> Arc { let mut reader = csv::ReaderBuilder::new() .delimiter(b'|') .from_path(path.to_string() + "Natures.csv") .unwrap(); - let mut nature_library = Box::new(NatureLibraryImpl::new(24)); + let mut nature_library = NatureLibraryImpl::new(24); for record in reader.records() { let record = record.unwrap(); let nature_name = record.get(0).unwrap().into(); @@ -152,17 +152,17 @@ pub fn load_natures(path: &String) -> Box { ); } } - nature_library + Arc::new(nature_library) } -pub fn load_items(path: &String) -> Box { +pub fn load_items(path: &String) -> Arc { let mut file = File::open(path.to_string() + "Items.json").unwrap(); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); let json: Value = serde_json::from_str(&data).unwrap(); let json_array = json.as_array().unwrap(); - let mut item_library = Box::new(ItemLibraryImpl::new(400)); + let mut item_library = ItemLibraryImpl::new(400); for v in json_array { let name = v.get("name").unwrap().as_str().unwrap().into(); let category = serde_json::from_value(v.get("itemType").unwrap().clone()).unwrap(); @@ -184,17 +184,17 @@ pub fn load_items(path: &String) -> Box { Arc::new(ItemImpl::new(&name, category, battle_category, price as i32, flags)), ); } - item_library + Arc::new(item_library) } -pub fn load_growth_rates(path: &String) -> Box { +pub fn load_growth_rates(path: &String) -> Arc { let mut file = File::open(path.to_string() + "GrowthRates.json").unwrap(); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); let json: Value = serde_json::from_str(&data).unwrap(); let o = json.as_object().unwrap(); - let mut growth_rate_library = Box::new(GrowthRateLibraryImpl::new(10)); + let mut growth_rate_library = GrowthRateLibraryImpl::new(10); for (key, value) in o { let name = StringKey::new(key); let experience_required_json = value.as_array().unwrap(); @@ -205,17 +205,17 @@ pub fn load_growth_rates(path: &String) -> Box { growth_rate_library.add_growth_rate(&name, Box::new(LookupGrowthRate::new(experience_required))); } - growth_rate_library + Arc::new(growth_rate_library) } -pub fn load_abilities(path: &String) -> Box { +pub fn load_abilities(path: &String) -> Arc { let mut file = File::open(path.to_string() + "Abilities.json").unwrap(); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); let json: Value = serde_json::from_str(&data).unwrap(); let o = json.as_object().unwrap(); - let mut ability_library = Box::new(AbilityLibraryImpl::new(400)); + let mut ability_library = AbilityLibraryImpl::new(400); for (key, value) in o { let name = StringKey::new(key); let mut effect = StringKey::empty(); @@ -231,16 +231,16 @@ pub fn load_abilities(path: &String) -> Box { ability_library.add(&name, Arc::new(AbilityImpl::new(&name, &effect, parameters))); } - ability_library + Arc::new(ability_library) } -pub fn load_moves(path: &String, types: &Box) -> Box { +pub fn load_moves(path: &String, types: &Arc) -> Arc { let mut file = File::open(path.to_string() + "Moves.json").unwrap(); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); let json: Value = serde_json::from_str(&data).unwrap(); let data = json.as_object().unwrap().get("data").unwrap().as_array().unwrap(); - let mut move_library = Box::new(MoveLibraryImpl::new(600)); + let mut move_library = MoveLibraryImpl::new(600); for move_data in data { let move_data = move_data.as_object().unwrap(); let move_name = move_data.get("name").unwrap().as_str().unwrap().into(); @@ -298,21 +298,21 @@ pub fn load_moves(path: &String, types: &Box) -> Box, - moves: &Box, -) -> Box { + types: &Arc, + moves: &Arc, +) -> Arc { let mut file = File::open(path.to_string() + "Pokemon.json").unwrap(); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); let json: Value = serde_json::from_str(&data).unwrap(); let o = json.as_object().unwrap(); - let mut species_library = Box::new(SpeciesLibraryImpl::new(800)); + let mut species_library = SpeciesLibraryImpl::new(800); for (key, value) in o.iter() { if key.starts_with('$') { continue; @@ -349,7 +349,7 @@ pub fn load_species( ); species_library.add(&name, Arc::new(species)); } - species_library + Arc::new(species_library) } #[cfg(not(feature = "wasm"))] @@ -372,8 +372,8 @@ fn load_script_resolver(path: &String) -> Box { fn parse_form( name: StringKey, value: &Value, - types: &Box, - moves: &Box, + types: &Arc, + moves: &Arc, ) -> Arc { let mut abilities = Vec::new(); for a in value.get("abilities").unwrap().as_array().unwrap() { @@ -457,7 +457,7 @@ where ) } -fn parse_moves(value: &Value, move_library: &Box) -> Box { +fn parse_moves(value: &Value, move_library: &Arc) -> Box { let mut moves = LearnableMovesImpl::new(100); let level_moves = value.get("levelMoves").unwrap().as_array().unwrap(); @@ -471,8 +471,8 @@ fn parse_moves(value: &Value, move_library: &Box) -> Box EffectParameter { - match value { +fn parse_effect_parameter(value: &Value) -> Arc { + Arc::new(match value { Value::Null => { panic!("Unexpected type") } @@ -491,7 +491,7 @@ fn parse_effect_parameter(value: &Value) -> EffectParameter { Value::Object(_) => { panic!("Unexpected type") } - } + }) } #[test] diff --git a/tests/common/test_case.rs b/tests/common/test_case.rs index d3d1159..8dff71d 100755 --- a/tests/common/test_case.rs +++ b/tests/common/test_case.rs @@ -49,14 +49,14 @@ impl TestCase { let pokemon = party .pokemon .iter() - .map(|a| Some(Arc::new(a.to_pokemon(library.clone())))) + .map(|a| Some(a.to_pokemon(library.clone()))) .collect(); let indices = party.indices.iter().map(|a| (a[0], a[1])).collect(); parties.push((Arc::new(PokemonParty::new_from_vec(pokemon)), indices)); } let mut battle_parties = Vec::new(); for party in parties { - battle_parties.push(BattleParty::new(party.0.clone(), party.1).unwrap()); + battle_parties.push(Arc::new(BattleParty::new(party.0.clone(), party.1).unwrap())); } let battle = Battle::new( library, diff --git a/tests/common/test_step.rs b/tests/common/test_step.rs index 7eab33a..f5fe1ed 100755 --- a/tests/common/test_step.rs +++ b/tests/common/test_step.rs @@ -1,5 +1,4 @@ use serde::Deserialize; -use std::sync::Arc; use pkmn_lib::dynamic_data::Battle; use pkmn_lib::dynamic_data::{MoveChoice, PassChoice, TurnChoice}; @@ -32,7 +31,7 @@ pub enum TestStep { } impl TestStep { - pub fn execute(&self, battle: &Arc) { + pub fn execute(&self, battle: &Battle) { match self { TestStep::SetPokemon { place, from_party } => { let p = battle.parties()[from_party[0] as usize].get_pokemon(from_party[1] as usize); @@ -73,7 +72,7 @@ impl TestStep { assert!(battle.try_set_choice(TurnChoice::Pass(PassChoice::new(p))).unwrap()); } TestStep::Assert { value, expected } => { - let v = value.get(&battle); + let v = value.get(battle); assert_eq!(&v, expected) } } diff --git a/tests/data/gen7_scripts.wasm b/tests/data/gen7_scripts.wasm index d966ca849215d545fadc11d01d52856168061807..e70de533702077b713048c3117cd415bb1181b85 100755 GIT binary patch delta 207373 zcmeEv31AdO)_+%Z&rJ7Bj>*B10AXfCMTHes#dC$puGf0u_3iqtyNH5H@Zef^-M`C( zBM>eLHqfBlq9BTbA|P@EMFlwwiVAWV5CIi6Dk>tv|M#l8XL^ze35UDxeny$;sjlPI zt9R9_S5?-uo3?y-eG{c~?|Yb{DC`hxaVv`{_{U?hTk(&LYSN*NFDCz#TfNSMZYLqH ziboa`Mf!@*e~z*WGAWhE{&}lHm9f5~-inO+zD?|-mUEce_1^n~y?b=My<6Abo$l<~ zr_;Ti@9Cvd1Hw48X_ZJm2}?bN$Z=RUnmu9Fj+b#&W`3bIYa}|8faES467yQ#%p>5j{@?T2d0b)UKUs6e%!#bPBg$BFpUZCS zlOW}p%UQ1V!E@OjpX@|FCFs5T^y+r+ohX{FHSy@wIcv^5s!TU8*9rfGxAiJqXVbnk9m985Q~lL1zrIDman#_X&AZ@#Qj_OVm_1-RBF+}E4|EV@sJ1)v@9FkBqa%H~# zuf2M8y|+)NK7Z|XFY+G_oP=si15NEx*xS7*hzcf3CE7|7VL8tF*nPiVy!BYU&`rv8!{2|x`pIN?>@%eX;lk<%Q*&)K) z-?LiS!?X2;?0iaTLD5vWZtlc|$3y zvx()xtW%L`v$v_lQ^Vvod-UpdU$1V0K{n}=JzR94`HMuFx_L!*b4f%k%nn)2())Do z1%YadN9&F9?6CP_b`+KWkR4*#X44#JNbl;_C($d>p6r^ayyx=l1_b=QIZf>5rYz5H z!j)X}shm9XoSe+x_J$lyloBAN$E&=fbGN${gKBC(jMeG(yYB0DyA&djqQse^m1|$j zZbYqA80`R{&)>{O>FgJ&E!~U+XrM0-gw3mSPi`Q@T6(uWUES)zoR~#qyCExNm;L9m z?1Hmp{aNw6w|DN-_0Ibosgw~<2Vx!Z-pp*C+rSLwo@Ry{G;Y>_;L9RK)2(-(Znt-$ z-r}FqP{7^t6&l)xW}gOO*3fLtPGY)w9XsR9M)X!fNJ8k2yF1^h2&lcC(9*qq6ooQ) zJ9X{V>%Lx!Vn$Vh{Z{cAQzICjX=)@-XooCYM)m4^ZVIxnpBY-ROVU?@_c2 zkE&*7dVHRA@t2vQru*p+eNSge|LBKD@n~8`h6jbHn5v+t$LI6drR_R0Gcr^)UDN|h z0M*bx`m$@t$cQCspw?4Xp`i|fgr}vYkwqbD@MWSJjq1u|Mq_{@-~m7aB7zA((I--T z1XvPe4t$xhjEoG_4VnFn=x(lc!^(Fi&#v4dV#!w;b%Fns`fJhFu@$sE4Dt z4HsuYU_;Y_hEFtN;HAbz4yHMo6dvRrUr-Brf+H+%lG*-Up}ytZ=e?d3Ln%K zdW6;}8^cc~9$Ap^MejuW3V}?2D8_Z!n1P`}A3$LE^A(_S6vd-JG*Irz7yXEdaiUtO z73YuYPWmH!)1Lsw5Kze$6(2zgOi{;WKUKpQl(~b3ra(FikY$Se)HJSBaex=|`E(z~ z93&G&W^^cDOM}SMwKReiZ+RN(Cs>=~pCU2n119Jv{o$43!%*V8F_AD2qSK8fKrn{T zh|d@F0ZgbNpGOU)14&J}hAMo(e5?sq3C})G>7ax#4-*!V&?Kl*r4iJ~(Kqf>Bb4(b z`jCG%g#|Sq>%pe_rlNYLWO?({7>gDcmn(so5}mSldE(pmP{!}P_rTWc^#E5qox0uI z?c6@f=R9!teVw~>vhAz!z6_YTeR|#ZfRplwFH^pg1|m yQlD$v=65FU@`dJ#%|^ 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�{;$)_ia-|jhX1i?NdaXnY{R% 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%0DuH| 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 delta 243062 zcmeFa34qi@_CKCV(%ngS&rIgxW=OZFfVhIXh>AEFWxdvGukWtwcUe?a22WtU{w_Vh z0K+K|B%>VSfuI6{prE1*F6yW#ps0hQ0-_?K0wSWK{6DWM=}!0bz;L9v8+#3q z3aOw46?E%ApkJRGu90|)<~?ueePz$yw+-ms?}ncJdSBi1n%)C?-qP#3fAp3#YLRpG zjs33Yg*WuNzPF^?ZRFPHMCeIg^2xQm`&@JF0Ew3tCQGmA)xUSo{sVdq=r8H%^>20S z+h@onOt%NJHZ6*%MnSS|uPgdo*JnVV-uU|}^@B#F>3g~~qS_8rla?1dtE}hb23Yz~_;@8c*%GdVl z-}CD0dR^1=irahQ`dab0$rZf@47jd$&zpMn8*n=Z$gcz$n+S}P<)9TPO6bp3nw`YQ zZ}_VSViE>L1O5TZUwQ3~eXi`yq4w=_!&Q=OZ&XgP|DnV%yU{?i0G}&o+pz^jGM;@E zDG6|x&X>m*w4jouxvfs)CB*{E{(Y_z(8R5a;1# z%AlM$sB&BM82lZSQ*7tvc;Ewl)AnwCkIG;7;S;Sy>bhKG@HQizUWct zG$(pAihrfH{xv6vzrXAHT+y#rzuUz~>k;)I{d@OA2Q?U{Y|mFO!{2qe_d$&A%!@m* z6APNOaY^K#4bBI>`drtW=A$CNi#rR0PAq5^6xE%{Cl<7{&j_>xyuSp9EO!KG%BBQb z2!V{*_XmpXRe@X-eiUepzd!N6yYi35b*loZe0h+{&kVL|$p=+<(~W(5Uw`8be2$W% zvhNL&q~F2+whSI+mxT+$H}tu(FXtrL{yNya8E>Kp%xSpd^7c2Bz@bs+kiE-Xh6Msj&Z@Z~?zdpThxU#oP+8a6+lr$ID1^$ZH z^uFP$-u-MVTuRb(yLE){>Bj&5HF6BD2S?iD@9aoRca*(O zDkupF*fBfbD0JR9p&+-FD9G(Iptn$pH}!+q3g$1NTJ{CeD8Sqb)CHR-YhKxFK<{gA z1cU1Kqyn>;6Cfw~ROo{VxiPo*n;}$g@68ErPX8~c6K*)ce{yhP3K%XmnT?YnOYS65`k7iBm zJ!m$}$ZOXh63_Jo7eKBcbCS)khE#}Ax3)VM7CTE$D$wi>g|R|Wj}Ob!qIS1}W191F zPv(WZ6Mv)c_lhQU=ETS+ThXUKaOz2Y)6=G;AT!GGOloG|Sr}u@>}iFuL^G-r?1_nh z)sw_c?|%Jm?1!7`$fSrAkkp)jqU7ZTLV;ZVFE2;Q4bwkK&dHV2{!cdnDWIx3IRP}H zW;tjX2!#S}V;4kTPL87F=7tjlMv?)B{!c$H1RsC~A*cXBc(7`l4`GD9@dpK=P$+>Pfs+3(uag7tK#})Azykmo$jx(cL?0NbAWcptZGeoV ziWbVg>5*`|C8Rh(Kn+RK2zieb>G%uOlKA`4CuGGxQ7MJKOJYtG;Kk{z$SQh*??nG} z?;rCn_^-45RJB!t!F3C@Xc^6KRTNQmkQ@K>NQ5ytqRXkOca%%217l)4ZxsU ziIgBv#Vw0t#rVfm6}+s6LaJ1PD;59Y7oDo)Uo~&c`R?o(}|=B!?=6 zTs+YX!^S8AAvK`!!5KOzfZ6J->-Ys)5_B%n8-Iv6{2!+#ZU`hVr$2VjrcE$wE;)G* zt>Xkl{6iOji($wiMODyWASkH}Klq0^6{0kig zl7QH%&JsW$&j>H~pG-({9O;)Jd5?)QE*-$LU0(7-GVlN5y`uBlO?WC#or#k?>{QD`#S@6G2&{l# zKnGJ97+`aOOb9H9nGYGE5V(*jp@4F9E>JcZ@j(cT+J-3%OA{7iR1Q`;h8=?oDrjRU z1oT)yB%V=ONI8}eZbyH_Kf$4f0cba|M?#MPK9ll;&6NbRDk^HENJ6s4z5GJzH#9c) z?BMlqdHUThOM#w!Zs;>W`bs{>xT^PcaH4x&eRZFnz54a*b-Oeu7{OBJs-CVbtP17A zAsx`~#@oG;5urTsjQdX9rjE3`w`_6rs8G~>03&l{Uuks6aPQ$~^+VUU^p?ih|7_W` z#n@1B>d6(o;Xw87Nj{-;movVqu(*{pF4V-WaOJhVZny^Pfqs2%gekpU8t>Fp6&6LL z387YQ4QJ_t1;xipcBs%TxT#<7TYBFh468ITi3FKbZfvEy?b22)T1?7<1jo^FE*??X zQkJUi@vWMlbWaj4M!{|M^?myHm+lQUO~){0#wXh!v?^g!>^-fTv8m3M5rt+eX`Pm>VbkrKTgSWIA8MHn6okA2onF=F>OOF-9|$!|f6P~ZH{EzEo;_%|8XV@)Ej%E+rR>{%I%uw8(S5jpEvm{TM6>3e_&h?`Tby<4Eo?FtCJ?e}dRoF(B z9&_f7Dr_c8vu)O<6`Nz1v^k2+b%LV{V-e}`BwCI&w4g|p=7rii<3<%Wb$X62%#o$} z&h?`UTeXm$2o<^bfmFS&#@ehWoSFX80_T1_m!*Xh%vj-*q1;p@?&3VHt9tX2o>#hy{m&hCV-c2$+w3i|)`@p>nPjoZ ztkCQ1RaVPhVJp~cY@&RZJV72O+wyq%J$aM-iM&EyATO35mYW@+5f+8zYaBN6Lfb zz3eMCRCeU|<#qB*`3d=H`62lkdA7VreomexFO=uYOXXU5x%`3rn7mScU4B*mms}&y zm0y-Wls{LtD6cDTDeow+Deo#@D65pWl{)1OWsUNtvR3&}S*NU2HYn?rkCepw%6rOc zBjY$038mar$-b8HcNmOam&WY4gt*;2Ng zy}(wnr`XHvCH5j)#%kDN_Aj=Yy~*BUYuMZD9riA(V{6%a?0vS5eZW3s>)8gjkxh}O z%G2cg)cfS=@~7;6c{lrn{lLCtyVxG~Iort++u6@-xLheeAb-Ke%A@5G@?g0_{+bPw zACw0Lh6E}DFDU;~cFN0@vC4LNobsbQLa~)^*EQJgY2Go>QJzmMS&MGUZFc7<2)OqUb z>N9Gs`lh--eMzlRe^%G1pQs1Pfwk%@YGSqer23-zy!y7fN&QHDO5Lh{p?;>WQ|r_v>IStcuuc71tqZ&x zcqgzT_)_4NKy6@J3TH26tiSn#93@ZiUR zs^G@Jh~SpM+kwr2@xe`j3BmURcJTAS#NdX&-NBK;QNhu{F^S;V;9bFS!S4e<1bz&B z5m*!08mJ4e4ZjzDKfEsdL3ns%NOWfK$>8kZGr@Vm$AZg)3xmtF7qowA%e5D^m$XNM zj|LwPz7(7rd^tELSQ~sI_^iG}e@=g1U#j1$Ptm99)AalF>H7Wp1Nwvdi~39Y%X+Q; zivFtpn!ZAxtS6q)U)SHzSL&w@2g-V82| z?uq;qSrdFN`fl*;;K<18;1|I+g5L*M2EPuj3VsuOE4WSDuIbvys^xgW8!HUrE@T9O4t`6T5t_uGUd{}!_o25OX&D3USL$zVrB<&4t zq?TBz4c1m^cWJA&yR|p9d$q~hi@_g5dqUrZKGMc$>$UGg8#G7zOuJ88qCKZQuPxPT zv?1DNty=qBv$ZYSC~dU%bLb0gkhWEusC}u8)4tO7hQ8J+wVm2kJ|3g9&MU7A`z*Iyb^vj{91TL_#SPFHZD9qJTyEk{D8JBc)#|L z_MrB9;Elk_z*Oy9ZIV7-pA>Y0)xmp$djdZP_6El2 zt>3MW)JN$P^x^tj`Y?TsK2(2OAECdakJjJStMod3Wq5V?&G1{{HQ~gn@Y~^c!taJB z>YwZH=_A9R>Z8IR=%d4*=wrfL^q=(a^&j*f^*#E}`d+;vJSaRk{DuC$UK!q?j}5QY z?+SmcZ_+pF6T<6tJN%J8F}zN{JN%ixS^r73_jIs93)e^M0e)`?g&+ki=jhgxB(s#bwF@6niFys68=S|+~>^-LJ zsZFoE`qs$zCp?!RK)aTVc>msK-y2<(D61P(w{p(C_bdn#poQ-~vu^cgPwc!qktmz7 zZ1pEkE}S!ZY$75{k+5VvR#8(SSw)c#uzE=x#gVeizVV>I&kUMM7deU_xr?mHrE;mM z_Q=7kn1L>GD>Kke)?y0o)Gl%}Q?U}|)@?VNYPqGATM|8u=7aJT5(gVsdz_WSMCBrr znJQ`-t7wwDNq}e-l^c(VDYu%HgLHV{u||-py8K|z>sr)$8)0p18HGAhgqBqp!*P~d zYH#TJCpOsLaPo=v%biR%$j&|Kc+kJwN#``-Fd}9s1&`16(qFf<$9B$7{31kXmLbFJCpMZ03s3r=MubgSiQtwdiSWC|aY4ubVI1(#;A|Y=mL>;(qJ8Qq zjpcv|U0eN+qCtzMnM$Be(8M?T@>3f-`l8L$IL%OO;x?%$i?!NhPd)9w{8nnv&7KKK zGg>8NiJ*T{<|}2zSdS9dI@GskErh}&dsYEesHKd?-|X=>pVBO;i<8PM0&5AAGw9~l z_Qhv(YMgmo_NOylD|yPMdRDSgCHuFe2We;1Eo}Oou)*0Zab`WwE{{8ltagTV_D@-c zvGyhgW*hC$Q&!{s-E)&ijQRfExsN8wK6=}Ew>E6BciPbOsv|`sKWj_yor{>wU3uZ2p$V5LgVgAyes(yIl8>wvXoS#14 zcF$MiUz5l}3nTsD5U}Nh-&_i-_PgJlUW9paZE8$~EvuDa9PRsmGXRg9ob%VH;Fplq z3B?QkdCoPg883;d5oUsU{23~lR+ns#T z3QWk#i#oaYg%`)1wnKXq|8JpAV3(~cJsi;>iUJl+^rdDj`;+m~I^trk9YzvPL@(mg#z*d8wi9 z7DII%DvwR7h6)1gzVt_z*rDHF6cKtUXtI2AvI_0~zaLj5%KTFbsxMVO4M|M1e$ zNy>p2_*~Eg*Q4U4e>_&6F__i>&9dm*8G|uWdv5#l{(U;PT|+t-n8AZhdi6&}L3ia8 zNV86jE3spxW&75_`+n+M`=o2vecc^C4efRh+h)&6oDYslSsscxv8P zqZ4IIH*NTG#@4O7H**C%?bQ*xUzk6;y0~oF)5}Ly&U|v!E4=*m#Um!xRqx((McLM8 z?^-+Z!LNsZDn*K=vbvSe){dI6J+G|d{>Pj*-x~BoG?Is930AQtGqw%GymyO4agEjM zgO#}3$4ygO&S>Rv$@q-gnUeAFF#C>PDOTJ8axoO zR#nw-4Ze8hNg#hOvG z_OhE#mn6VxR}S6bTWLb@>V|{uJ@gC!tJkb?O-px zwTs>Sw(frMtskB`l&1%!&>O4l_wSUaL;8DJx{KZzM;$D+PpeSRc?YvMaayeC($Obzslg zn+AwpVC*rE>(tkSGhN#{#B)LNhB7hqYm4 zO#`Hm)73NX#@ZW4PRBe98P%Sxupgh+qtPMo{b;xYiV>v8%!^hG!6?(5%@srT??^VN zS+Z$`l;meD%hQ79S9r6Kbgv+6)jqRx(zJIb4*Hx6=NS8qukQO` z_v+%ZH)^T|FTH2O>m4G$_Yx2>CYO@)rp0B7VzZc{l$r|YzjJ&?qIrBmsk?5BTmT1+ z{FG0K45F1pZP1!Wz$HtCxY~D3XoHD+e8N=O9%Wx-pQ+c0C6uw&o@D<9vi^bnIe6{m zi98qa#l%juRBe6t4)%#{O!{vuSZ|nAjprffM1VQX>459r&M|DWJ=*!LSdc=}*JIWC zDdBPO{2JA&Vy^yxH-lT`RIa06-q^mV`gZ_*e|1kXsq#Ig zokscN>j>df21ca;Z*n_=yXI*f@;&T+_x!OyxTJ~;7T)9Y_S$>?*!dGNW)O@LIrP+n zL)RXls7`krOS|X2*B$l5zHue{R}FqM zRo|UyP;sO?3+Z=@7FR!PKVEQL--?DPohUzhUrLu1(&0Fx{;~SS>MJ~j< zQ@@)rXw#4K z%Q|~ts*YRoNWGFZbIaw*(RS(Me-uBLB$lF6!(EYJJ;DrE)gIC{_3y!H=o)4MX4}4c z-i^>b#+p|4j(Pv>$tCGyVdAm)()gX-Xa3*Z1z9r6NzX88f_M004q9f*{C`)_Fqw5d zfQ;kgPo#1!TPI*H;8tx~Msu>oUjM|!APvO=&suN^PqmfUB@=`8;svMTZpVU)@waRt z{hh%7Zd}+YB&Zg&6HlIKU-0C|4LP7^KXr^ew~}Il$7YEM&aJG-a8gir-x~SgSEFgF zdS6Q0?|~oYW$1t6n)UK*vrvCt4zA=-?aE=XI^?Kg69@TDu>q~^Oa9uak><*8pt5E%fi#rwBp2eLSxIA`ZO&V(&Hc1UHfj$8cXuFqDzbP=tAKYDiBxVh_B z%E*kt(6IvSA-aGQc7oS=C)+bmc z&s(r(`s0&dyOgSb{Mdx==Z|~WzOZb_(6PbMuRgfqv8bJ2`!^_)n`(O@k@RNmLo6h` z&VYT_E0@{tzB-0x;p*2ebQOqV_kFF*-t(INYvJw*Kd!+XR#FLjnH!3WUc4fo&9-~3 z_!JU;?&}v635&*KoiX^=!f4HWoi>&H{Q7ZCQ==JbMpFZ_tv6buM$b2{FomAS1{W(} z23nG9EzsiYM{+PXym2A=I$`DYG?-~Ce{|!!!H6||IaDfCYvqghg=(F@>cU*1DiNdP z778KXUw22A%Bwk1)O3CDteLy4qn^0>N9uk2o5ctv&V2J^gc8@i=?f)l@Z!_fs$RiZ z`^2|sH8J$9voVNQ-|B&DV+~#Zam{S}mEXSFowKNIzSG^F^LALJA=q2r{+0dt+X;8D zT51T(-N9D8^XyS>KHm$5lGea=die8i@4lTQXzWd6Y2EF>;+eY3(SOsmX9$u-%z%je z+E=V?$F|#})^=+ujEK7o^wCX6%gNFcRE#X_CfdKTwB&1o32tmlw4|2O%eEJIap#^QN)>V z`S7S#f+ZWMu+v|mSq6k=fH3HTLVL?cEsl=#%IvRIM(tL~+VK;RA|Tdfk@GWKtpRFj zXs(fXyXKu%NXAPPG;V=r=VUfJIiuCdqEY9LsW#_jwppIp)pED16S}2Z)$6HKMo*pG zp4z3_)Em^g%%0X!Pa8J2IzBm~9+}t~$$n%$T?ShG{;wE^)S8-LaI z=FEzu4g3<>HaT;rA5)JY>oZYX?^6E6Iv+Cin7k&lS&iH5gs!O})WfV@24?M&n4M@h z+nAry8ATZti;@)!a*{$mRA#EPeW2Vl5$91Z2|2bnc70Ot3(QTL4O=u(T_{bXJgyio z4u`1oU7-kz6^WplY_amV+Q(wQ$zfJ)Evy)<*_v1I>m}7p20&#JXI_hS!S+&SRlkY~ zoU^U#cq2+TGD>DmrzgfHcYcx6L!4?(S#;n`D@40Z<4RvsHkEQqM?w;6yMpY62-CR( zPzik?Z-NT$bB45|2;8*uUZ&f_FV1$u)-Ak(l4;LbVrblv9Hfk4-wpW9+^V6=#59=v{6(s!+P+#rnz{cCn zzd1EEq`u#rakP(WC;Qtj;Di+uJBBa_aqHva^e;b7gyfS{kSAjIhM2lD*(V`FeWM!I9e259fnBteVIK&7qSYf}gRXxsTQ=9CMPT?PE| z9UV@ckb>o1BE#-oc1&F&ZsCn*K?!sW)5@!4o8sDf^r+ySMaBQaWp6 z*TnQMIp2QxCHK%uzQTK}-qdZzU_24tP1k&2+)%j~9E_s-*O5?kH~f-{E^?lI^Nypi z%<%~pG4UuW@H6qjkmuziL7v1fN}g3Pf4=u0SlbF`??4=0P|p}bT{mP7mmhvI;HB~X zlfhC4WHW=@u(T7JgbWL}%nB^?IXTcQ?c}zI=PFrSnn?qC2Dt#91r>URv;$1DXF-t& zpu`e}Ep#dSR0@2p(DJs?Y)`;Am=;Aj5dF@veEp zLy*AJ&g9K_*W|*C_q>rATe%L!y4T*jHBjKvKefm0W#Y574DmLyL?My0S z$3)lBJ^5&|^_tVBm>nfgAMJdJCvxSR&htg=c)CBk827W^bc&0`{bahYd(*iN_s31A zWnv7hBqwv~CcTkl(!MwvGI!cAp$`vR)McG~R4`Jfs6m_B=al8}@BV~dvkC!CcP!@~QCF7SlN-24= z5Sp+VEN?GK@my0M8fRsRp@6~8Va*b8b_sWcRdj+>2;5bRA*3^I1!#c+IIAn@(s)=n zt8yu_%Ru0cnU-=3V4CX9ap}SwhAyJ$M1t`3jmy?;*b)eQki|7~OkBZnMgkZ8-f6NS z)TJDSZ(&VbkpOI~owbDb;e|WWpp5;e$GSFCx$ zYd6H;r_%$^Q{SerfTvG8VVJ_*#$=-@sLu1~afMGBwiarF^ql;PJE43kY0mKWS4_g} zBucRnP^GEykJg(MAXx6vS}ivfPHSumG^Go2)a=6pO@T2;x-N~R5~jpdF2EK>pee9b zO{v_x954x2%$!ypBha8{gS6vg38Gy`2}`Npa+V1ZP;4JyfE=_!K#?|AO0>h#>HIS* zPGpD!CXExU8Td>l{t~PvQ)F(dpVjcRZMj%d$+V9!`VK z);vsn&(zGj{gTxvO6zy?8#K!-Vo+Ff=C^OTd5c;PHX80%P=n;t=bRqn`B1XN)QVo-`Oz% z;cn%vMU}DR5@z5Cjp0`XL!d`uA6c$GWODh3-55y}#AA*R|?+AHVA^|G$Rusf*YXJtrP zyJ3~ip4swQz35SCJUkG4I`I@6JKd*rS+OTHq^ub)t*F?YH>6A(R#}M$S{0sA0j`_T zHR@4$J6xkVuDVuLqNWbDGtk*OTmF3-v2H4l<(grx z;acWF=nV`h$Bc0GrJH#}<6%-?VNzesLt)ZzxVgN8Qj#!8E_A3TgOq`RHl~Cwpdg~n z;Mw=c36hc$s!Ke}zc42R{GgV?Lt5}! zyc7kV{wYpSgg#Zn|0p#l>j@awExgRA7We2UhqRAD91t@wCKW}r-8UM~F*pmxbD`6r zQ9%-DRi3p*H6w?1`hq})m0-fvh$<0~OmvSqXQm{=^%wzmE@M|rE3tvO86atp1<9C5 zNB}A^4G%BDo%4?hh!`5E*>X2)=~KU65m# zg*@gD0nF!onV;gz98linOYtmlVGd*<$(NuRI5)ugk{FD*ES)dS+|wj7WTv}|f>@DT zZ>`)+P%$I|4Jr1GUF_`@^Atsc1@SYyyeN7Q==MAewI_ z&If%6N({Gf%}v&q-o6f)K*bRAkq>0d;+fW7h_s9y8}yKjTI6ODcIc6OGoK`$KU1lP zR+vp<_5|-a&?Bb+B!oc)%t8(m03hj@=Q!s)jiPswpd^_?(t+D% zgbz$e1V&DS3P#*>M@6_$6PZ?>M;cMn1jo7_4o!? zj|tL`zHaEvo|Jm*7|Vf5$t7f>q#i>YN-USuV~&{%_1ICU$B^mj6dhA4$#w$fV;5d2O5sO6(*A$C6u4MTXSlR$FBmKyX5M$9&S2A_l z%1*jcC`VFr-i~rpM<^w`rm+$YlxLR^SC%t`HhXDy2$`j1mdGO_eMO6ru&1wRJ<$kt z)s3-#o+U{YXU0@;7xofL;7t|uV05$~JHomxWyWAwA1S8d zI1@-0IS9algoBn$_=6dnI?UOAk(932UD4K;26b4!*sUHdbQT8EwMUrjlb6jDbdG@D#pkhB@sbhJ#|@FvSAV5DGd=t+4LF4R9gg*7B`2FfvMMN&89ex6*D5jW4tb!qhKA~)t zw9b4@ypCJv>6}~KJ*HzBQo-O9XT6~uFV}wTK9okO1Qg{#pW#_8xQxO<*Nw^A|9$> zScZE$vRx_PBlNd+*+TyLv$j<(ImvHNE3v+$hQfe?us9n@v|`o429x6)uLgM zDI-E6qO*7cjbZ9txpszW?Tr`^xF7I3X`j62{Jm|)l;zSB%-&4ap5>}ON; zc}W&UCmAeSox!4&Uy=7+y$gSh4{0;JVH4KYEUK4eP;kEQP9MW>FJR0W6k&y}n2?9D zR7#44wiXzBIIxLMtWqx0S8!;V4en~N!JkEgl&p=Ts%f&eE@L1K%i4I3?d>wm(?6Zq zpX9;|jR{l)Ys3tjUy*oE}Fm4hEem@L8GX0R)t^*~q3-57T}mkee_ z!7F5(YCMp@H)&%`65zRIP)?z0LNp)|#`=-Yi%nU(|Mq0xp}LXONUP~SZKPZGJ5nCl zU&7S}zg+I*u9KVmcA1Fj%RtOE$~b&XZi@~u!5F@$Pb?_7#q@4?D&8-7@qQlYN1B?4 z!P;YPp}k@{wOB|kVu*VsWBS-jI5aL%P)y=Mu?YB(q7$SroiRtV7Kt1;!5+*p-2o&Spd6OJJ1ijM& z?{#n2V5lcpRqx=}*kTEx6GDpog2hK|&02$}Tx6|Hbm;|Pt=V*k5cV2gN-HO4{tRs5 zS^O@_g}1;}zt9E@*w`{NTduUm;%JKC>kNAf~Io6Qz3Dm+1u=-h|Nxlm7E(zm}Qz9 z4liwPmMQ1+d{?7nW7Yx7#zX%LWP^|TNXf<#$Y6;CR-*mEWU%~7^-PKMRz{4`|W44o7rIS*y zYC@y|Di>SFNb_PbO({lb#hd`Gu9yjGAhewCJ3&mAOAizmT48bUg@DgJ4n}?RXp%vg z-Qop|1ZE62m5;{^7(0Z+NnHw^6{0vg^!SB2fg~O#Cih?jC zNL}$rkt`s?Pzs7Mqn08@i54yP-6gWh9&q>yEEa~vC(+R;fE3`sV_}e-OgsWe9193T z8k%t!Bslv#kfc)}3C}6a9=h2|z|bSJiGyp*P`ODDX)fLQS z6CvYNLjh#9PCHG2~GZDFa6;m=}*k zAR$km$|A^&qoYzWW0yrFyraG68Jdbi!LcQeT zQUs!T=p1>vm2RF6O9JW#i7MJ$)K30`2t;EOdpr5hv0SKX*S-fnr&R!sTNc`b_<``#sDe!E5rCyIX0kFw1-vebu_)yHPAY}c zqI4PM1fj7n*;1zw|0Ec}L-TRwkHkPJfXtruB#h>W)_0Po4sH%Lv+JhwbuFb$t*5czvtX}GCN7Gc+Z)C zGHbyec2=CsTAsY0Xl&LHuVb`RIwiR81&fe8d?^(MJe1^uNGFg6h4nE+?Dh&CpLD~3 zh6n>XC;Um)8WpYls|QTPQV(B%x$z<424ct!2b~qfkewU4V)yhzBc1tUnCUF&%2vzw zj&ypxE}I=UapxqD4CMt(PPnEAW*vD2av$IZgDW;;JNFE?#mY$K1a!D8rRMoi0^1^ql!BERI_`9wwg1?$hZx25o!zIi|4x$Y)^048ym_V@J7dC6 zXXf5Z`XZ*paeApSWt44wS5hE0?cp=PaHCmE9FzW%ee-wiWV}jr{}0Vi6r0|$EP#U} z8y`}0+`I&7#^zk>SL8ThuPTMO$@`J99TY-3^GngLyWQc(1Mlms1D7XAoIh(4S?|nyk`+6zAEy*LElyCHII}-uf58!Z z&Oc93euu@O@!&A$Z|hkL(=}{)5o4|J5>A!)%1-zSlSc7c5nnCmx%JGH?;hoRw4NQ4 za4}^m^Oh$}5~2>XD2NdGZw^nugrdw{Oo9EJj(mJ+nJq^n->c2Bh5!?WEQ>mnXFA>=O*CA6&1xj-7BNqT9+)R zC=v3%w3tI|l@@r|?ZOir_*pKhQ9=;=Ur61FoeiK`YJIbh6mdA^OgJ}U0JDW=Wm$VOv{2#8WUY@^!O5qKaS{i%J|AM*TB=}F5i)}A}vG=iyU_W!Q zm74z#mkUmUBQ+QQVQJG09Wkn+LPFTl6aB?W(MKs1EP}6DUi@n?ZJHH0_Z1Fz+SF5G zh*sA6l^AqXy|k&TzbI|$>Ms+QXql$JtU7{Yb+$%Bdv;FQIqnD~)26# zhq-PrG0KgP7YY?xT(at@|1L6rCPJaBNG~FoO<6R4of!&!irlJ0OpG1~E{zX`3d|@J z%Hbmv`ZVDh!>-bHvM|>fp-@rJ7Ybd$jmka}qk>XQ^xzML?z2k~3gy5X6AI-OeW6ee z`yfK0eExVSR9q#=P(KvvQsSUPp-*xb%JhZnDHNJjmaWCyM~GYN=-2ZF8z{{VA*0mW z-<3>^`kfRW3f-@ha>se>wBCHXI-E{^5`8T>CRUzR{jncQuXI+{(#2UzYyG7+IGs;t zO=M$<)A@XMd~vGFm>i?X6H+{5o&M*uj%-VAnk z_hE%KozPEhxp&=pXTu<+`QNeb*hua=z0ZR94-!$v%Lq8~E4&vbmy)1xW9;b(Vbzj) zf~`s4*6EJt2a;?gk3H`G@5m7&5uR0og8NI+QI`KqgVW*lx7TzG$=uiMQJ+)UeQ4sl%s&H z6~L44!{9x|!@$%G^6eB6ihtudS!C_z1XwMO94BqyMvRUg(#*QE$+ZTbS1J;8&?z;F zeH}RB;KCdX(d88YbQ37pNx_MYH5Fgy60$1osNm^P+GinFzqC5;O7!nqE|y^!L^463 z;lvX6Y&hD!f!%>3$;z8TLPX?{FTp9eXw{K)iKV&wz9We*IV2fc!gW(T?UOic>PG;^ zaDYZvOmVs^hy+8r_Q4URbhKVP&1p)lmw@kCQN|4Gs0&xB~M_nB~hooT`s zanU@qZ03RB(s&atFe4Mr;isE$vEs`x;i8_;gs^OZWY1KG0?srCA z#!iYZ-^`T*Z3Q0Tym}dnowI^Ul9mJEAE*u-SRj|e7(PI@(>uSYZWZ2*z!nAkHENJ@ zao>=@i*%B;_!H;Ezp`VFOKUK12{oAhA~krH%&K^$f;V{RGiOKx4eFLrgC7WQUxRg@ zIcxsPrbR11uOaP5+m=Q;124xLnWL%18-R2G;Xte|SR45fo)n8EswkA^3)LNPur00i z0SEemXx{k1qJqGp^Z4J8i}iAP-~0`+Q}1TY$Lwf%#xQ68)vP!UZ@@jFlt-Q6EnttC z=6mbwb;s(-Ot#&*q9HCCV|*g1@ul*$ijH-&v6} zx|Bs6YTnQm|I zUYJ0xkr03vhzW#%LiefkM~esgn(Sf%PpL=aA!^7U#PBUBR6sFY5_)I6l>US4$mfy2 zYf0ze3@f<@UY6}g8_np2SZwja4nd_9ep}cK#YhoopahFWuqQC-uSUtiP*5X^O$YRP zc7zIWj(It~J4RR7`yGUHL=fq973!6?a0O`$^sz|2$#l{U2enbIfD^N0{t{SttmTpD9ZJa5}vp7vY7@bQJ@s z75c?fFWB4Amy&kId!%D%JtINke8d%w*rG{7o+h|yBHIVAJ9|K}+$Jpq03277{f2fs zqO>nu1ajNB>VgI+k1<84Exx&!dY}aq|24RBpu{K?4$KulkC_xsuwecZs1xUd}IA-R1Vh9Q06AJQic42iS(0SqZ|5RB*cQGZB9;AyhC zBsmZc@w)uJlc}90m|I0yXStLj37h$F3ky6^>+Mb zNv85qTZ_NOQ;0IG|4zE}@&VQoxAQEQbdp`wwFxc`T8BlC64#&Tx_wHhqkhE z-mq9}d8XKS>P#P8j{IS4&b}3?k9C%}5 zfxMzG7RX^AL@bccA74L;tEAbi9}9FT;RWX5D2gCDxR`sfY(MWgzCeC6y>XO$`-aka zStZ*V%B@t~nw7LtShqG%njJz$>A6B5p?5BH*o{ro=&EKO3*4WX1Oi_$rqDZx#m@3p zis^lRjNX}w!CmJ$24{CGYnh870JWHoO_3`Hb;AC%T^5zz1bx2-dO_;?nbA+Q00KL zcadD|ocFd8b@mQnZBKcK5Q_!qa4?EO&<_Pdxkf|)jh{({bqj^X=oLsF7BlW0GM&m0x@@SBPL3fw3tc*zqzP-YM_Q-Bnn z*jOIGo_y@ElunSAQ=8Kztb)izlG;m4sNj^Yyacxk`7Q2vKSp`k1dOf*r$tgpTlf7s z**eiZ!g;4NcPIj(px?@&thx3O$7sZ8XWLNLNv_`M#D}r7PyIp6hzm1XZvB`lK#mfp zyU-&Z+rit?KLZ~@zVDs;hOy2xZEp@^rC#9e*x~HYxULw^O5`cSoaw{aZCz5-6Qrx< zc(`-sBb}gIo%__esEQryd~!Q$$#y#I4tD18-u(O6cC#N8e@`$5;$dEQIoIF8nmYfe zVyDX0yPSDd?3e}(4EO0g10$THq=Dhjgn_Bxd0??$gkNVGn9mP`fjJOd8gF0(W@KPE z{B#2&JmL%kBkK7K%%1%j7(po}dhi>VeRe4f3Q7tg-CR++6(xpJ|>U z5MD)QvS2Q+uto{chbR>MpPvv|fwAJ)Dy1#Ap=2qzwUFAiZOp+*JY3L;AC;scYEK7i z#6)T!CescLr4$xVoS2l8abi+}QAY~?k8_Zu-@!EHm^t-72La~>Vh!d`&Fne(vPw)S< zMAZ-^g%DX-Ouxys8jwP4(V`QT>IUHfTqsz~iG!(*w?lg!JwQ;Ql9zhPrqo9%a9TG4 zlsIF++FXg66suIMbu{M`X5av~(E~5caJY}Va+;jYa{niua^;_r?5A8E^qd^vlx%|( z9qtc>l)}C={yQP~#1jR#19b4bYWXRXWLC>##D~%tao_ue>z}fe6}J2<65qoeZV=PQ zoy>)Pk>XyaZ`&FrkX_j!=h&4U6PHlSX|iK2Cpg$Iyc@qho`JgXg&TbR&f}8jit_dR z{Z)#dR`T6vJRyOm?c^%DhlS$#6vc3iiSi}Ce-j1|I|Q+En?4 zNMw{O{?Jqw^}0vwvor>C5(Pm%@klj>P>Jv#Ib?iCf`4=c(R5e`w{O@I`mTb`fuiss z>M0(kxrCX-o`->}vGLCb;HwF^kwQfbEXl zJ3xOfUz@ap0@raIN=vJB!iUcq7Z;o;|Lom3ks!Ee?lc4rv8B=#8jc4)A)P*OA!N(+ zm;;n*Cv$oAm;=h1Z!{G^#YZ=i%rUV(bKq=wcvT*A2w7*==mckFW~?9{1ak;*4RkA* zg9FcC4xC|`!5q96CLegBYghAtIdJ5>pE(ksmd72$9B}Q@nS-kz&K%+@Ne3`Tu3!#y z!I?u03hFO)_9a$Sd=}vB5eo?NA{EQ!GipTWLnlHZ;ZzHrvi0)^Qn)I`S@VcH=&8#e zQ3Teq8Zm1I_tfH6E&X~*uYpnyA(812W(N$e0}n$KETMZm`%>RkB(D!zw_=zdfrgPy zNY(*hd!Wb)3)3*NgO=g_&rHWQ>19-=c^R_swZW#Q5*ei8kPLaCTuRQk6lbIcE$ktd zOI|@N$8R}PTgV-pe@FCxtz3Ykr;)bh~$RdW5ob$)A!fv$xK*Xbb=g7lAi(v@wg0?e} z<^jJ`=nIQmP^;>N7o4RJvsPyh<7S8P2L@l1wp*xs}1;{<;>ZDz7= zd8rrO5u`<^v3w>wE-{>gclTz};Ve*v=suc17-W*TgJXyOO5pAn*+ zeK?^J#rz9G##DOJA_f^v#kVovNqnaJ`JI3{MYCAzmcuK^Sv!tHw-)l7dAugp-@l#3 z+ILT42@dkcR+9#cV{Q1bj^V8jck{cmU=};MWi}YAT>8k5;8c@OMmonn(m=)aZpB+r zvG5am)q@W*d2q60&WE`pcaPrRR=@{_`~Ehq!DC{66+_^lif^c3-l5@872dqZCF^r0C4x#xyg3yOGKIzdae@t>}L{9=AoX7IgT9$XTN1tte`lP5P{5I5HSmvr+0$*I|+cjrBb(+P#7y7Wrtxey1&}!C51KZ#$5J?JSs`~6jQ_n7njKwDfqed9!a+C zA8ksLc-1R-*RHS5OCw}V8k~s#sy7GI>p}1V$e4q9qOQ|wKI?dlCq(>QM9#qYS_l^h zMoi65tTCoI^H;HAWmVNOXWl&4f_kr6>*Ii$ti9LxS0Kr;?m5Wb+n{&I4Oa_Y^M}-v zm~@OrkI2T^*)f_T>K+_DE`K5mJ!T(3?@Qd?YoA~pF7{@F)||wm(8kHxzzmVmfOf$l zpeUb0ipUaFfr2m1$GxlIc{KyGWG!IDN&T^6UsyT+-vah4Nc*ydtneg{0pJ^x33L03 zp?f-LOt!LY_;VY9%XQP^f5!)|vBk34x`QhA0)^DL9XIYi6_IOMlk>`c1URE=)D)VQN? z*t6_c=Lo(H#zR^7_CT0u+4HQpy*D6_iLfUeC#UA$m13iS%SN4-`PiuXz>&|Gu2}-Y z;U7}OHzenR#jF+A-+ts5A2RZ_M+*7u^YQ2X&PUxL4{7s}8dBPP{PUSC^RfGok*^vf zbkZ**AAjHPd{hgBGI&0_GRj^$>qreLZ9X1fl4U*?A2RZ_zX=PYtHvH7Nl0UxAC_i8zFKsBC_I0pkk77G2kcjj7a#JFYLC>A(&nS%3t8r) z?vRn+>>}T}_At909eg&L`>x#Ws9^h@kllwormDMs2?6Q3Jj;Ysi>?nvKwRVx`-G%% z(`_#zXitHMG^gp1k*_^c$Y`%4=8#HYTpYzBvjhT6IqrO zE5$9H!c{Ck8M#}0;A3-s!BJEUB7$xTE4 zmDjT%UoE;m6cIgA$Y)nOv-hud79aAMYTYsYf&x3QaROU+;A3-ot~#)=Sog+hOX$jl zY`<}_%?E+-v^DjzCm*e5h4mu$yR*V}L;o2pT{S_-SQgkT-^>ELTGY>gk{$Le7xw3G zd3mH3UG{BhK)V{Y->CEAtT=el!vpGV`Tl#W-DhuE=+^%2EOe{O3MD(;HoJ7o0=xM; zSzzxz0N7QwnD2q_uw%2PJFDFaHSgdB7`VruzQayI5WnPI)}q_KCl5PqkVU{0u1S+w ze`Gb8IG81R$zUUI3g^Dd83t*>CT)F^cQf@zgkHaYJyW+Y+!`Ri z@gDotxgN$ouQD6Pt|zmPEY7$OSaCaVNL~OQ$ZtyHlB{W*(GzLt{v;-)AhSCQ*-NJg z2+l7vq|FTu>CawfYW5)yX^9`gJ5FpZJ31?SiSvun4!9CI*L4$@Idj&fq$cURukb@o zW>>QlcGlf|Y7T+0S-9It;}yZ?eDSfZLBjscBkW>fTA1lomz4Q1HN=6;Nnr&W=<58X@!D!xt%WeC4Mh7AGE{Lfio0*4&Q zrDj09)1YEwh#Ctjd^-;{!s~zdz#%qf((WKURIKTVa`Y2yuh5t5Xo(O<1GG;JEvpzb zxUy>8^qPvqka*w{{&{Z2!gg=|4utqr-fcL?lq8cH+-U}e#1i<#MDnx}zK5nQa$oLO z9>jh$l+JcYvG5oDYUt8?Z2?8R4n0%L{nh3*uG;j%$v$x3|C?!BabUFNlw|{~ZS3a0 z)=ln-T1mMuE}$*AUZJggM^I55Q(vJ>_mlkp*!i%s^Ra%~R<=;W z*mtXX@5$HTJ$Y5kwR3SBxlQ+z%f(wYFmqkxjyPs(C)r2(&W>u;iOWv7Y*%irpG0VG z5=Is#3} zbhb0S>ql5b=&&%|+D)HBzAz_2yY+ftNBhBJ;X7g(o1S!>LM)D1&Ov;ZKm$pg4koUk zO?tQm@(ne8V$d!YgJ`sW!ba$JiqC_XQqC0-N1O3p(Q--d0ZU^u)vT zN)N=K+>FxuKAZ!>J>Zt&{UOXfK4?!+0G4*T@-jfiq92d}B+(yIRL#xjzm7I+gX z7U13TF&iWnl4)4C{s!zW#qv!9_%S;CCKggk(S3o(*m&Ew{CF^S1=I?i^Ix2xs6Ox} zia~WS;F({JcWglFzOfwKLFPE2VugT2)i5cjsJMfG{S_jk<5Sz9C&<@0A>lGoR^jxY zz)Jp4N+9I>kdm2kd;v%6Yf7doFkmdLZ+|lYF(4L#n2;z0NPH6f2I?826OUr9V;Vkd zu2{`)i7O-}cuem-QPc7`WJE{J0Jw{usisCh8rb7ESQ2W`-ry+cTe72I3DsIp+7iDo zqno(moZ*^L-Z!}A!UTti*X($g93E|<4tYY0F7Y-PbxF^nIe3$fB%Qa1hNq`o1uNiG z^w06hXb&kxd-oy6>4|b72@bSGG>SI|QH(DQ#30XPBA_+`5Jw^h*=%Yb*KjAm>-58U zI{-h@MToB4ks~MRt|Ix!XK_Q~ogbD_C*hK(ij&8Ck>0a5yZHjt3lVvx)mbf5dfn_+ ztbQrRkFUCCHF<|z$|*yd2YT2bm6K1}GT$tKT7Y>kz=6qu1lipJC};pFtWdw7R!YRdOflMBt1@{$roV>&M9fj1IwH4w2AdvGU!iV%Pj1Z zfVa{>(=_%>Z+ZZeoifi2Mh2kMgjXy=ucWS$YY|JsWIRj^w!()Ut zVtu(%gvl)@p2O#C=-Bf}lA-Xu84uSfX<{eQ>t!k(fz=kgS3wBrk^P^#n1CyRSQ@_F zi1-)I82A2zri0#vuR#pP9RvixViJ#_9mqKdX~!UBnYIMR=CM$k*9z+H>)1uS3JdDT zNa%1#2#){Ft{(VA?CThWTsK~bZ!*K53Ob#CX2rk>k-Fr;Z+I~dx3Ct?EQSkAj9_Vg zn!!%LM#R&$ufE??`bIxbo-Uiq$sW5 zG$9;bq=pd^!J!vU{<(7D;aEG`%T^Q~wx#2tEZVXx9d$mdV(EzEHE8LmHe%_BMVNK4 z3)uZE9dSU-!7Uxfk*prt(h;i)v2?r%e!-DhI>Ndn7mlh4nKNFa6;3?2F)N+inF;dK z_-f|xu5?tq(#yDV!dfRD7Ap+6NLW`;6hYj>uSy_doU#1T-Q~|OvieChy!@%Z`Vkt7 zyvIDT0?PANK!(M{>c`CE-XDwQ$fd%sq__Ox`0*7GH$DW#`_D`yjgVLkL7|(Wj46aO zOQ2K`G#ADIemP7Kym7|8ael}Id88mYTqE5yu$+PeDnb*q{-nqR3<>-ZFbH9f#U@r& z+&4|YKhJg7yAr)W2H)B9oZ;i6BU%x222o9NTQMx1yX)eL9Cot3`*+y6*UeD@MQHWF zQAzmRZgP~9ZaZI-10B!prUkMXBwTlJQGSw(K#hFCPgptgg|y23@S7UrhtnEA<%lo5d^7H6?t!qMln`0Kv&{%L{~C{{OT0H2_*q+5gY?-e=yLnfj)w zshOId=P69@gM{&tq*XBVtl_+q#BIiM@P*sIs$Q|_;;ZymbH&MBt{RqqZK5SEwIl@2 z8Z3<~Xvgiigl6$rTA~!aqE4JzkWyD#qIJ68U&JY0Gp_KfG5rA;NdsB0mNuk?+?6T{ zZ55aW8*G1DLc^4juew6JbDfh40M1o^U2$98^BpQ#2lk_boh}LJ?H4L4NNY){L z4TI&3T?<2`3$Dh3`kPlerDd2E#?QlAb;9npR_W6sV&6DjY>G91F3FuatOU2_soiUx zB1NCIPPGI9d)BGA)2Mlh$1c(`0?S!TvQOm(>{C#$`-==VOyM#wGE8~4D$hPeeKYnc ztY(I@eF_

{D?Vs3y~mtjPu{W?Za^BBjAD+ov$GCzcuLaaJUUgvu6ZDdbNOaDV;O z-l8x|0)3&52a7rW8>d)59Tl%0yF2Yhy3@X5((WyB!@i<@JeF;iOEF2Npq5y(M6y{b z&LB-KZdQwsorYD|IV8K%U`9BN#adjvmJWzujsU1VBM{=kCJtp9BTx(llMtIThU9UY zB?xvI-9mA)1tAS`EsVdV#i7su61TE&FcQ?q4cUgGIcAoTG~jrgA7vVfTicLnbQ5et z^%qA*i@j^6A;;K;PHfONghSPmY(s!v!Zy@`WZ4F7L&&Bk*Nl&f0%!Lk%uj}b39F06 z#$>AtERnv|h2K4+OUqsm1Q=_5qYIKrMb`|Y3naECEaH5t%WgNi00ki*`YR96QA1W2 zq8wiSweiUWU$GsA%`jkrykJq2fg}?1e-3Nk2|znB!;oUp7>0Jy4%3W9pI!3>%`owV zt~Ggu_!BJ!Qx{Ni&$pt%>Kd@au(Ba&hoR}E`DU0bYlgw`d?`kh)E4fjfeq({K#lQl zni)pl-%S+nvAD39X{{IytnJ8_R)1;N$}a7)Jc~;fOf6(eiDWlCjy@$6br*yz@gJ?jjp==ba(DPe=4=Mh0#N)+K&#D9V&k0pLWa3(t;g&DnTgLzw9hJ{x2C|_Bbp3 zpL14%e^PO9L9`fn+EwQ$JY5l@Qs_EmNCYj^Xj zjdxS*5LKBJyNR}8t3QWS)$PH|IAn%OhIc1UhxeeCIY8vi`r!U{>_&=1bdc}}ffEk; z>Fxvc<}T(iKEc(jbYV6wOg<8AL@<+rITWM|D)abyYurGJ%UPr801aN+$Fk_unhZcP zDNyl^XA=7SM~WtlDt|N<}$B zO&8d&t6c2fxtCfS5jd=k3c%JH)F*M4R0a023eJt{=ExX0VFV}NN$#frqlkuR8y-2f z%83JzBO;<`3^*aNT53}XhR~rl(GNx^6D`}kkXm-Is=hbSFtzG2itVGe(ywjqb5i#` zLv(kZ*S|SKbc{dGc#)y(i*d!~o=o@g8T!jJ#L~yL zQZN#7-=kjut(t+>TWz3U3m&zd2f0Yi3&!>W;<(EgRJ1kAfWjG_fLf{Qk$^|`7-UD+ zJtU_2(ECJ=j!E=RfACwe@9_*3Owxdn*JCFVQnXoXfLKb=pw@8qJ|RJv#nZC^sQbR@ zfEP08`nc3d@c+cko?bftO4qza~c6{qE15nFa;K1RuU1MyH4^5Y6fPbSvVEJ;05-611e@u z8ggP|nwdur$2KRFU_fJ|($#h*_=D8a>Og~if{&}sL5la;!>Z)&(QKH>j3}fQNSa-+ z5poIVvBz7LGho`Q1&w{_O?3EhJ2!C-6&W3K zjM(PkKe&avi7}N>K8DXQr_!d(LA}Jl=)_BaEr5Atkf&Lpi4v-;5C{eWsxo;A z>LNkV5Z6$X$-J3rBa}g~TE;}K$VmggSwdYLpcVuuqGs|o}p{b6vJD;Mr3d3HwKz?U(&5dh)9Gv z6XtVbG|YM;86FN}OC8bpi6g{OB(sLy4Fp4M7RX+}M4FF{oJJ4_l@KLBzF`qPFpe~g zj{7K8%>o7jjZKJVYz)PLzL--GrG+CQH`sQdJfb4uBXi^E9y(&UER7EL#XAC>z=b?S zd$iTkWi>Q#c0{&PNOe(jmQt)0qwK5LwJsQ&Xn-L7ELs4Z$uSa6z@adNQ9uC@y`RQwGOZghe4$u4 zDBMK^$>tQLk2p(=h+|{P5}s-XsLerf0^5&Ww0`ssc!pU`b(IaZ4LX0)X`7Jg_ z)}~Qgr;ik;d!`s=pac)gJSn7AAM^%$2_U7h@@RSff^2C zYpAEX_u1mm_=iMs30^1-FEryo?aqk-9<&0&dnQgKYu@nN!*&^S0MgQY$(K+^K#w>g!-G@8|MfD)a!WYm%j~FAO9Sn~PyPTg2M9DGX z*38h8&k+~LYv|in=oNJl2y3LZY6*NmbBx@H^}VZjB)|Tc2jHV*N@%E!{1lU4#rY(-tq-iR&tf75iisOz#cO-5d#JV;e^!ejRUu4 zqnWor?;hxB*7%qrBc2qEEpH!&69fz;lrRKLr4rRq;oe+F8l1$m6UOOGNPF{|S*_p9 zrvW#*6p9P#Qa~dfW(opb6)2yyK0GclWJW*$5fdW^7DZG@10{$qdAW%RR!w^lFhd-c zoYU#m&Q)k4jtKKmid18Hgh5AQ2YzD8_0Dc)l?$Rh%n76Ba3n-LCTyCI84`{=#I(>r z9BV~tG3HwcVzovs_Zt;5Iv7@5U`em)xKW;JLv2}%CaRd4{V+AvpAtD1nl1YEW4Av< zdAq90U@R(1th{}Zw2ZR)AI_mF)gZMt&UJgW1e?f%ci)?=83dA-6D0u9LD z7*CPGA6riV;xln@oQjQy)`j`xaEwmaXM=$Zpw{Dap<|SR2?7Dj^qJ)&45q*N2?DR} zdo_hrFSUVV?HHDEYzH+j5R0AJRU|2?7ei`GP-J=}EurDXO2H$pgrpLLhw?SJ=(E+| zI?&$&0?^-vjs6z;2G-wt(hU|CICL})_DW>qO+k?K941vyo=HcA8F(^f388#Ys-f!w z=p+>U;6VFG8j)WXN(7J@Jb?P66Z6L`UmiF$A-r;3n7X*!(-`%_?(Uaz{z!p!s);G zDH-QI%v-s%{p#R^+)ne+>^z#z>LBkzr<<+>7Qw(s0 z#c9&d`BF2_Ko+MP669!c$|`s(Ph+L?g>W+Tior6UPPm#U?Epwm{t4h(5ZV9@pZym0 z;7N;EjEkqIG=QP;fUyB%k;Y>IW=+UMgCgx`7zXMa3GML^9dPTxH-nhH2VcO{hc8t7 zC?b-{r)A;8MxpEB2G!-m8H4J>ncm+b#zSz1;@^GjR|e410GdoPJ{O5JCK-F`dZVC7 zj6`K{Bt8_n^<0^!PiTSZ7hMtHh@cvf^~w+tA;1n17i2tnf+ym61P(cVp2K8_^BPGV z$u|Ok*7$TV#PNJ)0nc+cx{Ht9jm%Mjp`^`(Z~>HL3?z9Cc!?H3uS4>>DZ(2r58e>U z0TDB|GqW~}PCAp_=hn245Iznv1|kf;T2AU**V7TJ#RLbnCZx7|!jX452iJ}cpe;$~ zWI=AW67oDQ$g<)FwWdoPA{dfPFpORgJ?VsIoFwrC%m}>(`C8#QAY8i-(#V?*6MQ5e zzGiY#K~$7c44{2$AE7Qq)mHlT=jRHJ?W2|_#V#O`K&=m{G8=tD8P>Bsu*Oi1SxvaF zQr)JxJUyCfl`|Z)vuw`IA z@ynohQwF96TCpsdp^rNsdrVkCOJraoQ8#!L9byzF!t@I!T=kj>diJZJSi?D``|u3? z-1(w!OVb(K5bczT{s*q7=4Y=Jpg>WQ_~(OFm?;@R75;?G>`*^5LKMVfVX~+~p_Wb@ zl!aO>o8w34N|<58L<~&C_*h;eEMyV&^?Q z?saJ84RQd|SOWmyMwKD>A_`!gSs+0nOH@H-BBRQH<;1EoSnokJ$BQbgdh4k&q*Y^J z*h{;zZW(-~>?t%z6~$F0z4(k}Ewxj1Qd&M5U_?7z0Ww&Q>;tB zX?tEDJF*5_MF}K|Z&|tzd1NMugZf?4uU#l2*j9)dfi9Lpfexr>o~Pz_+`kgoVqvg3 zCtE=SKw15%Lj4g6{bLFf-+F|C5@7knbtf>JLe@~mJQ}8x?Z~MF(~yeNEkdCP-%SFF z!Ew`)q#4l994p?0-7lzefF#WbyvXkfV&MFaQDu;NhMQc8L-YhXja7&30T=rZ8&6ntCN~Mpc0Mz(Uf)3ln^Wy zz)npDJpcwJe@*Z(D+a(H32eTDE{ov?BT=Gy^cY$%^wraGQHoieczHd;kk=A3J)^=Z z>G2A*0;MMq1~?92TX{Z>jsXTZiDMH_tm3-7x!bRcrhJc~Q)enA&9xq*r$s~ZT8IgQ z>qydc1?>e`-#nb=WU$&=x2tek?rm8j3A3c*V$&{2q7>%bu$j5s)%whdVjS%5uT2y^ zi!3!u_c_KOUfH2tOrxuG`$^)6h|QB}^8jOLKBq-wy8HGu`ie==&VF=_zJHR~SG;qL zeruALoaGza^$C+j4*^Tm<&#DGvkchnIwWid%Ub3d?y>7lCFt>=9pe2W$O(v8lej_W zxPWpMR8EVQ#GnzCivX3=Xj#1tTOq`E}mB3&cN7Lv-B z7RO?Eq5A?k!$D<@LgVh2^le1YD27?JEUhNUh!WY$!?1`nU~ot@mmEucwvCcWV)VKV z=4Wmx3v?XyLB7JG6oKk1T z3^X0@rV?7nhlxGeI;rU_(SzQ+B{OOM837_;GE6f^GbBaRGjlXrz5`Fl&43!gs=t7~ zZV389i!2s~_EFn{vJhC4RpjB-T5pyoIIym`Se%?QhbCiyGqcqFPH(wb8R``E{|k9dkzB0I_250!+E;@U zy^T-4He7XZ@-iRKk?>!-B2ABc$Z79n!WpsSA&NN0%9SK9E1dj2P%NeZsB+E9V01Jk`ie@G{mBU|K>a$q@kspvSrPkZ_96 zYfTqjg;?06g=cfyf-yG5w?X+lmYz}GROlDEq}RmRx4g6xreuXHxEW%OyP7yj zyr5vmRYp;WS9ml_Infqaw_#m_Qn8SOh7g70)>DunJfmz7IYWK z+UNC&Wul!}`n*2BOcWnIm*57NiUi;s0FElOx6C&3ZduFyL|xf8Ir=XdPP@*A-7KQG zB=)iRb%wQ8VDn=2N4noDq9EgTs@$EW=f5oS4qr|=vJVc45>rNXy)u@rcw9_2-H#{z#8E~_7=j<@*!$uKl6eF_ zP#KeCi0?A-vt>nVt{tjB65&BauGYif5rv2BggP3dAP>~j^+If??34=7T_N{Iz?#A* zoR0NaN3?#Fsvq^-r6O9eK6V%wspTc%7E`a&01I z4PH0DtdIMHXkQS>0Zzr&p)#jh_p*-vLHr^<&o3c@izC}(P(UVk`)q4V$dYOk`Bq~i z(@1ZRvB(c(?nmNY%n0z6t5>7;^CDso0CD!RiV%2rO5=Eya=9R~(EcY!!prje9B4u_l^GYSpD|Xmxc-U=dsTHc9 zND`_=@{I8$eUntvmYvNQ;WE@}diPXgy_*y0-7Wbb?`4fi?yCvUtX4H*!C3b-f$?*I z(M}Dbc8$K_O3Z#wH&#-0$|O@uz)`E;qw<$~lMJl^^qtR%Lg$$zs2tt`W9f-${8~*T zkj<};C0?IPygr$DUCFN}IAYd`8@l!&(H|2hN#&W|N!UhcplvSbAh7Wn{vi7>{Z)y` zJKueswHK07Uf|b|w*=>?7rj(F>6=D9ov40!;&nyh^?rV()uq>sg;(oWD#ZJa)>8(` zw)&YH#o^+Wt99B{T@S|@247oil3HS`5vhSvfMV!H4LqW@K~ z5N_>4cIQZ^Lw1O2ka!Sr{oYPN9IF_}g=F7>n*ix@LVvgynofKl2~UVYY{$llVe!i% zp{Y?`Hb7>T#=n`6zrCK*5V9r<+;~G?`fFTlN!2rWE7nKf;9j-=A5vi3YE?F9V8I!qt-LujhG z##k2}=AyyfHLef3iDg4^-QDrq7+k%=6Ov%s_ zVRH-oifFw{f+OZFE)vVFQ(@=^L&r3N>8!!Yz!Xk16qHgS(w3?Cv6$+yZ<`8bPe82W zcIldIYA7ZLEET7dvdH>HBC2a?$~D>ETtZ6_Z!U?LFO%jHHHVc>{nQ%#_Dcv}Jd+tm zU#v#$M232&@l53DhR)Y-REWHjJ&A=SYnE7y2!Kg8i;PQTa0t0uN%x028tO3^D~x8I z>;U|g5o(}t_m@Zdh;yCJd02&Eeny@jc;h?%y3guM&vlB#ysP!C=Q>?t4M=q)=|oWn z&$}yE=s%z9Th`;Vro^;vkKv5_vp4Ueo zD1PPqNx!k5=#**N;jYlf_Yp;h&7+(=z@*8g?FBY~tKUg!j1BbE&3bD9B#^O*td;t# z{-U?uu0%|SFLff~(dl}hOPxL~zooLFbSF;%cZNRaQfGYY87#XY%Bl;sJ4?jQTuHKe z)B!3&3^s^LiU89V`aH492zgszRb$|SJ}?0NklM6TSIiP!Th$RZ1OMSjRO-%|q4Vd6 zeVpg?4+BM!n0vLpOpErSa=Jcnkmysmo)r432j^AOS#7!e1;Wy4<$COF(Iew(LIGIP zD`#UVhXMTcks{_?qc1;FT-wUl-18To>ajPAZh_j{bL|Ho1$ETZH;dLV`MrKKnt(#S zycxZ}M!UB_ErymHb&D85zxUlD`u0t3a%u$;z3(e zyML?}#hEW~QsvHjU-qKc`#OA9uQLqF7xf26n|?UQa6jU6#Qt7C2I`r|h_2~y0>Zk1 zJ5=02hbp{ExI>MZGgx-8<_t3l`{oQzBNg}nv6@5-&z!;UspgCaXyN}O%^A&UOViC6 zre$Q#;PR8r8AkF-F=v>30dvMGmd|!z&M=U|Nd}Y=qvD%0c37n`XK>+7F=uebfH{N9 z-UV|8j~|;e%o|o#1`PGh8K(KWZq9hv@TdQdIm52KBq1=FGlDW^lgt^q`=g?>xZ^r~ z%%dVIH(|yYd7tPgbKqXkufj|gmG_BaM7*TmD01>|xxvXOo=bc>z%#SkVlhvv*c|s| zk3S$o_Vaqg-QwiPR-+cgGS^PVb&rctJI~yzhBJ?-fy={+>3aUrPCI>9jffV%LLH3C zD5k9lRG_48W>X{J;oj!K1=G(={aFnhq3~&krD9;$HNme2h_2?V2nzuC6Df7U&A(b8 zI)#hr+nbk)W1X4$!=+;Xw#oWMt6l8=goqryk=P&EMSPX_%{>3Zt)Q1@0`T$^=VBKs z$RkjW*Gtz8=pmOpA$s?jXEcamj4x}|L%xv;X0cMW#w3$22cpvU7j^3=MZ4pc*x1Ch z+B8cQx7JfkHpb3bMPk{x(KYz&oYM?DuMMzseotlRi*^G$Z%$j9&dyECh@Er!$?Tka zoNApm`2y^G-Okv#fs|uv2(t4XR%zHd7v2K9N<1jLF|yh~7-Pw* zjwgAatT@e()usSh@p~#+RqqC}YEE04PFAL6L{?mWGFfqtQ_0HY3y{^uospG+lw)cL zlGP5YG-SnvH-)S?V}Pu<>|G!$9zP~4^9DRIV5m=49wc^|te*dGlGX94WM$C1d1Uo| zdRiKsr6lzJx-P@XA4E${Q9?@l&5RY~XlKIPD2x!Tg(-atr(1nn&Hl%Wi=2Ge0y635 zZS23G8?&*{DaE#f3^ob0qTPcclF0^u^hmaTf4hk2V}2o8N}QFXA07>3JFDn%$vKz> ztLU+{7TW$yIDhA4vLzsl@YZl z5}cslzg;RW^$k>UtRRsGdgd}+j>7FY^ha6)*V*SAj`oxu@;3!tTz@Y?fg{vcsBxxkdxZkiwhJ7EwBf&9A zQ6sJQX949hh9|(1)Cn)@M^}n&aZ~;-DSJdoVvq5sR68()Ac%^dEl03`v=CGiYaJ4j z6ZXVUcf8HZ`pK!H&5ySq=G3x5`$rkYbBs|uSVpsbWy0{4X^w4JAKwes$|f0F^+&Kc zi#xB=BVPsSROZPR`k^(VlbG+te6a>D&Xp$6^8iw`wIV;eqwL8VAlnd3WXo9xM>b&$ z)u2+h>N*&}omerMbhC%2WQMN$oM> zyryrQ0uSAQSsj4G(lhALo8o)0h=j8w--Dg{;bj^>yksASUGRF#G+uAX`D6k*mK#v^IH&MJxUUvGO zSH(f`9Xqrn{PN&hf==7-jkl2v-Jg`UFe#Mmpi;$YD#1|nH>*URSisZ}(N7L>a%*m0 zD;{;6-;?8Hf%8tPw~0}1_fYFLU=Z9-^@+cRYNWQYT&uiXl@3pG0;5y!}TIV%(zx(b{CyJ7epg_c@kK&Ty7iC zb9_+W+pz&3yt?k9t*BkEzw0hyowku9O#_~ebri4ro$a)BtJeEjI%3=7di|RoVnDRQ z#FAYNUX0}H;1z=(I5WC;vTxR5yXKpfN|yww9S@FXwJzCFuWM(5Y360gQx zn`3Jdv8(viy3+()fsFr;O;|{aP~)WYAb;_i3}@8$o|%>NeG8VSNm?#@4$^Z>lG6+w zi$G2@IR73p8%fCQ9lxFB=?oH_VUT2r8E1g76HN<9c_chTFlOO7Cz92;_srtku$P9t zXUv&nq`YIl$VNndvNs2&`b_MH7x> zlZXz5fFG*QaJ7V9e5Wr)2ok9o3~Jq*NMyNCRlG&{tYdmDCBQMgjAQ8p z0^>!q>21Ut%wh`my=b;jg7u==7VsR#AxlnIupiXJ@;u4m{xCo`2K=GeP96^IhR=+q%Xo<>p*0BsKgm)ZGL~wAL9`I2b26<%6m}EkhLs1CWRNJ^)k?ps zAwWKzEY;ZSN^(2=g2_P}eoAL@nbclnfTfy_ou#uH;kMLZ;nX9vH$7pg7RFM|%2MnB zB1<)j&x@p!Ka}qGZ!Ep&P$u6T8A-=TGvJ_q#|!k@X3)q}TrCa`8d9yLnpZp>5#fzj zd%Sw%6~QaMOd2n>oXWwm#9Q&(JAo};y)<%AzXs~9=Fw(k(e3+)0lLrW@YwuKAF&si z4afBnH#mQ0doBe#_Z6MnmJz8M7yYOcVf*o#Kj~xpi{H1hQ^B%75ej{(mmeT{IDZL# zUKCTC-dv6rZh8lQ-G33SCzmz-o#e8%1IAef%(3R+B(fUK?o2N5RjV52ATqSu6UZDI5K=atDYmZOo>D z+P_k5*RrefEZuo9Z1>f*`m6!skIu)*`ON}8c@G%pU(5MV)%OgFmug$RD5rJ8_MXU+ zXR^4QrG0~0>{*ohi3G|sk-waSl)fEk2}OTSX^+3IKcVB#{_V#Qr)ve40o>7 z7nO)E_?cTGx_6w;`}5r|sp(MMBs!diL98ngM^j33(IK(;wlp08h4;)2YtDX$9;h(m^fI=<1R!%SR2P-ersYsm5Hsw!J`dWSOAAAmpiay8Qz(7m?&VWcGDxvXD0D{`c zuwWafZ6dsXFwdG=vHv);6EgJ*Oxm@NsJg6B0S@pAP|KU-$J1Q6vZ`>0T$HDqImscaj=`6yH+!Af}C`G z8A%URo9IaUx{`_(2CFGCZy@Zd#^^~-A-0@aTMic^RA1foaPd3mJALipK!dxk*7qGQ z<~Gbow>8K}3maghn>NU97_@?09&hFEn++JBKl;w=$ZLFp$5@V za7t+i5>;N6I(~EmUPO-{4GP?z+A(+$^+|`INsdGCh~m!Sm_sDXYh4fgB)Mm9yW zlxyu{MmP_t}-*L63 zus+~E@@ewEoi;@)O2j12RsFqQW5iX-#p|~B<9n|zagCQ~bnE@Z^hEgWgPk7EkJg-p zNpoiQkTpeLcVH8Be@#)Y9q2SkKcxTtE7)C{qSG@Q%EYx(bo3IeMmFN`3aGxtbzayz z%t9BvD+*dC9Gk3D)0Fy}xDlVhzv{2L>@(4MufGEKlf0d(^uwQtf}#erWmW?_V~RN~ zLzGX^Uv+Rg1@>c`HAN4#^wR)IK^!pg$x`YLkIN`0)@Nsi3JPE<=qAQ{g4fU2@u$&K8AVpYzu zo=j+I@p_!@lCYG_oTBRv#xmoEMhSDxXMtXTFpUnQoHnq#FO!#%cThs_TB6`=Yp|)H zYU=Mku-msc>hkwQq!F*m1{l!%`aEex{ix`A(Y9deCum0lR)H;l)hDbM?FyzR(j%n` z%PXuxa1ENO{6v?n7X#G2v`nX^7nz;ALPQ3To3&M5&woe6#Ep$Ybae`=YYd_i5aly0 z#7&KswQN)J=$Pd|@(yD<#-jHc%4lo=fsMq^Q(yi4xCY5#H5jkIyh5~%J8X@MfviGM zO~kNJ35$}5cYg)PUWQvXNNtlioDu;5Ka&*lbXwTbzo1&9*I7od7Dt=1fwY(w;iTHG zWUggBcbc++cOHH5KfXCGg~+WFxiIi^6!%&gBBw?APKn%@9=4b%kBnM8hx(?6ZIob5 z4^4r;1;62`je~zD8x@)L{>;ZB$Z-FwW0CB{R-Y7hG8XJ3_cI?0#rdrPfe6bj2)0uLFE33pp|S_?t!p^)6-Z=SfI!!;?>ckl_^SrydsvL>AE8 zNEQgkR_Sexspi>rqaHe6oaj8FE9Q%Hcb=Jd#oX=fih0EBrUfx?MLOj4NiRumuL`(F6xA&AR5T_0D znlmT?Xw2>Ku2WvGJ)x5~)^r7Eib><`&YGfMUjQ9A*74XV6G4f4-2+>|Bl?Vc#28y< z{mG|+GI6-0SLSS^8^~dAH?#rw(>f7%Xhda0EkM1?QLDQ>5&rY=;Gd7TJ8z>nDU9mJ72CAZ2 z$5)Ayig{Sb-hxfJMnZ(j$gEgY?cy^%aXC)UO3q@Gv>NGs;M%#gIQY>IWBzo~KosbUIKJZV4D!Dio}a#7mwO zt>(bYF23e~(~RbT{mJPTFMQ(pmWRoUoDSM#7ghI5RvnqVfBuxO(EDNKp?CjHmpcKu&-#|T|0oQ*>^IadT;EjUWYA2xU#!GR@%57}%% zOF8ny$2^!Bp8z&NzWwB44?7$s(l~l!$65m(#RMGE4#$mPxW^v;LC58yiymg6KyMop z+@J_|_&`}KhWMOK>7TQSRCCTIqgebX48Up(Km{+c5P%iZP%Rn$*v(lrI0LKQqCfZr zX2%r81loZ1AB8~*ics9jov$rgeP&!zsx2 zbQfv}rDYSdqMRIJyD6P(bvI>gbYB%O2rZ49m6cE9oMvdeDxe+Y_f+lR=et2W*qpXB zT{~!6M%qCxKUq6yR?sQdBa<(n9bB+8?Vy2_V`>O$2X|Pd(GGIqP0|YjsJJFgNLSS2ZM@(CTa)yJi%1^0C^Zg#yu@V2{Njy`HoN)n-_gSy&g{PrU{O8eGaA#D{o?kqf37_kV;s=X0zFsbV;=s%6`qkQRdj0*SZ8i zz#V3t^$9JVc5yjSngtbI{}7S)vKtKwb&X>(;^?+z+$IugxM*$x<-i2y&L(t^lIxb^ znmYm@Ad;j`4rFlSq7eJl2&KmPNB>am#ZT6xRXkh_3|JR43+2|Foa z;f1}E@E^NQ&wu+rx&?1A)9EE?UGd!tqlXjf0O(~{ zh9r&Lw!i!F#iiuT4%rg&nW{^~cf@5WCLWt;R{vdp{U>p-Sp0Y0?;VkkBckaNfp`OY zgT^#vjm2BAhf}~Ty>JT*ljju(T?78_hV7uNaELiK3#a`7mE`o0ZA|Hbopt85ZHOdD z2$Uq_!rIN+*zAjD&F`0g7TM3CpJF#@@{flE3|*32+D(K8O31Dh1x=O29~%{s{KlfK zs`~;kL^hb!?JGH$yEAC3M`$-yvL#W8eXUo?**~@?WncO|v9G~O&Tt(4)fJ-6&Bx&m zh6>6{c;TqeuD}X$PHRt%5?~B~<6<|#J zBFn!fV0%79pjlc$LW9U)P}N(Ih)5vrCVI8r9cnR!=)xY3rLVoy#lJy3E!BjYMu_G+ zC#_LE;r*v`y##Zir^K-*rf4Na~NB2f-im>-4_|CcN&kj*%#@V$nHs) zz{(3{PiLWiqEPbbs_z!c-Z=dtv!e+Xca$eNRr)dn_p%m0tDnf;r27mN9btZP@6+cN znGbl=Q$v1Hyy>a-8`|_VL60t!oeQl2(l4(ixC=UrEor*_X3?$dBAU6QVdz(I@XtWe zmAD37gF`$Ue{g`{{Hyh-&0<`f!G(=^l%f_BuB+)Z*O`rZp=_^YVK%$jpiB;ilY?nq z5NC^_RhVQzMtmyq18y}n%rpK`@}QLT2U5~gk{|SwYtl327$>u_{TED4R*!C&0L~{f zSi)&WkG?dZN9XrcJ^G8Z#qUS%zi3WdnyyDTEi?NsR#PjZ&^qBBHjk@g&owM~_BuL> z4DP>J!nt;^|H8ls*U?c%t3l`e7pvn9Rc3VOTx?Tx=bSO1JLhV4pgV7D{{@d3>(0%a z4}ki*bFKn3vP(z(mAXrP)p^qXiw4=D`UlAZ3K!7@d2J)H4VTbhEQk~{(f;IF8=Tk7 z$*o+K^{h>u*K{P^Db^hvCtu8+p+}}Wo%f$d`boe841qYM`vPxf<=1DtSL|hc%6o+h zcI9xoOe7GK$O_mC`v%;A!va~9*sENLSd3Mh405q*NnJxS*OZQ zuaTnwiok}$_UJ|=Z z*a-JlYkBaH)vQCGOJXa}_9%ul<5yDzhK!HXfHt3%_rCUPL zt{z9nxr?b*S&~ENE!0u@Y9LC5+smBKDX^9P(uwF*_`HjqsHj@CoF}TvZ6*Us31v@) zF#f?PoVy!tBir}0xmo60%efZ^b0^$97$CTS>%=Qz3plHd%!qqfMp}Fr;{gtH_8P!L zn*>o=cB5}eZpd=ZF}-XqJ{+j5sYhYw0FJ2 z@9FQ<=vX>;AZ_N`^4!wqBP(|PxRTYQV28zt!B2fSxsmR69YMaFI%fPs36)CM% z2q(>{#oK8#`lvekwWFcd(AcP$+7cY2Mq+IV@#G?GbW*IDX0Un!D6xhw`ySOG4)WnL zdbM)6nnocko~k+4!;zO{I1{EdGOr~rypqPD1Sm0x@xp+__tbW9Xbj(UMfKsVNj}|@ z#|T&E0>&6%$DG>(@dzjp6&Um!SptBYTd#5>*;mCJy7kK8Td(2{VV}ekafXF{2{g-% z=C+y#Zg=P640yklW=q49j)&`==+mUTXBq^Ik7sLwc!t4f+>Q8j>Np)r69ogXch59fIN`}| zcOy0w!_L4Pf)oRWYgewqx)FQhL~g|1I7RS^m21*Csm)Al{Zu75z#yLmASVXacOwqe zTl+5>WltQeaU@+pXOvZO68U2MA-M7|7PFj4sGEfBH7qjyUZAz(ZX_v!XL!ntbt2MdN7jT{uj0h=A*^H;=gf$zw#c^v{=dazpOdBtxhW zoe<0d57zgahz152X~-=1SCc4YaJ|2{m#-e8GB_INoj4{*+)KVFIEf8Xuwq#%B8L5d z|5{D0d9sT$f&FNGA;xIXjT^1GI>?ELffBO`NXPvLi#BMOCzX*4swonZxce=oCb@9X zY|nfEve!E1)ZV-rgh!k=`>EXvkY>OGvfnNPLB_~Jz}t-$1fIL&$xPH@f<&KPHJ zDEM;2{%+p65s@G-*c#*oSnjD>YMFX1q^fwks@FuHcWeyi4+6r#L87An4*~H;;7BcS zPtmnZe>uv@;e~SqBjT2kj>71mFn%$NphM^buRs^u1j2_$7SjY$Qs{6Z3npa`RMOkl7= zMQCLd<7$0(Nz(2LU>^A-8qAB~h%QDt{L&SXj*h$mB&2u8yMdD7`rLjvLKhi%6 z+HIdn!$-Y@-tkQWVMO!_WvVo&PO#SlhC0WLFbnGg2~Ei$jsY`o;zHQ8X=k z$AeoO7~Gy1X3rP4se`*Hd2r*NXK8SBcWH2o?chR@Zh9UA3epvoA(eIn!wC9-@I-fp zX@D?_+A|4GJ09#^9c!XpQt%c0q|{pPmdn_Gm|B-459oh@T8$D9IQvsm>wjhhS8ZO7 ziE;B*{B^&h^~-;H1fTtnkDyP3O&r1auF@b}yJ(i2N}QHXNTI-09N5bQuB3okLTozQ zi<-t!n>or;d!K>No@NJ_4)_fGiI47}H*$@sph0SYED2^pHJ7#SC5ELl$Iv4q2_T-FNlFJ>9iJ-Y8wzaT7fXY>=j3!wsPB+lU7!w*X?CZo&80VW~FodgT=(@i0P;ui7 zy>~x(a+@hU&aEjC-NfOa?gm}nPxdI8HU%J2q^8R$Rpk^wGG6D=t9v(n7Nsa3r$gBb z)0~XB)hg@qd9suKzMm}23QvmRG}B2qaJ%zz%ch_vAxWpTc9G&@z?;mZYn{tz1mE_?x%5Y(5XP7 zLPwGPqABHOCYPI;TCP`Y)sqW}us}0VMjH=C=#q9S`88^w(>bU)9k`bxGq17E;xWV!S8T=)t-D)?khS z!63j2rN;%#}kwRW;{XdE9B{9Ptf>}b?Jp;_c#CjPg|a^n`fuLa9U%_1LdJv-h74G>emO#le>6qUj*2` zojzdG<>^DeaiHvwo0L(1cc2{atk>ramVNe}b~RdH>>}%^F*G-kiA0rCf;9+j)UQ9{ zw4LStGhM$sSdJF&%$W6yS?%;02g#m+Hr;rT?BHCh7ab(`?eqZQE?B4RCW{BkzMvr- zHi5r=OWj4~`pbjl!Q#Gh-Fpa5_P)PdA2$RlkK{()IYjpF{3n7W&4+`)81PdP#Tt`5 z3eEmG{ml^B9ndN|SaxhT{TehlmVQu(s1KHF++KU#H7oTA2g~jPRbFGX?J?#2f&S;7=WP#G|>

iKnZF_yzP&hfG{24=KzqbDPcq}cX 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 J7H_TD`~Pw;RY?E< diff --git a/tests/integration.rs b/tests/integration.rs index 6dfe6d7..33d49c1 100755 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -84,29 +84,28 @@ fn load_non_existing_wasm_script() { #[test] fn validate_assurance() { let lib = get_library(); - let p1 = Arc::new( - PokemonBuilder::new(lib.clone(), "charizard".into(), 100) - .learn_move("assurance".into()) - .build() - .unwrap(), + let p1 = PokemonBuilder::new(lib.clone(), "charizard".into(), 100) + .learn_move("assurance".into()) + .build() + .unwrap(); + let p2 = PokemonBuilder::new(lib.clone(), "venusaur".into(), 100) + .build() + .unwrap(); + let party1 = Arc::new( + BattleParty::new( + Arc::new(PokemonParty::new_from_vec(vec![Some(p1.clone())])), + vec![(0, 0)], + ) + .unwrap(), ); - let p2 = Arc::new( - PokemonBuilder::new(lib.clone(), "venusaur".into(), 100) - .build() - .unwrap(), + let party2 = Arc::new( + BattleParty::new( + Arc::new(PokemonParty::new_from_vec(vec![Some(p2.clone())])), + vec![(1, 0)], + ) + .unwrap(), ); - let party1 = BattleParty::new( - Arc::new(PokemonParty::new_from_vec(vec![Some(p1.clone())])), - vec![(0, 0)], - ) - .unwrap(); - let party2 = BattleParty::new( - Arc::new(PokemonParty::new_from_vec(vec![Some(p2.clone())])), - vec![(1, 0)], - ) - .unwrap(); - let battle = Battle::new(lib.clone(), vec![party1, party2], false, 2, 1, None); battle.sides()[0].set_pokemon(0, Some(p1.clone())).unwrap(); @@ -118,18 +117,18 @@ fn validate_assurance() { .unwrap(); let mv = p1.learned_moves().read()[0].as_ref().unwrap().clone(); - let choice = TurnChoice::Move(MoveChoice::new(p1.clone(), mv.clone(), 1, 0)); + let choice = Arc::new(TurnChoice::Move(MoveChoice::new(p1.clone(), mv.clone(), 1, 0))); script.on_before_turn(&choice).unwrap(); assert!(battle.sides()[1].has_volatile_script(&"assurance_data".into())); - let executing_move = ExecutingMove::new( + let executing_move = Arc::new(ExecutingMove::new( vec![], 1, p1, mv.clone(), mv.move_data().clone(), ScriptContainer::default(), - ); + )); let mut v = 20_u8; script.change_base_power(&executing_move, &p2, 0, &mut v).unwrap(); assert_eq!(v, 20_u8);