Skip to content
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

Add the ability to make reentrant &mut self calls #501

Merged
merged 7 commits into from
Jan 4, 2024
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
39 changes: 39 additions & 0 deletions .github/workflows/full-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,45 @@ jobs:
run: cargo test $GDEXT_FEATURES ${{ matrix.rust-extra-args }}


miri-test:
name: miri-test
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4

- name: "Install Rust"
uses: ./.github/composite/rust
with:
rust: nightly
components: miri

- name: "Setup Miri"
run: cargo miri setup

- name: "Compile tests"
run: cargo miri test -p godot-cell --no-run

- name: "Test stacked borrows"
run: cargo miri test -p godot-cell

- name: "Test tree borrows"
run: MIRIFLAGS="-Zmiri-tree-borrows" cargo miri test -p godot-cell

proptest:
name: proptest
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4

- name: "Install Rust"
uses: ./.github/composite/rust

- name: "Compile tests"
run: cargo test -p godot-cell --features="proptest" --no-run

- name: "Test"
run: cargo test -p godot-cell --features="proptest"

# For complex matrix workflow, see https://stackoverflow.com/a/65434401
godot-itest:
name: godot-itest (${{ matrix.name }})
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
"godot-ffi",
"godot-core",
"godot-macros",
"godot-cell",
"godot",

# Godot integration
Expand Down
20 changes: 10 additions & 10 deletions examples/dodge-the-creeps/rust/src/hud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,53 +15,53 @@ impl Hud {

#[func]
pub fn show_message(&self, text: GString) {
let mut message_label = self.base.get_node_as::<Label>("MessageLabel");
let mut message_label = self.base().get_node_as::<Label>("MessageLabel");
message_label.set_text(text);
message_label.show();

let mut timer = self.base.get_node_as::<Timer>("MessageTimer");
let mut timer = self.base().get_node_as::<Timer>("MessageTimer");
timer.start();
}

pub fn show_game_over(&self) {
self.show_message("Game Over".into());

let mut timer = self.base.get_tree().unwrap().create_timer(2.0).unwrap();
timer.connect("timeout".into(), self.base.callable("show_start_button"));
let mut timer = self.base().get_tree().unwrap().create_timer(2.0).unwrap();
timer.connect("timeout".into(), self.base().callable("show_start_button"));
}

#[func]
fn show_start_button(&mut self) {
let mut message_label = self.base.get_node_as::<Label>("MessageLabel");
let mut message_label = self.base().get_node_as::<Label>("MessageLabel");
message_label.set_text("Dodge the\nCreeps!".into());
message_label.show();

let mut button = self.base.get_node_as::<Button>("StartButton");
let mut button = self.base().get_node_as::<Button>("StartButton");
button.show();
}

#[func]
pub fn update_score(&self, score: i64) {
let mut label = self.base.get_node_as::<Label>("ScoreLabel");
let mut label = self.base().get_node_as::<Label>("ScoreLabel");

label.set_text(score.to_string().into());
}

#[func]
fn on_start_button_pressed(&mut self) {
let mut button = self.base.get_node_as::<Button>("StartButton");
let mut button = self.base().get_node_as::<Button>("StartButton");
button.hide();

// Note: this works only because `start_game` is a deferred signal.
// This method keeps a &mut Hud, and start_game calls Main::new_game(), which itself accesses this Hud
// instance through Gd<Hud>::bind_mut(). It will try creating a 2nd &mut reference, and thus panic.
// Deferring the signal is one option to work around it.
self.base.emit_signal("start_game".into(), &[]);
self.base_mut().emit_signal("start_game".into(), &[]);
}

#[func]
fn on_message_timer_timeout(&self) {
let mut message_label = self.base.get_node_as::<Label>("MessageLabel");
let mut message_label = self.base().get_node_as::<Label>("MessageLabel");
message_label.hide()
}
}
Expand Down
30 changes: 15 additions & 15 deletions examples/dodge-the-creeps/rust/src/main_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ pub struct Main {
impl Main {
#[func]
fn game_over(&mut self) {
let mut score_timer = self.base.get_node_as::<Timer>("ScoreTimer");
let mut mob_timer = self.base.get_node_as::<Timer>("MobTimer");
let mut score_timer = self.base().get_node_as::<Timer>("ScoreTimer");
let mut mob_timer = self.base().get_node_as::<Timer>("MobTimer");

score_timer.stop();
mob_timer.stop();

let mut hud = self.base.get_node_as::<Hud>("Hud");
let mut hud = self.base().get_node_as::<Hud>("Hud");
hud.bind_mut().show_game_over();

self.music().stop();
Expand All @@ -39,16 +39,16 @@ impl Main {

#[func]
pub fn new_game(&mut self) {
let start_position = self.base.get_node_as::<Marker2D>("StartPosition");
let mut player = self.base.get_node_as::<player::Player>("Player");
let mut start_timer = self.base.get_node_as::<Timer>("StartTimer");
let start_position = self.base().get_node_as::<Marker2D>("StartPosition");
let mut player = self.base().get_node_as::<player::Player>("Player");
let mut start_timer = self.base().get_node_as::<Timer>("StartTimer");

self.score = 0;

player.bind_mut().start(start_position.get_position());
start_timer.start();

let mut hud = self.base.get_node_as::<Hud>("Hud");
let mut hud = self.base().get_node_as::<Hud>("Hud");
let hud = hud.bind_mut();
hud.update_score(self.score);
hud.show_message("Get Ready".into());
Expand All @@ -58,8 +58,8 @@ impl Main {

#[func]
fn on_start_timer_timeout(&self) {
let mut mob_timer = self.base.get_node_as::<Timer>("MobTimer");
let mut score_timer = self.base.get_node_as::<Timer>("ScoreTimer");
let mut mob_timer = self.base().get_node_as::<Timer>("MobTimer");
let mut score_timer = self.base().get_node_as::<Timer>("ScoreTimer");
mob_timer.start();
score_timer.start();
}
Expand All @@ -68,14 +68,14 @@ impl Main {
fn on_score_timer_timeout(&mut self) {
self.score += 1;

let mut hud = self.base.get_node_as::<Hud>("Hud");
let mut hud = self.base().get_node_as::<Hud>("Hud");
hud.bind_mut().update_score(self.score);
}

#[func]
fn on_mob_timer_timeout(&mut self) {
let mut mob_spawn_location = self
.base
.base()
.get_node_as::<PathFollow2D>("MobPath/MobSpawnLocation");

let mut mob_scene = self.mob_scene.instantiate_as::<RigidBody2D>();
Expand All @@ -91,7 +91,7 @@ impl Main {

mob_scene.set_rotation(direction);

self.base.add_child(mob_scene.clone().upcast());
self.base_mut().add_child(mob_scene.clone().upcast());

let mut mob = mob_scene.cast::<mob::Mob>();
let range = {
Expand All @@ -102,7 +102,7 @@ impl Main {

mob.set_linear_velocity(Vector2::new(range, 0.0).rotated(real::from_f32(direction)));

let mut hud = self.base.get_node_as::<Hud>("Hud");
let mut hud = self.base().get_node_as::<Hud>("Hud");
hud.connect("start_game".into(), mob.callable("on_start_game"));
}

Expand Down Expand Up @@ -132,7 +132,7 @@ impl INode for Main {
// If the resource does not exist or has an incompatible type, this panics.
// There is also try_load() if you want to check whether loading succeeded.
self.mob_scene = load("res://Mob.tscn");
self.music = Some(self.base.get_node_as("Music"));
self.death_sound = Some(self.base.get_node_as("DeathSound"));
self.music = Some(self.base().get_node_as("Music"));
self.death_sound = Some(self.base().get_node_as("DeathSound"));
}
}
6 changes: 3 additions & 3 deletions examples/dodge-the-creeps/rust/src/mob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ pub struct Mob {
impl Mob {
#[func]
fn on_visibility_screen_exited(&mut self) {
self.base.queue_free();
self.base_mut().queue_free();
}

#[func]
fn on_start_game(&mut self) {
self.base.queue_free();
self.base_mut().queue_free();
}
}

Expand All @@ -37,7 +37,7 @@ impl IRigidBody2D for Mob {

fn ready(&mut self) {
let mut sprite = self
.base
.base()
.get_node_as::<AnimatedSprite2D>("AnimatedSprite2D");

sprite.play();
Expand Down
22 changes: 11 additions & 11 deletions examples/dodge-the-creeps/rust/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@ impl Player {

#[func]
fn on_player_body_entered(&mut self, _body: Gd<PhysicsBody2D>) {
self.base.hide();
self.base.emit_signal("hit".into(), &[]);
self.base_mut().hide();
self.base_mut().emit_signal("hit".into(), &[]);

let mut collision_shape = self
.base
.base()
.get_node_as::<CollisionShape2D>("CollisionShape2D");

collision_shape.set_deferred("disabled".into(), true.to_variant());
}

#[func]
pub fn start(&mut self, pos: Vector2) {
self.base.set_global_position(pos);
self.base.show();
self.base_mut().set_global_position(pos);
self.base_mut().show();

let mut collision_shape = self
.base
.base()
.get_node_as::<CollisionShape2D>("CollisionShape2D");

collision_shape.set_disabled(false);
Expand All @@ -52,14 +52,14 @@ impl IArea2D for Player {
}

fn ready(&mut self) {
let viewport = self.base.get_viewport_rect();
let viewport = self.base().get_viewport_rect();
self.screen_size = viewport.size;
self.base.hide();
self.base_mut().hide();
}

fn process(&mut self, delta: f64) {
let mut animated_sprite = self
.base
.base()
.get_node_as::<AnimatedSprite2D>("AnimatedSprite2D");

let mut velocity = Vector2::new(0.0, 0.0);
Expand Down Expand Up @@ -101,11 +101,11 @@ impl IArea2D for Player {
}

let change = velocity * real::from_f64(delta);
let position = self.base.get_global_position() + change;
let position = self.base().get_global_position() + change;
let position = Vector2::new(
position.x.clamp(0.0, self.screen_size.x),
position.y.clamp(0.0, self.screen_size.y),
);
self.base.set_global_position(position);
self.base_mut().set_global_position(position);
}
}
14 changes: 14 additions & 0 deletions godot-cell/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "godot-cell"
version = "0.1.0"
edition = "2021"
rust-version = "1.70"
license = "MPL-2.0"
keywords = ["gamedev", "godot", "engine", "ffi"]
categories = ["game-engines", "graphics"]

[features]
proptest = ["dep:proptest"]

[dependencies]
proptest = { version = "1.4.0", optional = true }
Loading
Loading