From 0c128e472b1b49e32c313874eaeccc8c5f1b1202 Mon Sep 17 00:00:00 2001 From: Mica White Date: Mon, 26 Oct 2026 10:51:49 -0400 Subject: Finish engine --- src/script/wasm.rs | 380 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 335 insertions(+), 45 deletions(-) (limited to 'src/script/wasm.rs') diff --git a/src/script/wasm.rs b/src/script/wasm.rs index 9b9cd91..aaf25cf 100644 --- a/src/script/wasm.rs +++ b/src/script/wasm.rs @@ -1,4 +1,4 @@ -use std::time::UNIX_EPOCH; +use std::time::{Duration, UNIX_EPOCH}; use crossterm::{ event::KeyCode, @@ -35,6 +35,60 @@ fn context_ref<'b>(address: u64) -> &'b FullScriptContext<'b> { } } +fn number_to_button(number: u32) -> gilrs::Button { + match number { + 0 => gilrs::Button::Unknown, + 1 => gilrs::Button::South, + 2 => gilrs::Button::East, + 3 => gilrs::Button::C, + 4 => gilrs::Button::North, + 5 => gilrs::Button::West, + 6 => gilrs::Button::Z, + 7 => gilrs::Button::LeftTrigger, + 8 => gilrs::Button::RightTrigger, + 9 => gilrs::Button::LeftTrigger2, + 10 => gilrs::Button::RightTrigger2, + 11 => gilrs::Button::Select, + 12 => gilrs::Button::Start, + 13 => gilrs::Button::Mode, + 14 => gilrs::Button::LeftThumb, + 15 => gilrs::Button::RightThumb, + 16 => gilrs::Button::DPadUp, + 17 => gilrs::Button::DPadDown, + 18 => gilrs::Button::DPadLeft, + 19 => gilrs::Button::DPadRight, + _ => gilrs::Button::Unknown, + } +} + +fn number_to_axis(number: u32) -> gilrs::Axis { + match number { + 0 => gilrs::Axis::Unknown, + 1 => gilrs::Axis::LeftStickX, + 2 => gilrs::Axis::LeftStickY, + 3 => gilrs::Axis::LeftZ, + 4 => gilrs::Axis::RightStickX, + 5 => gilrs::Axis::RightStickY, + 6 => gilrs::Axis::RightZ, + 7 => gilrs::Axis::DPadX, + 8 => gilrs::Axis::DPadY, + _ => gilrs::Axis::Unknown, + } +} + +fn set_framerate_limit( + _caller: FuncContext<'_>, + (context, limit): (u64, u32), +) -> tinywasm::Result<()> { + puffin::profile_function!(); + let context = context_mut(context); + let duration = (limit != 0).then(|| Duration::from_nanos(1_000_000_000 / limit as u64)); + context + .operation_queue + .push(Operation::SetFrameLimit(duration)); + Ok(()) +} + fn add_cell( _caller: FuncContext<'_>, ( @@ -219,6 +273,174 @@ fn is_key_pressed_this_frame( ) } +fn is_mouse_clicked_this_frame( + _caller: FuncContext<'_>, + (context,): (u64,), +) -> tinywasm::Result { + puffin::profile_function!(); + if context_ref(context).input.is_mouse_clicked_this_frame() { + Ok(1) + } else { + Ok(0) + } +} + +fn mouse_location(_caller: FuncContext<'_>, (context,): (u64,)) -> tinywasm::Result { + puffin::profile_function!(); + let location = context_ref(context).input.mouse_location(); + + if let Some(location) = location { + Ok(((location.y as u32) << 16) | location.x as u32) + } else { + Ok(u32::MAX) + } +} + +fn scroll_direction_this_frame( + _caller: FuncContext<'_>, + (context,): (u64,), +) -> tinywasm::Result { + puffin::profile_function!(); + let direction = context_ref(context).input.scroll_direction_this_frame(); + Ok(direction.map(|d| d as u32).unwrap_or_default()) +} + +fn is_focused(_caller: FuncContext<'_>, (context,): (u64,)) -> tinywasm::Result { + puffin::profile_function!(); + Ok(if context_ref(context).input.is_focused() { + 1 + } else { + 0 + }) +} + +fn is_gamepad_connected_this_frame( + _caller: FuncContext<'_>, + context: u64, +) -> tinywasm::Result { + puffin::profile_function!(); + Ok(context_ref(context) + .input + .is_gamepad_connected_this_frame() + .unwrap_or_default() as u32) +} + +fn is_button_pressed_this_frame( + _caller: FuncContext<'_>, + (context, gamepad_id, button): (u64, u32, u32), +) -> tinywasm::Result { + puffin::profile_function!(); + Ok( + if context_ref(context) + .input + .is_button_pressed_this_frame(gamepad_id as usize, number_to_button(button)) + { + 1 + } else { + 0 + }, + ) +} + +fn is_button_released_this_frame( + _caller: FuncContext<'_>, + (context, gamepad_id, button): (u64, u32, u32), +) -> tinywasm::Result { + puffin::profile_function!(); + Ok( + if context_ref(context) + .input + .is_button_released_this_frame(gamepad_id as usize, number_to_button(button)) + { + 1 + } else { + 0 + }, + ) +} + +fn is_button_down( + _caller: FuncContext<'_>, + (context, gamepad_id, button): (u64, u32, u32), +) -> tinywasm::Result { + puffin::profile_function!(); + Ok( + if context_ref(context) + .input + .is_button_down(gamepad_id as usize, number_to_button(button)) + { + 1 + } else { + 0 + }, + ) +} + +fn is_gamepad_connected( + _caller: FuncContext<'_>, + (context, gamepad_id): (u64, u32), +) -> tinywasm::Result { + puffin::profile_function!(); + Ok( + if context_ref(context) + .input + .is_gamepad_connected(gamepad_id as usize) + { + 1 + } else { + 0 + }, + ) +} + +fn axis_value( + _caller: FuncContext<'_>, + (context, gamepad_id, axis): (u64, u32, u32), +) -> tinywasm::Result { + puffin::profile_function!(); + Ok(context_ref(context) + .input + .axis_value(gamepad_id as usize, number_to_axis(axis)) + .unwrap_or_default()) +} + +fn gamepad_power_info( + _caller: FuncContext<'_>, + (context, gamepad_id): (u64, u32), +) -> tinywasm::Result { + puffin::profile_function!(); + Ok( + match context_ref(context) + .input + .gamepad_power_info(gamepad_id as usize) + { + None | Some(gilrs::PowerInfo::Unknown) => 0, + Some(gilrs::PowerInfo::Wired) => 1 << 16, + Some(gilrs::PowerInfo::Discharging(power)) => 2 << 16 | power as u32, + Some(gilrs::PowerInfo::Charging(power)) => 3 << 16 | power as u32, + Some(gilrs::PowerInfo::Charged) => 4 << 16, + }, + ) +} + +fn was_focused_this_frame(_caller: FuncContext<'_>, (context,): (u64,)) -> tinywasm::Result { + puffin::profile_function!(); + Ok(if context_ref(context).input.was_focused_this_frame() { + 1 + } else { + 0 + }) +} + +fn was_unfocused_this_frame(_caller: FuncContext<'_>, (context,): (u64,)) -> tinywasm::Result { + puffin::profile_function!(); + Ok(if context_ref(context).input.was_unfocused_this_frame() { + 1 + } else { + 0 + }) +} + fn log( caller: FuncContext<'_>, (severity, offset, length): (u32, u32, u32), @@ -261,30 +483,31 @@ fn scene_id_by_name( ) } -fn preload_scene(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { +fn switch_scene(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { puffin::profile_function!(); context_mut(context) .operation_queue - .push(Operation::PreloadScene(id as usize)); + .push(Operation::SwitchScene(id as usize)); Ok(()) } -fn is_scene_loaded(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result { +fn script_name_size(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result { puffin::profile_function!(); let context = context_ref(context); - Ok(crate::scene::is_scene_loaded( - &context.manifest.scenes, - context.scripts, - &context.files, - id as usize, - ) as u32) + Ok(context.scripts.scripts[context.script_id].name().len() as u32) } -fn switch_scene(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { +fn load_script_name( + mut caller: FuncContext<'_>, + (context, offset): (u64, u32), +) -> tinywasm::Result<()> { puffin::profile_function!(); - context_mut(context) - .operation_queue - .push(Operation::SwitchScene(id as usize)); + let memory = caller.memory("memory").unwrap(); + let context = context_ref(context); + let name = context.scripts.scripts[context.script_id].name(); + memory + .write(&mut caller, offset as usize, name.as_bytes()) + .unwrap(); Ok(()) } @@ -302,30 +525,6 @@ fn script_id_by_name( .unwrap_or(u32::MAX)) } -fn preload_script(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { - puffin::profile_function!(); - context_mut(context) - .operation_queue - .push(Operation::PreloadScript(id as usize)); - Ok(()) -} - -fn is_script_loaded(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result { - puffin::profile_function!(); - let context = context_ref(context); - Ok(context - .scripts - .is_script_loaded(id as usize, &context.files) as u32) -} - -fn unload_script(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { - puffin::profile_function!(); - context_mut(context) - .operation_queue - .push(Operation::UnloadScript(id as usize)); - Ok(()) -} - fn activate_script(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { puffin::profile_function!(); context_mut(context) @@ -444,7 +643,7 @@ fn storage_size(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result puffin::profile_function!(); Ok(context_ref(context) .storage - .get() + .data() .map(|data| data.len()) .unwrap_or_default() as u32) } @@ -456,7 +655,7 @@ fn load_storage( puffin::profile_function!(); let memory = caller.memory("memory").unwrap(); let context = context_ref(context); - let state = context.storage.get().unwrap_or(&[]); + let state = context.storage.data().unwrap_or(&[]); memory.write(&mut caller, offset as usize, state).unwrap(); Ok(()) } @@ -480,6 +679,79 @@ fn save_storage( Ok(()) } +fn is_saving_storage(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result { + puffin::profile_function!(); + Ok(if context_ref(context).storage.is_saving_data() { + 1 + } else { + 0 + }) +} + +fn saving_storage_failed(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result { + puffin::profile_function!(); + Ok(if context_ref(context).storage.saving_data_failed() { + 1 + } else { + 0 + }) +} + +fn config_size(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result { + puffin::profile_function!(); + Ok(context_ref(context) + .storage + .config() + .map(|data| data.len()) + .unwrap_or_default() as u32) +} + +fn load_config(mut caller: FuncContext<'_>, (context, offset): (u64, u32)) -> tinywasm::Result<()> { + puffin::profile_function!(); + let memory = caller.memory("memory").unwrap(); + let context = context_ref(context); + let state = context.storage.config().unwrap_or(&[]); + memory.write(&mut caller, offset as usize, state).unwrap(); + Ok(()) +} + +fn save_config( + mut caller: FuncContext<'_>, + (context, offset, length): (u64, u32, u32), +) -> tinywasm::Result<()> { + puffin::profile_function!(); + let offset = offset as usize; + let length = length as usize; + let memory = caller.memory("memory").unwrap(); + let new_save = memory + .read_vec(caller.store_mut(), offset, length) + .unwrap() + .into_boxed_slice(); + let context = context_mut(context); + context + .operation_queue + .push(Operation::UpdateConfig { new_save }); + Ok(()) +} + +fn is_saving_config(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result { + puffin::profile_function!(); + Ok(if context_ref(context).storage.is_saving_config() { + 1 + } else { + 0 + }) +} + +fn saving_config_failed(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result { + puffin::profile_function!(); + Ok(if context_ref(context).storage.saving_config_failed() { + 1 + } else { + 0 + }) +} + fn system_now(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result { puffin::profile_function!(); Ok(context_ref(context) @@ -514,18 +786,29 @@ pub fn linker(store: &mut Store) -> Result { [ add_cell, window_size, + set_framerate_limit, is_key_pressed_this_frame, + is_mouse_clicked_this_frame, + mouse_location, + scroll_direction_this_frame, + is_focused, + is_button_pressed_this_frame, + is_button_released_this_frame, + is_gamepad_connected_this_frame, + is_gamepad_connected, + is_button_down, + axis_value, + gamepad_power_info, + was_focused_this_frame, + was_unfocused_this_frame, log, exit, random_u64, + script_name_size, + load_script_name, 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, @@ -537,6 +820,13 @@ pub fn linker(store: &mut Store) -> Result { storage_size, load_storage, save_storage, + is_saving_storage, + saving_storage_failed, + config_size, + load_config, + save_config, + is_saving_config, + saving_config_failed, system_now, monotonic_now, delta_time -- cgit v1.3.1