Connected client cannot send custom object via RPC

Godot Version

4.2

Question

Hi, I’m new to godot and multiplayer/networking, so sorry for simple questions.

So my problem is that a connected client cannot send via RPC an instance of Player object (My custom class extended by Node2D)

public partial class Player : Node2D {
    public string username;
    public long id;

    public Player(long id, string username) {
        this.id = id;
        this.username = username;
    }
}

When I host and call rpc locally, everything works, but when connected client casts rpc there is error:
System.Runtime.CompilerServices.CastHelpers.ChkCastAny(System.Void*, object): System.InvalidCastException: Unable to cast object of type ‘Godot.EncodedObjectAsId’ to type ‘Player’.
<C++ Error> System.InvalidCastException

Here is sample code:

public void HostGame(string username) {
	peer.CreateServer(27105);
	Multiplayer.MultiplayerPeer = peer;

	player = new(Multiplayer.GetUniqueId(), username);

	Rpc("AddPlayer", player);
}

public async void JoinServer(string username) {
	peer.CreateClient("localhost", 27105);
	Multiplayer.MultiplayerPeer = peer;

	player = new(Multiplayer.GetUniqueId(), username);

	await ToSignal(GetTree().CreateTimer(1f), SceneTreeTimer.SignalName.Timeout);
		Rpc("AddPlayer", player);
	}

// Error here when connected client casts this RPC
[Rpc(MultiplayerApi.RpcMode.AnyPeer, CallLocal = true)]
private void AddPlayer(Player player) {
	players.Add(player);
	GD.Print(players);
}

I tried googling this problem and I found that I need to add [Tool] attribute to my Player class, but it doesn’t work (I build project after adding C# attributes like [Export] etc.)

I think I cannot cast objects? only primitives like int, long, string etc? What if I need to send more complex data, like some chunk of procedurally generated map?

Thanks for taking your time and trying to help me :slight_smile:

Serialization may be the topic for you. Variants like int, long, and string are already handled.

RPCs will not serialize objects or callables.

There’s more on serialization here.

1 Like

Hello! I had this issue long ago as well and opened a proposal Implement sending custom structs as RPC parameters · Issue #6216 · godotengine/godot-proposals · GitHub

As for the solution, you must serialize Player down to primitives (or PackedArrays) in order to input them as RPC parameters, and deserialize them from the receiver to construct a Player.

If you don’t need to construct a Player, but simply perform some operation on a Player existing on all clients, you should just sync the Player IDs, and inside the RPC simply send the Player ID.

2 Likes

Thanks for answer! So I have to use primitive values or do some boilerplate to create arrays like PackedInt32Array etc.? What about godot Resource class? They are lightweight but I got the same error

Cannot cast Godot.EncodedObjectAsId to type PlayerResource :confused:

The docs says that:

E 0:00:04:0307 object System.Runtime.CompilerServices.CastHelpers.ChkCastAny(System.Void*, object): System.InvalidCastException: Unable to cast object of type ‘Godot.EncodedObjectAsId’ to type ‘PlayerResource’.

Resources are serialized so they can be stored in the disk. Trying to send them directly will get the cast error because you’re sending the object rather than the serialized data.

2 Likes

ah alright, thank you for taking your time and answering! Have a nice day/night :slight_smile:

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.