How do I reliably know that an actor has spawned on all clients?

Hey, I’ve ran into this problem where after I made my important Custom events “Reliable” there are some sync issues between the server and the clients, especially when spawning new actors.

Ie. in my ability system when it’s time to spawn an ability in the the game, it executes everything as expected on the server, but the clients take a little longer to catch up with the fact that there is a new actor in the game. So the logic breaks on their side unless I put a delay between the Spawn Actor node and the rest of the flow because the “Ability” variable is empty for a few frames until they catch up.

What fixes it is a delay like this

but I suspect this isnt a very reliable long term solution and should be done differently.

How can I tell this blueprint to only “Begin Casting” once the ability has spawned on the clients?

What I’ve tried is making the Ability variable OnRep and calling Begin Casting from there, but there is no difference because the server fills the variable and it resumes without waiting for the clients that way too
Appreciate any help! :slight_smile:

Instead of replicating the function call, replicate the variable, and use RepNotify to get notified about variable change, and call the function (locally) from there.

Since RepNotify is also triggered on server, you don’t even need to call BeginCasting on server after the SpawnActor node. Just reduce the code to SpawnActor → set Abiltiy

Then, in function OnRep_Ability, call BeginCasting(Ability)

3 Likes

Thanks for the reply, I tried what you suggested



unfortunately it behaves exactly the same as before, the ability variable is empty on the clients. Again, only fixable with a short delay on BeginCasting

A couple of thoughts/suggestions

  1. You are correct in that adding a delay is a pretty good indicator of poor design and will inevitably end in unreliable functionality.
  2. Setting a reference variable to a spawned object as replicated does not guarantee that the object will actually be spawned and valid on the client side when the variable replicates. Which you have proven. Resulting again in unreliable behavior.

In order to make this sort of behavior work cleanly you’re going to have to adopt a slightly different design pattern. One way that I’ve solved this sort of issue in the past is to have the spawned actor itself let the owner know when it has been replicated. In your case, on begin play of the spawned actor it should grab the owner or instigator and let one or the other know that it is ready for action. There are a couple of ways to do this, events, interfaces, etc…you pick your poison. Maybe the spawned actor simply calls your BeginCasting function?

EDIT: I thought I’d add that you seem to be missing a notion of “locality” in some of your execution branches. For instance your BeginCasting appears to be doing something with the HUD. You’re only going to want that branch of code executing on the controlled pawn. In order to do that you’re going to want to add an “IsLocallyControlled” check.

1 Like

Thanks!


I tried triggering BeginCasting from the spawned actor, but its still the same, server is ahead of all the rest.

EDIT: I thought I’d add that you seem to be missing a notion of “locality” in some of your execution branches. For instance your BeginCasting appears to be doing something with the HUD. You’re only going to want that branch of code executing on the controlled pawn. In order to do that you’re going to want to add an “IsLocallyControlled” check.

Isn’t the validity check essentially doing the same? The HUD only exists locally so it is only valid on the local PC

EDIT:


I’m setting the Ability variable from the spawned actor and it still is empty on the clients :confused:

Trust but verify. I might be willing to argue that replacing the validity check with “IsLocallyControlled” will lead to more readable and thus maintainable code as the validity check requires you to know more about the situation/layout.

Yes, with the server and client running side by side (visually) the server will of course be “ahead” of everyone else. This delay though is hidden to the client in a real-world scenario (if visual delay is what you’re referencing of course)

Are you by chance destroying this actor somewhere? If so, remember that destroying the replicated actor on the server will propagate down to the clients. Depending on the speed of the animation, etc. this could be really fast and the actor could get destroyed before the client has time to do anything with it. You can verify if that’s your issue by unhooking any actor destroy calls. Logically it would not make any sense for the actor to provide a reference to itself on a function call and that reference end up being invalid otherwise…that would imply that “self” is invalid at the time of the function call.

They are being destroyed, but way later.

Anyway, here is the closest I got to a solution so far:
Inside the spawned actor on BeginPlay, I Multicast and make each PC report to the game mode that the Ability has spawned.

In game mode BP I add all of the instances of the ability into an array, then check if there is an equal amount of the ability instances as there are Player Controllers in the game.

Once there is the PC Begins Casting.

image

Only one issue. This only works when casting the ability on the clients, when doing it on the server it only registers the server spawn so it only advances the array length once per button click.

Not sure if this even is a valid way of doing things, but how come the clients don’t report to the game mode when its being cast on the server and are there any obvious fixes?

image

When I add a print string to the end of that flow, each PC prints it, so how come they don’t all fire the “Spawned” events?

I was overcomplicating things. The solution was quite simple

In the player controller I made BeginCasting multicast and “Set Ability” on the other end.

Everything works properly now, the variable is filled on all clients and the rest of the logic executes without issue, no delays needed

EDIT: Nope I was wrong, it works properly when the clients use an ability, but if its on the server it doesnt work because the variable is empty for a short duration on the clients

Okay I cannot even reproduce the issue in editor so it’s hard to tell, might depend on specific network conditions.

Your approach in this post is definitely the right one to ensure clients execute the cast as soon as they receive the ability, without running into race conditions. What kind of delay are you talking about there?

Wierd it isnt being reproduced, I havent tried myself. I should maybe mention that the Ability being cast is a child of a main Ability class then a Ability type class (there’s a type for projectiles/aoe/buffs/debuffs etc) So fireball would be decending from a hierarchy of Ability/Ability-Projectile/Fireball. Maybe this causes some extra delay when replicating to clients?

image
This delay is enough to make it work. Again, casting from a client window works as expected and replicates across all, but casting in the server window only works in that single window unless there is a delay for a few frames to allow the clients to catch up.

Ok, finally I think I found the solution.

Spawn the ability inside the PC then:

Inside the Ability BP

Inside the PC

The ability appears when the server or any of the clients cast it and replicates across the game.

Success!! Way to work through it.

1 Like

This was a great hint for me, too. I am currently extending an older system I’ve written to spawn replicated Actors in a multiplayer game to allow for any client to import Meshes at runtime and set a ProceduralMesh Component on the spawned Actor with the imported geometry so every client has the Mesh that was imported by any client.

Problem here is that the ProceduralMesh Component is not fully replicated itself so I need to set the geometry/Mesh data on each client. Consequently, I need a ref to the Actor that was spawned by the Server - but due to the networking, this Ref may not be valid immediately after the Server RPC was called to spawn the Actor.

Using RepNotify as replication setting for the Mesh data and the reference to the spawned Actor and then setting the Mesh section through the On_RepNotify event works like a charm.

Just posting to share this solution since I saw some forum posts on replication of ProceduralMeshes so this may come up in future internet or forum searches.