Camera and position of both characters controlled by host (3d multiplayer)

Godot Version

Godot Version 4.2.1 stable

Question

I am attempting to make a 3D multiplayer game that requires players to choose aspects of their character in a lobby.

Right now I have a system where the game successfully allows two players (the limit for now) to join a lobby, but both character’s movement and position is controlled by the host. Additionally, both screens show the host’s character’s camera.

Here are screenshots of the relevant scenes:



Here are relevant scripts:
scene_manage.gd (attached to Spawns):

@export var player_scene: PackedScene

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	var index = 0
	for i in global_ids.players:
		var current_player = player_scene.instantiate()
		add_child(current_player)
		for spawn in get_tree().get_nodes_in_group("spawn"):
			if spawn.name == str(index):
				current_player.global_position = spawn.global_position
		index += 1


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	pass

multi_manage.gd (attached to MainMenu):

extends Control

@export var address = "127.0.0.1"
@export var port = 9972
var peer
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	multiplayer.peer_connected.connect(peer_connected)
	multiplayer.peer_disconnected.connect(peer_disconnected)
	multiplayer.connected_to_server.connect(connected_to_server)
	multiplayer.connection_failed.connect(connection_failed)
	pass # Replace with function body.

func peer_connected(peer_id):
	print("Player " + str(peer_id) + " connected!")

func peer_disconnected(peer_id):
	print("Player " + str(peer_id) + " disconnected.")

func connected_to_server():
	print("Successfully connected!")
	send_peer_info.rpc_id(1, $CanvasLayer/PanelContainer/MarginContainer/VBoxContainer/Enter_Name.text, multiplayer.get_unique_id())

func connection_failed():
	print("Failed to connect.")

@rpc("any_peer")
func send_peer_info(name, peer_id):
	if !global_ids.players.has(peer_id):
		global_ids.players[peer_id] ={
			"name" : name,
			"peer_id" : peer_id,
		}
	if multiplayer.is_server():
		for i in global_ids.players:
			send_peer_info.rpc(global_ids.players[i].name, i)

@rpc("any_peer", "call_local")
func start_game():
	var scene = load("res://Scenes/world.tscn").instantiate()
	get_tree().root.add_child(scene)
	self.hide()
	$CanvasLayer.hide()

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	pass


func _on_host_pressed() -> void:
	peer = ENetMultiplayerPeer.new()
	var error = peer.create_server(port, 6)
	if error != OK:
		print("Hosting failed: " + error)
		return
	peer.get_host().compress(ENetConnection.COMPRESS_RANGE_CODER)
	
	multiplayer.set_multiplayer_peer(peer)
	print("Waiting for players...")
	
	send_peer_info($CanvasLayer/PanelContainer/MarginContainer/VBoxContainer/Enter_Name.text, multiplayer.get_unique_id())


func _on_join_pressed() -> void:
	peer = ENetMultiplayerPeer.new()
	peer.create_client(address, port)
	peer.get_host().compress(ENetConnection.COMPRESS_RANGE_CODER)
	multiplayer.set_multiplayer_peer(peer)


func _on_start_pressed() -> void:
	start_game.rpc()

global_ids.gd (script to store global variables):

extends Node

var players = {}

The player script is a standard controller with no multiplayer code.
The world script handles some non-multiplayer stuff and isn’t relevant.

Lmk if you need more information