use std::any::Any; use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize}; use std::sync::{Arc, Weak}; use hashbrown::HashSet; use wasmer::NativeFunc; use crate::dynamic_data::{DynamicLibrary, Script, TurnChoice}; use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData; use crate::script_implementations::wasm::WebAssemblyScriptCapabilities; use crate::static_data::EffectParameter; use crate::StringKey; /// A WebAssemblyScript is there to implement the Script trait within WebAssemblyScript. pub struct WebAssemblyScript { /// The unique identifier of the script. name: StringKey, /// Returns an atomic bool for internal marking of deletion. This is currently only specifically /// used for deletion of a script while we are holding a reference to it (i.e. executing a script /// hook on it). marked_for_deletion: AtomicBool, /// A script can be suppressed by other scripts. If a script is suppressed by at least one script /// we will not execute its methods. This holds the number of suppressions on the script. suppressed_count: AtomicUsize, /// The owner of this script (where the script is attached to) _owner_ptr: AtomicPtr, /// Pointer inside WebAssembly memory where the data is for this script. self_ptr: u32, /// Capabilities define which functions we actually implement. capabilities: Arc>, /// The global runtime environment data. environment: Weak, } impl WebAssemblyScript { /// Instantiates a new WebAssemblyScript. pub fn new( owner_ptr: *mut u8, self_ptr: u32, capabilities: Arc>, environment: Weak, name: StringKey, ) -> Self { Self { name, marked_for_deletion: Default::default(), suppressed_count: Default::default(), _owner_ptr: AtomicPtr::new(owner_ptr), self_ptr, capabilities, environment, } } } impl Script for WebAssemblyScript { fn name(&self) -> &StringKey { &self.name } fn get_marked_for_deletion(&self) -> &AtomicBool { &self.marked_for_deletion } fn get_suppressed_count(&self) -> &AtomicUsize { &self.suppressed_count } 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)).unwrap(); } } fn change_speed(&self, choice: &TurnChoice, speed: &mut u32) { if !self.capabilities.contains(&WebAssemblyScriptCapabilities::ChangeSpeed) { return; } let env = self.environment.upgrade().unwrap(); let exported = env.exported_functions(); if let Some(f) = exported.get::(&"script_change_speed".into()) { let func: NativeFunc<(u32, ExternRef, u32), ()> = f.native().unwrap(); let ptr = env.temp_allocate_mem_typed::(); func.call(self.self_ptr, ExternRef::new(env.as_ref(), choice), ptr.wasm_pointer) .unwrap(); unsafe { *speed = *ptr.ptr; } } } fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } }