summaryrefslogtreecommitdiff
path: root/src/script
diff options
context:
space:
mode:
Diffstat (limited to 'src/script')
-rw-r--r--src/script/lua.rs495
-rw-r--r--src/script/wasm.rs361
2 files changed, 716 insertions, 140 deletions
diff --git a/src/script/lua.rs b/src/script/lua.rs
new file mode 100644
index 0000000..a5c7f6a
--- /dev/null
+++ b/src/script/lua.rs
@@ -0,0 +1,495 @@
+use std::time::SystemTime;
+
+use crossterm::{
+ event::KeyCode,
+ style::{Attribute, Attributes, Color, ContentStyle},
+};
+use hex_color::HexColor;
+use mlua::{FromLua, IntoLua, Lua, LuaOptions, StdLib, Table, Value};
+use serde::{Deserialize, Serialize};
+
+use crate::{
+ buffer::Cell,
+ scene::scene_id_by_name,
+ script::{FullScriptContext, Operation},
+};
+
+pub struct LuaModule {
+ interpreter: Lua,
+}
+
+impl LuaModule {
+ pub fn new(data: &[u8]) -> mlua::Result<Self> {
+ macro_rules! add_fn {
+ ($lua: expr, $table: expr, $fn: ident) => {
+ $table.set(stringify!($fn), $lua.create_function($fn)?)?;
+ };
+ }
+
+ macro_rules! add_fns {
+ ($lua: expr, [$($fn: ident),*]) => {
+ let tortuise_module = $lua.create_table()?;
+ $(add_fn!($lua, tortuise_module, $fn);)*
+ let globals = $lua.globals();
+ globals.set("tortuise", tortuise_module)?;
+ $lua.set_globals(globals)?;
+ };
+ }
+
+ let interpreter = Lua::new_with(
+ StdLib::TABLE | StdLib::STRING | StdLib::MATH,
+ LuaOptions::new().catch_rust_panics(true),
+ )?;
+ add_fns!(
+ interpreter,
+ [
+ add_cell,
+ window_size,
+ is_key_pressed_this_frame,
+ log,
+ warn,
+ error,
+ exit,
+ is_scene_loaded,
+ preload_scene,
+ switch_scene,
+ is_script_loaded,
+ preload_script,
+ unload_script,
+ activate_script,
+ deactivate_script,
+ state,
+ set_state,
+ entity,
+ set_entity,
+ storage,
+ set_storage,
+ clock,
+ time,
+ delta_time
+ ]
+ );
+ interpreter.load(data).exec()?;
+ Ok(Self { interpreter })
+ }
+
+ pub fn run_function(&self, func: &str, ctx: FullScriptContext) -> mlua::Result<()> {
+ unsafe {
+ let ctx = std::ptr::from_mut(Box::leak(Box::new(ctx)));
+ self.interpreter.set_app_data(ctx.expose_provenance());
+ self.interpreter
+ .load(format!("if {func} then {func}() end"))
+ .exec()?;
+ drop(Box::from_raw(ctx));
+ Ok(())
+ }
+ }
+}
+
+#[allow(clippy::mut_from_ref)]
+unsafe fn context(lua: &Lua) -> &mut FullScriptContext<'_> {
+ unsafe {
+ std::ptr::with_exposed_provenance_mut::<FullScriptContext>(*lua.app_data_ref().unwrap())
+ .as_mut_unchecked()
+ }
+}
+
+fn add_cell(lua: &Lua, cell: Table) -> mlua::Result<()> {
+ fn parse_color(mut color: String) -> Option<Color> {
+ color.make_ascii_uppercase();
+ color.retain(|c| c != '-' && c != ' ' && c != '_');
+
+ match &*color {
+ "RESET" => return Some(Color::Reset),
+ "BLACK" => return Some(Color::Black),
+ "DARKGREY" => return Some(Color::DarkGrey),
+ "DARKGRAY" => return Some(Color::DarkGrey),
+ "RED" => return Some(Color::Red),
+ "DARKRED" => return Some(Color::DarkRed),
+ "GREEN" => return Some(Color::Green),
+ "DARKGREEN" => return Some(Color::DarkGreen),
+ "YELLOW" => return Some(Color::Yellow),
+ "DARKYELLOW" => return Some(Color::DarkYellow),
+ "BLUE" => return Some(Color::Blue),
+ "DARKBLUE" => return Some(Color::DarkBlue),
+ "MAGENTA" => return Some(Color::Magenta),
+ "DARKMAGENTA" => return Some(Color::DarkMagenta),
+ "CYAN" => return Some(Color::Cyan),
+ "DARKCYAN" => return Some(Color::DarkCyan),
+ "WHITE" => return Some(Color::White),
+ "GREY" => return Some(Color::Grey),
+ "GRAY" => return Some(Color::Grey),
+ _ => (),
+ };
+ if color.starts_with('#') {
+ let color = HexColor::parse(&color).ok()?;
+ return Some(Color::Rgb {
+ r: color.r,
+ g: color.g,
+ b: color.b,
+ });
+ }
+
+ None
+ }
+
+ fn parse_attributes(value: Vec<String>) -> Attributes {
+ let mut attributes = Attributes::none();
+ for mut value in value {
+ value.make_ascii_uppercase();
+ value.retain(|c| c != '-' && c != ' ' && c != '_');
+ match &*value {
+ "BOLD" => attributes.set(Attribute::Bold),
+ "DIM" => attributes.set(Attribute::Dim),
+ "ITALIC" => attributes.set(Attribute::Italic),
+ "UNDERLINED" => attributes.set(Attribute::Underlined),
+ "DOUBLEUNDERLINED" => attributes.set(Attribute::DoubleUnderlined),
+ "UNDERCURLED" => attributes.set(Attribute::Undercurled),
+ "UNDERDOTTED" => attributes.set(Attribute::Underdotted),
+ "SLOWBLINK" => attributes.set(Attribute::SlowBlink),
+ "RAPIDBLINK" => attributes.set(Attribute::RapidBlink),
+ "REVERSE" => attributes.set(Attribute::Reverse),
+ "HIDDEN" => attributes.set(Attribute::Hidden),
+ "CROSSEDOUT" => attributes.set(Attribute::CrossedOut),
+ "FRAKTUR" => attributes.set(Attribute::Fraktur),
+ "FRAMED" => attributes.set(Attribute::Framed),
+ "ENCIRCLED" => attributes.set(Attribute::Encircled),
+ "OVERLINED" => attributes.set(Attribute::OverLined),
+ _ => (),
+ }
+ }
+
+ attributes
+ }
+
+ puffin::profile_function!();
+ unsafe { context(lua) }
+ .operation_queue
+ .push(Operation::AddCell(Cell {
+ character: cell.get("character")?,
+ style: ContentStyle {
+ foreground_color: parse_color(cell.get("foreground_color").unwrap_or_default()),
+ background_color: parse_color(cell.get("background_color").unwrap_or_default()),
+ underline_color: parse_color(cell.get("underline_color").unwrap_or_default()),
+ attributes: parse_attributes(cell.get("attributes").unwrap_or_default()),
+ },
+ x: cell.get("x")?,
+ y: cell.get("y")?,
+ z: cell.get("z")?,
+ }));
+ Ok(())
+}
+
+fn window_size(lua: &Lua, _: ()) -> mlua::Result<Table> {
+ puffin::profile_function!();
+ let window_size = &unsafe { context(lua) }.buffer.window_size;
+ let value = lua.create_table()?;
+ value.set("rows", window_size.rows)?;
+ value.set("columns", window_size.columns)?;
+ value.set("width", window_size.width)?;
+ value.set("height", window_size.height)?;
+ Ok(value)
+}
+
+fn is_key_pressed_this_frame(lua: &Lua, mut key: String) -> mlua::Result<bool> {
+ puffin::profile_function!();
+ key.make_ascii_uppercase();
+ key.retain(|c| c != '-' && c != '_' && c != ' ');
+ let key = match &*key {
+ "BACKSPACE" => KeyCode::Backspace,
+ "ENTER" => KeyCode::Enter,
+ "LEFT" => KeyCode::Left,
+ "RIGHT" => KeyCode::Right,
+ "UP" => KeyCode::Up,
+ "DOWN" => KeyCode::Down,
+ "HOME" => KeyCode::Home,
+ "END" => KeyCode::End,
+ "PAGEUP" => KeyCode::PageUp,
+ "PAGEDOWN" => KeyCode::PageDown,
+ "TAB" => KeyCode::Tab,
+ "BACKTAB" => KeyCode::BackTab,
+ "DELETE" => KeyCode::Delete,
+ "INSERT" => KeyCode::Insert,
+ "ESC" => KeyCode::Esc,
+ "ESCAPE" => KeyCode::Esc,
+ f if f.starts_with('F') => KeyCode::F(f[1..].parse::<u8>().unwrap()),
+ c if c.len() == 1 => KeyCode::Char(c.chars().next().unwrap()),
+ _ => return Err(mlua::Error::runtime(format!("Invalid key code: {key}"))),
+ };
+
+ Ok(unsafe { context(lua).input.is_key_pressed_this_frame(key) })
+}
+
+fn log(_: &Lua, value: Value) -> mlua::Result<()> {
+ puffin::profile_function!();
+ log::info!("{}", value.to_string()?);
+ Ok(())
+}
+
+fn warn(_: &Lua, value: Value) -> mlua::Result<()> {
+ puffin::profile_function!();
+ log::warn!("{}", value.to_string()?);
+ Ok(())
+}
+
+fn error(_: &Lua, value: Value) -> mlua::Result<()> {
+ puffin::profile_function!();
+ log::error!("{}", value.to_string()?);
+ Ok(())
+}
+
+fn exit(lua: &Lua, _: ()) -> mlua::Result<()> {
+ puffin::profile_function!();
+ unsafe { context(lua) }
+ .operation_queue
+ .push(Operation::Exit);
+ Ok(())
+}
+
+fn is_scene_loaded(lua: &Lua, scene: String) -> mlua::Result<()> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ let scene = scene_id_by_name(&context.manifest.scenes, &scene)
+ .ok_or(mlua::Error::runtime("No scene with the given name found"))?;
+ crate::scene::is_scene_loaded(
+ &context.manifest.scenes,
+ context.scripts,
+ &context.files,
+ scene,
+ );
+ Ok(())
+}
+
+fn preload_scene(lua: &Lua, scene: String) -> mlua::Result<()> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ let scene = scene_id_by_name(&context.manifest.scenes, &scene)
+ .ok_or(mlua::Error::runtime("No scene with the given name found"))?;
+ context.operation_queue.push(Operation::PreloadScene(scene));
+ Ok(())
+}
+
+fn switch_scene(lua: &Lua, scene: String) -> mlua::Result<()> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ let scene = scene_id_by_name(&context.manifest.scenes, &scene)
+ .ok_or(mlua::Error::runtime("No scene with the given name found"))?;
+ context.operation_queue.push(Operation::SwitchScene(scene));
+ Ok(())
+}
+
+fn is_script_loaded(lua: &Lua, script: String) -> mlua::Result<()> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ let script = context
+ .scripts
+ .script_id_by_name(&script)
+ .ok_or(mlua::Error::runtime("No script with the given name found"))?;
+ context.scripts.is_script_loaded(script, &context.files);
+ Ok(())
+}
+
+fn preload_script(lua: &Lua, script: String) -> mlua::Result<()> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ let script = context
+ .scripts
+ .script_id_by_name(&script)
+ .ok_or(mlua::Error::runtime("No script with the given name found"))?;
+ context
+ .operation_queue
+ .push(Operation::PreloadScript(script));
+ Ok(())
+}
+
+fn unload_script(lua: &Lua, script: String) -> mlua::Result<()> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ let script = context
+ .scripts
+ .script_id_by_name(&script)
+ .ok_or(mlua::Error::runtime("No script with the given name found"))?;
+ context
+ .operation_queue
+ .push(Operation::UnloadScript(script));
+ Ok(())
+}
+
+fn activate_script(lua: &Lua, script: String) -> mlua::Result<()> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ let script = context
+ .scripts
+ .script_id_by_name(&script)
+ .ok_or(mlua::Error::runtime("No script with the given name found"))?;
+ context
+ .operation_queue
+ .push(Operation::ActivateScript(script));
+ Ok(())
+}
+
+fn deactivate_script(lua: &Lua, script: String) -> mlua::Result<()> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ let script = context
+ .scripts
+ .script_id_by_name(&script)
+ .ok_or(mlua::Error::runtime("No script with the given name found"))?;
+ context
+ .operation_queue
+ .push(Operation::DeactivateScript(script));
+ Ok(())
+}
+
+fn state(lua: &Lua, default: MyLuaValue) -> mlua::Result<MyLuaValue> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ Ok(context
+ .scripts
+ .script_states
+ .get(&context.script_id)
+ .map(|state| postcard::from_bytes(state).unwrap())
+ .unwrap_or(default))
+}
+
+fn set_state(lua: &Lua, new_state: MyLuaValue) -> mlua::Result<()> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ context.operation_queue.push(Operation::UpdateState {
+ script_id: context.script_id,
+ new_state: postcard::to_stdvec(&new_state).unwrap().into_boxed_slice(),
+ });
+ Ok(())
+}
+
+fn entity(lua: &Lua, (name, default): (String, MyLuaValue)) -> mlua::Result<MyLuaValue> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ Ok(context
+ .scripts
+ .entities
+ .get(&*name)
+ .map(|state| postcard::from_bytes(state).unwrap())
+ .unwrap_or(default))
+}
+
+fn set_entity(lua: &Lua, (name, new_state): (String, MyLuaValue)) -> mlua::Result<()> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ context.operation_queue.push(Operation::UpdateEntity {
+ name: name.into_boxed_str(),
+ new_state: postcard::to_stdvec(&new_state).unwrap().into_boxed_slice(),
+ });
+ Ok(())
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+enum MyLuaValue {
+ Nil,
+ Boolean(bool),
+ Number(f64),
+ String(String),
+ Table(Vec<(MyLuaValue, MyLuaValue)>),
+}
+
+impl FromLua for MyLuaValue {
+ fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
+ Ok(match value {
+ Value::Nil => MyLuaValue::Nil,
+ Value::Boolean(b) => MyLuaValue::Boolean(b),
+ Value::Number(n) => MyLuaValue::Number(n),
+ Value::Integer(n) => MyLuaValue::Number(n as f64),
+ Value::String(s) => MyLuaValue::String(s.to_string_lossy()),
+ Value::Table(t) => {
+ let mut pairs = Vec::new();
+ t.for_each(|k, v| {
+ pairs.push((k, v));
+ Ok(())
+ })?;
+ MyLuaValue::Table(pairs)
+ }
+ _ => return Err(mlua::Error::runtime("Invalid storage type")),
+ })
+ }
+}
+
+impl IntoLua for MyLuaValue {
+ fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
+ Ok(match self {
+ MyLuaValue::Nil => Value::Nil,
+ MyLuaValue::Boolean(b) => Value::Boolean(b),
+ MyLuaValue::Number(n) => Value::Number(n),
+ MyLuaValue::String(s) => Value::String(lua.create_string(s)?),
+ MyLuaValue::Table(items) => {
+ let table = lua.create_table()?;
+ for (k, v) in items {
+ table.set(k.into_lua(lua)?, v.into_lua(lua)?)?;
+ }
+ Value::Table(table)
+ }
+ })
+ }
+}
+
+fn storage(lua: &Lua, default: MyLuaValue) -> mlua::Result<MyLuaValue> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ Ok(context
+ .storage
+ .get()
+ .map(|state| postcard::from_bytes(state).unwrap())
+ .unwrap_or(default))
+}
+
+fn set_storage(lua: &Lua, new_state: MyLuaValue) -> mlua::Result<()> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ context.operation_queue.push(Operation::UpdateStorage {
+ new_save: postcard::to_stdvec(&new_state).unwrap().into_boxed_slice(),
+ });
+ Ok(())
+}
+
+fn clock(lua: &Lua, _: ()) -> mlua::Result<f64> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ Ok(context.time.monotonic_now().as_secs_f64())
+}
+
+fn time(lua: &Lua, input: Option<Table>) -> mlua::Result<f64> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ if let Some(input) = input {
+ let date = chrono::NaiveDate::from_ymd_opt(
+ input.get("year")?,
+ input.get("month")?,
+ input.get("day")?,
+ )
+ .unwrap();
+ let time = chrono::NaiveTime::from_hms_opt(
+ input.get("hour").unwrap_or_default(),
+ input.get("min").unwrap_or_default(),
+ input.get("sec").unwrap_or_default(),
+ )
+ .unwrap();
+ let datetime = chrono::NaiveDateTime::new(date, time)
+ .and_local_timezone(chrono::Local)
+ .unwrap();
+ Ok(datetime
+ .signed_duration_since(chrono::DateTime::UNIX_EPOCH)
+ .as_seconds_f64())
+ } else {
+ Ok(context
+ .time
+ .system_now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .unwrap()
+ .as_secs_f64())
+ }
+}
+
+fn delta_time(lua: &Lua, _: ()) -> mlua::Result<f64> {
+ puffin::profile_function!();
+ let context = unsafe { context(lua) };
+ Ok(context.time.delta_time().as_secs_f64())
+}
diff --git a/src/script/wasm.rs b/src/script/wasm.rs
index 25632fa..9b9cd91 100644
--- a/src/script/wasm.rs
+++ b/src/script/wasm.rs
@@ -1,41 +1,54 @@
-use core::slice;
use std::time::UNIX_EPOCH;
use crossterm::{
event::KeyCode,
style::{Attribute, Attributes, Color, ContentStyle},
};
-use wasmi::{Caller, Engine, Linker};
+use tinywasm::{FuncContext, HostFunction, Imports, ModuleInstance, Store};
use crate::{buffer::Cell, script::FullScriptContext};
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();
+pub struct WasmModule {
+ pub instance: ModuleInstance,
+}
+
+fn str_from_memory(caller: &FuncContext<'_>, offset: u32, length: u32) -> tinywasm::Result<String> {
+ let memory = caller.memory("memory").unwrap();
+ memory.read_string(caller.store(), offset as usize, length as usize)
+}
+
+fn context_mut<'b>(address: u64) -> &'b mut FullScriptContext<'b> {
unsafe {
- str::from_utf8_unchecked(slice::from_raw_parts(
- memory.data_ptr(caller).byte_add(offset as usize),
- length as usize,
- ))
+ std::ptr::with_exposed_provenance_mut::<FullScriptContext<'b>>(address as usize)
+ .as_mut()
+ .unwrap_unchecked()
+ }
+}
+
+fn context_ref<'b>(address: u64) -> &'b FullScriptContext<'b> {
+ unsafe {
+ std::ptr::with_exposed_provenance::<FullScriptContext<'b>>(address as usize)
+ .as_ref()
+ .unwrap_unchecked()
}
}
-#[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,
-) {
+ _caller: FuncContext<'_>,
+ (
+ context,
+ character,
+ location,
+ z,
+ foreground_color,
+ background_color,
+ underline_color,
+ attributes,
+ ): (u64, u32, u32, u32, u32, u32, u32, u32),
+) -> tinywasm::Result<()> {
+ puffin::profile_function!();
const STANDARD_COLOR: u32 = 1 << 24;
const ANSI_COLOR: u32 = 2 << 24;
const RGB_COLOR: u32 = 3 << 24;
@@ -127,8 +140,7 @@ fn add_cell(
attribute_set.set(Attribute::OverLined);
}
- caller
- .data_mut()
+ context_mut(context)
.operation_queue
.push(Operation::AddCell(Cell {
character: char::from_u32(character).unwrap_or_default(),
@@ -142,14 +154,20 @@ fn add_cell(
y: (location & 0xffff) as u16,
z: z as u8,
}));
+ Ok(())
}
-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 window_size(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u32> {
+ puffin::profile_function!();
+ let window_size = &context_ref(context).buffer.window_size;
+ Ok(((window_size.rows as u32) << 16) | (window_size.columns as u32))
}
-fn is_key_pressed_this_frame(caller: Caller<FullScriptContext<'_>>, key_code: u32) -> u32 {
+fn is_key_pressed_this_frame(
+ _caller: FuncContext<'_>,
+ (context, key_code): (u64, u32),
+) -> tinywasm::Result<u32> {
+ puffin::profile_function!();
const BACKSPACE: u32 = 1 << 24;
const ENTER: u32 = 2 << 24;
const LEFT: u32 = 3 << 24;
@@ -189,21 +207,24 @@ fn is_key_pressed_this_frame(caller: Caller<FullScriptContext<'_>>, key_code: u3
_ => KeyCode::Null,
};
- if caller.data().input.is_key_pressed_this_frame(key_code) {
- 1
- } else {
- 0
- }
+ Ok(
+ if context_ref(context)
+ .input
+ .is_key_pressed_this_frame(key_code)
+ {
+ 1
+ } else {
+ 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,
- ))
- };
+fn log(
+ caller: FuncContext<'_>,
+ (severity, offset, length): (u32, u32, u32),
+) -> tinywasm::Result<()> {
+ puffin::profile_function!();
+ let message = str_from_memory(&caller, offset, length)?;
let severity = match severity {
1 => log::Level::Error,
2 => log::Level::Warn,
@@ -213,103 +234,129 @@ fn log(caller: Caller<FullScriptContext<'_>>, severity: u32, offset: u32, length
_ => log::Level::Error,
};
log::log!(severity, "{message}");
+ Ok(())
}
-fn exit(mut caller: Caller<FullScriptContext<'_>>) {
- caller.data_mut().operation_queue.push(Operation::Exit)
+fn exit(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<()> {
+ puffin::profile_function!();
+ context_mut(context).operation_queue.push(Operation::Exit);
+ Ok(())
}
-fn random_u64() -> u64 {
- getrandom::u64().unwrap_or(4)
+fn random_u64(_caller: FuncContext<'_>, (): ()) -> tinywasm::Result<u64> {
+ puffin::profile_function!();
+ Ok(getrandom::u64().unwrap_or(4))
}
-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 scene_id_by_name(
+ caller: FuncContext<'_>,
+ (context, offset, length): (u64, u32, u32),
+) -> tinywasm::Result<u32> {
+ puffin::profile_function!();
+ let name = str_from_memory(&caller, offset, length)?;
+ Ok(
+ crate::scene::scene_id_by_name(&context_ref(context).manifest.scenes, &name)
+ .map(|u| u as u32)
+ .unwrap_or(u32::MAX),
+ )
}
-fn preload_scene(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
- caller
- .data_mut()
+fn preload_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::PreloadScene(id as usize));
+ Ok(())
}
-fn is_scene_loaded(caller: Caller<FullScriptContext<'_>>, id: u32) -> u32 {
- let context = caller.data();
- crate::scene::is_scene_loaded(
+fn is_scene_loaded(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<u32> {
+ puffin::profile_function!();
+ let context = context_ref(context);
+ Ok(crate::scene::is_scene_loaded(
&context.manifest.scenes,
context.scripts,
- context.files,
+ &context.files,
id as usize,
- ) as u32
+ ) as u32)
}
-fn switch_scene(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
- caller
- .data_mut()
+fn switch_scene(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> {
+ puffin::profile_function!();
+ context_mut(context)
.operation_queue
.push(Operation::SwitchScene(id as usize));
+ Ok(())
}
-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
+fn script_id_by_name(
+ caller: FuncContext<'_>,
+ (context, offset, length): (u64, u32, u32),
+) -> tinywasm::Result<u32> {
+ puffin::profile_function!();
+ let name = str_from_memory(&caller, offset, length)?;
+ let context = context_ref(context);
+ Ok(context
.scripts
- .script_id_by_name(name)
+ .script_id_by_name(&name)
.map(|u| u as u32)
- .unwrap_or(u32::MAX)
+ .unwrap_or(u32::MAX))
}
-fn preload_script(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
- caller
- .data_mut()
+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: Caller<FullScriptContext<'_>>, id: u32) -> u32 {
- let context = caller.data();
- context.scripts.is_script_loaded(id as usize, context.files) as u32
+fn is_script_loaded(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<u32> {
+ puffin::profile_function!();
+ let context = context_ref(context);
+ Ok(context
+ .scripts
+ .is_script_loaded(id as usize, &context.files) as u32)
}
-fn unload_script(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
- caller
- .data_mut()
+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(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
- caller
- .data_mut()
+fn activate_script(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> {
+ puffin::profile_function!();
+ context_mut(context)
.operation_queue
.push(Operation::ActivateScript(id as usize));
+ Ok(())
}
-fn deactivate_script(mut caller: Caller<FullScriptContext<'_>>, id: u32) {
- caller
- .data_mut()
+fn deactivate_script(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> {
+ puffin::profile_function!();
+ context_mut(context)
.operation_queue
.push(Operation::DeactivateScript(id as usize));
+ Ok(())
}
-fn state_size(caller: Caller<FullScriptContext<'_>>) -> u32 {
- let context = caller.data();
- context
+fn state_size(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u32> {
+ puffin::profile_function!();
+ let context = context_ref(context);
+ Ok(context
.scripts
.script_states
.get(&context.script_id)
.map(|state| state.len())
- .unwrap_or_default() as u32
+ .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();
+fn load_state(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
.scripts
.script_states
@@ -317,119 +364,153 @@ fn load_state(mut caller: Caller<FullScriptContext<'_>>, offset: u32) {
.map(|state| &**state)
.unwrap_or(&[]);
memory.write(&mut caller, offset as usize, state).unwrap();
+ Ok(())
}
-fn save_state(mut caller: Caller<FullScriptContext<'_>>, offset: u32, length: u32) {
+fn save_state(
+ 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.get_export("memory").unwrap().into_memory().unwrap();
- let new_state = Box::from(&memory.data(&caller)[offset..(offset + length)]);
- let context = caller.data_mut();
+ let memory = caller.memory("memory").unwrap();
+ let new_state = memory
+ .read_vec(caller.store_mut(), offset, length)
+ .unwrap()
+ .into_boxed_slice();
+ let context = context_mut(context);
context.operation_queue.push(Operation::UpdateState {
script_id: context.script_id,
new_state,
});
+ Ok(())
}
-fn entity_size(caller: Caller<FullScriptContext<'_>>, name: u32, name_len: u32) -> u32 {
- let context = caller.data();
- let name = unsafe { str_from_memory(&caller, name, name_len) };
- context
+fn entity_size(
+ caller: FuncContext<'_>,
+ (context, name, name_len): (u64, u32, u32),
+) -> tinywasm::Result<u32> {
+ puffin::profile_function!();
+ let name = str_from_memory(&caller, name, name_len)?;
+ let context = context_ref(context);
+ Ok(context
.scripts
.entities
- .get(name)
+ .get(&*name)
.map(|entity| entity.len())
- .unwrap_or_default() as u32
+ .unwrap_or_default() as u32)
}
-fn load_entity(mut caller: Caller<FullScriptContext<'_>>, name: u32, name_len: u32, offset: u32) {
- let memory = caller.get_export("memory").unwrap().into_memory().unwrap();
- let name = unsafe { str_from_memory(&caller, name, name_len) };
- let context = caller.data();
+fn load_entity(
+ mut caller: FuncContext<'_>,
+ (context, name, name_len, offset): (u64, u32, u32, u32),
+) -> tinywasm::Result<()> {
+ puffin::profile_function!();
+ let name = str_from_memory(&caller, name, name_len)?;
+ let memory = caller.memory("memory").unwrap();
+ let context = context_ref(context);
let entity = context
.scripts
.entities
- .get(name)
+ .get(&*name)
.map(|entity| &**entity)
.unwrap_or(&[]);
memory.write(&mut caller, offset as usize, entity).unwrap();
+ Ok(())
}
fn save_entity(
- mut caller: Caller<FullScriptContext<'_>>,
- name: u32,
- name_len: u32,
- offset: u32,
- length: u32,
-) {
+ mut caller: FuncContext<'_>,
+ (context, name, name_len, offset, length): (u64, u32, u32, u32, u32),
+) -> tinywasm::Result<()> {
+ puffin::profile_function!();
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 name = Box::from(unsafe { str_from_memory(&caller, name, name_len) });
- let context = caller.data_mut();
+ let memory = caller.memory("memory").unwrap();
+ let new_state = memory
+ .read_vec(caller.store_mut(), offset, length)
+ .unwrap()
+ .into_boxed_slice();
+ let name = str_from_memory(&caller, name, name_len)?.into_boxed_str();
+ let context = context_mut(context);
context
.operation_queue
.push(Operation::UpdateEntity { name, new_state });
+ Ok(())
}
-fn storage_size(caller: Caller<FullScriptContext<'_>>) -> u32 {
- caller
- .data()
+fn storage_size(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u32> {
+ puffin::profile_function!();
+ Ok(context_ref(context)
.storage
.get()
.map(|data| data.len())
- .unwrap_or_default() as u32
+ .unwrap_or_default() as u32)
}
-fn load_storage(mut caller: Caller<FullScriptContext<'_>>, offset: u32) {
- let memory = caller.get_export("memory").unwrap().into_memory().unwrap();
- let context = caller.data();
+fn load_storage(
+ 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.get().unwrap_or(&[]);
memory.write(&mut caller, offset as usize, state).unwrap();
+ Ok(())
}
-fn save_storage(mut caller: Caller<FullScriptContext<'_>>, offset: u32, length: u32) {
+fn save_storage(
+ 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.get_export("memory").unwrap().into_memory().unwrap();
- let new_save = Box::from(&memory.data(&caller)[offset..(offset + length)]);
- let context = caller.data_mut();
+ 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::UpdateStorage { new_save });
+ Ok(())
}
-fn system_now(caller: Caller<FullScriptContext<'_>>) -> u64 {
- caller
- .data()
+fn system_now(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u64> {
+ puffin::profile_function!();
+ Ok(context_ref(context)
.time
.system_now()
.duration_since(UNIX_EPOCH)
.unwrap()
- .as_nanos() as u64
+ .as_nanos() as u64)
}
-fn monotonic_now(caller: Caller<FullScriptContext<'_>>) -> u64 {
- caller.data().time.monotonic_now().as_nanos() as u64
+fn monotonic_now(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u64> {
+ puffin::profile_function!();
+ Ok(context_ref(context).time.monotonic_now().as_nanos() as u64)
}
-fn delta_time(caller: Caller<FullScriptContext<'_>>) -> u64 {
- caller.data().time.delta_time().as_nanos() as u64
+fn delta_time(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u64> {
+ puffin::profile_function!();
+ Ok(context_ref(context).time.delta_time().as_nanos() as u64)
}
-pub fn linker<'ctx>(
- engine: &Engine,
-) -> Result<wasmi::Linker<FullScriptContext<'ctx>>, wasmi::Error> {
+pub fn linker(store: &mut Store) -> Result<Imports, tinywasm::Error> {
macro_rules! add_fns {
- ($linker: expr, [$($func: ident),*]) => {
- $($linker.func_wrap("tortuise", stringify!($func), $func)?;)*
+ ($linker: expr, $store: expr, [$($func: ident),*]) => {
+ $($linker.define("tortuise", stringify!($func), HostFunction::from($store, $func));)*
};
}
- let mut linker = Linker::new(engine);
+ let mut linker = Imports::new();
add_fns!(
linker,
+ store,
[
add_cell,
window_size,