Serious replication issue: Server doesn't change variable on the client

Hello.

I’ve been trying to solve this issue for a while, but without any success.

THE PROBLEM

When the weapon with which my character is shooting gets out of ammo, I want my character to automatically switch weapon.

So, in the** AWeapon::Shoot** function everytime a shot is performed there is an if-statement that checks whether the weapon got out of ammo.

If yes, the function AMyCharacter::ForceWeaponSwitch() is called on the **Owner **of that weapon (which is stored in a variable)



void AWeapon::Shoot()
{
	if (Role < ROLE_Authority)
		ServerShoot();

	else
	{
		FireWeapon();

		DecreaseAmmo();

		// Check if out-of-ammo. If yes, switch weapon
		if (MyOwner->GetAmmo(WeaponConfig.PrimaryAmmo) == 0)
		{
			StopFire();
			MyOwner->ForceWeaponSwitch();

			return;
		}
	}
}


As you can see the function is executed on the Server via a **ServerRPC **call (ServerShoot)

**MyOwner **is the **AMyCharacter **which **owns **the weapon

And this is the AMyCharacter::ForceWeaponSwitch function:



void AMyCharacter::ForceWeaponSwitch()
{
	**EquipWeapon**(EWeaponType::Pistol);
}

########################

// The following function just uses the **FindWeapon()** function to cycle through my **TArray **of **AWeapon*** and, once found, sets it as the new **CurrentWeapon**

void AMyCharacter::EquipWeapon(EWeaponType toEquip)
{
	AWeapon* theWeapon = FindWeapon(toEquip);

	if (theWeapon && theWeapon->GetIsEnabled() && GetAmmo(theWeapon->GetPrimaryAmmo()) != 0)
	{
                *// this is the crucial step!!!*
		**CurrentWeapon = theWeapon;**
	}
}


THE RESULT

The result? Nothing.
Or better: nothing on the client!

On the Server window, when my Weapon goes out of ammo the WeaponSwitch succeffully happens.
On the Client window, it doesn’t.

"ARE YOU REPLICATING PROPERLY?"

These items are replicated in this issue:

  • AMyCharacter class
  • AWeapon class
  • AMyCharacter -> CurrentWeapon
  • AMyCharacter -> WeaponInventory
  • AWeapon -> MyOwner (replicated via OnRepNotify function)

FUN-FACTS!

  • The **ForceWeaponSwitch() **function is succeffully called and executed by the ServerAuthority when the weapon gets out of ammo

  • If in the ForceWeaponSwitch() function I change another variable (for example: bCanMove = false) that variable gets changed succeffully! Why isn’t it working with my CurrentWeapon?

Any help?

Can we see your .h file and the declaration of ForceWeaponSwitch?

It’s a simple function



void ForceWeaponSwitch();


So in this case, ForceWeaponSwitch() and EquipWeapon() will only run on the Server, neither of those functions will run on the Client (just clarifying that from what’s been posted so far).

Are you absolutely sure the problem is that the value of CurrentWeapon isn’t replicating? Create an OnRep_CurrentWeapon() function and use ReplicatedUsing = “OnRep_CurrentWeapon” instead of ‘Replicated’ in the header file, and print a string.

If that string prints, then CurrentWeapon is replicating. If it doesn’t print, then either CurrentWeapon has already changed to the same value on the client side, or the client doesn’t know about the value at the pointer Current Weapon, or it hasn’t changed.

In fact, I want the variable to be changed by the server for security reasons.

Done. What happens is this with two players (1 server, 1 client):

When the game starts, a default pistol is given to each player and gets set as CurrentWeapon (by the ServerAuthority): the string gets printed twice

When I manually switch Weapon with the Keyboard on the Server window: the string gets printed

When I manually switch Weapon with the Keyboard on the Client window: the string DOES NOT get printed

Any tip?

That’s fine about only running on the Server I just wanted to check/clarify that.

Interesting, so you placed that in OnRep only right? OnRep doesn’t fire on the Server (unless you call it manually ofc), so it must be firing on the client and therefore the var must be replicated. Unfortunately the print strings don’t seem to always correspond to the window calling it. Sometimes I also like to do this to make it clearer:



const FString NetString = GetNetMode() == NM_Client ? TEXT("Client") : TEXT("Server");
const FColor NetColor = GetNetMode() == NM_Client ? FColor::Blue : FColor::Green;

GEngine->AddOnScreenDebugMessage(-1, 10.f, NetColor, FString::Printf(TEXT("%s - Value = %s"), NetString, *GetNameSafe(CurrentWeapon));


So here’s a few more things to try to track it down:

  • Check any locations where you’re changing the ‘CurrentWeapon’ variable on the Client, and insert prints there too, just to see what’s occurin’. In practice it’s best to avoid changing any replicated variables on the client anywhere.

Judging by what you said about weapon swapping, it seems as though the client is changing the value locally then telling the Server what it is perhaps?

That was the *freaking *key!

When the player switches weapon using his keyboard, the function for the weapon equipping changes the CurrentWeapon on the client. So of course the Server couldn’t know about that change.

My question, though, is: why couldn’t the server change the CurrentWeapon variable EVEN THOUGH he didn’t get the latest update of that variable?

Hah, glad you found it. I’ve run into that situation many times where the Rep wouldn’t fire because I’d changed the variable early on the Client. I tend to drive anything that relies on that variable changing via OnRep functions, since I always know the value is the correct one at that point.

I’m slightly confused by your question though I’m afraid - do you mean the Server couldn’t change it’s local CurrentWeapon, or change CurrentWeapon for the Client?

Shortly, I’m confused about why I don’t have the bug anymore.

If you see in the second code snippet (post n#1) there’s an assignment



CurrentWeapon = theWeapon;


This assignment now has been modified: IF it’s the Client, then call a ServerRPC function that changes it on the Server; otherwise just change it.

What I don’t understand is: **why did I get that bug? **

I mean, the ForceWeaponSwitch() function that I call from the Weapon class is executed on the server. So it should have changed the CurrentWeapon variable.

But then it turned out that I needed to AVOID the CurrentWeapon from being changed by the Client

So why changing the variable from the client screwed my variable replication?

So the problem is likely that the Server knew at that point that the client had already changed their local version of the variable - and it didn’t bother sending out the new variable. Unfortunately it’s hard to say and until we get proper Server vs Client debugging in VS probably impossible to track it down easily…

The behind-the-scenes replication system doesn’t submit the properties to the client if the server doesn’t think they have changed. The Server probably received the updated version of that variable from the client BEFORE it received the RPC to change function, so once the value changed - the server checks and says “oh that client already has that value, I won’t send it out again”.

I’m not entirely sure on this part, but I believe in the backend the server also simulates what the client does or something, or it tries to take a best guess at what variable the client has before sending the data out again, to save on bandwidth.

Unfortunately you can never really guarantee the order of operations in MP :stuck_out_tongue:

Umh, so let’s say one would never want to let the client change a **replicated **variable, true?

I would say that for the most part, that is indeed true :slight_smile: