use core::{
alloc::Layout,
marker::PhantomData,
ops::{Deref, DerefMut},
};
use alloc::{
alloc::{alloc, dealloc},
borrow::Cow,
string::String,
};
use serde::{Deserialize, Serialize};
use crate::ScriptContext;
mod tortuise {
#[link(wasm_import_module = "tortuise")]
unsafe extern "C" {
pub unsafe fn entity_size(context: u64, name: *const u8, name_len: usize) -> usize;
pub unsafe fn load_entity(context: u64, name: *const u8, name_len: usize, offset: *mut u8);
pub unsafe fn save_entity(
context: u64,
name: *const u8,
name_len: usize,
offset: *const u8,
size: usize,
);
}
}
fn load_entity<'de, T: Deserialize<'de>>(context: u64, name: &str) -> Option<T> {
unsafe {
let size = tortuise::entity_size(context, name.as_ptr(), name.len());
let Ok(layout) = Layout::array::<u8>(size) else {
log::error!("Failed to create layout for size: {size}");
panic!();
};
let ptr = alloc(layout);
tortuise::load_entity(context, name.as_ptr(), name.len(), ptr);
let data = core::slice::from_raw_parts(ptr, size);
let data = messagepack_serde::from_slice(data).ok()?;
dealloc(ptr, layout);
Some(data)
}
}
fn save_entity<T: Serialize>(context: u64, name: &str, state: &T) {
unsafe {
let data = messagepack_serde::to_vec(state).unwrap();
tortuise::save_entity(
context,
name.as_ptr(),
name.len(),
data.as_ptr(),
data.len(),
);
}
}
pub struct Entity<T> {
name: Cow<'static, str>,
default: fn() -> T,
}
pub struct EntityRef<'entity, T> {
entity: PhantomData<&'entity Entity<T>>,
data: T,
}
pub struct EntityMut<'entity, T: Serialize> {
context: u64,
entity: &'entity Entity<T>,
data: T,
}
impl<'de, T: Serialize + Deserialize<'de>> Entity<T> {
pub const fn new(name: &'static str, default: fn() -> T) -> Self {
Self {
name: Cow::Borrowed(name),
default,
}
}
pub const fn from_string(name: String, default: fn() -> T) -> Self {
Self {
name: Cow::Owned(name),
default,
}
}
pub fn get(&self, context: &ScriptContext) -> EntityRef<'_, T> {
EntityRef {
entity: PhantomData,
data: load_entity(context.0, &self.name).unwrap_or_else(self.default),
}
}
pub fn get_mut(&mut self, context: &ScriptContext) -> EntityMut<'_, T> {
EntityMut {
context: context.0,
entity: self,
data: load_entity(context.0, &self.name).unwrap_or_else(self.default),
}
}
}
impl<T> Deref for EntityRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: Serialize> Deref for EntityMut<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: Serialize> DerefMut for EntityMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl<T: Serialize> Drop for EntityMut<'_, T> {
fn drop(&mut self) {
save_entity(self.context, &self.entity.name, &self.data);
}
}
|