use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock, RwLock};
pub struct ClassRegistration {
pub register: fn(&uika_ffi::UikaApiTable),
pub finalize: fn(&uika_ffi::UikaApiTable),
}
inventory::collect!(ClassRegistration);
pub struct ClassFunctionRegistration {
pub register_functions: fn(&uika_ffi::UikaApiTable),
}
inventory::collect!(ClassFunctionRegistration);
pub fn register_all_from_inventory(table: &uika_ffi::UikaApiTable) {
let mut class_count = 0u32;
for reg in inventory::iter::<ClassRegistration> {
(reg.register)(table);
class_count += 1;
}
let mut func_reg_count = 0u32;
for freg in inventory::iter::<ClassFunctionRegistration> {
(freg.register_functions)(table);
func_reg_count += 1;
}
for reg in inventory::iter::<ClassRegistration> {
(reg.finalize)(table);
}
if !table.logging.is_null() {
let total_funcs = func_registry().read().unwrap().len();
let msg = format!(
"[Uika] register_all_from_inventory: {} classes, {} impl blocks, {} function callbacks",
class_count, func_reg_count, total_funcs,
);
let bytes = msg.as_bytes();
unsafe {
((*table.logging).log)(0, bytes.as_ptr(), bytes.len() as u32);
}
}
}
use uika_ffi::UObjectHandle;
pub struct RustTypeInfo {
pub name: &'static str,
pub construct_fn: fn() -> *mut u8,
pub drop_fn: unsafe fn(*mut u8),
}
type ReifyFunctionCallback = Arc<dyn Fn(UObjectHandle, *mut u8, *mut u8) + Send + Sync>;
static TYPE_REGISTRY: OnceLock<Mutex<HashMap<u64, RustTypeInfo>>> = OnceLock::new();
static FUNC_REGISTRY: OnceLock<RwLock<Vec<ReifyFunctionCallback>>> = OnceLock::new();
static INSTANCE_DATA: OnceLock<RwLock<HashMap<usize, InstanceEntry>>> = OnceLock::new();
struct InstanceEntry {
data: *mut u8,
type_id: u64,
}
unsafe impl Send for InstanceEntry {}
unsafe impl Sync for InstanceEntry {}
fn type_registry() -> &'static Mutex<HashMap<u64, RustTypeInfo>> {
TYPE_REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
}
fn func_registry() -> &'static RwLock<Vec<ReifyFunctionCallback>> {
FUNC_REGISTRY.get_or_init(|| RwLock::new(Vec::new()))
}
fn instance_data() -> &'static RwLock<HashMap<usize, InstanceEntry>> {
INSTANCE_DATA.get_or_init(|| RwLock::new(HashMap::new()))
}
pub fn register_type(type_id: u64, info: RustTypeInfo) {
type_registry()
.lock()
.unwrap()
.insert(type_id, info);
}
pub fn register_function<F>(f: F) -> u64
where
F: Fn(UObjectHandle, *mut u8, *mut u8) + Send + Sync + 'static,
{
let mut vec = func_registry().write().unwrap();
let id = vec.len() as u64;
vec.push(Arc::new(f));
id
}
pub fn construct_instance(obj: UObjectHandle, type_id: u64) {
let types = type_registry().lock().unwrap();
let Some(info) = types.get(&type_id) else {
if crate::api::is_api_initialized() {
let msg = format!("[Uika] construct_instance: unknown type_id {type_id}");
let bytes = msg.as_bytes();
unsafe {
let api = crate::api::api();
((*api.logging).log)(1, bytes.as_ptr(), bytes.len() as u32);
}
}
return;
};
let data = (info.construct_fn)();
drop(types);
let key = obj.0 as usize;
instance_data()
.write()
.unwrap()
.insert(key, InstanceEntry { data, type_id });
}
pub fn drop_instance(obj: UObjectHandle, _type_id: u64) {
let key = obj.0 as usize;
let entry = instance_data().write().unwrap().remove(&key);
if let Some(entry) = entry {
let types = type_registry().lock().unwrap();
if let Some(info) = types.get(&entry.type_id) {
unsafe {
(info.drop_fn)(entry.data);
}
}
}
}
pub fn invoke_function(callback_id: u64, obj: UObjectHandle, params: *mut u8) {
let key = obj.0 as usize;
let rust_data = instance_data()
.read()
.unwrap()
.get(&key)
.map(|e| e.data)
.unwrap_or(std::ptr::null_mut());
let func = {
let vec = func_registry().read().unwrap();
vec.get(callback_id as usize).cloned()
};
if let Some(func) = func {
func(obj, rust_data, params);
} else if crate::api::is_api_initialized() {
let vec_len = func_registry().read().unwrap().len();
let msg = format!(
"[Uika] invoke_function: callback_id {} not found (registry size = {})",
callback_id, vec_len,
);
let bytes = msg.as_bytes();
unsafe {
let api = crate::api::api();
((*api.logging).log)(1, bytes.as_ptr(), bytes.len() as u32);
}
}
}
pub fn clear_all() {
if let Some(instances) = INSTANCE_DATA.get() {
let mut map = instances.write().unwrap();
let types = type_registry().lock().unwrap();
for (_, entry) in map.drain() {
if let Some(info) = types.get(&entry.type_id) {
unsafe {
(info.drop_fn)(entry.data);
}
}
}
drop(types);
}
if let Some(funcs) = FUNC_REGISTRY.get() {
funcs.write().unwrap().clear();
}
if let Some(types) = TYPE_REGISTRY.get() {
types.lock().unwrap().clear();
}
}
pub fn get_instance_data(obj: UObjectHandle) -> *mut u8 {
let key = obj.0 as usize;
instance_data()
.read()
.unwrap()
.get(&key)
.map(|e| e.data)
.unwrap_or(std::ptr::null_mut())
}