Skip to content

Commit 9c809d3

Browse files
Merge branch 'develop-2.0.0' into fix/networkobject-GetNetworkBehaviourAtOrderIndex-back-to-public
2 parents 2cf4169 + dc58e42 commit 9c809d3

File tree

6 files changed

+779
-514
lines changed

6 files changed

+779
-514
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
### Fixed
1818

1919
- Fixed regression issue in v2.x where `NetworkObject.GetNetworkBehaviourAtOrderIndex` was converted from public to internal. (#3541)
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. (#3539)
2021
- Fixed synchronizing the destroyGameObject parameter to clients for InScenePlaced network objects. (#3514)
2122
- Fixed distributed authority related issue where enabling the `NetworkObject.DestroyWithScene` would cause errors when a destroying non-authority instances due to loading (single mode) or unloading scene events. (#3500)
2223

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
}
@@ -212,11 +233,11 @@ public override void Dispose()
212233
m_InternalValue = default;
213234

214235
// Dispose the internal original value
215-
if (m_InternalOriginalValue is IDisposable internalOriginalValueDisposable)
236+
if (m_LastInternalValue is IDisposable internalOriginalValueDisposable)
216237
{
217238
internalOriginalValueDisposable.Dispose();
218239
}
219-
m_InternalOriginalValue = default;
240+
m_LastInternalValue = default;
220241

221242
// Dispose the previous value if there is one
222243
if (m_HasPreviousValue && m_PreviousValue is IDisposable previousValueDisposable)
@@ -245,9 +266,9 @@ public override bool IsDirty()
245266
{
246267
// 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
247268
// to the original collection value prior to applying updates (primarily for collections).
248-
if (!NetworkUpdaterCheck && CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref m_InternalOriginalValue))
269+
if (!NetworkUpdaterCheck && CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref m_LastInternalValue))
249270
{
250-
NetworkVariableSerialization<T>.Duplicate(m_InternalOriginalValue, ref m_InternalValue);
271+
NetworkVariableSerialization<T>.Duplicate(m_LastInternalValue, ref m_InternalValue);
251272
return true;
252273
}
253274
// For most cases we can use the dirty flag.
@@ -284,7 +305,7 @@ public override void ResetDirty()
284305
m_HasPreviousValue = true;
285306
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_PreviousValue);
286307
// Once updated, assure the original current value is updated for future comparison purposes
287-
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_InternalOriginalValue);
308+
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_LastInternalValue);
288309
}
289310
base.ResetDirty();
290311
}
@@ -307,9 +328,9 @@ public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta)
307328
{
308329
// 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
309330
// to the original collection value prior to applying updates (primarily for collections).
310-
if (CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_InternalOriginalValue, ref m_InternalValue))
331+
if (CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_LastInternalValue, ref m_InternalValue))
311332
{
312-
NetworkVariableSerialization<T>.Duplicate(m_InternalOriginalValue, ref m_InternalValue);
333+
NetworkVariableSerialization<T>.Duplicate(m_LastInternalValue, ref m_InternalValue);
313334
}
314335

315336
NetworkVariableSerialization<T>.ReadDelta(reader, ref m_InternalValue);
@@ -341,17 +362,17 @@ internal override void PostDeltaRead()
341362
m_HasPreviousValue = true;
342363
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_PreviousValue);
343364
// Once updated, assure the original current value is updated for future comparison purposes
344-
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_InternalOriginalValue);
365+
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_LastInternalValue);
345366
}
346367

347368
/// <inheritdoc />
348369
public override void ReadField(FastBufferReader reader)
349370
{
350371
// 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
351372
// to the original collection value prior to applying updates (primarily for collections).
352-
if (CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_InternalOriginalValue, ref m_InternalValue))
373+
if (CannotWrite && !NetworkVariableSerialization<T>.AreEqual(ref m_LastInternalValue, ref m_InternalValue))
353374
{
354-
NetworkVariableSerialization<T>.Duplicate(m_InternalOriginalValue, ref m_InternalValue);
375+
NetworkVariableSerialization<T>.Duplicate(m_LastInternalValue, ref m_InternalValue);
355376
}
356377

357378
NetworkVariableSerialization<T>.Read(reader, ref m_InternalValue);
@@ -363,7 +384,7 @@ public override void ReadField(FastBufferReader reader)
363384
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_PreviousValue);
364385

365386
// Once updated, assure the original current value is updated for future comparison purposes
366-
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_InternalOriginalValue);
387+
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_LastInternalValue);
367388
}
368389

369390
/// <inheritdoc />

0 commit comments

Comments
 (0)