Do I truly need to use the multiplayer spawner/synchronizer nodes?

Godot Version

4.3 Stable

Background

So I’ve been learning the ins and outs of multiplayer development over the past few weeks as I’m developing a MVP. It’s challenging, but fun and fulfilling. However, I’m running into an issue with codebase structure.

You see, the multiplayer synchronizer/spawner are useful nodes, but for me, they reduce codebase readability by a lot.

I like my code to be structured in logical order and have a single use principle. This is so I can troubleshoot issues, remember what my code does, and easily build upon it.

With the multiplayer sync/spawn nodes, I have to go through different menus in order to deduce how something I programmed works. Splitting it between code lines and the GUI.

Example:
image

I have to manage another menu in order to understand what the function does. The needed info is out of plain sight.

I understand how these nodes work and why they’re useful, but I much prefer having it all in one place.

Then, for the MVP I’m developing, it uses a dedicated server model because it’s a PvP shooter. I need to have the server to have authority at all times. Having to tangle with the multiplayer synchronizer to do this is not ideal for me or the MVP.

The Question

Can I make a multiplayer game without the multiplayer spawner and synchronizer?

What I’m thinking using only RPC called in order to replicate actions, properties, and variables. This sounds cumbersome, but it makes my code way more readable to me.

And for multiplayer authority, I can make 2 separate RPCs. One that the client calls in order to trigger the action, then a second one that calls on the server to check if this action can happen or not.

This (in theory) will allow me to create server-authorities code and have 100% control over what is called where.

I can refactor my entire codebase in 30 minutes because I prioritized implementing all necessary multiplayer functionality before any complex gameplay element.

Let me know what you think about this idea.

It is doable. I would look at any Godot 3 tutorials that didn’t have multiplayer nodes. The RPC API is the same between 3 and 4. And multiplayer nodes use RPCs under the hood.

The thing I like most about the multiplayer nodes is that you can almost write any player code like it isn’t multiplayer, and just slap a node to get it to sync state. This is the power of component architect.

You could replicate this with RPCs and follow the component design pattern. But you are more after code locality.

You could also setup the multiplayer nodes entirely in code as well if you don’t like the UI if you really don’t want to do the RPC thing and invent your own wheels.

2 Likes

Thanks for the advice.

My game specifically is multiplayer with PvP shooting at it’s core. So it needs to be able to perform advanced functions like rollback, interpolation, anti-cheat measures, etc, that aren’t built into the multiplayer sync/spawn nodes.

Using a series of controlled and localized RPCs seem to be the best way to go about this specific project.

It’s not impossible with the nodes but I’m not sure how scalable it is.

I approached client side prediction by saving the player state in a temporary value within a MultiplayerSynchronizer then sync those temp values. As they arrive on the clients, I buffer the state into previous frames and perform client side prediction with buffered data.

This allows me to avoid multiplayer code on the player script, and by having a generalized MultiplayerSynchronizer that I can just throw onto any player character and have client side prediction.

This is basically running the feature per entity, so if there was a way to optimize for many entities doing it per node would make it impossible.

Although I haven’t completed any reclamation code yet so I don’t know how that will work out.

I really want to just say you should go for it. And see how it goes, you will learn a lot.

1 Like

I take it back. You need the multiplayer spawner node because RPCs cannot serialize objects over a network. That’s pretty annoying to be honest.

To not use the sync node, I have to serialize the object I intend to spawn, then reserialize the object for all other peers. I’m not quite sure how to do that yet.

Serializing the object shouldnt be neccessary as you can just call the method thats spawning the node as an rpc call:

@rpc("call_local")
func spawn_stuff():
    var my_node = MY_NODE.instantiate()
    add_child(my_node)

and if neccessary you can create a dictionary or list with all packedscenes and access them with indeces and pass these indeces with an rpc-call

1 Like

I tried it, it almost works.

Code:
image

The issue is I get these 2 errors when spawning a second client.

E 0:00:02:0872 get_node: Node not found: "Main/Players/1599044655/MultiplayerSynchronizer" (relative to "/root"). <C++ Error> Method/function failed. Returning: nullptr <C++ Source> scene/main/node.cpp:1792 @ get_node()

E 0:00:02:0872 process_simplify_path: Parameter "node" is null. <C++ Source> modules/multiplayer/scene_cache_interface.cpp:118 @ process_simplify_path()

The second player doesn’t spawn at runtime.
image

Going to put a closing reply here to add closure for any future readers.

Applying your own spawn logic that spawns/despawns objects on the server is 100% possible. However, I’ve found the the multiplayer spawner is a far more effective way to sync spawning on all clients due to it’s ease of use.

The code locality isn’t an issues for me anymore now that I’ve got a better understanding of the multiplayer spawner node.

Edit:
You don’t need the multiplayer synchronizer node to replicate actions over the network It’s pretty simple as well. I prefer doing this so I can better establish server authority. Also, even with 25 ping, the sync node falls flat.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.