MultiplayerSynchronizer vs RPC's

Godot Version

4.4.1

Question

Im trying to make a simple multiplayer game with a headless server and some clients. The clients each controll a player which should be able to shoot stuff. Im thinking like a small PvE arena battle.

Searching around for what to think about when developing multiplayer games i’ve learnt that Synhronizing dynamic, real-time game states seems a bit tricky. Examples of solutions are the so called “Client-side prediction” or “Deterministic lockstep” if i’m not wrong?

So im wondering:

  1. Does the MultiplayerSynchronizer utlize some of these techniques behind the scenes, that is, is the Synchronizer 100% reliable for real time dynamic updates of game states, or does one have to roll these “Fences” yourself?
  2. Lots of tutorials i’ve come across seems to use both Synchronizers and RPC’s for sending data across peers, but then, the RPC’s are only function calls while the Synhronizers sync data. So should RPC’s only be used for data that should’nt be synchronized?

You will use both.

MultiplayerSynchronizers are great for data that must be synced often with little “fuss”, like positions and rotations are updated and only need to be copied between clients. MultiplayerSynchronizers fill the gap that this example would otherwise litter your scripts.

@rpc()
func _update_position(new_position: Vector3) -> void:
    position = new_position

Calling this every frame is also much slower than the MultiplayerSynchronizer’s C++ backed implementation.

You still need to implement prediction yourself if you want it, and the synchronizer has some signals that significantly help in that endeavour.

I would strictly use one or the other, but recommend multiplayer synchronizers as they require i specific coding pattern to utilize them correctly. And if done correctly you should have a minimal amount of multiplayer code in your regular nodes other than some authority handling.

RPCs are very easy to use/abuse and can easily make your code hard to understand.

Also dont forget multiplayer spawners, these work with synchronizers specifically, and share replication data. I would not mix RPCs and spawners as rpcs do not share replication data with the multiplayer nodes.

RPCs are the more bare bone option, you will need to devise everything from scratch. The multiplayer nodes give you network visibility options and facilitate node spawning. RPCs allow you to call functions, multiplayer nodes handle data only.

I think the biggest problem multiplayer nodes face is during a large resource spawn. I think its more of a deeper engine issue, but somehow if a spawner tries to sync a large scene on a remote peer that hasn’t loaded it yet, ReaourceLoader will turn asynchronous and the host will start sending synched data to a node that doesnt exist yet on the remote peer. Rpcs will still have to solve this problem regardless.

There are three solutions:

  1. deterministic lockstep, requires hardware determinism which is really hard if your clients are not on the same hardware, like a console. ( Unless its non-physics based game like checkers. )

  2. Snapshot interpolation, the host provides world snapshots that clients playback, interpolating objects between two know world states.

  3. State synchronization, the host provides frame data and the client runs its own local simulation.


Client side prediction is a feature that allows players to extrapolate/guess what the server will return, “rubber banding” the player if the client guesses wrong, usually during poor network quality.

Also you can checkout netfox which is a free addon with these features already implemented.

I know you said “a simple game” but network quality in a multiplayer game is probably one of the most complex undertakings in any game.

Good luck!

For example, I synchronize velocity quite often, but then occasionally sync the position as well to prevent snapping around. Then I use is_equal_approx for position, and if not, I use Vector3.move_toward to move the character towards the actual position. This way it actually looks pretty smooth for the most part and corrects smoothly without much jumping around at all.

It’s kind of a mix between 2 and 3, and I use the calculated rotation and another relative velocity (calculated) to then feed the animation blend tree.

In my case smoothness was more important than complete accuracy (no PVP)

1 Like