Download

Problem with multiplayer server call

Hi.
I’m trying to achieve toggling light by hitting something with a projectile in a multiplayer game. At first I tried calling server rpc function from this things, that got hitted but those things are placed on a map directly from editor so from what I have found out they have no owner and no connection to pass this call by. Next I tried to call server rpc function from OnHit method in projectile objects that are owned by ACharacter objects. Unfortunetly this also gave me no effect on dedicated server. When running listen-server the server player can switch the light, client will see this change, but client is unable to change it by himself. What is a proper way to do such things? I don’t want to spawn objects, that I can for example toggle light, from GameMode(I guess this will solve the problem).

The last answer on your AnswerHub post is the best bet.

I was unaware that NetMulticast didnt need a owner on the Actor to replicate:

https://answers.unrealengine.com/questions/596225/what-is-the-proper-way-of-replication-a-non-owned.html

This is where I would start.



#pragma once

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

UCLASS()
class MYGAME_API AStreetLight : public AActor
{
	GENERATED_BODY( )
	
public:
	AStreetLight( );
	virtual void BeginPlay( ) override;
	virtual void Tick(float DeltaTime) override;
	
	UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = "StreetLight"))
	UStaticMeshComponent *Mesh;
	
	UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "StreetLight")
	USphereComponent *Collider;	
	
	UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "StreetLight"))
	UPointLightComponent *Light;
	
	UFUNCTION(NetMulticast)
	void ToggleLights( );
	
	UFUNCTION( )
    void Overlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, 
                 int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult );
};




#include "MyGame.h"
#include "StreetLight.h"

AStreetLight::AStreetLight( )
{
	PrimaryActorTick.bCanEverTick = true;
	
	Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
	RootComponent = Mesh;
	
	Collider = CreateDefaultSubobject<USphereComponent>(TEXT("Collider"));
	Collider->OnComponentBeginOverlap.AddDynamic(this, &AStreetLight::Overlap);
	Collider->SetupAttachment(RootComponent);
	
	Light = CreateDefaultSubobject<UPointLightComponent>(TEXT("Light"));
	Light->SetupAttachment(RootComponent);
	
	bReplicates = true;
}

void AStreetLight::BeginPlay( )
{
	Super::BeginPlay( );
}

void AStreetLight::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}

void AStreetLight::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	
	// Can add any replicated variables here...
}

void AStreetLight::Overlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, 
             int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult )
{
	if(Role == ROLE_Authority)
	{
		// Probably need a cast to here to make sure OtherActor is a Projectile.
		ToggleLights();		
	}
}

void AStreetLight::ToggleActive_Implmentation( )
{
	if(Light)
	{
		Light->ToggleActive( );
	}
}


The only (proper) way you can do this is to spawn the switch on the Server, and replicate it down to clients. You can place hidden switch Spawners in the level which the Server looks for when the game starts, and spawn the switch (ensuring it replicates so that clients create their local version too). Remember that anything that is affected by the switch has to be replicated too, otherwise it won’t do anything anyway.

When a client interacts with the Switch, they send an RPC to the Server (from the Player Controller or their Pawn) - which contains a pointer to the switch they interacted with. The Server can then verify that the action is valid, and perform the task it needs to.

EDIT: See more posts below, there are other ways. My bad.

Huh? If you read my post again, I stated that you have to call it from the server. All you have to do is call a server RPC on your Pawn or PlayerController or use an overlap on the server (much like @Vawx posted above) to make it work.
I use NetMulticast like that all the time and it works flawlessly.

Not saying that your solution wouldn’t work, it would, but there is no need to use such a convoluted solution when you can just use NetMulticast instead.

EDIT:

I just started a small example project, the following has been achieved with NetMulticast with a simple Use Interface

EDIT2:

I put up my small project so you can check it out
It is 4.15.2, but should work pretty much with all the latest version. I only left the necessary files, you have to right-click the .uproject and generate project files.

It basically is just the FirstPersonTemplate with some network stuff.

The magic happens in NetMulticastActor, which is a UsableInterface thing and the multicast thing at the same time.
ActualNetworkThingCharacter (great name, I know) is a subclass of the FirstPerson template and handles the input for using stuff(Default Button E) and calls a ServerRPC if you are on the client and then calls the NetMulticast function of NetMulticastActor.
I handle the actual light stuff in Blueprint using a BlueprintImplementableEvent.

Here is the actual magic:

NetMulticastActor.h



#pragma once

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

UCLASS()
class NETMULTICASTPROJECT_API ANetMulticastActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ANetMulticastActor();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	
	UFUNCTION(reliable, NetMulticast)
	void OnNetMulticastUse();

	UFUNCTION()
	void FunctionCalledFromNetmulticastOnAllClients();

	UFUNCTION(BlueprintImplementableEvent, Category = "Useable")
	void HandleUsedOnClientThing();
};

NetMulticastActor.cpp



#include "NetMulticastProject.h"
#include "NetMulticastActor.h"


// Sets default values
ANetMulticastActor::ANetMulticastActor()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = false;

	bReplicates = true;
}

// Called when the game starts or when spawned
void ANetMulticastActor::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void ANetMulticastActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}



void ANetMulticastActor::OnNetMulticastUse_Implementation()
{
	UE_LOG(LogTemp, Warning, TEXT("Server Thing"));
	
	//This function call is not needed, you can also handle everything in here
	FunctionCalledFromNetmulticastOnAllClients();
}


void ANetMulticastActor::FunctionCalledFromNetmulticastOnAllClients()
{
	UE_LOG(LogTemp, Warning, TEXT("Client thing"));
	HandleUsedOnClientThing();
}

ActualNetworkThingCharacter.h




#pragma once

#include "NetMulticastProjectCharacter.h"
#include "ActualNetworkThingCharacter.generated.h"


UCLASS()
class NETMULTICASTPROJECT_API AActualNetworkThingCharacter : public ANetMulticastProjectCharacter
{
	GENERATED_BODY()
	
	

public:

	virtual void Tick(float DeltaTime) override;

	void GetViewTarget();


	void Use();

	UFUNCTION(reliable, Server, WithValidation)
	void ServerUse(class ANetMulticastActor* UsableActor);


protected:
	// APawn interface
	virtual void SetupPlayerInputComponent(UInputComponent* InputComponent) override;


	//UsableInterface thing
	class ANetMulticastActor* UsableActor;
	
};

ActualNetworkThingCharacter.cpp



#include "NetMulticastProject.h"
#include "ActualNetworkThingCharacter.h"

#include "NetMulticastActor.h"



void AActualNetworkThingCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	GetViewTarget();
}

void AActualNetworkThingCharacter::GetViewTarget()
{
	if (UWorld* world = GetWorld())
	{
		FVector StartLocation = GetFirstPersonCameraComponent()->GetComponentLocation();
		FVector EndLocation = StartLocation + GetFirstPersonCameraComponent()->GetForwardVector() * 400;
		FHitResult HitResult;

		if (world->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, ECollisionChannel::ECC_Visibility))
		{
			if (HitResult.Actor.IsValid())
			{
				UsableActor = Cast<ANetMulticastActor>(HitResult.Actor.Get());
			}
		}
	}
}


void AActualNetworkThingCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);


	PlayerInputComponent->BindAction("Use", IE_Pressed, this, &AActualNetworkThingCharacter::Use);
}



void AActualNetworkThingCharacter::Use()
{
	//If UsableActor == true, call function on the server
	if (UsableActor)
	{
		if (Role < ROLE_Authority)
		{
			ServerUse(UsableActor);
		}
		else
		{
			UsableActor->OnNetMulticastUse();
		}
	}
}


void AActualNetworkThingCharacter::ServerUse_Implementation(class ANetMulticastActor* InUsableActor)
{
	if (InUsableActor)
	{
		InUsableActor->OnNetMulticastUse();
	}
}

bool AActualNetworkThingCharacter::ServerUse_Validate(class ANetMulticastActor* UsableActor)
{
	//In a real project you want ot validate if the Pawn can actually interact with the UsableActor to prevent cheating, obviously not needed for an example like this.
	return true;
}

This was generally my method too but DennyR is right, Netmulticast doesn’t seem to require such things. NetMulticast Rules

Thanks for pointing this out @DennyR. I don’t use NetMulicast often and now the practical use of it suddenly makes a lot more sense.

Ah my apologies, I understand now. At first I thought you meant just call NetMulticast from the client directly on the object, but I couldn’t see how that would ever work.

But yeah, routing a NetMulticast call via a Player Controller / Pawn sounds like it would work well indeed. I rarely ever touch Net Multicast but it certainly makes sense.

Multicast is great to trigger functions on remote clients and update values that aren’t replicated (or replicates only to owner).
It’s an awesome way to save bandwidth, refreshing specific info only when requested, instead of replicating everything…

The part I don’t understand here is, you still have to make sure that the actor you’re calling the NetMulticast function is syncronised between Server and Client do you not, which means it has to be replicated anyway? For example, you can’t just call a Server function on some arbitrary object placed in the level, because the pointers will be different on one machine vs another? In PIE it doesn’t matter because you share the process.

Unless I’m missing something huge here it feels like we’re all going about the same thing in slightly different ways.

EDIT: Looking at Denny’s code it seems as though we are, so my comment still stands - you have to Spawn the actor you’re calling Net Multicast on on the Server either way, and the RPC has to be routed via the Player Controller or Pawn.

You don’t have to spawn anything. You only have to make sure the object is marked as replicated. If that is the case, the references are valid when passing from client to server and vice versa, even for Actors placed in the editor.

What I mean to say is that an actor still has to be replicated to call any kind of network function on it.

NetMulticast does seem like the way to go here for sure.