A lot more work on WASM script execution
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2022-09-07 18:01:26 +02:00
parent f9761f61da
commit b1890681a1
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
102 changed files with 748 additions and 202 deletions

0
.cargo/config.toml Normal file → Executable file
View File

0
.drone.yml Normal file → Executable file
View File

0
.gitignore vendored Normal file → Executable file
View File

0
Cargo.toml Normal file → Executable file
View File

0
lifetime_notes.txt Normal file → Executable file
View File

0
rustfmt.toml Normal file → Executable file
View File

0
src/defines.rs Normal file → Executable file
View File

50
src/dynamic_data/choices.rs Normal file → Executable file
View File

@ -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,
}
}
}

0
src/dynamic_data/event_hooks.rs Normal file → Executable file
View File

41
src/dynamic_data/flow/choice_queue.rs Normal file → Executable file
View File

@ -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()])
}
}

0
src/dynamic_data/flow/mod.rs Normal file → Executable file
View File

0
src/dynamic_data/flow/target_resolver.rs Normal file → Executable file
View File

0
src/dynamic_data/flow/turn_runner.rs Normal file → Executable file
View File

0
src/dynamic_data/libraries/battle_stat_calculator.rs Normal file → Executable file
View File

0
src/dynamic_data/libraries/damage_library.rs Normal file → Executable file
View File

4
src/dynamic_data/libraries/dynamic_library.rs Normal file → Executable file
View File

@ -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>>> {

0
src/dynamic_data/libraries/misc_library.rs Normal file → Executable file
View File

0
src/dynamic_data/libraries/mod.rs Normal file → Executable file
View File

6
src/dynamic_data/libraries/script_resolver.rs Normal file → Executable file
View File

@ -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>>> {

0
src/dynamic_data/mod.rs Normal file → Executable file
View File

5
src/dynamic_data/models/battle.rs Normal file → Executable file
View File

@ -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
src/dynamic_data/models/battle_party.rs Normal file → Executable file
View File

@ -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
}
}

1
src/dynamic_data/models/battle_random.rs Normal file → Executable file
View File

@ -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.

8
src/dynamic_data/models/battle_side.rs Normal file → Executable file
View File

@ -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)
}
}

3
src/dynamic_data/models/executing_move.rs Normal file → Executable file
View File

@ -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()) {

0
src/dynamic_data/models/learned_move.rs Normal file → Executable file
View File

0
src/dynamic_data/models/mod.rs Normal file → Executable file
View File

9
src/dynamic_data/models/pokemon.rs Normal file → Executable file
View File

@ -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)
}
}

0
src/dynamic_data/models/pokemon_builder.rs Normal file → Executable file
View File

13
src/dynamic_data/models/pokemon_party.rs Normal file → Executable file
View File

@ -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
}
}

0
src/dynamic_data/script_handling/item_script.rs Normal file → Executable file
View File

0
src/dynamic_data/script_handling/mod.rs Normal file → Executable file
View File

34
src/dynamic_data/script_handling/script.rs Normal file → Executable file
View File

@ -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))
}
}

0
src/dynamic_data/script_handling/script_set.rs Normal file → Executable file
View File

View File

@ -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
src/lib.rs Normal file → Executable file
View File

0
src/script_implementations/mod.rs Normal file → Executable file
View File

View File

@ -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())
}
}

View File

@ -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)
}
}

View File

@ -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());
}
}
}

View File

@ -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
}
}
}

View File

@ -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())
}
}

View File

@ -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()
}
}

View 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);
}

View File

@ -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
}
}

View 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()
}
}
}

View 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
src/script_implementations/wasm/export_registry/mod.rs Normal file → Executable file
View File

View File

@ -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()) }
}
}

View File

@ -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);
}

View File

View File

0
src/script_implementations/wasm/extern_ref.rs Normal file → Executable file
View File

0
src/script_implementations/wasm/mod.rs Normal file → Executable file
View File

21
src/script_implementations/wasm/script.rs Normal file → Executable file
View 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.

View File

152
src/script_implementations/wasm/script_resolver.rs Normal file → Executable file
View File

@ -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.

0
src/script_implementations/wasm/temp_wasm_allocator.rs Normal file → Executable file
View File

0
src/static_data/growth_rates.rs Normal file → Executable file
View File

0
src/static_data/items.rs Normal file → Executable file
View File

0
src/static_data/libraries/ability_library.rs Normal file → Executable file
View File

0
src/static_data/libraries/data_library.rs Normal file → Executable file
View File

0
src/static_data/libraries/growth_rate_library.rs Normal file → Executable file
View File

0
src/static_data/libraries/item_library.rs Normal file → Executable file
View File

0
src/static_data/libraries/library_settings.rs Normal file → Executable file
View File

0
src/static_data/libraries/mod.rs Normal file → Executable file
View File

0
src/static_data/libraries/move_library.rs Normal file → Executable file
View File

0
src/static_data/libraries/species_library.rs Normal file → Executable file
View File

0
src/static_data/libraries/static_data.rs Normal file → Executable file
View File

0
src/static_data/libraries/type_library.rs Normal file → Executable file
View File

0
src/static_data/mod.rs Normal file → Executable file
View File

0
src/static_data/moves/mod.rs Normal file → Executable file
View File

0
src/static_data/moves/move_data.rs Normal file → Executable file
View File

0
src/static_data/moves/secondary_effect.rs Normal file → Executable file
View File

0
src/static_data/natures.rs Normal file → Executable file
View File

0
src/static_data/species_data/ability.rs Normal file → Executable file
View File

0
src/static_data/species_data/form.rs Normal file → Executable file
View File

0
src/static_data/species_data/gender.rs Normal file → Executable file
View File

0
src/static_data/species_data/learnable_moves.rs Normal file → Executable file
View File

0
src/static_data/species_data/mod.rs Normal file → Executable file
View File

0
src/static_data/species_data/species.rs Normal file → Executable file
View File

0
src/static_data/statistic_set.rs Normal file → Executable file
View File

0
src/static_data/statistics.rs Normal file → Executable file
View File

0
src/utils/mod.rs Normal file → Executable file
View File

0
src/utils/random.rs Normal file → Executable file
View File

43
src/utils/string_key.rs Normal file → Executable file
View File

@ -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"));
}
}

0
tests/common/data_getter.rs Normal file → Executable file
View File

0
tests/common/library_loader.rs Normal file → Executable file
View File

0
tests/common/mod.rs Normal file → Executable file
View File

0
tests/common/test_case.rs Normal file → Executable file
View File

0
tests/common/test_step.rs Normal file → Executable file
View File

0
tests/data/Abilities.json Normal file → Executable file
View File

0
tests/data/GrowthRates.json Normal file → Executable file
View File

0
tests/data/Items.json Normal file → Executable file
View File

0
tests/data/Moves.json Normal file → Executable file
View File

0
tests/data/Natures.csv Normal file → Executable file
View File

0
tests/data/Pokemon.json Normal file → Executable file
View File

0
tests/data/Types.csv Normal file → Executable file
View File

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More