diff --git a/src/dynamic_data/choices.rs b/src/dynamic_data/choices.rs index 46e3505..7a4f7bd 100644 --- a/src/dynamic_data/choices.rs +++ b/src/dynamic_data/choices.rs @@ -175,7 +175,7 @@ pub struct MoveChoice { choice_data: Box, } -impl<'user, 'library> MoveChoice { +impl MoveChoice { /// Initializes the data for a new move choice. pub fn new(user: Arc, used_move: Arc, target_side: u8, target_index: u8) -> Self { Self { diff --git a/src/dynamic_data/event_hooks.rs b/src/dynamic_data/event_hooks.rs index d4df1b4..9adb531 100644 --- a/src/dynamic_data/event_hooks.rs +++ b/src/dynamic_data/event_hooks.rs @@ -18,7 +18,7 @@ pub struct EventHook { evt_hook_function: Vec)>, } -impl<'battle, 'library> EventHook { +impl EventHook { /// Register a new listener. This will start receiving all events in the battle. Multiple event /// listeners can exist at the same time. Note that for these functions the event will be disposed /// of after the event is finished being sent. diff --git a/src/dynamic_data/flow/choice_queue.rs b/src/dynamic_data/flow/choice_queue.rs index 27fcc03..af94a5d 100644 --- a/src/dynamic_data/flow/choice_queue.rs +++ b/src/dynamic_data/flow/choice_queue.rs @@ -25,7 +25,7 @@ impl ChoiceQueue { /// 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<'b>(&'b mut self) -> TurnChoice { + pub fn dequeue(&mut self) -> TurnChoice { let c = self.queue[self.current].take(); self.current += 1; c.unwrap() diff --git a/src/dynamic_data/flow/target_resolver.rs b/src/dynamic_data/flow/target_resolver.rs index 1a178c4..0de5a50 100644 --- a/src/dynamic_data/flow/target_resolver.rs +++ b/src/dynamic_data/flow/target_resolver.rs @@ -11,7 +11,7 @@ use crate::static_data::MoveTarget; pub type TargetList = Vec>>; /// This returns all Pokemon in the battle. -fn get_all_targets<'b, 'library>(battle: &Battle) -> TargetList { +fn get_all_targets(battle: &Battle) -> TargetList { let mut v = Vec::with_capacity(battle.pokemon_per_side() as usize * battle.number_of_sides() as usize); for side in battle.sides() { for pokemon in side.pokemon().deref() { @@ -31,7 +31,7 @@ fn get_opposite_side(side: u8) -> u8 { /// Gets all Pokemon that are adjacent to of directly opposite of a Pokemon. This means the target, /// the Pokemon left of it, the Pokemon right of it, and the Pokemon opposite of it. -fn get_all_adjacent_opponent<'b, 'library>(side: u8, index: u8, battle: &Battle) -> TargetList { +fn get_all_adjacent_opponent(side: u8, index: u8, battle: &Battle) -> TargetList { let left = index as i32 - 1; let right = index + 1; if left < 0 && right >= battle.pokemon_per_side() { @@ -76,7 +76,7 @@ fn get_all_adjacent_opponent<'b, 'library>(side: u8, index: u8, battle: &Battle) /// Gets all Pokemon that are adjacent to a Pokemon. This includes the target, the Pokemon to the /// left of it, and the Pokemon to the right of it. -fn get_all_adjacent<'b, 'library>(side: u8, index: u8, battle: &Battle) -> TargetList { +fn get_all_adjacent(side: u8, index: u8, battle: &Battle) -> TargetList { let left = index as i32 - 1; let right = index + 1; if left < 0 && right >= battle.pokemon_per_side() { @@ -102,7 +102,7 @@ fn get_all_adjacent<'b, 'library>(side: u8, index: u8, battle: &Battle) -> Targe } /// Gets the target for a specific move target type, given the targeted position. -pub fn resolve_targets<'b, 'library>(side: u8, index: u8, target: MoveTarget, battle: &Battle) -> TargetList { +pub fn resolve_targets(side: u8, index: u8, target: MoveTarget, battle: &Battle) -> TargetList { match target { // These all resolve to a single position. We let the client deal with where the target is, // and just return the Pokemon at that given target here. diff --git a/src/dynamic_data/libraries/misc_library.rs b/src/dynamic_data/libraries/misc_library.rs index d9c260c..1775a95 100644 --- a/src/dynamic_data/libraries/misc_library.rs +++ b/src/dynamic_data/libraries/misc_library.rs @@ -14,7 +14,7 @@ pub trait MiscLibrary: Debug { /// Returns whether or not a Pokemon is allowed to flee or switch out. fn can_flee(&self, choice: &TurnChoice) -> bool; /// Returns the move we need to use if we can't use another move. Typically Struggle. - fn replacement_move<'func>(&'func self, user: &Arc, target_side: u8, target_index: u8) -> TurnChoice; + fn replacement_move(&self, user: &Arc, target_side: u8, target_index: u8) -> TurnChoice; // TODO: can evolve from level up? // TODO: get time } @@ -41,7 +41,7 @@ impl Gen7MiscLibrary { Some(SecondaryEffect::new(-1.0, StringKey::new("struggle"), vec![])), HashSet::new(), )); - let struggle_learned_move = Arc::new(LearnedMove::new(&struggle_data.clone(), MoveLearnMethod::Unknown)); + let struggle_learned_move = Arc::new(LearnedMove::new(&struggle_data, MoveLearnMethod::Unknown)); Self { struggle_learned_move } } } @@ -57,7 +57,7 @@ impl MiscLibrary for Gen7MiscLibrary { todo!() } - fn replacement_move<'func>(&'func self, user: &Arc, target_side: u8, target_index: u8) -> TurnChoice { + fn replacement_move(&self, user: &Arc, target_side: u8, target_index: u8) -> TurnChoice { self.struggle_learned_move.restore_all_uses(); TurnChoice::Move(MoveChoice::new( user.clone(), diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index 6463af3..0e92842 100644 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -55,7 +55,7 @@ pub struct Battle { script_source_data: RwLock, } -impl<'own, 'library> Battle { +impl Battle { /// Initializes a new battle. pub fn new( library: Arc, diff --git a/src/dynamic_data/models/battle_side.rs b/src/dynamic_data/models/battle_side.rs index b515652..fe74bb4 100644 --- a/src/dynamic_data/models/battle_side.rs +++ b/src/dynamic_data/models/battle_side.rs @@ -43,7 +43,7 @@ pub struct BattleSide { script_source_data: RwLock, } -impl<'own, 'library> BattleSide { +impl BattleSide { /// Instantiates a battle side. pub fn new(index: u8, pokemon_per_side: u8) -> Self { let mut pokemon = Vec::with_capacity(pokemon_per_side as usize); diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index cb2a5c5..0d01579 100644 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -260,7 +260,7 @@ impl Pokemon { } let script = self .library - .load_item_script(&self.held_item.read().as_ref().unwrap()) + .load_item_script(self.held_item.read().as_ref().unwrap()) .unwrap(); if script.is_none() { return false; diff --git a/src/dynamic_data/models/pokemon_party.rs b/src/dynamic_data/models/pokemon_party.rs index a54b1a0..17f5154 100644 --- a/src/dynamic_data/models/pokemon_party.rs +++ b/src/dynamic_data/models/pokemon_party.rs @@ -9,7 +9,7 @@ pub struct PokemonParty { pokemon: Vec>>, } -impl<'own, 'library> PokemonParty { +impl PokemonParty { /// Instantiates a party with a set size. pub fn new(size: usize) -> Self { let mut pokemon = Vec::with_capacity(size); diff --git a/src/dynamic_data/script_handling/mod.rs b/src/dynamic_data/script_handling/mod.rs index 2e96583..5a16fb9 100644 --- a/src/dynamic_data/script_handling/mod.rs +++ b/src/dynamic_data/script_handling/mod.rs @@ -225,8 +225,6 @@ mod tests { use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use crate::dynamic_data::script_handling::script::ScriptContainer; - use crate::dynamic_data::DynamicLibrary; - use crate::static_data::EffectParameter; use crate::StringKey; use super::*; diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs b/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs index bb8b0b0..c4ba1b9 100644 --- a/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs @@ -1,13 +1,31 @@ +use crate::dynamic_data::DynamicLibrary; +use crate::script_implementations::wasm::export_registry::register; +use crate::script_implementations::wasm::extern_ref::ExternRef; use wasmer::{Exports, Store}; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; +use crate::static_data::StaticData; +/// Learned move registration mod learned_move; +/// Pokemon registration mod pokemon; +/// Turn choice registration mod turn_choice; -pub(crate) fn register(exports: &mut Exports, store: &Store, env: WebAssemblyEnv) { +register! { + fn dynamic_library_get_static_data( + env: &WebAssemblyEnv, + dynamic_lib: ExternRef, +) -> ExternRef { + ExternRef::new(env.data().as_ref(), dynamic_lib.value(env).unwrap().static_data()) +} + manual manual_register +} + +/// Additional required manual registration +fn manual_register(exports: &mut Exports, store: &Store, env: WebAssemblyEnv) { turn_choice::register(exports, store, env.clone()); pokemon::register(exports, store, env.clone()); - learned_move::register(exports, store, env.clone()); + learned_move::register(exports, store, env); } diff --git a/src/script_implementations/wasm/export_registry/mod.rs b/src/script_implementations/wasm/export_registry/mod.rs index cc03714..fac9c6b 100644 --- a/src/script_implementations/wasm/export_registry/mod.rs +++ b/src/script_implementations/wasm/export_registry/mod.rs @@ -3,15 +3,17 @@ use std::mem::{align_of, forget}; use wasmer::{Exports, Store}; -use crate::dynamic_data::DynamicLibrary; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use crate::static_data::{EffectParameter, StaticData}; +use crate::static_data::EffectParameter; use crate::StringKey; +/// Dynamic data registration mod dynamic_data; +/// Static data registration mod static_data; +/// Register a single function. #[allow(unused_macros)] macro_rules! register_func { ($exports: ident, $store: ident, $func: ident) => { @@ -19,6 +21,7 @@ macro_rules! register_func { }; } +/// Register a single function that requires environment data. macro_rules! register_func_with_env { ($exports: ident, $store: ident, $func: ident, $env: expr) => { $exports.insert( @@ -28,11 +31,15 @@ macro_rules! register_func_with_env { }; } +/// Utility macro to register a bunch of WASM functions easily. macro_rules! register { ( $( fn $name:ident($($par:ident: $par_type:ty),*$(,)?) $(-> $return:ty)? $block:block )* + $( + manual $manual_name:ident + )? ) => { pub(crate) fn register(exports: &mut crate::script_implementations::wasm::export_registry::Exports, store: &crate::script_implementations::wasm::export_registry::Store, @@ -46,7 +53,7 @@ macro_rules! register { ) $(-> $return)* $block crate::script_implementations::wasm::export_registry::register_func_with_env!(exports, store, $name, env); )* - + $( $manual_name(exports, store, env) )* } }; } @@ -54,6 +61,7 @@ macro_rules! register { pub(crate) use register; pub(crate) use register_func_with_env; +/// Register the functions we expose to WASM. pub(crate) fn register_webassembly_funcs(exports: &mut Exports, store: &Store, env: WebAssemblyEnv) { register_func_with_env!(exports, store, _print, env); register_func_with_env!(exports, store, _error, env); @@ -64,7 +72,6 @@ pub(crate) fn register_webassembly_funcs(exports: &mut Exports, store: &Store, e register_func_with_env!(exports, store, string_key_get_hash, env); register_func_with_env!(exports, store, string_key_get_str, env); - register_func_with_env!(exports, store, dynamic_library_get_static_data, env); register_func_with_env!(exports, store, effect_parameter_get_type, env); register_func_with_env!(exports, store, effect_parameter_as_bool, env); register_func_with_env!(exports, store, effect_parameter_as_int, env); @@ -72,6 +79,7 @@ pub(crate) fn register_webassembly_funcs(exports: &mut Exports, store: &Store, e register_func_with_env!(exports, store, effect_parameter_as_string, env); } +/// Logging function for WASM. fn _print(env: &WebAssemblyEnv, p: u32, len: u32) { unsafe { let mem: *mut u8 = env.data().memory().data_ptr().offset(p as isize); @@ -81,6 +89,7 @@ fn _print(env: &WebAssemblyEnv, p: u32, len: u32) { } } +/// Triggers when WASM panics. #[track_caller] fn _error(env: &WebAssemblyEnv, message: u32, message_len: u32, file: u32, file_len: u32, line: u32, position: u32) { unsafe { @@ -95,14 +104,18 @@ fn _error(env: &WebAssemblyEnv, message: u32, message_len: u32, file: u32, file_ } } +/// Get a single item from an earlier passed VecExternRef fn _vec_extern_ref_get_value(env: &WebAssemblyEnv, reference: u32, index: u32) -> u32 { env.data().get_extern_vec_ref_extern_ref(reference, index) } +/// Gets the hash value of a StringKey. fn string_key_get_hash(env: &WebAssemblyEnv, string_key: ExternRef) -> u32 { string_key.value(env).unwrap().hash() } +/// Get a null-terminated C string from a StringKey. Note that this involves a copy into WASM +/// memory, so this is relatively heavy. fn string_key_get_str(env: &WebAssemblyEnv, string_key: ExternRef) -> u32 { let string_key = string_key.value(env).unwrap().str(); let wasm_string_ptr = env @@ -112,18 +125,12 @@ fn string_key_get_str(env: &WebAssemblyEnv, string_key: ExternRef) -> unsafe { Vec::from_raw_parts(wasm_string_ptr.0, string_key.len() + 1, string_key.len() + 1) }; wasm_string.resize(string_key.len() + 1, 0); string_key.as_bytes().clone_into(&mut wasm_string); - wasm_string.insert(string_key.len(), 0 as u8); + wasm_string.insert(string_key.len(), 0_u8); forget(wasm_string); wasm_string_ptr.1 } -fn dynamic_library_get_static_data( - env: &WebAssemblyEnv, - dynamic_lib: ExternRef, -) -> ExternRef { - ExternRef::new(env.data().as_ref(), dynamic_lib.value(env).unwrap().static_data()) -} - +/// Gets the type of an EffectParameter fn effect_parameter_get_type(env: &WebAssemblyEnv, parameter: ExternRef) -> u8 { let v = parameter.value(env).unwrap(); match v { @@ -134,6 +141,7 @@ fn effect_parameter_get_type(env: &WebAssemblyEnv, parameter: ExternRef) -> u8 { let v = parameter.value(env).unwrap(); match v { @@ -148,6 +156,7 @@ fn effect_parameter_as_bool(env: &WebAssemblyEnv, parameter: ExternRef) -> i64 { let v = parameter.value(env).unwrap(); match v { @@ -156,6 +165,7 @@ fn effect_parameter_as_int(env: &WebAssemblyEnv, parameter: ExternRef) -> f32 { let v = parameter.value(env).unwrap(); match v { @@ -164,6 +174,7 @@ fn effect_parameter_as_float(env: &WebAssemblyEnv, parameter: ExternRef) -> ExternRef { let v = parameter.value(env).unwrap(); match v { diff --git a/src/script_implementations/wasm/export_registry/static_data/mod.rs b/src/script_implementations/wasm/export_registry/static_data/mod.rs index 22d83c3..2643b19 100644 --- a/src/script_implementations/wasm/export_registry/static_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/static_data/mod.rs @@ -1,52 +1,52 @@ use wasmer::{Exports, Store}; use crate::defines::LevelInt; -use crate::script_implementations::wasm::export_registry::register_func_with_env; +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::{ItemLibrary, LibrarySettings, MoveLibrary, SpeciesLibrary, StaticData, TypeLibrary}; +/// Moves data registration mod moves; +/// Species data registration mod species; -pub(crate) fn register(exports: &mut Exports, store: &Store, env: WebAssemblyEnv) { - register_func_with_env!(exports, store, static_data_get_move_library, env); - register_func_with_env!(exports, store, static_data_get_species_library, env); - register_func_with_env!(exports, store, static_data_get_item_library, env); - register_func_with_env!(exports, store, static_data_get_type_library, env); - register_func_with_env!(exports, store, static_data_get_library_settings, env); - register_func_with_env!(exports, store, library_settings_get_maximum_level, env); +register! { + fn static_data_get_move_library(env: &WebAssemblyEnv, data_library: ExternRef) -> ExternRef { + ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().moves()) + } + fn static_data_get_species_library( + env: &WebAssemblyEnv, + data_library: ExternRef, + ) -> ExternRef { + ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().species()) + } + + fn static_data_get_item_library(env: &WebAssemblyEnv, data_library: ExternRef) -> ExternRef { + ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().items()) + } + + fn static_data_get_type_library(env: &WebAssemblyEnv, data_library: ExternRef) -> ExternRef { + ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().types()) + } + + fn static_data_get_library_settings( + env: &WebAssemblyEnv, + data_library: ExternRef, + ) -> ExternRef { + ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().settings()) + } + + fn library_settings_get_maximum_level(env: &WebAssemblyEnv, data_library: ExternRef) -> LevelInt { + data_library.value(env).unwrap().maximum_level() + } + + manual manual_registration +} + +/// Additional required manual registration. +fn manual_registration(exports: &mut Exports, store: &Store, env: WebAssemblyEnv) { moves::register(exports, store, env.clone()); - species::register(exports, store, env.clone()); -} - -fn static_data_get_move_library(env: &WebAssemblyEnv, data_library: ExternRef) -> ExternRef { - ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().moves()) -} - -fn static_data_get_species_library( - env: &WebAssemblyEnv, - data_library: ExternRef, -) -> ExternRef { - ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().species()) -} - -fn static_data_get_item_library(env: &WebAssemblyEnv, data_library: ExternRef) -> ExternRef { - ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().items()) -} - -fn static_data_get_type_library(env: &WebAssemblyEnv, data_library: ExternRef) -> ExternRef { - ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().types()) -} - -fn static_data_get_library_settings( - env: &WebAssemblyEnv, - data_library: ExternRef, -) -> ExternRef { - ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().settings()) -} - -fn library_settings_get_maximum_level(env: &WebAssemblyEnv, data_library: ExternRef) -> LevelInt { - data_library.value(env).unwrap().maximum_level() + species::register(exports, store, env); } diff --git a/src/script_implementations/wasm/extern_ref.rs b/src/script_implementations/wasm/extern_ref.rs index f46a184..f55980f 100644 --- a/src/script_implementations/wasm/extern_ref.rs +++ b/src/script_implementations/wasm/extern_ref.rs @@ -8,12 +8,19 @@ use crate::script_implementations::wasm::script_resolver::{ WebAssemblyEnv, WebAssemblyEnvironmentData, WebAssemblyScriptResolver, }; +/// An Extern Ref allows us to pass objects to WASM without actually passing raw memory, or +/// requiring us to make copies. Instead, we pass a simple increment index, that we can then use +/// to find the relevant data. pub(crate) struct ExternRef> { + /// The lookup index we can use to find the data. index: u32, + /// Phantom data so we can get a type generic. _phantom: PhantomData, } impl> ExternRef { + /// Instantiates a new ExternRef for a bit of data. If we already have made an Extern Ref for + /// this data and type, we use that instead. pub fn new(env: &WebAssemblyEnvironmentData, value: &T) -> Self { Self { index: env.get_extern_ref_index(value), @@ -60,13 +67,19 @@ unsafe impl> FromToNativeWasmType for ExternRef { } } +/// A VecExternRef is an Extern Ref for vector types. This allows us to pass Vecs to WASM without +/// actually passing raw memory, or requiring us to make copies. pub(crate) struct VecExternRef { + /// The lookup index we can use to find the data. index: u32, + /// The number of items in the vec. size: u32, + /// Phantom data so we can get a type generic. _phantom: PhantomData, } impl> VecExternRef { + /// Instantiates a new VecExternRef for a given slice. pub fn new(env: &WebAssemblyEnvironmentData, value: &[T]) -> Self { Self { index: env.get_extern_vec_ref_index(value), diff --git a/src/script_implementations/wasm/mod.rs b/src/script_implementations/wasm/mod.rs index dff4e0d..df20be9 100644 --- a/src/script_implementations/wasm/mod.rs +++ b/src/script_implementations/wasm/mod.rs @@ -1,12 +1,15 @@ /// The export registry module deals with registering all functions we require in WebAssembly. mod export_registry; +/// A hacky extern ref implementation to ensure the client does not do things it is not allowed to do. pub(crate) mod extern_ref; /// The script module deals with the actual running of WASM functions. pub mod script; +/// A cache of all script functions for easy calls +mod script_function_cache; /// The script resolver deals with the loading of scripts. pub mod script_resolver; +/// A small simple allocator for use for rapid short lived WASM allocations. mod temp_wasm_allocator; -mod script_function_cache; /// The WebAssemblyScriptCapabilities define which functions are implemented on a script. This allows /// us to not call a function if we do not need to. diff --git a/src/script_implementations/wasm/script.rs b/src/script_implementations/wasm/script.rs index aa60e4d..4bfd1e9 100644 --- a/src/script_implementations/wasm/script.rs +++ b/src/script_implementations/wasm/script.rs @@ -3,7 +3,6 @@ use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize}; use std::sync::{Arc, Weak}; use hashbrown::HashSet; -use wasmer::NativeFunc; use crate::dynamic_data::{DynamicLibrary, Script, TurnChoice}; use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; @@ -67,6 +66,28 @@ impl Script for WebAssemblyScript { &self.suppressed_count } + fn stack(&self) { + if !self.capabilities.contains(&WebAssemblyScriptCapabilities::OnStack) { + return; + } + let env = self.environment.upgrade().unwrap(); + let func = env.script_function_cache().stack(&env); + if let Some(func) = func { + func.call(self.self_ptr).unwrap(); + } + } + + fn on_remove(&self) { + if !self.capabilities.contains(&WebAssemblyScriptCapabilities::OnRemove) { + return; + } + let env = self.environment.upgrade().unwrap(); + let func = env.script_function_cache().on_remove(&env); + if let Some(func) = func { + func.call(self.self_ptr).unwrap(); + } + } + fn on_initialize(&self, library: &DynamicLibrary, pars: &[EffectParameter]) { if !self.capabilities.contains(&WebAssemblyScriptCapabilities::Initialize) { return; @@ -101,15 +122,30 @@ impl Script for WebAssemblyScript { } let env = self.environment.upgrade().unwrap(); - let exported = env.exported_functions(); - if let Some(f) = exported.get::(&"script_change_speed".into()) { - let func: NativeFunc<(u32, ExternRef, u32), ()> = f.native().unwrap(); - let ptr = env.temp_allocate_mem_typed::(); + let func = env.script_function_cache().change_speed(&env); + if let Some(func) = func { + let ptr = env.temp_allocate_mem_typed::(*speed); func.call(self.self_ptr, ExternRef::new(env.as_ref(), choice), ptr.wasm_pointer) .unwrap(); - unsafe { - *speed = *ptr.ptr; - } + *speed = *ptr.value(); + } + } + + fn change_priority(&self, choice: &TurnChoice, priority: &mut i8) { + if !self + .capabilities + .contains(&WebAssemblyScriptCapabilities::ChangePriority) + { + return; + } + + let env = self.environment.upgrade().unwrap(); + let func = env.script_function_cache().change_priority(&env); + if let Some(func) = func { + let ptr = env.temp_allocate_mem_typed::(*priority); + func.call(self.self_ptr, ExternRef::new(env.as_ref(), choice), ptr.wasm_pointer) + .unwrap(); + *priority = *ptr.value(); } } diff --git a/src/script_implementations/wasm/script_function_cache.rs b/src/script_implementations/wasm/script_function_cache.rs index 3e1fec6..16ae8f7 100644 --- a/src/script_implementations/wasm/script_function_cache.rs +++ b/src/script_implementations/wasm/script_function_cache.rs @@ -10,16 +10,18 @@ use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironment use crate::static_data::EffectParameter; use crate::StringKey; +/// A macro to generate the script function cache a bit easier. macro_rules! script_function_cache { ( $( - $name:ident -> $return:ty + $name:ident($($par_type:ty),*$(,)?) )* ) => { #[derive(Default)] + #[allow(unused_parens)] pub struct ScriptFunctionCache { $( - $name: RwLock>, + $name: RwLock>>, )* } @@ -27,20 +29,22 @@ macro_rules! script_function_cache { $( paste! { #[cold] + #[allow(unused_parens)] fn [](&self, env: &Arc) { let exported = env.exported_functions(); let f = exported.get::(&stringify!([< script_ $name >]).into()); if let Some(f) = f { - let func: $return = f.native().unwrap(); + let func: NativeFunc<(u32 $(,$par_type)*), ()> = f.native().unwrap(); let _ = self.$name.write().insert(func); } } + #[allow(unused_parens)] pub(crate) fn [<$name>]( &self, env: &Arc, - ) -> Option<$return> { + ) -> Option> { { let read_lock = self.$name.read(); if let Some(f) = read_lock.as_ref() { @@ -57,6 +61,10 @@ macro_rules! script_function_cache { } script_function_cache! { - on_initialize -> NativeFunc<(u32, ExternRef, VecExternRef), ()> - on_before_turn -> NativeFunc<(u32, ExternRef), ()> + stack() + on_remove() + on_initialize(ExternRef, VecExternRef) + on_before_turn(ExternRef) + change_speed(ExternRef, u32) + change_priority(ExternRef, u32) } diff --git a/src/script_implementations/wasm/script_resolver.rs b/src/script_implementations/wasm/script_resolver.rs index c373169..8971e7c 100644 --- a/src/script_implementations/wasm/script_resolver.rs +++ b/src/script_implementations/wasm/script_resolver.rs @@ -38,6 +38,7 @@ pub struct WebAssemblyScriptResolver { /// WASM calls. script_capabilities: RwLock>>>, + /// The data for use in the scripting function calls. environment_data: Arc, } @@ -102,10 +103,8 @@ impl WebAssemblyScriptResolver { let exports = &instance.exports; let init_fn = exports.get_extern("_init"); - if let Some(init_fn) = init_fn { - if let Extern::Function(init_fn) = init_fn { - init_fn.call(&[]).unwrap(); - } + if let Some(Extern::Function(init_fn)) = init_fn { + init_fn.call(&[]).unwrap(); } let mut exported_functions = self.environment_data.exported_functions.write(); @@ -245,6 +244,7 @@ pub struct WebAssemblyEnvironmentData { /// This is a map of all the functions that WASM gives us. exported_functions: RwLock>, + /// A cache of all script functions for faster calls. script_function_cache: ScriptFunctionCache, /// This is the WASM function to allocate memory inside the WASM container. @@ -254,10 +254,14 @@ pub struct WebAssemblyEnvironmentData { temp_allocator: RwLock>, } +/// A quick lookup so we can find the extern ref of the value. #[derive(Clone, Eq, PartialEq, Hash)] struct ExternRefLookupKey { + /// The raw pointer to the data pub ptr: *const u8, + /// Whether or not the reference is a Vec pub is_vec: bool, + /// The unique identifier of the type. pub t: u64, } @@ -324,8 +328,8 @@ impl WebAssemblyEnvironmentData { /// Allocate a piece of memory inside WASM with a very short lifespan. This is mainly used for /// rapid allocation of script function parameters, where WASM needs to write to a specific /// pointer. - pub fn temp_allocate_mem_typed(&self) -> AllocatedObject { - self.temp_allocator.read().as_ref().unwrap().alloc::() + pub fn temp_allocate_mem_typed(&self, value: T) -> AllocatedObject { + self.temp_allocator.read().as_ref().unwrap().alloc::(value) } /// Get a numeric value from any given value. This is not a true Extern Ref from WASM, as this diff --git a/src/script_implementations/wasm/temp_wasm_allocator.rs b/src/script_implementations/wasm/temp_wasm_allocator.rs index f72a554..1399bdf 100644 --- a/src/script_implementations/wasm/temp_wasm_allocator.rs +++ b/src/script_implementations/wasm/temp_wasm_allocator.rs @@ -1,14 +1,26 @@ use std::mem::size_of; use std::sync::atomic::{AtomicUsize, Ordering}; +/// A TempWasmAllocator is a bump allocator for use with very short lived allocations within WASM. +/// This is for example used to allocate the values by reference in our script functions. These +/// live only during the call, and then get deallocated again. Once everything is deallocated, we +/// reset the allocator to the start again. pub(super) struct TempWasmAllocator { + /// The raw pointer in host memory where the allocation lives. data: *mut u8, + /// The pointer in client memory where the allocation lives. wasm_pointer: u32, + /// The total amount we've currently allocated. This fluctuates up and down, when allocating + /// and de-allocating. When this hits 0, the allocation gets reset. currently_allocated: AtomicUsize, + /// The bump position for our allocations. we increment this when we allocate, but don't decrement + /// when we de-allocate. We reset this to 0 when currently_allocated hits 0. offset_high: AtomicUsize, } impl TempWasmAllocator { + /// Creates a new allocator, with a given pointer to memory, and the associated position + /// within WASM memory. pub(super) fn new(data: *mut u8, wasm_pointer: u32) -> Self { Self { data, @@ -18,10 +30,14 @@ impl TempWasmAllocator { } } - pub fn alloc(&self) -> AllocatedObject { + /// Allocates a new object with a certain type. + pub fn alloc(&self, value: T) -> AllocatedObject { self.currently_allocated.fetch_add(size_of::(), Ordering::SeqCst); let ptr_offset = self.offset_high.fetch_add(size_of::(), Ordering::SeqCst); let ptr = unsafe { self.data.add(ptr_offset) } as *mut T; + unsafe { + *ptr = value; + } AllocatedObject:: { ptr, wasm_pointer: self.wasm_pointer + ptr_offset as u32, @@ -29,6 +45,7 @@ impl TempWasmAllocator { } } + /// Drops an earlier allocated type. pub fn drop(&self) { self.currently_allocated.fetch_sub(size_of::(), Ordering::SeqCst); // As soon as we've no longer allocated anything, we reset our allocating back to the start. @@ -38,12 +55,23 @@ impl TempWasmAllocator { } } +/// A value allocated within WASM memory. Once this goes out of scope, it gets deallocated, pub struct AllocatedObject { + /// The pointer in host memory to where the object lives. pub ptr: *mut T, + /// The pointer in client memory to where the object lives. pub wasm_pointer: u32, + /// A raw pointer to the allocator we use, so we know where to deallocate to. allocator: *const TempWasmAllocator, } +impl AllocatedObject { + /// Gets the currently underlying value from the pointer. + pub fn value(&self) -> &T { + unsafe { &*self.ptr } + } +} + impl Drop for AllocatedObject { fn drop(&mut self) { unsafe { diff --git a/src/static_data/libraries/type_library.rs b/src/static_data/libraries/type_library.rs index c6c7af9..215b69a 100644 --- a/src/static_data/libraries/type_library.rs +++ b/src/static_data/libraries/type_library.rs @@ -17,9 +17,9 @@ impl From for TypeIdentifier { } } -impl Into for TypeIdentifier { - fn into(self) -> u8 { - self.val +impl From for u8 { + fn from(id: TypeIdentifier) -> Self { + id.val } } diff --git a/tests/common/library_loader.rs b/tests/common/library_loader.rs index 3027432..f3aeac2 100644 --- a/tests/common/library_loader.rs +++ b/tests/common/library_loader.rs @@ -36,14 +36,13 @@ pub fn load_library() -> DynamicLibrary { let mut resolver = WebAssemblyScriptResolver::new(); load_wasm(&path, resolver.as_mut()); - let dynamic = DynamicLibrary::new( + DynamicLibrary::new( data, Box::new(Gen7BattleStatCalculator {}), Box::new(Gen7DamageLibrary::new(false)), Box::new(Gen7MiscLibrary::new()), resolver, - ); - dynamic + ) } pub fn load_types(path: &String, type_library: &mut TypeLibrary) { diff --git a/tests/main.rs b/tests/main.rs index 6cbb2a3..e2371b9 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -18,7 +18,7 @@ pub mod common; static LIBRARY: OnceCell> = OnceCell::uninit(); -fn get_library<'a>() -> Arc { +fn get_library() -> Arc { LIBRARY .get_or_init(|| { let start_time = chrono::Utc::now(); @@ -47,7 +47,7 @@ fn integration_tests(input: &Path) { let mut str: String = "".to_string(); let mut file = File::open(input).unwrap(); file.read_to_string(&mut str).unwrap(); - let test_case = serde_yaml::from_str::(&*str).unwrap(); + let test_case = serde_yaml::from_str::(&str).unwrap(); println!("\tRunning integration test {}", test_case.name); test_case.run_test(get_library()); } @@ -57,7 +57,7 @@ fn integration_tests(input: &Path) { fn validate_script() { let lib = get_library(); let script = lib - .load_script(0 as *const u8, ScriptCategory::Move, &"test".into()) + .load_script(std::ptr::null(), ScriptCategory::Move, &"test".into()) .unwrap() .unwrap(); let parameters = [EffectParameter::String("foo".into())]; @@ -71,7 +71,7 @@ fn validate_script() { fn validate_script_2() { let lib = get_library(); let script = lib - .load_script(0 as *const u8, ScriptCategory::Move, &"test".into()) + .load_script(std::ptr::null(), ScriptCategory::Move, &"test".into()) .unwrap() .unwrap(); let user = Arc::new(