#![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<T: Serialize>(val: &T) -> &'static [u8] {
Vec::leak(messagepack_serde::to_vec(val).unwrap())
}
#[doc(hidden)]
pub type Lazy<T> = spin::LazyLock<T>;
#[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<str>,
author: Box<str>,
tld: Box<str>,
first_scene: Box<str>,
scene: Box<[ManifestScene]>,
script: Box<[Box<str>]>,
}
#[derive(Debug, Clone, serde::Serialize)]
struct ManifestScene {
name: Box<str>,
scripts: Box<[Box<str>]>,
}
#[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<str>]> {
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<str>]>,
patched_scenes: Box<[ScenePatch]>,
}
#[derive(Debug, Clone, serde::Serialize)]
struct ManifestScene {
name: Box<str>,
scripts: Box<[Box<str>]>,
}
#[derive(Debug, Clone, serde::Serialize)]
struct ScenePatch {
add_scripts: Box<[Box<str>]>,
remove_scripts: Box<[Box<str>]>,
}
#[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<str>]> {
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);
}
};
}
|