Quick answer: RPC calls in Godot silently fail when the function is missing the @rpc annotation, the node path doesn’t match on all peers, or the multiplayer authority is misconfigured. Add @rpc("any_peer", "reliable") to your function, ensure identical scene trees across peers, and verify the ENet connection is established before calling rpc().
You’ve set up ENetMultiplayerPeer, connected a client to the server, and called my_function.rpc()—but nothing happens on the other side. No error in the console, no crash, just silence. RPC failures in Godot’s multiplayer system are notoriously quiet, which makes them frustrating to debug. The good news is that the problem almost always falls into one of a few categories, and each has a straightforward fix.
The @rpc Annotation
In Godot 4, the @rpc annotation replaced the old remote, puppet, and master keywords. If your function doesn’t have an @rpc annotation, calling .rpc() on it will silently do nothing. There is no runtime error or warning—the call is simply discarded.
The annotation syntax is:
@rpc("mode", "transfer_mode", "call_local_setting", channel)
# Examples:
@rpc("any_peer", "reliable", "call_local")
func take_damage(amount: int):
health -= amount
@rpc("authority", "unreliable")
func update_position(pos: Vector3):
global_position = pos
The parameters control who can call the function and how:
- Mode:
"authority"means only the node’s multiplayer authority can call this RPC."any_peer"means any connected peer can call it. If a non-authority peer tries to call an"authority"RPC, the call is silently dropped. - Transfer mode:
"reliable"guarantees delivery (like TCP)."unreliable"sends without confirmation (like UDP)."unreliable_ordered"drops old packets but preserves order. Use reliable for important events and unreliable for frequent updates like position. - Call local:
"call_local"executes the function on the calling peer as well as remote peers."call_remote"(the default) only executes on remote peers. Forgetting this is a common source of “the action works for other players but not for me.”
Node Path Mismatches
Godot’s RPC system identifies nodes by their path in the scene tree. When you call rpc() on a node, Godot sends the node’s path to the remote peer, which looks up the same path in its own scene tree and executes the function there. If the node doesn’t exist at that path on the remote peer, the RPC is silently dropped.
This is the most common issue when spawning players or objects dynamically. If the server creates a player node at /root/Game/Players/Player_2 but the client hasn’t received the spawn message yet, any RPC sent to that node will fail on the client.
The fix is to use Godot’s MultiplayerSpawner node, which synchronizes spawning across all peers. Add a MultiplayerSpawner as a child of the node that contains your spawnable objects, configure the spawn path and allowed scenes, and let the spawner handle replication:
# Server-side spawning with MultiplayerSpawner
func spawn_player(peer_id: int):
var player = player_scene.instantiate()
player.name = str(peer_id)
# Set the authority to the owning client
player.set_multiplayer_authority(peer_id)
$Players.add_child(player)
# MultiplayerSpawner automatically replicates to clients
If you’re not using MultiplayerSpawner, you need to ensure that the node exists on all peers before sending RPCs. A common pattern is to have the server send an RPC to all clients telling them to create the node, wait for acknowledgment, and only then begin sending RPCs to that node.
Multiplayer Authority Configuration
Every node in a multiplayer scene has a multiplayer authority—the peer ID that “owns” the node. By default, the server (peer ID 1) is the authority for all nodes. If your RPC is annotated with "authority" mode but a client is trying to call it, the call will fail because the client is not the authority.
Set the authority correctly when spawning player-controlled nodes:
func spawn_player(peer_id: int):
var player = player_scene.instantiate()
player.name = str(peer_id)
player.set_multiplayer_authority(peer_id) # The client owns their player
$Players.add_child(player)
# In the player script:
func _ready():
# Only process input if we are the authority
set_process_input(is_multiplayer_authority())
@rpc("authority", "reliable", "call_local")
func fire_weapon():
# Only the owning client can call this
var bullet = bullet_scene.instantiate()
get_parent().add_child(bullet)
A common mistake is setting authority after adding the node to the scene tree. In some cases, _ready() fires before set_multiplayer_authority() is called, causing early RPCs to use the wrong authority. Always set the authority before calling add_child().
Verifying the Connection
RPCs require an active ENet connection. If you call rpc() before the connection is fully established, the call is lost. Always wait for the appropriate signal before sending RPCs:
var peer = ENetMultiplayerPeer.new()
func host_game():
peer.create_server(7777)
multiplayer.multiplayer_peer = peer
multiplayer.peer_connected.connect(_on_peer_connected)
func join_game(address: String):
peer.create_client(address, 7777)
multiplayer.multiplayer_peer = peer
multiplayer.connected_to_server.connect(_on_connected)
func _on_connected():
print("Connected! Safe to send RPCs now.")
# Now RPCs will work
request_game_state.rpc_id(1)
func _on_peer_connected(id: int):
print("Peer %d connected" % id)
# Safe to send RPCs to this specific peer
send_welcome.rpc_id(id, get_game_state())
Also check the return value of create_server() and create_client(). They return an Error enum—if the port is already in use or the address is invalid, the function returns an error code but doesn’t crash. Always verify: if peer.create_server(7777) != OK: push_error("Failed to create server").
Debugging Tips
When an RPC isn’t working, add print statements at both the call site and the function body to determine which side is failing. If the call site prints but the function body doesn’t, the RPC is being dropped in transit. If neither prints, the calling code isn’t being reached at all.
Check the multiplayer peer state with multiplayer.multiplayer_peer.get_connection_status(). It should return CONNECTION_CONNECTED. If it returns CONNECTION_DISCONNECTED or CONNECTION_CONNECTING, the connection isn’t ready for RPCs.
For complex multiplayer setups, use Godot’s built-in MultiplayerSynchronizer alongside RPCs. The synchronizer handles continuous state replication (positions, rotations, animations) while RPCs handle one-off events (damage, item pickups, chat messages). Mixing the two correctly avoids many of the node path and authority issues that cause RPC failures.