Getting client reference to replicated server spawning actor

Hello UE4 Community!

We’re in the process of creating a basic multiplayer online game and we decided to make some of the actions available to the players to be controlled by different classes that we set as “modules” for the PlayerController.
To show you the issue we’re facing I’ll use one of these classes as an example: ChatModule.

So we have our class ChatModule inherited from AInfo. When the player connects to the server we need to instance ChatModule and set a reference to that instance in a variable in the PlayerController. In this case I’m using the variable AChatModule* ChatModule;
In the BeginPlay method of PlayerController I’m doing:



FActorSpawnParameters SpawnInfo;
SpawnInfo.Owner = this;
ChatModule = GetWorld()->SpawnActor<AChatModule>(AChatModule::StaticClass(), SpawnInfo);


The issue with this is that ChatModule is being instanced one time in the server but two times in the client. Would be great if someone can tell me why.

The next thing I tried is this:




 if (HasAuthority())
 {
  FActorSpawnParameters SpawnInfo;
  SpawnInfo.Owner = this;
  ChatModule = GetWorld()->SpawnActor<ASChatModule>(ASChatModule::StaticClass(), SpawnInfo);
 }



Now the issue is that ChatModule is being set only on the server. ChatModule variable on the client is null, so I can’t get a reference to the AChatModule instance from the client.

I resolved this by adding the following method to PlayerController:



void ASPlayerController::SetChatModule(ASChatModule* ChatModule)
{
 this->ChatModule = ChatModule;
}


and in the chatModule BeginPlay() method I made this:



void ASChatModule::BeginPlay()
{
 Super::BeginPlay();

 ((ASPlayerController*)this->GetOwner())->SetChatModule(this);
}


This is working as expected, but doesn’t seems to be the right way to do what we need.
How should we do this?

You’re right about spawning the ChatModule on the server-side. To my knowledge if you want an actor to exist on both server and client, you have to create it on the server, someone please correct me if I’m wrong. Then, for the actor to replicate to the client, you want it to always replicate, so set the following in your constructor if you haven’t:



	bReplicates = true;
	bAlwaysRelevant = true;


You want bAlwaysRelevant to be true since you want the ChatModule’s actor position to affect whether the ChatModule is currently replicating to a client or not. Then what I would do is make the this->ChatModule variable replicated. Is it replicated right now? If not, use this:

In ASPlayerController.h:



	UPROPERTY(Replicated)
	AChatModule* ChatModule;


In ASPlayerController.cpp create this function if it doesn’t exist, its header is auto-generated since the class has replicated vars:



#include "UnrealNetwork.h"
...
void ASPlayerController::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(ASPlayerController, ChatModule);
}


Now if you set this->ChatModule server-side, the client will have a reference to the same actor.

Thanks for the reply =D! but i still getting null reference on client :/. Any idea?

Could you try to access the ChatModule with a delay and see if perhaps its only null in the first few frames? For example check every tick whether its null and print it (but not do anything else yet)? It might be that there is a few frames delay before that the ChatModule that is created on the server is replicated to the client, because you have to wait for the network update to arrive client-side. Your AChatModule class is now replicated and always relevant, right?

Hello and thanks again!.

Yes i set bReplicates = true; bAlwaysRelevant = true;

the replication works fine, but the problem is how to get the reference of the object in the client.

I made this on the ChatModule beginPlay to test it!




void ASChatModule::BeginPlay()
{
	Super::BeginPlay();

	if (HasAuthority())
	{
		FString Fs = TEXT("ChatModule Server memory addr: ");
		Fs.AppendInt((uint32_t)this);
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, Fs);
	}
	else {
		FString Fs = TEXT("ChatModule Client memory addr: ");
		Fs.AppendInt((uint32_t)this);
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, Fs);
	}

}


this prints on the screen the memory address of the object, and the results:

RealMemAdd.png
PD: i think they are negative because appendString used a signed int32

in the tick event (on PlayerController):




void ASPlayerController::PlayerTick(float DeltaTime)
{
	Super::PlayerTick(DeltaTime);

	timeTesting += DeltaTime;

	if (timeTesting >= 5 && !setedTest && !HasAuthority())
	{
		FString Fs = TEXT("Replicated client memory addr: ");
		Fs.AppendInt((uint32_t)ChatModule);
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, Fs);
		setedTest = true;
	}
}



after 5 sec prints:

RealMemAdd.png

[HR][/HR]

Aditional code:



void ASPlayerController::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(ASPlayerController, ChatModule);
}




void ASPlayerController::BeginPlay()
{
	Super::BeginPlay();


	if (HasAuthority())
	{
		FActorSpawnParameters SpawnInfo;
		SpawnInfo.Owner = this;
		GetWorld()->SpawnActor<ASChatModule>(ASChatModule::StaticClass(), SpawnInfo);
	}
}




ASChatModule::ASChatModule(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
	this->bReplicates = true;
	bAlwaysRelevant = true;	
}




	UPROPERTY(Replicated)
	ASChatModule* ChatModule;


Thats all i think, and thanks again for your time :slight_smile:

I think the missing link is setting the pointer on the server in the PlayerController:



void ASPlayerController::BeginPlay()
{
	Super::BeginPlay();


	if (HasAuthority())
	{
		FActorSpawnParameters SpawnInfo;
		SpawnInfo.Owner = this;
		**ChatModule = Cast<ASChatModule>(GetWorld()->SpawnActor<ASChatModule>(ASChatModule::StaticClass(), SpawnInfo));**
	}
}


:stuck_out_tongue: my bad, it works =D.

thank you very much Nissh, thanks for your time :).

Glad I could help. :slight_smile:

Omg this post saved me… I was so focused on how to replicate the information I needed and totally forgot the DOREPLIFETIME on the actor. Thank god I found this old post, which reminded me to be more carefull, thanks to all in this post!!!