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, including a table from a
96/// plugin of another Rusteal version.
97///
98/// # Safety
99///
100/// `api_table` is null or points to a table the plugin keeps alive for as long
101/// as the library is loaded; at least its `version` field must be readable.
102pub unsafe fn init(api_table: *const ffi::RustealApiTable) -> *const ffi::RustealRustCallbacks {
103    std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
104        if api_table.is_null() {
105            return std::ptr::null();
106        }
107        // The plugin writes its own version in the first field, readable
108        // whatever the rest of the table looks like. Another version means
109        // another table: refuse it before touching anything else.
110        if unsafe { (*api_table).version } != ffi::RUSTEAL_VERSION {
111            return std::ptr::null();
112        }
113
114        // Delegate API table storage to rusteal-core.
115        runtime::init_api(api_table);
116
117        log_greeting();
118        register_all_classes();
119        &__CALLBACKS as *const ffi::RustealRustCallbacks
120    }))
121    .unwrap_or(std::ptr::null())
122}
123
124/// Log the Rusteal greeting message.
125fn log_greeting() {
126    let msg = "[Rusteal] Rust side initialized";
127    let bytes = msg.as_bytes();
128    unsafe {
129        runtime::ffi_dispatch::logging_log(0, bytes.as_ptr(), bytes.len() as u32);
130    }
131}
132
133/// Register all Rust-defined UE classes via inventory auto-registration.
134fn register_all_classes() {
135    runtime::reify_registry::register_all_from_inventory();
136}
137
138/// Shut down the Rusteal runtime. Called by the `entry!()` generated `rusteal_shutdown`.
139pub fn shutdown() {
140    let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
141        (__CALLBACKS.on_shutdown)();
142    }));
143}
144
145/// Generates DLL exports for the Rusteal runtime entry points.
146///
147/// Place this at the top of your cdylib crate's `lib.rs`:
148/// ```ignore
149/// rusteal_runtime::entry!();
150/// ```
151#[macro_export]
152macro_rules! entry {
153    () => {
154        mod __rusteal_native_entry {
155            #[unsafe(no_mangle)]
156            pub extern "C" fn rusteal_init(
157                api_table: *const $crate::ffi::RustealApiTable,
158            ) -> *const $crate::ffi::RustealRustCallbacks {
159                // SAFETY: called by the plugin with its own static table.
160                unsafe { $crate::init(api_table) }
161            }
162
163            #[unsafe(no_mangle)]
164            pub extern "C" fn rusteal_shutdown() {
165                $crate::shutdown()
166            }
167
168            /// The Rusteal version this library was built with, encoded; the
169            /// plugin compares it with its own before calling `rusteal_init`.
170            #[unsafe(no_mangle)]
171            pub extern "C" fn rusteal_version() -> u32 {
172                $crate::ffi::RUSTEAL_VERSION
173            }
174        }
175    };
176}