From ec40f6e3acc7874b82709ab81b254c741529fdff Mon Sep 17 00:00:00 2001 From: Mica White Date: Mon, 26 Oct 2026 10:52:04 -0400 Subject: Finish engine --- platforms/tortuise-script-wasm/src/lib.rs | 239 ++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 platforms/tortuise-script-wasm/src/lib.rs (limited to 'platforms/tortuise-script-wasm/src/lib.rs') diff --git a/platforms/tortuise-script-wasm/src/lib.rs b/platforms/tortuise-script-wasm/src/lib.rs new file mode 100644 index 0000000..6394b8f --- /dev/null +++ b/platforms/tortuise-script-wasm/src/lib.rs @@ -0,0 +1,239 @@ +#![no_std] + +use core::marker::PhantomData; + +use alloc::vec::Vec; +use serde::Serialize; + +extern crate alloc; + +pub mod allocator; +pub mod buffer; +pub mod entity; +pub mod input; +pub mod log; +pub mod process; +pub mod random; +pub mod scene; +pub mod script; +pub mod state; +pub mod storage; +pub mod time; + +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct ScriptContext<'a>(u64, PhantomData<&'a ()>); + +pub struct TortuiseScript { + pub start: fn(cx: ScriptContext), + pub update: fn(cx: ScriptContext), + pub end: fn(cx: ScriptContext), +} + +#[derive(Debug, Default, Clone)] +pub struct TortuiseScene { + pub scripts: &'static [&'static str], +} + +fn default_script(_cx: ScriptContext) {} + +impl Default for TortuiseScript { + fn default() -> Self { + Self { + start: default_script, + update: default_script, + end: default_script, + } + } +} + +impl TortuiseScript { + pub const fn empty() -> Self { + Self { + start: default_script, + update: default_script, + end: default_script, + } + } +} + +#[doc(hidden)] +pub fn to_vec(val: &T) -> &'static [u8] { + Vec::leak(messagepack_serde::to_vec(val).unwrap()) +} + +#[doc(hidden)] +pub type Lazy = spin::LazyLock; + +#[macro_export] +macro_rules! game { + { + name: $name: literal, + author: $author: literal, + tld: $tld: literal, + first_scene: $first_scene: literal, + scenes: { $($scene_name: literal => $scene: expr),* }, + scripts: { $($script_name: literal => $script: expr),* } + } => { + use alloc::boxed::Box; + + #[derive(Debug, Clone, serde::Serialize)] + struct GameManifest { + name: Box, + author: Box, + tld: Box, + first_scene: Box, + scene: Box<[ManifestScene]>, + script: Box<[Box]>, + } + + #[derive(Debug, Clone, serde::Serialize)] + struct ManifestScene { + name: Box, + scripts: Box<[Box]>, + } + + #[global_allocator] + static ALLOCATOR: $crate::allocator::TalcAllocator = + $crate::allocator::TalcAllocator::new(); + + static SCRIPTS: phf::Map<&'static str, $crate::TortuiseScript> = phf::phf_map! { + $($script_name => $script,)* + }; + + static MANIFEST: $crate::Lazy<&'static [u8]> = $crate::Lazy::new(|| { + fn slice_to_boxed_slice(names: &[&str]) -> Box<[Box]> { + names.iter().cloned().map(Into::into).collect() + } + + let manifest = GameManifest { + name: $name.into(), + author: $author.into(), + tld: $tld.into(), + first_scene: $first_scene.into(), + script: slice_to_boxed_slice([$($script_name,)*].as_slice()), + scene: Box::from([$(ManifestScene { + name: $scene_name.into(), + scripts: slice_to_boxed_slice($scene.scripts), + },)*].as_slice()) + }; + + $crate::to_vec(&manifest) + }); + + #[unsafe(no_mangle)] + extern "C" fn manifest_addr() -> *const u8 { + MANIFEST.as_ptr() + } + + #[unsafe(no_mangle)] + extern "C" fn manifest_len() -> usize { + MANIFEST.len() + } + + #[unsafe(no_mangle)] + extern "C" fn start_script(cx: $crate::ScriptContext) { + let _ = $crate::log::TortuiseLogger::init(); + let Some(script) = SCRIPTS.get(&*cx.current_script_name()) else { return }; + (script.start)(cx); + } + + #[unsafe(no_mangle)] + extern "C" fn update_script(cx: $crate::ScriptContext) { + let _ = $crate::log::TortuiseLogger::init(); + let Some(script) = SCRIPTS.get(&*cx.current_script_name()) else { return }; + (script.update)(cx); + } + + #[unsafe(no_mangle)] + extern "C" fn end_script(cx: $crate::ScriptContext) { + let _ = $crate::log::TortuiseLogger::init(); + let Some(script) = SCRIPTS.get(&*cx.current_script_name()) else { return }; + (script.end)(cx); + } + }; +} + +#[macro_export] +macro_rules! patch { + { + scenes: { $($scene_name: literal => $scene: expr),* }, + scripts: { $($script_name: literal => $script: expr),* } + } => { + use alloc::boxed::Box; + + #[derive(Debug, Clone, serde::Serialize)] + struct GameManifestPatch { + new_scenes: Box<[ManifestScene]>, + new_scripts: Box<[Box]>, + patched_scenes: Box<[ScenePatch]>, + } + + #[derive(Debug, Clone, serde::Serialize)] + struct ManifestScene { + name: Box, + scripts: Box<[Box]>, + } + + #[derive(Debug, Clone, serde::Serialize)] + struct ScenePatch { + add_scripts: Box<[Box]>, + remove_scripts: Box<[Box]>, + } + + #[global_allocator] + static ALLOCATOR: $crate::allocator::TalcAllocator = + $crate::allocator::TalcAllocator::new(); + + static SCRIPTS: phf::Map<&'static str, $crate::TortuiseScript> = phf::phf_map! { + $($script_name => $script,)* + }; + + static MANIFEST: $crate::Lazy<&'static [u8]> = $crate::Lazy::new(|| { + fn slice_to_boxed_slice(names: &[&str]) -> Box<[Box]> { + names.iter().cloned().map(Into::into).collect() + } + + let manifest = GameManifestPatch { + added_scripts: slice_to_boxed_slice([$($script_name,)*].as_slice()), + added_scenes: Box::from([$(ManifestScene { + name: $scene_name.into(), + scripts: slice_to_boxed_slice($scene.scripts), + },)*].as_slice()) + }; + + $crate::to_vec(&manifest) + }); + + #[unsafe(no_mangle)] + extern "C" fn manifest_addr() -> *const u8 { + MANIFEST.as_ptrhttps://botahamec.dev/cgit/shake/plain/shake() + } + + #[unsafe(no_mangle)] + extern "C" fn manifest_len() -> usize { + MANIFEST.len() + } + + #[unsafe(no_mangle)] + extern "C" fn start_script(cx: $crate::ScriptContext) { + let _ = $crate::log::TortuiseLogger::init(); + let Some(script) = SCRIPTS.get(&*cx.current_script_name()) else { return }; + (script.start)(cx); + } + + #[unsafe(no_mangle)] + extern "C" fn update_script(cx: $crate::ScriptContext) { + let _ = $crate::log::TortuiseLogger::init(); + let Some(script) = SCRIPTS.get(&*cx.current_script_name()) else { return }; + (script.update)(cx); + } + + #[unsafe(no_mangle)] + extern "C" fn end_script(cx: $crate::ScriptContext) { + let _ = $crate::log::TortuiseLogger::init(); + let Some(script) = SCRIPTS.get(&*cx.current_script_name()) else { return }; + (script.end)(cx); + } + }; +} -- cgit v1.3.1