Approach to implementing reconnect functionality

Hello everyone,
I recently started looking into Unreal Engine again after having worked with CryEngine for many years. Most things are still familiar to me but there are a few question I have regarding networking. There is a chance that players join the game at a later point, depending on how fast their PC is. Their pawns already exist in the world however, even if they are not controlled yet.

These pawns each have a component on them that keeps track of active buffs on that actor. I apply a general buff to all those player entities in the BeginPlay function on the server which works fine. If applied at ā€˜runtimeā€™ (when every player is connected) this will send an RPC to the relevant players that tells them of the added buff class and remaining duration. However, since players can join in late, the buff is already on that actor by the time it is being possessed. Ideally, I would want to re-send the active buffs using another bulk-RPC but I cannot find an appropriate callback to do that.

Calling an RPC on a PlayerController is safe after AGameModeBase::PostLogin but when is it safe to send an RPC on the owned pawn of a player? I could send the data over the PlayerController and cache it there until the pawn is ready but that seems very clunky. I could also send an RPC from the client to the server telling it to start syncing all the relevant data. But again, that seems clunky to me.

Also, yes, I am aware of replicating variables but for my purposes that would be overkill. Sometimes sending data in an RPC instead of a replicated variable is preferrable.

I guess I have a bunch of workarounds I could use but is there perhaps a more comfortable way of achieving this? I am thankful for anyone trying to help.

Regards,
Schadek

1 Like

Iā€™m sure this wonā€™t ā€˜helpā€™ much, but, after straight 3 months of MP development on our game with very little forward progress due to the 30 mins of package/upload/update for every hour of development that has been most difficult due to the way in which MP development occurs, especially if you are using Steam, I would say:

Forget about comfortable. Make it work in whatever way you can. It is ALL very clunky. Unless your sole goal IS exclusively network replication and RPC programming, then forget the theory and get it working in practice in any way that you can.

Once youā€™re game is done, only then seek comfort.

ā€œDonā€™t pray for an easy life. Pray for the strength to endure a difficult oneā€ - Bruce Lee

I have had to stick this quote in easy eye-reach. My estimates on completion of my tasks have been blown out by orders of magnitude.

For the record, here is what I am doing for the moment:
I store the relevant properties as a struct inside of the class (in this case the BuffComponent) and mark it as replicated. I set the replication of this struct to COND_InitialOnly, so it will only be synchronized once at the beginning of an actorā€™s lifetime on client machines. Then I override PreReplication and check how many clients there are connected to the UNetDriver in my UWorld. If it differs from my last check, I update the replicated struct to the current state. It will be synchronized over the network with the initial bunch of data and in BeginPlay I can read out the variableā€™s content on the client. Itā€™s not ideal by any means but anything further will require changes to the engine itself, which is why I am currently downloading the engine source code.

To me it sounds like youā€™re doing what needs to be done, as am I :). Please let me know here how it progresses for you.

After as much time as Iā€™ve spent in the engine networking code, and have had to resort to doing data transfers using RPC one row at a time, I would be great to get your takes after your analysis and implementations!

Okay, some new findings:
It seems like there is something equivalent to the NetworkSpawnParams of CryEngine for Actors. You can override OnSerializeNewActor to feed in data which you can then read out in OnActorChannelOpen. This is already being used for the APlayerController class. This data will be serialized once when the actor is first sent to clients.

Nice, good findings! Thanks for the update.

For me on my custom classes, I ended up not actually replicating the objects themselves, but sending the data in a struct and then newing up the object on the receiving end and setting the data in the RPC.

Some more information for whoever will find this useful:
I am now storing all of my buffs as actor sub objects. There is a very helpful article here: Replicating UObjects: Building a Flexible Inventory System ā€“ James Baxter about the topic. It explains how to even allow RPCs on these subobjects which makes network communication a lot simpler.
In addition, I have found a somewhat weird looking article about Unrealā€™s (somewhat new) Push Model, which mimics aspects of CryEngine. Basically, instead of relying on the network layer to always check whether a property has been changed, you can now explicitly mark a property as dirty. If it is not dirty, the replicator can skip a property and does not waste resources on that variable. For a lot of objects with a lot of variables this is much preferable in my opinion. You just have to keep track of who or what changes your variable and mark the variable as dirty.

Combining these two things allow me to achieve my goal, even if it was using a different approach than I initially was intended to use.

1 Like