#RPC calls and client/server node structure

45 messages · Page 1 of 1 (latest)

rare whale
#

Hi there, I have a client/server FPS game where we have to separate projects for both client and server. I have a few questions about how to structure the code.

For example, let's say on the client we have this structure:
Main
---- HUD
---- ---- ChatBox

Now when a client types a message and presses enter, we could have an RPC call directly in chatbox.gd. If we look at the RPC call documentation:

Sends a remote procedure call request for the given method to peers on the network (and locally), optionally sending all additional arguments as arguments to the method called by the RPC. The call request will only be received by nodes with the same NodePath, including the exact same node name. Behavior depends on the RPC configuration for the given method, see rpc_config and @GDScript.@rpc. Methods are not exposed to RPCs by default. Returns null.

Due to the restraint that the request will only be received by nodes with the same NodePath, then that forces the server to also at least have the same structure :

Main
---- HUD
---- ---- ChatBox

so that it can receive the rpc call on the server side so that the NodePath matches.

My question/problem is that this doesn't really make sense for the server to have a hud or a chatbox since it's simply a headless server which runs, so what could I do in this situation?

astral tiger
#

You could put the functions on a different node

rare whale
#

Which node would we put them on?

#

The closest ancestor in both trees would be main, but then main would be polluted with random rpc calls...

pliant flicker
#

Create a autoload script named something like "NetworkManager" on both client and server then where you call the RPC you replace it with NetworkManager.ChatBoxSubmit() and move the RPC call to the NetworkManager

rare whale
#

So how would I keep the NetworkManager organized and not just a file full of random rpc calls?

astral tiger
#

You wouldn't necessarily put all of them on that node. A lot of nodes will probably need to be the same by necessity, like the players, enemies, etc

#

You could also have a dedicated ChatManager node, and make different nodes for each category of action you have

pliant flicker
#

By structuring the data you send well.

In my implementation of CSP I have 3 RPCs,
Server to all Clients
Server to Single Client
Client to Server

You also would only need to move stuff to the NetworkManager script if the node doesn't exist on the Server

rare whale
#

Ok, so you're suggesting I have something like this as an autoload:

#

Then for each of the children I can have their own scripts which have rpc calls contained within them?

#

That way I can stay organized while also minimizing the structure mirroring due to rpc calls?

pliant flicker
#

👍 Looks good, having a clean structure helps alot with networking and makes debugging much easier

rare whale
#

One last question, assuming that we added the function send_message to the server Network/Messaging node, then on the client we do rpc("send_message", ...) it will only work if the client also has this function on their own Network/Messaging node?

astral tiger
#

Yes, it needs to exist on both

rare whale
#

Can anyone explain the rationale behind that?

astral tiger
#

This wasn't necessarily required in Godot 3, but Godot 4 requires it so that it knows exactly what could be sent. It uses this to make it more efficient

rare whale
#

Ah I see, I really appreciate your answers, both of you

astral tiger
#

Godot networking is also kind of designed around using the same project for both client and server. You don't have to do it that way, but it's designed to easily support it

rare whale
#

I guess one other thing I was thinking about is that if we have a client project and also a server project, but they both need to have the same Network node, how do I keep it updated in both projects without copy pasting it every time I make a change? (other than merging the two projects together)

pliant flicker
#

I don't think there is a easy way of doing that in godot unfortunately that is often left up to package managers or source control (git) which isnt really worth the effort than just ctrl c ctrl v. but yeah it is a pain

astral tiger
#

The script wouldn't necessarily need to be the same, just have the same rpc function signatures

#

Like that send_message function could be empty on the client

rare whale
#

I see

#

Ok, so I can save a bit of time

rare whale
#

@pliant flicker So I ended up creating the network scene like this:

#

I added it on both client and server and made sure it autoloads

#

I have the function send_message inside of Messaging.gd, but how would I call this from anywhere as you mentioned?

pliant flicker
#

need global variable to be ticked

rare whale
#

Oh so you're not making the scene an autoload, you're just making the script an autoload?

#

I guess I curious about how I'll be able to call Network.ServerToClient.Messaging.send_message()

pliant flicker
#

Is the entire scene autoload? you might be able to do Network.find_node("Messaging").send_message() the way you have it set up

#

not sure about the syntax with autoloaded scenes but should still work similar to just having the script autoloaded

rare whale
#

So the way you have yours is just that every rpc function call is located in NetworkManager.gd?

pliant flicker
rare whale
#

Also, I guess send_message wouldn't be the rpc call itself, so there would be another send_message function inside there which is the real rpc call?

pliant flicker
#

yeah I do

NetworkManager.my_function();

func my_function():
  rpc("sv_my_function");

func sv_my_function():
  pass
#

guess it is a bit awkward but better than duplicating the scene tree when it is not needed on the server

rare whale
#

Ok, I've come up with a really nice solution to this problem @astral tiger @pliant flicker

#

So what you do is that you create a Network scene like this where each node has a property which is a link to the other nodes in the structure:

#

Then you can hook into the new rpc syntax to just literally have to do this to call your rpc functions:

#

No duplicate functions anymore @pliant flicker

#

Oh yeah and make sure that Network.tscn is autoloaded, not Network.gd

#

Tested in 4.0-stable