Learning RPCS - need help to understand this behaviour

Godot Version

4.2.2stable

Question

Hello,

I’m currently working on coding a simple multiplayer game as a hobby project. While I’m making good progress, I’m encountering some issues understanding RPC calls in the multiplayer setup.

Here’s a brief overview of my current setup:

Server:

extends Node

@onready var multiplayer_peer = ENetMultiplayerPeer.new()
@onready var char_selection = %CharSelection
const MAX_PLAYERS = 4
var players_connected = 0

# Called when the node enters the scene tree for the first time.
func _ready():
	multiplayer.connect("connected_to_server", Callable(self, "_on_connected_ok"))
	multiplayer.connect("connection_failed", Callable(self, "_on_connected_fail"))
	multiplayer.connect("peer_connected", Callable(self, "_on_player_connected"))
	multiplayer.connect("peer_disconnected", Callable(self, "_on_player_disconnected"))
	multiplayer.connect("server_disconnected", Callable(self, "_on_server_disconnected"))
	
func _on_host_btn_pressed():
	if error == OK:
		multiplayer.multiplayer_peer = multiplayer_peer
		%ConsoleText.text = "Creating the server..."
		await get_tree().create_timer(0.2).timeout
		%ConsoleText.text = "Server created!"
		await get_tree().create_timer(0.5).timeout
		%ConsoleText.text = "Waiting for players...(1/4)"
		players_connected += 1
	elif error != OK:
		%ConsoleText.text = "Error creating a server! Error code: " + str(error)

func _on_join_btn_pressed():
	multiplayer_peer.create_client(%JoinAddress.text, int(%JoinPort.text))
	multiplayer.multiplayer_peer = multiplayer_peer
	players_connected += 1
	%ConsoleText.text = "Connecting..."
	
func _on_connected_ok():
	%ConsoleText.text = "Server found!"
	await get_tree().create_timer(0.5).timeout
	set_player_name()
		
func _on_connected_fail():
	%ConsoleText.text = "Server not found!"
	
func _on_player_connected(_id):
	%ConsoleText.text = "Connected to the server!"
	await get_tree().create_timer(0.5).timeout
	players_connected += 1
	update_console_text()
	if players_connected >= 2:
		start_game()
	
func _on_player_disconnected(id):
	multiplayer_peer.close()
	players_connected = 0
	update_console_text()
	return_to_lobby()
	if (id == 1):
		%ConsoleText.text = "Server closed!"
	else:
		%ConsoleText.text = "Someone disconnected, re-host the server!"
		
func set_player_name():
	char_selection.rpc("set_player_name", %PlayerName.text)
	
func update_console_text():
	%ConsoleText.text = "Waiting for players...(" + str(players_connected) + "/4)"
	
func start_game():
	%GridContainer.visible = false
	%ConsoleText.visible = false
	%CharSelection.visible = true
	
func return_to_lobby():
	%GridContainer.visible = true
	%ConsoleText.visible = true
	%CharSelection.visible = false
	

The server setup is straightforward. Users can host games, and clients can join. The “Start Game” button doesn’t have any functionality yet.

Client Interface:

In a specific part of my code, I have the following function:

func set_player_name():
  char_selection.rpc("set_player_name", %PlayerName.text)

The intention here is to use an RPC call to transmit the player’s name to another scene (char_selection): a lobby where players can choose their character classes.

Regarding char_selection:

extends Control

var players = []
var list = []

# Called when the node enters the scene tree for the first time.
func _ready():
	if is_multiplayer_authority():
		%StartGame.disabled = false
	else:
		%StartGame.disabled = true

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
	pass
	
@rpc("any_peer", "call_local")
func set_player_name(name):
	print(name)

Currently, I’m only printing the received player name for testing purposes.

However, the problem I’m facing is that the RPC call prints the client’s name twice without printing the host’s name. I’m wondering if I’m overlooking something in my setup.

To summarize my questions:

  1. Why does the RPC call print the client’s name twice but not the host’s name?
  2. Is my approach correct if want to consider the host as a player as well?
  3. Do you have any general advice or tips for working with multiplayer functionality in Godot?

EDIT: Found another issue: both client and host are treated with priority and both are seen as server.

Thanks in advance for any assistance you can provide. Have a great day!

First you never create a host from Enet when host button is pressed. You only creat the client.

If you have multiple debug game instances running they both share the same output window.

Change your print statement to print(multiplayer.get_unique_id(), " ", player name)

I bet you see two because it prints local and on remote side.

RPC are fine, but MultiplayerSynchronizer and MultiplayerSpawner are your friends.

rpc does not take the function as an argument, it is called from a callable, much like the new signal connect syntax. I was wrong it is valid to use RPCs with string method names on Nodes.

func set_player_name() -> void:
    char_selection.set_player_name.rpc(%PlayerName.text)

# for reference the 4.x way to connect signals (no strings!)
func _ready() -> void:
    multiplayer.peer_connected.connect(_on_player_connected)
    multiplayer.peer_disconnected.connect(_on_player_disconnected)