diff --git a/crates/voltex_script/Cargo.toml b/crates/voltex_script/Cargo.toml index 6b5d7ad..3f71540 100644 --- a/crates/voltex_script/Cargo.toml +++ b/crates/voltex_script/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +voltex_math.workspace = true +voltex_ecs.workspace = true [build-dependencies] cc = "1" diff --git a/crates/voltex_script/src/engine_api.rs b/crates/voltex_script/src/engine_api.rs new file mode 100644 index 0000000..616e684 --- /dev/null +++ b/crates/voltex_script/src/engine_api.rs @@ -0,0 +1,117 @@ +use crate::ffi; +use crate::state::LuaState; +use voltex_ecs::World; +use voltex_ecs::Transform; +use voltex_math::Vec3; +use std::os::raw::c_int; + +/// Register engine API functions into the Lua state. +/// The World pointer is stored in a global "__voltex_world" as a light userdata. +pub fn register_engine_api(lua: &LuaState, world: *mut World) { + // Store world pointer as light userdata in global + unsafe { + ffi::lua_pushlightuserdata(lua.raw(), world as *mut std::ffi::c_void); + let name = std::ffi::CString::new("__voltex_world").unwrap(); + ffi::lua_setglobal(lua.raw(), name.as_ptr()); + } + + lua.register_fn("spawn_entity", lua_spawn_entity); + lua.register_fn("set_position", lua_set_position); + lua.register_fn("get_position", lua_get_position); + lua.register_fn("entity_count", lua_entity_count); +} + +/// Helper to retrieve the World pointer from the Lua registry global. +unsafe fn get_world(L: *mut ffi::lua_State) -> &'static mut World { + let name = std::ffi::CString::new("__voltex_world").unwrap(); + ffi::lua_getglobal(L, name.as_ptr()); + let ptr = ffi::lua_touserdata(L, -1) as *mut World; + ffi::lua_pop(L, 1); + &mut *ptr +} + +unsafe extern "C" fn lua_spawn_entity(L: *mut ffi::lua_State) -> c_int { + let world = get_world(L); + let entity = world.spawn(); + world.add(entity, Transform::new()); + ffi::lua_pushnumber(L, entity.id as f64); + 1 +} + +unsafe extern "C" fn lua_set_position(L: *mut ffi::lua_State) -> c_int { + let world = get_world(L); + let mut isnum = 0; + let id = ffi::lua_tonumberx(L, 1, &mut isnum) as u32; + let x = ffi::lua_tonumberx(L, 2, &mut isnum) as f32; + let y = ffi::lua_tonumberx(L, 3, &mut isnum) as f32; + let z = ffi::lua_tonumberx(L, 4, &mut isnum) as f32; + + let entity = voltex_ecs::Entity { id, generation: 0 }; + if let Some(t) = world.get_mut::(entity) { + t.position = Vec3::new(x, y, z); + } + 0 +} + +unsafe extern "C" fn lua_get_position(L: *mut ffi::lua_State) -> c_int { + let world = get_world(L); + let mut isnum = 0; + let id = ffi::lua_tonumberx(L, 1, &mut isnum) as u32; + + let entity = voltex_ecs::Entity { id, generation: 0 }; + if let Some(t) = world.get::(entity) { + ffi::lua_pushnumber(L, t.position.x as f64); + ffi::lua_pushnumber(L, t.position.y as f64); + ffi::lua_pushnumber(L, t.position.z as f64); + 3 + } else { + 0 + } +} + +unsafe extern "C" fn lua_entity_count(L: *mut ffi::lua_State) -> c_int { + let world = get_world(L); + ffi::lua_pushnumber(L, world.entity_count() as f64); + 1 +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::state::LuaState; + + #[test] + fn test_spawn_and_get_position() { + let lua = LuaState::new(); + let mut world = World::new(); + register_engine_api(&lua, &mut world as *mut World); + + lua.exec(" + local id = spawn_entity() + set_position(id, 5.0, 10.0, 15.0) + local x, y, z = get_position(id) + result_x = x + result_y = y + result_z = z + ").unwrap(); + + assert_eq!(lua.get_global_number("result_x"), Some(5.0)); + assert_eq!(lua.get_global_number("result_y"), Some(10.0)); + assert_eq!(lua.get_global_number("result_z"), Some(15.0)); + } + + #[test] + fn test_entity_count() { + let lua = LuaState::new(); + let mut world = World::new(); + register_engine_api(&lua, &mut world as *mut World); + + lua.exec(" + spawn_entity() + spawn_entity() + count = entity_count() + ").unwrap(); + + assert_eq!(lua.get_global_number("count"), Some(2.0)); + } +} diff --git a/crates/voltex_script/src/ffi.rs b/crates/voltex_script/src/ffi.rs index cce41b4..038576b 100644 --- a/crates/voltex_script/src/ffi.rs +++ b/crates/voltex_script/src/ffi.rs @@ -31,10 +31,12 @@ extern "C" { pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number); pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char; pub fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, n: c_int); + pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void); // Get pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number; pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char; + pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void; // Globals pub fn lua_getglobal(L: *mut lua_State, name: *const c_char) -> c_int; diff --git a/crates/voltex_script/src/lib.rs b/crates/voltex_script/src/lib.rs index 041dccd..01d19a0 100644 --- a/crates/voltex_script/src/lib.rs +++ b/crates/voltex_script/src/lib.rs @@ -1,6 +1,8 @@ pub mod ffi; pub mod state; pub mod bindings; +pub mod engine_api; pub use state::LuaState; pub use bindings::register_default_bindings; +pub use engine_api::register_engine_api;