Compare commits
3 Commits
1.0.0-pre.
...
1.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6969670f5 | ||
|
|
e15bd056c5 | ||
|
|
18ffd5fdc8 |
45
CHANGELOG.md
45
CHANGELOG.md
@@ -1,3 +1,4 @@
|
||||
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
@@ -6,6 +7,48 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||
|
||||
Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).
|
||||
|
||||
|
||||
## [1.0.2] - 2022-09-12
|
||||
|
||||
- Fixed issue where `NetworkTransform` was not honoring the InLocalSpace property on the authority side during OnNetworkSpawn. (#2170)
|
||||
- Fixed issue where `NetworkTransform` was not ending extrapolation for the previous state causing non-authoritative instances to become out of synch. (#2170)
|
||||
- Fixed issue where `NetworkTransform` was not continuing to interpolate for the remainder of the associated tick period. (#2170)
|
||||
- Fixed issue during `NetworkTransform.OnNetworkSpawn` for non-authoritative instances where it was initializing interpolators with the replicated network state which now only contains the transform deltas that occurred during a network tick and not the entire transform state. (#2170)
|
||||
|
||||
## [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)
|
||||
- Performance 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)
|
||||
|
||||
## [1.0.0] - 2022-06-27
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed version to 1.0.0. (#2046)
|
||||
|
||||
## [1.0.0-pre.10] - 2022-06-21
|
||||
|
||||
### Added
|
||||
@@ -18,7 +61,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
|
||||
### Changed
|
||||
|
||||
- Updated `UnityTransport` dependency on `com.unity.transport` to 1.1.0. (#2025)
|
||||
- (API Breaking) `ConnectionApprovalCallback` is no longer an `event` and will not allow more than 1 handler registered at a time. Also, `ConnectionApprovalCallback` is now a `Func<>` taking `ConnectionApprovalRequest` in and returning `ConnectionApprovalResponse` back out (#1972)
|
||||
- (API Breaking) `ConnectionApprovalCallback` is no longer an `event` and will not allow more than 1 handler registered at a time. Also, `ConnectionApprovalCallback` is now an `Action<>` taking a `ConnectionApprovalRequest` and a `ConnectionApprovalResponse` that the client code must fill (#1972) (#2002)
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Unity.Netcode
|
||||
/// Solves for incoming values that are jittered
|
||||
/// Partially solves for message loss. Unclamped lerping helps hide this, but not completely
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of interpolated value</typeparam>
|
||||
public abstract class BufferedLinearInterpolator<T> where T : struct
|
||||
{
|
||||
internal float MaxInterpolationBound = 3.0f;
|
||||
@@ -24,7 +25,7 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// There’s two factors affecting interpolation: buffering (set in NetworkManager’s NetworkTimeSystem) and interpolation time, which is the amount of time it’ll take to reach the target. This is to affect the second one.
|
||||
/// There's two factors affecting interpolation: buffering (set in NetworkManager's NetworkTimeSystem) and interpolation time, which is the amount of time it'll take to reach the target. This is to affect the second one.
|
||||
/// </summary>
|
||||
public float MaximumInterpolationTime = 0.1f;
|
||||
|
||||
@@ -73,7 +74,7 @@ namespace Unity.Netcode
|
||||
private bool InvalidState => m_Buffer.Count == 0 && m_LifetimeConsumedCount == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Resets Interpolator to initial state
|
||||
/// Resets interpolator to initial state
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
@@ -85,6 +86,8 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Teleports current interpolation value to targetValue.
|
||||
/// </summary>
|
||||
/// <param name="targetValue">The target value to teleport instantly</param>
|
||||
/// <param name="serverTime">The current server time</param>
|
||||
public void ResetTo(T targetValue, double serverTime)
|
||||
{
|
||||
m_LifetimeConsumedCount = 1;
|
||||
@@ -159,6 +162,7 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">time since call</param>
|
||||
/// <param name="serverTime">current server time</param>
|
||||
/// <returns>The newly interpolated value of type 'T'</returns>
|
||||
public T Update(float deltaTime, NetworkTime serverTime)
|
||||
{
|
||||
return Update(deltaTime, serverTime.TimeTicksAgo(1).Time, serverTime.Time);
|
||||
@@ -170,6 +174,7 @@ namespace Unity.Netcode
|
||||
/// <param name="deltaTime">time since last call</param>
|
||||
/// <param name="renderTime">our current time</param>
|
||||
/// <param name="serverTime">current server time</param>
|
||||
/// <returns>The newly interpolated value of type 'T'</returns>
|
||||
public T Update(float deltaTime, double renderTime, double serverTime)
|
||||
{
|
||||
TryConsumeFromBuffer(renderTime, serverTime);
|
||||
@@ -222,6 +227,8 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Add measurements to be used during interpolation. These will be buffered before being made available to be displayed as "latest value".
|
||||
/// </summary>
|
||||
/// <param name="newMeasurement">The new measurement value to use</param>
|
||||
/// <param name="sentTime">The time to record for measurement</param>
|
||||
public void AddMeasurement(T newMeasurement, double sentTime)
|
||||
{
|
||||
m_NbItemsReceivedThisFrame++;
|
||||
@@ -241,6 +248,8 @@ namespace Unity.Netcode
|
||||
return;
|
||||
}
|
||||
|
||||
// Part the of reason for disabling extrapolation is how we add and use measurements over time.
|
||||
// TODO: Add detailed description of this area in Jira ticket
|
||||
if (sentTime > m_EndTimeConsumed || m_LifetimeConsumedCount == 0) // treat only if value is newer than the one being interpolated to right now
|
||||
{
|
||||
m_LastBufferedItemReceived = new BufferedItem(newMeasurement, sentTime);
|
||||
@@ -251,6 +260,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Gets latest value from the interpolator. This is updated every update as time goes by.
|
||||
/// </summary>
|
||||
/// <returns>The current interpolated value of type 'T'</returns>
|
||||
public T GetInterpolatedValue()
|
||||
{
|
||||
return m_CurrentInterpValue;
|
||||
@@ -259,36 +269,63 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Method to override and adapted to the generic type. This assumes interpolation for that value will be clamped.
|
||||
/// </summary>
|
||||
/// <param name="start">The start value (min)</param>
|
||||
/// <param name="end">The end value (max)</param>
|
||||
/// <param name="time">The time value used to interpolate between start and end values (pos)</param>
|
||||
/// <returns>The interpolated value</returns>
|
||||
protected abstract T Interpolate(T start, T end, float time);
|
||||
|
||||
/// <summary>
|
||||
/// Method to override and adapted to the generic type. This assumes interpolation for that value will not be clamped.
|
||||
/// </summary>
|
||||
/// <param name="start">The start value (min)</param>
|
||||
/// <param name="end">The end value (max)</param>
|
||||
/// <param name="time">The time value used to interpolate between start and end values (pos)</param>
|
||||
/// <returns>The interpolated value</returns>
|
||||
protected abstract T InterpolateUnclamped(T start, T end, float time);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// This is a buffered linear interpolator for a <see cref="float"/> type value
|
||||
/// </remarks>
|
||||
public class BufferedLinearInterpolatorFloat : BufferedLinearInterpolator<float>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override float InterpolateUnclamped(float start, float end, float time)
|
||||
{
|
||||
return Mathf.LerpUnclamped(start, end, time);
|
||||
// Disabling Extrapolation:
|
||||
// TODO: Add Jira Ticket
|
||||
return Mathf.Lerp(start, end, time);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override float Interpolate(float start, float end, float time)
|
||||
{
|
||||
return Mathf.Lerp(start, end, time);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// This is a buffered linear interpolator for a <see cref="Quaternion"/> type value
|
||||
/// </remarks>
|
||||
public class BufferedLinearInterpolatorQuaternion : BufferedLinearInterpolator<Quaternion>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override Quaternion InterpolateUnclamped(Quaternion start, Quaternion end, float time)
|
||||
{
|
||||
return Quaternion.SlerpUnclamped(start, end, time);
|
||||
// Disabling Extrapolation:
|
||||
// TODO: Add Jira Ticket
|
||||
return Quaternion.Slerp(start, end, time);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Quaternion Interpolate(Quaternion start, Quaternion end, float time)
|
||||
{
|
||||
return Quaternion.SlerpUnclamped(start, end, time);
|
||||
// Disabling Extrapolation:
|
||||
// TODO: Add Jira Ticket
|
||||
return Quaternion.Slerp(start, end, time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace Unity.Netcode.Components
|
||||
m_SendTriggerUpdates.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void NetworkUpdate(NetworkUpdateStage updateStage)
|
||||
{
|
||||
switch (updateStage)
|
||||
@@ -162,6 +163,7 @@ namespace Unity.Netcode.Components
|
||||
internal struct AnimationMessage : INetworkSerializable
|
||||
{
|
||||
// state hash per layer. if non-zero, then Play() this animation, skipping transitions
|
||||
internal bool Transition;
|
||||
internal int StateHash;
|
||||
internal float NormalizedTime;
|
||||
internal int Layer;
|
||||
@@ -423,14 +425,10 @@ namespace Unity.Netcode.Components
|
||||
|
||||
stateHash = nextState.fullPathHash;
|
||||
}
|
||||
else
|
||||
if (st.normalizedTime >= adjustedNormalizedMaxTime)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var animMsg = new AnimationMessage
|
||||
{
|
||||
Transition = m_Animator.IsInTransition(layer),
|
||||
StateHash = stateHash,
|
||||
NormalizedTime = normalizedTime,
|
||||
Layer = layer,
|
||||
@@ -446,6 +444,9 @@ namespace Unity.Netcode.Components
|
||||
m_NetworkAnimatorStateChangeHandler.SynchronizeClient(playerId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for changes in both Animator parameters and state.
|
||||
/// </summary>
|
||||
internal void CheckForAnimatorChanges()
|
||||
{
|
||||
if (!IsOwner && !IsServerAuthoritative() || IsServerAuthoritative() && !IsServer)
|
||||
@@ -486,6 +487,7 @@ namespace Unity.Netcode.Components
|
||||
|
||||
var animMsg = new AnimationMessage
|
||||
{
|
||||
Transition = m_Animator.IsInTransition(layer),
|
||||
StateHash = stateHash,
|
||||
NormalizedTime = normalizedTime,
|
||||
Layer = layer,
|
||||
@@ -748,7 +750,13 @@ namespace Unity.Netcode.Components
|
||||
/// </summary>
|
||||
private unsafe void UpdateAnimationState(AnimationMessage animationState)
|
||||
{
|
||||
if (animationState.StateHash != 0)
|
||||
if (animationState.StateHash == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var currentState = m_Animator.GetCurrentAnimatorStateInfo(animationState.Layer);
|
||||
if (currentState.fullPathHash != animationState.StateHash || m_Animator.IsInTransition(animationState.Layer) != animationState.Transition)
|
||||
{
|
||||
m_Animator.Play(animationState.StateHash, animationState.Layer, animationState.NormalizedTime);
|
||||
}
|
||||
@@ -834,6 +842,10 @@ namespace Unity.Netcode.Components
|
||||
[ClientRpc]
|
||||
private unsafe void SendAnimStateClientRpc(AnimationMessage animSnapshot, ClientRpcParams clientRpcParams = default)
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var isServerAuthoritative = IsServerAuthoritative();
|
||||
if (!isServerAuthoritative && !IsOwner || isServerAuthoritative)
|
||||
{
|
||||
@@ -883,11 +895,7 @@ namespace Unity.Netcode.Components
|
||||
[ClientRpc]
|
||||
internal void SendAnimTriggerClientRpc(AnimationTriggerMessage animationTriggerMessage, ClientRpcParams clientRpcParams = default)
|
||||
{
|
||||
var isServerAuthoritative = IsServerAuthoritative();
|
||||
if (!isServerAuthoritative && !IsOwner || isServerAuthoritative)
|
||||
{
|
||||
m_Animator.SetBool(animationTriggerMessage.Hash, animationTriggerMessage.IsTriggerSet);
|
||||
}
|
||||
m_Animator.SetBool(animationTriggerMessage.Hash, animationTriggerMessage.IsTriggerSet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -904,8 +912,13 @@ namespace Unity.Netcode.Components
|
||||
/// <param name="setTrigger">sets (true) or resets (false) the trigger. The default is to set it (true).</param>
|
||||
public void SetTrigger(int hash, bool setTrigger = true)
|
||||
{
|
||||
var isServerAuthoritative = IsServerAuthoritative();
|
||||
if (IsOwner && !isServerAuthoritative || IsServer && isServerAuthoritative)
|
||||
// MTT-3564:
|
||||
// After fixing the issue with trigger controlled Transitions being synchronized twice,
|
||||
// it exposed additional issues with this logic. Now, either the owner or the server can
|
||||
// update triggers. Since server-side RPCs are immediately invoked, for a host a trigger
|
||||
// will happen when SendAnimTriggerClientRpc is called. For a client owner, we call the
|
||||
// SendAnimTriggerServerRpc and then trigger locally when running in owner authority mode.
|
||||
if (IsOwner || IsServer)
|
||||
{
|
||||
var animTriggerMessage = new AnimationTriggerMessage() { Hash = hash, IsTriggerSet = setTrigger };
|
||||
if (IsServer)
|
||||
@@ -915,9 +928,11 @@ namespace Unity.Netcode.Components
|
||||
else
|
||||
{
|
||||
SendAnimTriggerServerRpc(animTriggerMessage);
|
||||
if (!IsServerAuthoritative())
|
||||
{
|
||||
m_Animator.SetTrigger(hash);
|
||||
}
|
||||
}
|
||||
// trigger the animation locally on the server...
|
||||
m_Animator.SetBool(hash, setTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,8 +18,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
||||
public override ILPPInterface GetInstance() => this;
|
||||
|
||||
public override bool WillProcess(ICompiledAssembly compiledAssembly) =>
|
||||
compiledAssembly.Name == CodeGenHelpers.RuntimeAssemblyName ||
|
||||
compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == CodeGenHelpers.RuntimeAssemblyName);
|
||||
compiledAssembly.Name == CodeGenHelpers.RuntimeAssemblyName;
|
||||
|
||||
private readonly List<DiagnosticMessage> m_Diagnostics = new List<DiagnosticMessage>();
|
||||
|
||||
|
||||
@@ -151,6 +151,8 @@ namespace Unity.Netcode.Editor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
if (!m_Initialized)
|
||||
@@ -230,6 +232,11 @@ namespace Unity.Netcode.Editor
|
||||
|
||||
internal const string AutoAddNetworkObjectIfNoneExists = "AutoAdd-NetworkObject-When-None-Exist";
|
||||
|
||||
/// <summary>
|
||||
/// Recursively finds the root parent of a <see cref="Transform"/>
|
||||
/// </summary>
|
||||
/// <param name="transform">The current <see cref="Transform"/> we are inspecting for a parent</param>
|
||||
/// <returns>the root parent for the first <see cref="Transform"/> passed into the method</returns>
|
||||
public static Transform GetRootParentTransform(Transform transform)
|
||||
{
|
||||
if (transform.parent == null || transform.parent == transform)
|
||||
@@ -244,6 +251,8 @@ namespace Unity.Netcode.Editor
|
||||
/// does not already have a NetworkObject component. If not it will notify
|
||||
/// the user that NetworkBehaviours require a NetworkObject.
|
||||
/// </summary>
|
||||
/// <param name="gameObject"><see cref="GameObject"/> to start checking for a <see cref="NetworkObject"/></param>
|
||||
/// <param name="networkObjectRemoved">used internally</param>
|
||||
public static void CheckForNetworkObject(GameObject gameObject, bool networkObjectRemoved = false)
|
||||
{
|
||||
// If there are no NetworkBehaviours or no gameObject, then exit early
|
||||
|
||||
@@ -6,6 +6,10 @@ using UnityEditorInternal;
|
||||
|
||||
namespace Unity.Netcode.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// This <see cref="CustomEditor"/> handles the translation between the <see cref="NetworkConfig"/> and
|
||||
/// the <see cref="NetworkManager"/> properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(NetworkManager), true)]
|
||||
[CanEditMultipleObjects]
|
||||
public class NetworkManagerEditor : UnityEditor.Editor
|
||||
@@ -200,6 +204,7 @@ namespace Unity.Netcode.Editor
|
||||
m_NetworkPrefabsList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "NetworkPrefabs");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
Initialize();
|
||||
|
||||
@@ -4,6 +4,9 @@ using UnityEditor;
|
||||
|
||||
namespace Unity.Netcode.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="CustomEditor"/> for <see cref="NetworkObject"/>
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(NetworkObject), true)]
|
||||
[CanEditMultipleObjects]
|
||||
public class NetworkObjectEditor : UnityEditor.Editor
|
||||
@@ -23,6 +26,7 @@ namespace Unity.Netcode.Editor
|
||||
m_NetworkObject = (NetworkObject)target;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
Initialize();
|
||||
|
||||
@@ -4,6 +4,9 @@ using Unity.Netcode.Components;
|
||||
|
||||
namespace Unity.Netcode.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="CustomEditor"/> for <see cref="NetworkTransform"/>
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(NetworkTransform), true)]
|
||||
public class NetworkTransformEditor : UnityEditor.Editor
|
||||
{
|
||||
@@ -28,6 +31,7 @@ namespace Unity.Netcode.Editor
|
||||
private static GUIContent s_RotationLabel = EditorGUIUtility.TrTextContent("Rotation");
|
||||
private static GUIContent s_ScaleLabel = EditorGUIUtility.TrTextContent("Scale");
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnEnable()
|
||||
{
|
||||
m_SyncPositionXProperty = serializedObject.FindProperty(nameof(NetworkTransform.SyncPositionX));
|
||||
@@ -46,6 +50,7 @@ namespace Unity.Netcode.Editor
|
||||
m_InterpolateProperty = serializedObject.FindProperty(nameof(NetworkTransform.Interpolate));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("Syncing", EditorStyles.boldLabel);
|
||||
|
||||
@@ -150,8 +150,16 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
public bool EnableNetworkLogs = true;
|
||||
|
||||
/// <summary>
|
||||
/// The number of RTT samples that is kept as an average for calculations
|
||||
/// </summary>
|
||||
public const int RttAverageSamples = 5; // number of RTT to keep an average of (plus one)
|
||||
|
||||
/// <summary>
|
||||
/// The number of slots used for RTT calculations. This is the maximum amount of in-flight messages
|
||||
/// </summary>
|
||||
public const int RttWindowSize = 64; // number of slots to use for RTT computations (max number of in-flight packets)
|
||||
|
||||
/// <summary>
|
||||
/// Returns a base64 encoded version of the configuration
|
||||
/// </summary>
|
||||
|
||||
@@ -331,7 +331,8 @@ namespace Unity.Netcode
|
||||
// in Update and/or in FixedUpdate could still be checking NetworkBehaviour.NetworkObject directly (i.e. does it exist?)
|
||||
// or NetworkBehaviour.IsSpawned (i.e. to early exit if not spawned) which, in turn, could generate several Warning messages
|
||||
// per spawned NetworkObject. Checking for ShutdownInProgress prevents these unnecessary LogWarning messages.
|
||||
if (m_NetworkObject == null && (NetworkManager.Singleton == null || !NetworkManager.Singleton.ShutdownInProgress))
|
||||
// We must check IsSpawned, otherwise a warning will be logged under certain valid conditions (see OnDestroy)
|
||||
if (IsSpawned && m_NetworkObject == null && (NetworkManager.Singleton == null || !NetworkManager.Singleton.ShutdownInProgress))
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
@@ -470,6 +471,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed
|
||||
/// </summary>
|
||||
/// <param name="parentNetworkObject">the new <see cref="NetworkObject"/> parent</param>
|
||||
public virtual void OnNetworkObjectParentChanged(NetworkObject parentNetworkObject) { }
|
||||
|
||||
private bool m_VarInit = false;
|
||||
@@ -581,9 +583,11 @@ namespace Unity.Netcode
|
||||
{
|
||||
NetworkVariableFields[NetworkVariableIndexesToReset[i]].ResetDirty();
|
||||
}
|
||||
|
||||
MarkVariablesDirty(false);
|
||||
}
|
||||
|
||||
internal void VariableUpdate(ulong targetClientId)
|
||||
internal void PreVariableUpdate()
|
||||
{
|
||||
if (!m_VarInit)
|
||||
{
|
||||
@@ -591,6 +595,10 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
PreNetworkVariableWrite();
|
||||
}
|
||||
|
||||
internal void VariableUpdate(ulong targetClientId)
|
||||
{
|
||||
NetworkVariableUpdate(targetClientId, NetworkBehaviourId);
|
||||
}
|
||||
|
||||
@@ -662,11 +670,11 @@ namespace Unity.Netcode
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void MarkVariablesDirty()
|
||||
internal void MarkVariablesDirty(bool dirty)
|
||||
{
|
||||
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
||||
{
|
||||
NetworkVariableFields[j].SetDirty(true);
|
||||
NetworkVariableFields[j].SetDirty(dirty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -751,8 +759,21 @@ namespace Unity.Netcode
|
||||
return NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(networkId, out NetworkObject networkObject) ? networkObject : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the <see cref="GameObject"/> the <see cref="NetworkBehaviour"/> is attached to.
|
||||
/// NOTE: If you override this, you will want to always invoke this base class version of this
|
||||
/// <see cref="OnDestroy"/> method!!
|
||||
/// </summary>
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
if (NetworkObject != null && NetworkObject.IsSpawned && IsSpawned)
|
||||
{
|
||||
// If the associated NetworkObject is still spawned then this
|
||||
// NetworkBehaviour will be removed from the NetworkObject's
|
||||
// ChildNetworkBehaviours list.
|
||||
NetworkObject.OnNetworkBehaviourDestroyed(this);
|
||||
}
|
||||
|
||||
// this seems odd to do here, but in fact especially in tests we can find ourselves
|
||||
// here without having called InitializedVariables, which causes problems if any
|
||||
// of those variables use native containers (e.g. NetworkList) as they won't be
|
||||
@@ -764,6 +785,7 @@ namespace Unity.Netcode
|
||||
InitializeVariables();
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < NetworkVariableFields.Count; i++)
|
||||
{
|
||||
NetworkVariableFields[i].Dispose();
|
||||
|
||||
@@ -3,14 +3,22 @@ using Unity.Profiling;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// An helper class that helps NetworkManager update NetworkBehaviours and replicate them down to connected clients.
|
||||
/// </summary>
|
||||
public class NetworkBehaviourUpdater
|
||||
{
|
||||
private HashSet<NetworkObject> m_Touched = new HashSet<NetworkObject>();
|
||||
private HashSet<NetworkObject> m_DirtyNetworkObjects = new HashSet<NetworkObject>();
|
||||
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
private ProfilerMarker m_NetworkBehaviourUpdate = new ProfilerMarker($"{nameof(NetworkBehaviour)}.{nameof(NetworkBehaviourUpdate)}");
|
||||
#endif
|
||||
|
||||
internal void AddForUpdate(NetworkObject networkObject)
|
||||
{
|
||||
m_DirtyNetworkObjects.Add(networkObject);
|
||||
}
|
||||
|
||||
internal void NetworkBehaviourUpdate(NetworkManager networkManager)
|
||||
{
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
@@ -20,57 +28,59 @@ namespace Unity.Netcode
|
||||
{
|
||||
if (networkManager.IsServer)
|
||||
{
|
||||
m_Touched.Clear();
|
||||
for (int i = 0; i < networkManager.ConnectedClientsList.Count; i++)
|
||||
foreach (var dirtyObj in m_DirtyNetworkObjects)
|
||||
{
|
||||
var client = networkManager.ConnectedClientsList[i];
|
||||
var spawnedObjs = networkManager.SpawnManager.SpawnedObjectsList;
|
||||
m_Touched.UnionWith(spawnedObjs);
|
||||
foreach (var sobj in spawnedObjs)
|
||||
for (int k = 0; k < dirtyObj.ChildNetworkBehaviours.Count; k++)
|
||||
{
|
||||
if (sobj.IsNetworkVisibleTo(client.ClientId))
|
||||
dirtyObj.ChildNetworkBehaviours[k].PreVariableUpdate();
|
||||
}
|
||||
|
||||
for (int i = 0; i < networkManager.ConnectedClientsList.Count; i++)
|
||||
{
|
||||
var client = networkManager.ConnectedClientsList[i];
|
||||
if (networkManager.IsHost && client.ClientId == networkManager.LocalClientId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dirtyObj.IsNetworkVisibleTo(client.ClientId))
|
||||
{
|
||||
// Sync just the variables for just the objects this client sees
|
||||
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
||||
for (int k = 0; k < dirtyObj.ChildNetworkBehaviours.Count; k++)
|
||||
{
|
||||
sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId);
|
||||
dirtyObj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, reset all the no-longer-dirty variables
|
||||
foreach (var sobj in m_Touched)
|
||||
{
|
||||
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
||||
{
|
||||
sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// when client updates the server, it tells it about all its objects
|
||||
foreach (var sobj in networkManager.SpawnManager.SpawnedObjectsList)
|
||||
foreach (var sobj in m_DirtyNetworkObjects)
|
||||
{
|
||||
if (sobj.IsOwner)
|
||||
{
|
||||
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
||||
{
|
||||
sobj.ChildNetworkBehaviours[k].PreVariableUpdate();
|
||||
}
|
||||
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
||||
{
|
||||
sobj.ChildNetworkBehaviours[k].VariableUpdate(NetworkManager.ServerClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, reset all the no-longer-dirty variables
|
||||
foreach (var sobj in networkManager.SpawnManager.SpawnedObjectsList)
|
||||
}
|
||||
// Now, reset all the no-longer-dirty variables
|
||||
foreach (var dirtyobj in m_DirtyNetworkObjects)
|
||||
{
|
||||
for (int k = 0; k < dirtyobj.ChildNetworkBehaviours.Count; k++)
|
||||
{
|
||||
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
||||
{
|
||||
sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite();
|
||||
}
|
||||
dirtyobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite();
|
||||
}
|
||||
}
|
||||
m_DirtyNetworkObjects.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -54,7 +54,12 @@ namespace Unity.Netcode
|
||||
return $"{nameof(NetworkPrefab)} \"{networkPrefab.Prefab.gameObject.name}\"";
|
||||
}
|
||||
|
||||
internal NetworkBehaviourUpdater BehaviourUpdater { get; private set; }
|
||||
internal NetworkBehaviourUpdater BehaviourUpdater { get; set; }
|
||||
|
||||
internal void MarkNetworkObjectDirty(NetworkObject networkObject)
|
||||
{
|
||||
BehaviourUpdater.AddForUpdate(networkObject);
|
||||
}
|
||||
|
||||
internal MessagingSystem MessagingSystem { get; private set; }
|
||||
|
||||
@@ -62,6 +67,9 @@ namespace Unity.Netcode
|
||||
|
||||
internal Dictionary<ulong, ConnectionApprovalResponse> ClientsToApprove = new Dictionary<ulong, ConnectionApprovalResponse>();
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="NetworkPrefabHandler"/> instance created after starting the <see cref="NetworkManager"/>
|
||||
/// </summary>
|
||||
public NetworkPrefabHandler PrefabHandler
|
||||
{
|
||||
get
|
||||
@@ -197,12 +205,25 @@ namespace Unity.Netcode
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for the <see cref="NetworkTimeSystem"/> of the NetworkManager.
|
||||
/// Prefer the use of the LocalTime and ServerTime properties
|
||||
/// </summary>
|
||||
public NetworkTimeSystem NetworkTimeSystem { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for the <see cref="NetworkTickSystem"/> of the NetworkManager.
|
||||
/// </summary>
|
||||
public NetworkTickSystem NetworkTickSystem { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The local <see cref="NetworkTime"/>
|
||||
/// </summary>
|
||||
public NetworkTime LocalTime => NetworkTickSystem?.LocalTime ?? default;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="NetworkTime"/> on the server
|
||||
/// </summary>
|
||||
public NetworkTime ServerTime => NetworkTickSystem?.ServerTime ?? default;
|
||||
|
||||
/// <summary>
|
||||
@@ -229,10 +250,19 @@ namespace Unity.Netcode
|
||||
|
||||
internal IDeferredMessageManager DeferredMessageManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CustomMessagingManager for this NetworkManager
|
||||
/// </summary>
|
||||
public CustomMessagingManager CustomMessagingManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="NetworkSceneManager"/> instance created after starting the <see cref="NetworkManager"/>
|
||||
/// </summary>
|
||||
public NetworkSceneManager SceneManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The client id used to represent the server
|
||||
/// </summary>
|
||||
public const ulong ServerClientId = 0;
|
||||
|
||||
/// <summary>
|
||||
@@ -341,7 +371,9 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
public bool IsConnectedClient { get; internal set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Can be used to determine if the <see cref="NetworkManager"/> is currently shutting itself down
|
||||
/// </summary>
|
||||
public bool ShutdownInProgress { get { return m_ShuttingDown; } }
|
||||
|
||||
/// <summary>
|
||||
@@ -374,30 +406,46 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Connection Approval Response
|
||||
/// </summary>
|
||||
/// <param name="Approved">Whether or not the client was approved</param>
|
||||
/// <param name="CreatePlayerObject">If true, a player object will be created. Otherwise the client will have no object.</param>
|
||||
/// <param name="PlayerPrefabHash">The prefabHash to use for the client. If createPlayerObject is false, this is ignored. If playerPrefabHash is null, the default player prefab is used.</param>
|
||||
/// <param name="Position">The position to spawn the client at. If null, the prefab position is used.</param>
|
||||
/// <param name="Rotation">The rotation to spawn the client with. If null, the prefab position is used.</param>
|
||||
/// <param name="Pending">If the Approval decision cannot be made immediately, the client code can set Pending to true, keep a reference to the ConnectionApprovalResponse object and write to it later. Client code must exercise care to setting all the members to the value it wants before marking Pending to false, to indicate completion. If the field is set as Pending = true, we'll monitor the object until it gets set to not pending anymore and use the parameters then.</param>
|
||||
public class ConnectionApprovalResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not the client was approved
|
||||
/// </summary>
|
||||
public bool Approved;
|
||||
/// <summary>
|
||||
/// If true, a player object will be created. Otherwise the client will have no object.
|
||||
/// </summary>
|
||||
public bool CreatePlayerObject;
|
||||
/// <summary>
|
||||
/// The prefabHash to use for the client. If createPlayerObject is false, this is ignored. If playerPrefabHash is null, the default player prefab is used.
|
||||
/// </summary>
|
||||
public uint? PlayerPrefabHash;
|
||||
/// <summary>
|
||||
/// The position to spawn the client at. If null, the prefab position is used.
|
||||
/// </summary>
|
||||
public Vector3? Position;
|
||||
/// <summary>
|
||||
/// The rotation to spawn the client with. If null, the prefab position is used.
|
||||
/// </summary>
|
||||
public Quaternion? Rotation;
|
||||
/// <summary>
|
||||
/// If the Approval decision cannot be made immediately, the client code can set Pending to true, keep a reference to the ConnectionApprovalResponse object and write to it later. Client code must exercise care to setting all the members to the value it wants before marking Pending to false, to indicate completion. If the field is set as Pending = true, we'll monitor the object until it gets set to not pending anymore and use the parameters then.
|
||||
/// </summary>
|
||||
public bool Pending;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connection Approval Request
|
||||
/// </summary>
|
||||
/// <param name="Payload">The connection data payload</param>
|
||||
/// <param name="ClientNetworkId">The Network Id of the client we are about to handle</param>
|
||||
public struct ConnectionApprovalRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The connection data payload
|
||||
/// </summary>
|
||||
public byte[] Payload;
|
||||
/// <summary>
|
||||
/// The Network Id of the client we are about to handle
|
||||
/// </summary>
|
||||
public ulong ClientNetworkId;
|
||||
}
|
||||
|
||||
@@ -961,6 +1009,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Starts a server
|
||||
/// </summary>
|
||||
/// <returns>(<see cref="true"/>/<see cref="false"/>) returns true if <see cref="NetworkManager"/> started in server mode successfully.</returns>
|
||||
public bool StartServer()
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||
@@ -1000,6 +1049,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Starts a client
|
||||
/// </summary>
|
||||
/// <returns>(<see cref="true"/>/<see cref="false"/>) returns true if <see cref="NetworkManager"/> started in client mode successfully.</returns>
|
||||
public bool StartClient()
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||
@@ -1033,6 +1083,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Starts a Host
|
||||
/// </summary>
|
||||
/// <returns>(<see cref="true"/>/<see cref="false"/>) returns true if <see cref="NetworkManager"/> started in host mode successfully.</returns>
|
||||
public bool StartHost()
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||
@@ -1144,6 +1195,9 @@ namespace Unity.Netcode
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set this NetworkManager instance as the static NetworkManager singleton
|
||||
/// </summary>
|
||||
public void SetSingleton()
|
||||
{
|
||||
Singleton = this;
|
||||
@@ -1335,6 +1389,19 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
IsConnectedClient = false;
|
||||
|
||||
// We need to clean up NetworkObjects before we reset the IsServer
|
||||
// and IsClient properties. This provides consistency of these two
|
||||
// property values for NetworkObjects that are still spawned when
|
||||
// the shutdown cycle begins.
|
||||
if (SpawnManager != null)
|
||||
{
|
||||
SpawnManager.DespawnAndDestroyNetworkObjects();
|
||||
SpawnManager.ServerResetShudownStateForSceneObjects();
|
||||
|
||||
SpawnManager = null;
|
||||
}
|
||||
|
||||
IsServer = false;
|
||||
IsClient = false;
|
||||
|
||||
@@ -1357,14 +1424,6 @@ namespace Unity.Netcode
|
||||
NetworkConfig.NetworkTransport.OnTransportEvent -= HandleRawTransportPoll;
|
||||
}
|
||||
|
||||
if (SpawnManager != null)
|
||||
{
|
||||
SpawnManager.DespawnAndDestroyNetworkObjects();
|
||||
SpawnManager.ServerResetShudownStateForSceneObjects();
|
||||
|
||||
SpawnManager = null;
|
||||
}
|
||||
|
||||
if (DeferredMessageManager != null)
|
||||
{
|
||||
DeferredMessageManager.CleanupAllTriggers();
|
||||
@@ -1405,7 +1464,7 @@ namespace Unity.Netcode
|
||||
ClearClients();
|
||||
}
|
||||
|
||||
// INetworkUpdateSystem
|
||||
/// <inheritdoc />
|
||||
public void NetworkUpdate(NetworkUpdateStage updateStage)
|
||||
{
|
||||
switch (updateStage)
|
||||
@@ -2011,6 +2070,20 @@ namespace Unity.Netcode
|
||||
|
||||
SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId);
|
||||
|
||||
for (int index = 0; index < MessagingSystem.MessageHandlers.Length; index++)
|
||||
{
|
||||
if (MessagingSystem.MessageTypes[index] != null)
|
||||
{
|
||||
var orderingMessage = new OrderingMessage
|
||||
{
|
||||
Order = index,
|
||||
Hash = XXHash.Hash32(MessagingSystem.MessageTypes[index].FullName)
|
||||
};
|
||||
|
||||
SendMessage(ref orderingMessage, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId);
|
||||
}
|
||||
}
|
||||
|
||||
// If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization
|
||||
if (!NetworkConfig.EnableSceneManagement)
|
||||
{
|
||||
|
||||
@@ -235,9 +235,19 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows a previously hidden <see cref="NetworkObject"/> to a client
|
||||
/// Makes the previously hidden <see cref="NetworkObject"/> "netcode visible" to the targeted client.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client to show the <see cref="NetworkObject"/> to</param>
|
||||
/// <remarks>
|
||||
/// Usage: Use to start sending updates for a previously hidden <see cref="NetworkObject"/> to the targeted client.<br />
|
||||
/// <br />
|
||||
/// Dynamically Spawned: <see cref="NetworkObject"/>s will be instantiated and spawned on the targeted client side.<br />
|
||||
/// In-Scene Placed: The instantiated but despawned <see cref="NetworkObject"/>s will be spawned on the targeted client side.<br />
|
||||
/// <br />
|
||||
/// See Also:<br />
|
||||
/// <see cref="NetworkShow(ulong)"/><br />
|
||||
/// <see cref="NetworkHide(ulong)"/> or <see cref="NetworkHide(List{NetworkObject}, ulong)"/><br />
|
||||
/// </remarks>
|
||||
/// <param name="clientId">The targeted client</param>
|
||||
public void NetworkShow(ulong clientId)
|
||||
{
|
||||
if (!IsSpawned)
|
||||
@@ -260,11 +270,22 @@ namespace Unity.Netcode
|
||||
NetworkManager.SpawnManager.SendSpawnCallForObject(clientId, this);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Shows a list of previously hidden <see cref="NetworkObject"/>s to a client
|
||||
/// Makes a list of previously hidden <see cref="NetworkObject"/>s "netcode visible" for the client specified.
|
||||
/// </summary>
|
||||
/// <param name="networkObjects">The <see cref="NetworkObject"/>s to show</param>
|
||||
/// <param name="clientId">The client to show the objects to</param>
|
||||
/// <remarks>
|
||||
/// Usage: Use to start sending updates for previously hidden <see cref="NetworkObject"/>s to the targeted client.<br />
|
||||
/// <br />
|
||||
/// Dynamically Spawned: <see cref="NetworkObject"/>s will be instantiated and spawned on the targeted client's side.<br />
|
||||
/// In-Scene Placed: Already instantiated but despawned <see cref="NetworkObject"/>s will be spawned on the targeted client's side.<br />
|
||||
/// <br />
|
||||
/// See Also:<br />
|
||||
/// <see cref="NetworkShow(ulong)"/><br />
|
||||
/// <see cref="NetworkHide(ulong)"/> or <see cref="NetworkHide(List{NetworkObject}, ulong)"/><br />
|
||||
/// </remarks>
|
||||
/// <param name="networkObjects">The objects to become "netcode visible" to the targeted client</param>
|
||||
/// <param name="clientId">The targeted client</param>
|
||||
public static void NetworkShow(List<NetworkObject> networkObjects, ulong clientId)
|
||||
{
|
||||
if (networkObjects == null || networkObjects.Count == 0)
|
||||
@@ -305,9 +326,19 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides a object from a specific client
|
||||
/// Hides the <see cref="NetworkObject"/> from the targeted client.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client to hide the object for</param>
|
||||
/// <remarks>
|
||||
/// Usage: Use to stop sending updates to the targeted client, "netcode invisible", for a currently visible <see cref="NetworkObject"/>.<br />
|
||||
/// <br />
|
||||
/// Dynamically Spawned: <see cref="NetworkObject"/>s will be despawned and destroyed on the targeted client's side.<br />
|
||||
/// In-Scene Placed: <see cref="NetworkObject"/>s will only be despawned on the targeted client's side.<br />
|
||||
/// <br />
|
||||
/// See Also:<br />
|
||||
/// <see cref="NetworkHide(List{NetworkObject}, ulong)"/><br />
|
||||
/// <see cref="NetworkShow(ulong)"/> or <see cref="NetworkShow(List{NetworkObject}, ulong)"/><br />
|
||||
/// </remarks>
|
||||
/// <param name="clientId">The targeted client</param>
|
||||
public void NetworkHide(ulong clientId)
|
||||
{
|
||||
if (!IsSpawned)
|
||||
@@ -335,7 +366,7 @@ namespace Unity.Netcode
|
||||
var message = new DestroyObjectMessage
|
||||
{
|
||||
NetworkObjectId = NetworkObjectId,
|
||||
DestroyGameObject = true
|
||||
DestroyGameObject = !IsSceneObject.Value
|
||||
};
|
||||
// Send destroy call
|
||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
|
||||
@@ -343,10 +374,20 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides a list of objects from a client
|
||||
/// Hides a list of <see cref="NetworkObject"/>s from the targeted client.
|
||||
/// </summary>
|
||||
/// <param name="networkObjects">The objects to hide</param>
|
||||
/// <param name="clientId">The client to hide the objects from</param>
|
||||
/// <remarks>
|
||||
/// Usage: Use to stop sending updates to the targeted client, "netcode invisible", for the currently visible <see cref="NetworkObject"/>s.<br />
|
||||
/// <br />
|
||||
/// Dynamically Spawned: <see cref="NetworkObject"/>s will be despawned and destroyed on the targeted client's side.<br />
|
||||
/// In-Scene Placed: <see cref="NetworkObject"/>s will only be despawned on the targeted client's side.<br />
|
||||
/// <br />
|
||||
/// See Also:<br />
|
||||
/// <see cref="NetworkHide(ulong)"/><br />
|
||||
/// <see cref="NetworkShow(ulong)"/> or <see cref="NetworkShow(List{NetworkObject}, ulong)"/><br />
|
||||
/// </remarks>
|
||||
/// <param name="networkObjects">The <see cref="NetworkObject"/>s that will become "netcode invisible" to the targeted client</param>
|
||||
/// <param name="clientId">The targeted client</param>
|
||||
public static void NetworkHide(List<NetworkObject> networkObjects, ulong clientId)
|
||||
{
|
||||
if (networkObjects == null || networkObjects.Count == 0)
|
||||
@@ -455,8 +496,8 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Spawns a <see cref="NetworkObject"/> across the network and makes it the player object for the given client
|
||||
/// </summary>
|
||||
/// <param name="clientId">The clientId whos player object this is</param>
|
||||
/// <param name="destroyWithScene">Should the object be destroyd when the scene is changed</param>
|
||||
/// <param name="clientId">The clientId who's player object this is</param>
|
||||
/// <param name="destroyWithScene">Should the object be destroyed when the scene is changed</param>
|
||||
public void SpawnAsPlayerObject(ulong clientId, bool destroyWithScene = false)
|
||||
{
|
||||
SpawnInternal(destroyWithScene, clientId, true);
|
||||
@@ -512,7 +553,14 @@ namespace Unity.Netcode
|
||||
|
||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||
{
|
||||
ChildNetworkBehaviours[i].InternalOnGainedOwnership();
|
||||
if (ChildNetworkBehaviours[i].gameObject.activeInHierarchy)
|
||||
{
|
||||
ChildNetworkBehaviours[i].InternalOnGainedOwnership();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"{ChildNetworkBehaviours[i].gameObject.name} is disabled! Netcode for GameObjects does not support disabled NetworkBehaviours! The {ChildNetworkBehaviours[i].GetType().Name} component was skipped during ownership assignment!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,16 +589,34 @@ namespace Unity.Netcode
|
||||
m_LatestParent = latestParent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the parent of the NetworkObject transform.
|
||||
/// </summary>
|
||||
/// <param name="parent">The new parent for this NetworkObject transform will be the child of.</param>
|
||||
/// <param name="worldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.</param>
|
||||
/// <returns>Whether or not reparenting was successful.</returns>
|
||||
public bool TrySetParent(Transform parent, bool worldPositionStays = true)
|
||||
{
|
||||
return TrySetParent(parent.GetComponent<NetworkObject>(), worldPositionStays);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the parent of the NetworkObject transform.
|
||||
/// </summary>
|
||||
/// <param name="parent">The new parent for this NetworkObject transform will be the child of.</param>
|
||||
/// <param name="worldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.</param>
|
||||
/// <returns>Whether or not reparenting was successful.</returns>
|
||||
public bool TrySetParent(GameObject parent, bool worldPositionStays = true)
|
||||
{
|
||||
return TrySetParent(parent.GetComponent<NetworkObject>(), worldPositionStays);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the parent of the NetworkObject transform.
|
||||
/// </summary>
|
||||
/// <param name="parent">The new parent for this NetworkObject transform will be the child of.</param>
|
||||
/// <param name="worldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.</param>
|
||||
/// <returns>Whether or not reparenting was successful.</returns>
|
||||
public bool TrySetParent(NetworkObject parent, bool worldPositionStays = true)
|
||||
{
|
||||
if (!AutoObjectParentSync)
|
||||
@@ -679,7 +745,7 @@ namespace Unity.Netcode
|
||||
// For instance, if we're spawning NetworkObject 5 and its parent is 10, what should happen if we do not have 10 yet?
|
||||
// let's say 10 is on the way to be replicated in a few frames and we could fix that parent-child relationship later.
|
||||
//
|
||||
// If you couldn't find your parent, we put you into OrphanChildren set and everytime we spawn another NetworkObject locally due to replication,
|
||||
// If you couldn't find your parent, we put you into OrphanChildren set and every time we spawn another NetworkObject locally due to replication,
|
||||
// we call CheckOrphanChildren() method and quickly iterate over OrphanChildren set and see if we can reparent/adopt one.
|
||||
internal static HashSet<NetworkObject> OrphanChildren = new HashSet<NetworkObject>();
|
||||
|
||||
@@ -746,7 +812,14 @@ namespace Unity.Netcode
|
||||
|
||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||
{
|
||||
ChildNetworkBehaviours[i].InternalOnNetworkSpawn();
|
||||
if (ChildNetworkBehaviours[i].gameObject.activeInHierarchy)
|
||||
{
|
||||
ChildNetworkBehaviours[i].InternalOnNetworkSpawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"{ChildNetworkBehaviours[i].gameObject.name} is disabled! Netcode for GameObjects does not support spawning disabled NetworkBehaviours! The {ChildNetworkBehaviours[i].GetType().Name} component was skipped during spawn!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,12 +868,12 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
internal void MarkVariablesDirty()
|
||||
internal void MarkVariablesDirty(bool dirty)
|
||||
{
|
||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||
{
|
||||
var behavior = ChildNetworkBehaviours[i];
|
||||
behavior.MarkVariablesDirty();
|
||||
behavior.MarkVariablesDirty(dirty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,8 +886,8 @@ namespace Unity.Netcode
|
||||
// if and when we have different systems for where it is expected that orphans survive across ticks,
|
||||
// then this warning will remind us that we need to revamp the system because then we can no longer simply
|
||||
// spawn the orphan without its parent (at least, not when its transform is set to local coords mode)
|
||||
// - because then you’ll have children popping at the wrong location not having their parent’s global position to root them
|
||||
// - and then they’ll pop to the correct location after they get the parent, and that would be not good
|
||||
// - because then you'll have children popping at the wrong location not having their parent's global position to root them
|
||||
// - and then they'll pop to the correct location after they get the parent, and that would be not good
|
||||
internal static void VerifyParentingStatus()
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
@@ -1146,5 +1219,21 @@ namespace Unity.Netcode
|
||||
|
||||
return GlobalObjectIdHash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a NetworkBehaviour from the ChildNetworkBehaviours list when destroyed
|
||||
/// while the NetworkObject is still spawned.
|
||||
/// </summary>
|
||||
internal void OnNetworkBehaviourDestroyed(NetworkBehaviour networkBehaviour)
|
||||
{
|
||||
if (networkBehaviour.IsSpawned && IsSpawned)
|
||||
{
|
||||
if (NetworkManager.LogLevel == LogLevel.Developer)
|
||||
{
|
||||
NetworkLog.LogWarning($"{nameof(NetworkBehaviour)}-{networkBehaviour.name} is being destroyed while {nameof(NetworkObject)}-{name} is still spawned! (could break state synchronization)");
|
||||
}
|
||||
ChildNetworkBehaviours.Remove(networkBehaviour);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,25 +7,55 @@ using UnityEngine.PlayerLoop;
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the required interface of a network update system being executed by the network update loop.
|
||||
/// Defines the required interface of a network update system being executed by the <see cref="NetworkUpdateLoop"/>.
|
||||
/// </summary>
|
||||
public interface INetworkUpdateSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// The update method that is being executed in the context of related <see cref="NetworkUpdateStage"/>.
|
||||
/// </summary>
|
||||
/// <param name="updateStage">The <see cref="NetworkUpdateStage"/> that is being executed.</param>
|
||||
void NetworkUpdate(NetworkUpdateStage updateStage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines network update stages being executed by the network update loop.
|
||||
/// See for more details on update stages:
|
||||
/// https://docs.unity3d.com/ScriptReference/PlayerLoop.Initialization.html
|
||||
/// </summary>
|
||||
public enum NetworkUpdateStage : byte
|
||||
{
|
||||
Unset = 0, // Default
|
||||
/// <summary>
|
||||
/// Default value
|
||||
/// </summary>
|
||||
Unset = 0,
|
||||
/// <summary>
|
||||
/// Very first initialization update
|
||||
/// </summary>
|
||||
Initialization = 1,
|
||||
/// <summary>
|
||||
/// Invoked before Fixed update
|
||||
/// </summary>
|
||||
EarlyUpdate = 2,
|
||||
/// <summary>
|
||||
/// Fixed Update (i.e. state machine, physics, animations, etc)
|
||||
/// </summary>
|
||||
FixedUpdate = 3,
|
||||
/// <summary>
|
||||
/// Updated before the Monobehaviour.Update for all components is invoked
|
||||
/// </summary>
|
||||
PreUpdate = 4,
|
||||
/// <summary>
|
||||
/// Updated when the Monobehaviour.Update for all components is invoked
|
||||
/// </summary>
|
||||
Update = 5,
|
||||
/// <summary>
|
||||
/// Updated before the Monobehaviour.LateUpdate for all components is invoked
|
||||
/// </summary>
|
||||
PreLateUpdate = 6,
|
||||
/// <summary>
|
||||
/// Updated after the Monobehaviour.LateUpdate for all components is invoked
|
||||
/// </summary>
|
||||
PostLateUpdate = 7
|
||||
}
|
||||
|
||||
@@ -53,6 +83,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Registers a network update system to be executed in all network update stages.
|
||||
/// </summary>
|
||||
/// <param name="updateSystem">The <see cref="INetworkUpdateSystem"/> implementation to register for all network updates</param>
|
||||
public static void RegisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem)
|
||||
{
|
||||
foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
|
||||
@@ -64,6 +95,8 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Registers a network update system to be executed in a specific network update stage.
|
||||
/// </summary>
|
||||
/// <param name="updateSystem">The <see cref="INetworkUpdateSystem"/> implementation to register for all network updates</param>
|
||||
/// <param name="updateStage">The <see cref="NetworkUpdateStage"/> being registered for the <see cref="INetworkUpdateSystem"/> implementation</param>
|
||||
public static void RegisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
|
||||
{
|
||||
var sysSet = s_UpdateSystem_Sets[updateStage];
|
||||
@@ -94,6 +127,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Unregisters a network update system from all network update stages.
|
||||
/// </summary>
|
||||
/// <param name="updateSystem">The <see cref="INetworkUpdateSystem"/> implementation to deregister from all network updates</param>
|
||||
public static void UnregisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem)
|
||||
{
|
||||
foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
|
||||
@@ -105,6 +139,8 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Unregisters a network update system from a specific network update stage.
|
||||
/// </summary>
|
||||
/// <param name="updateSystem">The <see cref="INetworkUpdateSystem"/> implementation to deregister from all network updates</param>
|
||||
/// <param name="updateStage">The <see cref="NetworkUpdateStage"/> to be deregistered from the <see cref="INetworkUpdateSystem"/> implementation</param>
|
||||
public static void UnregisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
|
||||
{
|
||||
var sysSet = s_UpdateSystem_Sets[updateStage];
|
||||
|
||||
@@ -7,8 +7,18 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
public class InvalidParentException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor for <see cref="InvalidParentException"/>
|
||||
/// </summary>
|
||||
public InvalidParentException() { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <param name="message"></param>
|
||||
public InvalidParentException(string message) : base(message) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="innerException"></param>
|
||||
public InvalidParentException(string message, Exception innerException) : base(message, innerException) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,15 @@ namespace Unity.Netcode
|
||||
public SpawnStateException(string message, Exception inner) : base(message, inner) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception thrown when a specified network channel is invalid
|
||||
/// </summary>
|
||||
public class InvalidChannelException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs an InvalidChannelException with a message
|
||||
/// </summary>
|
||||
/// <param name="message">the message</param>
|
||||
public InvalidChannelException(string message) : base(message) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,23 @@ namespace Unity.Netcode
|
||||
public static LogLevel CurrentLogLevel => NetworkManager.Singleton == null ? LogLevel.Normal : NetworkManager.Singleton.LogLevel;
|
||||
|
||||
// internal logging
|
||||
|
||||
/// <summary>
|
||||
/// Locally logs a info log with Netcode prefixing.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log</param>
|
||||
public static void LogInfo(string message) => Debug.Log($"[Netcode] {message}");
|
||||
|
||||
/// <summary>
|
||||
/// Locally logs a warning log with Netcode prefixing.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log</param>
|
||||
public static void LogWarning(string message) => Debug.LogWarning($"[Netcode] {message}");
|
||||
|
||||
/// <summary>
|
||||
/// Locally logs a error log with Netcode prefixing.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log</param>
|
||||
public static void LogError(string message) => Debug.LogError($"[Netcode] {message}");
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -193,6 +193,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Sends a named message to all clients
|
||||
/// </summary>
|
||||
/// <param name="messageName">The message name to send</param>
|
||||
/// <param name="messageStream">The message stream containing the data</param>
|
||||
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
|
||||
public void SendNamedMessageToAll(string messageName, FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
|
||||
|
||||
50
Runtime/Messaging/Messages/OrderingMessage.cs
Normal file
50
Runtime/Messaging/Messages/OrderingMessage.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Upon connecting, the host sends a series of OrderingMessage to the client so that it can make sure both sides
|
||||
/// have the same message types in the same positions in
|
||||
/// - MessagingSystem.m_MessageHandlers
|
||||
/// - MessagingSystem.m_ReverseTypeMap
|
||||
/// even if one side has extra messages (compilation, version, patch, or platform differences, etc...)
|
||||
///
|
||||
/// The ConnectionRequestedMessage, ConnectionApprovedMessage and OrderingMessage are prioritized at the beginning
|
||||
/// of the mapping, to guarantee they can be exchanged before the two sides share their ordering
|
||||
/// The sorting used in also stable so that even if MessageType names share hashes, it will work most of the time
|
||||
/// </summary>
|
||||
internal struct OrderingMessage : INetworkMessage
|
||||
{
|
||||
public int Order;
|
||||
public uint Hash;
|
||||
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(Order) + FastBufferWriter.GetWriteSize(Hash)))
|
||||
{
|
||||
throw new OverflowException($"Not enough space in the buffer to write {nameof(OrderingMessage)}");
|
||||
}
|
||||
|
||||
writer.WriteValue(Order);
|
||||
writer.WriteValue(Hash);
|
||||
}
|
||||
|
||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||
{
|
||||
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(Order) + FastBufferWriter.GetWriteSize(Hash)))
|
||||
{
|
||||
throw new OverflowException($"Not enough data in the buffer to read {nameof(OrderingMessage)}");
|
||||
}
|
||||
|
||||
reader.ReadValue(out Order);
|
||||
reader.ReadValue(out Hash);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Handle(ref NetworkContext context)
|
||||
{
|
||||
((NetworkManager)context.SystemOwner).MessagingSystem.ReorderMessage(Order, Hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2e5a740c1abd4315801e3f26ecf8adb
|
||||
guid: 3ada9e8fd5bf94b1f9a6a21531c8a3ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -8,6 +8,11 @@ using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal class HandlerNotRegisteredException : SystemException
|
||||
{
|
||||
public HandlerNotRegisteredException() { }
|
||||
public HandlerNotRegisteredException(string issue) : base(issue) { }
|
||||
}
|
||||
|
||||
internal class InvalidMessageStructureException : SystemException
|
||||
{
|
||||
@@ -44,8 +49,9 @@ namespace Unity.Netcode
|
||||
|
||||
private NativeList<ReceiveQueueItem> m_IncomingMessageQueue = new NativeList<ReceiveQueueItem>(16, Allocator.Persistent);
|
||||
|
||||
private MessageHandler[] m_MessageHandlers = new MessageHandler[255];
|
||||
private Type[] m_ReverseTypeMap = new Type[255];
|
||||
// These array will grow as we need more message handlers. 4 is just a starting size.
|
||||
private MessageHandler[] m_MessageHandlers = new MessageHandler[4];
|
||||
private Type[] m_ReverseTypeMap = new Type[4];
|
||||
|
||||
private Dictionary<Type, uint> m_MessageTypes = new Dictionary<Type, uint>();
|
||||
private Dictionary<ulong, NativeList<SendQueueItem>> m_SendQueues = new Dictionary<ulong, NativeList<SendQueueItem>>();
|
||||
@@ -59,6 +65,7 @@ namespace Unity.Netcode
|
||||
|
||||
internal Type[] MessageTypes => m_ReverseTypeMap;
|
||||
internal MessageHandler[] MessageHandlers => m_MessageHandlers;
|
||||
|
||||
internal uint MessageHandlerCount => m_HighMessageType;
|
||||
|
||||
internal uint GetMessageType(Type t)
|
||||
@@ -75,6 +82,35 @@ namespace Unity.Netcode
|
||||
public MessageHandler Handler;
|
||||
}
|
||||
|
||||
internal List<MessageWithHandler> PrioritizeMessageOrder(List<MessageWithHandler> allowedTypes)
|
||||
{
|
||||
var prioritizedTypes = new List<MessageWithHandler>();
|
||||
|
||||
// first pass puts the priority message in the first indices
|
||||
// Those are the messages that must be delivered in order to allow re-ordering the others later
|
||||
foreach (var t in allowedTypes)
|
||||
{
|
||||
if (t.MessageType.FullName == "Unity.Netcode.ConnectionRequestMessage" ||
|
||||
t.MessageType.FullName == "Unity.Netcode.ConnectionApprovedMessage" ||
|
||||
t.MessageType.FullName == "Unity.Netcode.OrderingMessage")
|
||||
{
|
||||
prioritizedTypes.Add(t);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var t in allowedTypes)
|
||||
{
|
||||
if (t.MessageType.FullName != "Unity.Netcode.ConnectionRequestMessage" &&
|
||||
t.MessageType.FullName != "Unity.Netcode.ConnectionApprovedMessage" &&
|
||||
t.MessageType.FullName != "Unity.Netcode.OrderingMessage")
|
||||
{
|
||||
prioritizedTypes.Add(t);
|
||||
}
|
||||
}
|
||||
|
||||
return prioritizedTypes;
|
||||
}
|
||||
|
||||
public MessagingSystem(IMessageSender messageSender, object owner, IMessageProvider provider = null)
|
||||
{
|
||||
try
|
||||
@@ -89,6 +125,7 @@ namespace Unity.Netcode
|
||||
var allowedTypes = provider.GetMessages();
|
||||
|
||||
allowedTypes.Sort((a, b) => string.CompareOrdinal(a.MessageType.FullName, b.MessageType.FullName));
|
||||
allowedTypes = PrioritizeMessageOrder(allowedTypes);
|
||||
foreach (var type in allowedTypes)
|
||||
{
|
||||
RegisterMessageType(type);
|
||||
@@ -143,6 +180,13 @@ namespace Unity.Netcode
|
||||
|
||||
private void RegisterMessageType(MessageWithHandler messageWithHandler)
|
||||
{
|
||||
// if we are out of space, perform amortized linear growth
|
||||
if (m_HighMessageType == m_MessageHandlers.Length)
|
||||
{
|
||||
Array.Resize(ref m_MessageHandlers, 2 * m_MessageHandlers.Length);
|
||||
Array.Resize(ref m_ReverseTypeMap, 2 * m_ReverseTypeMap.Length);
|
||||
}
|
||||
|
||||
m_MessageHandlers[m_HighMessageType] = messageWithHandler.Handler;
|
||||
m_ReverseTypeMap[m_HighMessageType] = messageWithHandler.MessageType;
|
||||
m_MessageTypes[messageWithHandler.MessageType] = m_HighMessageType++;
|
||||
@@ -226,6 +270,70 @@ namespace Unity.Netcode
|
||||
return true;
|
||||
}
|
||||
|
||||
// Moves the handler for the type having hash `targetHash` to the `desiredOrder` position, in the handler list
|
||||
// This allows the server to tell the client which id it is using for which message and make sure the right
|
||||
// message is used when deserializing.
|
||||
internal void ReorderMessage(int desiredOrder, uint targetHash)
|
||||
{
|
||||
if (desiredOrder < 0)
|
||||
{
|
||||
throw new ArgumentException("ReorderMessage desiredOrder must be positive");
|
||||
}
|
||||
|
||||
if (desiredOrder < m_ReverseTypeMap.Length &&
|
||||
XXHash.Hash32(m_ReverseTypeMap[desiredOrder].FullName) == targetHash)
|
||||
{
|
||||
// matching positions and hashes. All good.
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"Unexpected hash for {desiredOrder}");
|
||||
|
||||
// Since the message at `desiredOrder` is not the expected one,
|
||||
// insert an empty placeholder and move the messages down
|
||||
var typesAsList = new List<Type>(m_ReverseTypeMap);
|
||||
|
||||
typesAsList.Insert(desiredOrder, null);
|
||||
var handlersAsList = new List<MessageHandler>(m_MessageHandlers);
|
||||
handlersAsList.Insert(desiredOrder, null);
|
||||
|
||||
// we added a dummy message, bump the end up
|
||||
m_HighMessageType++;
|
||||
|
||||
// Here, we rely on the server telling us about all messages, in order.
|
||||
// So, we know the handlers before desiredOrder are correct.
|
||||
// We start at desiredOrder to not shift them when we insert.
|
||||
int position = desiredOrder;
|
||||
bool found = false;
|
||||
while (position < typesAsList.Count)
|
||||
{
|
||||
if (typesAsList[position] != null &&
|
||||
XXHash.Hash32(typesAsList[position].FullName) == targetHash)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
// Copy the handler and type to the right index
|
||||
|
||||
typesAsList[desiredOrder] = typesAsList[position];
|
||||
handlersAsList[desiredOrder] = handlersAsList[position];
|
||||
typesAsList.RemoveAt(position);
|
||||
handlersAsList.RemoveAt(position);
|
||||
|
||||
// we removed a copy after moving a message, reduce the high message index
|
||||
m_HighMessageType--;
|
||||
}
|
||||
|
||||
m_ReverseTypeMap = typesAsList.ToArray();
|
||||
m_MessageHandlers = handlersAsList.ToArray();
|
||||
}
|
||||
|
||||
public void HandleMessage(in MessageHeader header, FastBufferReader reader, ulong senderId, float timestamp, int serializedHeaderSize)
|
||||
{
|
||||
if (header.MessageType >= m_HighMessageType)
|
||||
@@ -259,18 +367,29 @@ namespace Unity.Netcode
|
||||
var handler = m_MessageHandlers[header.MessageType];
|
||||
using (reader)
|
||||
{
|
||||
// No user-land message handler exceptions should escape the receive loop.
|
||||
// If an exception is throw, the message is ignored.
|
||||
// Example use case: A bad message is received that can't be deserialized and throws
|
||||
// an OverflowException because it specifies a length greater than the number of bytes in it
|
||||
// for some dynamic-length value.
|
||||
try
|
||||
// This will also log an exception is if the server knows about a message type the client doesn't know
|
||||
// about. In this case the handler will be null. It is still an issue the user must deal with: If the
|
||||
// two connecting builds know about different messages, the server should not send a message to a client
|
||||
// that doesn't know about it
|
||||
if (handler == null)
|
||||
{
|
||||
handler.Invoke(reader, ref context, this);
|
||||
Debug.LogException(new HandlerNotRegisteredException(header.MessageType.ToString()));
|
||||
}
|
||||
catch (Exception e)
|
||||
else
|
||||
{
|
||||
Debug.LogException(e);
|
||||
// No user-land message handler exceptions should escape the receive loop.
|
||||
// If an exception is throw, the message is ignored.
|
||||
// Example use case: A bad message is received that can't be deserialized and throws
|
||||
// an OverflowException because it specifies a length greater than the number of bytes in it
|
||||
// for some dynamic-length value.
|
||||
try
|
||||
{
|
||||
handler.Invoke(reader, ref context, this);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||
|
||||
@@ -3,21 +3,52 @@ using Unity.Collections;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Server-Side RPC
|
||||
/// Place holder. <see cref="ServerRpcParams"/>
|
||||
/// Note: Clients always send to one destination when sending RPCs to the server
|
||||
/// so this structure is a place holder
|
||||
/// </summary>
|
||||
public struct ServerRpcSendParams
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The receive parameters for server-side remote procedure calls
|
||||
/// </summary>
|
||||
public struct ServerRpcReceiveParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Server-Side RPC
|
||||
/// The client identifier of the sender
|
||||
/// </summary>
|
||||
public ulong SenderClientId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server-Side RPC
|
||||
/// Can be used with any sever-side remote procedure call
|
||||
/// Note: typically this is use primarily for the <see cref="ServerRpcReceiveParams"/>
|
||||
/// </summary>
|
||||
public struct ServerRpcParams
|
||||
{
|
||||
/// <summary>
|
||||
/// The server RPC send parameters (currently a place holder)
|
||||
/// </summary>
|
||||
public ServerRpcSendParams Send;
|
||||
|
||||
/// <summary>
|
||||
/// The client RPC receive parameters provides you with the sender's identifier
|
||||
/// </summary>
|
||||
public ServerRpcReceiveParams Receive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client-Side RPC
|
||||
/// The send parameters, when sending client RPCs, provides you wil the ability to
|
||||
/// target specific clients as a managed or unmanaged list:
|
||||
/// <see cref="TargetClientIds"/> and <see cref="TargetClientIdsNativeArray"/>
|
||||
/// </summary>
|
||||
public struct ClientRpcSendParams
|
||||
{
|
||||
/// <summary>
|
||||
@@ -34,13 +65,32 @@ namespace Unity.Netcode
|
||||
public NativeArray<ulong>? TargetClientIdsNativeArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client-Side RPC
|
||||
/// Place holder. <see cref="ServerRpcParams"/>
|
||||
/// Note: Server will always be the sender, so this structure is a place holder
|
||||
/// </summary>
|
||||
public struct ClientRpcReceiveParams
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client-Side RPC
|
||||
/// Can be used with any client-side remote procedure call
|
||||
/// Note: Typically this is used primarily for sending to a specific list
|
||||
/// of clients as opposed to the default (all).
|
||||
/// <see cref="ClientRpcSendParams"/>
|
||||
/// </summary>
|
||||
public struct ClientRpcParams
|
||||
{
|
||||
/// <summary>
|
||||
/// The client RPC send parameters provides you with the ability to send to a specific list of clients
|
||||
/// </summary>
|
||||
public ClientRpcSendParams Send;
|
||||
|
||||
/// <summary>
|
||||
/// The client RPC receive parameters (currently a place holder)
|
||||
/// </summary>
|
||||
public ClientRpcReceiveParams Receive;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,15 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
public event OnListChangedDelegate OnListChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor method for <see cref="NetworkList"/>
|
||||
/// </summary>
|
||||
public NetworkList() { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <param name="values"></param>
|
||||
/// <param name="readPerm"></param>
|
||||
/// <param name="writePerm"></param>
|
||||
public NetworkList(IEnumerable<T> values = default,
|
||||
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
||||
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||
@@ -56,6 +63,11 @@ namespace Unity.Netcode
|
||||
return base.IsDirty() || m_DirtyEvents.Length > 0;
|
||||
}
|
||||
|
||||
internal void MarkNetworkObjectDirty()
|
||||
{
|
||||
m_NetworkBehaviour.NetworkManager.MarkNetworkObjectDirty(m_NetworkBehaviour.NetworkObject);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteDelta(FastBufferWriter writer)
|
||||
{
|
||||
@@ -115,10 +127,26 @@ namespace Unity.Netcode
|
||||
/// <inheritdoc />
|
||||
public override void WriteField(FastBufferWriter writer)
|
||||
{
|
||||
writer.WriteValueSafe((ushort)m_ListAtLastReset.Length);
|
||||
for (int i = 0; i < m_ListAtLastReset.Length; i++)
|
||||
// The listAtLastReset mechanism was put in place to deal with duplicate adds
|
||||
// upon initial spawn. However, it causes issues with in-scene placed objects
|
||||
// due to difference in spawn order. In order to address this, we pick the right
|
||||
// list based on the type of object.
|
||||
bool isSceneObject = m_NetworkBehaviour.NetworkObject.IsSceneObject != false;
|
||||
if (isSceneObject)
|
||||
{
|
||||
NetworkVariableSerialization<T>.Write(writer, ref m_ListAtLastReset.ElementAt(i));
|
||||
writer.WriteValueSafe((ushort)m_ListAtLastReset.Length);
|
||||
for (int i = 0; i < m_ListAtLastReset.Length; i++)
|
||||
{
|
||||
NetworkVariableSerialization<T>.Write(writer, ref m_ListAtLastReset.ElementAt(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValueSafe((ushort)m_List.Length);
|
||||
for (int i = 0; i < m_List.Length; i++)
|
||||
{
|
||||
NetworkVariableSerialization<T>.Write(writer, ref m_List.ElementAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +194,7 @@ namespace Unity.Netcode
|
||||
Index = m_List.Length - 1,
|
||||
Value = m_List[m_List.Length - 1]
|
||||
});
|
||||
MarkNetworkObjectDirty();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -173,8 +202,16 @@ namespace Unity.Netcode
|
||||
{
|
||||
reader.ReadValueSafe(out int index);
|
||||
NetworkVariableSerialization<T>.Read(reader, out T value);
|
||||
m_List.InsertRangeWithBeginEnd(index, index + 1);
|
||||
m_List[index] = value;
|
||||
|
||||
if (index < m_List.Length)
|
||||
{
|
||||
m_List.InsertRangeWithBeginEnd(index, index + 1);
|
||||
m_List[index] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_List.Add(value);
|
||||
}
|
||||
|
||||
if (OnListChanged != null)
|
||||
{
|
||||
@@ -194,6 +231,7 @@ namespace Unity.Netcode
|
||||
Index = index,
|
||||
Value = m_List[index]
|
||||
});
|
||||
MarkNetworkObjectDirty();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -226,6 +264,7 @@ namespace Unity.Netcode
|
||||
Index = index,
|
||||
Value = value
|
||||
});
|
||||
MarkNetworkObjectDirty();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -253,6 +292,7 @@ namespace Unity.Netcode
|
||||
Index = index,
|
||||
Value = value
|
||||
});
|
||||
MarkNetworkObjectDirty();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -288,6 +328,7 @@ namespace Unity.Netcode
|
||||
Value = value,
|
||||
PreviousValue = previousValue
|
||||
});
|
||||
MarkNetworkObjectDirty();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -310,6 +351,7 @@ namespace Unity.Netcode
|
||||
{
|
||||
Type = eventType
|
||||
});
|
||||
MarkNetworkObjectDirty();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -396,8 +438,15 @@ namespace Unity.Netcode
|
||||
/// <inheritdoc />
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
m_List.InsertRangeWithBeginEnd(index, index + 1);
|
||||
m_List[index] = item;
|
||||
if (index < m_List.Length)
|
||||
{
|
||||
m_List.InsertRangeWithBeginEnd(index, index + 1);
|
||||
m_List[index] = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_List.Add(item);
|
||||
}
|
||||
|
||||
var listEvent = new NetworkListEvent<T>()
|
||||
{
|
||||
@@ -429,13 +478,15 @@ namespace Unity.Netcode
|
||||
get => m_List[index];
|
||||
set
|
||||
{
|
||||
var previousValue = m_List[index];
|
||||
m_List[index] = value;
|
||||
|
||||
var listEvent = new NetworkListEvent<T>()
|
||||
{
|
||||
Type = NetworkListEvent<T>.EventType.Value,
|
||||
Index = index,
|
||||
Value = value
|
||||
Value = value,
|
||||
PreviousValue = previousValue
|
||||
};
|
||||
|
||||
HandleAddListEvent(listEvent);
|
||||
@@ -445,9 +496,13 @@ namespace Unity.Netcode
|
||||
private void HandleAddListEvent(NetworkListEvent<T> listEvent)
|
||||
{
|
||||
m_DirtyEvents.Add(listEvent);
|
||||
MarkNetworkObjectDirty();
|
||||
OnListChanged?.Invoke(listEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is actually unused left-over from a previous interface
|
||||
/// </summary>
|
||||
public int LastModifiedTick
|
||||
{
|
||||
get
|
||||
@@ -457,6 +512,11 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden <see cref="IDisposable"/> implementation.
|
||||
/// CAUTION: If you derive from this class and override the <see cref="Dispose"/> method,
|
||||
/// you **must** always invoke the base.Dispose() method!
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
m_List.Dispose();
|
||||
|
||||
@@ -8,6 +8,7 @@ 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
|
||||
{
|
||||
@@ -22,7 +23,12 @@ namespace Unity.Netcode
|
||||
/// </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)
|
||||
@@ -31,6 +37,9 @@ namespace Unity.Netcode
|
||||
m_InternalValue = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The internal value of the NetworkVariable
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
private protected T m_InternalValue;
|
||||
|
||||
@@ -58,7 +67,7 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
// Compares two values of the same unmanaged type by underlying memory
|
||||
// Ignoring any overriden value checks
|
||||
// Ignoring any overridden value checks
|
||||
// Size is fixed
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe bool ValueEquals(ref T a, ref T b)
|
||||
@@ -71,10 +80,14 @@ namespace Unity.Netcode
|
||||
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)
|
||||
{
|
||||
m_IsDirty = true;
|
||||
SetDirty(true);
|
||||
T previousValue = m_InternalValue;
|
||||
m_InternalValue = value;
|
||||
OnValueChanged?.Invoke(previousValue, m_InternalValue);
|
||||
@@ -106,7 +119,7 @@ namespace Unity.Netcode
|
||||
|
||||
if (keepDirtyDelta)
|
||||
{
|
||||
m_IsDirty = true;
|
||||
SetDirty(true);
|
||||
}
|
||||
|
||||
OnValueChanged?.Invoke(previousValue, m_InternalValue);
|
||||
|
||||
@@ -12,16 +12,36 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
internal const NetworkDelivery Delivery = NetworkDelivery.ReliableFragmentedSequenced;
|
||||
|
||||
/// <summary>
|
||||
/// Maintains a link to the associated NetworkBehaviour
|
||||
/// </summary>
|
||||
private protected NetworkBehaviour m_NetworkBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the NetworkVariable
|
||||
/// </summary>
|
||||
/// <param name="networkBehaviour">The NetworkBehaviour the NetworkVariable belongs to</param>
|
||||
public void Initialize(NetworkBehaviour networkBehaviour)
|
||||
{
|
||||
m_NetworkBehaviour = networkBehaviour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default read permissions
|
||||
/// </summary>
|
||||
public const NetworkVariableReadPermission DefaultReadPerm = NetworkVariableReadPermission.Everyone;
|
||||
|
||||
/// <summary>
|
||||
/// The default write permissions
|
||||
/// </summary>
|
||||
public const NetworkVariableWritePermission DefaultWritePerm = NetworkVariableWritePermission.Server;
|
||||
|
||||
/// <summary>
|
||||
/// The default constructor for <see cref="NetworkVariableBase"/> that can be used to create a
|
||||
/// custom NetworkVariable.
|
||||
/// </summary>
|
||||
/// <param name="readPerm">the <see cref="NetworkVariableReadPermission"/> access settings</param>
|
||||
/// <param name="writePerm">the <see cref="NetworkVariableWritePermission"/> access settings</param>
|
||||
protected NetworkVariableBase(
|
||||
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
||||
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||
@@ -30,7 +50,11 @@ namespace Unity.Netcode
|
||||
WritePerm = writePerm;
|
||||
}
|
||||
|
||||
private protected bool m_IsDirty;
|
||||
/// <summary>
|
||||
/// The <see cref="m_IsDirty"/> property is used to determine if the
|
||||
/// value of the `NetworkVariable` has changed.
|
||||
/// </summary>
|
||||
private bool m_IsDirty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the network variable's instance
|
||||
@@ -43,14 +67,22 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
public readonly NetworkVariableReadPermission ReadPerm;
|
||||
|
||||
/// <summary>
|
||||
/// The write permission for this var
|
||||
/// </summary>
|
||||
public readonly NetworkVariableWritePermission WritePerm;
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether or not the variable needs to be delta synced
|
||||
/// </summary>
|
||||
/// <param name="isDirty">Whether or not the var is dirty</param>
|
||||
public virtual void SetDirty(bool isDirty)
|
||||
{
|
||||
m_IsDirty = isDirty;
|
||||
if (m_IsDirty && m_NetworkBehaviour != null)
|
||||
{
|
||||
m_NetworkBehaviour.NetworkManager.MarkNetworkObjectDirty(m_NetworkBehaviour.NetworkObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -70,6 +102,11 @@ namespace Unity.Netcode
|
||||
return m_IsDirty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if a specific client has permission to read the var or not
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client id</param>
|
||||
/// <returns>Whether or not the client has permission to read</returns>
|
||||
public bool CanClientRead(ulong clientId)
|
||||
{
|
||||
switch (ReadPerm)
|
||||
@@ -78,10 +115,15 @@ namespace Unity.Netcode
|
||||
case NetworkVariableReadPermission.Everyone:
|
||||
return true;
|
||||
case NetworkVariableReadPermission.Owner:
|
||||
return clientId == m_NetworkBehaviour.NetworkObject.OwnerClientId;
|
||||
return clientId == m_NetworkBehaviour.NetworkObject.OwnerClientId || NetworkManager.ServerClientId == clientId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if a specific client has permission to write the var or not
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client id</param>
|
||||
/// <returns>Whether or not the client has permission to write</returns>
|
||||
public bool CanClientWrite(ulong clientId)
|
||||
{
|
||||
switch (WritePerm)
|
||||
@@ -127,6 +169,9 @@ namespace Unity.Netcode
|
||||
/// <param name="keepDirtyDelta">Whether or not the delta should be kept as dirty or consumed</param>
|
||||
public abstract void ReadDelta(FastBufferReader reader, bool keepDirtyDelta);
|
||||
|
||||
/// <summary>
|
||||
/// Virtual <see cref="IDisposable"/> implementation
|
||||
/// </summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,14 +1,32 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// The permission types for reading a var
|
||||
/// </summary>
|
||||
public enum NetworkVariableReadPermission
|
||||
{
|
||||
/// <summary>
|
||||
/// Everyone can read
|
||||
/// </summary>
|
||||
Everyone,
|
||||
/// <summary>
|
||||
/// Only the owner and the server can read
|
||||
/// </summary>
|
||||
Owner,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The permission types for writing a var
|
||||
/// </summary>
|
||||
public enum NetworkVariableWritePermission
|
||||
{
|
||||
/// <summary>
|
||||
/// Only the server can write
|
||||
/// </summary>
|
||||
Server,
|
||||
/// <summary>
|
||||
/// Only the owner can write
|
||||
/// </summary>
|
||||
Owner
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,10 +120,28 @@ namespace Unity.Netcode
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class UserNetworkVariableSerialization<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The write value delegate handler definition
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="FastBufferWriter"/> to write the value of type `T`</param>
|
||||
/// <param name="value">The value of type `T` to be written</param>
|
||||
public delegate void WriteValueDelegate(FastBufferWriter writer, in T value);
|
||||
|
||||
/// <summary>
|
||||
/// The read value delegate handler definition
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="FastBufferReader"/> to read the value of type `T`</param>
|
||||
/// <param name="value">The value of type `T` to be read</param>
|
||||
public delegate void ReadValueDelegate(FastBufferReader reader, out T value);
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="WriteValueDelegate"/> delegate handler declaration
|
||||
/// </summary>
|
||||
public static WriteValueDelegate WriteValue;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ReadValueDelegate"/> delegate handler declaration
|
||||
/// </summary>
|
||||
public static ReadValueDelegate ReadValue;
|
||||
}
|
||||
|
||||
@@ -192,6 +210,7 @@ namespace Unity.Netcode
|
||||
/// but there's no way to achieve the same thing with a class, this sets up various read/write schemes
|
||||
/// based on which constraints are met by `T` using reflection, which is done at module load time.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type the associated NetworkVariable is templated on</typeparam>
|
||||
[Serializable]
|
||||
public static class NetworkVariableSerialization<T> where T : unmanaged
|
||||
{
|
||||
|
||||
@@ -935,7 +935,7 @@ namespace Unity.Netcode
|
||||
/// Unloads an additively loaded scene. If you want to unload a <see cref="LoadSceneMode.Single"/> mode loaded scene load another <see cref="LoadSceneMode.Single"/> scene.
|
||||
/// When applicable, the <see cref="AsyncOperation"/> is delivered within the <see cref="SceneEvent"/> via the <see cref="OnSceneEvent"/>
|
||||
/// </summary>
|
||||
/// <param name="sceneName">scene name to unload</param>
|
||||
/// <param name="scene"></param>
|
||||
/// <returns><see cref="SceneEventProgressStatus"/> (<see cref="SceneEventProgressStatus.Started"/> means it was successful)</returns>
|
||||
public SceneEventProgressStatus UnloadScene(Scene scene)
|
||||
{
|
||||
@@ -1134,6 +1134,7 @@ namespace Unity.Netcode
|
||||
/// When applicable, the <see cref="AsyncOperation"/> is delivered within the <see cref="SceneEvent"/> via <see cref="OnSceneEvent"/>
|
||||
/// </summary>
|
||||
/// <param name="sceneName">the name of the scene to be loaded</param>
|
||||
/// <param name="loadSceneMode">how the scene will be loaded (single or additive mode)</param>
|
||||
/// <returns><see cref="SceneEventProgressStatus"/> (<see cref="SceneEventProgressStatus.Started"/> means it was successful)</returns>
|
||||
public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSceneMode)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,9 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class to count the number of bytes or bits needed to serialize a value.
|
||||
/// </summary>
|
||||
public static class BitCounter
|
||||
{
|
||||
// Since we don't have access to BitOperations.LeadingZeroCount() (which would have been the fastest)
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace Unity.Netcode
|
||||
|
||||
private const int k_BitsPerByte = 8;
|
||||
|
||||
private int BytePosition => m_BitPosition >> 3;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary.
|
||||
/// </summary>
|
||||
@@ -98,11 +100,6 @@ namespace Unity.Netcode
|
||||
throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 64 bits from a 64-bit value!");
|
||||
}
|
||||
|
||||
if (bitCount < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!");
|
||||
}
|
||||
|
||||
int checkPos = (int)(m_BitPosition + bitCount);
|
||||
if (checkPos > m_AllowedBitwiseReadMark)
|
||||
{
|
||||
@@ -165,7 +162,7 @@ namespace Unity.Netcode
|
||||
#endif
|
||||
|
||||
int offset = m_BitPosition & 7;
|
||||
int pos = m_BitPosition >> 3;
|
||||
int pos = BytePosition;
|
||||
bit = (m_BufferPointer[pos] & (1 << offset)) != 0;
|
||||
++m_BitPosition;
|
||||
}
|
||||
@@ -175,7 +172,7 @@ namespace Unity.Netcode
|
||||
{
|
||||
var val = new T();
|
||||
byte* ptr = ((byte*)&val) + offsetBytes;
|
||||
byte* bufferPointer = m_BufferPointer + m_Position;
|
||||
byte* bufferPointer = m_BufferPointer + BytePosition;
|
||||
UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead);
|
||||
|
||||
m_BitPosition += bytesToRead * k_BitsPerByte;
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace Unity.Netcode
|
||||
get => (m_BitPosition & 7) == 0;
|
||||
}
|
||||
|
||||
private int BytePosition => m_BitPosition >> 3;
|
||||
|
||||
internal unsafe BitWriter(FastBufferWriter writer)
|
||||
{
|
||||
m_Writer = writer;
|
||||
@@ -181,7 +183,7 @@ namespace Unity.Netcode
|
||||
#endif
|
||||
|
||||
int offset = m_BitPosition & 7;
|
||||
int pos = m_BitPosition >> 3;
|
||||
int pos = BytePosition;
|
||||
++m_BitPosition;
|
||||
m_BufferPointer[pos] = (byte)(bit ? (m_BufferPointer[pos] & ~(1 << offset)) | (1 << offset) : (m_BufferPointer[pos] & ~(1 << offset)));
|
||||
}
|
||||
@@ -190,7 +192,7 @@ namespace Unity.Netcode
|
||||
private unsafe void WritePartialValue<T>(T value, int bytesToWrite, int offsetBytes = 0) where T : unmanaged
|
||||
{
|
||||
byte* ptr = ((byte*)&value) + offsetBytes;
|
||||
byte* bufferPointer = m_BufferPointer + m_Position;
|
||||
byte* bufferPointer = m_BufferPointer + BytePosition;
|
||||
UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite);
|
||||
|
||||
m_BitPosition += bytesToWrite * k_BitsPerByte;
|
||||
|
||||
@@ -62,80 +62,507 @@ namespace Unity.Netcode
|
||||
return m_Implementation.GetFastBufferWriter();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a string
|
||||
/// </summary>
|
||||
/// <param name="s">The value to read/write</param>
|
||||
/// <param name="oneByteChars">If true, characters will be limited to one-byte ASCII characters</param>
|
||||
public void SerializeValue(ref string s, bool oneByteChars = false) => m_Implementation.SerializeValue(ref s, oneByteChars);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a single byte
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValue(ref byte value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a primitive value (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of primitive values (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an enum value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of enum values
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a struct value implementing ISerializeByMemcpy
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of struct values implementing ISerializeByMemcpy
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a struct or class value implementing INetworkSerializable
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of struct or class values implementing INetworkSerializable
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Vector2 value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValue(ref Vector2 value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Vector2 values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValue(ref Vector2[] value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Vector3 value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValue(ref Vector3 value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Vector3 values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValue(ref Vector3[] value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Vector2Int value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValue(ref Vector2Int value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Vector2Int values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValue(ref Vector2Int[] value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Vector3Int value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValue(ref Vector3Int value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Vector3Int values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValue(ref Vector3Int[] value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Vector4 value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValue(ref Vector4 value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Vector4 values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValue(ref Vector4[] value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Quaternion value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValue(ref Quaternion value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Quaternion values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValue(ref Quaternion[] value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Color value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValue(ref Color value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Color values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValue(ref Color[] value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Color32 value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValue(ref Color32 value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Color32 values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValue(ref Color32[] value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Ray value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValue(ref Ray value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Ray values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValue(ref Ray[] value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Ray2D value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValue(ref Ray2D value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Ray2D values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValue(ref Ray2D[] value) => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
// There are many FixedString types, but all of them share the interfaces INativeList<bool> and IUTF8Bytes.
|
||||
// INativeList<bool> provides the Length property
|
||||
// IUTF8Bytes provides GetUnsafePtr()
|
||||
// Those two are necessary to serialize FixedStrings efficiently
|
||||
// - otherwise we'd just be memcpying the whole thing even if
|
||||
// - otherwise we'd just be memcpy'ing the whole thing even if
|
||||
// most of it isn't used.
|
||||
/// <summary>
|
||||
/// Read or write a FixedString value
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The network serializable type</typeparam>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution of FixedStrings</param>
|
||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Implementation.SerializeValue(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a NetworkSerializable value.
|
||||
/// SerializeValue() is the preferred method to do this - this is provided for backward compatibility only.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The network serializable type</typeparam>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeNetworkSerializable<T>(ref T value) where T : INetworkSerializable, new() => m_Implementation.SerializeNetworkSerializable(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Performs an advance check to ensure space is available to read/write one or more values.
|
||||
/// This provides a performance benefit for serializing multiple values using the
|
||||
/// SerializeValuePreChecked methods. But note that the benefit is small and only likely to be
|
||||
/// noticeable if serializing a very large number of items.
|
||||
/// </summary>
|
||||
/// <param name="amount"></param>
|
||||
/// <returns></returns>
|
||||
public bool PreCheck(int amount)
|
||||
{
|
||||
return m_Implementation.PreCheck(amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a string, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="s">The value to read/write</param>
|
||||
/// <param name="oneByteChars">If true, characters will be limited to one-byte ASCII characters</param>
|
||||
public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) => m_Implementation.SerializeValuePreChecked(ref s, oneByteChars);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a byte, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref byte value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a primitive, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The network serializable type</typeparam>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize an array of primitives, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The network serializable types in an array</typeparam>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution of primitives</param>
|
||||
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize an enum, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The network serializable type</typeparam>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution of enums</param>
|
||||
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize an array of enums, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The network serializable types in an array</typeparam>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution of enums</param>
|
||||
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a struct, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The network serializable type</typeparam>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution of structs</param>
|
||||
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize an array of structs, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The network serializable types in an array</typeparam>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution of structs</param>
|
||||
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector2, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Vector2 value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector2 array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Vector2[] value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector3, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Vector3 value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector3 array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Vector3[] value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector2Int, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Vector2Int value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector2Int array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Vector2Int[] value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector3Int, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Vector3Int value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector3Int array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Vector3Int[] value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector4, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Vector4 value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector4Array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Vector4[] value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Quaternion, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Quaternion value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Quaternion array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Quaternion[] value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Color, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Color value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Color array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Color[] value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Color32, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Color32 value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Color32 array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Color32[] value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Ray, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Ray value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Ray array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Ray[] value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Ray2D, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Ray2D value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Ray2D array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
public void SerializeValuePreChecked(ref Ray2D[] value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
|
||||
// There are many FixedString types, but all of them share the interfaces INativeList<bool> and IUTF8Bytes.
|
||||
@@ -144,6 +571,16 @@ namespace Unity.Netcode
|
||||
// Those two are necessary to serialize FixedStrings efficiently
|
||||
// - otherwise we'd just be memcpying the whole thing even if
|
||||
// most of it isn't used.
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a FixedString, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The network serializable type</typeparam>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter that can be used for enabling overload resolution for fixed strings</param>
|
||||
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Implementation.SerializeValuePreChecked(ref value);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class for packing values in serialization.
|
||||
/// <seealso cref="ByteUnpacker"/> to unpack packed values.
|
||||
/// </summary>
|
||||
public static class BytePacker
|
||||
{
|
||||
@@ -282,14 +283,49 @@ namespace Unity.Netcode
|
||||
public void WriteValueBitPacked<T>(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value);
|
||||
#else
|
||||
|
||||
/// <summary>
|
||||
/// Maximum serializable value for a BitPacked ushort (minimum for unsigned is 0)
|
||||
/// </summary>
|
||||
public const ushort BitPackedUshortMax = (1 << 15) - 1;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum serializable value for a BitPacked short
|
||||
/// </summary>
|
||||
public const short BitPackedShortMax = (1 << 14) - 1;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum serializable value size for a BitPacked ushort
|
||||
/// </summary>
|
||||
public const short BitPackedShortMin = -(1 << 14);
|
||||
|
||||
/// <summary>
|
||||
/// Maximum serializable value for a BitPacked uint (minimum for unsigned is 0)
|
||||
/// </summary>
|
||||
public const uint BitPackedUintMax = (1 << 30) - 1;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum serializable value for a BitPacked int
|
||||
/// </summary>
|
||||
public const int BitPackedIntMax = (1 << 29) - 1;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum serializable value size for a BitPacked int
|
||||
/// </summary>
|
||||
public const int BitPackedIntMin = -(1 << 29);
|
||||
|
||||
/// <summary>
|
||||
/// Maximum serializable value for a BitPacked ulong (minimum for unsigned is 0)
|
||||
/// </summary>
|
||||
public const ulong BitPackedULongMax = (1L << 61) - 1;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum serializable value for a BitPacked long
|
||||
/// </summary>
|
||||
public const long BitPackedLongMax = (1L << 60) - 1;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum serializable value size for a BitPacked long
|
||||
/// </summary>
|
||||
public const long BitPackedLongMin = -(1L << 60);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,14 +4,26 @@ using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Byte Unpacking Helper Class
|
||||
/// Use this class to unpack values during deserialization for values that were packed.
|
||||
/// <seealso cref="BytePacker"/> to pack unpacked values
|
||||
/// </summary>
|
||||
public static class ByteUnpacker
|
||||
{
|
||||
|
||||
#if UNITY_NETCODE_DEBUG_NO_PACKING
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValuePacked<T>(FastBufferReader reader, out T value) where T: unmanaged => reader.ReadValueSafe(out value);
|
||||
#else
|
||||
/// <summary>
|
||||
/// Read a packed enum value
|
||||
/// </summary>
|
||||
/// <param name="reader">The reader to read from</param>
|
||||
/// <param name="value">The value that's read</param>
|
||||
/// <typeparam name="TEnum">Type of enum to read</typeparam>
|
||||
/// <exception cref="InvalidOperationException">Throws InvalidOperationException if an enum somehow ends up not being the size of a byte, short, int, or long (which should be impossible)</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void ReadValuePacked<TEnum>(FastBufferReader reader, out TEnum value) where TEnum : unmanaged, Enum
|
||||
{
|
||||
@@ -302,7 +314,7 @@ namespace Unity.Netcode
|
||||
#endif
|
||||
|
||||
#if UNITY_NETCODE_DEBUG_NO_PACKING
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueBitPacked<T>(FastBufferReader reader, T value) where T: unmanaged => reader.ReadValueSafe(out value);
|
||||
#else
|
||||
|
||||
@@ -6,6 +6,12 @@ using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Optimized class used for reading values from a byte stream
|
||||
/// <seealso cref="FastBufferWriter"/>
|
||||
/// <seealso cref="BytePacker"/>
|
||||
/// <seealso cref="ByteUnpacker"/>
|
||||
/// </summary>
|
||||
public struct FastBufferReader : IDisposable
|
||||
{
|
||||
internal struct ReaderHandle
|
||||
@@ -87,15 +93,15 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Create a FastBufferReader from a NativeArray.
|
||||
///
|
||||
/// A new buffer will be created using the given <param name="copyAllocator"> and the value will be copied in.
|
||||
/// A new buffer will be created using the given <param name="copyAllocator"></param> and the value will be copied in.
|
||||
/// FastBufferReader will then own the data.
|
||||
///
|
||||
/// The exception to this is when the <param name="copyAllocator"> passed in is Allocator.None. In this scenario,
|
||||
/// The exception to this is when the <param name="copyAllocator"></param> passed in is Allocator.None. In this scenario,
|
||||
/// ownership of the data remains with the caller and the reader will point at it directly.
|
||||
/// When created with Allocator.None, FastBufferReader will allocate some internal data using
|
||||
/// Allocator.Temp so it should be treated as if it's a ref struct and not allowed to outlive
|
||||
/// the context in which it was created (it should neither be returned from that function nor
|
||||
/// stored anywhere in heap memory). This is true, unless the <param name="internalAllocator"> param is explicitly set
|
||||
/// stored anywhere in heap memory). This is true, unless the <param name="internalAllocator"></param> param is explicitly set
|
||||
/// to i.e.: Allocator.Persistent in which case it would allow the internal data to Persist for longer, but the caller
|
||||
/// should manually call Dispose() when it is no longer needed.
|
||||
/// </summary>
|
||||
@@ -162,15 +168,15 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Create a FastBufferReader from an existing byte buffer.
|
||||
///
|
||||
/// A new buffer will be created using the given <param name="copyAllocator"> and the value will be copied in.
|
||||
/// A new buffer will be created using the given <param name="copyAllocator"></param> and the value will be copied in.
|
||||
/// FastBufferReader will then own the data.
|
||||
///
|
||||
/// The exception to this is when the <param name="copyAllocator"> passed in is Allocator.None. In this scenario,
|
||||
/// The exception to this is when the <param name="copyAllocator"></param> passed in is Allocator.None. In this scenario,
|
||||
/// ownership of the data remains with the caller and the reader will point at it directly.
|
||||
/// When created with Allocator.None, FastBufferReader will allocate some internal data using
|
||||
/// Allocator.Temp, so it should be treated as if it's a ref struct and not allowed to outlive
|
||||
/// the context in which it was created (it should neither be returned from that function nor
|
||||
/// stored anywhere in heap memory). This is true, unless the <param name="internalAllocator"> param is explicitly set
|
||||
/// stored anywhere in heap memory). This is true, unless the <param name="internalAllocator"></param> param is explicitly set
|
||||
/// to i.e.: Allocator.Persistent in which case it would allow the internal data to Persist for longer, but the caller
|
||||
/// should manually call Dispose() when it is no longer needed.
|
||||
/// </summary>
|
||||
@@ -187,15 +193,15 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Create a FastBufferReader from a FastBufferWriter.
|
||||
///
|
||||
/// A new buffer will be created using the given <param name="copyAllocator"> and the value will be copied in.
|
||||
/// A new buffer will be created using the given <param name="copyAllocator"></param> and the value will be copied in.
|
||||
/// FastBufferReader will then own the data.
|
||||
///
|
||||
/// The exception to this is when the <param name="copyAllocator"> passed in is Allocator.None. In this scenario,
|
||||
/// The exception to this is when the <param name="copyAllocator"></param> passed in is Allocator.None. In this scenario,
|
||||
/// ownership of the data remains with the caller and the reader will point at it directly.
|
||||
/// When created with Allocator.None, FastBufferReader will allocate some internal data using
|
||||
/// Allocator.Temp, so it should be treated as if it's a ref struct and not allowed to outlive
|
||||
/// the context in which it was created (it should neither be returned from that function nor
|
||||
/// stored anywhere in heap memory). This is true, unless the <param name="internalAllocator"> param is explicitly set
|
||||
/// stored anywhere in heap memory). This is true, unless the <param name="internalAllocator"></param> param is explicitly set
|
||||
/// to i.e.: Allocator.Persistent in which case it would allow the internal data to Persist for longer, but the caller
|
||||
/// should manually call Dispose() when it is no longer needed.
|
||||
/// </summary>
|
||||
@@ -214,10 +220,10 @@ namespace Unity.Netcode
|
||||
/// want to change the copyAllocator that a reader is allocated to - for example, upgrading a Temp reader to
|
||||
/// a Persistent one to be processed later.
|
||||
///
|
||||
/// A new buffer will be created using the given <param name="copyAllocator"> and the value will be copied in.
|
||||
/// A new buffer will be created using the given <param name="copyAllocator"></param> and the value will be copied in.
|
||||
/// FastBufferReader will then own the data.
|
||||
///
|
||||
/// The exception to this is when the <param name="copyAllocator"> passed in is Allocator.None. In this scenario,
|
||||
/// The exception to this is when the <param name="copyAllocator"></param> passed in is Allocator.None. In this scenario,
|
||||
/// ownership of the data remains with the caller and the reader will point at it directly.
|
||||
/// When created with Allocator.None, FastBufferReader will allocate some internal data using
|
||||
/// Allocator.Temp, so it should be treated as if it's a ref struct and not allowed to outlive
|
||||
@@ -235,7 +241,7 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees the allocated buffer
|
||||
/// <see cref="IDisposable"/> implementation that frees the allocated buffer
|
||||
/// </summary>
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
@@ -335,6 +341,7 @@ namespace Unity.Netcode
|
||||
/// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following
|
||||
/// operations in release builds.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">the type `T` of the value you are trying to read</typeparam>
|
||||
/// <param name="value">The value you want to read</param>
|
||||
/// <returns>True if the read is allowed, false otherwise</returns>
|
||||
/// <exception cref="InvalidOperationException">If called while in a bitwise context</exception>
|
||||
@@ -364,7 +371,7 @@ namespace Unity.Netcode
|
||||
/// Differs from TryBeginRead only in that it won't ever move the AllowedReadMark backward.
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <returns></returns>
|
||||
/// <returns>true upon success</returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal unsafe bool TryBeginReadInternal(int bytes)
|
||||
@@ -393,7 +400,7 @@ namespace Unity.Netcode
|
||||
/// Returns an array representation of the underlying byte buffer.
|
||||
/// !!Allocates a new array!!
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <returns>byte array</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe byte[] ToArray()
|
||||
{
|
||||
@@ -408,7 +415,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Gets a direct pointer to the underlying buffer
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <returns><see cref="byte"/> pointer</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe byte* GetUnsafePtr()
|
||||
{
|
||||
@@ -418,7 +425,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Gets a direct pointer to the underlying buffer at the current read position
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <returns><see cref="byte"/> pointer</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe byte* GetUnsafePtrAtCurrentPosition()
|
||||
{
|
||||
@@ -428,8 +435,8 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Read an INetworkSerializable
|
||||
/// </summary>
|
||||
/// <param name="value">INetworkSerializable instance</param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="value">INetworkSerializable instance</param>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public void ReadNetworkSerializable<T>(out T value) where T : INetworkSerializable, new()
|
||||
{
|
||||
@@ -442,7 +449,7 @@ namespace Unity.Netcode
|
||||
/// Read an array of INetworkSerializables
|
||||
/// </summary>
|
||||
/// <param name="value">INetworkSerializable instance</param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="T">the array to read the values of type `T` into</typeparam>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public void ReadNetworkSerializable<T>(out T[] value) where T : INetworkSerializable, new()
|
||||
{
|
||||
@@ -537,7 +544,7 @@ namespace Unity.Netcode
|
||||
/// <param name="value">Value to read</param>
|
||||
/// <param name="bytesToRead">Number of bytes</param>
|
||||
/// <param name="offsetBytes">Offset into the value to write the bytes</param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="T">the type value to read the value into</typeparam>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
/// <exception cref="OverflowException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -738,127 +745,522 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a NetworkSerializable value
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue<T>(out T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a NetworkSerializable array
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The values to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue<T>(out T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a NetworkSerializable value
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a NetworkSerializable array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The values to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read a struct
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue<T>(out T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a struct array
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The values to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue<T>(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a struct
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a struct array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The values to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a primitive value (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue<T>(out T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a primitive value array (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The values to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue<T>(out T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a primitive value (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a primitive value (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read an enum value
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue<T>(out T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read an enum array
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue<T>(out T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanaged(out value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
/// <summary>
|
||||
/// Read an enum value
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanagedSafe(out value);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
/// <summary>
|
||||
/// Read an enum array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The values to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector2
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Vector2 value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector2 array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Vector2[] value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector3
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Vector3 value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector3 array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Vector3[] value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector2Int
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Vector2Int value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector2Int array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Vector2Int[] value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector3Int
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Vector3Int value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector3Int array
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Vector3Int[] value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector4
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Vector4 value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector4
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Vector4[] value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Quaternion
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Quaternion value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Quaternion array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Quaternion[] value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Color
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Color value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Color array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Color[] value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Color32
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Color32 value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Color32 array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Color32[] value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Ray
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Ray value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Ray array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Ray[] value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Ray2D
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Ray2D value) => ReadUnmanaged(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Ray2D array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValue(out Ray2D[] value) => ReadUnmanaged(out value);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector2
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Vector2 value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector2 array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Vector2[] value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector3
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Vector3 value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector3 array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Vector3[] value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector2Int
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Vector2Int value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector2Int array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Vector2Int[] value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector3Int
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Vector3Int value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector3Int array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Vector3Int[] value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector4
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Vector4 value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Vector4 array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Vector4[] value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Quaternion
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Quaternion value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Quaternion array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Quaternion[] value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Color
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Color value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Collor array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Color[] value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Color32
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Color32 value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Color32 array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Color32[] value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Ray
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Ray value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Ray array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Ray[] value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Ray2D
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Ray2D value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
/// <summary>
|
||||
/// Read a Ray2D array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to read</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadValueSafe(out Ray2D[] value) => ReadUnmanagedSafe(out value);
|
||||
|
||||
@@ -868,6 +1270,17 @@ namespace Unity.Netcode
|
||||
// Those two are necessary to serialize FixedStrings efficiently
|
||||
// - otherwise we'd just be memcpying the whole thing even if
|
||||
// most of it isn't used.
|
||||
|
||||
/// <summary>
|
||||
/// Read a FixedString value.
|
||||
/// This method is a little difficult to use, since you have to know the size of the string before
|
||||
/// reading it, but is useful when the string is a known, fixed size. Note that the size of the
|
||||
/// string is also encoded, so the size to call TryBeginRead on is actually the fixed size (in bytes)
|
||||
/// plus sizeof(int)
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe void ReadValue<T>(out T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||
@@ -878,6 +1291,16 @@ namespace Unity.Netcode
|
||||
ReadBytes(value.GetUnsafePtr(), length);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read a FixedString value.
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple reads at once by calling TryBeginRead.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to read</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe void ReadValueSafe<T>(out T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||
|
||||
@@ -6,6 +6,12 @@ using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Optimized class used for writing values into a byte stream
|
||||
/// <seealso cref="FastBufferReader"/>
|
||||
/// <seealso cref="BytePacker"/>
|
||||
/// <seealso cref="ByteUnpacker"/>
|
||||
/// </summary>
|
||||
public struct FastBufferWriter : IDisposable
|
||||
{
|
||||
internal struct WriterHandle
|
||||
@@ -108,7 +114,7 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees the allocated buffer
|
||||
/// <see cref="IDisposable"/> implementation that frees the allocated buffer
|
||||
/// </summary>
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
@@ -267,7 +273,8 @@ namespace Unity.Netcode
|
||||
/// operations in release builds. Instead, attempting to write past the marked position in release builds
|
||||
/// will write to random memory and cause undefined behavior, likely including instability and crashes.
|
||||
/// </summary>
|
||||
/// <param name="value">The value you want to write</param>
|
||||
/// <typeparam name="T">The value type to write</typeparam>
|
||||
/// <param name="value">The value of the type `T` you want to write</param>
|
||||
/// <returns>True if the write is allowed, false otherwise</returns>
|
||||
/// <exception cref="InvalidOperationException">If called while in a bitwise context</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -792,151 +799,590 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
// These structs enable overloading of WriteValue with different generic constraints.
|
||||
// The compiler's actually able to distinguish between overloads based on generic constraints.
|
||||
// But at the bytecode level, the constraints aren't included in the method signature.
|
||||
// By adding a second parameter with a defaulted value, the signatures of each generic are different,
|
||||
// thus allowing overloads of methods based on the first parameter meeting constraints.
|
||||
/// <summary>
|
||||
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
|
||||
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
|
||||
/// methods exist with the same signature, it causes a compile error because they would end up
|
||||
/// being emitted as the same method, even if the constraints are different.
|
||||
/// Adding an empty struct with a default value gives them different signatures in the bytecode,
|
||||
/// which then allows the compiler to do overload resolution based on the generic constraints
|
||||
/// without the user having to pass the struct in themselves.
|
||||
/// </summary>
|
||||
public struct ForPrimitives
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
|
||||
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
|
||||
/// methods exist with the same signature, it causes a compile error because they would end up
|
||||
/// being emitted as the same method, even if the constraints are different.
|
||||
/// Adding an empty struct with a default value gives them different signatures in the bytecode,
|
||||
/// which then allows the compiler to do overload resolution based on the generic constraints
|
||||
/// without the user having to pass the struct in themselves.
|
||||
/// </summary>
|
||||
public struct ForEnums
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
|
||||
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
|
||||
/// methods exist with the same signature, it causes a compile error because they would end up
|
||||
/// being emitted as the same method, even if the constraints are different.
|
||||
/// Adding an empty struct with a default value gives them different signatures in the bytecode,
|
||||
/// which then allows the compiler to do overload resolution based on the generic constraints
|
||||
/// without the user having to pass the struct in themselves.
|
||||
/// </summary>
|
||||
public struct ForStructs
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
|
||||
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
|
||||
/// methods exist with the same signature, it causes a compile error because they would end up
|
||||
/// being emitted as the same method, even if the constraints are different.
|
||||
/// Adding an empty struct with a default value gives them different signatures in the bytecode,
|
||||
/// which then allows the compiler to do overload resolution based on the generic constraints
|
||||
/// without the user having to pass the struct in themselves.
|
||||
/// </summary>
|
||||
public struct ForNetworkSerializable
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
|
||||
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
|
||||
/// methods exist with the same signature, it causes a compile error because they would end up
|
||||
/// being emitted as the same method, even if the constraints are different.
|
||||
/// Adding an empty struct with a default value gives them different signatures in the bytecode,
|
||||
/// which then allows the compiler to do overload resolution based on the generic constraints
|
||||
/// without the user having to pass the struct in themselves.
|
||||
/// </summary>
|
||||
public struct ForFixedStrings
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a NetworkSerializable value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue<T>(in T value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a NetworkSerializable array
|
||||
/// </summary>
|
||||
/// <param name="value">The values to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue<T>(T[] value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a NetworkSerializable value
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe<T>(in T value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a NetworkSerializable array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe<T>(T[] value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a struct
|
||||
/// </summary>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue<T>(in T value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a struct array
|
||||
/// </summary>
|
||||
/// <param name="value">The values to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue<T>(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a struct
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe<T>(in T value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a struct array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe<T>(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a primitive value (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue<T>(in T value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a primitive value array (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue<T>(T[] value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a primitive value (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe<T>(in T value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a primitive value (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe<T>(T[] value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write an enum value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue<T>(in T value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write an enum array
|
||||
/// </summary>
|
||||
/// <param name="value">The values to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue<T>(T[] value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write an enum value
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe<T>(in T value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write an enum array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe<T>(T[] value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector2
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(in Vector2 value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector2 array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(Vector2[] value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector3
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(in Vector3 value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector3 array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(Vector3[] value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector2Int
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(in Vector2Int value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector2Int array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(Vector2Int[] value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector3Int
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(in Vector3Int value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector3Int array
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(Vector3Int[] value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector4
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(in Vector4 value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector4
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(Vector4[] value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Quaternion
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(in Quaternion value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Quaternion array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(Quaternion[] value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Color
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(in Color value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Color array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(Color[] value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Color32
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(in Color32 value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Color32 array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(Color32[] value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Ray
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(in Ray value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Ray array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(Ray[] value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Ray2D
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(in Ray2D value) => WriteUnmanaged(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Ray2D array
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValue(Ray2D[] value) => WriteUnmanaged(value);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector2
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(in Vector2 value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector2 array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(Vector2[] value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector3
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(in Vector3 value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector3 array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(Vector3[] value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector2Int
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(in Vector2Int value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector2Int array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(Vector2Int[] value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector3Int
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(in Vector3Int value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector3Int array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(Vector3Int[] value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector4
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(in Vector4 value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Vector4 array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(Vector4[] value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Quaternion
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(in Quaternion value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Quaternion array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(Quaternion[] value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Color
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(in Color value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Collor array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(Color[] value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Color32
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(in Color32 value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Color32 array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(Color32[] value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Ray
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(in Ray value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Ray array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(Ray[] value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Ray2D
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(in Ray2D value) => WriteUnmanagedSafe(value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Ray2D array
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the values to write</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe(Ray2D[] value) => WriteUnmanagedSafe(value);
|
||||
|
||||
@@ -946,6 +1392,15 @@ namespace Unity.Netcode
|
||||
// Those two are necessary to serialize FixedStrings efficiently
|
||||
// - otherwise we'd just be memcpying the whole thing even if
|
||||
// most of it isn't used.
|
||||
|
||||
/// <summary>
|
||||
/// Write a FixedString value. Writes only the part of the string that's actually used.
|
||||
/// When calling TryBeginWrite, ensure you calculate the write size correctly (preferably by calling
|
||||
/// FastBufferWriter.GetWriteSize())
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe void WriteValue<T>(in T value, ForFixedStrings unused = default)
|
||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||
@@ -960,6 +1415,16 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write a FixedString value. Writes only the part of the string that's actually used.
|
||||
///
|
||||
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||
/// for multiple writes at once by calling TryBeginWrite.
|
||||
/// </summary>
|
||||
/// <param name="value">the value to write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueSafe<T>(in T value, ForFixedStrings unused = default)
|
||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||
|
||||
@@ -9,26 +9,58 @@ namespace Unity.Netcode
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public struct ForceNetworkSerializeByMemcpy<T> : INetworkSerializeByMemcpy, IEquatable<ForceNetworkSerializeByMemcpy<T>> where T : unmanaged, IEquatable<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The wrapped value
|
||||
/// </summary>
|
||||
public T Value;
|
||||
|
||||
/// <summary>
|
||||
/// The default constructor for <see cref="ForceNetworkSerializeByMemcpy{T}"/>
|
||||
/// </summary>
|
||||
/// <param name="value">sets the initial value of type `T`</param>
|
||||
public ForceNetworkSerializeByMemcpy(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert implicitly from the ForceNetworkSerializeByMemcpy wrapper to the underlying value
|
||||
/// </summary>
|
||||
/// <param name="container">The wrapper</param>
|
||||
/// <returns>The underlying value</returns>
|
||||
public static implicit operator T(ForceNetworkSerializeByMemcpy<T> container) => container.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Convert implicitly from a T value to a ForceNetworkSerializeByMemcpy wrapper
|
||||
/// </summary>
|
||||
/// <param name="underlyingValue">the value</param>
|
||||
/// <returns>a new wrapper</returns>
|
||||
public static implicit operator ForceNetworkSerializeByMemcpy<T>(T underlyingValue) => new ForceNetworkSerializeByMemcpy<T> { Value = underlyingValue };
|
||||
|
||||
/// <summary>
|
||||
/// Check if wrapped values are equal
|
||||
/// </summary>
|
||||
/// <param name="other">Other wrapper</param>
|
||||
/// <returns>true if equal</returns>
|
||||
public bool Equals(ForceNetworkSerializeByMemcpy<T> other)
|
||||
{
|
||||
return Value.Equals(other.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this value is equal to a boxed object value
|
||||
/// </summary>
|
||||
/// <param name="obj">The boxed value to check against</param>
|
||||
/// <returns>true if equal</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ForceNetworkSerializeByMemcpy<T> other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtains the wrapped value's hash code
|
||||
/// </summary>
|
||||
/// <returns>Wrapped value's hash code</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Unity.Netcode
|
||||
/// by memcpy. It's up to the developer of the struct to analyze the struct's contents and ensure it
|
||||
/// is actually serializable by memcpy. This requires all of the members of the struct to be
|
||||
/// `unmanaged` Plain-Old-Data values - if your struct contains a pointer (or a type that contains a pointer,
|
||||
/// like `NativeList<T>`), it should be serialized via `INetworkSerializable` or via
|
||||
/// like `NativeList<T>`), it should be serialized via `INetworkSerializable` or via
|
||||
/// `FastBufferReader`/`FastBufferWriter` extension methods.
|
||||
/// </summary>
|
||||
public interface INetworkSerializeByMemcpy
|
||||
|
||||
@@ -4,81 +4,536 @@ using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for an implementation of one side of a two-way serializer
|
||||
/// </summary>
|
||||
public interface IReaderWriter
|
||||
{
|
||||
/// <summary>
|
||||
/// Check whether this implementation is a "reader" - if it's been constructed to deserialize data
|
||||
/// </summary>
|
||||
bool IsReader { get; }
|
||||
/// <summary>
|
||||
/// Check whether this implementation is a "writer" - if it's been constructed to serialize data
|
||||
/// </summary>
|
||||
bool IsWriter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the underlying FastBufferReader struct.
|
||||
/// Only valid when IsReader == true
|
||||
/// </summary>
|
||||
/// <returns>underlying FastBufferReader</returns>
|
||||
FastBufferReader GetFastBufferReader();
|
||||
/// <summary>
|
||||
/// Get the underlying FastBufferWriter struct.
|
||||
/// Only valid when IsWriter == true
|
||||
/// </summary>
|
||||
/// <returns>underlying FastBufferWriter</returns>
|
||||
FastBufferWriter GetFastBufferWriter();
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a string
|
||||
/// </summary>
|
||||
/// <param name="s">The value to read/write</param>
|
||||
/// <param name="oneByteChars">If true, characters will be limited to one-byte ASCII characters</param>
|
||||
void SerializeValue(ref string s, bool oneByteChars = false);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a single byte
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValue(ref byte value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a primitive value (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
void SerializeValue<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of primitive values (int, bool, etc)
|
||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||
/// on values that are not primitives.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an enum value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of enum values
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a struct value implementing ISerializeByMemcpy
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of struct values implementing ISerializeByMemcpy
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a struct or class value implementing INetworkSerializable
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new();
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of struct or class values implementing INetworkSerializable
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new();
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a FixedString value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes;
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Vector2 value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValue(ref Vector2 value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Vector2 values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValue(ref Vector2[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Vector3 value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValue(ref Vector3 value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Vector3 values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValue(ref Vector3[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Vector2Int value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValue(ref Vector2Int value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Vector2Int values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValue(ref Vector2Int[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Vector3Int value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValue(ref Vector3Int value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Vector3Int values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValue(ref Vector3Int[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Vector4 value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValue(ref Vector4 value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Vector4 values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValue(ref Vector4[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Quaternion value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValue(ref Quaternion value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Quaternion values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValue(ref Quaternion[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Color value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValue(ref Color value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Color values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValue(ref Color[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Color32 value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValue(ref Color32 value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Color32 values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValue(ref Color32[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Ray value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValue(ref Ray value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Ray values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValue(ref Ray[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write a Ray2D value
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValue(ref Ray2D value);
|
||||
|
||||
/// <summary>
|
||||
/// Read or write an array of Ray2D values
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValue(ref Ray2D[] value);
|
||||
|
||||
// Has to have a different name to avoid conflicting with "where T: unmananged"
|
||||
/// <summary>
|
||||
/// Read or write a NetworkSerializable value.
|
||||
/// SerializeValue() is the preferred method to do this - this is provided for backward compatibility only.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <typeparam name="T">The network serializable type</typeparam>
|
||||
void SerializeNetworkSerializable<T>(ref T value) where T : INetworkSerializable, new();
|
||||
|
||||
/// <summary>
|
||||
/// Performs an advance check to ensure space is available to read/write one or more values.
|
||||
/// This provides a performance benefit for serializing multiple values using the
|
||||
/// SerializeValuePreChecked methods. But note that the benefit is small and only likely to be
|
||||
/// noticeable if serializing a very large number of items.
|
||||
/// </summary>
|
||||
/// <param name="amount"></param>
|
||||
/// <returns></returns>
|
||||
bool PreCheck(int amount);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a string, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="s">The value to read/write</param>
|
||||
/// <param name="oneByteChars">If true, characters will be limited to one-byte ASCII characters</param>
|
||||
void SerializeValuePreChecked(ref string s, bool oneByteChars = false);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a byte, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref byte value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a primitive, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
|
||||
void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Serialize an array of primitives, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
|
||||
void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Serialize an enum, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
|
||||
void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
|
||||
|
||||
/// <summary>
|
||||
/// Serialize an array of enums, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
|
||||
void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a struct, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
|
||||
void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
|
||||
|
||||
/// <summary>
|
||||
/// Serialize an array of structs, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
|
||||
void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a FixedString, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type being serialized</typeparam>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
|
||||
void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes;
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector2, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Vector2 value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector2 array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValuePreChecked(ref Vector2[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector3, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Vector3 value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector3 array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValuePreChecked(ref Vector3[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector2Int, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Vector2Int value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector2Int array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The values to read/write</param>
|
||||
void SerializeValuePreChecked(ref Vector2Int[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector3Int, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Vector3Int value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector3Int array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Vector3Int[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector4, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Vector4 value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Vector4Array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Vector4[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Quaternion, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Quaternion value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Quaternion array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Quaternion[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Color, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Color value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Color array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Color[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Color32, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Color32 value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Color32 array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Color32[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Ray, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Ray value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Ray array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Ray[] value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Ray2D, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Ray2D value);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a Ray2D array, "pre-checked", which skips buffer checks.
|
||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to read/write</param>
|
||||
void SerializeValuePreChecked(ref Ray2D[] value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +96,18 @@ namespace Unity.Netcode
|
||||
serializer.SerializeValue(ref m_NetworkBehaviourId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly convert <see cref="NetworkBehaviourReference"/> to <see cref="NetworkBehaviour"/>.
|
||||
/// </summary>
|
||||
/// <param name="networkBehaviourRef">The <see cref="NetworkBehaviourReference"/> to convert from.</param>
|
||||
/// <returns>The <see cref="NetworkBehaviour"/> this class is holding a reference to</returns>
|
||||
public static implicit operator NetworkBehaviour(NetworkBehaviourReference networkBehaviourRef) => GetInternal(networkBehaviourRef);
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly convert <see cref="NetworkBehaviour"/> to <see cref="NetworkBehaviourReference"/>.
|
||||
/// </summary>
|
||||
/// <param name="networkBehaviour">The <see cref="NetworkBehaviour"/> to convert from.</param>
|
||||
/// <returns>The <see cref="NetworkBehaviourReference"/> created from the <see cref="NetworkBehaviour"/> passed in as a parameter</returns>
|
||||
public static implicit operator NetworkBehaviourReference(NetworkBehaviour networkBehaviour) => new NetworkBehaviourReference(networkBehaviour);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,12 +120,32 @@ namespace Unity.Netcode
|
||||
serializer.SerializeValue(ref m_NetworkObjectId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly convert <see cref="NetworkObjectReference"/> to <see cref="NetworkObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="networkObjectRef">The <see cref="NetworkObjectReference"/> to convert from.</param>
|
||||
/// <returns>The <see cref="NetworkObject"/> the <see cref="NetworkObjectReference"/> is referencing</returns>
|
||||
public static implicit operator NetworkObject(NetworkObjectReference networkObjectRef) => Resolve(networkObjectRef);
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly convert <see cref="NetworkObject"/> to <see cref="NetworkObjectReference"/>.
|
||||
/// </summary>
|
||||
/// <param name="networkObject">The <see cref="NetworkObject"/> to convert from.</param>
|
||||
/// <returns>The <see cref="NetworkObjectReference"/> created from the <see cref="NetworkObject"/> parameter</returns>
|
||||
public static implicit operator NetworkObjectReference(NetworkObject networkObject) => new NetworkObjectReference(networkObject);
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly convert <see cref="NetworkObjectReference"/> to <see cref="GameObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="networkObjectRef">The <see cref="NetworkObjectReference"/> to convert from.</param>
|
||||
/// <returns>This returns the <see cref="GameObject"/> that the <see cref="NetworkObject"/> is attached to and is referenced by the <see cref="NetworkObjectReference"/> passed in as a parameter</returns>
|
||||
public static implicit operator GameObject(NetworkObjectReference networkObjectRef) => Resolve(networkObjectRef).gameObject;
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly convert <see cref="GameObject"/> to <see cref="NetworkObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The <see cref="GameObject"/> to convert from.</param>
|
||||
/// <returns>The <see cref="NetworkObjectReference"/> created from the <see cref="GameObject"/> parameter that has a <see cref="NetworkObject"/> component attached to it</returns>
|
||||
public static implicit operator NetworkObjectReference(GameObject gameObject) => new NetworkObjectReference(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ namespace Unity.Netcode
|
||||
/// Returns a list of all NetworkObjects that belong to a client.
|
||||
/// </summary>
|
||||
/// <param name="clientId">the client's id <see cref="NetworkManager.LocalClientId"/></param>
|
||||
/// <returns>returns the list of <see cref="NetworkObject"/>s owned by the client</returns>
|
||||
public List<NetworkObject> GetClientOwnedObjects(ulong clientId)
|
||||
{
|
||||
if (!OwnershipToObjectsTable.ContainsKey(clientId))
|
||||
@@ -172,9 +173,11 @@ namespace Unity.Netcode
|
||||
return GetPlayerNetworkObject(NetworkManager.LocalClientId);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the player object with a given clientId or null if one does not exist. This is only valid server side.
|
||||
/// </summary>
|
||||
/// <param name="clientId">the client identifier of the player</param>
|
||||
/// <returns>The player object with a given clientId or null if one does not exist</returns>
|
||||
public NetworkObject GetPlayerNetworkObject(ulong clientId)
|
||||
{
|
||||
@@ -573,7 +576,7 @@ namespace Unity.Netcode
|
||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, clientId);
|
||||
NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject, size);
|
||||
|
||||
networkObject.MarkVariablesDirty();
|
||||
networkObject.MarkVariablesDirty(true);
|
||||
}
|
||||
|
||||
internal ulong? GetSpawnParentId(NetworkObject networkObject)
|
||||
|
||||
@@ -3,6 +3,10 @@ using Unity.Profiling;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides discretized time.
|
||||
/// This is useful for games that require ticks happening at regular interval on the server and clients.
|
||||
/// </summary>
|
||||
public class NetworkTickSystem
|
||||
{
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
@@ -69,6 +73,8 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Called after advancing the time system to run ticks based on the difference in time.
|
||||
/// </summary>
|
||||
/// <param name="localTimeSec">The local time in seconds</param>
|
||||
/// <param name="serverTimeSec">The server time in seconds</param>
|
||||
public void UpdateTick(double localTimeSec, double serverTimeSec)
|
||||
{
|
||||
// store old local tick to know how many fixed ticks passed
|
||||
|
||||
@@ -108,6 +108,11 @@ namespace Unity.Netcode
|
||||
return new NetworkTime(m_TickRate, m_CachedTick);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the time a number of ticks in the past.
|
||||
/// </summary>
|
||||
/// <param name="ticks">The number of ticks ago we're querying the time</param>
|
||||
/// <returns></returns>
|
||||
public NetworkTime TimeTicksAgo(int ticks)
|
||||
{
|
||||
return this - new NetworkTime(TickRate, ticks);
|
||||
@@ -132,16 +137,34 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the time difference between two ticks
|
||||
/// </summary>
|
||||
/// <param name="a">End time</param>
|
||||
/// <param name="b">Start time</param>
|
||||
/// <returns>The time difference between start and end</returns>
|
||||
public static NetworkTime operator -(NetworkTime a, NetworkTime b)
|
||||
{
|
||||
return new NetworkTime(a.TickRate, a.Time - b.Time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the sum of two times
|
||||
/// </summary>
|
||||
/// <param name="a">First time</param>
|
||||
/// <param name="b">Second time</param>
|
||||
/// <returns>The sum of the two times passed in</returns>
|
||||
public static NetworkTime operator +(NetworkTime a, NetworkTime b)
|
||||
{
|
||||
return new NetworkTime(a.TickRate, a.Time + b.Time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the time a number of seconds later
|
||||
/// </summary>
|
||||
/// <param name="a">The start time</param>
|
||||
/// <param name="b">The number of seconds to add</param>
|
||||
/// <returns>The resulting time</returns>
|
||||
public static NetworkTime operator +(NetworkTime a, double b)
|
||||
{
|
||||
a.m_TimeSec += b;
|
||||
@@ -149,6 +172,12 @@ namespace Unity.Netcode
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the time a number of seconds before
|
||||
/// </summary>
|
||||
/// <param name="a">The start time</param>
|
||||
/// <param name="b">The number of seconds to remove</param>
|
||||
/// <returns>The resulting time</returns>
|
||||
public static NetworkTime operator -(NetworkTime a, double b)
|
||||
{
|
||||
return a + -b;
|
||||
|
||||
@@ -36,12 +36,27 @@ namespace Unity.Netcode
|
||||
/// Gets or sets the ratio at which the NetworkTimeSystem speeds up or slows down time.
|
||||
/// </summary>
|
||||
public double AdjustmentRatio { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current local time with the local time offset applied
|
||||
/// </summary>
|
||||
public double LocalTime => m_TimeSec + m_CurrentLocalTimeOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The current server time with the server time offset applied
|
||||
/// </summary>
|
||||
public double ServerTime => m_TimeSec + m_CurrentServerTimeOffset;
|
||||
|
||||
internal double LastSyncedServerTimeSec { get; private set; }
|
||||
internal double LastSyncedRttSec { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The constructor class for <see cref="NetworkTickSystem"/>
|
||||
/// </summary>
|
||||
/// <param name="localBufferSec">The amount of time, in seconds, the server should buffer incoming client messages.</param>
|
||||
/// <param name="serverBufferSec">The amount of the time in seconds the client should buffer incoming messages from the server.</param>
|
||||
/// <param name="hardResetThresholdSec">The threshold, in seconds, used to force a hard catchup of network time.</param>
|
||||
/// <param name="adjustmentRatio">The ratio at which the NetworkTimeSystem speeds up or slows down time.</param>
|
||||
public NetworkTimeSystem(double localBufferSec, double serverBufferSec, double hardResetThresholdSec, double adjustmentRatio = 0.01d)
|
||||
{
|
||||
LocalBufferSec = localBufferSec;
|
||||
|
||||
@@ -3,6 +3,11 @@ using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// The generic transport class all Netcode for GameObjects network transport implementations
|
||||
/// derive from. Use this class to add a custom transport.
|
||||
/// <seealso cref="Transports.UTP.UnityTransport"> for an example of how a transport is integrated</seealso>
|
||||
/// </summary>
|
||||
public abstract class NetworkTransport : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
@@ -45,7 +50,7 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a payload to the specified clientId, data and channelName.
|
||||
/// Send a payload to the specified clientId, data and networkDelivery.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The clientId to send to</param>
|
||||
/// <param name="payload">The data to send</param>
|
||||
@@ -64,11 +69,13 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Connects client to the server
|
||||
/// </summary>
|
||||
/// <returns>Returns success or failure</returns>
|
||||
public abstract bool StartClient();
|
||||
|
||||
/// <summary>
|
||||
/// Starts to listening for incoming clients
|
||||
/// </summary>
|
||||
/// <returns>Returns success or failure</returns>
|
||||
public abstract bool StartServer();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
namespace Unity.Netcode.Transports.UTP
|
||||
{
|
||||
/// <summary>
|
||||
/// Caching structure to track network metrics related information.
|
||||
/// </summary>
|
||||
public struct NetworkMetricsContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of packet sent.
|
||||
/// </summary>
|
||||
public uint PacketSentCount;
|
||||
/// <summary>
|
||||
/// The number of packet received.
|
||||
/// </summary>
|
||||
public uint PacketReceivedCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,14 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// </summary>
|
||||
public interface INetworkStreamDriverConstructor
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the internal NetworkDriver
|
||||
/// </summary>
|
||||
/// <param name="transport">The owner transport</param>
|
||||
/// <param name="driver">The driver</param>
|
||||
/// <param name="unreliableFragmentedPipeline">The UnreliableFragmented NetworkPipeline</param>
|
||||
/// <param name="unreliableSequencedFragmentedPipeline">The UnreliableSequencedFragmented NetworkPipeline</param>
|
||||
/// <param name="reliableSequencedPipeline">The ReliableSequenced NetworkPipeline</param>
|
||||
void CreateDriver(
|
||||
UnityTransport transport,
|
||||
out NetworkDriver driver,
|
||||
@@ -24,6 +32,9 @@ namespace Unity.Netcode.Transports.UTP
|
||||
out NetworkPipeline reliableSequencedPipeline);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper utility class to convert <see cref="Networking.Transport"/> error codes to human readable error messages.
|
||||
/// </summary>
|
||||
public static class ErrorUtilities
|
||||
{
|
||||
private const string k_NetworkSuccess = "Success";
|
||||
@@ -37,6 +48,12 @@ namespace Unity.Netcode.Transports.UTP
|
||||
private const string k_NetworkSendHandleInvalid = "Invalid NetworkInterface Send Handle. Likely caused by pipeline send data corruption.";
|
||||
private const string k_NetworkArgumentMismatch = "Invalid NetworkEndpoint Arguments.";
|
||||
|
||||
/// <summary>
|
||||
/// Convert error code to human readable error message.
|
||||
/// </summary>
|
||||
/// <param name="error">Status code of the error</param>
|
||||
/// <param name="connectionId">Subject connection ID of the error</param>
|
||||
/// <returns>Human readable error message.</returns>
|
||||
public static string ErrorToString(Networking.Transport.Error.StatusCode error, ulong connectionId)
|
||||
{
|
||||
switch (error)
|
||||
@@ -67,11 +84,24 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Netcode for GameObjects NetworkTransport for UnityTransport.
|
||||
/// Note: This is highly recommended to use over UNet.
|
||||
/// </summary>
|
||||
public partial class UnityTransport : NetworkTransport, INetworkStreamDriverConstructor
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum type stating the type of protocol
|
||||
/// </summary>
|
||||
public enum ProtocolType
|
||||
{
|
||||
/// <summary>
|
||||
/// Unity Transport Protocol
|
||||
/// </summary>
|
||||
UnityTransport,
|
||||
/// <summary>
|
||||
/// Unity Transport Protocol over Relay
|
||||
/// </summary>
|
||||
RelayUnityTransport,
|
||||
}
|
||||
|
||||
@@ -82,15 +112,34 @@ namespace Unity.Netcode.Transports.UTP
|
||||
Connected,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default maximum (receive) packet queue size
|
||||
/// </summary>
|
||||
public const int InitialMaxPacketQueueSize = 128;
|
||||
|
||||
/// <summary>
|
||||
/// The default maximum payload size
|
||||
/// </summary>
|
||||
public const int InitialMaxPayloadSize = 6 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// The default maximum send queue size
|
||||
/// </summary>
|
||||
public const int InitialMaxSendQueueSize = 16 * InitialMaxPayloadSize;
|
||||
|
||||
private static ConnectionAddressData s_DefaultConnectionAddressData = new ConnectionAddressData { Address = "127.0.0.1", Port = 7777, ServerListenAddress = string.Empty };
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
/// <summary>
|
||||
/// The global <see cref="INetworkStreamDriverConstructor"/> implementation
|
||||
/// </summary>
|
||||
public static INetworkStreamDriverConstructor s_DriverConstructor;
|
||||
#pragma warning restore IDE1006 // Naming Styles
|
||||
|
||||
/// <summary>
|
||||
/// Returns either the global <see cref="INetworkStreamDriverConstructor"/> implementation or the current <see cref="UnityTransport"/> instance
|
||||
/// </summary>
|
||||
public INetworkStreamDriverConstructor DriverConstructor => s_DriverConstructor ?? this;
|
||||
|
||||
[Tooltip("Which protocol should be selected (Relay/Non-Relay).")]
|
||||
@@ -109,11 +158,11 @@ namespace Unity.Netcode.Transports.UTP
|
||||
set => m_MaxPacketQueueSize = value;
|
||||
}
|
||||
|
||||
[Tooltip("The maximum size of a payload that can be handled by the transport.")]
|
||||
[Tooltip("The maximum size of an unreliable payload that can be handled by the transport.")]
|
||||
[SerializeField]
|
||||
private int m_MaxPayloadSize = InitialMaxPayloadSize;
|
||||
|
||||
/// <summary>The maximum size of a payload that can be handled by the transport.</summary>
|
||||
/// <summary>The maximum size of an unreliable payload that can be handled by the transport.</summary>
|
||||
public int MaxPayloadSize
|
||||
{
|
||||
get => m_MaxPayloadSize;
|
||||
@@ -187,17 +236,29 @@ namespace Unity.Netcode.Transports.UTP
|
||||
set => m_DisconnectTimeoutMS = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Structure to store the address to connect to
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct ConnectionAddressData
|
||||
{
|
||||
/// <summary>
|
||||
/// IP address of the server (address to which clients will connect to).
|
||||
/// </summary>
|
||||
[Tooltip("IP address of the server (address to which clients will connect to).")]
|
||||
[SerializeField]
|
||||
public string Address;
|
||||
|
||||
/// <summary>
|
||||
/// UDP port of the server.
|
||||
/// </summary>
|
||||
[Tooltip("UDP port of the server.")]
|
||||
[SerializeField]
|
||||
public ushort Port;
|
||||
|
||||
/// <summary>
|
||||
/// IP address the server will listen on. If not provided, will use 'Address'.
|
||||
/// </summary>
|
||||
[Tooltip("IP address the server will listen on. If not provided, will use 'Address'.")]
|
||||
[SerializeField]
|
||||
public string ServerListenAddress;
|
||||
@@ -213,29 +274,58 @@ namespace Unity.Netcode.Transports.UTP
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Endpoint (IP address and port) clients will connect to.
|
||||
/// </summary>
|
||||
public NetworkEndPoint ServerEndPoint => ParseNetworkEndpoint(Address, Port);
|
||||
|
||||
/// <summary>
|
||||
/// Endpoint (IP address and port) server will listen/bind on.
|
||||
/// </summary>
|
||||
public NetworkEndPoint ListenEndPoint => ParseNetworkEndpoint((ServerListenAddress == string.Empty) ? Address : ServerListenAddress, Port);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The connection (address) data for this <see cref="UnityTransport"/> instance.
|
||||
/// This is where you can change IP Address, Port, or server's listen address.
|
||||
/// <see cref="ConnectionAddressData"/>
|
||||
/// </summary>
|
||||
public ConnectionAddressData ConnectionData = s_DefaultConnectionAddressData;
|
||||
|
||||
/// <summary>
|
||||
/// Parameters for the Network Simulator
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct SimulatorParameters
|
||||
{
|
||||
/// <summary>
|
||||
/// Delay to add to every send and received packet (in milliseconds). Only applies in the editor and in development builds. The value is ignored in production builds.
|
||||
/// </summary>
|
||||
[Tooltip("Delay to add to every send and received packet (in milliseconds). Only applies in the editor and in development builds. The value is ignored in production builds.")]
|
||||
[SerializeField]
|
||||
public int PacketDelayMS;
|
||||
|
||||
/// <summary>
|
||||
/// Jitter (random variation) to add/substract to the packet delay (in milliseconds). Only applies in the editor and in development builds. The value is ignored in production builds.
|
||||
/// </summary>
|
||||
[Tooltip("Jitter (random variation) to add/substract to the packet delay (in milliseconds). Only applies in the editor and in development builds. The value is ignored in production builds.")]
|
||||
[SerializeField]
|
||||
public int PacketJitterMS;
|
||||
|
||||
/// <summary>
|
||||
/// Percentage of sent and received packets to drop. Only applies in the editor and in the editor and in developments builds.
|
||||
/// </summary>
|
||||
[Tooltip("Percentage of sent and received packets to drop. Only applies in the editor and in the editor and in developments builds.")]
|
||||
[SerializeField]
|
||||
public int PacketDropRate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be used to simulate poor network conditions such as:
|
||||
/// - packet delay/latency
|
||||
/// - packet jitter (variances in latency, see: https://en.wikipedia.org/wiki/Jitter)
|
||||
/// - packet drop rate (packet loss)
|
||||
/// </summary>
|
||||
public SimulatorParameters DebugSimulator = new SimulatorParameters
|
||||
{
|
||||
PacketDelayMS = 0,
|
||||
@@ -261,8 +351,14 @@ namespace Unity.Netcode.Transports.UTP
|
||||
private NetworkPipeline m_UnreliableSequencedFragmentedPipeline;
|
||||
private NetworkPipeline m_ReliableSequencedPipeline;
|
||||
|
||||
/// <summary>
|
||||
/// The client id used to represent the server.
|
||||
/// </summary>
|
||||
public override ulong ServerClientId => m_ServerClientId;
|
||||
|
||||
/// <summary>
|
||||
/// The current ProtocolType used by the transport
|
||||
/// </summary>
|
||||
public ProtocolType Protocol => m_ProtocolType;
|
||||
|
||||
private RelayServerData m_RelayServerData;
|
||||
@@ -428,6 +524,14 @@ namespace Unity.Netcode.Transports.UTP
|
||||
m_ProtocolType = inProtocol;
|
||||
}
|
||||
|
||||
/// <summary>Set the relay server data for the server.</summary>
|
||||
/// <param name="ipv4Address">IP address of the relay server.</param>
|
||||
/// <param name="port">UDP port of the relay server.</param>
|
||||
/// <param name="allocationIdBytes">Allocation ID as a byte array.</param>
|
||||
/// <param name="keyBytes">Allocation key as a byte array.</param>
|
||||
/// <param name="connectionDataBytes">Connection data as a byte array.</param>
|
||||
/// <param name="hostConnectionDataBytes">The HostConnectionData as a byte array.</param>
|
||||
/// <param name="isSecure">Whether the connection is secure (uses DTLS).</param>
|
||||
public void SetRelayServerData(string ipv4Address, ushort port, byte[] allocationIdBytes, byte[] keyBytes, byte[] connectionDataBytes, byte[] hostConnectionDataBytes = null, bool isSecure = false)
|
||||
{
|
||||
RelayConnectionData hostConnectionData;
|
||||
@@ -489,6 +593,9 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// <summary>
|
||||
/// Sets IP and Port information. This will be ignored if using the Unity Relay and you should call <see cref="SetRelayServerData"/>
|
||||
/// </summary>
|
||||
/// <param name="ipv4Address">The remote IP address</param>
|
||||
/// <param name="port">The remote port</param>
|
||||
/// <param name="listenAddress">The local listen address</param>
|
||||
public void SetConnectionData(string ipv4Address, ushort port, string listenAddress = null)
|
||||
{
|
||||
ConnectionData = new ConnectionAddressData
|
||||
@@ -504,6 +611,8 @@ namespace Unity.Netcode.Transports.UTP
|
||||
/// <summary>
|
||||
/// Sets IP and Port information. This will be ignored if using the Unity Relay and you should call <see cref="SetRelayServerData"/>
|
||||
/// </summary>
|
||||
/// <param name="endPoint">The remote end point</param>
|
||||
/// <param name="listenEndPoint">The local listen endpoint</param>
|
||||
public void SetConnectionData(NetworkEndPoint endPoint, NetworkEndPoint listenEndPoint = default)
|
||||
{
|
||||
string serverAddress = endPoint.Address.Split(':')[0];
|
||||
@@ -916,6 +1025,9 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects the local client from the remote
|
||||
/// </summary>
|
||||
public override void DisconnectLocalClient()
|
||||
{
|
||||
if (m_State == State.Connected)
|
||||
@@ -940,6 +1052,10 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects a remote client from the server
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client to disconnect</param>
|
||||
public override void DisconnectRemoteClient(ulong clientId)
|
||||
{
|
||||
Debug.Assert(m_State == State.Listening, "DisconnectRemoteClient should be called on a listening server");
|
||||
@@ -959,6 +1075,11 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current RTT for a specific client
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client RTT to get</param>
|
||||
/// <returns>The RTT</returns>
|
||||
public override ulong GetCurrentRtt(ulong clientId)
|
||||
{
|
||||
// We don't know if this is getting called from inside NGO (which presumably knows to
|
||||
@@ -979,6 +1100,10 @@ namespace Unity.Netcode.Transports.UTP
|
||||
return (ulong)ExtractRtt(ParseClientId(clientId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the transport
|
||||
/// </summary>
|
||||
/// <param name="networkManager">The NetworkManager that initialized and owns the transport</param>
|
||||
public override void Initialize(NetworkManager networkManager = null)
|
||||
{
|
||||
Debug.Assert(sizeof(ulong) == UnsafeUtility.SizeOf<NetworkConnection>(), "Netcode connection id size does not match UTP connection id size");
|
||||
@@ -1000,6 +1125,13 @@ namespace Unity.Netcode.Transports.UTP
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Polls for incoming events, with an extra output parameter to report the precise time the event was received.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The clientId this event is for</param>
|
||||
/// <param name="payload">The incoming data payload</param>
|
||||
/// <param name="receiveTime">The time the event was received, as reported by Time.realtimeSinceStartup.</param>
|
||||
/// <returns>Returns the event type</returns>
|
||||
public override NetcodeNetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte> payload, out float receiveTime)
|
||||
{
|
||||
clientId = default;
|
||||
@@ -1008,16 +1140,22 @@ namespace Unity.Netcode.Transports.UTP
|
||||
return NetcodeNetworkEvent.Nothing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a payload to the specified clientId, data and networkDelivery.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The clientId to send to</param>
|
||||
/// <param name="payload">The data to send</param>
|
||||
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
|
||||
public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDelivery networkDelivery)
|
||||
{
|
||||
if (payload.Count > m_MaxPayloadSize)
|
||||
var pipeline = SelectSendPipeline(networkDelivery);
|
||||
|
||||
if (pipeline != m_ReliableSequencedPipeline && payload.Count > m_MaxPayloadSize)
|
||||
{
|
||||
Debug.LogError($"Payload of size {payload.Count} larger than configured 'Max Payload Size' ({m_MaxPayloadSize}).");
|
||||
Debug.LogError($"Unreliable payload of size {payload.Count} larger than configured 'Max Payload Size' ({m_MaxPayloadSize}).");
|
||||
return;
|
||||
}
|
||||
|
||||
var pipeline = SelectSendPipeline(networkDelivery);
|
||||
|
||||
var sendTarget = new SendTarget(clientId, pipeline);
|
||||
if (!m_SendQueue.TryGetValue(sendTarget, out var queue))
|
||||
{
|
||||
@@ -1069,6 +1207,14 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects client to the server
|
||||
/// Note:
|
||||
/// When this method returns false it could mean:
|
||||
/// - You are trying to start a client that is already started
|
||||
/// - It failed during the initial port binding when attempting to begin to connect
|
||||
/// </summary>
|
||||
/// <returns>true if the client was started and false if it failed to start the client</returns>
|
||||
public override bool StartClient()
|
||||
{
|
||||
if (m_Driver.IsCreated)
|
||||
@@ -1084,6 +1230,14 @@ namespace Unity.Netcode.Transports.UTP
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts to listening for incoming clients
|
||||
/// Note:
|
||||
/// When this method returns false it could mean:
|
||||
/// - You are trying to start a client that is already started
|
||||
/// - It failed during the initial port binding when attempting to begin to connect
|
||||
/// </summary>
|
||||
/// <returns>true if the server was started and false if it failed to start the server</returns>
|
||||
public override bool StartServer()
|
||||
{
|
||||
if (m_Driver.IsCreated)
|
||||
@@ -1113,6 +1267,9 @@ namespace Unity.Netcode.Transports.UTP
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down the transport
|
||||
/// </summary>
|
||||
public override void Shutdown()
|
||||
{
|
||||
if (!m_Driver.IsCreated)
|
||||
@@ -1128,10 +1285,10 @@ namespace Unity.Netcode.Transports.UTP
|
||||
SendBatchedMessages(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
// The above flush only puts the message in UTP internal buffers, need the flush send
|
||||
// job to execute to actually get things out on the wire. This will also ensure any
|
||||
// disconnect messages are sent out.
|
||||
m_Driver.ScheduleFlushSend(default).Complete();
|
||||
// The above flush only puts the message in UTP internal buffers, need an update to
|
||||
// actually get the messages on the wire. (Normally a flush send would be sufficient,
|
||||
// but there might be disconnect messages and those require an update call.)
|
||||
m_Driver.ScheduleUpdate().Complete();
|
||||
|
||||
DisposeInternals();
|
||||
|
||||
@@ -1152,6 +1309,14 @@ namespace Unity.Netcode.Transports.UTP
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the internal NetworkDriver
|
||||
/// </summary>
|
||||
/// <param name="transport">The owner transport</param>
|
||||
/// <param name="driver">The driver</param>
|
||||
/// <param name="unreliableFragmentedPipeline">The UnreliableFragmented NetworkPipeline</param>
|
||||
/// <param name="unreliableSequencedFragmentedPipeline">The UnreliableSequencedFragmented NetworkPipeline</param>
|
||||
/// <param name="reliableSequencedPipeline">The ReliableSequenced NetworkPipeline</param>
|
||||
public void CreateDriver(UnityTransport transport, out NetworkDriver driver,
|
||||
out NetworkPipeline unreliableFragmentedPipeline,
|
||||
out NetworkPipeline unreliableSequencedFragmentedPipeline,
|
||||
@@ -1160,10 +1325,8 @@ namespace Unity.Netcode.Transports.UTP
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
NetworkPipelineStageCollection.RegisterPipelineStage(new NetworkMetricsPipelineStage());
|
||||
#endif
|
||||
var maxFrameTimeMS = 0;
|
||||
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
maxFrameTimeMS = 100;
|
||||
ConfigureSimulator();
|
||||
#endif
|
||||
|
||||
@@ -1171,8 +1334,7 @@ namespace Unity.Netcode.Transports.UTP
|
||||
maxConnectAttempts: transport.m_MaxConnectAttempts,
|
||||
connectTimeoutMS: transport.m_ConnectTimeoutMS,
|
||||
disconnectTimeoutMS: transport.m_DisconnectTimeoutMS,
|
||||
heartbeatTimeoutMS: transport.m_HeartbeatTimeoutMS,
|
||||
maxFrameTimeMS: maxFrameTimeMS);
|
||||
heartbeatTimeoutMS: transport.m_HeartbeatTimeoutMS);
|
||||
|
||||
driver = NetworkDriver.Create(m_NetworkSettings);
|
||||
|
||||
|
||||
@@ -869,12 +869,14 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
/// Constructor that allows you To break tests up as a host
|
||||
/// and a server.
|
||||
/// Example: Decorate your child derived class with TestFixture
|
||||
/// and then create a constructor at the child level
|
||||
/// and then create a constructor at the child level.
|
||||
/// Don't forget to set your constructor public, else Unity will
|
||||
/// give you a hard to decipher error
|
||||
/// [TestFixture(HostOrServer.Host)]
|
||||
/// [TestFixture(HostOrServer.Server)]
|
||||
/// public class MyChildClass : NetcodeIntegrationTest
|
||||
/// {
|
||||
/// MyChildClass(HostOrServer hostOrServer) : base(hostOrServer) { }
|
||||
/// public MyChildClass(HostOrServer hostOrServer) : base(hostOrServer) { }
|
||||
/// }
|
||||
/// </summary>
|
||||
/// <param name="hostOrServer"></param>
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
],
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.unity.multiplayer.tools",
|
||||
|
||||
@@ -4,7 +4,6 @@ using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Unity.Netcode.EditorTests
|
||||
{
|
||||
@@ -21,20 +20,20 @@ namespace Unity.Netcode.EditorTests
|
||||
var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget);
|
||||
var buildTargetSupported = BuildPipeline.IsBuildTargetSupported(buildTargetGroup, buildTarget);
|
||||
|
||||
var buildReport = BuildPipeline.BuildPlayer(
|
||||
new[] { Path.Combine(packagePath, DefaultBuildScenePath) },
|
||||
Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", nameof(BuildTests)),
|
||||
buildTarget,
|
||||
BuildOptions.None
|
||||
);
|
||||
|
||||
if (buildTargetSupported)
|
||||
{
|
||||
var buildReport = BuildPipeline.BuildPlayer(
|
||||
new[] { Path.Combine(packagePath, DefaultBuildScenePath) },
|
||||
Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", nameof(BuildTests)),
|
||||
buildTarget,
|
||||
BuildOptions.None
|
||||
);
|
||||
|
||||
Assert.AreEqual(BuildResult.Succeeded, buildReport.summary.result);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAssert.Expect(LogType.Error, "Error building player because build target was unsupported");
|
||||
Debug.Log($"Skipped building player due to Unsupported Build Target");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ namespace Unity.Netcode.EditorTests
|
||||
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(2f).Within(k_Precision));
|
||||
}
|
||||
|
||||
[Ignore("TODO: Fix this test to still handle testing message loss without extrapolation")]
|
||||
[Test]
|
||||
public void MessageLoss()
|
||||
{
|
||||
@@ -305,6 +306,7 @@ namespace Unity.Netcode.EditorTests
|
||||
Assert.Throws<InvalidOperationException>(() => interpolator.Update(1f, serverTime));
|
||||
}
|
||||
|
||||
[Ignore("TODO: Fix this test to still test duplicated values without extrapolation")]
|
||||
[Test]
|
||||
public void TestDuplicatedValues()
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -179,5 +180,122 @@ namespace Unity.Netcode.EditorTests
|
||||
Assert.AreEqual(handlerFour, systemThree.MessageHandlers[systemThree.GetMessageType(typeof(TestMessageFour))]);
|
||||
}
|
||||
}
|
||||
|
||||
internal class AAAEarlyLexicographicNetworkMessage : INetworkMessage
|
||||
{
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
}
|
||||
|
||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Handle(ref NetworkContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable IDE1006
|
||||
internal class zzzLateLexicographicNetworkMessage : AAAEarlyLexicographicNetworkMessage
|
||||
{
|
||||
}
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
internal class OrderingMessageProvider : IMessageProvider
|
||||
{
|
||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
||||
{
|
||||
var listMessages = new List<MessagingSystem.MessageWithHandler>();
|
||||
|
||||
var messageWithHandler = new MessagingSystem.MessageWithHandler();
|
||||
|
||||
messageWithHandler.MessageType = typeof(zzzLateLexicographicNetworkMessage);
|
||||
listMessages.Add(messageWithHandler);
|
||||
|
||||
messageWithHandler.MessageType = typeof(ConnectionRequestMessage);
|
||||
listMessages.Add(messageWithHandler);
|
||||
|
||||
messageWithHandler.MessageType = typeof(ConnectionApprovedMessage);
|
||||
listMessages.Add(messageWithHandler);
|
||||
|
||||
messageWithHandler.MessageType = typeof(OrderingMessage);
|
||||
listMessages.Add(messageWithHandler);
|
||||
|
||||
messageWithHandler.MessageType = typeof(AAAEarlyLexicographicNetworkMessage);
|
||||
listMessages.Add(messageWithHandler);
|
||||
|
||||
return listMessages;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MessagesGetPrioritizedCorrectly()
|
||||
{
|
||||
var sender = new NopMessageSender();
|
||||
var provider = new OrderingMessageProvider();
|
||||
var messagingSystem = new MessagingSystem(sender, null, provider);
|
||||
|
||||
// the 3 priority messages should appear first, in lexicographic order
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[0], typeof(ConnectionApprovedMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[1], typeof(ConnectionRequestMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[2], typeof(OrderingMessage));
|
||||
|
||||
// the other should follow after
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[3], typeof(AAAEarlyLexicographicNetworkMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(zzzLateLexicographicNetworkMessage));
|
||||
|
||||
// there should not be any extras
|
||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 5);
|
||||
|
||||
// reorder the zzz one to position 3
|
||||
messagingSystem.ReorderMessage(3, XXHash.Hash32(typeof(zzzLateLexicographicNetworkMessage).FullName));
|
||||
|
||||
// the 3 priority messages should still appear first, in lexicographic order
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[0], typeof(ConnectionApprovedMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[1], typeof(ConnectionRequestMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[2], typeof(OrderingMessage));
|
||||
|
||||
// the other should follow after, but reordered
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[3], typeof(zzzLateLexicographicNetworkMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(AAAEarlyLexicographicNetworkMessage));
|
||||
|
||||
// there should still not be any extras
|
||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 5);
|
||||
|
||||
// verify we get an exception when asking for an invalid position
|
||||
try
|
||||
{
|
||||
messagingSystem.ReorderMessage(-1, XXHash.Hash32(typeof(zzzLateLexicographicNetworkMessage).FullName));
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
}
|
||||
|
||||
// reorder the zzz one to position 3, again, to check nothing bad happens
|
||||
messagingSystem.ReorderMessage(3, XXHash.Hash32(typeof(zzzLateLexicographicNetworkMessage).FullName));
|
||||
|
||||
// the two non-priority should not have moved
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[3], typeof(zzzLateLexicographicNetworkMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(AAAEarlyLexicographicNetworkMessage));
|
||||
|
||||
// there should still not be any extras
|
||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 5);
|
||||
|
||||
// 4242 is a random hash that should not match anything
|
||||
messagingSystem.ReorderMessage(3, 4242);
|
||||
|
||||
// that should result in an extra entry
|
||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 6);
|
||||
|
||||
// with a null handler
|
||||
Assert.AreEqual(messagingSystem.MessageHandlers[3], null);
|
||||
|
||||
// and it should have bumped the previous messages down
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(zzzLateLexicographicNetworkMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[5], typeof(AAAEarlyLexicographicNetworkMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using NUnit.Framework;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Random = System.Random;
|
||||
|
||||
namespace Unity.Netcode.EditorTests
|
||||
{
|
||||
@@ -40,11 +44,24 @@ namespace Unity.Netcode.EditorTests
|
||||
}
|
||||
}
|
||||
|
||||
private class TestMessageProvider : IMessageProvider
|
||||
private class TestMessageProvider : IMessageProvider, IDisposable
|
||||
{
|
||||
// Keep track of what we sent
|
||||
private List<List<MessagingSystem.MessageWithHandler>> m_CachedMessages = new List<List<MessagingSystem.MessageWithHandler>>();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var cachedItem in m_CachedMessages)
|
||||
{
|
||||
// Clear out any references to MessagingSystem.MessageWithHandlers
|
||||
cachedItem.Clear();
|
||||
}
|
||||
m_CachedMessages.Clear();
|
||||
}
|
||||
|
||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
||||
{
|
||||
return new List<MessagingSystem.MessageWithHandler>
|
||||
var messageList = new List<MessagingSystem.MessageWithHandler>
|
||||
{
|
||||
new MessagingSystem.MessageWithHandler
|
||||
{
|
||||
@@ -52,9 +69,13 @@ namespace Unity.Netcode.EditorTests
|
||||
Handler = MessagingSystem.ReceiveMessage<TestMessage>
|
||||
}
|
||||
};
|
||||
// Track messages sent
|
||||
m_CachedMessages.Add(messageList);
|
||||
return messageList;
|
||||
}
|
||||
}
|
||||
|
||||
private TestMessageProvider m_TestMessageProvider;
|
||||
private TestMessageSender m_MessageSender;
|
||||
private MessagingSystem m_MessagingSystem;
|
||||
private ulong[] m_Clients = { 0 };
|
||||
@@ -63,15 +84,16 @@ namespace Unity.Netcode.EditorTests
|
||||
public void SetUp()
|
||||
{
|
||||
TestMessage.Serialized = false;
|
||||
|
||||
m_MessageSender = new TestMessageSender();
|
||||
m_MessagingSystem = new MessagingSystem(m_MessageSender, this, new TestMessageProvider());
|
||||
m_TestMessageProvider = new TestMessageProvider();
|
||||
m_MessagingSystem = new MessagingSystem(m_MessageSender, this, m_TestMessageProvider);
|
||||
m_MessagingSystem.ClientConnected(0);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
m_TestMessageProvider.Dispose();
|
||||
m_MessagingSystem.Dispose();
|
||||
}
|
||||
|
||||
@@ -224,5 +246,56 @@ namespace Unity.Netcode.EditorTests
|
||||
Assert.AreEqual(message2, receivedMessage2);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestNoHandlerMessageProvider : IMessageProvider
|
||||
{
|
||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
||||
{
|
||||
return new List<MessagingSystem.MessageWithHandler>
|
||||
{
|
||||
new MessagingSystem.MessageWithHandler
|
||||
{
|
||||
MessageType = typeof(TestMessage),
|
||||
Handler = null
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WhenReceivingAMessageWithoutAHandler_ExceptionIsLogged()
|
||||
{
|
||||
// If a MessagingSystem already exists then dispose of it before creating a new MessagingSystem (otherwise memory leak)
|
||||
if (m_MessagingSystem != null)
|
||||
{
|
||||
m_MessagingSystem.Dispose();
|
||||
m_MessagingSystem = null;
|
||||
}
|
||||
|
||||
// Since m_MessagingSystem is disposed during teardown we don't need to worry about that here.
|
||||
m_MessagingSystem = new MessagingSystem(new NopMessageSender(), this, new TestNoHandlerMessageProvider());
|
||||
m_MessagingSystem.ClientConnected(0);
|
||||
|
||||
var messageHeader = new MessageHeader
|
||||
{
|
||||
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
||||
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
|
||||
};
|
||||
var message = GetMessage();
|
||||
|
||||
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
||||
using (writer)
|
||||
{
|
||||
writer.TryBeginWrite(FastBufferWriter.GetWriteSize(message));
|
||||
writer.WriteValue(message);
|
||||
|
||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||
using (reader)
|
||||
{
|
||||
m_MessagingSystem.HandleMessage(messageHeader, reader, 0, 0, 0);
|
||||
LogAssert.Expect(LogType.Exception, new Regex(".*HandlerNotRegisteredException.*"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,55 @@
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode.EditorTests.NetworkVar
|
||||
{
|
||||
public class NetworkVarTests
|
||||
{
|
||||
public class NetworkVarComponent : NetworkBehaviour
|
||||
{
|
||||
public NetworkVariable<int> NetworkVariable = new NetworkVariable<int>();
|
||||
}
|
||||
[Test]
|
||||
public void TestAssignmentUnchanged()
|
||||
{
|
||||
var intVar = new NetworkVariable<int>();
|
||||
|
||||
intVar.Value = 314159265;
|
||||
|
||||
intVar.OnValueChanged += (value, newValue) =>
|
||||
var gameObjectMan = new GameObject();
|
||||
var networkManager = gameObjectMan.AddComponent<NetworkManager>();
|
||||
networkManager.BehaviourUpdater = new NetworkBehaviourUpdater();
|
||||
var gameObject = new GameObject();
|
||||
var networkObject = gameObject.AddComponent<NetworkObject>();
|
||||
networkObject.NetworkManagerOwner = networkManager;
|
||||
var networkVarComponent = gameObject.AddComponent<NetworkVarComponent>();
|
||||
networkVarComponent.NetworkVariable.Initialize(networkVarComponent);
|
||||
networkVarComponent.NetworkVariable.Value = 314159265;
|
||||
networkVarComponent.NetworkVariable.OnValueChanged += (value, newValue) =>
|
||||
{
|
||||
Assert.Fail("OnValueChanged was invoked when setting the same value");
|
||||
};
|
||||
|
||||
intVar.Value = 314159265;
|
||||
networkVarComponent.NetworkVariable.Value = 314159265;
|
||||
Object.DestroyImmediate(gameObject);
|
||||
Object.DestroyImmediate(gameObjectMan);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAssignmentChanged()
|
||||
{
|
||||
var intVar = new NetworkVariable<int>();
|
||||
|
||||
intVar.Value = 314159265;
|
||||
|
||||
var gameObjectMan = new GameObject();
|
||||
var networkManager = gameObjectMan.AddComponent<NetworkManager>();
|
||||
networkManager.BehaviourUpdater = new NetworkBehaviourUpdater();
|
||||
var gameObject = new GameObject();
|
||||
var networkObject = gameObject.AddComponent<NetworkObject>();
|
||||
var networkVarComponent = gameObject.AddComponent<NetworkVarComponent>();
|
||||
networkObject.NetworkManagerOwner = networkManager;
|
||||
networkVarComponent.NetworkVariable.Initialize(networkVarComponent);
|
||||
networkVarComponent.NetworkVariable.Value = 314159265;
|
||||
var changed = false;
|
||||
|
||||
intVar.OnValueChanged += (value, newValue) =>
|
||||
networkVarComponent.NetworkVariable.OnValueChanged += (value, newValue) =>
|
||||
{
|
||||
changed = true;
|
||||
};
|
||||
|
||||
intVar.Value = 314159266;
|
||||
|
||||
networkVarComponent.NetworkVariable.Value = 314159266;
|
||||
Assert.True(changed);
|
||||
Object.DestroyImmediate(gameObject);
|
||||
Object.DestroyImmediate(gameObjectMan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using NUnit.Framework;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Unity.Netcode.EditorTests
|
||||
{
|
||||
public class UserBitReaderAndBitWriterTests_NCCBUG175
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void WhenBitwiseWritingMoreThan8Bits_ValuesAreCorrect()
|
||||
{
|
||||
using var writer = new FastBufferWriter(1024, Allocator.Temp);
|
||||
ulong inVal = 123456789;
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
writer.WriteValueSafe(i);
|
||||
}
|
||||
|
||||
using (var bitWriter = writer.EnterBitwiseContext())
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
Assert.IsTrue((bitWriter.TryBeginWriteBits(32)));
|
||||
bitWriter.WriteBits(inVal, 31);
|
||||
bitWriter.WriteBit(true);
|
||||
}
|
||||
}
|
||||
|
||||
using var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
reader.ReadValueSafe(out int outVal);
|
||||
Assert.AreEqual(i, outVal);
|
||||
}
|
||||
|
||||
using var bitReader = reader.EnterBitwiseContext();
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
Assert.IsTrue(bitReader.TryBeginReadBits(32));
|
||||
bitReader.ReadBits(out ulong outVal, 31);
|
||||
bitReader.ReadBit(out bool bit);
|
||||
Assert.AreEqual(inVal, outVal);
|
||||
Assert.AreEqual(true, bit);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WhenBitwiseReadingMoreThan8Bits_ValuesAreCorrect()
|
||||
{
|
||||
using var writer = new FastBufferWriter(1024, Allocator.Temp);
|
||||
ulong inVal = 123456789;
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
writer.WriteValueSafe(i);
|
||||
}
|
||||
|
||||
uint combined = (uint)inVal | (1u << 31);
|
||||
writer.WriteValueSafe(combined);
|
||||
writer.WriteValueSafe(combined);
|
||||
writer.WriteValueSafe(combined);
|
||||
|
||||
using var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
reader.ReadValueSafe(out int outVal);
|
||||
Assert.AreEqual(i, outVal);
|
||||
}
|
||||
|
||||
using (var bitReader = reader.EnterBitwiseContext())
|
||||
{
|
||||
Assert.IsTrue(bitReader.TryBeginReadBits(32));
|
||||
bitReader.ReadBits(out ulong outVal, 31);
|
||||
bitReader.ReadBit(out bool bit);
|
||||
Assert.AreEqual(inVal, outVal);
|
||||
Assert.AreEqual(true, bit);
|
||||
|
||||
Assert.IsTrue(bitReader.TryBeginReadBits(32));
|
||||
bitReader.ReadBits(out outVal, 31);
|
||||
bitReader.ReadBit(out bit);
|
||||
Assert.AreEqual(inVal, outVal);
|
||||
Assert.AreEqual(true, bit);
|
||||
|
||||
Assert.IsTrue(bitReader.TryBeginReadBits(32));
|
||||
bitReader.ReadBits(out outVal, 31);
|
||||
bitReader.ReadBit(out bit);
|
||||
Assert.AreEqual(inVal, outVal);
|
||||
Assert.AreEqual(true, bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adfa622d42824b70a39a30b6aa22c9c5
|
||||
timeCreated: 1660758428
|
||||
@@ -15,6 +15,9 @@
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
],
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
|
||||
@@ -53,7 +53,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
|
||||
public bool EnableTesting;
|
||||
private bool m_Initialized;
|
||||
private bool m_FinishedTests;
|
||||
private bool m_ChangesAppliedToNetworkVariables;
|
||||
|
||||
@@ -148,6 +147,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
return m_FinishedTests;
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
InitializeTest();
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
private void Update()
|
||||
{
|
||||
@@ -164,37 +168,29 @@ namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
if (NetworkManager != null && NetworkManager.IsListening)
|
||||
{
|
||||
if (!m_Initialized)
|
||||
{
|
||||
InitializeTest();
|
||||
m_Initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Now change all of the values to make sure we are at least testing the local callback
|
||||
m_NetworkVariableBool.Value = false;
|
||||
m_NetworkVariableByte.Value = 255;
|
||||
m_NetworkVariableColor.Value = new Color(100, 100, 100);
|
||||
m_NetworkVariableColor32.Value = new Color32(100, 100, 100, 100);
|
||||
m_NetworkVariableDouble.Value = 1000;
|
||||
m_NetworkVariableFloat.Value = 1000.0f;
|
||||
m_NetworkVariableInt.Value = 1000;
|
||||
m_NetworkVariableLong.Value = 100000;
|
||||
m_NetworkVariableSByte.Value = -127;
|
||||
m_NetworkVariableQuaternion.Value = new Quaternion(100, 100, 100, 100);
|
||||
m_NetworkVariableShort.Value = short.MaxValue;
|
||||
m_NetworkVariableVector4.Value = new Vector4(1000, 1000, 1000, 1000);
|
||||
m_NetworkVariableVector3.Value = new Vector3(1000, 1000, 1000);
|
||||
m_NetworkVariableVector2.Value = new Vector2(1000, 1000);
|
||||
m_NetworkVariableRay.Value = new Ray(Vector3.one, Vector3.right);
|
||||
m_NetworkVariableULong.Value = ulong.MaxValue;
|
||||
m_NetworkVariableUInt.Value = uint.MaxValue;
|
||||
m_NetworkVariableUShort.Value = ushort.MaxValue;
|
||||
//Now change all of the values to make sure we are at least testing the local callback
|
||||
m_NetworkVariableBool.Value = false;
|
||||
m_NetworkVariableByte.Value = 255;
|
||||
m_NetworkVariableColor.Value = new Color(100, 100, 100);
|
||||
m_NetworkVariableColor32.Value = new Color32(100, 100, 100, 100);
|
||||
m_NetworkVariableDouble.Value = 1000;
|
||||
m_NetworkVariableFloat.Value = 1000.0f;
|
||||
m_NetworkVariableInt.Value = 1000;
|
||||
m_NetworkVariableLong.Value = 100000;
|
||||
m_NetworkVariableSByte.Value = -127;
|
||||
m_NetworkVariableQuaternion.Value = new Quaternion(100, 100, 100, 100);
|
||||
m_NetworkVariableShort.Value = short.MaxValue;
|
||||
m_NetworkVariableVector4.Value = new Vector4(1000, 1000, 1000, 1000);
|
||||
m_NetworkVariableVector3.Value = new Vector3(1000, 1000, 1000);
|
||||
m_NetworkVariableVector2.Value = new Vector2(1000, 1000);
|
||||
m_NetworkVariableRay.Value = new Ray(Vector3.one, Vector3.right);
|
||||
m_NetworkVariableULong.Value = ulong.MaxValue;
|
||||
m_NetworkVariableUInt.Value = uint.MaxValue;
|
||||
m_NetworkVariableUShort.Value = ushort.MaxValue;
|
||||
|
||||
//Set the timeout (i.e. how long we will wait for all NetworkVariables to have registered their changes)
|
||||
m_WaitForChangesTimeout = Time.realtimeSinceStartup + 0.50f;
|
||||
m_ChangesAppliedToNetworkVariables = true;
|
||||
}
|
||||
//Set the timeout (i.e. how long we will wait for all NetworkVariables to have registered their changes)
|
||||
m_WaitForChangesTimeout = Time.realtimeSinceStartup + 0.50f;
|
||||
m_ChangesAppliedToNetworkVariables = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,6 +813,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
[Ignore("This test is unstable (MTT-4146)")]
|
||||
public IEnumerator WhenAMessageIsDeferredForMoreThanTheConfiguredTime_ItIsRemoved([Values(1, 2, 3)] int timeout)
|
||||
{
|
||||
RegisterClientPrefabs();
|
||||
|
||||
82
Tests/Runtime/ListChangedTest.cs
Normal file
82
Tests/Runtime/ListChangedTest.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
|
||||
namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
public class NetworkListChangedTestComponent : NetworkBehaviour
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class ListChangedObject : NetworkBehaviour
|
||||
{
|
||||
public int ExpectedPreviousValue = 0;
|
||||
public int ExpectedValue = 0;
|
||||
public bool AddDone = false;
|
||||
|
||||
public NetworkList<int> MyNetworkList = new NetworkList<int>();
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
MyNetworkList.OnListChanged += Changed;
|
||||
base.OnNetworkSpawn();
|
||||
}
|
||||
|
||||
public void Changed(NetworkListEvent<int> listEvent)
|
||||
{
|
||||
if (listEvent.Type == NetworkListEvent<int>.EventType.Value)
|
||||
{
|
||||
if (listEvent.PreviousValue != ExpectedPreviousValue)
|
||||
{
|
||||
Debug.Log($"Expected previous value mismatch {listEvent.PreviousValue} versus {ExpectedPreviousValue}");
|
||||
Debug.Assert(listEvent.PreviousValue == ExpectedPreviousValue);
|
||||
}
|
||||
|
||||
if (listEvent.Value != ExpectedValue)
|
||||
{
|
||||
Debug.Log($"Expected value mismatch {listEvent.Value} versus {ExpectedValue}");
|
||||
Debug.Assert(listEvent.Value == ExpectedValue);
|
||||
}
|
||||
|
||||
AddDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class NetworkListChangedTests : NetcodeIntegrationTest
|
||||
{
|
||||
protected override int NumberOfClients => 2;
|
||||
|
||||
private ulong m_ClientId0;
|
||||
private GameObject m_PrefabToSpawn;
|
||||
|
||||
private NetworkObject m_NetSpawnedObject1;
|
||||
|
||||
protected override void OnServerAndClientsCreated()
|
||||
{
|
||||
m_PrefabToSpawn = CreateNetworkObjectPrefab("ListChangedObject");
|
||||
m_PrefabToSpawn.AddComponent<ListChangedObject>();
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator NetworkListChangedTest()
|
||||
{
|
||||
m_ClientId0 = m_ClientNetworkManagers[0].LocalClientId;
|
||||
|
||||
// create 3 objects
|
||||
var spawnedObject1 = SpawnObject(m_PrefabToSpawn, m_ServerNetworkManager);
|
||||
m_NetSpawnedObject1 = spawnedObject1.GetComponent<NetworkObject>();
|
||||
|
||||
m_NetSpawnedObject1.GetComponent<ListChangedObject>().MyNetworkList.Add(42);
|
||||
m_NetSpawnedObject1.GetComponent<ListChangedObject>().ExpectedPreviousValue = 42;
|
||||
m_NetSpawnedObject1.GetComponent<ListChangedObject>().ExpectedValue = 44;
|
||||
m_NetSpawnedObject1.GetComponent<ListChangedObject>().MyNetworkList[0] = 44;
|
||||
|
||||
Debug.Assert(m_NetSpawnedObject1.GetComponent<ListChangedObject>().AddDone);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Tests/Runtime/ListChangedTest.cs.meta
Normal file
11
Tests/Runtime/ListChangedTest.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b269e2a059f814075a737691bc02afa4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -25,10 +25,17 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
||||
|
||||
var metricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
|
||||
|
||||
var networkVariableDeltaSent = metricValues.First();
|
||||
Assert.AreEqual(nameof(NetworkVariableComponent.MyNetworkVariable), networkVariableDeltaSent.Name);
|
||||
Assert.AreEqual(Server.LocalClientId, networkVariableDeltaSent.Connection.Id);
|
||||
Assert.AreNotEqual(0, networkVariableDeltaSent.BytesCount);
|
||||
bool found = false;
|
||||
foreach (var networkVariableDeltaSent in metricValues)
|
||||
{
|
||||
if (nameof(NetworkVariableComponent.MyNetworkVariable) == networkVariableDeltaSent.Name &&
|
||||
Client.LocalClientId == networkVariableDeltaSent.Connection.Id &&
|
||||
0 != networkVariableDeltaSent.BytesCount)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
Assert.IsTrue(found);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
|
||||
@@ -1,239 +0,0 @@
|
||||
#if COM_UNITY_MODULES_ANIMATION
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using Unity.Netcode.Components;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
|
||||
namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
[TestFixture(HostOrServer.Host)]
|
||||
[TestFixture(HostOrServer.Server)]
|
||||
public class NetworkAnimatorTests : NetcodeIntegrationTest
|
||||
{
|
||||
protected override int NumberOfClients => 1;
|
||||
|
||||
private GameObject m_PlayerOnServer;
|
||||
private GameObject m_PlayerOnClient;
|
||||
|
||||
private Animator m_PlayerOnServerAnimator;
|
||||
private Animator m_PlayerOnClientAnimator;
|
||||
|
||||
public NetworkAnimatorTests(HostOrServer hostOrServer) : base(hostOrServer) { }
|
||||
|
||||
protected override void OnCreatePlayerPrefab()
|
||||
{
|
||||
// ideally, we would build up the AnimatorController entirely in code and not need an asset,
|
||||
// but after some attempts this doesn't seem readily doable. Instead, we load a controller
|
||||
var controller = Resources.Load("TestAnimatorController") as RuntimeAnimatorController;
|
||||
var animator = m_PlayerPrefab.AddComponent<Animator>();
|
||||
animator.runtimeAnimatorController = controller;
|
||||
|
||||
var networkAnimator = m_PlayerPrefab.AddComponent<NetworkAnimator>();
|
||||
networkAnimator.Animator = animator;
|
||||
}
|
||||
|
||||
protected override IEnumerator OnServerAndClientsConnected()
|
||||
{
|
||||
m_PlayerOnServer = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId].gameObject;
|
||||
m_PlayerOnServerAnimator = m_PlayerOnServerAnimator = m_PlayerOnServer.GetComponent<Animator>();
|
||||
|
||||
m_PlayerOnClient = m_PlayerNetworkObjects[m_ClientNetworkManagers[0].LocalClientId][m_ClientNetworkManagers[0].LocalClientId].gameObject;
|
||||
m_PlayerOnClientAnimator = m_PlayerOnClient.GetComponent<Animator>();
|
||||
|
||||
return base.OnServerAndClientsConnected();
|
||||
}
|
||||
|
||||
// helper function to scan an animator and verify a given clip is present
|
||||
private bool HasClip(Animator animator, string clipName)
|
||||
{
|
||||
var clips = new List<AnimatorClipInfo>();
|
||||
animator.GetCurrentAnimatorClipInfo(0, clips);
|
||||
foreach (var clip in clips)
|
||||
{
|
||||
if (clip.clip.name == clipName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationTriggerReset([Values(true, false)] bool asHash)
|
||||
{
|
||||
// We have "UnboundTrigger" purposely not bound to any animations so we can test resetting.
|
||||
// If we used a trigger that was bound to a transition, then the trigger would reset as soon as the
|
||||
// transition happens. This way it will stay stuck on
|
||||
string triggerString = "UnboundTrigger";
|
||||
int triggerHash = Animator.StringToHash(triggerString);
|
||||
|
||||
// Verify trigger is off
|
||||
Assert.True(m_PlayerOnServerAnimator.GetBool(triggerString) == false);
|
||||
Assert.True(m_PlayerOnClientAnimator.GetBool(triggerString) == false);
|
||||
|
||||
// trigger.
|
||||
if (asHash)
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().SetTrigger(triggerHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().SetTrigger(triggerString);
|
||||
}
|
||||
|
||||
// verify trigger is set for client and server
|
||||
yield return WaitForConditionOrTimeOut(() => asHash ? m_PlayerOnServerAnimator.GetBool(triggerHash) : m_PlayerOnServerAnimator.GetBool(triggerString));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out on server trigger set check");
|
||||
|
||||
yield return WaitForConditionOrTimeOut(() => asHash ? m_PlayerOnClientAnimator.GetBool(triggerHash) : m_PlayerOnClientAnimator.GetBool(triggerString));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out on client trigger set check");
|
||||
|
||||
// reset the trigger
|
||||
if (asHash)
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().ResetTrigger(triggerHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().ResetTrigger(triggerString);
|
||||
}
|
||||
|
||||
// verify trigger is reset for client and server
|
||||
yield return WaitForConditionOrTimeOut(() => asHash ? m_PlayerOnServerAnimator.GetBool(triggerHash) == false : m_PlayerOnServerAnimator.GetBool(triggerString) == false);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out on server reset check");
|
||||
|
||||
yield return WaitForConditionOrTimeOut(() => asHash ? m_PlayerOnClientAnimator.GetBool(triggerHash) == false : m_PlayerOnClientAnimator.GetBool(triggerString) == false);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out on client reset check");
|
||||
}
|
||||
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationStateSyncTest()
|
||||
{
|
||||
// check that we have started in the default state
|
||||
Assert.True(m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(0).IsName("DefaultState"));
|
||||
Assert.True(m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(0).IsName("DefaultState"));
|
||||
|
||||
// cause a change to the AlphaState state by setting AlphaParameter, which is
|
||||
// the variable bound to the transition from default to AlphaState (see the TestAnimatorController asset)
|
||||
m_PlayerOnServerAnimator.SetBool("AlphaParameter", true);
|
||||
|
||||
// ...and now we should be in the AlphaState having triggered the AlphaParameter
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(0).IsName("AlphaState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its animation state");
|
||||
|
||||
// ...and now the client should also have sync'd and arrived at the correct state
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(0).IsName("AlphaState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Client failed to sync its animation state from the server");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationLayerStateSyncTest()
|
||||
{
|
||||
int layer = 1;
|
||||
// check that we have started in the default state
|
||||
Assert.True(m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(layer).IsName("DefaultStateLayer2"));
|
||||
Assert.True(m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(layer).IsName("DefaultStateLayer2"));
|
||||
|
||||
// cause a change to the AlphaState state by setting AlphaParameter, which is
|
||||
// the variable bound to the transition from default to AlphaState (see the TestAnimatorController asset)
|
||||
m_PlayerOnServerAnimator.SetBool("Layer2AlphaParameter", true);
|
||||
|
||||
// ...and now we should be in the AlphaState having triggered the AlphaParameter
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(layer).IsName("Layer2AlphaState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its animation state");
|
||||
|
||||
// ...and now the client should also have sync'd and arrived at the correct state
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(layer).IsName("Layer2AlphaState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Client failed to sync its animation state from the server");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationLayerWeightTest()
|
||||
{
|
||||
int layer = 1;
|
||||
float targetWeight = 0.333f;
|
||||
|
||||
// check that we have started in the default state
|
||||
Assert.True(Mathf.Approximately(m_PlayerOnServerAnimator.GetLayerWeight(layer), 1f));
|
||||
Assert.True(Mathf.Approximately(m_PlayerOnClientAnimator.GetLayerWeight(layer), 1f));
|
||||
|
||||
m_PlayerOnServerAnimator.SetLayerWeight(layer, targetWeight);
|
||||
|
||||
// ...and now we should be in the AlphaState having triggered the AlphaParameter
|
||||
yield return WaitForConditionOrTimeOut(() =>
|
||||
Mathf.Approximately(m_PlayerOnServerAnimator.GetLayerWeight(layer), targetWeight)
|
||||
);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its animation state");
|
||||
|
||||
// ...and now the client should also have sync'd and arrived at the correct state
|
||||
yield return WaitForConditionOrTimeOut(() =>
|
||||
Mathf.Approximately(m_PlayerOnClientAnimator.GetLayerWeight(layer), targetWeight)
|
||||
);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its animation state");
|
||||
}
|
||||
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationStateSyncTriggerTest([Values(true, false)] bool asHash)
|
||||
{
|
||||
string triggerString = "TestTrigger";
|
||||
int triggerHash = Animator.StringToHash(triggerString);
|
||||
|
||||
// check that we have started in the default state
|
||||
Assert.True(m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(0).IsName("DefaultState"));
|
||||
Assert.True(m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(0).IsName("DefaultState"));
|
||||
|
||||
// cause a change to the AlphaState state by setting TestTrigger
|
||||
// note, we have a special test for triggers because activating triggers via the
|
||||
// NetworkAnimator is special; for other parameters you set them on the Animator and NetworkAnimator
|
||||
// listens. But because triggers are super short and transitory, we require users to call
|
||||
// NetworkAnimator.SetTrigger so we don't miss it
|
||||
if (asHash)
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().SetTrigger(triggerHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().SetTrigger(triggerString);
|
||||
}
|
||||
|
||||
// ...and now we should be in the AlphaState having triggered the AlphaParameter
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(0).IsName("TriggeredState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its animation state via trigger");
|
||||
|
||||
// ...and now the client should also have sync'd and arrived at the correct state
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(0).IsName("TriggeredState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Client failed to sync its animation state from the server via trigger");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationStateSyncTestWithOverride()
|
||||
{
|
||||
// set up the animation override controller
|
||||
var overrideController = Resources.Load("TestAnimatorOverrideController") as AnimatorOverrideController;
|
||||
m_PlayerOnServer.GetComponent<Animator>().runtimeAnimatorController = overrideController;
|
||||
m_PlayerOnClient.GetComponent<Animator>().runtimeAnimatorController = overrideController;
|
||||
|
||||
// in our default state, we should see the OverrideDefaultAnimation clip
|
||||
Assert.True(HasClip(m_PlayerOnServerAnimator, "OverrideDefaultAnimation"));
|
||||
Assert.True(HasClip(m_PlayerOnClientAnimator, "OverrideDefaultAnimation"));
|
||||
|
||||
// cause a change to the AlphaState state by setting AlphaParameter, which is
|
||||
// the variable bound to the transition from default to AlphaState (see the TestAnimatorController asset)
|
||||
m_PlayerOnServerAnimator.SetBool("AlphaParameter", true);
|
||||
|
||||
// ...and now we should be in the AlphaState having set the AlphaParameter
|
||||
yield return WaitForConditionOrTimeOut(() => HasClip(m_PlayerOnServerAnimator, "OverrideAlphaAnimation"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its overriden animation state");
|
||||
|
||||
// ...and now the client should also have sync'd and arrived at the correct state
|
||||
yield return WaitForConditionOrTimeOut(() => HasClip(m_PlayerOnServerAnimator, "OverrideAlphaAnimation"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Client failed to reach its overriden animation state");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // COM_UNITY_MODULES_ANIMATION
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3a8707ef624947a7ae8843ca6c70c0a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,53 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AlphaAnimation
|
||||
serializedVersion: 6
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db8faf64ca46248abb6624513ac1fb1b
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,53 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: DefaultAnimation
|
||||
serializedVersion: 6
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f6191147839943ab93e2171cc15c5e9
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,53 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Layer2Animation
|
||||
serializedVersion: 6
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d31c84f6372c54d7eb8decb27010d005
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,53 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: OverrideAlphaAnimation
|
||||
serializedVersion: 6
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05a2afc2ff8884d32afc64ed6765880a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,53 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: OverrideDefaultAnimation
|
||||
serializedVersion: 6
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf503a5569d0b4df4910a26d09ce4530
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,449 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1102 &-8144973961595650150
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: New State
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 0}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1102 &-7257898091357968356
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: New State
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 0}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1101 &-7235917949335567458
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 2
|
||||
m_ConditionEvent: Layer2AlphaParameter
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 6016706997111698284}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 2
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1101 &-6097014330458455406
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 2
|
||||
m_ConditionEvent: AlphaParameter
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: -1198466922477486815}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1107 &-1914299053840757887
|
||||
AnimatorStateMachine:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Base Layer
|
||||
m_ChildStates:
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: -1198466922477486815}
|
||||
m_Position: {x: 70, y: 290, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 320527679719022362}
|
||||
m_Position: {x: 110, y: 490, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 3942933370568001311}
|
||||
m_Position: {x: 380, y: 280, z: 0}
|
||||
m_ChildStateMachines: []
|
||||
m_AnyStateTransitions: []
|
||||
m_EntryTransitions: []
|
||||
m_StateMachineTransitions: {}
|
||||
m_StateMachineBehaviours: []
|
||||
m_AnyStatePosition: {x: 50, y: 20, z: 0}
|
||||
m_EntryPosition: {x: 30, y: 180, z: 0}
|
||||
m_ExitPosition: {x: 800, y: 120, z: 0}
|
||||
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
|
||||
m_DefaultState: {fileID: -1198466922477486815}
|
||||
--- !u!1102 &-1198466922477486815
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: DefaultState
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: 232953446134799302}
|
||||
- {fileID: 8340347106517238820}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: 1f6191147839943ab93e2171cc15c5e9, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!91 &9100000
|
||||
AnimatorController:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: TestAnimatorController
|
||||
serializedVersion: 5
|
||||
m_AnimatorParameters:
|
||||
- m_Name: AlphaParameter
|
||||
m_Type: 4
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: TestTrigger
|
||||
m_Type: 9
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: UnboundTrigger
|
||||
m_Type: 9
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: Layer2AlphaParameter
|
||||
m_Type: 4
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
m_AnimatorLayers:
|
||||
- serializedVersion: 5
|
||||
m_Name: Base Layer
|
||||
m_StateMachine: {fileID: -1914299053840757887}
|
||||
m_Mask: {fileID: 0}
|
||||
m_Motions: []
|
||||
m_Behaviours: []
|
||||
m_BlendingMode: 0
|
||||
m_SyncedLayerIndex: -1
|
||||
m_DefaultWeight: 0
|
||||
m_IKPass: 0
|
||||
m_SyncedLayerAffectsTiming: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- serializedVersion: 5
|
||||
m_Name: Layer2
|
||||
m_StateMachine: {fileID: 1433017894673297828}
|
||||
m_Mask: {fileID: 0}
|
||||
m_Motions: []
|
||||
m_Behaviours: []
|
||||
m_BlendingMode: 0
|
||||
m_SyncedLayerIndex: -1
|
||||
m_DefaultWeight: 1
|
||||
m_IKPass: 0
|
||||
m_SyncedLayerAffectsTiming: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
--- !u!1101 &232953446134799302
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: AlphaParameter
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 320527679719022362}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1102 &320527679719022362
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AlphaState
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: db8faf64ca46248abb6624513ac1fb1b, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1102 &927597079590233140
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Layer2AlphaState
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: -7235917949335567458}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: d31c84f6372c54d7eb8decb27010d005, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1107 &1433017894673297828
|
||||
AnimatorStateMachine:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Layer2
|
||||
m_ChildStates:
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 6016706997111698284}
|
||||
m_Position: {x: 160, y: 250, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 927597079590233140}
|
||||
m_Position: {x: 270, y: 370, z: 0}
|
||||
m_ChildStateMachines: []
|
||||
m_AnyStateTransitions: []
|
||||
m_EntryTransitions: []
|
||||
m_StateMachineTransitions: {}
|
||||
m_StateMachineBehaviours: []
|
||||
m_AnyStatePosition: {x: 50, y: 20, z: 0}
|
||||
m_EntryPosition: {x: 50, y: 120, z: 0}
|
||||
m_ExitPosition: {x: 800, y: 120, z: 0}
|
||||
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
|
||||
m_DefaultState: {fileID: 6016706997111698284}
|
||||
--- !u!1102 &3942933370568001311
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: TriggeredState
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: db8faf64ca46248abb6624513ac1fb1b, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1101 &5326371122012901575
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 2
|
||||
m_ConditionEvent: AlphaParameter
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: -1198466922477486815}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1102 &6016706997111698284
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: DefaultStateLayer2
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: 6324505406226331058}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 0}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1101 &6324505406226331058
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: Layer2AlphaParameter
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 927597079590233140}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 2
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1101 &8340347106517238820
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: TestTrigger
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 3942933370568001311}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0b8ebecb362240989d16159bdfa067c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 9100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,15 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!221 &22100000
|
||||
AnimatorOverrideController:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: TestAnimatorOverrideController
|
||||
m_Controller: {fileID: 9100000, guid: a0b8ebecb362240989d16159bdfa067c, type: 2}
|
||||
m_Clips:
|
||||
- m_OriginalClip: {fileID: 7400000, guid: 1f6191147839943ab93e2171cc15c5e9, type: 2}
|
||||
m_OverrideClip: {fileID: 7400000, guid: cf503a5569d0b4df4910a26d09ce4530, type: 2}
|
||||
- m_OriginalClip: {fileID: 7400000, guid: db8faf64ca46248abb6624513ac1fb1b, type: 2}
|
||||
m_OverrideClip: {fileID: 7400000, guid: 05a2afc2ff8884d32afc64ed6765880a, type: 2}
|
||||
@@ -2,6 +2,7 @@ using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
using Unity.Netcode.Components;
|
||||
|
||||
namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
@@ -21,6 +22,53 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
public class SimpleNetworkBehaviour : NetworkBehaviour
|
||||
{
|
||||
public bool OnNetworkDespawnCalled;
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
OnNetworkDespawnCalled = true;
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerator OnSetup()
|
||||
{
|
||||
m_AllowServerToStart = false;
|
||||
return base.OnSetup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This validates the fix for when a child GameObject with a NetworkBehaviour
|
||||
/// is deleted while the parent GameObject with a NetworkObject is spawned and
|
||||
/// is not deleted until a later time would cause an exception due to the
|
||||
/// NetworkBehaviour not being removed from the NetworkObject.ChildNetworkBehaviours
|
||||
/// list.
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator ValidatedDisableddNetworkBehaviourWarning()
|
||||
{
|
||||
m_AllowServerToStart = true;
|
||||
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// Now just start the Host
|
||||
yield return StartServerAndClients();
|
||||
|
||||
var parentObject = new GameObject();
|
||||
var childObject = new GameObject();
|
||||
childObject.name = "ChildObject";
|
||||
childObject.transform.parent = parentObject.transform;
|
||||
var parentNetworkObject = parentObject.AddComponent<NetworkObject>();
|
||||
var childBehaviour = childObject.AddComponent<NetworkTransform>();
|
||||
|
||||
// Set the child object to be inactive in the hierarchy
|
||||
childObject.SetActive(false);
|
||||
|
||||
LogAssert.Expect(LogType.Warning, $"{childObject.name} is disabled! Netcode for GameObjects does not support disabled NetworkBehaviours! The {childBehaviour.GetType().Name} component was skipped during ownership assignment!");
|
||||
LogAssert.Expect(LogType.Warning, $"{childObject.name} is disabled! Netcode for GameObjects does not support spawning disabled NetworkBehaviours! The {childBehaviour.GetType().Name} component was skipped during spawn!");
|
||||
|
||||
parentNetworkObject.Spawn();
|
||||
yield return s_DefaultWaitForTick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -42,6 +90,9 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// set the log level to developer
|
||||
m_ServerNetworkManager.LogLevel = LogLevel.Developer;
|
||||
|
||||
// The only valid condition for this would be if the NetworkBehaviour is spawned.
|
||||
simpleNetworkBehaviour.IsSpawned = true;
|
||||
|
||||
// Verify the warning gets logged under normal conditions
|
||||
var isNull = simpleNetworkBehaviour.NetworkObject == null;
|
||||
LogAssert.Expect(LogType.Warning, $"[Netcode] Could not get {nameof(NetworkObject)} for the {nameof(NetworkBehaviour)}. Are you missing a {nameof(NetworkObject)} component?");
|
||||
@@ -57,5 +108,44 @@ namespace Unity.Netcode.RuntimeTests
|
||||
networkObjectToTest.Despawn();
|
||||
Object.Destroy(networkObjectToTest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This validates the fix for when a child GameObject with a NetworkBehaviour
|
||||
/// is deleted while the parent GameObject with a NetworkObject is spawned and
|
||||
/// is not deleted until a later time would cause an exception due to the
|
||||
/// NetworkBehaviour not being removed from the NetworkObject.ChildNetworkBehaviours
|
||||
/// list.
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator ValidateDeleteChildNetworkBehaviour()
|
||||
{
|
||||
m_AllowServerToStart = true;
|
||||
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// Now just start the Host
|
||||
yield return StartServerAndClients();
|
||||
|
||||
var parentObject = new GameObject();
|
||||
var childObject = new GameObject();
|
||||
childObject.transform.parent = parentObject.transform;
|
||||
var parentNetworkObject = parentObject.AddComponent<NetworkObject>();
|
||||
childObject.AddComponent<SimpleNetworkBehaviour>();
|
||||
|
||||
parentNetworkObject.Spawn();
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// Destroy the child object with child NetworkBehaviour
|
||||
Object.Destroy(childObject);
|
||||
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// Assure no log messages are logged when they should not be logged
|
||||
LogAssert.NoUnexpectedReceived();
|
||||
|
||||
// Destroy the parent object which should not cause any exceptions
|
||||
// (validating the fix)
|
||||
Object.Destroy(parentObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
public NetworkVariable<int> MyNetworkVariable;
|
||||
|
||||
private void Start()
|
||||
private void Awake()
|
||||
{
|
||||
MyNetworkVariable = new NetworkVariable<int>();
|
||||
MyNetworkVariable.OnValueChanged += Changed;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,17 +15,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
public bool FieldWritten;
|
||||
public bool DeltaRead;
|
||||
public bool FieldRead;
|
||||
public bool Dirty = false;
|
||||
|
||||
public override void ResetDirty()
|
||||
{
|
||||
Dirty = false;
|
||||
}
|
||||
|
||||
public override bool IsDirty()
|
||||
{
|
||||
return Dirty;
|
||||
}
|
||||
|
||||
public override void WriteDelta(FastBufferWriter writer)
|
||||
{
|
||||
@@ -138,12 +127,12 @@ namespace Unity.Netcode.RuntimeTests
|
||||
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for client side DummyNetBehaviour to register it was spawned!");
|
||||
|
||||
// Check that FieldWritten is written when dirty
|
||||
serverComponent.NetVar.Dirty = true;
|
||||
serverComponent.NetVar.SetDirty(true);
|
||||
yield return s_DefaultWaitForTick;
|
||||
Assert.True(serverComponent.NetVar.FieldWritten);
|
||||
|
||||
// Check that DeltaWritten is written when dirty
|
||||
serverComponent.NetVar.Dirty = true;
|
||||
serverComponent.NetVar.SetDirty(true);
|
||||
yield return s_DefaultWaitForTick;
|
||||
Assert.True(serverComponent.NetVar.DeltaWritten);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
public NetworkVariable<Vector3> OwnerWritable_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableBase.DefaultReadPerm, NetworkVariableWritePermission.Owner);
|
||||
public NetworkVariable<Vector3> ServerWritable_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableBase.DefaultReadPerm, NetworkVariableWritePermission.Server);
|
||||
public NetworkVariable<Vector3> OwnerReadWrite_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Owner);
|
||||
}
|
||||
|
||||
[TestFixtureSource(nameof(TestDataSource))]
|
||||
@@ -104,6 +105,42 @@ namespace Unity.Netcode.RuntimeTests
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CheckOwnerReadWriteAreEqualOnOwnerAndServer()
|
||||
{
|
||||
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
|
||||
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
|
||||
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
||||
{
|
||||
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
|
||||
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
|
||||
if (testObjServer.OwnerClientId == testObjClient.OwnerClientId &&
|
||||
testCompServer.OwnerReadWrite_Position.Value == testCompClient.ServerWritable_Position.Value &&
|
||||
testCompServer.OwnerReadWrite_Position.ReadPerm == testCompClient.ServerWritable_Position.ReadPerm &&
|
||||
testCompServer.OwnerReadWrite_Position.WritePerm == testCompClient.ServerWritable_Position.WritePerm)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CheckOwnerReadWriteAreNotEqualOnNonOwnerClients(NetVarPermTestComp ownerReadWriteObject)
|
||||
{
|
||||
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
||||
{
|
||||
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
|
||||
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
|
||||
if (testObjClient.OwnerClientId != ownerReadWriteObject.OwnerClientId ||
|
||||
ownerReadWriteObject.OwnerReadWrite_Position.Value == testCompClient.ServerWritable_Position.Value ||
|
||||
ownerReadWriteObject.OwnerReadWrite_Position.ReadPerm != testCompClient.ServerWritable_Position.ReadPerm ||
|
||||
ownerReadWriteObject.OwnerReadWrite_Position.WritePerm != testCompClient.ServerWritable_Position.WritePerm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ServerChangesOwnerWritableNetVar()
|
||||
{
|
||||
@@ -164,6 +201,44 @@ namespace Unity.Netcode.RuntimeTests
|
||||
yield return WaitForOwnerWritableAreEqualOnAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This tests the scenario where a client owner has both read and write
|
||||
/// permissions set. The server should be the only instance that can read
|
||||
/// the NetworkVariable. ServerCannotChangeOwnerWritableNetVar performs
|
||||
/// the same check to make sure the server cannot write to a client owner
|
||||
/// NetworkVariable with owner write permissions.
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator ClientOwnerWithReadWriteChangesNetVar()
|
||||
{
|
||||
yield return WaitForOwnerWritableAreEqualOnAll();
|
||||
|
||||
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
|
||||
|
||||
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
|
||||
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
|
||||
testObjServer.ChangeOwnership(newOwnerClientId);
|
||||
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 2);
|
||||
|
||||
yield return WaitForOwnerWritableAreEqualOnAll();
|
||||
|
||||
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
|
||||
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
|
||||
|
||||
var oldValue = testCompClient.OwnerReadWrite_Position.Value;
|
||||
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
|
||||
|
||||
testCompClient.OwnerWritable_Position.Value = newValue;
|
||||
yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue);
|
||||
|
||||
// Verify the client owner and server match
|
||||
yield return CheckOwnerReadWriteAreEqualOnOwnerAndServer();
|
||||
|
||||
// Verify the non-owner clients do not have the same Value but do have the same permissions
|
||||
yield return CheckOwnerReadWriteAreNotEqualOnNonOwnerClients(testCompClient);
|
||||
}
|
||||
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ClientCannotChangeServerWritableNetVar()
|
||||
{
|
||||
|
||||
103
Tests/Runtime/OwnerModifiedTests.cs
Normal file
103
Tests/Runtime/OwnerModifiedTests.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
|
||||
namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
// This is a bit of a quirky test.
|
||||
// Addresses MTT-4386 #2109
|
||||
// Where the NetworkVariable updates would be repeated on some clients.
|
||||
// The twist comes fom the updates needing to happens very specifically for the issue to repro in tests
|
||||
|
||||
public class OwnerModifiedObject : NetworkBehaviour, INetworkUpdateSystem
|
||||
{
|
||||
public NetworkList<int> MyNetworkList;
|
||||
|
||||
static internal int Updates = 0;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
MyNetworkList = new NetworkList<int>(new List<int>(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
MyNetworkList.OnListChanged += Changed;
|
||||
}
|
||||
|
||||
public void Changed(NetworkListEvent<int> listEvent)
|
||||
{
|
||||
var expected = 0;
|
||||
var listString = "";
|
||||
foreach (var i in MyNetworkList)
|
||||
{
|
||||
Assert.AreEqual(i, expected);
|
||||
expected++;
|
||||
listString += i.ToString();
|
||||
}
|
||||
Debug.Log($"[{NetworkManager.LocalClientId}] Value changed to {listString}");
|
||||
Updates++;
|
||||
}
|
||||
|
||||
public bool AddValues;
|
||||
|
||||
public NetworkUpdateStage NetworkUpdateStageToCheck;
|
||||
|
||||
private int m_ValueToUpdate;
|
||||
|
||||
public void NetworkUpdate(NetworkUpdateStage updateStage)
|
||||
{
|
||||
if (updateStage == NetworkUpdateStageToCheck)
|
||||
{
|
||||
if (AddValues)
|
||||
{
|
||||
MyNetworkList.Add(m_ValueToUpdate++);
|
||||
AddValues = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
NetworkUpdateLoop.UnregisterAllNetworkUpdates(this);
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
public void InitializeLastCient()
|
||||
{
|
||||
NetworkUpdateLoop.RegisterAllNetworkUpdates(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class OwnerModifiedTests : NetcodeIntegrationTest
|
||||
{
|
||||
protected override int NumberOfClients => 2;
|
||||
|
||||
protected override void OnCreatePlayerPrefab()
|
||||
{
|
||||
m_PlayerPrefab.AddComponent<OwnerModifiedObject>();
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator OwnerModifiedTest()
|
||||
{
|
||||
// We use this to assure we are the "last client" connected.
|
||||
yield return CreateAndStartNewClient();
|
||||
var ownerModLastClient = m_ClientNetworkManagers[2].LocalClient.PlayerObject.GetComponent<OwnerModifiedObject>();
|
||||
ownerModLastClient.InitializeLastCient();
|
||||
|
||||
// Run through all update loops setting the value once every 5 frames
|
||||
foreach (var updateLoopType in System.Enum.GetValues(typeof(NetworkUpdateStage)))
|
||||
{
|
||||
ownerModLastClient.NetworkUpdateStageToCheck = (NetworkUpdateStage)updateLoopType;
|
||||
Debug.Log($"Testing Update Stage: {ownerModLastClient.NetworkUpdateStageToCheck}");
|
||||
ownerModLastClient.AddValues = true;
|
||||
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 5);
|
||||
}
|
||||
|
||||
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 5);
|
||||
|
||||
// We'll have at least one update per stage per client, if all goes well.
|
||||
Assert.True(OwnerModifiedObject.Updates > 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Tests/Runtime/OwnerModifiedTests.cs.meta
Normal file
11
Tests/Runtime/OwnerModifiedTests.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 419d83ebac7544ea9b0a9d5c3eab2c71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -10,21 +10,19 @@ namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
public class TransformInterpolationObject : NetworkBehaviour
|
||||
{
|
||||
// Set the minimum threshold which we will use as our margin of error
|
||||
public const float MinThreshold = 0.001f;
|
||||
|
||||
public bool CheckPosition;
|
||||
public bool IsMoving;
|
||||
public bool IsFixed;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// Since the local position is transformed from local to global and vice-versa on the server and client
|
||||
// it may accumulate some error. We allow an error of 0.01 over the range of 1000 used in this test.
|
||||
// This requires precision to 5 digits, so it doesn't weaken the test, while preventing spurious failures
|
||||
const float maxRoundingError = 0.01f;
|
||||
|
||||
// Check the position of the nested object on the client
|
||||
if (CheckPosition)
|
||||
{
|
||||
if (transform.position.y < -maxRoundingError || transform.position.y > 100.0f + maxRoundingError)
|
||||
if (transform.position.y < -MinThreshold || transform.position.y > 100.0f + MinThreshold)
|
||||
{
|
||||
Debug.LogError($"Interpolation failure. transform.position.y is {transform.position.y}. Should be between 0.0 and 100.0");
|
||||
}
|
||||
@@ -65,7 +63,8 @@ namespace Unity.Netcode.RuntimeTests
|
||||
protected override void OnServerAndClientsCreated()
|
||||
{
|
||||
m_PrefabToSpawn = CreateNetworkObjectPrefab("InterpTestObject");
|
||||
m_PrefabToSpawn.AddComponent<NetworkTransform>();
|
||||
var networkTransform = m_PrefabToSpawn.AddComponent<NetworkTransform>();
|
||||
networkTransform.PositionThreshold = TransformInterpolationObject.MinThreshold;
|
||||
m_PrefabToSpawn.AddComponent<TransformInterpolationObject>();
|
||||
}
|
||||
|
||||
@@ -85,8 +84,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
m_SpawnedObjectOnClient = s_GlobalNetworkObjects[clientId][m_SpawnedAsNetworkObject.NetworkObjectId];
|
||||
// make sure the objects are set with the right network manager
|
||||
m_SpawnedObjectOnClient.NetworkManagerOwner = m_ClientNetworkManagers[0];
|
||||
|
||||
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
|
||||
@@ -457,5 +457,26 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReliablePayloadsCanBeLargerThanMaximum()
|
||||
{
|
||||
InitializeTransport(out m_Server, out m_ServerEvents);
|
||||
InitializeTransport(out m_Client1, out m_Client1Events);
|
||||
|
||||
m_Server.StartServer();
|
||||
m_Client1.StartClient();
|
||||
|
||||
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
|
||||
|
||||
var payloadSize = UnityTransport.InitialMaxPayloadSize + 1;
|
||||
var data = new ArraySegment<byte>(new byte[payloadSize]);
|
||||
|
||||
m_Server.Send(m_Client1.ServerClientId, data, NetworkDelivery.Reliable);
|
||||
|
||||
yield return WaitForNetworkEvent(NetworkEvent.Data, m_Client1Events);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
],
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.unity.multiplayer.tools",
|
||||
|
||||
11
package.json
11
package.json
@@ -2,22 +2,19 @@
|
||||
"name": "com.unity.netcode.gameobjects",
|
||||
"displayName": "Netcode for GameObjects",
|
||||
"description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.",
|
||||
"version": "1.0.0-pre.10",
|
||||
"version": "1.0.2",
|
||||
"unity": "2020.3",
|
||||
"dependencies": {
|
||||
"com.unity.nuget.mono-cecil": "1.10.1",
|
||||
"com.unity.transport": "1.1.0"
|
||||
},
|
||||
"_upm": {
|
||||
"changelog": "### Added\n\n- Added a new `OnTransportFailure` callback to `NetworkManager`. This callback is invoked when the manager's `NetworkTransport` encounters an unrecoverable error. Transport failures also cause the `NetworkManager` to shut down. Currently, this is only used by `UnityTransport` to signal a timeout of its connection to the Unity Relay servers. (#1994)\n- Added `NetworkEvent.TransportFailure`, which can be used by implementations of `NetworkTransport` to signal to `NetworkManager` that an unrecoverable error was encountered. (#1994)\n- Added test to ensure a warning occurs when nesting NetworkObjects in a NetworkPrefab (#1969)\n- Added `NetworkManager.RemoveNetworkPrefab(...)` to remove a prefab from the prefabs list (#1950)\n\n### Changed\n\n- Updated `UnityTransport` dependency on `com.unity.transport` to 1.1.0. (#2025)\n- (API Breaking) `ConnectionApprovalCallback` is no longer an `event` and will not allow more than 1 handler registered at a time. Also, `ConnectionApprovalCallback` is now a `Func<>` taking `ConnectionApprovalRequest` in and returning `ConnectionApprovalResponse` back out (#1972)\n\n### Removed\n\n### Fixed\n- Fixed issue where dynamically spawned `NetworkObject`s could throw an exception if the scene of origin handle was zero (0) and the `NetworkObject` was already spawned. (#2017)\n- Fixed issue where `NetworkObject.Observers` was not being cleared when despawned. (#2009)\n- Fixed `NetworkAnimator` could not run in the server authoritative mode. (#2003)\n- Fixed issue where late joining clients would get a soft synchronization error if any in-scene placed NetworkObjects were parented under another `NetworkObject`. (#1985)\n- Fixed issue where `NetworkBehaviourReference` would throw a type cast exception if using `NetworkBehaviourReference.TryGet` and the component type was not found. (#1984)\n- Fixed `NetworkSceneManager` was not sending scene event notifications for the currently active scene and any additively loaded scenes when loading a new scene in `LoadSceneMode.Single` mode. (#1975)\n- Fixed issue where one or more clients disconnecting during a scene event would cause `LoadEventCompleted` or `UnloadEventCompleted` to wait until the `NetworkConfig.LoadSceneTimeOut` period before being triggered. (#1973)\n- Fixed issues when multiple `ConnectionApprovalCallback`s were registered (#1972)\n- Fixed a regression in serialization support: `FixedString`, `Vector2Int`, and `Vector3Int` types can now be used in NetworkVariables and RPCs again without requiring a `ForceNetworkSerializeByMemcpy<>` wrapper. (#1961)\n- Fixed generic types that inherit from NetworkBehaviour causing crashes at compile time. (#1976)\n- Fixed endless dialog boxes when adding a `NetworkBehaviour` to a `NetworkManager` or vice-versa. (#1947)\n- Fixed `NetworkAnimator` issue where it was only synchronizing parameters if the layer or state changed or was transitioning between states. (#1946)\n- Fixed `NetworkAnimator` issue where when it did detect a parameter had changed it would send all parameters as opposed to only the parameters that changed. (#1946)\n- Fixed `NetworkAnimator` issue where it was not always disposing the `NativeArray` that is allocated when spawned. (#1946)\n- Fixed `NetworkAnimator` issue where it was not taking the animation speed or state speed multiplier into consideration. (#1946)\n- Fixed `NetworkAnimator` issue where it was not properly synchronizing late joining clients if they joined while `Animator` was transitioning between states. (#1946)\n- Fixed `NetworkAnimator` issue where the server was not relaying changes to non-owner clients when a client was the owner. (#1946)\n- Fixed issue where the `PacketLoss` metric for tools would return the packet loss over a connection lifetime instead of a single frame. (#2004)"
|
||||
"com.unity.transport": "1.2.0"
|
||||
},
|
||||
"upmCi": {
|
||||
"footprint": "13bd9771fd94050d43c32b238d73617da4b2389d"
|
||||
"footprint": "01764b7751e27d1e2af672c49cec3ed5691b53b7"
|
||||
},
|
||||
"repository": {
|
||||
"url": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git",
|
||||
"type": "git",
|
||||
"revision": "d1302ce0b946a675f49425dbd11ee2c4afe89d3f"
|
||||
"revision": "fe0c300aa691f31d2aec1d4b73e2971f28122d3b"
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user