Issue with Replicating Inventory Contents in UE5.4 - Array of Null pointers

Hello everyone,

I’m experiencing an issue with my inventory system in UE5.4 and would appreciate any suggestions.

I have a class UInventoryActorComponent : public UActorComponent attached to my APlayerCharacter class. The inventory contents are defined as follows:

UPROPERTY(ReplicatedUsing = OnRep_InventoryContents)
TArray<UItemBase *> InventoryContents;

The inventory system works perfectly on the server, for both the client and server. However, when a client picks up items, the InventoryContents array gets filled with nulls. Here’s a log of the inventory after picking up 3 items:

The replication for UInventoryActorComponent is set in the constructor:

UInventoryActorComponent::UInventoryActorComponent()
{
    SetIsReplicatedByDefault(true);
}

I’ve also tried changing the definition of UItemBase from UObject to AItemBase, but that didn’t make a difference.

I have gone through similar topics on the forum but couldn’t find any relevant solutions. Any suggestions would be greatly appreciated!

Thank you!

your Itembase has to be set to replicated too, which by default UObjects cant.
you can enable it in c++ though

Thanks for the suggestion @Auran13. I have tried to change the definition of my ItemBase to inherit from AActor, and it didnt help.

I’ve been tinkering around for a while now and setting bReplicates, or even serializing the object manually or replicating each property of the AItemBase manually did not make any change either.

I have tried to create a copy of TArray<AItemBase *> InventoryContents in the PlayerCharacter and update it each time server updates it’s version from the AInventoryActorComponent, but it has also replicated only nulls.

I did try changing the array declaration to TArray<TObjectPtr>, but no luck.

Any other ideas?

You need to somehow replicate your items
If they are actors, then they can replicate themselves
If they are objects, they need to be replicated through an actor, like for example the inventory owner

It’s pretty easy if you set bReplicateUsingRegisteredSubObjectList to true on your actor, and then when creating the item to add it to the inventory, you also add it as a replicated subobject using AddReplicatedSubObject(MyItem)

That should then replicate the item to other clients, and the pointers should be valid on clients as well

More on replicated subobjects list:
Using the Replicated SubObjects List | Epic Developer Community (epicgames.com)

Thank you @zeaf, This has put a new light on the case. After implementing this form of UObject replication the client started to see the array of empty (not initialized) objects rather than array of nulls - which is a progress.

for the context this is how I initialize the objects before adding to the inventory:

void UInventoryActorComponent::ServerAddNewItem(UItemBase *Item, int32 AmountToAdd)
{
	if (!Item || AmountToAdd <= 0)
	{
		UE_LOG(LogTemp, Error, TEXT("Invalid item or amount in ServerAddNewItem"));
		return;
	}

	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("AddNewItem function called"));

	UItemBase *NewItem;

	if (Item->bIsCopy || Item->bIsPickup)
	{
		NewItem = Item;
		NewItem->ResetItemFlags();
	}
	else
	{
		NewItem = Item->CreateItemCopy();
		if (!NewItem)
		{
			UE_LOG(LogTemp, Error, TEXT("Failed to create item copy"));
			return;
		}
		AddReplicatedSubObject(NewItem);
	}

	NewItem->OwningInventory = this;
	NewItem->SetQuantity(AmountToAdd);

	InventoryContents.Add(NewItem);

	InventoryWeight += NewItem->GetItemStackWeight();
	UE_LOG(LogTemp, Error, TEXT("==================================== Inventory Contents ========================================"));
	OnInventoryUpdated.Broadcast();
}
UItemBase *UItemBase::CreateItemCopy() const
{
    UItemBase *ItemCopy = NewObject<UItemBase>(GetClass());

    // Copy properties from the original item
    ItemCopy->ItemID = this->ItemID;
    ItemCopy->Quantity = this->Quantity;
    ItemCopy->ItemQuality = this->ItemQuality;
    ItemCopy->ItemType = this->ItemType;
    ItemCopy->ItemTextData = this->ItemTextData;
    ItemCopy->ItemStatistics = this->ItemStatistics;
    ItemCopy->ItemNumericData = this->ItemNumericData;
    ItemCopy->ItemAssetData = this->ItemAssetData;
    ItemCopy->bIsCopy = true;

    return ItemCopy;
}

So at this point something is being replicated but still not the same object as seen on the server.

Properties that you want replicated need to be marked as replicated as well

So your ItemID, Quantity, ItemQuality etc all need to be marked as Replicated, and added to the GetLifetimeReplicatedProps of your Item class

Thank you @zeaf.
Setting bReplicateUsingRegisteredSubObjectList method of replicating UObject worked when also marked all UItemBase properties for replication

void UItemBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty> &OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(UItemBase, ItemID);
    DOREPLIFETIME(UItemBase, Quantity);
    DOREPLIFETIME(UItemBase, ItemType);
    DOREPLIFETIME(UItemBase, ItemQuality);
    DOREPLIFETIME(UItemBase, ItemStatistics);
    DOREPLIFETIME(UItemBase, ItemTextData);
    DOREPLIFETIME(UItemBase, ItemNumericData);
    DOREPLIFETIME(UItemBase, ItemAssetData);
    DOREPLIFETIME(UItemBase, OwningInventory);
}

I mark your answer as Solution as this mostly solves my problem, but I’d appreciate if you could help me understand why It wouldnt work when replicating array of AActors even if configured as:

AItemBase::AItemBase() : bIsCopy(false), bIsPickup(false)
{
    bReplicates = true;
    bAlwaysRelevant = true;
}

Shouldnt AActors have the replication functionality under the hood?

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.