Host controls Local player on both instances, Client controls remote character

Godot Version

4.3

Question

Hi, I’m following this really cool tutorial here https://www.youtube.com/watch?v=n8D3vEx7NAE&t=1s and i’ve gotten to the multiplayer setup, at this point https://youtu.be/n8D3vEx7NAE?t=1845.
my problem is that when I am playing from the host, it looks like I’m controlling the peer, in both the host and client window.
If I’m playing from the client, It looks like I’m playing the local character in both the host and client window.

From what I can tell, I’ve set authority up correctly, and also replication, but something’s wrong.

world.gd

# https://youtu.be/n8D3vEx7NAE?t=1488
extends Node

@onready var main_menu = $CanvasLayer/MainMenu
@onready var address_entry = $CanvasLayer/MainMenu/MarginContainer/VBoxContainer/AddressEntry

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

func _unhandled_input(_event: InputEvent) -> void:
	if Input.is_action_just_pressed("quit"):
		get_tree().quit()
		
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta: float) -> void:
	pass
func _on_host_button_pressed() -> void:
	main_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_button_pressed() -> void:
	main_menu.hide()
	
	enet_peer.create_client("localhost", PORT)
	multiplayer.multiplayer_peer = enet_peer

func add_player(peer_id):
	var player = Player.instantiate()
	player.name = str(peer_id)
	add_child(player)

player.gd

extends CharacterBody3D

@onready var camera = $Camera3D
@onready var anim_player = $AnimationPlayer
@onready var muzzle_flash = $Camera3D/Pistol/MuzzleFlash

const SPEED = 10
const JUMP_VELOCITY = 10

#Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = 20.0

func _enter_tree() -> void:
	set_multiplayer_authority(str(name).to_int())
	
func _ready() -> void:
	if not is_multiplayer_authority(): return
	
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
	camera.current = true
	
func _unhandled_input(event: InputEvent) -> void:
	if not is_multiplayer_authority(): return
	
	if event is InputEventMouseMotion:
		rotate_y(-event.relative.x * .005)
		camera.rotate_x(-event.relative.y * .005)
		camera.rotation.x = clamp(camera.rotation.x, -PI/2, PI/2)
		
	if Input.is_action_just_pressed("shoot") \
		and anim_player.current_animation != "shoot":
		play_shoot_effects.rpc()

func _physics_process(delta: float) -> void:
	if not is_multiplayer_authority(): return
	
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir = Input.get_vector("left", "right", "up", "down")
	var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	if anim_player.current_animation == "shoot":
		pass
	elif input_dir != Vector2.ZERO and is_on_floor():
		anim_player.play("move")
	else:
		anim_player.play("idle")
		
	move_and_slide()

@rpc("call_local")
func play_shoot_effects():
	anim_player.stop()
	anim_player.play("shoot")
	muzzle_flash.restart()
	muzzle_flash.emitting = true
	


func _on_animation_player_animation_finished(anim_name: StringName) -> void:
	if anim_name == "shoot":
		anim_player.play("idle")
	

Your multiplayer code is fine,

I think the issue is the “current” camera.

You only want to make the local player camera current and not the newly spawned player.

I would stick a ready statement on the player script to check if is multiplayer authority set camera current. (And make sure current is not default on the camera)

1 Like

Hi thank you very much for your reply.
Looking at my player script, I can see that there is a ready function with the following:

func _ready() -> void:
	if not is_multiplayer_authority(): return
	
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
	camera.current = true

Would I need to modify this?
I’m very new to Godot and GDScript (finished the GDScript from Zero app last weekend)

Oh I missed that, I think it looks okay

Did you also make sure the default value isn’t set to true as well?

If you watch your video it’s definitely switching cameras.

1 Like

So I need to uncheck this?
image
Testing…

It worked!!! Seems odd t hat such a small thing could be so significant. I’ll have to read up on that. Thank you so much!!

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