When you spawn a replicated actor on a server, it is my understanding that the creation of the actor on the client side will only have default values for that actor. It will not have the replicated variables the server has until after the actor is created.
I’ve been searching for the recommended way to handle a setup where you want to provide a spawning actor variables on creation. I’ve come up empty handed from official documentation and from years of people asking similar questions on these forums except someone suggesting the
Event BeginPlay is a good place.
What I’ve concluded investigating the code and playing with it:
Replicated vars provided at spawn* will not show up in the
Construction Script on Blueprints for that replicated actor.
Replicated vars provided at spawn* will show up, even under large 1000ms+ latency and packet loss, after the
BeginPlay event on Blueprints (and C++ code) for the replicated actor.
Alternatively, RepNotify setting on the var works as well. However, if your logic is “only on spawn”/trigger once - you need to guard against the value possibly changing in the future and retriggering logic. unless you set COND_Initial - I think that prevents it from replicating changes in the future correct?
Here’s an example scenario:
Server spawns a cube, but it has to tell the cube at spawn to be a specific color - ie blue or red.
The receiving player gets the cube spawned, but does not know its color during the
Any logic the cube needs that wants to know about the color var needs to happen after
Event BeginPlay if the logic is for only triggered once on spawn (eg play a spawn FX), and/or logic in ‘RepNotify’ if the cube needs to react to color ever possibly changing (eg change the color of the appearence)
Is this understanding correct? Are there better ways I have overlooked or misunderstood? I need to be able to provide a concrete method for non-programmers to be able to spawn replicated actors, with parameters, that won’t bite us later.
Event BeginPlay seems to be the hotness.
- Note: When I say “provided at spawn”, I mean in Blueprints the replicated variable has been flagged as “Expose on Spawn” so that when the Blueprint that spawns the replicated actor (eg our color cube), the variable is provided immediately on the spawn node (eg the color var).
I recently encountered a similar issue when attempting to create a reconnect feature in my game. While I solved my issue in a different way, perhaps this can help you as a workaround:
Every AActor has two functions ‘OnSerializeNewActor’ (Server) and ‘OnActorChannelOpen’ (Client) that you can override and send custom data with. As a reference, you can take a look at the implementation in APlayerController. Unreal does not provide any syntactic sugar with this, so if you wanted to leverage this feature, you would have to implement it manually, I’m afraid. Still, I think this is one possibility to send such ‘spawn parameters’. I am unable to test it out myself right now but judging from the code this serialization step is executed after BeginPlay on clients.
Best approach from my experience is to have each variable set to replicate and trigger initial usage via Begin Play. You can also use Rep_Notify. It’s all case dependent.
- note : If you have actors in the level that spawn things, said actor needs to use switch has authority (auth) before spawning. Otherwise both the server and the client will spawn the new actor. This results in two identical actors instead of one.
I think in your situation, RepNotify is your best choice. I understand that if you don’t want the colour to change later, then you have to write extra code, but in most cases, I don’t see why you wouldn’t want it to be mutable. If you don’t want it to be changed, then don’t tell the server to change it.
Just to make this clear as I had to learn this out myself, the initial replication happens after AActor::BeginPlay has been executed. Any logic in BeginPlay but before AActor::BeginPlay (usually Super::BeginPlay) is the only place where the client can do stuff to the Actor before replication. (Useful for subscribing to events that rely on replicated values).
I do not believe OnConstruction/ConstructionScript is ever called on clients on a replicated actor. It is only called when the actor is ‘created’, and it’s the server (or the Editor) who is responsible for creating it.
Thanks. I’m wondering if maybe I used too trivial of an example with the cube. It’s not so much about things not being mutable, it’s about triggering logic on spawn.
if your cube color is the only variable, but you want to trigger logic on the client only the first time that color changes - then you have to add that extra code I was talking about. You want to change the color multiple times (mutable), but you only want specific logic to happen on the first RepNotify. Pain in the butt. I see this as a problem every designer is going to have if I present this as a solution, or they’ll just forget to not manually add guards.
Beyond the cube example - say you have an actor with 5+ variables to set on spawn, that means 5+ different repnotify callbacks trigger and you have no ordering to them and no confirmation that other variables have been set too. In the scope of a repnotify call, it has nothing to do with “spawning” and in code or in blueprint, you have no way of checking “Is This Spawning”.
RepNotify is called anytime that single variable has been modified by the server, there’s no context to it. That’s problematic to me but maybe I’m missing something about RepNotify that allows all of them to trigger together somehow. The problem becomes more challenging too if you have 2 values that rely on each other being set prior to triggering some kind of other event. So both repnotifies would need manual code done to check if the other values are present in some combination.
A potentially larger problem I’m curious of is if “RepNotify” is guaranteed to send a variable with the spawn data in the same frame, but I haven’t dug that far under the hood in the ActorChannel.
For example, you spawn a cube and set the color in the same frame on the server. Will RepNotify trigger the color change on the same frame it spawns on the replicated clients? Is it possible a cube spawns on the client, and the server has decided to put the color change in a different packet or the client executes the RepNotify on a different frame than spawn? That’s more troubling than the rest in my mind and I need to dig deeper on it. I can’t have things spawning without parameters guaranteed to be there.
Sidenote: Yes OnConstruction is called for replicated actors (at least the debugger triggered on my blueprints for a client on it). Though, like I said, variables are not replicated at that stage. So you attempted to get/use a variable in OnConstruction that’s replicated, expect problems and potentially crashes which is how I landed here.
RepNotify is triggered in two ways.
The first is whenever a value is changed (in this case, it’s whenever the client receives the packet).
The second is when the actor is first replicated (I believe it is first frame, since stuff is replicated before BeginPlay is finished). You can disable the second behaviour through conditions if you wish.
If you truly want initial only logic, then you should use the initial only condition.
If you want logic that differs based on the number of times it has changed (such as the first call), then I don’t see any other way than writing some code that records the occurrences.
As for different values depending on each other. This is extremely difficult to solve I think. I have definitely had some trouble with this in the past. It’s definitely not an simple solution though.
Two possible solutions I can think of is to simply check if the condition has been met, through both RepNotifys or through the Tick. (I know it doesn’t sound optimal but it works in many cases).
The second is to remove the values depending on each other completely. Do these two replicated values NEED to depend on each other? Can you just merge them into a USTRUCT? Does the client even need to know about it? I know in my case, it’s very rare that two values need to depend on each other.
In my experience, multiplayer games need to be designed differently and very carefully as you can never guarantee the order of execution.
Oh cool! I did not know RepNotify will trigger with the spawn, that’s very good info.
As long as they’re available together before BeginPlay, I think it’s safe to presume that BeginPlay can be treated as a “OnSpawn” event with replication. And then RepNotify can be used for any logic that doesn’t need to have context like spawning or other stuff. Like if the color value of the cube changes, just do the logic to change colors.
I have a lot of cases where when something spawns off the server, the client will need to know multiple pieces of info before triggering logic.
I thought about the Ustruct bundle, but that’s programming work for designers I want to avoid (although easy if needed, just boilerplate). That could be useful for super specific scenarios so that’s good to note it can be used - ie if only these linked values change trigger a RepNotify for the bundle. Probably will come up eventually.
You can give it a go but I do think the BeginPlay solution isn’t perfect though. There is a good chance that if there’s high latency or a dropped packet, some values may not be ready in time (I do not believe I have ever encountered this though).
I cannot confirm this. It is entirely possible that Unreal does something where the spawning of a replicated object is only triggered when the client has all the initial replicated values ready.
I’m not sure if you need any more advice but I just spent the last few hours going through this so I 100% know what I’m doing is right.
It seems I was incorrect when I said OnConstruction isn’t called on Clients. However, it should still be avoided because it will only ever be called for Actors that weren’t originally part of the level (i.e. created from SpawnActor()). There is a version for Actors that were originally part of the level called OnPostLoad, however, at this point in the lifecycle, the Actor isn’t aware of its NetRole (whether it’s a server or a client).
Ultimately, both should be avoided for anything other than what it was designed for (which is adding components and setting initial values).
Replication and thus RepNotifies do indeed always seem to be triggered before BeginPlay. Alternatively, you can also use PostNetInit but that only works for clients and doesn’t work for Actors that were originally part of the level.
If you want to do stuff before any replication (i.e. subscribe to certain events before any RepNotifies which will later trigger them), then your best (and basically only choice) is to use PostInitializeComponents. However, when using this method, you will have to add an
if (IsValid(GetWorld()) && GetWorld()->IsGameWorld()) check since it is also called when opening a Blueprint in the Editor (under certain circumstances, this can cause crashes).
The same idea applies to Components too, with OnRegister being the Component version of PostInitializeComponents.
In conclusion, BeginPlay is basically OnStart (as in, the Actor is completely ready and the game has started, but I assume most of us already knew that), then PostInitializeComponents and OnRegister is basically OnReady (as in, the Actor is ready to be started; the only thing left is replication).
I would have called OnReady OnCreated instead but UE4’s definition of ‘created’ doesn’t include objects that were instantiated from a serialized file (i.e. a level).