summaryrefslogtreecommitdiff
path: root/src/script.rs
blob: dd651067802f39c7028266485a773b40f57999c9 (plain)
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();
	}
}