diff --git a/.cargo/config.toml b/.cargo/config.toml old mode 100644 new mode 100755 diff --git a/.drone.yml b/.drone.yml old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/Cargo.toml b/Cargo.toml old mode 100644 new mode 100755 diff --git a/lifetime_notes.txt b/lifetime_notes.txt old mode 100644 new mode 100755 diff --git a/rustfmt.toml b/rustfmt.toml old mode 100644 new mode 100755 diff --git a/src/defines.rs b/src/defines.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/choices.rs b/src/dynamic_data/choices.rs old mode 100644 new mode 100755 index 7a4f7bd..fbd4705 --- a/src/dynamic_data/choices.rs +++ b/src/dynamic_data/choices.rs @@ -1,4 +1,4 @@ -use std::cmp::Ordering; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use parking_lot::RwLock; @@ -20,7 +20,7 @@ struct CommonChoiceData { random_value: u32, /// Whether or not the choice has failed. A failed choice will stop running, and execute special /// fail handling during turn execution. - has_failed: bool, + has_failed: AtomicBool, /// The data we can use to retrieve scripts that are affecting this choice. This will be written /// to once: when we need to initialize it. After that, this is only used to read. To prevent a /// read while we're writing to it, this is a RwLock. @@ -85,13 +85,13 @@ impl TurnChoice { /// Gets whether or not the choice has failed. If we notice this when we execute the choice, we /// will not execute it. pub fn has_failed(&self) -> bool { - self.choice_data().has_failed + self.choice_data().has_failed.load(Ordering::SeqCst) } /// Fails the choice. This will prevent it from executing and run a specific fail handling during /// execution. Note that this can not be undone. - pub fn fail(&mut self) { - self.choice_data_mut().has_failed = true + pub fn fail(&self) { + self.choice_data().has_failed.store(true, Ordering::SeqCst) } /// The random value of a turn choice gets set during the start of a choice, and is used for tie @@ -188,7 +188,7 @@ impl MoveChoice { user, speed: 0, random_value: 0, - has_failed: false, + has_failed: Default::default(), script_source_data: Default::default(), }), } @@ -259,7 +259,7 @@ impl ItemChoice { user, speed: 0, random_value: 0, - has_failed: false, + has_failed: Default::default(), script_source_data: Default::default(), }), } @@ -297,7 +297,7 @@ impl SwitchChoice { user, speed: 0, random_value: 0, - has_failed: false, + has_failed: Default::default(), script_source_data: Default::default(), }), } @@ -335,7 +335,7 @@ impl FleeChoice { user, speed: 0, random_value: 0, - has_failed: false, + has_failed: Default::default(), script_source_data: Default::default(), }), } @@ -373,7 +373,7 @@ impl PassChoice { user, speed: 0, random_value: 0, - has_failed: false, + has_failed: Default::default(), script_source_data: Default::default(), }), } @@ -405,68 +405,68 @@ impl PartialEq for TurnChoice { impl Eq for TurnChoice {} impl PartialOrd for TurnChoice { - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for TurnChoice { - fn cmp(&self, other: &Self) -> Ordering { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { match self { TurnChoice::Move(data) => { if let TurnChoice::Move(other_data) = other { let priority_compare = data.priority.cmp(&other_data.priority); - if priority_compare != Ordering::Equal { + if priority_compare != std::cmp::Ordering::Equal { return priority_compare; } let speed_compare = self.speed().cmp(&other.speed()); - if speed_compare != Ordering::Equal { + if speed_compare != std::cmp::Ordering::Equal { return speed_compare; } return self.random_value().cmp(&other.random_value()); } - Ordering::Greater + std::cmp::Ordering::Greater } TurnChoice::Item { .. } => { if let TurnChoice::Move { .. } = other { - return Ordering::Less; + return std::cmp::Ordering::Less; } if let TurnChoice::Item { .. } = other { let speed_compare = self.speed().cmp(&other.speed()); - if speed_compare != Ordering::Equal { + if speed_compare != std::cmp::Ordering::Equal { return speed_compare; } return self.random_value().cmp(&other.random_value()); } - Ordering::Greater + std::cmp::Ordering::Greater } TurnChoice::Switch { .. } => { if let TurnChoice::Move { .. } = other { - return Ordering::Less; + return std::cmp::Ordering::Less; } if let TurnChoice::Item { .. } = other { - return Ordering::Less; + return std::cmp::Ordering::Less; } if let TurnChoice::Switch { .. } = other { let speed_compare = self.speed().cmp(&other.speed()); - if speed_compare != Ordering::Equal { + if speed_compare != std::cmp::Ordering::Equal { return speed_compare; } return self.random_value().cmp(&other.random_value()); } - Ordering::Greater + std::cmp::Ordering::Greater } TurnChoice::Flee { .. } => { if let TurnChoice::Flee { .. } = other { let speed_compare = self.speed().cmp(&other.speed()); - if speed_compare != Ordering::Equal { + if speed_compare != std::cmp::Ordering::Equal { return speed_compare; } return self.random_value().cmp(&other.random_value()); } - Ordering::Less + std::cmp::Ordering::Less } - TurnChoice::Pass(..) => Ordering::Less, + TurnChoice::Pass(..) => std::cmp::Ordering::Less, } } } diff --git a/src/dynamic_data/event_hooks.rs b/src/dynamic_data/event_hooks.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/flow/choice_queue.rs b/src/dynamic_data/flow/choice_queue.rs old mode 100644 new mode 100755 index f262b6b..c3d696b --- a/src/dynamic_data/flow/choice_queue.rs +++ b/src/dynamic_data/flow/choice_queue.rs @@ -1,5 +1,7 @@ use crate::dynamic_data::choices::TurnChoice; use crate::dynamic_data::Pokemon; +use parking_lot::lock_api::MappedRwLockReadGuard; +use parking_lot::{RawRwLock, RwLock, RwLockReadGuard}; /// The ChoiceQueue is used to run choices one by one. /// @@ -8,10 +10,11 @@ use crate::dynamic_data::Pokemon; /// 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)] +#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct ChoiceQueue { /// Our storage of turn choices. Starts out completely filled, then slowly empties as turns get /// executed. - queue: Vec>, + queue: RwLock>>, /// The current index of the turn we need to execute next. current: usize, } @@ -19,42 +22,47 @@ pub struct ChoiceQueue { impl ChoiceQueue { /// Initializes a ChoiceQueue. We expect the given queue to already be sorted here. pub(crate) fn new(queue: Vec>) -> Self { - Self { queue, current: 0 } + Self { + queue: RwLock::new(queue), + current: 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) -> TurnChoice { - let c = self.queue[self.current].take(); + let c = self.queue.write()[self.current].take(); self.current += 1; c.unwrap() } /// This reads what the next choice to execute will be, without modifying state. - pub fn peek(&self) -> &TurnChoice { - self.queue[self.current].as_ref().unwrap() + pub fn peek(&self) -> MappedRwLockReadGuard<'_, RawRwLock, TurnChoice> { + let read_lock = self.queue.read(); + RwLockReadGuard::map(read_lock, |a| a[self.current].as_ref().unwrap()) } /// Check if we have any choices remaining. pub fn has_next(&self) -> bool { - self.current < self.queue.len() + self.current < self.queue.read().len() } /// This resorts the yet to be executed choices. This can be useful for dealing with situations /// such as Pokemon changing forms just after the very start of a turn, when turn order has /// technically already been decided. pub fn resort(&mut self) { - let len = self.queue.len(); - self.queue[self.current..len].sort_unstable_by(|a, b| b.cmp(a)); + let len = self.queue.read().len(); + self.queue.write()[self.current..len].sort_unstable_by(|a, b| b.cmp(a)); } /// This moves the choice of a specific Pokemon up to the next choice to be executed. - pub fn move_pokemon_choice_next(&mut self, pokemon: &Pokemon) -> bool { + pub fn move_pokemon_choice_next(&self, pokemon: &Pokemon) -> bool { + 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..self.queue.len() { - if let Some(choice) = &self.queue[index] { + for index in self.current..queue_lock.len() { + if let Some(choice) = &queue_lock[index] { if std::ptr::eq(choice.user().as_ref(), pokemon) { desired_index = Some(index); break; @@ -72,19 +80,20 @@ impl ChoiceQueue { } // Take the choice we want to move forward out of it's place. - let choice = self.queue[desired_index].take().unwrap(); + let choice = queue_lock[desired_index].take().unwrap(); // Iterate backwards from the spot before the choice we want to move up, push them all back // by 1 spot. for index in (desired_index - 1)..self.current { - self.queue.swap(index, index + 1); + queue_lock.swap(index, index + 1); } // Place the choice that needs to be next in the next to be executed position. - let _ = self.queue[self.current].insert(choice); + let _ = queue_lock[self.current].insert(choice); true } /// Internal helper function to be easily able to iterate over the yet to be executed choices. - pub(crate) fn get_queue(&self) -> &[Option] { - &self.queue[self.current..self.queue.len()] + pub(crate) fn get_queue(&self) -> MappedRwLockReadGuard<'_, RawRwLock, [Option]> { + let read_lock = self.queue.read(); + RwLockReadGuard::map(read_lock, |a| &a[self.current..self.queue.read().len()]) } } diff --git a/src/dynamic_data/flow/mod.rs b/src/dynamic_data/flow/mod.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/flow/target_resolver.rs b/src/dynamic_data/flow/target_resolver.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/flow/turn_runner.rs b/src/dynamic_data/flow/turn_runner.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/libraries/battle_stat_calculator.rs b/src/dynamic_data/libraries/battle_stat_calculator.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/libraries/damage_library.rs b/src/dynamic_data/libraries/damage_library.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/libraries/dynamic_library.rs b/src/dynamic_data/libraries/dynamic_library.rs old mode 100644 new mode 100755 index 3812def..70219d2 --- a/src/dynamic_data/libraries/dynamic_library.rs +++ b/src/dynamic_data/libraries/dynamic_library.rs @@ -5,8 +5,8 @@ use crate::dynamic_data::libraries::battle_stat_calculator::BattleStatCalculator use crate::dynamic_data::libraries::damage_library::DamageLibrary; use crate::dynamic_data::libraries::misc_library::MiscLibrary; use crate::dynamic_data::libraries::script_resolver::ScriptCategory; -use crate::dynamic_data::Script; use crate::dynamic_data::{ItemScript, ScriptResolver}; +use crate::dynamic_data::{Script, ScriptOwnerData}; use crate::static_data::Item; use crate::static_data::StaticData; use crate::{PkmnResult, StringKey}; @@ -75,7 +75,7 @@ impl DynamicLibrary { /// can be created with this combination, returns None. pub fn load_script( &self, - owner: *const u8, + owner: ScriptOwnerData, _category: ScriptCategory, _key: &StringKey, ) -> PkmnResult>> { diff --git a/src/dynamic_data/libraries/misc_library.rs b/src/dynamic_data/libraries/misc_library.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/libraries/mod.rs b/src/dynamic_data/libraries/mod.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/libraries/script_resolver.rs b/src/dynamic_data/libraries/script_resolver.rs old mode 100644 new mode 100755 index be168da..d403677 --- a/src/dynamic_data/libraries/script_resolver.rs +++ b/src/dynamic_data/libraries/script_resolver.rs @@ -1,7 +1,7 @@ use std::fmt::Debug; use std::sync::Arc; -use crate::dynamic_data::{ItemScript, Script}; +use crate::dynamic_data::{ItemScript, Script, ScriptOwnerData}; use crate::static_data::Item; use crate::{PkmnResult, StringKey}; @@ -13,7 +13,7 @@ pub trait ScriptResolver: Debug { /// can be created with this combination, returns None. fn load_script( &self, - owner: *const u8, + owner: ScriptOwnerData, category: ScriptCategory, script_key: &StringKey, ) -> PkmnResult>>; @@ -58,7 +58,7 @@ pub struct EmptyScriptResolver {} impl ScriptResolver for EmptyScriptResolver { fn load_script( &self, - _owner: *const u8, + _owner: ScriptOwnerData, _category: ScriptCategory, _script_key: &StringKey, ) -> PkmnResult>> { diff --git a/src/dynamic_data/mod.rs b/src/dynamic_data/mod.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs old mode 100644 new mode 100755 index 195c8a9..f4112ea --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -194,7 +194,7 @@ impl Battle { let mut winning_side = None; for (side_index, side) in self.sides.iter().enumerate() { // If any side has fled, the battle end. - if side.has_fled() { + if side.has_fled_battle() { let _w = self.result.write(); unsafe { self.result.data_ptr().replace(BattleResult::Inconclusive); @@ -339,8 +339,7 @@ impl VolatileScriptsOwner for Battle { } fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>> { - self.library - .load_script((self as *const Self).cast(), ScriptCategory::Battle, key) + self.library.load_script(self.into(), ScriptCategory::Battle, key) } } diff --git a/src/dynamic_data/models/battle_party.rs b/src/dynamic_data/models/battle_party.rs old mode 100644 new mode 100755 index 0273e4f..ee58fd8 --- a/src/dynamic_data/models/battle_party.rs +++ b/src/dynamic_data/models/battle_party.rs @@ -6,6 +6,7 @@ use crate::dynamic_data::models::pokemon_party::PokemonParty; /// A battle party is a wrapper around a party, with the indices for which the party is responsible /// on the field attached. #[derive(Debug)] +#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct BattleParty { /// The party the BattleParty is holding. party: Arc, @@ -47,4 +48,9 @@ impl BattleParty { pub fn get_pokemon(&self, index: usize) -> &Option> { self.party.at(index) } + + /// Gets the underlying Pokemon Party + pub fn party(&self) -> &Arc { + &self.party + } } diff --git a/src/dynamic_data/models/battle_random.rs b/src/dynamic_data/models/battle_random.rs old mode 100644 new mode 100755 index e14e664..073ee9b --- a/src/dynamic_data/models/battle_random.rs +++ b/src/dynamic_data/models/battle_random.rs @@ -9,6 +9,7 @@ use crate::utils::Random; /// The RNG for a battle. #[derive(Default)] +#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct BattleRandom { /// The actual underlying RNG. This is in a mutex, so it is thread safe, and can be ran /// predictably, with guaranteed the same outputs. diff --git a/src/dynamic_data/models/battle_side.rs b/src/dynamic_data/models/battle_side.rs old mode 100644 new mode 100755 index fe74bb4..5ed3136 --- a/src/dynamic_data/models/battle_side.rs +++ b/src/dynamic_data/models/battle_side.rs @@ -18,6 +18,7 @@ use crate::{script_hook, PkmnResult, StringKey}; /// A side on a battle. #[derive(Debug)] +#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct BattleSide { /// The index of the side on the battle. index: u8, @@ -239,11 +240,6 @@ impl BattleSide { true } - /// Checks whether the side has fled. - pub fn has_fled(&self) -> bool { - self.has_fled_battle - } - /// Mark the side as fled. pub fn mark_as_fled(&mut self) { self.has_fled_battle = true; @@ -304,7 +300,7 @@ impl VolatileScriptsOwner for BattleSide { fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>> { self.battle() .library() - .load_script((self as *const Self).cast(), crate::ScriptCategory::Side, key) + .load_script(self.into(), crate::ScriptCategory::Side, key) } } diff --git a/src/dynamic_data/models/executing_move.rs b/src/dynamic_data/models/executing_move.rs old mode 100644 new mode 100755 index 41cee96..16ddcc4 --- a/src/dynamic_data/models/executing_move.rs +++ b/src/dynamic_data/models/executing_move.rs @@ -15,6 +15,7 @@ use crate::{PkmnResult, PokemonError}; /// A hit data is the data for a single hit, on a single target. #[derive(Default, Debug)] +#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct HitData { /// Whether or not the hit is critical. critical: AtomicBool, @@ -158,7 +159,7 @@ impl ExecutingMove { } /// Gets a hit data for a target, with a specific index. - pub fn get_hit_data<'func>(&'func self, for_target: &'func Arc, hit: u8) -> PkmnResult<&'func HitData> { + pub fn get_hit_data(&self, for_target: &Pokemon, hit: u8) -> PkmnResult<&HitData> { for (index, target) in self.targets.iter().enumerate() { if let Some(target) = target { if std::ptr::eq(target.deref().deref(), for_target.deref().deref()) { diff --git a/src/dynamic_data/models/learned_move.rs b/src/dynamic_data/models/learned_move.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/models/mod.rs b/src/dynamic_data/models/mod.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs old mode 100644 new mode 100755 index 0d01579..ac31fa0 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -502,11 +502,7 @@ impl Pokemon { let ability_script = self .library - .load_script( - (self as *const Self).cast(), - ScriptCategory::Ability, - self.active_ability().name(), - ) + .load_script((&*self).into(), ScriptCategory::Ability, self.active_ability().name()) .unwrap(); if let Some(ability_script) = ability_script { self.ability_script @@ -772,8 +768,7 @@ impl VolatileScriptsOwner for Pokemon { } fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>> { - self.library - .load_script((self as *const Self).cast(), ScriptCategory::Pokemon, key) + self.library.load_script(self.into(), ScriptCategory::Pokemon, key) } } diff --git a/src/dynamic_data/models/pokemon_builder.rs b/src/dynamic_data/models/pokemon_builder.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/models/pokemon_party.rs b/src/dynamic_data/models/pokemon_party.rs old mode 100644 new mode 100755 index 17f5154..2d33460 --- a/src/dynamic_data/models/pokemon_party.rs +++ b/src/dynamic_data/models/pokemon_party.rs @@ -4,6 +4,7 @@ use crate::dynamic_data::models::pokemon::Pokemon; /// A list of Pokemon belonging to a trainer. #[derive(Debug)] +#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct PokemonParty { /// The underlying list of Pokemon. pokemon: Vec>>, @@ -89,4 +90,16 @@ impl PokemonParty { } } } + + /// Checks if the party contains a given pokemon. + pub fn has_pokemon(&self, pokemon: &Pokemon) -> bool { + for p in &self.pokemon { + if let Some(p) = p { + if std::ptr::eq(p.as_ref(), pokemon) { + return true; + } + } + } + false + } } diff --git a/src/dynamic_data/script_handling/item_script.rs b/src/dynamic_data/script_handling/item_script.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/script_handling/mod.rs b/src/dynamic_data/script_handling/mod.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/script_handling/script.rs b/src/dynamic_data/script_handling/script.rs old mode 100644 new mode 100755 index f4eaa72..024109c --- a/src/dynamic_data/script_handling/script.rs +++ b/src/dynamic_data/script_handling/script.rs @@ -1,17 +1,17 @@ use std::any::Any; use std::fmt::{Debug, Formatter}; use std::ops::Deref; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicPtr, 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::DamageSource; use crate::dynamic_data::ExecutingMove; use crate::dynamic_data::Pokemon; use crate::dynamic_data::{Battle, DynamicLibrary}; +use crate::dynamic_data::{BattleSide, DamageSource}; use crate::static_data::{EffectParameter, TypeIdentifier}; use crate::static_data::{Item, Statistic}; use crate::StringKey; @@ -543,3 +543,33 @@ 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), + /// A script attached to a Battle Side has a reference to that Battle Side. + BattleSide(AtomicPtr), + /// A script attached to a Battle has a reference to that Battle. + Battle(AtomicPtr), + /// A script also can have no owner. + None, +} + +impl Into for &Pokemon { + fn into(self) -> ScriptOwnerData { + ScriptOwnerData::Pokemon(AtomicPtr::new(self as *const Pokemon as *mut Pokemon)) + } +} + +impl Into for &BattleSide { + fn into(self) -> ScriptOwnerData { + ScriptOwnerData::BattleSide(AtomicPtr::new(self as *const BattleSide as *mut BattleSide)) + } +} + +impl Into for &Battle { + fn into(self) -> ScriptOwnerData { + ScriptOwnerData::Battle(AtomicPtr::new(self as *const Battle as *mut Battle)) + } +} diff --git a/src/dynamic_data/script_handling/script_set.rs b/src/dynamic_data/script_handling/script_set.rs old mode 100644 new mode 100755 diff --git a/src/dynamic_data/script_handling/volatile_scripts_owner.rs b/src/dynamic_data/script_handling/volatile_scripts_owner.rs old mode 100644 new mode 100755 index c503174..fe620c9 --- a/src/dynamic_data/script_handling/volatile_scripts_owner.rs +++ b/src/dynamic_data/script_handling/volatile_scripts_owner.rs @@ -22,13 +22,19 @@ pub trait VolatileScriptsOwner { } /// Adds a volatile script by name. - fn add_volatile_script(&mut self, key: &StringKey) -> PkmnResult> { + fn add_volatile_script(&self, key: &StringKey) -> PkmnResult> { self.volatile_scripts() .stack_or_add(key, &|| self.load_volatile_script(key)) } + /// Adds a volatile script by name. + fn add_volatile_script_with_script(&self, script: Arc) -> PkmnResult> { + self.volatile_scripts() + .stack_or_add(&script.name().clone(), &|| Ok(Some(script.clone()))) + } + /// Removes a volatile script by name. - fn remove_volatile_script(&mut self, key: &StringKey) { + fn remove_volatile_script(&self, key: &StringKey) { self.volatile_scripts().remove(key) } } diff --git a/src/lib.rs b/src/lib.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/mod.rs b/src/script_implementations/mod.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/battle.rs b/src/script_implementations/wasm/export_registry/dynamic_data/battle.rs new file mode 100644 index 0000000..5c05240 --- /dev/null +++ b/src/script_implementations/wasm/export_registry/dynamic_data/battle.rs @@ -0,0 +1,71 @@ +use crate::dynamic_data::{ + Battle, BattleParty, BattleRandom, BattleSide, ChoiceQueue, DynamicLibrary, Pokemon, PokemonParty, +}; +use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; +use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; +use wasmer::FunctionEnvMut; + +register! { + fn battle_get_parties( + env: FunctionEnvMut, + battle: ExternRef, + ) -> VecExternRef { + VecExternRef::new(env.data().data().as_ref(), battle.value_func(&env).unwrap().parties()) + } + + fn battle_get_choice_queue( + env: FunctionEnvMut, + battle: ExternRef, + ) -> ExternRef { + let queue = battle.value_func(&env).unwrap().current_turn_queue().read(); + if let Some(queue) = queue.as_ref() { + ExternRef::func_new(&env, queue) + } else { + ExternRef::null() + } + } + + fn battle_get_library( + env: FunctionEnvMut, + battle: ExternRef, + ) -> ExternRef { + ExternRef::func_new(&env, battle.value_func(&env).unwrap().library().as_ref()) + } + + fn battle_get_sides( + env: FunctionEnvMut, + battle: ExternRef, + ) -> VecExternRef { + VecExternRef::new(env.data().data().as_ref(), battle.value_func(&env).unwrap().sides()) + } + + fn battle_get_random( + env: FunctionEnvMut, + battle: ExternRef, + ) -> ExternRef { + ExternRef::func_new(&env, battle.value_func(&env).unwrap().random()) + } + + fn battle_find_party_for_pokemon( + env: FunctionEnvMut, + battle: ExternRef, + pokemon: ExternRef + ) -> ExternRef { + let battle = battle.value_func(&env).unwrap(); + let pokemon = pokemon.value_func(&env).unwrap(); + for party in battle.parties() { + if party.party().has_pokemon(pokemon) { + return ExternRef::func_new(&env, party); + } + } + ExternRef::null() + } + + fn battle_party_get_party( + env: FunctionEnvMut, + battle_party: ExternRef, + ) -> ExternRef { + ExternRef::func_new(&env, battle_party.value_func(&env).unwrap().party().as_ref()) + } +} 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 new file mode 100644 index 0000000..308fa45 --- /dev/null +++ b/src/script_implementations/wasm/export_registry/dynamic_data/battle_random.rs @@ -0,0 +1,30 @@ +use crate::dynamic_data::BattleRandom; +use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::extern_ref::ExternRef; +use wasmer::FunctionEnvMut; + +use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; + +register! { + fn battle_random_get( + env: FunctionEnvMut, + battle_random: ExternRef, + ) -> i32 { + battle_random.value_func(&env).unwrap().get() + } + fn battle_random_get_max( + env: FunctionEnvMut, + battle_random: ExternRef, + max: i32 + ) -> i32 { + battle_random.value_func(&env).unwrap().get_max(max) + } + fn battle_random_get_between( + env: FunctionEnvMut, + battle_random: ExternRef, + min: i32, + max: i32 + ) -> i32 { + battle_random.value_func(&env).unwrap().get_between(min, max) + } +} 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 new file mode 100755 index 0000000..af5be68 --- /dev/null +++ b/src/script_implementations/wasm/export_registry/dynamic_data/battle_side.rs @@ -0,0 +1,123 @@ +use crate::dynamic_data::{Battle, BattleSide, Pokemon, VolatileScriptsOwner}; +use crate::script_implementations::wasm::export_registry::register; +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::{ScriptCategory, StringKey}; +use std::ffi::CString; +use wasmer::{FunctionEnvMut, Value}; + +register! { + fn battleside_has_fled_battle( + env: FunctionEnvMut, + side: ExternRef, + ) -> u8 { + if side.value_func(&env).unwrap().has_fled_battle() { 1 } else { 0 } + } + + fn battleside_is_defeated( + env: FunctionEnvMut, + side: ExternRef, + ) -> u8 { + if side.value_func(&env).unwrap().is_defeated() { 1 } else { 0 } + } + + fn battleside_get_side_index( + env: FunctionEnvMut, + side: ExternRef, + ) -> u8 { + side.value_func(&env).unwrap().index() + } + + fn battleside_get_pokemon_per_side( + env: FunctionEnvMut, + side: ExternRef, + ) -> u8 { + side.value_func(&env).unwrap().pokemon_per_side() + } + + fn battleside_get_battle( + env: FunctionEnvMut, + side: ExternRef, + ) -> ExternRef { + ExternRef::func_new(&env, side.value_func(&env).unwrap().battle()) + } + + fn battleside_get_pokemon( + env: FunctionEnvMut, + side: ExternRef, + index: u32 + ) -> ExternRef { + if let Some(Some(p)) = side.value_func(&env).unwrap().pokemon().get(index as usize) { + ExternRef::func_new(&env, p.as_ref()) + } else { + ExternRef::null() + } + } + + fn battleside_add_volatile_by_name( + env: FunctionEnvMut, + side: ExternRef, + name_ptr: u32 + ) -> u32 { + unsafe { + let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr)); + let script = side.value_func(&env).unwrap().add_volatile_script(&c_name.into()).unwrap(); + if let Some(script) = script { + let script = script.get_as::(); + script.get_wasm_pointer() + } else { + 0 + } + } + } + + fn battleside_add_volatile( + env: FunctionEnvMut, + side: ExternRef, + script_ptr: u32 + ) -> u32 { + let side = side.value_func(&env).unwrap(); + let name_ptr = env.data().data().exported_functions().get(&StringKey::new("script_get_name")).unwrap().call(&mut env.data().data().store_mut(), &[Value::I32(script_ptr as i32)]).unwrap().get(0).unwrap().i32().unwrap() as u32; + unsafe{ + let name_ptr: CString = CString::from_raw(env.data().data().get_raw_pointer(name_ptr)); + let script = env.data().data().setup_script(script_ptr, ScriptCategory::Side, &name_ptr.into(), side.into()).unwrap(); + if let Some(script) = script { + let script = side.add_volatile_script_with_script(script); + script.unwrap().unwrap().get_as::().get_wasm_pointer() + } else { + 0 + } + } + } + + + fn battleside_get_volatile( + env: FunctionEnvMut, + side: ExternRef, + name_ptr: u32 + ) -> u32 { + unsafe { + let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr)); + let script = side.value_func(&env).unwrap().get_volatile_script(&c_name.into()); + if let Some(script) = script { + let script = script.get_as::(); + script.get_wasm_pointer() + } else { + 0 + } + } + } + + fn battleside_remove_volatile( + env: FunctionEnvMut, + side: ExternRef, + name_ptr: u32 + ) { + unsafe { + let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr)); + side.value_func(&env).unwrap().remove_volatile_script(&c_name.into()); + } + } + +} 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 new file mode 100644 index 0000000..ecbac83 --- /dev/null +++ b/src/script_implementations/wasm/export_registry/dynamic_data/choice_queue.rs @@ -0,0 +1,19 @@ +use crate::dynamic_data::{ChoiceQueue, Pokemon}; +use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::extern_ref::ExternRef; +use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; +use wasmer::FunctionEnvMut; + +register! { + fn choice_queue_move_pokemon_choice_next( + env: FunctionEnvMut, + battle_random: ExternRef, + pokemon: ExternRef + ) -> u8 { + if battle_random.value_func(&env).unwrap().move_pokemon_choice_next(pokemon.value_func(&env).unwrap()) { + 1 + } else { + 0 + } + } +} 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 new file mode 100644 index 0000000..7493e08 --- /dev/null +++ b/src/script_implementations/wasm/export_registry/dynamic_data/executing_move.rs @@ -0,0 +1,45 @@ +use crate::dynamic_data::{ExecutingMove, HitData, LearnedMove, Pokemon}; +use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::extern_ref::ExternRef; +use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; +use crate::static_data::MoveData; +use wasmer::FunctionEnvMut; + +register! { + fn executing_move_get_user( + env: FunctionEnvMut, + executing_move: ExternRef, + ) -> ExternRef { + ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().user().as_ref()) + } + + fn executing_move_get_use_move( + env: FunctionEnvMut, + executing_move: ExternRef, + ) -> ExternRef { + ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().use_move().as_ref()) + } + + fn executing_move_get_chosen_move( + env: FunctionEnvMut, + executing_move: ExternRef, + ) -> ExternRef { + ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().chosen_move().as_ref()) + } + + fn executing_move_get_number_of_hits( + env: FunctionEnvMut, + executing_move: ExternRef, + ) -> u8 { + executing_move.value_func(&env).unwrap().number_of_hits() + } + + fn executing_move_get_hit_data( + env: FunctionEnvMut, + executing_move: ExternRef, + target: ExternRef, + hit: u8 + ) -> ExternRef { + ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().get_hit_data(target.value_func(&env).unwrap(), hit).unwrap()) + } +} 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 new file mode 100644 index 0000000..44c36e7 --- /dev/null +++ b/src/script_implementations/wasm/export_registry/dynamic_data/hit_data.rs @@ -0,0 +1,14 @@ +use crate::dynamic_data::HitData; +use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::extern_ref::ExternRef; +use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; +use wasmer::FunctionEnvMut; + +register! { + fn hit_data_fail( + env: FunctionEnvMut, + hit: ExternRef, + ) { + hit.value_func(&env).unwrap().fail() + } +} 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 old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs b/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs old mode 100644 new mode 100755 index ea0a804..b297070 --- a/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs @@ -1,31 +1,67 @@ -use crate::dynamic_data::DynamicLibrary; +use crate::dynamic_data::{DynamicLibrary, ScriptOwnerData}; use crate::script_implementations::wasm::export_registry::register; 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; +mod battle_random; +/// Battle side registration +mod battle_side; +mod choice_queue; +mod executing_move; /// Learned move registration mod learned_move; +mod party; /// Pokemon registration mod pokemon; /// Turn choice registration mod turn_choice; +mod hit_data; +mod battle; register! { fn dynamic_library_get_static_data( - env: FunctionEnvMut, - dynamic_lib: ExternRef, -) -> ExternRef { - ExternRef::func_new(&env, dynamic_lib.value_func(&env).unwrap().static_data()) -} + env: FunctionEnvMut, + dynamic_lib: ExternRef, + ) -> ExternRef { + ExternRef::func_new(&env, dynamic_lib.value_func(&env).unwrap().static_data()) + } + + 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()), + ScriptOwnerData::BattleSide(p) => env.data().data().get_extern_ref_index(p.load(Ordering::Relaxed).as_ref().unwrap()), + ScriptOwnerData::Battle(p) => env.data().data().get_extern_ref_index(p.load(Ordering::Relaxed).as_ref().unwrap()), + ScriptOwnerData::None => 0, + } + } else { + 0 + } + } + } + manual manual_register } /// Additional required manual registration fn manual_register(imports: &mut Imports, store: &mut StoreMut, env: &FunctionEnv) { + battle::register(imports, store, env); turn_choice::register(imports, store, env); pokemon::register(imports, store, env); learned_move::register(imports, store, env); + battle_side::register(imports, store, env); + battle_random::register(imports, store, env); + choice_queue::register(imports, store, env); + party::register(imports, store, env); + executing_move::register(imports, store, env); + hit_data::register(imports, store, env); } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/party.rs b/src/script_implementations/wasm/export_registry/dynamic_data/party.rs new file mode 100644 index 0000000..b205c20 --- /dev/null +++ b/src/script_implementations/wasm/export_registry/dynamic_data/party.rs @@ -0,0 +1,26 @@ +use crate::dynamic_data::{Pokemon, PokemonParty}; +use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::extern_ref::ExternRef; +use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; +use wasmer::FunctionEnvMut; + +register! { + fn party_get_pokemon( + env: FunctionEnvMut, + party: ExternRef, + index: u32 + ) -> ExternRef { + if let Some(v) = &party.value_func(&env).unwrap().pokemon()[index as usize] { + ExternRef::func_new(&env, v.as_ref()) + } else { + ExternRef::null() + } + } + + fn party_get_length( + env: FunctionEnvMut, + party: ExternRef, + ) -> u32 { + party.value_func(&env).unwrap().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 old mode 100644 new mode 100755 index e878424..d6824a3 --- a/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs @@ -1,11 +1,11 @@ use std::mem::transmute; -use crate::dynamic_data::{DynamicLibrary, Pokemon}; +use crate::dynamic_data::{Battle, DynamicLibrary, LearnedMove, Pokemon}; use crate::script_implementations::wasm::export_registry::register; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use crate::static_data::StatisticSet; use crate::static_data::{ClampedStatisticSet, Species}; +use crate::static_data::{Item, StatisticSet}; use wasmer::FunctionEnvMut; register! { @@ -75,4 +75,70 @@ register! { pokemon.value_func(&env).unwrap().damage(damage, transmute(source)); } } + + fn pokemon_get_learned_move( + env: FunctionEnvMut, + pokemon: ExternRef, + index: u32 + ) -> ExternRef { + let read_lock = pokemon.value_func(&env).unwrap().learned_moves().read(); + let mv = read_lock.get(index as usize); + if let Some(Some(mv)) = mv { + ExternRef::func_new(&env, mv) + } + else{ + ExternRef::null() + } + } + + fn pokemon_change_stat_boost( + env: FunctionEnvMut, + pokemon: ExternRef, + stat: u8, + amount: i8, + self_inflicted: u8 + ) -> u8 { + unsafe{ + if pokemon.value_func(&env).unwrap().change_stat_boost(transmute(stat), amount, self_inflicted == 1) { + 1 + } else { + 0 + } + } + } + + fn pokemon_get_battle( + env: FunctionEnvMut, + pokemon: ExternRef, + ) -> ExternRef { + if let Some(battle) = pokemon.value_func(&env).unwrap().get_battle() { + ExternRef::func_new(&env, battle) + } else { + ExternRef::null() + } + } + + fn pokemon_get_battle_side_index( + env: FunctionEnvMut, + pokemon: ExternRef, + ) -> u8 { + if let Some(i) = pokemon.value_func(&env).unwrap().get_battle_index() { + i + } else { + 0 + } + } + + fn pokemon_get_held_item( + env: FunctionEnvMut, + pokemon: ExternRef, + ) -> ExternRef { + let read_lock = pokemon.value_func(&env).unwrap().held_item().read(); + if let Some(item) = read_lock.as_ref() { + ExternRef::func_new(&env, item.as_ref()) + } else { + ExternRef::null() + } + } + } 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 old mode 100644 new mode 100755 index 84c8574..c85ffbc --- a/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs @@ -59,4 +59,11 @@ register! { panic!("Invalid turn choice"); } + fn turn_choice_fail( + env: FunctionEnvMut, + turn_choice: ExternRef, + ) { + turn_choice.value_func(&env).unwrap().fail(); + } + } diff --git a/src/script_implementations/wasm/export_registry/mod.rs b/src/script_implementations/wasm/export_registry/mod.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/export_registry/static_data/item.rs b/src/script_implementations/wasm/export_registry/static_data/item.rs new file mode 100644 index 0000000..1ea754e --- /dev/null +++ b/src/script_implementations/wasm/export_registry/static_data/item.rs @@ -0,0 +1,38 @@ +use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::extern_ref::ExternRef; +use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; +use crate::static_data::Item; +use crate::StringKey; +use std::mem::transmute; +use wasmer::FunctionEnvMut; + +register! { + fn item_get_price( + env: FunctionEnvMut, + item: ExternRef, + ) -> i32 { + item.value_func(&env).unwrap().price() + } + + fn item_get_name( + env: FunctionEnvMut, + item: ExternRef, + ) -> ExternRef { + ExternRef::func_new(&env, item.value_func(&env).unwrap().name()) + } + + fn item_get_category( + env: FunctionEnvMut, + item: ExternRef, + ) -> u8 { + unsafe { transmute(item.value_func(&env).unwrap().category()) } + + } + + fn item_get_battle_category( + env: FunctionEnvMut, + item: ExternRef, + ) -> u8 { + unsafe { transmute(item.value_func(&env).unwrap().battle_category()) } + } +} diff --git a/src/script_implementations/wasm/export_registry/static_data/mod.rs b/src/script_implementations/wasm/export_registry/static_data/mod.rs old mode 100644 new mode 100755 index 699c58a..513832f --- a/src/script_implementations/wasm/export_registry/static_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/static_data/mod.rs @@ -6,6 +6,7 @@ use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::{ItemLibrary, LibrarySettings, MoveLibrary, SpeciesLibrary, StaticData, TypeLibrary}; +mod item; /// Moves data registration mod moves; /// Species data registration @@ -49,4 +50,5 @@ register! { fn manual_registration(imports: &mut Imports, store: &mut StoreMut, env: &FunctionEnv) { moves::register(imports, store, env); species::register(imports, store, env); + item::register(imports, store, env); } diff --git a/src/script_implementations/wasm/export_registry/static_data/moves.rs b/src/script_implementations/wasm/export_registry/static_data/moves.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/export_registry/static_data/species.rs b/src/script_implementations/wasm/export_registry/static_data/species.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/extern_ref.rs b/src/script_implementations/wasm/extern_ref.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/mod.rs b/src/script_implementations/wasm/mod.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/script.rs b/src/script_implementations/wasm/script.rs old mode 100644 new mode 100755 index 5f4987a..9765bf9 --- a/src/script_implementations/wasm/script.rs +++ b/src/script_implementations/wasm/script.rs @@ -1,10 +1,12 @@ use std::any::Any; -use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize}; +use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::Arc; use hashbrown::HashSet; -use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, ExecutingMove, Pokemon, Script, TurnChoice}; +use crate::dynamic_data::{ + Battle, DamageSource, DynamicLibrary, ExecutingMove, Pokemon, Script, ScriptOwnerData, TurnChoice, +}; use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData; use crate::script_implementations::wasm::WebAssemblyScriptCapabilities; @@ -23,7 +25,7 @@ pub struct WebAssemblyScript { /// we will not execute its methods. This holds the number of suppressions on the script. suppressed_count: AtomicUsize, /// The owner of this script (where the script is attached to) - _owner_ptr: AtomicPtr, + owner: ScriptOwnerData, /// Pointer inside WebAssembly memory where the data is for this script. self_ptr: u32, /// Capabilities define which functions we actually implement. @@ -35,7 +37,7 @@ pub struct WebAssemblyScript { impl WebAssemblyScript { /// Instantiates a new WebAssemblyScript. pub fn new( - owner_ptr: *mut u8, + owner: ScriptOwnerData, self_ptr: u32, capabilities: Arc>, environment: Arc, @@ -45,18 +47,27 @@ impl WebAssemblyScript { name, marked_for_deletion: Default::default(), suppressed_count: Default::default(), - _owner_ptr: AtomicPtr::new(owner_ptr), + owner, self_ptr, capabilities, environment, } } + pub(crate) fn get_wasm_pointer(&self) -> u32 { + self.self_ptr + } + /// Check if this script implements a certain capability. #[inline(always)] fn has_capability(&self, cap: &WebAssemblyScriptCapabilities) -> bool { self.capabilities.contains(cap) } + + /// Gets the thing the script is owned by. + pub fn get_owner(&self) -> &ScriptOwnerData { + &self.owner + } } /// Util macro to reduce function call verbosity. diff --git a/src/script_implementations/wasm/script_function_cache.rs b/src/script_implementations/wasm/script_function_cache.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/script_resolver.rs b/src/script_implementations/wasm/script_resolver.rs old mode 100644 new mode 100755 index 5382611..ba4a774 --- a/src/script_implementations/wasm/script_resolver.rs +++ b/src/script_implementations/wasm/script_resolver.rs @@ -11,7 +11,7 @@ use wasmer::{ Memory, Module, Store, StoreMut, StoreRef, TypedFunction, Value, }; -use crate::dynamic_data::{ItemScript, Script, ScriptResolver}; +use crate::dynamic_data::{ItemScript, Script, ScriptOwnerData, ScriptResolver}; use crate::script_implementations::wasm::export_registry::register_webassembly_funcs; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script::WebAssemblyScript; @@ -34,10 +34,6 @@ pub struct WebAssemblyScriptResolver { /// This is the WASM function to load a script. load_script_fn: Option), u32>>, - /// Script capabilities tell us which functions are implemented on a given script. This allows us to skip unneeded - /// WASM calls. - script_capabilities: RwLock>>>, - /// The data for use in the scripting function calls. environment_data: Arc, } @@ -63,13 +59,15 @@ impl WebAssemblyScriptResolver { let store_ptr: *mut Store = store.as_mut(); forget(store); + let environment = Arc::new(WebAssemblyEnvironmentData::new(store_ptr)); + environment.self_arc.write().replace(Arc::downgrade(&environment)); + let s = Self { _store: store_ptr, modules: Default::default(), instances: Default::default(), load_script_fn: None, - script_capabilities: Default::default(), - environment_data: Arc::new(WebAssemblyEnvironmentData::new(store_ptr)), + environment_data: environment, }; Box::new(s) @@ -168,7 +166,7 @@ impl WebAssemblyScriptResolver { impl ScriptResolver for WebAssemblyScriptResolver { fn load_script( &self, - owner: *const u8, + owner: ScriptOwnerData, category: ScriptCategory, script_key: &StringKey, ) -> PkmnResult>> { @@ -182,50 +180,7 @@ impl ScriptResolver for WebAssemblyScriptResolver { ExternRef::new_with_resolver(self, script_key), ) .unwrap(); - if script == 0 { - return Ok(None); - } - - let key = ScriptCapabilitiesKey { - category, - script_key: script_key.clone(), - }; - - if !self.script_capabilities.read().contains_key(&key) { - let mut capabilities = HashSet::new(); - unsafe { - if let Some(get_cap) = self - .environment_data - .exported_functions - .read() - .get::(&"get_script_capabilities".into()) - { - let res = get_cap - .call(&mut self.store_mut(), &[Value::I32(script as i32)]) - .unwrap(); - let ptr = (self.environment_data.memory() as *const WebAssemblyScriptCapabilities) - .offset(res[0].i32().unwrap() as isize); - let length = res[1].i32().unwrap() as usize; - for i in 0..length { - capabilities.insert(*ptr.add(i)); - } - } - } - self.script_capabilities - .write() - .insert(key.clone(), Arc::new(capabilities)); - } - - let read_guard = self.script_capabilities.read(); - let capabilities = read_guard.get(&key).unwrap(); - - Ok(Some(Arc::new(WebAssemblyScript::new( - owner as *mut u8, - script, - capabilities.clone(), - self.environment_data.clone(), - script_key.clone(), - )))) + self.environment_data.setup_script(script, category, script_key, owner) } fn load_item_script(&self, _key: &Item) -> PkmnResult>> { @@ -283,6 +238,16 @@ pub struct WebAssemblyEnvironmentData { /// The WASM store. store: *mut Store, + + /// Script capabilities tell us which functions are implemented on a given script. This allows us to skip unneeded + /// WASM calls. + script_capabilities: RwLock>>>, + + /// A weak reference to ourselves. + self_arc: RwLock>>, + + /// A lookup from WASM memory pointer to their actual script wrappers. + loaded_scripts: RwLock>>, } /// A quick lookup so we can find the extern ref of the value. @@ -310,6 +275,9 @@ impl WebAssemblyEnvironmentData { allocate_mem_fn: Default::default(), temp_allocator: Default::default(), store, + script_capabilities: Default::default(), + self_arc: Default::default(), + loaded_scripts: Default::default(), } } @@ -318,6 +286,19 @@ impl WebAssemblyEnvironmentData { self.memory.read().as_ref().unwrap().view(&self.store_ref()).data_ptr() } + /// Return a pointer to something inside the WASM memory. + pub fn get_raw_pointer(&self, offset: u32) -> *mut T { + unsafe { + self.memory + .read() + .as_ref() + .unwrap() + .view(&self.store_ref()) + .data_ptr() + .offset(offset as isize) as *mut T + } + } + /// This returns the functions exported from WASM. pub fn exported_functions(&self) -> RwLockReadGuard<'_, RawRwLock, HashMap> { self.exported_functions.read() @@ -371,6 +352,7 @@ 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. + #[inline(always)] pub fn get_extern_ref_index + ?Sized>(&self, value: &T) -> u32 { self.get_extern_ref_from_ptr(value as *const T as *const u8, T::id().0, false) } @@ -400,6 +382,7 @@ 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_ptr(&self, ptr: *const u8, type_id: u64, is_vec: bool) -> u32 { if let Some(v) = self.extern_ref_pointers_lookup.read().get(&ExternRefLookupKey { ptr, @@ -462,6 +445,71 @@ impl WebAssemblyEnvironmentData { pub fn store_mut(&self) -> StoreMut<'_> { unsafe { self.store.as_mut().unwrap().as_store_mut() } } + + /// Find a loaded script based on the pointer in WASM memory. + pub(crate) fn get_loaded_script(&self, wasm_ptr: u32) -> Option> { + if let Some(script) = self.loaded_scripts.read().get(&wasm_ptr) { + script.upgrade() + } else { + None + } + } + + /// Wrap a script pointer in WASM memory into a host managed script. + pub fn setup_script( + &self, + script_ptr: u32, + category: ScriptCategory, + script_key: &StringKey, + owner: ScriptOwnerData, + ) -> PkmnResult>> { + if script_ptr == 0 { + return Ok(None); + } + + let key = ScriptCapabilitiesKey { + category, + script_key: script_key.clone(), + }; + + if !self.script_capabilities.read().contains_key(&key) { + let mut capabilities = HashSet::new(); + unsafe { + if let Some(get_cap) = self + .exported_functions + .read() + .get::(&"get_script_capabilities".into()) + { + let res = get_cap + .call(&mut self.store_mut(), &[Value::I32(script_ptr as i32)]) + .unwrap(); + let ptr = + (self.memory() as *const WebAssemblyScriptCapabilities).offset(res[0].i32().unwrap() as isize); + let length = res[1].i32().unwrap() as usize; + for i in 0..length { + capabilities.insert(*ptr.add(i)); + } + } + } + self.script_capabilities + .write() + .insert(key.clone(), Arc::new(capabilities)); + } + + let read_guard = self.script_capabilities.read(); + let capabilities = read_guard.get(&key).unwrap(); + + let script = Arc::new(WebAssemblyScript::new( + owner, + script_ptr, + capabilities.clone(), + self.self_arc.read().as_ref().unwrap().upgrade().unwrap(), + script_key.clone(), + )); + + self.loaded_scripts.write().insert(script_ptr, Arc::downgrade(&script)); + Ok(Some(script)) + } } /// The runtime environment for script execution. This is passed to most of the host functions being called. diff --git a/src/script_implementations/wasm/temp_wasm_allocator.rs b/src/script_implementations/wasm/temp_wasm_allocator.rs old mode 100644 new mode 100755 diff --git a/src/static_data/growth_rates.rs b/src/static_data/growth_rates.rs old mode 100644 new mode 100755 diff --git a/src/static_data/items.rs b/src/static_data/items.rs old mode 100644 new mode 100755 diff --git a/src/static_data/libraries/ability_library.rs b/src/static_data/libraries/ability_library.rs old mode 100644 new mode 100755 diff --git a/src/static_data/libraries/data_library.rs b/src/static_data/libraries/data_library.rs old mode 100644 new mode 100755 diff --git a/src/static_data/libraries/growth_rate_library.rs b/src/static_data/libraries/growth_rate_library.rs old mode 100644 new mode 100755 diff --git a/src/static_data/libraries/item_library.rs b/src/static_data/libraries/item_library.rs old mode 100644 new mode 100755 diff --git a/src/static_data/libraries/library_settings.rs b/src/static_data/libraries/library_settings.rs old mode 100644 new mode 100755 diff --git a/src/static_data/libraries/mod.rs b/src/static_data/libraries/mod.rs old mode 100644 new mode 100755 diff --git a/src/static_data/libraries/move_library.rs b/src/static_data/libraries/move_library.rs old mode 100644 new mode 100755 diff --git a/src/static_data/libraries/species_library.rs b/src/static_data/libraries/species_library.rs old mode 100644 new mode 100755 diff --git a/src/static_data/libraries/static_data.rs b/src/static_data/libraries/static_data.rs old mode 100644 new mode 100755 diff --git a/src/static_data/libraries/type_library.rs b/src/static_data/libraries/type_library.rs old mode 100644 new mode 100755 diff --git a/src/static_data/mod.rs b/src/static_data/mod.rs old mode 100644 new mode 100755 diff --git a/src/static_data/moves/mod.rs b/src/static_data/moves/mod.rs old mode 100644 new mode 100755 diff --git a/src/static_data/moves/move_data.rs b/src/static_data/moves/move_data.rs old mode 100644 new mode 100755 diff --git a/src/static_data/moves/secondary_effect.rs b/src/static_data/moves/secondary_effect.rs old mode 100644 new mode 100755 diff --git a/src/static_data/natures.rs b/src/static_data/natures.rs old mode 100644 new mode 100755 diff --git a/src/static_data/species_data/ability.rs b/src/static_data/species_data/ability.rs old mode 100644 new mode 100755 diff --git a/src/static_data/species_data/form.rs b/src/static_data/species_data/form.rs old mode 100644 new mode 100755 diff --git a/src/static_data/species_data/gender.rs b/src/static_data/species_data/gender.rs old mode 100644 new mode 100755 diff --git a/src/static_data/species_data/learnable_moves.rs b/src/static_data/species_data/learnable_moves.rs old mode 100644 new mode 100755 diff --git a/src/static_data/species_data/mod.rs b/src/static_data/species_data/mod.rs old mode 100644 new mode 100755 diff --git a/src/static_data/species_data/species.rs b/src/static_data/species_data/species.rs old mode 100644 new mode 100755 diff --git a/src/static_data/statistic_set.rs b/src/static_data/statistic_set.rs old mode 100644 new mode 100755 diff --git a/src/static_data/statistics.rs b/src/static_data/statistics.rs old mode 100644 new mode 100755 diff --git a/src/utils/mod.rs b/src/utils/mod.rs old mode 100644 new mode 100755 diff --git a/src/utils/random.rs b/src/utils/random.rs old mode 100644 new mode 100755 diff --git a/src/utils/string_key.rs b/src/utils/string_key.rs old mode 100644 new mode 100755 index 0949bb0..8202f1e --- a/src/utils/string_key.rs +++ b/src/utils/string_key.rs @@ -1,4 +1,5 @@ use std::borrow::Borrow; +use std::ffi::CString; use std::fmt::{Display, Formatter}; use std::hash::{Hash, Hasher}; use std::ops::Deref; @@ -27,23 +28,13 @@ static STRING_CACHE: OnceCell>>> = OnceCell::uninit static EMPTY: OnceCell = OnceCell::uninit(); impl StringKey { - /// Calculates the hash of a string key in a const manner. - pub const fn get_hash_const(s: &[u8; N]) -> u32 { - let mut crc: u32 = 0xffffffff; - - let mut i: usize = 0; - while i < N { - crc = (crc >> 8) ^ CRC_TABLE[((crc ^ (to_lower(s[i]) as u32)) & 0xff) as usize]; - i += 1; - } - crc ^ 0xffffffff - } - /// Gets the hash of a string. - pub fn get_hash(s: &str) -> u32 { + pub const fn get_hash(s: &str) -> u32 { let mut crc: u32 = 0xffffffff; - for byte in s.bytes() { - crc = (crc >> 8) ^ CRC_TABLE[((crc ^ (to_lower(byte) as u32)) & 0xff) as usize]; + let mut i: usize = 0; + while i < s.len() { + crc = (crc >> 8) ^ CRC_TABLE[((crc ^ (to_lower(s.as_bytes()[i]) as u32)) & 0xff) as usize]; + i += 1; } crc ^ 0xffffffff } @@ -115,6 +106,18 @@ impl Display for StringKey { } } +impl Into for CString { + fn into(self) -> StringKey { + StringKey::new(self.to_str().unwrap()) + } +} + +impl Into for &CString { + fn into(self) -> StringKey { + StringKey::new(self.to_str().unwrap()) + } +} + /// Converts a character to lowercased in a const safe way. const fn to_lower(c: u8) -> u8 { if c >= b'A' && c <= b'Z' { @@ -167,7 +170,7 @@ mod tests { let sk = StringKey::new(""); assert_eq!(sk.str(), ""); assert_eq!(sk.hash(), 0); - assert_eq!(sk.hash(), StringKey::get_hash_const(b"")); + assert_eq!(sk.hash(), StringKey::get_hash("")); } #[test] @@ -175,8 +178,8 @@ mod tests { let sk = StringKey::new("foo"); assert_eq!(sk.str(), "foo"); assert_eq!(sk.hash(), 2356372769); - assert_eq!(sk.hash(), StringKey::get_hash_const(b"foo")); - assert_eq!(sk.hash(), StringKey::get_hash_const(b"FOo")); + assert_eq!(sk.hash(), StringKey::get_hash("foo")); + assert_eq!(sk.hash(), StringKey::get_hash("FOo")); } #[test] @@ -184,7 +187,7 @@ mod tests { let sk = StringKey::new("bar"); assert_eq!(sk.str(), "bar"); assert_eq!(sk.hash(), 1996459178); - assert_eq!(sk.hash(), StringKey::get_hash_const(b"bar")); - assert_eq!(sk.hash(), StringKey::get_hash_const(b"baR")); + assert_eq!(sk.hash(), StringKey::get_hash("bar")); + assert_eq!(sk.hash(), StringKey::get_hash("baR")); } } diff --git a/tests/common/data_getter.rs b/tests/common/data_getter.rs old mode 100644 new mode 100755 diff --git a/tests/common/library_loader.rs b/tests/common/library_loader.rs old mode 100644 new mode 100755 diff --git a/tests/common/mod.rs b/tests/common/mod.rs old mode 100644 new mode 100755 diff --git a/tests/common/test_case.rs b/tests/common/test_case.rs old mode 100644 new mode 100755 diff --git a/tests/common/test_step.rs b/tests/common/test_step.rs old mode 100644 new mode 100755 diff --git a/tests/data/Abilities.json b/tests/data/Abilities.json old mode 100644 new mode 100755 diff --git a/tests/data/GrowthRates.json b/tests/data/GrowthRates.json old mode 100644 new mode 100755 diff --git a/tests/data/Items.json b/tests/data/Items.json old mode 100644 new mode 100755 diff --git a/tests/data/Moves.json b/tests/data/Moves.json old mode 100644 new mode 100755 diff --git a/tests/data/Natures.csv b/tests/data/Natures.csv old mode 100644 new mode 100755 diff --git a/tests/data/Pokemon.json b/tests/data/Pokemon.json old mode 100644 new mode 100755 diff --git a/tests/data/Types.csv b/tests/data/Types.csv old mode 100644 new mode 100755 diff --git a/tests/data/gen7_scripts.wasm b/tests/data/gen7_scripts.wasm index b736ee0..9ca435c 100755 Binary files a/tests/data/gen7_scripts.wasm and b/tests/data/gen7_scripts.wasm differ diff --git a/tests/main.rs b/tests/main.rs old mode 100644 new mode 100755 index e2371b9..7ce09ee --- a/tests/main.rs +++ b/tests/main.rs @@ -9,8 +9,7 @@ use std::sync::Arc; use conquer_once::OnceCell; -use pkmn_lib::dynamic_data::{DynamicLibrary, MoveChoice, PokemonBuilder, ScriptCategory, TurnChoice}; -use pkmn_lib::static_data::EffectParameter; +use pkmn_lib::dynamic_data::DynamicLibrary; use crate::common::{library_loader, TestCase}; @@ -51,51 +50,3 @@ fn integration_tests(input: &Path) { println!("\tRunning integration test {}", test_case.name); test_case.run_test(get_library()); } - -#[test] -#[cfg_attr(miri, ignore)] -fn validate_script() { - let lib = get_library(); - let script = lib - .load_script(std::ptr::null(), ScriptCategory::Move, &"test".into()) - .unwrap() - .unwrap(); - let parameters = [EffectParameter::String("foo".into())]; - script.on_initialize(&lib, ¶meters); - script.on_initialize(&lib, ¶meters); - script.on_initialize(&lib, ¶meters); -} - -#[test] -#[cfg_attr(miri, ignore)] -fn validate_script_2() { - let lib = get_library(); - let script = lib - .load_script(std::ptr::null(), ScriptCategory::Move, &"test".into()) - .unwrap() - .unwrap(); - let user = Arc::new( - PokemonBuilder::new(lib, "charizard".into(), 100) - .learn_move("fire_blast".into()) - .build(), - ); - script.on_before_turn(&TurnChoice::Move(MoveChoice::new( - user.clone(), - user.learned_moves().read().get(0).unwrap().as_ref().unwrap().clone(), - 0, - 0, - ))); - assert_eq!(user.current_health(), user.max_health() - 50); - - let mut speed: u32 = 100; - script.change_speed( - &TurnChoice::Move(MoveChoice::new( - user.clone(), - user.learned_moves().read().get(0).unwrap().as_ref().unwrap().clone(), - 0, - 0, - )), - &mut speed, - ); - assert_eq!(speed, 684); -} diff --git a/tests/test_cases/basic_single_turn.yaml b/tests/test_cases/basic_single_turn.yaml old mode 100644 new mode 100755