Cannot get "Client -> Server -> Everyone" Replication to work (C++)

Hey,
I’ve struggled for too long with this now and would be really happy if anyone could help me out.
My setup is this: I have a cube mesh as a blueprint and with a custom class sitting in my map. Now I want to walk up to it, press a use button, and for it to change color
to my team’s color. This works if I am playing as the server, but if a client walks up to this mesh and presses use, it only updates on his end. So the client cannot get the
cube’s material varbiable change so, that the ReplicatedUsing function is being called.

My custom class has these

bReplicates = true;
bAlwaysRelevant = true;
bNetLoadOnClient = true;

DOREPLIFETIME(AXEnergySource, CurrentSkin);

in the right places.

Now the functions and variables:


void OnUse(AXPlayerController* User);

UPROPERTY(Replicated, ReplicatedUsing = OnRep_Skin)
UMaterialInterface* CurrentSkin;

UFUNCTION(Reliable, Server, WithValidation)
void ServerOnUse(AXPlayerController* User);	

void AXEnergySource::OnUse(AXPlayerController* User)
{		
	if (Role < ROLE_Authority)
	{
		ServerOnUse(User);
	}
}

void AXEnergySource::ServerOnUse_Implementation(AXPlayerController* User)
{
	CurrentSkin = WhiteSkin;
	OnRep_Skin();
}

void AXEnergySource::OnRep_Skin()
{
	UE_LOG(LogTemp, Warning, TEXT("OnRep_Skin"));
	for (int32 i = 0; i < Mesh->GetNumMaterials(); i++)
	{
		GetMesh()->SetMaterial(i, CurrentSkin);
	}
}

bool AXEnergySource::ServerOnUse_Validate(AXPlayerController* User)
{
	return true;
}


Again to clarify: I want the client to be able to do
CurrentSkin = WhiteSkin;
and as a result I want the ReplicatedUsing = OnRep_Skin function to be called on all other clients. What am I doing wrong???

Can you post your .H file as well? I have an idea as to what the problem might be… but i’m not sure. What I am suspecting is that you’re not using the following:



 UFUNCTION(reliable, Server, WithValidation) // Called on the client executed on the server
void ClientRPCCFunction();

UFUNCTION(reliable, NetMulticast) // Called on the server executed to all the clients
void ClientFunc();


Granted I haven’t tried replication for a few revisions now but more or less the concept is the same. I had the same issue as you until I had the server perform a NetMulticast.

Thanks for trying to help!
What you suggested did also not work :frowning:

Here’s what I got now (header and .cpp files combined, it’s not actually like this in one file…)



UFUNCTION()
OnUse(AXPlayerController* User)
{
	ServerOnUse(User); // please call from server
}

UFUNCTION(Reliable, Server, WithValidation)
ServerOnUse_Implementation(AXPlayerController* User)
{
	CurrentSkin = WhiteSkin; // server sets skin, so it will be changed and replicated by OnRep_Skin
	OnUseMulticast(); // server call this on all clients
	OnRep_Skin(); // set the skin for yourself because no OnRep_Skin will be called here
}

UFUNCTION(Reliable, NetMulticast)
OnUseMulticast_Implementation()
{	
	OnRep_Skin(); // all clients set the skin because of getting a multicast call
}

UFUNCTION()
OnRep_Skin()
{
	CurrentSkin = WhiteSkin; // set the skin, I know it's doubled atm but it should still work
	... SetMaterial(i, CurrentSkin) ..
}

Again, this works if I use the object as a server, then multicast and OnRep_Skin gets called on server and client (on server because of the extra call).
If I’m the client, only my own cube object gets changed, but not that on the server.

Why are you calling OnRep_Skin instead of OnUse_Multicast? Also, it seems a little odd to make a function specifically to call another function. You could easily combine them and it might fix your issue.

:slight_smile:

Or am I missing something here?

EDIT:

Oh okay. I see what you’re trying to do. Well, if you want a function called you could also use an iterator for your player class and all the clients that share that class you can call that function that way upon the request of the server.

OH BOY ANOTHER EDIT!:

Essentially… there are a few ways of handling this. The object(actor) itself as you said can send this information out. That isn’t a bad way of handling it. To further explain on my previous edit though you could set it up so that when a client presses the use button it changes it client side. Then when that function hits use an iterator to all the other players to also run that same function.

Though truthfully any object that needs to “speak” to other players I would have that object actually “speak” to the players rather someones client handle that kind of work. There are a variety of reasons for that. The first one being… you never really want to place alot of authority in the hands of a client.

Yeah but what other option do I have then? The player interacts with an object ob the map/server, and I want the interaction to be recognized. Hm. Let’s say we have vehicles in UT and you as a player look at the vehicle and press the use button - this is essentially the same thing… how do I implement that? I don’t think the PlayerController iteration is a good idea. I though that’s what multicast would be for?

Well, yeah. Multicast should be working. Maybe the variable itself simply isn’t replicated?



 UPROPERTY(Replicated)
   FString test;


This is what I had to do for an older project of mine. Try giving that a shot.

The material variable is replicated. I sent you a PM.

I’m not sure why you’re seeing the behaviour you report, but a couple of suggestions:

  1. I don’t think you need to use net multicast here, your original version looks more the correct approach to me. Having a variable replicated with notification and also doing a multicast rpc seems like a recipe for timing/synchronization issues.

  2. I believe if you declare a property ‘ReplicatedUsing=’, you don’t need to also declare it as ‘Replicated’. In fact it’s possible that ‘Replicated’ is overriding the following specifier, preventing your notification handler from firing.

  3. Did you remember to put the necessary DOREPLIFETIME macro in GetLifetimeReplicatedProps override?

If you still have problems, I’d suggest you print things to the log so you can see in what order things are being called and where.

Thank you both for your replies, I will test this first thing tomorrow morning. :slight_smile:

“If the RPC is being called from client to be executed on the server, the client must own the Actor that the RPC is being called on.”

Could this be my issue? Because I’m a client, trying to change an object on the map, which I do not own.

Edit: I have an idea, I will do the update on the material to be called by the object itself, if it sees that its state changed (team changed). Maybe that could work :slight_smile:

Also someone tell me what “ForceNetUpdate();” is and why it is so rigorously used within the UTGame scope? It sounds like it’s a workaround or something for a not properly functioning system?

That’s probably it. I think the standard approach is to route server RPCs through your player controller, since that is guaranteed to be owned by the client. So rather than declare your ServerOnUse in your object class and pass in the player controller, declare it in your player controller and pass in your object.
You should still be able to have the functionality in your object class for encapsulation if you prefer, and delegate to that from within ServerOnUse_Implementation.

Killer post man! I managed to solve it this way. I did all the functionality etc. from a server function within PlayerController to check if there is a usable target etc. and then call Target.OnUse from the Controller.
So happy I can finally move on, and without MultiCast stuff! :slight_smile: Thanks again guys!

Great! Can you maybe quickly post an example of how you set up your playercontroller then? I am having the exact same problem but I don’t see how I can get my PlayerController to do the work you’re describing. cheers!

Sure


.h
UFUNCTION(Server, Reliable, WithValidation)
void ServerOnUse();
	
void OnUse();

.cpp
void AXPlayerController::OnUse()
{
	if (Role != ROLE_Authority)
	{
			ServerOnUse();
	}	
	
	...
	Cast<AXEnergySource>(UseTarget)->OnUse(this);
	...
}

void AXPlayerController::ServerOnUse_Implementation()
{
	// Et voilá, this gets done on the server, thus variables that have ReplicatedUsing functions, actually
	// get replicated, and the ReplicatedUsing function gets called on all clients.
	OnUse();
}

bool AXPlayerController::ServerOnUse_Validate()
{
	return true;
}

Saiboat Edit: "Last edited by TheJamsh; 03-19-2015 at 03:14 PM. Reason: Added Correct ‘Code’ tags "
Meh, the code tags don’t highlight anything. Would it be possible to get proper C++ tagging in a C++ dev forum? :stuck_out_tongue:

awesome! thank you so much!