How does Networking work in C++/UE4?

Hello guys.

I’m studying the UE by taking a look at ShooterGame example.

Well, there are things that I don’t understand, like this one:

The **StartFire() **function get’s called.

If I’m a client, the ServerStartFire_Implementation() gets called and it calls the **StartFire() ** function AGAIN
Since I’m still a client, the ServerStartFire_Implementation() gets called AGAIN and it calls StartFire() AGAIN and so on!

If my match is hosted by a dedicated server (thus, it’s not a player who actually hosts the match) that IF-STATEMENT will probably never be true for every player

Maybe I’m misunderstanding the concept.

Will you explain me what happens and how it actually works?

Thanks!

If you look at the .h file for that, you’ll see that Server_StartFire has the ‘Server, Reliable, WithValidation’ tags. If a function is a ‘Server’ function, then it means that the Client will tell the Server to run that function. This call to the Server is known as an ‘RPC’ (Remote Procedure Call). The call will ONLY work if the Client Player is the ‘Owner’ of that object as far as both it and the Server are concerned.

In this case, when the Client calls ‘Start Fire’, they also tell the server to call ‘Start Fire’ for this weapon so that the Server runs through (some of) the same code (in this case anyway), and actually ends up spawning the projectile or processing an instant hit. I recommend reading the following:

A new, community-hosted Unreal Engine Wiki - Announcements and Releases - Unreal Engine Forums,Using_ReplicatedUsing/_RepNotify_vars

This is strange; listen to this problem:

I start with **2 **of Health

I have these functions to increase my Health

When executed on the Server it adds me 20 of Health (that’s right)

When executed on the Client it adds me 40! That’s the twice!

It’s like if you are a client, the code get’s called twice O.o

Also, consider the DebugMessage in the top-left of the screen.
There is a call for the Client and 2 for the Server…

I’m really confused…

You should place a “return” after calling the Server version of the function. And the Health variable should replicate on the server (using the method GetLifetimeReplicatedProps that should be in your actor).

And your pickup code should run ONLY on the server, otherwise the client can send whatever value it wants on the “increasingValue” variable. What you can do is call the method to add health on the server version of your pickup, from there only the server will run the code, so the “client” version of your character can simply ignore any call coming from client-side code (basically returning if not Authority).

I suggest you take a look for the network blueprint tutorials on youtube before starting playing with multiplayer code, since it is different from the normal code used in single-player games.

At the end I changed my code like this, and it works as I want:

Hmm, how can I prevent my Pickup from cheating?

The Pickup’s increasingValue variable gets his value through this function:

After, when the Pickup gets taken I execute this function:

How can I protect that variable?

Just make sure InitPickup (and the one calling it) and OnPickup run only on the Server (ROLE_Authority) and it will be fine :slight_smile:

I did this:

For some reason, though, the functions I highlithed don’t fire on the client.

The client can’t see the Pickup disappearing, and also can’t hear the pickup sound.

You can do one out of the two:

  • Create another function to run on the client after it runs on the server to play the sound and display on hud (my preferred way).
  • If the role is not authority (the “else” for the “if”) run the client part (play sound and update hud).

For the actor, is it marked to replicate?

Mhmm, even with the else the client isn’t able to play that sound.

Should I replicate also the SoundCue variable?

Why is Networking so frustrating sometimes? …

Networking Logic, in very abstract terms should be like this:

Client

  1. Determines distance to object, pickup button is enabled, all this fun stuff …
  2. User presses the pickup key
  3. Client Sends TryPickup packet/RPC

Server
4) Server runs TryPickup with the passed parameters, which should really be nothing more than the objectId
5) Server verifies in TryPickup that you are actually within the defined Pickup range, and that the character can pickup objects, and is not dead or anything - all the can…do logic goes here
6) If that is all good the server sends a DoPickup packet/RPC to the client

Client
7) Run DoPickup, actually pick up the item. The item disappearing from the screen/map should be handled in a different packet/RPC by the server, in the class where items being created and removed is being handled, called from TryPickup on the server


What you DONT want to do is something along the lines of client tells server “I took X damage, I dealt Y damage” etc…
Similar to this in your example the client is telling the server what kind of pickup it is.

What you want to be doing is for the client to tell the server TryPickup ( pickupId )
The server has a list of pickups, takes out the relevant pickup, checks that you are actually in range, gets the type and handles everything then sends a confirmation to the client.

Classic doc from udk
https://udn.epicgames.com/Three/NetworkingOverview.html

It has everything you need to know about replication (just ignore the simulated part its not in ue4)

The easiest way is to run the collision code both Client and Server side, but only actually add Health / Ammo if you’re on the Server. This is how I handle it in my game for instance.

You really don’t want to be sending RPC’s purely for Visual / Audio things unless you absolutely have to. It’s usually best to drive that via other means. For example, you’ll notice that SimulateWeaponFire() in AShooterWeapon is NOT called via an RPC, it’s instead called when ‘BurstCounter’ is replicated from the Server (which indicates that the weapon has fired). Doing so prevents you from flooding the network with RPC’s that are doing trivial/non-important things like FX and whatnot.



void ABZGame_PowerUp::OnOverlapBegin(AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if (bActive) //Replicated Bool from Server
	{
		APawn* Collector = Cast<APawn>(OtherActor);
		if (Collector)
		{
			if (CanPawnCollect(Collector))
			{
				CollectPowerUp(GetGameObjectFromOverlap(Collector));
			}
			else
			{
				RejectPowerUp();
			}
		}
	}
}

void ABZGame_AmmoPowerUp::CollectPowerUp(UBZGame_GameObjectComponent* UniqueGameObjectComp)
{
	UGameplayStatics::PlaySoundAtLocation(this, SoundPickup, GetActorLocation());
        if (Role == ROLE_Authority)
        {
        	UniqueGameObjectComp->ModifyAmmo(true, AmmoRestore);
	        DestroyPowerup();
        }
}

void UBZGame_GameObjectComponent::ModifyAmmo(bool bAddingAmmo, float AmmoVal)
{
	ASSERTV(GetOwner()->Role == ROLE_Authority, TEXT("ModifyAmmo Should not be called from Client"));

	if (bAddingAmmo)
	{
		CurrentAmmo = FMath::Clamp(CurrentAmmo + AmmoVal, 0.f, BZGameObjectData.MaxAmmo);
	}
	else
	{
		if (!IsAmmoDepleted())
		{
			CurrentAmmo = FMath::Clamp(CurrentAmmo - AmmoVal, 0.f, BZGameObjectData.MaxAmmo);
		}
	}
}


You should never modify important things like Health or Ammo on the client side, you should always let the Server take care of that and rely on Replication to carry the changed value down to the client, even if it is a little latent. Not following that opens up your game to hacking and/or cheating.

I am wondering why you are posting code as advice, which you have your own problems with in networked games. As a design choice one should never use replicated bool values for logic decisions, purely because multiple changes resulting in the same value during the same network tick does not cause an OnRep event to fire. The use of RPCs should be strictly encouraged and the use of replicated variables.

I am also happy to say that I was able to help gedamial with his issues.

Not that it’s really relevant, bit I do really know what I’m talking about - regardless of the issues you mentioned (which are engine issues, not logic ones). [SUB]I’ve been the sole multiplayer programmer on three titles now. One is a 30-player LAN game across PC/Android devices, another is a two-player cat and mouse arena game, and the other is my pet project - what will be an large-scale RTS / FPS mix. Back OT:[/SUB]

Being forced to use RPC’s to control gameplay events is fundamentally false - VehicleGame, ShooterGame, Unreal Tournament & Fortnite use var replication in many situations for good reason, they’re cheaper and more often than not, not time-critical (as a general rule, you want to avoid time & order-critical logic in Multiplayer as much as possible). If you plan on doing Multiplayer with anything more than 2-3 players, or more than a handful of network objects, then you need to make whatever shavings you can. E.g: If something is visible for half a second when it shouldn’t be, it’s not the end of the world, and vice-versa.

You’re right, variable replication is ‘unreliable’ (in network terms), meaning that not every change to that variable will be sent to the Client - but if you don’t need every change and you merely want to replicate the ‘state’ of something, then you can use them safely. The Client will always, eventually mirror the same as the Server, and ‘eventually’ is a matter of seconds at worst (depending on bandwidth usage and latency). If your gameplay logic is not time / order critical, or there’s the possibility that you could in some circumstances flood the network with RPCs - then avoid them.

As an example: In OP’s case - there may be a situation where several powerups are overlapping each other or in close proximity. What happens if the client runs over all of them, or if lots of clients run over different powerups at the same time? If you’re using reliable RPC for that you’re going to see a huge spike - and that’s where games go wrong or out of sync. Reliable RPC’s have double the cost of an unreliable RPC / var replication (the client has to send validation that it received it back to the Server. If it doesn’t, the server will kick the player) - use them sparingly and only when you absolutely must (there are many situations where you have no choice).