Repication of UObjects?

Hi. It’s been many months since I last experimented with a multiplayer-project. For my current project I hadn’t planned on supporting multiplayer before, but now I think it would benefit a great deal from a coop mode. There’s just one big, open question for me. I am using a lot of UObject-derived classes, because I need them to persist throughout the levels. Actors in levels often need to reference those objects, because they represent them in one way or another. The problem is that UObjects don’t support replication iirc.

An example: Character-UObjects that store all the data that is used by Character-AActors (like health, appearence, skills, inventory etc.) while a level is being played. So actors have to read and can also manipulate data from their persistent UObject-counterparts. When the level is done, the character-AActor gets destroyed, the character-UObject persists and gets connected to a new character-AActor in the next level. I basically need the full replication program for this, replicating member-variables, syncing construction and destruction.

So how can I approach this??

I also want to make this a feature request: add an object in the hierarchy between UObject and AActor that supports replication, but is not bound to a level and therefore persistent.

edit: found this link https://wiki.unrealengine.com/Replication#Advanced:_Generic_replication_of_Actor_Subobjects

This almost sounds like it could help me out, but I don’t know if it will work with UObjects that exist long before creating the replication-actor. The way it’s described there is to create an actor and then some replicated UObject as a subobject of the actor.
The way I would be able to make use of it is to create a replication actor and then somehow “make” the existing UObject a subobject of the actor. Don’t know if clients will know what to make of that and actually construct and replicate objects this way… Anyone know if this will work?

Why do the UObjects needs to be persistent? Where are you storing them, the game instance? game state? If you are using them for simple data storing i.e. health, inventory I would suggest using structs instead you can have them as variables on actors and replicate them.

If I understand your problem correctly you want your actors to replicate data that you are storing inside UObjects external to the actors that use them and this data needs to be level persistent (but not game persistent, as in character progress). I have a question about your game. Is it a multiplayer where characters join a level map but you want to have persistent data when they leave the map? Now if they leave the map, or disconnect then how is the server supposed to know who does the data that it saved belongs to? You would have to create some sort of complicated id logic that only lasts while the server lives. Maybe what you really need is an external database

I guess the best option to be able to port your game to be multiplayer ready is to use the GameState for game related data which will be replicated to all connections while using the PlayerState for the player related data (health, appearance, etc).

The GameState instance is persistent while you do not change the GameMode, this means when traveling the data will be there. The PlayerState is a different beast. What the engine does is to build a new PlayerState and let you persist values when it needs to. For example if a player disconnects it will save the PlayerState into an inactive array so you can recover the persistent data if the same player connects again. To persist the PlayerState data you should override the following methods: OverrideWith, SeamlessTravelTo and CopyProperties. This is also supported in BP from version 4.13.

I would really not base all your networking on subobjects, the more objects you have that need replication the slower all network tasks will be. GameState and PlayerState are generally enough to handle the main load of state interaction.

It sounds like you understand the idea behind what I’m trying to do. I’m not sure what you mean by “not game persistent” though. Yes, all the data-objects need to be game-persistent and exist on a “meta-plane”, which is basically independent of maps/levels, yet levels need to reference them so they can represent them with actors. Players have to beat level after level, but all the UObject data needs to be carried over from one to the next. So far, I have been managing those objects in my GameInstance exclusively.

If I use structs, I basically have to make copies of my UObjects during each level and move the data back and forth all the time. I hope there’s a better way. An external database would complicate things even more. After reading the link I posted above, I’ve got some hope I can still use the inbuilt GUID system, even if it takes some workarounds.

And the game I am making, I think you could imagine something between Alien Swarm and Dungeon of the Endless, but with a meta-game on top that connects the individual levels and puts them into context. And it needs persistent data for that (lots of it).

I’m not so worried about network traffic. I am aiming for a 4-player coop mode and it’s not like all my persistent UObject-DataObjects are changing all the time - definitely not on a per-frame basis. The most important part is that clients know they exist, can access their data and know how to sync them.

Since when is a GameState persistent by the way? :confused:

You mean with seamless travel? Yeah, maybe an empty persistent level (not really familiar with the concept, though) that only contains my persistent data-objects as replicated actors might be a plan B. But first I’m gonna try what I wrote in the first post. If it works like I’m imagining, it would be a really clean and simlpe solution.

To make the gamestate persistent just save/load what you need using https://docs.unrealengine.com/latest/INT/API/Runtime/Engine/GameFramework/AGameState/SeamlessTravelTransitionCheckpoi-/index.html

I tested the code from the link with some test classes. I don’t see any reason why it shouldn’t work with my other UObjects as well. However, since the replicated subobjects need to use the replicated actor as “outer”, I don’t think I can do it the way I wanted - dynamically attaching and removing (back to transient package) objects to the actor to control their replication. Because afaik, you can’t change an object’s outer dynamically. So, if I understand it correctly, this means that all my UObjects need to be created “within” the actor right from the start and are doomed to go down with the ship when the actor gets destroyed on level change.

I was already thinking about persistent levels and actors, when I realised that I won’t be needing any level changes at all in my game, because all environments are procedurally generated. So, I just need to clear my level geometry when building a new scene and leave the UObject-replication-actor alone. Any actor can be persistent when you only need one level :stuck_out_tongue:

I’m just a bit worried about network performance after all, because I don’t know how the following function works, when it is called, and if it just replicates what needs to be replicated or creates lots of redundant replication traffic for ALL subobjects all the time :confused:

bool AReplicatedActor::ReplicateSubobjects(class UActorChannel *Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags)
{
    bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
 
    if (Subobject != nullptr)
    {
        WroteSomething |= Channel->ReplicateSubobject(Subobject, *Bunch, *RepFlags);
    }
 
    return WroteSomething;
}

@Moss "I would really not base all your networking on subobjects, the more objects you have that need replication the slower all network tasks will be. GameState and PlayerState are generally enough to handle the main load of state interaction."

I am already using UObjects, which are much smaller and probably less involved in “engine-internal referencing” than AActors. What else can I do?

There is lots of data that is not directly related to any single player (or some global state), and the objects I am organizing it in share a rather expansive inheritance-tree and are highly “inter-connected” among themselves and to level-actors. Using structures inside a game-state wouldn’t give me the flexibility I need for this, and since structs don’t have a GUID, replication of arrays of structs would probably inevitably lead to extremely redundant data-replication, because I can’t replicate a struct inside an array on its own afaik. I would need to use replicated functions for that, which would be a big problem when players have to re-connect or join the game in progress and need the current state of every object. Not to mention it would also be a lot more work writing functions for replicating specific structure-instances.

So I guess I have to do it this way and hope that the engine’s replication system is good enough to know when and what properties of a sub-object need to be replicated, instead of replicating un-changed data.

Unchanged data wont be replicatef again so you don’t have to worry about that.