We’re observing that an InitialOnly array can contain incorrect values for a replay checkpoint, particularly if:
- the array had values for the prior checkpoint
- some array entry was removed, with the same value being readded later
- some other property replicated between the value removal and addition
My current understanding is that the LifetimeChangelist will contain the correct array properties when first captured in a checkpoint, but those values can be pruned when the other property replicates while the array is smaller. The catch is that the shadow data still contains the data from the previous checkpoint (the comparison will be skipped as the property is InitialOnly), so if the same values are added back before the next checkpoint, then the comparison against the shadow data at that point in time will appear as though nothing has changed - and the properties won’t be added back to the LifetimeChangelist for checkpoint capture. Note that this can apply to individual properties on a struct as well - i.e. if a mostly different struct is added in place of another in an InitialOnly array, but one of the struct’s properties is the same as the struct removed before it.
Something we’ve tried to mitigate this behavior is to avoid updating the shadow data after comparison for InitialOnly properties - this means that *any* non-default data will appear changed and can be added to the changelist, but this regresses some behavior around the SharedSerialization for live game reconnects where a property that is changed back to default will not appear as changed (and so the SharedSerialization may persist a stale value). Some things we’ve considered from here are:
- avoid updating the shadow data specifically for InitialOnly properties in replay connection
- avoid updating the shadow data for InitialOnly properties and prospectively reset the SharedSerialization when an InitialOnly properties appear unchanged
- prune changelists to the shadow data rather than the live object data
All of these options feel somewhat flawed, so we would appreciate any thoughts on if there’s a good way to resolve this in line with the intent for the system. I’d be happy to propose the fix for UE if we can get it clean enough, but it would be good to least understand if we can resolve this on our current version without digging too much of a hole.