More work on WASM handling.
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
703fd2c147
commit
2d4253e155
|
@ -56,6 +56,7 @@ serde = { version = "1.0.137", optional = true, features = ["derive"] }
|
||||||
wasmer = { version = "2.3.0", optional = true, default-features = false, features = ["default-cranelift", "universal", "experimental-reference-types-extern-ref"] }
|
wasmer = { version = "2.3.0", optional = true, default-features = false, features = ["default-cranelift", "universal", "experimental-reference-types-extern-ref"] }
|
||||||
unique-type-id = { version = "1.0.0", optional = true }
|
unique-type-id = { version = "1.0.0", optional = true }
|
||||||
unique-type-id-derive = { version = "1.0.0", optional = true }
|
unique-type-id-derive = { version = "1.0.0", optional = true }
|
||||||
|
paste = { version = "1.0.8" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
csv = "1.1.6"
|
csv = "1.1.6"
|
||||||
|
|
|
@ -29,6 +29,7 @@ struct CommonChoiceData<'user, 'library> {
|
||||||
|
|
||||||
/// This enum defines a single choice for a Pokemon for a battle turn.
|
/// This enum defines a single choice for a Pokemon for a battle turn.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||||
pub enum TurnChoice<'user, 'library> {
|
pub enum TurnChoice<'user, 'library> {
|
||||||
/// A move choice tells a Pokemon to use a move on a target for this turn.
|
/// A move choice tells a Pokemon to use a move on a target for this turn.
|
||||||
Move(MoveChoice<'user, 'library>),
|
Move(MoveChoice<'user, 'library>),
|
||||||
|
|
|
@ -91,6 +91,15 @@ pub enum Event<'own, 'battle, 'library> {
|
||||||
/// The health of the Pokemon after the damage.
|
/// The health of the Pokemon after the damage.
|
||||||
new_health: u32,
|
new_health: u32,
|
||||||
},
|
},
|
||||||
|
/// This event happens when a Pokemon gets healed
|
||||||
|
Heal {
|
||||||
|
/// The Pokemon that gets healed.
|
||||||
|
pokemon: &'own Pokemon<'battle, 'library>,
|
||||||
|
/// The health of the Pokemon before the heal.
|
||||||
|
original_health: u32,
|
||||||
|
/// The health of the Pokemon after the heal.
|
||||||
|
new_health: u32,
|
||||||
|
},
|
||||||
/// This event happens when a Pokemon faints.
|
/// This event happens when a Pokemon faints.
|
||||||
Faint {
|
Faint {
|
||||||
/// The pokemon that has fainted.
|
/// The pokemon that has fainted.
|
||||||
|
|
|
@ -68,7 +68,7 @@ impl<'library> Default for Gen7MiscLibrary<'library> {
|
||||||
impl<'library> Drop for Gen7MiscLibrary<'library> {
|
impl<'library> Drop for Gen7MiscLibrary<'library> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
Box::from_raw(self.struggle_data as *mut MoveData);
|
let _ = Box::from_raw(self.struggle_data as *mut MoveData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::static_data::MoveData;
|
||||||
/// A learned move is the data attached to a Pokemon for a move it has learned. It has information
|
/// A learned move is the data attached to a Pokemon for a move it has learned. It has information
|
||||||
/// such as the remaining amount of users, how it has been learned, etc.
|
/// such as the remaining amount of users, how it has been learned, etc.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||||
pub struct LearnedMove<'library> {
|
pub struct LearnedMove<'library> {
|
||||||
/// The immutable move information of the move.
|
/// The immutable move information of the move.
|
||||||
move_data: &'library MoveData,
|
move_data: &'library MoveData,
|
||||||
|
@ -18,6 +19,7 @@ pub struct LearnedMove<'library> {
|
||||||
|
|
||||||
/// The different ways a move can be learned.
|
/// The different ways a move can be learned.
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum MoveLearnMethod {
|
pub enum MoveLearnMethod {
|
||||||
/// We do not know the learn method.
|
/// We do not know the learn method.
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
@ -25,6 +25,7 @@ use crate::{script_hook, PkmnResult, StringKey};
|
||||||
|
|
||||||
/// An individual Pokemon as we know and love them.
|
/// An individual Pokemon as we know and love them.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||||
pub struct Pokemon<'own, 'library>
|
pub struct Pokemon<'own, 'library>
|
||||||
where
|
where
|
||||||
'own: 'library,
|
'own: 'library,
|
||||||
|
@ -306,6 +307,12 @@ impl<'own, 'library> Pokemon<'own, 'library> {
|
||||||
pub fn flat_stats(&self) -> &StatisticSet<u32> {
|
pub fn flat_stats(&self) -> &StatisticSet<u32> {
|
||||||
&self.flat_stats
|
&self.flat_stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The stats of the Pokemon including the stat boosts
|
||||||
|
pub fn stat_boosts(&self) -> &ClampedStatisticSet<i8, -6, 6> {
|
||||||
|
&self.stat_boost
|
||||||
|
}
|
||||||
|
|
||||||
/// The stats of the Pokemon including the stat boosts
|
/// The stats of the Pokemon including the stat boosts
|
||||||
pub fn boosted_stats(&self) -> &StatisticSet<u32> {
|
pub fn boosted_stats(&self) -> &StatisticSet<u32> {
|
||||||
&self.boosted_stats
|
&self.boosted_stats
|
||||||
|
@ -613,7 +620,6 @@ impl<'own, 'library> Pokemon<'own, 'library> {
|
||||||
original_health: self.current_health(),
|
original_health: self.current_health(),
|
||||||
new_health,
|
new_health,
|
||||||
});
|
});
|
||||||
// TODO: register history
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.battle_data.read().is_some_and(|a| a.on_battle_field()) {
|
if self.battle_data.read().is_some_and(|a| a.on_battle_field()) {
|
||||||
|
@ -645,6 +651,33 @@ impl<'own, 'library> Pokemon<'own, 'library> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Heals the Pokemon by a specific amount. Unless allow_revive is set to true, this will not
|
||||||
|
/// heal if the Pokemon has 0 health. If the amount healed is 0, this will return false.
|
||||||
|
pub fn heal(&self, mut amount: u32, allow_revive: bool) -> bool {
|
||||||
|
if self.current_health() == 0 && !allow_revive {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let max_amount = self.max_health() - self.current_health();
|
||||||
|
if amount > max_amount {
|
||||||
|
amount = max_amount;
|
||||||
|
}
|
||||||
|
if amount == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let new_health = self.current_health() + max_amount;
|
||||||
|
if let Some(battle_data) = &self.battle_data.read().deref() {
|
||||||
|
if let Some(battle) = battle_data.battle() {
|
||||||
|
battle.event_hook().trigger(Event::Heal {
|
||||||
|
pokemon: self,
|
||||||
|
original_health: self.current_health(),
|
||||||
|
new_health,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.current_health.store(new_health, Ordering::SeqCst);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// Learn a move.
|
/// Learn a move.
|
||||||
pub fn learn_move(&self, move_name: &StringKey, learn_method: MoveLearnMethod) {
|
pub fn learn_move(&self, move_name: &StringKey, learn_method: MoveLearnMethod) {
|
||||||
let mut learned_moves = self.learned_moves().write();
|
let mut learned_moves = self.learned_moves().write();
|
||||||
|
@ -745,6 +778,7 @@ impl<'own, 'library> VolatileScriptsOwner<'own> for Pokemon<'own, 'library> {
|
||||||
|
|
||||||
/// A source of damage. This should be as unique as possible.
|
/// A source of damage. This should be as unique as possible.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum DamageSource {
|
pub enum DamageSource {
|
||||||
/// The damage is done by a move.
|
/// The damage is done by a move.
|
||||||
MoveDamage = 0,
|
MoveDamage = 0,
|
||||||
|
|
|
@ -5,11 +5,9 @@
|
||||||
#![deny(clippy::missing_docs_in_private_items)]
|
#![deny(clippy::missing_docs_in_private_items)]
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
#![feature(bench_black_box)]
|
#![feature(bench_black_box)]
|
||||||
#![feature(let_chains)]
|
|
||||||
#![feature(once_cell)]
|
#![feature(once_cell)]
|
||||||
#![feature(const_option)]
|
#![feature(const_option)]
|
||||||
#![feature(is_some_with)]
|
#![feature(is_some_with)]
|
||||||
#![feature(core_ffi_c)]
|
|
||||||
#![feature(new_uninit)]
|
#![feature(new_uninit)]
|
||||||
#![feature(get_mut_unchecked)]
|
#![feature(get_mut_unchecked)]
|
||||||
|
|
||||||
|
@ -18,7 +16,6 @@
|
||||||
//! generation 7, this library tries to offload generational differences such as move effects
|
//! generation 7, this library tries to offload generational differences such as move effects
|
||||||
//! to a scripting library.
|
//! to a scripting library.
|
||||||
//!
|
//!
|
||||||
extern crate core;
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use utils::*;
|
pub use utils::*;
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
use std::intrinsics::transmute;
|
||||||
|
|
||||||
|
use crate::dynamic_data::{LearnedMove, MoveLearnMethod, Pokemon, TurnChoice};
|
||||||
|
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;
|
||||||
|
|
||||||
|
register! {
|
||||||
|
fn learned_move_get_learn_method<'a>(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
turn_choice: ExternRef<LearnedMove>,
|
||||||
|
) -> u8 {
|
||||||
|
unsafe {
|
||||||
|
transmute(turn_choice.value(env).unwrap().learn_method())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn learned_move_get_move_data<'a>(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
turn_choice: ExternRef<LearnedMove>,
|
||||||
|
) -> ExternRef<MoveData> {
|
||||||
|
ExternRef::new(env.data().as_ref(), turn_choice.value(env).unwrap().move_data())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
use wasmer::{Exports, Store};
|
||||||
|
|
||||||
|
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||||
|
|
||||||
|
mod learned_move;
|
||||||
|
mod pokemon;
|
||||||
|
mod turn_choice;
|
||||||
|
|
||||||
|
pub(crate) fn 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());
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
use std::mem::transmute;
|
||||||
|
|
||||||
|
use crate::dynamic_data::{DamageSource, DynamicLibrary, 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};
|
||||||
|
|
||||||
|
register! {
|
||||||
|
fn pokemon_get_library(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
pokemon: ExternRef<Pokemon>,
|
||||||
|
) -> ExternRef<DynamicLibrary> {
|
||||||
|
let lib = pokemon.value(env).unwrap().library();
|
||||||
|
ExternRef::new(env.data().as_ref(), lib)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pokemon_get_boosted_stats(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
pokemon: ExternRef<Pokemon>,
|
||||||
|
) -> ExternRef<StatisticSet<u32>> {
|
||||||
|
let statistic_set = pokemon.value(env).unwrap().boosted_stats();
|
||||||
|
ExternRef::new(env.data().as_ref(), statistic_set)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pokemon_get_flat_stats(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
pokemon: ExternRef<Pokemon>,
|
||||||
|
) -> ExternRef<StatisticSet<u32>> {
|
||||||
|
let statistic_set = pokemon.value(env).unwrap().flat_stats();
|
||||||
|
ExternRef::new(env.data().as_ref(), statistic_set)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pokemon_get_stat_boosts(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
pokemon: ExternRef<Pokemon>,
|
||||||
|
) -> ExternRef<ClampedStatisticSet<i8, -6, 6>> {
|
||||||
|
let statistic_set = pokemon.value(env).unwrap().stat_boosts();
|
||||||
|
ExternRef::new(env.data().as_ref(), statistic_set)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pokemon_get_individual_values(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
pokemon: ExternRef<Pokemon>,
|
||||||
|
) -> ExternRef<ClampedStatisticSet<u8, 0, 31>> {
|
||||||
|
let statistic_set = pokemon.value(env).unwrap().individual_values();
|
||||||
|
ExternRef::new(env.data().as_ref(), statistic_set)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pokemon_get_effort_values(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
pokemon: ExternRef<Pokemon>,
|
||||||
|
) -> ExternRef<ClampedStatisticSet<u8, 0, 252>> {
|
||||||
|
let statistic_set = pokemon.value(env).unwrap().effort_values();
|
||||||
|
ExternRef::new(env.data().as_ref(), statistic_set)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pokemon_get_species(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
pokemon: ExternRef<Pokemon>,
|
||||||
|
) -> ExternRef<Species> {
|
||||||
|
let species = pokemon.value(env).unwrap().species();
|
||||||
|
ExternRef::new(env.data().as_ref(), species)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pokemon_damage(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
pokemon: ExternRef<Pokemon>,
|
||||||
|
damage: u32,
|
||||||
|
source: u8
|
||||||
|
) {
|
||||||
|
unsafe{
|
||||||
|
pokemon.value(env).unwrap().damage(damage, transmute(source));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use crate::dynamic_data::{LearnedMove, Pokemon, TurnChoice};
|
||||||
|
use crate::script_implementations::wasm::export_registry::register;
|
||||||
|
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||||
|
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||||
|
|
||||||
|
register! {
|
||||||
|
|
||||||
|
fn turn_choice_get_user<'a>(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
turn_choice: ExternRef<TurnChoice<'a, 'a>>,
|
||||||
|
) -> ExternRef<Pokemon<'a, 'a>> {
|
||||||
|
let turn_choice = turn_choice.value(env).unwrap();
|
||||||
|
ExternRef::new(env.data().as_ref(), turn_choice.user().as_ref().deref())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_choice_get_kind(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
turn_choice: ExternRef<TurnChoice>,
|
||||||
|
) -> u8 {
|
||||||
|
match turn_choice.value(env).unwrap() {
|
||||||
|
TurnChoice::Move(_) => 0,
|
||||||
|
TurnChoice::Item(_) => 1,
|
||||||
|
TurnChoice::Switch(_) => 2,
|
||||||
|
TurnChoice::Flee(_) => 3,
|
||||||
|
TurnChoice::Pass(_) => 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_choice_move_used_move<'a>(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
turn_choice: ExternRef<TurnChoice<'a, 'a>>,
|
||||||
|
) -> ExternRef<LearnedMove<'a>> {
|
||||||
|
if let TurnChoice::Move(d) = turn_choice.value(env).unwrap() {
|
||||||
|
return ExternRef::new(env.data().as_ref(), d.used_move().as_ref());
|
||||||
|
}
|
||||||
|
panic!("Invalid turn choice");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_choice_move_target_side(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
turn_choice: ExternRef<TurnChoice>,
|
||||||
|
) -> u8 {
|
||||||
|
if let TurnChoice::Move(d) = turn_choice.value(env).unwrap() {
|
||||||
|
return d.target_side();
|
||||||
|
}
|
||||||
|
panic!("Invalid turn choice");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_choice_move_target_index(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
turn_choice: ExternRef<TurnChoice>,
|
||||||
|
) -> u8 {
|
||||||
|
if let TurnChoice::Move(d) = turn_choice.value(env).unwrap() {
|
||||||
|
return d.target_index();
|
||||||
|
}
|
||||||
|
panic!("Invalid turn choice");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,14 +1,21 @@
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::mem::{align_of, forget};
|
use std::mem::{align_of, forget};
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
use wasmer::{Exports, Function, Store};
|
use wasmer::{Exports, Store};
|
||||||
|
|
||||||
|
pub(crate) use register;
|
||||||
|
pub(crate) use register_func_with_env;
|
||||||
|
|
||||||
use crate::dynamic_data::DynamicLibrary;
|
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::{DataLibrary, MoveData, MoveLibrary, StaticData};
|
use crate::static_data::{EffectParameter, StaticData};
|
||||||
use crate::StringKey;
|
use crate::StringKey;
|
||||||
|
|
||||||
|
mod dynamic_data;
|
||||||
|
mod static_data;
|
||||||
|
|
||||||
#[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) => {
|
||||||
|
@ -20,21 +27,50 @@ 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(
|
||||||
stringify!($func),
|
stringify!($func),
|
||||||
Function::new_native_with_env($store, $env.clone(), $func),
|
wasmer::Function::new_native_with_env($store, $env.clone(), $func),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! register {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
fn $name:ident$(<$($lt:lifetime)*>)?($($par:ident: $par_type:ty),*$(,)?) $(-> $return:ty)? $block:block
|
||||||
|
)*
|
||||||
|
) => {
|
||||||
|
pub(crate) fn register(exports: &mut crate::script_implementations::wasm::export_registry::Exports,
|
||||||
|
store: &crate::script_implementations::wasm::export_registry::Store,
|
||||||
|
env: crate::script_implementations::wasm::script_resolver::WebAssemblyEnv) {
|
||||||
|
$(
|
||||||
|
|
||||||
|
fn $name<$($($lt)*)*>(
|
||||||
|
$(
|
||||||
|
$par: $par_type,
|
||||||
|
)*
|
||||||
|
) $(-> $return)* $block
|
||||||
|
crate::script_implementations::wasm::export_registry::register_func_with_env!(exports, store, $name, env);
|
||||||
|
)*
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
register_func_with_env!(exports, store, move_library_get_move_by_hash, env);
|
register_func_with_env!(exports, store, _vec_extern_ref_get_value, env);
|
||||||
register_func_with_env!(exports, store, move_data_get_name, env);
|
|
||||||
register_func_with_env!(exports, store, move_data_get_base_power, env);
|
static_data::register(exports, store, env.clone());
|
||||||
register_func_with_env!(exports, store, const_string_get_hash, env);
|
dynamic_data::register(exports, store, env.clone());
|
||||||
register_func_with_env!(exports, store, const_string_get_str, env);
|
|
||||||
register_func_with_env!(exports, store, battle_library_get_data_library, env);
|
register_func_with_env!(exports, store, string_key_get_hash, env);
|
||||||
register_func_with_env!(exports, store, data_library_get_move_library, 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);
|
||||||
|
register_func_with_env!(exports, store, effect_parameter_as_float, env);
|
||||||
|
register_func_with_env!(exports, store, effect_parameter_as_string, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _print(env: &WebAssemblyEnv, p: u32, len: u32) {
|
fn _print(env: &WebAssemblyEnv, p: u32, len: u32) {
|
||||||
|
@ -46,42 +82,29 @@ fn _print(env: &WebAssemblyEnv, p: u32, len: u32) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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 {
|
||||||
let mem: *mut u8 = env.data().memory().data_ptr().offset(message as isize);
|
let mem: *mut u8 = env.data().memory().data_ptr().offset(message as isize);
|
||||||
let message = String::from_raw_parts(mem, message_len as usize, message_len as usize);
|
let message_str = String::from_raw_parts(mem, message_len as usize, message_len as usize);
|
||||||
let mem: *mut u8 = env.data().memory().data_ptr().offset(file as isize);
|
let mem: *mut u8 = env.data().memory().data_ptr().offset(file as isize);
|
||||||
let file = String::from_raw_parts(mem, file_len as usize, file_len as usize);
|
let file_str = String::from_raw_parts(mem, file_len as usize, file_len as usize);
|
||||||
println!("Error: {} in file {}, line: {}:{}", message, file, line, position);
|
panic!(
|
||||||
forget(message);
|
"Error: {} in file {}, line: {}:{}",
|
||||||
forget(file);
|
message_str, file_str, line, position
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_library_get_move_by_hash(env: &WebAssemblyEnv, lib: ExternRef<MoveLibrary>, hash: u32) -> ExternRef<MoveData> {
|
fn _vec_extern_ref_get_value(env: &WebAssemblyEnv, reference: u32, index: u32) -> u32 {
|
||||||
let lib = lib.value(env).unwrap();
|
env.data().get_extern_vec_ref_extern_ref(reference, index)
|
||||||
let m = lib.get_by_hash(hash);
|
|
||||||
if let Some(v) = m {
|
|
||||||
ExternRef::new(env.data().as_ref(), v)
|
|
||||||
} else {
|
|
||||||
ExternRef::null()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_data_get_name(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>) -> ExternRef<StringKey> {
|
fn string_key_get_hash(env: &WebAssemblyEnv, string_key: ExternRef<StringKey>) -> u32 {
|
||||||
let move_data = move_data.value(env).unwrap();
|
|
||||||
ExternRef::new(env.data().as_ref(), move_data.name())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn move_data_get_base_power(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>) -> u8 {
|
|
||||||
move_data.value(env).unwrap().base_power()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn const_string_get_hash(env: &WebAssemblyEnv, string_key: ExternRef<StringKey>) -> u32 {
|
|
||||||
string_key.value(env).unwrap().hash()
|
string_key.value(env).unwrap().hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn const_string_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
|
||||||
.data()
|
.data()
|
||||||
|
@ -95,13 +118,57 @@ fn const_string_get_str(env: &WebAssemblyEnv, string_key: ExternRef<StringKey>)
|
||||||
wasm_string_ptr.1
|
wasm_string_ptr.1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn battle_library_get_data_library(
|
fn dynamic_library_get_static_data(
|
||||||
env: &WebAssemblyEnv,
|
env: &WebAssemblyEnv,
|
||||||
dynamic_lib: ExternRef<DynamicLibrary>,
|
dynamic_lib: ExternRef<DynamicLibrary>,
|
||||||
) -> ExternRef<StaticData> {
|
) -> ExternRef<StaticData> {
|
||||||
ExternRef::new(env.data().as_ref(), dynamic_lib.value(env).unwrap().static_data())
|
ExternRef::new(env.data().as_ref(), dynamic_lib.value(env).unwrap().static_data())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn data_library_get_move_library(env: &WebAssemblyEnv, data_library: ExternRef<StaticData>) -> ExternRef<MoveLibrary> {
|
fn effect_parameter_get_type(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> u8 {
|
||||||
ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().moves())
|
let v = parameter.value(env).unwrap();
|
||||||
|
match v {
|
||||||
|
EffectParameter::Bool(_) => 1,
|
||||||
|
EffectParameter::Int(_) => 2,
|
||||||
|
EffectParameter::Float(_) => 3,
|
||||||
|
EffectParameter::String(_) => 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn effect_parameter_as_bool(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> u8 {
|
||||||
|
let v = parameter.value(env).unwrap();
|
||||||
|
match v {
|
||||||
|
EffectParameter::Bool(b) => {
|
||||||
|
if *b {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("Unexpected parameter type!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn effect_parameter_as_int(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> i64 {
|
||||||
|
let v = parameter.value(env).unwrap();
|
||||||
|
match v {
|
||||||
|
EffectParameter::Int(i) => *i,
|
||||||
|
_ => panic!("Unexpected parameter type!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn effect_parameter_as_float(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> f32 {
|
||||||
|
let v = parameter.value(env).unwrap();
|
||||||
|
match v {
|
||||||
|
EffectParameter::Float(f) => *f,
|
||||||
|
_ => panic!("Unexpected parameter type!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn effect_parameter_as_string(env: &WebAssemblyEnv, parameter: ExternRef<EffectParameter>) -> ExternRef<StringKey> {
|
||||||
|
let v = parameter.value(env).unwrap();
|
||||||
|
match v {
|
||||||
|
EffectParameter::String(s) => ExternRef::new(env.data().as_ref(), s),
|
||||||
|
_ => panic!("Unexpected parameter type!"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +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::extern_ref::ExternRef;
|
||||||
|
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
|
||||||
|
use crate::static_data::{ItemLibrary, LibrarySettings, MoveLibrary, SpeciesLibrary, StaticData, TypeLibrary};
|
||||||
|
|
||||||
|
mod moves;
|
||||||
|
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);
|
||||||
|
|
||||||
|
moves::register(exports, store, env.clone());
|
||||||
|
species::register(exports, store, env.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
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::{DataLibrary, MoveData, MoveLibrary};
|
||||||
|
use crate::StringKey;
|
||||||
|
|
||||||
|
register! {
|
||||||
|
fn move_library_get_move(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
lib: ExternRef<MoveLibrary>,
|
||||||
|
string_key: ExternRef<StringKey>,
|
||||||
|
) -> ExternRef<MoveData> {
|
||||||
|
let lib = lib.value(env).unwrap();
|
||||||
|
let m = lib.get(string_key.value(env).unwrap());
|
||||||
|
if let Some(v) = m {
|
||||||
|
ExternRef::new(env.data().as_ref(), v)
|
||||||
|
} else {
|
||||||
|
ExternRef::null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_library_get_move_by_hash(env: &WebAssemblyEnv, lib: ExternRef<MoveLibrary>, hash: u32) -> ExternRef<MoveData> {
|
||||||
|
let lib = lib.value(env).unwrap();
|
||||||
|
let m = lib.get_by_hash(hash);
|
||||||
|
if let Some(v) = m {
|
||||||
|
ExternRef::new(env.data().as_ref(), v)
|
||||||
|
} else {
|
||||||
|
ExternRef::null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_data_get_name(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>) -> ExternRef<StringKey> {
|
||||||
|
ExternRef::new(env.data().as_ref(), move_data.value(env).unwrap().name())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_data_get_type(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>) -> u8 {
|
||||||
|
move_data.value(env).unwrap().move_type().into()
|
||||||
|
}
|
||||||
|
fn move_data_get_category(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>) -> u8 {
|
||||||
|
move_data.value(env).unwrap().category() as u8
|
||||||
|
}
|
||||||
|
fn move_data_get_base_power(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>) -> u8 {
|
||||||
|
move_data.value(env).unwrap().base_power()
|
||||||
|
}
|
||||||
|
fn move_data_get_accuracy(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>) -> u8 {
|
||||||
|
move_data.value(env).unwrap().accuracy()
|
||||||
|
}
|
||||||
|
fn move_data_get_base_usages(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>) -> u8 {
|
||||||
|
move_data.value(env).unwrap().base_usages()
|
||||||
|
}
|
||||||
|
fn move_data_get_target(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>) -> u8 {
|
||||||
|
move_data.value(env).unwrap().target() as u8
|
||||||
|
}
|
||||||
|
fn move_data_get_priority(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>) -> i8 {
|
||||||
|
move_data.value(env).unwrap().priority()
|
||||||
|
}
|
||||||
|
fn move_data_has_flag(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>, flag: ExternRef<StringKey>) -> u8 {
|
||||||
|
if move_data.value(env).unwrap().has_flag(flag.value(env).unwrap()) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn move_data_has_flag_by_hash(env: &WebAssemblyEnv, move_data: ExternRef<MoveData>, flag_hash: u32) -> u8 {
|
||||||
|
if move_data.value(env).unwrap().has_flag_by_hash(flag_hash) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
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::{DataLibrary, Species, SpeciesLibrary};
|
||||||
|
use crate::StringKey;
|
||||||
|
|
||||||
|
register! {
|
||||||
|
|
||||||
|
fn species_library_get_species(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
lib: ExternRef<SpeciesLibrary>,
|
||||||
|
string_key: ExternRef<StringKey>,
|
||||||
|
) -> ExternRef<Species> {
|
||||||
|
let lib = lib.value(env).unwrap();
|
||||||
|
let m = lib.get(string_key.value(env).unwrap());
|
||||||
|
if let Some(v) = m {
|
||||||
|
ExternRef::new(env.data().as_ref(), v)
|
||||||
|
} else {
|
||||||
|
ExternRef::null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn species_get_capture_rate(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
species: ExternRef<Species>,
|
||||||
|
) -> u8 {
|
||||||
|
species.value(env).unwrap().capture_rate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn species_get_growth_rate(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
species: ExternRef<Species>,
|
||||||
|
) -> ExternRef<StringKey> {
|
||||||
|
let species = species.value(env).unwrap();
|
||||||
|
ExternRef::new(env.data().as_ref(), species.growth_rate())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn species_get_gender_rate(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
species: ExternRef<Species>,
|
||||||
|
) -> f32 {
|
||||||
|
species.value(env).unwrap().gender_rate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn species_get_name(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
species: ExternRef<Species>,
|
||||||
|
) -> ExternRef<StringKey> {
|
||||||
|
let species = species.value(env).unwrap();
|
||||||
|
ExternRef::new(env.data().as_ref(), species.name())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn species_get_id(
|
||||||
|
env: &WebAssemblyEnv,
|
||||||
|
species: ExternRef<Species>,
|
||||||
|
) -> u16 {
|
||||||
|
species.value(env).unwrap().id()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::mem::transmute;
|
||||||
|
|
||||||
use unique_type_id::UniqueTypeId;
|
use unique_type_id::UniqueTypeId;
|
||||||
use wasmer::FromToNativeWasmType;
|
use wasmer::FromToNativeWasmType;
|
||||||
|
@ -7,7 +8,7 @@ use crate::script_implementations::wasm::script_resolver::{
|
||||||
WebAssemblyEnv, WebAssemblyEnvironmentData, WebAssemblyScriptResolver,
|
WebAssemblyEnv, WebAssemblyEnvironmentData, WebAssemblyScriptResolver,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) struct ExternRef<T: UniqueTypeId<u64> + ?Sized> {
|
pub(crate) struct ExternRef<T: UniqueTypeId<u64>> {
|
||||||
index: u32,
|
index: u32,
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
@ -20,6 +21,10 @@ impl<T: UniqueTypeId<u64>> ExternRef<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn index(&self) -> u32 {
|
||||||
|
self.index
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates an ExternRef with a given resolver. This can be used in cases where we do not have an environment variable.
|
/// Creates an ExternRef with a given resolver. This can be used in cases where we do not have an environment variable.
|
||||||
pub(crate) fn new_with_resolver(resolver: &WebAssemblyScriptResolver, value: &T) -> Self {
|
pub(crate) fn new_with_resolver(resolver: &WebAssemblyScriptResolver, value: &T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -58,3 +63,37 @@ unsafe impl<T: UniqueTypeId<u64>> FromToNativeWasmType for ExternRef<T> {
|
||||||
self.index as i32
|
self.index as i32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct VecExternRef<T> {
|
||||||
|
index: u32,
|
||||||
|
size: u32,
|
||||||
|
_phantom: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: UniqueTypeId<u64>> VecExternRef<T> {
|
||||||
|
pub fn new(env: &WebAssemblyEnvironmentData, value: &[T]) -> Self {
|
||||||
|
Self {
|
||||||
|
index: env.get_extern_vec_ref_index(value),
|
||||||
|
size: value.len() as u32,
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: UniqueTypeId<u64>> FromToNativeWasmType for VecExternRef<T> {
|
||||||
|
type Native = i64;
|
||||||
|
|
||||||
|
fn from_native(native: Self::Native) -> Self {
|
||||||
|
let split: (u32, u32) = unsafe { transmute(native) };
|
||||||
|
Self {
|
||||||
|
index: split.0,
|
||||||
|
size: split.1,
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_native(self) -> Self::Native {
|
||||||
|
let v: i64 = unsafe { transmute((self.index, self.size)) };
|
||||||
|
v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ pub(crate) mod extern_ref;
|
||||||
pub mod script;
|
pub mod script;
|
||||||
/// 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;
|
||||||
|
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.
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::mem::{align_of, size_of};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize};
|
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize};
|
||||||
use std::sync::Weak;
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use wasmer::NativeFunc;
|
use wasmer::NativeFunc;
|
||||||
|
|
||||||
use crate::dynamic_data::{DynamicLibrary, Script};
|
use crate::dynamic_data::{DynamicLibrary, Pokemon, Script, TurnChoice};
|
||||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef};
|
||||||
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData;
|
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData;
|
||||||
use crate::script_implementations::wasm::WebAssemblyScriptCapabilities;
|
use crate::script_implementations::wasm::WebAssemblyScriptCapabilities;
|
||||||
use crate::static_data::EffectParameter;
|
use crate::static_data::EffectParameter;
|
||||||
|
@ -24,11 +25,11 @@ pub struct WebAssemblyScript {
|
||||||
/// we will not execute its methods. This holds the number of suppressions on the script.
|
/// we will not execute its methods. This holds the number of suppressions on the script.
|
||||||
suppressed_count: AtomicUsize,
|
suppressed_count: AtomicUsize,
|
||||||
/// The owner of this script (where the script is attached to)
|
/// The owner of this script (where the script is attached to)
|
||||||
owner_ptr: AtomicPtr<u8>,
|
_owner_ptr: AtomicPtr<u8>,
|
||||||
/// Pointer inside WebAssembly memory where the data is for this script.
|
/// Pointer inside WebAssembly memory where the data is for this script.
|
||||||
self_ptr: u32,
|
self_ptr: u32,
|
||||||
/// Capabilities define which functions we actually implement.
|
/// Capabilities define which functions we actually implement.
|
||||||
capabilities: AtomicPtr<HashSet<WebAssemblyScriptCapabilities>>,
|
capabilities: Arc<HashSet<WebAssemblyScriptCapabilities>>,
|
||||||
/// The global runtime environment data.
|
/// The global runtime environment data.
|
||||||
environment: Weak<WebAssemblyEnvironmentData>,
|
environment: Weak<WebAssemblyEnvironmentData>,
|
||||||
}
|
}
|
||||||
|
@ -38,7 +39,7 @@ impl WebAssemblyScript {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
owner_ptr: *mut u8,
|
owner_ptr: *mut u8,
|
||||||
self_ptr: u32,
|
self_ptr: u32,
|
||||||
capabilities: *mut HashSet<WebAssemblyScriptCapabilities>,
|
capabilities: Arc<HashSet<WebAssemblyScriptCapabilities>>,
|
||||||
environment: Weak<WebAssemblyEnvironmentData>,
|
environment: Weak<WebAssemblyEnvironmentData>,
|
||||||
name: StringKey,
|
name: StringKey,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -46,9 +47,9 @@ impl WebAssemblyScript {
|
||||||
name,
|
name,
|
||||||
marked_for_deletion: Default::default(),
|
marked_for_deletion: Default::default(),
|
||||||
suppressed_count: Default::default(),
|
suppressed_count: Default::default(),
|
||||||
owner_ptr: AtomicPtr::new(owner_ptr),
|
_owner_ptr: AtomicPtr::new(owner_ptr),
|
||||||
self_ptr,
|
self_ptr,
|
||||||
capabilities: AtomicPtr::new(capabilities),
|
capabilities,
|
||||||
environment,
|
environment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,20 +68,56 @@ impl Script for WebAssemblyScript {
|
||||||
&self.suppressed_count
|
&self.suppressed_count
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_initialize(&self, library: &DynamicLibrary, _pars: &[EffectParameter]) {
|
fn on_initialize(&self, library: &DynamicLibrary, pars: &[EffectParameter]) {
|
||||||
|
if !self.capabilities.contains(&WebAssemblyScriptCapabilities::Initialize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let env = self.environment.upgrade().unwrap();
|
||||||
|
let func = env.script_function_cache().on_initialize(&env);
|
||||||
|
if let Some(func) = func {
|
||||||
|
func.call(
|
||||||
|
self.self_ptr,
|
||||||
|
ExternRef::new(env.as_ref(), library),
|
||||||
|
VecExternRef::new(env.as_ref(), pars),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_before_turn(&self, choice: &TurnChoice) {
|
||||||
|
if !self.capabilities.contains(&WebAssemblyScriptCapabilities::OnBeforeTurn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let env = self.environment.upgrade().unwrap();
|
||||||
|
let func = env.script_function_cache().on_before_turn(&env);
|
||||||
|
if let Some(func) = func {
|
||||||
|
func.call(self.self_ptr, ExternRef::new(env.as_ref(), choice).index())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_speed(&self, choice: &TurnChoice, speed: &mut u32) {
|
||||||
|
if !self.capabilities.contains(&WebAssemblyScriptCapabilities::ChangeSpeed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let env = self.environment.upgrade().unwrap();
|
let env = self.environment.upgrade().unwrap();
|
||||||
let exported = env.exported_functions();
|
let exported = env.exported_functions();
|
||||||
if let Some(f) = exported.get(&"script_on_initialize".into()) {
|
if let Some(f) = exported.get::<StringKey>(&"script_change_speed".into()) {
|
||||||
let func: NativeFunc<(u32, ExternRef<DynamicLibrary>, u32), ()> = f.native().unwrap();
|
let func: NativeFunc<(u32, ExternRef<TurnChoice>, u32), ()> = f.native().unwrap();
|
||||||
func.call(self.self_ptr, ExternRef::new(env.as_ref(), library), 0)
|
let ptr = env.temp_allocate_mem_typed::<u32>();
|
||||||
|
func.call(self.self_ptr, ExternRef::new(env.as_ref(), choice), ptr.wasm_pointer)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
unsafe {
|
||||||
|
*speed = *ptr.ptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use paste::paste;
|
||||||
|
use wasmer::NativeFunc;
|
||||||
|
|
||||||
|
use crate::dynamic_data::{DynamicLibrary, TurnChoice};
|
||||||
|
use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef};
|
||||||
|
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData;
|
||||||
|
use crate::static_data::EffectParameter;
|
||||||
|
use crate::StringKey;
|
||||||
|
|
||||||
|
macro_rules! script_function_cache {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
$name:ident -> $return:ty
|
||||||
|
)*
|
||||||
|
) => {
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ScriptFunctionCache {
|
||||||
|
$(
|
||||||
|
$name: RwLock<Option<$return>>,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScriptFunctionCache {
|
||||||
|
$(
|
||||||
|
paste! {
|
||||||
|
#[cold]
|
||||||
|
fn [<initialize_ $name>](&self, env: &Arc<WebAssemblyEnvironmentData>) {
|
||||||
|
let exported = env.exported_functions();
|
||||||
|
let f = exported.get::<StringKey>(&stringify!([< script_ $name >]).into());
|
||||||
|
if let Some(f) = f {
|
||||||
|
let func: $return = f.native().unwrap();
|
||||||
|
let _ = self.$name.write().insert(func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(crate) fn [<$name>](
|
||||||
|
&self,
|
||||||
|
env: &Arc<WebAssemblyEnvironmentData>,
|
||||||
|
) -> Option<$return> {
|
||||||
|
{
|
||||||
|
let read_lock = self.$name.read();
|
||||||
|
if let Some(f) = read_lock.as_ref() {
|
||||||
|
return Some(f.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.[<initialize_ $name>](env);
|
||||||
|
self.$name.read().as_ref().cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script_function_cache! {
|
||||||
|
on_initialize -> NativeFunc<(u32, ExternRef<DynamicLibrary>, VecExternRef<EffectParameter>), ()>
|
||||||
|
on_before_turn -> NativeFunc<(u32, u32), ()>
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::mem::{align_of, size_of};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
@ -14,6 +15,8 @@ use crate::dynamic_data::{ItemScript, Script, ScriptResolver};
|
||||||
use crate::script_implementations::wasm::export_registry::register_webassembly_funcs;
|
use crate::script_implementations::wasm::export_registry::register_webassembly_funcs;
|
||||||
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
use crate::script_implementations::wasm::extern_ref::ExternRef;
|
||||||
use crate::script_implementations::wasm::script::WebAssemblyScript;
|
use crate::script_implementations::wasm::script::WebAssemblyScript;
|
||||||
|
use crate::script_implementations::wasm::script_function_cache::ScriptFunctionCache;
|
||||||
|
use crate::script_implementations::wasm::temp_wasm_allocator::{AllocatedObject, TempWasmAllocator};
|
||||||
use crate::script_implementations::wasm::WebAssemblyScriptCapabilities;
|
use crate::script_implementations::wasm::WebAssemblyScriptCapabilities;
|
||||||
use crate::static_data::Item;
|
use crate::static_data::Item;
|
||||||
use crate::{PkmnResult, ScriptCategory, StringKey};
|
use crate::{PkmnResult, ScriptCategory, StringKey};
|
||||||
|
@ -33,7 +36,7 @@ pub struct WebAssemblyScriptResolver {
|
||||||
|
|
||||||
/// Script capabilities tell us which functions are implemented on a given script. This allows us to skip unneeded
|
/// Script capabilities tell us which functions are implemented on a given script. This allows us to skip unneeded
|
||||||
/// WASM calls.
|
/// WASM calls.
|
||||||
script_capabilities: RwLock<HashMap<ScriptCapabilitiesKey, HashSet<WebAssemblyScriptCapabilities>>>,
|
script_capabilities: RwLock<HashMap<ScriptCapabilitiesKey, Arc<HashSet<WebAssemblyScriptCapabilities>>>>,
|
||||||
|
|
||||||
environment_data: Arc<WebAssemblyEnvironmentData>,
|
environment_data: Arc<WebAssemblyEnvironmentData>,
|
||||||
}
|
}
|
||||||
|
@ -85,8 +88,26 @@ impl WebAssemblyScriptResolver {
|
||||||
imports.register("env", exports);
|
imports.register("env", exports);
|
||||||
|
|
||||||
for module in &self.modules {
|
for module in &self.modules {
|
||||||
|
for import in module.imports() {
|
||||||
|
if imports.get_export("env", import.name()).is_none() {
|
||||||
|
println!(
|
||||||
|
"\x1b[91mMissing import: \"{}\" with type: {:?} \x1b[0m",
|
||||||
|
import.name(),
|
||||||
|
import.ty()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let instance = Instance::new(module, &imports).unwrap();
|
let instance = Instance::new(module, &imports).unwrap();
|
||||||
let exports = &instance.exports;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut exported_functions = self.environment_data.exported_functions.write();
|
let mut exported_functions = self.environment_data.exported_functions.write();
|
||||||
for export in exports.iter() {
|
for export in exports.iter() {
|
||||||
match export.1 {
|
match export.1 {
|
||||||
|
@ -102,15 +123,22 @@ impl WebAssemblyScriptResolver {
|
||||||
if let Some(m) = &self.environment_data.memory.read().as_ref() {
|
if let Some(m) = &self.environment_data.memory.read().as_ref() {
|
||||||
m.grow(32).unwrap();
|
m.grow(32).unwrap();
|
||||||
}
|
}
|
||||||
if let Some(f) = exported_functions.get(&"load_script".into()) {
|
if let Some(f) = exported_functions.get::<StringKey>(&"load_script".into()) {
|
||||||
self.load_script_fn = Some(f.native().unwrap())
|
self.load_script_fn = Some(f.native().unwrap())
|
||||||
}
|
}
|
||||||
if let Some(f) = exported_functions.get(&"allocate_mem".into()) {
|
if let Some(f) = exported_functions.get::<StringKey>(&"allocate_mem".into()) {
|
||||||
let _ = self
|
let _ = self
|
||||||
.environment_data
|
.environment_data
|
||||||
.allocate_mem_fn
|
.allocate_mem_fn
|
||||||
.write()
|
.write()
|
||||||
.insert(f.native().unwrap());
|
.insert(f.native().unwrap());
|
||||||
|
|
||||||
|
let temp_memory_slab = self.environment_data.allocate_mem(128, 1);
|
||||||
|
let _ = self
|
||||||
|
.environment_data
|
||||||
|
.temp_allocator
|
||||||
|
.write()
|
||||||
|
.insert(TempWasmAllocator::new(temp_memory_slab.0, temp_memory_slab.1));
|
||||||
}
|
}
|
||||||
self.instances.push(instance);
|
self.instances.push(instance);
|
||||||
}
|
}
|
||||||
|
@ -151,7 +179,7 @@ impl ScriptResolver for WebAssemblyScriptResolver {
|
||||||
.environment_data
|
.environment_data
|
||||||
.exported_functions
|
.exported_functions
|
||||||
.read()
|
.read()
|
||||||
.get(&"get_script_capabilities".into())
|
.get::<StringKey>(&"get_script_capabilities".into())
|
||||||
{
|
{
|
||||||
let res = get_cap.call(&[Value::I32(script as i32)]).unwrap();
|
let res = get_cap.call(&[Value::I32(script as i32)]).unwrap();
|
||||||
let ptr = (self.environment_data.memory.read().as_ref().unwrap().data_ptr()
|
let ptr = (self.environment_data.memory.read().as_ref().unwrap().data_ptr()
|
||||||
|
@ -163,7 +191,9 @@ impl ScriptResolver for WebAssemblyScriptResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.script_capabilities.write().insert(key.clone(), capabilities);
|
self.script_capabilities
|
||||||
|
.write()
|
||||||
|
.insert(key.clone(), Arc::new(capabilities));
|
||||||
}
|
}
|
||||||
|
|
||||||
let read_guard = self.script_capabilities.read();
|
let read_guard = self.script_capabilities.read();
|
||||||
|
@ -172,8 +202,7 @@ impl ScriptResolver for WebAssemblyScriptResolver {
|
||||||
Ok(Some(Arc::new(WebAssemblyScript::new(
|
Ok(Some(Arc::new(WebAssemblyScript::new(
|
||||||
owner as *mut u8,
|
owner as *mut u8,
|
||||||
script,
|
script,
|
||||||
capabilities as *const HashSet<WebAssemblyScriptCapabilities>
|
capabilities.clone(),
|
||||||
as *mut HashSet<WebAssemblyScriptCapabilities>,
|
|
||||||
Arc::downgrade(&self.environment_data),
|
Arc::downgrade(&self.environment_data),
|
||||||
script_key.clone(),
|
script_key.clone(),
|
||||||
))))
|
))))
|
||||||
|
@ -207,19 +236,28 @@ pub struct WebAssemblyEnvironmentData {
|
||||||
/// allow for modifying memory we might not want to. If we get a type mismatch, we will panic, preventing this.
|
/// allow for modifying memory we might not want to. If we get a type mismatch, we will panic, preventing this.
|
||||||
extern_ref_type_lookup: RwLock<HashSet<ExternRefLookupKey>>,
|
extern_ref_type_lookup: RwLock<HashSet<ExternRefLookupKey>>,
|
||||||
|
|
||||||
|
/// Additional security for data slices passed to WASM.
|
||||||
|
extern_vec_ref_lookup: RwLock<HashMap<u32, Vec<u32>>>,
|
||||||
|
|
||||||
/// The memory inside of the WASM container.
|
/// The memory inside of the WASM container.
|
||||||
memory: RwLock<Option<Memory>>,
|
memory: RwLock<Option<Memory>>,
|
||||||
|
|
||||||
/// 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>>,
|
||||||
|
|
||||||
|
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.
|
||||||
allocate_mem_fn: RwLock<Option<NativeFunc<(u32, u32), u32>>>,
|
allocate_mem_fn: RwLock<Option<NativeFunc<(u32, u32), u32>>>,
|
||||||
|
|
||||||
|
/// An allocator for quick short lifetime allocations within WASM.
|
||||||
|
temp_allocator: RwLock<Option<TempWasmAllocator>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||||
struct ExternRefLookupKey {
|
struct ExternRefLookupKey {
|
||||||
pub ptr: *const u8,
|
pub ptr: *const u8,
|
||||||
|
pub is_vec: bool,
|
||||||
pub t: u64,
|
pub t: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,6 +272,11 @@ impl WebAssemblyEnvironmentData {
|
||||||
self.exported_functions.read()
|
self.exported_functions.read()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn script_function_cache(&self) -> &ScriptFunctionCache {
|
||||||
|
&self.script_function_cache
|
||||||
|
}
|
||||||
|
|
||||||
/// Allocates memory inside the WASM container with a given size and alignment. This memory is
|
/// Allocates memory inside the WASM container with a given size and alignment. This memory is
|
||||||
/// owned by WASM, and is how we can pass memory references that the host allocated to WASM.
|
/// owned by WASM, and is how we can pass memory references that the host allocated to WASM.
|
||||||
/// The return is a tuple containing both the actual pointer to the memory (usable by the host),
|
/// The return is a tuple containing both the actual pointer to the memory (usable by the host),
|
||||||
|
@ -253,19 +296,79 @@ impl WebAssemblyEnvironmentData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allocates memory inside the WASM container with a given size and alignment. This memory is
|
||||||
|
/// owned by WASM, and is how we can pass memory references that the host allocated to WASM.
|
||||||
|
/// The return is a tuple containing both the actual pointer to the memory (usable by the host),
|
||||||
|
/// and the WASM offset to the memory (usable by the client).
|
||||||
|
pub fn allocate_mem_typed<T>(&self) -> (*mut u8, u32) {
|
||||||
|
let wasm_ptr = self
|
||||||
|
.allocate_mem_fn
|
||||||
|
.read()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.call(size_of::<T>() as u32, align_of::<T>() as u32)
|
||||||
|
.unwrap();
|
||||||
|
unsafe {
|
||||||
|
(
|
||||||
|
self.memory
|
||||||
|
.read()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.data_ptr()
|
||||||
|
.offset(wasm_ptr as isize),
|
||||||
|
wasm_ptr,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<T>(&self) -> AllocatedObject<T> {
|
||||||
|
self.temp_allocator.read().as_ref().unwrap().alloc::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
/// is not supported by our current WASM platform (Rust). Instead, this is simply a way to not
|
/// is not supported by our current WASM platform (Rust). Instead, this is simply a way to not
|
||||||
/// have to send arbitrary pointer values back and forth with WASM. Only values WASM can actually
|
/// have to send arbitrary pointer values back and forth with WASM. Only values WASM can actually
|
||||||
/// access can be touched through this, and we ensure the value is the correct type. In the future,
|
/// 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
|
/// when extern refs get actually properly implemented at compile time we might want to get rid
|
||||||
/// of this code.
|
/// of this code.
|
||||||
pub fn get_extern_ref_index<T: UniqueTypeId<u64>>(&self, value: &T) -> u32 {
|
pub fn get_extern_ref_index<T: UniqueTypeId<u64> + ?Sized>(&self, value: &T) -> u32 {
|
||||||
let ptr = value as *const T as *const u8;
|
self.get_extern_ref_from_ptr(value as *const T as *const u8, T::id().0, false)
|
||||||
if let Some(v) = self
|
}
|
||||||
.extern_ref_pointers_lookup
|
|
||||||
.read()
|
/// Get a numeric value from any given value. This is not a true Extern Ref from WASM, as this
|
||||||
.get(&ExternRefLookupKey { ptr, t: T::id().0 })
|
/// is not supported by our current WASM platform (Rust). Instead, this is simply a way to not
|
||||||
{
|
/// have to send arbitrary pointer values back and forth with WASM. Only values WASM can actually
|
||||||
|
/// 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.
|
||||||
|
pub fn get_extern_vec_ref_index<T: UniqueTypeId<u64>>(&self, value: &[T]) -> u32 {
|
||||||
|
let mut vec = Vec::with_capacity(value.len());
|
||||||
|
for v in value {
|
||||||
|
vec.push(self.get_extern_ref_index(v));
|
||||||
|
}
|
||||||
|
let p = self.get_extern_ref_from_ptr(value as *const [T] as *const u8, T::id().0, true);
|
||||||
|
self.extern_vec_ref_lookup.write().insert(p, vec);
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an extern ref belonging to a vector we have passed to WASM.
|
||||||
|
pub fn get_extern_vec_ref_extern_ref(&self, extern_vec_ref: u32, index: u32) -> u32 {
|
||||||
|
let r = self.extern_vec_ref_lookup.read();
|
||||||
|
let v = r.get(&extern_vec_ref).unwrap();
|
||||||
|
v[index as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the extern ref index belonging to a specific pointer. If none exists, this will create
|
||||||
|
/// a new one.
|
||||||
|
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,
|
||||||
|
is_vec,
|
||||||
|
t: type_id,
|
||||||
|
}) {
|
||||||
return *v as u32;
|
return *v as u32;
|
||||||
}
|
}
|
||||||
let index = {
|
let index = {
|
||||||
|
@ -273,12 +376,19 @@ impl WebAssemblyEnvironmentData {
|
||||||
extern_ref_guard.push(ptr);
|
extern_ref_guard.push(ptr);
|
||||||
extern_ref_guard.len() as u32
|
extern_ref_guard.len() as u32
|
||||||
};
|
};
|
||||||
self.extern_ref_pointers_lookup
|
self.extern_ref_pointers_lookup.write().insert(
|
||||||
.write()
|
ExternRefLookupKey {
|
||||||
.insert(ExternRefLookupKey { ptr, t: T::id().0 }, index);
|
ptr,
|
||||||
self.extern_ref_type_lookup
|
is_vec,
|
||||||
.write()
|
t: type_id,
|
||||||
.insert(ExternRefLookupKey { ptr, t: T::id().0 });
|
},
|
||||||
|
index,
|
||||||
|
);
|
||||||
|
self.extern_ref_type_lookup.write().insert(ExternRefLookupKey {
|
||||||
|
ptr,
|
||||||
|
is_vec,
|
||||||
|
t: type_id,
|
||||||
|
});
|
||||||
index
|
index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,6 +402,7 @@ impl WebAssemblyEnvironmentData {
|
||||||
.read()
|
.read()
|
||||||
.get(&ExternRefLookupKey {
|
.get(&ExternRefLookupKey {
|
||||||
ptr: *ptr,
|
ptr: *ptr,
|
||||||
|
is_vec: false,
|
||||||
t: T::id().0,
|
t: T::id().0,
|
||||||
})
|
})
|
||||||
.is_none()
|
.is_none()
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
use std::mem::size_of;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
pub(super) struct TempWasmAllocator {
|
||||||
|
data: *mut u8,
|
||||||
|
wasm_pointer: u32,
|
||||||
|
currently_allocated: AtomicUsize,
|
||||||
|
offset_high: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TempWasmAllocator {
|
||||||
|
pub(super) fn new(data: *mut u8, wasm_pointer: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
data,
|
||||||
|
wasm_pointer,
|
||||||
|
currently_allocated: AtomicUsize::new(0),
|
||||||
|
offset_high: AtomicUsize::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc<T>(&self) -> AllocatedObject<T> {
|
||||||
|
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 = unsafe { self.data.add(ptr_offset) } as *mut T;
|
||||||
|
AllocatedObject::<T> {
|
||||||
|
ptr,
|
||||||
|
wasm_pointer: self.wasm_pointer + ptr_offset as u32,
|
||||||
|
allocator: self as *const TempWasmAllocator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drop<T>(&self) {
|
||||||
|
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.
|
||||||
|
if self.currently_allocated.load(Ordering::SeqCst) == 0 {
|
||||||
|
self.offset_high.store(0, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AllocatedObject<T> {
|
||||||
|
pub ptr: *mut T,
|
||||||
|
pub wasm_pointer: u32,
|
||||||
|
allocator: *const TempWasmAllocator,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for AllocatedObject<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
self.allocator.as_ref().unwrap().drop::<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ use crate::StringKey;
|
||||||
|
|
||||||
/// A library to store all items.
|
/// A library to store all items.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||||
pub struct ItemLibrary {
|
pub struct ItemLibrary {
|
||||||
/// The underlying data structure.
|
/// The underlying data structure.
|
||||||
map: IndexMap<StringKey, Box<Item>>,
|
map: IndexMap<StringKey, Box<Item>>,
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::defines::LevelInt;
|
||||||
|
|
||||||
/// This library holds several misc settings for the library.
|
/// This library holds several misc settings for the library.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||||
pub struct LibrarySettings {
|
pub struct LibrarySettings {
|
||||||
/// The highest level a Pokemon can be.
|
/// The highest level a Pokemon can be.
|
||||||
maximum_level: LevelInt,
|
maximum_level: LevelInt,
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::StringKey;
|
||||||
|
|
||||||
/// A library to store all data for Pokemon species.
|
/// A library to store all data for Pokemon species.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||||
pub struct SpeciesLibrary {
|
pub struct SpeciesLibrary {
|
||||||
/// The underlying map.
|
/// The underlying map.
|
||||||
map: IndexMap<StringKey, Box<Species>>,
|
map: IndexMap<StringKey, Box<Species>>,
|
||||||
|
|
|
@ -17,8 +17,15 @@ impl From<u8> for TypeIdentifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<u8> for TypeIdentifier {
|
||||||
|
fn into(self) -> u8 {
|
||||||
|
self.val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// All data related to types and effectiveness.
|
/// All data related to types and effectiveness.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||||
pub struct TypeLibrary {
|
pub struct TypeLibrary {
|
||||||
/// A list of types
|
/// A list of types
|
||||||
types: HashMap<StringKey, TypeIdentifier>,
|
types: HashMap<StringKey, TypeIdentifier>,
|
||||||
|
|
|
@ -153,6 +153,11 @@ impl MoveData {
|
||||||
|
|
||||||
/// Arbitrary flags that can be applied to the move.
|
/// Arbitrary flags that can be applied to the move.
|
||||||
pub fn has_flag(&self, key: &StringKey) -> bool {
|
pub fn has_flag(&self, key: &StringKey) -> bool {
|
||||||
self.flags.contains(key)
|
self.flags.contains::<StringKey>(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Arbitrary flags that can be applied to the move.
|
||||||
|
pub fn has_flag_by_hash(&self, key_hash: u32) -> bool {
|
||||||
|
self.flags.contains::<u32>(&key_hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::StringKey;
|
||||||
/// A parameter for an effect. This is basically a simple way to dynamically store multiple different
|
/// A parameter for an effect. This is basically a simple way to dynamically store multiple different
|
||||||
/// primitives on data.
|
/// primitives on data.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||||
pub enum EffectParameter {
|
pub enum EffectParameter {
|
||||||
/// A boolean value.
|
/// A boolean value.
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
@ -11,7 +12,7 @@ pub enum EffectParameter {
|
||||||
/// A float value. Stored as a 32 bit float.
|
/// A float value. Stored as a 32 bit float.
|
||||||
Float(f32),
|
Float(f32),
|
||||||
/// A string value.
|
/// A string value.
|
||||||
String(String),
|
String(StringKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A secondary effect is an effect on a move that happens after it hits.
|
/// A secondary effect is an effect on a move that happens after it hits.
|
||||||
|
|
|
@ -39,6 +39,7 @@ impl Ability {
|
||||||
/// An ability index allows us to find an ability on a form. It combines a bool for whether the
|
/// An ability index allows us to find an ability on a form. It combines a bool for whether the
|
||||||
/// ability is hidden or not, and then an index of the ability.
|
/// ability is hidden or not, and then an index of the ability.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct AbilityIndex {
|
pub struct AbilityIndex {
|
||||||
/// Whether or not the ability we're referring to is a hidden ability.
|
/// Whether or not the ability we're referring to is a hidden ability.
|
||||||
pub hidden: bool,
|
pub hidden: bool,
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::StringKey;
|
||||||
|
|
||||||
/// The data belonging to a Pokemon with certain characteristics.
|
/// The data belonging to a Pokemon with certain characteristics.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||||
pub struct Species {
|
pub struct Species {
|
||||||
/// The national dex identifier of the Pokemon.
|
/// The national dex identifier of the Pokemon.
|
||||||
id: u16,
|
id: u16,
|
||||||
|
|
|
@ -11,6 +11,7 @@ use super::statistics::Statistic;
|
||||||
///
|
///
|
||||||
/// As all data in this type is atomic, threaded access to this struct is completely legal.
|
/// As all data in this type is atomic, threaded access to this struct is completely legal.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||||
pub struct StatisticSet<T>
|
pub struct StatisticSet<T>
|
||||||
where
|
where
|
||||||
T: PrimitiveAtom,
|
T: PrimitiveAtom,
|
||||||
|
@ -206,6 +207,7 @@ where
|
||||||
/// A clamped statistic set holds the 6 normal stats for a Pokemon, but ensures it always remains
|
/// A clamped statistic set holds the 6 normal stats for a Pokemon, but ensures it always remains
|
||||||
/// between two values (inclusive on the two values).
|
/// between two values (inclusive on the two values).
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||||
pub struct ClampedStatisticSet<T, const MIN: i64, const MAX: i64>
|
pub struct ClampedStatisticSet<T, const MIN: i64, const MAX: i64>
|
||||||
where
|
where
|
||||||
T: PrimitiveAtom,
|
T: PrimitiveAtom,
|
||||||
|
|
|
@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
|
||||||
/// Stats are numerical values on Pokemon that are used in battle.
|
/// Stats are numerical values on Pokemon that are used in battle.
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum Statistic {
|
pub enum Statistic {
|
||||||
/// Health Points determine how much damage a Pokemon can receive before fainting.
|
/// Health Points determine how much damage a Pokemon can receive before fainting.
|
||||||
HP,
|
HP,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::borrow::Borrow;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -5,7 +6,6 @@ use std::sync::{Arc, Mutex, Weak};
|
||||||
|
|
||||||
use conquer_once::OnceCell;
|
use conquer_once::OnceCell;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use indexmap::Equivalent;
|
|
||||||
|
|
||||||
/// StringKey is an immutable string that is used for indexing of hashmaps or equality a lot.
|
/// StringKey is an immutable string that is used for indexing of hashmaps or equality a lot.
|
||||||
/// By reference counting the string instead of copying, and caching the hash, we can get some
|
/// By reference counting the string instead of copying, and caching the hash, we can get some
|
||||||
|
@ -103,9 +103,9 @@ impl From<&str> for StringKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Equivalent<StringKey> for u32 {
|
impl Borrow<u32> for StringKey {
|
||||||
fn equivalent(&self, key: &StringKey) -> bool {
|
fn borrow(&self) -> &u32 {
|
||||||
*self == key.hash
|
&self.hash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -276,7 +276,7 @@ pub fn load_species(path: &String, library: &mut StaticData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_wasm(path: &String, library: &mut WebAssemblyScriptResolver) {
|
fn load_wasm(path: &String, library: &mut WebAssemblyScriptResolver) {
|
||||||
let file = File::open(path.to_string() + "gen7_scripts_rs.wasm").unwrap();
|
let file = File::open(path.to_string() + "gen7_scripts.wasm").unwrap();
|
||||||
let mut reader = BufReader::new(file);
|
let mut reader = BufReader::new(file);
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
reader.read_to_end(&mut buffer).unwrap();
|
reader.read_to_end(&mut buffer).unwrap();
|
||||||
|
@ -392,7 +392,7 @@ fn parse_effect_parameter(value: &Value) -> EffectParameter {
|
||||||
EffectParameter::Int(n.as_i64().unwrap())
|
EffectParameter::Int(n.as_i64().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::String(s) => EffectParameter::String(s.clone()),
|
Value::String(s) => EffectParameter::String(StringKey::new(s.as_str())),
|
||||||
Value::Array(_) => {
|
Value::Array(_) => {
|
||||||
panic!("Unexpected type")
|
panic!("Unexpected type")
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -5,10 +5,12 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use conquer_once::OnceCell;
|
use conquer_once::OnceCell;
|
||||||
|
|
||||||
use pkmn_lib::dynamic_data::{DynamicLibrary, ScriptCategory};
|
use pkmn_lib::dynamic_data::{DynamicLibrary, MoveChoice, PokemonBuilder, ScriptCategory, TurnChoice};
|
||||||
|
use pkmn_lib::static_data::EffectParameter;
|
||||||
|
|
||||||
use crate::common::{library_loader, TestCase};
|
use crate::common::{library_loader, TestCase};
|
||||||
|
|
||||||
|
@ -51,10 +53,47 @@ fn integration_tests(input: &Path) {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore)]
|
#[cfg_attr(miri, ignore)]
|
||||||
fn validate_script() {
|
fn validate_script() {
|
||||||
let lib = library_loader::load_library();
|
let lib = get_library();
|
||||||
let script = lib
|
let script = lib
|
||||||
.load_script(0 as *const u8, ScriptCategory::Move, &"test".into())
|
.load_script(0 as *const u8, ScriptCategory::Move, &"test".into())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
script.on_initialize(&lib, &[]);
|
let parameters = [EffectParameter::String("foo".into())];
|
||||||
|
script.on_initialize(&lib, ¶meters);
|
||||||
|
script.on_initialize(&lib, ¶meters);
|
||||||
|
script.on_initialize(&lib, ¶meters);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(miri, ignore)]
|
||||||
|
fn validate_script_2() {
|
||||||
|
let lib = get_library();
|
||||||
|
let script = lib
|
||||||
|
.load_script(0 as *const u8, ScriptCategory::Move, &"test".into())
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
let user = Arc::new(
|
||||||
|
PokemonBuilder::new(&lib, "charizard".into(), 100)
|
||||||
|
.learn_move("fire_blast".into())
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
script.on_before_turn(&TurnChoice::Move(MoveChoice::new(
|
||||||
|
user.clone(),
|
||||||
|
user.learned_moves().read().get(0).unwrap().as_ref().unwrap().clone(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)));
|
||||||
|
assert_eq!(user.current_health(), user.max_health() - 50);
|
||||||
|
|
||||||
|
let mut speed: u32 = 100;
|
||||||
|
script.change_speed(
|
||||||
|
&TurnChoice::Move(MoveChoice::new(
|
||||||
|
user.clone(),
|
||||||
|
user.learned_moves().read().get(0).unwrap().as_ref().unwrap().clone(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)),
|
||||||
|
&mut speed,
|
||||||
|
);
|
||||||
|
assert_eq!(speed, 684);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue