0 votes

Just a small warning and or disclaimer, this is a possibly difficult topic to explain since this relates to utilizing the new Multiplayer functions in Godot 3.0:

Lately I've been messing around the multiplayer demo to get a better understanding on how networking works in the game engine, and what I was trying to do is rewrite part of the network code for players to take control of an avatar object from a remote control node.

(For those who are confused, I wanted to create an player avatar system where players over the multiplayer network can remotely control their characters or objects which in thought allows respawning in a multiplayer network)

Unfortunately, I've ran into a bit of a small issue: I was able to get the controller to work but the spawning system isn't working right where I through it was possible. I learned that the objects can be accessed over the network using the is_network_master(): and that I've created an if statement in a Synchronized Function:

var pos = Vector2()
slave var m_pos = Vector2()

sync func m_spawn(by_who):
    randomize()
    if (is_network_master()):
        var rename = str(self.get_name())
        var import = load("res://Objects/Test/Player_test_Online.tscn").instance()
        import.set_name(str(import.get_name())+rename)
        pos = get_tree().get_nodes_in_group("spawn")[round(rand_range(0,3))].position
        rset("m_pos", pos)
        import.position = pos 
        get_tree().get_root().add_child(import)
    else:
        var rename = str(self.get_name())
        var import = load("res://Objects/Test/Player_test_Online.tscn").instance()
        import.set_name(str(import.get_name())+rename)
        import.position = m_pos
        print(m_pos)
        get_tree().get_root().add_child(import)
    pass

The way the "Spawn" function works is that it would use the randomize() to randomize the number seed before using round(rand_range(0,3)) in the get_tree().get_nodes_in_group("spawn")[].position for the pos variable and assigning it to the import.position to make the avatar respawn to one of the spawners. Then I took the POS variable which contains the randomized spawn position and used the rset() command to make the m_pos slave variable equal to the position for the slave object to spawn at.

At the "Master" view, the object has spawned at the intended randomized position; but on the "Slave" view, the recieving object is not spawned at the right spot. I've checked out where it spawned using the print() command and i was met with the reply from the terminal: (0,0). Its like as if the rset command did not transfer over the intended spawn location at all. Bottom line, the spawn location positions are not in sync due to the m_pos not assigning the right Vector2() result.

I feel like I may not be approaching the spawning mechanism right but I was trying to experiment a way to create a functioning respawning system to work in a multiplayer network without the need to accidentally delete the player controller from the network. If anybody has any idea on how this can get resolved or if there is a solution to this I'd really appreciate it. I'm not in any rush to create something but this is something done on my free time to self teach myself on how multiplayer works in Godot.

Feedbacks are welcome!

in Engine by (204 points)

1 Answer

+1 vote
Best answer

My guess is that rset() function is waiting until m_spawn() is finished. Those functions probably use TCP and TCP is ordered.
Try doing it in two steps with rpc calls inverted, something like:

pos = functionToChooseSpawnPosition()
rset("m_pos", pos)
rpc("m_spawn", by_who)

It is called by network master.
I'm relying on rpc calling order and Godot did not fail me so far.

by (2,292 points)
selected by

You were absolutely correct! I've tried out what you've suggested and to my amazement it was because indeed the Sync Function needs to finish first before anything else happens. I didn't know about this but definitely a lesson worth learning. THANKS!

Here is the fixed code as a result:

var pos = Vector2()
slave var m_pos = Vector2()

func _physics_process(delta):

    if spawn == true:
        randomizer()
        rset("m_pos", pos)
        rpc("m_spawn",get_tree().get_network_unique_id())
        pass
    else:
        pass

sync func m_spawn(by_who):
    if (is_network_master()):
        var rename = str(self.get_name())
        var import = load("res://Objects/Test/Player_test_Online.tscn").instance()
        import.set_name(str(import.get_name())+rename)
        import.position = pos 
        get_tree().get_root().add_child(import)
    else:
        var rename = str(self.get_name())
        var import = load("res://Objects/Test/Player_test_Online.tscn").instance()
        import.set_name(str(import.get_name())+rename)
        import.position = m_pos
        print(m_pos)
        get_tree().get_root().add_child(import)
    pass

func randomizer():
    randomize()
    pos = get_tree().get_nodes_in_group("spawn")[round(rand_range(0,3))].position
    return pos

Just something in case others working in the multiplayer network needs it as a handy coding reference.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.