Repository navigation
fix: [UpPort] NetworkList changes pending during client synchronization are applied twice #4216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop-3.x.x
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,10 @@ public class NetworkList<T> : NetworkVariableBase where T : unmanaged, IEquatabl | |
| private NativeList<T> m_List = new NativeList<T>(64, Allocator.Persistent); | ||
| private NativeList<NetworkListEvent<T>> m_DirtyEvents = new NativeList<NetworkListEvent<T>>(64, Allocator.Persistent); | ||
|
|
||
| // The list as it was before the first local change that is still pending in m_DirtyEvents | ||
| private NativeList<T> m_LastSentList = new NativeList<T>(64, Allocator.Persistent); | ||
| private bool m_HasLastSentList; | ||
|
|
||
| /// <summary> | ||
| /// Delegate type for list changed event | ||
| /// </summary> | ||
|
|
@@ -68,6 +72,7 @@ public override void ResetDirty() | |
| { | ||
| m_DirtyEvents.Clear(); | ||
| } | ||
| m_HasLastSentList = false; | ||
| } | ||
|
|
||
| /// <inheritdoc cref="NetworkVariable{T}.IsDirty"/> | ||
|
|
@@ -150,10 +155,42 @@ private T ReadValue(FastBufferReader reader) | |
| /// <inheritdoc cref="NetworkVariable{T}.WriteField"/> | ||
| public override void WriteField(FastBufferWriter writer) | ||
| { | ||
| writer.WriteValueSafe((ushort)m_List.Length); | ||
| for (int i = 0; i < m_List.Length; i++) | ||
| WriteList(writer, m_List); | ||
| } | ||
|
|
||
| internal override void WriteFieldSynchronization(FastBufferWriter writer) | ||
| { | ||
| // The pending events are sent to the synchronizing client with the next delta, so it must | ||
| // receive the list they apply to. Otherwise it would apply them twice. | ||
| if (m_HasLastSentList && m_DirtyEvents.Length > 0) | ||
| { | ||
| WriteList(writer, m_LastSentList); | ||
| } | ||
| else | ||
| { | ||
| base.WriteFieldSynchronization(writer); | ||
| } | ||
| } | ||
|
|
||
| private static void WriteList(FastBufferWriter writer, NativeList<T> list) | ||
| { | ||
| writer.WriteValueSafe((ushort)list.Length); | ||
| for (int i = 0; i < list.Length; i++) | ||
| { | ||
| NetworkVariableSerialization<T>.Serializer.Write(writer, ref m_List.ElementAt(i)); | ||
| NetworkVariableSerialization<T>.Serializer.Write(writer, ref list.ElementAt(i)); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Invoked before a local change. Keeps a copy of the list as of the last sent delta | ||
| /// so <see cref="WriteFieldSynchronization"/> can send it while changes are pending. | ||
| /// </summary> | ||
| private void CaptureLastSentList() | ||
| { | ||
| if (m_DirtyEvents.Length == 0) | ||
| { | ||
| m_LastSentList.CopyFrom(m_List); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An initially empty list populated with 🤖 Helpful? 👍/👎 |
||
| m_HasLastSentList = true; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -440,6 +477,7 @@ public void Add(T item) | |
| return; | ||
| } | ||
|
|
||
| CaptureLastSentList(); | ||
| m_List.Add(item); | ||
|
|
||
| var listEvent = new NetworkListEvent<T>() | ||
|
|
@@ -467,6 +505,7 @@ public void Clear() | |
| return; | ||
| } | ||
|
|
||
| CaptureLastSentList(); | ||
| m_List.Clear(); | ||
|
|
||
| var listEvent = new NetworkListEvent<T>() | ||
|
|
@@ -511,6 +550,7 @@ public bool Remove(T item) | |
| return false; | ||
| } | ||
|
|
||
| CaptureLastSentList(); | ||
| m_List.RemoveAt(index); | ||
| var listEvent = new NetworkListEvent<T>() | ||
| { | ||
|
|
@@ -554,6 +594,7 @@ public void Insert(int index, T item) | |
| return; | ||
| } | ||
|
|
||
| CaptureLastSentList(); | ||
| if (index < m_List.Length) | ||
| { | ||
| m_List.InsertRangeWithBeginEnd(index, index + 1); | ||
|
|
@@ -590,6 +631,7 @@ public void RemoveAt(int index) | |
| } | ||
|
|
||
| var value = m_List[index]; | ||
| CaptureLastSentList(); | ||
| m_List.RemoveAt(index); | ||
|
|
||
| var listEvent = new NetworkListEvent<T>() | ||
|
|
@@ -632,6 +674,7 @@ public void Set(int index, T value, bool forceUpdate = false) | |
| return; | ||
| } | ||
|
|
||
| CaptureLastSentList(); | ||
| m_List[index] = value; | ||
|
|
||
| var listEvent = new NetworkListEvent<T>() | ||
|
|
@@ -707,6 +750,11 @@ public override void Dispose() | |
| m_DirtyEvents.Dispose(); | ||
| } | ||
|
|
||
| if (m_LastSentList.IsCreated) | ||
| { | ||
| m_LastSentList.Dispose(); | ||
| } | ||
|
|
||
| base.Dispose(); | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A client shown a list through
NetworkShowwhile updates are throttled can remain permanently on the pre-change contents.NetworkShowcallsForceSendIfDirtyOnNetworkShowbefore serializing the spawn.CanSend()blocks sending and resetting the pending events, butForceSendIfDirtyOnNetworkShowstill removes the object from both updater sets.WriteFieldSynchronizationnow sendsm_LastSentList, although the assumed next delta is no longer scheduled. Without another mutation, expiration of the update interval does not correct the client. Keep unsent objects queued afterForceSendIfDirtyOnNetworkShow. Add a throttledNetworkShowregression test with no subsequent mutations.🤖 Helpful? 👍/👎