summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2026-07-07 22:45:22 -0400
committerMica White <botahamec@outlook.com>2026-07-07 22:45:22 -0400
commita9977e7fabeb06a6f21824cc4a41b36ea39f6799 (patch)
tree86b586fa8b8a312b2e4d2e5800260fc98430b91b /src
parent07018cb37b278dbd73c864a662454f1cf9d33457 (diff)
Full host environment
Diffstat (limited to 'src')
-rw-r--r--src/file.rs24
-rw-r--r--src/main.rs2
-rw-r--r--src/scene.rs4
-rw-r--r--src/script.rs249
-rw-r--r--src/script/wasm.rs307
5 files changed, 499 insertions, 87 deletions
diff --git a/src/file.rs b/src/file.rs
index 20fdd68..9b9888c 100644
--- a/src/file.rs
+++ b/src/file.rs
@@ -2,7 +2,7 @@ use std::{ffi::OsStr, path::Path};
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
-use smol::{Task, block_on, unblock};
+use smol::{Task, unblock};
use crate::{
scene::{ResourceRequirements, Scene, scene_id_by_name},
@@ -63,7 +63,10 @@ fn normalize_game_manifest(manifest: TomlGameManifest) -> std::io::Result<GameMa
.into_iter()
.map(|script| match script {
TomlScript::Wasm { name, path } => {
- scripts.push(ScriptDeclaration::Wasm { path });
+ scripts.push(ScriptDeclaration::Wasm {
+ name: name.clone(),
+ path,
+ });
(name, scripts.len() - 1)
}
})
@@ -159,21 +162,8 @@ impl FileManager {
Ok(self.cache.get(&path).unwrap())
}
- pub fn get_loaded_file(&mut self, path: Box<Path>) -> Option<std::io::Result<&FileData>> {
- if let Some(task) = self.active_tasks.get(path.as_ref())
- && task.is_finished()
- {
- let data = match block_on(self.active_tasks.remove(&path).unwrap()) {
- Ok(data) => data,
- Err(e) => return Some(Err(e)),
- };
- self.cache.entry(path.clone()).insert_entry(data);
- Some(Ok(self.cache.get(&path).unwrap()))
- } else if let Some(data) = self.cache.get(&path) {
- Some(Ok(data))
- } else {
- None
- }
+ pub fn get_cached_file(&self, path: &Path) -> Option<&FileData> {
+ self.cache.get(path)
}
pub fn start_loading_file(&mut self, path: impl AsRef<Path>, wasm_compiler: &wasmi::Engine) {
diff --git a/src/main.rs b/src/main.rs
index 2978393..f5c869e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -32,6 +32,7 @@ fn main() {
buffer: &mut buffer,
input: &input,
time: &time,
+ operation_queue: Vec::new(),
};
switch_scenes(
@@ -59,6 +60,7 @@ fn main() {
buffer,
input: &input,
time: &time,
+ operation_queue: Vec::new(),
};
scripts.run_scripts(&mut context, &wasm_runner, &mut files);
input.reset_next_frame();
diff --git a/src/scene.rs b/src/scene.rs
index af6b84e..89a0d99 100644
--- a/src/scene.rs
+++ b/src/scene.rs
@@ -44,8 +44,8 @@ pub fn preload_scene(
pub fn is_scene_loaded(
scenes: &[Scene],
- scripts: &mut ScriptManager,
- files: &mut FileManager,
+ scripts: &ScriptManager,
+ files: &FileManager,
id: usize,
) -> bool {
let scene = &scenes[id];
diff --git a/src/script.rs b/src/script.rs
index 4429b9c..93f6f99 100644
--- a/src/script.rs
+++ b/src/script.rs
@@ -3,9 +3,10 @@ use std::path::Path;
use rustc_hash::FxHashMap;
use crate::{
- buffer::Buffer,
+ buffer::{Buffer, Cell},
file::{FileManager, GameManifest},
input::InputManager,
+ scene::{preload_scene, switch_scenes},
time::TimeContext,
};
@@ -19,7 +20,7 @@ pub struct ScriptManager<'a> {
pub struct WasmRunner {
pub engine: wasmi::Engine,
- pub linker: wasmi::Linker<&'static mut ScriptContext<'static>>,
+ pub linker: wasmi::Linker<FullScriptContext<'static>>,
}
pub struct ScriptContext<'a> {
@@ -27,6 +28,34 @@ pub struct ScriptContext<'a> {
pub buffer: &'a mut Buffer,
pub input: &'a InputManager,
pub time: &'a TimeContext,
+ pub operation_queue: Vec<Operation>,
+}
+
+pub struct FullScriptContext<'a> {
+ pub script_id: usize,
+ pub manifest: &'a GameManifest,
+ pub buffer: &'a Buffer,
+ pub input: &'a InputManager,
+ pub time: &'a TimeContext,
+ pub scripts: &'a ScriptManager<'a>,
+ pub files: &'a FileManager,
+ pub wasm_runner: &'a WasmRunner,
+ pub operation_queue: &'a mut Vec<Operation>,
+}
+
+pub enum Operation {
+ AddCell(Cell),
+ PreloadScene(usize),
+ SwitchScene(usize),
+ PreloadScript(usize),
+ UnloadScript(usize),
+ ActivateScript(usize),
+ DeactivateScript(usize),
+ UpdateState {
+ script_id: usize,
+ new_state: Box<[u8]>,
+ },
+ Exit,
}
type StaticScriptFunction = Option<fn(&mut ScriptContext<'_>, &mut Box<[u8]>)>;
@@ -34,11 +63,13 @@ type StaticScriptFunction = Option<fn(&mut ScriptContext<'_>, &mut Box<[u8]>)>;
#[derive(Debug, Clone)]
pub enum ScriptDeclaration {
Static {
+ name: Box<str>,
start: StaticScriptFunction,
update: StaticScriptFunction,
end: StaticScriptFunction,
},
Wasm {
+ name: Box<str>,
path: Box<Path>,
},
}
@@ -65,8 +96,8 @@ impl WasmRunner {
let engine = wasmi::Engine::default();
let linker = unsafe {
std::mem::transmute::<
- wasmi::Linker<&mut ScriptContext<'_>>,
- wasmi::Linker<&mut ScriptContext<'_>>,
+ wasmi::Linker<FullScriptContext<'_>>,
+ wasmi::Linker<FullScriptContext<'_>>,
>(wasm::linker(&engine).unwrap())
};
Self { engine, linker }
@@ -76,9 +107,14 @@ impl WasmRunner {
wasmi::Module::new(&self.engine, data)
}
+ #[allow(clippy::too_many_arguments)]
pub fn run_function(
&self,
+ script_id: usize,
module: &wasmi::Module,
+ scripts: &ScriptManager,
+ files: &FileManager,
+ wasm_runner: &WasmRunner,
context: &mut ScriptContext<'_>,
function_name: &str,
) -> Result<(), wasmi::Error> {
@@ -87,7 +123,19 @@ impl WasmRunner {
let mut store = {
puffin::profile_scope!("create store");
wasmi::Store::new(&self.engine, unsafe {
- std::mem::transmute::<&mut ScriptContext<'_>, &mut ScriptContext<'_>>(context)
+ std::mem::transmute::<FullScriptContext<'_>, FullScriptContext<'_>>(
+ FullScriptContext {
+ script_id,
+ manifest: context.manifest,
+ buffer: context.buffer,
+ input: context.input,
+ time: context.time,
+ scripts,
+ files,
+ wasm_runner,
+ operation_queue: &mut context.operation_queue,
+ },
+ )
})
};
let instance = {
@@ -116,6 +164,25 @@ impl<'a> ScriptManager<'a> {
}
}
+ pub fn script_id_by_name(&self, needle: &str) -> Option<usize> {
+ for (i, script) in self.scripts.iter().enumerate() {
+ match script {
+ ScriptDeclaration::Static { name, .. } => {
+ if &**name == needle {
+ return Some(i);
+ }
+ }
+ ScriptDeclaration::Wasm { name, .. } => {
+ if &**name == needle {
+ return Some(i);
+ }
+ }
+ }
+ }
+
+ None
+ }
+
pub fn preload_script(
&self,
script_id: usize,
@@ -125,15 +192,17 @@ impl<'a> ScriptManager<'a> {
let script = &self.scripts[script_id];
match script {
ScriptDeclaration::Static { .. } => (),
- ScriptDeclaration::Wasm { path } => files.start_loading_file(path, &wasm_runner.engine),
+ ScriptDeclaration::Wasm { path, .. } => {
+ files.start_loading_file(path, &wasm_runner.engine)
+ }
}
}
- pub fn is_script_loaded(&self, script_id: usize, files: &mut FileManager) -> bool {
+ pub fn is_script_loaded(&self, script_id: usize, files: &FileManager) -> bool {
let script = &self.scripts[script_id];
match script {
ScriptDeclaration::Static { .. } => true,
- ScriptDeclaration::Wasm { path } => files.is_file_loaded(path),
+ ScriptDeclaration::Wasm { path, .. } => files.is_file_loaded(path),
}
}
@@ -141,8 +210,69 @@ impl<'a> ScriptManager<'a> {
let script = &self.scripts[script_id];
match script {
ScriptDeclaration::Static { .. } => (),
- ScriptDeclaration::Wasm { path } => files.unload_file(path),
+ ScriptDeclaration::Wasm { path, .. } => files.unload_file(path),
+ }
+ }
+
+ pub fn handle_queue(
+ &mut self,
+ context: &mut ScriptContext<'_>,
+ wasm_runner: &WasmRunner,
+ files: &mut FileManager,
+ ) {
+ for operation in &context.operation_queue {
+ match operation {
+ Operation::AddCell(cell) => context.buffer.cells.push(*cell),
+ Operation::PreloadScene(id) => {
+ preload_scene(&context.manifest.scenes, self, files, wasm_runner, *id)
+ }
+ Operation::SwitchScene(id) => switch_scenes(
+ self,
+ &mut ScriptContext {
+ manifest: context.manifest,
+ buffer: context.buffer,
+ input: context.input,
+ time: context.time,
+ operation_queue: Vec::new(),
+ },
+ files,
+ wasm_runner,
+ *id,
+ ),
+ Operation::PreloadScript(id) => self.preload_script(*id, files, wasm_runner),
+ Operation::UnloadScript(id) => self.unload_script(*id, files),
+ Operation::ActivateScript(id) => self.activate_script(
+ *id,
+ &mut ScriptContext {
+ manifest: context.manifest,
+ buffer: context.buffer,
+ input: context.input,
+ time: context.time,
+ operation_queue: Vec::new(),
+ },
+ wasm_runner,
+ files,
+ ),
+ Operation::DeactivateScript(id) => self.deactivate_script(
+ *id,
+ &mut ScriptContext {
+ manifest: context.manifest,
+ buffer: context.buffer,
+ input: context.input,
+ time: context.time,
+ operation_queue: Vec::new(),
+ },
+ wasm_runner,
+ files,
+ ),
+ Operation::UpdateState {
+ script_id,
+ new_state,
+ } => *get_script_state(&mut self.script_states, *script_id) = new_state.clone(),
+ Operation::Exit => context.exit(),
+ }
}
+ context.operation_queue.clear();
}
pub fn activate_script(
@@ -164,15 +294,22 @@ impl<'a> ScriptManager<'a> {
)
});
}
- ScriptDeclaration::Wasm { path } => {
- let module = files
- .load_file(path.clone(), &wasm_runner.engine)
- .unwrap()
- .unwrap_wasm_module();
- wasm_runner.run_function(module, context, "start");
+ ScriptDeclaration::Wasm { path, .. } => {
+ files.load_file(path.clone(), &wasm_runner.engine).unwrap();
+ let module = files.get_cached_file(path).unwrap().unwrap_wasm_module();
+ wasm_runner.run_function(
+ script_id,
+ module,
+ self,
+ files,
+ wasm_runner,
+ context,
+ "start",
+ );
}
}
self.active_scripts.push(script_id);
+ self.handle_queue(context, wasm_runner, files);
}
pub fn deactivate_script(
@@ -182,34 +319,41 @@ impl<'a> ScriptManager<'a> {
wasm_runner: &WasmRunner,
files: &mut FileManager,
) {
- 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 { path } => {
- let module = files
- .load_file(path.clone(), &wasm_runner.engine)
- .unwrap()
- .unwrap_wasm_module();
- wasm_runner.run_function(module, context, "end");
- }
- };
-
- true
- } else {
- false
- }
- });
+ for script_id in self
+ .active_scripts
+ .iter()
+ .cloned()
+ .filter(|element| *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 { path, .. } => {
+ files.load_file(path.clone(), &wasm_runner.engine).unwrap();
+ let module = files.get_cached_file(path).unwrap().unwrap_wasm_module();
+ wasm_runner.run_function(
+ script_id,
+ module,
+ self,
+ files,
+ wasm_runner,
+ context,
+ "end",
+ );
+ }
+ };
+ }
+ self.active_scripts.retain(|element| *element != script_id);
+ self.handle_queue(context, wasm_runner, files);
}
pub fn run_scripts(
@@ -232,17 +376,24 @@ impl<'a> ScriptManager<'a> {
)
}
}
- ScriptDeclaration::Wasm { path } => {
+ ScriptDeclaration::Wasm { path, .. } => {
let module = {
puffin::profile_scope!("load module");
- files
- .load_file(path.clone(), &wasm_runner.engine)
- .unwrap()
- .unwrap_wasm_module()
+ files.load_file(path.clone(), &wasm_runner.engine).unwrap();
+ files.get_cached_file(path).unwrap().unwrap_wasm_module()
};
- wasm_runner.run_function(module, context, "update");
+ wasm_runner.run_function(
+ *script_id,
+ module,
+ self,
+ files,
+ wasm_runner,
+ context,
+ "update",
+ );
}
}
}
+ self.handle_queue(context, wasm_runner, files);
}
}
diff --git a/src/script/wasm.rs b/src/script/wasm.rs
index 96190f6..a1b923b 100644
--- a/src/script/wasm.rs
+++ b/src/script/wasm.rs
@@ -1,26 +1,155 @@
+use core::slice;
use std::time::UNIX_EPOCH;
-use crossterm::{event::KeyCode, style::ContentStyle};
+use crossterm::{
+ event::KeyCode,
+ style::{Attribute, Attributes, Color, ContentStyle},
+};
use wasmi::{Caller, Engine, Linker};
-use crate::{buffer::Cell, script::ScriptContext};
+use crate::{buffer::Cell, script::FullScriptContext};
-fn add_cell(mut caller: Caller<&mut ScriptContext>, character: u32, location: u32, z: u32) {
- caller.data_mut().buffer.cells.push(Cell {
- character: char::from_u32(character).unwrap_or_default(),
- style: ContentStyle::new(),
- x: (location >> 16) as u16,
- y: (location & 0xffff) as u16,
- z: z as u8,
- });
+use super::Operation;
+
+unsafe fn str_from_memory<'a>(
+ caller: &'a Caller<FullScriptContext<'_>>,
+ offset: u32,
+ length: u32,
+) -> &'a str {
+ let memory = caller.get_export("memory").unwrap().into_memory().unwrap();
+ unsafe {
+ str::from_utf8_unchecked(slice::from_raw_parts(
+ memory.data_ptr(caller).byte_add(offset as usize),
+ length as usize,
+ ))
+ }
}
-fn window_size(caller: Caller<&mut ScriptContext>) -> u32 {
+#[allow(clippy::too_many_arguments)]
+fn add_cell(
+ mut caller: Caller<FullScriptContext<'_>>,
+ character: u32,
+ location: u32,
+ z: u32,
+ foreground_color: u32,
+ background_color: u32,
+ underline_color: u32,
+ attributes: u32,
+) {
+ const STANDARD_COLOR: u32 = 1 << 24;
+ const ANSI_COLOR: u32 = 2 << 24;
+ const RGB_COLOR: u32 = 3 << 24;
+ const UNSUPPORTED: u32 = 4 << 24;
+
+ fn get_color(color: u32) -> Option<Color> {
+ match color {
+ 0 => None,
+ STANDARD_COLOR..ANSI_COLOR => Some(match color & 0xff {
+ 0 => Color::Reset,
+ 1 => Color::Black,
+ 2 => Color::DarkGrey,
+ 3 => Color::Red,
+ 4 => Color::DarkRed,
+ 5 => Color::Green,
+ 6 => Color::DarkGreen,
+ 7 => Color::Yellow,
+ 8 => Color::DarkYellow,
+ 9 => Color::Blue,
+ 10 => Color::DarkBlue,
+ 11 => Color::Magenta,
+ 12 => Color::DarkMagenta,
+ 13 => Color::Cyan,
+ 14 => Color::DarkCyan,
+ 15 => Color::White,
+ 16 => Color::Grey,
+ _ => Color::Reset,
+ }),
+ ANSI_COLOR..RGB_COLOR => Some(Color::AnsiValue((color & 0xff) as u8)),
+ RGB_COLOR..UNSUPPORTED => Some(Color::Rgb {
+ r: ((color >> 16) & 0xff) as u8,
+ g: ((color >> 8) & 0xff) as u8,
+ b: (color & 0xff) as u8,
+ }),
+ _ => None,
+ }
+ }
+
+ let mut attribute_set = Attributes::none();
+ if attributes & 1 != 0 {
+ attribute_set.set(Attribute::Bold);
+ }
+ if attributes & 2 != 0 {
+ attribute_set.set(Attribute::Dim);
+ }
+ if attributes & 4 != 0 {
+ attribute_set.set(Attribute::Italic);
+ }
+ if attributes & 8 != 0 {
+ attribute_set.set(Attribute::Underlined);
+ }
+ if attributes & 16 != 0 {
+ attribute_set.set(Attribute::DoubleUnderlined);
+ }
+ if attributes & 32 != 0 {
+ attribute_set.set(Attribute::Undercurled);
+ }
+ if attributes & 64 != 0 {
+ attribute_set.set(Attribute::Underdotted);
+ }
+ if attributes & 128 != 0 {
+ attribute_set.set(Attribute::Underdashed);
+ }
+ if attributes & 256 != 0 {
+ attribute_set.set(Attribute::SlowBlink);
+ }
+ if attributes & 512 != 0 {
+ attribute_set.set(Attribute::RapidBlink);
+ }
+ if attributes & 1024 != 0 {
+ attribute_set.set(Attribute::Reverse);
+ }
+ if attributes & 2048 != 0 {
+ attribute_set.set(Attribute::Hidden);
+ }
+ if attributes & 2048 != 0 {
+ attribute_set.set(Attribute::CrossedOut);
+ }
+ if attributes & 4096 != 0 {
+ attribute_set.set(Attribute::Fraktur);
+ }
+ if attributes & 8192 != 0 {
+ attribute_set.set(Attribute::Framed);
+ }
+ if attributes & 16384 != 0 {
+ attribute_set.set(Attribute::Encircled);
+ }
+ if attributes & 32768 != 0 {
+ attribute_set.set(Attribute::OverLined);
+ }
+
+ caller
+ .data_mut()
+ .operation_queue
+ .push(Operation::AddCell(Cell {
+ character: char::from_u32(character).unwrap_or_default(),
+ style: ContentStyle {
+ foreground_color: get_color(foreground_color),
+ background_color: get_color(background_color),
+ underline_color: get_color(underline_color),
+ attributes: attribute_set,
+ },
+ x: (location >> 16) as u16,
+ y: (location & 0xffff) as u16,
+ z: z as u8,
+ }));
+}
+
+fn window_size(caller: Caller<FullScriptContext<'_>>) -> u32 {
let window_size = &caller.data().buffer.window_size;
((window_size.rows as u32) << 16) | (window_size.columns as u32)
}
-fn is_key_pressed_this_frame(caller: Caller<&mut ScriptContext>, key_code: u32) -> u32 {
+fn is_key_pressed_this_frame(caller: Caller<FullScriptContext<'_>>, key_code: u32) -> u32 {
const BACKSPACE: u32 = 1 << 24;
const ENTER: u32 = 2 << 24;
const LEFT: u32 = 3 << 24;
@@ -67,16 +196,142 @@ fn is_key_pressed_this_frame(caller: Caller<&mut ScriptContext>, key_code: u32)
}
}
-fn exit(mut caller: Caller<&mut ScriptContext>) {
- let _ = caller.data_mut().buffer.finish();
- std::process::exit(0);
+fn log(caller: Caller<FullScriptContext<'_>>, severity: u32, offset: u32, length: u32) {
+ let memory = caller.get_export("memory").unwrap().into_memory().unwrap();
+ let message = unsafe {
+ str::from_utf8_unchecked(slice::from_raw_parts(
+ memory.data_ptr(&caller).byte_add(offset as usize),
+ length as usize,
+ ))
+ };
+ let severity = match severity {
+ 1 => log::Level::Error,
+ 2 => log::Level::Warn,
+ 3 => log::Level::Info,
+ 4 => log::Level::Debug,
+ 5 => log::Level::Trace,
+ _ => log::Level::Error,
+ };
+ log::log!(severity, "{message}");
+}
+
+fn exit(mut caller: Caller<FullScriptContext<'_>>) {
+ caller.data_mut().operation_queue.push(Operation::Exit)
}
fn random_u64() -> u64 {
getrandom::u64().unwrap_or(4)
}
-fn system_now(caller: Caller<&mut ScriptContext>) -> u64 {
+fn scene_id_by_name(caller: Caller<FullScriptContext<'_>>, offset: u32, length: u32) -> u32 {
+ let name = unsafe { str_from_memory(&caller, offset, length) };
+ crate::scene::scene_id_by_name(&caller.data().manifest.scenes, name)
+ .map(|u| u as u32)
+ .unwrap_or(u32::MAX)
+}
+
+fn preload_scene(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
+ caller
+ .data_mut()
+ .operation_queue
+ .push(Operation::PreloadScene(id as usize))
+}
+
+fn is_scene_loaded(caller: Caller<FullScriptContext<'_>>, id: u32) -> u32 {
+ let context = caller.data();
+ crate::scene::is_scene_loaded(
+ &context.manifest.scenes,
+ context.scripts,
+ context.files,
+ id as usize,
+ ) as u32
+}
+
+fn switch_scene(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
+ caller
+ .data_mut()
+ .operation_queue
+ .push(Operation::SwitchScene(id as usize));
+}
+
+fn script_id_by_name(caller: Caller<FullScriptContext<'_>>, offset: u32, length: u32) -> u32 {
+ let context = caller.data();
+ let name = unsafe { str_from_memory(&caller, offset, length) };
+ context
+ .scripts
+ .script_id_by_name(name)
+ .map(|u| u as u32)
+ .unwrap_or(u32::MAX)
+}
+
+fn preload_script(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
+ caller
+ .data_mut()
+ .operation_queue
+ .push(Operation::PreloadScript(id as usize));
+}
+
+fn is_script_loaded(caller: Caller<FullScriptContext<'_>>, id: u32) -> u32 {
+ let context = caller.data();
+ context.scripts.is_script_loaded(id as usize, context.files) as u32
+}
+
+fn unload_script(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
+ caller
+ .data_mut()
+ .operation_queue
+ .push(Operation::UnloadScript(id as usize));
+}
+
+fn activate_script(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
+ caller
+ .data_mut()
+ .operation_queue
+ .push(Operation::ActivateScript(id as usize));
+}
+
+fn deactivate_script(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
+ caller
+ .data_mut()
+ .operation_queue
+ .push(Operation::DeactivateScript(id as usize));
+}
+
+fn state_size(caller: Caller<FullScriptContext<'_>>) -> u32 {
+ let context = caller.data();
+ context
+ .scripts
+ .script_states
+ .get(&context.script_id)
+ .map(|state| state.len())
+ .unwrap_or_default() as u32
+}
+
+fn load_state(mut caller: Caller<FullScriptContext<'_>>, offset: u32) {
+ let memory = caller.get_export("memory").unwrap().into_memory().unwrap();
+ let context = caller.data();
+ let state = context
+ .scripts
+ .script_states
+ .get(&context.script_id)
+ .map(|state| &**state)
+ .unwrap_or(&[]);
+ memory.write(&mut caller, offset as usize, state).unwrap();
+}
+
+fn save_state(mut caller: Caller<FullScriptContext<'_>>, offset: u32, length: u32) {
+ let offset = offset as usize;
+ let length = length as usize;
+ let memory = caller.get_export("memory").unwrap().into_memory().unwrap();
+ let new_state = Box::from(&memory.data(&caller)[offset..(offset + length)]);
+ let context = caller.data_mut();
+ context.operation_queue.push(Operation::UpdateState {
+ script_id: context.script_id,
+ new_state,
+ })
+}
+
+fn system_now(caller: Caller<FullScriptContext<'_>>) -> u64 {
caller
.data()
.time
@@ -86,17 +341,17 @@ fn system_now(caller: Caller<&mut ScriptContext>) -> u64 {
.as_nanos() as u64
}
-fn monotonic_now(caller: Caller<&mut ScriptContext>) -> u64 {
+fn monotonic_now(caller: Caller<FullScriptContext<'_>>) -> u64 {
caller.data().time.monotonic_now().as_nanos() as u64
}
-fn delta_time(caller: Caller<&mut ScriptContext>) -> u64 {
+fn delta_time(caller: Caller<FullScriptContext<'_>>) -> u64 {
caller.data().time.delta_time().as_nanos() as u64
}
pub fn linker<'ctx>(
engine: &Engine,
-) -> Result<wasmi::Linker<&'ctx mut ScriptContext<'ctx>>, wasmi::Error> {
+) -> Result<wasmi::Linker<FullScriptContext<'ctx>>, wasmi::Error> {
macro_rules! add_fns {
($linker: expr, [$($func: ident),*]) => {
$($linker.func_wrap("tortuise", stringify!($func), $func)?;)*
@@ -110,8 +365,22 @@ pub fn linker<'ctx>(
add_cell,
window_size,
is_key_pressed_this_frame,
+ log,
exit,
random_u64,
+ scene_id_by_name,
+ preload_scene,
+ switch_scene,
+ is_scene_loaded,
+ script_id_by_name,
+ preload_script,
+ is_script_loaded,
+ unload_script,
+ activate_script,
+ deactivate_script,
+ state_size,
+ load_state,
+ save_state,
system_now,
monotonic_now,
delta_time