hero

Is Godot 4's Multiplayer a Worthy Alternative to Unity?


TL;DR

Godot 4's multiplayer (a.k.a. "scene replication") has the foundations required to build hobbyist multiplayer games (e.g. RPC & replication), but lacks some features required to build a production-ready game (e.g. sync data types & client-side replication). It is also buggy and still very young.

Given the recent rapid growth of the Godot development fund, it's bound to get better very soon.

If you're building a game as a hobby, dive head-first into Godot's multiplayer; it's easy to get started. If you're building a game as a business, you'll need to (a) prepare to contribute bug fixes to Godot, (b) write an in-house multiplayer system & transport, or (c) use a different engine.

This post specifically uses Fish-Net for Unity library for comparison.


Introduction

Given the recent events in the world of Unity, Godot has exploded in popularity and money has poured into the Godot Development Fund as game developers search for a viable alternative. Developers have been asking us about our experience with Godot's multiplayer compared to Unity's, so we aggregated our thoughts into this article.

We will compare developing multiplayer games in Unity with Godot's high-level multiplayer API. We'll be looking specifically at using Unity with the Fish-Net library, since it's arguably the most comprehensive free option compared to Mirror and Unity's native NGO.

Godot's multiplayer (a.k.a. "scene replication") was rewritten from the ground up for Godot 4.0, released just 6 months ago. It's still very young, but is a very promising foundation.


Why use an engine for multiplayer games?

Many multiplayer games write custom game servers from scratch instead of using an engine's multiplayer API. Before we dive in to the comparison, it's important to address why you might consider using an engine's high-level multiplayer API in the first place.

Engines are an excellent choice for many multiplayer games because they:

  • Provide the core functionality required to build a multiplayer game out of the box (e.g. physics, platform support, transports)
  • Makes code reuse between client & server easy
  • Handles the low-level networking details for you (e.g. serialization, transport, etc)

While this guide specifically explores Godot's high-level API, you can also (a) use Godot on both the client & server with your own multiplayer system, or (b) use Godot on the client with your own custom game server.


Feature comparison

Transports

Status: Mature

TransportGodotFish-NetImportance
UDPENetMultiplayerPeerTugboat (uses LiteNetLib)High-performance networking
WebSocketsWebSocketPeerBayouSupports HTML5, does not support UDP
WebRTCWebRTCMultiplayerPeerNoUnreliable transport (UDP) in HTML5
SteamGodotSteamFishySteamworks or FishyFacepunchLeverage Steam's free multiplayer sockets
Epic Online ServicesWIP (A, B)YesLeverage Epic's free multiplayer sockets
Unity TransportNoYesLeverage Unity's free multiplayer sockets

Ownership/authority

Status: Mature

Fish-Net's ownership and Godot's authority models are nearly identical.

RPCs

Status: Mature

Godot's RPCs will feel familiar to developers who have used Fish-Net's RPCs.

Fish-NetGodot
[ServerRpc]@rpc
[ServerRpc(RequireOwnership = false)]Unsupported
[ObserversRpc]@rpc("any_peer", "call_local")
[ObserversRpc(ExcludeOwner = true)]@rpc("any_peer", "call_remote")
[ObserversRpc(BufferLast = true)]Unsupported
[TargetRpc]@rpc("any_peer") + my_rpc.rpc_id(client_id)
Multi-purpose ([ObserversRpc][TargetRpc])Multi-purpose by default
Channel.Unreliable@rpc("unreliable")
Unsupported@rpc("unreliable_ordered")
Channel.Reliable@rpc("reliable")
[ServerRpc(RunLocally = true)]@rpc("call_local")
[ServerRpc(DataLength = 3500)]Unsupported
UnsupportedChannel index (@rpc(3))

Synchronizers

Status: Good enough, very buggy

Developers use to Fish-Net's sync types will find that Godot's default MultiplayerSynchronizer lacks a lot of features, but is still a pleasant experience to use.

However, many developers choose to avoid Godot's native synchronizer because of its lack of stability. (A – partially resolved by #75467, B)

Sync types

Fish-Net supports many types of primitive synchronization out of the box (SyncVar, SyncList, SyncHashSet, SyncDictionary, SyncTimer).

By default, Godot's synchronization only supports synchronizing primitives (float, string, etc) and most built-in types (Vector2, Color, Array (see #74391), Dictionary, etc). allow_object_decoding can be enabled to allow synchronization of more complex types. Using this property is not recommended since it creates the potential for RCE exploits.

To manually synchronize your own complex types, it's possible to use a getter/setter to encode a PackedByteArray, like this:

custom_sync_type.gd

# Node state
var weapon: int = 0
var ammo: int = 100

# Custom encoding & decoding

var sync_state:
  get:
    var buf = PackedByteArray()
    buf.resize(6)
    buf.encode_half(0, position.x)
    buf.encode_half(2, position.y)
    buf.encode_u8(4, weapon)
    buf.encode_u8(5, ammo)
    return buf

  set(value):
    assert(typeof(value) == TYPE_RAW_ARRAY and value.size() == 6)
    position = Vector2(value.decode_half(0), value.decode_half(2))
    weapon = value.decode_u8(4)
    ammo = value.decode_u8(5)

You'll likely find yourself writing repetitive encoding & decoding code if building a game with complex data types (e.g. board games, strategy games).

Send rate/replication intervals

Fish-Net supports configuring send rate for individual properties.

Godot also supports configuring the send rate with MultiplayerSynchronizer.replication_interval for synced properties and MultiplayerSynchronizer.delta_interval for watched properties. However, this is only supported at the node level; you can't customize for each property individually. You can work around this issue by creating multiple separate MultiplayerSynchronizer nodes.

Neither Fish-Net nor Godot supports configuring the send rate of objects on a per-peer basis. This is important for sending data less frequently when further away from a peer.

Visibility/observers

Fish-Net has a robust system for automatically filtering what entities get replicated using ObserverCondition. They include 6 conditions available out of the box that you can enable with one line of code.

Godot lets you either (a) set visibility for specific peers (MultiplayerSynchronizer.set_visibility_for) or (b) provide a custom callback for filtering peers (MultiplayerSynchronizer.add_visibility_filter). The visibility filter is similar to Fish-Net's ObserverCondition, but requires more boilerplate code to implement.

Configuring reliability

Fish-Net supports configuring reliability for each variable individually with Channel.Reliable and Channel.Unreliable.

Godot approaches this slightly differently. Variables can either configured to "synced" (i.e. be sent each update, sent unreliabilty) or be "watched" (i.e. be sent only when changed, sent reliably).

Godot sync/watch

Listening for changes

Fish-Net supports the OnChange parameter to listen for property changes.

Godot supports responding to synchronized properties using the MultiplayerSynchronizer.synchronized and MultiplayerSynchronizer.delta_synchronized signals. However, these are only emitted after the property has been updated, so you cannot access the previous value. Instead, you can use GDScript's native getter & setters to be able to access the previous property value.

Client-side prediction, lag compensation, and predicted spawning

Status: Not supported

Client-side prediction is a deal breaker for most serious multiplayer game developers. It won't be fun for most players if a multiplayer game feels choppy on a high-latency connection.

Fish-Net shines when it comes to all the extra goodies required to make your game feel buttery smooth. They provide:

Godot 4's multiplayer replication is very young, so none of this is provided out of the box. It would require a lot of work to implement these features yourself.

Authentication

Status: Mature

Both Fish-Net's authenticator & Godot's authentication callback support the same functionality. Fish-Net does provide some handy authenticators out of the box, but they're not difficult to re-implement in Godot.

No-code multiplayer prototyping

Status: Proof of concept

One of the core goals of Godot's replication system was to allow for "(almost) no-code prototyping" of multiplayer games. They achieve this by enabling you to enable replication just by adding MultiplayerSynchronizer and MultiplayerSpawner nodes, but you'll still need some boilerplate code to connect & start a server.

There's no equivalent in Fish-Net.

It's debatable how much time this saves in the long run, but the effort to lower the barrier of entry for building network replication without compromising flexibility is admirable.

Priority

Status: Not supported

Network prioritizing is important for games with a lot of data to send, but need to ensure that the most relevant data is sent first (e.g. open world games). Alex Forsythe has a great explanation of how this system works in Unreal Engine here.

Both Fish-Net and Godot lack support for prioritizing synchronization updates. Tuning nodes' replication intervals can mimic similar results, but it's not as powerful as a dedicated prioritization system.


License

Status: Killer

Always make sure to closely inspect the license of software you include in your game.

Fish-Net uses a non-standard license. Their website states:

There are no restrictions for personal and commercial use.

Fish-Net also provides a Pro version which has its own limitations.

Using Fish-Net requires using Unity. Do with that what you will.

Godot is licensed under the permissive MIT license, which is an OSI-approved license. Read more about the MIT license. Godot is also governed by the Godot Foundation non-profit. All that to say, if Godot does the job for your game, it's a very safe bet regarding licensing.

Ask your lawyer or do your research if you have more questions.


Stability & maintenance

Status: Needs work, but will improve

Fish-Net is a mature library that has been used in many games. The developer takes stability and breaking changes very seriously and offers LTS releases.

Godot's multiplayer is still very young. While trying to recreate Fish-Net's benchmark in Godot for this blog post, I could not connect more than ~40 CCU to a server without players sporadically disconnecting. For comparison, Fish-Net's lowest benchmark result measures 100 CCU. We'll publish the issue for this soon.

The Godot GitHub has 66 open issues relating to networking bugs, 31 of which are over a year old. This indicates there's limited bandwidth to work on Godot's multiplayer system, but expect this to improve soon given the recent growth in funding & interest.


Performance

Status: Untested

Godot's performance deserves a blog post of its own.

From a high level, Unity benefits from:

  • Language C# is generally faster than GDScript (but it's getting better).
  • Maturity Unity has a long history of performance optimizations, while Godot 4 — which rewrote most of the engine — is still very young.

Godot benefits from:

  • Architecture Godot's engine is architected to be an event-based engine, meaning slow user scripts are only run in response events instead of every frame if possible (read more). Unity frequently relies on C# logic being called every update.
  • C++ multiplayer Multiplayer system is written in C++ which has the potential to be faster than Fish-Net's C# implementation (needs benchmarks).
  • Multilingual Godot add-ons can be written in any language (GDScript, C#, C++, Rust), enabling developers to selectively optimize performance-critical code.
  • Contributions Anyone can contribute to Godot's engine, meaning that performance issues can be resolved by anyone instead of waiting for Unity employees to fix them.

I recommend reading this overview of optimization strategies for the Godot engine. [Juan Linietsky] recently posted a great thread on X with resources about Godot's architecture.

More detailed benchmarks between Godot & Fish-Net need to be developed.


Support

Community

Status: Manture

Fish-Net has an active Discord and is responsive to issues on GitHub. There is no forum, and Reddit posts frequently go unanswered.

Godot has a well-organized Discord, a very active Reddit, and developers are responsive to GitHub issues. However, questions about multiplayer frequently go unanswered since few developers have extensive experience with it. Additionally, the huge influx of developers from Unity seem to have overwhelmed the Discord & Reddit with questions without enough people to answer them. This will likely improve over time.

Commercial

Status: Unknown

Most large studios require good support around their tooling to ensure they can focus on shipping their game instead of fixing bugs in other peoples' code.

Fish-Net offers professional support.

Godot also offers commercial support.

The support SLA for both support plans is unspecified.


Documentation

Status: Good enough

Fish-Net has good inline documentation and guides on its website. Regardless, I still frequently dive into the source code to understand how things work.

Godot's documentation is very well organized but lacks many important specifics about multiplayer (e.g. custom sync encoders/decoders, sync vs watch reliability, supported sync types). Much of the information is scattered between documentation, blog posts, forums, and the source code itself.


Prior art

Status: Just getting started

At the time of writing, only 1 of the games featured on their showcase supports multiplayer. (That game runs on 3.1.) Given Godot 4 – which includes the new multiplayer system – is very young, it's not surprising that there aren't many multiplayer games built with Godot.

Here are a few Godot games using Godot 4's scene replication that I could find:


Alternative multiplayer solutions

Outside of the land of Unity, it's the Wild West for multiplayer solutions.

BYO Backend

You don't have to use Godot's multiplayer system if you're building a multiplayer game with Godot.

You're able to either:

  • Use Godot on both the client & server with your own custom multiplayer synchronization & transport
  • Use Godot only on the client with your own custom game server (e.g. C++, NodeJS, etc)

Unreal Engine

Unreal Engine has a well-built native multiplayer replication system. It's arguably the only mature high-level multiplayer API outside of Unity. It's an excellent alternative for Unity developers developing high-end games.

However, Unreal Engine is not an excellent choice for many Unity developers developing casual games since it's a heavy engine that requires a lot of resources to run and is not as easy to learn.

Unreal Engine also charges a 5% royalty after $1 million in revenue, while Godot is a permissively licensed open-source project. You'll never pay a dime to use Godot.

Unreal Engine also requires C++ or Blueprints knowledge, while Godot's GDScript is much easier to learn.

HTML5 & JavaScript

HTML5 + JavaScript is a popular alternative for casual multiplayer games since JavaScript is easy to run on both the client and server (e.g. NodeJS, Bun, Deno). This lets you easily share logic between the client & server since they're written in the same language. There are a wide variety of tools for building HTML5 games, like Phaser, Three.js, PlayCanvas, and Babylon.js.

However, there is no widely used JavaScript multiplayer system similar to Fish-Net; expect to roll this yourself. JavaScript's performance is generally not as good as Unity's C# or Godot's C++ core.

Other Engines

GamesFromScratch published a great list of alternative game engines for Unity developers. However, only GameMaker, Flax, and O3DE natively support high-level multiplayer. The rest will require writing a custom multiplayer system.


Dedicated game server hosting

This section includes self-promotion.

One of the most challenging parts of building a multiplayer game is deploying & scaling dedicated game servers.

Rivet provides the features required to build a production-ready multiplayer game, including managed game servers, matchmaking, DDoS protection, and analytics.


Conclusion

While Godot 4's multiplayer has the basics you need for hobby-level projects, it's still a work in progress for serious, production-ready games. There are gaps like data type syncing and client-side replication that you'd have to build yourself. That said, the Godot development fund is booming, which suggests improvements are on the horizon.

For hobbyists, Godot's multiplayer is a solid choice to get your feet wet. If you're in it for the long haul, be ready to either contribute to Godot's codebase, roll your own multiplayer framework, or consider a different engine altogether. Keep in mind Godot's doing a lot with less—compared to giants like Unity—and it's only going to get better.


  • Updated September 18th: Clarified the comparison to Fish-Net in the TL;DR.
  • Updated September 18th: Note that part of the stability complaint has been fixed
  • Updated September 18th: Clarify that arrays & dictionaries can be synced by default, list affected issue
  • Updated September 18th: Add mention of synchronized and delta_synchronized signals
  • Updated September 18th: Include missing send rate on a per-client basis functionality
  • Updated February 7th 2024: Removed information about closed beta. Rivet is now open to the public!