ENet synchronization errors

Godot Version

Any

Question

Does ENet not handle nodes on peers properly or am I missing something?

I seem to need to write a whole bunch of boilerplate code for every networked node to ensure that it’s properly synchronized and it doesn’t cause any errors when freed.

Here’s an example:
Whenever the server spawns a node, that node can send an RPC that arrives on the client before the node is replicated, causing a node not found error. I can get around this by writing what seems to be a whole bunch of pointless code which sends an RPC from the client to the server telling it that the node is ready. It’s a similar thing when freeing a node from the server, previously sent RPC calls arrive late and cause node not found errors.

To be clear that is not an ENet problem. It’s more like an Multiplayer API (MAPI) problem. but I would use multiplayer nodes, I don’t see any errors when using those.

You could potentially probably try and change the MAPI behavior to work how the multiplayer nodes get worked. queue, or drop packets, to nodes that haven’t been instantiated yet on the client peers.

I don’t know how it all works, but if you want to dig into the default MAPI look at the SceneMultiplayer and its internal helper classes for scene replication.

The point I was trying to make is that this should be default behavior. Otherwise the debugger gets cluttered with pointless errors. I’ll look through the MAPI documentation just because I want to learn more, but having to reimplement spawners and synchronizers doesn’t sound fun.

I’m just trying to point you in the right direction, and it already is the default behavior if you use the Multiplayer nodes.

I think it’s entirely fine to role your own RPC mechanisms, but you will need to probably extend and define your own multiplayer API to build the packet handling behavior you desire.

I have looked at how SceneMultiplayer is built a little, and the multiplayer nodes are tightly integrated with SceneMultiplayer.

Well I still get the errors cluttering my debugger so it is not the default behavior. You could even see the issue on the other forum post I made. That took me days to figure out the root cause of these errors which even lead to unexpected behavior.

I also looked at the docs and source code and wasn’t able to find any way to alter this behavior other than to edit the source code itself which means I’d need to recompile the engine. That’s a silly amount of effort for something so simple.
The errors come from functions in the cpp files which specifically check whether a node exists and throw errors if it doesn’t. There’s no way to override this behavior.

I appreciate your insight and the fact that you want to help and discuss the issue is very kind of you.

In my multiplayer game, I have only one RPC out of convenience in my entire game, and I could convert it to a syncer. Everything else is synchronizers and spawners. Even score boards, Player configs, and level selection.

If you organize appropriately you don’t need any manual RPCs. And you can write code with very little visible multiplayer code and leverage component design principles.

If this is a spawner issue. Then there probably is a problem with the spawner parent taking to long to load to be able to spawn a node to a child spawner. You should remove that dependency.

For instance you have spawn locations on a level. There are many ways to go about this but you could just have a level API to request the spawn location and position the spawning node accordingly in a static sibling spawner. This can make the player spawner a static path that doesn’t depend on the level being loaded.

How would you synchronize actions? The action needs to be performed on the server and sent to clients with an RPC call.

RPC functions are meant to work alongside spawners and synchronizers anyways. There’s no reason to make temporary workarounds wasting time and effort. The spawner is a node, nodes are part of a tree and scenes. Making it static goes against all of that.

I take a central authority approach, client peers send only input to the server and the server uses that input like a local player.

My player node has an input component that is synchronized. When input arrives I just pass down an optimized bit mask for buttons and vectors for movement directions to the local/remote player node to act on.

I also allow the local player to also use the input while providing a state buffer (extending a synchronizer) on the player node to smooth incoming state and provide client side prediction.

Anyway, I will say, assuming that child spawners are not being loaded in time because the parent scene takes multiple frames to load, It is a problem that could probably be fixed by Godot devs.

There are object configurations within the SceneMultiplayer API that could be changed to indicate that a scene has fully loaded rather than, I guess, a single boolean that says, “oh, we have spawned it.” But doesn’t know it hasn’t it fully loaded yet.

Which makes me think that you could potentially use preload over load for level scenes. If you are okay with the startup time of the game.