Can UObjects be replicated?


...
	UPROPERTY(Replicated)
	UGameplayState* MoveState;
...

void UArenaMovementComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(UArenaMovementComponent, MoveState);
}


UGameplayState is just a UObject.

The problem is that “MoveState” is not replicated to the client at all, it is always NULL.

My use case is


bool UArenaMovementComponent::IsMoving()
{
	return Cast<UGameplayState_MoveToLocation>(MoveState) != nullptr;
}

to check which state the server is currently in, which allows me to play the run animation in the animation Blueprint.

How do I replicate UObjects to clients?

1 Like

Okay I “solved” it.

First you need to override


	
bool IsSupportedForNetworking() const override{
    return true;
}

in your UObject.

And then you have to replicate it manually, like so:



bool UArenaMovementComponent::ReplicateSubobjects(class UActorChannel *Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags)
{
    bool wrote = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
    if (MoveState){
	wrote |= Channel->ReplicateSubobject(MoveState, *Bunch, *RepFlags);
    }
    return wrote;
}

Now I probably missed some important stuff, it’s probably not wise to just return true in “IsSupportedForNetworking”. I update this thread once I know more.

Looks a bit like you are trying to implement the Engines “GameState” yourself. Take a look here: Documentation: GameState

This object supports full replication.

As far as I remember the replication/networking support begins in the AActor class of the class hierarchy.

Thanks but I have also looked at this class. I want to implement something similar to the “State pattern”, the GameplayTask system comes very close but it supports multiple tasks where I want to be limited to 1 task.

I noticed most stuff inherits from AActor, I wonder if I should too? But there is so many stuff in AActor that I don’t need, like the MovementComponent, InputComponent, Transform, Velocity and the list goes on.

Hm, AActor is the first class that normally supports replication. It’s correct that they have a lot of stuff that you may not need, but then you could just not use it.
As long as Actors have not 3D representation like a Mesh or something, they are good to be used for Object like things.

So they are just invisible Actors somewhere in the world that you don’t have to think about. Epic does the same in several other projects of them.
UObjects are cool to use for non replicated Stuff though.

I’d also suggest to go with the AActor. AActors are linked to the world and have nice lifecycle methods you can use to handle certain events. See BeginPlay, EndPlay etc.

Also there is a good way to implement the states of a state machine with the “Within” class specifier (see here). You can find an example for that within the Unreal Tournament Project. Just look at the WeaponStates (e.g. UTWeaponStateFiring, UTWeaponStateActive, etc.).

Not entirely true. Actors are big (about 2KB) while plain object is less than 300 bytes.
Using actor just for the replication is just wasteful. If something doesn’t need physical representation in world, but you still need reflection and polymorphism, UObjects are very good choice for replication. The only cevat is that you have to replicate them trough actor or actor component. But that’s not really hard.

Unless the you only have very few replicated actors, then whatever. For anything more than 20 I would think twice.

I came to the same conclusion, Actors are great for single long lived objects, but I need many small short lived objects. For example I currently have around 15 different movement states for my MovementComponent like GameplayState_MoveToLocation, GameplayState_MoveToTarget, GameplayState_DashToLocation etc. Those objects may only live for a few seconds until they get replaced. Considering that this movement component will be used by every “unit” in my game and I can have ~100 of active units in my game things might matter.

Also it’s very easy to replicate an UObject, I mean it’s “basically” one line if they are owned by an Actor or ActorComponent.

Actually it might be a bit harder than I thought.

I spawn my objects like this


static UGameplayState_Nothing* Create(){
	return NewObject<UGameplayState_Nothing>();
}

Which works but some errors appear the in log


LogNetTraffic:Error: UActorChannel::ReadContentBlockHeader: Sub-object not in parent actor. SubObj: GameplayState_MoveToLocation /Engine/Transient.GameplayState_MoveToLocation_174, Actor: ArenaPawn_BP_C /Game/TopDown/Maps/UEDPIE_2_TopDownExampleMap.TopDownExampleMap:PersistentLevel.ArenaPawn_BP_C_5

My initial thought was that I should set the “outer” to my owning pawn.



return NewObject<UGameplayState_Nothing>(Owner);
//or
return NewObject<UGameplayState_Nothing>(MovementComponent);


but this will immediately disconnect the client like this


[2015.09.15-15.38.32:253][708]LogNet: Open TopDownExampleMap 09/15/15 17:38:32 127.0.0.1
[2015.09.15-15.38.32:253][708]LogNet: Added client connection.  Remote address = 127.0.0.1:51123
[2015.09.15-15.38.32:253][708]LogNetTraffic:Warning: High single frame packet loss: 11
[2015.09.15-15.38.32:403][720]LogNet: UNetConnection::Cleanup: Closing open connection. Name: IpConnection_26, RemoteAddr: 127.0.0.1:51124 Driver: GameNetDriver, PC: NoPC, Owner: No Owner
[2015.09.15-15.38.32:403][720]LogNet: UNetConnection::Close: Name: IpConnection_26, Driver: GameNetDriver IpNetDriver_20, PC: NULL, Owner: NULL, Channels: 1, RemoteAddr: 127.0.0.1:51124, Time: 2015.09.15-15.38.32
[2015.09.15-15.38.32:527][730]LogNet: UNetConnection::Cleanup: Closing open connection. Name: IpConnection_27, RemoteAddr: 127.0.0.1:51123 Driver: GameNetDriver, PC: NoPC, Owner: No Owner
[2015.09.15-15.38.32:527][730]LogNet: UNetConnection::Close: Name: IpConnection_27, Driver: GameNetDriver IpNetDriver_20, PC: NULL, Owner: NULL, Channels: 1, RemoteAddr: 127.0.0.1:51123, Time: 2015.09.15-15.38.32

Any ideas?

Are you sure, you are creating objects on server.?The outer is part of the problem. It seems like you trying to replicate uobject from different actor than it’s owner(ie outer and where the replicated uproperty is). This might happen indirectly, by copying reference to other object and tryingvto replicate it here.

Yes I am creating it on the server like this:


void AArenaPawn::ServerMoveToLocation_Implementation(FVector Location)
{
	ArenaMovementComponent->SetMovementState(UGameplayState_MoveToLocation::Create(this, Location, ArenaMovementComponent, RotateActorComponent));
}

But as you have guessed I call this function from AArenaPawn and not UArenaMovementComponent where it is replicated.


	static UGameplayState_MoveToLocation* Create(APawn* Owner, FVector GoalLocation, UArenaMovementComponent* MovementComp, URotateActorComponent* RotateActorComp){
		UGameplayState_MoveToLocation* Object = NewObject<UGameplayState_MoveToLocation>();
		Object->Pawn = Owner;
		Object->MovementComponent = MovementComp;
		Object->GoalLocation = GoalLocation;
		Object->CurrenPathIndex = 0;
		Object->RotateActorComponent = RotateActorComp;
		return Object;
	}


I tried to set the “Outer” explicitly to


UGameplayState_MoveToLocation* Object = NewObject<UGameplayState_MoveToLocation>(MovementComp);

But it also directly disconnects the client.

Update:

The code that I have posted works. The problem that I had was that I overwrote UObject::IsNameStableForNetworking | Unreal Engine Documentation to return true.

This actually resulted in a strange behavior. Once the object was marked as replicated, the client and server had access to the exact same object in memory.

I think this might be a bug with “Use Single instance” in the editor. Unfortunately I can’t disable it because it will result in a crash if I do so.

Should I make a bug report or is this “expected” behavior?