Two characters in multiplayer

Godot Version

4.4.stable

Question

I’ve been trying to create a system that allows players to choose between two characters, the host works fine but when another player joins doesn’t work. The host can see the other player, but in the other player screen, it’s like the script didn’t add the character. Also, the other player Point of Light is visible when it shouldn’t be

I used this as reference

This is a video of what happens

main.gd

extends Node
@onready var main = $Menu/Main
@onready var vs1 = $Menu/vs1
@onready var menu = $Menu
@onready var join = $Menu/Join
@onready var host = $Menu/Host
@export var ip : String
var port : int = 621
var addresses: Array = []

@onready var fox = preload("res://fox.tscn")
@onready var tiger = preload("res://tiger.tscn")

var enet_peer = ENetMultiplayerPeer.new()
var ip_assigned : bool = false
var port_assigned : bool = false
var player_character : String

func _ready() -> void:
	main.visible = true
	vs1.visible = false
	
	join.hide()
	vs1.hide()
	host.hide()
	$Map.hide()

 	  
func _process(delta: float) -> void:
	if join.visible == true:
		if port_assigned == false:
			$Menu/Join/TextInput/PORT.text = str(port)
			port_assigned = true
			
		port = int(float($Menu/Join/TextInput/PORT.text))
		ip = $Menu/Join/TextInput/IP.text
			
		
	elif $Menu/Host.visible == true:
		if ip_assigned == false:
			ip = IP.resolve_hostname(str(OS.get_environment("COMPUTERNAME")),1)
			$Menu/Host/TextInput/IP.text = ip
			$Menu/Host/TextInput/PORT.text = str(port)
			ip_assigned = true
			
		
func _on_button_pressed() -> void:
	main.visible = false
	vs1.visible = true

func _on_button_2_pressed() -> void:
	get_tree().change_scene_to_file("res://test_world_2.tscn")

func _on_host_pressed() -> void:
	vs1.hide()
	host.show()
	enet_peer = ENetMultiplayerPeer.new()
	var error = enet_peer.create_server(port)
	if error != OK:
		print("error")
		return
	multiplayer.multiplayer_peer = enet_peer
	multiplayer.peer_connected.connect(peer_connected)

func _on_join_pressed() -> void:
	vs1.hide()
	join.show()
	
	enet_peer = ENetMultiplayerPeer.new()
	enet_peer.create_client(ip, port)
	
	multiplayer.multiplayer_peer = enet_peer

@rpc("any_peer")
func _load_player(id, character):
	if is_multiplayer_authority():
		var spawn_node = get_tree().current_scene.get_node("Player")
		var pl
		
		
		if character == "fox":
			pl = fox.instantiate()
			pl.player_id = id
			pl.name = "Player_" + str(id)
			pl.global_position = Vector2(randi_range(50, 450),randi_range(50, 450) )
			spawn_node.add_child(pl, true)
			print("Authority : ", pl.get_multiplayer_authority())
			print("Input Authority : ", pl.get_node("MultiplayerSynchronizer").get_multiplayer_authority())
		elif character == "tiger":
			pl = tiger.instantiate()
			pl.player_id = id
			pl.name = "Player_" + str(id)
			pl.global_position = Vector2(randi_range(50, 450),randi_range(50, 450) )
			print("Authority : ", pl.get_multiplayer_authority())
			print("Input Authority : ", pl.get_node("MultiplayerSynchronizer").get_multiplayer_authority())
			spawn_node.add_child(pl, true)


func peer_connected(id):
	print("Peer connected : ", id)


func _on_fox_pressed() -> void:
	player_character = "fox"
	await get_tree().create_timer(0.2).timeout
	print(multiplayer.get_unique_id())
	print(player_character)
	if multiplayer.is_server():
		_load_player(multiplayer.get_unique_id(), player_character)
	else:
		_load_player.rpc(multiplayer.get_unique_id(), player_character)
	menu.hide()
	$Map.show()

func _on_tiger_pressed() -> void:
	player_character = "tiger"
	await get_tree().create_timer(0.2).timeout
	print(multiplayer.get_unique_id())
	print(player_character)
	if multiplayer.is_server():
		_load_player(multiplayer.get_unique_id(), player_character)
	else:
		_load_player.rpc(multiplayer.get_unique_id(), player_character)
	menu.hide()
	$Map.show()

your _load_player isn’t called locally for the connecting player so that client doesn’t create their own player.

_load_player makes two random random calls that will not be the same across server/clients, generate your random numbers as part of the rpc for stability

func _load_player(id, character, spawn_position: Vector2) -> void:
# etc
			pl.global_position = spawn_position


# when sending rpc
var spawn_pos := Vector2(randi_range(50, 450),randi_range(50, 450))
_load_player.rpc(multiplayer.get_unique_id(), player_character, spawn_pos)

The server needs to update all newly connected clients about already existing players, probably through calling _load_player.rpc_id for each existing player onto the new client id. This is something the MultiplayerSpawner tries to do, automatically calling it’s spawn_func on connection with all the previously spawned information.

2 Likes

I just fixed it, but thank you anyway. I’ll check it to see if it’s better than my solution

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