Can't move player separately in multiplayer

Godot Version

4.4.stable

Question

I can’t move the players separately in multiplayer, if I create a lobby as a host, works fine, if I join I can only move the host


Player.gd

extends CharacterBody2D
class_name Player
@onready var dashduration = $dashduration
@onready var dashcooldown = $dashcooldown
@export var normal_speed = 300.0
@export var dash_speed = 600.0
var speed = normal_speed
var is_dashing = false
var dash_available = true
@export var health : int
signal player_death
var player_dead = false

func _ready():
	$Sprite2D.visible = true
	$Gun/Sprite2D.visible = true
	$CollisionShape2D.disabled = false

func _physics_process(delta: float) -> void:
	if health == 0 and not player_dead:
		player_dead = true
		$Sprite2D.visible = false
		$Gun/Sprite2D.visible = false
		$CollisionShape2D.disabled = true
		health_label()
		score.enemy_score += 1
		emit_signal("player_death")
		
	else:
		var direction_x := Input.get_axis("ui_left", "ui_right")
		if direction_x:
			velocity.x = direction_x * speed
		else:
			velocity.x = move_toward(velocity.x, 0, speed)
		var direction_y := Input.get_axis("ui_up", "ui_down")
		if direction_y:
			velocity.y = direction_y * speed
		else:
			velocity.y = move_toward(velocity.y, 0, speed)

		move_and_slide()
		if Input.is_action_just_pressed("wedashing") and dash_available == true and not is_dashing:
			start_wedashing()
		health_label()
	
func start_wedashing():
	is_dashing = true
	dash_available = false
	speed = dash_speed
	dashcooldown.start()
	dashduration.start()
	
func _on_dashduration_timeout() -> void:
	is_dashing = false
	speed = normal_speed

func _on_dashcooldown_timeout() -> void:
	dash_available = true

func _on_area_2d_area_entered(area: Area2D) -> void:
	if area.is_in_group("bullets"):
		health -= 1

func health_label():
	$Camera2D/Label.text = "<3: " + str(health) + "/3"

main.gd

extends Node
@onready var main = $Menu/Main
@onready var vs1 = $Menu/vs1
@onready var menu = $Menu

const port = 9999
const Player = preload("res://player_2.tscn")
var enet_peer = ENetMultiplayerPeer.new()


func _ready() -> void:
	main.visible = true
	vs1.visible = false
 	  
func _process(delta: float) -> void:
	pass

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:
	menu.hide()
	
	enet_peer.create_server(port)
	multiplayer.multiplayer_peer = enet_peer
	multiplayer.peer_connected.connect(_add_player)
	
	_add_player(multiplayer.get_unique_id())

func _on_join_pressed() -> void:
	menu.hide()
	
	enet_peer.create_client("localhost", port)
	multiplayer.multiplayer_peer = enet_peer

func _on_bot_pressed() -> void:
	menu.hide()

func _add_player(id):
	var player_instance = Player.instantiate()
	player_instance.set_multiplayer_authority(id)
	player_instance.name = str(id)
	add_child(player_instance)

I see a few issues.

One, authority is not given to the joining player correctly in _add_player. I assume a MultiplayerSpawner will spawn for the client, but its authority will still set to the server on the client.

Consider using the MultiplayerSpawner.custom_spawn function for the player, or reading the multiplayer id from the player name and set authority when the node is added to the tree.

The other issue is how you pull input in the player in _physics_process. This should be guarded by is_multiplayer_authority() checks. Otherwise the peer will be controlling both players.

1 Like

I can’t find any function called MultiplayerSpawner.custom_spawn and I put all the is_multiplayer_authority() I could but is not working either

I apologize, i meant it as a property, but it also has a different name spawn_function

This is how you can set it up. I’m assuming the MultiplayerSpawner is a child of this scripts Node.

func _ready() -> void:
	...
	$MultiplayerSpawner.spawn_function  = _add_player

func _on_host_pressed() -> void:
	...
	multiplayer.peer_connected.connect($MultiplayerSpawner.spawn)

	$MultiplayerSpawner.spawn(multiplayer.get_unique_id())

# function must return a node for spawn to work.
func _add_player(id) -> Node:
	var player_instance = Player.instantiate()
	player_instance.set_multiplayer_authority(id)
	player_instance.name = str(id)
	return player_instance
1 Like

I fixex it already, but thank you anyway :smiley:

1 Like