Godot Net-Code Blog #1

posted in Misc
Published April 21, 2024
Advertisement

With The Krilling set for release, this week was more time to work on some side projects. This week I started a small and short project that is mostly just an experiment of how multiplayer networking works in Godot.

At the start, I had one scene and just placed a host and join button for the player to click on. The goal was for the host button to spawn in the player and start a server. Then the join button would spawn in the player and join that server. Then these two player characters would be controlled individually by the two different players, and synchronized on both screens.

This initial task was completed primarily by following this tutorial here.

The movement of the players is not a focus for this, so I just used the built-in character body script for basic player movement.

Godot makes the multiplayer networking portion actually rather simple. When the program starts I create a ENetMultiplayerPeer. Then when the host button is pressed I tell this peer to create a server at a given port. Then I set the current multiplayer peer to this peer, and connect a function to the multiplayer built-in signal for when a new peer connects to the server. Since the host button is effectively connecting a new player we also call this new function manually.

This function spawns in the player's character. The only small detail difference here is that we set the Node’s name to be the ID of the joining peer to be used later to make sure that a player character is only controlled by one player.

If instead the join button is pressed we have the peer create a client to the server at a given IP address and port and again set the current multiplayer peer to the newly created peer.

Now we need to account for the player ID inside of the player script. To do this we set the multiplayer authority of the Node when it enters the tree and check if the player has multiplayer authority over this Node before applying physics.

To synchronize the two peers, we need two other built-in components from Godot. The MultiplayerSpawner and the MultiplayerSynchronizer. The MultiplayerSpawner can be given the player character path to automatically spawn characters for both clients.

The MultiplayerSynchronizer can be added to the player object itself to automatically synchronize properties of the player characters between the clients. For now, I just have the player position being synchronized.

After this, I started working on moving the host and join buttons to a menu screen. The main difference here is by making our multiplayer manager into an autoload singleton. Then the methods that were previously connected to the host and join buttons are instead public functions to be called by the menu.

Then instead of spawning a character on player connection, we emit a custom signal telling other objects that a player needs to be spawned. This signal is listened to by a player manager that handles the spawning of player characters. There is one small issue, however. When the host or join button is pressed we want to swap the scene to the game scene. When we do so we also want to emit the spawn signal, but if the scene has yet to be fully loaded, then it might not be ready to receive the signal. To solve this problem I have an on_load_complete function that is called by the player manager when the scene is done loading. This then emits a load complete signal that can be awaited inside the function that emits the spawn signal, meaning that it will not emit the spawn signal until it has received an emission from the load complete signal.

After that, the last thing to do is set up the player manager. All this does is connect to the spawn signal and tell the MultiplayerManager that the scene is done loading. Then when the spawn signal is received it spawns the player character.

This is enough to make the host button work, but we want to allow the player to enter the IP address of the server they want to connect to. To do so we make the join button load a scene with an input prompt where they can input the IP address and once the player presses the button to enter it tells the MultiplayerManager to initiate a join at this IP address.

To avoid invalid IP addresses we first check if the IP address is valid with a built-in method or if it is ‘localhost’. If it isn’t then we just return and let the player continue inputting.

One last problem is what happens when the player enters a valid IP address but there is no server at that IP address. In this case, the connection will fail, but currently, the scene will change to the game scene anyway and the player will be stuck. To avoid this we can listen to the built-in connected_to_sever signal that emits when the peer successfully connects to the server. Only when this signal is emitted will we change the scene to the game scene.

With all of this together we have a small, but functional P2P networking system. In these gifs, I have shown only using localhost for simplicity, but I have tested it from different computers as well.

Next week I plan to improve this by adding more features to the gameplay that require synchronization, and if I have time experiment with rollback net-code.

0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement