@@ -70,9 +70,13 @@ pub fn ensure_static_runtime_compatibility(
7070 // SAFETY: see above.
7171 let minor = unsafe { data_ptr. offset ( 1 ) . read ( ) } ;
7272 if minor == 0 {
73+ let data_ptr = get_proc_address as * const sys:: GDExtensionGodotVersion ; // Always v1 of the struct.
74+
7375 // SAFETY: at this point it's reasonably safe to say that we are indeed dealing with that version struct; read the whole.
74- let data_ptr = get_proc_address as * const sys:: GodotSysVersion ;
75- let runtime_version_str = unsafe { read_version_string ( & data_ptr. read ( ) ) } ;
76+ let runtime_version_str = unsafe {
77+ let data_ref = & * data_ptr;
78+ read_version_string ( data_ref. string )
79+ } ;
7680
7781 panic ! (
7882 "gdext was compiled against a newer Godot version: {static_version_str}\n \
@@ -99,7 +103,8 @@ pub fn ensure_static_runtime_compatibility(
99103 ) ;
100104
101105 if runtime_version < static_version {
102- let runtime_version_str = read_version_string ( & runtime_version_raw) ;
106+ // SAFETY: valid `runtime_version_raw`.
107+ let runtime_version_str = unsafe { read_version_string ( runtime_version_raw. string ) } ;
103108
104109 panic ! (
105110 "gdext was compiled against newer Godot version: {static_version_str}\n \
@@ -114,33 +119,75 @@ pub fn ensure_static_runtime_compatibility(
114119
115120pub unsafe fn runtime_version (
116121 get_proc_address : sys:: GDExtensionInterfaceGetProcAddress ,
117- ) -> sys:: GodotSysVersion {
122+ ) -> sys:: GDExtensionGodotVersion {
118123 let get_proc_address = get_proc_address. expect ( "get_proc_address unexpectedly null" ) ;
119124
120125 runtime_version_inner ( get_proc_address)
121126}
122127
128+ /// Generic helper to fetch and call a version function.
129+ ///
130+ /// # Safety
131+ /// - `get_proc_address` must be a valid function pointer from Godot.
132+ /// - The function pointer associated with `fn_name` must be valid, have signature `unsafe extern "C" fn(*mut V)` and initialize
133+ /// the version struct.
123134#[ deny( unsafe_op_in_unsafe_fn) ]
124- unsafe fn runtime_version_inner (
135+ unsafe fn fetch_version < V > (
125136 get_proc_address : unsafe extern "C" fn (
126137 * const std:: ffi:: c_char ,
127138 ) -> sys:: GDExtensionInterfaceFunctionPtr ,
128- ) -> sys:: GodotSysVersion {
129- // SAFETY: `self.0` is a valid `get_proc_address` pointer.
130- let get_godot_version = unsafe { get_proc_address ( sys:: c_str ( sys:: GET_GODOT_VERSION_SYS_STR ) ) } ; //.expect("get_godot_version unexpectedly null");
139+ fn_name : & std:: ffi:: CStr ,
140+ ) -> Option < V > {
141+ // SAFETY: `get_proc_address` is a valid function pointer.
142+ let fn_ptr = unsafe { get_proc_address ( fn_name. as_ptr ( ) ) } ;
143+ let fn_ptr = fn_ptr?;
144+
145+ // SAFETY: Caller guarantees correct signature (either GDExtensionInterfaceGetGodotVersion or GDExtensionInterfaceGetGodotVersion2).
146+ let caller: unsafe extern "C" fn ( * mut V ) = unsafe {
147+ std:: mem:: transmute :: < unsafe extern "C" fn ( ) , unsafe extern "C" fn ( * mut V ) > ( fn_ptr)
148+ } ;
149+
150+ let mut version = std:: mem:: MaybeUninit :: < V > :: zeroed ( ) ;
131151
132- // SAFETY: `GDExtensionInterfaceGetGodotVersion` is an `Option` of an `unsafe extern "C"` function pointer.
133- let get_godot_version =
134- crate :: unsafe_cast_fn_ptr!( get_godot_version as sys:: GetGodotSysVersion ) ;
152+ // SAFETY: `caller` is a valid function pointer from Godot and must be callable.
153+ unsafe { caller ( version. as_mut_ptr ( ) ) } ;
135154
136- let mut version = std:: mem:: MaybeUninit :: < sys:: GodotSysVersion > :: zeroed ( ) ;
155+ // SAFETY: The version function initializes `version`.
156+ Some ( unsafe { version. assume_init ( ) } )
157+ }
137158
138- // SAFETY: `get_proc_address` with "get_godot_version" does return a valid `GDExtensionInterfaceGetGodotVersion` pointer, and since we have a valid
139- // `get_proc_address` pointer then it must be callable.
140- unsafe { get_godot_version ( version. as_mut_ptr ( ) ) } ;
159+ #[ deny( unsafe_op_in_unsafe_fn) ]
160+ unsafe fn runtime_version_inner (
161+ get_proc_address : unsafe extern "C" fn (
162+ * const std:: ffi:: c_char ,
163+ ) -> sys:: GDExtensionInterfaceFunctionPtr ,
164+ ) -> sys:: GDExtensionGodotVersion {
165+ // Try get_godot_version first (available in all versions, unless Godot built with deprecated features).
166+
167+ // SAFETY: `get_proc_address` is valid, function has signature fn(*mut GDExtensionGodotVersion).
168+ if let Some ( version1) = unsafe { fetch_version ( get_proc_address, c"get_godot_version" ) } {
169+ return version1;
170+ }
171+
172+ // Fall back to get_godot_version2 for 4.5+ builds that have removed the original function.
173+ #[ cfg( since_api = "4.5" ) ]
174+ {
175+ // SAFETY: `get_proc_address` is valid, function has signature fn(*mut GDExtensionGodotVersion2).
176+ let version2: Option < sys:: GDExtensionGodotVersion2 > =
177+ unsafe { fetch_version ( get_proc_address, c"get_godot_version2" ) } ;
178+
179+ if let Some ( version2) = version2 {
180+ // Convert to old "common denominator" struct.
181+ return sys:: GDExtensionGodotVersion {
182+ major : version2. major ,
183+ minor : version2. minor ,
184+ patch : version2. patch ,
185+ string : version2. string ,
186+ } ;
187+ }
188+ }
141189
142- // SAFETY: `get_godot_version` initializes `version`.
143- unsafe { version. assume_init ( ) }
190+ panic ! ( "None of `get_godot_version`, `get_godot_version2` function pointers available" )
144191}
145192
146193pub unsafe fn load_interface (
0 commit comments