Skip to content

Commit 8ba828f

Browse files
committed
Move version detection out of user crate
This should make the `custom-godot` feature silence the warning properly. Currently, the version requirement is hard-coded, but it should be easy to later introduce a mechanism where the versions requirements can be collected from `-sys` and `-bindings` and combined, for better usefulness and less maintenance work in the future. Close #1023
1 parent 9050211 commit 8ba828f

File tree

6 files changed

+151
-25
lines changed

6 files changed

+151
-25
lines changed

gdnative-core/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rust-version = "1.63"
1515
default = []
1616
gd-test = []
1717
type-tag-fallback = []
18+
custom-godot = []
1819

1920
[dependencies]
2021
gdnative-sys = { path = "../gdnative-sys", version = "=0.11.3" }
@@ -26,11 +27,12 @@ atomic-take = "1"
2627
bitflags = "1"
2728
glam = "0.22"
2829
indexmap = "1"
30+
inventory = { version = "0.3", optional = true }
2931
libc = "0.2"
3032
once_cell = "1"
3133
parking_lot = "0.12"
34+
semver = "1"
3235
serde = { version = "1", features = ["derive"], optional = true }
33-
inventory = { version = "0.3", optional = true }
3436

3537
[dev-dependencies]
3638
gdnative = { path = "../gdnative" } # for doc-tests

gdnative-core/src/init/diagnostics.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
//! any problems were found. This is so that they can be freely improved without compatibility
66
//! concerns.
77
8+
mod godot_version_mismatch;
89
mod missing_manual_registration;
910
mod missing_suggested_diagnostics;
1011

12+
#[doc(inline)]
13+
pub use godot_version_mismatch::godot_version_mismatch;
14+
1115
#[doc(inline)]
1216
pub use missing_manual_registration::missing_manual_registration;
1317

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use semver::{BuildMetadata, Prerelease};
2+
3+
use crate::core_types::Dictionary;
4+
use crate::core_types::FromVariant;
5+
use crate::object::ownership::Unique;
6+
use crate::private::{get_api, EngineMethodTable};
7+
8+
/// Checks the version number of the host Godot instance to see if it matches the generated API.
9+
/// Returns `true` if the test isn't applicable, or if no mismatch was found.
10+
#[inline]
11+
pub fn godot_version_mismatch() -> bool {
12+
let ret = check_godot_version_mismatch();
13+
if !ret {
14+
godot_warn!(concat!(
15+
"gdnative-core: GDNative version mismatches may lead to subtle bugs, undefined behavior or crashes at runtime.\n",
16+
"Apply the 'custom-godot' feature if you want to use current godot-rust with another Godot engine version.",
17+
));
18+
}
19+
20+
ret
21+
}
22+
23+
#[cfg(feature = "custom-godot")]
24+
fn check_godot_version_mismatch() -> bool {
25+
true
26+
}
27+
28+
#[cfg(not(feature = "custom-godot"))]
29+
fn check_godot_version_mismatch() -> bool {
30+
use semver::VersionReq;
31+
32+
let version = if let Some(version) = godot_version() {
33+
version
34+
} else {
35+
godot_warn!("gdnative-core: failed to get version info from the engine.");
36+
return false;
37+
};
38+
39+
let version_req = VersionReq::parse("~3.5.1").unwrap();
40+
41+
if version_req.matches(&version) {
42+
true
43+
} else {
44+
godot_warn!("This godot-rust version is only compatible with Godot `{version_req}`; detected version `{version}`.");
45+
false
46+
}
47+
}
48+
49+
fn godot_version() -> Option<semver::Version> {
50+
let version = unsafe {
51+
let api = get_api();
52+
let engine = (api.godot_global_get_singleton)(b"Engine\0".as_ptr() as *mut _);
53+
54+
let mut dictionary = sys::godot_dictionary::default();
55+
56+
(api.godot_method_bind_ptrcall)(
57+
EngineMethodTable::get(api).get_version_info,
58+
engine,
59+
[].as_mut_ptr() as *mut _,
60+
&mut dictionary as *mut _ as *mut _,
61+
);
62+
63+
Dictionary::<Unique>::from_sys(dictionary)
64+
};
65+
66+
let major = u64::from_variant(&version.get("major")?).ok()?;
67+
let minor = u64::from_variant(&version.get("minor")?).ok()?;
68+
let patch = u64::from_variant(&version.get("patch")?).ok()?;
69+
70+
let pre = version
71+
.get("status")
72+
.and_then(|v| {
73+
let s = String::from_variant(&v).ok()?;
74+
if s == "stable" {
75+
return None;
76+
}
77+
78+
let s = s.chars().map(sanitize_for_semver).collect::<String>();
79+
Some(Prerelease::new(&s).expect("string sanitized"))
80+
})
81+
.unwrap_or(Prerelease::EMPTY);
82+
83+
let build = {
84+
let mut build_metadata = String::new();
85+
let mut sep = false;
86+
87+
if let Some(build_name) = version
88+
.get("build")
89+
.and_then(|v| String::from_variant(&v).ok())
90+
{
91+
build_metadata.extend(build_name.chars().map(sanitize_for_semver));
92+
sep = true;
93+
};
94+
95+
if let Some(hash) = version
96+
.get("hash")
97+
.and_then(|v| String::from_variant(&v).ok())
98+
{
99+
if sep {
100+
build_metadata.push('.');
101+
}
102+
build_metadata.extend(hash.chars().map(sanitize_for_semver));
103+
};
104+
105+
if build_metadata.is_empty() {
106+
BuildMetadata::EMPTY
107+
} else {
108+
BuildMetadata::new(&build_metadata).expect("build metadata sanitized")
109+
}
110+
};
111+
112+
Some(semver::Version {
113+
major,
114+
minor,
115+
patch,
116+
pre,
117+
build,
118+
})
119+
}
120+
121+
fn sanitize_for_semver(s: char) -> char {
122+
if s.is_ascii_alphanumeric() {
123+
s
124+
} else {
125+
'-'
126+
}
127+
}

gdnative-core/src/init/macros.rs

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,30 +35,15 @@ macro_rules! godot_nativescript_init {
3535
return;
3636
}
3737

38-
// Compatibility warning if using in-house Godot version (not applicable for custom ones)
39-
#[cfg(not(feature = "custom-godot"))]
40-
{
41-
use $crate::core_types::Variant;
42-
43-
let engine = gdnative::api::Engine::godot_singleton();
44-
let info = engine.get_version_info();
45-
46-
if info.get("major").expect("major version") != Variant::new(3)
47-
|| info.get("minor").expect("minor version") != Variant::new(5)
48-
|| info.get("patch").expect("patch version") < Variant::new(1) {
49-
let string = info.get("string").expect("version str").to::<String>().expect("version str type");
50-
$crate::log::godot_warn!(
51-
"This godot-rust version is only compatible with Godot >= 3.5.1 and < 3.6; detected version {}.\n\
52-
GDNative mismatches may lead to subtle bugs, undefined behavior or crashes at runtime.\n\
53-
Apply the 'custom-godot' feature if you want to use current godot-rust with another Godot engine version.",
54-
string
55-
);
56-
}
57-
}
58-
5938
$crate::private::report_panics("nativescript_init", || {
60-
$crate::init::auto_register($crate::init::InitHandle::new(handle, $crate::init::InitLevel::AUTO));
61-
$callback($crate::init::InitHandle::new(handle, $crate::init::InitLevel::USER));
39+
$crate::init::auto_register($crate::init::InitHandle::new(
40+
handle,
41+
$crate::init::InitLevel::AUTO,
42+
));
43+
$callback($crate::init::InitHandle::new(
44+
handle,
45+
$crate::init::InitLevel::USER,
46+
));
6247

6348
$crate::init::diagnostics::missing_suggested_diagnostics();
6449
});
@@ -103,6 +88,8 @@ macro_rules! godot_gdnative_init {
10388
return;
10489
}
10590

91+
$crate::init::diagnostics::godot_version_mismatch();
92+
10693
$crate::private::report_panics("gdnative_init", || {
10794
let init_info = $crate::init::InitializeInfo::new(options);
10895
$callback(&init_info)

gdnative-core/src/private.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub unsafe fn bind_api(options: *mut sys::godot_gdnative_init_options) -> bool {
3535
ObjectMethodTable::get(get_api());
3636
ReferenceMethodTable::get(get_api());
3737
NativeScriptMethodTable::get(get_api());
38+
EngineMethodTable::get(get_api());
3839

3940
true
4041
}
@@ -326,3 +327,8 @@ make_method_table!(struct NativeScriptMethodTable for NativeScript {
326327
set_library,
327328
new,
328329
});
330+
331+
// `Engine` is known to the engine as `_Engine`.
332+
make_method_table!(struct EngineMethodTable for _Engine {
333+
get_version_info,
334+
});

gdnative/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ rust-version = "1.63"
1717
# Public
1818
default = []
1919
async = ["gdnative-async"]
20-
custom-godot = ["gdnative-bindings/custom-godot"]
20+
custom-godot = ["gdnative-bindings/custom-godot", "gdnative-core/custom-godot"]
2121
formatted = ["gdnative-bindings/formatted", "gdnative-bindings/one-class-one-file"]
2222
ptrcall = ["gdnative-bindings/ptrcall"]
2323
serde = ["gdnative-core/serde"]

0 commit comments

Comments
 (0)