Multiplayer in Godot 4.0: On servers, RSETs and state updates
By: Fabio Alessandrelli 5 August 2021
It's time for the first update on Godot 4.0 multiplayer and networking changes.
In this post, I'll focus on the new "headless" display, and the removal of multiplayer RSETs (read below before despairing!), along with keeping you hyped with some of the new features planned or in the work.
server platform, hello headless display!
One of the emergent feature of Godot that users have discovered and really started to like is its ability to run on a headless Linux machine and act as game server.
Finally, in Godot 4.0, you will be able to run Godot with headless display (no rendering) on any desktop platform.
--display-driver headless command line argument will start Godot without rendering, like the old
server platform, no matter if you are on Linux, macOS or Windows.
Additionally, we are working on a series of "server" export options, to let developers further reduce the memory and load time footprint by replacing specific assets with "fake" files that only contains metadata. For example, a "fake" audio file will not contain any audio, but will retain informations about length, loops, etc. This means that server-side code relying on audio timing or texture sizes will still work correctly.
When building the multiplayer API, two forms of network communications were introduced: RPCs to call functions remotely, and RSETs to set variables remotely. This seemed like a good idea at the time: RSETs were a fast way to prototype, and easily make your game networked… or were they?
In reality, using RSET proved to be almost always a bad idea:
- First, you have very little control over who is setting it beside the
masterkeyword. Anything more complex than that, and you will need a function so you can base your logic on the RPC caller.
- Second, performances are bad!
Take this example from the multiplayer bomber demo:
extends KinematicBody2D puppet var puppet_pos = Vector2() puppet var puppet_motion = Vector2() func _physics_process(_delta): # ... if is_network_master(): rset("puppet_motion", motion) rset("puppet_pos", position) else: position = puppet_pos motion = puppet_motion move_and_slide(motion * MOTION_SPEED) if not is_network_master(): puppet_pos = position # To avoid jitter
It's making 2 separate
rset()s, generating 2 packets, which may arrive each at a different frame under certain conditions. Additionally, while one would think it could use a direct RSET for the
position, it actually can't. This is because it needs to add some logic to avoid jittering anyway.
Compare it with this:
extends KinematicBody2D var puppet_pos = Vector2() var puppet_motion = Vector2() puppet func _update_state(p_pos, p_motion): puppet_pos = p_pos puppet_motion = p_motion func _physics_process(_delta): # ... if is_network_master(): rpc("_update_state", puppet_pos, puppet_motion) else: position = puppet_pos motion = puppet_motion move_and_slide(motion * MOTION_SPEED) if not is_network_master(): puppet_pos = position # To avoid jitter
In this example, we only make 1
rpc(), reducing the network usage and latency. More importantly, we make sure that in each frame, either the client will have the old state, or a fully updated one. This avoids having an inconsistent state where the position is up-to-date but the motion isn't (or vice versa).
This is just one example which shows a common pitfall in networking.
So, to avoid tricking developers into these common mistakes (which could be unexpected for newcomers), and given the rarity of
rset() usage in general, we decided to remove
rset(). We really think you won't miss it in the end, but let us know if you feel you had a strong use case for them. Like most decisions in Godot, this is not set in stone.
If you feel a bit disappointed from this progress update, keep in mind this was just the ground work for more important changes. We have a few surprises in the works!
There's the new GDScript syntax for RPCs to talk about, the newly exposed
ordered transfer mode, channels, ENet and WebRTC improvements, and even some news about the long-awaited node replication! So stay tuned for more :)