I’m trying to understand how RPCs and their arguments work. (i.e. how they transfer information, to who, and why)
I read the docs front to back, however, I’m trying to understand how it applies to server-authoritative gameplay where clients only send inputs to the dedicated server. As the docs seem tailored for peer-to-peer models.
Here are my specific questions:
What does an argument-less RPC do exactly? Base functions, default values/arguments, etc.
With the “mode” argument, what’s the difference between “authority” and “any_peer” in relation to dedicated server authority? Is it better to check for authority via if not multiplayer.is_server(): return?
With the “sync” argument, what’s the difference between the “call_remote” and “call_local” in relation to dedicated server authority? Will “call_remote” still send data to all clients? Will “call_local” allow for cheating?
When “authority” (the default) only the authority can execute the rpc to all peers. When “any_peer” any peer can execute the rpc to all peers.
If the authority of the node is kept at it’s default state, then the server will have authority and only the server can execute, similar to your example, but produces an unauthorized error.
In addition to executing the function on other clients, call_local executes on the calling peer. For example an animation that is played for the client and all their peers, like firing a weapon should play the animation for the player firing and everyone else. But third person animations should only play for other clients, so they will not use call_local
In your case with a very strict dedicated server, you may not find much use in call_local, when there is a disconnect between the calling peer (your server) and the accepting clients.
I’ll never leave an @rpc blank ever. As I need to know what the RPC is doing and where at all times. Noted.
With the mode argument, I’ll make sure to only use authority as the server needs to broadcast and verify every client input. The least the client will do is call a pre-function that calls the server function. Noted.
I need more information regarding the sync argument. Could I get a more in-depth example? I’m taking notes on all of the use cases so I can apply it.
Also, an unrelated question. For the argument transfer_channel, how many channels can you realistically run at once? I was thinking 3 - 5 channels, but I’d like a second opinion.
call_remote only calls the function on other peers. Thus a call_local rpc is equivalent to this example, because it calls locally in addition to remotely.
I use call_local a ton in my game which expects the host to be just another player. Since call_local synchronizes the local client with the server by using the same functions.
So far I’ve only found call_remote useful for triggering animations, since they do not always need to be synced between the triggering player and others. A first person player will have different animations to trigger for their peers.
I exclusively use the default 0, it automatically uses separate channels for reliable and unreliable_ordered modes. I would profile my game before trying to optimize channel usage past that.
@gertkeno
For clarification, using call_remote would replicate the function to all other peers except the peer who called it. Since the server is the peer who called it, it would sync to all other clients including the one who triggered the RPC. Is that logic sound?
Also, I did not know that reliable and reliable_ordered worked on separate channels. That’s great to know.
Also, do you have any other misc multiplayer programming tips from your experiences? Every piece of knowledge is extremely valuable to me.
If the server uses a call_remote rpc, then the server will not be calling the function. It will sync to all other clients.
I am a little confused by “including the one who triggered the rpc” since in this scenario the server is triggering the rpc, not other clients, and again since the server is using (or triggering) the rpc the server won’t call the function.
only on channel 0; in which different modes is largely what you would use channels for anyways.
I’ve worked on popular Roblox games, for network bandwidth and lag reasons you’ll want to trust the client with some information. If they move forward on their device, let them think they’ve moved forward and sync that to the server. Registering each input with the server means the player is waiting round-trip to see each pace and turn go through, like playing with drunk goggles, so the game is only passable at a hard-sell sub 100ms network.
Detecting hackers usually ends up detecting poor network players more than anything. It really stings to be kicked and called a cheater for having a rural connection, roll backs instead of kicks/bans go a long way for player retention. Of course the popularity of Roblox on mobile and tablet platforms so we saw significantly more 4G lte players than hackers.
I might flip that. Critical server actions should probably also be done on the server; like respawning a player or flag the server should also know about, thus call_local. A dedicated server does need to update relevant players about a explosive barrel damaging them, but doesn’t need to animate the explosion thus the fiery boom part is call_remote.
It is strange, it took me a while to get it; again I mostly end up using call_local.
Here’s an extra comment for future readers trying to use RPCs with a dedicated server setup in Godot 4.3. This comment summarizes the thread.
Always specify the RPC’s arguments. NEVER leave it blank. The default parameters are not to be trusted and you must know what every RPC is doing at all times.
The default arguments are: @rpc("authority", "call_remote", "unreliable", 0)
For the mode argument:
Use authority for most RPCs, with the exception of the client input functions. In which case, use any_peer
For the sync argument:
For critical server actions, use call_local. As the server IS the local peer. So, most functions.
For non-critical server actions, use call_remote. As the server doesn’t need to know about certain details, like animations and the like.
For the transfer_mode argument:
Use reliable for critical actions such as loading, weapon switching, shooting, etc.
Use unreliable_ordered for non-critical actions that require order. Like a chat box.
Use unreliable for client actions that proc constantly, like movement.
For the transfer_channel argument:
Manage each transfer channel with care and diligence.
reliable and unreliable_ordered operate on their own separate channels adjacent to channel 0. So keep that in mind.
Immediate gameplay actions need to be on channel 0.
Secondary server functions like chat and data syncing need to be channel 1+.