Skip to main content

rusteal_runtime/
lib.rs

1// rusteal-runtime: what a game crate depends on. It carries the safe runtime,
2// the FFI contract, the engine's reflection flags and the proc macros, and
3// generates the library entry points through `rusteal_runtime::entry!()`.
4//
5// The Unreal types themselves are not here: they are generated per project by
6// `rusteal build` into the game's own `bindings` crate, whose features decide
7// which engine modules exist.
8
9// Re-exports for proc macro path resolution and user access.
10pub use rusteal_ffi as ffi;
11pub use rusteal_core as runtime;
12pub use rusteal_ue_flags as ue_flags;
13pub use rusteal_macros::{uclass, uclass_impl};
14
15// For proc macro generated inventory::submit! invocations.
16#[doc(hidden)]
17pub extern crate inventory as __inventory;
18
19pub mod prelude;
20
21// Re-export glam for convenience.
22pub use glam;
23
24// ---------------------------------------------------------------------------
25// Callbacks (shared between init and entry! macro)
26// ---------------------------------------------------------------------------
27
28extern "C" fn real_drop_rust_instance(
29    handle: ffi::UObjectHandle,
30    type_id: u64,
31    _rust_data: *mut u8,
32) {
33    runtime::ffi_boundary((), || {
34        runtime::reify_registry::drop_instance(handle, type_id);
35    });
36}
37
38extern "C" fn real_invoke_rust_function(
39    callback_id: u64,
40    obj: ffi::UObjectHandle,
41    params: *mut u8,
42) {
43    runtime::ffi_boundary((), || {
44        runtime::reify_registry::invoke_function(callback_id, obj, params);
45    });
46}
47
48extern "C" fn real_invoke_delegate_callback(callback_id: u64, params: *mut u8) {
49    runtime::ffi_boundary((), || {
50        runtime::delegate_registry::invoke(callback_id, params);
51    });
52}
53
54extern "C" fn real_construct_rust_instance(
55    obj: ffi::UObjectHandle,
56    type_id: u64,
57    _is_cdo: bool,
58) {
59    runtime::ffi_boundary((), || {
60        runtime::reify_registry::construct_instance(obj, type_id);
61    });
62}
63
64extern "C" fn real_on_shutdown() {
65    runtime::ffi_boundary((), || {
66        runtime::reify_registry::clear_all();
67        runtime::delegate_registry::clear_all();
68        runtime::pinned::clear_all();
69    });
70}
71
72extern "C" fn real_notify_pinned_destroyed(handle: ffi::UObjectHandle) {
73    runtime::ffi_boundary((), || {
74        runtime::pinned::notify_pinned_destroyed(handle);
75    });
76}
77
78#[doc(hidden)]
79pub static __CALLBACKS: ffi::RustealRustCallbacks = ffi::RustealRustCallbacks {
80    drop_rust_instance: real_drop_rust_instance,
81    invoke_rust_function: real_invoke_rust_function,
82    invoke_delegate_callback: real_invoke_delegate_callback,
83    on_shutdown: real_on_shutdown,
84    construct_rust_instance: real_construct_rust_instance,
85    notify_pinned_destroyed: real_notify_pinned_destroyed,
86};
87
88// ---------------------------------------------------------------------------
89// Init / Shutdown (called from entry!() generated code)
90// ---------------------------------------------------------------------------
91
92/// Initialize the Rusteal runtime. Called by the `entry!()` generated `rusteal_init`.
93///
94/// Stores the API table, registers all reified classes, and returns the
95/// callback table pointer. Returns null on failure.
96pub fn init(api_table: *const ffi::RustealApiTable) -> *const ffi::RustealRustCallbacks {
97    std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
98        if api_table.is_null() {
99            return std::ptr::null();
100        }
101
102        // Delegate API table storage to rusteal-core.
103        runtime::init_api(api_table);
104
105        log_greeting();
106        register_all_classes();
107        &__CALLBACKS as *const ffi::RustealRustCallbacks
108    }))
109    .unwrap_or(std::ptr::null())
110}
111
112/// Log the Rusteal greeting message.
113fn log_greeting() {
114    let msg = "[Rusteal] Rust side initialized";
115    let bytes = msg.as_bytes();
116    unsafe {
117        runtime::ffi_dispatch::logging_log(0, bytes.as_ptr(), bytes.len() as u32);
118    }
119}
120
121/// Register all Rust-defined UE classes via inventory auto-registration.
122fn register_all_classes() {
123    runtime::reify_registry::register_all_from_inventory();
124}
125
126/// Shut down the Rusteal runtime. Called by the `entry!()` generated `rusteal_shutdown`.
127pub fn shutdown() {
128    let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
129        (__CALLBACKS.on_shutdown)();
130    }));
131}
132
133/// Generates DLL exports for the Rusteal runtime entry points.
134///
135/// Place this at the top of your cdylib crate's `lib.rs`:
136/// ```ignore
137/// rusteal_runtime::entry!();
138/// ```
139#[macro_export]
140macro_rules! entry {
141    () => {
142        mod __rusteal_native_entry {
143            #[unsafe(no_mangle)]
144            pub extern "C" fn rusteal_init(
145                api_table: *const $crate::ffi::RustealApiTable,
146            ) -> *const $crate::ffi::RustealRustCallbacks {
147                $crate::init(api_table)
148            }
149
150            #[unsafe(no_mangle)]
151            pub extern "C" fn rusteal_shutdown() {
152                $crate::shutdown()
153            }
154        }
155    };
156}