Skip to content

Commit 68e9eff

Browse files
Merge branch 'develop' into automated-project-builders-backport
2 parents 4cb1dfb + a93c4d8 commit 68e9eff

File tree

7 files changed

+796
-517
lines changed

7 files changed

+796
-517
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1717

1818
### Fixed
1919

20+
- Fixed ensuring OnValueChanged callback is still triggered on the authority when a collection changes and then reverts to the previous value in the same frame. (#3544)
2021
- Fixed `NullReferenceException` on `NetworkList` when used without a NetworkManager in scene. (#3502)
2122
- Fixed inconsistencies in the `OnSceneEvent` callback. (#3487)
2223
- Fixed issue where `NetworkClient` could persist some settings if re-using the same `NetworkManager` instance. (#3494)

com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public override void OnInitialize()
5757
base.OnInitialize();
5858

5959
m_HasPreviousValue = true;
60-
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_InternalOriginalValue);
60+
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_LastInternalValue);
6161
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_PreviousValue);
6262
}
6363

@@ -73,7 +73,7 @@ public NetworkVariable(T value = default,
7373
: base(readPerm, writePerm)
7474
{
7575
m_InternalValue = value;
76-
m_InternalOriginalValue = default;
76+
m_LastInternalValue = default;
7777
// Since we start with IsDirty = true, this doesn't need to be duplicated
7878
// right away. It won't get read until after ResetDirty() is called, and
7979
// the duplicate will be made there. Avoiding calling
@@ -92,25 +92,45 @@ public void Reset(T value = default)
9292
if (m_NetworkBehaviour == null || m_NetworkBehaviour != null && !m_NetworkBehaviour.NetworkObject.IsSpawned)
9393
{
9494
m_InternalValue = value;
95-
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_InternalOriginalValue);
95+
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_LastInternalValue);
9696
m_PreviousValue = default;
9797
}
9898
}
9999

100100
/// <summary>
101-
/// The internal value of the NetworkVariable
101+
/// The current internal value of the NetworkVariable.
102102
/// </summary>
103+
/// <remarks>
104+
/// When using collections, this InternalValue can be updated directly without going through the <see cref="NetworkVariable{T}.Value"/> setter.
105+
/// </remarks>
103106
[SerializeField]
104107
private protected T m_InternalValue;
105108

106-
// The introduction of standard .NET collections caused an issue with permissions since there is no way to detect changes in the
107-
// collection without doing a full comparison. While this approach does consume more memory per collection instance, it is the
108-
// lowest risk approach to resolving the issue where a client with no write permissions could make changes to a collection locally
109-
// which can cause a myriad of issues.
110-
private protected T m_InternalOriginalValue;
109+
/// <summary>
110+
/// The last valid/authorized value of the network variable.
111+
/// </summary>
112+
/// <remarks>
113+
/// The introduction of standard .NET collections caused an issue with permissions since there is no way to detect changes in the
114+
/// collection without doing a full comparison. While this approach does consume more memory per collection instance, it is the
115+
/// lowest risk approach to resolving the issue where a client with no write permissions could make changes to a collection locally
116+
/// which can cause a myriad of issues.
117+
/// </remarks>
118+
private protected T m_LastInternalValue;
111119

120+
/// <summary>
121+
/// The most recent value that was synchronized over the network.
122+
/// Synchronized over the network at the end of the frame in which the <see cref="NetworkVariable{T}"/> was marked dirty.
123+
/// </summary>
124+
/// <remarks>
125+
/// Only contains the value synchronized over the network at the end of the last frame.
126+
/// All in-between changes on the authority are tracked by <see cref="m_LastInternalValue"/>.
127+
/// </remarks>
112128
private protected T m_PreviousValue;
113129

130+
/// <summary>
131+
/// Whether this network variable has had changes synchronized over the network.
132+
/// Indicates whether <see cref="m_PreviousValue"/> is populated and valid.
133+
/// </summary>
114134
private bool m_HasPreviousValue;
115135
private bool m_IsDisposed;
116136

@@ -139,7 +159,7 @@ public virtual T Value
139159
{
140160
T previousValue = m_InternalValue;
141161
m_InternalValue = value;
142-
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_InternalOriginalValue);
162+
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_LastInternalValue);
143163
SetDirty(true);
144164
m_IsDisposed = false;
145165
OnValueChanged?.Invoke(previousValue, m_InternalValue);
@@ -165,20 +185,21 @@ public bool CheckDirtyState(bool forceCheck = false)
165185
if (CannotWrite)
166186
{
167187
// If modifications are detected, then revert back to the last known current value
168-
if (!NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref m_InternalOriginalValue))
188+
if (!NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref m_LastInternalValue))
169189
{
170-
NetworkVariableSerialization<T>.Duplicate(m_InternalOriginalValue, ref m_InternalValue);
190+
NetworkVariableSerialization<T>.Duplicate(m_LastInternalValue, ref m_InternalValue);
171191
}
172192
return false;
173193
}
174194

175-
// Compare the previous with the current if not dirty or forcing a check.
176-
if ((!isDirty || forceCheck) && !NetworkVariableSerialization<T>.AreEqual(ref m_PreviousValue, ref m_InternalValue))
195+
// Compare the last internal value with the current value if not dirty or forcing a check.
196+
if ((!isDirty || forceCheck) && !NetworkVariableSerialization<T>.AreEqual(ref m_LastInternalValue, ref m_InternalValue))
177197
{
178198
SetDirty(true);
179-
OnValueChanged?.Invoke(m_PreviousValue, m_InternalValue);
199+
OnValueChanged?.Invoke(m_LastInternalValue, m_InternalValue);
180200
m_IsDisposed = false;
181201
isDirty = true;
202+
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_LastInternalValue);
182203
}
183204
return isDirty;
184205
}
@@ -211,11 +232,11 @@ public override void Dispose()
211232

212233
m_InternalValue = default;
213234

214-
if (m_InternalOriginalValue is IDisposable internalOriginalValueDisposable)
235+
if (m_LastInternalValue is IDisposable internalOriginalValueDisposable)
215236
{
216237
internalOriginalValueDisposable.Dispose();
217238
}
218-
m_InternalOriginalValue = default;
239+
m_LastInternalValue = default;
219240

220241
if (m_HasPreviousValue && m_PreviousValue is IDisposable previousValueDisposable)
221242
{
@@ -242,9 +263,9 @@ public override bool IsDirty()
242263
{
243264
// If the client does not have write permissions but the internal value is determined to be locally modified and we are applying updates, then we should revert
244265
// to the original collection value prior to applying updates (primarily for collections).
245-
if (!NetworkUpdaterCheck && CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref m_InternalOriginalValue))
266+
if (!NetworkUpdaterCheck && CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref m_LastInternalValue))
246267
{
247-
NetworkVariableSerialization<T>.Duplicate(m_InternalOriginalValue, ref m_InternalValue);
268+
NetworkVariableSerialization<T>.Duplicate(m_LastInternalValue, ref m_InternalValue);
248269
return true;
249270
}
250271

@@ -283,7 +304,7 @@ public override void ResetDirty()
283304
m_HasPreviousValue = true;
284305
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_PreviousValue);
285306
// Once updated, assure the original current value is updated for future comparison purposes
286-
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_InternalOriginalValue);
307+
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_LastInternalValue);
287308
}
288309
base.ResetDirty();
289310
}
@@ -318,9 +339,9 @@ public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta)
318339
{
319340
// If the client does not have write permissions but the internal value is determined to be locally modified and we are applying updates, then we should revert
320341
// to the original collection value prior to applying updates (primarily for collections).
321-
if (CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_InternalOriginalValue, ref m_InternalValue))
342+
if (CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_LastInternalValue, ref m_InternalValue))
322343
{
323-
NetworkVariableSerialization<T>.Duplicate(m_InternalOriginalValue, ref m_InternalValue);
344+
NetworkVariableSerialization<T>.Duplicate(m_LastInternalValue, ref m_InternalValue);
324345
}
325346

326347
NetworkVariableSerialization<T>.ReadDelta(reader, ref m_InternalValue);
@@ -351,17 +372,17 @@ internal override void PostDeltaRead()
351372
m_HasPreviousValue = true;
352373
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_PreviousValue);
353374
// Once updated, assure the original current value is updated for future comparison purposes
354-
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_InternalOriginalValue);
375+
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_LastInternalValue);
355376
}
356377

357378
/// <inheritdoc />
358379
public override void ReadField(FastBufferReader reader)
359380
{
360381
// If the client does not have write permissions but the internal value is determined to be locally modified and we are applying updates, then we should revert
361382
// to the original collection value prior to applying updates (primarily for collections).
362-
if (CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_InternalOriginalValue, ref m_InternalValue))
383+
if (CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_LastInternalValue, ref m_InternalValue))
363384
{
364-
NetworkVariableSerialization<T>.Duplicate(m_InternalOriginalValue, ref m_InternalValue);
385+
NetworkVariableSerialization<T>.Duplicate(m_LastInternalValue, ref m_InternalValue);
365386
}
366387

367388
NetworkVariableSerialization<T>.Read(reader, ref m_InternalValue);
@@ -373,7 +394,7 @@ public override void ReadField(FastBufferReader reader)
373394
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_PreviousValue);
374395

375396
// Once updated, assure the original current value is updated for future comparison purposes
376-
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_InternalOriginalValue);
397+
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_LastInternalValue);
377398
}
378399

379400
/// <inheritdoc />

0 commit comments

Comments
 (0)