use std::path::Path;
use rustc_hash::FxHashMap;
use wasmer::{Imports, Instance, Module, Store};
use crate::{buffer::Buffer, file::FileManager, input::InputManager};
pub struct ScriptManager<'a> {
scripts: &'a [ScriptDeclaration],
pub active_scripts: Vec<usize>,
script_states: FxHashMap<usize, Box<[u8]>>,
}
pub struct ScriptContext<'a> {
pub buffer: &'a mut Buffer,
pub input: &'a InputManager,
}
type StaticScriptFunction = Option<fn(&mut ScriptContext, &mut Box<[u8]>)>;
pub enum ScriptDeclaration {
Static {
start: StaticScriptFunction,
update: StaticScriptFunction,
end: StaticScriptFunction,
},
Wasm {
path: Box<Path>,
},
}
pub struct WasmScript {
pub module: Module,
pub instance: Instance,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Profile {
Debug,
Release,
}
impl<'a> ScriptContext<'a> {
pub fn exit(&mut self) -> ! {
let _ = self.buffer.finish();
std::process::exit(0);
}
}
fn get_script_state(states: &mut FxHashMap<usize, Box<[u8]>>, script_id: usize) -> &mut Box<[u8]> {
states.entry(script_id).or_insert(Box::from([]))
}
impl<'a> ScriptManager<'a> {
pub fn new(scripts: &'a [ScriptDeclaration]) -> Self {
Self {
scripts,
active_scripts: Vec::new(),
script_states: FxHashMap::default(),
}
}
pub fn activate_script(
&mut self,
script_id: usize,
context: &mut ScriptContext<'_>,
files: &mut FileManager,
) {
let Some(script_declaration) = self.scripts.get(script_id) else {
unreachable!("The active script does not exist");
};
match script_declaration {
ScriptDeclaration::Static { start, .. } => {
start.map(|s| {
s(
context,
get_script_state(&mut self.script_states, script_id),
)
});
}
ScriptDeclaration::Wasm { path } => {
let module = files.load_file(path.clone()).unwrap().unwrap_wasm_module();
let mut store = wasmi::Store::new(&files.wasm_compiler, context);
let instance = self
.linker
.instantiate_and_start(&mut store, &module)
.unwrap();
}
}
self.active_scripts.push(script_id);
}
pub fn deactivate_script(&mut self, script_id: usize, context: &mut ScriptContext<'_>) {
self.active_scripts.retain(|element| {
if *element != script_id {
let Some(script) = self.scripts.get(script_id) else {
unreachable!("The active script does not exist");
};
match script {
ScriptDeclaration::Static { end, .. } => {
end.map(|s| {
s(
context,
get_script_state(&mut self.script_states, script_id),
);
});
}
ScriptDeclaration::Wasm { .. } => todo!("wasm scripts"),
};
true
} else {
false
}
});
}
pub fn run_scripts(&mut self, context: &mut ScriptContext<'_>) {
for script_id in &self.active_scripts {
let Some(script) = self.scripts.get(*script_id) else {
unreachable!("The active script does not exist");
};
match *script {
ScriptDeclaration::Static { update, .. } => {
if let Some(s) = update {
s(
context,
get_script_state(&mut self.script_states, *script_id),
)
}
}
ScriptDeclaration::Wasm { .. } => todo!("wasm scripts"),
}
}
}
pub fn reset_script(&mut self, script_id: usize, context: &mut ScriptContext<'_>) {
let script = &self.scripts[script_id];
match script {
ScriptDeclaration::Static { start, end, .. } => {
if let Some(s) = start {
s(
context,
get_script_state(&mut self.script_states, script_id),
)
}
if let Some(s) = end {
s(
context,
get_script_state(&mut self.script_states, script_id),
)
}
}
ScriptDeclaration::Wasm { .. } => todo!("wasm scripts"),
}
}
}
impl WasmScript {
fn call_function(&self, name: &str, profile: Profile, store: &mut Store, imports: &Imports) {
let instance = match profile {
Profile::Debug => &Instance::new(store, &self.module, imports).unwrap(),
Profile::Release => &self.instance,
};
let function = instance.exports.get_function(name).unwrap();
function.call(store, &[]).unwrap();
}
}
|