lotus_script_sys/
lib.rs

1use lotus_bindgen_macros::lotus_bindgen;
2use serde::{de::DeserializeOwned, Serialize};
3
4#[no_mangle]
5pub extern "C" fn allocate(size: u32) -> u32 {
6    unsafe {
7        std::alloc::alloc(std::alloc::Layout::from_size_align(size as usize, 8).unwrap()) as u32
8    }
9}
10
11#[no_mangle]
12pub extern "C" fn deallocate(ptr: u32, size: u32) {
13    unsafe {
14        std::alloc::dealloc(
15            ptr as *mut u8,
16            std::alloc::Layout::from_size_align(size as usize, 8).unwrap(),
17        )
18    }
19}
20
21extern "C" {
22    #[lotus_bindgen]
23    pub fn is_rc() -> bool;
24}
25
26pub mod assets {
27    #[link(wasm_import_module = "assets")]
28    extern "C" {
29        pub fn preload(id: u64);
30    }
31}
32
33pub mod time {
34    #[link(wasm_import_module = "time")]
35    extern "C" {
36        pub fn delta_f64() -> f64;
37        pub fn ticks_alive() -> u64;
38    }
39}
40
41pub mod log {
42    #[link(wasm_import_module = "log")]
43    extern "C" {
44        pub fn write(level: i32, message: u64);
45    }
46}
47
48pub mod messages {
49    #[link(wasm_import_module = "messages")]
50    extern "C" {
51        pub fn take() -> u64;
52        pub fn send(target: u64, message: u64);
53    }
54}
55
56pub mod textures {
57    #[link(wasm_import_module = "textures")]
58    extern "C" {
59        pub fn create(options: u64) -> u32;
60        pub fn add_action(texture: u32, options: u64);
61        pub fn get_pixel(texture: u32, x: u32, y: u32) -> u32;
62        pub fn apply_to(texture: u32, name: u64);
63        pub fn flush_actions(texture: u32) -> u32;
64        pub fn dispose(texture: u32);
65    }
66}
67
68pub mod var {
69    #[link(wasm_import_module = "var")]
70    extern "C" {
71        pub fn get_i64(name: u64) -> i64;
72        pub fn set_i64(name: u64, value: i64);
73        pub fn get_f64(name: u64) -> f64;
74        pub fn set_f64(name: u64, value: f64);
75        pub fn get_string(name: u64) -> u64;
76        pub fn set_string(name: u64, value: u64);
77        pub fn get_bool(name: u64) -> i32;
78        pub fn set_bool(name: u64, value: i32);
79        pub fn get_content_id(name: u64) -> u64;
80        pub fn set_content_id(name: u64, value: u64);
81    }
82}
83
84pub mod rand {
85    #[link(wasm_import_module = "rand")]
86    extern "C" {
87        pub fn f64() -> f64;
88        /// Generate a random u64 in the range `min` to `max` inclusive.
89        pub fn u64(min: u64, max: u64) -> u64;
90        pub fn seed(seed: u64);
91        pub fn random_seed();
92    }
93}
94
95pub mod gizmo {
96    #[link(wasm_import_module = "gizmo")]
97    extern "C" {
98        pub fn draw(gizmo: u64);
99    }
100}
101
102pub mod action {
103    #[link(wasm_import_module = "action")]
104    extern "C" {
105        pub fn register(action: u64);
106        pub fn state(action: u64) -> u64;
107    }
108}
109
110pub mod input {
111    #[link(wasm_import_module = "input")]
112    extern "C" {
113        pub fn mouse_delta() -> u64;
114    }
115}
116
117pub mod font {
118    #[link(wasm_import_module = "font")]
119    extern "C" {
120        pub fn bitmap_font_properties(font: u64) -> u64;
121
122        /// Returns: -1 if the font is not loaded.
123        /// Returns: >0 is the width of the text.
124        pub fn text_len(font: u64, text: u64, letter_spacing: i32) -> i32;
125    }
126}
127
128pub trait FromFfi {
129    type FfiType;
130    fn from_ffi(ffi: Self::FfiType) -> Self;
131}
132
133impl FromFfi for String {
134    type FfiType = u64;
135    fn from_ffi(ffi: Self::FfiType) -> Self {
136        FfiObject::from_packed(ffi).deserialize()
137    }
138}
139
140enum FfiObjectData {
141    Boxed(Box<[u8]>),
142    Raw(*mut u8, usize),
143}
144
145impl FfiObjectData {
146    fn as_slice(&self) -> &[u8] {
147        match self {
148            Self::Boxed(data) => data,
149            Self::Raw(ptr, len) => unsafe { std::slice::from_raw_parts(*ptr, *len) },
150        }
151    }
152}
153
154impl Drop for FfiObject {
155    fn drop(&mut self) {
156        match self.data {
157            FfiObjectData::Boxed(_) => {}
158            FfiObjectData::Raw(ptr, len) => unsafe {
159                std::alloc::dealloc(ptr, std::alloc::Layout::from_size_align(len, 8).unwrap())
160            },
161        }
162    }
163}
164
165pub struct FfiObject {
166    data: FfiObjectData,
167}
168
169impl FfiObject {
170    pub fn new<T: Serialize>(value: &T) -> Self {
171        let data = rmp_serde::to_vec_named(value)
172            .expect("Failed to serialize value")
173            .into_boxed_slice();
174
175        Self {
176            data: FfiObjectData::Boxed(data),
177        }
178    }
179
180    pub fn deserialize<T: DeserializeOwned>(&self) -> T {
181        rmp_serde::from_slice(self.data.as_slice()).expect("Failed to deserialize value")
182    }
183
184    pub fn packed(&self) -> u64 {
185        let ptr = self.data.as_slice().as_ptr() as u32;
186        let len = self.data.as_slice().len() as u32;
187
188        let mut packed = [0u8; 8];
189        packed[..4].copy_from_slice(&ptr.to_be_bytes());
190        packed[4..].copy_from_slice(&len.to_be_bytes());
191
192        u64::from_be_bytes(packed)
193    }
194
195    pub fn from_packed(packed: u64) -> Self {
196        let packed = packed.to_be_bytes();
197        let ptr = u32::from_be_bytes(packed[..4].try_into().unwrap());
198        let len = u32::from_be_bytes(packed[4..].try_into().unwrap());
199
200        Self {
201            data: FfiObjectData::Raw(ptr as *mut u8, len as usize),
202        }
203    }
204}