godot: RPC error is unclear when the function is missing from local script.

Godot version

4.0 alpha 2

System information

Windows 10

Issue description

Attempting to perform RPC call from client to server, or from server to client results in respectively: “connected, Unable to get the RPC configuration for the function”, and “connected: unable to get the RPC configuration for the function”.

According to my knowledge, the code presented in “Steps to reproduce” should be valid, and result in RPC calls making their way to their destination.

player_connected is being fired, which would indicate that player connection to the server is working, just not the RPC’s themselves.

Steps to reproduce

Server Code:

extends Node

const PORT = 5000
const MAX_PLAYERS = 200

func _ready():
	var server = ENetMultiplayerPeer.new()
	server.create_server(PORT, MAX_PLAYERS)
	multiplayer.peer_connected.connect(self.player_connected)
	multiplayer.peer_disconnected.connect(self.player_disconnected)
	
	multiplayer.set_multiplayer_peer(server)
	print("Server Started at port %s" % PORT)

func player_connected(id):
	print("Player " + str(id) + " connected to Server")
	rpc_id(id, &"testing_to_client")

func player_disconnected(id):
	print("Player " + str(id) + " left the Server")

@rpc(any_peer)
func send_hello():
	print("Hello there!")

Error:

E 0:00:01:0919   player_connected: Unable to get the RPC configuration for the function "testing_to_client" at path: "/root/World". This happens when the method is not marked for RPCs.
  <C++ Error>    Condition "config.name == StringName()" is true.
  <C++ Source>   scene/multiplayer/scene_rpc_interface.cpp:462 @ rpcp()
  <Stack Trace>  Server.gd:19 @ player_connected()

Client Code:

extends Node

func _ready():
	var client = ENetMultiplayerPeer.new()
	client.create_client("127.0.0.1", 5000)
	
	multiplayer.connected_to_server.connect(self.connected)
	
	multiplayer.set_multiplayer_peer(client)

func connected():
	print("connected to game server")
	rpc_id(1, &"send_hello")

@rpc
func testing_to_client():
	print("works")

Error:

E 0:00:00:0942   connected: Unable to get the RPC configuration for the function "send_hello" at path: "/root/World". This happens when the method is not marked for RPCs.
  <C++ Error>    Condition "config.name == StringName()" is true.
  <C++ Source>   scene/multiplayer/scene_rpc_interface.cpp:462 @ rpcp()
  <Stack Trace>  Client.gd:13 @ connected()

Minimal reproduction project

No response

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 2
  • Comments: 24 (4 by maintainers)

Commits related to this issue

Most upvoted comments

I was convinced (and still am) that consistently using (multiple) RPCs which mismatch from client and server is a very rare case

Is this actually true though? I imagine any client-server architecture would have plenty of RPCs that are intended to be one way. i.e. from Client to Server only, or vice versa.

I don’t recall it being this way in 3.x,

No, it was changed in the PR mentioned above (#35522).

The system already relied on client and server nodes having the same position in tree, now it also also rely on server and client having the same function names in those scripts.

More specifically, the implementation does not send the StringName at all, just an ID which depends on the function name (or more correctly the position in the ordered RPC list for that node).

Note that this is also in line with the new syntax of doing my_func.rpc().

As mentioned before, I didn’t like this idea too much originally, but I was convinced (and still am) that consistently using (multiple) RPCs which mismatch from client and server is a very rare case, and even in that case forcing to define the function locally makes the code more readable. E.g.:

extends Node

const PORT = 5000
const MAX_PLAYERS = 200

func _ready():
	var server = ENetMultiplayerPeer.new()
	server.create_server(PORT, MAX_PLAYERS)
	multiplayer.peer_connected.connect(self.player_connected)
	multiplayer.peer_disconnected.connect(self.player_disconnected)
	
	multiplayer.set_multiplayer_peer(server)
	print("Server Started at port %s" % PORT)

func player_connected(id):
	print("Player " + str(id) + " connected to Server")
	testing_to_client.rpc_id(id)

func player_disconnected(id):
	print("Player " + str(id) + " left the Server")

@rpc(any_peer)
func send_hello():
	print("Hello there!")

@rpc
func testing_to_client():
	# Only implemented in the client
	pass

The RPC functions must be defined in both client and server. What the error is telling you is that you need to define send_hello and testing_to_client in both client and server. In general, both scripts must have the same RPC functions with the same name.

This seems wrong (on an end-user level, not optimization level). I don’t recall it being this way in 3.x, 'nor does it make sense imo. Now, admittingly, all of my games use a listen server approach, so I wouldn’t even notice anyway to be honest. I could be wrong on this front as the script would contain both functions anyway. That said, a very common convention is to prefix a function with server or client to represent the direction it is going or where the function actually resides (on the server or on the client). I wouldn’t expect for clients to have server_update_client_name in their scripts, as that is something that should only exist on the server. So you’d end up doing rpc_id(1, "server_update_client_name"). Assuming a dedicated server setup.

With the quoted reply, it sounds to be as though the rpc takes the StringName and assumes it is the calling and destination function name. (Not saying that is how it is done mind you)

I wrote my comment before you posted and then went back to work - I went back and read through the issue. I understand the optimization gains from it, but I am not a fan of implementing boilerplate. I find readability to be subjective as well. Functions will end up existing just to exist, which is not ideal. I don’t have a solution to have both worlds currently, other than letting people assign ids in the rpc annotation, then if it is not set, it is automatically generated. But that is just an off the cuff suggestion with little thought put into it.

Maybe it would be possible to do an fifth RPC attribute (like @rpc(by_name)), where those Functions are called by the Godot 3.4 RPC-Method?

The @rpc annotation goes before the function definitions, if you don’t want to define the function, you cannot use the annotation. Using the annotation on the remote peer does not provide any information to the local one on how to call those functions (reliable, unreliable, authority, etc).

If you want to specify RPCs without a function (thus losing the benefits of calling RPCs via my_function.rpc()), you can use Node.rpc_config .

func _init():
    rpc_config("server_only_function", {rpc_mode = MultiplayerAPI.RPC_MODE_ANY_PEER})

You can then call rpc("server_only_function", arg1, arg2).

But it’s really not faster then doing:

@rpc func server_only_function(arg1, arg2): pass

which also gives you the benefit of parameter typing (catching errors in the client), and rpc names “spell check” (if you misspell e.g. server_only_functionZ.rpc you’ll get a break-point in the client, because the function doesn’t exists).

Turns out the error I was getting was because you have to have all rpc methods on both the client and server even if you don’t rpc one of the methods.

So if I had these two methods on the server, but the client only had TestMethod1

So the server would look like this:

@rpc("any_peer") 
func TestMethod1(arg1, arg2):
	pass

@rpc("any_peer") 
func TestMethod2(arg1):
	pass

client would look like this:

@rpc
func TestMethod1(arg1, arg2):
	pass

And then if you do this on the client rpc_id(1, “TestMethod1”, “test1”, “test2”)

You will get this error: The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: .

Strange.

100% Wonky and confusing. Ill even say harder to learn than previous godot versions.

The RPC functions must be defined in both client and server. What the error is telling you is that you need to define send_hello and testing_to_client in both client and server. In general, both scripts must have the same RPC functions with the same name.

On the other end, there should also be an error telling you the RPC configuration doesn’t match, so if that doesn’t happen, that is a bug.

@Faless, could you explain this in more detail, please? I’ve seen other people being confused by this situation too, and now that you mention that the declaration of RPC functions has to be done on both server and client, isn’t it a potential security risk for multiplayer games, since you need to have your server code on the client? As much as it’s only a duplication, it also means you need to have more code in your scripts, and adjust your 3.x networking workflow to this new approach. What was the reasoning behind the change from one side declaration of RPC functions to this if I may ask?

The RPC functions must be defined in both client and server. What the error is telling you is that you need to define send_hello and testing_to_client in both client and server. In general, both scripts must have the same RPC functions with the same name.

On the other end, there should also be an error telling you the RPC configuration doesn’t match, so if that doesn’t happen, that is a bug.

@Kun-guru thank you so SO much for the comment and extensive example (on a close issue no less)! People like you make the Godot community!

Nah - it isn’t a deal breaker for me, just wonky in my opinion.

@DuendeBrek2 @Duroxxigar I have closed the issue via #57876 , but if you feel this design decision is a deal-breaker for you please open a proposal here so we can keep the conversation going.

I don’t recall it being this way in 3.x,

No, it was changed in the PR mentioned above (#35522).

The system already relied on client and server nodes having the same position in tree, now it also also rely on server and client having the same function names in those scripts.

More specifically, the implementation does not send the StringName at all, just an ID which depends on the function name (or more correctly the position in the ordered RPC list for that node).

Note that this is also in line with the new syntax of doing my_func.rpc().

As mentioned before, I didn’t like this idea too much originally, but I was convinced (and still am) that consistently using (multiple) RPCs which mismatch from client and server is a very rare case, and even in that case forcing to define the function locally makes the code more readable. E.g.:

I’m really not sure what to think about it. This seems like a major forceful workflow change. I remember that adjusting to the scene trees needing to be the same position and name was sometimes tricky, and this seems like additional … overhead? What I can think of the top of my head right now, is that alongside having to sometimes do ghost nodes for the sake of scene tree server-client mirroring, you will also add an additional section, probably on the bottom of every networked scene named something alongside “empty function declarations”, and that’s also additional place in which you could make a mistake and make the code work in an unexpected way (if the @rpc won’t be replicated correctly? Do you need @rpc to have same arguments if you do empty function on either server or client, or just @rpc without brackets?)

As much as it grants performance gain, shouldn’t it be made an optional thing, similar to how static casting works, due to the code practices adjustment it requires you to make?