Skip to content

update GDNative headers and API bindings, add associated constants #207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169,325 changes: 90,159 additions & 79,166 deletions bindings_generator/api.json

Large diffs are not rendered by default.

539 changes: 0 additions & 539 deletions bindings_generator/namespaces.json

This file was deleted.

14 changes: 7 additions & 7 deletions bindings_generator/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub struct Api {
impl Api {
pub fn new() -> Self {
let mut api = Api {
classes: serde_json::from_slice(get_api_json())
classes: serde_json::from_str(get_api_json())
.expect("Failed to parse the API description"),
api_underscore: Default::default(),
};
Expand Down Expand Up @@ -72,6 +72,7 @@ pub struct GodotClass {

pub methods: Vec<GodotMethod>,
pub enums: Vec<Enum>,
pub constants: HashMap<ConstantName, ConstantValue>,
}

impl GodotClass {
Expand All @@ -84,6 +85,9 @@ impl GodotClass {
}
}

pub type ConstantName = String;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why these type aliases are needed. Perhaps you wanted newtypes for type safety...?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just put type aliases so the HashMap is a bit easier to read meaning into. I did not really aim for strongly typed wrappers. I could make them newtypes if you think it's worth, I just preferred this over a comment :D

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see! I think the String should be pretty self-explanatory, but I agree that a bare i64 can be a bit hard to make sense of.

I don't think they have to be newtypes. Maybe ConstantValue can be an enum if you want, in case constants of other types get added in the future, but at this point it probably only adds complexity. I think it's fine either way.

pub type ConstantValue = i64;

#[derive(Deserialize, Debug)]
pub struct Enum {
pub name: String,
Expand Down Expand Up @@ -301,10 +305,6 @@ impl Ty {
}
}

pub fn get_api_json() -> &'static [u8] {
include_bytes!("../api.json")
}

pub fn get_namespaces_json() -> &'static [u8] {
include_bytes!("../namespaces.json")
pub fn get_api_json() -> &'static str {
include_str!("../api.json")
}
57 changes: 57 additions & 0 deletions bindings_generator/src/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,53 @@ pub struct {name} {{
Ok(())
}

pub fn generate_class_constants(output: &mut impl Write, class: &GodotClass) -> GeneratorResult {
if class.constants.is_empty() {
return Ok(());
}

writeln!(output, "/// Constants")?;
writeln!(output, "#[allow(non_upper_case_globals)]")?;
writeln!(output, "impl {} {{", class.name)?;

for (name, value) in &class.constants {
writeln!(
output,
" pub const {name}: i64 = {value};",
name = name,
value = value,
)?;
}

writeln!(output, "}}")?;
Ok(())
}

#[derive(Copy, Clone, PartialEq)]
struct EnumReference<'a> {
class: &'a str,
enum_name: &'a str,
enum_variant: &'a str,
}

const ENUM_VARIANTS_TO_SKIP: &[EnumReference<'static>] = &[
Copy link

@ghost ghost Sep 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to skip deprecated constants? It might not be very sustainable to manually maintain a list of exceptions, when there are so many classes and constants around. Maybe it's better to just leave it to Godot's documentation?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. I see it's required for Rust enum generation. But methods still take i64s, so these might not be as useful as the associated constants, that don't require this uniqueness?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The associated constants do exist as well, but most of the enums (all except the two that are special cased here) have distinct variants, so for me this is a matter of deprecated items and a single weird "Default" enum that I think should probably not be there in the first place.

That said I agree with you, enums are only a subset of the associated constants and have more limitations, but when these limitations are dealt with properly they are quite nice to use (being able to do an exhaustive match for example).

EnumReference {
class: "MultiplayerAPI",
enum_name: "RPCMode",
enum_variant: "RPC_MODE_SLAVE",
},
EnumReference {
class: "MultiplayerAPI",
enum_name: "RPCMode",
enum_variant: "RPC_MODE_SYNC",
},
EnumReference {
class: "TextureLayered",
enum_name: "Flags",
enum_variant: "FLAGS_DEFAULT",
},
];

pub fn generate_enum(output: &mut impl Write, class: &GodotClass, e: &Enum) -> GeneratorResult {
// TODO: check whether the start of the variant name is
// equal to the end of the enum name and if so don't repeat it
Expand All @@ -43,6 +90,16 @@ pub enum {class_name}{enum_name} {{"#,

for &(key, val) in &values {
// Use lowercase to test because of different CamelCase conventions (Msaa/MSAA, etc.).
let enum_ref = EnumReference {
class: class.name.as_str(),
enum_name: e.name.as_str(),
enum_variant: key.as_str(),
};

if ENUM_VARIANTS_TO_SKIP.contains(&enum_ref) {
continue;
}

let enum_name_without_mode = if e.name.ends_with("Mode") {
e.name[0..(e.name.len() - 4)].to_lowercase()
} else {
Expand Down
2 changes: 2 additions & 0 deletions bindings_generator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ fn generate_class_bindings(
generate_enum(output_types_impls, class, e)?;
}

generate_class_constants(output_types_impls, class)?;

writeln!(output_types_impls, "impl {} {{", class.name)?;

if class.singleton {
Expand Down
1 change: 0 additions & 1 deletion examples/hello_world/Main.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@ library = ExtResource( 1 )

[node name="Node" type="Node"]
script = SubResource( 1 )

1 change: 0 additions & 1 deletion examples/hello_world/default_env.tres
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,3 @@ sun_energy = 16.0
[resource]
background_mode = 2
background_sky = SubResource( 1 )

2 changes: 1 addition & 1 deletion examples/scene_create/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl SceneCreate {

// You need to parent the new scene under some node if you want it in the scene.
// We parent it under ourselves.
owner.add_child(Some(spatial.to_object()), false);
owner.add_child(Some(spatial.to_node()), false);
self.children_spawned += 1;
}
Err(err) => godot_print!("Could not instance Child : {:?}", err),
Expand Down
1 change: 1 addition & 0 deletions gdnative-core/src/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use super::*;
use crate::get_api;
use crate::sys;
use crate::sys::GodotApi;

use libc;
use std::ops::*;
Expand Down
3,418 changes: 0 additions & 3,418 deletions gdnative-core/src/internal.rs

This file was deleted.

4 changes: 2 additions & 2 deletions gdnative-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ mod free_on_drop;
mod generated;
pub mod init;
mod int32_array;
mod internal;
mod node_path;
#[doc(hidden)]
pub mod object;
Expand All @@ -72,7 +71,6 @@ pub use crate::free_on_drop::*;
pub use crate::generated::*;
pub use crate::geom::*;
pub use crate::int32_array::*;
pub use crate::internal::*;
pub use crate::node_path::*;
pub use crate::object::GodotObject;
pub use crate::object::Instanciable;
Expand All @@ -89,6 +87,8 @@ pub use crate::vector2_array::*;
pub use crate::vector3::*;
pub use crate::vector3_array::*;

pub use sys::GodotApi;

use std::mem;

#[doc(hidden)]
Expand Down
2 changes: 2 additions & 0 deletions gdnative-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ libc = "0.2"

[build-dependencies]
bindgen = "0.51.0"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
Loading