Skip to content

api discomfort: MultiplayerApi.set_multiplayer_peer() requires manual upcast #796

@onkoe

Description

@onkoe

It's confusing to alter Godot's MultiplayerAPI on the Rust side of things.

In GDScript, you can freely assign the MultiplayerAPI a MultiplayerPeer, which lets you change things like the port, communication type, and networking privacy (WAN/LAN). MultiplayerPeer is inherited by the specialized peer types, like ENetMultiplayerPeer and WebRTCMultiplayerPeer.

So, networking in Godot looks just like the documentation:

const PORT: int = 1234
const MAX_CLIENTS: int = 16

# grab the API
var multiplayer_api: MultiplayerAPI = get_tree().get_multiplayer()

# make a server
var peer: ENetMultiplayerPeer = ENetMultiplayerPeer.new()
peer.create_server(PORT, MAX_CLIENTS)

# hand it to the global MultiplayerAPI
multiplayer_api.multiplayer_peer = peer 

That works great in GDScript! However, Rust isn't happy when you try to do that.

use godot::{
    engine::{ENetMultiplayerPeer, MultiplayerPeer},
    prelude::*,
};

#[derive(GodotClass)]
#[class(base = Node, init)]
struct SomeNode {
    base: Base<Node>,
}

#[godot_api]
impl INode for SomeNode {
    fn ready(&mut self) {
        const PORT: u16 = 1234;
        const _MAX_CLIENTS: u8 = 16;

        // grab the multiplayer api
        let multiplayer_api = self
            .base_mut()
            .get_tree()
            .unwrap()
            .get_multiplayer()
            .unwrap();

        // make a server
        let mut peer: Gd<ENetMultiplayerPeer> = ENetMultiplayerPeer::new_gd();
        peer.create_server(PORT.into());

        // unrelated, but two quick notes about `peer.create_server(...)`:
        // - why does it always return a `crate::prelude::Error`? why isn't the error in a result, like `Result<(), Error>`?
        // - does it not support specifying the `max_clients`/channels/bandwidth props?

        // hand it to the global MultiplayerAPI
        multiplayer_api.set_multiplayer_peer(peer); // COMPILE ERROR!

        // the following also have compile errors:
        multiplayer_api.set_multiplayer_peer(peer.into());
        multiplayer_api.set_multiplayer_peer(peer.cast());
        multiplayer_api.set_multiplayer_peer(peer.cast::<MultiplayerPeer>());
        multiplayer_api.set_multiplayer_peer(MultiplayerPeer::from(peer));

        // but this works!
        multiplayer_api.set_multiplayer_peer(peer.upcast());
    }

MultiplayerApi.set_multiplayer_peer() expects a MultiplayerPeer instead of any inherited node.

In other words, set_multiplayer_peer() would preferably take some kind of "impl MultiplayerPeer" over a MultiplayerPeer, as without it, you're left doing an upcast(), which doesn't appear to be documented for the type.

error[E0308]: mismatched types
   --> src/lib.rs:55:50
    |
55  |             multiplayer_api.set_multiplayer_peer(peer); // COMPILE ERROR!
    |                             -------------------- ^^^^ expected `Gd<MultiplayerPeer>`, found `Gd<ENetMultiplayerPeer>`
    |                             |
    |                             arguments to this method are incorrect
    |
    = note: expected struct `godot::prelude::Gd<godot::classes::MultiplayerPeer>`
               found struct `godot::prelude::Gd<godot::classes::ENetMultiplayerPeer>`

Fixing this across the many types of this workspace might be incompatible with the current method of resolving Inherits<T>. I can understand the difficulty - there isn't a form of class inheritance in Rust, and it's awkward to generate a hacky stateful trait for each type that requires inheritance.

Nonetheless, I think taking an impl Inherits<MultiplayerPeer> would suffice in this method and many others. I'm happy to attempt fixing this if it's something accessible to a first-time contributor.

Otherwise, a small documentation example would help a lot for multiplayer features in general, as the inheritance + getter-setter patterns for these types are somewhat obfuscated. Thank you for the incredible crate! :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    c: engineGodot classes (nodes, resources, ...)quality-of-lifeNo new functionality, but improves ergonomics/internals

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions