Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,21 @@ Additional documentation and release notes are available at [Multiplayer Documen
[Unreleased]
### Added

- Added `NetworkVariable.CheckDirtyState` that is to be used in tandem with collections in order to detect whether the collection or an item within the collection has changed. (#3005)

### Fixed

- Fixed issue using collections within `NetworkVariable` where the collection would not detect changes to items or nested items. (#3005)
- Fixed issue where `List`, `Dictionary`, and `HashSet` collections would not uniquely duplicate nested collections. (#3005)
- Fixed Issue where a state with dual triggers, inbound and outbound, could cause a false layer to layer state transition message to be sent to non-authority `NetworkAnimator` instances and cause a warning message to be logged. (#2999)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were missed changelog entries: 2999 and 2992. They are not in this PR.

- Fixed issue where `FixedStringSerializer<T>` was using `NetworkVariableSerialization<byte>.AreEqual` to determine if two bytes were equal causes an exception to be thrown due to no byte serializer having been defined. (#2992)

### Changed

- Changed permissions exception thrown in `NetworkList` to exiting early with a logged error that is now a unified permissions message within `NetworkVariableBase`. (#3005)
- Changed permissions exception thrown in `NetworkVariable.Value` to exiting early with a logged error that is now a unified permissions message within `NetworkVariableBase`. (#3005)


## [1.10.0] - 2024-07-22

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,8 @@ public void Add(T item)
// check write permissions
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
{
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
LogWritePermissionError();
return;
}

m_List.Add(item);
Expand All @@ -390,7 +391,8 @@ public void Clear()
// check write permissions
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
{
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
LogWritePermissionError();
return;
}

m_List.Clear();
Expand All @@ -416,7 +418,8 @@ public bool Remove(T item)
// check write permissions
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
{
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
LogWritePermissionError();
return false;
}

int index = m_List.IndexOf(item);
Expand Down Expand Up @@ -451,7 +454,8 @@ public void Insert(int index, T item)
// check write permissions
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
{
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
LogWritePermissionError();
return;
}

if (index < m_List.Length)
Expand Down Expand Up @@ -480,7 +484,8 @@ public void RemoveAt(int index)
// check write permissions
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
{
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
LogWritePermissionError();
return;
}

var value = m_List[index];
Expand All @@ -505,7 +510,8 @@ public T this[int index]
// check write permissions
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
{
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious how come all of these exceptions have been removed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Been making it a point to swap out throwing exceptions with just logging errors when I run across areas where an exception is a bit harsh for the issue (if it wouldn't normally cause some form of exception then there is no point in interrupting the call stack for some form of improper usage or the like). Also wanted the permissions error to be unified for both NetworkVariable and NetworkList. 👍

LogWritePermissionError();
return;
}

var previousValue = m_List[index];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,25 +79,57 @@ public NetworkVariable(T value = default,
/// <summary>
/// The value of the NetworkVariable container
/// </summary>
/// <remarks>
/// When assigning collections to <see cref="Value"/>, unless it is a completely new collection this will not
/// detect any deltas with most managed collection classes since assignment of one collection value to another
/// is actually just a reference to the collection itself. <br />
/// To detect deltas in a collection, you should invoke <see cref="CheckDirtyState"/> after making modifications to the collection.
/// </remarks>
public virtual T Value
{
get => m_InternalValue;
set
{
// Compare bitwise
if (NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref value))
if (m_NetworkManager && !CanClientWrite(m_NetworkManager.LocalClientId))
{
LogWritePermissionError();
return;
}

if (m_NetworkBehaviour && !CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
// Compare the Value being applied to the current value
if (!NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref value))
{
throw new InvalidOperationException("Client is not allowed to write to this NetworkVariable");
T previousValue = m_InternalValue;
m_InternalValue = value;
SetDirty(true);
m_IsDisposed = false;
OnValueChanged?.Invoke(previousValue, m_InternalValue);
}
}
}

/// <summary>
/// Invoke this method to check if a collection's items are dirty.
/// The default behavior is to exit early if the <see cref="NetworkVariable{T}"/> is already dirty.
/// </summary>
/// <param name="forceCheck"> when true, this check will force a full item collection check even if the NetworkVariable is already dirty</param>
/// <remarks>
/// This is to be used as a way to check if a <see cref="NetworkVariable{T}"/> containing a managed collection has any changees to the collection items.<br />
/// If you invoked this when a collection is dirty, it will not trigger the <see cref="OnValueChanged"/> unless you set <param name="forceCheck"/> to true. <br />
/// </remarks>
public bool CheckDirtyState(bool forceCheck = false)
{
var isDirty = base.IsDirty();

Set(value);
// Compare the previous with the current if not dirty or forcing a check.
if ((!isDirty || forceCheck) && !NetworkVariableSerialization<T>.AreEqual(ref m_PreviousValue, ref m_InternalValue))
{
SetDirty(true);
OnValueChanged?.Invoke(m_PreviousValue, m_InternalValue);
m_IsDisposed = false;
isDirty = true;
}
return isDirty;
}

internal ref T RefValue()
Expand Down Expand Up @@ -184,9 +216,8 @@ public override void ResetDirty()
private protected void Set(T value)
{
SetDirty(true);
T previousValue = m_InternalValue;
m_InternalValue = value;
OnValueChanged?.Invoke(previousValue, m_InternalValue);
OnValueChanged?.Invoke(m_PreviousValue, m_InternalValue);
}

/// <summary>
Expand All @@ -205,20 +236,22 @@ public override void WriteDelta(FastBufferWriter writer)
/// <param name="keepDirtyDelta">Whether or not the container should keep the dirty delta, or mark the delta as consumed</param>
public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta)
{
// In order to get managed collections to properly have a previous and current value, we have to
// duplicate the collection at this point before making any modifications to the current.
m_HasPreviousValue = true;
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_PreviousValue);
NetworkVariableSerialization<T>.ReadDelta(reader, ref m_InternalValue);

// todo:
// keepDirtyDelta marks a variable received as dirty and causes the server to send the value to clients
// In a prefect world, whether a variable was A) modified locally or B) received and needs retransmit
// would be stored in different fields

T previousValue = m_InternalValue;
NetworkVariableSerialization<T>.ReadDelta(reader, ref m_InternalValue);

if (keepDirtyDelta)
{
SetDirty(true);
}

OnValueChanged?.Invoke(previousValue, m_InternalValue);
OnValueChanged?.Invoke(m_PreviousValue, m_InternalValue);
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,47 @@ public abstract class NetworkVariableBase : IDisposable
/// Maintains a link to the associated NetworkBehaviour
/// </summary>
private protected NetworkBehaviour m_NetworkBehaviour;
private NetworkManager m_InternalNetworkManager;

public NetworkBehaviour GetBehaviour()
{
return m_NetworkBehaviour;
}

internal string GetWritePermissionError()
{
return $"|Client-{m_NetworkManager.LocalClientId}|{m_NetworkBehaviour.name}|{Name}| Write permissions ({WritePerm}) for this client instance is not allowed!";
}

internal void LogWritePermissionError()
{
Debug.LogError(GetWritePermissionError());
}

private protected NetworkManager m_NetworkManager
{
get
{
if (m_InternalNetworkManager == null && m_NetworkBehaviour && m_NetworkBehaviour.NetworkObject?.NetworkManager)
{
m_InternalNetworkManager = m_NetworkBehaviour.NetworkObject?.NetworkManager;
}
return m_InternalNetworkManager;
}
}

/// <summary>
/// Initializes the NetworkVariable
/// </summary>
/// <param name="networkBehaviour">The NetworkBehaviour the NetworkVariable belongs to</param>
public void Initialize(NetworkBehaviour networkBehaviour)
{
m_InternalNetworkManager = null;
m_NetworkBehaviour = networkBehaviour;
if (m_NetworkBehaviour.NetworkManager)
if (m_NetworkBehaviour && m_NetworkBehaviour.NetworkObject?.NetworkManager)
{
m_InternalNetworkManager = m_NetworkBehaviour.NetworkObject?.NetworkManager;

if (m_NetworkBehaviour.NetworkManager.NetworkTimeSystem != null)
{
UpdateLastSentTime();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,10 @@ public void Duplicate(in List<T> value, ref List<T> duplicatedValue)
duplicatedValue.Clear();
foreach (var item in value)
{
duplicatedValue.Add(item);
// This handles the nested list scenario List<List<T>>
T subValue = default;
NetworkVariableSerialization<T>.Duplicate(item, ref subValue);
duplicatedValue.Add(subValue);
}
}
}
Expand Down Expand Up @@ -421,6 +424,9 @@ public void Duplicate(in HashSet<T> value, ref HashSet<T> duplicatedValue)
duplicatedValue.Clear();
foreach (var item in value)
{
// Handles nested HashSets
T subValue = default;
NetworkVariableSerialization<T>.Duplicate(item, ref subValue);
duplicatedValue.Add(item);
}
}
Expand Down Expand Up @@ -497,7 +503,12 @@ public void Duplicate(in Dictionary<TKey, TVal> value, ref Dictionary<TKey, TVal
duplicatedValue.Clear();
foreach (var item in value)
{
duplicatedValue.Add(item.Key, item.Value);
// Handles nested dictionaries
TKey subKey = default;
TVal subValue = default;
NetworkVariableSerialization<TKey>.Duplicate(item.Key, ref subKey);
NetworkVariableSerialization<TVal>.Duplicate(item.Value, ref subValue);
duplicatedValue.Add(subKey, subValue);
}
}
}
Expand Down
Loading