Download

Problem with replication of sub-objects when changing inventories.

Hello! I’ve been searching about this problem I have but I couldn’t find anything, so I decided to ask here. Thanks in advance!

I have an Inventory which is an ActorComponent, and it has a TArray of UItem (UObject). The replication works almost fine, but there is an error when an item change its inventory.

I don’t see any gameplay problems, I can move items from one inventory to another, everything looks fine, and that’s probably because it seems that the error is not thrown by the server (I might be wrong) but the Client.
The error I’m getting is: LogNetTraffic: Error: UActorChannel::ReadContentBlockHeader: Sub-object not in parent actor.

I did some debug and I found that the ‘OuterPrivate’ of the UItem is not changing in the client. The server has the updated OuterPrivate.
When the item changes from one inventory to another, I use the Rename method with the new Outer.

// This is in the Inventory.cpp when adding an item.
Item->Rename(*Item->GetName(), this);

The only solution I found is that instead of changing the outer, I could duplicate the object. That ‘seems to work’ but I don’t like that solution.

UItem* NewItem = DuplicateObject<UItem>(Item, this);

This is the relevant code:

Inventory.h

class UInventory : public UActorComponent

public:
  UPROPERTY(Replicated, BlueprintReadWrite, ReplicatedUsing = OnRep_Items)
  TArray<class UItem*> Items; 

  virtual bool ReplicateSubobjects(class UActorChannel *Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags) override;
  ...

Inventory.cpp

void UInventory::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME_CONDITION_NOTIFY(UInventory, Items, COND_None, REPNOTIFY_Always);
}

bool UInventory::ReplicateSubobjects(UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags)
{
	bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);

	for (int32 Index = 0; Index != Items.Num(); ++Index)
	{
		UItem* Item = Items[Index];
		if (!IsValid(Item)) continue;
		WroteSomething |= Channel->ReplicateSubobject(Item, *Bunch, *RepFlags);
	}
	return WroteSomething;
}

UItem.h

class UItem : public UObject

public:
  UPROPERTY(Replicated, BlueprintReadOnly, Category = "Item", Meta = (ExposeOnSpawn = "true"))
  UItemData* Data;
  UPROPERTY(Replicated, EditDefaultsOnly, BlueprintReadOnly, ReplicatedUsing = OnRep_OnChanged, Category = "Item")
  int Quantity = 1;
  UPROPERTY(Replicated, EditDefaultsOnly, BlueprintReadOnly, ReplicatedUsing = OnRep_OnChanged, Category = "Item")
  int RequiredLevel = 0;
  ...

  virtual bool IsSupportedForNetworking() const override;
  virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

UItem.cpp

bool UItem::IsSupportedForNetworking() const
{
	return true;
}

void UItem::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(UItem, Data);
	DOREPLIFETIME(UItem, Quantity);
	DOREPLIFETIME(UItem, RequiredLevel);
}

Thank you!