How Can I Access Server SpawnActor In Client?

I want to replicate an Actor that has SpawnActor. How can I do that?

I’ve created a player’s weapon system that allows the player to equip items onto their weapon. However, this functionality doesn’t work in multiplayer, mainly because the weapon is created using SpawnActor at BeginPlay based on a DataAsset. So, I tried spawning it only on the server and replicating the value, but I still get null on the client side.

The ActionComponent has DataObjects registered for replication, and within these DataObjects, weapon data (Attachment, Equipment, DoAction) is registered. Both of them have Replicates set to true. Then, I use DataAssets to spawn the actual Actor and assign it. My expectation is that replication should occur during this assignment process, but the client doesn’t respond.

Here My Code:

this is DataObject
CActionObjectContainer.cpp 


void ACActionObjectContainer::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(ACActionObjectContainer, Attachment);
	DOREPLIFETIME(ACActionObjectContainer, Equipment);
	DOREPLIFETIME(ACActionObjectContainer, DoAction);
	DOREPLIFETIME(ACActionObjectContainer, EquipmentColor);
}
CActionComponent.h
private:
	UPROPERTY(EditDefaultsOnly)
		class UCActionData* Datas[(int32)EActionType::Max];
	UPROPERTY(Replicated)
		TArray<class ACActionObjectContainer*> DataObjects;
CActionComponent.cpp 
void UCActionComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(UCActionComponent, Type);
	DOREPLIFETIME(UCActionComponent, DataObjects);
}
UCActionComponent::UCActionComponent()
{
	DataObjects.SetNum((int32)EActionType::Max);
	PrimaryComponentTick.bCanEverTick = true;
	SetIsReplicatedByDefault(true);
}
void UCActionComponent::BeginPlay()
{
	if (!!charcater && charcater->HasAuthority())
	{
		for (int i = 0; i < (int32)EActionType::Max; ++i)
		{
			if (!!Datas[i])
			{
				// DataAsset`s Custom BeginPlay 
				//SpawnActor Attachment, Equipment, DoAction
				Datas[i]->BeginPlay(charcater, &DataObjects[i], i);
			}
		}
	}
}

CActionData.cpp
void UCActionData::BeginPlay(ACharacter* InOwnerCharacter, ACActionObjectContainer** OutObject, int32 Index)
{
	FTransform transform;

	ACAttachment* Attachment = nullptr;
	ACEquipment* Equipment = nullptr;
	ACDoAction* DoAction = nullptr;
	UCAttachmentStatusComponent* AttachmentStatusComp = nullptr;
     
/*       SpawnActor Attachment, Equipment, DoAction...
	if (!!AttachmentClass)
	{
		Attachment = InOwnerCharacter->GetWorld()->SpawnActorDeferred<ACAttachment>(AttachmentClass, transform, InOwnerCharacter);
		Attachment->Tags.Add(FName(*(GetLabelName(InOwnerCharacter, "Attachment"))));

		Attachment->SetRole(OwnerRole);
		Attachment->bNetUseOwnerRelevancy = true;
#if WITH_EDITOR
		Attachment->SetActorLabel(GetLabelName(InOwnerCharacter, "Attachment"));
#endif
		UGameplayStatics::FinishSpawningActor(Attachment, transform);	// 얘는 상속받은 위치에서 AttachTo 함 
	}
	

	//CEquipment 생성: Character 만 World가 있기에 


	if (!!Attachment)
	{
		AttachmentStatusComp = NewObject<UCAttachmentStatusComponent>(Attachment, UCAttachmentStatusComponent::StaticClass(), TEXT("AttachmentStatus"));
		if (!!AttachmentStatusComp)
		{
			Attachment->SetAttachmentStatusComponent(AttachmentStatusComp);		
			AttachmentStatusComp->InitStatus(Attachment->AttachmentStatusData);
		}
	}

	if (!!EquipmentClass)
	{
		Equipment = InOwnerCharacter->GetWorld()->SpawnActorDeferred<ACEquipment>(EquipmentClass, transform, InOwnerCharacter);
		Equipment->SetData(EquipmentData);	
		Equipment->SetColor(EquipmentColor);
		Equipment->SetRole(OwnerRole); 
		Equipment->bNetUseOwnerRelevancy = true;
		Equipment->Tags.Add(FName(*(GetLabelName(InOwnerCharacter, "Equipment"))));
#if WITH_EDITOR	
		Equipment->SetActorLabel(GetLabelName(InOwnerCharacter, "Equipment"));
#endif
		UGameplayStatics::FinishSpawningActor(Equipment, transform);
		Equipment->AttachToComponent(InOwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true));
	}	

	if (!!Attachment && !!Equipment) {
		Equipment->OnEquipmentDelegate.AddDynamic(Attachment, &ACAttachment::OnEquip);
		Equipment->OnUnequipmentDelegate.AddDynamic(Attachment, &ACAttachment::OnUnequip);
	}

	if (!!DoActionClass)
	{
		DoAction = InOwnerCharacter->GetWorld()->SpawnActorDeferred<ACDoAction>(DoActionClass, transform, InOwnerCharacter);
		DoAction->AttachToComponent(InOwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true));

		DoAction->SetRole(OwnerRole);
		DoAction->bNetUseOwnerRelevancy = true;
		DoAction->SetDatas(DoActionDatas);
		DoAction->SetStrongData(StrongData);
		DoAction->SetGuardData(BlockData);
		DoAction->SetParryData(ParryData);
		DoAction->SetParryDamageType(ParryDamageType); 

		DoAction->SetMyIndex(Index);
		DoAction->Tags.Add(FName(*(GetLabelName(InOwnerCharacter, "DoAction"))));
#if WITH_EDITOR
		DoAction->SetActorLabel(GetLabelName(InOwnerCharacter, "DoAction"));
#endif
		UGameplayStatics::FinishSpawningActor(DoAction, transform);
		
		if (!!Attachment)
		{
			Attachment->OnAttachmentBeginOverlap.AddDynamic(DoAction, &ACDoAction::OnAttachmentBeginOverlap);
			Attachment->OnAttachmentEndOverlap.AddDynamic(DoAction, &ACDoAction::OnAttachmentEndOverlap);
		}


	}//end if
	FActorSpawnParameters SpawnParams;
*/
	*OutObject = InOwnerCharacter->GetWorld()->SpawnActorDeferred<ACActionObjectContainer>(ACActionObjectContainer::StaticClass(), transform, InOwnerCharacter);
	(*OutObject)->SetOwnerCharacter(InOwnerCharacter);
	(*OutObject)->Attachment = Attachment;
	(*OutObject)->Equipment = Equipment;
	(*OutObject)->DoAction = DoAction;	
	(*OutObject)->EquipmentColor = EquipmentColor;

	UGameplayStatics::FinishSpawningActor(*OutObject, FTransform());
}

Set the Weapon Actor class to Replicate.
Spawn the actor on the server (Authoritative Proxy).
Set the Owner of the actor (Authoritative Proxy).
Attach the actor to the character (Authoritative Proxy).

This will replicate the weapon and attach it.

Adding attachments…
Attachment actors aren’t typically replicated. You’d generally setup a rep_notify enum in the weapon class where the onrep_function would handle specific states and changes.

For example a Grip attachment: Enum (none, Vertical, Horizontal, Thumb etc).

When players enter the characters/weapons net cull distance the rep_notify vars will be replicated and the onrep functions would execute.

The OnRep_GripAttachment function would be called check the enum → switch or whatever → Spawn “grip” → attach to weapon → update anim state for Grip etc.

In that case, it seems like my implementation approach might be incorrect. However, I still don’t think there should be an issue with accessing the replicated actors spawned on the server from the client. I’d like to inquire about replication once again if that’s okay with you.

All the actors and ActorComponent I mentioned have replication enabled(ActionObjectContainer, ActionComponent, etc…).
with SetReplicates() or SetReplicateByDefault()
also Object Reference Variables are Set UPROPERTY too.

CActionComponent.h
	UPROPERTY(Replicated)	
		TArray<class ACActionObjectContainer*> DataObjects;

CActionComponent.cpp
void UCActionComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(UCActionComponent, Type);
//	DOREPLIFETIME(UCActionComponent, DataObjects);
	DOREPLIFETIME_CONDITION_NOTIFY(UCActionComponent, DataObjects, COND_None, REPNOTIFY_Always);
}

UCActionComponent::UCActionComponent()
{
	DataObjects.SetNum((int32)EActionType::Max);
	PrimaryComponentTick.bCanEverTick = true;
	SetIsReplicatedByDefault(true);
}

CActionObjectContainer.h
	UPROPERTY(Replicated)
	class ACAttachment* Attachment;
	UPROPERTY(Replicated)
	class ACEquipment* Equipment;
	UPROPERTY(Replicated)
	class ACDoAction* DoAction;
CActionObjectContainer.cpp

void ACActionObjectContainer::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//	DOREPLIFETIME_CONDITION_NOTIFY(ACActionObjectContainer, Attachment, COND_None, REPNOTIFY_Always);
//	DOREPLIFETIME_CONDITION_NOTIFY(ACActionObjectContainer, Equipment, COND_None, REPNOTIFY_Always);
//	DOREPLIFETIME_CONDITION_NOTIFY(ACActionObjectContainer, DoAction, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME(ACActionObjectContainer, Attachment);
	DOREPLIFETIME(ACActionObjectContainer, Equipment);
	DOREPLIFETIME(ACActionObjectContainer, DoAction);
}

The rest is the same as the above code, spawning on the Server and setting the owner to the Player. Here, even though DataObject, which clearly spawns and assigns actors with data such as Attachment, Equipment, DoAction, etc., on the server, the DataObject on the client side still returns Null.

Replication only works Server → Client.

Messing with a local replicated actor won’t affect the servers authoritative proxy. You have to RPC the server and have it do modifications. Those changes/mods will replicate back down to clients.