diff --git a/src/dynamic_data/libraries/script_resolver.rs b/src/dynamic_data/libraries/script_resolver.rs index 44ff61a..c4ea185 100755 --- a/src/dynamic_data/libraries/script_resolver.rs +++ b/src/dynamic_data/libraries/script_resolver.rs @@ -25,6 +25,8 @@ pub trait ScriptResolver: Debug { /// shared between all different usages. fn load_item_script(&self, _key: &dyn Item) -> Result>>; + /// Returns a reference to this script resolver as an Any. This is used to downcast the + /// ScriptResolver to a specific implementation. fn as_any(&self) -> &dyn std::any::Any; } diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index 86f714d..117a29b 100755 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -378,6 +378,7 @@ impl Battle { } } + /// Gets the inner pointer to the reference counted data. pub fn as_ptr(&self) -> *const c_void { Arc::as_ptr(&self.data) as *const c_void } diff --git a/src/dynamic_data/models/battle_side.rs b/src/dynamic_data/models/battle_side.rs index 208f7ca..4fe0eaf 100755 --- a/src/dynamic_data/models/battle_side.rs +++ b/src/dynamic_data/models/battle_side.rs @@ -354,6 +354,7 @@ impl BattleSide { } } + /// Gets the inner pointer to the reference counted data. pub fn as_ptr(&self) -> *const c_void { Arc::as_ptr(&self.data) as *const c_void } diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index d1fe708..b7bbafd 100755 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -543,7 +543,7 @@ impl Pokemon { /// Change the form of the Pokemon. pub fn change_form(&self, form: &Arc) -> Result<()> { - if Arc::ptr_eq(&self.form(), form) { + if self.form().eq(form.deref()) { return Ok(()); } *self.data.form.write() = form.clone(); @@ -816,6 +816,7 @@ impl Pokemon { } } + /// Gets the inner pointer to the reference counted data. pub fn as_ptr(&self) -> *const c_void { Arc::as_ptr(&self.data) as *const c_void } diff --git a/src/ffi/dynamic_data/models/pokemon.rs b/src/ffi/dynamic_data/models/pokemon.rs index 3b7b994..830b961 100644 --- a/src/ffi/dynamic_data/models/pokemon.rs +++ b/src/ffi/dynamic_data/models/pokemon.rs @@ -75,26 +75,31 @@ extern "C" fn pokemon_display_form(handle: FFIHandle) -> FFIHandle) -> LevelInt { handle.from_ffi_handle().level() } +/// The experience of the Pokemon. #[no_mangle] extern "C" fn pokemon_experience(handle: FFIHandle) -> u32 { handle.from_ffi_handle().experience() } +/// The unique identifier of the Pokemon. #[no_mangle] extern "C" fn pokemon_unique_identifier(handle: FFIHandle) -> u32 { handle.from_ffi_handle().unique_identifier() } +/// The gender of the Pokemon. #[no_mangle] extern "C" fn pokemon_gender(handle: FFIHandle) -> Gender { handle.from_ffi_handle().gender() } +/// The coloring of the Pokemon. #[no_mangle] extern "C" fn pokemon_coloring(handle: FFIHandle) -> u8 { handle.from_ffi_handle().coloring() @@ -149,21 +154,25 @@ extern "C" fn pokemon_consume_held_item(handle: FFIHandle) -> FFIResult } } +/// The current health of the Pokemon. #[no_mangle] extern "C" fn pokemon_current_health(handle: FFIHandle) -> u32 { handle.from_ffi_handle().current_health() } +/// The max health of the Pokemon. #[no_mangle] extern "C" fn pokemon_max_health(handle: FFIHandle) -> u32 { handle.from_ffi_handle().max_health() } +/// The current weight of the Pokemon. #[no_mangle] extern "C" fn pokemon_weight(handle: FFIHandle) -> f32 { handle.from_ffi_handle().weight() } +/// The current height of the Pokemon. #[no_mangle] extern "C" fn pokemon_height(handle: FFIHandle) -> f32 { handle.from_ffi_handle().height() @@ -196,11 +205,13 @@ extern "C" fn pokemon_real_ability_index(handle: FFIHandle) -> u8 { handle.from_ffi_handle().real_ability().index } +/// The amount of types the Pokemon has. #[no_mangle] extern "C" fn pokemon_types_length(ptr: FFIHandle) -> usize { ptr.from_ffi_handle().types().len() } +/// Gets a type of the Pokemon. #[no_mangle] extern "C" fn pokemon_types_get(ptr: FFIHandle, index: usize) -> FFIResult { match ptr.from_ffi_handle().types().get_res(index) { diff --git a/src/ffi/ffi_handle.rs b/src/ffi/ffi_handle.rs index 935a3bb..82b70c4 100644 --- a/src/ffi/ffi_handle.rs +++ b/src/ffi/ffi_handle.rs @@ -9,9 +9,14 @@ use std::hash::Hash; use std::sync::atomic::AtomicUsize; use std::sync::{Arc, LazyLock}; +/// A handle of an object that can be passed over FFI. We use this to avoid passing pointers over the +/// FFI boundary. This allows us to be able to move the data around in memory without having to worry +/// about the pointers being invalidated. It also allows us to have a type-safe interface. #[repr(C)] pub(super) struct FFIHandle { + /// An incrementing handle that is unique for each object. handle: usize, + /// The type of the object. _marker: std::marker::PhantomData, } @@ -26,7 +31,10 @@ impl Clone for FFIHandle { impl Copy for FFIHandle {} +/// An FFIObject we can fetch from the FFIHandle. We store this in a hashmap to be able to fetch +/// the object from the handle, and to be able to drop the object when the FFI is done with the handle #[derive(Clone)] +#[allow(clippy::missing_docs_in_private_items)] // I'm not documenting these items. pub(super) enum FFIObject { Ability(Arc), EffectParameter(Arc), @@ -75,11 +83,15 @@ unsafe impl Send for FFIObject {} unsafe impl Sync for FFIObject {} +/// The next handle to be used. static NEXT_HANDLE: AtomicUsize = AtomicUsize::new(0); +/// A lookup to get an actual object from a handle. static FFI_OBJECTS: LazyLock>> = LazyLock::new(|| RwLock::new(HashMap::new())); +/// A lookup to get a handle from an object. static FFI_OBJECTS_INVERSE: LazyLock>> = LazyLock::new(|| RwLock::new(HashMap::new())); impl FFIHandle { + /// Get a handle from an object. pub fn get_handle(o: FFIObject) -> Self { if let Some(handle) = FFI_OBJECTS_INVERSE.read().get(&o) { return Self { @@ -97,6 +109,7 @@ impl FFIHandle { } } + /// An empty handle. This is used when no value is returned. Represented as handle 0 pub fn none() -> Self { Self { handle: 0, @@ -104,6 +117,7 @@ impl FFIHandle { } } + /// Get the object from the handle. #[allow(clippy::unwrap_used)] // Unwrap used, as it is a potential security bug if this fails pub fn resolve(&self) -> FFIObject { FFI_OBJECTS @@ -114,6 +128,7 @@ impl FFIHandle { .clone() } + /// Check if the handle is empty. pub fn is_none(&self) -> bool { self.handle == 0 } @@ -132,9 +147,12 @@ impl Into> for FFIHandle { impl Eq for FFIObject {} +/// A trait to convert a handle into an object. #[allow(clippy::wrong_self_convention)] pub(super) trait FromFFIHandle { + /// Convert a handle into an object. fn from_ffi_handle(&self) -> T; + /// Convert a handle into an object, returning `None` if the handle is empty. fn from_ffi_handle_opt(&self) -> Option; } @@ -229,6 +247,8 @@ impl PartialEq for FFIObject { } } +/// Helper macro to implement the `From` trait for a `FFIObject` enum variant, and the `FromFFIHandle` +/// trait for a `FFIHandle` enum variant. macro_rules! ffi_obj_conversions { ($res_type:ty, $ffi_obj_name:ident) => { impl From<$res_type> for FFIObject { diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 80c1bdc..9c26636 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -1,5 +1,6 @@ /// The foreign function interfaces for the dynamic data mod dynamic_data; +/// The handling of handles for the foreign function interfaces mod ffi_handle; /// The foreign function interfaces for that static data mod static_data; diff --git a/src/lib.rs b/src/lib.rs index f1f4f29..c7045d0 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,8 +8,8 @@ #![allow(ambiguous_glob_reexports)] #![allow(hidden_glob_reexports)] // Documentation linters -// #![deny(missing_docs)] -// #![deny(clippy::missing_docs_in_private_items)] +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] // Linter rules to prevent panics // Currently still a WIP to fix all of these #![deny(clippy::unwrap_used)] diff --git a/src/static_data/libraries/nature_library.rs b/src/static_data/libraries/nature_library.rs index 75f6002..220ed65 100644 --- a/src/static_data/libraries/nature_library.rs +++ b/src/static_data/libraries/nature_library.rs @@ -5,6 +5,7 @@ use anyhow_ext::{ensure, Result}; use indexmap::IndexMap; use parking_lot::RwLock; use std::fmt::Debug; +use std::ops::Deref; use std::sync::Arc; /// A library of all natures that can be used, stored by their names. @@ -64,7 +65,7 @@ impl NatureLibrary for NatureLibraryImpl { for kv in read_lock.iter() { // As natures can't be copied, and should always be the same reference as the value // in the map, we just compare by reference. - if Arc::ptr_eq(&kv.1, nature) { + if kv.1.eq(nature.deref()) { return Ok(kv.0.clone()); } } diff --git a/src/static_data/libraries/type_library.rs b/src/static_data/libraries/type_library.rs index 99f0b37..477ab47 100755 --- a/src/static_data/libraries/type_library.rs +++ b/src/static_data/libraries/type_library.rs @@ -77,6 +77,9 @@ impl TypeLibraryImpl { } impl TypeLibraryImpl { + /// Helper function to get the effectiveness for a single attacking type against a single + /// defending type, without having to acquire read locks multiple times when used in a loop. + #[inline] fn get_single_effectiveness_with_lock( lock: &RwLockReadGuard>>, attacking: TypeIdentifier, diff --git a/src/static_data/natures.rs b/src/static_data/natures.rs index ede2472..ee276f2 100755 --- a/src/static_data/natures.rs +++ b/src/static_data/natures.rs @@ -20,6 +20,9 @@ pub trait Nature: Debug { /// Calculates the modifier for a given stat. If it's the increased stat, returns the increased /// modifier, if it's the decreased stat, returns the decreased modifier. Otherwise returns 1.0 fn get_stat_modifier(&self, stat: Statistic) -> f32; + + /// Checks if two natures are equal. + fn eq(&self, other: &dyn Nature) -> bool; } /// A nature is an attribute on a Pokemon that modifies the effective base stats on a Pokemon. They @@ -83,6 +86,10 @@ impl Nature for NatureImpl { 1.0 } } + + fn eq(&self, other: &dyn Nature) -> bool { + std::ptr::eq(self, other as *const dyn Nature as *const Self) + } } #[cfg(test)] @@ -100,6 +107,7 @@ pub(crate) mod tests { fn increased_modifier(&self) -> f32; fn decreased_modifier(&self) -> f32; fn get_stat_modifier(&self, stat: Statistic) -> f32; + fn eq(&self, other: &dyn Nature) -> bool; } } } diff --git a/src/static_data/species_data/form.rs b/src/static_data/species_data/form.rs index 110ec3b..5fbc98f 100755 --- a/src/static_data/species_data/form.rs +++ b/src/static_data/species_data/form.rs @@ -56,6 +56,9 @@ pub trait Form: Debug { /// Arbitrary flags that can be applied to the move. fn has_flag_by_hash(&self, key_hash: u32) -> bool; + + /// Check if two forms are equal. + fn eq(&self, other: &dyn Form) -> bool; } /// A form is a variant of a specific species. A species always has at least one form, but can have @@ -217,6 +220,10 @@ impl Form for FormImpl { fn has_flag_by_hash(&self, key_hash: u32) -> bool { self.flags.contains::(&key_hash) } + + fn eq(&self, other: &dyn Form) -> bool { + std::ptr::eq(self, other as *const dyn Form as *const Self) + } } #[cfg(test)] @@ -247,6 +254,7 @@ pub(crate) mod tests { fn get_random_hidden_ability<'a>(&'a self, rand: &mut Random) -> Result<&'a StringKey>; fn has_flag(&self, key: &StringKey) -> bool; fn has_flag_by_hash(&self, key_hash: u32) -> bool; + fn eq(&self, other: &dyn Form) -> bool; } } }