Multiplayer with dedicated server - best practises for seperating client/server

Hi!
Whats a good / working practise to seperate client and server code?
Since I can use the “with server” directive in code I guess I should put all critical game logic into code (and wrap it with the directive). I still want to use blueprints for some (many) things.
Now what I dont know how can I seperate these 2 worlds so that I wont run into Problems?

Would this be a decently working scenario: Create BP GameMode. Create a cpp class GmServerHelper. Create variable in GameMode that is a reference to a helper who takes a GameMode as reference.

In the helper functions such as players losing health are obviously going into the helper like…
funcLoseHp(*dude)
{
#with server
remove dudes hp
#endwith server
}
Now the server will remove the dudes hp
But I think I will get really bugge behavior with replication if im not careful am I right?
For example if I would do this with player movement the whole greatness of predicting and executing player movement on client and then interpolating with the server result would get ignored…
So ofc movement wont go in there… am I getting this right?
Now things like a fog of war would this make really complicated (if I want to implement it “good” as in, the player has no information about the enemies actor… not even hidden…)
Any ideas / help? Has anyone expierience with a project that uses dedicated servers (wich wont ship to clients) and blueprints?

That’s not the way that UE4 games are made.

In Unreal Engine 4, the object structure on client and server is basically the same. The server is the one that we consider authoritative, though. All multiplayer actors have copies everywhere they exist, and they are linked through RPCs and replicated variables. A very common example is the PlayerController. On a client’s machine, it has its own PlayerController, which possesses pawns and handles input. On the server’s machine, there exists a PlayerController for every connected player, including its own if it’s a listen server. (Pretty much the only difference between a dedicated server and a listen server is whether the server is also a client.)

Typically, multiplayer involves a client calling a Server RPC from their PlayerController (which I’ll call ServerWalkForward()). When a client calls ServerWalkFoward(), UE4 will call ServerWalkForward_Implementation() on the Server. This will be where the Pawn moves around, fires its gun, and so forth. Then, all the server needs to do is send the outcome back to any relevant client. It can do this with replicated variables, Client RPCs, or Multicast RPCs.

((The server will know which client called the Server RPC, because it will happen on that specific PlayerController. If PlayerController_2 had ServerWalkForward_Implementation() called, then it knows that the client that owns PlayerController_2 called ServerWalkForward()))

Replicated variables will be set to their most recent state the next time a client cares about that actor. This is useful for simple properties, like character positions, whether a chest is open or closed, health, and so forth. Relevancy is a complicated series of rules, including how far away things are, whether they’re tagged as always relevant, and so forth. For instance, the server might not bother updating positions of enemies on the other side of the map, fifteen hallways away, for performance and even anti-cheat reasons.

Client RPCs are called on the server version of an Actor that the client owns (like the PlayerController). It forces the client to run a function on that actor. This is useful for events or other, simple commands that don’t just simply change a value. In fact, a “Reliable” descriptor exists that will guarantee RPCs called in a specific order (on a single actor) will be executed, and in that order.

Multicast RPCs are called on the server version of an Actor that exists on multiple clients, like a chest or a lamp, and will be executed on all (relevant) instances. This is useful for events that are complex and relatively short, but replicated variables should control the long-term parts. Basically, it’s possible that a client that wasn’t relevant for this actor (on the other side of the map) eventually becomes relevant (walks into the room) after the RPC has been called. Imagine a chest that, when opened, gives loot to everyone in the room, but only once. The gifting event could be an RPC, but you’d then want a replicated variable to store that the chest has already been opened, if someone else walks in the room a few minutes later.

One major advantage of Client or Multicast RPCs (over replicated variables) is that they can be unreliable. If network traffic is too harsh, UE4 can just drop some RPCs (unless they’re marked Reliable) to save bandwidth for replicated variables and other, more important RPCs. If you want an actor to do something cosmetic on all clients, but it doesn’t really matter to gameplay if it occurs, then it can be an unreliable, multicast RPC.

Now on to your question

To see whether you’re on the server, you can check the variable “Role” for the actor you’re writing code for to see if it’s “ROLE_Authority”:



if (Role == ROLE_Authority)
{

}

This doesn’t exactly mean “am I on the server?”, but it can be used in that way because variable replication goes from Server -> Client in UE4.

One good example of when you’d want to use Role == ROLE_Authority is if you want to add damage to the actor in a Tick(). You can block off a chunk of Tick() in a Role == ROLE_Authority check that computes a bit of damage for that chunk of DeltaTime, and assigns it to a replicated variable. The client won’t run it – only the server will. The client will find out what happened when the variable gets replicated (or there’s a Client or Multicast RPC in that block).

Also: GameMode does not exist on clients!

When GameMode needs to do something, it will get and write to another actor that does exist on both Server and Client, such as setting a replicated variable on GameState (or a PlayerState, or a door, etc.) or calling a Client RPC on the relevant PlayerController. Trying to get a pointer to GameMode on the client will return NULL, which will obviously crash if you try to do anything with it. It simply doesn’t exist outside of the server.

Hope this helps!

Relevant Documentation:
RPCs
Actor Roles
Network Relevancy

4 Likes