Actor Components have an inbuilt ability to replicate sub-objects and have done since forever. This feature doesn’t seem to be used very often, but it’s extremely useful for creating self-contained systems.
I’ve been building an inventory system - and it works(ed) pretty much flawlessly, until actors start being destroyed and recreated by the Net Relevancy system. It’s exposed a pretty gigantic flaw with the system. The following is a brief summary of the issue, and how it’s being caused.
Inventory Component
Contains a replicated array of ‘UInventorySlots’. The slots are EditInlineNew, and blueprintable so that they can add custom properties.
class UInventoryComponent : public UActorComponent
UPROPERTY(EditAnywhere, Instanced, BlueprintReadOnly, Category = "Slots", ReplicatedUsing = "OnRep_Slots")
TArray<UST_InventorySlot*> Slots;
Note the ‘Instanced’ flag on the array of slots. This is especially important because I need the slots to be able to be created inline. This allows an actor to have an ‘Inventory’, then specify whatever ‘Slots’ it likes for that inventory like so:
Another side-effect of this is that the slot is created with the Inventory Component as it’s ‘Outer’ - this is good and what I want to happen.
The slots are replicated by the inventory component the same way you would replicate any other sub-object and as described by the documentation, like so:
bool UST_InventoryComponent::ReplicateSubobjects(UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags)
{
bool bWroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
bWroteSomething |= Channel->ReplicateSubobjectList(Slots, *Bunch, *RepFlags);
return bWroteSomething;
}
void UST_InventoryComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UST_InventoryComponent, Slots);
}
Now - this works absolutely flawlessly. I can spawn two clients with a pawn that has an inventory, and the default slots and corresponding items are also created as they should be.
Unfortunately however, as soon as that pawn goes out of relevancy range of a player and then returns (i.e, all objects are destroyed and re-created from replication) - the Actor Channel causes a crash. The sub-objects are recreated but are created inside the OLD inventory component and actor, which is pending kill. As soon as GC runs, the game crashes. It’s pretty obvious that this is a bug to me.
Another issue is that sub-objects which are created dynamically and not in that array by default - are actually created on the client using the Actor as the outer and NOT the component. This is caused by DataChannel.cpp line 3140 - where it creates the new object using the actor channels’ actor as the outer of the new object. It should be using the component that replicated it as the outer.
So basically, sub-object replication in the actor component is pretty useless while this is the case. This feels like the perfect application for it.
There seem to be a lot of other issues when using EditInlineNew and creating Instances of UObjects directly inline, such as struct UPROPERTY’s not showing in details panels etc. This area really could use some attention…