PlayerController on Server and Client

In a multiplayer game, there is a copy of a player’s PlayerController on his machine and on the server. I’m a little bit confused on how to tell whether functions on the PlayerController are called on the server or client (or both).

PreClientTravel() has Client in the name so I assume this is called only on the client?

What about other functions on the player controller? Are they called on both the client and server? For example, BeginPlayingState() UnFreeze() ChangeState() PostSeamlessTravel()? How do we know if they are called on both the client and server, server only, or client only?

If it doesn’t have “Server” or “Client” in the function name is it assumed that it’s called on both server and client instances of the PlayerController?

The function can be called on both the server and the clients, but where it will be executed it depends on whether the function is RPCs. Non-RPCs functions are executed where they were called. You have the RPCs function marked with the UFUNCTION() macro (see in the declaration) in which you have:

  • Server - a function called on the
    client, but executed on the server.
  • Client - a function called on the
    server, but executed on the client.
  • NetMulticast - a function called on
    the server and executed on the server
    and on all clients.

In addition, whoever calls this function is also important. It’s best to see the table https://docs.unrealengine.com/en-US/Gameplay/Networking/Actors/RPCs. Usually, this function will have a server or client in the name. Read also descriptions of functions.

The PreClientTravel() function is not RPC (it will be executed where it was called) is called in ClientTravelInternal(), which is RPCs (Client type):

  • If you call the ClientTravelInternal() function on the server, this function will be executed on the client that owns this actor.
  • If you call the ClientTravelInternal() function on the client, this function will be executed on the client that owns the actor.

So where the ClientTravelInternal() function will be executed, the PreClientTravel() function will also be executed there.

To divide the code into a server and client, you can use:

if (Role == ROLE_Authority) // or if(HasAuthority()) 
{ 
    // will execute the Server
} 
else 
{ 
    // will execute the Client
} 
2 Likes

Thanks, the distinction between calling and execution makes your post one of the most comprehensive explanations for networking! (unfortunately upvoting doesn’t work for me)

This shows why “virtual functions for implementation inheritance” is so bad.
I’m not interested in whether it can be called – I’m interested in who calls it in the actual engine.
The documentation doesn’t tell me what to expect.
The way the base class is structured, mixing implementation with interface, also doesn’t tell me at all.
Is this a callback from the engine, that I’m supposed to override, or is it a function I’m supposed to invoke to make something happen? If the engine calls me, is it on the server, or client, or both?

Using less implementation inheritance and a more structured approach to interfaces would make this clearer, and splitting out “interface for client objects” versus “interface for server objects” would make it even better.

Death to implementation inheritance! And, in the meanwhile, if there’s a good write-up (other than the source) on how these functions are supposed to be called/used/overridden, that’d be super helpful.

1 Like