A lot more work on WASM script execution
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
f9761f61da
commit
b1890681a1
|
@ -1,4 +1,4 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::RwLock;
|
||||
|
@ -20,7 +20,7 @@ struct CommonChoiceData {
|
|||
random_value: u32,
|
||||
/// Whether or not the choice has failed. A failed choice will stop running, and execute special
|
||||
/// fail handling during turn execution.
|
||||
has_failed: bool,
|
||||
has_failed: AtomicBool,
|
||||
/// The data we can use to retrieve scripts that are affecting this choice. This will be written
|
||||
/// to once: when we need to initialize it. After that, this is only used to read. To prevent a
|
||||
/// read while we're writing to it, this is a RwLock.
|
||||
|
@ -85,13 +85,13 @@ impl TurnChoice {
|
|||
/// Gets whether or not the choice has failed. If we notice this when we execute the choice, we
|
||||
/// will not execute it.
|
||||
pub fn has_failed(&self) -> bool {
|
||||
self.choice_data().has_failed
|
||||
self.choice_data().has_failed.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// Fails the choice. This will prevent it from executing and run a specific fail handling during
|
||||
/// execution. Note that this can not be undone.
|
||||
pub fn fail(&mut self) {
|
||||
self.choice_data_mut().has_failed = true
|
||||
pub fn fail(&self) {
|
||||
self.choice_data().has_failed.store(true, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// The random value of a turn choice gets set during the start of a choice, and is used for tie
|
||||
|
@ -188,7 +188,7 @@ impl MoveChoice {
|
|||
user,
|
||||
speed: 0,
|
||||
random_value: 0,
|
||||
has_failed: false,
|
||||
has_failed: Default::default(),
|
||||
script_source_data: Default::default(),
|
||||
}),
|
||||
}
|
||||
|
@ -259,7 +259,7 @@ impl ItemChoice {
|
|||
user,
|
||||
speed: 0,
|
||||
random_value: 0,
|
||||
has_failed: false,
|
||||
has_failed: Default::default(),
|
||||
script_source_data: Default::default(),
|
||||
}),
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ impl SwitchChoice {
|
|||
user,
|
||||
speed: 0,
|
||||
random_value: 0,
|
||||
has_failed: false,
|
||||
has_failed: Default::default(),
|
||||
script_source_data: Default::default(),
|
||||
}),
|
||||
}
|
||||
|
@ -335,7 +335,7 @@ impl FleeChoice {
|
|||
user,
|
||||
speed: 0,
|
||||
random_value: 0,
|
||||
has_failed: false,
|
||||
has_failed: Default::default(),
|
||||
script_source_data: Default::default(),
|
||||
}),
|
||||
}
|
||||
|
@ -373,7 +373,7 @@ impl PassChoice {
|
|||
user,
|
||||
speed: 0,
|
||||
random_value: 0,
|
||||
has_failed: false,
|
||||
has_failed: Default::default(),
|
||||
script_source_data: Default::default(),
|
||||
}),
|
||||
}
|
||||
|
@ -405,68 +405,68 @@ impl PartialEq<Self> for TurnChoice {
|
|||
impl Eq for TurnChoice {}
|
||||
|
||||
impl PartialOrd for TurnChoice {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for TurnChoice {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
match self {
|
||||
TurnChoice::Move(data) => {
|
||||
if let TurnChoice::Move(other_data) = other {
|
||||
let priority_compare = data.priority.cmp(&other_data.priority);
|
||||
if priority_compare != Ordering::Equal {
|
||||
if priority_compare != std::cmp::Ordering::Equal {
|
||||
return priority_compare;
|
||||
}
|
||||
let speed_compare = self.speed().cmp(&other.speed());
|
||||
if speed_compare != Ordering::Equal {
|
||||
if speed_compare != std::cmp::Ordering::Equal {
|
||||
return speed_compare;
|
||||
}
|
||||
return self.random_value().cmp(&other.random_value());
|
||||
}
|
||||
Ordering::Greater
|
||||
std::cmp::Ordering::Greater
|
||||
}
|
||||
TurnChoice::Item { .. } => {
|
||||
if let TurnChoice::Move { .. } = other {
|
||||
return Ordering::Less;
|
||||
return std::cmp::Ordering::Less;
|
||||
}
|
||||
if let TurnChoice::Item { .. } = other {
|
||||
let speed_compare = self.speed().cmp(&other.speed());
|
||||
if speed_compare != Ordering::Equal {
|
||||
if speed_compare != std::cmp::Ordering::Equal {
|
||||
return speed_compare;
|
||||
}
|
||||
return self.random_value().cmp(&other.random_value());
|
||||
}
|
||||
Ordering::Greater
|
||||
std::cmp::Ordering::Greater
|
||||
}
|
||||
TurnChoice::Switch { .. } => {
|
||||
if let TurnChoice::Move { .. } = other {
|
||||
return Ordering::Less;
|
||||
return std::cmp::Ordering::Less;
|
||||
}
|
||||
if let TurnChoice::Item { .. } = other {
|
||||
return Ordering::Less;
|
||||
return std::cmp::Ordering::Less;
|
||||
}
|
||||
if let TurnChoice::Switch { .. } = other {
|
||||
let speed_compare = self.speed().cmp(&other.speed());
|
||||
if speed_compare != Ordering::Equal {
|
||||
if speed_compare != std::cmp::Ordering::Equal {
|
||||
return speed_compare;
|
||||
}
|
||||
return self.random_value().cmp(&other.random_value());
|
||||
}
|
||||
Ordering::Greater
|
||||
std::cmp::Ordering::Greater
|
||||
}
|
||||
TurnChoice::Flee { .. } => {
|
||||
if let TurnChoice::Flee { .. } = other {
|
||||
let speed_compare = self.speed().cmp(&other.speed());
|
||||
if speed_compare != Ordering::Equal {
|
||||
if speed_compare != std::cmp::Ordering::Equal {
|
||||
return speed_compare;
|
||||
}
|
||||
return self.random_value().cmp(&other.random_value());
|
||||
}
|
||||
Ordering::Less
|
||||
std::cmp::Ordering::Less
|
||||
}
|
||||
TurnChoice::Pass(..) => Ordering::Less,
|
||||
TurnChoice::Pass(..) => std::cmp::Ordering::Less,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use crate::dynamic_data::choices::TurnChoice;
|
||||
use crate::dynamic_data::Pokemon;
|
||||
use parking_lot::lock_api::MappedRwLockReadGuard;
|
||||
use parking_lot::{RawRwLock, RwLock, RwLockReadGuard};
|
||||
|
||||
/// The ChoiceQueue is used to run choices one by one.
|
||||
///
|
||||
|
@ -8,10 +10,11 @@ use crate::dynamic_data::Pokemon;
|
|||
/// helper functions to change the turn order while doing the execution. This is needed, as several
|
||||
/// moves in Pokemon actively mess with this order.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||
pub struct ChoiceQueue {
|
||||
/// Our storage of turn choices. Starts out completely filled, then slowly empties as turns get
|
||||
/// executed.
|
||||
queue: Vec<Option<TurnChoice>>,
|
||||
queue: RwLock<Vec<Option<TurnChoice>>>,
|
||||
/// The current index of the turn we need to execute next.
|
||||
current: usize,
|
||||
}
|
||||
|
@ -19,42 +22,47 @@ pub struct ChoiceQueue {
|
|||
impl ChoiceQueue {
|
||||
/// Initializes a ChoiceQueue. We expect the given queue to already be sorted here.
|
||||
pub(crate) fn new(queue: Vec<Option<TurnChoice>>) -> Self {
|
||||
Self { queue, current: 0 }
|
||||
Self {
|
||||
queue: RwLock::new(queue),
|
||||
current: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Dequeues the next turn choice to be executed. This gives ownership to the callee, and replaces
|
||||
/// our own reference to the turn choice with an empty spot. It also increments the current position
|
||||
/// by one.
|
||||
pub fn dequeue(&mut self) -> TurnChoice {
|
||||
let c = self.queue[self.current].take();
|
||||
let c = self.queue.write()[self.current].take();
|
||||
self.current += 1;
|
||||
c.unwrap()
|
||||
}
|
||||
|
||||
/// This reads what the next choice to execute will be, without modifying state.
|
||||
pub fn peek(&self) -> &TurnChoice {
|
||||
self.queue[self.current].as_ref().unwrap()
|
||||
pub fn peek(&self) -> MappedRwLockReadGuard<'_, RawRwLock, TurnChoice> {
|
||||
let read_lock = self.queue.read();
|
||||
RwLockReadGuard::map(read_lock, |a| a[self.current].as_ref().unwrap())
|
||||
}
|
||||
|
||||
/// Check if we have any choices remaining.
|
||||
pub fn has_next(&self) -> bool {
|
||||
self.current < self.queue.len()
|
||||
self.current < self.queue.read().len()
|
||||
}
|
||||
|
||||
/// This resorts the yet to be executed choices. This can be useful for dealing with situations
|
||||
/// such as Pokemon changing forms just after the very start of a turn, when turn order has
|
||||
/// technically already been decided.
|
||||
pub fn resort(&mut self) {
|
||||
let len = self.queue.len();
|
||||
self.queue[self.current..len].sort_unstable_by(|a, b| b.cmp(a));
|
||||
let len = self.queue.read().len();
|
||||
self.queue.write()[self.current..len].sort_unstable_by(|a, b| b.cmp(a));
|
||||
}
|
||||
|
||||
/// This moves the choice of a specific Pokemon up to the next choice to be executed.
|
||||
pub fn move_pokemon_choice_next(&mut self, pokemon: &Pokemon) -> bool {
|
||||
pub fn move_pokemon_choice_next(&self, pokemon: &Pokemon) -> bool {
|
||||
let mut queue_lock = self.queue.write();
|
||||
let mut desired_index = None;
|
||||
// Find the index for the choice we want to move up.
|
||||
for index in self.current..self.queue.len() {
|
||||
if let Some(choice) = &self.queue[index] {
|
||||
for index in self.current..queue_lock.len() {
|
||||
if let Some(choice) = &queue_lock[index] {
|
||||
if std::ptr::eq(choice.user().as_ref(), pokemon) {
|
||||
desired_index = Some(index);
|
||||
break;
|
||||
|
@ -72,19 +80,20 @@ impl ChoiceQueue {
|
|||
}
|
||||
|
||||
// Take the choice we want to move forward out of it's place.
|
||||
let choice = self.queue[desired_index].take().unwrap();
|
||||
let choice = queue_lock[desired_index].take().unwrap();
|
||||
// Iterate backwards from the spot before the choice we want to move up, push them all back
|
||||
// by 1 spot.
|
||||
for index in (desired_index - 1)..self.current {
|
||||
self.queue.swap(index, index + 1);
|
||||
queue_lock.swap(index, index + 1);
|
||||
}
|
||||
// Place the choice that needs to be next in the next to be executed position.
|
||||
let _ = self.queue[self.current].insert(choice);
|
||||
let _ = queue_lock[self.current].insert(choice);
|
||||
true
|
||||
}
|
||||
|
||||
/// Internal helper function to be easily able to iterate over the yet to be executed choices.
|
||||
pub(crate) fn get_queue(&self) -> &[Option<TurnChoice>] {
|
||||
&self.queue[self.current..self.queue.len()]
|
||||
pub(crate) fn get_queue(&self) -> MappedRwLockReadGuard<'_, RawRwLock, [Option<TurnChoice>]> {
|
||||
let read_lock = self.queue.read();
|
||||
RwLockReadGuard::map(read_lock, |a| &a[self.current..self.queue.read().len()])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ use crate::dynamic_data::libraries::battle_stat_calculator::BattleStatCalculator
|
|||
use crate::dynamic_data::libraries::damage_library::DamageLibrary;
|
||||
use crate::dynamic_data::libraries::misc_library::MiscLibrary;
|
||||
use crate::dynamic_data::libraries::script_resolver::ScriptCategory;
|
||||
use crate::dynamic_data::Script;
|
||||
use crate::dynamic_data::{ItemScript, ScriptResolver};
|
||||
use crate::dynamic_data::{Script, ScriptOwnerData};
|
||||
use crate::static_data::Item;
|
||||
use crate::static_data::StaticData;
|
||||
use crate::{PkmnResult, StringKey};
|
||||
|
@ -75,7 +75,7 @@ impl DynamicLibrary {
|
|||
/// can be created with this combination, returns None.
|
||||
pub fn load_script(
|
||||
&self,
|
||||
owner: *const u8,
|
||||
owner: ScriptOwnerData,
|
||||
_category: ScriptCategory,
|
||||
_key: &StringKey,
|
||||
) -> PkmnResult<Option<Arc<dyn Script>>> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::dynamic_data::{ItemScript, Script};
|
||||
use crate::dynamic_data::{ItemScript, Script, ScriptOwnerData};
|
||||
use crate::static_data::Item;
|
||||
use crate::{PkmnResult, StringKey};
|
||||
|
||||
|
@ -13,7 +13,7 @@ pub trait ScriptResolver: Debug {
|
|||
/// can be created with this combination, returns None.
|
||||
fn load_script(
|
||||
&self,
|
||||
owner: *const u8,
|
||||
owner: ScriptOwnerData,
|
||||
category: ScriptCategory,
|
||||
script_key: &StringKey,
|
||||
) -> PkmnResult<Option<Arc<dyn Script>>>;
|
||||
|
@ -58,7 +58,7 @@ pub struct EmptyScriptResolver {}
|
|||
impl ScriptResolver for EmptyScriptResolver {
|
||||
fn load_script(
|
||||
&self,
|
||||
_owner: *const u8,
|
||||
_owner: ScriptOwnerData,
|
||||
_category: ScriptCategory,
|
||||
_script_key: &StringKey,
|
||||
) -> PkmnResult<Option<Arc<dyn Script>>> {
|
||||
|
|
|
@ -194,7 +194,7 @@ impl Battle {
|
|||
let mut winning_side = None;
|
||||
for (side_index, side) in self.sides.iter().enumerate() {
|
||||
// If any side has fled, the battle end.
|
||||
if side.has_fled() {
|
||||
if side.has_fled_battle() {
|
||||
let _w = self.result.write();
|
||||
unsafe {
|
||||
self.result.data_ptr().replace(BattleResult::Inconclusive);
|
||||
|
@ -339,8 +339,7 @@ impl VolatileScriptsOwner for Battle {
|
|||
}
|
||||
|
||||
fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>> {
|
||||
self.library
|
||||
.load_script((self as *const Self).cast(), ScriptCategory::Battle, key)
|
||||
self.library.load_script(self.into(), ScriptCategory::Battle, key)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::dynamic_data::models::pokemon_party::PokemonParty;
|
|||
/// A battle party is a wrapper around a party, with the indices for which the party is responsible
|
||||
/// on the field attached.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||
pub struct BattleParty {
|
||||
/// The party the BattleParty is holding.
|
||||
party: Arc<PokemonParty>,
|
||||
|
@ -47,4 +48,9 @@ impl BattleParty {
|
|||
pub fn get_pokemon(&self, index: usize) -> &Option<Arc<Pokemon>> {
|
||||
self.party.at(index)
|
||||
}
|
||||
|
||||
/// Gets the underlying Pokemon Party
|
||||
pub fn party(&self) -> &Arc<PokemonParty> {
|
||||
&self.party
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use crate::utils::Random;
|
|||
|
||||
/// The RNG for a battle.
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||
pub struct BattleRandom {
|
||||
/// The actual underlying RNG. This is in a mutex, so it is thread safe, and can be ran
|
||||
/// predictably, with guaranteed the same outputs.
|
||||
|
|
|
@ -18,6 +18,7 @@ use crate::{script_hook, PkmnResult, StringKey};
|
|||
|
||||
/// A side on a battle.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||
pub struct BattleSide {
|
||||
/// The index of the side on the battle.
|
||||
index: u8,
|
||||
|
@ -239,11 +240,6 @@ impl BattleSide {
|
|||
true
|
||||
}
|
||||
|
||||
/// Checks whether the side has fled.
|
||||
pub fn has_fled(&self) -> bool {
|
||||
self.has_fled_battle
|
||||
}
|
||||
|
||||
/// Mark the side as fled.
|
||||
pub fn mark_as_fled(&mut self) {
|
||||
self.has_fled_battle = true;
|
||||
|
@ -304,7 +300,7 @@ impl VolatileScriptsOwner for BattleSide {
|
|||
fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>> {
|
||||
self.battle()
|
||||
.library()
|
||||
.load_script((self as *const Self).cast(), crate::ScriptCategory::Side, key)
|
||||
.load_script(self.into(), crate::ScriptCategory::Side, key)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ use crate::{PkmnResult, PokemonError};
|
|||
|
||||
/// A hit data is the data for a single hit, on a single target.
|
||||
#[derive(Default, Debug)]
|
||||
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||
pub struct HitData {
|
||||
/// Whether or not the hit is critical.
|
||||
critical: AtomicBool,
|
||||
|
@ -158,7 +159,7 @@ impl ExecutingMove {
|
|||
}
|
||||
|
||||
/// Gets a hit data for a target, with a specific index.
|
||||
pub fn get_hit_data<'func>(&'func self, for_target: &'func Arc<Pokemon>, hit: u8) -> PkmnResult<&'func HitData> {
|
||||
pub fn get_hit_data(&self, for_target: &Pokemon, hit: u8) -> PkmnResult<&HitData> {
|
||||
for (index, target) in self.targets.iter().enumerate() {
|
||||
if let Some(target) = target {
|
||||
if std::ptr::eq(target.deref().deref(), for_target.deref().deref()) {
|
||||
|
|
|
@ -502,11 +502,7 @@ impl Pokemon {
|
|||
|
||||
let ability_script = self
|
||||
.library
|
||||
.load_script(
|
||||
(self as *const Self).cast(),
|
||||
ScriptCategory::Ability,
|
||||
self.active_ability().name(),
|
||||
)
|
||||
.load_script((&*self).into(), ScriptCategory::Ability, self.active_ability().name())
|
||||
.unwrap();
|
||||
if let Some(ability_script) = ability_script {
|
||||
self.ability_script
|
||||
|
@ -772,8 +768,7 @@ impl VolatileScriptsOwner for Pokemon {
|
|||
}
|
||||
|
||||
fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>> {
|
||||
self.library
|
||||
.load_script((self as *const Self).cast(), ScriptCategory::Pokemon, key)
|
||||
self.library.load_script(self.into(), ScriptCategory::Pokemon, key)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::dynamic_data::models::pokemon::Pokemon;
|
|||
|
||||
/// A list of Pokemon belonging to a trainer.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||
pub struct PokemonParty {
|
||||
/// The underlying list of Pokemon.
|
||||
pokemon: Vec<Option<Arc<Pokemon>>>,
|
||||
|
@ -89,4 +90,16 @@ impl PokemonParty {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the party contains a given pokemon.
|
||||
pub fn has_pokemon(&self, pokemon: &Pokemon) -> bool {
|
||||
for p in &self.pokemon {
|
||||
if let Some(p) = p {
|
||||
if std::ptr::eq(p.as_ref(), pokemon) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
use std::any::Any;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread::JoinHandle;
|
||||
|
||||
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
|
||||
|
||||
use crate::dynamic_data::choices::TurnChoice;
|
||||
use crate::dynamic_data::DamageSource;
|
||||
use crate::dynamic_data::ExecutingMove;
|
||||
use crate::dynamic_data::Pokemon;
|
||||
use crate::dynamic_data::{Battle, DynamicLibrary};
|
||||
use crate::dynamic_data::{BattleSide, DamageSource};
|
||||
use crate::static_data::{EffectParameter, TypeIdentifier};
|
||||
use crate::static_data::{Item, Statistic};
|
||||
use crate::StringKey;
|
||||
|
@ -543,3 +543,33 @@ mod tests {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Data to store references to their owning objects on scripts.
|
||||
pub enum ScriptOwnerData {
|
||||
/// A script attached to a Pokemon has a reference to that Pokemon.
|
||||
Pokemon(AtomicPtr<Pokemon>),
|
||||
/// A script attached to a Battle Side has a reference to that Battle Side.
|
||||
BattleSide(AtomicPtr<BattleSide>),
|
||||
/// A script attached to a Battle has a reference to that Battle.
|
||||
Battle(AtomicPtr<Battle>),
|
||||
/// A script also can have no owner.
|
||||
None,
|
||||
}
|
||||
|
||||
impl Into<ScriptOwnerData> for &Pokemon {
|
||||
fn into(self) -> ScriptOwnerData {
|
||||
ScriptOwnerData::Pokemon(AtomicPtr::new(self as *const Pokemon as *mut Pokemon))
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<ScriptOwnerData> for &BattleSide {
|
||||
fn into(self) -> ScriptOwnerData {
|
||||
ScriptOwnerData::BattleSide(AtomicPtr::new(self as *const BattleSide as *mut BattleSide))
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<ScriptOwnerData> for &Battle {
|
||||
fn into(self) -> ScriptOwnerData {
|
||||
ScriptOwnerData::Battle(AtomicPtr::new(self as *const Battle as *mut Battle))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,13 +22,19 @@ pub trait VolatileScriptsOwner {
|
|||
}
|
||||
|
||||
/// Adds a volatile script by name.
|
||||
fn add_volatile_script(&mut self, key: &StringKey) -> PkmnResult<Option<ScriptContainer>> {
|
||||
fn add_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<ScriptContainer>> {
|
||||
self.volatile_scripts()
|
||||
.stack_or_add(key, &|| self.load_volatile_script(key))
|
||||
}
|
||||
|
||||
/// Adds a volatile script by name.
|
||||
fn add_volatile_script_with_script(&self, script: Arc<dyn Script>) -> PkmnResult<Option<ScriptContainer>> {
|
||||
self.volatile_scripts()
|
||||
.stack_or_add(&script.name().clone(), &|| Ok(Some(script.clone())))
|
||||
}
|
||||
|
||||
/// Removes a volatile script by name.
|
||||
fn remove_volatile_script(&mut self, key: &StringKey) {
|
||||
fn remove_volatile_script(&self, key: &StringKey) {
|
||||
self.volatile_scripts().remove(key)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
use crate::dynamic_data::{
|
||||
Battle, BattleParty, BattleRandom, BattleSide, ChoiceQueue, DynamicLibrary, Pokemon, PokemonParty,
|
||||
};
|
||||
use crate::script_implementations::wasm::export_registry::register;
|
||||
use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef};
|
||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||
use wasmer::FunctionEnvMut;
|
||||
|
||||
register! {
|
||||
fn battle_get_parties(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
battle: ExternRef<Battle>,
|
||||
) -> VecExternRef<BattleParty> {
|
||||
VecExternRef::new(env.data().data().as_ref(), battle.value_func(&env).unwrap().parties())
|
||||
}
|
||||
|
||||
fn battle_get_choice_queue(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
battle: ExternRef<Battle>,
|
||||
) -> ExternRef<ChoiceQueue> {
|
||||
let queue = battle.value_func(&env).unwrap().current_turn_queue().read();
|
||||
if let Some(queue) = queue.as_ref() {
|
||||
ExternRef::func_new(&env, queue)
|
||||
} else {
|
||||
ExternRef::null()
|
||||
}
|
||||
}
|
||||
|
||||
fn battle_get_library(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
battle: ExternRef<Battle>,
|
||||
) -> ExternRef<DynamicLibrary> {
|
||||
ExternRef::func_new(&env, battle.value_func(&env).unwrap().library().as_ref())
|
||||
}
|
||||
|
||||
fn battle_get_sides(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
battle: ExternRef<Battle>,
|
||||
) -> VecExternRef<BattleSide> {
|
||||
VecExternRef::new(env.data().data().as_ref(), battle.value_func(&env).unwrap().sides())
|
||||
}
|
||||
|
||||
fn battle_get_random(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
battle: ExternRef<Battle>,
|
||||
) -> ExternRef<BattleRandom> {
|
||||
ExternRef::func_new(&env, battle.value_func(&env).unwrap().random())
|
||||
}
|
||||
|
||||
fn battle_find_party_for_pokemon(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
battle: ExternRef<Battle>,
|
||||
pokemon: ExternRef<Pokemon>
|
||||
) -> ExternRef<BattleParty> {
|
||||
let battle = battle.value_func(&env).unwrap();
|
||||
let pokemon = pokemon.value_func(&env).unwrap();
|
||||
for party in battle.parties() {
|
||||
if party.party().has_pokemon(pokemon) {
|
||||
return ExternRef::func_new(&env, party);
|
||||
}
|
||||
}
|
||||
ExternRef::null()
|
||||
}
|
||||
|
||||
fn battle_party_get_party(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
battle_party: ExternRef<BattleParty>,
|
||||
) -> ExternRef<PokemonParty> {
|
||||
ExternRef::func_new(&env, battle_party.value_func(&env).unwrap().party().as_ref())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
use crate::dynamic_data::BattleRandom;
|
||||
use crate::script_implementations::wasm::export_registry::register;
|
||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||
use wasmer::FunctionEnvMut;
|
||||
|
||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||
|
||||
register! {
|
||||
fn battle_random_get(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
battle_random: ExternRef<BattleRandom>,
|
||||
) -> i32 {
|
||||
battle_random.value_func(&env).unwrap().get()
|
||||
}
|
||||
fn battle_random_get_max(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
battle_random: ExternRef<BattleRandom>,
|
||||
max: i32
|
||||
) -> i32 {
|
||||
battle_random.value_func(&env).unwrap().get_max(max)
|
||||
}
|
||||
fn battle_random_get_between(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
battle_random: ExternRef<BattleRandom>,
|
||||
min: i32,
|
||||
max: i32
|
||||
) -> i32 {
|
||||
battle_random.value_func(&env).unwrap().get_between(min, max)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
use crate::dynamic_data::{Battle, BattleSide, Pokemon, VolatileScriptsOwner};
|
||||
use crate::script_implementations::wasm::export_registry::register;
|
||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||
use crate::script_implementations::wasm::script::WebAssemblyScript;
|
||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||
use crate::{ScriptCategory, StringKey};
|
||||
use std::ffi::CString;
|
||||
use wasmer::{FunctionEnvMut, Value};
|
||||
|
||||
register! {
|
||||
fn battleside_has_fled_battle(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
side: ExternRef<BattleSide>,
|
||||
) -> u8 {
|
||||
if side.value_func(&env).unwrap().has_fled_battle() { 1 } else { 0 }
|
||||
}
|
||||
|
||||
fn battleside_is_defeated(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
side: ExternRef<BattleSide>,
|
||||
) -> u8 {
|
||||
if side.value_func(&env).unwrap().is_defeated() { 1 } else { 0 }
|
||||
}
|
||||
|
||||
fn battleside_get_side_index(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
side: ExternRef<BattleSide>,
|
||||
) -> u8 {
|
||||
side.value_func(&env).unwrap().index()
|
||||
}
|
||||
|
||||
fn battleside_get_pokemon_per_side(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
side: ExternRef<BattleSide>,
|
||||
) -> u8 {
|
||||
side.value_func(&env).unwrap().pokemon_per_side()
|
||||
}
|
||||
|
||||
fn battleside_get_battle(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
side: ExternRef<BattleSide>,
|
||||
) -> ExternRef<Battle> {
|
||||
ExternRef::func_new(&env, side.value_func(&env).unwrap().battle())
|
||||
}
|
||||
|
||||
fn battleside_get_pokemon(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
side: ExternRef<BattleSide>,
|
||||
index: u32
|
||||
) -> ExternRef<Pokemon> {
|
||||
if let Some(Some(p)) = side.value_func(&env).unwrap().pokemon().get(index as usize) {
|
||||
ExternRef::func_new(&env, p.as_ref())
|
||||
} else {
|
||||
ExternRef::null()
|
||||
}
|
||||
}
|
||||
|
||||
fn battleside_add_volatile_by_name(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
side: ExternRef<BattleSide>,
|
||||
name_ptr: u32
|
||||
) -> u32 {
|
||||
unsafe {
|
||||
let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr));
|
||||
let script = side.value_func(&env).unwrap().add_volatile_script(&c_name.into()).unwrap();
|
||||
if let Some(script) = script {
|
||||
let script = script.get_as::<WebAssemblyScript>();
|
||||
script.get_wasm_pointer()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn battleside_add_volatile(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
side: ExternRef<BattleSide>,
|
||||
script_ptr: u32
|
||||
) -> u32 {
|
||||
let side = side.value_func(&env).unwrap();
|
||||
let name_ptr = env.data().data().exported_functions().get(&StringKey::new("script_get_name")).unwrap().call(&mut env.data().data().store_mut(), &[Value::I32(script_ptr as i32)]).unwrap().get(0).unwrap().i32().unwrap() as u32;
|
||||
unsafe{
|
||||
let name_ptr: CString = CString::from_raw(env.data().data().get_raw_pointer(name_ptr));
|
||||
let script = env.data().data().setup_script(script_ptr, ScriptCategory::Side, &name_ptr.into(), side.into()).unwrap();
|
||||
if let Some(script) = script {
|
||||
let script = side.add_volatile_script_with_script(script);
|
||||
script.unwrap().unwrap().get_as::<WebAssemblyScript>().get_wasm_pointer()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn battleside_get_volatile(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
side: ExternRef<BattleSide>,
|
||||
name_ptr: u32
|
||||
) -> u32 {
|
||||
unsafe {
|
||||
let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr));
|
||||
let script = side.value_func(&env).unwrap().get_volatile_script(&c_name.into());
|
||||
if let Some(script) = script {
|
||||
let script = script.get_as::<WebAssemblyScript>();
|
||||
script.get_wasm_pointer()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn battleside_remove_volatile(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
side: ExternRef<BattleSide>,
|
||||
name_ptr: u32
|
||||
) {
|
||||
unsafe {
|
||||
let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr));
|
||||
side.value_func(&env).unwrap().remove_volatile_script(&c_name.into());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
use crate::dynamic_data::{ChoiceQueue, Pokemon};
|
||||
use crate::script_implementations::wasm::export_registry::register;
|
||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||
use wasmer::FunctionEnvMut;
|
||||
|
||||
register! {
|
||||
fn choice_queue_move_pokemon_choice_next(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
battle_random: ExternRef<ChoiceQueue>,
|
||||
pokemon: ExternRef<Pokemon>
|
||||
) -> u8 {
|
||||
if battle_random.value_func(&env).unwrap().move_pokemon_choice_next(pokemon.value_func(&env).unwrap()) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
use crate::dynamic_data::{ExecutingMove, HitData, LearnedMove, Pokemon};
|
||||
use crate::script_implementations::wasm::export_registry::register;
|
||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||
use crate::static_data::MoveData;
|
||||
use wasmer::FunctionEnvMut;
|
||||
|
||||
register! {
|
||||
fn executing_move_get_user(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
executing_move: ExternRef<ExecutingMove>,
|
||||
) -> ExternRef<Pokemon> {
|
||||
ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().user().as_ref())
|
||||
}
|
||||
|
||||
fn executing_move_get_use_move(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
executing_move: ExternRef<ExecutingMove>,
|
||||
) -> ExternRef<MoveData> {
|
||||
ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().use_move().as_ref())
|
||||
}
|
||||
|
||||
fn executing_move_get_chosen_move(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
executing_move: ExternRef<ExecutingMove>,
|
||||
) -> ExternRef<LearnedMove> {
|
||||
ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().chosen_move().as_ref())
|
||||
}
|
||||
|
||||
fn executing_move_get_number_of_hits(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
executing_move: ExternRef<ExecutingMove>,
|
||||
) -> u8 {
|
||||
executing_move.value_func(&env).unwrap().number_of_hits()
|
||||
}
|
||||
|
||||
fn executing_move_get_hit_data(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
executing_move: ExternRef<ExecutingMove>,
|
||||
target: ExternRef<Pokemon>,
|
||||
hit: u8
|
||||
) -> ExternRef<HitData> {
|
||||
ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().get_hit_data(target.value_func(&env).unwrap(), hit).unwrap())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
use crate::dynamic_data::HitData;
|
||||
use crate::script_implementations::wasm::export_registry::register;
|
||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||
use wasmer::FunctionEnvMut;
|
||||
|
||||
register! {
|
||||
fn hit_data_fail(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
hit: ExternRef<HitData>,
|
||||
) {
|
||||
hit.value_func(&env).unwrap().fail()
|
||||
}
|
||||
}
|
0
src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs
Normal file → Executable file
0
src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs
Normal file → Executable file
48
src/script_implementations/wasm/export_registry/dynamic_data/mod.rs
Normal file → Executable file
48
src/script_implementations/wasm/export_registry/dynamic_data/mod.rs
Normal file → Executable file
|
@ -1,31 +1,67 @@
|
|||
use crate::dynamic_data::DynamicLibrary;
|
||||
use crate::dynamic_data::{DynamicLibrary, ScriptOwnerData};
|
||||
use crate::script_implementations::wasm::export_registry::register;
|
||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||
use std::sync::atomic::Ordering;
|
||||
use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut};
|
||||
|
||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||
use crate::static_data::StaticData;
|
||||
|
||||
mod battle_random;
|
||||
/// Battle side registration
|
||||
mod battle_side;
|
||||
mod choice_queue;
|
||||
mod executing_move;
|
||||
/// Learned move registration
|
||||
mod learned_move;
|
||||
mod party;
|
||||
/// Pokemon registration
|
||||
mod pokemon;
|
||||
/// Turn choice registration
|
||||
mod turn_choice;
|
||||
mod hit_data;
|
||||
mod battle;
|
||||
|
||||
register! {
|
||||
fn dynamic_library_get_static_data(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
dynamic_lib: ExternRef<DynamicLibrary>,
|
||||
) -> ExternRef<StaticData> {
|
||||
ExternRef::func_new(&env, dynamic_lib.value_func(&env).unwrap().static_data())
|
||||
}
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
dynamic_lib: ExternRef<DynamicLibrary>,
|
||||
) -> ExternRef<StaticData> {
|
||||
ExternRef::func_new(&env, dynamic_lib.value_func(&env).unwrap().static_data())
|
||||
}
|
||||
|
||||
fn script_get_owner(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
script: u32,
|
||||
) -> u32 {
|
||||
unsafe {
|
||||
let script = env.data().data().get_loaded_script(script);
|
||||
if let Some(script) = script {
|
||||
match script.get_owner() {
|
||||
ScriptOwnerData::Pokemon(p) => env.data().data().get_extern_ref_index(p.load(Ordering::Relaxed).as_ref().unwrap()),
|
||||
ScriptOwnerData::BattleSide(p) => env.data().data().get_extern_ref_index(p.load(Ordering::Relaxed).as_ref().unwrap()),
|
||||
ScriptOwnerData::Battle(p) => env.data().data().get_extern_ref_index(p.load(Ordering::Relaxed).as_ref().unwrap()),
|
||||
ScriptOwnerData::None => 0,
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manual manual_register
|
||||
}
|
||||
|
||||
/// Additional required manual registration
|
||||
fn manual_register(imports: &mut Imports, store: &mut StoreMut, env: &FunctionEnv<WebAssemblyEnv>) {
|
||||
battle::register(imports, store, env);
|
||||
turn_choice::register(imports, store, env);
|
||||
pokemon::register(imports, store, env);
|
||||
learned_move::register(imports, store, env);
|
||||
battle_side::register(imports, store, env);
|
||||
battle_random::register(imports, store, env);
|
||||
choice_queue::register(imports, store, env);
|
||||
party::register(imports, store, env);
|
||||
executing_move::register(imports, store, env);
|
||||
hit_data::register(imports, store, env);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
use crate::dynamic_data::{Pokemon, PokemonParty};
|
||||
use crate::script_implementations::wasm::export_registry::register;
|
||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||
use wasmer::FunctionEnvMut;
|
||||
|
||||
register! {
|
||||
fn party_get_pokemon(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
party: ExternRef<PokemonParty>,
|
||||
index: u32
|
||||
) -> ExternRef<Pokemon> {
|
||||
if let Some(v) = &party.value_func(&env).unwrap().pokemon()[index as usize] {
|
||||
ExternRef::func_new(&env, v.as_ref())
|
||||
} else {
|
||||
ExternRef::null()
|
||||
}
|
||||
}
|
||||
|
||||
fn party_get_length(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
party: ExternRef<PokemonParty>,
|
||||
) -> u32 {
|
||||
party.value_func(&env).unwrap().length() as u32
|
||||
}
|
||||
}
|
70
src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs
Normal file → Executable file
70
src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs
Normal file → Executable file
|
@ -1,11 +1,11 @@
|
|||
use std::mem::transmute;
|
||||
|
||||
use crate::dynamic_data::{DynamicLibrary, Pokemon};
|
||||
use crate::dynamic_data::{Battle, DynamicLibrary, LearnedMove, Pokemon};
|
||||
use crate::script_implementations::wasm::export_registry::register;
|
||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||
use crate::static_data::StatisticSet;
|
||||
use crate::static_data::{ClampedStatisticSet, Species};
|
||||
use crate::static_data::{Item, StatisticSet};
|
||||
use wasmer::FunctionEnvMut;
|
||||
|
||||
register! {
|
||||
|
@ -75,4 +75,70 @@ register! {
|
|||
pokemon.value_func(&env).unwrap().damage(damage, transmute(source));
|
||||
}
|
||||
}
|
||||
|
||||
fn pokemon_get_learned_move(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
pokemon: ExternRef<Pokemon>,
|
||||
index: u32
|
||||
) -> ExternRef<LearnedMove> {
|
||||
let read_lock = pokemon.value_func(&env).unwrap().learned_moves().read();
|
||||
let mv = read_lock.get(index as usize);
|
||||
if let Some(Some(mv)) = mv {
|
||||
ExternRef::func_new(&env, mv)
|
||||
}
|
||||
else{
|
||||
ExternRef::null()
|
||||
}
|
||||
}
|
||||
|
||||
fn pokemon_change_stat_boost(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
pokemon: ExternRef<Pokemon>,
|
||||
stat: u8,
|
||||
amount: i8,
|
||||
self_inflicted: u8
|
||||
) -> u8 {
|
||||
unsafe{
|
||||
if pokemon.value_func(&env).unwrap().change_stat_boost(transmute(stat), amount, self_inflicted == 1) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pokemon_get_battle(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
pokemon: ExternRef<Pokemon>,
|
||||
) -> ExternRef<Battle> {
|
||||
if let Some(battle) = pokemon.value_func(&env).unwrap().get_battle() {
|
||||
ExternRef::func_new(&env, battle)
|
||||
} else {
|
||||
ExternRef::null()
|
||||
}
|
||||
}
|
||||
|
||||
fn pokemon_get_battle_side_index(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
pokemon: ExternRef<Pokemon>,
|
||||
) -> u8 {
|
||||
if let Some(i) = pokemon.value_func(&env).unwrap().get_battle_index() {
|
||||
i
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn pokemon_get_held_item(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
pokemon: ExternRef<Pokemon>,
|
||||
) -> ExternRef<Item> {
|
||||
let read_lock = pokemon.value_func(&env).unwrap().held_item().read();
|
||||
if let Some(item) = read_lock.as_ref() {
|
||||
ExternRef::func_new(&env, item.as_ref())
|
||||
} else {
|
||||
ExternRef::null()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
7
src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs
Normal file → Executable file
7
src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs
Normal file → Executable file
|
@ -59,4 +59,11 @@ register! {
|
|||
panic!("Invalid turn choice");
|
||||
}
|
||||
|
||||
fn turn_choice_fail(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
turn_choice: ExternRef<TurnChoice>,
|
||||
) {
|
||||
turn_choice.value_func(&env).unwrap().fail();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
use crate::script_implementations::wasm::export_registry::register;
|
||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||
use crate::static_data::Item;
|
||||
use crate::StringKey;
|
||||
use std::mem::transmute;
|
||||
use wasmer::FunctionEnvMut;
|
||||
|
||||
register! {
|
||||
fn item_get_price(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
item: ExternRef<Item>,
|
||||
) -> i32 {
|
||||
item.value_func(&env).unwrap().price()
|
||||
}
|
||||
|
||||
fn item_get_name(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
item: ExternRef<Item>,
|
||||
) -> ExternRef<StringKey> {
|
||||
ExternRef::func_new(&env, item.value_func(&env).unwrap().name())
|
||||
}
|
||||
|
||||
fn item_get_category(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
item: ExternRef<Item>,
|
||||
) -> u8 {
|
||||
unsafe { transmute(item.value_func(&env).unwrap().category()) }
|
||||
|
||||
}
|
||||
|
||||
fn item_get_battle_category(
|
||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||
item: ExternRef<Item>,
|
||||
) -> u8 {
|
||||
unsafe { transmute(item.value_func(&env).unwrap().battle_category()) }
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use crate::script_implementations::wasm::extern_ref::ExternRef;
|
|||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||
use crate::static_data::{ItemLibrary, LibrarySettings, MoveLibrary, SpeciesLibrary, StaticData, TypeLibrary};
|
||||
|
||||
mod item;
|
||||
/// Moves data registration
|
||||
mod moves;
|
||||
/// Species data registration
|
||||
|
@ -49,4 +50,5 @@ register! {
|
|||
fn manual_registration(imports: &mut Imports, store: &mut StoreMut, env: &FunctionEnv<WebAssemblyEnv>) {
|
||||
moves::register(imports, store, env);
|
||||
species::register(imports, store, env);
|
||||
item::register(imports, store, env);
|
||||
}
|
||||
|
|
0
src/script_implementations/wasm/export_registry/static_data/moves.rs
Normal file → Executable file
0
src/script_implementations/wasm/export_registry/static_data/moves.rs
Normal file → Executable file
0
src/script_implementations/wasm/export_registry/static_data/species.rs
Normal file → Executable file
0
src/script_implementations/wasm/export_registry/static_data/species.rs
Normal file → Executable file
|
@ -1,10 +1,12 @@
|
|||
use std::any::Any;
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize};
|
||||
use std::sync::Arc;
|
||||
|
||||
use hashbrown::HashSet;
|
||||
|
||||
use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, ExecutingMove, Pokemon, Script, TurnChoice};
|
||||
use crate::dynamic_data::{
|
||||
Battle, DamageSource, DynamicLibrary, ExecutingMove, Pokemon, Script, ScriptOwnerData, TurnChoice,
|
||||
};
|
||||
use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef};
|
||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData;
|
||||
use crate::script_implementations::wasm::WebAssemblyScriptCapabilities;
|
||||
|
@ -23,7 +25,7 @@ pub struct WebAssemblyScript {
|
|||
/// we will not execute its methods. This holds the number of suppressions on the script.
|
||||
suppressed_count: AtomicUsize,
|
||||
/// The owner of this script (where the script is attached to)
|
||||
_owner_ptr: AtomicPtr<u8>,
|
||||
owner: ScriptOwnerData,
|
||||
/// Pointer inside WebAssembly memory where the data is for this script.
|
||||
self_ptr: u32,
|
||||
/// Capabilities define which functions we actually implement.
|
||||
|
@ -35,7 +37,7 @@ pub struct WebAssemblyScript {
|
|||
impl WebAssemblyScript {
|
||||
/// Instantiates a new WebAssemblyScript.
|
||||
pub fn new(
|
||||
owner_ptr: *mut u8,
|
||||
owner: ScriptOwnerData,
|
||||
self_ptr: u32,
|
||||
capabilities: Arc<HashSet<WebAssemblyScriptCapabilities>>,
|
||||
environment: Arc<WebAssemblyEnvironmentData>,
|
||||
|
@ -45,18 +47,27 @@ impl WebAssemblyScript {
|
|||
name,
|
||||
marked_for_deletion: Default::default(),
|
||||
suppressed_count: Default::default(),
|
||||
_owner_ptr: AtomicPtr::new(owner_ptr),
|
||||
owner,
|
||||
self_ptr,
|
||||
capabilities,
|
||||
environment,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_wasm_pointer(&self) -> u32 {
|
||||
self.self_ptr
|
||||
}
|
||||
|
||||
/// Check if this script implements a certain capability.
|
||||
#[inline(always)]
|
||||
fn has_capability(&self, cap: &WebAssemblyScriptCapabilities) -> bool {
|
||||
self.capabilities.contains(cap)
|
||||
}
|
||||
|
||||
/// Gets the thing the script is owned by.
|
||||
pub fn get_owner(&self) -> &ScriptOwnerData {
|
||||
&self.owner
|
||||
}
|
||||
}
|
||||
|
||||
/// Util macro to reduce function call verbosity.
|
||||
|
|
|
@ -11,7 +11,7 @@ use wasmer::{
|
|||
Memory, Module, Store, StoreMut, StoreRef, TypedFunction, Value,
|
||||
};
|
||||
|
||||
use crate::dynamic_data::{ItemScript, Script, ScriptResolver};
|
||||
use crate::dynamic_data::{ItemScript, Script, ScriptOwnerData, ScriptResolver};
|
||||
use crate::script_implementations::wasm::export_registry::register_webassembly_funcs;
|
||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||
use crate::script_implementations::wasm::script::WebAssemblyScript;
|
||||
|
@ -34,10 +34,6 @@ pub struct WebAssemblyScriptResolver {
|
|||
/// This is the WASM function to load a script.
|
||||
load_script_fn: Option<TypedFunction<(u8, ExternRef<StringKey>), u32>>,
|
||||
|
||||
/// Script capabilities tell us which functions are implemented on a given script. This allows us to skip unneeded
|
||||
/// WASM calls.
|
||||
script_capabilities: RwLock<HashMap<ScriptCapabilitiesKey, Arc<HashSet<WebAssemblyScriptCapabilities>>>>,
|
||||
|
||||
/// The data for use in the scripting function calls.
|
||||
environment_data: Arc<WebAssemblyEnvironmentData>,
|
||||
}
|
||||
|
@ -63,13 +59,15 @@ impl WebAssemblyScriptResolver {
|
|||
let store_ptr: *mut Store = store.as_mut();
|
||||
forget(store);
|
||||
|
||||
let environment = Arc::new(WebAssemblyEnvironmentData::new(store_ptr));
|
||||
environment.self_arc.write().replace(Arc::downgrade(&environment));
|
||||
|
||||
let s = Self {
|
||||
_store: store_ptr,
|
||||
modules: Default::default(),
|
||||
instances: Default::default(),
|
||||
load_script_fn: None,
|
||||
script_capabilities: Default::default(),
|
||||
environment_data: Arc::new(WebAssemblyEnvironmentData::new(store_ptr)),
|
||||
environment_data: environment,
|
||||
};
|
||||
|
||||
Box::new(s)
|
||||
|
@ -168,7 +166,7 @@ impl WebAssemblyScriptResolver {
|
|||
impl ScriptResolver for WebAssemblyScriptResolver {
|
||||
fn load_script(
|
||||
&self,
|
||||
owner: *const u8,
|
||||
owner: ScriptOwnerData,
|
||||
category: ScriptCategory,
|
||||
script_key: &StringKey,
|
||||
) -> PkmnResult<Option<Arc<dyn Script>>> {
|
||||
|
@ -182,50 +180,7 @@ impl ScriptResolver for WebAssemblyScriptResolver {
|
|||
ExternRef::new_with_resolver(self, script_key),
|
||||
)
|
||||
.unwrap();
|
||||
if script == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let key = ScriptCapabilitiesKey {
|
||||
category,
|
||||
script_key: script_key.clone(),
|
||||
};
|
||||
|
||||
if !self.script_capabilities.read().contains_key(&key) {
|
||||
let mut capabilities = HashSet::new();
|
||||
unsafe {
|
||||
if let Some(get_cap) = self
|
||||
.environment_data
|
||||
.exported_functions
|
||||
.read()
|
||||
.get::<StringKey>(&"get_script_capabilities".into())
|
||||
{
|
||||
let res = get_cap
|
||||
.call(&mut self.store_mut(), &[Value::I32(script as i32)])
|
||||
.unwrap();
|
||||
let ptr = (self.environment_data.memory() as *const WebAssemblyScriptCapabilities)
|
||||
.offset(res[0].i32().unwrap() as isize);
|
||||
let length = res[1].i32().unwrap() as usize;
|
||||
for i in 0..length {
|
||||
capabilities.insert(*ptr.add(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
self.script_capabilities
|
||||
.write()
|
||||
.insert(key.clone(), Arc::new(capabilities));
|
||||
}
|
||||
|
||||
let read_guard = self.script_capabilities.read();
|
||||
let capabilities = read_guard.get(&key).unwrap();
|
||||
|
||||
Ok(Some(Arc::new(WebAssemblyScript::new(
|
||||
owner as *mut u8,
|
||||
script,
|
||||
capabilities.clone(),
|
||||
self.environment_data.clone(),
|
||||
script_key.clone(),
|
||||
))))
|
||||
self.environment_data.setup_script(script, category, script_key, owner)
|
||||
}
|
||||
|
||||
fn load_item_script(&self, _key: &Item) -> PkmnResult<Option<Arc<dyn ItemScript>>> {
|
||||
|
@ -283,6 +238,16 @@ pub struct WebAssemblyEnvironmentData {
|
|||
|
||||
/// The WASM store.
|
||||
store: *mut Store,
|
||||
|
||||
/// Script capabilities tell us which functions are implemented on a given script. This allows us to skip unneeded
|
||||
/// WASM calls.
|
||||
script_capabilities: RwLock<HashMap<ScriptCapabilitiesKey, Arc<HashSet<WebAssemblyScriptCapabilities>>>>,
|
||||
|
||||
/// A weak reference to ourselves.
|
||||
self_arc: RwLock<Option<Weak<Self>>>,
|
||||
|
||||
/// A lookup from WASM memory pointer to their actual script wrappers.
|
||||
loaded_scripts: RwLock<HashMap<u32, Weak<WebAssemblyScript>>>,
|
||||
}
|
||||
|
||||
/// A quick lookup so we can find the extern ref of the value.
|
||||
|
@ -310,6 +275,9 @@ impl WebAssemblyEnvironmentData {
|
|||
allocate_mem_fn: Default::default(),
|
||||
temp_allocator: Default::default(),
|
||||
store,
|
||||
script_capabilities: Default::default(),
|
||||
self_arc: Default::default(),
|
||||
loaded_scripts: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,6 +286,19 @@ impl WebAssemblyEnvironmentData {
|
|||
self.memory.read().as_ref().unwrap().view(&self.store_ref()).data_ptr()
|
||||
}
|
||||
|
||||
/// Return a pointer to something inside the WASM memory.
|
||||
pub fn get_raw_pointer<T>(&self, offset: u32) -> *mut T {
|
||||
unsafe {
|
||||
self.memory
|
||||
.read()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.view(&self.store_ref())
|
||||
.data_ptr()
|
||||
.offset(offset as isize) as *mut T
|
||||
}
|
||||
}
|
||||
|
||||
/// This returns the functions exported from WASM.
|
||||
pub fn exported_functions(&self) -> RwLockReadGuard<'_, RawRwLock, HashMap<StringKey, Function>> {
|
||||
self.exported_functions.read()
|
||||
|
@ -371,6 +352,7 @@ impl WebAssemblyEnvironmentData {
|
|||
/// access can be touched through this, and we ensure the value is the correct type. In the future,
|
||||
/// when extern refs get actually properly implemented at compile time we might want to get rid
|
||||
/// of this code.
|
||||
#[inline(always)]
|
||||
pub fn get_extern_ref_index<T: UniqueTypeId<u64> + ?Sized>(&self, value: &T) -> u32 {
|
||||
self.get_extern_ref_from_ptr(value as *const T as *const u8, T::id().0, false)
|
||||
}
|
||||
|
@ -400,6 +382,7 @@ impl WebAssemblyEnvironmentData {
|
|||
|
||||
/// Gets the extern ref index belonging to a specific pointer. If none exists, this will create
|
||||
/// a new one.
|
||||
#[inline(always)]
|
||||
fn get_extern_ref_from_ptr(&self, ptr: *const u8, type_id: u64, is_vec: bool) -> u32 {
|
||||
if let Some(v) = self.extern_ref_pointers_lookup.read().get(&ExternRefLookupKey {
|
||||
ptr,
|
||||
|
@ -462,6 +445,71 @@ impl WebAssemblyEnvironmentData {
|
|||
pub fn store_mut(&self) -> StoreMut<'_> {
|
||||
unsafe { self.store.as_mut().unwrap().as_store_mut() }
|
||||
}
|
||||
|
||||
/// Find a loaded script based on the pointer in WASM memory.
|
||||
pub(crate) fn get_loaded_script(&self, wasm_ptr: u32) -> Option<Arc<WebAssemblyScript>> {
|
||||
if let Some(script) = self.loaded_scripts.read().get(&wasm_ptr) {
|
||||
script.upgrade()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a script pointer in WASM memory into a host managed script.
|
||||
pub fn setup_script(
|
||||
&self,
|
||||
script_ptr: u32,
|
||||
category: ScriptCategory,
|
||||
script_key: &StringKey,
|
||||
owner: ScriptOwnerData,
|
||||
) -> PkmnResult<Option<Arc<dyn Script>>> {
|
||||
if script_ptr == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let key = ScriptCapabilitiesKey {
|
||||
category,
|
||||
script_key: script_key.clone(),
|
||||
};
|
||||
|
||||
if !self.script_capabilities.read().contains_key(&key) {
|
||||
let mut capabilities = HashSet::new();
|
||||
unsafe {
|
||||
if let Some(get_cap) = self
|
||||
.exported_functions
|
||||
.read()
|
||||
.get::<StringKey>(&"get_script_capabilities".into())
|
||||
{
|
||||
let res = get_cap
|
||||
.call(&mut self.store_mut(), &[Value::I32(script_ptr as i32)])
|
||||
.unwrap();
|
||||
let ptr =
|
||||
(self.memory() as *const WebAssemblyScriptCapabilities).offset(res[0].i32().unwrap() as isize);
|
||||
let length = res[1].i32().unwrap() as usize;
|
||||
for i in 0..length {
|
||||
capabilities.insert(*ptr.add(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
self.script_capabilities
|
||||
.write()
|
||||
.insert(key.clone(), Arc::new(capabilities));
|
||||
}
|
||||
|
||||
let read_guard = self.script_capabilities.read();
|
||||
let capabilities = read_guard.get(&key).unwrap();
|
||||
|
||||
let script = Arc::new(WebAssemblyScript::new(
|
||||
owner,
|
||||
script_ptr,
|
||||
capabilities.clone(),
|
||||
self.self_arc.read().as_ref().unwrap().upgrade().unwrap(),
|
||||
script_key.clone(),
|
||||
));
|
||||
|
||||
self.loaded_scripts.write().insert(script_ptr, Arc::downgrade(&script));
|
||||
Ok(Some(script))
|
||||
}
|
||||
}
|
||||
|
||||
/// The runtime environment for script execution. This is passed to most of the host functions being called.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::borrow::Borrow;
|
||||
use std::ffi::CString;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
|
@ -27,23 +28,13 @@ static STRING_CACHE: OnceCell<Mutex<HashMap<u32, Weak<str>>>> = OnceCell::uninit
|
|||
static EMPTY: OnceCell<StringKey> = OnceCell::uninit();
|
||||
|
||||
impl StringKey {
|
||||
/// Calculates the hash of a string key in a const manner.
|
||||
pub const fn get_hash_const<const N: usize>(s: &[u8; N]) -> u32 {
|
||||
let mut crc: u32 = 0xffffffff;
|
||||
|
||||
let mut i: usize = 0;
|
||||
while i < N {
|
||||
crc = (crc >> 8) ^ CRC_TABLE[((crc ^ (to_lower(s[i]) as u32)) & 0xff) as usize];
|
||||
i += 1;
|
||||
}
|
||||
crc ^ 0xffffffff
|
||||
}
|
||||
|
||||
/// Gets the hash of a string.
|
||||
pub fn get_hash(s: &str) -> u32 {
|
||||
pub const fn get_hash(s: &str) -> u32 {
|
||||
let mut crc: u32 = 0xffffffff;
|
||||
for byte in s.bytes() {
|
||||
crc = (crc >> 8) ^ CRC_TABLE[((crc ^ (to_lower(byte) as u32)) & 0xff) as usize];
|
||||
let mut i: usize = 0;
|
||||
while i < s.len() {
|
||||
crc = (crc >> 8) ^ CRC_TABLE[((crc ^ (to_lower(s.as_bytes()[i]) as u32)) & 0xff) as usize];
|
||||
i += 1;
|
||||
}
|
||||
crc ^ 0xffffffff
|
||||
}
|
||||
|
@ -115,6 +106,18 @@ impl Display for StringKey {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<StringKey> for CString {
|
||||
fn into(self) -> StringKey {
|
||||
StringKey::new(self.to_str().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<StringKey> for &CString {
|
||||
fn into(self) -> StringKey {
|
||||
StringKey::new(self.to_str().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a character to lowercased in a const safe way.
|
||||
const fn to_lower(c: u8) -> u8 {
|
||||
if c >= b'A' && c <= b'Z' {
|
||||
|
@ -167,7 +170,7 @@ mod tests {
|
|||
let sk = StringKey::new("");
|
||||
assert_eq!(sk.str(), "");
|
||||
assert_eq!(sk.hash(), 0);
|
||||
assert_eq!(sk.hash(), StringKey::get_hash_const(b""));
|
||||
assert_eq!(sk.hash(), StringKey::get_hash(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -175,8 +178,8 @@ mod tests {
|
|||
let sk = StringKey::new("foo");
|
||||
assert_eq!(sk.str(), "foo");
|
||||
assert_eq!(sk.hash(), 2356372769);
|
||||
assert_eq!(sk.hash(), StringKey::get_hash_const(b"foo"));
|
||||
assert_eq!(sk.hash(), StringKey::get_hash_const(b"FOo"));
|
||||
assert_eq!(sk.hash(), StringKey::get_hash("foo"));
|
||||
assert_eq!(sk.hash(), StringKey::get_hash("FOo"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -184,7 +187,7 @@ mod tests {
|
|||
let sk = StringKey::new("bar");
|
||||
assert_eq!(sk.str(), "bar");
|
||||
assert_eq!(sk.hash(), 1996459178);
|
||||
assert_eq!(sk.hash(), StringKey::get_hash_const(b"bar"));
|
||||
assert_eq!(sk.hash(), StringKey::get_hash_const(b"baR"));
|
||||
assert_eq!(sk.hash(), StringKey::get_hash("bar"));
|
||||
assert_eq!(sk.hash(), StringKey::get_hash("baR"));
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue