Could I get some help understanding Roles and some issues with server RPCs?

I’m working on replicating a weapon class and using ShooterGame as an example, but I’m running to some issues and I wanted to see if I could get some clarification on what the proper roles for each party should be. In the examples you see things like:

if (Role < ROLE_Authority)
{
    ServerCall();
}

The only problem is that this isn’t working for me, because when I run from the editor both the listen server and the client are running as ROLE_Authority. If I check into RemoteRole it says that it is ROLE_SimulatedProxy. Which seems completely backwards based on the examples.

The reason I bring this up is because I’m trying to wrap my head around why this isn’t working at all for me, and I have a feeling that whatever I’m missing is causing the roles to be wrong. My issue is that client → server calls aren’t working at all. I tried changing my code to do something like this:

if (GetNetMode() == NM_Client)
{
    ServerStartFire();
}

And verified that it was executing that block, but it still doesn’t seem to execute ServerStartFire_Implementation.

That being said, the replication from the listen server to the client is working fine. So, in my case, I’m able to see the shots replicated on the client, but not the other way around.

Thanks!

EDIT

I tried to start over with a new Actor to see if I could have done something wrong while implementing my weapon class, but the problem still exists here. Here’s the code of the new class:

Example.h

#pragma once

#include "GameFramework/Actor.h"
#include "Example.generated.h"

/**
 * 
 */
UCLASS()
class AExample : public AActor
{
	GENERATED_UCLASS_BODY()

	UFUNCTION()
	void Call();

	UFUNCTION(Server, Reliable, WithValidation)
	void ServerCall();
	
};

Example.cpp

#include "Mach.h"
#include "Example.h"


AExample::AExample(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
	bReplicates = true;
	bAlwaysRelevant = true; // Had to add this because it was complaining about no root component
}

void AExample::Call()
{
	UE_LOG(LogTemp, Log, TEXT("Example Call"));

	switch (Role)
	{
	case ROLE_None:
		UE_LOG(LogTemp, Log, TEXT("ROLE_None"));
		break;
	case ROLE_SimulatedProxy:
		UE_LOG(LogTemp, Log, TEXT("ROLE_SimulatedProxy"));
		break;
	case ROLE_AutonomousProxy:
		UE_LOG(LogTemp, Log, TEXT("ROLE_AutonomousProxy"));
		break;
	case ROLE_Authority:
		UE_LOG(LogTemp, Log, TEXT("ROLE_Authority"));
		break;
	case ROLE_MAX:
		UE_LOG(LogTemp, Log, TEXT("ROLE_MAX"));
		break;
	}

	ServerCall();
}

bool AExample::ServerCall_Validate()
{
	return true;
}

void AExample::ServerCall_Implementation()
{
	UE_LOG(LogTemp, Log, TEXT("Server call implementation"));
}

Then in my Character class I added the following in PostInitializeComponents:

FActorSpawnParameters SpawnInfo;
Example = GetWorld()->SpawnActor<AExample>(AExample::StaticClass(), SpawnInfo);

And then added the call in OnStartFire which is bound to Fire:

Example->Call();

Here’s what the logs show:

[2014.05.23-22.54.30:644][551]LogTemp: Example Call
[2014.05.23-22.54.30:644][551]LogTemp: ROLE_Authority

Try if(HasAuthority())

From the source, this does return (Role == ROLE_Authority);. Shouldn’t the actor on the server have ROLE_Authority? Right now, this will get triggered on the client.

Well, the problem is that in my case the client is set to ROLE_Authority. I’ve verified that it’s running as a client using GetNetMode(), and verified that the meshes are replicated, this seems to be working fine. But, my custom actor specifically seems to have roles swapped. I’m thinking there’s some definition or call I’m missing to set the roles correctly, but I’m not sure what and the documentation is really lacking.

Yeah, the server will have Authority.
If you want to run client specific code, use if(!HasAuthority())
:slight_smile:

I haven’t used much code for networking, I have prototyped most of a networked game in blueprint though, so I’m familiar with the concepts.

In my game, there is pretty much no “client only” code. Even client calls to the server for things such as fire input just go through a “Run on server” replicated function on the controller, so that listen servers can use the same code.

What I do check for is ownership of the controller with IsLocalPlayerController() before sending input, otherwise I get input from the server player completely overriding the client input of that controller.

That seems strange. Only the server should have the Role == ROLE_Authority, and RemoteRole should be ROLE_SimulatedProxy/ROLE_AutonomousProxy.

When the client spawns the actor, it will reverse the roles. So Role should should be ROLE_SimulatedProxy, and RemoteRole == ROLE_Authority.

Is this not what you are seeing?

Well, that’s essentially the problem I’m facing. Unless I’m doing something completely wrong, the roles seem to be swapped. RemoteRole is set to ROLE_SimulatedProxy and Role is set to ROLE_AutonomousProxy.

I could send you the whole cpp and h file if that’d be helpful.

I’m sorry yes, you should only see it on the client in that case, and that is odd that it is authority on the client.

How is this actor being created on the client?

Updated with a full example showing the issue.

Role being ROLE_Authority on the server is correct. Are you saying you see both client and server printing this message? If you are using PIE this can happen, and you are just seeing the server output in both windows. If this is happening on two instances separately spawned, you should only see the message on the server (since this is a server call).

Hi John, thank you for your help. Unless I’m misunderstanding, the logging the role is happening on the client, since I do that before calling ServerCall. Is my understanding incorrect? In either case it looks like ServerCall_Implementation is never getting executed, because the log line is never printed. This is the issue I’m experiencing that lead me to realize that the roles seemed backwards. Also, wanted to point out that I am running this from the editor with dedicated server checked.

In this example I’m creating it in the character class in PostInitializeComponents:

FActorSpawnParameters SpawnInfo;
Example = GetWorld()->SpawnActor<AExample>(AExample::StaticClass(), SpawnInfo);

My weapon class is actually added through the blueprint editor, though. Both cases exhibit the same behavior.

That might be the problem. You are trying to send an RPC using an actor that was created locally on the client.

To send RPC’s from client to server, the actor used to do this must either be a part of the level, or have been spawned on the server, and then replicated to the client. It’s generally not safe to directly spawn actors on client unless you know you only need it on that client.

In this case, you could wrap a if ( Role == ROLE_Authority ) check around that spawn so it only spawns on the server initially only. Then make sure Example is a replicated property (UPROPERTY( Replicated ) above the property declaration).

You also need to make sure Example is marked to be replicated as an actor either be setting bReplicates to true in Examples constructor, or calling Example->SetIsReplicated( true ) after the server spawns it.

One last thing, you need to make sure to add the Example property to the GetLifetimeReplicatedProps function.

Now on the server, you should be able to call RPC’s on Example that get replicated to the copy the client has.

Yes, thank you! That was the problem. So, making sure the authority was the only one creating the actor in the first place fixed the issue. Thanks!