- Why use an engine for multiplayer games?
- Feature comparison
- License (killer)
- Stability & maintenance (needs work)
- Prior art
- Alternative multiplayer solutions
- Dedicated game server hosting
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.
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.
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.
|UDP||ENetMultiplayerPeer||Tugboat (uses LiteNetLib)||High-performance networking|
|WebSockets||WebSocketPeer||Bayou||Supports HTML5, does not support UDP|
|WebRTC||WebRTCMultiplayerPeer||No||Unreliable transport (UDP) in HTML5|
|Steam||GodotSteam||FishySteamworks or FishyFacepunch||Leverage Steam's free multiplayer sockets|
|Epic Online Services||WIP (A, B)||Yes||Leverage Epic's free multiplayer sockets|
|Unity Transport||No||Yes||Leverage Unity's free multiplayer sockets|
|Multi-purpose (||Multi-purpose by default|
|Unsupported||Channel index (|
Status: Good enough, very buggy
Fish-Net supports many types of primitive synchronization out of the box (
By default, Godot's synchronization only supports synchronizing primitives (
string, etc) and most built-in types (
Array (see #74391),
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:
# 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
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.
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.
Fish-Net supports configuring reliability for each variable individually with
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).
Listening for changes
Fish-Net supports the
OnChange parameter to listen for property changes.
Godot supports responding to synchronized properties using the
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.
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:
- Client-side prediction (v2 is a WIP)
- Predicted spawning out of the box.
- Transform extrapolation (requires Pro)
- Lag compensation (requires Pro)
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.
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.
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.
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.
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.
Status: Needs work, but will improve
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.
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.
More detailed benchmarks between Godot & Fish-Net need to be developed.
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.
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.
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.
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:
Outside of the land of Unity, it's the Wild West for multiplayer solutions.
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 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.
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.
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.
Join the closed beta to get your game up and running in less than 10 minutes. Supports Godot, Unreal Engine, and Unity.
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
- Updated September 18th: Include missing send rate on a per-client basis functionality