I have got a state object in my custom player state that needs to be replicated across when it changes. This is something the engine can do, as referenced in the docs. Those are slightly out of date, but so far I have implemented ReplicateSubObject and something is happening.
The SubObject in question is UWeaponState, and is created in its parent state in a PostInitializeComponents() call.
void ARobotPlayerState::initWeaponStates()
{
if (WeaponMounts.Num() == 0)
{
for (int i = 0; i < NumberOfWeaponMounts; i++)
{
FName key = FName(*FString::Printf(TEXT("mount_%02d"), i));
UWeaponState *state = NewObject<UWeaponState>();
state->mountName = key;
state->weaponName = NAME_None;
state->RepObjId = i + 1;
state->SetFlags(RF_RootSet | RF_WasLoaded);
WeaponMounts.Add(key, state);
}
}
}
Like so. This function is called from PostInitializeComponents(). Setting the flags like that does two things - lets it be eligible for replication, and let it not be GCed. This is the bad way to do it. I know how to do the GC, how do I properly get it to not fire " Assertion failed: Object->IsSupportedForNetworking()" when I try to replicate without the RF_WasLoaded flag?
Here’s the function in RobotPlayerState that does the sub object replication:
bool ARobotPlayerState::ReplicateSubobjects(UActorChannel *Channel, FOutBunch *Bunch, FReplicationFlags *RepFlags)
{
check(Channel);
check(Bunch);
check(RepFlags);
bool WroteSomething = false;
if (Channel->KeyNeedsToReplicate(0, WeaponMountsRepKey)) // Does the array need to replicate?
{
TArray<FName> keys;
WeaponMounts.GetKeys(keys);
for (auto wmIt = keys.CreateIterator(); wmIt; wmIt++)
{
FName key = *wmIt;
UWeaponState * weaponState = WeaponMounts[key];
if (Channel->KeyNeedsToReplicate(weaponState->RepObjId, weaponState->RepKey))
{
WroteSomething |= Channel->ReplicateSubobject(weaponState, *Bunch, *RepFlags);
}
}
}
return WroteSomething;
}
Now, when I try to replicate with those flags in place, I get an error and the connection is closed. It’s here:
LogNetTraffic:Error: UActorChannel::ReadContentBlockHeader: Sub-object not in parent actor 1. SubObj: WeaponState_1, Actor: RobotPlayerState_2
LogNet:Error: UActorChannel::ReceivedBunch: ReadContentBlockHeader FAILED. Bunch.IsError() == TRUE. Closing connection.
What’s the proper way to set this up, so that it actually works?
EDIT: The header of UWeaponState so it’s clear what I am trying to replicate.
DECLARE_DELEGATE_OneParam(FWeaponStateChanged, UWeaponState *);
UCLASS()
class UWeaponState : public UObject
{
GENERATED_UCLASS_BODY()
private:
UFUNCTION()
void OnRep_Weapon();
public:
FWeaponStateChanged OnChange;
~UWeaponState();
int32 RepObjId;
int32 RepKey;
UPROPERTY(replicated, ReplicatedUsing=OnRep_Weapon)
FName weaponName;
UPROPERTY(replicated)
FName mountName;
bool IsSupportedForNetworking() const override;
};