What's the proper way to check if we should call a Client RPC

I was recently trying to use the ‘Client’ RPC markup to enable me to notify a remote Player’s CharacterMovementComponent about something. To determine if I needed to make the call, I tried checking GetOwner()->GetRemoteRole == ROLE_AutonomousProxy. However, it turns out on a listen server, the server player’s RemoteRole is AutonomousProxy which seems wrong to me. His Role was ROLE_Authority and his RemoteRole was ROLE_AutonomouseProxy. It seems like it would be ROLE_SimulatedProxy if there’s not a valid PlayerController out there for it.

So if I can’t check RemoteRole, what’s the best way people have found to know when Client RPC calls should be made?

I don’t fully understand what you’re trying to do but if you want to call a client RPC function, you need to be calling from the server (dedicated or listen) to the client, so the client will call that function.

If you explain more about what you are trying to do, I can try to help more.

Specifically I want to AddImpulse on the server’s CharacterMovementComponent. If I do that, and that Character has a PlayerController controlling it out there on a client, I need to push that impulse to that client so the CharacterMovementComponent on the client also knows about it. Otherwise the server authoritative movement will rubber band the player to the new position.

So I added a function called Client_AddImpulse that has UFUNCTION(Reliable, Client) markup to MyCharacterMovementComponent. Then I want to call Client_AddImpulse if that impulse is needed on a remote client to keep it up to date with the server. Before I call Client_AddImpulse on the server, I need to know that it’s needed for a client PlayerController. Because on a ListenServer it will end up calling back around and calling AddImpulse on the ListenServer’s local player.

Also your statement of “so the client will call that function” doesn’t make sense to me. Can you clarify what you meant?

Because I haven’t done impulses like this in a multiplayer game, my reply here is based on a couple assumptions:

CharacterMovementComponent is designed to replicate movement out to the clients, as in, if the server sets an impulse, the other client(s) should see the movement through the normal movement replication.

You want to add a impulse to the character through some input from that character. As in, it is not like a jump pad, where the player steps on something and then gets launched.

// Called by key press by client (can also be listen server)
void MyCharacter::StartImpulse( )
{
	if(Role < ROLE_Authority)
	{
		// Not the server calling, have it...
		ServerStartImpulse( );
	}
	else
	{
		// Add 512 velocity up (z) with no bone as root and set as velocity change
		MyCharacter->GetMovementComponent( )->AddImpulse(FVector(0.f, 0.f, 512.f), NAME_None, true);
	}
}

bool MyCharacter::ServerStartImpulse_Validate( )
{
	return true;
}

bool MyCharacter::ServerStartImpulse_Implementation( )
{
	// server call startimpulse to add impulse with velocity change
	StartImpulse( );
}

If my assumptions are wrong, please reply again with more detail about what you’re doing and I’ll try to help more.

Your basic assumption is only half correct. The server is the one always starting the AddImpulse call b/c it comes from damaging the Character and all my damage is server authoritative.

The CharacterMovementComponent is designed to allow the client player to be autonomous with his movement (hence the ROLE_AutonomousProxy) and then the server duplicates and validates the same movements. If the server determines the client has gotten too far off track from where it thinks the player should be, they’ll force the players position back to where it needs to be causing rubber banding.

Therefore, I need to send out to any player controlled character on a client that has ROLE_AutonomousProxy an RPC letting them know they need to add an impulse to their calculations. Which leads me back to my original question. What’s the best way to determine if an Actor has a RemoteRole of ROLE_AutonomousProxy taking into account that on ListenServers the local player has its RemoteRole set to autonomous.

You’ll only be able to check the role of the class that is calling the function, meaning, whichever is calling the function; client, server, listen. I don’t believe there is a way to call down directly from the server and then check the roll of a reference, because on the server, everything is ROLE_Authority.

If you are doing this on damage of the character, this should be done on the server, so every time on “TakeDamage”, it is happening on the server and therefor, Authority.

From this point, you call down to all the clients and tell them anything, while passing the reference of what character is getting damaged / impulsed via a client function. As a note, Characters are replicated to all clients by default.

You do have access to the roles and the net modes.

Roles being:

ROLE_None,
ROLE_Authority,
ROLE_SimulatedProxy,
ROLE_AutonomouseProxy

Net modes being:

NM_Client,
NM_Standalone,
NM_ListenServer,
NM_DedicatedServer

If you checkout Actor, TakeDamage( ), there is a condition checking:

DamageEvent.IsOfType(FRadialDamageEvent::ClassID)

If this is true, it will call on all “ComponentHits”, to ReceiveComponentDamage, which in turn has 2 more conditions:

DamageEvent.IsOfType(FPointDamageEvent::ClassID)

This will add a impulse to the Actor being hit.

DamageEvent.IsOfType(FRadialDamageEvent::ClassID)

This will do a RadialImpulse, which should do impulse to anything in the “OuterRadius” of the RadialDamageEvent.

From this, it looks like the engine is setup to handle the situation of a player taking damage and getting some sort of impulse added to it on that event.

“I don’t believe there is a way to call down directly from the server and then check the roll of a reference, because on the server, everything is ROLE_Authority.”

I had believed that’s what RemoteRole was supposed to tell us. See this: Actor Role and RemoteRole | Unreal Engine Documentation

And I’m familiar with how that damage path works currently. The problem is the CharacterMovementComponent isn’t hit b/c I don’t want to damage the Capsule b/c that’s too large of a target so projectile collision for it is turned off. I want to damage the PhysicsAsset which has the correct sized capsules to shoot on the target. Those are taking the damage impulses but they’re not in control until they’re ragdolled therefore I need I way to transfer damage impulses through the Character to the CharacterMovementComponent which they already have half setup.

Which leads me back to my original problem: How do I tell if I should call a Reliable Client RPC? I would’ve thought it was if the actor’s Role == ROLE_Authority and RemoteRole == ROLE_AutonomousProxy but that doesn’t work on a ListenServer.

On a ListenServer, if your condition is

(Role == ROLE_Authority && RemoteRole == ROLE_AutonomousProxy)

do you know which of these is false?

“(Role == ROLE_Authority && RemoteRole == ROLE_AutonomousProxy)” evaluates to true for a locally playing player on a ListenServer and that is the problem. I feel like its RemoteRole shouldn’t be ROLE_AutonomousProxy b/c there’s not one out there on any client.

That makes sense because:

ROLE_AutonomousProxy
This is generally only used on actors that are possessed by PlayerControllers

Because a ListenServer is technically possessed by a PlayerController.

You may want to have a condition specific for ListenServers, where it is checking something specific for being a ListenServer.

I’m sorry, I am still a bit confused regarding the reason you have to call down to the clients in this way.

To clarify, if you are in the TakeDamage function, you should always be on the server (Role_Authority) and (NM_DedicatedServer || NM_ListenServer). If you want to call down to client(s) from this, you need a reference to all of them that you want to call and then call a client function on them.

If you are already a ListenServer, then you dont need to do anything more as it is also a client, just acting as a server. Unless of course you want to replicate to the “other” clients which would be a collection of reference, except for “yourself” as the listen server, who then call client side functions.

“If you are already a ListenServer, then you dont need to do anything more as it is also a client, just acting as a server.” - This is where you’re off. I do need to know b/c otherwise I’ll double apply the impulses on the local ListenServer player.

Anyways, I realized I wasn’t searching the right area. On UDN, your engineers have already commented that what I’ve described is a bug in the engine referencing UE-32444. However, I don’t know why I can’t see that bug in the issues tracker. Here’s a link to the post I’m referring to.

https://udn.unrealengine.com/questions/299421/listen-server-and-remoterole.html

I don’t know what you are trying to do but it seems that you want to know wether you are executing code on server or client.

You can do that using the IsNetMode() method in UWorld.

For example to execute code in dedicated server and listen server but not on client you can use:

if (!GetWorld()->IsNetMode(NM_Client))
{
    // Do something...
}

I looked at the internal tracker for it. The issue is marked as backlogged and there isn’t a note as to why. I also didn’t know this was an issue, with the incorrect role being assigned.

For now, I would rely on conditioning through the NetMode, one for listen and one for dedicated, then having different results to only apply the impulse in whichever way you want.

As for the question of what is the proper way to call a client RPC; well, this is largely up to you and when you want to call a client function. I don’t have any guideline as to specifically when you should, just whenever you need to. How you manage that condition is up to you. Just remember that the authority is the one that will have to have a reference to the Actor you want to call, the Actor needs to be replicated, and the server will have to tell the Actor to call the client function.