use std::ffi::{c_char, c_int, c_void};
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GDExtensionInitializationLevel {
Core = 0,
Servers = 1,
Scene = 2,
Editor = 3,
MaxInitializationLevel = 4,
}
pub type GDExtensionBool = u8;
pub type GDExtensionClassLibraryPtr = *mut c_void;
pub type GDExtensionInterfaceGetProcAddress =
unsafe extern "C" fn(function_name: *const c_char) -> Option<unsafe extern "C" fn()>;
#[repr(C)]
#[derive(Debug)]
pub struct GDExtensionInitialization {
pub minimum_initialization_level: GDExtensionInitializationLevel,
pub userdata: *mut c_void,
pub initialize:
Option<extern "C" fn(userdata: *mut c_void, level: GDExtensionInitializationLevel)>,
pub deinitialize:
Option<extern "C" fn(userdata: *mut c_void, level: GDExtensionInitializationLevel)>,
}
pub type GDExtensionInitializationFunction = extern "C" fn(
get_proc_address: GDExtensionInterfaceGetProcAddress,
library: GDExtensionClassLibraryPtr,
initialization: *mut GDExtensionInitialization,
) -> GDExtensionBool;
#[repr(C)]
#[derive(Debug)]
pub struct GDExtensionGodotVersion {
pub major: u32,
pub minor: u32,
pub patch: u32,
pub string: *const c_char,
}
pub type LibgodotGDExtensionBindCallback = extern "C" fn(
get_proc_address: Option<GDExtensionInterfaceGetProcAddress>,
library: GDExtensionClassLibraryPtr,
initialization: *mut GDExtensionInitialization,
) -> c_int;
pub type LibgodotSceneCallback = extern "C" fn(scene_tree: *mut c_void);
extern "C" {
pub fn libgodot_init_resource();
pub fn libgodot_scene_load(scene: *mut c_void);
pub fn libgodot_is_scene_loadable() -> c_int;
pub fn libgodot_gdextension_bind(
initialization_bind: LibgodotGDExtensionBindCallback,
scene_function_bind: Option<LibgodotSceneCallback>,
);
pub fn godot_main(argc: c_int, argv: *const *const c_char) -> c_int;
}
#[derive(Debug)]
pub struct LibgodotRuntime {
initialized: bool,
}
impl LibgodotRuntime {
pub fn new() -> Self {
Self { initialized: false }
}
pub fn initialize(&mut self) -> Result<(), String> {
if self.initialized {
return Err("Runtime already initialized".to_string());
}
self.initialized = true;
Ok(())
}
pub fn run_main(&mut self, args: &[String]) -> Result<i32, String> {
if !self.initialized {
return Err("Runtime not initialized".to_string());
}
let c_strings: Vec<std::ffi::CString> = args
.iter()
.map(|s| std::ffi::CString::new(s.as_str()).unwrap())
.collect();
let c_args: Vec<*const c_char> = c_strings.iter().map(|s| s.as_ptr()).collect();
let result = unsafe { godot_main(c_args.len() as c_int, c_args.as_ptr()) };
Ok(result)
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
pub fn has_scene_tree(&self) -> bool {
self.initialized
}
}
impl Default for LibgodotRuntime {
fn default() -> Self {
Self::new()
}
}
#[allow(dead_code)]
extern "C" fn gdextension_init_wrapper(
_get_proc_addr: Option<GDExtensionInterfaceGetProcAddress>,
library: GDExtensionClassLibraryPtr,
init: *mut GDExtensionInitialization,
) -> c_int {
println!("GDExtension initialization callback called");
if let Some(init_ptr) = unsafe { init.as_mut() } {
init_ptr.minimum_initialization_level = GDExtensionInitializationLevel::Core;
init_ptr.userdata = library;
init_ptr.initialize = Some(default_initialize);
init_ptr.deinitialize = Some(default_deinitialize);
}
1 }
#[allow(dead_code)]
extern "C" fn scene_load_wrapper(scene_tree: *mut c_void) {
println!("Scene tree loaded: {:?}", scene_tree);
}
extern "C" fn default_initialize(_userdata: *mut c_void, level: GDExtensionInitializationLevel) {
println!("Initializing GDExtension level: {:?}", level);
}
extern "C" fn default_deinitialize(_userdata: *mut c_void, level: GDExtensionInitializationLevel) {
println!("Deinitializing GDExtension level: {:?}", level);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_runtime_creation() {
let runtime = LibgodotRuntime::new();
assert!(!runtime.is_initialized());
assert!(!runtime.has_scene_tree());
}
#[test]
fn test_gdextension_initialization_level() {
assert_eq!(GDExtensionInitializationLevel::Core as i32, 0);
assert_eq!(GDExtensionInitializationLevel::Scene as i32, 2);
}
}