From 554a665b8fbed8e8b818972a1cd262993000074b Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sat, 15 Oct 2022 11:16:41 +0200 Subject: [PATCH] FFI for Pokemon Party, make Pokemon Party use interior mutability. --- src/dynamic_data/models/battle_party.rs | 2 +- src/dynamic_data/models/pokemon_party.rs | 64 +++++++++++------ src/ffi/dynamic_data/models/mod.rs | 2 + src/ffi/dynamic_data/models/pokemon_party.rs | 74 ++++++++++++++++++++ tests/common/test_step.rs | 4 +- 5 files changed, 120 insertions(+), 26 deletions(-) create mode 100644 src/ffi/dynamic_data/models/pokemon_party.rs diff --git a/src/dynamic_data/models/battle_party.rs b/src/dynamic_data/models/battle_party.rs index ee58fd8..58a9cbc 100755 --- a/src/dynamic_data/models/battle_party.rs +++ b/src/dynamic_data/models/battle_party.rs @@ -45,7 +45,7 @@ impl BattleParty { } /// Gets a Pokemon at an index. - pub fn get_pokemon(&self, index: usize) -> &Option> { + pub fn get_pokemon(&self, index: usize) -> Option> { self.party.at(index) } diff --git a/src/dynamic_data/models/pokemon_party.rs b/src/dynamic_data/models/pokemon_party.rs index 0c65475..332ac55 100755 --- a/src/dynamic_data/models/pokemon_party.rs +++ b/src/dynamic_data/models/pokemon_party.rs @@ -1,13 +1,18 @@ +use parking_lot::lock_api::RwLockReadGuard; +use parking_lot::{RawRwLock, RwLock}; use std::sync::Arc; use crate::dynamic_data::models::pokemon::Pokemon; +use crate::{ValueIdentifiable, ValueIdentifier}; /// A list of Pokemon belonging to a trainer. #[derive(Debug)] #[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct PokemonParty { + /// A unique identifier so we know what value this is. + identifier: ValueIdentifier, /// The underlying list of Pokemon. - pokemon: Vec>>, + pokemon: RwLock>>>, } impl PokemonParty { @@ -17,42 +22,50 @@ impl PokemonParty { for _i in 0..size { pokemon.push(None); } - Self { pokemon } + Self { + identifier: Default::default(), + pokemon: RwLock::new(pokemon), + } } /// Instantiates a party with a list. pub fn new_from_vec(pokemon: Vec>>) -> Self { - Self { pokemon } + Self { + identifier: Default::default(), + pokemon: RwLock::new(pokemon), + } } /// Gets a Pokemon at an index in the party. - pub fn at(&self, index: usize) -> &Option> { - let opt = self.pokemon.get(index); + pub fn at(&self, index: usize) -> Option> { + let read_lock = self.pokemon.read(); + let opt = read_lock.get(index); if let Some(v) = opt { - v + v.clone() } else { - &None + None } } /// Swaps two Pokemon in the party around. - pub fn switch(&mut self, a: usize, b: usize) { - self.pokemon.swap(a, b); + pub fn switch(&self, a: usize, b: usize) { + self.pokemon.write().swap(a, b); } /// Sets the Pokemon at an index to a Pokemon, returning the old Pokemon. - pub fn swap_into(&mut self, index: usize, pokemon: Option>) -> Option> { - if index >= self.pokemon.len() { + pub fn swap_into(&self, index: usize, pokemon: Option>) -> Option> { + let mut party = self.pokemon.write(); + if index >= party.len() { return pokemon; } - let old = self.pokemon[index].as_ref().cloned(); - self.pokemon[index] = pokemon; + let old = party[index].as_ref().cloned(); + party[index] = pokemon; old } /// Whether or not the party still has Pokemon that can be used in battle. pub fn has_usable_pokemon(&self) -> bool { - for pokemon in self.pokemon.iter().flatten() { + for pokemon in self.pokemon.read().iter().flatten() { if pokemon.is_usable() { return true; } @@ -62,30 +75,31 @@ impl PokemonParty { /// Get the length of the underlying list of Pokemon. pub fn length(&self) -> usize { - self.pokemon.len() + self.pokemon.read().len() } /// Gets the underlying list of Pokemon. - pub fn pokemon(&self) -> &Vec>> { - &self.pokemon + pub fn pokemon(&self) -> RwLockReadGuard<'_, RawRwLock, Vec>>> { + self.pokemon.read() } /// Makes sure there are no empty spots in the party anymore, leaving the length the same. - pub fn pack_party(&mut self) { + pub fn pack_party(&self) { let mut first_empty = None; let mut i = 0; + let mut party = self.pokemon.write(); loop { - if self.pokemon[i].is_none() { + if party[i].is_none() { if first_empty.is_none() { first_empty = Some(i) } } else if first_empty.is_some() { - self.pokemon.swap(first_empty.unwrap(), i); + party.swap(first_empty.unwrap(), i); i = first_empty.unwrap(); first_empty = None; } i += 1; - if i >= self.pokemon.len() { + if i >= party.len() { break; } } @@ -93,7 +107,7 @@ impl PokemonParty { /// Checks if the party contains a given pokemon. pub fn has_pokemon(&self, pokemon: &Pokemon) -> bool { - for p in self.pokemon.iter().flatten() { + for p in self.pokemon.read().iter().flatten() { if std::ptr::eq(p.as_ref(), pokemon) { return true; } @@ -101,3 +115,9 @@ impl PokemonParty { false } } + +impl ValueIdentifiable for PokemonParty { + fn value_identifier(&self) -> ValueIdentifier { + self.identifier + } +} diff --git a/src/ffi/dynamic_data/models/mod.rs b/src/ffi/dynamic_data/models/mod.rs index b5a3c88..d79f48f 100644 --- a/src/ffi/dynamic_data/models/mod.rs +++ b/src/ffi/dynamic_data/models/mod.rs @@ -2,3 +2,5 @@ mod learned_move; /// The foreign function interface for a Pokemon. mod pokemon; +/// The foreign function interface for a party of Pokemon. +mod pokemon_party; diff --git a/src/ffi/dynamic_data/models/pokemon_party.rs b/src/ffi/dynamic_data/models/pokemon_party.rs new file mode 100644 index 0000000..ea2472a --- /dev/null +++ b/src/ffi/dynamic_data/models/pokemon_party.rs @@ -0,0 +1,74 @@ +use crate::dynamic_data::{Pokemon, PokemonParty}; +use crate::ffi::{ExternPointer, IdentifiablePointer}; +use std::sync::Arc; + +/// Instantiates a party with a set size. +#[no_mangle] +extern "C" fn pokemon_party_new(capacity: usize) -> IdentifiablePointer> { + 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>, + index: usize, +) -> IdentifiablePointer> { + if let Some(v) = ptr.as_ref().at(index) { + v.into() + } else { + IdentifiablePointer::none() + } +} + +/// Gets a Pokemon at an index in the party. +#[no_mangle] +extern "C" fn pokemon_party_switch(ptr: ExternPointer>, a: usize, b: usize) { + ptr.as_ref().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>, + index: usize, + pokemon: ExternPointer>, +) -> IdentifiablePointer> { + let pokemon = if pokemon.ptr.is_null() { + None + } else { + Some(pokemon.as_ref().clone()) + }; + if let Some(v) = ptr.as_ref().swap_into(index, pokemon) { + v.into() + } else { + IdentifiablePointer::none() + } +} + +/// 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>) -> u8 { + u8::from(ptr.as_ref().has_usable_pokemon()) +} + +/// Get the length of the underlying list of Pokemon. +#[no_mangle] +extern "C" fn pokemon_party_length(ptr: ExternPointer>) -> usize { + ptr.as_ref().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>) { + ptr.as_ref().pack_party() +} + +/// Checks if the party contains a given pokemon. +#[no_mangle] +extern "C" fn pokemon_party_has_pokemon( + ptr: ExternPointer>, + pokemon: ExternPointer>, +) -> u8 { + u8::from(ptr.as_ref().has_pokemon(pokemon.as_ref())) +} diff --git a/tests/common/test_step.rs b/tests/common/test_step.rs index 82ee85a..83a5cec 100755 --- a/tests/common/test_step.rs +++ b/tests/common/test_step.rs @@ -34,9 +34,7 @@ impl TestStep { pub fn execute(&self, battle: &mut Battle) { match self { TestStep::SetPokemon { place, from_party } => { - let p = battle.parties()[from_party[0] as usize] - .get_pokemon(from_party[1] as usize) - .clone(); + let p = battle.parties()[from_party[0] as usize].get_pokemon(from_party[1] as usize); battle.sides_mut()[place[0] as usize].set_pokemon(place[1], p); } TestStep::SetMoveChoice {