I’ve been trying to learn more on networking and I’ve been going through BatteryAcidDev’s networking FPS tutorial and applying it to a FPS template I’ve been working on to sort of learn more about how multiplayer works in Godot.
Here is a repo with what I’ve done so far on it and a quick readme of what everything does: GitHub.
I’m running into an issue with getting the correct IDs and authority set for each unique player. So far, I have it set up so that what a player hosts the game, their ID is set to 1 and they are added as a child to a spawn node and their given mp authority over an Input script and a player script. This part works fine, the host can join and all is well.
The problem starts when I try to join a second player. From what I can tell with print statements and looking at the “Remote” viewer, the joined player is correctly spawned in the playerSpawner node with a unique ID as their name and they are correctly assigned that unique ID as their name and player_id (which is used to set multiplayer authority). I can even print out ‘is_multiplayer_authority()’ in the player controller script and it returns true. However, running ‘get_mulltiplayer_authority’ returns the hosts’ ID of 1.
If I put in code to set the multiplayer authority in the player script ready() to ‘set_multiplayer_authority(player_id)’, it correctly sets the MP authority ID to the joined player’s ID, but it then returns ‘false’ for ‘is_multiplayer_authority’.
I’ve also tried using call deferred with set_multiplayer_authority, I’ve seen a few people recommend that, but I get the same result.
In all cases, a model spawns for the joined player, but it is completely controlled by the host and the joined player gets a blank screen.
I’m not really sure what I’m missing, I’m pretty sure I’m not doing something correctly with how the MP authority is set for each player, but I don’t know enough about networking or godot’s multiplayer to know where to look. Any help would be welcome!
You dont need to manually do this as everything defaults to the host.
You need to be careful here as both client and server need to do this same action so both agree who the node is authed to and one of them will always report false.
There are lots a of ways to set auth. Through a setter, through the name of the object, and through the multiplayer spawner custom spawn function.
Exported Setters can be weird because they will be called after init and before enter tree. A node that is not in the tree will not be able to access the static multiplayer property until they enter the tree.
Using the custom spawn function is by far easier because you dont have to manage auth in the node.
$MultuplayerSpawner.spawn_function = my_spawn
$MultuplayerSpawner.spawn(peer_uid)
func my_spawn(uid:int) -> Node:
var player = packed_scene.instanciate()
player.set_multiplayer_authority(uid)
player.name = str(uid)
return player
The custom spawn will happen for every peer that is connected, or will join, without the server needing to call it again, as long as the node needs to be replicated by the spawner.
So I do have that function in my code on the level scene’s script, just called ‘addPlayer’, but the “.spawn(peer_uid)” on the multiplayerSpawner is new to me. So from what you’re saying, I need to also set the MultiplaerSpawner.spawn_function to “addPlayer” (which does the same thing as yours there), then when Multiplayer.spawn is called whena player spawns, the peer_uid is passed into the addPlayer/my_spawn function as the uid?
Yes, there is a requirement that the function does not add the node to the tree itself but is returned to the caller. I.e the spawner. The spawner will add it as child to the watched node. The spawn function justs builds and configures the scene.
The spawn function also only supports one parameter and is by default a Variant type. So if you need to pass more information on what to spawn you will need to be a little more creative and restrictive as object decoding is not allowed for security reasons. Arrays and dictionaries can be useful here.
Ok, and the peer_id that’s passed into the function then using .spawn, is there a better way to go about getting that generated? Or is that automatically generated when a new client joins?
Ok, so even after adding in setting the spawn function and also setting the mp auth in the addplayer function like yours, for some reason it’s still saying the mp auth is connected to the host. I’ve also got the addPlayer function connected to peer_connect by multiplayer.peer_connected.connect(addPlayer) and it prints out the correct netID for both host and joined player
func addPlayer(netID: int):
# Represents every new player that joins the game
var playerToAdd = PlayerCharacter.instantiate()
playerToAdd.set_multiplayer_authority(netID)
playerToAdd.player_id = netID
playerToAdd.name = str(netID)
playerSpawnNode.add_child(playerToAdd,true)
#return playerToAdd
print('--'+str(get_multiplayer_authority()))
MESSAGE | Open Join Menu
id847642158
mp authority: 847642158
**is mp auth: false**
playerid: 847642158
**--1**
SERVER | Player joined server. PlayerID: 847642158
So it looks like somehow the mp auth (and also the multiplayer unique id) is still owned by the host