Skip to content

Conversation

@ValorZard
Copy link
Contributor

add multiplayer pong

@Bromeon
Copy link
Member

Bromeon commented Jun 27, 2025

Thanks a lot 👍

We are a bit behind on adding new demos, with #1 and #4 still pending review. We also need to decide what we want to showcase in demos, I'm not sure if more than one with networking is justifiable regarding maintenance... but we can discuss this 🙂

Are the config files (.gitattributes, .gitignore, .editorconfig) present in the original example, or what's the reason for adding them?

Furthermore, this repo is currently under MPL-2 license, but your example is MIT.

@Bromeon Bromeon added the new-demo New demo or content added label Jun 27, 2025
@ValorZard
Copy link
Contributor Author

Yeah so, I propose adding demo over #1, mostly because it’s MUCH smaller and easier to maintain. Since it’s only 5 rust files, it should be a lot easier to deal with breaking changes

(also the gitingore stuff was in the original project iirc)

@Yarwin
Copy link
Contributor

Yarwin commented Oct 9, 2025

Pong :O!

image

Sorry that it took so long 😅, shouldn't happen again

As for demo itself – yeah, I think it is better than #1 because of much much much smaller scope

@ValorZard
Copy link
Contributor Author

ValorZard commented Oct 10, 2025

I think the game has issues with ending the game specifically, but I'm not sure if thats really a blocker or not
Nevermind, that was a change I made and then reverted

// player's screen but not this one.
if ball_pos.x < 0.0 {
let args = vslice![false];
parent.rpc("update_score", args);
Copy link
Contributor

Choose a reason for hiding this comment

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

if game ends, update_score calls back to Ball::stop, causing double bind error.

There are multiple ways to deal with it, I think base_mut() could be a good band-aid fix here.

something along the lines of

        // Allows re-entrancy – required if a game stops and we need to reset our ball.
        let mut guard = self.base_mut();
        (...)
                parent.rpc("update_score", args);
                guard.rpc("reset_ball", args);

Copy link
Contributor

@Yarwin Yarwin left a comment

Choose a reason for hiding this comment

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

Getting there!

In general:

  • Use OnEditor instead of Option if you assume that given value won't be null – i.e. if all the uses of given value unwrap() it
  • types in signal closures can be inferred (i.e. | this | instead of | this: &mut Self |. RustRover might get lost in the closure atm, but will properly infer the type, so it is no biggie; works fine with rust-analyzer.
  • Use connect_other_mut instead of connect_other_gd if you bind() one-liner in the closure

Comment on lines 9 to 27
pub struct Pong {
score_left: i32,
score_right: i32,
#[export]
player1: Option<Gd<Area2D>>,
#[export]
player2: Option<Gd<Area2D>>,
#[export]
score_left_node: Option<Gd<Label>>,
#[export]
score_right_node: Option<Gd<Label>>,
#[export]
winner_left: Option<Gd<Label>>,
#[export]
winner_right: Option<Gd<Label>>,
#[export]
exit_game: Option<Gd<Button>>,
#[export]
ball: Option<Gd<Ball>>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as in other places – we assume that these won't be null (we don't match on Option, we only unwrap it) thus OnEditor would be better choice; player1 is unused (maybe we should call player2 a client_player or something more descriptive)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

player1 isnt unused because thats the left side of the board, and player2 is the right side.
I think the host is always on the right side if I'm not mistake

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep, but we enforce/set it using only the player_2/client_paddle 🤔

        if self.base().get_multiplayer().unwrap().is_server() {
            // For the server, give control of player 2 to the other peer.
            let authority = self.base().get_multiplayer().unwrap().get_peers()[0];
            self.player2.set_multiplayer_authority(authority);
        } else {
            // For the client, give control of player 2 to itself.
            let authority = self.base().get_multiplayer().unwrap().get_unique_id();
            self.player2.set_multiplayer_authority(authority);
        }
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah, k, ill just rename it then.

config/name="Multiplayer Pong"
run/main_scene="uid://f85s2avde6r4"
config/features=PackedStringArray("4.5", "GL Compatibility")
config/icon="uid://71q6yteydysw"
Copy link
Contributor

Choose a reason for hiding this comment

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

nonexisting icon 😅

Copy link
Contributor Author

@ValorZard ValorZard Oct 10, 2025

Choose a reason for hiding this comment

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

hm, this seems to not exist on my end. Weird Godot UID shenanigans i guess

@Yarwin Yarwin added this pull request to the merge queue Oct 10, 2025
Merged via the queue into godot-rust:master with commit f2e7f1d Oct 10, 2025
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new-demo New demo or content added

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants