The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [1.0.1] - 2022-08-23 ### Changed - Changed version to 1.0.1. (#2131) - Updated dependency on `com.unity.transport` to 1.2.0. (#2129) - When using `UnityTransport`, _reliable_ payloads are now allowed to exceed the configured 'Max Payload Size'. Unreliable payloads remain bounded by this setting. (#2081) - Preformance improvements for cases with large number of NetworkObjects, by not iterating over all unchanged NetworkObjects ### Fixed - Fixed an issue where reading/writing more than 8 bits at a time with BitReader/BitWriter would write/read from the wrong place, returning and incorrect result. (#2130) - Fixed issue with the internal `NetworkTransformState.m_Bitset` flag not getting cleared upon the next tick advancement. (#2110) - Fixed interpolation issue with `NetworkTransform.Teleport`. (#2110) - Fixed issue where the authoritative side was interpolating its transform. (#2110) - Fixed Owner-written NetworkVariable infinitely write themselves (#2109) - Fixed NetworkList issue that showed when inserting at the very end of a NetworkList (#2099) - Fixed issue where a client owner of a `NetworkVariable` with both owner read and write permissions would not update the server side when changed. (#2097) - Fixed issue when attempting to spawn a parent `GameObject`, with `NetworkObject` component attached, that has one or more child `GameObject`s, that are inactive in the hierarchy, with `NetworkBehaviour` components it will no longer attempt to spawn the associated `NetworkBehaviour`(s) or invoke ownership changed notifications but will log a warning message. (#2096) - Fixed an issue where destroying a NetworkBehaviour would not deregister it from the parent NetworkObject, leading to exceptions when the parent was later destroyed. (#2091) - Fixed issue where `NetworkObject.NetworkHide` was despawning and destroying, as opposed to only despawning, in-scene placed `NetworkObject`s. (#2086) - Fixed `NetworkAnimator` synchronizing transitions twice due to it detecting the change in animation state once a transition is started by a trigger. (#2084) - Fixed issue where `NetworkAnimator` would not synchronize a looping animation for late joining clients if it was at the very end of its loop. (#2076) - Fixed issue where `NetworkAnimator` was not removing its subscription from `OnClientConnectedCallback` when despawned during the shutdown sequence. (#2074) - Fixed IsServer and IsClient being set to false before object despawn during the shutdown sequence. (#2074) - Fixed NetworkList Value event on the server. PreviousValue is now set correctly when a new value is set through property setter. (#2067) - Fixed NetworkLists not populating on client. NetworkList now uses the most recent list as opposed to the list at the end of previous frame, when sending full updates to dynamically spawned NetworkObject. The difference in behaviour is required as scene management spawns those objects at a different time in the frame, relative to updates. (#2062)
141 lines
5.2 KiB
C#
141 lines
5.2 KiB
C#
using UnityEngine;
|
|
using System;
|
|
using System.Runtime.CompilerServices;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
|
|
namespace Unity.Netcode
|
|
{
|
|
/// <summary>
|
|
/// A variable that can be synchronized over the network.
|
|
/// </summary>
|
|
/// <typeparam name="T">the unmanaged type for <see cref="NetworkVariable{T}"/> </typeparam>
|
|
[Serializable]
|
|
public class NetworkVariable<T> : NetworkVariableBase where T : unmanaged
|
|
{
|
|
/// <summary>
|
|
/// Delegate type for value changed event
|
|
/// </summary>
|
|
/// <param name="previousValue">The value before the change</param>
|
|
/// <param name="newValue">The new value</param>
|
|
public delegate void OnValueChangedDelegate(T previousValue, T newValue);
|
|
/// <summary>
|
|
/// The callback to be invoked when the value gets changed
|
|
/// </summary>
|
|
public OnValueChangedDelegate OnValueChanged;
|
|
|
|
/// <summary>
|
|
/// Constructor for <see cref="NetworkVariable{T}"/>
|
|
/// </summary>
|
|
/// <param name="value">initial value set that is of type T</param>
|
|
/// <param name="readPerm">the <see cref="NetworkVariableReadPermission"/> for this <see cref="NetworkVariable{T}"/></param>
|
|
/// <param name="writePerm">the <see cref="NetworkVariableWritePermission"/> for this <see cref="NetworkVariable{T}"/></param>
|
|
public NetworkVariable(T value = default,
|
|
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
|
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
|
: base(readPerm, writePerm)
|
|
{
|
|
m_InternalValue = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The internal value of the NetworkVariable
|
|
/// </summary>
|
|
[SerializeField]
|
|
private protected T m_InternalValue;
|
|
|
|
/// <summary>
|
|
/// The value of the NetworkVariable container
|
|
/// </summary>
|
|
public virtual T Value
|
|
{
|
|
get => m_InternalValue;
|
|
set
|
|
{
|
|
// Compare bitwise
|
|
if (ValueEquals(ref m_InternalValue, ref value))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_NetworkBehaviour && !CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
|
|
{
|
|
throw new InvalidOperationException("Client is not allowed to write to this NetworkVariable");
|
|
}
|
|
|
|
Set(value);
|
|
}
|
|
}
|
|
|
|
// Compares two values of the same unmanaged type by underlying memory
|
|
// Ignoring any overridden value checks
|
|
// Size is fixed
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static unsafe bool ValueEquals(ref T a, ref T b)
|
|
{
|
|
// get unmanaged pointers
|
|
var aptr = UnsafeUtility.AddressOf(ref a);
|
|
var bptr = UnsafeUtility.AddressOf(ref b);
|
|
|
|
// compare addresses
|
|
return UnsafeUtility.MemCmp(aptr, bptr, sizeof(T)) == 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the <see cref="Value"/>, marks the <see cref="NetworkVariable{T}"/> dirty, and invokes the <see cref="OnValueChanged"/> callback
|
|
/// if there are subscribers to that event.
|
|
/// </summary>
|
|
/// <param name="value">the new value of type `T` to be set/></param>
|
|
private protected void Set(T value)
|
|
{
|
|
SetDirty(true);
|
|
T previousValue = m_InternalValue;
|
|
m_InternalValue = value;
|
|
OnValueChanged?.Invoke(previousValue, m_InternalValue);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the variable to the writer
|
|
/// </summary>
|
|
/// <param name="writer">The stream to write the value to</param>
|
|
public override void WriteDelta(FastBufferWriter writer)
|
|
{
|
|
WriteField(writer);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads value from the reader and applies it
|
|
/// </summary>
|
|
/// <param name="reader">The stream to read the value from</param>
|
|
/// <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)
|
|
{
|
|
// 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>.Read(reader, out m_InternalValue);
|
|
|
|
if (keepDirtyDelta)
|
|
{
|
|
SetDirty(true);
|
|
}
|
|
|
|
OnValueChanged?.Invoke(previousValue, m_InternalValue);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void ReadField(FastBufferReader reader)
|
|
{
|
|
NetworkVariableSerialization<T>.Read(reader, out m_InternalValue);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void WriteField(FastBufferWriter writer)
|
|
{
|
|
NetworkVariableSerialization<T>.Write(writer, ref m_InternalValue);
|
|
}
|
|
}
|
|
}
|