Complete refactor of the FFI to use handles instead of pointers.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2023-06-24 14:44:23 +02:00
parent 4c222cb753
commit 78bb91093b
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
76 changed files with 1510 additions and 1952 deletions

View File

@ -8,13 +8,10 @@ use crate::dynamic_data::Pokemon;
use crate::dynamic_data::ScriptContainer;
use crate::dynamic_data::{LearnedMove, ScriptWrapper};
use crate::dynamic_data::{ScriptSource, ScriptSourceData};
use crate::{ValueIdentifiable, ValueIdentifier};
/// The data on a turn choice that should be contained in every turn choice, regardless of type.
#[derive(Debug)]
struct CommonChoiceData {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The user of the turn choice
user: Pokemon,
/// The speed of the user at the beginning of the turn.
@ -180,7 +177,6 @@ impl MoveChoice {
script: Default::default(),
priority: AtomicI8::new(0),
choice_data: Box::new(CommonChoiceData {
identifier: Default::default(),
user,
speed: AtomicU32::new(speed),
random_value: AtomicU32::new(0),
@ -253,7 +249,6 @@ impl ItemChoice {
let speed = user.boosted_stats().speed();
Self {
choice_data: Box::new(CommonChoiceData {
identifier: Default::default(),
user,
speed: AtomicU32::new(speed),
random_value: AtomicU32::new(0),
@ -293,7 +288,6 @@ impl SwitchChoice {
let speed = user.boosted_stats().speed();
Self {
choice_data: Box::new(CommonChoiceData {
identifier: Default::default(),
user,
speed: AtomicU32::new(speed),
random_value: AtomicU32::new(0),
@ -332,7 +326,6 @@ impl FleeChoice {
pub fn new(user: Pokemon) -> Self {
Self {
choice_data: Box::new(CommonChoiceData {
identifier: Default::default(),
user,
speed: AtomicU32::new(0),
random_value: AtomicU32::new(0),
@ -372,7 +365,6 @@ impl PassChoice {
let speed = user.boosted_stats().speed();
Self {
choice_data: Box::new(CommonChoiceData {
identifier: Default::default(),
user,
speed: AtomicU32::new(speed),
random_value: AtomicU32::new(0),
@ -485,9 +477,3 @@ impl Ord for TurnChoice {
}
}
}
impl ValueIdentifiable for TurnChoice {
fn value_identifier(&self) -> ValueIdentifier {
self.choice_data().identifier
}
}

View File

@ -1,7 +1,7 @@
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::script_handling::ScriptSource;
use crate::dynamic_data::Pokemon;
use crate::{script_hook, PkmnError, ValueIdentifiable, ValueIdentifier, VecExt};
use crate::{script_hook, PkmnError, VecExt};
use anyhow::Result;
use anyhow_ext::anyhow;
use parking_lot::lock_api::MappedRwLockReadGuard;
@ -17,8 +17,6 @@ use std::sync::Arc;
/// 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<Vec<Option<Arc<TurnChoice>>>>,
@ -31,7 +29,6 @@ impl ChoiceQueue {
pub(crate) fn new(mut queue: Vec<Option<Arc<TurnChoice>>>) -> Self {
queue.sort_unstable_by(|a, b| b.cmp(a));
Self {
identifier: Default::default(),
queue: RwLock::new(queue),
current: AtomicUsize::new(0),
}
@ -107,7 +104,7 @@ impl ChoiceQueue {
// Find the index for the choice we want to move up.
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() {
if pokemon.eq(choice.user()) {
desired_index = Some(index);
break;
}
@ -158,12 +155,6 @@ impl ChoiceQueue {
}
}
impl ValueIdentifiable for ChoiceQueue {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
#[allow(clippy::indexing_slicing)]
@ -253,10 +244,7 @@ mod tests {
Some(Arc::new(TurnChoice::Pass(PassChoice::new(user2)))),
]);
let inner_queue = queue.get_queue().unwrap();
assert_eq!(
inner_queue[0].as_ref().unwrap().user().value_identifier(),
user1.value_identifier()
);
assert_eq!(inner_queue[0].as_ref().unwrap().user().clone(), user1);
}
#[test]
@ -283,15 +271,9 @@ mod tests {
]);
user2.change_level_by(60).unwrap();
assert_eq!(
user1.value_identifier(),
queue.peek().unwrap().unwrap().user().value_identifier()
);
assert_eq!(user1, queue.peek().unwrap().unwrap().user().clone(),);
queue.resort().unwrap();
assert_eq!(
user2.value_identifier(),
queue.peek().unwrap().unwrap().user().value_identifier()
);
assert_eq!(user2, queue.peek().unwrap().unwrap().user().clone(),);
}
#[test]
@ -303,20 +285,11 @@ mod tests {
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_eq!(user1, queue.peek().unwrap().unwrap().user().clone(),);
assert!(queue.move_pokemon_choice_next(&user2).unwrap());
assert_eq!(
user2.value_identifier(),
queue.dequeue().unwrap().unwrap().user().value_identifier()
);
assert_eq!(
user1.value_identifier(),
queue.dequeue().unwrap().unwrap().user().value_identifier()
);
assert_eq!(user2, queue.dequeue().unwrap().unwrap().user().clone(),);
assert_eq!(user1, queue.dequeue().unwrap().unwrap().user().clone(),);
}
#[test]
@ -328,16 +301,10 @@ mod tests {
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_eq!(user2, queue.dequeue().unwrap().unwrap().user().clone(),);
assert!(!queue.move_pokemon_choice_next(&user2).unwrap());
assert_eq!(
user1.value_identifier(),
queue.dequeue().unwrap().unwrap().user().value_identifier()
);
assert_eq!(user1, queue.dequeue().unwrap().unwrap().user().clone(),);
assert!(queue.peek().unwrap().is_none())
}
@ -350,15 +317,9 @@ mod tests {
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_eq!(user1, queue.peek().unwrap().unwrap().user().clone(),);
assert!(queue.move_pokemon_choice_next(&user1).unwrap());
assert_eq!(
user1.value_identifier(),
queue.peek().unwrap().unwrap().user().value_identifier()
);
assert_eq!(user1, queue.peek().unwrap().unwrap().user().clone(),);
}
#[test]
@ -382,27 +343,15 @@ mod tests {
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_eq!(users[0], queue.peek().unwrap().unwrap().user().clone(),);
assert!(queue.move_pokemon_choice_next(&users[4]).unwrap());
assert_eq!(
users[4].value_identifier(),
queue.dequeue().unwrap().unwrap().user().value_identifier()
);
assert_eq!(users[4], queue.dequeue().unwrap().unwrap().user().clone(),);
for index in 0..4 {
assert_eq!(
users[index].value_identifier(),
queue.dequeue().unwrap().unwrap().user().value_identifier()
);
assert_eq!(users[index], queue.dequeue().unwrap().unwrap().user().clone(),);
}
for index in 5..7 {
assert_eq!(
users[index].value_identifier(),
queue.dequeue().unwrap().unwrap().user().value_identifier()
);
assert_eq!(users[index], queue.dequeue().unwrap().unwrap().user().clone(),);
}
}
}

View File

@ -4,10 +4,9 @@ use std::fmt::Debug;
use crate::dynamic_data::Pokemon;
use crate::static_data::Statistic;
use crate::static_data::StatisticSet;
use crate::{ValueIdentifiable, ValueIdentifier};
/// A battle stat calculator is used to calculate stats for a Pokemon.
pub trait BattleStatCalculator: Debug + ValueIdentifiable {
pub trait BattleStatCalculator: Debug {
/// Calculate all the flat stats of a Pokemon, disregarding stat boosts.
fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()>;
/// Calculate a single flat stat of a Pokemon, disregarding stat boost
@ -20,10 +19,7 @@ pub trait BattleStatCalculator: Debug + ValueIdentifiable {
/// A basic implementation of the Gen 7 stat calculator.
#[derive(Debug)]
pub struct Gen7BattleStatCalculator {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
}
pub struct Gen7BattleStatCalculator {}
impl Default for Gen7BattleStatCalculator {
fn default() -> Self {
@ -34,9 +30,7 @@ impl Default for Gen7BattleStatCalculator {
impl Gen7BattleStatCalculator {
/// Creates a new Gen 7 battle stat calculator
pub fn new() -> Self {
Self {
identifier: Default::default(),
}
Self {}
}
/// The calculation used for health points.
@ -141,11 +135,6 @@ impl BattleStatCalculator for Gen7BattleStatCalculator {
}
}
impl ValueIdentifiable for Gen7BattleStatCalculator {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
pub mod tests {
@ -160,10 +149,5 @@ pub mod tests {
fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()>;
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32>;
}
impl ValueIdentifiable for BattleStatCalculator {
fn value_identifier(&self) -> ValueIdentifier{
ValueIdentifier::new(0)
}
}
}
}

View File

@ -5,12 +5,12 @@ use std::sync::Arc;
use crate::dynamic_data::script_handling::ScriptSource;
use crate::dynamic_data::{Battle, Pokemon};
use crate::dynamic_data::{ExecutingMove, HitData};
use crate::script_hook;
use crate::static_data::{MoveCategory, Statistic};
use crate::{script_hook, ValueIdentifiable, ValueIdentifier};
/// A damage library holds the functions related to the calculation of damage. As this can change in
/// different generations and implementations, this is handled through a trait.
pub trait DamageLibrary: std::fmt::Debug + ValueIdentifiable {
pub trait DamageLibrary: std::fmt::Debug {
/// Calculate the damage for a given hit on a Pokemon.
fn get_damage(
&self,
@ -42,8 +42,6 @@ pub trait DamageLibrary: std::fmt::Debug + ValueIdentifiable {
/// The implementation of a Damage Library for generation 7.
#[derive(Debug)]
pub struct Gen7DamageLibrary {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// Defines whether or not a random damage modifier is applied to damage (0.85 - 1.00).
has_randomness: bool,
}
@ -52,10 +50,7 @@ impl Gen7DamageLibrary {
/// Creates a new generation 7 damage library. `has_randomness` defines whether a random damage
/// modifier (0.85x - 1.00x) is applied to the calculated damage.
pub fn new(has_randomness: bool) -> Self {
Self {
identifier: Default::default(),
has_randomness,
}
Self { has_randomness }
}
/// Calculates the modifier applied to damage from the statistics of the relevant Pokemon.
@ -312,9 +307,3 @@ impl DamageLibrary for Gen7DamageLibrary {
})
}
}
impl ValueIdentifiable for Gen7DamageLibrary {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}

View File

@ -10,11 +10,11 @@ use crate::dynamic_data::{ItemScript, ScriptResolver};
use crate::dynamic_data::{Script, ScriptOwnerData};
use crate::static_data::Item;
use crate::static_data::StaticData;
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
/// The dynamic library stores a static data library, as well as holding different libraries and
/// calculators that might be customized between different generations and implementations.
pub trait DynamicLibrary: Debug + ValueIdentifiable {
pub trait DynamicLibrary: Debug + Send + Sync {
/// The static data is the immutable storage data for this library.
fn static_data(&self) -> &Arc<dyn StaticData>;
/// The stat calculator deals with the calculation of flat and boosted stats, based on the
@ -39,13 +39,10 @@ pub trait DynamicLibrary: Debug + ValueIdentifiable {
/// shared between all different usages.
fn load_item_script(&self, _key: &Arc<dyn Item>) -> Result<Option<Arc<dyn ItemScript>>>;
}
/// The dynamic library stores a static data library, as well as holding different libraries and
/// calculators that might be customized between different generations and implementations.
#[derive(Debug)]
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: Arc<dyn StaticData>,
/// The stat calculator deals with the calculation of flat and boosted stats, based on the
@ -58,9 +55,13 @@ pub struct DynamicLibraryImpl {
misc_library: Arc<dyn MiscLibrary>,
/// The script resolver deals with how to resolve the scripts from specific unique key combinations.
script_resolver: Box<dyn ScriptResolver>,
script_resolver: Arc<dyn ScriptResolver>,
}
unsafe impl Send for DynamicLibraryImpl {}
unsafe impl Sync for DynamicLibraryImpl {}
impl DynamicLibraryImpl {
/// Instantiates a new DynamicLibrary with given parameters.
pub fn new(
@ -68,10 +69,9 @@ impl DynamicLibraryImpl {
stat_calculator: Arc<dyn BattleStatCalculator>,
damage_calculator: Arc<dyn DamageLibrary>,
misc_library: Arc<dyn MiscLibrary>,
script_resolver: Box<dyn ScriptResolver>,
script_resolver: Arc<dyn ScriptResolver>,
) -> Self {
Self {
identifier: Default::default(),
static_data,
stat_calculator,
damage_calculator,
@ -120,12 +120,6 @@ impl DynamicLibrary for DynamicLibraryImpl {
}
}
impl ValueIdentifiable for DynamicLibraryImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
pub mod test {
@ -138,6 +132,10 @@ pub mod test {
mockall::mock! {
#[derive(Debug)]
pub DynamicLibrary{}
unsafe impl Send for DynamicLibrary{}
unsafe impl Sync for DynamicLibrary{}
impl DynamicLibrary for DynamicLibrary {
fn static_data(&self) -> &Arc<dyn StaticData>;
fn stat_calculator(&self) -> &Arc<dyn BattleStatCalculator>;
@ -151,23 +149,15 @@ pub mod test {
) -> Result<Option<Arc<dyn Script>>>;
fn load_item_script(&self, _key: &Arc<dyn Item>) -> Result<Option<Arc<dyn ItemScript>>>;
}
impl ValueIdentifiable for DynamicLibrary{
fn value_identifier(&self) -> ValueIdentifier{
ValueIdentifier::new(0)
}
}
}
pub fn build() -> DynamicLibraryImpl {
DynamicLibraryImpl {
identifier: Default::default(),
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(),
}),
script_resolver: Arc::new(EmptyScriptResolver {}),
}
}
}

View File

@ -7,10 +7,10 @@ use crate::dynamic_data::choices::{MoveChoice, TurnChoice};
use crate::dynamic_data::Pokemon;
use crate::dynamic_data::{LearnedMove, MoveLearnMethod};
use crate::static_data::{MoveCategory, MoveData, MoveDataImpl, MoveTarget, SecondaryEffectImpl};
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
/// The misc library holds several misc functions required for the battle to run.
pub trait MiscLibrary: Debug + ValueIdentifiable {
pub trait MiscLibrary: Debug {
/// 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.
@ -22,8 +22,6 @@ pub trait MiscLibrary: Debug + ValueIdentifiable {
/// A gen 7 implementation for the MiscLibrary.
#[derive(Debug)]
pub struct Gen7MiscLibrary {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The learned move data for struggle.
struggle_learned_move: Arc<LearnedMove>,
}
@ -40,7 +38,7 @@ impl Gen7MiscLibrary {
255,
MoveTarget::Any,
0,
Some(Box::new(SecondaryEffectImpl::new(
Some(Arc::new(SecondaryEffectImpl::new(
-1.0,
StringKey::new("struggle"),
vec![],
@ -48,10 +46,7 @@ impl Gen7MiscLibrary {
HashSet::new(),
));
let struggle_learned_move = Arc::new(LearnedMove::new(struggle_data, MoveLearnMethod::Unknown));
Self {
identifier: Default::default(),
struggle_learned_move,
}
Self { struggle_learned_move }
}
}
@ -76,9 +71,3 @@ impl MiscLibrary for Gen7MiscLibrary {
))
}
}
impl ValueIdentifiable for Gen7MiscLibrary {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}

View File

@ -1,15 +1,16 @@
use anyhow::Result;
use std::any::Any;
use std::fmt::Debug;
use std::sync::Arc;
use crate::dynamic_data::{ItemScript, Script, ScriptOwnerData};
use crate::static_data::Item;
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
/// A script resolver deals with the resolving of scripts. These scripts are non-hardcoded
/// implementations of different effects in Pokemon. This allows for things such as generational
/// differences, and custom implementations.
pub trait ScriptResolver: Debug + ValueIdentifiable {
pub trait ScriptResolver: Debug {
/// Loads a standard script with a given unique combination of category and key. If no script
/// can be created with this combination, returns None.
fn load_script(
@ -23,6 +24,8 @@ pub trait ScriptResolver: Debug + ValueIdentifiable {
/// combinations, returns None. Note that ItemScripts are immutable, as their script should be
/// shared between all different usages.
fn load_item_script(&self, _key: &dyn Item) -> Result<Option<Arc<dyn ItemScript>>>;
fn as_any(&self) -> &dyn std::any::Any;
}
use std::fmt::Display;
@ -57,16 +60,7 @@ pub enum ScriptCategory {
/// A basic empty script resolver, that always returns None.
#[derive(Debug, Default)]
pub struct EmptyScriptResolver {
/// A unique identifier so we know what value this is.
pub identifier: ValueIdentifier,
}
impl ValueIdentifiable for EmptyScriptResolver {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
pub struct EmptyScriptResolver {}
impl ScriptResolver for EmptyScriptResolver {
fn load_script(
@ -81,4 +75,8 @@ impl ScriptResolver for EmptyScriptResolver {
fn load_item_script(&self, _key: &dyn Item) -> Result<Option<Arc<dyn ItemScript>>> {
Ok(None)
}
fn as_any(&self) -> &dyn Any {
self
}
}

View File

@ -21,13 +21,11 @@ use crate::dynamic_data::VolatileScriptsOwner;
use crate::dynamic_data::{is_valid_target, ScriptWrapper};
use crate::dynamic_data::{ChoiceQueue, ScriptContainer};
use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData};
use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier, VecExt};
use crate::{script_hook, PkmnError, StringKey, VecExt};
/// The data of a battle.
#[derive(Debug)]
struct BattleData {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The library the battle uses for handling.
library: Arc<dyn DynamicLibrary>,
/// A list of all different parties in the battle.
@ -99,7 +97,6 @@ impl Battle {
}
let battle = BattleData {
identifier: Default::default(),
library,
parties,
can_flee,
@ -127,13 +124,6 @@ impl Battle {
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<dyn DynamicLibrary> {
&self.data.library
@ -272,7 +262,7 @@ impl Battle {
}
/// Try and set the choice for the battle. If the choice is not valid, this returns false.
pub fn try_set_choice(&self, choice: TurnChoice) -> Result<bool> {
pub fn try_set_choice(&self, choice: Arc<TurnChoice>) -> Result<bool> {
if !self.can_use(&choice) {
return Ok(false);
}
@ -380,6 +370,23 @@ impl Battle {
Ok(None)
}
}
/// Returns a weak reference to the battle.
pub fn weak(&self) -> WeakBattleReference {
WeakBattleReference {
data: Arc::downgrade(&self.data),
}
}
pub fn as_ptr(&self) -> *const c_void {
Arc::as_ptr(&self.data) as *const c_void
}
}
impl PartialEq for Battle {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.data, &other.data)
}
}
impl WeakBattleReference {
@ -437,12 +444,6 @@ impl ScriptSource for Battle {
}
}
impl ValueIdentifiable for Battle {
fn value_identifier(&self) -> ValueIdentifier {
self.data.identifier
}
}
/// The result of a battle.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BattleResult {

View File

@ -3,14 +3,11 @@ use std::sync::Arc;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::models::pokemon_party::PokemonParty;
use crate::{ValueIdentifiable, ValueIdentifier};
/// A battle party is a wrapper around a party, with the indices for which the party is responsible
/// on the field attached.
#[derive(Debug)]
pub struct BattleParty {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The party the BattleParty is holding.
party: Arc<PokemonParty>,
/// The indices for which the party is responsible, in the format (side, index)
@ -22,7 +19,6 @@ impl BattleParty {
/// for.
pub fn new(party: Arc<PokemonParty>, responsible_indices: Vec<(u8, u8)>) -> Result<Self> {
Ok(Self {
identifier: Default::default(),
party,
responsible_indices,
})
@ -58,9 +54,3 @@ impl BattleParty {
&self.party
}
}
impl ValueIdentifiable for BattleParty {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}

View File

@ -6,13 +6,11 @@ use crate::dynamic_data::models::executing_move::ExecutingMove;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::ScriptSource;
use crate::utils::Random;
use crate::{script_hook, PkmnError, ValueIdentifiable, ValueIdentifier};
use crate::{script_hook, PkmnError};
/// The RNG for a battle.
#[derive(Default)]
pub struct BattleRandom {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The actual underlying RNG. This is in a mutex, so it is thread safe, and can be ran
/// predictably, with guaranteed the same outputs.
random: Mutex<Random>,
@ -22,7 +20,6 @@ impl BattleRandom {
/// Initializes a new RNG with a given seed.
pub fn new_with_seed(seed: u128) -> Self {
BattleRandom {
identifier: Default::default(),
random: Mutex::new(Random::new(seed)),
}
}
@ -104,8 +101,6 @@ impl Debug for BattleRandom {
impl Clone for BattleRandom {
fn clone(&self) -> Self {
Self {
identifier: Default::default(),
// As cloning when we can't get a lock on the randomness is completely impossible, we
// should unwrap here.
#[allow(clippy::unwrap_used)]
@ -113,9 +108,3 @@ impl Clone for BattleRandom {
}
}
}
impl ValueIdentifiable for BattleRandom {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}

View File

@ -15,13 +15,11 @@ use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, Scrip
use crate::dynamic_data::ScriptSet;
use crate::dynamic_data::VolatileScriptsOwner;
use crate::dynamic_data::{Script, WeakBattleReference};
use crate::{script_hook, StringKey, ValueIdentifiable, ValueIdentifier, VecExt};
use crate::{script_hook, StringKey, VecExt};
/// The data that is stored for a battle side.
#[derive(Debug)]
struct BattleSideData {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The index of the side on the battle.
index: u8,
/// The number of Pokemon that can be on the side.
@ -47,7 +45,7 @@ struct BattleSideData {
}
/// A side on a battle.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct BattleSide {
/// The data that is stored for this side.
data: Arc<BattleSideData>,
@ -77,7 +75,6 @@ impl BattleSide {
Self {
data: Arc::new(BattleSideData {
identifier: Default::default(),
index,
pokemon_per_side,
pokemon,
@ -171,11 +168,11 @@ impl BattleSide {
}
/// Sets a choice for a Pokemon on this side.
pub(crate) fn set_choice(&self, choice: TurnChoice) -> Result<()> {
pub(crate) fn set_choice(&self, choice: Arc<TurnChoice>) -> Result<()> {
for (index, pokemon_slot) in self.data.pokemon.read().iter().enumerate() {
if let Some(pokemon) = pokemon_slot {
if Pokemon::eq(pokemon, choice.user()) {
self.data.choices.write().get_mut_res(index)?.replace(choice.into());
self.data.choices.write().get_mut_res(index)?.replace(choice);
self.data.choices_set.fetch_add(1, Ordering::SeqCst);
return Ok(());
}
@ -337,7 +334,7 @@ impl BattleSide {
None => return Ok(false),
};
// Don't allow swapping if different parties are responsible for the indices.
if party_a.value_identifier() != party_b.value_identifier() {
if !Arc::ptr_eq(party_a, party_b) {
return Ok(false);
}
@ -356,6 +353,16 @@ impl BattleSide {
data: Arc::downgrade(&self.data),
}
}
pub fn as_ptr(&self) -> *const c_void {
Arc::as_ptr(&self.data) as *const c_void
}
}
impl PartialEq for BattleSide {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.data, &other.data)
}
}
impl WeakBattleSideReference {
@ -411,9 +418,3 @@ impl ScriptSource for BattleSide {
self.battle()?.collect_scripts(scripts)
}
}
impl ValueIdentifiable for BattleSide {
fn value_identifier(&self) -> ValueIdentifier {
self.data.identifier
}
}

View File

@ -12,13 +12,11 @@ use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, Scrip
use crate::dynamic_data::ScriptContainer;
use crate::dynamic_data::TargetList;
use crate::static_data::{MoveData, TypeIdentifier};
use crate::{PkmnError, ValueIdentifiable, ValueIdentifier};
use crate::PkmnError;
/// 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,
/// Whether or not the hit is critical.
critical: AtomicBool,
/// The base power of the hit.
@ -89,8 +87,6 @@ impl HitData {
#[derive(Debug)]
pub struct ExecutingMove {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The number of hits this move has.
number_of_hits: u8,
/// A list of hits for this move. For multi target multi hit moves, this stores the hits linearly,
@ -126,7 +122,6 @@ impl ExecutingMove {
hits.push(Arc::new(HitData::default()))
}
Self {
identifier: Default::default(),
number_of_hits,
hits,
user,
@ -238,15 +233,3 @@ impl ScriptSource for ExecutingMove {
Ok(())
}
}
impl ValueIdentifiable for ExecutingMove {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
impl ValueIdentifiable for HitData {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}

View File

@ -2,14 +2,11 @@ use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::Arc;
use crate::static_data::MoveData;
use crate::{ValueIdentifiable, ValueIdentifier};
/// A learned move is the data attached to a Pokemon for a move it has learned. It has information
/// such as the remaining amount of users, how it has been learned, etc.
#[derive(Debug)]
pub struct LearnedMove {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The immutable move information of the move.
move_data: Arc<dyn MoveData>,
/// The maximal power points for this move.
@ -36,7 +33,6 @@ impl LearnedMove {
pub fn new(move_data: Arc<dyn MoveData>, learn_method: MoveLearnMethod) -> Self {
let max_pp = move_data.base_usages();
Self {
identifier: Default::default(),
move_data,
max_pp,
remaining_pp: AtomicU8::new(max_pp),
@ -92,12 +88,6 @@ impl LearnedMove {
}
}
impl ValueIdentifiable for LearnedMove {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {

View File

@ -26,13 +26,11 @@ use crate::static_data::TypeIdentifier;
use crate::static_data::{Ability, Statistic};
use crate::static_data::{ClampedStatisticSet, StatisticSet};
use crate::utils::Random;
use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier, VecExt};
use crate::{script_hook, PkmnError, StringKey, VecExt};
use anyhow::{anyhow, bail, Result};
/// 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.
library: Arc<dyn DynamicLibrary>,
/// The species of the Pokemon.
@ -165,7 +163,6 @@ impl Pokemon {
.get_nature(nature)
.ok_or(PkmnError::InvalidNatureName { nature: nature.clone() })?;
let pokemon_data = PokemonData {
identifier: Default::default(),
library,
species: RwLock::new(species),
form: RwLock::new(form.clone()),
@ -546,7 +543,7 @@ impl Pokemon {
/// Change the form of the Pokemon.
pub fn change_form(&self, form: &Arc<dyn Form>) -> Result<()> {
if self.form().value_identifier() == form.value_identifier() {
if Arc::ptr_eq(&self.form(), form) {
return Ok(());
}
*self.data.form.write() = form.clone();
@ -818,6 +815,10 @@ impl Pokemon {
data: Arc::downgrade(&self.data),
}
}
pub fn as_ptr(&self) -> *const c_void {
Arc::as_ptr(&self.data) as *const c_void
}
}
impl PartialEq for Pokemon {
@ -938,12 +939,6 @@ impl VolatileScriptsOwner for Pokemon {
}
}
impl ValueIdentifiable for Pokemon {
fn value_identifier(&self) -> ValueIdentifier {
self.data.identifier
}
}
/// A source of damage. This should be as unique as possible.
#[derive(Debug, Clone, Copy)]
#[repr(u8)]

View File

@ -3,13 +3,11 @@ use parking_lot::lock_api::RwLockReadGuard;
use parking_lot::{RawRwLock, RwLock};
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::{ValueIdentifiable, ValueIdentifier, VecExt};
use crate::VecExt;
/// A list of Pokemon belonging to a trainer.
#[derive(Debug)]
pub struct PokemonParty {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The underlying list of Pokemon.
pokemon: RwLock<Vec<Option<Pokemon>>>,
}
@ -22,7 +20,6 @@ impl PokemonParty {
pokemon.push(None);
}
Self {
identifier: Default::default(),
pokemon: RwLock::new(pokemon),
}
}
@ -30,7 +27,6 @@ impl PokemonParty {
/// Instantiates a party with a list.
pub fn new_from_vec(pokemon: Vec<Option<Pokemon>>) -> Self {
Self {
identifier: Default::default(),
pokemon: RwLock::new(pokemon),
}
}
@ -118,9 +114,3 @@ impl PokemonParty {
false
}
}
impl ValueIdentifiable for PokemonParty {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}

View File

@ -1,105 +1,102 @@
use crate::dynamic_data::{FleeChoice, LearnedMove, MoveChoice, PassChoice, Pokemon, TurnChoice};
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::FFIResult;
use anyhow::anyhow;
use std::ptr::drop_in_place;
use std::ops::Deref;
use std::sync::Arc;
/// Disposes a turn choice.
#[no_mangle]
extern "C" fn turn_choice_drop(choice: OwnedPtr<TurnChoice>) {
unsafe { drop_in_place(choice) }
}
/// Get the user of the given choice.
#[no_mangle]
extern "C" fn turn_choice_user(choice: ExternPointer<TurnChoice>) -> IdentifiablePointer<Pokemon> {
choice.as_ref().user().clone().into()
extern "C" fn turn_choice_user(choice: FFIHandle<Arc<TurnChoice>>) -> FFIHandle<Pokemon> {
FFIHandle::get_handle(choice.from_ffi_handle().user().clone().into())
}
/// 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!
#[no_mangle]
extern "C" fn turn_choice_speed(choice: ExternPointer<TurnChoice>) -> u32 {
choice.as_ref().speed()
extern "C" fn turn_choice_speed(choice: FFIHandle<Arc<TurnChoice>>) -> u32 {
choice.from_ffi_handle().speed()
}
/// Gets whether or not the choice has failed. If we notice this when we execute the choice, we
/// will not execute it.
#[no_mangle]
extern "C" fn turn_choice_has_failed(choice: ExternPointer<TurnChoice>) -> u8 {
u8::from(choice.as_ref().has_failed())
extern "C" fn turn_choice_has_failed(choice: FFIHandle<Arc<TurnChoice>>) -> u8 {
u8::from(choice.from_ffi_handle().has_failed())
}
/// Fails the choice. This will prevent it from executing and run a specific fail handling during
/// execution. Note that this can not be undone.
#[no_mangle]
extern "C" fn turn_choice_fail(choice: ExternPointer<TurnChoice>) {
choice.as_ref().fail()
extern "C" fn turn_choice_fail(choice: FFIHandle<Arc<TurnChoice>>) {
choice.from_ffi_handle().fail()
}
/// Creates a new Turn Choice with a move to use.
#[no_mangle]
extern "C" fn turn_choice_move_new(
user: ExternPointer<Pokemon>,
learned_move: ExternPointer<Arc<LearnedMove>>,
user: FFIHandle<Pokemon>,
learned_move: FFIHandle<Arc<LearnedMove>>,
target_side: u8,
target_index: u8,
) -> IdentifiablePointer<TurnChoice> {
Box::new(TurnChoice::Move(MoveChoice::new(
user.as_ref().clone(),
learned_move.as_ref().clone(),
target_side,
target_index,
)))
.into()
) -> FFIHandle<Arc<TurnChoice>> {
FFIHandle::get_handle(
Arc::new(TurnChoice::Move(MoveChoice::new(
user.from_ffi_handle(),
learned_move.from_ffi_handle(),
target_side,
target_index,
)))
.into(),
)
}
/// The actual learned move on the Pokemon we use for this choice.
#[no_mangle]
extern "C" fn turn_choice_move_learned_move(
choice: ExternPointer<TurnChoice>,
) -> NativeResult<IdentifiablePointer<Arc<LearnedMove>>> {
if let TurnChoice::Move(c) = choice.as_ref() {
return NativeResult::ok(c.used_move().clone().into());
choice: FFIHandle<Arc<TurnChoice>>,
) -> FFIResult<FFIHandle<Arc<LearnedMove>>> {
if let TurnChoice::Move(c) = choice.from_ffi_handle().deref() {
return FFIResult::ok(FFIHandle::get_handle(c.used_move().clone().into()));
}
NativeResult::err(anyhow!("Turn choice was not a learned move"))
FFIResult::err(anyhow!("Turn choice was not a learned move"))
}
/// The target side the move is aimed at.
#[no_mangle]
extern "C" fn turn_choice_move_target_side(choice: ExternPointer<TurnChoice>) -> NativeResult<u8> {
if let TurnChoice::Move(c) = choice.as_ref() {
return NativeResult::ok(c.target_side());
extern "C" fn turn_choice_move_target_side(choice: FFIHandle<Arc<TurnChoice>>) -> FFIResult<u8> {
if let TurnChoice::Move(c) = choice.from_ffi_handle().deref() {
return FFIResult::ok(c.target_side());
}
NativeResult::err(anyhow!("Turn choice was not a learned move"))
FFIResult::err(anyhow!("Turn choice was not a learned move"))
}
/// The Pokemon index on the side we're aiming at.
#[no_mangle]
extern "C" fn turn_choice_move_target_index(choice: ExternPointer<TurnChoice>) -> NativeResult<u8> {
if let TurnChoice::Move(c) = choice.as_ref() {
return NativeResult::ok(c.target_index());
extern "C" fn turn_choice_move_target_index(choice: FFIHandle<Arc<TurnChoice>>) -> FFIResult<u8> {
if let TurnChoice::Move(c) = choice.from_ffi_handle().deref() {
return FFIResult::ok(c.target_index());
}
NativeResult::err(anyhow!("Turn choice was not a learned move"))
FFIResult::err(anyhow!("Turn choice was not a learned move"))
}
/// The priority of the move choice at the beginning of the turn.
#[no_mangle]
extern "C" fn turn_choice_move_priority(choice: ExternPointer<TurnChoice>) -> NativeResult<i8> {
if let TurnChoice::Move(c) = choice.as_ref() {
return NativeResult::ok(c.priority());
extern "C" fn turn_choice_move_priority(choice: FFIHandle<Arc<TurnChoice>>) -> FFIResult<i8> {
if let TurnChoice::Move(c) = choice.from_ffi_handle().deref() {
return FFIResult::ok(c.priority());
}
NativeResult::err(anyhow!("Turn choice was not a learned move"))
FFIResult::err(anyhow!("Turn choice was not a learned move"))
}
/// Creates a new Turn Choice for the user to flee.
#[no_mangle]
extern "C" fn turn_choice_flee_new(user: ExternPointer<Pokemon>) -> IdentifiablePointer<TurnChoice> {
Box::new(TurnChoice::Flee(FleeChoice::new(user.as_ref().clone()))).into()
extern "C" fn turn_choice_flee_new(user: FFIHandle<Pokemon>) -> FFIHandle<Arc<TurnChoice>> {
FFIHandle::get_handle(Arc::new(TurnChoice::Flee(FleeChoice::new(user.from_ffi_handle()))).into())
}
/// Creates a new Turn Choice for the user to pass the turn.
#[no_mangle]
extern "C" fn turn_choice_pass_new(user: ExternPointer<Pokemon>) -> IdentifiablePointer<TurnChoice> {
Box::new(TurnChoice::Pass(PassChoice::new(user.as_ref().clone()))).into()
extern "C" fn turn_choice_pass_new(user: FFIHandle<Pokemon>) -> FFIHandle<Arc<TurnChoice>> {
FFIHandle::get_handle(Arc::new(TurnChoice::Pass(PassChoice::new(user.from_ffi_handle()))).into())
}

View File

@ -1,63 +1,61 @@
use crate::dynamic_data::{BattleStatCalculator, Gen7BattleStatCalculator, Pokemon};
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::{ExternPointer, FFIResult};
use crate::static_data::{Statistic, StatisticSet};
use std::ptr::drop_in_place;
use std::ops::Deref;
use std::sync::Arc;
/// Creates a new Gen 7 battle stat calculator
#[no_mangle]
extern "C" fn gen_7_battle_stat_calculator_new() -> IdentifiablePointer<Box<dyn BattleStatCalculator>> {
let v: Box<dyn BattleStatCalculator> = Box::new(Gen7BattleStatCalculator::new());
let id = v.value_identifier();
let ptr = Box::into_raw(Box::new(v));
IdentifiablePointer::new(ptr, id)
}
/// Drops a battle stat calculator.
#[no_mangle]
extern "C" fn battle_stat_calculator_drop(ptr: OwnedPtr<Box<dyn BattleStatCalculator>>) {
unsafe { drop_in_place(ptr) };
extern "C" fn gen_7_battle_stat_calculator_new() -> FFIHandle<Arc<dyn BattleStatCalculator>> {
let v: Arc<dyn BattleStatCalculator> = Arc::new(Gen7BattleStatCalculator::new());
FFIHandle::get_handle(v.into())
}
/// Calculate all the flat stats of a Pokemon, disregarding stat boosts.
#[no_mangle]
extern "C" fn battle_stat_calculator_calculate_flat_stats(
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
ptr: FFIHandle<Arc<dyn BattleStatCalculator>>,
pokemon: ExternPointer<Pokemon>,
mut stats: ExternPointer<Box<StatisticSet<u32>>>,
) -> NativeResult<()> {
ptr.as_ref()
.calculate_flat_stats(pokemon.as_ref(), stats.as_mut())
stats: FFIHandle<Arc<StatisticSet<u32>>>,
) -> FFIResult<()> {
ptr.from_ffi_handle()
.calculate_flat_stats(pokemon.as_ref(), stats.from_ffi_handle().deref())
.into()
}
/// Calculate a single flat stat of a Pokemon, disregarding stat boost
#[no_mangle]
extern "C" fn battle_stat_calculator_calculate_flat_stat(
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
pokemon: ExternPointer<Pokemon>,
ptr: FFIHandle<Arc<dyn BattleStatCalculator>>,
pokemon: FFIHandle<Pokemon>,
stat: Statistic,
) -> NativeResult<u32> {
ptr.as_ref().calculate_flat_stat(pokemon.as_ref(), stat).into()
) -> FFIResult<u32> {
ptr.from_ffi_handle()
.calculate_flat_stat(&pokemon.from_ffi_handle(), stat)
.into()
}
/// Calculate all the boosted stats of a Pokemon, including stat boosts.
#[no_mangle]
extern "C" fn battle_stat_calculator_calculate_boosted_stats(
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
pokemon: ExternPointer<Pokemon>,
mut stats: ExternPointer<Box<StatisticSet<u32>>>,
) -> NativeResult<()> {
ptr.as_ref()
.calculate_boosted_stats(pokemon.as_ref(), stats.as_mut())
ptr: FFIHandle<Arc<dyn BattleStatCalculator>>,
pokemon: FFIHandle<Pokemon>,
stats: FFIHandle<Arc<StatisticSet<u32>>>,
) -> FFIResult<()> {
ptr.from_ffi_handle()
.calculate_boosted_stats(&pokemon.from_ffi_handle(), stats.from_ffi_handle().deref())
.into()
}
/// Calculate a single boosted stat of a Pokemon, including stat boosts.
#[no_mangle]
extern "C" fn battle_stat_calculator_calculate_boosted_stat(
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
pokemon: ExternPointer<Pokemon>,
ptr: FFIHandle<Arc<dyn BattleStatCalculator>>,
pokemon: FFIHandle<Pokemon>,
stat: Statistic,
) -> NativeResult<u32> {
ptr.as_ref().calculate_boosted_stat(pokemon.as_ref(), stat).into()
) -> FFIResult<u32> {
ptr.from_ffi_handle()
.calculate_boosted_stat(&pokemon.from_ffi_handle(), stat)
.into()
}

View File

@ -1,19 +1,11 @@
use crate::dynamic_data::{DamageLibrary, Gen7DamageLibrary};
use crate::ffi::{IdentifiablePointer, OwnedPtr};
use std::ptr::drop_in_place;
use crate::ffi::ffi_handle::FFIHandle;
use std::sync::Arc;
/// Creates a new generation 7 damage library. `has_randomness` defines whether a random damage
/// modifier (0.85x - 1.00x) is applied to the calculated damage.
#[no_mangle]
extern "C" fn gen_7_damage_library_new(has_randomness: u8) -> IdentifiablePointer<Box<dyn DamageLibrary>> {
let v: Box<dyn DamageLibrary> = Box::new(Gen7DamageLibrary::new(has_randomness == 1));
let id = v.value_identifier();
let ptr = Box::into_raw(Box::new(v));
IdentifiablePointer::new(ptr, id)
}
/// Drops a DamageLibrary.
#[no_mangle]
extern "C" fn damage_library_drop(ptr: OwnedPtr<Box<dyn DamageLibrary>>) {
unsafe { drop_in_place(ptr) };
extern "C" fn gen_7_damage_library_new(has_randomness: u8) -> FFIHandle<Arc<dyn DamageLibrary>> {
let v: Arc<dyn DamageLibrary> = Arc::new(Gen7DamageLibrary::new(has_randomness == 1));
FFIHandle::get_handle(v.into())
}

View File

@ -1,68 +1,59 @@
use crate::dynamic_data::{
BattleStatCalculator, DamageLibrary, DynamicLibrary, DynamicLibraryImpl, MiscLibrary, ScriptResolver,
};
use crate::ffi::{ExternPointer, IdentifiablePointer, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::static_data::StaticData;
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Instantiates a new DynamicLibrary with given parameters.
#[no_mangle]
extern "C" fn dynamic_library_new(
static_data: OwnedPtr<Arc<dyn StaticData>>,
stat_calculator: OwnedPtr<Arc<dyn BattleStatCalculator>>,
damage_library: OwnedPtr<Arc<dyn DamageLibrary>>,
misc_library: OwnedPtr<Arc<dyn MiscLibrary>>,
script_resolver: OwnedPtr<Box<dyn ScriptResolver>>,
) -> IdentifiablePointer<Arc<dyn DynamicLibrary>> {
unsafe {
let a: Arc<dyn DynamicLibrary> = Arc::new(DynamicLibraryImpl::new(
static_data.read(),
stat_calculator.read(),
damage_library.read(),
misc_library.read(),
*Box::from_raw(script_resolver),
));
a.into()
}
}
/// Drops a dynamic library.
#[no_mangle]
extern "C" fn dynamic_library_drop(ptr: OwnedPtr<Arc<dyn DynamicLibrary>>) {
unsafe { drop_in_place(ptr) };
static_data: FFIHandle<Arc<dyn StaticData>>,
stat_calculator: FFIHandle<Arc<dyn BattleStatCalculator>>,
damage_library: FFIHandle<Arc<dyn DamageLibrary>>,
misc_library: FFIHandle<Arc<dyn MiscLibrary>>,
script_resolver: FFIHandle<Arc<dyn ScriptResolver>>,
) -> FFIHandle<Arc<dyn DynamicLibrary>> {
let a: Arc<dyn DynamicLibrary> = Arc::new(DynamicLibraryImpl::new(
static_data.from_ffi_handle(),
stat_calculator.from_ffi_handle(),
damage_library.from_ffi_handle(),
misc_library.from_ffi_handle(),
script_resolver.from_ffi_handle(),
));
FFIHandle::get_handle(a.into())
}
/// The static data is the immutable storage data for this library.
#[no_mangle]
extern "C" fn dynamic_library_get_static_data(
ptr: ExternPointer<Arc<dyn DynamicLibrary>>,
) -> IdentifiablePointer<Arc<dyn StaticData>> {
ptr.as_ref().static_data().clone().into()
ptr: FFIHandle<Arc<dyn DynamicLibrary>>,
) -> FFIHandle<Arc<dyn StaticData>> {
FFIHandle::get_handle(ptr.from_ffi_handle().static_data().clone().into())
}
/// The stat calculator deals with the calculation of flat and boosted stats, based on the
/// Pokemons attributes.
#[no_mangle]
extern "C" fn dynamic_library_get_stat_calculator(
ptr: ExternPointer<Arc<dyn DynamicLibrary>>,
) -> IdentifiablePointer<Arc<dyn BattleStatCalculator>> {
ptr.as_ref().stat_calculator().clone().into()
ptr: FFIHandle<Arc<dyn DynamicLibrary>>,
) -> FFIHandle<Arc<dyn BattleStatCalculator>> {
FFIHandle::get_handle(ptr.from_ffi_handle().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<Arc<dyn DynamicLibrary>>,
) -> IdentifiablePointer<Arc<dyn DamageLibrary>> {
ptr.as_ref().damage_calculator().clone().into()
ptr: FFIHandle<Arc<dyn DynamicLibrary>>,
) -> FFIHandle<Arc<dyn DamageLibrary>> {
FFIHandle::get_handle(ptr.from_ffi_handle().damage_calculator().clone().into())
}
/// The Misc Library holds minor functions that do not fall in any of the other libraries and
/// calculators.
#[no_mangle]
extern "C" fn dynamic_library_get_misc_library(
ptr: ExternPointer<Arc<dyn DynamicLibrary>>,
) -> IdentifiablePointer<Arc<dyn MiscLibrary>> {
ptr.as_ref().misc_library().clone().into()
ptr: FFIHandle<Arc<dyn DynamicLibrary>>,
) -> FFIHandle<Arc<dyn MiscLibrary>> {
FFIHandle::get_handle(ptr.from_ffi_handle().misc_library().clone().into())
}

View File

@ -1,18 +1,10 @@
use crate::dynamic_data::{Gen7MiscLibrary, MiscLibrary};
use crate::ffi::{IdentifiablePointer, OwnedPtr};
use std::ptr::drop_in_place;
use crate::ffi::ffi_handle::FFIHandle;
use std::sync::Arc;
/// Instantiates a new MiscLibrary.
#[no_mangle]
extern "C" fn gen_7_misc_library_new() -> IdentifiablePointer<Box<dyn MiscLibrary>> {
let v: Box<dyn MiscLibrary> = Box::new(Gen7MiscLibrary::new());
let id = v.value_identifier();
let ptr = Box::into_raw(Box::new(v));
IdentifiablePointer::new(ptr, id)
}
/// Drops a MiscLibrary.
#[no_mangle]
extern "C" fn misc_library_drop(ptr: OwnedPtr<Box<dyn MiscLibrary>>) {
unsafe { drop_in_place(ptr) };
extern "C" fn gen_7_misc_library_new() -> FFIHandle<Arc<dyn MiscLibrary>> {
let v: Arc<dyn MiscLibrary> = Arc::new(Gen7MiscLibrary::new());
FFIHandle::get_handle(v.into())
}

View File

@ -1,49 +1,45 @@
use crate::dynamic_data::{EmptyScriptResolver, ScriptResolver};
use crate::ffi::{IdentifiablePointer, OwnedPtr};
use std::ptr::drop_in_place;
use crate::ffi::ffi_handle::FFIHandle;
use std::sync::Arc;
/// Instantiates a basic empty script resolver, that always returns None.
#[no_mangle]
extern "C" fn empty_script_resolver_new() -> IdentifiablePointer<Box<dyn ScriptResolver>> {
let v: Box<dyn ScriptResolver> = Box::new(EmptyScriptResolver {
identifier: Default::default(),
});
let id = v.value_identifier();
let ptr = Box::into_raw(Box::new(v));
IdentifiablePointer::new(ptr, id)
}
/// Drops a script resolver.
#[no_mangle]
extern "C" fn script_resolver_drop(ptr: OwnedPtr<Box<dyn ScriptResolver>>) {
unsafe { drop_in_place(ptr) };
extern "C" fn empty_script_resolver_new() -> FFIHandle<Arc<dyn ScriptResolver>> {
let v: Arc<dyn ScriptResolver> = Arc::new(EmptyScriptResolver {});
FFIHandle::get_handle(v.into())
}
/// Foreign function interfaces for the Webassembly script resolver.
#[cfg(feature = "wasm")]
mod web_assembly_script_resolver {
use crate::dynamic_data::ScriptResolver;
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::FFIResult;
use crate::script_implementations::wasm::script_resolver::WebAssemblyScriptResolver;
use std::sync::Arc;
/// Instantiates a new WebAssemblyScriptResolver.
#[no_mangle]
extern "C" fn webassembly_script_resolver_new() -> IdentifiablePointer<Box<dyn ScriptResolver>> {
let v: Box<dyn ScriptResolver> = WebAssemblyScriptResolver::new();
let id = v.value_identifier();
let ptr = Box::into_raw(Box::new(v));
IdentifiablePointer::new(ptr, id)
extern "C" fn webassembly_script_resolver_new() -> FFIHandle<Arc<dyn ScriptResolver>> {
let v: Arc<dyn ScriptResolver> = WebAssemblyScriptResolver::new();
FFIHandle::get_handle(v.into())
}
/// Load a compiled WASM module.
#[no_mangle]
extern "C" fn webassembly_script_resolver_load_wasm_from_bytes(
mut ptr: ExternPointer<Box<WebAssemblyScriptResolver>>,
ptr: FFIHandle<Arc<dyn ScriptResolver>>,
arr: *const u8,
len: usize,
) -> NativeResult<()> {
) -> FFIResult<()> {
#[allow(clippy::unwrap_used)] // Unwrap used, but we know it cannot fail.
unsafe {
ptr.as_mut()
let script_resolver = ptr.from_ffi_handle();
let wasm_script_resolver = script_resolver
.as_any()
.downcast_ref::<WebAssemblyScriptResolver>()
.unwrap();
wasm_script_resolver
.load_wasm_from_bytes(std::slice::from_raw_parts(arr, len))
.into()
}
@ -51,9 +47,13 @@ mod web_assembly_script_resolver {
/// Tells the script resolver we're done loading wasm modules, and to finalize the resolver.
#[no_mangle]
extern "C" fn webassembly_script_resolver_finalize(
mut ptr: ExternPointer<Box<WebAssemblyScriptResolver>>,
) -> NativeResult<()> {
ptr.as_mut().finalize().into()
#[allow(clippy::unwrap_used)] // Unwrap used, but we know it cannot fail.
extern "C" fn webassembly_script_resolver_finalize(ptr: FFIHandle<Arc<dyn ScriptResolver>>) -> FFIResult<()> {
let script_resolver = ptr.from_ffi_handle();
let wasm_script_resolver = script_resolver
.as_any()
.downcast_ref::<WebAssemblyScriptResolver>()
.unwrap();
wasm_script_resolver.finalize().into()
}
}

View File

@ -2,16 +2,18 @@ use crate::dynamic_data::{
Battle, BattleParty, BattleRandom, BattleResult, BattleSide, DynamicLibrary, Pokemon, TurnChoice,
};
use crate::ffi::dynamic_data::models::native_event_hook::NativeEventHook;
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::FFIResult;
use anyhow::anyhow;
use std::ffi::{c_char, CStr, CString};
use std::ops::Deref;
use std::sync::Arc;
/// Initializes a new battle.
#[no_mangle]
extern "C" fn battle_new(
library: ExternPointer<Arc<dyn DynamicLibrary>>,
parties: *const OwnedPtr<Arc<BattleParty>>,
library: FFIHandle<Arc<dyn DynamicLibrary>>,
parties: *const FFIHandle<Arc<BattleParty>>,
parties_length: usize,
can_flee: u8,
number_of_sides: u8,
@ -19,11 +21,11 @@ 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<Battle> {
) -> FFIHandle<Battle> {
let parties = unsafe {
std::slice::from_raw_parts(parties, parties_length)
.iter()
.map(|x| *Box::from_raw(*x))
.map(|x| x.from_ffi_handle())
.collect()
};
@ -34,99 +36,98 @@ extern "C" fn battle_new(
};
let random_seed = if random_seed == 0 { None } else { Some(random_seed) };
Battle::new(
library.as_ref().clone(),
parties,
can_flee == 1,
number_of_sides,
pokemon_per_side,
random_seed,
FFIHandle::get_handle(
Battle::new(
library.from_ffi_handle(),
parties,
can_flee == 1,
number_of_sides,
pokemon_per_side,
random_seed,
)
.into(),
)
.into()
}
/// The library the battle uses for handling.
#[no_mangle]
extern "C" fn battle_library(ptr: ExternPointer<Arc<Battle>>) -> IdentifiablePointer<Arc<dyn DynamicLibrary>> {
ptr.as_ref().library().clone().into()
extern "C" fn battle_library(ptr: FFIHandle<Battle>) -> FFIHandle<Arc<dyn DynamicLibrary>> {
FFIHandle::get_handle(ptr.from_ffi_handle().library().clone().into())
}
/// The length of the list of all different parties in the battle.
#[no_mangle]
extern "C" fn battle_parties_length(ptr: ExternPointer<Arc<Battle>>) -> usize {
ptr.as_ref().parties().len()
extern "C" fn battle_parties_length(ptr: FFIHandle<Battle>) -> usize {
ptr.from_ffi_handle().parties().len()
}
/// Get a party in the battle.
#[no_mangle]
extern "C" fn battle_parties_get(
ptr: ExternPointer<Arc<Battle>>,
index: usize,
) -> IdentifiablePointer<Arc<BattleParty>> {
if let Some(v) = ptr.as_ref().parties().get(index) {
v.clone().into()
extern "C" fn battle_parties_get(ptr: FFIHandle<Battle>, index: usize) -> FFIHandle<Arc<BattleParty>> {
if let Some(v) = ptr.from_ffi_handle().parties().get(index) {
FFIHandle::get_handle(v.clone().into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
/// Whether or not Pokemon can flee from the battle.
#[no_mangle]
extern "C" fn battle_can_flee(ptr: ExternPointer<Arc<Battle>>) -> u8 {
u8::from(ptr.as_ref().can_flee())
extern "C" fn battle_can_flee(ptr: FFIHandle<Battle>) -> u8 {
u8::from(ptr.from_ffi_handle().can_flee())
}
/// The number of sides in the battle. Typically 2.
#[no_mangle]
extern "C" fn battle_number_of_sides(ptr: ExternPointer<Arc<Battle>>) -> u8 {
ptr.as_ref().number_of_sides()
extern "C" fn battle_number_of_sides(ptr: FFIHandle<Battle>) -> u8 {
ptr.from_ffi_handle().number_of_sides()
}
/// The number of Pokemon that can be on each side.
#[no_mangle]
extern "C" fn battle_pokemon_per_side(ptr: ExternPointer<Arc<Battle>>) -> u8 {
ptr.as_ref().pokemon_per_side()
extern "C" fn battle_pokemon_per_side(ptr: FFIHandle<Battle>) -> u8 {
ptr.from_ffi_handle().pokemon_per_side()
}
/// The length of the list of all different sides in the battle.
#[no_mangle]
extern "C" fn battle_sides_length(ptr: ExternPointer<Arc<Battle>>) -> usize {
ptr.as_ref().sides().len()
extern "C" fn battle_sides_length(ptr: FFIHandle<Battle>) -> usize {
ptr.from_ffi_handle().sides().len()
}
/// Get a side in the battle.
#[no_mangle]
extern "C" fn battle_sides_get(ptr: ExternPointer<Arc<Battle>>, index: usize) -> IdentifiablePointer<BattleSide> {
if let Some(v) = ptr.as_ref().sides().get(index) {
(v as *const BattleSide).into()
extern "C" fn battle_sides_get(ptr: FFIHandle<Battle>, index: usize) -> FFIHandle<BattleSide> {
if let Some(v) = ptr.from_ffi_handle().sides().get(index) {
FFIHandle::get_handle(v.clone().into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
/// The RNG used for the battle.
#[no_mangle]
extern "C" fn battle_random(ptr: ExternPointer<Arc<Battle>>) -> IdentifiablePointer<Arc<BattleRandom>> {
ptr.as_ref().random().clone().into()
extern "C" fn battle_random(ptr: FFIHandle<Battle>) -> FFIHandle<Arc<BattleRandom>> {
FFIHandle::get_handle(ptr.from_ffi_handle().random().clone().into())
}
/// Whether or not the battle has ended.
#[no_mangle]
extern "C" fn battle_has_ended(ptr: ExternPointer<Arc<Battle>>) -> u8 {
u8::from(ptr.as_ref().has_ended())
extern "C" fn battle_has_ended(ptr: FFIHandle<Battle>) -> u8 {
u8::from(ptr.from_ffi_handle().has_ended())
}
/// Whether or not we have a conclusive winner
#[no_mangle]
extern "C" fn battle_has_conclusive_result(ptr: ExternPointer<Arc<Battle>>) -> u8 {
u8::from(ptr.as_ref().result() != BattleResult::Inconclusive)
extern "C" fn battle_has_conclusive_result(ptr: FFIHandle<Battle>) -> u8 {
u8::from(ptr.from_ffi_handle().result() != BattleResult::Inconclusive)
}
/// If we have a conclusive winner, the side that has won. If we don't have a conclusive winner, this
/// always returns 0.
#[no_mangle]
extern "C" fn battle_winning_side(ptr: ExternPointer<Arc<Battle>>) -> u8 {
if let BattleResult::Conclusive(winner) = ptr.as_ref().result() {
extern "C" fn battle_winning_side(ptr: FFIHandle<Battle>) -> u8 {
if let BattleResult::Conclusive(winner) = ptr.from_ffi_handle().result() {
winner
} else {
0
@ -135,29 +136,29 @@ extern "C" fn battle_winning_side(ptr: ExternPointer<Arc<Battle>>) -> u8 {
/// Register a function to be triggered when an event in a battle occurs.
#[no_mangle]
extern "C" fn battle_register_event_hook(ptr: ExternPointer<Arc<Battle>>, f: NativeEventHook) {
ptr.as_ref().event_hook().register_listener(Box::new(f))
extern "C" fn battle_register_event_hook(ptr: FFIHandle<Battle>, f: NativeEventHook) {
ptr.from_ffi_handle().event_hook().register_listener(Box::new(f))
}
/// The index of the current turn. 0 until all choices
#[no_mangle]
extern "C" fn battle_current_turn(ptr: ExternPointer<Arc<Battle>>) -> u32 {
ptr.as_ref().current_turn()
extern "C" fn battle_current_turn(ptr: FFIHandle<Battle>) -> u32 {
ptr.from_ffi_handle().current_turn()
}
/// The time in nanoseconds the last turn took to run. Defaults to 0.
#[no_mangle]
extern "C" fn battle_last_turn_time(ptr: ExternPointer<Arc<Battle>>) -> u64 {
ptr.as_ref().last_turn_time()
extern "C" fn battle_last_turn_time(ptr: FFIHandle<Battle>) -> u64 {
ptr.from_ffi_handle().last_turn_time()
}
/// 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<Arc<Battle>>, side: u8, index: u8) -> IdentifiablePointer<Pokemon> {
if let Some(v) = ptr.as_ref().get_pokemon(side, index) {
v.into()
extern "C" fn battle_get_pokemon(ptr: FFIHandle<Battle>, side: u8, index: u8) -> FFIHandle<Pokemon> {
if let Some(v) = ptr.from_ffi_handle().get_pokemon(side, index) {
FFIHandle::get_handle(v.into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
@ -165,47 +166,51 @@ extern "C" fn battle_get_pokemon(ptr: ExternPointer<Arc<Battle>>, side: u8, inde
/// for that slot, or a party is responsible, but has no remaining Pokemon to throw out anymore,
/// this returns false.
#[no_mangle]
extern "C" fn battle_can_slot_be_filled(ptr: ExternPointer<Arc<Battle>>, side: u8, index: u8) -> u8 {
u8::from(ptr.as_ref().can_slot_be_filled(side, index))
extern "C" fn battle_can_slot_be_filled(ptr: FFIHandle<Battle>, side: u8, index: u8) -> u8 {
u8::from(ptr.from_ffi_handle().can_slot_be_filled(side, index))
}
/// Checks whether a choice is actually possible.
#[no_mangle]
extern "C" fn battle_can_use(ptr: ExternPointer<Arc<Battle>>, choice: ExternPointer<TurnChoice>) -> u8 {
u8::from(ptr.as_ref().can_use(choice.as_ref()))
extern "C" fn battle_can_use(ptr: FFIHandle<Battle>, choice: FFIHandle<Arc<TurnChoice>>) -> u8 {
u8::from(ptr.from_ffi_handle().can_use(choice.from_ffi_handle().deref()))
}
/// Checks to see whether all Pokemon on the field have set their choices. If so, we then run
/// the turn.
#[no_mangle]
extern "C" fn battle_try_set_choice(ptr: ExternPointer<Arc<Battle>>, choice: OwnedPtr<TurnChoice>) -> NativeResult<u8> {
let choice = unsafe { choice.read() };
let result = ptr.as_ref().try_set_choice(choice);
extern "C" fn battle_try_set_choice(ptr: FFIHandle<Battle>, choice: FFIHandle<Arc<TurnChoice>>) -> FFIResult<u8> {
let choice = choice.from_ffi_handle();
let result = ptr.from_ffi_handle().try_set_choice(choice);
match result {
Ok(b) => NativeResult::ok(u8::from(b)),
Err(e) => NativeResult::err(e),
Ok(b) => FFIResult::ok(u8::from(b)),
Err(e) => FFIResult::err(e),
}
}
/// Sets the current weather for the battle. If nullptr is passed, this clears the weather.
#[no_mangle]
extern "C" fn battle_set_weather(ptr: ExternPointer<Arc<Battle>>, weather: *const c_char) -> NativeResult<()> {
extern "C" fn battle_set_weather(ptr: FFIHandle<Battle>, weather: *const c_char) -> FFIResult<()> {
if weather.is_null() {
ptr.as_ref().set_weather(None).into()
ptr.from_ffi_handle().set_weather(None).into()
} else {
unsafe { ptr.as_ref().set_weather(Some(CStr::from_ptr(weather).into())).into() }
unsafe {
ptr.from_ffi_handle()
.set_weather(Some(CStr::from_ptr(weather).into()))
.into()
}
}
}
/// Gets the current weather of the battle. If no weather is present, this returns nullptr.
#[no_mangle]
extern "C" fn battle_weather_name(ptr: ExternPointer<Arc<Battle>>) -> NativeResult<*mut c_char> {
match ptr.as_ref().weather_name() {
extern "C" fn battle_weather_name(ptr: FFIHandle<Battle>) -> FFIResult<*mut c_char> {
match ptr.from_ffi_handle().weather_name() {
Ok(Some(w)) => match CString::new(w.str()) {
Ok(s) => s.into_raw().into(),
Err(e) => NativeResult::err(anyhow!("Failed to convert weather name to CString: {}", e)),
Err(e) => FFIResult::err(anyhow!("Failed to convert weather name to CString: {}", e)),
},
Ok(None) => std::ptr::null_mut::<c_char>().into(),
Err(e) => NativeResult::err(e),
Err(e) => FFIResult::err(e),
}
}

View File

@ -1,5 +1,6 @@
use crate::dynamic_data::{BattleParty, Pokemon, PokemonParty};
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::FFIResult;
use crate::VecExt;
use anyhow::{anyhow, Result};
use std::sync::Arc;
@ -8,12 +9,12 @@ use std::sync::Arc;
/// on the field attached. The indices are stored
#[no_mangle]
extern "C" fn battle_party_new(
party: ExternPointer<Arc<PokemonParty>>,
party: FFIHandle<Arc<PokemonParty>>,
responsible_indices_ptr: *const u8,
responsible_indices_length: usize,
) -> NativeResult<IdentifiablePointer<Arc<BattleParty>>> {
) -> FFIResult<FFIHandle<Arc<BattleParty>>> {
if responsible_indices_length % 2 != 0 {
return NativeResult::err(anyhow!("The length of responsible indices should be dividable by two"));
return FFIResult::err(anyhow!("The length of responsible indices should be dividable by two"));
}
let responsible_indices_slice =
@ -31,43 +32,40 @@ extern "C" fn battle_party_new(
for i in 0..responsible_indices_length / 2 {
match split(i) {
Ok(_) => (),
Err(e) => return NativeResult::err(e),
Err(e) => return FFIResult::err(e),
}
}
match BattleParty::new(party.as_ref().clone(), responsible_indices) {
Ok(v) => NativeResult::ok(Arc::new(v).into()),
Err(e) => NativeResult::err(e),
match BattleParty::new(party.from_ffi_handle(), responsible_indices) {
Ok(v) => FFIResult::ok(FFIHandle::get_handle(Arc::new(v).into())),
Err(e) => FFIResult::err(e),
}
}
/// Checks whether the party is responsible for the given index.
#[no_mangle]
extern "C" fn battle_party_is_responsible_for_index(ptr: ExternPointer<Arc<BattleParty>>, side: u8, index: u8) -> u8 {
u8::from(ptr.as_ref().is_responsible_for_index(side, index))
extern "C" fn battle_party_is_responsible_for_index(ptr: FFIHandle<Arc<BattleParty>>, side: u8, index: u8) -> u8 {
u8::from(ptr.from_ffi_handle().is_responsible_for_index(side, index))
}
/// Whether or not the party has non fainted Pokemon that could be thrown out into the field.
#[no_mangle]
extern "C" fn battle_party_has_pokemon_not_in_field(ptr: ExternPointer<Arc<BattleParty>>) -> u8 {
u8::from(ptr.as_ref().has_pokemon_not_in_field())
extern "C" fn battle_party_has_pokemon_not_in_field(ptr: FFIHandle<Arc<BattleParty>>) -> u8 {
u8::from(ptr.from_ffi_handle().has_pokemon_not_in_field())
}
/// Gets a Pokemon at an index.
#[no_mangle]
extern "C" fn battle_party_get_pokemon(
ptr: ExternPointer<Arc<BattleParty>>,
index: usize,
) -> IdentifiablePointer<Pokemon> {
if let Some(v) = ptr.as_ref().get_pokemon(index) {
v.into()
extern "C" fn battle_party_get_pokemon(ptr: FFIHandle<Arc<BattleParty>>, index: usize) -> FFIHandle<Pokemon> {
if let Some(v) = ptr.from_ffi_handle().get_pokemon(index) {
FFIHandle::get_handle(v.into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
/// Gets the underlying Pokemon Party
#[no_mangle]
extern "C" fn battle_party_party(ptr: ExternPointer<Arc<BattleParty>>) -> IdentifiablePointer<Arc<PokemonParty>> {
ptr.as_ref().party().clone().into()
extern "C" fn battle_party_party(ptr: FFIHandle<Arc<BattleParty>>) -> FFIHandle<Arc<PokemonParty>> {
FFIHandle::get_handle(ptr.from_ffi_handle().party().clone().into())
}

View File

@ -1,5 +1,6 @@
use crate::dynamic_data::{Event, Pokemon};
use crate::ffi::{ExternPointer, IdentifiablePointer};
use crate::ffi::ExternPointer;
use crate::ffi::FFIHandle;
/// The kind of the event.
#[no_mangle]
@ -66,13 +67,18 @@ 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>,
pokemon: FFIHandle<Pokemon>,
},
{
let pokemon_handle = match pokemon {
Some(pokemon) => FFIHandle::get_handle(pokemon.clone().into()),
None => FFIHandle::none(),
};
return SwitchData{
side_index: *side_index,
index: *index,
pokemon: pokemon.clone().into(),
pokemon: pokemon_handle,
}
}
);

View File

@ -1,66 +1,58 @@
use crate::dynamic_data::{LearnedMove, MoveLearnMethod};
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::FFIResult;
use crate::static_data::MoveData;
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Instantiate a new learned move.
#[no_mangle]
extern "C" fn learned_move_new(
move_data: ExternPointer<Arc<dyn MoveData>>,
move_data: FFIHandle<Arc<dyn MoveData>>,
learn_method: MoveLearnMethod,
) -> IdentifiablePointer<Arc<LearnedMove>> {
Arc::new(LearnedMove::new(move_data.as_ref().clone(), learn_method)).into()
}
/// Drops a learned move.
#[no_mangle]
extern "C" fn learned_move_drop(learned_move: OwnedPtr<Arc<LearnedMove>>) {
unsafe { drop_in_place(learned_move) }
) -> FFIHandle<Arc<LearnedMove>> {
FFIHandle::get_handle(Arc::new(LearnedMove::new(move_data.from_ffi_handle().clone(), learn_method)).into())
}
/// The immutable move information of the move.
#[no_mangle]
extern "C" fn learned_move_move_data(
learned_move: ExternPointer<Arc<LearnedMove>>,
) -> IdentifiablePointer<Arc<dyn MoveData>> {
learned_move.as_ref().move_data().clone().into()
extern "C" fn learned_move_move_data(learned_move: FFIHandle<Arc<LearnedMove>>) -> FFIHandle<Arc<dyn MoveData>> {
FFIHandle::get_handle(learned_move.from_ffi_handle().move_data().clone().into())
}
/// The maximal power points for this move.
#[no_mangle]
extern "C" fn learned_move_max_pp(learned_move: ExternPointer<Arc<LearnedMove>>) -> u8 {
learned_move.as_ref().max_pp()
extern "C" fn learned_move_max_pp(learned_move: FFIHandle<Arc<LearnedMove>>) -> u8 {
learned_move.from_ffi_handle().max_pp()
}
/// The amount of remaining power points. If this is 0, we can not use the move anymore.
#[no_mangle]
extern "C" fn learned_move_remaining_pp(learned_move: ExternPointer<Arc<LearnedMove>>) -> u8 {
learned_move.as_ref().remaining_pp()
extern "C" fn learned_move_remaining_pp(learned_move: FFIHandle<Arc<LearnedMove>>) -> u8 {
learned_move.from_ffi_handle().remaining_pp()
}
/// The way the move was learned.
#[no_mangle]
extern "C" fn learned_move_learn_method(learned_move: ExternPointer<Arc<LearnedMove>>) -> MoveLearnMethod {
learned_move.as_ref().learn_method()
extern "C" fn learned_move_learn_method(learned_move: FFIHandle<Arc<LearnedMove>>) -> MoveLearnMethod {
learned_move.from_ffi_handle().learn_method()
}
/// Try and reduce the PP by a certain amount. If the amount is higher than the current uses,
/// return 0. Otherwise, reduce the PP, and return 1.
#[no_mangle]
extern "C" fn learned_move_try_use(learned_move: ExternPointer<Arc<LearnedMove>>, amount: u8) -> u8 {
u8::from(learned_move.as_ref().try_use(amount))
extern "C" fn learned_move_try_use(learned_move: FFIHandle<Arc<LearnedMove>>, amount: u8) -> u8 {
u8::from(learned_move.from_ffi_handle().try_use(amount))
}
/// Set the remaining PP to the max amount of PP.
#[no_mangle]
extern "C" fn learned_move_restore_all_uses(learned_move: ExternPointer<Arc<LearnedMove>>) {
learned_move.as_ref().restore_all_uses();
extern "C" fn learned_move_restore_all_uses(learned_move: FFIHandle<Arc<LearnedMove>>) {
learned_move.from_ffi_handle().restore_all_uses();
}
/// Restore the remaining PP by a certain amount. Will prevent it from going above max PP.
#[no_mangle]
extern "C" fn learned_move_restore_uses(learned_move: ExternPointer<Arc<LearnedMove>>, amount: u8) -> NativeResult<()> {
learned_move.as_ref().restore_uses(amount);
NativeResult::ok(())
extern "C" fn learned_move_restore_uses(learned_move: FFIHandle<Arc<LearnedMove>>, amount: u8) -> FFIResult<()> {
learned_move.from_ffi_handle().restore_uses(amount);
FFIResult::ok(())
}

View File

@ -1,11 +1,10 @@
use crate::dynamic_data::Event;
use crate::ffi::BorrowedPtr;
/// Wrapper class for easier use of an external function pointer.
#[repr(C)]
pub(super) struct NativeEventHook {
/// The actual C function to be called.
f: extern "C" fn(BorrowedPtr<Event>),
f: extern "C" fn(*const Event<'_>),
}
impl NativeEventHook {

View File

@ -1,20 +1,21 @@
use crate::defines::LevelInt;
use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, LearnedMove, MoveLearnMethod, Pokemon};
use crate::ffi::{ffi_arc_getter, ffi_vec_value_getters, ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::FFIResult;
use crate::static_data::{
Ability, AbilityIndex, Form, Gender, Item, Nature, Species, Statistic, StatisticSet, TypeIdentifier,
};
use crate::VecExt;
use anyhow::anyhow;
use std::ffi::{c_char, CStr, CString};
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Instantiates a new Pokemon.
#[no_mangle]
extern "C" fn pokemon_new(
library: ExternPointer<Arc<dyn DynamicLibrary>>,
species: ExternPointer<Arc<dyn Species>>,
form: ExternPointer<Arc<dyn Form>>,
library: FFIHandle<Arc<dyn DynamicLibrary>>,
species: FFIHandle<Arc<dyn Species>>,
form: FFIHandle<Arc<dyn Form>>,
hidden_ability: u8,
ability_index: u8,
level: LevelInt,
@ -22,12 +23,12 @@ extern "C" fn pokemon_new(
gender: Gender,
coloring: u8,
nature: *const c_char,
) -> NativeResult<IdentifiablePointer<Pokemon>> {
) -> FFIResult<FFIHandle<Pokemon>> {
let nature = unsafe { CStr::from_ptr(nature) }.into();
let pokemon = Pokemon::new(
library.as_ref().clone(),
species.as_ref().clone(),
form.as_ref(),
library.from_ffi_handle(),
species.from_ffi_handle(),
&form.from_ffi_handle(),
AbilityIndex {
hidden: hidden_ability == 1,
index: ability_index,
@ -39,337 +40,373 @@ extern "C" fn pokemon_new(
&nature,
);
match pokemon {
Ok(pokemon) => NativeResult::ok(pokemon.into()),
Err(err) => NativeResult::err(err),
Ok(pokemon) => FFIResult::ok(FFIHandle::get_handle(pokemon.into())),
Err(err) => FFIResult::err(err),
}
}
/// Drops an Arc reference held by the FFI.
#[no_mangle]
unsafe extern "C" fn pokemon_drop(ptr: OwnedPtr<Pokemon>) {
drop_in_place(ptr);
}
/// The library data of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_library(ptr: ExternPointer<Pokemon>) -> IdentifiablePointer<Arc<dyn DynamicLibrary>> {
ptr.as_ref().library().clone().into()
extern "C" fn pokemon_library(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn DynamicLibrary>> {
FFIHandle::get_handle(handle.from_ffi_handle().library().clone().into())
}
/// The species of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_species(ptr: ExternPointer<Pokemon>) -> IdentifiablePointer<Arc<dyn Species>> {
ptr.as_ref().species().into()
extern "C" fn pokemon_species(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Species>> {
FFIHandle::get_handle(handle.from_ffi_handle().species().into())
}
/// The form of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_form(ptr: ExternPointer<Pokemon>) -> IdentifiablePointer<Arc<dyn Form>> {
ptr.as_ref().form().into()
extern "C" fn pokemon_form(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Form>> {
FFIHandle::get_handle(handle.from_ffi_handle().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<Pokemon>) -> IdentifiablePointer<Arc<dyn Species>> {
ptr.as_ref().display_species().into()
extern "C" fn pokemon_display_species(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Species>> {
FFIHandle::get_handle(handle.from_ffi_handle().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<Pokemon>) -> IdentifiablePointer<Arc<dyn Form>> {
ptr.as_ref().display_form().into()
extern "C" fn pokemon_display_form(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Form>> {
FFIHandle::get_handle(handle.from_ffi_handle().display_form().into())
}
ffi_arc_getter!(Pokemon, level, LevelInt);
ffi_arc_getter!(Pokemon, experience, u32);
ffi_arc_getter!(Pokemon, unique_identifier, u32);
ffi_arc_getter!(Pokemon, gender, Gender);
ffi_arc_getter!(Pokemon, coloring, u8);
#[no_mangle]
extern "C" fn pokemon_level(handle: FFIHandle<Pokemon>) -> LevelInt {
handle.from_ffi_handle().level()
}
#[no_mangle]
extern "C" fn pokemon_experience(handle: FFIHandle<Pokemon>) -> u32 {
handle.from_ffi_handle().experience()
}
#[no_mangle]
extern "C" fn pokemon_unique_identifier(handle: FFIHandle<Pokemon>) -> u32 {
handle.from_ffi_handle().unique_identifier()
}
#[no_mangle]
extern "C" fn pokemon_gender(handle: FFIHandle<Pokemon>) -> Gender {
handle.from_ffi_handle().gender()
}
#[no_mangle]
extern "C" fn pokemon_coloring(handle: FFIHandle<Pokemon>) -> u8 {
handle.from_ffi_handle().coloring()
}
/// Gets the held item of a Pokemon
#[no_mangle]
extern "C" fn pokemon_held_item(ptr: ExternPointer<Pokemon>) -> IdentifiablePointer<Arc<dyn Item>> {
if let Some(v) = ptr.as_ref().held_item().read().as_ref() {
v.clone().into()
extern "C" fn pokemon_held_item(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Item>> {
if let Some(v) = handle.from_ffi_handle().held_item().read().as_ref() {
FFIHandle::get_handle(v.clone().into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
/// Checks whether the Pokemon is holding a specific item.
#[no_mangle]
extern "C" fn pokemon_has_held_item(ptr: ExternPointer<Pokemon>, name: *const c_char) -> u8 {
extern "C" fn pokemon_has_held_item(handle: FFIHandle<Pokemon>, name: *const c_char) -> u8 {
let name = unsafe { CStr::from_ptr(name) }.into();
u8::from(ptr.as_ref().has_held_item(&name))
u8::from(handle.from_ffi_handle().has_held_item(&name))
}
/// Changes the held item of the Pokemon. Returns the previously held item.
#[no_mangle]
extern "C" fn pokemon_set_held_item(
ptr: ExternPointer<Pokemon>,
item: ExternPointer<Arc<dyn Item>>,
) -> IdentifiablePointer<Arc<dyn Item>> {
if let Some(v) = ptr.as_ref().set_held_item(item.as_ref()) {
v.into()
handle: FFIHandle<Pokemon>,
item: FFIHandle<Arc<dyn Item>>,
) -> FFIHandle<Arc<dyn Item>> {
if let Some(v) = handle.from_ffi_handle().set_held_item(&item.from_ffi_handle()) {
FFIHandle::get_handle(v.into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
/// Removes the held item from the Pokemon. Returns the previously held item.
#[no_mangle]
extern "C" fn pokemon_remove_held_item(ptr: ExternPointer<Pokemon>) -> IdentifiablePointer<Arc<dyn Item>> {
if let Some(v) = ptr.as_ref().remove_held_item() {
v.into()
extern "C" fn pokemon_remove_held_item(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Item>> {
if let Some(v) = handle.from_ffi_handle().remove_held_item() {
FFIHandle::get_handle(v.into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
/// Makes the Pokemon uses its held item.
#[no_mangle]
extern "C" fn pokemon_consume_held_item(ptr: ExternPointer<Pokemon>) -> NativeResult<u8> {
match ptr.as_ref().consume_held_item() {
Ok(v) => NativeResult::ok(u8::from(v)),
Err(err) => NativeResult::err(err),
extern "C" fn pokemon_consume_held_item(handle: FFIHandle<Pokemon>) -> FFIResult<u8> {
match handle.from_ffi_handle().consume_held_item() {
Ok(v) => FFIResult::ok(u8::from(v)),
Err(err) => FFIResult::err(err),
}
}
ffi_arc_getter!(Pokemon, current_health, u32);
ffi_arc_getter!(Pokemon, max_health, u32);
ffi_arc_getter!(Pokemon, weight, f32);
ffi_arc_getter!(Pokemon, height, f32);
#[no_mangle]
extern "C" fn pokemon_current_health(handle: FFIHandle<Pokemon>) -> u32 {
handle.from_ffi_handle().current_health()
}
#[no_mangle]
extern "C" fn pokemon_max_health(handle: FFIHandle<Pokemon>) -> u32 {
handle.from_ffi_handle().max_health()
}
#[no_mangle]
extern "C" fn pokemon_weight(handle: FFIHandle<Pokemon>) -> f32 {
handle.from_ffi_handle().weight()
}
#[no_mangle]
extern "C" fn pokemon_height(handle: FFIHandle<Pokemon>) -> f32 {
handle.from_ffi_handle().height()
}
/// An optional nickname of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_nickname(ptr: ExternPointer<Pokemon>) -> NativeResult<*mut c_char> {
let name = ptr.as_ref().nickname();
extern "C" fn pokemon_nickname(handle: FFIHandle<Pokemon>) -> FFIResult<*mut c_char> {
let form = handle.from_ffi_handle();
let name = form.nickname();
if let Some(v) = name {
match CString::new(v.as_str()) {
Ok(v) => NativeResult::ok(v.into_raw()),
Err(err) => NativeResult::err(anyhow!("Could not convert nickname to CString: {}", err)),
Ok(v) => FFIResult::ok(v.into_raw()),
Err(err) => FFIResult::err(anyhow!("Could not convert nickname to CString: {}", err)),
}
} else {
NativeResult::ok(std::ptr::null_mut())
FFIResult::ok(std::ptr::null_mut())
}
}
/// Whether the actual ability on the form is a hidden ability.
#[no_mangle]
extern "C" fn pokemon_real_ability_is_hidden(ptr: ExternPointer<Pokemon>) -> u8 {
u8::from(ptr.as_ref().real_ability().hidden)
extern "C" fn pokemon_real_ability_is_hidden(handle: FFIHandle<Pokemon>) -> u8 {
u8::from(handle.from_ffi_handle().real_ability().hidden)
}
/// The index of the actual ability on the form.
#[no_mangle]
extern "C" fn pokemon_real_ability_index(ptr: ExternPointer<Pokemon>) -> u8 {
ptr.as_ref().real_ability().index
extern "C" fn pokemon_real_ability_index(handle: FFIHandle<Pokemon>) -> u8 {
handle.from_ffi_handle().real_ability().index
}
ffi_vec_value_getters!(Pokemon, Pokemon, types, TypeIdentifier);
#[no_mangle]
extern "C" fn pokemon_types_length(ptr: FFIHandle<Pokemon>) -> usize {
ptr.from_ffi_handle().types().len()
}
#[no_mangle]
extern "C" fn pokemon_types_get(ptr: FFIHandle<Pokemon>, index: usize) -> FFIResult<TypeIdentifier> {
match ptr.from_ffi_handle().types().get_res(index) {
Ok(v) => FFIResult::ok(*v),
Err(err) => FFIResult::err(err),
}
}
/// Gets a learned move of the Pokemon. Index should generally be below [`MAX_MOVES`], you will get
/// a null pointer otherwise.
#[no_mangle]
extern "C" fn pokemon_learned_move_get(
ptr: ExternPointer<Pokemon>,
index: usize,
) -> IdentifiablePointer<Arc<LearnedMove>> {
if let Some(Some(v)) = ptr.as_ref().learned_moves().read().get(index) {
v.clone().into()
extern "C" fn pokemon_learned_move_get(handle: FFIHandle<Pokemon>, index: usize) -> FFIHandle<Arc<LearnedMove>> {
if let Some(Some(v)) = handle.from_ffi_handle().learned_moves().read().get(index) {
FFIHandle::get_handle(v.clone().into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
/// The stats of the Pokemon when disregarding any stat boosts.
#[no_mangle]
extern "C" fn pokemon_flat_stats(ptr: ExternPointer<Pokemon>) -> IdentifiablePointer<Arc<StatisticSet<u32>>> {
ptr.as_ref().flat_stats().clone().into()
extern "C" fn pokemon_flat_stats(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<StatisticSet<u32>>> {
FFIHandle::get_handle(handle.from_ffi_handle().flat_stats().clone().into())
}
/// The stats of the Pokemon including the stat boosts.
#[no_mangle]
extern "C" fn pokemon_boosted_stats(ptr: ExternPointer<Pokemon>) -> IdentifiablePointer<Arc<StatisticSet<u32>>> {
ptr.as_ref().boosted_stats().clone().into()
extern "C" fn pokemon_boosted_stats(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<StatisticSet<u32>>> {
FFIHandle::get_handle(handle.from_ffi_handle().boosted_stats().clone().into())
}
/// Get the stat boosts for a specific stat.
#[no_mangle]
extern "C" fn pokemon_get_stat_boost(ptr: ExternPointer<Pokemon>, statistic: Statistic) -> i8 {
ptr.as_ref().stat_boost(statistic)
extern "C" fn pokemon_get_stat_boost(handle: FFIHandle<Pokemon>, statistic: Statistic) -> i8 {
handle.from_ffi_handle().stat_boost(statistic)
}
/// Change a boosted stat by a certain amount.
#[no_mangle]
extern "C" fn pokemon_change_stat_boost(
ptr: ExternPointer<Pokemon>,
handle: FFIHandle<Pokemon>,
stat: Statistic,
diff_amount: i8,
self_inflicted: u8,
) -> NativeResult<u8> {
match ptr.as_ref().change_stat_boost(stat, diff_amount, self_inflicted == 1) {
Ok(v) => NativeResult::ok(u8::from(v)),
Err(e) => NativeResult::err(e),
) -> FFIResult<u8> {
match handle
.from_ffi_handle()
.change_stat_boost(stat, diff_amount, self_inflicted == 1)
{
Ok(v) => FFIResult::ok(u8::from(v)),
Err(e) => FFIResult::err(e),
}
}
/// 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<Pokemon>, stat: Statistic) -> u8 {
ptr.as_ref().individual_values().get_stat(stat)
extern "C" fn pokemon_get_individual_value(handle: FFIHandle<Pokemon>, stat: Statistic) -> u8 {
handle.from_ffi_handle().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<Pokemon>,
stat: Statistic,
value: u8,
) -> NativeResult<()> {
ptr.as_ref().individual_values().set_stat(stat, value);
ptr.as_ref().recalculate_flat_stats().into()
extern "C" fn pokemon_set_individual_value(handle: FFIHandle<Pokemon>, stat: Statistic, value: u8) -> FFIResult<()> {
handle.from_ffi_handle().individual_values().set_stat(stat, value);
handle.from_ffi_handle().recalculate_flat_stats().into()
}
/// 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<Pokemon>, stat: Statistic) -> u8 {
ptr.as_ref().effort_values().get_stat(stat)
extern "C" fn pokemon_get_effort_value(handle: FFIHandle<Pokemon>, stat: Statistic) -> u8 {
handle.from_ffi_handle().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<Pokemon>, stat: Statistic, value: u8) -> NativeResult<()> {
ptr.as_ref().effort_values().set_stat(stat, value);
ptr.as_ref().recalculate_flat_stats().into()
extern "C" fn pokemon_set_effort_value(handle: FFIHandle<Pokemon>, stat: Statistic, value: u8) -> FFIResult<()> {
handle.from_ffi_handle().effort_values().set_stat(stat, value);
handle.from_ffi_handle().recalculate_flat_stats().into()
}
/// 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<Pokemon>) -> IdentifiablePointer<Battle> {
if let Some(v) = ptr.as_ref().get_battle() {
v.into()
extern "C" fn pokemon_get_battle(handle: FFIHandle<Pokemon>) -> FFIHandle<Battle> {
if let Some(v) = handle.from_ffi_handle().get_battle() {
FFIHandle::get_handle(v.into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
/// 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<Pokemon>) -> u8 {
ptr.as_ref().get_battle_side_index().unwrap_or_default()
extern "C" fn pokemon_get_battle_side_index(handle: FFIHandle<Pokemon>) -> u8 {
handle.from_ffi_handle().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<Pokemon>) -> u8 {
ptr.as_ref().get_battle_index().unwrap_or_default()
extern "C" fn pokemon_get_battle_index(handle: FFIHandle<Pokemon>) -> u8 {
handle.from_ffi_handle().get_battle_index().unwrap_or_default()
}
/// Returns whether something overrides the ability.
#[no_mangle]
extern "C" fn pokemon_is_ability_overriden(ptr: ExternPointer<Pokemon>) -> u8 {
u8::from(ptr.as_ref().is_ability_overriden())
extern "C" fn pokemon_is_ability_overriden(handle: FFIHandle<Pokemon>) -> u8 {
u8::from(handle.from_ffi_handle().is_ability_overriden())
}
/// Returns the currently active ability.
#[no_mangle]
extern "C" fn pokemon_active_ability(
ptr: ExternPointer<Pokemon>,
) -> NativeResult<IdentifiablePointer<Arc<dyn Ability>>> {
match ptr.as_ref().active_ability() {
Ok(v) => NativeResult::ok(v.clone().into()),
Err(e) => NativeResult::err(e),
extern "C" fn pokemon_active_ability(handle: FFIHandle<Pokemon>) -> FFIResult<FFIHandle<Arc<dyn Ability>>> {
match handle.from_ffi_handle().active_ability() {
Ok(v) => FFIResult::ok(FFIHandle::get_handle(v.clone().into())),
Err(e) => FFIResult::err(e),
}
}
/// Whether or not the Pokemon is allowed to gain experience.
#[no_mangle]
extern "C" fn pokemon_allowed_experience_gain(ptr: ExternPointer<Pokemon>) -> u8 {
u8::from(ptr.as_ref().allowed_experience_gain())
extern "C" fn pokemon_allowed_experience_gain(handle: FFIHandle<Pokemon>) -> u8 {
u8::from(handle.from_ffi_handle().allowed_experience_gain())
}
/// The [nature](https://bulbapedia.bulbagarden.net/wiki/Nature) of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_nature(ptr: ExternPointer<Pokemon>) -> IdentifiablePointer<Arc<dyn Nature>> {
ptr.as_ref().nature().clone().into()
extern "C" fn pokemon_nature(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Nature>> {
FFIHandle::get_handle(handle.from_ffi_handle().nature().clone().into())
}
/// 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.
#[no_mangle]
extern "C" fn pokemon_recalculate_flat_stats(ptr: ExternPointer<Pokemon>) -> NativeResult<()> {
ptr.as_ref().recalculate_flat_stats().into()
extern "C" fn pokemon_recalculate_flat_stats(handle: FFIHandle<Pokemon>) -> FFIResult<()> {
handle.from_ffi_handle().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<Pokemon>) -> NativeResult<()> {
ptr.as_ref().recalculate_boosted_stats().into()
extern "C" fn pokemon_recalculate_boosted_stats(handle: FFIHandle<Pokemon>) -> FFIResult<()> {
handle.from_ffi_handle().recalculate_boosted_stats().into()
}
/// Change the species of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_change_species(
ptr: ExternPointer<Pokemon>,
species: ExternPointer<Arc<dyn Species>>,
form: ExternPointer<Arc<dyn Form>>,
) -> NativeResult<()> {
ptr.as_ref()
.change_species(species.as_ref().clone(), form.as_ref().clone())
handle: FFIHandle<Pokemon>,
species: FFIHandle<Arc<dyn Species>>,
form: FFIHandle<Arc<dyn Form>>,
) -> FFIResult<()> {
handle
.from_ffi_handle()
.change_species(species.from_ffi_handle().clone(), form.from_ffi_handle().clone())
.into()
}
/// Change the form of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_change_form(ptr: ExternPointer<Pokemon>, form: ExternPointer<Arc<dyn Form>>) -> NativeResult<()> {
ptr.as_ref().change_form(form.as_ref()).into()
extern "C" fn pokemon_change_form(handle: FFIHandle<Pokemon>, form: FFIHandle<Arc<dyn Form>>) -> FFIResult<()> {
handle.from_ffi_handle().change_form(&form.from_ffi_handle()).into()
}
/// Whether or not the Pokemon is useable in a battle.
#[no_mangle]
extern "C" fn pokemon_is_usable(ptr: ExternPointer<Pokemon>) -> u8 {
u8::from(ptr.as_ref().is_usable())
extern "C" fn pokemon_is_usable(handle: FFIHandle<Pokemon>) -> u8 {
u8::from(handle.from_ffi_handle().is_usable())
}
/// Returns whether the Pokemon is fainted.
#[no_mangle]
extern "C" fn pokemon_is_fainted(ptr: ExternPointer<Pokemon>) -> u8 {
u8::from(ptr.as_ref().is_fainted())
extern "C" fn pokemon_is_fainted(handle: FFIHandle<Pokemon>) -> u8 {
u8::from(handle.from_ffi_handle().is_fainted())
}
/// Whether or not the Pokemon is on the battlefield.
#[no_mangle]
extern "C" fn pokemon_is_on_battlefield(ptr: ExternPointer<Pokemon>) -> u8 {
u8::from(ptr.as_ref().is_on_battlefield())
extern "C" fn pokemon_is_on_battlefield(handle: FFIHandle<Pokemon>) -> u8 {
u8::from(handle.from_ffi_handle().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<Pokemon>, damage: u32, source: DamageSource) -> NativeResult<()> {
ptr.as_ref().damage(damage, source).into()
extern "C" fn pokemon_damage(handle: FFIHandle<Pokemon>, damage: u32, source: DamageSource) -> FFIResult<()> {
handle.from_ffi_handle().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<Pokemon>, amount: u32, allow_revive: u8) -> bool {
ptr.as_ref().heal(amount, allow_revive == 1)
extern "C" fn pokemon_heal(handle: FFIHandle<Pokemon>, amount: u32, allow_revive: u8) -> bool {
handle.from_ffi_handle().heal(amount, allow_revive == 1)
}
/// Learn a move.
#[no_mangle]
extern "C" fn pokemon_learn_move(
ptr: ExternPointer<Pokemon>,
handle: FFIHandle<Pokemon>,
move_name: *const c_char,
learn_method: MoveLearnMethod,
) -> NativeResult<()> {
) -> FFIResult<()> {
unsafe {
ptr.as_ref()
handle
.from_ffi_handle()
.learn_move(&CStr::from_ptr(move_name).into(), learn_method)
.into()
}
@ -377,6 +414,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<Pokemon>) {
ptr.as_ref().clear_status()
extern "C" fn pokemon_clear_status(handle: FFIHandle<Pokemon>) {
handle.from_ffi_handle().clear_status()
}

View File

@ -1,68 +1,69 @@
use crate::dynamic_data::{Pokemon, PokemonParty};
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::FFIResult;
use std::sync::Arc;
/// Instantiates a party with a set size.
#[no_mangle]
extern "C" fn pokemon_party_new(capacity: usize) -> IdentifiablePointer<Arc<PokemonParty>> {
Arc::new(PokemonParty::new(capacity)).into()
extern "C" fn pokemon_party_new(capacity: usize) -> FFIHandle<Arc<PokemonParty>> {
FFIHandle::get_handle(Arc::new(PokemonParty::new(capacity)).into())
}
/// Gets a Pokemon at an index in the party.
#[no_mangle]
extern "C" fn pokemon_party_at(ptr: ExternPointer<Arc<PokemonParty>>, index: usize) -> IdentifiablePointer<Pokemon> {
if let Some(v) = ptr.as_ref().at(index) {
v.into()
extern "C" fn pokemon_party_at(ptr: FFIHandle<Arc<PokemonParty>>, index: usize) -> FFIHandle<Pokemon> {
if let Some(v) = ptr.from_ffi_handle().at(index) {
FFIHandle::get_handle(v.into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
/// Gets a Pokemon at an index in the party.
#[no_mangle]
extern "C" fn pokemon_party_switch(ptr: ExternPointer<Arc<PokemonParty>>, a: usize, b: usize) {
ptr.as_ref().switch(a, b);
extern "C" fn pokemon_party_switch(ptr: FFIHandle<Arc<PokemonParty>>, a: usize, b: usize) {
ptr.from_ffi_handle().switch(a, b);
}
/// Sets the Pokemon at an index to a Pokemon, returning the old Pokemon.
#[no_mangle]
extern "C" fn pokemon_party_swap_into(
ptr: ExternPointer<Arc<PokemonParty>>,
ptr: FFIHandle<Arc<PokemonParty>>,
index: usize,
pokemon: ExternPointer<Pokemon>,
) -> NativeResult<IdentifiablePointer<Pokemon>> {
let pokemon = if pokemon.ptr.is_null() {
pokemon: FFIHandle<Pokemon>,
) -> FFIResult<FFIHandle<Pokemon>> {
let pokemon = if pokemon.is_none() {
None
} else {
Some(pokemon.as_ref().clone())
Some(pokemon.from_ffi_handle())
};
match ptr.as_ref().swap_into(index, pokemon) {
Ok(Some(v)) => NativeResult::ok(v.into()),
Ok(None) => NativeResult::ok(IdentifiablePointer::none()),
Err(e) => NativeResult::err(e),
match ptr.from_ffi_handle().swap_into(index, pokemon) {
Ok(Some(v)) => FFIResult::ok(FFIHandle::get_handle(v.into())),
Ok(None) => FFIResult::ok(FFIHandle::none()),
Err(e) => FFIResult::err(e),
}
}
/// Whether or not the party still has Pokemon that can be used in battle.
#[no_mangle]
extern "C" fn pokemon_party_has_usable_pokemon(ptr: ExternPointer<Arc<PokemonParty>>) -> u8 {
u8::from(ptr.as_ref().has_usable_pokemon())
extern "C" fn pokemon_party_has_usable_pokemon(ptr: FFIHandle<Arc<PokemonParty>>) -> u8 {
u8::from(ptr.from_ffi_handle().has_usable_pokemon())
}
/// Get the length of the underlying list of Pokemon.
#[no_mangle]
extern "C" fn pokemon_party_length(ptr: ExternPointer<Arc<PokemonParty>>) -> usize {
ptr.as_ref().length()
extern "C" fn pokemon_party_length(ptr: FFIHandle<Arc<PokemonParty>>) -> usize {
ptr.from_ffi_handle().length()
}
/// Makes sure there are no empty spots in the party anymore, leaving the length the same.
#[no_mangle]
extern "C" fn pokemon_party_pack_party(ptr: ExternPointer<Arc<PokemonParty>>) -> NativeResult<()> {
ptr.as_ref().pack_party().into()
extern "C" fn pokemon_party_pack_party(ptr: FFIHandle<Arc<PokemonParty>>) -> FFIResult<()> {
ptr.from_ffi_handle().pack_party().into()
}
/// Checks if the party contains a given pokemon.
#[no_mangle]
extern "C" fn pokemon_party_has_pokemon(ptr: ExternPointer<Arc<PokemonParty>>, pokemon: ExternPointer<Pokemon>) -> u8 {
u8::from(ptr.as_ref().has_pokemon(pokemon.as_ref()))
extern "C" fn pokemon_party_has_pokemon(ptr: FFIHandle<Arc<PokemonParty>>, pokemon: FFIHandle<Pokemon>) -> u8 {
u8::from(ptr.from_ffi_handle().has_pokemon(&pokemon.from_ffi_handle()))
}

296
src/ffi/ffi_handle.rs Normal file
View File

@ -0,0 +1,296 @@
use crate::static_data::{
Ability, EffectParameter, Form, GrowthRate, Item, LearnableMoves, MoveData, Nature, SecondaryEffect, Species,
StaticStatisticSet, StatisticSet,
};
use anyhow::anyhow;
use hashbrown::HashMap;
use parking_lot::RwLock;
use std::hash::Hash;
use std::sync::atomic::AtomicUsize;
use std::sync::{Arc, LazyLock};
#[repr(C)]
pub(super) struct FFIHandle<T> {
handle: usize,
_marker: std::marker::PhantomData<T>,
}
impl<T> Clone for FFIHandle<T> {
fn clone(&self) -> Self {
Self {
handle: self.handle,
_marker: std::marker::PhantomData,
}
}
}
impl<T> Copy for FFIHandle<T> {}
#[derive(Clone)]
pub(super) enum FFIObject {
Ability(Arc<dyn Ability>),
EffectParameter(Arc<EffectParameter>),
StatisticSetU8(Arc<StatisticSet<u8>>),
StatisticSetI8(Arc<StatisticSet<i8>>),
StatisticSetU32(Arc<StatisticSet<u32>>),
StaticStatisticSetU16(Arc<StaticStatisticSet<u16>>),
Form(Arc<dyn Form>),
LearnableMoves(Arc<dyn LearnableMoves>),
GrowthRate(Arc<dyn GrowthRate>),
Item(Arc<dyn Item>),
SecondaryEffect(Arc<dyn SecondaryEffect>),
MoveData(Arc<dyn MoveData>),
Nature(Arc<dyn Nature>),
Species(Arc<dyn Species>),
SpeciesLibrary(Arc<dyn crate::static_data::SpeciesLibrary>),
MoveLibrary(Arc<dyn crate::static_data::MoveLibrary>),
AbilityLibrary(Arc<dyn crate::static_data::AbilityLibrary>),
ItemLibrary(Arc<dyn crate::static_data::ItemLibrary>),
GrowthRateLibrary(Arc<dyn crate::static_data::GrowthRateLibrary>),
LibrarySettings(Arc<dyn crate::static_data::LibrarySettings>),
NatureLibrary(Arc<dyn crate::static_data::NatureLibrary>),
TypeLibrary(Arc<dyn crate::static_data::TypeLibrary>),
StaticData(Arc<dyn crate::static_data::StaticData>),
// DynamicData
TurnChoice(Arc<crate::dynamic_data::TurnChoice>),
Pokemon(crate::dynamic_data::Pokemon),
LearnedMove(Arc<crate::dynamic_data::LearnedMove>),
PokemonParty(Arc<crate::dynamic_data::PokemonParty>),
BattleParty(Arc<crate::dynamic_data::BattleParty>),
Battle(crate::dynamic_data::Battle),
BattleSide(crate::dynamic_data::BattleSide),
BattleRandom(Arc<crate::dynamic_data::BattleRandom>),
// DynamicLibrary
BattleStatCalculator(Arc<dyn crate::dynamic_data::BattleStatCalculator>),
DamageLibrary(Arc<dyn crate::dynamic_data::DamageLibrary>),
MiscLibrary(Arc<dyn crate::dynamic_data::MiscLibrary>),
ScriptResolver(Arc<dyn crate::dynamic_data::ScriptResolver>),
DynamicLibrary(Arc<dyn crate::dynamic_data::DynamicLibrary>),
}
unsafe impl Send for FFIObject {}
unsafe impl Sync for FFIObject {}
static NEXT_HANDLE: AtomicUsize = AtomicUsize::new(0);
static FFI_OBJECTS: LazyLock<RwLock<HashMap<usize, FFIObject>>> = LazyLock::new(|| RwLock::new(HashMap::new()));
static FFI_OBJECTS_INVERSE: LazyLock<RwLock<HashMap<FFIObject, usize>>> = LazyLock::new(|| RwLock::new(HashMap::new()));
impl<T> FFIHandle<T> {
pub fn get_handle(o: FFIObject) -> Self {
if let Some(handle) = FFI_OBJECTS_INVERSE.read().get(&o) {
return Self {
handle: *handle,
_marker: std::marker::PhantomData,
};
}
let handle = NEXT_HANDLE.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
FFI_OBJECTS.write().insert(handle, o.clone());
FFI_OBJECTS_INVERSE.write().insert(o, handle);
Self {
handle,
_marker: std::marker::PhantomData,
}
}
pub fn none() -> Self {
Self {
handle: 0,
_marker: std::marker::PhantomData,
}
}
#[allow(clippy::unwrap_used)] // Unwrap used, as it is a potential security bug if this fails
pub fn resolve(&self) -> FFIObject {
FFI_OBJECTS
.read()
.get(&self.handle)
.ok_or(anyhow!("Unable to get handle"))
.unwrap()
.clone()
}
pub fn is_none(&self) -> bool {
self.handle == 0
}
}
#[allow(clippy::from_over_into)]
impl<T> Into<anyhow_ext::Result<FFIObject>> for FFIHandle<T> {
fn into(self) -> anyhow_ext::Result<FFIObject> {
Ok(FFI_OBJECTS
.read()
.get(&self.handle)
.ok_or(anyhow!("Unable to get handle"))?
.clone())
}
}
impl Eq for FFIObject {}
#[allow(clippy::wrong_self_convention)]
pub(super) trait FromFFIHandle<T> {
fn from_ffi_handle(&self) -> T;
fn from_ffi_handle_opt(&self) -> Option<T>;
}
impl Hash for FFIObject {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Ability(a) => Arc::as_ptr(a).hash(state),
Self::EffectParameter(a) => Arc::as_ptr(a).hash(state),
Self::StatisticSetU8(a) => Arc::as_ptr(a).hash(state),
Self::StatisticSetI8(a) => Arc::as_ptr(a).hash(state),
Self::StatisticSetU32(a) => Arc::as_ptr(a).hash(state),
Self::StaticStatisticSetU16(a) => Arc::as_ptr(a).hash(state),
Self::Form(a) => Arc::as_ptr(a).hash(state),
Self::LearnableMoves(a) => Arc::as_ptr(a).hash(state),
Self::GrowthRate(a) => Arc::as_ptr(a).hash(state),
Self::Item(a) => Arc::as_ptr(a).hash(state),
Self::SecondaryEffect(a) => Arc::as_ptr(a).hash(state),
Self::MoveData(a) => Arc::as_ptr(a).hash(state),
Self::Nature(a) => Arc::as_ptr(a).hash(state),
Self::Species(a) => Arc::as_ptr(a).hash(state),
Self::SpeciesLibrary(a) => Arc::as_ptr(a).hash(state),
Self::MoveLibrary(a) => Arc::as_ptr(a).hash(state),
Self::AbilityLibrary(a) => Arc::as_ptr(a).hash(state),
Self::ItemLibrary(a) => Arc::as_ptr(a).hash(state),
Self::GrowthRateLibrary(a) => Arc::as_ptr(a).hash(state),
Self::LibrarySettings(a) => Arc::as_ptr(a).hash(state),
Self::NatureLibrary(a) => Arc::as_ptr(a).hash(state),
Self::TypeLibrary(a) => Arc::as_ptr(a).hash(state),
Self::StaticData(a) => Arc::as_ptr(a).hash(state),
Self::TurnChoice(a) => Arc::as_ptr(a).hash(state),
Self::Pokemon(a) => a.as_ptr().hash(state),
Self::LearnedMove(a) => Arc::as_ptr(a).hash(state),
Self::PokemonParty(a) => Arc::as_ptr(a).hash(state),
Self::BattleParty(a) => Arc::as_ptr(a).hash(state),
Self::Battle(a) => a.as_ptr().hash(state),
Self::BattleSide(a) => a.as_ptr().hash(state),
Self::BattleRandom(a) => Arc::as_ptr(a).hash(state),
Self::BattleStatCalculator(a) => Arc::as_ptr(a).hash(state),
Self::DamageLibrary(a) => Arc::as_ptr(a).hash(state),
Self::MiscLibrary(a) => Arc::as_ptr(a).hash(state),
Self::ScriptResolver(a) => Arc::as_ptr(a).hash(state),
Self::DynamicLibrary(a) => Arc::as_ptr(a).hash(state),
}
}
}
impl PartialEq for FFIObject {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Ability(a), Self::Ability(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::EffectParameter(a), Self::EffectParameter(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::StatisticSetU8(a), Self::StatisticSetU8(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::StatisticSetI8(a), Self::StatisticSetI8(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::StatisticSetU32(a), Self::StatisticSetU32(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::StaticStatisticSetU16(a), Self::StaticStatisticSetU16(b)) => {
Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr()
}
(Self::Form(a), Self::Form(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::LearnableMoves(a), Self::LearnableMoves(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::GrowthRate(a), Self::GrowthRate(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::Item(a), Self::Item(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::SecondaryEffect(a), Self::SecondaryEffect(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::MoveData(a), Self::MoveData(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::Nature(a), Self::Nature(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::Species(a), Self::Species(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::SpeciesLibrary(a), Self::SpeciesLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::MoveLibrary(a), Self::MoveLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::AbilityLibrary(a), Self::AbilityLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::ItemLibrary(a), Self::ItemLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::GrowthRateLibrary(a), Self::GrowthRateLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::LibrarySettings(a), Self::LibrarySettings(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::NatureLibrary(a), Self::NatureLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::TypeLibrary(a), Self::TypeLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::StaticData(a), Self::StaticData(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::TurnChoice(a), Self::TurnChoice(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::Pokemon(a), Self::Pokemon(b)) => a.eq(b),
(Self::LearnedMove(a), Self::LearnedMove(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::PokemonParty(a), Self::PokemonParty(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::BattleParty(a), Self::BattleParty(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::Battle(a), Self::Battle(b)) => a.eq(b),
(Self::BattleSide(a), Self::BattleSide(b)) => a.eq(b),
(Self::BattleRandom(a), Self::BattleRandom(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::BattleStatCalculator(a), Self::BattleStatCalculator(b)) => {
Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr()
}
(Self::DamageLibrary(a), Self::DamageLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::MiscLibrary(a), Self::MiscLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::ScriptResolver(a), Self::ScriptResolver(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::DynamicLibrary(a), Self::DynamicLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
_ => false,
}
}
}
macro_rules! ffi_obj_conversions {
($res_type:ty, $ffi_obj_name:ident) => {
impl From<$res_type> for FFIObject {
fn from(a: $res_type) -> Self {
Self::$ffi_obj_name(a)
}
}
impl FromFFIHandle<$res_type> for FFIHandle<$res_type> {
fn from_ffi_handle(&self) -> $res_type {
match self.resolve() {
FFIObject::$ffi_obj_name(a) => a.clone(),
_ => panic!("Invalid handle"),
}
}
fn from_ffi_handle_opt(&self) -> Option<$res_type> {
if (self.is_none()) {
return None;
}
match self.resolve() {
FFIObject::$ffi_obj_name(a) => Some(a.clone()),
_ => panic!("Invalid handle"),
}
}
}
};
}
ffi_obj_conversions!(Arc<dyn Ability>, Ability);
ffi_obj_conversions!(Arc<EffectParameter>, EffectParameter);
ffi_obj_conversions!(Arc<StatisticSet<i8>>, StatisticSetI8);
ffi_obj_conversions!(Arc<StatisticSet<u8>>, StatisticSetU8);
ffi_obj_conversions!(Arc<StatisticSet<u32>>, StatisticSetU32);
ffi_obj_conversions!(Arc<StaticStatisticSet<u16>>, StaticStatisticSetU16);
ffi_obj_conversions!(Arc<dyn Form>, Form);
ffi_obj_conversions!(Arc<dyn LearnableMoves>, LearnableMoves);
ffi_obj_conversions!(Arc<dyn GrowthRate>, GrowthRate);
ffi_obj_conversions!(Arc<dyn Item>, Item);
ffi_obj_conversions!(Arc<dyn SecondaryEffect>, SecondaryEffect);
ffi_obj_conversions!(Arc<dyn MoveData>, MoveData);
ffi_obj_conversions!(Arc<dyn Nature>, Nature);
ffi_obj_conversions!(Arc<dyn Species>, Species);
ffi_obj_conversions!(Arc<dyn crate::static_data::SpeciesLibrary>, SpeciesLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::MoveLibrary>, MoveLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::AbilityLibrary>, AbilityLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::ItemLibrary>, ItemLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::GrowthRateLibrary>, GrowthRateLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::LibrarySettings>, LibrarySettings);
ffi_obj_conversions!(Arc<dyn crate::static_data::NatureLibrary>, NatureLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::TypeLibrary>, TypeLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::StaticData>, StaticData);
ffi_obj_conversions!(Arc<crate::dynamic_data::TurnChoice>, TurnChoice);
ffi_obj_conversions!(crate::dynamic_data::Pokemon, Pokemon);
ffi_obj_conversions!(Arc<crate::dynamic_data::LearnedMove>, LearnedMove);
ffi_obj_conversions!(Arc<crate::dynamic_data::PokemonParty>, PokemonParty);
ffi_obj_conversions!(Arc<crate::dynamic_data::BattleParty>, BattleParty);
ffi_obj_conversions!(crate::dynamic_data::Battle, Battle);
ffi_obj_conversions!(crate::dynamic_data::BattleSide, BattleSide);
ffi_obj_conversions!(Arc<crate::dynamic_data::BattleRandom>, BattleRandom);
ffi_obj_conversions!(Arc<dyn crate::dynamic_data::BattleStatCalculator>, BattleStatCalculator);
ffi_obj_conversions!(Arc<dyn crate::dynamic_data::DamageLibrary>, DamageLibrary);
ffi_obj_conversions!(Arc<dyn crate::dynamic_data::MiscLibrary>, MiscLibrary);
ffi_obj_conversions!(Arc<dyn crate::dynamic_data::ScriptResolver>, ScriptResolver);
ffi_obj_conversions!(Arc<dyn crate::dynamic_data::DynamicLibrary>, DynamicLibrary);

View File

@ -1,68 +1,57 @@
/// The foreign function interfaces for the dynamic data
mod dynamic_data;
mod ffi_handle;
/// The foreign function interfaces for that static data
mod static_data;
pub(self) use ffi_handle::*;
/// Helper type for clearer functions.
type OwnedPtr<T> = *mut T;
type OwnedPtrString = *mut c_char;
/// Helper type for clearer functions.
type BorrowedPtr<T> = *const T;
type NonOwnedPtrString = *const c_char;
/// Generates a basic getter foreign function interface.
macro_rules! ffi_arc_getter {
macro_rules! ffi_handle_arc_dyn_getter {
(
$type:ty, $func:ident, $returns: ty
) => {
paste::paste! {
#[no_mangle]
extern "C" fn [< $type:snake _ $func >](ptr: ExternPointer<Arc<$type>>) -> $returns {
ptr.as_ref().$func()
}
}
};
}
/// Generates a basic getter foreign function interface.
macro_rules! ffi_arc_dyn_getter {
(
$type:ty, $func:ident, $returns: ty
) => {
paste::paste! {
#[no_mangle]
extern "C" fn [< $type:snake _ $func >](ptr: ExternPointer<Arc<dyn $type>>) -> $returns {
ptr.as_ref().$func()
extern "C" fn [< $type:snake _ $func >](ptr: FFIHandle<Arc<dyn $type>>) -> $returns {
ptr.from_ffi_handle().$func()
}
}
};
}
/// Generates a basic getter foreign function interface where the return type is a [`crate::StringKey`].
macro_rules! ffi_arc_stringkey_getter {
macro_rules! ffi_handle_arc_stringkey_getter {
(
$type:ty, $func:ident
) => {
paste::paste! {
#[no_mangle]
extern "C" fn [< $type:lower _ $func >](ptr: ExternPointer<Arc<dyn $type>>) -> OwnedPtr<c_char> {
std::ffi::CString::new(ptr.as_ref().$func().str()).unwrap().into_raw()
extern "C" fn [< $type:lower _ $func >](ptr: FFIHandle<Arc<dyn $type>>) -> NonOwnedPtrString {
std::ffi::CString::new(ptr.from_ffi_handle().$func().str()).unwrap().into_raw()
}
}
};
}
/// Generates a foreign function interface for a vec. This generates a length function, and a getter.
macro_rules! ffi_vec_value_getters {
macro_rules! ffi_handle_vec_value_getters {
(
$name:ident, $type:ty, $func:ident, $returns: ty
) => {
paste::paste! {
#[no_mangle]
extern "C" fn [< $name:lower _ $func _length>](ptr: ExternPointer<Arc<$type>>) -> usize {
ptr.as_ref().$func().len()
extern "C" fn [< $name:lower _ $func _length>](ptr: FFIHandle<Arc<$type>>) -> usize {
ptr.from_ffi_handle().$func().len()
}
#[no_mangle]
extern "C" fn [< $name:lower _ $func _get>](ptr: ExternPointer<Arc<$type>>, index: usize) -> $returns {
*ptr.as_ref().$func().get(index).unwrap()
extern "C" fn [< $name:lower _ $func _get>](ptr: FFIHandle<Arc<$type>>, index: usize) -> $returns {
*ptr.from_ffi_handle().$func().get(index).unwrap()
}
}
};
@ -70,33 +59,28 @@ macro_rules! ffi_vec_value_getters {
/// Generates a foreign function interface for a vec of [`crate::StringKey`]. This generates a
/// length function, and a getter.
macro_rules! ffi_vec_stringkey_getters {
macro_rules! ffi_handle_vec_stringkey_getters {
(
$type:ty, $func:ident
) => {
paste::paste! {
#[no_mangle]
extern "C" fn [< $type:lower _ $func _length>](ptr: ExternPointer<Arc<dyn $type>>) -> usize {
ptr.as_ref().$func().len()
extern "C" fn [< $type:lower _ $func _length>](ptr: FFIHandle<Arc<dyn $type>>) -> usize {
ptr.from_ffi_handle().$func().len()
}
#[no_mangle]
extern "C" fn [< $type:lower _ $func _get>](ptr: ExternPointer<Arc<dyn $type>>, index: usize) -> OwnedPtr<c_char> {
CString::new(ptr.as_ref().$func().get(index).unwrap().str()).unwrap().into_raw()
extern "C" fn [< $type:lower _ $func _get>](ptr: FFIHandle<Arc<dyn $type>>, index: usize) -> OwnedPtrString {
CString::new(ptr.from_ffi_handle().$func().get(index).unwrap().str()).unwrap().into_raw()
}
}
};
}
use crate::dynamic_data::{Battle, Pokemon};
use crate::{ValueIdentifiable, ValueIdentifier};
pub(self) use ffi_arc_dyn_getter;
pub(self) use ffi_arc_getter;
pub(self) use ffi_arc_stringkey_getter;
pub(self) use ffi_vec_stringkey_getters;
pub(self) use ffi_vec_value_getters;
pub(self) use ffi_handle_arc_dyn_getter;
pub(self) use ffi_handle_arc_stringkey_getter;
pub(self) use ffi_handle_vec_stringkey_getters;
pub(self) use ffi_handle_vec_value_getters;
use std::ffi::{c_char, CString};
use std::mem::transmute;
use std::sync::Arc;
/// Helper utility class to wrap a pointer for extern functions.
#[repr(C)]
@ -115,184 +99,6 @@ impl<T: ?Sized> ExternPointer<T> {
.unwrap_or_else(|| panic!("Given pointer of type '{}' was null", std::any::type_name::<T>()))
}
}
/// Get the internal pointer as mutable reference.
#[allow(clippy::panic)] // We currently allow this as these should never be null, but we might want to change this in the future.
pub(self) fn as_mut(&mut self) -> &mut T {
unsafe {
self.ptr
.as_mut()
.unwrap_or_else(|| panic!("Given pointer of type '{}' was null", std::any::type_name::<T>()))
}
}
}
/// Helper utility class to give both the pointer and identifier to a FFI.
#[repr(C)]
pub(self) struct IdentifiablePointer<T> {
/// The wrapped pointer.
pub ptr: *const T,
/// The identifier of the pointer.
pub id: usize,
}
impl<T> Clone for IdentifiablePointer<T> {
fn clone(&self) -> Self {
Self {
id: self.id,
ptr: self.ptr,
}
}
}
impl<T> Copy for IdentifiablePointer<T> {}
impl<T> IdentifiablePointer<T> {
/// Creates a new IdentifiablePointer.
pub(self) fn new(ptr: *const T, id: ValueIdentifier) -> Self {
unsafe { Self { ptr, id: transmute(id) } }
}
}
impl<T> From<*mut T> for ExternPointer<T> {
fn from(ptr: *mut T) -> Self {
ExternPointer { ptr }
}
}
impl<T: ValueIdentifiable + ?Sized> From<Arc<T>> for IdentifiablePointer<Arc<T>> {
fn from(v: Arc<T>) -> Self {
let id = v.value_identifier().value();
Self {
ptr: Box::into_raw(Box::new(v)),
id,
}
}
}
impl<T: ValueIdentifiable + ?Sized> From<Option<Arc<T>>> for IdentifiablePointer<Arc<T>> {
fn from(v: Option<Arc<T>>) -> 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<T: ValueIdentifiable + ?Sized> From<Box<T>> for IdentifiablePointer<Box<T>> {
fn from(v: Box<T>) -> Self {
let id = unsafe { transmute(v.value_identifier()) };
Self {
ptr: Box::into_raw(Box::new(v)),
id,
}
}
}
impl<T: ValueIdentifiable + ?Sized> From<&Box<T>> for IdentifiablePointer<Box<T>> {
fn from(v: &Box<T>) -> Self {
let id = unsafe { transmute(v.value_identifier()) };
Self {
ptr: v as *const Box<T>,
id,
}
}
}
impl<T: ValueIdentifiable + ?Sized> From<Option<Box<T>>> for IdentifiablePointer<Box<T>> {
fn from(v: Option<Box<T>>) -> 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<T: ValueIdentifiable + ?Sized> From<&Option<Box<T>>> for IdentifiablePointer<Box<T>> {
fn from(v: &Option<Box<T>>) -> Self {
if let Some(v) = v {
let id = unsafe { transmute(v.value_identifier()) };
unsafe {
Self {
ptr: *Box::into_raw(Box::new(v as *const Box<T>)),
id,
}
}
} else {
IdentifiablePointer::none()
}
}
}
impl<T: ValueIdentifiable> From<Box<T>> for IdentifiablePointer<T> {
fn from(v: Box<T>) -> Self {
let id = unsafe { transmute(v.value_identifier()) };
Self {
ptr: Box::into_raw(v),
id,
}
}
}
impl<T: ValueIdentifiable> From<*const T> for IdentifiablePointer<T> {
#[allow(clippy::unwrap_used)] // We currently allow this as these should never be null, but we might want to change this in the future.
fn from(v: *const T) -> Self {
let id = unsafe { transmute(v.as_ref().unwrap().value_identifier()) };
Self { ptr: v, id }
}
}
impl From<Pokemon> for IdentifiablePointer<Pokemon> {
fn from(v: Pokemon) -> Self {
let id = v.value_identifier().value();
Self {
ptr: Box::into_raw(Box::new(v)),
id,
}
}
}
impl From<Option<Pokemon>> for IdentifiablePointer<Pokemon> {
fn from(v: Option<Pokemon>) -> 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<Battle> for IdentifiablePointer<Battle> {
fn from(v: Battle) -> Self {
let id = v.value_identifier().value();
Self {
ptr: Box::into_raw(Box::new(v)),
id,
}
}
}
impl<T> IdentifiablePointer<T> {
/// Returns an identifiable pointer with null as pointer, and 0 as identifier.
pub fn none() -> Self {
Self {
ptr: std::ptr::null(),
id: Default::default(),
}
}
}
/// Helper utility class to give either the data or an error to a FFI.
@ -301,19 +107,19 @@ union ResultUnion<T: Copy> {
/// If the result is ok, this contains the value.
ok: T,
/// If the result is an error, this contains the error message.
err: OwnedPtr<c_char>,
err: NonOwnedPtrString,
}
/// The result of a FFI call that can either be an error or a value.
#[repr(C)]
pub struct NativeResult<T: Copy> {
pub struct FFIResult<T: Copy> {
/// If the result is ok, this is 1, otherwise 0.
ok: u8,
/// The value or error.
value: ResultUnion<T>,
}
impl<T: Copy> NativeResult<T> {
impl<T: Copy> FFIResult<T> {
/// Creates a new NativeResult with the given value.
pub fn ok(value: T) -> Self {
Self {
@ -344,7 +150,7 @@ impl<T: Copy> NativeResult<T> {
}
}
impl<T: Copy> From<anyhow::Result<T>> for NativeResult<T> {
impl<T: Copy> From<anyhow::Result<T>> for FFIResult<T> {
fn from(value: anyhow::Result<T>) -> Self {
match value {
Ok(v) => Self::ok(v),
@ -353,7 +159,7 @@ impl<T: Copy> From<anyhow::Result<T>> for NativeResult<T> {
}
}
impl<T: Copy> From<T> for NativeResult<T> {
impl<T: Copy> From<T> for FFIResult<T> {
fn from(value: T) -> Self {
Self::ok(value)
}

View File

@ -1,9 +1,10 @@
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::FFIHandle;
use crate::ffi::FromFFIHandle;
use crate::ffi::{FFIResult, OwnedPtrString};
use crate::static_data::{Ability, AbilityImpl, EffectParameter};
use crate::StringKey;
use anyhow::anyhow;
use std::ffi::{c_char, CStr, CString};
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Instantiates a new ability.
@ -11,67 +12,61 @@ use std::sync::Arc;
unsafe extern "C" fn ability_new(
name: *const c_char,
effect: *const c_char,
parameters: *const OwnedPtr<Arc<EffectParameter>>,
parameters: *const FFIHandle<Arc<EffectParameter>>,
parameters_length: usize,
) -> NativeResult<IdentifiablePointer<Arc<dyn Ability>>> {
) -> FFIResult<FFIHandle<Arc<dyn Ability>>> {
let parameters = std::slice::from_raw_parts(parameters, parameters_length);
let mut parameters_vec: Vec<Arc<EffectParameter>> = Vec::with_capacity(parameters_length);
for parameter in parameters {
parameters_vec.push(parameter.read());
parameters_vec.push(parameter.from_ffi_handle());
}
let name: StringKey = match CStr::from_ptr(name).to_str() {
Ok(s) => s.into(),
Err(_) => return NativeResult::err(anyhow!("Failed to convert name to CStr")),
Err(_) => return FFIResult::err(anyhow!("Failed to convert name to CStr")),
};
let effect: StringKey = match CStr::from_ptr(effect).to_str() {
Ok(s) => s.into(),
Err(_) => return NativeResult::err(anyhow!("Failed to convert effect to CStr")),
Err(_) => return FFIResult::err(anyhow!("Failed to convert effect to CStr")),
};
let arc: Arc<dyn Ability> = Arc::new(AbilityImpl::new(&name, &effect, parameters_vec));
NativeResult::ok(arc.into())
}
/// Drops a reference counted ability.
#[no_mangle]
unsafe extern "C" fn ability_drop(ptr: OwnedPtr<Arc<dyn Ability>>) {
drop_in_place(ptr)
FFIResult::ok(FFIHandle::get_handle(arc.into()))
}
/// The name of the ability.
#[no_mangle]
unsafe extern "C" fn ability_name(ptr: ExternPointer<Arc<dyn Ability>>) -> NativeResult<OwnedPtr<c_char>> {
match CString::new(ptr.as_ref().name().str()) {
Ok(s) => NativeResult::ok(s.into_raw()),
Err(_) => NativeResult::err(anyhow!("Failed to convert name to CString")),
unsafe extern "C" fn ability_name(handle: FFIHandle<Arc<dyn Ability>>) -> FFIResult<OwnedPtrString> {
match CString::new(handle.from_ffi_handle().name().str()) {
Ok(s) => FFIResult::ok(s.into_raw()),
Err(_) => FFIResult::err(anyhow!("Failed to convert name to CString")),
}
}
/// The name of the script effect of the ability.
#[no_mangle]
unsafe extern "C" fn ability_effect(ptr: ExternPointer<Arc<dyn Ability>>) -> NativeResult<OwnedPtr<c_char>> {
match CString::new(ptr.as_ref().effect().str()) {
Ok(s) => NativeResult::ok(s.into_raw()),
Err(_) => NativeResult::err(anyhow!("Failed to convert effect to CString")),
unsafe extern "C" fn ability_effect(ptr: FFIHandle<Arc<dyn Ability>>) -> FFIResult<OwnedPtrString> {
match CString::new(ptr.from_ffi_handle().effect().str()) {
Ok(s) => FFIResult::ok(s.into_raw()),
Err(_) => FFIResult::err(anyhow!("Failed to convert effect to CString")),
}
}
/// The length of the parameters for the script effect of the ability.
#[no_mangle]
unsafe extern "C" fn ability_parameter_length(ptr: ExternPointer<Arc<dyn Ability>>) -> usize {
ptr.as_ref().parameters().len()
unsafe extern "C" fn ability_parameter_length(ptr: FFIHandle<Arc<dyn Ability>>) -> usize {
ptr.from_ffi_handle().parameters().len()
}
/// Gets a parameter for the script effect of the ability.
#[no_mangle]
unsafe extern "C" fn ability_parameter_get(
ptr: ExternPointer<Arc<dyn Ability>>,
ptr: FFIHandle<Arc<dyn Ability>>,
index: usize,
) -> IdentifiablePointer<Arc<EffectParameter>> {
if let Some(p) = ptr.as_ref().parameters().get(index) {
p.clone().into()
) -> FFIHandle<Arc<EffectParameter>> {
if let Some(p) = ptr.from_ffi_handle().parameters().get(index) {
FFIHandle::get_handle(p.clone().into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}

View File

@ -1,13 +1,13 @@
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::{
ffi_arc_dyn_getter, ffi_vec_stringkey_getters, ffi_vec_value_getters, BorrowedPtr, ExternPointer,
IdentifiablePointer, NativeResult, OwnedPtr,
ffi_handle_arc_dyn_getter, ffi_handle_vec_stringkey_getters, ffi_handle_vec_value_getters, FFIResult,
NonOwnedPtrString, OwnedPtrString,
};
use crate::static_data::{Form, FormImpl, LearnableMoves, StaticStatisticSet, TypeIdentifier};
use crate::StringKey;
use anyhow::anyhow;
use hashbrown::HashSet;
use std::ffi::{c_char, CStr, CString};
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Instantiates a new form.
@ -19,18 +19,18 @@ unsafe extern "C" fn form_new(
base_experience: u32,
types: *const TypeIdentifier,
types_length: usize,
base_stats: OwnedPtr<StaticStatisticSet<u16>>,
abilities: *const BorrowedPtr<c_char>,
base_stats: FFIHandle<Arc<StaticStatisticSet<u16>>>,
abilities: *const NonOwnedPtrString,
abilities_length: usize,
hidden_abilities: *const BorrowedPtr<c_char>,
hidden_abilities: *const NonOwnedPtrString,
hidden_abilities_length: usize,
moves: OwnedPtr<Box<dyn LearnableMoves>>,
moves: FFIHandle<Arc<dyn LearnableMoves>>,
flags: *const *const c_char,
flags_length: usize,
) -> NativeResult<IdentifiablePointer<Arc<dyn Form>>> {
) -> FFIResult<FFIHandle<Arc<dyn Form>>> {
let name: StringKey = match CStr::from_ptr(name).to_str() {
Ok(name) => name.into(),
Err(_) => return NativeResult::err(anyhow!("Unable to convert name to string")),
Err(_) => return FFIResult::err(anyhow!("Unable to convert name to string")),
};
let abilities = std::slice::from_raw_parts(abilities, abilities_length);
@ -49,7 +49,7 @@ unsafe extern "C" fn form_new(
for flag in flags {
let flag = match CStr::from_ptr(*flag).to_str() {
Ok(flag) => flag,
Err(_) => return NativeResult::err(anyhow!("Unable to convert flag to string")),
Err(_) => return FFIResult::err(anyhow!("Unable to convert flag to string")),
};
flags_set.insert(flag.into());
}
@ -60,57 +60,50 @@ unsafe extern "C" fn form_new(
weight,
base_experience,
std::slice::from_raw_parts(types, types_length).to_vec(),
*Box::from_raw(base_stats),
base_stats.from_ffi_handle(),
abilities_vec,
hidden_abilities_vec,
*Box::from_raw(moves),
moves.from_ffi_handle(),
flags_set,
));
NativeResult::ok(a.into())
}
/// Drops a reference count for a form.
#[no_mangle]
unsafe extern "C" fn form_drop(ptr: OwnedPtr<Arc<dyn Form>>) {
drop_in_place(ptr)
FFIResult::ok(FFIHandle::get_handle(a.into()))
}
/// The name of the form.
#[no_mangle]
unsafe extern "C" fn form_name(ptr: ExternPointer<Arc<dyn Form>>) -> NativeResult<OwnedPtr<c_char>> {
let name = ptr.as_ref().name();
unsafe extern "C" fn form_name(ptr: FFIHandle<Arc<dyn Form>>) -> FFIResult<OwnedPtrString> {
let obj = ptr.from_ffi_handle();
let name = obj.name();
match CString::new(name.str()) {
Ok(name) => NativeResult::ok(name.into_raw()),
Err(_) => NativeResult::err(anyhow!("Unable to convert name `{}` to CString", name.str())),
Ok(name) => FFIResult::ok(name.into_raw()),
Err(_) => FFIResult::err(anyhow!("Unable to convert name `{}` to CString", name.str())),
}
}
ffi_arc_dyn_getter!(Form, height, f32);
ffi_arc_dyn_getter!(Form, weight, f32);
ffi_arc_dyn_getter!(Form, base_experience, u32);
ffi_handle_arc_dyn_getter!(Form, height, f32);
ffi_handle_arc_dyn_getter!(Form, weight, f32);
ffi_handle_arc_dyn_getter!(Form, base_experience, u32);
ffi_vec_value_getters!(Form, dyn Form, types, TypeIdentifier);
ffi_handle_vec_value_getters!(Form, dyn Form, types, TypeIdentifier);
/// The inherent values of a form of species that are used for the stats of a Pokemon.
#[no_mangle]
unsafe extern "C" fn form_base_stats(
ptr: ExternPointer<Arc<dyn Form>>,
) -> IdentifiablePointer<Arc<StaticStatisticSet<u16>>> {
ptr.as_ref().base_stats().clone().into()
unsafe extern "C" fn form_base_stats(ptr: FFIHandle<Arc<dyn Form>>) -> FFIHandle<Arc<StaticStatisticSet<u16>>> {
FFIHandle::get_handle(ptr.from_ffi_handle().base_stats().clone().into())
}
ffi_vec_stringkey_getters!(Form, abilities);
ffi_vec_stringkey_getters!(Form, hidden_abilities);
ffi_handle_vec_stringkey_getters!(Form, abilities);
ffi_handle_vec_stringkey_getters!(Form, hidden_abilities);
/// The moves a Pokemon with this form can learn.
#[no_mangle]
extern "C" fn form_moves(ptr: ExternPointer<Arc<dyn Form>>) -> BorrowedPtr<Box<dyn LearnableMoves>> {
ptr.as_ref().moves() as *const Box<dyn LearnableMoves>
extern "C" fn form_moves(ptr: FFIHandle<Arc<dyn Form>>) -> FFIHandle<Arc<dyn LearnableMoves>> {
FFIHandle::get_handle(ptr.from_ffi_handle().moves().clone().into())
}
/// Check if the form has a specific flag set.
#[no_mangle]
unsafe extern "C" fn form_has_flag(ptr: ExternPointer<Arc<dyn Form>>, flag: *const c_char) -> u8 {
unsafe extern "C" fn form_has_flag(ptr: FFIHandle<Arc<dyn Form>>, flag: *const c_char) -> u8 {
let flag = CStr::from_ptr(flag).into();
u8::from(ptr.as_ref().has_flag(&flag))
u8::from(ptr.from_ffi_handle().has_flag(&flag))
}

View File

@ -1,33 +1,26 @@
use crate::defines::LevelInt;
use crate::ffi::{ExternPointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::FFIResult;
use crate::static_data::{GrowthRate, LookupGrowthRate};
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Instantiates a new lookup growth rate. The experience array should be the amount of experience
/// required per level, with the first element being the experience required for level 1 (generally 0).
#[no_mangle]
unsafe extern "C" fn growth_rate_lookup_new(array: *const u32, length: usize) -> OwnedPtr<Box<dyn GrowthRate>> {
unsafe extern "C" fn growth_rate_lookup_new(array: *const u32, length: usize) -> FFIHandle<Arc<dyn GrowthRate>> {
let array = std::slice::from_raw_parts(array, length);
Box::into_raw(Box::new(Box::new(LookupGrowthRate::new(array.to_vec()))))
}
/// Drops the growth rate.
#[no_mangle]
unsafe extern "C" fn growth_rate_lookup_drop(ptr: OwnedPtr<Box<dyn GrowthRate>>) {
drop_in_place(ptr)
let g: Arc<dyn GrowthRate> = Arc::new(LookupGrowthRate::new(array.to_vec()));
FFIHandle::get_handle(g.into())
}
/// Calculate the level something with this growth rate would have at a certain experience.
#[no_mangle]
extern "C" fn growth_rate_calculate_level(ptr: ExternPointer<Box<dyn GrowthRate>>, experience: u32) -> LevelInt {
ptr.as_ref().calculate_level(experience)
extern "C" fn growth_rate_calculate_level(ptr: FFIHandle<Arc<dyn GrowthRate>>, experience: u32) -> LevelInt {
ptr.from_ffi_handle().calculate_level(experience)
}
/// Calculate the experience something with this growth rate would have at a certain level.
#[no_mangle]
extern "C" fn growth_rate_calculate_experience(
ptr: ExternPointer<Box<dyn GrowthRate>>,
level: LevelInt,
) -> NativeResult<u32> {
ptr.as_ref().calculate_experience(level).into()
extern "C" fn growth_rate_calculate_experience(ptr: FFIHandle<Arc<dyn GrowthRate>>, level: LevelInt) -> FFIResult<u32> {
ptr.from_ffi_handle().calculate_experience(level).into()
}

View File

@ -1,10 +1,10 @@
use crate::ffi::{ffi_arc_dyn_getter, ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::{ffi_handle_arc_dyn_getter, FFIResult, OwnedPtrString};
use crate::static_data::{BattleItemCategory, Item, ItemCategory, ItemImpl};
use crate::StringKey;
use anyhow::anyhow;
use hashbrown::HashSet;
use std::ffi::{c_char, CStr, CString};
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Instantiates an item.
@ -16,48 +16,43 @@ unsafe extern "C" fn item_new(
price: i32,
flags: *const *const c_char,
flags_length: usize,
) -> NativeResult<IdentifiablePointer<Arc<dyn Item>>> {
) -> FFIResult<FFIHandle<Arc<dyn Item>>> {
let flags = std::slice::from_raw_parts(flags, flags_length);
let name: StringKey = match CStr::from_ptr(name).to_str() {
Ok(name) => name.into(),
Err(_) => return NativeResult::err(anyhow!("Unable to convert name to string")),
Err(_) => return FFIResult::err(anyhow!("Unable to convert name to string")),
};
let mut flags_set: HashSet<StringKey> = HashSet::with_capacity(flags_length);
for flag in flags {
let flag = match CStr::from_ptr(*flag).to_str() {
Ok(flag) => flag,
Err(_) => return NativeResult::err(anyhow!("Unable to convert flag to string")),
Err(_) => return FFIResult::err(anyhow!("Unable to convert flag to string")),
};
flags_set.insert(flag.into());
}
let item: Arc<dyn Item> = Arc::new(ItemImpl::new(&name, category, battle_category, price, flags_set));
NativeResult::ok(item.into())
}
/// Drops a reference counted item.
#[no_mangle]
unsafe extern "C" fn item_drop(ptr: OwnedPtr<Arc<dyn Item>>) {
drop_in_place(ptr)
FFIResult::ok(FFIHandle::get_handle(item.into()))
}
/// The name of the item.
#[no_mangle]
unsafe extern "C" fn item_name(ptr: ExternPointer<Arc<dyn Item>>) -> NativeResult<OwnedPtr<c_char>> {
let name = ptr.as_ref().name();
unsafe extern "C" fn item_name(ptr: FFIHandle<Arc<dyn Item>>) -> FFIResult<OwnedPtrString> {
let item = ptr.from_ffi_handle();
let name = item.name();
match CString::new(name.str()) {
Ok(name) => NativeResult::ok(name.into_raw()),
Err(_) => NativeResult::err(anyhow!("Unable to convert name `{}` to CString", name.str())),
Ok(name) => FFIResult::ok(name.into_raw()),
Err(_) => FFIResult::err(anyhow!("Unable to convert name `{}` to CString", name.str())),
}
}
ffi_arc_dyn_getter!(Item, category, ItemCategory);
ffi_arc_dyn_getter!(Item, battle_category, BattleItemCategory);
ffi_arc_dyn_getter!(Item, price, i32);
ffi_handle_arc_dyn_getter!(Item, category, ItemCategory);
ffi_handle_arc_dyn_getter!(Item, battle_category, BattleItemCategory);
ffi_handle_arc_dyn_getter!(Item, price, i32);
/// Checks whether the item has a specific flag.
#[no_mangle]
unsafe extern "C" fn item_has_flag(ptr: ExternPointer<Arc<dyn Item>>, flag: *const c_char) -> u8 {
unsafe extern "C" fn item_has_flag(ptr: FFIHandle<Arc<dyn Item>>, flag: *const c_char) -> u8 {
let flag = CStr::from_ptr(flag).into();
u8::from(ptr.as_ref().has_flag(&flag))
u8::from(ptr.from_ffi_handle().has_flag(&flag))
}

View File

@ -1,29 +1,25 @@
use crate::defines::LevelInt;
use crate::ffi::{BorrowedPtr, ExternPointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::{FFIResult, NonOwnedPtrString};
use crate::static_data::{LearnableMoves, LearnableMovesImpl};
use std::ffi::{c_char, CStr};
use std::ptr::drop_in_place;
use std::ffi::CStr;
use std::sync::Arc;
/// Instantiates a new Learnable Moves.
#[no_mangle]
extern "C" fn learnable_moves_new(max_level: LevelInt) -> OwnedPtr<Box<dyn LearnableMoves>> {
Box::into_raw(Box::new(Box::new(LearnableMovesImpl::new(max_level))))
}
/// drops a learnablemoves struct.
#[no_mangle]
unsafe extern "C" fn learnable_moves_drop(ptr: OwnedPtr<Box<dyn LearnableMoves>>) {
drop_in_place(ptr)
extern "C" fn learnable_moves_new(max_level: LevelInt) -> FFIHandle<Arc<dyn LearnableMoves>> {
let p: Arc<dyn LearnableMoves> = Arc::new(LearnableMovesImpl::new(max_level));
FFIHandle::get_handle(p.into())
}
/// Adds a new level move the Pokemon can learn.
#[no_mangle]
unsafe extern "C" fn learnable_moves_add_level_move(
mut ptr: ExternPointer<Box<dyn LearnableMoves>>,
ptr: FFIHandle<Arc<dyn LearnableMoves>>,
level: LevelInt,
move_name: BorrowedPtr<c_char>,
) -> NativeResult<()> {
ptr.as_mut()
move_name: NonOwnedPtrString,
) -> FFIResult<()> {
ptr.from_ffi_handle()
.add_level_move(level, &CStr::from_ptr(move_name).into())
.into()
}

View File

@ -1,30 +1,25 @@
use crate::defines::LevelInt;
use crate::ffi::{BorrowedPtr, ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::{FFIResult, NonOwnedPtrString};
use crate::static_data::{GrowthRate, GrowthRateLibrary, GrowthRateLibraryImpl};
use std::ffi::{c_char, CStr};
use std::ptr::drop_in_place;
use std::ffi::CStr;
use std::sync::Arc;
/// Instantiates a new growth rate library with a capacity
#[no_mangle]
extern "C" fn growth_rate_library_new(capacity: usize) -> IdentifiablePointer<Box<dyn GrowthRateLibrary>> {
let b: Box<dyn GrowthRateLibrary> = Box::new(GrowthRateLibraryImpl::new(capacity));
b.into()
}
/// Drops the growthrate library.
#[no_mangle]
unsafe extern "C" fn growth_rate_library_drop(ptr: OwnedPtr<Box<dyn GrowthRateLibrary>>) {
drop_in_place(ptr)
extern "C" fn growth_rate_library_new(capacity: usize) -> FFIHandle<Arc<dyn GrowthRateLibrary>> {
let b: Arc<dyn GrowthRateLibrary> = Arc::new(GrowthRateLibraryImpl::new(capacity));
FFIHandle::get_handle(b.into())
}
/// Calculates the level for a given growth key name and a certain experience.
#[no_mangle]
unsafe extern "C" fn growth_rate_library_calculate_level(
ptr: ExternPointer<Box<dyn GrowthRateLibrary>>,
growth_rate: BorrowedPtr<c_char>,
ptr: FFIHandle<Arc<dyn GrowthRateLibrary>>,
growth_rate: NonOwnedPtrString,
experience: u32,
) -> NativeResult<LevelInt> {
ptr.as_ref()
) -> FFIResult<LevelInt> {
ptr.from_ffi_handle()
.calculate_level(&CStr::from_ptr(growth_rate).into(), experience)
.into()
}
@ -32,11 +27,11 @@ unsafe extern "C" fn growth_rate_library_calculate_level(
/// Calculates the experience for a given growth key name and a certain level.
#[no_mangle]
unsafe extern "C" fn growth_rate_library_calculate_experience(
ptr: ExternPointer<Box<dyn GrowthRateLibrary>>,
growth_rate: BorrowedPtr<c_char>,
ptr: FFIHandle<Arc<dyn GrowthRateLibrary>>,
growth_rate: NonOwnedPtrString,
level: LevelInt,
) -> NativeResult<u32> {
ptr.as_ref()
) -> FFIResult<u32> {
ptr.from_ffi_handle()
.calculate_experience(&CStr::from_ptr(growth_rate).into(), level)
.into()
}
@ -44,10 +39,10 @@ unsafe extern "C" fn growth_rate_library_calculate_experience(
/// Adds a new growth rate with a name and value.
#[no_mangle]
unsafe extern "C" fn growth_rate_library_add_growth_rate(
mut ptr: ExternPointer<Box<dyn GrowthRateLibrary>>,
name: BorrowedPtr<c_char>,
growth_rate: OwnedPtr<Box<dyn GrowthRate>>,
ptr: FFIHandle<Arc<dyn GrowthRateLibrary>>,
name: NonOwnedPtrString,
growth_rate: FFIHandle<Arc<dyn GrowthRate>>,
) {
ptr.as_mut()
.add_growth_rate(&CStr::from_ptr(name).into(), *Box::from_raw(growth_rate));
ptr.from_ffi_handle()
.add_growth_rate(&CStr::from_ptr(name).into(), growth_rate.from_ffi_handle());
}

View File

@ -1,7 +1,8 @@
use crate::defines::LevelInt;
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::FFIResult;
use crate::static_data::{LibrarySettings, LibrarySettingsImpl};
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Creates a new settings library.
/// - `maximum_level` is the highest level a Pokemon can be.
@ -12,32 +13,26 @@ use std::ptr::drop_in_place;
extern "C" fn library_settings_new(
max_level: LevelInt,
shiny_rate: u32,
) -> NativeResult<IdentifiablePointer<Box<dyn LibrarySettings>>> {
) -> FFIResult<FFIHandle<Arc<dyn LibrarySettings>>> {
match LibrarySettingsImpl::new(max_level, shiny_rate) {
Ok(settings) => {
let b: Box<dyn LibrarySettings> = Box::new(settings);
let p: IdentifiablePointer<Box<dyn LibrarySettings>> = b.into();
p.into()
let b: Arc<dyn LibrarySettings> = Arc::new(settings);
let p: FFIHandle<Arc<dyn LibrarySettings>> = FFIHandle::get_handle(b.into());
FFIResult::ok(p)
}
Err(e) => NativeResult::err(e),
Err(e) => FFIResult::err(e),
}
}
/// Drop a library settings object.
#[no_mangle]
unsafe extern "C" fn library_settings_drop(ptr: OwnedPtr<Box<dyn LibrarySettings>>) {
drop_in_place(ptr)
}
/// The highest level a Pokemon can be.
#[no_mangle]
extern "C" fn library_settings_maximum_level(ptr: ExternPointer<Box<dyn LibrarySettings>>) -> LevelInt {
ptr.as_ref().maximum_level()
extern "C" fn library_settings_maximum_level(ptr: FFIHandle<Arc<dyn LibrarySettings>>) -> LevelInt {
ptr.from_ffi_handle().maximum_level()
}
/// The chance of a Pokemon being shiny, as the denominator of a fraction, where the nominator
/// is 1. For example, if this is 1000, then the chance of a Pokemon being shiny is 1/1000.
#[no_mangle]
extern "C" fn library_settings_shiny_rate(ptr: ExternPointer<Box<dyn LibrarySettings>>) -> u32 {
ptr.as_ref().shiny_rate()
extern "C" fn library_settings_shiny_rate(ptr: FFIHandle<Arc<dyn LibrarySettings>>) -> u32 {
ptr.from_ffi_handle().shiny_rate()
}

View File

@ -9,10 +9,10 @@ mod static_data;
/// The foreign function interface for the type library.
mod type_library;
use crate::ffi::{BorrowedPtr, IdentifiablePointer, OwnedPtr};
use crate::ffi::{FFIHandle, FromFFIHandle};
use crate::ffi::{NonOwnedPtrString, OwnedPtrString};
use crate::static_data::*;
use std::ffi::{c_char, CStr};
use std::ptr::drop_in_place;
use std::ffi::CStr;
use std::sync::Arc;
/// Generates foreign function interfaces for a DataLibrary trait implementation.
@ -20,42 +20,37 @@ macro_rules! library_interface {
($library_type_name:ident, $library_type:ty, $return_type:ty) => {
paste::paste! {
#[no_mangle]
extern "C" fn [< $library_type_name:snake _new >](capacity: usize) -> IdentifiablePointer<$library_type> {
let value: $library_type = Box::new([<$library_type_name Impl>]::new(capacity));
value.into()
extern "C" fn [< $library_type_name:snake _new >](capacity: usize) -> FFIHandle<$library_type> {
let value: $library_type = Arc::new([<$library_type_name Impl>]::new(capacity));
FFIHandle::get_handle(value.into())
}
#[no_mangle]
unsafe extern "C" fn [< $library_type_name:snake _drop >](ptr: OwnedPtr<$library_type>) {
drop_in_place(ptr);
unsafe extern "C" fn [< $library_type_name:snake _add >](ptr: FFIHandle<$library_type>, key: NonOwnedPtrString, value: FFIHandle<Arc<$return_type>>) {
let lib = ptr.from_ffi_handle();
lib.add(&CStr::from_ptr(key).into(), value.from_ffi_handle());
}
#[no_mangle]
unsafe extern "C" fn [< $library_type_name:snake _add >](ptr: OwnedPtr<$library_type>, key: BorrowedPtr<c_char>, value: OwnedPtr<Arc<$return_type>>) {
let lib = ptr.as_mut().unwrap();
lib.add(&CStr::from_ptr(key).into(), *Box::from_raw(value));
}
#[no_mangle]
unsafe extern "C" fn [< $library_type_name:snake _remove >](ptr: OwnedPtr<$library_type>, key: BorrowedPtr<c_char>) {
let lib = ptr.as_mut().unwrap();
unsafe extern "C" fn [< $library_type_name:snake _remove >](ptr: FFIHandle<$library_type>, key: NonOwnedPtrString) {
let lib = ptr.from_ffi_handle();
lib.remove(&CStr::from_ptr(key).into());
}
#[no_mangle]
unsafe extern "C" fn [< $library_type_name:snake _get >](ptr: OwnedPtr<$library_type>, key: BorrowedPtr<c_char>) -> IdentifiablePointer<Arc<$return_type>> {
let lib = ptr.as_mut().unwrap();
unsafe extern "C" fn [< $library_type_name:snake _get >](ptr: FFIHandle<$library_type>, key: NonOwnedPtrString) -> FFIHandle<Arc<$return_type>> {
let lib = ptr.from_ffi_handle();
let v = lib.get(&CStr::from_ptr(key).into());
if let Some(value) = v {
value.clone().into()
FFIHandle::get_handle(value.clone().into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
#[no_mangle]
unsafe extern "C" fn [< $library_type_name:snake _get_key_by_index >](ptr: OwnedPtr<$library_type>, index: usize) -> OwnedPtr<c_char> {
let lib = ptr.as_mut().unwrap();
unsafe extern "C" fn [< $library_type_name:snake _get_key_by_index >](ptr: FFIHandle<$library_type>, index: usize) -> OwnedPtrString {
let lib = ptr.from_ffi_handle();
let v = lib.get_key_by_index(index);
if let Some(value) = v {
std::ffi::CString::new(value.str()).unwrap().into_raw()
@ -65,15 +60,15 @@ macro_rules! library_interface {
}
#[no_mangle]
unsafe extern "C" fn [< $library_type_name:snake _len >](ptr: OwnedPtr<$library_type>) -> usize {
let lib = ptr.as_mut().unwrap();
unsafe extern "C" fn [< $library_type_name:snake _len >](ptr: FFIHandle<$library_type>) -> usize {
let lib = ptr.from_ffi_handle();
lib.len()
}
}
};
}
library_interface!(SpeciesLibrary, Box<dyn SpeciesLibrary>, dyn Species);
library_interface!(MoveLibrary, Box<dyn MoveLibrary>, dyn MoveData);
library_interface!(AbilityLibrary, Box<dyn AbilityLibrary>, dyn Ability);
library_interface!(ItemLibrary, Box<dyn ItemLibrary>, dyn Item);
library_interface!(SpeciesLibrary, Arc<dyn SpeciesLibrary>, dyn Species);
library_interface!(MoveLibrary, Arc<dyn MoveLibrary>, dyn MoveData);
library_interface!(AbilityLibrary, Arc<dyn AbilityLibrary>, dyn Ability);
library_interface!(ItemLibrary, Arc<dyn ItemLibrary>, dyn Item);

View File

@ -1,84 +1,79 @@
use crate::ffi::{BorrowedPtr, ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::{FFIResult, NonOwnedPtrString, OwnedPtrString};
use crate::static_data::{Nature, NatureLibrary, NatureLibraryImpl};
use crate::{PkmnError, Random};
use anyhow::anyhow;
use std::ffi::{c_char, CStr, CString};
use std::ptr::drop_in_place;
use std::ffi::{CStr, CString};
use std::sync::Arc;
/// Creates a new nature library with a given capacity.
#[no_mangle]
extern "C" fn nature_library_new(capacity: usize) -> IdentifiablePointer<Box<dyn NatureLibrary>> {
let b: Box<dyn NatureLibrary> = Box::new(NatureLibraryImpl::new(capacity));
b.into()
}
/// Drop a nature library.
#[no_mangle]
unsafe extern "C" fn nature_library_drop(ptr: OwnedPtr<Box<dyn NatureLibrary>>) {
drop_in_place(ptr);
extern "C" fn nature_library_new(capacity: usize) -> FFIHandle<Arc<dyn NatureLibrary>> {
let b: Arc<dyn NatureLibrary> = Arc::new(NatureLibraryImpl::new(capacity));
FFIHandle::get_handle(b.into())
}
/// Adds a new nature with name to the library.
#[no_mangle]
unsafe extern "C" fn nature_library_load_nature(
mut ptr: ExternPointer<Box<dyn NatureLibrary>>,
name: BorrowedPtr<c_char>,
nature: OwnedPtr<Arc<dyn Nature>>,
) -> NativeResult<()> {
let nature = nature.as_ref().ok_or(PkmnError::NullReference);
ptr: FFIHandle<Arc<dyn NatureLibrary>>,
name: NonOwnedPtrString,
nature: FFIHandle<Arc<dyn Nature>>,
) -> FFIResult<()> {
let nature = nature.from_ffi_handle_opt().ok_or(PkmnError::NullReference);
match nature {
Ok(nature) => {
ptr.as_mut().load_nature(CStr::from_ptr(name).into(), nature.clone());
NativeResult::ok(())
ptr.from_ffi_handle()
.load_nature(CStr::from_ptr(name).into(), nature.clone());
FFIResult::ok(())
}
Err(e) => NativeResult::err(e.into()),
Err(e) => FFIResult::err(e.into()),
}
}
/// Gets a nature by name.
#[no_mangle]
unsafe extern "C" fn nature_library_get_nature(
ptr: ExternPointer<Box<dyn NatureLibrary>>,
name: BorrowedPtr<c_char>,
) -> IdentifiablePointer<Arc<dyn Nature>> {
if let Some(nature) = ptr.as_ref().get_nature(&CStr::from_ptr(name).into()) {
nature.into()
ptr: FFIHandle<Arc<dyn NatureLibrary>>,
name: NonOwnedPtrString,
) -> FFIHandle<Arc<dyn Nature>> {
if let Some(nature) = ptr.from_ffi_handle().get_nature(&CStr::from_ptr(name).into()) {
FFIHandle::get_handle(nature.into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
/// Gets a random nature.
#[no_mangle]
unsafe extern "C" fn nature_library_get_random_nature(
ptr: ExternPointer<Box<dyn NatureLibrary>>,
ptr: FFIHandle<Arc<dyn NatureLibrary>>,
seed: u64,
) -> NativeResult<IdentifiablePointer<Arc<dyn Nature>>> {
) -> FFIResult<FFIHandle<Arc<dyn Nature>>> {
let mut rand = Random::new(seed as u128);
match ptr.as_ref().get_random_nature(&mut rand) {
Ok(nature) => NativeResult::ok(nature.into()),
Err(e) => NativeResult::err(e),
match ptr.from_ffi_handle().get_random_nature(&mut rand) {
Ok(nature) => FFIResult::ok(FFIHandle::get_handle(nature.into())),
Err(e) => FFIResult::err(e),
}
}
/// Finds a nature name by nature.
#[no_mangle]
unsafe extern "C" fn nature_library_get_nature_name(
ptr: ExternPointer<Box<dyn NatureLibrary>>,
nature: BorrowedPtr<Arc<dyn Nature>>,
) -> NativeResult<OwnedPtr<c_char>> {
match nature.as_ref() {
ptr: FFIHandle<Arc<dyn NatureLibrary>>,
nature: FFIHandle<Arc<dyn Nature>>,
) -> FFIResult<OwnedPtrString> {
match nature.from_ffi_handle_opt() {
Some(nature) => {
let name = ptr.as_ref().get_nature_name(nature);
let name = ptr.from_ffi_handle().get_nature_name(&nature);
match name {
Ok(name) => match CString::new(name.str()) {
Ok(cstr) => NativeResult::ok(cstr.into_raw()),
Err(_) => NativeResult::err(anyhow!("Failed to convert nature name to C string")),
Ok(cstr) => FFIResult::ok(cstr.into_raw()),
Err(_) => FFIResult::err(anyhow!("Failed to convert nature name to C string")),
},
Err(e) => NativeResult::err(e),
Err(e) => FFIResult::err(e),
}
}
None => NativeResult::err(PkmnError::NullReference.into()),
None => FFIResult::err(PkmnError::NullReference.into()),
}
}

View File

@ -1,102 +1,81 @@
use crate::ffi::{ExternPointer, IdentifiablePointer, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::static_data::{
AbilityLibrary, GrowthRateLibrary, ItemLibrary, LibrarySettings, MoveLibrary, NatureLibrary, SpeciesLibrary,
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<Arc<dyn LibrarySettings>>,
species: OwnedPtr<Arc<dyn SpeciesLibrary>>,
moves: OwnedPtr<Arc<dyn MoveLibrary>>,
items: OwnedPtr<Arc<dyn ItemLibrary>>,
growth_rates: OwnedPtr<Arc<dyn GrowthRateLibrary>>,
types: OwnedPtr<Arc<dyn TypeLibrary>>,
natures: OwnedPtr<Arc<dyn NatureLibrary>>,
abilities: OwnedPtr<Arc<dyn AbilityLibrary>>,
) -> IdentifiablePointer<Arc<dyn StaticData>> {
settings: FFIHandle<Arc<dyn LibrarySettings>>,
species: FFIHandle<Arc<dyn SpeciesLibrary>>,
moves: FFIHandle<Arc<dyn MoveLibrary>>,
items: FFIHandle<Arc<dyn ItemLibrary>>,
growth_rates: FFIHandle<Arc<dyn GrowthRateLibrary>>,
types: FFIHandle<Arc<dyn TypeLibrary>>,
natures: FFIHandle<Arc<dyn NatureLibrary>>,
abilities: FFIHandle<Arc<dyn AbilityLibrary>>,
) -> FFIHandle<Arc<dyn StaticData>> {
let b: Arc<dyn StaticData> = Arc::new(StaticDataImpl::new(
settings.read(),
species.read(),
moves.read(),
items.read(),
growth_rates.read(),
types.read(),
natures.read(),
abilities.read(),
settings.from_ffi_handle(),
species.from_ffi_handle(),
moves.from_ffi_handle(),
items.from_ffi_handle(),
growth_rates.from_ffi_handle(),
types.from_ffi_handle(),
natures.from_ffi_handle(),
abilities.from_ffi_handle(),
));
b.into()
}
/// Drop a static data.
#[no_mangle]
unsafe extern "C" fn static_data_drop(ptr: OwnedPtr<Box<dyn StaticData>>) {
drop_in_place(ptr)
FFIHandle::get_handle(b.into())
}
/// Several misc settings for the library.
#[no_mangle]
unsafe extern "C" fn static_data_settings(
mut data: ExternPointer<Arc<dyn StaticData>>,
) -> IdentifiablePointer<Arc<dyn LibrarySettings>> {
data.as_mut().settings().clone().into()
unsafe extern "C" fn static_data_settings(data: FFIHandle<Arc<dyn StaticData>>) -> FFIHandle<Arc<dyn LibrarySettings>> {
FFIHandle::get_handle(data.from_ffi_handle().settings().clone().into())
}
/// All data for Pokemon species.
#[no_mangle]
unsafe extern "C" fn static_data_species(
mut data: ExternPointer<Arc<dyn StaticData>>,
) -> IdentifiablePointer<Arc<dyn SpeciesLibrary>> {
data.as_mut().species().clone().into()
unsafe extern "C" fn static_data_species(data: FFIHandle<Arc<dyn StaticData>>) -> FFIHandle<Arc<dyn SpeciesLibrary>> {
FFIHandle::get_handle(data.from_ffi_handle().species().clone().into())
}
/// All data for the moves.
#[no_mangle]
unsafe extern "C" fn static_data_moves(
mut data: ExternPointer<Arc<dyn StaticData>>,
) -> IdentifiablePointer<Arc<dyn MoveLibrary>> {
data.as_mut().moves().clone().into()
unsafe extern "C" fn static_data_moves(data: FFIHandle<Arc<dyn StaticData>>) -> FFIHandle<Arc<dyn MoveLibrary>> {
FFIHandle::get_handle(data.from_ffi_handle().moves().clone().into())
}
/// All data for the items.
#[no_mangle]
unsafe extern "C" fn static_data_items(
mut data: ExternPointer<Arc<dyn StaticData>>,
) -> IdentifiablePointer<Arc<dyn ItemLibrary>> {
(data.as_mut().items()).clone().into()
unsafe extern "C" fn static_data_items(data: FFIHandle<Arc<dyn StaticData>>) -> FFIHandle<Arc<dyn ItemLibrary>> {
FFIHandle::get_handle((data.from_ffi_handle().items()).clone().into())
}
/// All data for growth rates.
#[no_mangle]
unsafe extern "C" fn static_data_growth_rates(
mut data: ExternPointer<Arc<dyn StaticData>>,
) -> IdentifiablePointer<Arc<dyn GrowthRateLibrary>> {
data.as_mut().growth_rates().clone().into()
data: FFIHandle<Arc<dyn StaticData>>,
) -> FFIHandle<Arc<dyn GrowthRateLibrary>> {
FFIHandle::get_handle(data.from_ffi_handle().growth_rates().clone().into())
}
/// All data related to types and type effectiveness.
#[no_mangle]
unsafe extern "C" fn static_data_types(
mut data: ExternPointer<Arc<dyn StaticData>>,
) -> IdentifiablePointer<Arc<dyn TypeLibrary>> {
data.as_mut().types().clone().into()
unsafe extern "C" fn static_data_types(data: FFIHandle<Arc<dyn StaticData>>) -> FFIHandle<Arc<dyn TypeLibrary>> {
FFIHandle::get_handle(data.from_ffi_handle().types().clone().into())
}
/// All data related to natures.
#[no_mangle]
unsafe extern "C" fn static_data_natures(
data: ExternPointer<Arc<dyn StaticData>>,
) -> IdentifiablePointer<Arc<dyn NatureLibrary>> {
data.as_ref().natures().clone().into()
unsafe extern "C" fn static_data_natures(data: FFIHandle<Arc<dyn StaticData>>) -> FFIHandle<Arc<dyn NatureLibrary>> {
FFIHandle::get_handle(data.from_ffi_handle().natures().clone().into())
}
/// All data related to abilities.
#[no_mangle]
unsafe extern "C" fn static_data_abilities(
mut data: ExternPointer<Arc<dyn StaticData>>,
) -> IdentifiablePointer<Arc<dyn AbilityLibrary>> {
(data.as_mut().abilities()).clone().into()
unsafe extern "C" fn static_data_abilities(data: FFIHandle<Arc<dyn StaticData>>) -> FFIHandle<Arc<dyn AbilityLibrary>> {
FFIHandle::get_handle((data.from_ffi_handle().abilities()).clone().into())
}

View File

@ -1,29 +1,24 @@
use crate::ffi::{BorrowedPtr, ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::{FFIResult, NonOwnedPtrString};
use crate::static_data::{TypeIdentifier, TypeLibrary, TypeLibraryImpl};
use std::ffi::{c_char, CStr, CString};
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Instantiates a new type library with a specific capacity.
#[no_mangle]
extern "C" fn type_library_new(capacity: usize) -> IdentifiablePointer<Box<dyn TypeLibrary>> {
let b: Box<dyn TypeLibrary> = Box::new(TypeLibraryImpl::new(capacity));
b.into()
}
/// Drops a type library.
#[no_mangle]
unsafe extern "C" fn type_library_drop(ptr: OwnedPtr<Box<dyn TypeLibrary>>) {
drop_in_place(ptr);
extern "C" fn type_library_new(capacity: usize) -> FFIHandle<Arc<dyn TypeLibrary>> {
let b: Arc<dyn TypeLibrary> = Arc::new(TypeLibraryImpl::new(capacity));
FFIHandle::get_handle(b.into())
}
/// Gets the type identifier for a type with a name.
#[no_mangle]
unsafe extern "C" fn type_library_get_type_id(
ptr: ExternPointer<Box<dyn TypeLibrary>>,
key: BorrowedPtr<c_char>,
ptr: FFIHandle<Arc<dyn TypeLibrary>>,
key: NonOwnedPtrString,
found: *mut bool,
) -> TypeIdentifier {
if let Some(v) = ptr.as_ref().get_type_id(&CStr::from_ptr(key).into()) {
if let Some(v) = ptr.from_ffi_handle().get_type_id(&CStr::from_ptr(key).into()) {
*found = true;
v
} else {
@ -35,31 +30,33 @@ unsafe extern "C" fn type_library_get_type_id(
/// Gets the type name from the type identifier.
#[no_mangle]
unsafe extern "C" fn type_library_get_type_name(
ptr: ExternPointer<Box<dyn TypeLibrary>>,
ptr: FFIHandle<Arc<dyn TypeLibrary>>,
type_id: TypeIdentifier,
found: *mut bool,
) -> NativeResult<*mut c_char> {
if let Some(v) = ptr.as_ref().get_type_name(type_id) {
) -> FFIResult<*mut c_char> {
if let Some(v) = ptr.from_ffi_handle().get_type_name(type_id) {
*found = true;
match CString::new(v.str()) {
Ok(v) => NativeResult::ok(v.into_raw()),
Err(e) => NativeResult::err(e.into()),
Ok(v) => FFIResult::ok(v.into_raw()),
Err(e) => FFIResult::err(e.into()),
}
} else {
*found = false;
NativeResult::ok(std::ptr::null_mut())
FFIResult::ok(std::ptr::null_mut())
}
}
/// Gets the effectiveness for a single attacking type against a single defending type.
#[no_mangle]
extern "C" fn type_library_get_single_effectiveness(
ptr: ExternPointer<Box<dyn TypeLibrary>>,
ptr: FFIHandle<Arc<dyn TypeLibrary>>,
attacking: TypeIdentifier,
defending: TypeIdentifier,
) -> NativeResult<f32> {
ptr.as_ref().get_single_effectiveness(attacking, defending).into()
) -> FFIResult<f32> {
ptr.from_ffi_handle()
.get_single_effectiveness(attacking, defending)
.into()
}
/// Gets the effectiveness for a single attacking type against an amount of defending types.
@ -67,33 +64,33 @@ extern "C" fn type_library_get_single_effectiveness(
/// and multiplying the results with each other.
#[no_mangle]
unsafe extern "C" fn type_library_get_effectiveness(
ptr: ExternPointer<Box<dyn TypeLibrary>>,
ptr: FFIHandle<Arc<dyn TypeLibrary>>,
attacking: TypeIdentifier,
defending: OwnedPtr<TypeIdentifier>,
defending: *const TypeIdentifier,
defending_length: usize,
) -> NativeResult<f32> {
) -> FFIResult<f32> {
let v = std::slice::from_raw_parts(defending, defending_length);
ptr.as_ref().get_effectiveness(attacking, v).into()
ptr.from_ffi_handle().get_effectiveness(attacking, v).into()
}
/// Registers a new type in the library.
#[no_mangle]
unsafe extern "C" fn type_library_register_type(
mut ptr: ExternPointer<Box<dyn TypeLibrary>>,
name: BorrowedPtr<c_char>,
ptr: FFIHandle<Arc<dyn TypeLibrary>>,
name: NonOwnedPtrString,
) -> TypeIdentifier {
ptr.as_mut().register_type(&CStr::from_ptr(name).into())
ptr.from_ffi_handle().register_type(&CStr::from_ptr(name).into())
}
/// Sets the effectiveness for an attacking type against a defending type.
#[no_mangle]
unsafe extern "C" fn type_library_set_effectiveness(
mut ptr: ExternPointer<Box<dyn TypeLibrary>>,
ptr: FFIHandle<Arc<dyn TypeLibrary>>,
attacking: TypeIdentifier,
defending: TypeIdentifier,
effectiveness: f32,
) -> NativeResult<()> {
ptr.as_mut()
) -> FFIResult<()> {
ptr.from_ffi_handle()
.set_effectiveness(attacking, defending, effectiveness)
.into()
}

View File

@ -1,9 +1,11 @@
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FFIObject, FromFFIHandle};
use crate::ffi::{FFIResult, OwnedPtrString};
use crate::static_data::EffectParameter;
use crate::{PkmnError, StringKey};
use anyhow::anyhow;
use std::ffi::{c_char, CStr, CString};
use std::ptr::drop_in_place;
use std::ops::Deref;
use std::sync::Arc;
/// The Foreign Function Interface for abilities
mod ability;
@ -28,94 +30,88 @@ mod statistic_set;
/// Instantiates an effect parameter with a boolean.
#[no_mangle]
extern "C" fn effect_parameter_new_bool(value: u8) -> IdentifiablePointer<EffectParameter> {
Box::<EffectParameter>::new((value == 1).into()).into()
extern "C" fn effect_parameter_new_bool(value: u8) -> FFIHandle<Arc<EffectParameter>> {
FFIHandle::get_handle(FFIObject::EffectParameter(Arc::new(EffectParameter::from(value == 1))))
}
/// Instantiates an effect parameter with an integer.
#[no_mangle]
extern "C" fn effect_parameter_new_int(value: i64) -> IdentifiablePointer<EffectParameter> {
Box::<EffectParameter>::new(value.into()).into()
extern "C" fn effect_parameter_new_int(value: i64) -> FFIHandle<Arc<EffectParameter>> {
FFIHandle::get_handle(FFIObject::EffectParameter(Arc::new(EffectParameter::from(value))))
}
/// Instantiates an effect parameter with a float.
#[no_mangle]
extern "C" fn effect_parameter_new_float(value: f32) -> IdentifiablePointer<EffectParameter> {
Box::<EffectParameter>::new(value.into()).into()
extern "C" fn effect_parameter_new_float(value: f32) -> FFIHandle<Arc<EffectParameter>> {
FFIHandle::get_handle(FFIObject::EffectParameter(Arc::new(EffectParameter::from(value))))
}
/// Instantiates an effect parameter with a string.
#[no_mangle]
unsafe extern "C" fn effect_parameter_new_string(
value: *const c_char,
) -> NativeResult<IdentifiablePointer<EffectParameter>> {
unsafe extern "C" fn effect_parameter_new_string(value: *const c_char) -> FFIResult<FFIHandle<Arc<EffectParameter>>> {
let sk: StringKey = match CStr::from_ptr(value).to_str() {
Ok(sk) => sk.into(),
Err(_) => return NativeResult::err(PkmnError::InvalidCString.into()),
Err(_) => return FFIResult::err(PkmnError::InvalidCString.into()),
};
NativeResult::ok(Box::<EffectParameter>::new(sk.into()).into())
}
/// Drop an effect parameter.
#[no_mangle]
unsafe extern "C" fn effect_parameter_drop(ptr: OwnedPtr<EffectParameter>) {
drop_in_place(ptr)
FFIResult::ok(FFIHandle::get_handle(FFIObject::EffectParameter(Arc::new(
EffectParameter::from(sk),
))))
}
/// Get the type of an effect parameter.
#[no_mangle]
extern "C" fn effect_parameter_get_type(ptr: ExternPointer<EffectParameter>) -> u8 {
match ptr.as_ref() {
EffectParameter::Bool(_, _) => 0,
EffectParameter::Int(_, _) => 1,
EffectParameter::Float(_, _) => 2,
EffectParameter::String(_, _) => 3,
extern "C" fn effect_parameter_get_type(ptr: FFIHandle<Arc<EffectParameter>>) -> u8 {
match ptr.from_ffi_handle().deref() {
EffectParameter::Bool(_) => 0,
EffectParameter::Int(_) => 1,
EffectParameter::Float(_) => 2,
EffectParameter::String(_) => 3,
}
}
/// Get the boolean contained in the effect parameter, panics if the effect parameter is not a bool.
#[no_mangle]
extern "C" fn effect_parameter_get_as_bool(ptr: ExternPointer<EffectParameter>) -> NativeResult<u8> {
let p = ptr.as_ref();
if let EffectParameter::Bool(_, b) = p {
NativeResult::ok(u8::from(*b))
extern "C" fn effect_parameter_get_as_bool(ptr: FFIHandle<Arc<EffectParameter>>) -> FFIResult<u8> {
let p = ptr.from_ffi_handle();
if let EffectParameter::Bool(b) = p.deref() {
FFIResult::ok(u8::from(*b))
} else {
NativeResult::err(anyhow!("Unexpected effect parameter. Expected bool, was: {}", p))
FFIResult::err(anyhow!("Unexpected effect parameter. Expected bool, was: {}", p))
}
}
/// Get the int contained in the effect parameter, panics if the effect parameter is not a int.
#[no_mangle]
extern "C" fn effect_parameter_get_as_int(ptr: ExternPointer<EffectParameter>) -> NativeResult<i64> {
let p = ptr.as_ref();
if let EffectParameter::Int(_, b) = p {
NativeResult::ok(*b)
extern "C" fn effect_parameter_get_as_int(ptr: FFIHandle<Arc<EffectParameter>>) -> FFIResult<i64> {
let p = ptr.from_ffi_handle();
if let EffectParameter::Int(b) = p.deref() {
FFIResult::ok(*b)
} else {
NativeResult::err(anyhow!("Unexpected effect parameter. Expected int, was: {}", p))
FFIResult::err(anyhow!("Unexpected effect parameter. Expected int, was: {}", p))
}
}
/// Get the float contained in the effect parameter, panics if the effect parameter is not a float.
#[no_mangle]
extern "C" fn effect_parameter_get_as_float(ptr: ExternPointer<EffectParameter>) -> NativeResult<f32> {
let p = ptr.as_ref();
if let EffectParameter::Float(_, b) = p {
NativeResult::ok(*b)
extern "C" fn effect_parameter_get_as_float(ptr: FFIHandle<Arc<EffectParameter>>) -> FFIResult<f32> {
let p = ptr.from_ffi_handle();
if let EffectParameter::Float(b) = p.deref() {
FFIResult::ok(*b)
} else {
NativeResult::err(anyhow!("Unexpected effect parameter. Expected float, was: {}", p))
FFIResult::err(anyhow!("Unexpected effect parameter. Expected float, was: {}", p))
}
}
/// Get the string contained in the effect parameter, panics if the effect parameter is not a string.
#[no_mangle]
extern "C" fn effect_parameter_get_as_string(ptr: ExternPointer<EffectParameter>) -> NativeResult<OwnedPtr<c_char>> {
let p = ptr.as_ref();
if let EffectParameter::String(_, b) = p {
extern "C" fn effect_parameter_get_as_string(ptr: FFIHandle<Arc<EffectParameter>>) -> FFIResult<OwnedPtrString> {
let p = ptr.from_ffi_handle();
if let EffectParameter::String(b) = p.deref() {
match CString::new(b.str().to_string()) {
Ok(cstr) => NativeResult::ok(cstr.into_raw()),
Err(_) => NativeResult::err(PkmnError::InvalidCString.into()),
Ok(cstr) => FFIResult::ok(cstr.into_raw()),
Err(_) => FFIResult::err(PkmnError::InvalidCString.into()),
}
} else {
NativeResult::err(anyhow!("Unexpected effect parameter. Expected string, was: {}", p))
FFIResult::err(anyhow!("Unexpected effect parameter. Expected string, was: {}", p))
}
}

View File

@ -1,4 +1,5 @@
use crate::ffi::{ffi_arc_dyn_getter, BorrowedPtr, ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::{ffi_handle_arc_dyn_getter, ExternPointer, FFIResult, NonOwnedPtrString, OwnedPtrString};
use crate::static_data::{
EffectParameter, MoveCategory, MoveData, MoveDataImpl, MoveTarget, SecondaryEffect, SecondaryEffectImpl,
TypeIdentifier,
@ -7,7 +8,6 @@ use crate::StringKey;
use anyhow::anyhow;
use hashbrown::HashSet;
use std::ffi::{c_char, CStr, CString};
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Instantiates a new move.
@ -21,27 +21,27 @@ unsafe extern "C" fn move_data_new(
base_usages: u8,
target: MoveTarget,
priority: i8,
secondary_effect: *mut Box<dyn SecondaryEffect>,
secondary_effect: FFIHandle<Arc<dyn SecondaryEffect>>,
flags: *const *const c_char,
flags_length: usize,
) -> NativeResult<IdentifiablePointer<Arc<dyn MoveData>>> {
) -> FFIResult<FFIHandle<Arc<dyn MoveData>>> {
let flags = std::slice::from_raw_parts(flags, flags_length);
let name: StringKey = match CStr::from_ptr(name).to_str() {
Ok(name) => name.into(),
Err(_) => return NativeResult::err_from_str("Unable to convert name to string"),
Err(_) => return FFIResult::err_from_str("Unable to convert name to string"),
};
let mut flags_set: HashSet<StringKey> = HashSet::with_capacity(flags_length);
for flag in flags {
let flag = match CStr::from_ptr(*flag).to_str() {
Ok(flag) => flag,
Err(_) => return NativeResult::err_from_str("Unable to convert flag to string"),
Err(_) => return FFIResult::err_from_str("Unable to convert flag to string"),
};
flags_set.insert(flag.into());
}
let secondary_effect = if secondary_effect.is_null() {
let secondary_effect = if secondary_effect.is_none() {
None
} else {
Some(*Box::from_raw(secondary_effect))
Some(secondary_effect.from_ffi_handle())
};
let a: Arc<dyn MoveData> = Arc::new(MoveDataImpl::new(
&name,
@ -55,39 +55,37 @@ unsafe extern "C" fn move_data_new(
secondary_effect,
flags_set,
));
NativeResult::ok(a.into())
}
/// Drops a reference counted move.
#[no_mangle]
unsafe extern "C" fn move_data_drop(ptr: OwnedPtr<Arc<dyn MoveData>>) {
drop_in_place(ptr)
FFIResult::ok(FFIHandle::get_handle(a.into()))
}
/// The name of the move.
#[no_mangle]
unsafe extern "C" fn move_data_name(ptr: ExternPointer<Arc<dyn MoveData>>) -> NativeResult<OwnedPtr<c_char>> {
let name = ptr.as_ref().name();
unsafe extern "C" fn move_data_name(ptr: FFIHandle<Arc<dyn MoveData>>) -> FFIResult<OwnedPtrString> {
let move_data = ptr.from_ffi_handle();
let name = move_data.name();
match CString::new(name.str()) {
Ok(name) => NativeResult::ok(name.into_raw()),
Err(_) => NativeResult::err_from_str("Unable to convert name to string"),
Ok(name) => FFIResult::ok(name.into_raw()),
Err(_) => FFIResult::err_from_str("Unable to convert name to string"),
}
}
ffi_arc_dyn_getter!(MoveData, move_type, TypeIdentifier);
ffi_arc_dyn_getter!(MoveData, category, MoveCategory);
ffi_arc_dyn_getter!(MoveData, base_power, u8);
ffi_arc_dyn_getter!(MoveData, accuracy, u8);
ffi_arc_dyn_getter!(MoveData, base_usages, u8);
ffi_arc_dyn_getter!(MoveData, target, MoveTarget);
ffi_arc_dyn_getter!(MoveData, priority, i8);
ffi_handle_arc_dyn_getter!(MoveData, move_type, TypeIdentifier);
ffi_handle_arc_dyn_getter!(MoveData, category, MoveCategory);
ffi_handle_arc_dyn_getter!(MoveData, base_power, u8);
ffi_handle_arc_dyn_getter!(MoveData, accuracy, u8);
ffi_handle_arc_dyn_getter!(MoveData, base_usages, u8);
ffi_handle_arc_dyn_getter!(MoveData, target, MoveTarget);
ffi_handle_arc_dyn_getter!(MoveData, priority, i8);
/// The optional secondary effect the move has.
#[no_mangle]
unsafe extern "C" fn move_data_secondary_effect(
ptr: ExternPointer<Arc<dyn MoveData>>,
) -> IdentifiablePointer<Box<dyn SecondaryEffect>> {
ptr.as_ref().secondary_effect().into()
ptr: FFIHandle<Arc<dyn MoveData>>,
) -> FFIHandle<Arc<dyn SecondaryEffect>> {
match ptr.from_ffi_handle().secondary_effect() {
Some(secondary_effect) => FFIHandle::get_handle(secondary_effect.clone().into()),
None => FFIHandle::none(),
}
}
/// Arbitrary flags that can be applied to the move.
@ -101,28 +99,22 @@ unsafe extern "C" fn move_data_has_flag(ptr: ExternPointer<Arc<dyn MoveData>>, f
#[no_mangle]
unsafe extern "C" fn secondary_effect_new(
chance: f32,
effect_name: BorrowedPtr<c_char>,
parameters: *mut OwnedPtr<Arc<EffectParameter>>,
effect_name: NonOwnedPtrString,
parameters: *mut FFIHandle<Arc<EffectParameter>>,
parameters_length: usize,
) -> IdentifiablePointer<Box<dyn SecondaryEffect>> {
) -> FFIHandle<Box<dyn SecondaryEffect>> {
let parameter_slice = std::slice::from_raw_parts(parameters, parameters_length);
let mut parameters = Vec::with_capacity(parameters_length);
for parameter in parameter_slice {
parameters.push(*Box::from_raw(*parameter))
parameters.push(parameter.from_ffi_handle())
}
let b: Box<dyn SecondaryEffect> = Box::new(SecondaryEffectImpl::new(
let b: Arc<dyn SecondaryEffect> = Arc::new(SecondaryEffectImpl::new(
chance,
CStr::from_ptr(effect_name).into(),
parameters,
));
b.into()
}
/// Drop a secondary effect.
#[no_mangle]
unsafe extern "C" fn secondary_effect_drop(ptr: OwnedPtr<Box<dyn SecondaryEffect>>) {
drop_in_place(ptr)
FFIHandle::get_handle(b.into())
}
/// The chance the effect triggers.
@ -135,10 +127,10 @@ unsafe extern "C" fn secondary_effect_chance(ptr: ExternPointer<Box<dyn Secondar
#[no_mangle]
unsafe extern "C" fn secondary_effect_effect_name(
ptr: ExternPointer<Box<dyn SecondaryEffect>>,
) -> NativeResult<OwnedPtr<c_char>> {
) -> FFIResult<OwnedPtrString> {
match CString::new(ptr.as_ref().effect_name().str()) {
Ok(name) => NativeResult::ok(name.into_raw()),
Err(_) => NativeResult::err(anyhow!(
Ok(name) => FFIResult::ok(name.into_raw()),
Err(_) => FFIResult::err(anyhow!(
"Unable to convert effect name '{}' to CString",
ptr.as_ref().effect_name()
)),
@ -156,10 +148,10 @@ unsafe extern "C" fn secondary_effect_parameter_length(ptr: ExternPointer<Box<dy
unsafe extern "C" fn secondary_effect_parameter_get(
ptr: ExternPointer<Box<dyn SecondaryEffect>>,
index: usize,
) -> IdentifiablePointer<Arc<EffectParameter>> {
) -> FFIHandle<Arc<EffectParameter>> {
if let Some(v) = ptr.as_ref().parameters().get(index) {
v.clone().into()
FFIHandle::get_handle(v.clone().into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}

View File

@ -1,6 +1,5 @@
use crate::ffi::{ExternPointer, IdentifiablePointer, OwnedPtr};
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::static_data::{Nature, NatureImpl, Statistic};
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Instantiates a new statistic.
@ -10,32 +9,26 @@ extern "C" fn nature_new(
decrease_stat: Statistic,
increase_modifier: f32,
decrease_modifier: f32,
) -> IdentifiablePointer<Arc<dyn Nature>> {
) -> FFIHandle<Arc<dyn Nature>> {
let arc: Arc<dyn Nature> = NatureImpl::new(increase_stat, decrease_stat, increase_modifier, decrease_modifier);
arc.into()
}
/// Reduce the reference count for a nature.
#[no_mangle]
unsafe extern "C" fn nature_drop(ptr: OwnedPtr<Arc<dyn Nature>>) {
drop_in_place(ptr)
FFIHandle::get_handle(arc.into())
}
/// The stat that should receive the increased modifier.
#[no_mangle]
extern "C" fn nature_increased_stat(ptr: ExternPointer<Arc<dyn Nature>>) -> Statistic {
ptr.as_ref().increased_stat()
extern "C" fn nature_increased_stat(ptr: FFIHandle<Arc<dyn Nature>>) -> Statistic {
ptr.from_ffi_handle().increased_stat()
}
/// The stat that should receive the decreased modifier.
#[no_mangle]
extern "C" fn nature_decreased_stat(ptr: ExternPointer<Arc<dyn Nature>>) -> Statistic {
ptr.as_ref().decreased_stat()
extern "C" fn nature_decreased_stat(ptr: FFIHandle<Arc<dyn Nature>>) -> Statistic {
ptr.from_ffi_handle().decreased_stat()
}
/// 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
#[no_mangle]
extern "C" fn nature_get_stat_modifier(ptr: ExternPointer<Arc<dyn Nature>>, stat: Statistic) -> f32 {
ptr.as_ref().get_stat_modifier(stat)
extern "C" fn nature_get_stat_modifier(ptr: FFIHandle<Arc<dyn Nature>>, stat: Statistic) -> f32 {
ptr.from_ffi_handle().get_stat_modifier(stat)
}

View File

@ -1,33 +1,32 @@
use crate::ffi::ffi_handle::FFIHandle;
use crate::ffi::{
ffi_arc_dyn_getter, ffi_arc_stringkey_getter, BorrowedPtr, ExternPointer, IdentifiablePointer, NativeResult,
OwnedPtr,
ffi_handle_arc_dyn_getter, ffi_handle_arc_stringkey_getter, FFIResult, FromFFIHandle, NonOwnedPtrString,
};
use crate::static_data::{Form, Gender, Species, SpeciesImpl};
use crate::{PkmnError, Random, StringKey};
use hashbrown::HashSet;
use std::ffi::{c_char, CStr};
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Creates a new species.
#[no_mangle]
unsafe extern "C" fn species_new(
id: u16,
name: BorrowedPtr<c_char>,
name: NonOwnedPtrString,
gender_rate: f32,
growth_rate: BorrowedPtr<c_char>,
growth_rate: NonOwnedPtrString,
capture_rate: u8,
default_form: OwnedPtr<Arc<dyn Form>>,
default_form: FFIHandle<Arc<dyn Form>>,
flags: *const *const c_char,
flags_length: usize,
) -> NativeResult<IdentifiablePointer<Arc<dyn Species>>> {
) -> FFIResult<FFIHandle<Arc<dyn Species>>> {
let name: StringKey = match CStr::from_ptr(name).to_str() {
Ok(name) => name.into(),
Err(_) => return NativeResult::err(PkmnError::InvalidCString.into()),
Err(_) => return FFIResult::err(PkmnError::InvalidCString.into()),
};
let growth_rate: StringKey = match CStr::from_ptr(growth_rate).to_str() {
Ok(growth_rate) => growth_rate.into(),
Err(_) => return NativeResult::err(PkmnError::InvalidCString.into()),
Err(_) => return FFIResult::err(PkmnError::InvalidCString.into()),
};
let flags = std::slice::from_raw_parts(flags, flags_length);
@ -35,13 +34,14 @@ unsafe extern "C" fn species_new(
for flag in flags {
let flag = match CStr::from_ptr(*flag).to_str() {
Ok(flag) => flag,
Err(_) => return NativeResult::err(PkmnError::InvalidCString.into()),
Err(_) => return FFIResult::err(PkmnError::InvalidCString.into()),
};
flags_set.insert(flag.into());
}
let default_form = match default_form.as_ref() {
Some(default_form) => default_form,
None => return NativeResult::err(PkmnError::NullReference.into()),
let default_form = if default_form.is_none() {
return FFIResult::err(PkmnError::NullReference.into());
} else {
default_form.from_ffi_handle()
};
let a: Arc<dyn Species> = Arc::new(SpeciesImpl::new(
@ -53,53 +53,48 @@ unsafe extern "C" fn species_new(
default_form.clone(),
flags_set,
));
NativeResult::ok(a.into())
FFIResult::ok(FFIHandle::get_handle(a.into()))
}
/// Drop a reference to the species.
#[no_mangle]
unsafe extern "C" fn species_drop(ptr: OwnedPtr<Arc<dyn Species>>) {
drop_in_place(ptr);
}
ffi_arc_dyn_getter!(Species, id, u16);
ffi_arc_stringkey_getter!(Species, name);
ffi_arc_dyn_getter!(Species, gender_rate, f32);
ffi_arc_stringkey_getter!(Species, growth_rate);
ffi_arc_dyn_getter!(Species, capture_rate, u8);
ffi_handle_arc_dyn_getter!(Species, id, u16);
ffi_handle_arc_stringkey_getter!(Species, name);
ffi_handle_arc_dyn_getter!(Species, gender_rate, f32);
ffi_handle_arc_stringkey_getter!(Species, growth_rate);
ffi_handle_arc_dyn_getter!(Species, capture_rate, u8);
/// Adds a new form to the species.
#[no_mangle]
unsafe extern "C" fn species_add_form(
mut species: ExternPointer<Arc<dyn Species>>,
name: BorrowedPtr<c_char>,
form: OwnedPtr<Arc<dyn Form>>,
) -> NativeResult<()> {
let form = match form.as_ref() {
Some(form) => form.clone(),
None => return NativeResult::err(PkmnError::NullReference.into()),
species: FFIHandle<Arc<dyn Species>>,
name: NonOwnedPtrString,
form: FFIHandle<Arc<dyn Form>>,
) -> FFIResult<()> {
let form = if form.is_none() {
return FFIResult::err(PkmnError::NullReference.into());
} else {
form.from_ffi_handle()
};
species.as_mut().add_form(CStr::from_ptr(name).into(), form);
NativeResult::ok(())
species.from_ffi_handle().add_form(CStr::from_ptr(name).into(), form);
FFIResult::ok(())
}
/// Gets a form by name.
#[no_mangle]
unsafe extern "C" fn species_get_form(
species: ExternPointer<Arc<dyn Species>>,
name: BorrowedPtr<c_char>,
) -> IdentifiablePointer<Arc<dyn Form>> {
let form = species.as_ref().get_form(&CStr::from_ptr(name).into());
species: FFIHandle<Arc<dyn Species>>,
name: NonOwnedPtrString,
) -> FFIHandle<Arc<dyn Form>> {
let form = species.from_ffi_handle().get_form(&CStr::from_ptr(name).into());
if let Some(form) = form {
form.into()
FFIHandle::get_handle(form.into())
} else {
IdentifiablePointer::none()
FFIHandle::none()
}
}
/// Gets a form by name.
#[no_mangle]
unsafe extern "C" fn species_get_random_gender(species: ExternPointer<Arc<dyn Species>>, seed: u64) -> Gender {
unsafe extern "C" fn species_get_random_gender(species: FFIHandle<Arc<dyn Species>>, seed: u64) -> Gender {
let mut rand = Random::new(seed as u128);
species.as_ref().get_random_gender(&mut rand)
species.from_ffi_handle().get_random_gender(&mut rand)
}

View File

@ -1,6 +1,7 @@
use crate::ffi::{ExternPointer, IdentifiablePointer, OwnedPtr};
use crate::ffi::FFIHandle;
use crate::ffi::FromFFIHandle;
use crate::static_data::{StaticStatisticSet, Statistic, StatisticSet};
use std::ptr::drop_in_place;
use std::sync::Arc;
/// Basic foreign function interface for a statistic set.
macro_rules! statistic_set {
@ -15,40 +16,35 @@ extern "C" fn [<statistic_set_ $num_type _new>](
special_attack: $num_type,
special_defense: $num_type,
speed: $num_type,
) -> IdentifiablePointer<StatisticSet<$num_type>> {
Box::new(StatisticSet::new(
) -> FFIHandle<Arc<StatisticSet<$num_type>>> {
FFIHandle::get_handle(Arc::new(StatisticSet::new(
hp,
attack,
defense,
special_attack,
special_defense,
speed,
)).into()
)).into())
}
#[no_mangle]
unsafe extern "C" fn [<statistic_set_ $num_type _drop>](ptr: OwnedPtr<StatisticSet<$num_type>>) {
drop_in_place(ptr)
extern "C" fn [<statistic_set_ $num_type _get_stat>](ptr: FFIHandle<Arc<StatisticSet<$num_type>>>, stat: Statistic) -> $num_type {
ptr.from_ffi_handle().get_stat(stat)
}
#[no_mangle]
extern "C" fn [<statistic_set_ $num_type _get_stat>](ptr: ExternPointer<StatisticSet<$num_type>>, stat: Statistic) -> $num_type {
ptr.as_ref().get_stat(stat)
extern "C" fn [<statistic_set_ $num_type _set_stat>](ptr: FFIHandle<Arc<StatisticSet<$num_type>>>, stat: Statistic, value: $num_type) {
ptr.from_ffi_handle().set_stat(stat, value)
}
#[no_mangle]
extern "C" fn [<statistic_set_ $num_type _set_stat>](ptr: ExternPointer<StatisticSet<$num_type>>, stat: Statistic, value: $num_type) {
ptr.as_ref().set_stat(stat, value)
extern "C" fn [<statistic_set_ $num_type _increase_stat>](ptr: FFIHandle<Arc<StatisticSet<$num_type>>>, stat: Statistic, value: $num_type) {
ptr.from_ffi_handle().increase_stat(stat, value)
}
#[no_mangle]
extern "C" fn [<statistic_set_ $num_type _increase_stat>](ptr: ExternPointer<StatisticSet<$num_type>>, stat: Statistic, value: $num_type) {
ptr.as_ref().increase_stat(stat, value)
}
#[no_mangle]
extern "C" fn [<statistic_set_ $num_type _decrease_stat>](ptr: ExternPointer<StatisticSet<$num_type>>, stat: Statistic, value: $num_type) {
ptr.as_ref().decrease_stat(stat, value)
extern "C" fn [<statistic_set_ $num_type _decrease_stat>](ptr: FFIHandle<Arc<StatisticSet<$num_type>>>, stat: Statistic, value: $num_type) {
ptr.from_ffi_handle().decrease_stat(stat, value)
}
}
@ -56,11 +52,11 @@ extern "C" fn [<statistic_set_ $num_type _decrease_stat>](ptr: ExternPointer<Sta
}
statistic_set!(u8);
statistic_set!(u16);
// statistic_set!(u16);
statistic_set!(u32);
statistic_set!(i8);
statistic_set!(i16);
statistic_set!(i32);
// statistic_set!(i16);
// statistic_set!(i32);
/// Basic foreign function interface for a static statistic set.
macro_rules! static_statistic_set {
@ -75,34 +71,29 @@ extern "C" fn [<static_statistic_set_ $num_type _new>](
special_attack: $num_type,
special_defense: $num_type,
speed: $num_type,
) -> IdentifiablePointer<StaticStatisticSet<$num_type>> {
Box::new(StaticStatisticSet::new(
) -> FFIHandle<Arc<StaticStatisticSet<$num_type>>> {
FFIHandle::get_handle(Arc::new(StaticStatisticSet::new(
hp,
attack,
defense,
special_attack,
special_defense,
speed,
)).into()
)).into())
}
#[no_mangle]
unsafe extern "C" fn [<static_statistic_set_ $num_type _drop>](ptr: OwnedPtr<StaticStatisticSet<$num_type>>) {
drop_in_place(ptr)
}
#[no_mangle]
extern "C" fn [<static_statistic_set_ $num_type _get_stat>](ptr: ExternPointer<StaticStatisticSet<$num_type>>, stat: Statistic) -> $num_type {
ptr.as_ref().get_stat(stat)
extern "C" fn [<static_statistic_set_ $num_type _get_stat>](ptr: FFIHandle<Arc<StaticStatisticSet<$num_type>>>, stat: Statistic) -> $num_type {
ptr.from_ffi_handle().get_stat(stat)
}
}
};
}
static_statistic_set!(u8);
// static_statistic_set!(u8);
static_statistic_set!(u16);
static_statistic_set!(u32);
static_statistic_set!(i8);
static_statistic_set!(i16);
static_statistic_set!(i32);
// static_statistic_set!(u32);
// static_statistic_set!(i8);
// static_statistic_set!(i16);
// static_statistic_set!(i32);

View File

@ -7,8 +7,9 @@
#![allow(incomplete_features)]
#![allow(ambiguous_glob_reexports)]
#![allow(hidden_glob_reexports)]
#![deny(missing_docs)]
#![deny(clippy::missing_docs_in_private_items)]
// Documentation linters
// #![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)]

View File

@ -146,10 +146,10 @@ fn effect_parameter_get_type(
) -> WasmResult<u8> {
let value = get_value_arc!(parameter, env);
wasm_ok(match value.deref() {
EffectParameter::Bool(_, _) => 1,
EffectParameter::Int(_, _) => 2,
EffectParameter::Float(_, _) => 3,
EffectParameter::String(_, _) => 4,
EffectParameter::Bool(_) => 1,
EffectParameter::Int(_) => 2,
EffectParameter::Float(_) => 3,
EffectParameter::String(_) => 4,
})
}
@ -160,7 +160,7 @@ fn effect_parameter_as_bool(
) -> WasmResult<u8> {
let value = get_value_arc!(parameter, env);
match value.deref() {
EffectParameter::Bool(_, b) => wasm_ok(<u8 as From<bool>>::from(*b)),
EffectParameter::Bool(b) => wasm_ok(<u8 as From<bool>>::from(*b)),
_ => wasm_err::<u8>(anyhow!("Unexpected parameter type. Expected bool, got {}", value), &env),
}
}
@ -172,7 +172,7 @@ fn effect_parameter_as_int(
) -> WasmResult<i64> {
let value = get_value_arc!(parameter, env);
match value.deref() {
EffectParameter::Int(_, i) => wasm_ok(*i),
EffectParameter::Int(i) => wasm_ok(*i),
_ => wasm_err::<i64>(anyhow!("Unexpected parameter type. Expected int, got {}", value), &env),
}
}
@ -184,7 +184,7 @@ fn effect_parameter_as_float(
) -> WasmResult<f32> {
let value = get_value_arc!(parameter, env);
match value.deref() {
EffectParameter::Float(_, f) => wasm_ok(*f),
EffectParameter::Float(f) => wasm_ok(*f),
_ => wasm_err::<f32>(
anyhow!("Unexpected parameter type. Expected float, got {}", value),
&env,
@ -199,7 +199,7 @@ fn effect_parameter_as_string(
) -> WasmResult<ExternRef<StringKey>> {
let value = get_value_arc!(parameter, env);
match value.deref() {
EffectParameter::String(_, s) => wasm_ok(ExternRef::<StringKey>::func_new(&env, s.clone().into())),
EffectParameter::String(s) => wasm_ok(ExternRef::<StringKey>::func_new(&env, s.clone().into())),
_ => wasm_err::<ExternRef<StringKey>>(
anyhow!("Unexpected parameter type. Expected string, got {}", value),
&env,

View File

@ -1,4 +1,3 @@
use crate::ValueIdentifiable;
use anyhow_ext::Result;
use std::marker::PhantomData;
use std::mem::transmute;
@ -41,7 +40,7 @@ impl<T: ?Sized> Default for ExternRef<T> {
}
}
impl<T: ValueIdentifiable + ?Sized> ExternRef<T> {
impl<T: ?Sized> ExternRef<T> {
/// 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: WasmObject) -> Self {

View File

@ -1,3 +1,4 @@
use std::any::Any;
use std::fmt::{Debug, Formatter};
use std::mem::{align_of, forget, size_of};
use std::sync::{Arc, Weak};
@ -19,19 +20,17 @@ use crate::script_implementations::wasm::script_function_cache::ScriptFunctionCa
use crate::script_implementations::wasm::temp_wasm_allocator::{AllocatedObject, TempWasmAllocator};
use crate::script_implementations::wasm::WebAssemblyScriptCapabilities;
use crate::static_data::Item;
use crate::{ScriptCategory, StringKey, ValueIdentifiable, ValueIdentifier, VecExt};
use crate::{ScriptCategory, StringKey, VecExt};
/// A WebAssembly script resolver implements the dynamic scripts functionality with WebAssembly.
pub struct WebAssemblyScriptResolver {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The global state storage of WASM.
_store: *mut Store,
/// The WASM modules we have loaded.
modules: Vec<Module>,
modules: RwLock<Vec<Module>>,
/// Our currently loaded WASM instances. Empty until finalize() is called, after which the loaded modules get turned
/// into actual instances.
instances: Vec<Instance>,
instances: RwLock<Vec<Instance>>,
/// This is the WASM function to load a script.
load_script_fn: Option<TypedFunction<(u8, ExternRef<StringKey>), u32>>,
@ -51,7 +50,7 @@ struct ScriptCapabilitiesKey {
impl WebAssemblyScriptResolver {
/// Instantiates a new WebAssemblyScriptResolver.
pub fn new() -> Box<WebAssemblyScriptResolver> {
pub fn new() -> Arc<WebAssemblyScriptResolver> {
let compiler = wasmer::LLVM::default();
let mut features = Features::new();
features.multi_value = true;
@ -65,7 +64,6 @@ impl WebAssemblyScriptResolver {
environment.self_arc.write().replace(Arc::downgrade(&environment));
let s = Self {
identifier: Default::default(),
_store: store_ptr,
modules: Default::default(),
instances: Default::default(),
@ -73,7 +71,7 @@ impl WebAssemblyScriptResolver {
environment_data: environment,
};
Box::new(s)
Arc::new(s)
}
/// Get an immutable reference to the current WASM Store.
@ -93,15 +91,15 @@ impl WebAssemblyScriptResolver {
}
/// Load a compiled WASM module.
pub fn load_wasm_from_bytes(&mut self, bytes: &[u8]) -> Result<()> {
pub fn load_wasm_from_bytes(&self, bytes: &[u8]) -> Result<()> {
// FIXME: Error handling
let module = Module::new(&self.store_ref(), bytes)?;
self.modules.push(module);
self.modules.write().push(module);
Ok(())
}
/// Tells the script resolver we're done loading wasm modules, and to finalize the resolver.
pub fn finalize(&mut self) -> Result<()> {
pub fn finalize(&self) -> Result<()> {
let mut imports = Imports::new();
let env = FunctionEnv::new(
@ -109,7 +107,8 @@ impl WebAssemblyScriptResolver {
WebAssemblyEnv::new(Arc::downgrade(&self.environment_data)),
);
register_webassembly_funcs(&mut imports, &mut self.store_mut(), &env);
for module in &self.modules {
let modules = self.modules.read();
for module in modules.iter() {
for import in module.imports() {
if imports.get_export("env", import.name()).is_none() {
println!(
@ -143,8 +142,12 @@ impl WebAssemblyScriptResolver {
if let Some(m) = &self.environment_data.memory.read().as_ref() {
m.grow(&mut self.store_mut(), 32)?;
}
if let Some(f) = exported_functions.get::<StringKey>(&"load_script".into()) {
self.load_script_fn = Some(f.typed(&self.store_ref())?)
unsafe {
#[allow(clippy::unwrap_used)] // We know this is valid.
if let Some(f) = exported_functions.get::<StringKey>(&"load_script".into()) {
let self_mut = (self as *const Self as *mut Self).as_mut().unwrap();
self_mut.load_script_fn = Some(f.typed(&self.store_ref())?)
}
}
if let Some(f) = exported_functions.get::<StringKey>(&"allocate_mem".into()) {
let _ = self
@ -160,7 +163,7 @@ impl WebAssemblyScriptResolver {
.write()
.insert(TempWasmAllocator::new(temp_memory_slab.0, temp_memory_slab.1));
}
self.instances.push(instance);
self.instances.write().push(instance);
}
Ok(())
}
@ -171,12 +174,6 @@ impl WebAssemblyScriptResolver {
}
}
impl ValueIdentifiable for WebAssemblyScriptResolver {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
impl ScriptResolver for WebAssemblyScriptResolver {
fn load_script(
&self,
@ -199,6 +196,10 @@ impl ScriptResolver for WebAssemblyScriptResolver {
fn load_item_script(&self, _key: &dyn Item) -> Result<Option<Arc<dyn ItemScript>>> {
todo!()
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl Debug for WebAssemblyScriptResolver {

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use std::any::Any;
use std::fmt::Debug;
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
/// An item category defines which bag slot items are stored in.
#[derive(Debug, Copy, Clone)]
@ -47,7 +47,7 @@ pub enum BattleItemCategory {
}
/// An item is an object which the player can pick up, keep in their Bag, and use in some manner
pub trait Item: ValueIdentifiable + Debug + Any {
pub trait Item: Debug + Any {
/// The name of the item.
fn name(&self) -> &StringKey;
/// Which bag slot items are stored in.
@ -66,8 +66,6 @@ pub trait Item: ValueIdentifiable + Debug + Any {
/// An item is an object which the player can pick up, keep in their Bag, and use in some manner
#[derive(Debug)]
pub struct ItemImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The name of the item.
name: StringKey,
/// Which bag slot items are stored in.
@ -90,7 +88,6 @@ impl ItemImpl {
flags: HashSet<StringKey>,
) -> ItemImpl {
ItemImpl {
identifier: Default::default(),
name: name.clone(),
category,
battle_category,
@ -128,12 +125,6 @@ impl Item for ItemImpl {
}
}
impl ValueIdentifiable for ItemImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
pub(crate) mod tests {
@ -150,10 +141,5 @@ pub(crate) mod tests {
fn flags(&self) -> &HashSet<StringKey>;
fn has_flag(&self, key: &StringKey) -> bool;
}
impl ValueIdentifiable for Item {
fn value_identifier(&self) -> ValueIdentifier {
ValueIdentifier::new(0)
}
}
}
}

View File

@ -5,16 +5,14 @@ use indexmap::IndexMap;
use crate::static_data::Ability;
use crate::static_data::DataLibrary;
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
/// A storage for all abilities that can be used in this data library.
pub trait AbilityLibrary: DataLibrary<dyn Ability> + ValueIdentifiable + Debug {}
pub trait AbilityLibrary: DataLibrary<dyn Ability> + Debug {}
/// A storage for all abilities that can be used in this data library.
#[derive(Debug)]
pub struct AbilityLibraryImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The underlying map for the library.
map: IndexMap<StringKey, Arc<dyn Ability>>,
}
@ -23,7 +21,6 @@ impl AbilityLibraryImpl {
/// Instantiates a new ability library.
pub fn new(capacity: usize) -> Self {
Self {
identifier: Default::default(),
map: IndexMap::with_capacity(capacity),
}
}
@ -40,12 +37,6 @@ impl DataLibrary<dyn Ability> for AbilityLibraryImpl {
}
}
impl ValueIdentifiable for AbilityLibraryImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
@ -57,7 +48,7 @@ pub mod tests {
use std::sync::Arc;
pub fn build() -> AbilityLibraryImpl {
let mut lib = AbilityLibraryImpl::new(1);
let lib = AbilityLibraryImpl::new(1);
lib.add(
&StringKey::new("test_ability"),
Arc::new(AbilityImpl::new(

View File

@ -15,13 +15,17 @@ pub trait DataLibrary<T: ?Sized> {
fn get_modify(&mut self) -> &mut IndexMap<StringKey, Arc<T>>;
/// Adds a new value to the library.
fn add(&mut self, key: &StringKey, value: Arc<T>) {
self.get_modify().insert(key.clone(), value);
fn add(&self, key: &StringKey, value: Arc<T>) {
#[allow(clippy::unwrap_used)] // We know this cant fail.
let self_mut = unsafe { (self as *const Self as *mut Self).as_mut() }.unwrap();
self_mut.get_modify().insert(key.clone(), value);
}
/// Removes a value from the library.
fn remove(&mut self, key: &StringKey) {
self.get_modify().remove(key);
fn remove(&self, key: &StringKey) {
#[allow(clippy::unwrap_used)] // We know this cant fail.
let self_mut = unsafe { (self as *const Self as *mut Self).as_mut() }.unwrap();
self_mut.get_modify().remove(key);
}
/// Gets a value from the library.

View File

@ -1,37 +1,36 @@
use anyhow::Result;
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use hashbrown::HashMap;
use parking_lot::RwLock;
use crate::defines::LevelInt;
use crate::static_data::GrowthRate;
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
/// A library to store all growth rates.
pub trait GrowthRateLibrary: Debug + ValueIdentifiable {
pub trait GrowthRateLibrary: Debug {
/// Calculates the level for a given growth key name and a certain experience.
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result<LevelInt>;
/// Calculates the experience for a given growth key name and a certain level.
fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32>;
/// Adds a new growth rate with a name and value.
fn add_growth_rate(&mut self, key: &StringKey, value: Box<dyn GrowthRate>);
fn add_growth_rate(&self, key: &StringKey, value: Arc<dyn GrowthRate>);
}
/// A library to store all growth rates.
pub struct GrowthRateLibraryImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The underlying data structure.
growth_rates: HashMap<StringKey, Box<dyn GrowthRate>>,
growth_rates: RwLock<HashMap<StringKey, Arc<dyn GrowthRate>>>,
}
impl GrowthRateLibraryImpl {
/// Instantiates a new growth rate library with a capacity.
pub fn new(capacity: usize) -> Self {
Self {
identifier: Default::default(),
growth_rates: HashMap::with_capacity(capacity),
growth_rates: RwLock::new(HashMap::with_capacity(capacity)),
}
}
}
@ -41,6 +40,7 @@ impl GrowthRateLibrary for GrowthRateLibraryImpl {
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result<LevelInt> {
Ok(self
.growth_rates
.read()
.get(growth_rate)
.ok_or_else(|| anyhow::anyhow!("No growth rate found with key {}", growth_rate.to_string()))?
.calculate_level(experience))
@ -48,20 +48,15 @@ impl GrowthRateLibrary for GrowthRateLibraryImpl {
/// Calculates the experience for a given growth key name and a certain level.
fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32> {
self.growth_rates
.read()
.get(growth_rate)
.ok_or_else(|| anyhow::anyhow!("No growth rate found with key {}", growth_rate.to_string()))?
.calculate_experience(level)
}
/// Adds a new growth rate with a name and value.
fn add_growth_rate(&mut self, key: &StringKey, value: Box<dyn GrowthRate>) {
self.growth_rates.insert(key.clone(), value);
}
}
impl ValueIdentifiable for GrowthRateLibraryImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
fn add_growth_rate(&self, key: &StringKey, value: Arc<dyn GrowthRate>) {
self.growth_rates.write().insert(key.clone(), value);
}
}
@ -87,7 +82,7 @@ pub mod tests {
let w = &mut lib;
w.add_growth_rate(
&"test_growthrate".into(),
Box::new(LookupGrowthRate::new(vec![0, 5, 10, 100])),
Arc::new(LookupGrowthRate::new(vec![0, 5, 10, 100])),
);
// Drops borrow as mut
@ -100,12 +95,7 @@ pub mod tests {
impl GrowthRateLibrary for GrowthRateLibrary {
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result<LevelInt>;
fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32>;
fn add_growth_rate(&mut self, key: &StringKey, value: Box<dyn GrowthRate>);
}
impl ValueIdentifiable for GrowthRateLibrary {
fn value_identifier(&self) -> ValueIdentifier{
ValueIdentifier::new(0)
}
fn add_growth_rate(&self, key: &StringKey, value: Arc<dyn GrowthRate>);
}
}

View File

@ -5,16 +5,14 @@ use indexmap::IndexMap;
use crate::static_data::DataLibrary;
use crate::static_data::Item;
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
/// A library to store all items.
pub trait ItemLibrary: DataLibrary<dyn Item> + ValueIdentifiable + Debug {}
pub trait ItemLibrary: DataLibrary<dyn Item> + Debug {}
/// A library to store all items.
#[derive(Debug)]
pub struct ItemLibraryImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The underlying data structure.
map: IndexMap<StringKey, Arc<dyn Item>>,
}
@ -23,7 +21,6 @@ impl ItemLibraryImpl {
/// Instantiates a new Item Library.
pub fn new(capacity: usize) -> Self {
Self {
identifier: Default::default(),
map: IndexMap::with_capacity(capacity),
}
}
@ -41,12 +38,6 @@ impl DataLibrary<dyn Item> for ItemLibraryImpl {
}
}
impl ValueIdentifiable for ItemLibraryImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
pub mod tests {
use hashbrown::HashSet;

View File

@ -1,11 +1,10 @@
use crate::defines::LevelInt;
use crate::{ValueIdentifiable, ValueIdentifier};
use anyhow::Result;
use anyhow_ext::ensure;
use std::fmt::Debug;
/// This library holds several misc settings for the library.
pub trait LibrarySettings: Debug + ValueIdentifiable {
pub trait LibrarySettings: Debug {
/// The highest level a Pokemon can be.
fn maximum_level(&self) -> LevelInt;
@ -17,8 +16,6 @@ pub trait LibrarySettings: Debug + ValueIdentifiable {
/// This library holds several misc settings for the library.
#[derive(Debug)]
pub struct LibrarySettingsImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The highest level a Pokemon can be.
maximum_level: LevelInt,
/// The chance of a Pokemon being shiny, as the denominator of a fraction, where the nominator
@ -36,7 +33,6 @@ impl LibrarySettingsImpl {
ensure!(shiny_rate >= 1);
ensure!(maximum_level >= 1);
Ok(Self {
identifier: Default::default(),
maximum_level,
shiny_rate,
})
@ -51,9 +47,3 @@ impl LibrarySettings for LibrarySettingsImpl {
self.shiny_rate
}
}
impl ValueIdentifiable for LibrarySettingsImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}

View File

@ -5,16 +5,14 @@ use indexmap::IndexMap;
use crate::static_data::DataLibrary;
use crate::static_data::MoveData;
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
/// A library to store all data for moves.
pub trait MoveLibrary: DataLibrary<dyn MoveData> + ValueIdentifiable + Debug {}
pub trait MoveLibrary: DataLibrary<dyn MoveData> + Debug {}
/// A library to store all data for moves.
#[derive(Debug)]
pub struct MoveLibraryImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The underlying map.
map: IndexMap<StringKey, Arc<dyn MoveData>>,
}
@ -23,7 +21,6 @@ impl MoveLibraryImpl {
/// Instantiates a new Move Library.
pub fn new(capacity: usize) -> Self {
Self {
identifier: Default::default(),
map: IndexMap::with_capacity(capacity),
}
}
@ -40,12 +37,6 @@ impl DataLibrary<dyn MoveData> for MoveLibraryImpl {
}
}
impl ValueIdentifiable for MoveLibraryImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
pub mod tests {
use hashbrown::HashSet;

View File

@ -1,15 +1,16 @@
use crate::static_data::Nature;
use crate::{Random, StringKey, ValueIdentifiable, ValueIdentifier};
use crate::{Random, StringKey};
use anyhow::bail;
use anyhow_ext::{ensure, Result};
use indexmap::IndexMap;
use parking_lot::RwLock;
use std::fmt::Debug;
use std::sync::Arc;
/// A library of all natures that can be used, stored by their names.
pub trait NatureLibrary: Debug + ValueIdentifiable {
pub trait NatureLibrary: Debug {
/// Adds a new nature with name to the library.
fn load_nature(&mut self, name: StringKey, nature: Arc<dyn Nature>);
fn load_nature(&self, name: StringKey, nature: Arc<dyn Nature>);
/// Gets a nature by name.
fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn Nature>>;
/// Gets a random nature.
@ -21,38 +22,35 @@ pub trait NatureLibrary: Debug + ValueIdentifiable {
/// A library of all natures that can be used, stored by their names.
#[derive(Debug)]
pub struct NatureLibraryImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The underlying data structure.
map: IndexMap<StringKey, Arc<dyn Nature>>,
map: RwLock<IndexMap<StringKey, Arc<dyn Nature>>>,
}
impl NatureLibraryImpl {
/// Creates a new nature library with a given capacity.
pub fn new(capacity: usize) -> Self {
Self {
identifier: Default::default(),
map: IndexMap::with_capacity(capacity),
map: RwLock::new(IndexMap::with_capacity(capacity)),
}
}
}
impl NatureLibrary for NatureLibraryImpl {
/// Adds a new nature with name to the library.
fn load_nature(&mut self, name: StringKey, nature: Arc<dyn Nature>) {
self.map.insert(name, nature);
fn load_nature(&self, name: StringKey, nature: Arc<dyn Nature>) {
self.map.write().insert(name, nature);
}
/// Gets a nature by name.
fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn Nature>> {
self.map.get(key).cloned()
self.map.read().get(key).cloned()
}
fn get_random_nature(&self, rand: &mut Random) -> Result<Arc<dyn Nature>> {
ensure!(!self.map.is_empty(), "No natures were loaded into the library.");
let i = rand.get_between(0, self.map.len() as i32);
Ok(self
.map
let map = self.map.read();
ensure!(!map.is_empty(), "No natures were loaded into the library.");
let i = rand.get_between(0, map.len() as i32);
Ok(map
.get_index(i as usize)
// this should never happen, but we'll check anyway.
.ok_or(anyhow_ext::anyhow!("Failed to get a random nature from the library."))?
@ -62,10 +60,11 @@ impl NatureLibrary for NatureLibraryImpl {
/// Finds a nature name by nature.
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> Result<StringKey> {
for kv in &self.map {
let read_lock = self.map.read();
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 kv.1.value_identifier() == nature.value_identifier() {
if Arc::ptr_eq(&kv.1, nature) {
return Ok(kv.0.clone());
}
}
@ -73,12 +72,6 @@ impl NatureLibrary for NatureLibraryImpl {
}
}
impl ValueIdentifiable for NatureLibraryImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
@ -89,7 +82,7 @@ pub mod tests {
use crate::static_data::{NatureImpl, NatureLibrary, NatureLibraryImpl};
pub fn build() -> NatureLibraryImpl {
let mut lib = NatureLibraryImpl::new(2);
let lib = NatureLibraryImpl::new(2);
lib.load_nature(
"test_nature".into(),
@ -103,21 +96,16 @@ pub mod tests {
#[derive(Debug)]
pub NatureLibrary{}
impl NatureLibrary for NatureLibrary {
fn load_nature(&mut self, name: StringKey, nature: Arc<dyn Nature>);
fn load_nature(&self, name: StringKey, nature: Arc<dyn Nature>);
fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn Nature>>;
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> Result<StringKey>;
fn get_random_nature(&self, rand: &mut Random) -> Result<Arc<dyn Nature>>;
}
impl ValueIdentifiable for NatureLibrary {
fn value_identifier(&self) -> ValueIdentifier{
ValueIdentifier::new(0)
}
}
}
#[test]
fn create_nature_library_insert_and_retrieve() {
let mut lib = NatureLibraryImpl::new(2);
let lib = NatureLibraryImpl::new(2);
lib.load_nature(
"foo".into(),
NatureImpl::new(Statistic::HP, Statistic::Attack, 1.1, 0.9),
@ -135,7 +123,7 @@ pub mod tests {
#[test]
fn create_nature_library_insert_and_get_name() {
let mut lib = NatureLibraryImpl::new(2);
let lib = NatureLibraryImpl::new(2);
lib.load_nature(
"foo".into(),
NatureImpl::new(Statistic::HP, Statistic::Attack, 1.1, 0.9),
@ -155,7 +143,7 @@ pub mod tests {
#[test]
fn create_nature_library_insert_single_and_retrieve_random() {
let mut lib = NatureLibraryImpl::new(2);
let lib = NatureLibraryImpl::new(2);
lib.load_nature(
"foo".into(),
NatureImpl::new(Statistic::HP, Statistic::Attack, 1.1, 0.9),

View File

@ -5,16 +5,14 @@ use indexmap::IndexMap;
use crate::static_data::DataLibrary;
use crate::static_data::Species;
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
/// A library to store all data for Pokemon species.
pub trait SpeciesLibrary: DataLibrary<dyn Species> + ValueIdentifiable + Debug {}
pub trait SpeciesLibrary: DataLibrary<dyn Species> + Debug {}
/// A library to store all data for Pokemon species.
#[derive(Debug)]
pub struct SpeciesLibraryImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The underlying map.
map: IndexMap<StringKey, Arc<dyn Species>>,
}
@ -23,7 +21,6 @@ impl SpeciesLibraryImpl {
/// Instantiates a new Species Library.
pub fn new(capacity: usize) -> Self {
Self {
identifier: Default::default(),
map: IndexMap::with_capacity(capacity),
}
}
@ -40,12 +37,6 @@ impl DataLibrary<dyn Species> for SpeciesLibraryImpl {
}
}
impl ValueIdentifiable for SpeciesLibraryImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
@ -65,11 +56,6 @@ pub mod tests {
fn map(&self) -> &IndexMap<StringKey, Arc<dyn Species>>;
fn get_modify(&mut self) -> &mut IndexMap<StringKey, Arc<dyn Species>>;
}
impl ValueIdentifiable for SpeciesLibrary {
fn value_identifier(&self) -> ValueIdentifier{
ValueIdentifier::new(0)
}
}
}
fn build_species() -> Arc<dyn Species> {
@ -85,10 +71,10 @@ pub mod tests {
0.0,
0,
Vec::new(),
StaticStatisticSet::new(10, 10, 10, 10, 10, 10),
Arc::new(StaticStatisticSet::new(10, 10, 10, 10, 10, 10)),
Vec::new(),
Vec::new(),
Box::new(LearnableMovesImpl::new(100)),
Arc::new(LearnableMovesImpl::new(100)),
HashSet::new(),
)),
HashSet::new(),

View File

@ -6,12 +6,11 @@ use crate::static_data::MoveLibrary;
use crate::static_data::NatureLibrary;
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 {
pub trait StaticData: Debug {
/// Several misc settings for the library.
fn settings(&self) -> &Arc<dyn LibrarySettings>;
/// All data for Pokemon species.
@ -34,8 +33,6 @@ pub trait StaticData: Debug + ValueIdentifiable {
/// The storage for all different libraries.
#[derive(Debug)]
pub struct StaticDataImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// Several misc settings for the library.
settings: Arc<dyn LibrarySettings>,
/// All data for Pokemon species.
@ -67,7 +64,6 @@ impl StaticDataImpl {
abilities: Arc<dyn AbilityLibrary>,
) -> Self {
Self {
identifier: Default::default(),
settings,
species,
moves,
@ -116,12 +112,6 @@ impl StaticData for StaticDataImpl {
}
}
impl ValueIdentifiable for StaticDataImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
@ -143,16 +133,10 @@ pub mod test {
fn natures(&self) -> &Arc<dyn NatureLibrary>;
fn abilities(&self) -> &Arc<dyn AbilityLibrary>;
}
impl ValueIdentifiable for StaticData {
fn value_identifier(&self) -> ValueIdentifier{
ValueIdentifier::new(0)
}
}
}
pub fn build() -> StaticDataImpl {
StaticDataImpl {
identifier: Default::default(),
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(),

View File

@ -1,9 +1,10 @@
use anyhow_ext::Result;
use atomig::Atom;
use hashbrown::HashMap;
use parking_lot::{RwLock, RwLockReadGuard};
use std::fmt::{Debug, Display};
use crate::{PkmnError, StringKey, ValueIdentifiable, ValueIdentifier};
use crate::{PkmnError, StringKey};
/// A unique key that can be used to store a reference to a type. Opaque reference to a byte
/// internally.
@ -33,7 +34,7 @@ impl Display for TypeIdentifier {
}
/// All data related to types and effectiveness.
pub trait TypeLibrary: Debug + ValueIdentifiable {
pub trait TypeLibrary: Debug {
/// Gets the type identifier for a type with a name.
fn get_type_id(&self, key: &StringKey) -> Option<TypeIdentifier>;
@ -49,48 +50,56 @@ pub trait TypeLibrary: Debug + ValueIdentifiable {
fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> Result<f32>;
/// Registers a new type in the library.
fn register_type(&mut self, name: &StringKey) -> TypeIdentifier;
fn register_type(&self, name: &StringKey) -> TypeIdentifier;
/// Sets the effectiveness for an attacking type against a defending type.
fn set_effectiveness(
&mut self,
attacking: TypeIdentifier,
defending: TypeIdentifier,
effectiveness: f32,
) -> Result<()>;
fn set_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier, effectiveness: f32)
-> Result<()>;
}
/// All data related to types and effectiveness.
#[derive(Debug)]
pub struct TypeLibraryImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// A list of types
types: HashMap<StringKey, TypeIdentifier>,
types: RwLock<HashMap<StringKey, TypeIdentifier>>,
/// The effectiveness of the different types against each other.
effectiveness: Vec<Vec<f32>>,
effectiveness: RwLock<Vec<Vec<f32>>>,
}
impl TypeLibraryImpl {
/// Instantiates a new type library with a specific capacity.
pub fn new(capacity: usize) -> Self {
Self {
identifier: Default::default(),
types: HashMap::with_capacity(capacity),
effectiveness: vec![],
types: RwLock::new(HashMap::with_capacity(capacity)),
effectiveness: RwLock::new(vec![]),
}
}
}
impl TypeLibraryImpl {
fn get_single_effectiveness_with_lock(
lock: &RwLockReadGuard<Vec<Vec<f32>>>,
attacking: TypeIdentifier,
defending: TypeIdentifier,
) -> Result<f32> {
Ok(*lock
.get((attacking.val - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: attacking })?
.get((defending.val - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: defending })?)
}
}
impl TypeLibrary for TypeLibraryImpl {
/// Gets the type identifier for a type with a name.
fn get_type_id(&self, key: &StringKey) -> Option<TypeIdentifier> {
self.types.get(key).cloned()
self.types.read().get(key).cloned()
}
/// Gets the type name from the type identifier.
fn get_type_name(&self, t: TypeIdentifier) -> Option<StringKey> {
for kv in &self.types {
let types = self.types.read();
for kv in types.iter() {
if *kv.1 == t {
return Some(kv.0.clone());
}
@ -100,12 +109,7 @@ impl TypeLibrary for TypeLibraryImpl {
/// Gets the effectiveness for a single attacking type against a single defending type.
fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> Result<f32> {
Ok(*self
.effectiveness
.get((attacking.val - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: attacking })?
.get((defending.val - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: defending })?)
Self::get_single_effectiveness_with_lock(&self.effectiveness.read(), attacking, defending)
}
/// Gets the effectiveness for a single attacking type against an amount of defending types.
@ -113,20 +117,24 @@ impl TypeLibrary for TypeLibraryImpl {
/// multiplying the results with each other.
fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> Result<f32> {
let mut e = 1.0;
let lock = self.effectiveness.read();
for def in defending {
e *= self.get_single_effectiveness(attacking, *def)?;
e *= Self::get_single_effectiveness_with_lock(&lock, attacking, *def)?;
}
Ok(e)
}
/// Registers a new type in the library.
fn register_type(&mut self, name: &StringKey) -> TypeIdentifier {
fn register_type(&self, name: &StringKey) -> TypeIdentifier {
let mut types_write_lock = self.types.write();
let mut effectiveness_write_lock = self.effectiveness.write();
let id = TypeIdentifier {
val: (self.types.len() + 1) as u8,
val: (types_write_lock.len() + 1) as u8,
};
self.types.insert(name.clone(), id);
self.effectiveness.resize((id.val) as usize, vec![]);
for effectiveness in &mut self.effectiveness {
types_write_lock.insert(name.clone(), id);
effectiveness_write_lock.resize((id.val) as usize, vec![]);
for effectiveness in &mut effectiveness_write_lock.iter_mut() {
effectiveness.resize((id.val) as usize, 1.0)
}
id
@ -134,13 +142,14 @@ impl TypeLibrary for TypeLibraryImpl {
/// Sets the effectiveness for an attacking type against a defending type.
fn set_effectiveness(
&mut self,
&self,
attacking: TypeIdentifier,
defending: TypeIdentifier,
effectiveness: f32,
) -> Result<()> {
*self
.effectiveness
.write()
.get_mut((attacking.val - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: attacking })?
.get_mut((defending.val - 1) as usize)
@ -149,12 +158,6 @@ impl TypeLibrary for TypeLibraryImpl {
}
}
impl ValueIdentifiable for TypeLibraryImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]

View File

@ -1,4 +1,4 @@
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
#[doc(inline)]
pub use growth_rates::*;
#[doc(inline)]
@ -56,57 +56,46 @@ mod statistics;
#[derive(PartialEq, Debug, Clone)]
pub enum EffectParameter {
/// A boolean value.
Bool(ValueIdentifier, bool),
Bool(bool),
/// An integer value. Stored as a 64 bit int to deal with potentially large numbers.
Int(ValueIdentifier, i64),
Int(i64),
/// A float value. Stored as a 32 bit float.
Float(ValueIdentifier, f32),
Float(f32),
/// A string value.
String(ValueIdentifier, StringKey),
String(StringKey),
}
impl From<bool> for EffectParameter {
fn from(b: bool) -> Self {
EffectParameter::Bool(Default::default(), b)
EffectParameter::Bool(b)
}
}
impl From<i64> for EffectParameter {
fn from(i: i64) -> Self {
EffectParameter::Int(Default::default(), i)
EffectParameter::Int(i)
}
}
impl From<f32> for EffectParameter {
fn from(f: f32) -> Self {
EffectParameter::Float(Default::default(), f)
EffectParameter::Float(f)
}
}
impl From<StringKey> for EffectParameter {
fn from(s: StringKey) -> Self {
EffectParameter::String(Default::default(), s)
EffectParameter::String(s)
}
}
impl Display for EffectParameter {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
EffectParameter::Bool(_, v) => f.write_fmt(format_args!("EffectParameter::Bool({v})")),
EffectParameter::Int(_, v) => f.write_fmt(format_args!("EffectParameter::Int({v})")),
EffectParameter::Float(_, v) => f.write_fmt(format_args!("EffectParameter::Float({v})")),
EffectParameter::String(_, v) => f.write_fmt(format_args!("EffectParameter::String({v})")),
}
}
}
impl ValueIdentifiable for EffectParameter {
fn value_identifier(&self) -> ValueIdentifier {
match self {
EffectParameter::Bool(i, _) => *i,
EffectParameter::Int(i, _) => *i,
EffectParameter::Float(i, _) => *i,
EffectParameter::String(i, _) => *i,
EffectParameter::Bool(v) => f.write_fmt(format_args!("EffectParameter::Bool({v})")),
EffectParameter::Int(v) => f.write_fmt(format_args!("EffectParameter::Int({v})")),
EffectParameter::Float(v) => f.write_fmt(format_args!("EffectParameter::Float({v})")),
EffectParameter::String(v) => f.write_fmt(format_args!("EffectParameter::String({v})")),
}
}
}

View File

@ -2,9 +2,10 @@ use hashbrown::HashSet;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::sync::Arc;
use crate::static_data::{SecondaryEffect, TypeIdentifier};
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
/// The move category defines what global kind of move this move is.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@ -61,7 +62,7 @@ pub enum MoveTarget {
}
/// A move is the skill Pokémon primarily use in battle. This is the data related to that.
pub trait MoveData: Debug + ValueIdentifiable {
pub trait MoveData: Debug {
/// The name of the move.
fn name(&self) -> &StringKey;
/// The attacking type of the move.
@ -82,7 +83,7 @@ pub trait MoveData: Debug + ValueIdentifiable {
fn priority(&self) -> i8;
/// The optional secondary effect the move has.
fn secondary_effect(&self) -> &Option<Box<dyn SecondaryEffect>>;
fn secondary_effect(&self) -> &Option<Arc<dyn SecondaryEffect>>;
/// Arbitrary flags that can be applied to the move.
fn has_flag(&self, key: &StringKey) -> bool;
@ -94,8 +95,6 @@ pub trait MoveData: Debug + ValueIdentifiable {
/// A move is the skill Pokémon primarily use in battle. This is the data related to that.
#[derive(Debug)]
pub struct MoveDataImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The name of the move.
name: StringKey,
/// The attacking type of the move.
@ -114,7 +113,7 @@ pub struct MoveDataImpl {
/// The priority of the move. A higher priority means the move should go before other moves.
priority: i8,
/// The optional secondary effect the move has.
secondary_effect: Option<Box<dyn SecondaryEffect>>,
secondary_effect: Option<Arc<dyn SecondaryEffect>>,
/// Arbitrary flags that can be applied to the move.
flags: HashSet<StringKey>,
}
@ -130,11 +129,10 @@ impl MoveDataImpl {
base_usages: u8,
target: MoveTarget,
priority: i8,
secondary_effect: Option<Box<dyn SecondaryEffect>>,
secondary_effect: Option<Arc<dyn SecondaryEffect>>,
flags: HashSet<StringKey>,
) -> Self {
Self {
identifier: Default::default(),
name: name.clone(),
move_type,
category,
@ -186,7 +184,7 @@ impl MoveData for MoveDataImpl {
}
/// The optional secondary effect the move has.
fn secondary_effect(&self) -> &Option<Box<dyn SecondaryEffect>> {
fn secondary_effect(&self) -> &Option<Arc<dyn SecondaryEffect>> {
&self.secondary_effect
}
@ -201,12 +199,6 @@ impl MoveData for MoveDataImpl {
}
}
impl ValueIdentifiable for MoveDataImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
@ -225,14 +217,9 @@ pub(crate) mod tests {
fn base_usages(&self) -> u8;
fn target(&self) -> MoveTarget;
fn priority(&self) -> i8;
fn secondary_effect(&self) -> &Option<Box<dyn SecondaryEffect>>;
fn secondary_effect(&self) -> &Option<Arc<dyn SecondaryEffect>>;
fn has_flag(&self, key: &StringKey) -> bool;
fn has_flag_by_hash(&self, key_hash: u32) -> bool;
}
impl ValueIdentifiable for MoveData{
fn value_identifier(&self) -> ValueIdentifier{
ValueIdentifier::new(0)
}
}
}
}

View File

@ -1,10 +1,10 @@
use crate::static_data::EffectParameter;
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
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 {
pub trait SecondaryEffect: Debug {
/// The chance in percentages that the effect triggers. -1 to make it always trigger.
fn chance(&self) -> f32;
/// The name of the effect.
@ -16,8 +16,6 @@ pub trait SecondaryEffect: Debug + ValueIdentifiable {
/// A secondary effect is an effect on a move that happens after it hits.
#[derive(Debug)]
pub struct SecondaryEffectImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The chance in percentages that the effect triggers. -1 to make it always trigger.
chance: f32,
/// The name of the effect.
@ -30,7 +28,6 @@ impl SecondaryEffectImpl {
/// Instantiates a new Secondary Effect.
pub fn new(chance: f32, effect_name: StringKey, parameters: Vec<Arc<EffectParameter>>) -> Self {
Self {
identifier: Default::default(),
chance,
effect_name,
parameters,
@ -53,12 +50,6 @@ impl SecondaryEffect for SecondaryEffectImpl {
}
}
impl ValueIdentifiable for SecondaryEffectImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
@ -77,11 +68,6 @@ pub(crate) mod tests {
fn effect_name(&self) -> &StringKey;
fn parameters(&self) -> &Vec<Arc<EffectParameter>>;
}
impl ValueIdentifiable for SecondaryEffect{
fn value_identifier(&self) -> ValueIdentifier{
ValueIdentifier::new(0)
}
}
}
#[test]

View File

@ -1,11 +1,10 @@
use crate::static_data::Statistic;
use crate::{ValueIdentifiable, ValueIdentifier};
use std::fmt::Debug;
use std::sync::Arc;
/// A nature is an attribute on a Pokemon that modifies the effective base stats on a Pokemon. They
/// can have an increased statistic and a decreased statistic, or be neutral.
pub trait Nature: ValueIdentifiable + Debug {
pub trait Nature: Debug {
/// The stat that should receive the increased modifier.
fn increased_stat(&self) -> Statistic;
@ -27,8 +26,6 @@ pub trait Nature: ValueIdentifiable + Debug {
/// can have an increased statistic and a decreased statistic, or be neutral.
#[derive(Debug)]
pub struct NatureImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The stat that should receive the increased modifier.
increase_stat: Statistic,
/// The stat that should receive the decreased modifier.
@ -48,7 +45,6 @@ impl NatureImpl {
decrease_modifier: f32,
) -> Arc<Self> {
Arc::new(Self {
identifier: Default::default(),
increase_stat,
decrease_stat,
increase_modifier,
@ -89,12 +85,6 @@ impl Nature for NatureImpl {
}
}
impl ValueIdentifiable for NatureImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
@ -111,10 +101,5 @@ pub(crate) mod tests {
fn decreased_modifier(&self) -> f32;
fn get_stat_modifier(&self, stat: Statistic) -> f32;
}
impl ValueIdentifiable for Nature {
fn value_identifier(&self) -> ValueIdentifier {
ValueIdentifier::new(0)
}
}
}
}

View File

@ -1,10 +1,10 @@
use crate::static_data::EffectParameter;
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
use crate::StringKey;
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 {
pub trait Ability: Debug {
/// The name of the ability.
fn name(&self) -> &StringKey;
/// The name of the script effect of the ability.
@ -16,8 +16,6 @@ pub trait Ability: Debug + ValueIdentifiable {
/// An ability is a passive effect in battle that is attached to a Pokemon.
#[derive(Debug)]
pub struct AbilityImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The name of the ability.
name: StringKey,
/// The name of the script effect of the ability.
@ -30,7 +28,6 @@ impl AbilityImpl {
/// Instantiates a new ability.
pub fn new(name: &StringKey, effect: &StringKey, parameters: Vec<Arc<EffectParameter>>) -> Self {
Self {
identifier: Default::default(),
name: name.clone(),
effect: effect.clone(),
parameters,
@ -53,12 +50,6 @@ impl Ability for AbilityImpl {
}
}
impl ValueIdentifiable for AbilityImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
/// An ability index allows us to find an ability on a form. It combines a bool for whether the
/// ability is hidden or not, and then an index of the ability.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -84,10 +75,5 @@ pub(crate) mod tests {
fn effect(&self) -> &StringKey;
fn parameters(&self) -> &Vec<Arc<EffectParameter>>;
}
impl ValueIdentifiable for Ability {
fn value_identifier(&self) -> ValueIdentifier {
ValueIdentifier::new(0)
}
}
}
}

View File

@ -7,12 +7,12 @@ use crate::static_data::Statistic;
use crate::static_data::TypeIdentifier;
use crate::static_data::{Ability, StaticStatisticSet};
use crate::static_data::{AbilityIndex, LearnableMoves};
use crate::{Random, ValueIdentifiable, ValueIdentifier};
use crate::Random;
use crate::{StringKey, VecExt};
/// A form is a variant of a specific species. A species always has at least one form, but can have
/// many more.
pub trait Form: ValueIdentifiable + Debug {
pub trait Form: Debug {
/// The name of the form.
fn name(&self) -> &StringKey;
/// The height of the form in meters.
@ -31,7 +31,7 @@ pub trait Form: ValueIdentifiable + Debug {
fn hidden_abilities(&self) -> &Vec<StringKey>;
/// The moves a Pokemon with this form can learn.
fn moves(&self) -> &Box<dyn LearnableMoves>;
fn moves(&self) -> &Arc<dyn LearnableMoves>;
/// Arbitrary flags can be set on a form for scripting use.
fn flags(&self) -> &HashSet<StringKey>;
@ -62,8 +62,6 @@ pub trait Form: ValueIdentifiable + Debug {
/// many more.
#[derive(Debug)]
pub struct FormImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The name of the form.
name: StringKey,
/// The height of the form in meters.
@ -81,7 +79,7 @@ pub struct FormImpl {
/// The possible hidden abilities a Pokemon with this form can have.
hidden_abilities: Vec<StringKey>,
/// The moves a Pokemon with this form can learn.
moves: Box<dyn LearnableMoves>,
moves: Arc<dyn LearnableMoves>,
/// Arbitrary flags can be set on a form for scripting use.
flags: HashSet<StringKey>,
}
@ -94,20 +92,19 @@ impl FormImpl {
weight: f32,
base_experience: u32,
types: Vec<TypeIdentifier>,
base_stats: StaticStatisticSet<u16>,
base_stats: Arc<StaticStatisticSet<u16>>,
abilities: Vec<StringKey>,
hidden_abilities: Vec<StringKey>,
moves: Box<dyn LearnableMoves>,
moves: Arc<dyn LearnableMoves>,
flags: HashSet<StringKey>,
) -> Self {
Self {
identifier: Default::default(),
name: name.clone(),
height,
weight,
base_experience,
types,
base_stats: Arc::new(base_stats),
base_stats,
abilities,
hidden_abilities,
moves,
@ -151,7 +148,7 @@ impl Form for FormImpl {
}
/// The moves a Pokemon with this form can learn.
fn moves(&self) -> &Box<dyn LearnableMoves> {
fn moves(&self) -> &Arc<dyn LearnableMoves> {
&self.moves
}
/// Arbitrary flags can be set on a form for scripting use.
@ -222,12 +219,6 @@ impl Form for FormImpl {
}
}
impl ValueIdentifiable for FormImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
@ -246,7 +237,7 @@ pub(crate) mod tests {
fn base_stats(&self) -> &Arc<StaticStatisticSet<u16>>;
fn abilities(&self) -> &Vec<StringKey>;
fn hidden_abilities(&self) -> &Vec<StringKey>;
fn moves(&self) -> &Box<dyn LearnableMoves>;
fn moves(&self) -> &Arc<dyn LearnableMoves>;
fn flags(&self) -> &HashSet<StringKey>;
fn get_type(&self, index: usize) -> Result<TypeIdentifier>;
fn get_base_stat(&self, stat: Statistic) -> u16;
@ -257,10 +248,5 @@ pub(crate) mod tests {
fn has_flag(&self, key: &StringKey) -> bool;
fn has_flag_by_hash(&self, key_hash: u32) -> bool;
}
impl ValueIdentifiable for Form {
fn value_identifier(&self) -> ValueIdentifier {
ValueIdentifier::new(0)
}
}
}
}

View File

@ -1,5 +1,6 @@
use anyhow_ext::Result;
use indexmap::IndexSet;
use parking_lot::RwLock;
use std::fmt::Debug;
use crate::defines::LevelInt;
@ -8,27 +9,27 @@ use crate::{StringKey, VecExt};
/// The storage of the moves a Pokemon can learn.
pub trait LearnableMoves: Debug {
/// Adds a new level move the Pokemon can learn.
fn add_level_move(&mut self, level: LevelInt, m: &StringKey) -> Result<()>;
fn add_level_move(&self, level: LevelInt, m: &StringKey) -> Result<()>;
/// Gets all moves a Pokemon can learn when leveling up to a specific level.
fn get_learned_by_level(&self, level: LevelInt) -> Option<&Vec<StringKey>>;
fn get_learned_by_level(&self, level: LevelInt) -> Option<Vec<StringKey>>;
/// Gets the distinct moves a Pokemon can learn through leveling up.
fn get_distinct_level_moves(&self) -> &IndexSet<StringKey>;
fn get_distinct_level_moves(&self) -> IndexSet<StringKey>;
}
/// The storage of the moves a Pokemon can learn.
#[derive(PartialEq, Eq, Debug)]
#[derive(Debug)]
pub struct LearnableMovesImpl {
/// A map of the moves a Pokemon can learn per level.
learned_by_level: Vec<Vec<StringKey>>,
learned_by_level: RwLock<Vec<Vec<StringKey>>>,
/// A list of the distinct moves a Pokemon can learn through leveling up.
distinct_level_moves: IndexSet<StringKey>,
distinct_level_moves: RwLock<IndexSet<StringKey>>,
}
impl LearnableMovesImpl {
/// Instantiates a new Learnable Moves.
pub fn new(max_level: LevelInt) -> Self {
Self {
learned_by_level: vec![Vec::new(); (max_level + 1) as usize],
learned_by_level: RwLock::new(vec![Vec::new(); (max_level + 1) as usize]),
distinct_level_moves: Default::default(),
}
}
@ -36,20 +37,23 @@ impl LearnableMovesImpl {
impl LearnableMoves for LearnableMovesImpl {
/// Adds a new level move the Pokemon can learn.
fn add_level_move(&mut self, level: LevelInt, m: &StringKey) -> Result<()> {
self.learned_by_level.get_mut_res(level as usize)?.push(m.clone());
self.distinct_level_moves.insert(m.clone());
fn add_level_move(&self, level: LevelInt, m: &StringKey) -> Result<()> {
self.learned_by_level
.write()
.get_mut_res(level as usize)?
.push(m.clone());
self.distinct_level_moves.write().insert(m.clone());
Ok(())
}
/// Gets all moves a Pokemon can learn when leveling up to a specific level.
fn get_learned_by_level(&self, level: LevelInt) -> Option<&Vec<StringKey>> {
self.learned_by_level.get(level as usize)
fn get_learned_by_level(&self, level: LevelInt) -> Option<Vec<StringKey>> {
self.learned_by_level.read().get(level as usize).cloned()
}
/// Gets the distinct moves a Pokemon can learn through leveling up.
fn get_distinct_level_moves(&self) -> &IndexSet<StringKey> {
&self.distinct_level_moves
fn get_distinct_level_moves(&self) -> IndexSet<StringKey> {
self.distinct_level_moves.read().clone()
}
}
@ -61,7 +65,7 @@ pub(crate) mod tests {
#[test]
fn adds_level_moves() {
let mut moves = LearnableMovesImpl::new(100);
let moves = LearnableMovesImpl::new(100);
moves.add_level_move(1, &"foo".into()).unwrap();
moves.add_level_move(1, &"bar".into()).unwrap();
@ -73,7 +77,7 @@ pub(crate) mod tests {
#[test]
fn adds_two_same_moves_at_different_level() {
let mut moves = LearnableMovesImpl::new(100);
let moves = LearnableMovesImpl::new(100);
moves.add_level_move(1, &"foo".into()).unwrap();
moves.add_level_move(5, &"foo".into()).unwrap();

View File

@ -8,11 +8,11 @@ use parking_lot::{RawRwLock, RwLock};
use crate::static_data::Form;
use crate::static_data::Gender;
use crate::Random;
use crate::StringKey;
use crate::{Random, ValueIdentifiable, ValueIdentifier};
/// The data belonging to a Pokemon with certain characteristics.
pub trait Species: ValueIdentifiable + Debug {
pub trait Species: Debug {
/// The national dex identifier of the Pokemon.
fn id(&self) -> u16;
/// The name of the Pokemon.
@ -47,8 +47,6 @@ pub trait Species: ValueIdentifiable + Debug {
/// The data belonging to a Pokemon with certain characteristics.
#[derive(Debug)]
pub struct SpeciesImpl {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The national dex identifier of the Pokemon.
id: u16,
/// The name of the Pokemon.
@ -88,7 +86,6 @@ impl SpeciesImpl {
let mut forms = HashMap::with_capacity(1);
forms.insert_unique_unchecked(get_default_key(), default_form);
Self {
identifier: Default::default(),
id,
name: name.clone(),
gender_rate,
@ -176,12 +173,6 @@ impl Species for SpeciesImpl {
}
}
impl ValueIdentifiable for SpeciesImpl {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
@ -207,10 +198,5 @@ pub(crate) mod tests {
fn has_flag(&self, key: &StringKey) -> bool;
fn has_flag_by_hash(&self, key_hash: u32) -> bool;
}
impl ValueIdentifiable for Species {
fn value_identifier(&self) -> ValueIdentifier {
ValueIdentifier::new(0)
}
}
}
}

View File

@ -1,6 +1,5 @@
use std::sync::atomic::Ordering;
use crate::{ValueIdentifiable, ValueIdentifier};
use atomig::impls::{PrimitiveAtom, PrimitiveAtomInteger};
use atomig::{Atom, AtomInteger, Atomic};
use num_traits::{clamp, NumCast, PrimInt};
@ -20,8 +19,6 @@ where
<T as Atom>::Repr: PrimitiveAtomInteger,
T: AtomInteger,
{
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The health point stat value.
hp: Atomic<T>,
/// The physical attack stat value.
@ -47,7 +44,6 @@ where
/// Creates a new statistic set with given stats.
pub fn new(hp: T, attack: T, defense: T, special_attack: T, special_defense: T, speed: T) -> Self {
Self {
identifier: Default::default(),
hp: Atomic::<T>::new(hp),
attack: Atomic::<T>::new(attack),
defense: Atomic::<T>::new(defense),
@ -131,19 +127,6 @@ where
}
}
impl<T> ValueIdentifiable for StatisticSet<T>
where
T: PrimitiveAtom,
T: Atom,
T: PrimitiveAtomInteger,
<T as Atom>::Repr: PrimitiveAtomInteger,
T: AtomInteger,
{
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
/// A collection of statistics that can not be modified after creation.
///
/// As no modifications happen, this struct does not use atomics.
@ -152,8 +135,6 @@ pub struct StaticStatisticSet<T>
where
T: PrimInt,
{
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The health point stat value.
hp: T,
/// The physical attack stat value.
@ -175,7 +156,6 @@ where
/// Create a new static statistic set.
pub fn new(hp: T, attack: T, defense: T, special_attack: T, special_defense: T, speed: T) -> Self {
Self {
identifier: Default::default(),
hp,
attack,
defense,
@ -223,15 +203,6 @@ where
}
}
impl<T> ValueIdentifiable for StaticStatisticSet<T>
where
T: PrimInt,
{
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
/// A clamped statistic set holds the 6 normal stats for a Pokemon, but ensures it always remains
/// between two values (inclusive on the two values).
#[derive(Default, Debug)]
@ -245,8 +216,6 @@ where
T: NumCast,
T: PrimInt,
{
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The health point stat value.
hp: Atomic<T>,
/// The physical attack stat value.
@ -295,7 +264,6 @@ where
/// Instantiates a new clamped statistic set.
pub fn new(hp: T, attack: T, defense: T, special_attack: T, special_defense: T, speed: T) -> Self {
Self {
identifier: Default::default(),
hp: Self::clamped_cast(hp),
attack: Self::clamped_cast(attack),
defense: Self::clamped_cast(defense),
@ -409,21 +377,6 @@ where
}
}
impl<T, const MIN: i64, const MAX: i64> ValueIdentifiable for ClampedStatisticSet<T, MIN, MAX>
where
T: PrimitiveAtom,
T: Atom,
T: PrimitiveAtomInteger,
<T as Atom>::Repr: PrimitiveAtomInteger,
T: AtomInteger,
T: NumCast,
T: PrimInt,
{
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -3,8 +3,6 @@ pub use random::Random;
#[doc(inline)]
pub use string_key::StringKey;
#[doc(inline)]
pub use value_identifier::*;
#[doc(inline)]
pub use vec_ext::*;
/// The random module defines a RNG implementation used in pkmn_lib
@ -12,7 +10,5 @@ mod random;
/// The string_key module defines a custom string handling for reduced allocations and fast lookups
/// and equality checks.
mod string_key;
/// Helper tool to keep track of moving memory for FFI.
mod value_identifier;
/// Helper functions for vecs
mod vec_ext;

View File

@ -1,4 +1,3 @@
use crate::{ValueIdentifiable, ValueIdentifier};
use arcstr::ArcStr;
use hashbrown::HashMap;
use parking_lot::RwLock;
@ -126,12 +125,6 @@ impl From<&CStr> for StringKey {
}
}
impl ValueIdentifiable for StringKey {
fn value_identifier(&self) -> ValueIdentifier {
ValueIdentifier::new(self.hash as usize)
}
}
/// The difference in ascii characters to translate from uppercase to lowercase.
const CAPITAL_DIFF: u8 = b'a' - b'A';

View File

@ -1,33 +0,0 @@
use std::sync::atomic::{AtomicUsize, Ordering};
/// The current index for the value counter.
static CURRENT: AtomicUsize = AtomicUsize::new(1);
/// An extremely basic way to identify a piece of data.
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[repr(C)]
pub struct ValueIdentifier(usize);
impl Default for ValueIdentifier {
fn default() -> Self {
Self(CURRENT.fetch_add(1, Ordering::SeqCst))
}
}
impl ValueIdentifier {
/// Creates an identifier by number.
pub(crate) fn new(v: usize) -> Self {
Self(v)
}
/// Get the underlying numeric integer of the value
pub fn value(&self) -> usize {
self.0
}
}
/// An object with a specific identifier.
pub trait ValueIdentifiable {
/// Get the identifier for the current object.
fn value_identifier(&self) -> ValueIdentifier;
}

View File

@ -107,7 +107,7 @@ pub fn load_types(path: &String) -> Arc<dyn TypeLibrary> {
.from_path(path.to_string() + "Types.csv")
.unwrap();
let mut type_library = TypeLibraryImpl::new(20);
let type_library = TypeLibraryImpl::new(20);
let headers = reader.headers().unwrap();
for header in headers.iter().skip(1) {
@ -135,7 +135,7 @@ pub fn load_natures(path: &String) -> Arc<dyn NatureLibrary> {
.from_path(path.to_string() + "Natures.csv")
.unwrap();
let mut nature_library = NatureLibraryImpl::new(24);
let nature_library = NatureLibraryImpl::new(24);
for record in reader.records() {
let record = record.unwrap();
let nature_name = record.get(0).unwrap().into();
@ -162,7 +162,7 @@ pub fn load_items(path: &String) -> Arc<dyn ItemLibrary> {
let json: Value = serde_json::from_str(&data).unwrap();
let json_array = json.as_array().unwrap();
let mut item_library = ItemLibraryImpl::new(400);
let 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();
@ -194,7 +194,7 @@ pub fn load_growth_rates(path: &String) -> Arc<dyn GrowthRateLibrary> {
let json: Value = serde_json::from_str(&data).unwrap();
let o = json.as_object().unwrap();
let mut growth_rate_library = GrowthRateLibraryImpl::new(10);
let growth_rate_library = GrowthRateLibraryImpl::new(10);
for (key, value) in o {
let name = StringKey::new(key);
let experience_required_json = value.as_array().unwrap();
@ -203,7 +203,7 @@ pub fn load_growth_rates(path: &String) -> Arc<dyn GrowthRateLibrary> {
experience_required.push(v.as_i64().unwrap() as u32);
}
growth_rate_library.add_growth_rate(&name, Box::new(LookupGrowthRate::new(experience_required)));
growth_rate_library.add_growth_rate(&name, Arc::new(LookupGrowthRate::new(experience_required)));
}
Arc::new(growth_rate_library)
}
@ -215,7 +215,7 @@ pub fn load_abilities(path: &String) -> Arc<dyn AbilityLibrary> {
let json: Value = serde_json::from_str(&data).unwrap();
let o = json.as_object().unwrap();
let mut ability_library = AbilityLibraryImpl::new(400);
let ability_library = AbilityLibraryImpl::new(400);
for (key, value) in o {
let name = StringKey::new(key);
let mut effect = StringKey::empty();
@ -240,7 +240,7 @@ pub fn load_moves(path: &String, types: &Arc<dyn TypeLibrary>) -> Arc<dyn MoveLi
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 = MoveLibraryImpl::new(600);
let 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();
@ -252,7 +252,7 @@ pub fn load_moves(path: &String, types: &Arc<dyn TypeLibrary>) -> Arc<dyn MoveLi
let pp = move_data.get("pp").unwrap().as_i64().unwrap() as u8;
let target = serde_json::from_value(move_data.get("target").unwrap().clone()).unwrap();
let priority = move_data.get("priority").unwrap().as_i64().unwrap() as i8;
let secondary_effect: Option<Box<dyn SecondaryEffect>> = if let Some(v) = move_data.get("effect") {
let secondary_effect: Option<Arc<dyn SecondaryEffect>> = if let Some(v) = move_data.get("effect") {
let mut chance = -1.0;
if let Some(chance_value) = v.get("chance") {
chance = chance_value.as_f64().unwrap() as f32;
@ -265,7 +265,7 @@ pub fn load_moves(path: &String, types: &Arc<dyn TypeLibrary>) -> Arc<dyn MoveLi
}
}
Some(Box::new(SecondaryEffectImpl::new(
Some(Arc::new(SecondaryEffectImpl::new(
chance,
v.get("name").unwrap().as_str().unwrap().into(),
parameters,
@ -312,7 +312,7 @@ pub fn load_species(
let json: Value = serde_json::from_str(&data).unwrap();
let o = json.as_object().unwrap();
let mut species_library = SpeciesLibraryImpl::new(800);
let species_library = SpeciesLibraryImpl::new(800);
for (key, value) in o.iter() {
if key.starts_with('$') {
continue;
@ -353,13 +353,13 @@ pub fn load_species(
}
#[cfg(not(feature = "wasm"))]
fn load_script_resolver(path: &String) -> Box<dyn ScriptResolver> {
Box::new(EmptyScriptResolver::default())
fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> {
Arc::new(EmptyScriptResolver::default())
}
#[cfg(feature = "wasm")]
fn load_script_resolver(path: &String) -> Box<dyn ScriptResolver> {
let mut resolver = pkmn_lib::script_implementations::wasm::script_resolver::WebAssemblyScriptResolver::new();
fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> {
let resolver = pkmn_lib::script_implementations::wasm::script_resolver::WebAssemblyScriptResolver::new();
let file = File::open(path.to_string() + "gen7_scripts.wasm").unwrap();
let mut reader = BufReader::new(file);
let mut buffer = Vec::new();
@ -413,12 +413,12 @@ fn parse_form(
))
}
fn parse_statistics<T>(value: &Value) -> StaticStatisticSet<T>
fn parse_statistics<T>(value: &Value) -> Arc<StaticStatisticSet<T>>
where
T: PrimInt + TryFrom<u64>,
<T as TryFrom<u64>>::Error: Debug,
{
StaticStatisticSet::new(
Arc::new(StaticStatisticSet::new(
<T as TryFrom<u64>>::try_from(value.get("hp").unwrap_or(&Value::Number(0.into())).as_u64().unwrap()).unwrap(),
<T as TryFrom<u64>>::try_from(
value
@ -454,11 +454,11 @@ where
.unwrap(),
<T as TryFrom<u64>>::try_from(value.get("speed").unwrap_or(&Value::Number(0.into())).as_u64().unwrap())
.unwrap(),
)
))
}
fn parse_moves(value: &Value, move_library: &Arc<dyn MoveLibrary>) -> Box<dyn LearnableMoves> {
let mut moves = LearnableMovesImpl::new(100);
fn parse_moves(value: &Value, move_library: &Arc<dyn MoveLibrary>) -> Arc<dyn LearnableMoves> {
let moves = LearnableMovesImpl::new(100);
let level_moves = value.get("levelMoves").unwrap().as_array().unwrap();
for level_move in level_moves {
@ -468,7 +468,7 @@ fn parse_moves(value: &Value, move_library: &Arc<dyn MoveLibrary>) -> Box<dyn Le
moves.add_level_move(level, &name).unwrap();
}
Box::new(moves)
Arc::new(moves)
}
fn parse_effect_parameter(value: &Value) -> Arc<EffectParameter> {

View File

@ -1,4 +1,5 @@
use serde::Deserialize;
use std::sync::Arc;
use pkmn_lib::dynamic_data::Battle;
use pkmn_lib::dynamic_data::{MoveChoice, PassChoice, TurnChoice};
@ -56,12 +57,12 @@ impl TestStep {
assert!(used_move.is_some());
assert!(battle
.try_set_choice(TurnChoice::Move(MoveChoice::new(
.try_set_choice(Arc::new(TurnChoice::Move(MoveChoice::new(
pokemon,
used_move.unwrap(),
target[0],
target[1],
)))
))))
.unwrap());
}
TestStep::SetPassChoice { for_pokemon } => {
@ -69,7 +70,9 @@ impl TestStep {
.as_ref()
.unwrap()
.clone();
assert!(battle.try_set_choice(TurnChoice::Pass(PassChoice::new(p))).unwrap());
assert!(battle
.try_set_choice(Arc::new(TurnChoice::Pass(PassChoice::new(p))))
.unwrap());
}
TestStep::Assert { value, expected } => {
let v = value.get(battle);

View File

@ -1,7 +1,8 @@
#![feature(custom_test_frameworks)]
#![feature(lazy_cell)]
#![allow(clippy::borrowed_box)]
use std::sync::Arc;
use std::sync::{Arc, LazyLock};
use pkmn_lib::dynamic_data::{
Battle, BattleParty, DamageSource, DynamicLibrary, ExecutingMove, MoveChoice, PokemonBuilder, PokemonParty,
@ -13,8 +14,10 @@ use crate::common::library_loader;
pub mod common;
pub mod datatests;
static LIBRARY: LazyLock<Arc<dyn DynamicLibrary>> = LazyLock::new(|| library_loader::load_library().library);
fn get_library() -> Arc<dyn DynamicLibrary> {
library_loader::load_library().library
LIBRARY.clone()
}
#[test]