Unveiling Godot 4's Multiplayer Magic: Setting Up Your Dedicated Server in C#
Welcome back, fellow Godot enthusiasts! In this instalment, we're diving into the realm of multiplayer game development with Godot 4. Buckle up as we explore the steps to set up a dedicated multiplayer server using C#. Don't worry; we'll keep it concise, aiming for a quick 5-minute read.
Why a Dedicated Server?
Before we dive in, let's briefly address why you might opt for a dedicated server. Dedicated servers provide a centralized, authoritative source of truth for your multiplayer game. They handle the game's logic, ensuring consistency across all connected clients and offering a robust solution for multiplayer experiences.
Prerequisites: Godot 4 and .NET
Ensure you have Godot 4 installed, and .NET support is configured, as we covered in our previous posts. If not, check out the quick setup guide to get yourself up to speed.
Setting Up the Dedicated Server
Create a new Godot project for your dedicated server.
Create a New Scene
multiplayer.tscn
:Add a new node of type Control and call it
Multiplayer
.Each node in Godot has a
multiplayer
property, which is a reference to theMultiplayerAPI
instance configured for it by the scene tree.We will use Godot’s high-level MultiplayerAPI to implement our server logic.
Add a C# Script
MultiplayerController.cs
and attach it to your scene.Add basic configuration for your server:
Create a new private variable for
_port
,_ip
and_maylayerCount
(Optional) export all variables to make them visible in the Godot 4 Editor
using Godot;
using System;
using System.Linq;
public partial class MultiplayerController : Control
{
[Export]
private int _port = 8910;
[Export]
private string _ip = "127.0.0.1";
[Export]
private int _maxPlayerCount = 2;
...
}
Initialize Networking:
Create a new private variable
_peer
to store a reference to the peerCreate a new method
HostGame()
, which will contain all the necessary logic to initialize a peer as a server and listen for client connections.Create new methods
PlayerConnected()
andPlayerDisconnected()
and set them as event handlers for the respective events ofMultiplayer
. These events will be triggered for all connected peersBoth EventHandlers take the userId as a parameter, so we simply print it for now.
Override Godot’s
_Ready()
method, which is called when the node enters the scene tree for the first time, to set the EventHandlers and start hosting the game by calling theHostGame()
method.
using Godot;
using System;
using System.Linq;
public partial class MultiplayerController : Control
{
[Export]
private int _port = 8910;
[Export]
private string _ip = "127.0.0.1";
[Export]
private int _maxPlayerCount = 2;
private ENetMultiplayerPeer _peer;
public override void _Ready()
{
GDPrint.Print("<<< START SERVER >>>");
Multiplayer.PeerConnected += PlayerConnected;
Multiplayer.PeerDisconnected += PlayerDisconnected;
HostGame();
this.Hide();
}
private void HostGame()
{
_peer = new ENetMultiplayerPeer();
var status = _peer.CreateServer(_port, _maxPlayerCount);
if (status != Error.Ok)
{
GDPrint.PrintErr("Server could not be created:");
GDPrint.PrintErr($"Port: {_port}");
return;
}
_peer.Host.Compress(ENetConnection.CompressionMode.RangeCoder);
Multiplayer.MultiplayerPeer = _peer;
GDPrint.Print("Server started SUCCESSFULLY.");
GDPrint.Print("Waiting for players to connect ...");
}
private void PlayerConnected(long id)
{
GDPrint.Print($"Player <{id}> connected.");
}
private void PlayerDisconnected(long id)
{
GDPrint.Print($"Player <{id}> disconected.");
}
}
Run the Server:
Run your dedicated server scene.
You'll now have a dedicated server running, waiting for client connections.
Setting up the Client
Setting up the client follows the same logic. Please setup a new project with a main scene, that contains a Join button.
Create basic
MultiplayerController
:- Create a new private variable
_playerId
to store theplayerId
once e successfully creates a connection to the server
- Create a new private variable
using Godot;
using System;
using System.Linq;
public partial class MultiplayerController : Control
{
[Export]
private int _port = 8910;
[Export]
private string _ip = "127.0.0.1";
private ENetMultiplayerPeer _peer;
private int _playerId;
public override void _Ready()
{
Multiplayer.PeerConnected += PlayerConnected;
Multiplayer.PeerDisconnected += PlayerDisconnected;
}
private void PlayerConnected(long id)
{
GDPrint.Print(_playerId, $"Player <{id}> connected.");
}
private void PlayerDisconnected(long id)
{
GDPrint.Print(_playerId, $"Player <${id}> disconnected.");
}
}
Initialize Networking
Create new methods
ConnectionSuccessful()
andConnectionFailed()
and set them as event handlers for the respective events ofMultiplayer
. These events will only be triggered for the current client.Add a new
ConnectToServer()
method which contains all the necessary logic to initialize a peer as a client and connect to the server.(Optional) Add a new
OnJoinPressed()
method and connect it to a Join button in your Godot UI. You can use this button to trigger connecting to the server
using Godot;
using System;
using System.Linq;
public partial class MultiplayerController : Control
{
[Export]
private int _port = 8910;
[Export]
private string _ip = "127.0.0.1";
private ENetMultiplayerPeer _peer;
private int _playerId;
public override void _Ready()
{
Multiplayer.PeerConnected += PlayerConnected;
Multiplayer.PeerDisconnected += PlayerDisconnected;
Multiplayer.ConnectedToServer += ConnectionSuccessful;
Multiplayer.ConnectionFailed += ConnectionFailed;
}
private void ConnectionFailed()
{
GDPrint.Print("Connection FAILED.");
GDPrint.Print("Could not connect to server.");
}
private void ConnectionSuccessful()
{
GDPrint.Print("Connection SUCCESSFULL.");
_playerId = Multiplayer.GetUniqueId();
GDPrint.Print(_playerId, "Sending player information to server.");
GDPrint.Print(_playerId, $"Id: {_playerId}");
RpcId(1, "SendPlayerInformation", _playerId);
}
private void PlayerConnected(long id)
{
GDPrint.Print(_playerId, $"Player <{id}> connected.");
}
private void PlayerDisconnected(long id)
{
GDPrint.Print(_playerId, $"Player <${id}> disconnected.");
}
public void ConnectToServer()
{
_peer = new ENetMultiplayerPeer();
var status = _peer.CreateClient(_ip, _port);
if (status != Error.Ok)
{
GDPrint.PrintErr("Creating client FAILED.");
return;
}
_peer.Host.Compress(ENetConnection.CompressionMode.RangeCoder);
Multiplayer.MultiplayerPeer = _peer;
}
public void OnJoinPressed()
{
ConnectToServer();
}
}
Compression Types
The selection of the appropriate compression type depends on the nature of your game and the characteristics of the data being transmitted. Experiment with different compression modes to find the right balance between compression ratio, computational overhead, and network bandwidth.
The compression mode must be set to the same value on both the server and all its clients. Clients will fail to connect if the compression mode set on the client differs from the one set on the server.
For more in-depth information you can take a look at the following article:
Decoding Data Efficiency: A Comprehensive Guide to Compression Types in Godot Multiplayer Networking
GDPrint
GDPrint is just a small wrapper around GD.Print(), to be able to easily add additional information like timestamps or unique player IDs to print statements.
using System;
using Godot;
public static class GDPrint
{
public static string GetTimestamp()
{
return DateTime.Now.ToString("dd.MM.yyyy-HH:mm:ss");
}
public static void Print(string message)
{
GD.Print($"{GetTimestamp()}: {message}");
}
public static void Print(int id, string message)
{
GD.Print($"{GetTimestamp()} - <{id}>: {message}");
}
public static void PrintErr(string message)
{
GD.PrintErr($"{GetTimestamp()}: {message}");
}
public static void PrintErr(int id, string message)
{
GD.PrintErr($"{GetTimestamp()} - <{id}>: {message}");
}
}
Testing Locally:
Run your Server Project:
- Check the console output to see if we are listening for player connections.
Run your Client Project:
Hit the Join button in your Godot UI
Check the console output to see if we successfully connected to the server.
References
For more in-depth information check the following links:
Basics of Multiplayer in Godot 4! C# Godot Basics! - by FinePointCGI
Decoding Data Efficiency: A Comprehensive Guide to Compression Types in Godot Multiplayer Networking
Conclusion:
Congratulations! You've set up a dedicated multiplayer server in Godot 4 using C#. In just a few minutes, you've unlocked the potential for robust, scalable multiplayer experiences. Stay tuned for more deep dives into Godot's capabilities in our upcoming posts.
Happy coding and multiplayer gaming