Clippy fixes, additional WASM registration work
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Deukhoofd 2022-08-26 18:23:35 +02:00
parent 1e43c07d43
commit c5fb81c179
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
22 changed files with 219 additions and 101 deletions

View File

@ -175,7 +175,7 @@ pub struct MoveChoice {
choice_data: Box<CommonChoiceData>, choice_data: Box<CommonChoiceData>,
} }
impl<'user, 'library> MoveChoice { impl MoveChoice {
/// Initializes the data for a new move choice. /// Initializes the data for a new move choice.
pub fn new(user: Arc<Pokemon>, used_move: Arc<LearnedMove>, target_side: u8, target_index: u8) -> Self { pub fn new(user: Arc<Pokemon>, used_move: Arc<LearnedMove>, target_side: u8, target_index: u8) -> Self {
Self { Self {

View File

@ -18,7 +18,7 @@ pub struct EventHook {
evt_hook_function: Vec<fn(&Box<&Event>)>, evt_hook_function: Vec<fn(&Box<&Event>)>,
} }
impl<'battle, 'library> EventHook { impl EventHook {
/// Register a new listener. This will start receiving all events in the battle. Multiple event /// 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 /// 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. /// of after the event is finished being sent.

View File

@ -25,7 +25,7 @@ impl ChoiceQueue {
/// Dequeues the next turn choice to be executed. This gives ownership to the callee, and replaces /// 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 /// our own reference to the turn choice with an empty spot. It also increments the current position
/// by one. /// by one.
pub fn dequeue<'b>(&'b mut self) -> TurnChoice { pub fn dequeue(&mut self) -> TurnChoice {
let c = self.queue[self.current].take(); let c = self.queue[self.current].take();
self.current += 1; self.current += 1;
c.unwrap() c.unwrap()

View File

@ -11,7 +11,7 @@ use crate::static_data::MoveTarget;
pub type TargetList = Vec<Option<Arc<Pokemon>>>; pub type TargetList = Vec<Option<Arc<Pokemon>>>;
/// This returns all Pokemon in the battle. /// 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); let mut v = Vec::with_capacity(battle.pokemon_per_side() as usize * battle.number_of_sides() as usize);
for side in battle.sides() { for side in battle.sides() {
for pokemon in side.pokemon().deref() { 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, /// 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. /// 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 left = index as i32 - 1;
let right = index + 1; let right = index + 1;
if left < 0 && right >= battle.pokemon_per_side() { 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 /// 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. /// 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 left = index as i32 - 1;
let right = index + 1; let right = index + 1;
if left < 0 && right >= battle.pokemon_per_side() { 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. /// 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 { match target {
// These all resolve to a single position. We let the client deal with where the target is, // 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. // and just return the Pokemon at that given target here.

View File

@ -14,7 +14,7 @@ pub trait MiscLibrary: Debug {
/// Returns whether or not a Pokemon is allowed to flee or switch out. /// Returns whether or not a Pokemon is allowed to flee or switch out.
fn can_flee(&self, choice: &TurnChoice) -> bool; fn can_flee(&self, choice: &TurnChoice) -> bool;
/// Returns the move we need to use if we can't use another move. Typically Struggle. /// Returns the move we need to use if we can't use another move. Typically Struggle.
fn replacement_move<'func>(&'func self, user: &Arc<Pokemon>, target_side: u8, target_index: u8) -> TurnChoice; fn replacement_move(&self, user: &Arc<Pokemon>, target_side: u8, target_index: u8) -> TurnChoice;
// TODO: can evolve from level up? // TODO: can evolve from level up?
// TODO: get time // TODO: get time
} }
@ -41,7 +41,7 @@ impl Gen7MiscLibrary {
Some(SecondaryEffect::new(-1.0, StringKey::new("struggle"), vec![])), Some(SecondaryEffect::new(-1.0, StringKey::new("struggle"), vec![])),
HashSet::new(), 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 } Self { struggle_learned_move }
} }
} }
@ -57,7 +57,7 @@ impl MiscLibrary for Gen7MiscLibrary {
todo!() todo!()
} }
fn replacement_move<'func>(&'func self, user: &Arc<Pokemon>, target_side: u8, target_index: u8) -> TurnChoice { fn replacement_move(&self, user: &Arc<Pokemon>, target_side: u8, target_index: u8) -> TurnChoice {
self.struggle_learned_move.restore_all_uses(); self.struggle_learned_move.restore_all_uses();
TurnChoice::Move(MoveChoice::new( TurnChoice::Move(MoveChoice::new(
user.clone(), user.clone(),

View File

@ -55,7 +55,7 @@ pub struct Battle {
script_source_data: RwLock<ScriptSourceData>, script_source_data: RwLock<ScriptSourceData>,
} }
impl<'own, 'library> Battle { impl Battle {
/// Initializes a new battle. /// Initializes a new battle.
pub fn new( pub fn new(
library: Arc<DynamicLibrary>, library: Arc<DynamicLibrary>,

View File

@ -43,7 +43,7 @@ pub struct BattleSide {
script_source_data: RwLock<ScriptSourceData>, script_source_data: RwLock<ScriptSourceData>,
} }
impl<'own, 'library> BattleSide { impl BattleSide {
/// Instantiates a battle side. /// Instantiates a battle side.
pub fn new(index: u8, pokemon_per_side: u8) -> Self { pub fn new(index: u8, pokemon_per_side: u8) -> Self {
let mut pokemon = Vec::with_capacity(pokemon_per_side as usize); let mut pokemon = Vec::with_capacity(pokemon_per_side as usize);

View File

@ -260,7 +260,7 @@ impl Pokemon {
} }
let script = self let script = self
.library .library
.load_item_script(&self.held_item.read().as_ref().unwrap()) .load_item_script(self.held_item.read().as_ref().unwrap())
.unwrap(); .unwrap();
if script.is_none() { if script.is_none() {
return false; return false;

View File

@ -9,7 +9,7 @@ pub struct PokemonParty {
pokemon: Vec<Option<Arc<Pokemon>>>, pokemon: Vec<Option<Arc<Pokemon>>>,
} }
impl<'own, 'library> PokemonParty { impl PokemonParty {
/// Instantiates a party with a set size. /// Instantiates a party with a set size.
pub fn new(size: usize) -> Self { pub fn new(size: usize) -> Self {
let mut pokemon = Vec::with_capacity(size); let mut pokemon = Vec::with_capacity(size);

View File

@ -225,8 +225,6 @@ mod tests {
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use crate::dynamic_data::script_handling::script::ScriptContainer; use crate::dynamic_data::script_handling::script::ScriptContainer;
use crate::dynamic_data::DynamicLibrary;
use crate::static_data::EffectParameter;
use crate::StringKey; use crate::StringKey;
use super::*; use super::*;

View File

@ -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 wasmer::{Exports, Store};
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
use crate::static_data::StaticData;
/// Learned move registration
mod learned_move; mod learned_move;
/// Pokemon registration
mod pokemon; mod pokemon;
/// Turn choice registration
mod turn_choice; 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<DynamicLibrary>,
) -> ExternRef<StaticData> {
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()); turn_choice::register(exports, store, env.clone());
pokemon::register(exports, store, env.clone()); pokemon::register(exports, store, env.clone());
learned_move::register(exports, store, env.clone()); learned_move::register(exports, store, env);
} }

View File

@ -3,15 +3,17 @@ use std::mem::{align_of, forget};
use wasmer::{Exports, Store}; use wasmer::{Exports, Store};
use crate::dynamic_data::DynamicLibrary;
use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::extern_ref::ExternRef;
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
use crate::static_data::{EffectParameter, StaticData}; use crate::static_data::EffectParameter;
use crate::StringKey; use crate::StringKey;
/// Dynamic data registration
mod dynamic_data; mod dynamic_data;
/// Static data registration
mod static_data; mod static_data;
/// Register a single function.
#[allow(unused_macros)] #[allow(unused_macros)]
macro_rules! register_func { macro_rules! register_func {
($exports: ident, $store: ident, $func: ident) => { ($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 { macro_rules! register_func_with_env {
($exports: ident, $store: ident, $func: ident, $env: expr) => { ($exports: ident, $store: ident, $func: ident, $env: expr) => {
$exports.insert( $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 { macro_rules! register {
( (
$( $(
fn $name:ident($($par:ident: $par_type:ty),*$(,)?) $(-> $return:ty)? $block:block 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, pub(crate) fn register(exports: &mut crate::script_implementations::wasm::export_registry::Exports,
store: &crate::script_implementations::wasm::export_registry::Store, store: &crate::script_implementations::wasm::export_registry::Store,
@ -46,7 +53,7 @@ macro_rules! register {
) $(-> $return)* $block ) $(-> $return)* $block
crate::script_implementations::wasm::export_registry::register_func_with_env!(exports, store, $name, env); 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;
pub(crate) use register_func_with_env; 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) { 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, _print, env);
register_func_with_env!(exports, store, _error, 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_hash, env);
register_func_with_env!(exports, store, string_key_get_str, 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_get_type, env);
register_func_with_env!(exports, store, effect_parameter_as_bool, env); register_func_with_env!(exports, store, effect_parameter_as_bool, env);
register_func_with_env!(exports, store, effect_parameter_as_int, 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); register_func_with_env!(exports, store, effect_parameter_as_string, env);
} }
/// Logging function for WASM.
fn _print(env: &WebAssemblyEnv, p: u32, len: u32) { fn _print(env: &WebAssemblyEnv, p: u32, len: u32) {
unsafe { unsafe {
let mem: *mut u8 = env.data().memory().data_ptr().offset(p as isize); 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] #[track_caller]
fn _error(env: &WebAssemblyEnv, message: u32, message_len: u32, file: u32, file_len: u32, line: u32, position: u32) { fn _error(env: &WebAssemblyEnv, message: u32, message_len: u32, file: u32, file_len: u32, line: u32, position: u32) {
unsafe { 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 { fn _vec_extern_ref_get_value(env: &WebAssemblyEnv, reference: u32, index: u32) -> u32 {
env.data().get_extern_vec_ref_extern_ref(reference, index) 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<StringKey>) -> u32 { fn string_key_get_hash(env: &WebAssemblyEnv, string_key: ExternRef<StringKey>) -> u32 {
string_key.value(env).unwrap().hash() 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<StringKey>) -> u32 { fn string_key_get_str(env: &WebAssemblyEnv, string_key: ExternRef<StringKey>) -> u32 {
let string_key = string_key.value(env).unwrap().str(); let string_key = string_key.value(env).unwrap().str();
let wasm_string_ptr = env let wasm_string_ptr = env
@ -112,18 +125,12 @@ fn string_key_get_str(env: &WebAssemblyEnv, string_key: ExternRef<StringKey>) ->
unsafe { Vec::from_raw_parts(wasm_string_ptr.0, string_key.len() + 1, string_key.len() + 1) }; 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); wasm_string.resize(string_key.len() + 1, 0);
string_key.as_bytes().clone_into(&mut wasm_string); 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); forget(wasm_string);
wasm_string_ptr.1 wasm_string_ptr.1
} }
fn dynamic_library_get_static_data( /// Gets the type of an EffectParameter
env: &WebAssemblyEnv,
dynamic_lib: ExternRef<DynamicLibrary>,
) -> ExternRef<StaticData> {
ExternRef::new(env.data().as_ref(), dynamic_lib.value(env).unwrap().static_data())
}
fn effect_parameter_get_type(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> u8 { fn effect_parameter_get_type(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> u8 {
let v = parameter.value(env).unwrap(); let v = parameter.value(env).unwrap();
match v { match v {
@ -134,6 +141,7 @@ fn effect_parameter_get_type(env: &WebAssemblyEnv, parameter: ExternRef<EffectPa
} }
} }
/// Gets the inner bool data of an EffectParameter. Panics if it's not a bool.
fn effect_parameter_as_bool(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> u8 { fn effect_parameter_as_bool(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> u8 {
let v = parameter.value(env).unwrap(); let v = parameter.value(env).unwrap();
match v { match v {
@ -148,6 +156,7 @@ fn effect_parameter_as_bool(env: &WebAssemblyEnv, parameter: ExternRef<EffectPar
} }
} }
/// Gets the inner int data of an EffectParameter. Panics if it's not an int.
fn effect_parameter_as_int(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> i64 { fn effect_parameter_as_int(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> i64 {
let v = parameter.value(env).unwrap(); let v = parameter.value(env).unwrap();
match v { match v {
@ -156,6 +165,7 @@ fn effect_parameter_as_int(env: &WebAssemblyEnv, parameter: ExternRef<EffectPara
} }
} }
/// Gets the inner float data of an EffectParameter. Panics if it's not a float.
fn effect_parameter_as_float(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> f32 { fn effect_parameter_as_float(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> f32 {
let v = parameter.value(env).unwrap(); let v = parameter.value(env).unwrap();
match v { match v {
@ -164,6 +174,7 @@ fn effect_parameter_as_float(env: &WebAssemblyEnv, parameter: ExternRef<EffectPa
} }
} }
/// Gets the inner string data of an EffectParameter. Panics if it's not a string.
fn effect_parameter_as_string(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> ExternRef<StringKey> { fn effect_parameter_as_string(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> ExternRef<StringKey> {
let v = parameter.value(env).unwrap(); let v = parameter.value(env).unwrap();
match v { match v {

View File

@ -1,52 +1,52 @@
use wasmer::{Exports, Store}; use wasmer::{Exports, Store};
use crate::defines::LevelInt; 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::extern_ref::ExternRef;
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
use crate::static_data::{ItemLibrary, LibrarySettings, MoveLibrary, SpeciesLibrary, StaticData, TypeLibrary}; use crate::static_data::{ItemLibrary, LibrarySettings, MoveLibrary, SpeciesLibrary, StaticData, TypeLibrary};
/// Moves data registration
mod moves; mod moves;
/// Species data registration
mod species; mod species;
pub(crate) fn register(exports: &mut Exports, store: &Store, env: WebAssemblyEnv) { register! {
register_func_with_env!(exports, store, static_data_get_move_library, env); fn static_data_get_move_library(env: &WebAssemblyEnv, data_library: ExternRef<StaticData>) -> ExternRef<MoveLibrary> {
register_func_with_env!(exports, store, static_data_get_species_library, env); ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().moves())
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);
fn static_data_get_species_library(
env: &WebAssemblyEnv,
data_library: ExternRef<StaticData>,
) -> ExternRef<SpeciesLibrary> {
ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().species())
}
fn static_data_get_item_library(env: &WebAssemblyEnv, data_library: ExternRef<StaticData>) -> ExternRef<ItemLibrary> {
ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().items())
}
fn static_data_get_type_library(env: &WebAssemblyEnv, data_library: ExternRef<StaticData>) -> ExternRef<TypeLibrary> {
ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().types())
}
fn static_data_get_library_settings(
env: &WebAssemblyEnv,
data_library: ExternRef<StaticData>,
) -> ExternRef<LibrarySettings> {
ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().settings())
}
fn library_settings_get_maximum_level(env: &WebAssemblyEnv, data_library: ExternRef<LibrarySettings>) -> 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()); moves::register(exports, store, env.clone());
species::register(exports, store, env.clone()); species::register(exports, store, env);
}
fn static_data_get_move_library(env: &WebAssemblyEnv, data_library: ExternRef<StaticData>) -> ExternRef<MoveLibrary> {
ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().moves())
}
fn static_data_get_species_library(
env: &WebAssemblyEnv,
data_library: ExternRef<StaticData>,
) -> ExternRef<SpeciesLibrary> {
ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().species())
}
fn static_data_get_item_library(env: &WebAssemblyEnv, data_library: ExternRef<StaticData>) -> ExternRef<ItemLibrary> {
ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().items())
}
fn static_data_get_type_library(env: &WebAssemblyEnv, data_library: ExternRef<StaticData>) -> ExternRef<TypeLibrary> {
ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().types())
}
fn static_data_get_library_settings(
env: &WebAssemblyEnv,
data_library: ExternRef<StaticData>,
) -> ExternRef<LibrarySettings> {
ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().settings())
}
fn library_settings_get_maximum_level(env: &WebAssemblyEnv, data_library: ExternRef<LibrarySettings>) -> LevelInt {
data_library.value(env).unwrap().maximum_level()
} }

View File

@ -8,12 +8,19 @@ use crate::script_implementations::wasm::script_resolver::{
WebAssemblyEnv, WebAssemblyEnvironmentData, WebAssemblyScriptResolver, 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<T: UniqueTypeId<u64>> { pub(crate) struct ExternRef<T: UniqueTypeId<u64>> {
/// The lookup index we can use to find the data.
index: u32, index: u32,
/// Phantom data so we can get a type generic.
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: UniqueTypeId<u64>> ExternRef<T> { impl<T: UniqueTypeId<u64>> ExternRef<T> {
/// Instantiates a new ExternRef for a bit of data. If we already have made an Extern Ref for
/// this data and type, we use that instead.
pub fn new(env: &WebAssemblyEnvironmentData, value: &T) -> Self { pub fn new(env: &WebAssemblyEnvironmentData, value: &T) -> Self {
Self { Self {
index: env.get_extern_ref_index(value), index: env.get_extern_ref_index(value),
@ -60,13 +67,19 @@ unsafe impl<T: UniqueTypeId<u64>> FromToNativeWasmType for ExternRef<T> {
} }
} }
/// 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<T> { pub(crate) struct VecExternRef<T> {
/// The lookup index we can use to find the data.
index: u32, index: u32,
/// The number of items in the vec.
size: u32, size: u32,
/// Phantom data so we can get a type generic.
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: UniqueTypeId<u64>> VecExternRef<T> { impl<T: UniqueTypeId<u64>> VecExternRef<T> {
/// Instantiates a new VecExternRef for a given slice.
pub fn new(env: &WebAssemblyEnvironmentData, value: &[T]) -> Self { pub fn new(env: &WebAssemblyEnvironmentData, value: &[T]) -> Self {
Self { Self {
index: env.get_extern_vec_ref_index(value), index: env.get_extern_vec_ref_index(value),

View File

@ -1,12 +1,15 @@
/// The export registry module deals with registering all functions we require in WebAssembly. /// The export registry module deals with registering all functions we require in WebAssembly.
mod export_registry; 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; pub(crate) mod extern_ref;
/// The script module deals with the actual running of WASM functions. /// The script module deals with the actual running of WASM functions.
pub mod script; 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. /// The script resolver deals with the loading of scripts.
pub mod script_resolver; pub mod script_resolver;
/// A small simple allocator for use for rapid short lived WASM allocations.
mod temp_wasm_allocator; mod temp_wasm_allocator;
mod script_function_cache;
/// The WebAssemblyScriptCapabilities define which functions are implemented on a script. This allows /// The WebAssemblyScriptCapabilities define which functions are implemented on a script. This allows
/// us to not call a function if we do not need to. /// us to not call a function if we do not need to.

View File

@ -3,7 +3,6 @@ use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize};
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use hashbrown::HashSet; use hashbrown::HashSet;
use wasmer::NativeFunc;
use crate::dynamic_data::{DynamicLibrary, Script, TurnChoice}; use crate::dynamic_data::{DynamicLibrary, Script, TurnChoice};
use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef};
@ -67,6 +66,28 @@ impl Script for WebAssemblyScript {
&self.suppressed_count &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]) { fn on_initialize(&self, library: &DynamicLibrary, pars: &[EffectParameter]) {
if !self.capabilities.contains(&WebAssemblyScriptCapabilities::Initialize) { if !self.capabilities.contains(&WebAssemblyScriptCapabilities::Initialize) {
return; return;
@ -101,15 +122,30 @@ impl Script for WebAssemblyScript {
} }
let env = self.environment.upgrade().unwrap(); let env = self.environment.upgrade().unwrap();
let exported = env.exported_functions(); let func = env.script_function_cache().change_speed(&env);
if let Some(f) = exported.get::<StringKey>(&"script_change_speed".into()) { if let Some(func) = func {
let func: NativeFunc<(u32, ExternRef<TurnChoice>, u32), ()> = f.native().unwrap(); let ptr = env.temp_allocate_mem_typed::<u32>(*speed);
let ptr = env.temp_allocate_mem_typed::<u32>();
func.call(self.self_ptr, ExternRef::new(env.as_ref(), choice), ptr.wasm_pointer) func.call(self.self_ptr, ExternRef::new(env.as_ref(), choice), ptr.wasm_pointer)
.unwrap(); .unwrap();
unsafe { *speed = *ptr.value();
*speed = *ptr.ptr; }
} }
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::<i8>(*priority);
func.call(self.self_ptr, ExternRef::new(env.as_ref(), choice), ptr.wasm_pointer)
.unwrap();
*priority = *ptr.value();
} }
} }

View File

@ -10,16 +10,18 @@ use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironment
use crate::static_data::EffectParameter; use crate::static_data::EffectParameter;
use crate::StringKey; use crate::StringKey;
/// A macro to generate the script function cache a bit easier.
macro_rules! script_function_cache { macro_rules! script_function_cache {
( (
$( $(
$name:ident -> $return:ty $name:ident($($par_type:ty),*$(,)?)
)* )*
) => { ) => {
#[derive(Default)] #[derive(Default)]
#[allow(unused_parens)]
pub struct ScriptFunctionCache { pub struct ScriptFunctionCache {
$( $(
$name: RwLock<Option<$return>>, $name: RwLock<Option<NativeFunc<(u32$(, $par_type)*), ()>>>,
)* )*
} }
@ -27,20 +29,22 @@ macro_rules! script_function_cache {
$( $(
paste! { paste! {
#[cold] #[cold]
#[allow(unused_parens)]
fn [<initialize_ $name>](&self, env: &Arc<WebAssemblyEnvironmentData>) { fn [<initialize_ $name>](&self, env: &Arc<WebAssemblyEnvironmentData>) {
let exported = env.exported_functions(); let exported = env.exported_functions();
let f = exported.get::<StringKey>(&stringify!([< script_ $name >]).into()); let f = exported.get::<StringKey>(&stringify!([< script_ $name >]).into());
if let Some(f) = f { 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); let _ = self.$name.write().insert(func);
} }
} }
#[allow(unused_parens)]
pub(crate) fn [<$name>]( pub(crate) fn [<$name>](
&self, &self,
env: &Arc<WebAssemblyEnvironmentData>, env: &Arc<WebAssemblyEnvironmentData>,
) -> Option<$return> { ) -> Option<NativeFunc<(u32 $(,$par_type)*), ()>> {
{ {
let read_lock = self.$name.read(); let read_lock = self.$name.read();
if let Some(f) = read_lock.as_ref() { if let Some(f) = read_lock.as_ref() {
@ -57,6 +61,10 @@ macro_rules! script_function_cache {
} }
script_function_cache! { script_function_cache! {
on_initialize -> NativeFunc<(u32, ExternRef<DynamicLibrary>, VecExternRef<EffectParameter>), ()> stack()
on_before_turn -> NativeFunc<(u32, ExternRef<TurnChoice>), ()> on_remove()
on_initialize(ExternRef<DynamicLibrary>, VecExternRef<EffectParameter>)
on_before_turn(ExternRef<TurnChoice>)
change_speed(ExternRef<TurnChoice>, u32)
change_priority(ExternRef<TurnChoice>, u32)
} }

View File

@ -38,6 +38,7 @@ pub struct WebAssemblyScriptResolver {
/// WASM calls. /// WASM calls.
script_capabilities: RwLock<HashMap<ScriptCapabilitiesKey, Arc<HashSet<WebAssemblyScriptCapabilities>>>>, script_capabilities: RwLock<HashMap<ScriptCapabilitiesKey, Arc<HashSet<WebAssemblyScriptCapabilities>>>>,
/// The data for use in the scripting function calls.
environment_data: Arc<WebAssemblyEnvironmentData>, environment_data: Arc<WebAssemblyEnvironmentData>,
} }
@ -102,10 +103,8 @@ impl WebAssemblyScriptResolver {
let exports = &instance.exports; let exports = &instance.exports;
let init_fn = exports.get_extern("_init"); let init_fn = exports.get_extern("_init");
if let Some(init_fn) = init_fn { if let Some(Extern::Function(init_fn)) = init_fn {
if let Extern::Function(init_fn) = init_fn { init_fn.call(&[]).unwrap();
init_fn.call(&[]).unwrap();
}
} }
let mut exported_functions = self.environment_data.exported_functions.write(); 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. /// This is a map of all the functions that WASM gives us.
exported_functions: RwLock<HashMap<StringKey, Function>>, exported_functions: RwLock<HashMap<StringKey, Function>>,
/// A cache of all script functions for faster calls.
script_function_cache: ScriptFunctionCache, script_function_cache: ScriptFunctionCache,
/// This is the WASM function to allocate memory inside the WASM container. /// This is the WASM function to allocate memory inside the WASM container.
@ -254,10 +254,14 @@ pub struct WebAssemblyEnvironmentData {
temp_allocator: RwLock<Option<TempWasmAllocator>>, temp_allocator: RwLock<Option<TempWasmAllocator>>,
} }
/// A quick lookup so we can find the extern ref of the value.
#[derive(Clone, Eq, PartialEq, Hash)] #[derive(Clone, Eq, PartialEq, Hash)]
struct ExternRefLookupKey { struct ExternRefLookupKey {
/// The raw pointer to the data
pub ptr: *const u8, pub ptr: *const u8,
/// Whether or not the reference is a Vec
pub is_vec: bool, pub is_vec: bool,
/// The unique identifier of the type.
pub t: u64, 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 /// 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 /// rapid allocation of script function parameters, where WASM needs to write to a specific
/// pointer. /// pointer.
pub fn temp_allocate_mem_typed<T>(&self) -> AllocatedObject<T> { pub fn temp_allocate_mem_typed<T>(&self, value: T) -> AllocatedObject<T> {
self.temp_allocator.read().as_ref().unwrap().alloc::<T>() self.temp_allocator.read().as_ref().unwrap().alloc::<T>(value)
} }
/// Get a numeric value from any given value. This is not a true Extern Ref from WASM, as this /// Get a numeric value from any given value. This is not a true Extern Ref from WASM, as this

View File

@ -1,14 +1,26 @@
use std::mem::size_of; use std::mem::size_of;
use std::sync::atomic::{AtomicUsize, Ordering}; 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 { pub(super) struct TempWasmAllocator {
/// The raw pointer in host memory where the allocation lives.
data: *mut u8, data: *mut u8,
/// The pointer in client memory where the allocation lives.
wasm_pointer: u32, 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, 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, offset_high: AtomicUsize,
} }
impl TempWasmAllocator { 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 { pub(super) fn new(data: *mut u8, wasm_pointer: u32) -> Self {
Self { Self {
data, data,
@ -18,10 +30,14 @@ impl TempWasmAllocator {
} }
} }
pub fn alloc<T>(&self) -> AllocatedObject<T> { /// Allocates a new object with a certain type.
pub fn alloc<T>(&self, value: T) -> AllocatedObject<T> {
self.currently_allocated.fetch_add(size_of::<T>(), Ordering::SeqCst); self.currently_allocated.fetch_add(size_of::<T>(), Ordering::SeqCst);
let ptr_offset = self.offset_high.fetch_add(size_of::<T>(), Ordering::SeqCst); let ptr_offset = self.offset_high.fetch_add(size_of::<T>(), Ordering::SeqCst);
let ptr = unsafe { self.data.add(ptr_offset) } as *mut T; let ptr = unsafe { self.data.add(ptr_offset) } as *mut T;
unsafe {
*ptr = value;
}
AllocatedObject::<T> { AllocatedObject::<T> {
ptr, ptr,
wasm_pointer: self.wasm_pointer + ptr_offset as u32, wasm_pointer: self.wasm_pointer + ptr_offset as u32,
@ -29,6 +45,7 @@ impl TempWasmAllocator {
} }
} }
/// Drops an earlier allocated type.
pub fn drop<T>(&self) { pub fn drop<T>(&self) {
self.currently_allocated.fetch_sub(size_of::<T>(), Ordering::SeqCst); self.currently_allocated.fetch_sub(size_of::<T>(), Ordering::SeqCst);
// As soon as we've no longer allocated anything, we reset our allocating back to the start. // 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<T> { pub struct AllocatedObject<T> {
/// The pointer in host memory to where the object lives.
pub ptr: *mut T, pub ptr: *mut T,
/// The pointer in client memory to where the object lives.
pub wasm_pointer: u32, pub wasm_pointer: u32,
/// A raw pointer to the allocator we use, so we know where to deallocate to.
allocator: *const TempWasmAllocator, allocator: *const TempWasmAllocator,
} }
impl<T> AllocatedObject<T> {
/// Gets the currently underlying value from the pointer.
pub fn value(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<T> Drop for AllocatedObject<T> { impl<T> Drop for AllocatedObject<T> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {

View File

@ -17,9 +17,9 @@ impl From<u8> for TypeIdentifier {
} }
} }
impl Into<u8> for TypeIdentifier { impl From<TypeIdentifier> for u8 {
fn into(self) -> u8 { fn from(id: TypeIdentifier) -> Self {
self.val id.val
} }
} }

View File

@ -36,14 +36,13 @@ pub fn load_library() -> DynamicLibrary {
let mut resolver = WebAssemblyScriptResolver::new(); let mut resolver = WebAssemblyScriptResolver::new();
load_wasm(&path, resolver.as_mut()); load_wasm(&path, resolver.as_mut());
let dynamic = DynamicLibrary::new( DynamicLibrary::new(
data, data,
Box::new(Gen7BattleStatCalculator {}), Box::new(Gen7BattleStatCalculator {}),
Box::new(Gen7DamageLibrary::new(false)), Box::new(Gen7DamageLibrary::new(false)),
Box::new(Gen7MiscLibrary::new()), Box::new(Gen7MiscLibrary::new()),
resolver, resolver,
); )
dynamic
} }
pub fn load_types(path: &String, type_library: &mut TypeLibrary) { pub fn load_types(path: &String, type_library: &mut TypeLibrary) {

View File

@ -18,7 +18,7 @@ pub mod common;
static LIBRARY: OnceCell<Arc<DynamicLibrary>> = OnceCell::uninit(); static LIBRARY: OnceCell<Arc<DynamicLibrary>> = OnceCell::uninit();
fn get_library<'a>() -> Arc<DynamicLibrary> { fn get_library() -> Arc<DynamicLibrary> {
LIBRARY LIBRARY
.get_or_init(|| { .get_or_init(|| {
let start_time = chrono::Utc::now(); let start_time = chrono::Utc::now();
@ -47,7 +47,7 @@ fn integration_tests(input: &Path) {
let mut str: String = "".to_string(); let mut str: String = "".to_string();
let mut file = File::open(input).unwrap(); let mut file = File::open(input).unwrap();
file.read_to_string(&mut str).unwrap(); file.read_to_string(&mut str).unwrap();
let test_case = serde_yaml::from_str::<TestCase>(&*str).unwrap(); let test_case = serde_yaml::from_str::<TestCase>(&str).unwrap();
println!("\tRunning integration test {}", test_case.name); println!("\tRunning integration test {}", test_case.name);
test_case.run_test(get_library()); test_case.run_test(get_library());
} }
@ -57,7 +57,7 @@ fn integration_tests(input: &Path) {
fn validate_script() { fn validate_script() {
let lib = get_library(); let lib = get_library();
let script = lib let script = lib
.load_script(0 as *const u8, ScriptCategory::Move, &"test".into()) .load_script(std::ptr::null(), ScriptCategory::Move, &"test".into())
.unwrap() .unwrap()
.unwrap(); .unwrap();
let parameters = [EffectParameter::String("foo".into())]; let parameters = [EffectParameter::String("foo".into())];
@ -71,7 +71,7 @@ fn validate_script() {
fn validate_script_2() { fn validate_script_2() {
let lib = get_library(); let lib = get_library();
let script = lib let script = lib
.load_script(0 as *const u8, ScriptCategory::Move, &"test".into()) .load_script(std::ptr::null(), ScriptCategory::Move, &"test".into())
.unwrap() .unwrap()
.unwrap(); .unwrap();
let user = Arc::new( let user = Arc::new(