com.unity.netcode.gameobjects@1.0.0-pre.7
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [1.0.0-pre.7] - 2022-04-01 ### Added - Added editor only check prior to entering into play mode if the currently open and active scene is in the build list and if not displays a dialog box asking the user if they would like to automatically add it prior to entering into play mode. (#1828) - Added `UnityTransport` implementation and `com.unity.transport` package dependency (#1823) - Added `NetworkVariableWritePermission` to `NetworkVariableBase` and implemented `Owner` client writable netvars. (#1762) - `UnityTransport` settings can now be set programmatically. (#1845) - `FastBufferWriter` and Reader IsInitialized property. (#1859) ### Changed - Updated `UnityTransport` dependency on `com.unity.transport` to 1.0.0 (#1849) ### Removed - Removed `SnapshotSystem` (#1852) - Removed `com.unity.modules.animation`, `com.unity.modules.physics` and `com.unity.modules.physics2d` dependencies from the package (#1812) - Removed `com.unity.collections` dependency from the package (#1849) ### Fixed - Fixed in-scene placed NetworkObjects not being found/ignored after a client disconnects and then reconnects. (#1850) - Fixed issue where `UnityTransport` send queues were not flushed when calling `DisconnectLocalClient` or `DisconnectRemoteClient`. (#1847) - Fixed NetworkBehaviour dependency verification check for an existing NetworkObject not searching from root parent transform relative GameObject. (#1841) - Fixed issue where entries were not being removed from the NetworkSpawnManager.OwnershipToObjectsTable. (#1838) - Fixed ClientRpcs would always send to all connected clients by default as opposed to only sending to the NetworkObject's Observers list by default. (#1836) - Fixed clarity for NetworkSceneManager client side notification when it receives a scene hash value that does not exist in its local hash table. (#1828) - Fixed client throws a key not found exception when it times out using UNet or UTP. (#1821) - Fixed network variable updates are no longer limited to 32,768 bytes when NetworkConfig.EnsureNetworkVariableLengthSafety is enabled. The limits are now determined by what the transport can send in a message. (#1811) - Fixed in-scene NetworkObjects get destroyed if a client fails to connect and shuts down the NetworkManager. (#1809) - Fixed user never being notified in the editor that a NetworkBehaviour requires a NetworkObject to function properly. (#1808) - Fixed PlayerObjects and dynamically spawned NetworkObjects not being added to the NetworkClient's OwnedObjects (#1801) - Fixed issue where NetworkManager would continue starting even if the NetworkTransport selected failed. (#1780) - Fixed issue when spawning new player if an already existing player exists it does not remove IsPlayer from the previous player (#1779) - Fixed lack of notification that NetworkManager and NetworkObject cannot be added to the same GameObject with in-editor notifications (#1777) - Fixed parenting warning printing for false positives (#1855)
This commit is contained in:
38
CHANGELOG.md
38
CHANGELOG.md
@@ -6,6 +6,43 @@ 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).
|
Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).
|
||||||
|
|
||||||
|
## [1.0.0-pre.7] - 2022-04-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added editor only check prior to entering into play mode if the currently open and active scene is in the build list and if not displays a dialog box asking the user if they would like to automatically add it prior to entering into play mode. (#1828)
|
||||||
|
- Added `UnityTransport` implementation and `com.unity.transport` package dependency (#1823)
|
||||||
|
- Added `NetworkVariableWritePermission` to `NetworkVariableBase` and implemented `Owner` client writable netvars. (#1762)
|
||||||
|
- `UnityTransport` settings can now be set programmatically. (#1845)
|
||||||
|
- `FastBufferWriter` and Reader IsInitialized property. (#1859)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated `UnityTransport` dependency on `com.unity.transport` to 1.0.0 (#1849)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Removed `SnapshotSystem` (#1852)
|
||||||
|
- Removed `com.unity.modules.animation`, `com.unity.modules.physics` and `com.unity.modules.physics2d` dependencies from the package (#1812)
|
||||||
|
- Removed `com.unity.collections` dependency from the package (#1849)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed in-scene placed NetworkObjects not being found/ignored after a client disconnects and then reconnects. (#1850)
|
||||||
|
- Fixed issue where `UnityTransport` send queues were not flushed when calling `DisconnectLocalClient` or `DisconnectRemoteClient`. (#1847)
|
||||||
|
- Fixed NetworkBehaviour dependency verification check for an existing NetworkObject not searching from root parent transform relative GameObject. (#1841)
|
||||||
|
- Fixed issue where entries were not being removed from the NetworkSpawnManager.OwnershipToObjectsTable. (#1838)
|
||||||
|
- Fixed ClientRpcs would always send to all connected clients by default as opposed to only sending to the NetworkObject's Observers list by default. (#1836)
|
||||||
|
- Fixed clarity for NetworkSceneManager client side notification when it receives a scene hash value that does not exist in its local hash table. (#1828)
|
||||||
|
- Fixed client throws a key not found exception when it times out using UNet or UTP. (#1821)
|
||||||
|
- Fixed network variable updates are no longer limited to 32,768 bytes when NetworkConfig.EnsureNetworkVariableLengthSafety is enabled. The limits are now determined by what the transport can send in a message. (#1811)
|
||||||
|
- Fixed in-scene NetworkObjects get destroyed if a client fails to connect and shuts down the NetworkManager. (#1809)
|
||||||
|
- Fixed user never being notified in the editor that a NetworkBehaviour requires a NetworkObject to function properly. (#1808)
|
||||||
|
- Fixed PlayerObjects and dynamically spawned NetworkObjects not being added to the NetworkClient's OwnedObjects (#1801)
|
||||||
|
- Fixed issue where NetworkManager would continue starting even if the NetworkTransport selected failed. (#1780)
|
||||||
|
- Fixed issue when spawning new player if an already existing player exists it does not remove IsPlayer from the previous player (#1779)
|
||||||
|
- Fixed lack of notification that NetworkManager and NetworkObject cannot be added to the same GameObject with in-editor notifications (#1777)
|
||||||
|
- Fixed parenting warning printing for false positives (#1855)
|
||||||
|
|
||||||
## [1.0.0-pre.6] - 2022-03-02
|
## [1.0.0-pre.6] - 2022-03-02
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -33,6 +70,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
|
|||||||
- Fixed OwnedObjects not being properly modified when using ChangeOwnership (#1731)
|
- Fixed OwnedObjects not being properly modified when using ChangeOwnership (#1731)
|
||||||
- Improved performance in NetworkAnimator (#1735)
|
- Improved performance in NetworkAnimator (#1735)
|
||||||
- Removed the "always sync" network animator (aka "autosend") parameters (#1746)
|
- Removed the "always sync" network animator (aka "autosend") parameters (#1746)
|
||||||
|
- Fixed in-scene placed NetworkObjects not respawning after shutting down the NetworkManager and then starting it back up again (#1769)
|
||||||
|
|
||||||
## [1.0.0-pre.5] - 2022-01-26
|
## [1.0.0-pre.5] - 2022-01-26
|
||||||
|
|
||||||
|
|||||||
@@ -186,7 +186,14 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (t < 0.0f)
|
if (t < 0.0f)
|
||||||
{
|
{
|
||||||
throw new OverflowException($"t = {t} but must be >= 0. range {range}, RenderTime {renderTime}, Start time {m_StartTimeConsumed}, end time {m_EndTimeConsumed}");
|
// There is no mechanism to guarantee renderTime to not be before m_StartTimeConsumed
|
||||||
|
// This clamps t to a minimum of 0 and fixes issues with longer frames and pauses
|
||||||
|
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
|
{
|
||||||
|
NetworkLog.LogError($"renderTime was before m_StartTimeConsumed. This should never happen. {nameof(renderTime)} is {renderTime}, {nameof(m_StartTimeConsumed)} is {m_StartTimeConsumed}");
|
||||||
|
}
|
||||||
|
t = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t > 3.0f) // max extrapolation
|
if (t > 3.0f) // max extrapolation
|
||||||
@@ -218,6 +225,8 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
m_LastBufferedItemReceived = new BufferedItem(newMeasurement, sentTime);
|
m_LastBufferedItemReceived = new BufferedItem(newMeasurement, sentTime);
|
||||||
ResetTo(newMeasurement, sentTime);
|
ResetTo(newMeasurement, sentTime);
|
||||||
|
// Next line keeps renderTime above m_StartTimeConsumed. Fixes pause/unpause issues
|
||||||
|
m_Buffer.Add(m_LastBufferedItemReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#if COM_UNITY_MODULES_ANIMATION
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using Unity.Collections.LowLevel.Unsafe;
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -5,7 +6,7 @@ using UnityEngine;
|
|||||||
namespace Unity.Netcode.Components
|
namespace Unity.Netcode.Components
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A prototype component for syncing animations
|
/// NetworkAnimator enables remote synchronization of <see cref="UnityEngine.Animator"/> state for on network objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AddComponentMenu("Netcode/" + nameof(NetworkAnimator))]
|
[AddComponentMenu("Netcode/" + nameof(NetworkAnimator))]
|
||||||
[RequireComponent(typeof(Animator))]
|
[RequireComponent(typeof(Animator))]
|
||||||
@@ -380,9 +381,7 @@ namespace Unity.Netcode.Components
|
|||||||
SetTrigger(Animator.StringToHash(triggerName));
|
SetTrigger(Animator.StringToHash(triggerName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="SetTrigger(string)" />
|
||||||
/// Sets the trigger for the associated animation. See note for SetTrigger(string)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hash">The hash for the trigger to activate</param>
|
/// <param name="hash">The hash for the trigger to activate</param>
|
||||||
/// <param name="reset">If true, resets the trigger</param>
|
/// <param name="reset">If true, resets the trigger</param>
|
||||||
public void SetTrigger(int hash, bool reset = false)
|
public void SetTrigger(int hash, bool reset = false)
|
||||||
@@ -413,7 +412,7 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets the trigger for the associated animation. See note for SetTrigger(string)
|
/// Resets the trigger for the associated animation. See <see cref="SetTrigger(string)">SetTrigger</see> for more on how triggers are special
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="triggerName">The string name of the trigger to reset</param>
|
/// <param name="triggerName">The string name of the trigger to reset</param>
|
||||||
public void ResetTrigger(string triggerName)
|
public void ResetTrigger(string triggerName)
|
||||||
@@ -421,13 +420,12 @@ namespace Unity.Netcode.Components
|
|||||||
ResetTrigger(Animator.StringToHash(triggerName));
|
ResetTrigger(Animator.StringToHash(triggerName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="ResetTrigger(string)" path="summary" />
|
||||||
/// Resets the trigger for the associated animation. See note for SetTrigger(string)
|
/// <param name="hash">The hash for the trigger to activate</param>
|
||||||
/// </summary>
|
|
||||||
/// <param name="hash">The hash for the trigger to reset</param>
|
|
||||||
public void ResetTrigger(int hash)
|
public void ResetTrigger(int hash)
|
||||||
{
|
{
|
||||||
SetTrigger(hash, true);
|
SetTrigger(hash, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // COM_UNITY_MODULES_ANIMATION
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#if COM_UNITY_MODULES_PHYSICS
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Unity.Netcode.Components
|
namespace Unity.Netcode.Components
|
||||||
@@ -78,3 +79,4 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // COM_UNITY_MODULES_PHYSICS
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#if COM_UNITY_MODULES_PHYSICS2D
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Unity.Netcode.Components
|
namespace Unity.Netcode.Components
|
||||||
@@ -78,3 +79,4 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // COM_UNITY_MODULES_PHYSICS2D
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ namespace Unity.Netcode.Components
|
|||||||
/// If using different values, please use RPCs to write to the server. Netcode doesn't support client side network variable writing
|
/// If using different values, please use RPCs to write to the server. Netcode doesn't support client side network variable writing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// This is public to make sure that users don't depend on this IsClient && IsOwner check in their code. If this logic changes in the future, we can make it invisible here
|
// This is public to make sure that users don't depend on this IsClient && IsOwner check in their code. If this logic changes in the future, we can make it invisible here
|
||||||
public bool CanCommitToTransform;
|
public bool CanCommitToTransform { get; protected set; }
|
||||||
protected bool m_CachedIsServer;
|
protected bool m_CachedIsServer;
|
||||||
protected NetworkManager m_CachedNetworkManager;
|
protected NetworkManager m_CachedNetworkManager;
|
||||||
|
|
||||||
@@ -691,7 +691,6 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
if (!NetworkObject.IsSpawned)
|
if (!NetworkObject.IsSpawned)
|
||||||
{
|
{
|
||||||
// todo MTT-849 should never happen but yet it does! maybe revisit/dig after NetVar updates and snapshot system lands?
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -785,7 +784,7 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
m_ReplicatedNetworkState.SetDirty(true);
|
m_ReplicatedNetworkState.SetDirty(true);
|
||||||
}
|
}
|
||||||
else
|
else if (m_Transform != null)
|
||||||
{
|
{
|
||||||
ApplyInterpolatedNetworkStateToTransform(m_ReplicatedNetworkState.Value, m_Transform);
|
ApplyInterpolatedNetworkStateToTransform(m_ReplicatedNetworkState.Value, m_Transform);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,22 @@
|
|||||||
"Unity.Netcode.Runtime",
|
"Unity.Netcode.Runtime",
|
||||||
"Unity.Collections"
|
"Unity.Collections"
|
||||||
],
|
],
|
||||||
"allowUnsafeCode": true
|
"allowUnsafeCode": true,
|
||||||
|
"versionDefines": [
|
||||||
|
{
|
||||||
|
"name": "com.unity.modules.animation",
|
||||||
|
"expression": "",
|
||||||
|
"define": "COM_UNITY_MODULES_ANIMATION"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "com.unity.modules.physics",
|
||||||
|
"expression": "",
|
||||||
|
"define": "COM_UNITY_MODULES_PHYSICS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "com.unity.modules.physics2d",
|
||||||
|
"expression": "",
|
||||||
|
"define": "COM_UNITY_MODULES_PHYSICS2D"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using Unity.Netcode.Components;
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Unity.Netcode.Editor
|
|
||||||
{
|
|
||||||
[CustomEditor(typeof(NetworkAnimator), true)]
|
|
||||||
[CanEditMultipleObjects]
|
|
||||||
public class NetworkAnimatorEditor : UnityEditor.Editor
|
|
||||||
{
|
|
||||||
public override void OnInspectorGUI()
|
|
||||||
{
|
|
||||||
serializedObject.Update();
|
|
||||||
|
|
||||||
EditorGUI.BeginChangeCheck();
|
|
||||||
var label = new GUIContent("Animator", "The Animator component to synchronize");
|
|
||||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Animator"), label);
|
|
||||||
EditorGUI.EndChangeCheck();
|
|
||||||
|
|
||||||
serializedObject.ApplyModifiedProperties();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -211,5 +211,91 @@ namespace Unity.Netcode.Editor
|
|||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
EditorGUI.EndChangeCheck();
|
EditorGUI.EndChangeCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked once when a NetworkBehaviour component is
|
||||||
|
/// displayed in the inspector view.
|
||||||
|
/// </summary>
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
// When we first add a NetworkBehaviour this editor will be enabled
|
||||||
|
// so we go ahead and check for an already existing NetworkObject here
|
||||||
|
CheckForNetworkObject((target as NetworkBehaviour).gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal const string AutoAddNetworkObjectIfNoneExists = "AutoAdd-NetworkObject-When-None-Exist";
|
||||||
|
|
||||||
|
public static Transform GetRootParentTransform(Transform transform)
|
||||||
|
{
|
||||||
|
if (transform.parent == null || transform.parent == transform)
|
||||||
|
{
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
return GetRootParentTransform(transform.parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to determine if a GameObject has one or more NetworkBehaviours but
|
||||||
|
/// does not already have a NetworkObject component. If not it will notify
|
||||||
|
/// the user that NetworkBehaviours require a NetworkObject.
|
||||||
|
/// </summary>
|
||||||
|
public static void CheckForNetworkObject(GameObject gameObject, bool networkObjectRemoved = false)
|
||||||
|
{
|
||||||
|
// If there are no NetworkBehaviours or no gameObject, then exit early
|
||||||
|
if (gameObject == null || (gameObject.GetComponent<NetworkBehaviour>() == null && gameObject.GetComponentInChildren<NetworkBehaviour>() == null))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now get the root parent transform to the current GameObject (or itself)
|
||||||
|
var rootTransform = GetRootParentTransform(gameObject.transform);
|
||||||
|
|
||||||
|
// Otherwise, check to see if there is any NetworkObject from the root GameObject down to all children.
|
||||||
|
// If not, notify the user that NetworkBehaviours require that the relative GameObject has a NetworkObject component.
|
||||||
|
var networkObject = rootTransform.GetComponent<NetworkObject>();
|
||||||
|
if (networkObject == null)
|
||||||
|
{
|
||||||
|
networkObject = rootTransform.GetComponentInChildren<NetworkObject>();
|
||||||
|
|
||||||
|
if (networkObject == null)
|
||||||
|
{
|
||||||
|
// If we are removing a NetworkObject but there is still one or more NetworkBehaviour components
|
||||||
|
// and the user has already turned "Auto-Add NetworkObject" on when first notified about the requirement
|
||||||
|
// then just send a reminder to the user why the NetworkObject they just deleted seemingly "re-appeared"
|
||||||
|
// again.
|
||||||
|
if (networkObjectRemoved && EditorPrefs.HasKey(AutoAddNetworkObjectIfNoneExists) && EditorPrefs.GetBool(AutoAddNetworkObjectIfNoneExists))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"{gameObject.name} still has {nameof(NetworkBehaviour)}s and Auto-Add NetworkObjects is enabled. A NetworkObject is being added back to {gameObject.name}.");
|
||||||
|
Debug.Log($"To reset Auto-Add NetworkObjects: Select the Netcode->General->Reset Auto-Add NetworkObject menu item.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify and provide the option to add it one time, always add a NetworkObject, or do nothing and let the user manually add it
|
||||||
|
if (EditorUtility.DisplayDialog($"{nameof(NetworkBehaviour)}s require a {nameof(NetworkObject)}",
|
||||||
|
$"{gameObject.name} does not have a {nameof(NetworkObject)} component. Would you like to add one now?", "Yes", "No (manually add it)",
|
||||||
|
DialogOptOutDecisionType.ForThisMachine, AutoAddNetworkObjectIfNoneExists))
|
||||||
|
{
|
||||||
|
gameObject.AddComponent<NetworkObject>();
|
||||||
|
var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
||||||
|
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(activeScene);
|
||||||
|
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(activeScene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This allows users to reset the Auto-Add NetworkObject preference
|
||||||
|
/// so the next time they add a NetworkBehaviour to a GameObject without
|
||||||
|
/// a NetworkObject it will display the dialog box again and not
|
||||||
|
/// automatically add a NetworkObject.
|
||||||
|
/// </summary>
|
||||||
|
[MenuItem("Netcode/General/Reset Auto-Add NetworkObject", false, 1)]
|
||||||
|
private static void ResetMultiplayerToolsTipStatus()
|
||||||
|
{
|
||||||
|
if (EditorPrefs.HasKey(AutoAddNetworkObjectIfNoneExists))
|
||||||
|
{
|
||||||
|
EditorPrefs.SetBool(AutoAddNetworkObjectIfNoneExists, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,9 +135,13 @@ namespace Unity.Netcode.Editor
|
|||||||
m_NetworkPrefabsList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.NetworkPrefabs)), true, true, true, true);
|
m_NetworkPrefabsList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.NetworkPrefabs)), true, true, true, true);
|
||||||
m_NetworkPrefabsList.elementHeightCallback = index =>
|
m_NetworkPrefabsList.elementHeightCallback = index =>
|
||||||
{
|
{
|
||||||
var networkPrefab = m_NetworkPrefabsList.serializedProperty.GetArrayElementAtIndex(index);
|
var networkOverrideInt = 0;
|
||||||
var networkOverrideProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.Override));
|
if (m_NetworkPrefabsList.count > 0)
|
||||||
var networkOverrideInt = networkOverrideProp.enumValueIndex;
|
{
|
||||||
|
var networkPrefab = m_NetworkPrefabsList.serializedProperty.GetArrayElementAtIndex(index);
|
||||||
|
var networkOverrideProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.Override));
|
||||||
|
networkOverrideInt = networkOverrideProp.enumValueIndex;
|
||||||
|
}
|
||||||
|
|
||||||
return 8 + (networkOverrideInt == 0 ? EditorGUIUtility.singleLineHeight : (EditorGUIUtility.singleLineHeight * 2) + 5);
|
return 8 + (networkOverrideInt == 0 ? EditorGUIUtility.singleLineHeight : (EditorGUIUtility.singleLineHeight * 2) + 5);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
|
|
||||||
namespace Unity.Netcode.Editor
|
namespace Unity.Netcode.Editor
|
||||||
@@ -25,7 +27,6 @@ namespace Unity.Netcode.Editor
|
|||||||
{
|
{
|
||||||
Singleton = new NetworkManagerHelper();
|
Singleton = new NetworkManagerHelper();
|
||||||
NetworkManager.NetworkManagerHelper = Singleton;
|
NetworkManager.NetworkManagerHelper = Singleton;
|
||||||
|
|
||||||
EditorApplication.playModeStateChanged -= EditorApplication_playModeStateChanged;
|
EditorApplication.playModeStateChanged -= EditorApplication_playModeStateChanged;
|
||||||
EditorApplication.hierarchyChanged -= EditorApplication_hierarchyChanged;
|
EditorApplication.hierarchyChanged -= EditorApplication_hierarchyChanged;
|
||||||
|
|
||||||
@@ -40,20 +41,106 @@ namespace Unity.Netcode.Editor
|
|||||||
case PlayModeStateChange.ExitingEditMode:
|
case PlayModeStateChange.ExitingEditMode:
|
||||||
{
|
{
|
||||||
s_LastKnownNetworkManagerParents.Clear();
|
s_LastKnownNetworkManagerParents.Clear();
|
||||||
|
ScenesInBuildActiveSceneCheck();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detects if a user is trying to enter into play mode when both conditions are true:
|
||||||
|
/// - the currently active and open scene is not added to the scenes in build list
|
||||||
|
/// - an instance of a NetworkManager with scene management enabled can be found
|
||||||
|
/// If both conditions are met then the user is presented with a dialog box that
|
||||||
|
/// provides the user with the option to add the scene to the scenes in build list
|
||||||
|
/// before entering into play mode or the user can continue under those conditions.
|
||||||
|
///
|
||||||
|
/// ** When scene management is enabled the user should treat all scenes that need to
|
||||||
|
/// be synchronized using network scene management as if they were preparing for a build.
|
||||||
|
/// Any scene that needs to be loaded at run time has to be included in the scenes in
|
||||||
|
/// build list. **
|
||||||
|
/// </summary>
|
||||||
|
private static void ScenesInBuildActiveSceneCheck()
|
||||||
|
{
|
||||||
|
var scenesList = EditorBuildSettings.scenes.ToList();
|
||||||
|
var activeScene = SceneManager.GetActiveScene();
|
||||||
|
var isSceneInBuildSettings = scenesList.Where((c) => c.path == activeScene.path).Count() == 1;
|
||||||
|
var networkManager = Object.FindObjectOfType<NetworkManager>();
|
||||||
|
if (!isSceneInBuildSettings && networkManager != null)
|
||||||
|
{
|
||||||
|
if (networkManager.NetworkConfig != null && networkManager.NetworkConfig.EnableSceneManagement)
|
||||||
|
{
|
||||||
|
if (EditorUtility.DisplayDialog("Add Scene to Scenes in Build", $"The current scene was not found in the scenes" +
|
||||||
|
$" in build and a {nameof(NetworkManager)} instance was found with scene management enabled! Clients will not be able " +
|
||||||
|
$"to synchronize to this scene unless it is added to the scenes in build list.\n\nWould you like to add it now?",
|
||||||
|
"Yes", "No - Continue"))
|
||||||
|
{
|
||||||
|
scenesList.Add(new EditorBuildSettingsScene(activeScene.path, true));
|
||||||
|
EditorBuildSettings.scenes = scenesList.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked only when the hierarchy changes
|
||||||
|
/// </summary>
|
||||||
private static void EditorApplication_hierarchyChanged()
|
private static void EditorApplication_hierarchyChanged()
|
||||||
{
|
{
|
||||||
var allNetworkManagers = Resources.FindObjectsOfTypeAll<NetworkManager>();
|
var allNetworkManagers = Resources.FindObjectsOfTypeAll<NetworkManager>();
|
||||||
foreach (var networkManager in allNetworkManagers)
|
foreach (var networkManager in allNetworkManagers)
|
||||||
{
|
{
|
||||||
networkManager.NetworkManagerCheckForParent();
|
if (!networkManager.NetworkManagerCheckForParent())
|
||||||
|
{
|
||||||
|
Singleton.CheckAndNotifyUserNetworkObjectRemoved(networkManager);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles notifying users that they cannot add a NetworkObject component
|
||||||
|
/// to a GameObject that also has a NetworkManager component. The NetworkObject
|
||||||
|
/// will always be removed.
|
||||||
|
/// GameObject + NetworkObject then NetworkManager = NetworkObject removed
|
||||||
|
/// GameObject + NetworkManager then NetworkObject = NetworkObject removed
|
||||||
|
/// Note: Since this is always invoked after <see cref="NetworkManagerCheckForParent"/>
|
||||||
|
/// we do not need to check for parent when searching for a NetworkObject component
|
||||||
|
/// </summary>
|
||||||
|
public void CheckAndNotifyUserNetworkObjectRemoved(NetworkManager networkManager, bool editorTest = false)
|
||||||
|
{
|
||||||
|
// Check for any NetworkObject at the same gameObject relative layer
|
||||||
|
var networkObject = networkManager.gameObject.GetComponent<NetworkObject>();
|
||||||
|
|
||||||
|
if (networkObject == null)
|
||||||
|
{
|
||||||
|
// if none is found, check to see if any children have a NetworkObject
|
||||||
|
networkObject = networkManager.gameObject.GetComponentInChildren<NetworkObject>();
|
||||||
|
if (networkObject == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EditorApplication.isUpdating)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(networkObject);
|
||||||
|
|
||||||
|
if (!EditorApplication.isPlaying && !editorTest)
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog($"Removing {nameof(NetworkObject)}", NetworkManagerAndNetworkObjectNotAllowedMessage(), "OK");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError(NetworkManagerAndNetworkObjectNotAllowedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NetworkManagerAndNetworkObjectNotAllowedMessage()
|
||||||
|
{
|
||||||
|
return $"A {nameof(GameObject)} cannot have both a {nameof(NetworkManager)} and {nameof(NetworkObject)} assigned to it or any children under it.";
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles notifying the user, via display dialog window, that they have nested a NetworkManager.
|
/// Handles notifying the user, via display dialog window, that they have nested a NetworkManager.
|
||||||
/// When in edit mode it provides the option to automatically fix the issue
|
/// When in edit mode it provides the option to automatically fix the issue
|
||||||
|
|||||||
@@ -100,5 +100,32 @@ namespace Unity.Netcode.Editor
|
|||||||
GUI.enabled = guiEnabled;
|
GUI.enabled = guiEnabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Saved for use in OnDestroy
|
||||||
|
private GameObject m_GameObject;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked once when a NetworkObject component is
|
||||||
|
/// displayed in the inspector view.
|
||||||
|
/// </summary>
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
// We set the GameObject upon being enabled because when the
|
||||||
|
// NetworkObject component is removed (i.e. when OnDestroy is invoked)
|
||||||
|
// it is no longer valid/available.
|
||||||
|
m_GameObject = (target as NetworkObject).gameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked once when a NetworkObject component is
|
||||||
|
/// no longer displayed in the inspector view.
|
||||||
|
/// </summary>
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
// Since this is also invoked when a NetworkObject component is removed
|
||||||
|
// from a GameObject, we go ahead and check for a NetworkObject when
|
||||||
|
// this custom editor is destroyed.
|
||||||
|
NetworkBehaviourEditor.CheckForNetworkObject(m_GameObject, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Unity.Netcode.Components;
|
|||||||
|
|
||||||
namespace Unity.Netcode.Editor
|
namespace Unity.Netcode.Editor
|
||||||
{
|
{
|
||||||
[CustomEditor(typeof(NetworkTransform))]
|
[CustomEditor(typeof(NetworkTransform), true)]
|
||||||
public class NetworkTransformEditor : UnityEditor.Editor
|
public class NetworkTransformEditor : UnityEditor.Editor
|
||||||
{
|
{
|
||||||
private SerializedProperty m_SyncPositionXProperty;
|
private SerializedProperty m_SyncPositionXProperty;
|
||||||
@@ -112,6 +112,7 @@ namespace Unity.Netcode.Editor
|
|||||||
EditorGUILayout.PropertyField(m_InLocalSpaceProperty);
|
EditorGUILayout.PropertyField(m_InLocalSpaceProperty);
|
||||||
EditorGUILayout.PropertyField(m_InterpolateProperty);
|
EditorGUILayout.PropertyField(m_InterpolateProperty);
|
||||||
|
|
||||||
|
#if COM_UNITY_MODULES_PHYSICS
|
||||||
// if rigidbody is present but network rigidbody is not present
|
// if rigidbody is present but network rigidbody is not present
|
||||||
var go = ((NetworkTransform)target).gameObject;
|
var go = ((NetworkTransform)target).gameObject;
|
||||||
if (go.TryGetComponent<Rigidbody>(out _) && go.TryGetComponent<NetworkRigidbody>(out _) == false)
|
if (go.TryGetComponent<Rigidbody>(out _) && go.TryGetComponent<NetworkRigidbody>(out _) == false)
|
||||||
@@ -119,12 +120,15 @@ namespace Unity.Netcode.Editor
|
|||||||
EditorGUILayout.HelpBox("This GameObject contains a Rigidbody but no NetworkRigidbody.\n" +
|
EditorGUILayout.HelpBox("This GameObject contains a Rigidbody but no NetworkRigidbody.\n" +
|
||||||
"Add a NetworkRigidbody component to improve Rigidbody synchronization.", MessageType.Warning);
|
"Add a NetworkRigidbody component to improve Rigidbody synchronization.", MessageType.Warning);
|
||||||
}
|
}
|
||||||
|
#endif // COM_UNITY_MODULES_PHYSICS
|
||||||
|
|
||||||
|
#if COM_UNITY_MODULES_PHYSICS2D
|
||||||
if (go.TryGetComponent<Rigidbody2D>(out _) && go.TryGetComponent<NetworkRigidbody2D>(out _) == false)
|
if (go.TryGetComponent<Rigidbody2D>(out _) && go.TryGetComponent<NetworkRigidbody2D>(out _) == false)
|
||||||
{
|
{
|
||||||
EditorGUILayout.HelpBox("This GameObject contains a Rigidbody2D but no NetworkRigidbody2D.\n" +
|
EditorGUILayout.HelpBox("This GameObject contains a Rigidbody2D but no NetworkRigidbody2D.\n" +
|
||||||
"Add a NetworkRigidbody2D component to improve Rigidbody2D synchronization.", MessageType.Warning);
|
"Add a NetworkRigidbody2D component to improve Rigidbody2D synchronization.", MessageType.Warning);
|
||||||
}
|
}
|
||||||
|
#endif // COM_UNITY_MODULES_PHYSICS2D
|
||||||
|
|
||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
}
|
}
|
||||||
|
|||||||
8
Editor/PackageChecker.meta
Normal file
8
Editor/PackageChecker.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a325130169714440ba1b4878082e8956
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
53
Editor/PackageChecker/UTPAdapterChecker.cs
Normal file
53
Editor/PackageChecker/UTPAdapterChecker.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#if COM_UNITY_NETCODE_ADAPTER_UTP
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.PackageManager;
|
||||||
|
using UnityEditor.PackageManager.Requests;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Editor.PackageChecker
|
||||||
|
{
|
||||||
|
[InitializeOnLoad]
|
||||||
|
internal class UTPAdapterChecker
|
||||||
|
{
|
||||||
|
private const string k_UTPAdapterPackageName = "com.unity.netcode.adapter.utp";
|
||||||
|
|
||||||
|
private static ListRequest s_ListRequest = null;
|
||||||
|
|
||||||
|
static UTPAdapterChecker()
|
||||||
|
{
|
||||||
|
if (s_ListRequest == null)
|
||||||
|
{
|
||||||
|
s_ListRequest = Client.List();
|
||||||
|
EditorApplication.update += EditorUpdate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EditorUpdate()
|
||||||
|
{
|
||||||
|
if (!s_ListRequest.IsCompleted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorApplication.update -= EditorUpdate;
|
||||||
|
|
||||||
|
if (s_ListRequest.Status == StatusCode.Success)
|
||||||
|
{
|
||||||
|
if (s_ListRequest.Result.Any(p => p.name == k_UTPAdapterPackageName))
|
||||||
|
{
|
||||||
|
Debug.Log($"({nameof(UTPAdapterChecker)}) Found UTP Adapter package, it is no longer needed, `UnityTransport` is now directly integrated into the SDK therefore removing it from the project.");
|
||||||
|
Client.Remove(k_UTPAdapterPackageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var error = s_ListRequest.Error;
|
||||||
|
Debug.LogError($"({nameof(UTPAdapterChecker)}) Cannot check the list of packages -> error #{error.errorCode}: {error.message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
s_ListRequest = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // COM_UNITY_NETCODE_ADAPTER_UTP
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 69c3c1c5a885d4aed99ee2e1fa40f763
|
guid: df5ed97df956b4aad91a221ba59fa304
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "Unity.Netcode.Editor.PackageChecker",
|
||||||
|
"rootNamespace": "Unity.Netcode.Editor.PackageChecker",
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"versionDefines": [
|
||||||
|
{
|
||||||
|
"name": "com.unity.netcode.adapter.utp",
|
||||||
|
"expression": "",
|
||||||
|
"define": "COM_UNITY_NETCODE_ADAPTER_UTP"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: de64d7f9ca85d4bf59c8c24738bc1057
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -11,4 +11,3 @@ using System.Runtime.CompilerServices;
|
|||||||
[assembly: InternalsVisibleTo("Unity.Netcode.RuntimeTests")]
|
[assembly: InternalsVisibleTo("Unity.Netcode.RuntimeTests")]
|
||||||
[assembly: InternalsVisibleTo("Unity.Netcode.TestHelpers.Runtime")]
|
[assembly: InternalsVisibleTo("Unity.Netcode.TestHelpers.Runtime")]
|
||||||
[assembly: InternalsVisibleTo("Unity.Netcode.Adapter.UTP")]
|
[assembly: InternalsVisibleTo("Unity.Netcode.Adapter.UTP")]
|
||||||
|
|
||||||
|
|||||||
@@ -138,19 +138,6 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EnableNetworkLogs = true;
|
public bool EnableNetworkLogs = true;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether or not to enable Snapshot System for variable updates. Not supported in this version.
|
|
||||||
/// </summary>
|
|
||||||
public bool UseSnapshotDelta { get; internal set; } = false;
|
|
||||||
/// <summary>
|
|
||||||
/// Whether or not to enable Snapshot System for spawn and despawn commands. Not supported in this version.
|
|
||||||
/// </summary>
|
|
||||||
public bool UseSnapshotSpawn { get; internal set; } = false;
|
|
||||||
/// <summary>
|
|
||||||
/// When Snapshot System spawn is enabled: max size of Snapshot Messages. Meant to fit MTU.
|
|
||||||
/// </summary>
|
|
||||||
public int SnapshotMaxSpawnUsage { get; } = 1000;
|
|
||||||
|
|
||||||
public const int RttAverageSamples = 5; // number of RTT to keep an average of (plus one)
|
public const int RttAverageSamples = 5; // number of RTT to keep an average of (plus one)
|
||||||
public const int RttWindowSize = 64; // number of slots to use for RTT computations (max number of in-flight packets)
|
public const int RttWindowSize = 64; // number of slots to use for RTT computations (max number of in-flight packets)
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -20,6 +20,17 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The NetworkObject's owned by this Client
|
/// The NetworkObject's owned by this Client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly List<NetworkObject> OwnedObjects = new List<NetworkObject>();
|
public List<NetworkObject> OwnedObjects
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (PlayerObject != null && PlayerObject.NetworkManager != null && PlayerObject.NetworkManager.IsListening)
|
||||||
|
{
|
||||||
|
return PlayerObject.NetworkManager.SpawnManager.GetClientOwnedObjects(ClientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new List<NetworkObject>();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,388 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
internal struct IndexAllocatorEntry
|
|
||||||
{
|
|
||||||
internal int Pos; // Position where the memory of this slot is
|
|
||||||
internal int Length; // Length of the memory allocated to this slot
|
|
||||||
internal int Next; // Next and Prev define the order of the slots in the buffer
|
|
||||||
internal int Prev;
|
|
||||||
internal bool Free; // Whether this is a free slot
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class IndexAllocator
|
|
||||||
{
|
|
||||||
private const int k_NotSet = -1;
|
|
||||||
private readonly int m_MaxSlot; // Maximum number of sections (free or not) in the buffer
|
|
||||||
private readonly int m_BufferSize; // Size of the buffer we allocated into
|
|
||||||
private int m_LastSlot = 0; // Last allocated slot
|
|
||||||
private IndexAllocatorEntry[] m_Slots; // Array of slots
|
|
||||||
private int[] m_IndexToSlot; // Mapping from the client's index to the slot index
|
|
||||||
|
|
||||||
internal IndexAllocator(int bufferSize, int maxSlot)
|
|
||||||
{
|
|
||||||
m_MaxSlot = maxSlot;
|
|
||||||
m_BufferSize = bufferSize;
|
|
||||||
m_Slots = new IndexAllocatorEntry[m_MaxSlot];
|
|
||||||
m_IndexToSlot = new int[m_MaxSlot];
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reset this IndexAllocator to an empty one, with the same sized buffer and slots
|
|
||||||
/// </summary>
|
|
||||||
internal void Reset()
|
|
||||||
{
|
|
||||||
// todo: could be made faster, for example by having a last index
|
|
||||||
// and not needing valid stuff past it
|
|
||||||
for (int i = 0; i < m_MaxSlot; i++)
|
|
||||||
{
|
|
||||||
m_Slots[i].Free = true;
|
|
||||||
m_Slots[i].Next = i + 1;
|
|
||||||
m_Slots[i].Prev = i - 1;
|
|
||||||
m_Slots[i].Pos = m_BufferSize;
|
|
||||||
m_Slots[i].Length = 0;
|
|
||||||
|
|
||||||
m_IndexToSlot[i] = k_NotSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Slots[0].Pos = 0;
|
|
||||||
m_Slots[0].Length = m_BufferSize;
|
|
||||||
m_Slots[0].Prev = k_NotSet;
|
|
||||||
m_Slots[m_MaxSlot - 1].Next = k_NotSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the amount of memory used
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// Returns the amount of memory used, starting at 0, ending after the last used slot
|
|
||||||
/// </returns>
|
|
||||||
internal int Range
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
// when the whole buffer is free, m_LastSlot points to an empty slot
|
|
||||||
if (m_Slots[m_LastSlot].Free)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// otherwise return the end of the last slot used
|
|
||||||
return m_Slots[m_LastSlot].Pos + m_Slots[m_LastSlot].Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Allocate a slot with "size" position, for index "index"
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">The client index to identify this. Used in Deallocate to identify which slot</param>
|
|
||||||
/// <param name="size">The size required. </param>
|
|
||||||
/// <param name="pos">Returns the position to use in the buffer </param>
|
|
||||||
/// <returns>
|
|
||||||
/// true if successful, false is there isn't enough memory available or no slots are large enough
|
|
||||||
/// </returns>
|
|
||||||
internal bool Allocate(int index, int size, out int pos)
|
|
||||||
{
|
|
||||||
pos = 0;
|
|
||||||
// size must be positive, index must be within range
|
|
||||||
if (size < 0 || index < 0 || index >= m_MaxSlot)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// refuse allocation if the index is already in use
|
|
||||||
if (m_IndexToSlot[index] != k_NotSet)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: this is the slowest part
|
|
||||||
// improvement 1: list of free blocks (minor)
|
|
||||||
// improvement 2: heap of free blocks
|
|
||||||
for (int i = 0; i < m_MaxSlot; i++)
|
|
||||||
{
|
|
||||||
if (m_Slots[i].Free && m_Slots[i].Length >= size)
|
|
||||||
{
|
|
||||||
m_IndexToSlot[index] = i;
|
|
||||||
|
|
||||||
int leftOver = m_Slots[i].Length - size;
|
|
||||||
int next = m_Slots[i].Next;
|
|
||||||
if (m_Slots[next].Free)
|
|
||||||
{
|
|
||||||
m_Slots[next].Pos -= leftOver;
|
|
||||||
m_Slots[next].Length += leftOver;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int add = MoveSlotAfter(i);
|
|
||||||
|
|
||||||
m_Slots[add].Pos = m_Slots[i].Pos + size;
|
|
||||||
m_Slots[add].Length = m_Slots[i].Length - size;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Slots[i].Free = false;
|
|
||||||
m_Slots[i].Length = size;
|
|
||||||
|
|
||||||
pos = m_Slots[i].Pos;
|
|
||||||
|
|
||||||
// if we allocate past the current range, we are the last slot
|
|
||||||
if (m_Slots[i].Pos + m_Slots[i].Length > Range)
|
|
||||||
{
|
|
||||||
m_LastSlot = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deallocate a slot
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">The client index to identify this. Same index used in Allocate </param>
|
|
||||||
/// <returns>
|
|
||||||
/// true if successful, false is there isn't an allocated slot at this index
|
|
||||||
/// </returns>
|
|
||||||
internal bool Deallocate(int index)
|
|
||||||
{
|
|
||||||
// size must be positive, index must be within range
|
|
||||||
if (index < 0 || index >= m_MaxSlot)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int slot = m_IndexToSlot[index];
|
|
||||||
|
|
||||||
if (slot == k_NotSet)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Slots[slot].Free)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Slots[slot].Free = true;
|
|
||||||
|
|
||||||
int prev = m_Slots[slot].Prev;
|
|
||||||
int next = m_Slots[slot].Next;
|
|
||||||
|
|
||||||
// if previous slot was free, merge and grow
|
|
||||||
if (prev != k_NotSet && m_Slots[prev].Free)
|
|
||||||
{
|
|
||||||
m_Slots[prev].Length += m_Slots[slot].Length;
|
|
||||||
m_Slots[slot].Length = 0;
|
|
||||||
|
|
||||||
// if the slot we're merging was the last one, the last one is now the one we merged with
|
|
||||||
if (slot == m_LastSlot)
|
|
||||||
{
|
|
||||||
m_LastSlot = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: verify what this does on full or nearly full cases
|
|
||||||
MoveSlotToEnd(slot);
|
|
||||||
slot = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
next = m_Slots[slot].Next;
|
|
||||||
|
|
||||||
// merge with next slot if it is free
|
|
||||||
if (next != k_NotSet && m_Slots[next].Free)
|
|
||||||
{
|
|
||||||
m_Slots[slot].Length += m_Slots[next].Length;
|
|
||||||
m_Slots[next].Length = 0;
|
|
||||||
MoveSlotToEnd(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we just deallocate the last one, we need to move last back
|
|
||||||
if (slot == m_LastSlot)
|
|
||||||
{
|
|
||||||
m_LastSlot = m_Slots[m_LastSlot].Prev;
|
|
||||||
// if there's nothing allocated anymore, use 0
|
|
||||||
if (m_LastSlot == k_NotSet)
|
|
||||||
{
|
|
||||||
m_LastSlot = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark the index as available
|
|
||||||
m_IndexToSlot[index] = k_NotSet;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take a slot at the end and link it to go just after "slot".
|
|
||||||
// Used when allocating part of a slot and we need an entry for the rest
|
|
||||||
// Returns the slot that was picked
|
|
||||||
private int MoveSlotAfter(int slot)
|
|
||||||
{
|
|
||||||
int ret = m_Slots[m_MaxSlot - 1].Prev;
|
|
||||||
int p0 = m_Slots[ret].Prev;
|
|
||||||
|
|
||||||
m_Slots[p0].Next = m_MaxSlot - 1;
|
|
||||||
m_Slots[m_MaxSlot - 1].Prev = p0;
|
|
||||||
|
|
||||||
int p1 = m_Slots[slot].Next;
|
|
||||||
m_Slots[slot].Next = ret;
|
|
||||||
m_Slots[p1].Prev = ret;
|
|
||||||
|
|
||||||
m_Slots[ret].Prev = slot;
|
|
||||||
m_Slots[ret].Next = p1;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the slot "slot" to the end of the list.
|
|
||||||
// Used when merging two slots, that gives us an extra entry at the end
|
|
||||||
private void MoveSlotToEnd(int slot)
|
|
||||||
{
|
|
||||||
// if we're already there
|
|
||||||
if (m_Slots[slot].Next == k_NotSet)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int prev = m_Slots[slot].Prev;
|
|
||||||
int next = m_Slots[slot].Next;
|
|
||||||
|
|
||||||
m_Slots[prev].Next = next;
|
|
||||||
if (next != k_NotSet)
|
|
||||||
{
|
|
||||||
m_Slots[next].Prev = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
int p0 = m_Slots[m_MaxSlot - 1].Prev;
|
|
||||||
|
|
||||||
m_Slots[p0].Next = slot;
|
|
||||||
m_Slots[slot].Next = m_MaxSlot - 1;
|
|
||||||
|
|
||||||
m_Slots[m_MaxSlot - 1].Prev = slot;
|
|
||||||
m_Slots[slot].Prev = p0;
|
|
||||||
|
|
||||||
m_Slots[slot].Pos = m_BufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// runs a bunch of consistency check on the Allocator
|
|
||||||
internal bool Verify()
|
|
||||||
{
|
|
||||||
int pos = k_NotSet;
|
|
||||||
int count = 0;
|
|
||||||
int total = 0;
|
|
||||||
int endPos = 0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
int prev = pos;
|
|
||||||
if (pos != k_NotSet)
|
|
||||||
{
|
|
||||||
pos = m_Slots[pos].Next;
|
|
||||||
if (pos == k_NotSet)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Slots[pos].Prev != prev)
|
|
||||||
{
|
|
||||||
// the previous is not correct
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Slots[pos].Length < 0)
|
|
||||||
{
|
|
||||||
// Length should be positive
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev != k_NotSet && m_Slots[prev].Free && m_Slots[pos].Free && m_Slots[pos].Length > 0)
|
|
||||||
{
|
|
||||||
// should not have two consecutive free slots
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Slots[pos].Pos != total)
|
|
||||||
{
|
|
||||||
// slots should all line up nicely
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_Slots[pos].Free)
|
|
||||||
{
|
|
||||||
endPos = m_Slots[pos].Pos + m_Slots[pos].Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
total += m_Slots[pos].Length;
|
|
||||||
count++;
|
|
||||||
|
|
||||||
} while (pos != k_NotSet);
|
|
||||||
|
|
||||||
if (count != m_MaxSlot)
|
|
||||||
{
|
|
||||||
// some slots were lost
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (total != m_BufferSize)
|
|
||||||
{
|
|
||||||
// total buffer should be accounted for
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endPos != Range)
|
|
||||||
{
|
|
||||||
// end position should match reported end position
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug display the allocator structure
|
|
||||||
internal void DebugDisplay()
|
|
||||||
{
|
|
||||||
string logMessage = "IndexAllocator structure\n";
|
|
||||||
|
|
||||||
bool[] seen = new bool[m_MaxSlot];
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
int count = 0;
|
|
||||||
bool prevEmpty = false;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
seen[pos] = true;
|
|
||||||
count++;
|
|
||||||
if (m_Slots[pos].Length == 0 && prevEmpty)
|
|
||||||
{
|
|
||||||
// don't display repetitive empty slots
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logMessage += string.Format("{0}:{1}, {2} ({3}) \n", m_Slots[pos].Pos, m_Slots[pos].Length,
|
|
||||||
m_Slots[pos].Free ? "Free" : "Used", pos);
|
|
||||||
if (m_Slots[pos].Length == 0)
|
|
||||||
{
|
|
||||||
prevEmpty = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
prevEmpty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = m_Slots[pos].Next;
|
|
||||||
} while (pos != k_NotSet && !seen[pos]);
|
|
||||||
|
|
||||||
logMessage += string.Format("{0} Total entries\n", count);
|
|
||||||
|
|
||||||
Debug.Log(logMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -158,37 +158,57 @@ namespace Unity.Netcode
|
|||||||
// We check to see if we need to shortcut for the case where we are the host/server and we can send a clientRPC
|
// We check to see if we need to shortcut for the case where we are the host/server and we can send a clientRPC
|
||||||
// to ourself. Sadly we have to figure that out from the list of clientIds :(
|
// to ourself. Sadly we have to figure that out from the list of clientIds :(
|
||||||
bool shouldSendToHost = false;
|
bool shouldSendToHost = false;
|
||||||
|
|
||||||
if (clientRpcParams.Send.TargetClientIds != null)
|
if (clientRpcParams.Send.TargetClientIds != null)
|
||||||
{
|
{
|
||||||
foreach (var clientId in clientRpcParams.Send.TargetClientIds)
|
foreach (var targetClientId in clientRpcParams.Send.TargetClientIds)
|
||||||
{
|
{
|
||||||
if (clientId == NetworkManager.ServerClientId)
|
if (targetClientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
shouldSendToHost = true;
|
shouldSendToHost = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check to make sure we are sending to only observers, if not log an error.
|
||||||
|
if (NetworkManager.LogLevel >= LogLevel.Error && !NetworkObject.Observers.Contains(targetClientId))
|
||||||
|
{
|
||||||
|
NetworkLog.LogError(GenerateObserverErrorMessage(clientRpcParams, targetClientId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, in clientRpcParams.Send.TargetClientIds);
|
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, in clientRpcParams.Send.TargetClientIds);
|
||||||
}
|
}
|
||||||
else if (clientRpcParams.Send.TargetClientIdsNativeArray != null)
|
else if (clientRpcParams.Send.TargetClientIdsNativeArray != null)
|
||||||
{
|
{
|
||||||
foreach (var clientId in clientRpcParams.Send.TargetClientIdsNativeArray)
|
foreach (var targetClientId in clientRpcParams.Send.TargetClientIdsNativeArray)
|
||||||
{
|
{
|
||||||
if (clientId == NetworkManager.ServerClientId)
|
if (targetClientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
shouldSendToHost = true;
|
shouldSendToHost = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check to make sure we are sending to only observers, if not log an error.
|
||||||
|
if (NetworkManager.LogLevel >= LogLevel.Error && !NetworkObject.Observers.Contains(targetClientId))
|
||||||
|
{
|
||||||
|
NetworkLog.LogError(GenerateObserverErrorMessage(clientRpcParams, targetClientId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, clientRpcParams.Send.TargetClientIdsNativeArray.Value);
|
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, clientRpcParams.Send.TargetClientIdsNativeArray.Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
shouldSendToHost = IsHost;
|
var observerEnumerator = NetworkObject.Observers.GetEnumerator();
|
||||||
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, NetworkManager.ConnectedClientsIds);
|
while (observerEnumerator.MoveNext())
|
||||||
|
{
|
||||||
|
// Skip over the host
|
||||||
|
if (IsHost && observerEnumerator.Current == NetworkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
shouldSendToHost = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rpcWriteSize = NetworkManager.MessagingSystem.SendMessage(ref clientRpcMessage, networkDelivery, observerEnumerator.Current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are a server/host then we just no op and send to ourself
|
// If we are a server/host then we just no op and send to ourself
|
||||||
@@ -228,6 +248,12 @@ namespace Unity.Netcode
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal string GenerateObserverErrorMessage(ClientRpcParams clientRpcParams, ulong targetClientId)
|
||||||
|
{
|
||||||
|
var containerNameHoldingId = clientRpcParams.Send.TargetClientIds != null ? nameof(ClientRpcParams.Send.TargetClientIds) : nameof(ClientRpcParams.Send.TargetClientIdsNativeArray);
|
||||||
|
return $"Sending ClientRpc to non-observer! {containerNameHoldingId} contains clientId {targetClientId} that is not an observer!";
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the NetworkManager that owns this NetworkBehaviour instance
|
/// Gets the NetworkManager that owns this NetworkBehaviour instance
|
||||||
/// See note around `NetworkObject` for how there is a chicken / egg problem when we are not initialized
|
/// See note around `NetworkObject` for how there is a chicken / egg problem when we are not initialized
|
||||||
@@ -235,42 +261,42 @@ namespace Unity.Netcode
|
|||||||
public NetworkManager NetworkManager => NetworkObject.NetworkManager;
|
public NetworkManager NetworkManager => NetworkObject.NetworkManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets if the object is the the personal clients player object
|
/// If a NetworkObject is assigned, it will return whether or not this NetworkObject
|
||||||
|
/// is the local player object. If no NetworkObject is assigned it will always return false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsLocalPlayer => NetworkObject.IsLocalPlayer;
|
public bool IsLocalPlayer { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets if the object is owned by the local player or if the object is the local player object
|
/// Gets if the object is owned by the local player or if the object is the local player object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsOwner => NetworkObject.IsOwner;
|
public bool IsOwner { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets if we are executing as server
|
/// Gets if we are executing as server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool IsServer => IsRunning && NetworkManager.IsServer;
|
protected bool IsServer { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets if we are executing as client
|
/// Gets if we are executing as client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool IsClient => IsRunning && NetworkManager.IsClient;
|
protected bool IsClient { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets if we are executing as Host, I.E Server and Client
|
/// Gets if we are executing as Host, I.E Server and Client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool IsHost => IsRunning && NetworkManager.IsHost;
|
protected bool IsHost { get; private set; }
|
||||||
|
|
||||||
private bool IsRunning => NetworkManager && NetworkManager.IsListening;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets Whether or not the object has a owner
|
/// Gets Whether or not the object has a owner
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsOwnedByServer => NetworkObject.IsOwnedByServer;
|
public bool IsOwnedByServer { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to determine if it is safe to access NetworkObject and NetworkManager from within a NetworkBehaviour component
|
/// Used to determine if it is safe to access NetworkObject and NetworkManager from within a NetworkBehaviour component
|
||||||
/// Primarily useful when checking NetworkObject/NetworkManager properties within FixedUpate
|
/// Primarily useful when checking NetworkObject/NetworkManager properties within FixedUpate
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsSpawned => HasNetworkObject ? NetworkObject.IsSpawned : false;
|
public bool IsSpawned { get; internal set; }
|
||||||
|
|
||||||
internal bool IsBehaviourEditable()
|
internal bool IsBehaviourEditable()
|
||||||
{
|
{
|
||||||
@@ -327,12 +353,12 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the NetworkId of the NetworkObject that owns this NetworkBehaviour
|
/// Gets the NetworkId of the NetworkObject that owns this NetworkBehaviour
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong NetworkObjectId => NetworkObject.NetworkObjectId;
|
public ulong NetworkObjectId { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets NetworkId for this NetworkBehaviour from the owner NetworkObject
|
/// Gets NetworkId for this NetworkBehaviour from the owner NetworkObject
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort NetworkBehaviourId => NetworkObject.GetNetworkBehaviourOrderIndex(this);
|
public ushort NetworkBehaviourId { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internally caches the Id of this behaviour in a NetworkObject. Makes look-up faster
|
/// Internally caches the Id of this behaviour in a NetworkObject. Makes look-up faster
|
||||||
@@ -352,7 +378,47 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the ClientId that owns the NetworkObject
|
/// Gets the ClientId that owns the NetworkObject
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong OwnerClientId => NetworkObject.OwnerClientId;
|
public ulong OwnerClientId { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates properties with network session related
|
||||||
|
/// dependencies such as a NetworkObject's spawned
|
||||||
|
/// state or NetworkManager's session state.
|
||||||
|
/// </summary>
|
||||||
|
internal void UpdateNetworkProperties()
|
||||||
|
{
|
||||||
|
// Set NetworkObject dependent properties
|
||||||
|
if (NetworkObject != null)
|
||||||
|
{
|
||||||
|
// Set identification related properties
|
||||||
|
NetworkObjectId = NetworkObject.NetworkObjectId;
|
||||||
|
IsLocalPlayer = NetworkObject.IsLocalPlayer;
|
||||||
|
|
||||||
|
// This is "OK" because GetNetworkBehaviourOrderIndex uses the order of
|
||||||
|
// NetworkObject.ChildNetworkBehaviours which is set once when first
|
||||||
|
// accessed.
|
||||||
|
NetworkBehaviourId = NetworkObject.GetNetworkBehaviourOrderIndex(this);
|
||||||
|
|
||||||
|
// Set ownership related properties
|
||||||
|
IsOwnedByServer = NetworkObject.IsOwnedByServer;
|
||||||
|
IsOwner = NetworkObject.IsOwner;
|
||||||
|
OwnerClientId = NetworkObject.OwnerClientId;
|
||||||
|
|
||||||
|
// Set NetworkManager dependent properties
|
||||||
|
if (NetworkManager != null)
|
||||||
|
{
|
||||||
|
IsHost = NetworkManager.IsListening && NetworkManager.IsHost;
|
||||||
|
IsClient = NetworkManager.IsListening && NetworkManager.IsClient;
|
||||||
|
IsServer = NetworkManager.IsListening && NetworkManager.IsServer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Shouldn't happen, but if so then set the properties to their default value;
|
||||||
|
{
|
||||||
|
OwnerClientId = NetworkObjectId = default;
|
||||||
|
IsOwnedByServer = IsOwner = IsHost = IsClient = IsServer = default;
|
||||||
|
NetworkBehaviourId = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called when the <see cref="NetworkObject"/> gets spawned, message handlers are ready to be registered and the network is setup.
|
/// Gets called when the <see cref="NetworkObject"/> gets spawned, message handlers are ready to be registered and the network is setup.
|
||||||
@@ -366,21 +432,41 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void InternalOnNetworkSpawn()
|
internal void InternalOnNetworkSpawn()
|
||||||
{
|
{
|
||||||
|
IsSpawned = true;
|
||||||
InitializeVariables();
|
InitializeVariables();
|
||||||
|
UpdateNetworkProperties();
|
||||||
|
OnNetworkSpawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InternalOnNetworkDespawn() { }
|
internal void InternalOnNetworkDespawn()
|
||||||
|
{
|
||||||
|
IsSpawned = false;
|
||||||
|
UpdateNetworkProperties();
|
||||||
|
OnNetworkDespawn();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called when the local client gains ownership of this object
|
/// Gets called when the local client gains ownership of this object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void OnGainedOwnership() { }
|
public virtual void OnGainedOwnership() { }
|
||||||
|
|
||||||
|
internal void InternalOnGainedOwnership()
|
||||||
|
{
|
||||||
|
UpdateNetworkProperties();
|
||||||
|
OnGainedOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called when we loose ownership of this object
|
/// Gets called when we loose ownership of this object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void OnLostOwnership() { }
|
public virtual void OnLostOwnership() { }
|
||||||
|
|
||||||
|
internal void InternalOnLostOwnership()
|
||||||
|
{
|
||||||
|
UpdateNetworkProperties();
|
||||||
|
OnLostOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed
|
/// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -433,12 +519,10 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
m_VarInit = true;
|
m_VarInit = true;
|
||||||
|
|
||||||
FieldInfo[] sortedFields = GetFieldInfoForType(GetType());
|
var sortedFields = GetFieldInfoForType(GetType());
|
||||||
|
|
||||||
for (int i = 0; i < sortedFields.Length; i++)
|
for (int i = 0; i < sortedFields.Length; i++)
|
||||||
{
|
{
|
||||||
Type fieldType = sortedFields[i].FieldType;
|
var fieldType = sortedFields[i].FieldType;
|
||||||
|
|
||||||
if (fieldType.IsSubclassOf(typeof(NetworkVariableBase)))
|
if (fieldType.IsSubclassOf(typeof(NetworkVariableBase)))
|
||||||
{
|
{
|
||||||
var instance = (NetworkVariableBase)sortedFields[i].GetValue(this);
|
var instance = (NetworkVariableBase)sortedFields[i].GetValue(this);
|
||||||
@@ -499,7 +583,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void VariableUpdate(ulong clientId)
|
internal void VariableUpdate(ulong targetClientId)
|
||||||
{
|
{
|
||||||
if (!m_VarInit)
|
if (!m_VarInit)
|
||||||
{
|
{
|
||||||
@@ -507,67 +591,58 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
PreNetworkVariableWrite();
|
PreNetworkVariableWrite();
|
||||||
NetworkVariableUpdate(clientId, NetworkBehaviourId);
|
NetworkVariableUpdate(targetClientId, NetworkBehaviourId);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly List<int> NetworkVariableIndexesToReset = new List<int>();
|
internal readonly List<int> NetworkVariableIndexesToReset = new List<int>();
|
||||||
internal readonly HashSet<int> NetworkVariableIndexesToResetSet = new HashSet<int>();
|
internal readonly HashSet<int> NetworkVariableIndexesToResetSet = new HashSet<int>();
|
||||||
|
|
||||||
private void NetworkVariableUpdate(ulong clientId, int behaviourIndex)
|
private void NetworkVariableUpdate(ulong targetClientId, int behaviourIndex)
|
||||||
{
|
{
|
||||||
if (!CouldHaveDirtyNetworkVariables())
|
if (!CouldHaveDirtyNetworkVariables())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkManager.NetworkConfig.UseSnapshotDelta)
|
for (int j = 0; j < m_DeliveryMappedNetworkVariableIndices.Count; j++)
|
||||||
{
|
{
|
||||||
|
var shouldSend = false;
|
||||||
for (int k = 0; k < NetworkVariableFields.Count; k++)
|
for (int k = 0; k < NetworkVariableFields.Count; k++)
|
||||||
{
|
{
|
||||||
NetworkManager.SnapshotSystem.Store(NetworkObjectId, behaviourIndex, k, NetworkVariableFields[k]);
|
var networkVariable = NetworkVariableFields[k];
|
||||||
}
|
if (networkVariable.IsDirty() && networkVariable.CanClientRead(targetClientId))
|
||||||
}
|
|
||||||
|
|
||||||
if (!NetworkManager.NetworkConfig.UseSnapshotDelta)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < m_DeliveryMappedNetworkVariableIndices.Count; j++)
|
|
||||||
{
|
|
||||||
var shouldSend = false;
|
|
||||||
for (int k = 0; k < NetworkVariableFields.Count; k++)
|
|
||||||
{
|
{
|
||||||
if (NetworkVariableFields[k].ShouldWrite(clientId, IsServer))
|
shouldSend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldSend)
|
||||||
|
{
|
||||||
|
var message = new NetworkVariableDeltaMessage
|
||||||
|
{
|
||||||
|
NetworkObjectId = NetworkObjectId,
|
||||||
|
NetworkBehaviourIndex = NetworkObject.GetNetworkBehaviourOrderIndex(this),
|
||||||
|
NetworkBehaviour = this,
|
||||||
|
TargetClientId = targetClientId,
|
||||||
|
DeliveryMappedNetworkVariableIndex = m_DeliveryMappedNetworkVariableIndices[j]
|
||||||
|
};
|
||||||
|
// TODO: Serialization is where the IsDirty flag gets changed.
|
||||||
|
// Messages don't get sent from the server to itself, so if we're host and sending to ourselves,
|
||||||
|
// we still have to actually serialize the message even though we're not sending it, otherwise
|
||||||
|
// the dirty flag doesn't change properly. These two pieces should be decoupled at some point
|
||||||
|
// so we don't have to do this serialization work if we're not going to use the result.
|
||||||
|
if (IsServer && targetClientId == NetworkManager.ServerClientId)
|
||||||
|
{
|
||||||
|
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
||||||
|
using (tmpWriter)
|
||||||
{
|
{
|
||||||
shouldSend = true;
|
message.Serialize(tmpWriter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (shouldSend)
|
|
||||||
{
|
{
|
||||||
var message = new NetworkVariableDeltaMessage
|
NetworkManager.SendMessage(ref message, m_DeliveryTypesForNetworkVariableGroups[j], targetClientId);
|
||||||
{
|
|
||||||
NetworkObjectId = NetworkObjectId,
|
|
||||||
NetworkBehaviourIndex = NetworkObject.GetNetworkBehaviourOrderIndex(this),
|
|
||||||
NetworkBehaviour = this,
|
|
||||||
ClientId = clientId,
|
|
||||||
DeliveryMappedNetworkVariableIndex = m_DeliveryMappedNetworkVariableIndices[j]
|
|
||||||
};
|
|
||||||
// TODO: Serialization is where the IsDirty flag gets changed.
|
|
||||||
// Messages don't get sent from the server to itself, so if we're host and sending to ourselves,
|
|
||||||
// we still have to actually serialize the message even though we're not sending it, otherwise
|
|
||||||
// the dirty flag doesn't change properly. These two pieces should be decoupled at some point
|
|
||||||
// so we don't have to do this serialization work if we're not going to use the result.
|
|
||||||
if (IsServer && clientId == NetworkManager.ServerClientId)
|
|
||||||
{
|
|
||||||
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
|
||||||
using (tmpWriter)
|
|
||||||
{
|
|
||||||
message.Serialize(tmpWriter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NetworkManager.SendMessage(ref message, m_DeliveryTypesForNetworkVariableGroups[j], clientId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -595,7 +670,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong clientId)
|
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId)
|
||||||
{
|
{
|
||||||
if (NetworkVariableFields.Count == 0)
|
if (NetworkVariableFields.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -604,7 +679,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
||||||
{
|
{
|
||||||
bool canClientRead = NetworkVariableFields[j].CanClientRead(clientId);
|
bool canClientRead = NetworkVariableFields[j].CanClientRead(targetClientId);
|
||||||
|
|
||||||
if (canClientRead)
|
if (canClientRead)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
||||||
{
|
{
|
||||||
sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId);
|
sobj.ChildNetworkBehaviours[k].VariableUpdate(NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using Unity.Multiplayer.Tools;
|
|||||||
#endif
|
#endif
|
||||||
using Unity.Profiling;
|
using Unity.Profiling;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using Debug = UnityEngine.Debug;
|
using Debug = UnityEngine.Debug;
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
@@ -45,7 +46,7 @@ namespace Unity.Netcode
|
|||||||
private static ProfilerMarker s_TransportDisconnect = new ProfilerMarker($"{nameof(NetworkManager)}.TransportDisconnect");
|
private static ProfilerMarker s_TransportDisconnect = new ProfilerMarker($"{nameof(NetworkManager)}.TransportDisconnect");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private const double k_TimeSyncFrequency = 1.0d; // sync every second, TODO will be removed once timesync is done via snapshots
|
private const double k_TimeSyncFrequency = 1.0d; // sync every second
|
||||||
private const float k_DefaultBufferSizeSec = 0.05f; // todo talk with UX/Product, find good default value for this
|
private const float k_DefaultBufferSizeSec = 0.05f; // todo talk with UX/Product, find good default value for this
|
||||||
|
|
||||||
internal static string PrefabDebugHelper(NetworkPrefab networkPrefab)
|
internal static string PrefabDebugHelper(NetworkPrefab networkPrefab)
|
||||||
@@ -53,7 +54,6 @@ namespace Unity.Netcode
|
|||||||
return $"{nameof(NetworkPrefab)} \"{networkPrefab.Prefab.gameObject.name}\"";
|
return $"{nameof(NetworkPrefab)} \"{networkPrefab.Prefab.gameObject.name}\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal SnapshotSystem SnapshotSystem { get; private set; }
|
|
||||||
internal NetworkBehaviourUpdater BehaviourUpdater { get; private set; }
|
internal NetworkBehaviourUpdater BehaviourUpdater { get; private set; }
|
||||||
|
|
||||||
internal MessagingSystem MessagingSystem { get; private set; }
|
internal MessagingSystem MessagingSystem { get; private set; }
|
||||||
@@ -125,13 +125,11 @@ namespace Unity.Netcode
|
|||||||
public bool OnVerifyCanReceive(ulong senderId, Type messageType)
|
public bool OnVerifyCanReceive(ulong senderId, Type messageType)
|
||||||
{
|
{
|
||||||
if (m_NetworkManager.PendingClients.TryGetValue(senderId, out PendingClient client) &&
|
if (m_NetworkManager.PendingClients.TryGetValue(senderId, out PendingClient client) &&
|
||||||
(client.ConnectionState == PendingClient.State.PendingApproval ||
|
(client.ConnectionState == PendingClient.State.PendingApproval || (client.ConnectionState == PendingClient.State.PendingConnection && messageType != typeof(ConnectionRequestMessage))))
|
||||||
(client.ConnectionState == PendingClient.State.PendingConnection &&
|
|
||||||
messageType != typeof(ConnectionRequestMessage))))
|
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"Message received from {nameof(senderId)}={senderId.ToString()} before it has been accepted");
|
NetworkLog.LogWarning($"Message received from {nameof(senderId)}={senderId} before it has been accepted");
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -229,14 +227,12 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
public NetworkSceneManager SceneManager { get; private set; }
|
public NetworkSceneManager SceneManager { get; private set; }
|
||||||
|
|
||||||
public readonly ulong ServerClientId = 0;
|
public const ulong ServerClientId = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the networkId of the server
|
/// Gets the networkId of the server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ulong m_ServerTransportId => NetworkConfig.NetworkTransport?.ServerClientId ??
|
private ulong m_ServerTransportId => NetworkConfig.NetworkTransport?.ServerClientId ?? throw new NullReferenceException($"The transport in the active {nameof(NetworkConfig)} is null");
|
||||||
throw new NullReferenceException(
|
|
||||||
$"The transport in the active {nameof(NetworkConfig)} is null");
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns ServerClientId if IsServer or LocalClientId if not
|
/// Returns ServerClientId if IsServer or LocalClientId if not
|
||||||
@@ -367,16 +363,14 @@ namespace Unity.Netcode
|
|||||||
/// <param name="approved">Whether or not the client was approved</param>
|
/// <param name="approved">Whether or not the client was approved</param>
|
||||||
/// <param name="position">The position to spawn the client at. If null, the prefab position 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="rotation">The rotation to spawn the client with. If null, the prefab position is used.</param>
|
||||||
public delegate void ConnectionApprovedDelegate(bool createPlayerObject, uint? playerPrefabHash, bool approved,
|
public delegate void ConnectionApprovedDelegate(bool createPlayerObject, uint? playerPrefabHash, bool approved, Vector3? position, Quaternion? rotation);
|
||||||
Vector3? position, Quaternion? rotation);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The callback to invoke during connection approval
|
/// The callback to invoke during connection approval
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<byte[], ulong, ConnectionApprovedDelegate> ConnectionApprovalCallback = null;
|
public event Action<byte[], ulong, ConnectionApprovedDelegate> ConnectionApprovalCallback = null;
|
||||||
|
|
||||||
internal void InvokeConnectionApproval(byte[] payload, ulong clientId, ConnectionApprovedDelegate action) =>
|
internal void InvokeConnectionApproval(byte[] payload, ulong clientId, ConnectionApprovedDelegate action) => ConnectionApprovalCallback?.Invoke(payload, clientId, action);
|
||||||
ConnectionApprovalCallback?.Invoke(payload, clientId, action);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current NetworkConfig
|
/// The current NetworkConfig
|
||||||
@@ -565,13 +559,6 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
NetworkConfig.NetworkTransport.NetworkMetrics = NetworkMetrics;
|
NetworkConfig.NetworkTransport.NetworkMetrics = NetworkMetrics;
|
||||||
|
|
||||||
//This 'if' should never enter
|
|
||||||
if (SnapshotSystem != null)
|
|
||||||
{
|
|
||||||
SnapshotSystem.Dispose();
|
|
||||||
SnapshotSystem = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server)
|
if (server)
|
||||||
{
|
{
|
||||||
NetworkTimeSystem = NetworkTimeSystem.ServerTimeSystem();
|
NetworkTimeSystem = NetworkTimeSystem.ServerTimeSystem();
|
||||||
@@ -584,8 +571,6 @@ namespace Unity.Netcode
|
|||||||
NetworkTickSystem = new NetworkTickSystem(NetworkConfig.TickRate, 0, 0);
|
NetworkTickSystem = new NetworkTickSystem(NetworkConfig.TickRate, 0, 0);
|
||||||
NetworkTickSystem.Tick += OnNetworkManagerTick;
|
NetworkTickSystem.Tick += OnNetworkManagerTick;
|
||||||
|
|
||||||
SnapshotSystem = new SnapshotSystem(this, NetworkConfig, NetworkTickSystem);
|
|
||||||
|
|
||||||
this.RegisterNetworkUpdate(NetworkUpdateStage.PreUpdate);
|
this.RegisterNetworkUpdate(NetworkUpdateStage.PreUpdate);
|
||||||
|
|
||||||
// This is used to remove entries not needed or invalid
|
// This is used to remove entries not needed or invalid
|
||||||
@@ -800,44 +785,35 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogInfo("StartServer()");
|
NetworkLog.LogInfo(nameof(StartServer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsServer || IsClient)
|
if (!CanStart(StartType.Server))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning("Cannot start server while an instance is already running");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkConfig.ConnectionApproval)
|
|
||||||
{
|
|
||||||
if (ConnectionApprovalCallback == null)
|
|
||||||
{
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning(
|
|
||||||
"No ConnectionApproval callback defined. Connection approval will timeout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Initialize(true);
|
Initialize(true);
|
||||||
|
|
||||||
var result = NetworkConfig.NetworkTransport.StartServer();
|
// If we failed to start then shutdown and notify user that the transport failed to start
|
||||||
|
if (NetworkConfig.NetworkTransport.StartServer())
|
||||||
|
{
|
||||||
|
IsServer = true;
|
||||||
|
IsClient = false;
|
||||||
|
IsListening = true;
|
||||||
|
|
||||||
IsServer = true;
|
SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
|
||||||
IsClient = false;
|
|
||||||
IsListening = true;
|
|
||||||
|
|
||||||
SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
|
OnServerStarted?.Invoke();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError($"Server is shutting down due to network transport start failure of {NetworkConfig.NetworkTransport.GetType().Name}!");
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
OnServerStarted?.Invoke();
|
return false;
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -850,26 +826,26 @@ namespace Unity.Netcode
|
|||||||
NetworkLog.LogInfo(nameof(StartClient));
|
NetworkLog.LogInfo(nameof(StartClient));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsServer || IsClient)
|
if (!CanStart(StartType.Client))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning("Cannot start client while an instance is already running");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Initialize(false);
|
Initialize(false);
|
||||||
MessagingSystem.ClientConnected(ServerClientId);
|
MessagingSystem.ClientConnected(ServerClientId);
|
||||||
|
|
||||||
var result = NetworkConfig.NetworkTransport.StartClient();
|
if (!NetworkConfig.NetworkTransport.StartClient())
|
||||||
|
{
|
||||||
|
Debug.LogError($"Client is shutting down due to network transport start failure of {NetworkConfig.NetworkTransport.GetType().Name}!");
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
IsServer = false;
|
IsServer = false;
|
||||||
IsClient = true;
|
IsClient = true;
|
||||||
IsListening = true;
|
IsListening = true;
|
||||||
|
|
||||||
return result;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -882,31 +858,21 @@ namespace Unity.Netcode
|
|||||||
NetworkLog.LogInfo(nameof(StartHost));
|
NetworkLog.LogInfo(nameof(StartHost));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsServer || IsClient)
|
if (!CanStart(StartType.Host))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning("Cannot start host while an instance is already running");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkConfig.ConnectionApproval)
|
|
||||||
{
|
|
||||||
if (ConnectionApprovalCallback == null)
|
|
||||||
{
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning(
|
|
||||||
"No ConnectionApproval callback defined. Connection approval will timeout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Initialize(true);
|
Initialize(true);
|
||||||
|
|
||||||
var result = NetworkConfig.NetworkTransport.StartServer();
|
// If we failed to start then shutdown and notify user that the transport failed to start
|
||||||
|
if (!NetworkConfig.NetworkTransport.StartServer())
|
||||||
|
{
|
||||||
|
Debug.LogError($"Server is shutting down due to network transport start failure of {NetworkConfig.NetworkTransport.GetType().Name}!");
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
MessagingSystem.ClientConnected(ServerClientId);
|
MessagingSystem.ClientConnected(ServerClientId);
|
||||||
LocalClientId = ServerClientId;
|
LocalClientId = ServerClientId;
|
||||||
NetworkMetrics.SetConnectionId(LocalClientId);
|
NetworkMetrics.SetConnectionId(LocalClientId);
|
||||||
@@ -942,7 +908,53 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
OnServerStarted?.Invoke();
|
OnServerStarted?.Invoke();
|
||||||
|
|
||||||
return result;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum StartType
|
||||||
|
{
|
||||||
|
Server,
|
||||||
|
Host,
|
||||||
|
Client
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanStart(StartType type)
|
||||||
|
{
|
||||||
|
if (IsListening)
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning("Cannot start " + type + " while an instance is already running");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NetworkConfig.ConnectionApproval)
|
||||||
|
{
|
||||||
|
if (ConnectionApprovalCallback == null)
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning(
|
||||||
|
"No ConnectionApproval callback defined. Connection approval will timeout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConnectionApprovalCallback != null)
|
||||||
|
{
|
||||||
|
if (!NetworkConfig.ConnectionApproval)
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning(
|
||||||
|
"A ConnectionApproval callback is defined but ConnectionApproval is disabled. In order to use ConnectionApproval it has to be explicitly enabled ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSingleton()
|
public void SetSingleton()
|
||||||
@@ -1014,6 +1026,7 @@ namespace Unity.Netcode
|
|||||||
internal interface INetworkManagerHelper
|
internal interface INetworkManagerHelper
|
||||||
{
|
{
|
||||||
bool NotifyUserOfNestedNetworkManager(NetworkManager networkManager, bool ignoreNetworkManagerCache = false, bool editorTest = false);
|
bool NotifyUserOfNestedNetworkManager(NetworkManager networkManager, bool ignoreNetworkManagerCache = false, bool editorTest = false);
|
||||||
|
void CheckAndNotifyUserNetworkObjectRemoved(NetworkManager networkManager, bool editorTest = false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1133,12 +1146,6 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
this.UnregisterAllNetworkUpdates();
|
this.UnregisterAllNetworkUpdates();
|
||||||
|
|
||||||
if (SnapshotSystem != null)
|
|
||||||
{
|
|
||||||
SnapshotSystem.Dispose();
|
|
||||||
SnapshotSystem = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NetworkTickSystem != null)
|
if (NetworkTickSystem != null)
|
||||||
{
|
{
|
||||||
NetworkTickSystem.Tick -= OnNetworkManagerTick;
|
NetworkTickSystem.Tick -= OnNetworkManagerTick;
|
||||||
@@ -1274,7 +1281,11 @@ namespace Unity.Netcode
|
|||||||
if (!m_ShuttingDown || !m_StopProcessingMessages)
|
if (!m_ShuttingDown || !m_StopProcessingMessages)
|
||||||
{
|
{
|
||||||
MessagingSystem.ProcessSendQueues();
|
MessagingSystem.ProcessSendQueues();
|
||||||
|
NetworkMetrics.UpdateNetworkObjectsCount(SpawnManager.SpawnedObjects.Count);
|
||||||
|
NetworkMetrics.UpdateConnectionsCount((IsServer) ? ConnectedClients.Count : 1);
|
||||||
NetworkMetrics.DispatchFrame();
|
NetworkMetrics.DispatchFrame();
|
||||||
|
|
||||||
|
NetworkObject.VerifyParentingStatus();
|
||||||
}
|
}
|
||||||
SpawnManager.CleanupStaleTriggers();
|
SpawnManager.CleanupStaleTriggers();
|
||||||
|
|
||||||
@@ -1288,7 +1299,6 @@ namespace Unity.Netcode
|
|||||||
/// This function runs once whenever the local tick is incremented and is responsible for the following (in order):
|
/// This function runs once whenever the local tick is incremented and is responsible for the following (in order):
|
||||||
/// - collect commands/inputs and send them to the server (TBD)
|
/// - collect commands/inputs and send them to the server (TBD)
|
||||||
/// - call NetworkFixedUpdate on all NetworkBehaviours in prediction/client authority mode
|
/// - call NetworkFixedUpdate on all NetworkBehaviours in prediction/client authority mode
|
||||||
/// - create a snapshot from resulting state
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnNetworkManagerTick()
|
private void OnNetworkManagerTick()
|
||||||
{
|
{
|
||||||
@@ -1419,18 +1429,15 @@ namespace Unity.Netcode
|
|||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
s_TransportDisconnect.Begin();
|
s_TransportDisconnect.Begin();
|
||||||
#endif
|
#endif
|
||||||
clientId = TransportIdToClientId(clientId);
|
clientId = TransportIdCleanUp(clientId, transportId);
|
||||||
|
|
||||||
OnClientDisconnectCallback?.Invoke(clientId);
|
|
||||||
|
|
||||||
m_TransportIdToClientIdMap.Remove(transportId);
|
|
||||||
m_ClientIdToTransportIdMap.Remove(clientId);
|
|
||||||
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogInfo($"Disconnect Event From {clientId}");
|
NetworkLog.LogInfo($"Disconnect Event From {clientId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnClientDisconnectCallback?.Invoke(clientId);
|
||||||
|
|
||||||
if (IsServer)
|
if (IsServer)
|
||||||
{
|
{
|
||||||
OnClientDisconnectFromServer(clientId);
|
OnClientDisconnectFromServer(clientId);
|
||||||
@@ -1446,6 +1453,31 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles cleaning up the transport id/client id tables after
|
||||||
|
/// receiving a disconnect event from transport
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private ulong TransportIdCleanUp(ulong clientId, ulong transportId)
|
||||||
|
{
|
||||||
|
// This check is for clients that attempted to connect but failed.
|
||||||
|
// When this happens, the client will not have an entry within the
|
||||||
|
// m_TransportIdToClientIdMap or m_ClientIdToTransportIdMap lookup
|
||||||
|
// tables so we exit early and just return 0 to be used for the
|
||||||
|
// disconnect event.
|
||||||
|
if (!IsServer && !m_TransportIdToClientIdMap.ContainsKey(clientId))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientId = TransportIdToClientId(clientId);
|
||||||
|
|
||||||
|
m_TransportIdToClientIdMap.Remove(transportId);
|
||||||
|
m_ClientIdToTransportIdMap.Remove(clientId);
|
||||||
|
|
||||||
|
return clientId;
|
||||||
|
}
|
||||||
|
|
||||||
internal unsafe int SendMessage<TMessageType, TClientIdListType>(ref TMessageType message, NetworkDelivery delivery, in TClientIdListType clientIds)
|
internal unsafe int SendMessage<TMessageType, TClientIdListType>(ref TMessageType message, NetworkDelivery delivery, in TClientIdListType clientIds)
|
||||||
where TMessageType : INetworkMessage
|
where TMessageType : INetworkMessage
|
||||||
where TClientIdListType : IReadOnlyList<ulong>
|
where TClientIdListType : IReadOnlyList<ulong>
|
||||||
@@ -1591,32 +1623,45 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = networkClient.OwnedObjects.Count - 1; i >= 0; i--)
|
// Get the NetworkObjects owned by the disconnected client
|
||||||
|
var clientOwnedObjects = SpawnManager.GetClientOwnedObjects(clientId);
|
||||||
|
if (clientOwnedObjects == null)
|
||||||
{
|
{
|
||||||
var ownedObject = networkClient.OwnedObjects[i];
|
// This could happen if a client is never assigned a player object and is disconnected
|
||||||
if (ownedObject != null)
|
// Only log this in verbose/developer mode
|
||||||
|
if (LogLevel == LogLevel.Developer)
|
||||||
{
|
{
|
||||||
if (!ownedObject.DontDestroyWithOwner)
|
NetworkLog.LogWarning($"ClientID {clientId} disconnected with (0) zero owned objects! Was a player prefab not assigned?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Handle changing ownership and prefab handlers
|
||||||
|
for (int i = clientOwnedObjects.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var ownedObject = clientOwnedObjects[i];
|
||||||
|
if (ownedObject != null)
|
||||||
{
|
{
|
||||||
if (PrefabHandler.ContainsHandler(ConnectedClients[clientId].OwnedObjects[i]
|
if (!ownedObject.DontDestroyWithOwner)
|
||||||
.GlobalObjectIdHash))
|
|
||||||
{
|
{
|
||||||
PrefabHandler.HandleNetworkPrefabDestroy(ConnectedClients[clientId].OwnedObjects[i]);
|
if (PrefabHandler.ContainsHandler(clientOwnedObjects[i].GlobalObjectIdHash))
|
||||||
|
{
|
||||||
|
PrefabHandler.HandleNetworkPrefabDestroy(clientOwnedObjects[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Destroy(ownedObject.gameObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Destroy(ownedObject.gameObject);
|
ownedObject.RemoveOwnership();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
ownedObject.RemoveOwnership();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Could(should?) be replaced with more memory per client, by storing the visibility
|
// TODO: Could(should?) be replaced with more memory per client, by storing the visibility
|
||||||
|
|
||||||
foreach (var sobj in SpawnManager.SpawnedObjectsList)
|
foreach (var sobj in SpawnManager.SpawnedObjectsList)
|
||||||
{
|
{
|
||||||
sobj.Observers.Remove(clientId);
|
sobj.Observers.Remove(clientId);
|
||||||
@@ -1764,7 +1809,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
var message = new CreateObjectMessage
|
var message = new CreateObjectMessage
|
||||||
{
|
{
|
||||||
ObjectInfo = ConnectedClients[clientId].PlayerObject.GetMessageSceneObject(clientPair.Key, false)
|
ObjectInfo = ConnectedClients[clientId].PlayerObject.GetMessageSceneObject(clientPair.Key)
|
||||||
};
|
};
|
||||||
message.ObjectInfo.Header.Hash = playerPrefabHash;
|
message.ObjectInfo.Header.Hash = playerPrefabHash;
|
||||||
message.ObjectInfo.Header.IsSceneObject = false;
|
message.ObjectInfo.Header.IsSceneObject = false;
|
||||||
|
|||||||
@@ -54,8 +54,6 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal NetworkManager NetworkManagerOwner;
|
internal NetworkManager NetworkManagerOwner;
|
||||||
|
|
||||||
private ulong m_NetworkObjectId;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the unique Id of this object that is synced across the network
|
/// Gets the unique Id of this object that is synced across the network
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -64,33 +62,7 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the ClientId of the owner of this NetworkObject
|
/// Gets the ClientId of the owner of this NetworkObject
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong OwnerClientId
|
public ulong OwnerClientId { get; internal set; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (OwnerClientIdInternal == null)
|
|
||||||
{
|
|
||||||
return NetworkManager != null ? NetworkManager.ServerClientId : 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return OwnerClientIdInternal.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal set
|
|
||||||
{
|
|
||||||
if (NetworkManager != null && value == NetworkManager.ServerClientId)
|
|
||||||
{
|
|
||||||
OwnerClientIdInternal = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OwnerClientIdInternal = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ulong? OwnerClientIdInternal = null;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true, the object will always be replicated as root on clients and the parent will be ignored.
|
/// If true, the object will always be replicated as root on clients and the parent will be ignored.
|
||||||
@@ -234,11 +206,6 @@ namespace Unity.Netcode
|
|||||||
throw new VisibilityChangeException("The object is already visible");
|
throw new VisibilityChangeException("The object is already visible");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
|
||||||
{
|
|
||||||
SnapshotSpawn(clientId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Observers.Add(clientId);
|
Observers.Add(clientId);
|
||||||
|
|
||||||
NetworkManager.SpawnManager.SendSpawnCallForObject(clientId, this);
|
NetworkManager.SpawnManager.SendSpawnCallForObject(clientId, this);
|
||||||
@@ -314,23 +281,15 @@ namespace Unity.Netcode
|
|||||||
throw new VisibilityChangeException("Cannot hide an object from the server");
|
throw new VisibilityChangeException("Cannot hide an object from the server");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Observers.Remove(clientId);
|
Observers.Remove(clientId);
|
||||||
|
|
||||||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
var message = new DestroyObjectMessage
|
||||||
{
|
{
|
||||||
SnapshotDespawn(clientId);
|
NetworkObjectId = NetworkObjectId
|
||||||
}
|
};
|
||||||
else
|
// Send destroy call
|
||||||
{
|
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
|
||||||
var message = new DestroyObjectMessage
|
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
|
||||||
{
|
|
||||||
NetworkObjectId = NetworkObjectId
|
|
||||||
};
|
|
||||||
// Send destroy call
|
|
||||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
|
|
||||||
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -345,14 +304,14 @@ namespace Unity.Netcode
|
|||||||
throw new ArgumentNullException("At least one " + nameof(NetworkObject) + " has to be provided");
|
throw new ArgumentNullException("At least one " + nameof(NetworkObject) + " has to be provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManager networkManager = networkObjects[0].NetworkManager;
|
var networkManager = networkObjects[0].NetworkManager;
|
||||||
|
|
||||||
if (!networkManager.IsServer)
|
if (!networkManager.IsServer)
|
||||||
{
|
{
|
||||||
throw new NotServerException("Only server can change visibility");
|
throw new NotServerException("Only server can change visibility");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientId == networkManager.ServerClientId)
|
if (clientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
throw new VisibilityChangeException("Cannot hide an object from the server");
|
throw new VisibilityChangeException("Cannot hide an object from the server");
|
||||||
}
|
}
|
||||||
@@ -384,84 +343,21 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned
|
if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned &&
|
||||||
&& (IsSceneObject == null || (IsSceneObject != null && IsSceneObject.Value != true)))
|
(IsSceneObject == null || (IsSceneObject != null && IsSceneObject.Value != true)))
|
||||||
{
|
{
|
||||||
throw new NotServerException($"Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call {nameof(Destroy)} or {nameof(Despawn)} on the server/host instead.");
|
throw new NotServerException($"Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call {nameof(Destroy)} or {nameof(Despawn)} on the server/host instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkManager != null && NetworkManager.SpawnManager != null && NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
if (NetworkManager != null && NetworkManager.SpawnManager != null &&
|
||||||
|
NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
||||||
{
|
{
|
||||||
NetworkManager.SpawnManager.OnDespawnObject(networkObject, false);
|
NetworkManager.SpawnManager.OnDespawnObject(networkObject, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SnapshotDespawnCommand GetDespawnCommand()
|
|
||||||
{
|
|
||||||
var command = new SnapshotDespawnCommand();
|
|
||||||
command.NetworkObjectId = NetworkObjectId;
|
|
||||||
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SnapshotSpawnCommand GetSpawnCommand()
|
|
||||||
{
|
|
||||||
var command = new SnapshotSpawnCommand();
|
|
||||||
command.NetworkObjectId = NetworkObjectId;
|
|
||||||
command.OwnerClientId = OwnerClientId;
|
|
||||||
command.IsPlayerObject = IsPlayerObject;
|
|
||||||
command.IsSceneObject = (IsSceneObject == null) || IsSceneObject.Value;
|
|
||||||
|
|
||||||
ulong? parent = NetworkManager.SpawnManager.GetSpawnParentId(this);
|
|
||||||
if (parent != null)
|
|
||||||
{
|
|
||||||
command.ParentNetworkId = parent.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// write own network id, when no parents. todo: optimize this.
|
|
||||||
command.ParentNetworkId = command.NetworkObjectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
command.GlobalObjectIdHash = HostCheckForGlobalObjectIdHashOverride();
|
|
||||||
// todo: check if (IncludeTransformWhenSpawning == null || IncludeTransformWhenSpawning(clientId)) for any clientId
|
|
||||||
command.ObjectPosition = transform.position;
|
|
||||||
command.ObjectRotation = transform.rotation;
|
|
||||||
command.ObjectScale = transform.localScale;
|
|
||||||
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SnapshotSpawn()
|
|
||||||
{
|
|
||||||
var command = GetSpawnCommand();
|
|
||||||
NetworkManager.SnapshotSystem.Spawn(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SnapshotSpawn(ulong clientId)
|
|
||||||
{
|
|
||||||
var command = GetSpawnCommand();
|
|
||||||
command.TargetClientIds = new List<ulong>();
|
|
||||||
command.TargetClientIds.Add(clientId);
|
|
||||||
NetworkManager.SnapshotSystem.Spawn(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SnapshotDespawn()
|
|
||||||
{
|
|
||||||
var command = GetDespawnCommand();
|
|
||||||
NetworkManager.SnapshotSystem.Despawn(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SnapshotDespawn(ulong clientId)
|
|
||||||
{
|
|
||||||
var command = GetDespawnCommand();
|
|
||||||
command.TargetClientIds = new List<ulong>();
|
|
||||||
command.TargetClientIds.Add(clientId);
|
|
||||||
NetworkManager.SnapshotSystem.Despawn(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void SpawnInternal(bool destroyWithScene, ulong? ownerClientId, bool playerObject)
|
private void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool playerObject)
|
||||||
{
|
{
|
||||||
if (!NetworkManager.IsListening)
|
if (!NetworkManager.IsListening)
|
||||||
{
|
{
|
||||||
@@ -475,12 +371,6 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManager.SpawnManager.GetNetworkObjectId(), false, playerObject, ownerClientId, destroyWithScene);
|
NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManager.SpawnManager.GetNetworkObjectId(), false, playerObject, ownerClientId, destroyWithScene);
|
||||||
|
|
||||||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
|
||||||
{
|
|
||||||
SnapshotSpawn();
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong ownerId = ownerClientId != null ? ownerClientId.Value : NetworkManager.ServerClientId;
|
|
||||||
for (int i = 0; i < NetworkManager.ConnectedClientsList.Count; i++)
|
for (int i = 0; i < NetworkManager.ConnectedClientsList.Count; i++)
|
||||||
{
|
{
|
||||||
if (Observers.Contains(NetworkManager.ConnectedClientsList[i].ClientId))
|
if (Observers.Contains(NetworkManager.ConnectedClientsList[i].ClientId))
|
||||||
@@ -496,7 +386,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="destroyWithScene">Should the object be destroyed when the scene is changed</param>
|
/// <param name="destroyWithScene">Should the object be destroyed when the scene is changed</param>
|
||||||
public void Spawn(bool destroyWithScene = false)
|
public void Spawn(bool destroyWithScene = false)
|
||||||
{
|
{
|
||||||
SpawnInternal(destroyWithScene, null, false);
|
SpawnInternal(destroyWithScene, NetworkManager.ServerClientId, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -547,17 +437,29 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void InvokeBehaviourOnLostOwnership()
|
internal void InvokeBehaviourOnLostOwnership()
|
||||||
{
|
{
|
||||||
|
// Server already handles this earlier, hosts should ignore, all clients should update
|
||||||
|
if (!NetworkManager.IsServer)
|
||||||
|
{
|
||||||
|
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId, true);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
{
|
{
|
||||||
ChildNetworkBehaviours[i].OnLostOwnership();
|
ChildNetworkBehaviours[i].InternalOnLostOwnership();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InvokeBehaviourOnGainedOwnership()
|
internal void InvokeBehaviourOnGainedOwnership()
|
||||||
{
|
{
|
||||||
|
// Server already handles this earlier, hosts should ignore and only client owners should update
|
||||||
|
if (!NetworkManager.IsServer && NetworkManager.LocalClientId == OwnerClientId)
|
||||||
|
{
|
||||||
|
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
{
|
{
|
||||||
ChildNetworkBehaviours[i].OnGainedOwnership();
|
ChildNetworkBehaviours[i].InternalOnGainedOwnership();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,13 +658,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (!NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(m_LatestParent.Value))
|
if (!NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(m_LatestParent.Value))
|
||||||
{
|
{
|
||||||
if (OrphanChildren.Add(this))
|
OrphanChildren.Add(this);
|
||||||
{
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning($"{nameof(NetworkObject)} ({name}) cannot find its parent, added to {nameof(OrphanChildren)} set");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -793,19 +689,21 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void InvokeBehaviourNetworkSpawn()
|
internal void InvokeBehaviourNetworkSpawn()
|
||||||
{
|
{
|
||||||
|
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId);
|
||||||
|
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
{
|
{
|
||||||
ChildNetworkBehaviours[i].InternalOnNetworkSpawn();
|
ChildNetworkBehaviours[i].InternalOnNetworkSpawn();
|
||||||
ChildNetworkBehaviours[i].OnNetworkSpawn();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InvokeBehaviourNetworkDespawn()
|
internal void InvokeBehaviourNetworkDespawn()
|
||||||
{
|
{
|
||||||
|
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId, true);
|
||||||
|
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
{
|
{
|
||||||
ChildNetworkBehaviours[i].InternalOnNetworkDespawn();
|
ChildNetworkBehaviours[i].InternalOnNetworkDespawn();
|
||||||
ChildNetworkBehaviours[i].OnNetworkDespawn();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -834,13 +732,13 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong clientId)
|
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
{
|
{
|
||||||
var behavior = ChildNetworkBehaviours[i];
|
var behavior = ChildNetworkBehaviours[i];
|
||||||
behavior.InitializeVariables();
|
behavior.InitializeVariables();
|
||||||
behavior.WriteNetworkVariableData(writer, clientId);
|
behavior.WriteNetworkVariableData(writer, targetClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -853,6 +751,27 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NGO currently guarantees that the client will receive spawn data for all objects in one network tick.
|
||||||
|
// Children may arrive before their parents; when they do they are stored in OrphanedChildren and then
|
||||||
|
// resolved when their parents arrived. Because we don't send a partial list of spawns (yet), something
|
||||||
|
// has gone wrong if by the end of an update we still have unresolved orphans
|
||||||
|
//
|
||||||
|
|
||||||
|
// 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
|
||||||
|
internal static void VerifyParentingStatus()
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
if (OrphanChildren.Count > 0)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning($"{nameof(NetworkObject)} ({OrphanChildren.Count}) children not resolved to parents by the end of frame");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
internal void SetNetworkVariableData(FastBufferReader reader)
|
internal void SetNetworkVariableData(FastBufferReader reader)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
@@ -918,7 +837,6 @@ namespace Unity.Netcode
|
|||||||
public bool IsSceneObject;
|
public bool IsSceneObject;
|
||||||
public bool HasTransform;
|
public bool HasTransform;
|
||||||
public bool IsReparented;
|
public bool IsReparented;
|
||||||
public bool HasNetworkVariables;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HeaderData Header;
|
public HeaderData Header;
|
||||||
@@ -979,10 +897,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Header.HasNetworkVariables)
|
OwnerObject.WriteNetworkVariableData(writer, TargetClientId);
|
||||||
{
|
|
||||||
OwnerObject.WriteNetworkVariableData(writer, TargetClientId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Deserialize(FastBufferReader reader)
|
public unsafe void Deserialize(FastBufferReader reader)
|
||||||
@@ -1022,7 +937,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal SceneObject GetMessageSceneObject(ulong targetClientId, bool includeNetworkVariableData = true)
|
internal SceneObject GetMessageSceneObject(ulong targetClientId)
|
||||||
{
|
{
|
||||||
var obj = new SceneObject
|
var obj = new SceneObject
|
||||||
{
|
{
|
||||||
@@ -1033,7 +948,6 @@ namespace Unity.Netcode
|
|||||||
OwnerClientId = OwnerClientId,
|
OwnerClientId = OwnerClientId,
|
||||||
IsSceneObject = IsSceneObject ?? true,
|
IsSceneObject = IsSceneObject ?? true,
|
||||||
Hash = HostCheckForGlobalObjectIdHashOverride(),
|
Hash = HostCheckForGlobalObjectIdHashOverride(),
|
||||||
HasNetworkVariables = includeNetworkVariableData
|
|
||||||
},
|
},
|
||||||
OwnerObject = this,
|
OwnerObject = this,
|
||||||
TargetClientId = targetClientId
|
TargetClientId = targetClientId
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
internal class ConnectionRtt
|
|
||||||
{
|
|
||||||
private double[] m_RttSendTimes; // times at which packet were sent for RTT computations
|
|
||||||
private int[] m_SendSequence; // tick, or other key, at which packets were sent (to allow matching)
|
|
||||||
private double[] m_MeasuredLatencies; // measured latencies (ring buffer)
|
|
||||||
private int m_LatenciesBegin = 0; // ring buffer begin
|
|
||||||
private int m_LatenciesEnd = 0; // ring buffer end
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Round-trip-time data
|
|
||||||
/// </summary>
|
|
||||||
public struct Rtt
|
|
||||||
{
|
|
||||||
public double BestSec; // best RTT
|
|
||||||
public double AverageSec; // average RTT
|
|
||||||
public double WorstSec; // worst RTT
|
|
||||||
public double LastSec; // latest ack'ed RTT
|
|
||||||
public int SampleCount; // number of contributing samples
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionRtt()
|
|
||||||
{
|
|
||||||
m_RttSendTimes = new double[NetworkConfig.RttWindowSize];
|
|
||||||
m_SendSequence = new int[NetworkConfig.RttWindowSize];
|
|
||||||
m_MeasuredLatencies = new double[NetworkConfig.RttWindowSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the Round-trip-time computation for this client
|
|
||||||
/// </summary>
|
|
||||||
public Rtt GetRtt()
|
|
||||||
{
|
|
||||||
var ret = new Rtt();
|
|
||||||
var index = m_LatenciesBegin;
|
|
||||||
double total = 0.0;
|
|
||||||
ret.BestSec = m_MeasuredLatencies[m_LatenciesBegin];
|
|
||||||
ret.WorstSec = m_MeasuredLatencies[m_LatenciesBegin];
|
|
||||||
|
|
||||||
while (index != m_LatenciesEnd)
|
|
||||||
{
|
|
||||||
total += m_MeasuredLatencies[index];
|
|
||||||
ret.SampleCount++;
|
|
||||||
ret.BestSec = Math.Min(ret.BestSec, m_MeasuredLatencies[index]);
|
|
||||||
ret.WorstSec = Math.Max(ret.WorstSec, m_MeasuredLatencies[index]);
|
|
||||||
index = (index + 1) % NetworkConfig.RttAverageSamples;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret.SampleCount != 0)
|
|
||||||
{
|
|
||||||
ret.AverageSec = total / ret.SampleCount;
|
|
||||||
// the latest RTT is one before m_LatenciesEnd
|
|
||||||
ret.LastSec = m_MeasuredLatencies[(m_LatenciesEnd + (NetworkConfig.RttWindowSize - 1)) % NetworkConfig.RttWindowSize];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ret.AverageSec = 0;
|
|
||||||
ret.BestSec = 0;
|
|
||||||
ret.WorstSec = 0;
|
|
||||||
ret.SampleCount = 0;
|
|
||||||
ret.LastSec = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void NotifySend(int sequence, double timeSec)
|
|
||||||
{
|
|
||||||
m_RttSendTimes[sequence % NetworkConfig.RttWindowSize] = timeSec;
|
|
||||||
m_SendSequence[sequence % NetworkConfig.RttWindowSize] = sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void NotifyAck(int sequence, double timeSec)
|
|
||||||
{
|
|
||||||
// if the same slot was not used by a later send
|
|
||||||
if (m_SendSequence[sequence % NetworkConfig.RttWindowSize] == sequence)
|
|
||||||
{
|
|
||||||
double latency = timeSec - m_RttSendTimes[sequence % NetworkConfig.RttWindowSize];
|
|
||||||
|
|
||||||
m_MeasuredLatencies[m_LatenciesEnd] = latency;
|
|
||||||
m_LatenciesEnd = (m_LatenciesEnd + 1) % NetworkConfig.RttAverageSamples;
|
|
||||||
|
|
||||||
if (m_LatenciesEnd == m_LatenciesBegin)
|
|
||||||
{
|
|
||||||
m_LatenciesBegin = (m_LatenciesBegin + 1) % NetworkConfig.RttAverageSamples;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -14,9 +14,9 @@ namespace Unity.Netcode
|
|||||||
public static LogLevel CurrentLogLevel => NetworkManager.Singleton == null ? LogLevel.Normal : NetworkManager.Singleton.LogLevel;
|
public static LogLevel CurrentLogLevel => NetworkManager.Singleton == null ? LogLevel.Normal : NetworkManager.Singleton.LogLevel;
|
||||||
|
|
||||||
// internal logging
|
// internal logging
|
||||||
internal static void LogInfo(string message) => Debug.Log($"[Netcode] {message}");
|
public static void LogInfo(string message) => Debug.Log($"[Netcode] {message}");
|
||||||
internal static void LogWarning(string message) => Debug.LogWarning($"[Netcode] {message}");
|
public static void LogWarning(string message) => Debug.LogWarning($"[Netcode] {message}");
|
||||||
internal static void LogError(string message) => Debug.LogError($"[Netcode] {message}");
|
public static void LogError(string message) => Debug.LogError($"[Netcode] {message}");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs an info log locally and on the server if possible.
|
/// Logs an info log locally and on the server if possible.
|
||||||
@@ -62,9 +62,9 @@ namespace Unity.Netcode
|
|||||||
LogType = logType,
|
LogType = logType,
|
||||||
Message = message
|
Message = message
|
||||||
};
|
};
|
||||||
var size = NetworkManager.Singleton.SendMessage(ref networkMessage, NetworkDelivery.ReliableFragmentedSequenced, NetworkManager.Singleton.ServerClientId);
|
var size = NetworkManager.Singleton.SendMessage(ref networkMessage, NetworkDelivery.ReliableFragmentedSequenced, NetworkManager.ServerClientId);
|
||||||
|
|
||||||
NetworkManager.Singleton.NetworkMetrics.TrackServerLogSent(NetworkManager.Singleton.ServerClientId, (uint)logType, size);
|
NetworkManager.Singleton.NetworkMetrics.TrackServerLogSent(NetworkManager.ServerClientId, (uint)logType, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,24 +29,33 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
public void Handle(ref NetworkContext context)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
|
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
|
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
|
||||||
|
var originalOwner = networkObject.OwnerClientId;
|
||||||
if (networkObject.OwnerClientId == networkManager.LocalClientId)
|
|
||||||
{
|
|
||||||
//We are current owner.
|
|
||||||
networkObject.InvokeBehaviourOnLostOwnership();
|
|
||||||
}
|
|
||||||
|
|
||||||
networkObject.OwnerClientId = OwnerClientId;
|
networkObject.OwnerClientId = OwnerClientId;
|
||||||
|
|
||||||
|
// We are current owner.
|
||||||
|
if (originalOwner == networkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
networkObject.InvokeBehaviourOnLostOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are new owner.
|
||||||
if (OwnerClientId == networkManager.LocalClientId)
|
if (OwnerClientId == networkManager.LocalClientId)
|
||||||
{
|
{
|
||||||
//We are new owner.
|
|
||||||
networkObject.InvokeBehaviourOnGainedOwnership();
|
networkObject.InvokeBehaviourOnGainedOwnership();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For all other clients that are neither the former or current owner, update the behaviours' properties
|
||||||
|
if (OwnerClientId != networkManager.LocalClientId && originalOwner != networkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < networkObject.ChildNetworkBehaviours.Count; i++)
|
||||||
|
{
|
||||||
|
networkObject.ChildNetworkBehaviours[i].UpdateNetworkProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
networkManager.NetworkMetrics.TrackOwnershipChangeReceived(context.SenderId, networkObject, context.MessageSize);
|
networkManager.NetworkMetrics.TrackOwnershipChangeReceived(context.SenderId, networkObject, context.MessageSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace Unity.Netcode
|
|||||||
public ushort NetworkBehaviourIndex;
|
public ushort NetworkBehaviourIndex;
|
||||||
|
|
||||||
public HashSet<int> DeliveryMappedNetworkVariableIndex;
|
public HashSet<int> DeliveryMappedNetworkVariableIndex;
|
||||||
public ulong ClientId;
|
public ulong TargetClientId;
|
||||||
public NetworkBehaviour NetworkBehaviour;
|
public NetworkBehaviour NetworkBehaviour;
|
||||||
|
|
||||||
private FastBufferReader m_ReceivedNetworkVariableData;
|
private FastBufferReader m_ReceivedNetworkVariableData;
|
||||||
@@ -31,9 +31,9 @@ namespace Unity.Netcode
|
|||||||
writer.WriteValue(NetworkObjectId);
|
writer.WriteValue(NetworkObjectId);
|
||||||
writer.WriteValue(NetworkBehaviourIndex);
|
writer.WriteValue(NetworkBehaviourIndex);
|
||||||
|
|
||||||
for (int k = 0; k < NetworkBehaviour.NetworkVariableFields.Count; k++)
|
for (int i = 0; i < NetworkBehaviour.NetworkVariableFields.Count; i++)
|
||||||
{
|
{
|
||||||
if (!DeliveryMappedNetworkVariableIndex.Contains(k))
|
if (!DeliveryMappedNetworkVariableIndex.Contains(i))
|
||||||
{
|
{
|
||||||
// This var does not belong to the currently iterating delivery group.
|
// This var does not belong to the currently iterating delivery group.
|
||||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
@@ -48,15 +48,17 @@ namespace Unity.Netcode
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if I'm dirty AND a client, write (server always has all permissions)
|
var startingSize = writer.Length;
|
||||||
// if I'm dirty AND the server AND the client can read me, send.
|
var networkVariable = NetworkBehaviour.NetworkVariableFields[i];
|
||||||
bool shouldWrite = NetworkBehaviour.NetworkVariableFields[k].ShouldWrite(ClientId, NetworkBehaviour.NetworkManager.IsServer);
|
var shouldWrite = networkVariable.IsDirty() &&
|
||||||
|
networkVariable.CanClientRead(TargetClientId) &&
|
||||||
|
(NetworkBehaviour.NetworkManager.IsServer || networkVariable.CanClientWrite(NetworkBehaviour.NetworkManager.LocalClientId));
|
||||||
|
|
||||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
if (!shouldWrite)
|
if (!shouldWrite)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe((ushort)0);
|
BytePacker.WriteValueBitPacked(writer, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -68,34 +70,34 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, short.MaxValue);
|
var tempWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
||||||
NetworkBehaviour.NetworkVariableFields[k].WriteDelta(tmpWriter);
|
NetworkBehaviour.NetworkVariableFields[i].WriteDelta(tempWriter);
|
||||||
|
BytePacker.WriteValueBitPacked(writer, tempWriter.Length);
|
||||||
|
|
||||||
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize<ushort>() + tmpWriter.Length))
|
if (!writer.TryBeginWrite(tempWriter.Length))
|
||||||
{
|
{
|
||||||
throw new OverflowException($"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}");
|
throw new OverflowException($"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteValue((ushort)tmpWriter.Length);
|
tempWriter.CopyTo(writer);
|
||||||
tmpWriter.CopyTo(writer);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NetworkBehaviour.NetworkVariableFields[k].WriteDelta(writer);
|
networkVariable.WriteDelta(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NetworkBehaviour.NetworkVariableIndexesToResetSet.Contains(k))
|
if (!NetworkBehaviour.NetworkVariableIndexesToResetSet.Contains(i))
|
||||||
{
|
{
|
||||||
NetworkBehaviour.NetworkVariableIndexesToResetSet.Add(k);
|
NetworkBehaviour.NetworkVariableIndexesToResetSet.Add(i);
|
||||||
NetworkBehaviour.NetworkVariableIndexesToReset.Add(k);
|
NetworkBehaviour.NetworkVariableIndexesToReset.Add(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
|
NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
|
||||||
ClientId,
|
TargetClientId,
|
||||||
NetworkBehaviour.NetworkObject,
|
NetworkBehaviour.NetworkObject,
|
||||||
NetworkBehaviour.NetworkVariableFields[k].Name,
|
networkVariable.Name,
|
||||||
NetworkBehaviour.__getTypeName(),
|
NetworkBehaviour.__getTypeName(),
|
||||||
writer.Length);
|
writer.Length - startingSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,9 +123,9 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out NetworkObject networkObject))
|
if (networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out NetworkObject networkObject))
|
||||||
{
|
{
|
||||||
NetworkBehaviour behaviour = networkObject.GetNetworkBehaviourAtOrderIndex(NetworkBehaviourIndex);
|
var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(NetworkBehaviourIndex);
|
||||||
|
|
||||||
if (behaviour == null)
|
if (networkBehaviour == null)
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
@@ -132,13 +134,12 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < behaviour.NetworkVariableFields.Count; i++)
|
for (int i = 0; i < networkBehaviour.NetworkVariableFields.Count; i++)
|
||||||
{
|
{
|
||||||
ushort varSize = 0;
|
int varSize = 0;
|
||||||
|
|
||||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
m_ReceivedNetworkVariableData.ReadValueSafe(out varSize);
|
ByteUnpacker.ReadValueBitPacked(m_ReceivedNetworkVariableData, out varSize);
|
||||||
|
|
||||||
if (varSize == 0)
|
if (varSize == 0)
|
||||||
{
|
{
|
||||||
@@ -154,15 +155,17 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (networkManager.IsServer)
|
var networkVariable = networkBehaviour.NetworkVariableFields[i];
|
||||||
|
|
||||||
|
if (networkManager.IsServer && !networkVariable.CanClientWrite(context.SenderId))
|
||||||
{
|
{
|
||||||
// we are choosing not to fire an exception here, because otherwise a malicious client could use this to crash the server
|
// we are choosing not to fire an exception here, because otherwise a malicious client could use this to crash the server
|
||||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}");
|
NetworkLog.LogWarning($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(networkBehaviour)} - VariableIndex: {i}");
|
||||||
NetworkLog.LogError($"[{behaviour.NetworkVariableFields[i].GetType().Name}]");
|
NetworkLog.LogError($"[{networkVariable.GetType().Name}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ReceivedNetworkVariableData.Seek(m_ReceivedNetworkVariableData.Position + varSize);
|
m_ReceivedNetworkVariableData.Seek(m_ReceivedNetworkVariableData.Position + varSize);
|
||||||
@@ -176,23 +179,23 @@ namespace Unity.Netcode
|
|||||||
//A dummy read COULD be added to the interface for this situation, but it's just being too nice.
|
//A dummy read COULD be added to the interface for this situation, but it's just being too nice.
|
||||||
//This is after all a developer fault. A critical error should be fine.
|
//This is after all a developer fault. A critical error should be fine.
|
||||||
// - TwoTen
|
// - TwoTen
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogError($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. No more variables can be read. This is critical. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}");
|
NetworkLog.LogError($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. No more variables can be read. This is critical. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(networkBehaviour)} - VariableIndex: {i}");
|
||||||
NetworkLog.LogError($"[{behaviour.NetworkVariableFields[i].GetType().Name}]");
|
NetworkLog.LogError($"[{networkVariable.GetType().Name}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int readStartPos = m_ReceivedNetworkVariableData.Position;
|
int readStartPos = m_ReceivedNetworkVariableData.Position;
|
||||||
|
|
||||||
behaviour.NetworkVariableFields[i].ReadDelta(m_ReceivedNetworkVariableData, networkManager.IsServer);
|
networkVariable.ReadDelta(m_ReceivedNetworkVariableData, networkManager.IsServer);
|
||||||
|
|
||||||
networkManager.NetworkMetrics.TrackNetworkVariableDeltaReceived(
|
networkManager.NetworkMetrics.TrackNetworkVariableDeltaReceived(
|
||||||
context.SenderId,
|
context.SenderId,
|
||||||
networkObject,
|
networkObject,
|
||||||
behaviour.NetworkVariableFields[i].Name,
|
networkVariable.Name,
|
||||||
behaviour.__getTypeName(),
|
networkBehaviour.__getTypeName(),
|
||||||
context.MessageSize);
|
context.MessageSize);
|
||||||
|
|
||||||
|
|
||||||
@@ -202,7 +205,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"Var delta read too far. {m_ReceivedNetworkVariableData.Position - (readStartPos + varSize)} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}");
|
NetworkLog.LogWarning($"Var delta read too far. {m_ReceivedNetworkVariableData.Position - (readStartPos + varSize)} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(networkBehaviour)} - VariableIndex: {i}");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ReceivedNetworkVariableData.Seek(readStartPos + varSize);
|
m_ReceivedNetworkVariableData.Seek(readStartPos + varSize);
|
||||||
@@ -211,7 +214,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"Var delta read too little. {(readStartPos + varSize) - m_ReceivedNetworkVariableData.Position} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}");
|
NetworkLog.LogWarning($"Var delta read too little. {readStartPos + varSize - m_ReceivedNetworkVariableData.Position} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(networkBehaviour)} - VariableIndex: {i}");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ReceivedNetworkVariableData.Seek(readStartPos + varSize);
|
m_ReceivedNetworkVariableData.Seek(readStartPos + varSize);
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Unity.Collections;
|
|
||||||
using Unity.Collections.LowLevel.Unsafe;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
internal struct SnapshotDataMessage : INetworkMessage
|
|
||||||
{
|
|
||||||
public int CurrentTick;
|
|
||||||
public ushort Sequence;
|
|
||||||
|
|
||||||
public ushort Range;
|
|
||||||
|
|
||||||
public byte[] SendMainBuffer;
|
|
||||||
public NativeArray<byte> ReceiveMainBuffer;
|
|
||||||
|
|
||||||
public struct AckData
|
|
||||||
{
|
|
||||||
public ushort LastReceivedSequence;
|
|
||||||
public ushort ReceivedSequenceMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AckData Ack;
|
|
||||||
|
|
||||||
public struct EntryData
|
|
||||||
{
|
|
||||||
public ulong NetworkObjectId;
|
|
||||||
public ushort BehaviourIndex;
|
|
||||||
public ushort VariableIndex;
|
|
||||||
public int TickWritten;
|
|
||||||
public ushort Position;
|
|
||||||
public ushort Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NativeList<EntryData> Entries;
|
|
||||||
|
|
||||||
public struct SpawnData
|
|
||||||
{
|
|
||||||
public ulong NetworkObjectId;
|
|
||||||
public uint Hash;
|
|
||||||
public bool IsSceneObject;
|
|
||||||
|
|
||||||
public bool IsPlayerObject;
|
|
||||||
public ulong OwnerClientId;
|
|
||||||
public ulong ParentNetworkId;
|
|
||||||
public Vector3 Position;
|
|
||||||
public Quaternion Rotation;
|
|
||||||
public Vector3 Scale;
|
|
||||||
|
|
||||||
public int TickWritten;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NativeList<SpawnData> Spawns;
|
|
||||||
|
|
||||||
public struct DespawnData
|
|
||||||
{
|
|
||||||
public ulong NetworkObjectId;
|
|
||||||
public int TickWritten;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NativeList<DespawnData> Despawns;
|
|
||||||
|
|
||||||
public unsafe void Serialize(FastBufferWriter writer)
|
|
||||||
{
|
|
||||||
if (!writer.TryBeginWrite(
|
|
||||||
FastBufferWriter.GetWriteSize(CurrentTick) +
|
|
||||||
FastBufferWriter.GetWriteSize(Sequence) +
|
|
||||||
FastBufferWriter.GetWriteSize(Range) + Range +
|
|
||||||
FastBufferWriter.GetWriteSize(Ack) +
|
|
||||||
FastBufferWriter.GetWriteSize<ushort>() +
|
|
||||||
Entries.Length * sizeof(EntryData) +
|
|
||||||
FastBufferWriter.GetWriteSize<ushort>() +
|
|
||||||
Spawns.Length * sizeof(SpawnData) +
|
|
||||||
FastBufferWriter.GetWriteSize<ushort>() +
|
|
||||||
Despawns.Length * sizeof(DespawnData)
|
|
||||||
))
|
|
||||||
{
|
|
||||||
throw new OverflowException($"Not enough space to serialize {nameof(SnapshotDataMessage)}");
|
|
||||||
}
|
|
||||||
writer.WriteValue(CurrentTick);
|
|
||||||
writer.WriteValue(Sequence);
|
|
||||||
|
|
||||||
writer.WriteValue(Range);
|
|
||||||
writer.WriteBytes(SendMainBuffer, Range);
|
|
||||||
writer.WriteValue(Ack);
|
|
||||||
|
|
||||||
writer.WriteValue((ushort)Entries.Length);
|
|
||||||
writer.WriteBytes((byte*)Entries.GetUnsafePtr(), Entries.Length * sizeof(EntryData));
|
|
||||||
|
|
||||||
writer.WriteValue((ushort)Spawns.Length);
|
|
||||||
writer.WriteBytes((byte*)Spawns.GetUnsafePtr(), Spawns.Length * sizeof(SpawnData));
|
|
||||||
|
|
||||||
writer.WriteValue((ushort)Despawns.Length);
|
|
||||||
writer.WriteBytes((byte*)Despawns.GetUnsafePtr(), Despawns.Length * sizeof(DespawnData));
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
|
||||||
{
|
|
||||||
if (!reader.TryBeginRead(
|
|
||||||
FastBufferWriter.GetWriteSize(CurrentTick) +
|
|
||||||
FastBufferWriter.GetWriteSize(Sequence) +
|
|
||||||
FastBufferWriter.GetWriteSize(Range)
|
|
||||||
))
|
|
||||||
{
|
|
||||||
throw new OverflowException($"Not enough space to deserialize {nameof(SnapshotDataMessage)}");
|
|
||||||
}
|
|
||||||
reader.ReadValue(out CurrentTick);
|
|
||||||
reader.ReadValue(out Sequence);
|
|
||||||
|
|
||||||
reader.ReadValue(out Range);
|
|
||||||
ReceiveMainBuffer = new NativeArray<byte>(Range, Allocator.Temp);
|
|
||||||
reader.ReadBytesSafe((byte*)ReceiveMainBuffer.GetUnsafePtr(), Range);
|
|
||||||
reader.ReadValueSafe(out Ack);
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out ushort length);
|
|
||||||
Entries = new NativeList<EntryData>(length, Allocator.Temp) { Length = length };
|
|
||||||
reader.ReadBytesSafe((byte*)Entries.GetUnsafePtr(), Entries.Length * sizeof(EntryData));
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out length);
|
|
||||||
Spawns = new NativeList<SpawnData>(length, Allocator.Temp) { Length = length };
|
|
||||||
reader.ReadBytesSafe((byte*)Spawns.GetUnsafePtr(), Spawns.Length * sizeof(SpawnData));
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out length);
|
|
||||||
Despawns = new NativeList<DespawnData>(length, Allocator.Temp) { Length = length };
|
|
||||||
reader.ReadBytesSafe((byte*)Despawns.GetUnsafePtr(), Despawns.Length * sizeof(DespawnData));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(ref NetworkContext context)
|
|
||||||
{
|
|
||||||
using (ReceiveMainBuffer)
|
|
||||||
using (Entries)
|
|
||||||
using (Spawns)
|
|
||||||
using (Despawns)
|
|
||||||
{
|
|
||||||
var systemOwner = context.SystemOwner;
|
|
||||||
var senderId = context.SenderId;
|
|
||||||
if (systemOwner is NetworkManager networkManager)
|
|
||||||
{
|
|
||||||
// todo: temporary hack around bug
|
|
||||||
if (!networkManager.IsServer)
|
|
||||||
{
|
|
||||||
senderId = networkManager.ServerClientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
var snapshotSystem = networkManager.SnapshotSystem;
|
|
||||||
snapshotSystem.HandleSnapshot(senderId, this);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var ownerData = (Tuple<SnapshotSystem, ulong>)systemOwner;
|
|
||||||
var snapshotSystem = ownerData.Item1;
|
|
||||||
snapshotSystem.HandleSnapshot(ownerData.Item2, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 5cf75026c2ab86646aac16b39d7259ad
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -67,7 +67,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
public const int NON_FRAGMENTED_MESSAGE_MAX_SIZE = 1300;
|
public const int NON_FRAGMENTED_MESSAGE_MAX_SIZE = 1300;
|
||||||
public const int FRAGMENTED_MESSAGE_MAX_SIZE = int.MaxValue;
|
public const int FRAGMENTED_MESSAGE_MAX_SIZE = BytePacker.BitPackedIntMax;
|
||||||
|
|
||||||
internal struct MessageWithHandler
|
internal struct MessageWithHandler
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -87,7 +87,13 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
void TrackPacketReceived(uint packetCount);
|
void TrackPacketReceived(uint packetCount);
|
||||||
|
|
||||||
void TrackRttToServer(int rtt);
|
void UpdateRttToServer(int rtt);
|
||||||
|
|
||||||
|
void UpdateNetworkObjectsCount(int count);
|
||||||
|
|
||||||
|
void UpdateConnectionsCount(int count);
|
||||||
|
|
||||||
|
void UpdatePacketLoss(float packetLoss);
|
||||||
|
|
||||||
void DispatchFrame();
|
void DispatchFrame();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ namespace Unity.Netcode
|
|||||||
private readonly EventMetric<SceneEventMetric> m_SceneEventSentEvent = new EventMetric<SceneEventMetric>(NetworkMetricTypes.SceneEventSent.Id);
|
private readonly EventMetric<SceneEventMetric> m_SceneEventSentEvent = new EventMetric<SceneEventMetric>(NetworkMetricTypes.SceneEventSent.Id);
|
||||||
private readonly EventMetric<SceneEventMetric> m_SceneEventReceivedEvent = new EventMetric<SceneEventMetric>(NetworkMetricTypes.SceneEventReceived.Id);
|
private readonly EventMetric<SceneEventMetric> m_SceneEventReceivedEvent = new EventMetric<SceneEventMetric>(NetworkMetricTypes.SceneEventReceived.Id);
|
||||||
|
|
||||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
private readonly Counter m_PacketSentCounter = new Counter(NetworkMetricTypes.PacketsSent.Id)
|
private readonly Counter m_PacketSentCounter = new Counter(NetworkMetricTypes.PacketsSent.Id)
|
||||||
{
|
{
|
||||||
ShouldResetOnDispatch = true,
|
ShouldResetOnDispatch = true,
|
||||||
@@ -79,6 +79,15 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
ShouldResetOnDispatch = true,
|
ShouldResetOnDispatch = true,
|
||||||
};
|
};
|
||||||
|
private readonly Gauge m_NetworkObjectsGauge = new Gauge(NetworkMetricTypes.NetworkObjects.Id)
|
||||||
|
{
|
||||||
|
ShouldResetOnDispatch = true,
|
||||||
|
};
|
||||||
|
private readonly Gauge m_ConnectionsGauge = new Gauge(NetworkMetricTypes.ConnectedClients.Id)
|
||||||
|
{
|
||||||
|
ShouldResetOnDispatch = true,
|
||||||
|
};
|
||||||
|
private readonly Gauge m_PacketLossGauge = new Gauge(NetworkMetricTypes.PacketLoss.Id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private ulong m_NumberOfMetricsThisFrame;
|
private ulong m_NumberOfMetricsThisFrame;
|
||||||
@@ -97,9 +106,12 @@ namespace Unity.Netcode
|
|||||||
.WithMetricEvents(m_RpcSentEvent, m_RpcReceivedEvent)
|
.WithMetricEvents(m_RpcSentEvent, m_RpcReceivedEvent)
|
||||||
.WithMetricEvents(m_ServerLogSentEvent, m_ServerLogReceivedEvent)
|
.WithMetricEvents(m_ServerLogSentEvent, m_ServerLogReceivedEvent)
|
||||||
.WithMetricEvents(m_SceneEventSentEvent, m_SceneEventReceivedEvent)
|
.WithMetricEvents(m_SceneEventSentEvent, m_SceneEventReceivedEvent)
|
||||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
.WithCounters(m_PacketSentCounter, m_PacketReceivedCounter)
|
.WithCounters(m_PacketSentCounter, m_PacketReceivedCounter)
|
||||||
.WithGauges(m_RttToServerGauge)
|
.WithGauges(m_RttToServerGauge)
|
||||||
|
.WithGauges(m_NetworkObjectsGauge)
|
||||||
|
.WithGauges(m_ConnectionsGauge)
|
||||||
|
.WithGauges(m_PacketLossGauge)
|
||||||
#endif
|
#endif
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
@@ -428,7 +440,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
public void TrackPacketSent(uint packetCount)
|
public void TrackPacketSent(uint packetCount)
|
||||||
{
|
{
|
||||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
if (!CanSendMetrics)
|
if (!CanSendMetrics)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -441,7 +453,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
public void TrackPacketReceived(uint packetCount)
|
public void TrackPacketReceived(uint packetCount)
|
||||||
{
|
{
|
||||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
if (!CanSendMetrics)
|
if (!CanSendMetrics)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -452,15 +464,51 @@ namespace Unity.Netcode
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TrackRttToServer(int rtt)
|
public void UpdateRttToServer(int rttMilliseconds)
|
||||||
{
|
{
|
||||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var rttSeconds = rttMilliseconds * 1e-3;
|
||||||
|
m_RttToServerGauge.Set(rttSeconds);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateNetworkObjectsCount(int count)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
if (!CanSendMetrics)
|
if (!CanSendMetrics)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_RttToServerGauge.Set(rtt);
|
m_NetworkObjectsGauge.Set(count);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConnectionsCount(int count)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ConnectionsGauge.Set(count);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePacketLoss(float packetLoss)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PacketLossGauge.Set(packetLoss);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,7 +145,19 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TrackRttToServer(int rtt)
|
public void UpdateRttToServer(int rtt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateNetworkObjectsCount(int count)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConnectionsCount(int count)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePacketLoss(float packetLoss)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,17 +24,12 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event OnListChangedDelegate OnListChanged;
|
public event OnListChangedDelegate OnListChanged;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkList with the default value and settings
|
|
||||||
/// </summary>
|
|
||||||
public NetworkList() { }
|
public NetworkList() { }
|
||||||
|
|
||||||
/// <summary>
|
public NetworkList(IEnumerable<T> values = default,
|
||||||
/// Creates a NetworkList with the default value and custom settings
|
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
||||||
/// </summary>
|
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||||
/// <param name="readPerm">The read permission to use for the NetworkList</param>
|
: base(readPerm, writePerm)
|
||||||
/// <param name="values">The initial value to use for the NetworkList</param>
|
|
||||||
public NetworkList(NetworkVariableReadPermission readPerm, IEnumerable<T> values) : base(readPerm)
|
|
||||||
{
|
{
|
||||||
foreach (var value in values)
|
foreach (var value in values)
|
||||||
{
|
{
|
||||||
@@ -42,19 +37,6 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkList with a custom value and the default settings
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="values">The initial value to use for the NetworkList</param>
|
|
||||||
public NetworkList(IEnumerable<T> values)
|
|
||||||
{
|
|
||||||
foreach (var value in values)
|
|
||||||
{
|
|
||||||
m_List.Add(value);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void ResetDirty()
|
public override void ResetDirty()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
@@ -22,7 +24,8 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Functions that serialize other types
|
// Functions that serialize other types
|
||||||
private static void WriteValue<TForMethod>(FastBufferWriter writer, in TForMethod value) where TForMethod : unmanaged
|
private static void WriteValue<TForMethod>(FastBufferWriter writer, in TForMethod value)
|
||||||
|
where TForMethod : unmanaged
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(value);
|
writer.WriteValueSafe(value);
|
||||||
}
|
}
|
||||||
@@ -37,16 +40,13 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal delegate void ReadDelegate<TForMethod>(FastBufferReader reader, out TForMethod value);
|
internal delegate void ReadDelegate<TForMethod>(FastBufferReader reader, out TForMethod value);
|
||||||
|
|
||||||
// These static delegates provide the right implementation for writing and reading a particular network variable
|
// These static delegates provide the right implementation for writing and reading a particular network variable type.
|
||||||
// type.
|
|
||||||
//
|
|
||||||
// For most types, these default to WriteValue() and ReadValue(), which perform simple memcpy operations.
|
// For most types, these default to WriteValue() and ReadValue(), which perform simple memcpy operations.
|
||||||
//
|
//
|
||||||
// INetworkSerializableILPP will generate startup code that will set it to WriteNetworkSerializable()
|
// INetworkSerializableILPP will generate startup code that will set it to WriteNetworkSerializable()
|
||||||
// and ReadNetworkSerializable() for INetworkSerializable types, which will call NetworkSerialize().
|
// and ReadNetworkSerializable() for INetworkSerializable types, which will call NetworkSerialize().
|
||||||
//
|
//
|
||||||
// In the future we may be able to use this to provide packing implementations for floats and integers to
|
// In the future we may be able to use this to provide packing implementations for floats and integers to optimize bandwidth usage.
|
||||||
// optimize bandwidth usage.
|
|
||||||
//
|
//
|
||||||
// The reason this is done is to avoid runtime reflection and boxing in NetworkVariable - without this,
|
// The reason this is done is to avoid runtime reflection and boxing in NetworkVariable - without this,
|
||||||
// NetworkVariable would need to do a `var is INetworkSerializable` check, and then cast to INetworkSerializable,
|
// NetworkVariable would need to do a `var is INetworkSerializable` check, and then cast to INetworkSerializable,
|
||||||
@@ -69,38 +69,11 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public OnValueChangedDelegate OnValueChanged;
|
public OnValueChangedDelegate OnValueChanged;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkVariable with the default value and custom read permission
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="readPerm">The read permission for the NetworkVariable</param>
|
|
||||||
|
|
||||||
public NetworkVariable()
|
public NetworkVariable(T value = default,
|
||||||
{
|
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
||||||
}
|
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||||
|
: base(readPerm, writePerm)
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkVariable with the default value and custom read permission
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="readPerm">The read permission for the NetworkVariable</param>
|
|
||||||
public NetworkVariable(NetworkVariableReadPermission readPerm) : base(readPerm)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkVariable with a custom value and custom settings
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="readPerm">The read permission for the NetworkVariable</param>
|
|
||||||
/// <param name="value">The initial value to use for the NetworkVariable</param>
|
|
||||||
public NetworkVariable(NetworkVariableReadPermission readPerm, T value) : base(readPerm)
|
|
||||||
{
|
|
||||||
m_InternalValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkVariable with a custom value and the default read permission
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The initial value to use for the NetworkVariable</param>
|
|
||||||
public NetworkVariable(T value)
|
|
||||||
{
|
{
|
||||||
m_InternalValue = value;
|
m_InternalValue = value;
|
||||||
}
|
}
|
||||||
@@ -116,19 +89,36 @@ namespace Unity.Netcode
|
|||||||
get => m_InternalValue;
|
get => m_InternalValue;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
// this could be improved. The Networking Manager is not always initialized here
|
// Compare bitwise
|
||||||
// Good place to decouple network manager from the network variable
|
if (ValueEquals(ref m_InternalValue, ref value))
|
||||||
|
|
||||||
// Also, note this is not really very water-tight, if you are running as a host
|
|
||||||
// we cannot tell if a NetworkVariable write is happening inside client-ish code
|
|
||||||
if (m_NetworkBehaviour && (m_NetworkBehaviour.NetworkManager.IsClient && !m_NetworkBehaviour.NetworkManager.IsHost))
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Client can't write to NetworkVariables");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_NetworkBehaviour && !CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Client is not allowed to write to this NetworkVariable");
|
||||||
|
}
|
||||||
|
|
||||||
Set(value);
|
Set(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compares two values of the same unmanaged type by underlying memory
|
||||||
|
// Ignoring any overriden value checks
|
||||||
|
// Size is fixed
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static unsafe bool ValueEquals(ref T a, ref T b)
|
||||||
|
{
|
||||||
|
// get unmanaged pointers
|
||||||
|
var aptr = UnsafeUtility.AddressOf(ref a);
|
||||||
|
var bptr = UnsafeUtility.AddressOf(ref b);
|
||||||
|
|
||||||
|
// compare addresses
|
||||||
|
return UnsafeUtility.MemCmp(aptr, bptr, sizeof(T)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private protected void Set(T value)
|
private protected void Set(T value)
|
||||||
{
|
{
|
||||||
m_IsDirty = true;
|
m_IsDirty = true;
|
||||||
@@ -146,7 +136,6 @@ namespace Unity.Netcode
|
|||||||
WriteField(writer);
|
WriteField(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads value from the reader and applies it
|
/// Reads value from the reader and applies it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -19,9 +19,15 @@ namespace Unity.Netcode
|
|||||||
m_NetworkBehaviour = networkBehaviour;
|
m_NetworkBehaviour = networkBehaviour;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected NetworkVariableBase(NetworkVariableReadPermission readPermIn = NetworkVariableReadPermission.Everyone)
|
public const NetworkVariableReadPermission DefaultReadPerm = NetworkVariableReadPermission.Everyone;
|
||||||
|
public const NetworkVariableWritePermission DefaultWritePerm = NetworkVariableWritePermission.Server;
|
||||||
|
|
||||||
|
protected NetworkVariableBase(
|
||||||
|
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
||||||
|
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||||
{
|
{
|
||||||
ReadPerm = readPermIn;
|
ReadPerm = readPerm;
|
||||||
|
WritePerm = writePerm;
|
||||||
}
|
}
|
||||||
|
|
||||||
private protected bool m_IsDirty;
|
private protected bool m_IsDirty;
|
||||||
@@ -37,6 +43,8 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly NetworkVariableReadPermission ReadPerm;
|
public readonly NetworkVariableReadPermission ReadPerm;
|
||||||
|
|
||||||
|
public readonly NetworkVariableWritePermission WritePerm;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets whether or not the variable needs to be delta synced
|
/// Sets whether or not the variable needs to be delta synced
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -62,26 +70,28 @@ namespace Unity.Netcode
|
|||||||
return m_IsDirty;
|
return m_IsDirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool ShouldWrite(ulong clientId, bool isServer)
|
|
||||||
{
|
|
||||||
return IsDirty() && isServer && CanClientRead(clientId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets Whether or not a specific client can read to the varaible
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="clientId">The clientId of the remote client</param>
|
|
||||||
/// <returns>Whether or not the client can read to the variable</returns>
|
|
||||||
public bool CanClientRead(ulong clientId)
|
public bool CanClientRead(ulong clientId)
|
||||||
{
|
{
|
||||||
switch (ReadPerm)
|
switch (ReadPerm)
|
||||||
{
|
{
|
||||||
|
default:
|
||||||
case NetworkVariableReadPermission.Everyone:
|
case NetworkVariableReadPermission.Everyone:
|
||||||
return true;
|
return true;
|
||||||
case NetworkVariableReadPermission.OwnerOnly:
|
case NetworkVariableReadPermission.Owner:
|
||||||
return m_NetworkBehaviour.OwnerClientId == clientId;
|
return clientId == m_NetworkBehaviour.NetworkObject.OwnerClientId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanClientWrite(ulong clientId)
|
||||||
|
{
|
||||||
|
switch (WritePerm)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case NetworkVariableWritePermission.Server:
|
||||||
|
return clientId == NetworkManager.ServerClientId;
|
||||||
|
case NetworkVariableWritePermission.Owner:
|
||||||
|
return clientId == m_NetworkBehaviour.NetworkObject.OwnerClientId;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -107,7 +117,6 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="reader">The stream to read the delta from</param>
|
/// <param name="reader">The stream to read the delta from</param>
|
||||||
/// <param name="keepDirtyDelta">Whether or not the delta should be kept as dirty or consumed</param>
|
/// <param name="keepDirtyDelta">Whether or not the delta should be kept as dirty or consumed</param>
|
||||||
|
|
||||||
public abstract void ReadDelta(FastBufferReader reader, bool keepDirtyDelta);
|
public abstract void ReadDelta(FastBufferReader reader, bool keepDirtyDelta);
|
||||||
|
|
||||||
public virtual void Dispose()
|
public virtual void Dispose()
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Permission type
|
|
||||||
/// </summary>
|
|
||||||
public enum NetworkVariableReadPermission
|
public enum NetworkVariableReadPermission
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Everyone
|
|
||||||
/// </summary>
|
|
||||||
Everyone,
|
Everyone,
|
||||||
|
Owner,
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
public enum NetworkVariableWritePermission
|
||||||
/// Owner-ownly
|
{
|
||||||
/// </summary>
|
Server,
|
||||||
OwnerOnly,
|
Owner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ namespace Unity.Netcode
|
|||||||
private const NetworkDelivery k_DeliveryType = NetworkDelivery.ReliableFragmentedSequenced;
|
private const NetworkDelivery k_DeliveryType = NetworkDelivery.ReliableFragmentedSequenced;
|
||||||
internal const int InvalidSceneNameOrPath = -1;
|
internal const int InvalidSceneNameOrPath = -1;
|
||||||
|
|
||||||
// Used to be able to turn re-synchronization off for future snapshot development purposes.
|
// Used to be able to turn re-synchronization off
|
||||||
internal static bool DisableReSynchronization;
|
internal static bool DisableReSynchronization;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -488,8 +488,18 @@ namespace Unity.Netcode
|
|||||||
var scenePath = SceneUtility.GetScenePathByBuildIndex(i);
|
var scenePath = SceneUtility.GetScenePathByBuildIndex(i);
|
||||||
var hash = XXHash.Hash32(scenePath);
|
var hash = XXHash.Hash32(scenePath);
|
||||||
var buildIndex = SceneUtility.GetBuildIndexByScenePath(scenePath);
|
var buildIndex = SceneUtility.GetBuildIndexByScenePath(scenePath);
|
||||||
HashToBuildIndex.Add(hash, buildIndex);
|
|
||||||
BuildIndexToHash.Add(buildIndex, hash);
|
// In the rare-case scenario where a programmatically generated build has duplicate
|
||||||
|
// scene entries, we will log an error and skip the entry
|
||||||
|
if (!HashToBuildIndex.ContainsKey(hash))
|
||||||
|
{
|
||||||
|
HashToBuildIndex.Add(hash, buildIndex);
|
||||||
|
BuildIndexToHash.Add(buildIndex, hash);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError($"{nameof(NetworkSceneManager)} is skipping duplicate scene path entry {scenePath}. Make sure your scenes in build list does not contain duplicates!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,7 +530,8 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new Exception($"Scene Hash {sceneHash} does not exist in the {nameof(HashToBuildIndex)} table!");
|
throw new Exception($"Scene Hash {sceneHash} does not exist in the {nameof(HashToBuildIndex)} table! Verify that all scenes requiring" +
|
||||||
|
$" server to client synchronization are in the scenes in build list.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -876,7 +887,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
SceneEventType = sceneEventProgress.SceneEventType,
|
SceneEventType = sceneEventProgress.SceneEventType,
|
||||||
SceneName = SceneNameFromHash(sceneEventProgress.SceneHash),
|
SceneName = SceneNameFromHash(sceneEventProgress.SceneHash),
|
||||||
ClientId = m_NetworkManager.ServerClientId,
|
ClientId = NetworkManager.ServerClientId,
|
||||||
LoadSceneMode = sceneEventProgress.LoadSceneMode,
|
LoadSceneMode = sceneEventProgress.LoadSceneMode,
|
||||||
ClientsThatCompleted = sceneEventProgress.DoneClients,
|
ClientsThatCompleted = sceneEventProgress.DoneClients,
|
||||||
ClientsThatTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(),
|
ClientsThatTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(),
|
||||||
@@ -947,10 +958,10 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = sceneName,
|
SceneName = sceneName,
|
||||||
ClientId = m_NetworkManager.ServerClientId // Server can only invoke this
|
ClientId = NetworkManager.ServerClientId // Server can only invoke this
|
||||||
});
|
});
|
||||||
|
|
||||||
OnUnload?.Invoke(m_NetworkManager.ServerClientId, sceneName, sceneUnload);
|
OnUnload?.Invoke(NetworkManager.ServerClientId, sceneName, sceneUnload);
|
||||||
|
|
||||||
//Return the status
|
//Return the status
|
||||||
return sceneEventProgress.Status;
|
return sceneEventProgress.Status;
|
||||||
@@ -1017,12 +1028,12 @@ namespace Unity.Netcode
|
|||||||
// Server sends the unload scene notification after unloading because it will despawn all scene relative in-scene NetworkObjects
|
// Server sends the unload scene notification after unloading because it will despawn all scene relative in-scene NetworkObjects
|
||||||
// If we send this event to all clients before the server is finished unloading they will get warning about an object being
|
// If we send this event to all clients before the server is finished unloading they will get warning about an object being
|
||||||
// despawned that no longer exists
|
// despawned that no longer exists
|
||||||
SendSceneEventData(sceneEventId, m_NetworkManager.ConnectedClientsIds.Where(c => c != m_NetworkManager.ServerClientId).ToArray());
|
SendSceneEventData(sceneEventId, m_NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.ServerClientId).ToArray());
|
||||||
|
|
||||||
//Only if we are a host do we want register having loaded for the associated SceneEventProgress
|
//Only if we are a host do we want register having loaded for the associated SceneEventProgress
|
||||||
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
||||||
{
|
{
|
||||||
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(m_NetworkManager.ServerClientId);
|
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1035,7 +1046,7 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
||||||
ClientId = m_NetworkManager.IsServer ? m_NetworkManager.ServerClientId : m_NetworkManager.LocalClientId
|
ClientId = m_NetworkManager.IsServer ? NetworkManager.ServerClientId : m_NetworkManager.LocalClientId
|
||||||
});
|
});
|
||||||
|
|
||||||
OnUnloadComplete?.Invoke(m_NetworkManager.LocalClientId, SceneNameFromHash(sceneEventData.SceneHash));
|
OnUnloadComplete?.Invoke(m_NetworkManager.LocalClientId, SceneNameFromHash(sceneEventData.SceneHash));
|
||||||
@@ -1043,7 +1054,7 @@ namespace Unity.Netcode
|
|||||||
// Clients send a notification back to the server they have completed the unload scene event
|
// Clients send a notification back to the server they have completed the unload scene event
|
||||||
if (!m_NetworkManager.IsServer)
|
if (!m_NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
SendSceneEventData(sceneEventId, new ulong[] { m_NetworkManager.ServerClientId });
|
SendSceneEventData(sceneEventId, new ulong[] { NetworkManager.ServerClientId });
|
||||||
}
|
}
|
||||||
|
|
||||||
EndSceneEvent(sceneEventId);
|
EndSceneEvent(sceneEventId);
|
||||||
@@ -1079,7 +1090,7 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = SceneEventType.Unload,
|
SceneEventType = SceneEventType.Unload,
|
||||||
SceneName = keyHandleEntry.Value.name,
|
SceneName = keyHandleEntry.Value.name,
|
||||||
LoadSceneMode = LoadSceneMode.Additive, // The only scenes unloaded are scenes that were additively loaded
|
LoadSceneMode = LoadSceneMode.Additive, // The only scenes unloaded are scenes that were additively loaded
|
||||||
ClientId = m_NetworkManager.ServerClientId
|
ClientId = NetworkManager.ServerClientId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1147,10 +1158,10 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = sceneName,
|
SceneName = sceneName,
|
||||||
ClientId = m_NetworkManager.ServerClientId
|
ClientId = NetworkManager.ServerClientId
|
||||||
});
|
});
|
||||||
|
|
||||||
OnLoad?.Invoke(m_NetworkManager.ServerClientId, sceneName, sceneEventData.LoadSceneMode, sceneLoad);
|
OnLoad?.Invoke(NetworkManager.ServerClientId, sceneName, sceneEventData.LoadSceneMode, sceneLoad);
|
||||||
|
|
||||||
//Return our scene progress instance
|
//Return our scene progress instance
|
||||||
return sceneEventProgress.Status;
|
return sceneEventProgress.Status;
|
||||||
@@ -1187,7 +1198,6 @@ namespace Unity.Netcode
|
|||||||
// When it is set: Just before starting the asynchronous loading call
|
// When it is set: Just before starting the asynchronous loading call
|
||||||
// When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do
|
// When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do
|
||||||
// not destroy temporary scene are moved into the active scene
|
// not destroy temporary scene are moved into the active scene
|
||||||
// TODO: When Snapshot scene spawning is enabled this needs to be removed.
|
|
||||||
if (sceneEventData.LoadSceneMode == LoadSceneMode.Single)
|
if (sceneEventData.LoadSceneMode == LoadSceneMode.Single)
|
||||||
{
|
{
|
||||||
IsSpawnedObjectsPendingInDontDestroyOnLoad = true;
|
IsSpawnedObjectsPendingInDontDestroyOnLoad = true;
|
||||||
@@ -1278,7 +1288,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (!keyValuePairBySceneHandle.Value.IsPlayerObject)
|
if (!keyValuePairBySceneHandle.Value.IsPlayerObject)
|
||||||
{
|
{
|
||||||
m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value, m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, null, true);
|
// All in-scene placed NetworkObjects default to being owned by the server
|
||||||
|
m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value,
|
||||||
|
m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, NetworkManager.ServerClientId, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1290,7 +1302,7 @@ namespace Unity.Netcode
|
|||||||
for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++)
|
for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++)
|
||||||
{
|
{
|
||||||
var clientId = m_NetworkManager.ConnectedClientsList[j].ClientId;
|
var clientId = m_NetworkManager.ConnectedClientsList[j].ClientId;
|
||||||
if (clientId != m_NetworkManager.ServerClientId)
|
if (clientId != NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
sceneEventData.TargetClientId = clientId;
|
sceneEventData.TargetClientId = clientId;
|
||||||
var message = new SceneEventMessage
|
var message = new SceneEventMessage
|
||||||
@@ -1309,16 +1321,16 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = SceneEventType.LoadComplete,
|
SceneEventType = SceneEventType.LoadComplete,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
||||||
ClientId = m_NetworkManager.ServerClientId,
|
ClientId = NetworkManager.ServerClientId,
|
||||||
Scene = scene,
|
Scene = scene,
|
||||||
});
|
});
|
||||||
|
|
||||||
OnLoadComplete?.Invoke(m_NetworkManager.ServerClientId, SceneNameFromHash(sceneEventData.SceneHash), sceneEventData.LoadSceneMode);
|
OnLoadComplete?.Invoke(NetworkManager.ServerClientId, SceneNameFromHash(sceneEventData.SceneHash), sceneEventData.LoadSceneMode);
|
||||||
|
|
||||||
//Second, only if we are a host do we want register having loaded for the associated SceneEventProgress
|
//Second, only if we are a host do we want register having loaded for the associated SceneEventProgress
|
||||||
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
||||||
{
|
{
|
||||||
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(m_NetworkManager.ServerClientId);
|
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
EndSceneEvent(sceneEventId);
|
EndSceneEvent(sceneEventId);
|
||||||
}
|
}
|
||||||
@@ -1333,7 +1345,7 @@ namespace Unity.Netcode
|
|||||||
sceneEventData.DeserializeScenePlacedObjects();
|
sceneEventData.DeserializeScenePlacedObjects();
|
||||||
|
|
||||||
sceneEventData.SceneEventType = SceneEventType.LoadComplete;
|
sceneEventData.SceneEventType = SceneEventType.LoadComplete;
|
||||||
SendSceneEventData(sceneEventId, new ulong[] { m_NetworkManager.ServerClientId });
|
SendSceneEventData(sceneEventId, new ulong[] { NetworkManager.ServerClientId });
|
||||||
m_IsSceneEventActive = false;
|
m_IsSceneEventActive = false;
|
||||||
|
|
||||||
// Notify local client that the scene was loaded
|
// Notify local client that the scene was loaded
|
||||||
@@ -1544,9 +1556,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
EventData = responseSceneEventData
|
EventData = responseSceneEventData
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, m_NetworkManager.ServerClientId);
|
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, NetworkManager.ServerClientId);
|
||||||
|
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(m_NetworkManager.ServerClientId, (uint)responseSceneEventData.SceneEventType, sceneName, size);
|
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(NetworkManager.ServerClientId, (uint)responseSceneEventData.SceneEventType, sceneName, size);
|
||||||
|
|
||||||
EndSceneEvent(responseSceneEventData.SceneEventId);
|
EndSceneEvent(responseSceneEventData.SceneEventId);
|
||||||
|
|
||||||
@@ -1600,7 +1612,7 @@ namespace Unity.Netcode
|
|||||||
sceneEventData.SynchronizeSceneNetworkObjects(m_NetworkManager);
|
sceneEventData.SynchronizeSceneNetworkObjects(m_NetworkManager);
|
||||||
|
|
||||||
sceneEventData.SceneEventType = SceneEventType.SynchronizeComplete;
|
sceneEventData.SceneEventType = SceneEventType.SynchronizeComplete;
|
||||||
SendSceneEventData(sceneEventId, new ulong[] { m_NetworkManager.ServerClientId });
|
SendSceneEventData(sceneEventId, new ulong[] { NetworkManager.ServerClientId });
|
||||||
|
|
||||||
// All scenes are synchronized, let the server know we are done synchronizing
|
// All scenes are synchronized, let the server know we are done synchronizing
|
||||||
m_NetworkManager.IsConnectedClient = true;
|
m_NetworkManager.IsConnectedClient = true;
|
||||||
@@ -1627,7 +1639,7 @@ namespace Unity.Netcode
|
|||||||
OnSceneEvent?.Invoke(new SceneEvent()
|
OnSceneEvent?.Invoke(new SceneEvent()
|
||||||
{
|
{
|
||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
ClientId = m_NetworkManager.ServerClientId, // Server sent this to client
|
ClientId = NetworkManager.ServerClientId, // Server sent this to client
|
||||||
});
|
});
|
||||||
|
|
||||||
EndSceneEvent(sceneEventId);
|
EndSceneEvent(sceneEventId);
|
||||||
@@ -1642,7 +1654,7 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
||||||
ClientId = m_NetworkManager.ServerClientId,
|
ClientId = NetworkManager.ServerClientId,
|
||||||
ClientsThatCompleted = sceneEventData.ClientsCompleted,
|
ClientsThatCompleted = sceneEventData.ClientsCompleted,
|
||||||
ClientsThatTimedOut = sceneEventData.ClientsTimedOut,
|
ClientsThatTimedOut = sceneEventData.ClientsTimedOut,
|
||||||
});
|
});
|
||||||
@@ -1734,8 +1746,6 @@ namespace Unity.Netcode
|
|||||||
// NetworkObjects
|
// NetworkObjects
|
||||||
m_NetworkManager.InvokeOnClientConnectedCallback(clientId);
|
m_NetworkManager.InvokeOnClientConnectedCallback(clientId);
|
||||||
|
|
||||||
// TODO: This check and associated code can be removed once we determine all
|
|
||||||
// snapshot destroy messages are being updated until the server receives ACKs
|
|
||||||
if (sceneEventData.ClientNeedsReSynchronization() && !DisableReSynchronization)
|
if (sceneEventData.ClientNeedsReSynchronization() && !DisableReSynchronization)
|
||||||
{
|
{
|
||||||
sceneEventData.SceneEventType = SceneEventType.ReSynchronize;
|
sceneEventData.SceneEventType = SceneEventType.ReSynchronize;
|
||||||
@@ -1830,7 +1840,7 @@ namespace Unity.Netcode
|
|||||||
/// Using the local scene relative Scene.handle as a sub-key to the root dictionary allows us to
|
/// Using the local scene relative Scene.handle as a sub-key to the root dictionary allows us to
|
||||||
/// distinguish between duplicate in-scene placed NetworkObjects
|
/// distinguish between duplicate in-scene placed NetworkObjects
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true)
|
internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true)
|
||||||
{
|
{
|
||||||
if (clearScenePlacedObjects)
|
if (clearScenePlacedObjects)
|
||||||
{
|
{
|
||||||
@@ -1845,25 +1855,26 @@ namespace Unity.Netcode
|
|||||||
// at the end of scene loading we use this list to soft synchronize all in-scene placed NetworkObjects
|
// at the end of scene loading we use this list to soft synchronize all in-scene placed NetworkObjects
|
||||||
foreach (var networkObjectInstance in networkObjects)
|
foreach (var networkObjectInstance in networkObjects)
|
||||||
{
|
{
|
||||||
// We check to make sure the NetworkManager instance is the same one to be "NetcodeIntegrationTestHelpers" compatible and filter the list on a per scene basis (additive scenes)
|
var globalObjectIdHash = networkObjectInstance.GlobalObjectIdHash;
|
||||||
if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy &&
|
var sceneHandle = networkObjectInstance.gameObject.scene.handle;
|
||||||
networkObjectInstance.gameObject.scene.handle == sceneToFilterBy.handle)
|
// We check to make sure the NetworkManager instance is the same one to be "NetcodeIntegrationTestHelpers" compatible and filter the list on a per scene basis (for additive scenes)
|
||||||
|
if (networkObjectInstance.IsSceneObject != false && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy &&
|
||||||
|
sceneHandle == sceneToFilterBy.handle)
|
||||||
{
|
{
|
||||||
if (!ScenePlacedObjects.ContainsKey(networkObjectInstance.GlobalObjectIdHash))
|
if (!ScenePlacedObjects.ContainsKey(globalObjectIdHash))
|
||||||
{
|
{
|
||||||
ScenePlacedObjects.Add(networkObjectInstance.GlobalObjectIdHash, new Dictionary<int, NetworkObject>());
|
ScenePlacedObjects.Add(globalObjectIdHash, new Dictionary<int, NetworkObject>());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash].ContainsKey(networkObjectInstance.gameObject.scene.handle))
|
if (!ScenePlacedObjects[globalObjectIdHash].ContainsKey(sceneHandle))
|
||||||
{
|
{
|
||||||
ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash].Add(networkObjectInstance.gameObject.scene.handle, networkObjectInstance);
|
ScenePlacedObjects[globalObjectIdHash].Add(sceneHandle, networkObjectInstance);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var exitingEntryName = ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash][networkObjectInstance.gameObject.scene.handle] != null ?
|
var exitingEntryName = ScenePlacedObjects[globalObjectIdHash][sceneHandle] != null ? ScenePlacedObjects[globalObjectIdHash][sceneHandle].name : "Null Entry";
|
||||||
ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash][networkObjectInstance.gameObject.scene.handle].name : "Null Entry";
|
|
||||||
throw new Exception($"{networkObjectInstance.name} tried to registered with {nameof(ScenePlacedObjects)} which already contains " +
|
throw new Exception($"{networkObjectInstance.name} tried to registered with {nameof(ScenePlacedObjects)} which already contains " +
|
||||||
$"the same {nameof(NetworkObject.GlobalObjectIdHash)} value {networkObjectInstance.GlobalObjectIdHash} for {exitingEntryName}!");
|
$"the same {nameof(NetworkObject.GlobalObjectIdHash)} value {globalObjectIdHash} for {exitingEntryName}!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ namespace Unity.Netcode
|
|||||||
/// <b>Invocation:</b> Server Side<br/>
|
/// <b>Invocation:</b> Server Side<br/>
|
||||||
/// <b>Message Flow:</b> Server to client<br/>
|
/// <b>Message Flow:</b> Server to client<br/>
|
||||||
/// <b>Event Notification:</b> Both server and client receive a local notification<br/>
|
/// <b>Event Notification:</b> Both server and client receive a local notification<br/>
|
||||||
/// <em>Note: This will be removed once snapshot and buffered messages are finalized as it will no longer be needed at that point.</em>
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ReSynchronize,
|
ReSynchronize,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -592,9 +591,6 @@ namespace Unity.Netcode
|
|||||||
networkObject.IsSpawned = false;
|
networkObject.IsSpawned = false;
|
||||||
if (m_NetworkManager.PrefabHandler.ContainsHandler(networkObject))
|
if (m_NetworkManager.PrefabHandler.ContainsHandler(networkObject))
|
||||||
{
|
{
|
||||||
// Since this is the client side and we have missed the delete message, until the Snapshot system is in place for spawn and despawn handling
|
|
||||||
// we have to remove this from the list of spawned objects manually or when a NetworkObjectId is recycled the client will throw an error
|
|
||||||
// about the id already being assigned.
|
|
||||||
if (m_NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId))
|
if (m_NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId))
|
||||||
{
|
{
|
||||||
m_NetworkManager.SpawnManager.SpawnedObjects.Remove(networkObjectId);
|
m_NetworkManager.SpawnManager.SpawnedObjects.Remove(networkObjectId);
|
||||||
|
|||||||
@@ -281,6 +281,17 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteValueBitPacked<T>(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value);
|
public void WriteValueBitPacked<T>(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
public const ushort BitPackedUshortMax = (1 << 15) - 1;
|
||||||
|
public const short BitPackedShortMax = (1 << 14) - 1;
|
||||||
|
public const short BitPackedShortMin = -(1 << 14);
|
||||||
|
public const uint BitPackedUintMax = (1 << 30) - 1;
|
||||||
|
public const int BitPackedIntMax = (1 << 29) - 1;
|
||||||
|
public const int BitPackedIntMin = -(1 << 29);
|
||||||
|
public const ulong BitPackedULongMax = (1L << 61) - 1;
|
||||||
|
public const long BitPackedLongMax = (1L << 60) - 1;
|
||||||
|
public const long BitPackedLongMin = -(1L << 60);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a 14-bit signed short to the buffer in a bit-encoded packed format.
|
/// Writes a 14-bit signed short to the buffer in a bit-encoded packed format.
|
||||||
/// The first bit indicates whether the value is 1 byte or 2.
|
/// The first bit indicates whether the value is 1 byte or 2.
|
||||||
@@ -307,7 +318,7 @@ namespace Unity.Netcode
|
|||||||
public static void WriteValueBitPacked(FastBufferWriter writer, ushort value)
|
public static void WriteValueBitPacked(FastBufferWriter writer, ushort value)
|
||||||
{
|
{
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (value >= 0b1000_0000_0000_0000)
|
if (value >= BitPackedUshortMax)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("BitPacked ushorts must be <= 15 bits");
|
throw new ArgumentException("BitPacked ushorts must be <= 15 bits");
|
||||||
}
|
}
|
||||||
@@ -356,7 +367,7 @@ namespace Unity.Netcode
|
|||||||
public static void WriteValueBitPacked(FastBufferWriter writer, uint value)
|
public static void WriteValueBitPacked(FastBufferWriter writer, uint value)
|
||||||
{
|
{
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (value >= 0b0100_0000_0000_0000_0000_0000_0000_0000)
|
if (value > BitPackedUintMax)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("BitPacked uints must be <= 30 bits");
|
throw new ArgumentException("BitPacked uints must be <= 30 bits");
|
||||||
}
|
}
|
||||||
@@ -396,7 +407,7 @@ namespace Unity.Netcode
|
|||||||
public static void WriteValueBitPacked(FastBufferWriter writer, ulong value)
|
public static void WriteValueBitPacked(FastBufferWriter writer, ulong value)
|
||||||
{
|
{
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (value >= 0b0010_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000)
|
if (value > BitPackedULongMax)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("BitPacked ulongs must be <= 61 bits");
|
throw new ArgumentException("BitPacked ulongs must be <= 61 bits");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace Unity.Netcode
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly unsafe ReaderHandle* Handle;
|
internal unsafe ReaderHandle* Handle;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the current read position
|
/// Get the current read position
|
||||||
@@ -39,6 +39,11 @@ namespace Unity.Netcode
|
|||||||
get => Handle->Length;
|
get => Handle->Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the reader has been initialized and a handle allocated.
|
||||||
|
/// </summary>
|
||||||
|
public unsafe bool IsInitialized => Handle != null;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal unsafe void CommitBitwiseReads(int amount)
|
internal unsafe void CommitBitwiseReads(int amount)
|
||||||
{
|
{
|
||||||
@@ -196,6 +201,7 @@ namespace Unity.Netcode
|
|||||||
public unsafe void Dispose()
|
public unsafe void Dispose()
|
||||||
{
|
{
|
||||||
UnsafeUtility.Free(Handle, Handle->Allocator);
|
UnsafeUtility.Free(Handle, Handle->Allocator);
|
||||||
|
Handle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace Unity.Netcode
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly unsafe WriterHandle* Handle;
|
internal unsafe WriterHandle* Handle;
|
||||||
|
|
||||||
private static byte[] s_ByteArrayCache = new byte[65535];
|
private static byte[] s_ByteArrayCache = new byte[65535];
|
||||||
|
|
||||||
@@ -62,6 +62,11 @@ namespace Unity.Netcode
|
|||||||
get => Handle->Position > Handle->Length ? Handle->Position : Handle->Length;
|
get => Handle->Position > Handle->Length ? Handle->Position : Handle->Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the writer has been initialized and a handle allocated.
|
||||||
|
/// </summary>
|
||||||
|
public unsafe bool IsInitialized => Handle != null;
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal unsafe void CommitBitwiseWrites(int amount)
|
internal unsafe void CommitBitwiseWrites(int amount)
|
||||||
@@ -111,6 +116,7 @@ namespace Unity.Netcode
|
|||||||
UnsafeUtility.Free(Handle->BufferPointer, Handle->Allocator);
|
UnsafeUtility.Free(Handle->BufferPointer, Handle->Allocator);
|
||||||
}
|
}
|
||||||
UnsafeUtility.Free(Handle, Handle->Allocator);
|
UnsafeUtility.Free(Handle, Handle->Allocator);
|
||||||
|
Handle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -21,6 +21,122 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly HashSet<NetworkObject> SpawnedObjectsList = new HashSet<NetworkObject>();
|
public readonly HashSet<NetworkObject> SpawnedObjectsList = new HashSet<NetworkObject>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use to get all NetworkObjects owned by a client
|
||||||
|
/// Ownership to Objects Table Format:
|
||||||
|
/// [ClientId][NetworkObjectId][NetworkObject]
|
||||||
|
/// Server: Keeps track of all clients' ownership
|
||||||
|
/// Client: Keeps track of only its ownership
|
||||||
|
/// </summary>
|
||||||
|
public readonly Dictionary<ulong, Dictionary<ulong, NetworkObject>> OwnershipToObjectsTable = new Dictionary<ulong, Dictionary<ulong, NetworkObject>>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Object to Ownership Table:
|
||||||
|
/// [NetworkObjectId][ClientId]
|
||||||
|
/// Used internally to find the client Id that currently owns
|
||||||
|
/// the NetworkObject
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<ulong, ulong> m_ObjectToOwnershipTable = new Dictionary<ulong, ulong>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to update a NetworkObject's ownership
|
||||||
|
/// </summary>
|
||||||
|
internal void UpdateOwnershipTable(NetworkObject networkObject, ulong newOwner, bool isRemoving = false)
|
||||||
|
{
|
||||||
|
var previousOwner = newOwner;
|
||||||
|
|
||||||
|
// Use internal lookup table to see if the NetworkObject has a previous owner
|
||||||
|
if (m_ObjectToOwnershipTable.ContainsKey(networkObject.NetworkObjectId))
|
||||||
|
{
|
||||||
|
// Keep track of the previous owner's ClientId
|
||||||
|
previousOwner = m_ObjectToOwnershipTable[networkObject.NetworkObjectId];
|
||||||
|
|
||||||
|
// We are either despawning (remove) or changing ownership (assign)
|
||||||
|
if (isRemoving)
|
||||||
|
{
|
||||||
|
m_ObjectToOwnershipTable.Remove(networkObject.NetworkObjectId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ObjectToOwnershipTable[networkObject.NetworkObjectId] = newOwner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, just add a new lookup entry
|
||||||
|
m_ObjectToOwnershipTable.Add(networkObject.NetworkObjectId, newOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if we had a previous owner
|
||||||
|
if (previousOwner != newOwner && OwnershipToObjectsTable.ContainsKey(previousOwner))
|
||||||
|
{
|
||||||
|
// Before updating the previous owner, assure this entry exists
|
||||||
|
if (OwnershipToObjectsTable[previousOwner].ContainsKey(networkObject.NetworkObjectId))
|
||||||
|
{
|
||||||
|
// Remove the previous owner's entry
|
||||||
|
OwnershipToObjectsTable[previousOwner].Remove(networkObject.NetworkObjectId);
|
||||||
|
|
||||||
|
// Server or Host alway invokes the lost ownership notification locally
|
||||||
|
if (NetworkManager.IsServer)
|
||||||
|
{
|
||||||
|
networkObject.InvokeBehaviourOnLostOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are removing the entry (i.e. despawning or client lost ownership)
|
||||||
|
if (isRemoving)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Really, as long as UpdateOwnershipTable is invoked when ownership is gained or lost this should never happen
|
||||||
|
throw new Exception($"Client-ID {previousOwner} had a partial {nameof(m_ObjectToOwnershipTable)} entry! Potentially corrupted {nameof(OwnershipToObjectsTable)}?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the owner doesn't have an entry then create one
|
||||||
|
if (!OwnershipToObjectsTable.ContainsKey(newOwner))
|
||||||
|
{
|
||||||
|
OwnershipToObjectsTable.Add(newOwner, new Dictionary<ulong, NetworkObject>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check to make sure we don't already have this entry (we shouldn't)
|
||||||
|
if (!OwnershipToObjectsTable[newOwner].ContainsKey(networkObject.NetworkObjectId))
|
||||||
|
{
|
||||||
|
// Add the new ownership entry
|
||||||
|
OwnershipToObjectsTable[newOwner].Add(networkObject.NetworkObjectId, networkObject);
|
||||||
|
|
||||||
|
// Server or Host always invokes the gained ownership notification locally
|
||||||
|
if (NetworkManager.IsServer)
|
||||||
|
{
|
||||||
|
networkObject.InvokeBehaviourOnGainedOwnership();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isRemoving)
|
||||||
|
{
|
||||||
|
OwnershipToObjectsTable[previousOwner].Remove(networkObject.NetworkObjectId);
|
||||||
|
}
|
||||||
|
else if (NetworkManager.LogLevel == LogLevel.Developer)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning($"Setting ownership twice? Client-ID {previousOwner} already owns NetworkObject ID {networkObject.NetworkObjectId}!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of all NetworkObjects that belong to a client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">the client's id <see cref="NetworkManager.LocalClientId"/></param>
|
||||||
|
public List<NetworkObject> GetClientOwnedObjects(ulong clientId)
|
||||||
|
{
|
||||||
|
if (!OwnershipToObjectsTable.ContainsKey(clientId))
|
||||||
|
{
|
||||||
|
OwnershipToObjectsTable.Add(clientId, new Dictionary<ulong, NetworkObject>());
|
||||||
|
}
|
||||||
|
return OwnershipToObjectsTable[clientId].Values.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private struct TriggerData
|
private struct TriggerData
|
||||||
{
|
{
|
||||||
public FastBufferReader Reader;
|
public FastBufferReader Reader;
|
||||||
@@ -96,8 +212,7 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defers processing of a message until the moment a specific networkObjectId is spawned.
|
/// Defers processing of a message until the moment a specific networkObjectId is spawned.
|
||||||
/// This is to handle situations where an RPC or other object-specific message arrives before the spawn does,
|
/// This is to handle situations where an RPC or other object-specific message arrives before the spawn does,
|
||||||
/// either due to it being requested in OnNetworkSpawn before the spawn call has been executed, or with
|
/// either due to it being requested in OnNetworkSpawn before the spawn call has been executed
|
||||||
/// snapshot spawns enabled where the spawn is sent unreliably and not until the end of the frame.
|
|
||||||
///
|
///
|
||||||
/// There is a one second maximum lifetime of triggers to avoid memory leaks. After one second has passed
|
/// There is a one second maximum lifetime of triggers to avoid memory leaks. After one second has passed
|
||||||
/// without the requested object ID being spawned, the triggers for it are automatically deleted.
|
/// without the requested object ID being spawned, the triggers for it are automatically deleted.
|
||||||
@@ -194,37 +309,21 @@ namespace Unity.Netcode
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the connected client entry exists before trying to remove ownership.
|
// Server removes the entry and takes over ownership before notifying
|
||||||
if (TryGetNetworkClient(networkObject.OwnerClientId, out NetworkClient networkClient))
|
UpdateOwnershipTable(networkObject, NetworkManager.ServerClientId, true);
|
||||||
|
|
||||||
|
networkObject.OwnerClientId = NetworkManager.ServerClientId;
|
||||||
|
|
||||||
|
var message = new ChangeOwnershipMessage
|
||||||
{
|
{
|
||||||
for (int i = networkClient.OwnedObjects.Count - 1; i > -1; i--)
|
NetworkObjectId = networkObject.NetworkObjectId,
|
||||||
{
|
OwnerClientId = networkObject.OwnerClientId
|
||||||
if (networkClient.OwnedObjects[i] == networkObject)
|
};
|
||||||
{
|
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds);
|
||||||
networkClient.OwnedObjects.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
networkObject.OwnerClientIdInternal = null;
|
foreach (var client in NetworkManager.ConnectedClients)
|
||||||
|
|
||||||
var message = new ChangeOwnershipMessage
|
|
||||||
{
|
|
||||||
NetworkObjectId = networkObject.NetworkObjectId,
|
|
||||||
OwnerClientId = networkObject.OwnerClientId
|
|
||||||
};
|
|
||||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds);
|
|
||||||
|
|
||||||
foreach (var client in NetworkManager.ConnectedClients)
|
|
||||||
{
|
|
||||||
NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject, size);
|
||||||
{
|
|
||||||
NetworkLog.LogWarning($"No connected clients prior to removing ownership for {networkObject.name}. Make sure you are not initializing or shutting down when removing ownership.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,25 +364,10 @@ namespace Unity.Netcode
|
|||||||
throw new SpawnStateException("Object is not spawned");
|
throw new SpawnStateException("Object is not spawned");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryGetNetworkClient(networkObject.OwnerClientId, out NetworkClient networkClient))
|
|
||||||
{
|
|
||||||
for (int i = networkClient.OwnedObjects.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (networkClient.OwnedObjects[i] == networkObject)
|
|
||||||
{
|
|
||||||
networkClient.OwnedObjects.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
networkClient.OwnedObjects.Add(networkObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
networkObject.OwnerClientId = clientId;
|
networkObject.OwnerClientId = clientId;
|
||||||
|
|
||||||
if (TryGetNetworkClient(clientId, out NetworkClient newNetworkClient))
|
// Server adds entries for all client ownership
|
||||||
{
|
UpdateOwnershipTable(networkObject, networkObject.OwnerClientId);
|
||||||
newNetworkClient.OwnedObjects.Add(networkObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
var message = new ChangeOwnershipMessage
|
var message = new ChangeOwnershipMessage
|
||||||
{
|
{
|
||||||
@@ -414,7 +498,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ran on both server and client
|
// Ran on both server and client
|
||||||
internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong? ownerClientId, bool destroyWithScene)
|
internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene)
|
||||||
{
|
{
|
||||||
if (networkObject == null)
|
if (networkObject == null)
|
||||||
{
|
{
|
||||||
@@ -452,15 +536,12 @@ namespace Unity.Netcode
|
|||||||
throw new SpawnStateException("Object is already spawned");
|
throw new SpawnStateException("Object is already spawned");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sceneObject.Header.HasNetworkVariables)
|
networkObject.SetNetworkVariableData(variableData);
|
||||||
{
|
|
||||||
networkObject.SetNetworkVariableData(variableData);
|
|
||||||
}
|
|
||||||
|
|
||||||
SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.Header.NetworkObjectId, sceneObject.Header.IsSceneObject, sceneObject.Header.IsPlayerObject, sceneObject.Header.OwnerClientId, destroyWithScene);
|
SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.Header.NetworkObjectId, sceneObject.Header.IsSceneObject, sceneObject.Header.IsPlayerObject, sceneObject.Header.OwnerClientId, destroyWithScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong? ownerClientId, bool destroyWithScene)
|
private void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene)
|
||||||
{
|
{
|
||||||
if (SpawnedObjects.ContainsKey(networkId))
|
if (SpawnedObjects.ContainsKey(networkId))
|
||||||
{
|
{
|
||||||
@@ -471,38 +552,45 @@ namespace Unity.Netcode
|
|||||||
// this initialization really should be at the bottom of the function
|
// this initialization really should be at the bottom of the function
|
||||||
networkObject.IsSpawned = true;
|
networkObject.IsSpawned = true;
|
||||||
|
|
||||||
// this initialization really should be at the top of this function. If and when we break the
|
// this initialization really should be at the top of this function. If and when we break the
|
||||||
// NetworkVariable dependency on NetworkBehaviour, this otherwise creates problems because
|
// NetworkVariable dependency on NetworkBehaviour, this otherwise creates problems because
|
||||||
// SetNetworkVariableData above calls InitializeVariables, and the 'baked out' data isn't ready there;
|
// SetNetworkVariableData above calls InitializeVariables, and the 'baked out' data isn't ready there;
|
||||||
// the current design banks on getting the network behaviour set and then only reading from it
|
// the current design banks on getting the network behaviour set and then only reading from it after the
|
||||||
// after the below initialization code. However cowardice compels me to hold off on moving this until
|
// below initialization code. However cowardice compels me to hold off on moving this until that commit
|
||||||
// that commit
|
|
||||||
networkObject.IsSceneObject = sceneObject;
|
networkObject.IsSceneObject = sceneObject;
|
||||||
networkObject.NetworkObjectId = networkId;
|
networkObject.NetworkObjectId = networkId;
|
||||||
|
|
||||||
networkObject.DestroyWithScene = sceneObject || destroyWithScene;
|
networkObject.DestroyWithScene = sceneObject || destroyWithScene;
|
||||||
|
|
||||||
networkObject.OwnerClientIdInternal = ownerClientId;
|
networkObject.OwnerClientId = ownerClientId;
|
||||||
|
|
||||||
networkObject.IsPlayerObject = playerObject;
|
networkObject.IsPlayerObject = playerObject;
|
||||||
|
|
||||||
SpawnedObjects.Add(networkObject.NetworkObjectId, networkObject);
|
SpawnedObjects.Add(networkObject.NetworkObjectId, networkObject);
|
||||||
SpawnedObjectsList.Add(networkObject);
|
SpawnedObjectsList.Add(networkObject);
|
||||||
|
|
||||||
if (ownerClientId != null)
|
if (NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
if (NetworkManager.IsServer)
|
if (playerObject)
|
||||||
{
|
{
|
||||||
if (playerObject)
|
// If there was an already existing player object for this player, then mark it as no longer
|
||||||
|
// a player object.
|
||||||
|
if (NetworkManager.ConnectedClients[ownerClientId].PlayerObject != null)
|
||||||
{
|
{
|
||||||
NetworkManager.ConnectedClients[ownerClientId.Value].PlayerObject = networkObject;
|
NetworkManager.ConnectedClients[ownerClientId].PlayerObject.IsPlayerObject = false;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NetworkManager.ConnectedClients[ownerClientId.Value].OwnedObjects.Add(networkObject);
|
|
||||||
}
|
}
|
||||||
|
NetworkManager.ConnectedClients[ownerClientId].PlayerObject = networkObject;
|
||||||
}
|
}
|
||||||
else if (playerObject && ownerClientId.Value == NetworkManager.LocalClientId)
|
}
|
||||||
|
else if (ownerClientId == NetworkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
if (playerObject)
|
||||||
{
|
{
|
||||||
|
// If there was an already existing player object for this player, then mark it as no longer a player object.
|
||||||
|
if (NetworkManager.LocalClient.PlayerObject != null)
|
||||||
|
{
|
||||||
|
NetworkManager.LocalClient.PlayerObject.IsPlayerObject = false;
|
||||||
|
}
|
||||||
NetworkManager.LocalClient.PlayerObject = networkObject;
|
NetworkManager.LocalClient.PlayerObject = networkObject;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -549,25 +637,21 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject)
|
internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject)
|
||||||
{
|
{
|
||||||
if (!NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
//Currently, if this is called and the clientId (destination) is the server's client Id, this case will be checked
|
||||||
|
// within the below Send function. To avoid unwarranted allocation of a PooledNetworkBuffer placing this check here. [NSS]
|
||||||
|
if (NetworkManager.IsServer && clientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
//Currently, if this is called and the clientId (destination) is the server's client Id, this case
|
return;
|
||||||
//will be checked within the below Send function. To avoid unwarranted allocation of a PooledNetworkBuffer
|
|
||||||
//placing this check here. [NSS]
|
|
||||||
if (NetworkManager.IsServer && clientId == NetworkManager.ServerClientId)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var message = new CreateObjectMessage
|
|
||||||
{
|
|
||||||
ObjectInfo = networkObject.GetMessageSceneObject(clientId)
|
|
||||||
};
|
|
||||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, clientId);
|
|
||||||
NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject, size);
|
|
||||||
|
|
||||||
networkObject.MarkVariablesDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var message = new CreateObjectMessage
|
||||||
|
{
|
||||||
|
ObjectInfo = networkObject.GetMessageSceneObject(clientId)
|
||||||
|
};
|
||||||
|
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, clientId);
|
||||||
|
NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject, size);
|
||||||
|
|
||||||
|
networkObject.MarkVariablesDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ulong? GetSpawnParentId(NetworkObject networkObject)
|
internal ulong? GetSpawnParentId(NetworkObject networkObject)
|
||||||
@@ -605,14 +689,12 @@ namespace Unity.Netcode
|
|||||||
// Makes scene objects ready to be reused
|
// Makes scene objects ready to be reused
|
||||||
internal void ServerResetShudownStateForSceneObjects()
|
internal void ServerResetShudownStateForSceneObjects()
|
||||||
{
|
{
|
||||||
foreach (var sobj in SpawnedObjectsList)
|
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsSceneObject != null && c.IsSceneObject == true);
|
||||||
|
foreach (var sobj in networkObjects)
|
||||||
{
|
{
|
||||||
if ((sobj.IsSceneObject != null && sobj.IsSceneObject == true) || sobj.DestroyWithScene)
|
sobj.IsSpawned = false;
|
||||||
{
|
sobj.DestroyWithScene = false;
|
||||||
sobj.IsSpawned = false;
|
sobj.IsSceneObject = null;
|
||||||
sobj.DestroyWithScene = false;
|
|
||||||
sobj.IsSceneObject = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,14 +735,12 @@ namespace Unity.Netcode
|
|||||||
else if (networkObjects[i].IsSpawned)
|
else if (networkObjects[i].IsSpawned)
|
||||||
{
|
{
|
||||||
// If it is an in-scene placed NetworkObject then just despawn
|
// If it is an in-scene placed NetworkObject then just despawn
|
||||||
// and let it be destroyed when the scene is unloaded. Otherwise,
|
// and let it be destroyed when the scene is unloaded. Otherwise, despawn and destroy it.
|
||||||
// despawn and destroy it.
|
var shouldDestroy = !(networkObjects[i].IsSceneObject != null && networkObjects[i].IsSceneObject.Value);
|
||||||
var shouldDestroy = !(networkObjects[i].IsSceneObject != null
|
|
||||||
&& networkObjects[i].IsSceneObject.Value);
|
|
||||||
|
|
||||||
OnDespawnObject(networkObjects[i], shouldDestroy);
|
OnDespawnObject(networkObjects[i], shouldDestroy);
|
||||||
}
|
}
|
||||||
else
|
else if (networkObjects[i].IsSceneObject != null && !networkObjects[i].IsSceneObject.Value)
|
||||||
{
|
{
|
||||||
UnityEngine.Object.Destroy(networkObjects[i].gameObject);
|
UnityEngine.Object.Destroy(networkObjects[i].gameObject);
|
||||||
}
|
}
|
||||||
@@ -711,9 +791,10 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
foreach (var networkObject in networkObjectsToSpawn)
|
foreach (var networkObject in networkObjectsToSpawn)
|
||||||
{
|
{
|
||||||
SpawnNetworkObjectLocally(networkObject, GetNetworkObjectId(), true, false, null, true);
|
SpawnNetworkObjectLocally(networkObject, GetNetworkObjectId(), true, false, networkObject.OwnerClientId, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,18 +838,6 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!networkObject.IsOwnedByServer && !networkObject.IsPlayerObject && TryGetNetworkClient(networkObject.OwnerClientId, out NetworkClient networkClient))
|
|
||||||
{
|
|
||||||
//Someone owns it.
|
|
||||||
for (int i = networkClient.OwnedObjects.Count - 1; i > -1; i--)
|
|
||||||
{
|
|
||||||
if (networkClient.OwnedObjects[i].NetworkObjectId == networkObject.NetworkObjectId)
|
|
||||||
{
|
|
||||||
networkClient.OwnedObjects.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
networkObject.InvokeBehaviourNetworkDespawn();
|
networkObject.InvokeBehaviourNetworkDespawn();
|
||||||
|
|
||||||
if (NetworkManager != null && NetworkManager.IsServer)
|
if (NetworkManager != null && NetworkManager.IsServer)
|
||||||
@@ -782,38 +851,31 @@ namespace Unity.Netcode
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
if (networkObject != null)
|
||||||
{
|
{
|
||||||
networkObject.SnapshotDespawn();
|
// As long as we have any remaining clients, then notify of the object being destroy.
|
||||||
}
|
if (NetworkManager.ConnectedClientsList.Count > 0)
|
||||||
else
|
|
||||||
{
|
|
||||||
if (networkObject != null)
|
|
||||||
{
|
{
|
||||||
// As long as we have any remaining clients, then notify of the object being destroy.
|
m_TargetClientIds.Clear();
|
||||||
if (NetworkManager.ConnectedClientsList.Count > 0)
|
|
||||||
|
// We keep only the client for which the object is visible
|
||||||
|
// as the other clients have them already despawned
|
||||||
|
foreach (var clientId in NetworkManager.ConnectedClientsIds)
|
||||||
{
|
{
|
||||||
m_TargetClientIds.Clear();
|
if (networkObject.IsNetworkVisibleTo(clientId))
|
||||||
|
|
||||||
// We keep only the client for which the object is visible
|
|
||||||
// as the other clients have them already despawned
|
|
||||||
foreach (var clientId in NetworkManager.ConnectedClientsIds)
|
|
||||||
{
|
{
|
||||||
if (networkObject.IsNetworkVisibleTo(clientId))
|
m_TargetClientIds.Add(clientId);
|
||||||
{
|
|
||||||
m_TargetClientIds.Add(clientId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var message = new DestroyObjectMessage
|
var message = new DestroyObjectMessage
|
||||||
{
|
{
|
||||||
NetworkObjectId = networkObject.NetworkObjectId
|
NetworkObjectId = networkObject.NetworkObjectId
|
||||||
};
|
};
|
||||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, m_TargetClientIds);
|
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, m_TargetClientIds);
|
||||||
foreach (var targetClientId in m_TargetClientIds)
|
foreach (var targetClientId in m_TargetClientIds)
|
||||||
{
|
{
|
||||||
NetworkManager.NetworkMetrics.TrackObjectDestroySent(targetClientId, networkObject, size);
|
NetworkManager.NetworkMetrics.TrackObjectDestroySent(targetClientId, networkObject, size);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
#if UNITY_UNET_PRESENT
|
|
||||||
using System;
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
using UnityEditor;
|
|
||||||
#endif
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Networking;
|
|
||||||
|
|
||||||
namespace Unity.Netcode.Transports.UNET
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A transport channel used by the netcode
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
public class UNetChannel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the channel
|
|
||||||
/// </summary>
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
[ReadOnly]
|
|
||||||
#endif
|
|
||||||
public byte Id;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The type of channel
|
|
||||||
/// </summary>
|
|
||||||
public QosType Type;
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
private class ReadOnlyAttribute : PropertyAttribute { }
|
|
||||||
|
|
||||||
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
|
|
||||||
private class ReadOnlyDrawer : PropertyDrawer
|
|
||||||
{
|
|
||||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
|
||||||
{
|
|
||||||
// Saving previous GUI enabled value
|
|
||||||
var previousGUIState = GUI.enabled;
|
|
||||||
|
|
||||||
// Disabling edit for property
|
|
||||||
GUI.enabled = false;
|
|
||||||
|
|
||||||
// Drawing Property
|
|
||||||
EditorGUI.PropertyField(position, property, label);
|
|
||||||
|
|
||||||
// Setting old GUI enabled value
|
|
||||||
GUI.enabled = previousGUIState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: e864534da30ef604992c0ed33c75d3c6
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
8
Runtime/Transports/UTP.meta
Normal file
8
Runtime/Transports/UTP.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 81887adf6d9ca40c9b70728b7018b6f5
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
96
Runtime/Transports/UTP/BatchedReceiveQueue.cs
Normal file
96
Runtime/Transports/UTP/BatchedReceiveQueue.cs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
using System;
|
||||||
|
using Unity.Networking.Transport;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Transports.UTP
|
||||||
|
{
|
||||||
|
/// <summary>Queue for batched messages received through UTP.</summary>
|
||||||
|
/// <remarks>This is meant as a companion to <see cref="BatchedSendQueue"/>.</remarks>
|
||||||
|
internal class BatchedReceiveQueue
|
||||||
|
{
|
||||||
|
private byte[] m_Data;
|
||||||
|
private int m_Offset;
|
||||||
|
private int m_Length;
|
||||||
|
|
||||||
|
public bool IsEmpty => m_Length <= 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a new receive queue from a <see cref="DataStreamReader"/> returned by
|
||||||
|
/// <see cref="NetworkDriver"/> when popping a data event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The <see cref="DataStreamReader"/> to construct from.</param>
|
||||||
|
public BatchedReceiveQueue(DataStreamReader reader)
|
||||||
|
{
|
||||||
|
m_Data = new byte[reader.Length];
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* dataPtr = m_Data)
|
||||||
|
{
|
||||||
|
reader.ReadBytes(dataPtr, reader.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Offset = 0;
|
||||||
|
m_Length = reader.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Push the entire data from a <see cref="DataStreamReader"/> (as returned by popping an
|
||||||
|
/// event from a <see cref="NetworkDriver">) to the queue.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The <see cref="DataStreamReader"/> to push the data of.</param>
|
||||||
|
public void PushReader(DataStreamReader reader)
|
||||||
|
{
|
||||||
|
// Resize the array and copy the existing data to the beginning if there's not enough
|
||||||
|
// room to copy the reader's data at the end of the existing data.
|
||||||
|
var available = m_Data.Length - (m_Offset + m_Length);
|
||||||
|
if (available < reader.Length)
|
||||||
|
{
|
||||||
|
if (m_Length > 0)
|
||||||
|
{
|
||||||
|
Array.Copy(m_Data, m_Offset, m_Data, 0, m_Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Offset = 0;
|
||||||
|
|
||||||
|
while (m_Data.Length - m_Length < reader.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref m_Data, m_Data.Length * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* dataPtr = m_Data)
|
||||||
|
{
|
||||||
|
reader.ReadBytes(dataPtr + m_Offset + m_Length, reader.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Length += reader.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Pop the next full message in the queue.</summary>
|
||||||
|
/// <returns>The message, or the default value if no more full messages.</returns>
|
||||||
|
public ArraySegment<byte> PopMessage()
|
||||||
|
{
|
||||||
|
if (m_Length < sizeof(int))
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var messageLength = BitConverter.ToInt32(m_Data, m_Offset);
|
||||||
|
|
||||||
|
if (m_Length - sizeof(int) < messageLength)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = new ArraySegment<byte>(m_Data, m_Offset + sizeof(int), messageLength);
|
||||||
|
|
||||||
|
m_Offset += sizeof(int) + messageLength;
|
||||||
|
m_Length -= sizeof(int) + messageLength;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: a32aeecf69a2542469927066f5b88005
|
guid: e9ead10b891184bd5b8f2650fd66a5b1
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
233
Runtime/Transports/UTP/BatchedSendQueue.cs
Normal file
233
Runtime/Transports/UTP/BatchedSendQueue.cs
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
using System;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
|
using Unity.Networking.Transport;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Transports.UTP
|
||||||
|
{
|
||||||
|
/// <summary>Queue for batched messages meant to be sent through UTP.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Messages should be pushed on the queue with <see cref="PushMessage"/>. To send batched
|
||||||
|
/// messages, call <see cref="FillWriter"> with the <see cref="DataStreamWriter"/> obtained from
|
||||||
|
/// <see cref="NetworkDriver.BeginSend"/>. This will fill the writer with as many messages as
|
||||||
|
/// possible. If the send is successful, call <see cref="Consume"/> to remove the data from the
|
||||||
|
/// queue.
|
||||||
|
///
|
||||||
|
/// This is meant as a companion to <see cref="BatchedReceiveQueue"/>, which should be used to
|
||||||
|
/// read messages sent with this queue.
|
||||||
|
/// </remarks>
|
||||||
|
internal struct BatchedSendQueue : IDisposable
|
||||||
|
{
|
||||||
|
private NativeArray<byte> m_Data;
|
||||||
|
private NativeArray<int> m_HeadTailIndices;
|
||||||
|
|
||||||
|
/// <summary>Overhead that is added to each message in the queue.</summary>
|
||||||
|
public const int PerMessageOverhead = sizeof(int);
|
||||||
|
|
||||||
|
// Indices into m_HeadTailIndicies.
|
||||||
|
private const int k_HeadInternalIndex = 0;
|
||||||
|
private const int k_TailInternalIndex = 1;
|
||||||
|
|
||||||
|
/// <summary>Index of the first byte of the oldest data in the queue.</summary>
|
||||||
|
private int HeadIndex
|
||||||
|
{
|
||||||
|
get { return m_HeadTailIndices[k_HeadInternalIndex]; }
|
||||||
|
set { m_HeadTailIndices[k_HeadInternalIndex] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Index one past the last byte of the most recent data in the queue.</summary>
|
||||||
|
private int TailIndex
|
||||||
|
{
|
||||||
|
get { return m_HeadTailIndices[k_TailInternalIndex]; }
|
||||||
|
set { m_HeadTailIndices[k_TailInternalIndex] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Length => TailIndex - HeadIndex;
|
||||||
|
|
||||||
|
public bool IsEmpty => HeadIndex == TailIndex;
|
||||||
|
|
||||||
|
public bool IsCreated => m_Data.IsCreated;
|
||||||
|
|
||||||
|
/// <summary>Construct a new empty send queue.</summary>
|
||||||
|
/// <param name="capacity">Maximum capacity of the send queue.</param>
|
||||||
|
public BatchedSendQueue(int capacity)
|
||||||
|
{
|
||||||
|
m_Data = new NativeArray<byte>(capacity, Allocator.Persistent);
|
||||||
|
m_HeadTailIndices = new NativeArray<int>(2, Allocator.Persistent);
|
||||||
|
|
||||||
|
HeadIndex = 0;
|
||||||
|
TailIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (IsCreated)
|
||||||
|
{
|
||||||
|
m_Data.Dispose();
|
||||||
|
m_HeadTailIndices.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Append data at the tail of the queue. No safety checks.</summary>
|
||||||
|
private void AppendDataAtTail(ArraySegment<byte> data)
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var writer = new DataStreamWriter((byte*)m_Data.GetUnsafePtr() + TailIndex, m_Data.Length - TailIndex);
|
||||||
|
|
||||||
|
writer.WriteInt(data.Count);
|
||||||
|
|
||||||
|
fixed (byte* dataPtr = data.Array)
|
||||||
|
{
|
||||||
|
writer.WriteBytes(dataPtr + data.Offset, data.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TailIndex += sizeof(int) + data.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Append a new message to the queue.</summary>
|
||||||
|
/// <param name="message">Message to append to the queue.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Whether the message was appended successfully. The only way it can fail is if there's
|
||||||
|
/// no more room in the queue. On failure, nothing is written to the queue.
|
||||||
|
/// </returns>
|
||||||
|
public bool PushMessage(ArraySegment<byte> message)
|
||||||
|
{
|
||||||
|
if (!IsCreated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there's enough room after the current tail index.
|
||||||
|
if (m_Data.Length - TailIndex >= sizeof(int) + message.Count)
|
||||||
|
{
|
||||||
|
AppendDataAtTail(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there would be enough room if we moved data at the beginning of m_Data.
|
||||||
|
if (m_Data.Length - TailIndex + HeadIndex >= sizeof(int) + message.Count)
|
||||||
|
{
|
||||||
|
// Move the data back at the beginning of m_Data.
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
UnsafeUtility.MemMove(m_Data.GetUnsafePtr(), (byte*)m_Data.GetUnsafePtr() + HeadIndex, Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
TailIndex = Length;
|
||||||
|
HeadIndex = 0;
|
||||||
|
|
||||||
|
AppendDataAtTail(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fill as much of a <see cref="DataStreamWriter"/> as possible with data from the head of
|
||||||
|
/// the queue. Only full messages (and their length) are written to the writer.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This does NOT actually consume anything from the queue. That is, calling this method
|
||||||
|
/// does not reduce the length of the queue. Callers are expected to call
|
||||||
|
/// <see cref="Consume"/> with the value returned by this method afterwards if the data can
|
||||||
|
/// be safely removed from the queue (e.g. if it was sent successfully).
|
||||||
|
///
|
||||||
|
/// This method should not be used together with <see cref="FillWriterWithBytes"> since this
|
||||||
|
/// could lead to a corrupted queue.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="writer">The <see cref="DataStreamWriter"/> to write to.</param>
|
||||||
|
/// <returns>How many bytes were written to the writer.</returns>
|
||||||
|
public int FillWriterWithMessages(ref DataStreamWriter writer)
|
||||||
|
{
|
||||||
|
if (!IsCreated || Length == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var reader = new DataStreamReader((byte*)m_Data.GetUnsafePtr() + HeadIndex, Length);
|
||||||
|
|
||||||
|
var writerAvailable = writer.Capacity;
|
||||||
|
var readerOffset = 0;
|
||||||
|
|
||||||
|
while (readerOffset < Length)
|
||||||
|
{
|
||||||
|
reader.SeekSet(readerOffset);
|
||||||
|
var messageLength = reader.ReadInt();
|
||||||
|
|
||||||
|
if (writerAvailable < sizeof(int) + messageLength)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteInt(messageLength);
|
||||||
|
|
||||||
|
var messageOffset = HeadIndex + reader.GetBytesRead();
|
||||||
|
writer.WriteBytes((byte*)m_Data.GetUnsafePtr() + messageOffset, messageLength);
|
||||||
|
|
||||||
|
writerAvailable -= sizeof(int) + messageLength;
|
||||||
|
readerOffset += sizeof(int) + messageLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return writer.Capacity - writerAvailable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fill the given <see cref="DataStreamWriter"/> with as many bytes from the queue as
|
||||||
|
/// possible, disregarding message boundaries.
|
||||||
|
/// </summary>
|
||||||
|
///<remarks>
|
||||||
|
/// This does NOT actually consume anything from the queue. That is, calling this method
|
||||||
|
/// does not reduce the length of the queue. Callers are expected to call
|
||||||
|
/// <see cref="Consume"/> with the value returned by this method afterwards if the data can
|
||||||
|
/// be safely removed from the queue (e.g. if it was sent successfully).
|
||||||
|
///
|
||||||
|
/// This method should not be used together with <see cref="FillWriterWithMessages"/> since
|
||||||
|
/// this could lead to reading messages from a corrupted queue.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="writer">The <see cref="DataStreamWriter"/> to write to.</param>
|
||||||
|
/// <returns>How many bytes were written to the writer.</returns>
|
||||||
|
public int FillWriterWithBytes(ref DataStreamWriter writer)
|
||||||
|
{
|
||||||
|
if (!IsCreated || Length == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var copyLength = Math.Min(writer.Capacity, Length);
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
writer.WriteBytes((byte*)m_Data.GetUnsafePtr() + HeadIndex, copyLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
return copyLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Consume a number of bytes from the head of the queue.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This should only be called with a size that matches the last value returned by
|
||||||
|
/// <see cref="FillWriter"/>. Anything else will result in a corrupted queue.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="size">Number of bytes to consume from the queue.</param>
|
||||||
|
public void Consume(int size)
|
||||||
|
{
|
||||||
|
if (size >= Length)
|
||||||
|
{
|
||||||
|
HeadIndex = 0;
|
||||||
|
TailIndex = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HeadIndex += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: c275febadb27c4d18b41218e3353b84b
|
guid: ddf8f97f695d740f297dc42242b76b8c
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
8
Runtime/Transports/UTP/NetworkMetricsContext.cs
Normal file
8
Runtime/Transports/UTP/NetworkMetricsContext.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Unity.Netcode.Transports.UTP
|
||||||
|
{
|
||||||
|
public struct NetworkMetricsContext
|
||||||
|
{
|
||||||
|
public uint PacketSentCount;
|
||||||
|
public uint PacketReceivedCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: bd9e1475e8c8e4a6d935fe2409e3bd26
|
guid: adb0270501ff1421896ce15cc75bd56a
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
70
Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs
Normal file
70
Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#if MULTIPLAYER_TOOLS
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
using AOT;
|
||||||
|
using Unity.Burst;
|
||||||
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
|
using Unity.Networking.Transport;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Transports.UTP
|
||||||
|
{
|
||||||
|
[BurstCompile]
|
||||||
|
internal unsafe struct NetworkMetricsPipelineStage : INetworkPipelineStage
|
||||||
|
{
|
||||||
|
static TransportFunctionPointer<NetworkPipelineStage.ReceiveDelegate> ReceiveFunction = new TransportFunctionPointer<NetworkPipelineStage.ReceiveDelegate>(Receive);
|
||||||
|
static TransportFunctionPointer<NetworkPipelineStage.SendDelegate> SendFunction = new TransportFunctionPointer<NetworkPipelineStage.SendDelegate>(Send);
|
||||||
|
static TransportFunctionPointer<NetworkPipelineStage.InitializeConnectionDelegate> InitializeConnectionFunction = new TransportFunctionPointer<NetworkPipelineStage.InitializeConnectionDelegate>(InitializeConnection);
|
||||||
|
|
||||||
|
public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer,
|
||||||
|
int staticInstanceBufferLength,
|
||||||
|
NetworkSettings settings)
|
||||||
|
{
|
||||||
|
return new NetworkPipelineStage(
|
||||||
|
ReceiveFunction,
|
||||||
|
SendFunction,
|
||||||
|
InitializeConnectionFunction,
|
||||||
|
ReceiveCapacity: 0,
|
||||||
|
SendCapacity: 0,
|
||||||
|
HeaderCapacity: 0,
|
||||||
|
SharedStateCapacity: UnsafeUtility.SizeOf<NetworkMetricsContext>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int StaticSize => 0;
|
||||||
|
|
||||||
|
[BurstCompile(DisableDirectCall = true)]
|
||||||
|
[MonoPInvokeCallback(typeof(NetworkPipelineStage.ReceiveDelegate))]
|
||||||
|
private static void Receive(ref NetworkPipelineContext networkPipelineContext,
|
||||||
|
ref InboundRecvBuffer inboundReceiveBuffer,
|
||||||
|
ref NetworkPipelineStage.Requests requests,
|
||||||
|
int systemHeaderSize)
|
||||||
|
{
|
||||||
|
var networkMetricContext = (NetworkMetricsContext*)networkPipelineContext.internalSharedProcessBuffer;
|
||||||
|
networkMetricContext->PacketReceivedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BurstCompile(DisableDirectCall = true)]
|
||||||
|
[MonoPInvokeCallback(typeof(NetworkPipelineStage.SendDelegate))]
|
||||||
|
private static int Send(ref NetworkPipelineContext networkPipelineContext,
|
||||||
|
ref InboundSendBuffer inboundSendBuffer,
|
||||||
|
ref NetworkPipelineStage.Requests requests,
|
||||||
|
int systemHeaderSize)
|
||||||
|
{
|
||||||
|
var networkMetricContext = (NetworkMetricsContext*)networkPipelineContext.internalSharedProcessBuffer;
|
||||||
|
networkMetricContext->PacketSentCount++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BurstCompile(DisableDirectCall = true)]
|
||||||
|
[MonoPInvokeCallback(typeof(NetworkPipelineStage.InitializeConnectionDelegate))]
|
||||||
|
private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength,
|
||||||
|
byte* sendProcessBuffer, int sendProcessBufferLength, byte* receiveProcessBuffer, int receiveProcessBufferLength,
|
||||||
|
byte* sharedProcessBuffer, int sharedProcessBufferLength)
|
||||||
|
{
|
||||||
|
var networkMetricContext = (NetworkMetricsContext*)sharedProcessBuffer;
|
||||||
|
networkMetricContext->PacketSentCount = 0;
|
||||||
|
networkMetricContext->PacketReceivedCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
11
Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs.meta
Normal file
11
Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 52b1ce9f83ce049c59327064bf70cee8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
1222
Runtime/Transports/UTP/UnityTransport.cs
Normal file
1222
Runtime/Transports/UTP/UnityTransport.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Runtime/Transports/UTP/UnityTransport.cs.meta
Normal file
11
Runtime/Transports/UTP/UnityTransport.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6960e84d07fb87f47956e7a81d71c4e6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -10,7 +10,9 @@
|
|||||||
"Unity.Multiplayer.Tools.NetStats",
|
"Unity.Multiplayer.Tools.NetStats",
|
||||||
"Unity.Multiplayer.Tools.NetStatsReporting",
|
"Unity.Multiplayer.Tools.NetStatsReporting",
|
||||||
"Unity.Multiplayer.Tools.NetworkSolutionInterface",
|
"Unity.Multiplayer.Tools.NetworkSolutionInterface",
|
||||||
"Unity.Collections"
|
"Unity.Networking.Transport",
|
||||||
|
"Unity.Collections",
|
||||||
|
"Unity.Burst"
|
||||||
],
|
],
|
||||||
"allowUnsafeCode": true,
|
"allowUnsafeCode": true,
|
||||||
"versionDefines": [
|
"versionDefines": [
|
||||||
@@ -26,8 +28,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "com.unity.multiplayer.tools",
|
"name": "com.unity.multiplayer.tools",
|
||||||
"expression": "1.0.0-pre.4",
|
"expression": "1.0.0-pre.7",
|
||||||
"define": "MULTIPLAYER_TOOLS_1_0_0_PRE_4"
|
"define": "MULTIPLAYER_TOOLS_1_0_0_PRE_7"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.TestTools;
|
using UnityEngine.TestTools;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
@@ -111,6 +112,33 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
private NetworkManagerInstatiationMode m_NetworkManagerInstatiationMode;
|
private NetworkManagerInstatiationMode m_NetworkManagerInstatiationMode;
|
||||||
|
|
||||||
|
private bool m_EnableVerboseDebug;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to display the various integration test
|
||||||
|
/// stages and can be used to log verbose information
|
||||||
|
/// for troubleshooting an integration test.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="msg"></param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected void VerboseDebug(string msg)
|
||||||
|
{
|
||||||
|
if (m_EnableVerboseDebug)
|
||||||
|
{
|
||||||
|
Debug.Log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this and return true if you need
|
||||||
|
/// to troubleshoot a hard to track bug within an
|
||||||
|
/// integration test.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual bool OnSetVerboseDebug()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The very first thing invoked during the <see cref="OneTimeSetup"/> that
|
/// The very first thing invoked during the <see cref="OneTimeSetup"/> that
|
||||||
/// determines how this integration test handles NetworkManager instantiation
|
/// determines how this integration test handles NetworkManager instantiation
|
||||||
@@ -130,11 +158,17 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
[OneTimeSetUp]
|
[OneTimeSetUp]
|
||||||
public void OneTimeSetup()
|
public void OneTimeSetup()
|
||||||
{
|
{
|
||||||
|
m_EnableVerboseDebug = OnSetVerboseDebug();
|
||||||
|
|
||||||
|
VerboseDebug($"Entering {nameof(OneTimeSetup)}");
|
||||||
|
|
||||||
m_NetworkManagerInstatiationMode = OnSetIntegrationTestMode();
|
m_NetworkManagerInstatiationMode = OnSetIntegrationTestMode();
|
||||||
|
|
||||||
// Enable NetcodeIntegrationTest auto-label feature
|
// Enable NetcodeIntegrationTest auto-label feature
|
||||||
NetcodeIntegrationTestHelpers.RegisterNetcodeIntegrationTest(true);
|
NetcodeIntegrationTestHelpers.RegisterNetcodeIntegrationTest(true);
|
||||||
OnOneTimeSetup();
|
OnOneTimeSetup();
|
||||||
|
|
||||||
|
VerboseDebug($"Exiting {nameof(OneTimeSetup)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -153,6 +187,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
[UnitySetUp]
|
[UnitySetUp]
|
||||||
public IEnumerator SetUp()
|
public IEnumerator SetUp()
|
||||||
{
|
{
|
||||||
|
VerboseDebug($"Entering {nameof(SetUp)}");
|
||||||
|
|
||||||
yield return OnSetup();
|
yield return OnSetup();
|
||||||
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.AllTests && m_ServerNetworkManager == null ||
|
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.AllTests && m_ServerNetworkManager == null ||
|
||||||
m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.PerTest)
|
m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.PerTest)
|
||||||
@@ -161,6 +197,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
yield return StartServerAndClients();
|
yield return StartServerAndClients();
|
||||||
}
|
}
|
||||||
|
VerboseDebug($"Exiting {nameof(SetUp)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -173,6 +210,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
private void CreatePlayerPrefab()
|
private void CreatePlayerPrefab()
|
||||||
{
|
{
|
||||||
|
VerboseDebug($"Entering {nameof(CreatePlayerPrefab)}");
|
||||||
// Create playerPrefab
|
// Create playerPrefab
|
||||||
m_PlayerPrefab = new GameObject("Player");
|
m_PlayerPrefab = new GameObject("Player");
|
||||||
NetworkObject networkObject = m_PlayerPrefab.AddComponent<NetworkObject>();
|
NetworkObject networkObject = m_PlayerPrefab.AddComponent<NetworkObject>();
|
||||||
@@ -181,6 +219,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
|
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
|
||||||
|
|
||||||
OnCreatePlayerPrefab();
|
OnCreatePlayerPrefab();
|
||||||
|
|
||||||
|
VerboseDebug($"Exiting {nameof(CreatePlayerPrefab)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -207,6 +247,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
/// <param name="numberOfClients"></param>
|
/// <param name="numberOfClients"></param>
|
||||||
protected void CreateServerAndClients(int numberOfClients)
|
protected void CreateServerAndClients(int numberOfClients)
|
||||||
{
|
{
|
||||||
|
VerboseDebug($"Entering {nameof(CreateServerAndClients)}");
|
||||||
|
|
||||||
CreatePlayerPrefab();
|
CreatePlayerPrefab();
|
||||||
|
|
||||||
// Create multiple NetworkManager instances
|
// Create multiple NetworkManager instances
|
||||||
@@ -235,6 +277,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
// Provides opportunity to allow child derived classes to
|
// Provides opportunity to allow child derived classes to
|
||||||
// modify the NetworkManager's configuration before starting.
|
// modify the NetworkManager's configuration before starting.
|
||||||
OnServerAndClientsCreated();
|
OnServerAndClientsCreated();
|
||||||
|
|
||||||
|
VerboseDebug($"Exiting {nameof(CreateServerAndClients)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -272,6 +316,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
{
|
{
|
||||||
if (CanStartServerAndClients())
|
if (CanStartServerAndClients())
|
||||||
{
|
{
|
||||||
|
VerboseDebug($"Entering {nameof(StartServerAndClients)}");
|
||||||
|
|
||||||
// Start the instances and pass in our SceneManagerInitialization action that is invoked immediately after host-server
|
// Start the instances and pass in our SceneManagerInitialization action that is invoked immediately after host-server
|
||||||
// is started and after each client is started.
|
// is started and after each client is started.
|
||||||
if (!NetcodeIntegrationTestHelpers.Start(m_UseHost, m_ServerNetworkManager, m_ClientNetworkManagers))
|
if (!NetcodeIntegrationTestHelpers.Start(m_UseHost, m_ServerNetworkManager, m_ClientNetworkManagers))
|
||||||
@@ -290,11 +336,6 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"{nameof(StartServerAndClients)} timed out waiting for all clients to be connected!");
|
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"{nameof(StartServerAndClients)} timed out waiting for all clients to be connected!");
|
||||||
|
|
||||||
if (s_GlobalTimeoutHelper.TimedOut)
|
|
||||||
{
|
|
||||||
yield return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_UseHost || m_ServerNetworkManager.IsHost)
|
if (m_UseHost || m_ServerNetworkManager.IsHost)
|
||||||
{
|
{
|
||||||
// Add the server player instance to all m_ClientSidePlayerNetworkObjects entries
|
// Add the server player instance to all m_ClientSidePlayerNetworkObjects entries
|
||||||
@@ -332,6 +373,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
// Notification that at this time the server and client(s) are instantiated,
|
// Notification that at this time the server and client(s) are instantiated,
|
||||||
// started, and connected on both sides.
|
// started, and connected on both sides.
|
||||||
yield return OnServerAndClientsConnected();
|
yield return OnServerAndClientsConnected();
|
||||||
|
|
||||||
|
VerboseDebug($"Exiting {nameof(StartServerAndClients)}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,6 +441,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected void ShutdownAndCleanUp()
|
protected void ShutdownAndCleanUp()
|
||||||
{
|
{
|
||||||
|
VerboseDebug($"Entering {nameof(ShutdownAndCleanUp)}");
|
||||||
// Shutdown and clean up both of our NetworkManager instances
|
// Shutdown and clean up both of our NetworkManager instances
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -427,6 +471,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
// reset the m_ServerWaitForTick for the next test to initialize
|
// reset the m_ServerWaitForTick for the next test to initialize
|
||||||
s_DefaultWaitForTick = new WaitForSeconds(1.0f / k_DefaultTickRate);
|
s_DefaultWaitForTick = new WaitForSeconds(1.0f / k_DefaultTickRate);
|
||||||
|
VerboseDebug($"Exiting {nameof(ShutdownAndCleanUp)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -441,12 +486,15 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
[UnityTearDown]
|
[UnityTearDown]
|
||||||
public IEnumerator TearDown()
|
public IEnumerator TearDown()
|
||||||
{
|
{
|
||||||
|
VerboseDebug($"Entering {nameof(TearDown)}");
|
||||||
yield return OnTearDown();
|
yield return OnTearDown();
|
||||||
|
|
||||||
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.PerTest)
|
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.PerTest)
|
||||||
{
|
{
|
||||||
ShutdownAndCleanUp();
|
ShutdownAndCleanUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VerboseDebug($"Exiting {nameof(TearDown)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -462,6 +510,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
[OneTimeTearDown]
|
[OneTimeTearDown]
|
||||||
public void OneTimeTearDown()
|
public void OneTimeTearDown()
|
||||||
{
|
{
|
||||||
|
VerboseDebug($"Entering {nameof(OneTimeTearDown)}");
|
||||||
OnOneTimeTearDown();
|
OnOneTimeTearDown();
|
||||||
|
|
||||||
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.AllTests)
|
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.AllTests)
|
||||||
@@ -471,6 +520,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
// Disable NetcodeIntegrationTest auto-label feature
|
// Disable NetcodeIntegrationTest auto-label feature
|
||||||
NetcodeIntegrationTestHelpers.RegisterNetcodeIntegrationTest(false);
|
NetcodeIntegrationTestHelpers.RegisterNetcodeIntegrationTest(false);
|
||||||
|
|
||||||
|
VerboseDebug($"Exiting {nameof(OneTimeTearDown)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using Unity.Netcode.Transports.UTP;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
@@ -109,9 +110,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
public enum InstanceTransport
|
public enum InstanceTransport
|
||||||
{
|
{
|
||||||
SIP,
|
SIP,
|
||||||
#if UTP_ADAPTER
|
|
||||||
UTP
|
UTP
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IntegrationTestSceneHandler ClientSceneHandler = null;
|
internal static IntegrationTestSceneHandler ClientSceneHandler = null;
|
||||||
@@ -172,12 +171,10 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
switch (instanceTransport)
|
switch (instanceTransport)
|
||||||
{
|
{
|
||||||
case InstanceTransport.SIP:
|
case InstanceTransport.SIP:
|
||||||
default:
|
|
||||||
return go.AddComponent<SIPTransport>();
|
return go.AddComponent<SIPTransport>();
|
||||||
#if UTP_ADAPTER
|
default:
|
||||||
case InstanceTransport.UTP:
|
case InstanceTransport.UTP:
|
||||||
return go.AddComponent<UnityTransport>();
|
return go.AddComponent<UnityTransport>();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
"rootNamespace": "Unity.Netcode.TestHelpers.Runtime",
|
"rootNamespace": "Unity.Netcode.TestHelpers.Runtime",
|
||||||
"references": [
|
"references": [
|
||||||
"Unity.Netcode.Runtime",
|
"Unity.Netcode.Runtime",
|
||||||
"Unity.Netcode.Adapter.UTP",
|
|
||||||
"Unity.Multiplayer.MetricTypes",
|
"Unity.Multiplayer.MetricTypes",
|
||||||
"Unity.Multiplayer.NetStats",
|
"Unity.Multiplayer.NetStats",
|
||||||
"Unity.Multiplayer.Tools.MetricTypes",
|
"Unity.Multiplayer.Tools.MetricTypes",
|
||||||
@@ -18,15 +17,10 @@
|
|||||||
"expression": "",
|
"expression": "",
|
||||||
"define": "MULTIPLAYER_TOOLS"
|
"define": "MULTIPLAYER_TOOLS"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "com.unity.netcode.adapter.utp",
|
|
||||||
"expression": "",
|
|
||||||
"define": "UTP_ADAPTER"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "com.unity.multiplayer.tools",
|
"name": "com.unity.multiplayer.tools",
|
||||||
"expression": "1.0.0-pre.4",
|
"expression": "1.0.0-pre.7",
|
||||||
"define": "MULTIPLAYER_TOOLS_1_0_0_PRE_4"
|
"define": "MULTIPLAYER_TOOLS_1_0_0_PRE_7"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
using NUnit.Framework;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Unity.Netcode.EditorTests
|
|
||||||
{
|
|
||||||
public class FixedAllocatorTest
|
|
||||||
{
|
|
||||||
[Test]
|
|
||||||
public void SimpleTest()
|
|
||||||
{
|
|
||||||
int pos;
|
|
||||||
|
|
||||||
var allocator = new IndexAllocator(20000, 200);
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
|
|
||||||
// allocate 20 bytes
|
|
||||||
Assert.IsTrue(allocator.Allocate(0, 20, out pos));
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// can't ask for negative amount of memory
|
|
||||||
Assert.IsFalse(allocator.Allocate(1, -20, out pos));
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// can't ask for deallocation of negative index
|
|
||||||
Assert.IsFalse(allocator.Deallocate(-1));
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// can't ask for the same index twice
|
|
||||||
Assert.IsFalse(allocator.Allocate(0, 20, out pos));
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// allocate another 20 bytes
|
|
||||||
Assert.IsTrue(allocator.Allocate(1, 20, out pos));
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// allocate a third 20 bytes
|
|
||||||
Assert.IsTrue(allocator.Allocate(2, 20, out pos));
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// deallocate 0
|
|
||||||
Assert.IsTrue(allocator.Deallocate(0));
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// deallocate 1
|
|
||||||
allocator.Deallocate(1);
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// deallocate 2
|
|
||||||
allocator.Deallocate(2);
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// allocate 50 bytes
|
|
||||||
Assert.IsTrue(allocator.Allocate(0, 50, out pos));
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// allocate another 50 bytes
|
|
||||||
Assert.IsTrue(allocator.Allocate(1, 50, out pos));
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// allocate a third 50 bytes
|
|
||||||
Assert.IsTrue(allocator.Allocate(2, 50, out pos));
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// deallocate 1, a block in the middle this time
|
|
||||||
allocator.Deallocate(1);
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
|
|
||||||
// allocate a smaller one in its place
|
|
||||||
allocator.Allocate(1, 25, out pos);
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void ReuseTest()
|
|
||||||
{
|
|
||||||
int count = 100;
|
|
||||||
bool[] used = new bool[count];
|
|
||||||
int[] pos = new int[count];
|
|
||||||
int iterations = 10000;
|
|
||||||
|
|
||||||
var allocator = new IndexAllocator(20000, 200);
|
|
||||||
|
|
||||||
for (int i = 0; i < iterations; i++)
|
|
||||||
{
|
|
||||||
int index = Random.Range(0, count);
|
|
||||||
if (used[index])
|
|
||||||
{
|
|
||||||
Assert.IsTrue(allocator.Deallocate(index));
|
|
||||||
used[index] = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int position;
|
|
||||||
int length = 10 * Random.Range(1, 10);
|
|
||||||
Assert.IsTrue(allocator.Allocate(index, length, out position));
|
|
||||||
pos[index] = position;
|
|
||||||
used[index] = true;
|
|
||||||
}
|
|
||||||
Assert.IsTrue(allocator.Verify());
|
|
||||||
}
|
|
||||||
allocator.DebugDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 85ac488e1432d49668c711fa625a0743
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -34,5 +34,49 @@ namespace Unity.Netcode.EditorTests
|
|||||||
// Clean up
|
// Clean up
|
||||||
Object.DestroyImmediate(parent);
|
Object.DestroyImmediate(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum NetworkObjectPlacement
|
||||||
|
{
|
||||||
|
Root, // Added to the same root GameObject
|
||||||
|
Child // Added to a child GameObject
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void NetworkObjectNotAllowed([Values] NetworkObjectPlacement networkObjectPlacement)
|
||||||
|
{
|
||||||
|
var gameObject = new GameObject(nameof(NetworkManager));
|
||||||
|
var targetforNetworkObject = gameObject;
|
||||||
|
|
||||||
|
if (networkObjectPlacement == NetworkObjectPlacement.Child)
|
||||||
|
{
|
||||||
|
var childGameObject = new GameObject($"{nameof(NetworkManager)}-Child");
|
||||||
|
childGameObject.transform.parent = targetforNetworkObject.transform;
|
||||||
|
targetforNetworkObject = childGameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
var networkManager = gameObject.AddComponent<NetworkManager>();
|
||||||
|
|
||||||
|
// Trap for the error message generated when a NetworkObject is discovered on the same GameObject or any children under it
|
||||||
|
LogAssert.Expect(LogType.Error, NetworkManagerHelper.Singleton.NetworkManagerAndNetworkObjectNotAllowedMessage());
|
||||||
|
|
||||||
|
// Add the NetworkObject
|
||||||
|
var networkObject = targetforNetworkObject.AddComponent<NetworkObject>();
|
||||||
|
|
||||||
|
// Since this is an in-editor test, we must force this invocation
|
||||||
|
NetworkManagerHelper.Singleton.CheckAndNotifyUserNetworkObjectRemoved(networkManager, true);
|
||||||
|
|
||||||
|
// Validate that the NetworkObject has been removed
|
||||||
|
if (networkObjectPlacement == NetworkObjectPlacement.Root)
|
||||||
|
{
|
||||||
|
Assert.IsNull(networkManager.gameObject.GetComponent<NetworkObject>(), $"There is still a {nameof(NetworkObject)} on {nameof(NetworkManager)}'s GameObject!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.IsNull(networkManager.gameObject.GetComponentInChildren<NetworkObject>(), $"There is still a {nameof(NetworkObject)} on {nameof(NetworkManager)}'s child GameObject!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
Object.DestroyImmediate(gameObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
Tests/Editor/NetworkVar.meta
Normal file
3
Tests/Editor/NetworkVar.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ed3be13d96c34bc4b8676ce550cee041
|
||||||
|
timeCreated: 1647861659
|
||||||
41
Tests/Editor/NetworkVar/NetworkVarTests.cs
Normal file
41
Tests/Editor/NetworkVar/NetworkVarTests.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.EditorTests.NetworkVar
|
||||||
|
{
|
||||||
|
public class NetworkVarTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestAssignmentUnchanged()
|
||||||
|
{
|
||||||
|
var intVar = new NetworkVariable<int>();
|
||||||
|
|
||||||
|
intVar.Value = 314159265;
|
||||||
|
|
||||||
|
intVar.OnValueChanged += (value, newValue) =>
|
||||||
|
{
|
||||||
|
Assert.Fail("OnValueChanged was invoked when setting the same value");
|
||||||
|
};
|
||||||
|
|
||||||
|
intVar.Value = 314159265;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAssignmentChanged()
|
||||||
|
{
|
||||||
|
var intVar = new NetworkVariable<int>();
|
||||||
|
|
||||||
|
intVar.Value = 314159265;
|
||||||
|
|
||||||
|
var changed = false;
|
||||||
|
|
||||||
|
intVar.OnValueChanged += (value, newValue) =>
|
||||||
|
{
|
||||||
|
changed = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
intVar.Value = 314159266;
|
||||||
|
|
||||||
|
Assert.True(changed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Tests/Editor/NetworkVar/NetworkVarTests.cs.meta
Normal file
3
Tests/Editor/NetworkVar/NetworkVarTests.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a7cdd8c1251f4352b1f1d4825dc85182
|
||||||
|
timeCreated: 1647861669
|
||||||
@@ -393,6 +393,34 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenCreatingNewFastBufferReader_IsInitializedIsTrue()
|
||||||
|
{
|
||||||
|
var array = new NativeArray<byte>(100, Allocator.Temp);
|
||||||
|
var reader = new FastBufferReader(array, Allocator.Temp);
|
||||||
|
Assert.AreEqual(true, reader.IsInitialized);
|
||||||
|
reader.Dispose();
|
||||||
|
array.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenDisposingFastBufferReader_IsInitializedIsFalse()
|
||||||
|
{
|
||||||
|
var array = new NativeArray<byte>(100, Allocator.Temp);
|
||||||
|
var reader = new FastBufferReader(array, Allocator.Temp);
|
||||||
|
reader.Dispose();
|
||||||
|
Assert.AreEqual(false, reader.IsInitialized);
|
||||||
|
array.Dispose();
|
||||||
|
Assert.AreEqual(false, reader.IsInitialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenUsingDefaultFastBufferReader_IsInitializedIsFalse()
|
||||||
|
{
|
||||||
|
FastBufferReader writer = default;
|
||||||
|
Assert.AreEqual(false, writer.IsInitialized);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void WhenCallingReadByteWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown()
|
public void WhenCallingReadByteWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -891,6 +891,29 @@ namespace Unity.Netcode.EditorTests
|
|||||||
writer.Dispose();
|
writer.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenCreatingNewFastBufferWriter_IsInitializedIsTrue()
|
||||||
|
{
|
||||||
|
var writer = new FastBufferWriter(100, Allocator.Temp);
|
||||||
|
Assert.AreEqual(true, writer.IsInitialized);
|
||||||
|
writer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenDisposingFastBufferWriter_IsInitializedIsFalse()
|
||||||
|
{
|
||||||
|
var writer = new FastBufferWriter(100, Allocator.Temp);
|
||||||
|
writer.Dispose();
|
||||||
|
Assert.AreEqual(false, writer.IsInitialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenUsingDefaultFastBufferWriter_IsInitializedIsFalse()
|
||||||
|
{
|
||||||
|
FastBufferWriter writer = default;
|
||||||
|
Assert.AreEqual(false, writer.IsInitialized);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void WhenRequestingWritePastBoundsForNonGrowingWriter_TryBeginWriteReturnsFalse()
|
public void WhenRequestingWritePastBoundsForNonGrowingWriter_TryBeginWriteReturnsFalse()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
using NUnit.Framework;
|
|
||||||
|
|
||||||
namespace Unity.Netcode.EditorTests
|
|
||||||
{
|
|
||||||
public class SnapshotRttTests
|
|
||||||
{
|
|
||||||
private const double k_Epsilon = 0.0001;
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestBasicRtt()
|
|
||||||
{
|
|
||||||
var snapshot = new SnapshotSystem(null, new NetworkConfig(), null);
|
|
||||||
var client1 = snapshot.GetConnectionRtt(0);
|
|
||||||
|
|
||||||
client1.NotifySend(0, 0.0);
|
|
||||||
client1.NotifySend(1, 10.0);
|
|
||||||
|
|
||||||
client1.NotifyAck(1, 15.0);
|
|
||||||
|
|
||||||
client1.NotifySend(2, 20.0);
|
|
||||||
client1.NotifySend(3, 30.0);
|
|
||||||
client1.NotifySend(4, 32.0);
|
|
||||||
|
|
||||||
client1.NotifyAck(4, 38.0);
|
|
||||||
client1.NotifyAck(3, 40.0);
|
|
||||||
|
|
||||||
ConnectionRtt.Rtt ret = client1.GetRtt();
|
|
||||||
Assert.True(ret.AverageSec < 7.0 + k_Epsilon);
|
|
||||||
Assert.True(ret.AverageSec > 7.0 - k_Epsilon);
|
|
||||||
Assert.True(ret.WorstSec < 10.0 + k_Epsilon);
|
|
||||||
Assert.True(ret.WorstSec > 10.0 - k_Epsilon);
|
|
||||||
Assert.True(ret.BestSec < 5.0 + k_Epsilon);
|
|
||||||
Assert.True(ret.BestSec > 5.0 - k_Epsilon);
|
|
||||||
|
|
||||||
// note: `last` latency is latest received Ack, not latest sent sequence.
|
|
||||||
Assert.True(ret.LastSec < 10.0 + k_Epsilon);
|
|
||||||
Assert.True(ret.LastSec > 10.0 - k_Epsilon);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestEdgeCasesRtt()
|
|
||||||
{
|
|
||||||
var snapshot = new SnapshotSystem(null, new NetworkConfig(), null);
|
|
||||||
var client1 = snapshot.GetConnectionRtt(0);
|
|
||||||
var iterationCount = NetworkConfig.RttWindowSize * 3;
|
|
||||||
var extraCount = NetworkConfig.RttWindowSize * 2;
|
|
||||||
|
|
||||||
// feed in some messages
|
|
||||||
for (var iteration = 0; iteration < iterationCount; iteration++)
|
|
||||||
{
|
|
||||||
client1.NotifySend(iteration, 25.0 * iteration);
|
|
||||||
}
|
|
||||||
// ack some random ones in there (1 out of each 9), always 7.0 later
|
|
||||||
for (var iteration = 0; iteration < iterationCount; iteration += 9)
|
|
||||||
{
|
|
||||||
client1.NotifyAck(iteration, 25.0 * iteration + 7.0);
|
|
||||||
}
|
|
||||||
// ack some unused key, to check it doesn't throw off the values
|
|
||||||
for (var iteration = iterationCount; iteration < iterationCount + extraCount; iteration++)
|
|
||||||
{
|
|
||||||
client1.NotifyAck(iteration, 42.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionRtt.Rtt ret = client1.GetRtt();
|
|
||||||
Assert.True(ret.AverageSec < 7.0 + k_Epsilon);
|
|
||||||
Assert.True(ret.AverageSec > 7.0 - k_Epsilon);
|
|
||||||
Assert.True(ret.WorstSec < 7.0 + k_Epsilon);
|
|
||||||
Assert.True(ret.WorstSec > 7.0 - k_Epsilon);
|
|
||||||
Assert.True(ret.BestSec < 7.0 + k_Epsilon);
|
|
||||||
Assert.True(ret.BestSec > 7.0 - k_Epsilon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: a05afab7f08d44c07b2c5e144ba0b45a
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,363 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Unity.Collections;
|
|
||||||
using UnityEngine;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using Random = System.Random;
|
|
||||||
|
|
||||||
namespace Unity.Netcode.EditorTests
|
|
||||||
{
|
|
||||||
public class SnapshotTests
|
|
||||||
{
|
|
||||||
private SnapshotSystem m_SendSnapshot;
|
|
||||||
private SnapshotSystem m_RecvSnapshot;
|
|
||||||
|
|
||||||
private NetworkTimeSystem m_SendTimeSystem;
|
|
||||||
private NetworkTickSystem m_SendTickSystem;
|
|
||||||
private NetworkTimeSystem m_RecvTimeSystem;
|
|
||||||
private NetworkTickSystem m_RecvTickSystem;
|
|
||||||
|
|
||||||
private int m_SpawnedObjectCount;
|
|
||||||
private int m_DespawnedObjectCount;
|
|
||||||
private int m_NextSequence;
|
|
||||||
private uint m_TicksPerSec = 15;
|
|
||||||
private int m_MinSpawns;
|
|
||||||
private int m_MinDespawns;
|
|
||||||
|
|
||||||
private bool m_ExpectSpawns;
|
|
||||||
private bool m_ExpectDespawns;
|
|
||||||
private bool m_LoseNextMessage;
|
|
||||||
private bool m_PassBackResponses;
|
|
||||||
|
|
||||||
public void Prepare()
|
|
||||||
{
|
|
||||||
PrepareSendSideSnapshot();
|
|
||||||
PrepareRecvSideSnapshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AdvanceOneTickSendSide()
|
|
||||||
{
|
|
||||||
m_SendTimeSystem.Advance(1.0f / m_TicksPerSec);
|
|
||||||
m_SendTickSystem.UpdateTick(m_SendTimeSystem.LocalTime, m_SendTimeSystem.ServerTime);
|
|
||||||
m_SendSnapshot.NetworkUpdate(NetworkUpdateStage.EarlyUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AdvanceOneTickRecvSide()
|
|
||||||
{
|
|
||||||
m_RecvTimeSystem.Advance(1.0f / m_TicksPerSec);
|
|
||||||
m_RecvTickSystem.UpdateTick(m_RecvTimeSystem.LocalTime, m_RecvTimeSystem.ServerTime);
|
|
||||||
m_RecvSnapshot.NetworkUpdate(NetworkUpdateStage.EarlyUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AdvanceOneTick()
|
|
||||||
{
|
|
||||||
AdvanceOneTickSendSide();
|
|
||||||
AdvanceOneTickRecvSide();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int SpawnObject(SnapshotSpawnCommand command)
|
|
||||||
{
|
|
||||||
m_SpawnedObjectCount++;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int DespawnObject(SnapshotDespawnCommand command)
|
|
||||||
{
|
|
||||||
m_DespawnedObjectCount++;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int SendMessage(ref SnapshotDataMessage message, NetworkDelivery delivery, ulong clientId)
|
|
||||||
{
|
|
||||||
if (!m_PassBackResponses)
|
|
||||||
{
|
|
||||||
// we're not ack'ing anything, so those should stay 0
|
|
||||||
Debug.Assert(message.Ack.LastReceivedSequence == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Assert(message.Ack.ReceivedSequenceMask == 0);
|
|
||||||
Debug.Assert(message.Sequence == m_NextSequence); // sequence has to be the expected one
|
|
||||||
|
|
||||||
if (m_ExpectSpawns)
|
|
||||||
{
|
|
||||||
Debug.Assert(message.Spawns.Length >= m_MinSpawns); // there has to be multiple spawns per SnapshotMessage
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.Assert(message.Spawns.Length == 0); // Spawns were not expected
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_ExpectDespawns)
|
|
||||||
{
|
|
||||||
Debug.Assert(message.Despawns.Length >= m_MinDespawns); // there has to be multiple despawns per SnapshotMessage
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.Assert(message.Despawns.IsEmpty); // this test should not have despawns
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Assert(message.Entries.Length == 0);
|
|
||||||
|
|
||||||
m_NextSequence++;
|
|
||||||
|
|
||||||
if (!m_LoseNextMessage)
|
|
||||||
{
|
|
||||||
using var writer = new FastBufferWriter(1024, Allocator.Temp);
|
|
||||||
message.Serialize(writer);
|
|
||||||
using var reader = new FastBufferReader(writer, Allocator.Temp);
|
|
||||||
var context = new NetworkContext { SenderId = 0, Timestamp = 0.0f, SystemOwner = new Tuple<SnapshotSystem, ulong>(m_RecvSnapshot, 0) };
|
|
||||||
var newMessage = new SnapshotDataMessage();
|
|
||||||
newMessage.Deserialize(reader, ref context);
|
|
||||||
newMessage.Handle(ref context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int SendMessageRecvSide(ref SnapshotDataMessage message, NetworkDelivery delivery, ulong clientId)
|
|
||||||
{
|
|
||||||
if (m_PassBackResponses)
|
|
||||||
{
|
|
||||||
using var writer = new FastBufferWriter(1024, Allocator.Temp);
|
|
||||||
message.Serialize(writer);
|
|
||||||
using var reader = new FastBufferReader(writer, Allocator.Temp);
|
|
||||||
var context = new NetworkContext { SenderId = 0, Timestamp = 0.0f, SystemOwner = new Tuple<SnapshotSystem, ulong>(m_SendSnapshot, 1) };
|
|
||||||
var newMessage = new SnapshotDataMessage();
|
|
||||||
newMessage.Deserialize(reader, ref context);
|
|
||||||
newMessage.Handle(ref context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void PrepareSendSideSnapshot()
|
|
||||||
{
|
|
||||||
var config = new NetworkConfig();
|
|
||||||
|
|
||||||
m_SendTickSystem = new NetworkTickSystem(m_TicksPerSec, 0.0, 0.0);
|
|
||||||
m_SendTimeSystem = new NetworkTimeSystem(0.2, 0.2, 1.0);
|
|
||||||
|
|
||||||
config.UseSnapshotDelta = false;
|
|
||||||
config.UseSnapshotSpawn = true;
|
|
||||||
|
|
||||||
m_SendSnapshot = new SnapshotSystem(null, config, m_SendTickSystem);
|
|
||||||
|
|
||||||
m_SendSnapshot.IsServer = true;
|
|
||||||
m_SendSnapshot.IsConnectedClient = false;
|
|
||||||
m_SendSnapshot.ServerClientId = 0;
|
|
||||||
m_SendSnapshot.ConnectedClientsId.Clear();
|
|
||||||
m_SendSnapshot.ConnectedClientsId.Add(0);
|
|
||||||
m_SendSnapshot.ConnectedClientsId.Add(1);
|
|
||||||
m_SendSnapshot.MockSendMessage = SendMessage;
|
|
||||||
m_SendSnapshot.MockSpawnObject = SpawnObject;
|
|
||||||
m_SendSnapshot.MockDespawnObject = DespawnObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PrepareRecvSideSnapshot()
|
|
||||||
{
|
|
||||||
var config = new NetworkConfig();
|
|
||||||
|
|
||||||
m_RecvTickSystem = new NetworkTickSystem(m_TicksPerSec, 0.0, 0.0);
|
|
||||||
m_RecvTimeSystem = new NetworkTimeSystem(0.2, 0.2, 1.0);
|
|
||||||
|
|
||||||
config.UseSnapshotDelta = false;
|
|
||||||
config.UseSnapshotSpawn = true;
|
|
||||||
|
|
||||||
m_RecvSnapshot = new SnapshotSystem(null, config, m_RecvTickSystem);
|
|
||||||
|
|
||||||
m_RecvSnapshot.IsServer = false;
|
|
||||||
m_RecvSnapshot.IsConnectedClient = true;
|
|
||||||
m_RecvSnapshot.ServerClientId = 0;
|
|
||||||
m_RecvSnapshot.ConnectedClientsId.Clear();
|
|
||||||
m_SendSnapshot.ConnectedClientsId.Add(0);
|
|
||||||
m_SendSnapshot.ConnectedClientsId.Add(1);
|
|
||||||
m_RecvSnapshot.MockSendMessage = SendMessageRecvSide;
|
|
||||||
m_RecvSnapshot.MockSpawnObject = SpawnObject;
|
|
||||||
m_RecvSnapshot.MockDespawnObject = DespawnObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendSpawnToSnapshot(ulong objectId)
|
|
||||||
{
|
|
||||||
SnapshotSpawnCommand command = default;
|
|
||||||
// identity
|
|
||||||
command.NetworkObjectId = objectId;
|
|
||||||
// archetype
|
|
||||||
command.GlobalObjectIdHash = 0;
|
|
||||||
command.IsSceneObject = true;
|
|
||||||
// parameters
|
|
||||||
command.IsPlayerObject = false;
|
|
||||||
command.OwnerClientId = 0;
|
|
||||||
command.ParentNetworkId = 0;
|
|
||||||
command.ObjectPosition = default;
|
|
||||||
command.ObjectRotation = default;
|
|
||||||
command.ObjectScale = new Vector3(1.0f, 1.0f, 1.0f);
|
|
||||||
command.TargetClientIds = new List<ulong> { 1 };
|
|
||||||
m_SendSnapshot.Spawn(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendDespawnToSnapshot(ulong objectId)
|
|
||||||
{
|
|
||||||
SnapshotDespawnCommand command = default;
|
|
||||||
// identity
|
|
||||||
command.NetworkObjectId = objectId;
|
|
||||||
command.TargetClientIds = new List<ulong> { 1 };
|
|
||||||
m_SendSnapshot.Despawn(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestSnapshotSpawn()
|
|
||||||
{
|
|
||||||
Prepare();
|
|
||||||
|
|
||||||
m_SpawnedObjectCount = 0;
|
|
||||||
m_NextSequence = 0;
|
|
||||||
m_ExpectSpawns = true;
|
|
||||||
m_ExpectDespawns = false;
|
|
||||||
m_MinSpawns = 2; // many spawns are to be sent together
|
|
||||||
m_LoseNextMessage = false;
|
|
||||||
m_PassBackResponses = false;
|
|
||||||
|
|
||||||
var ticksToRun = 20;
|
|
||||||
|
|
||||||
// spawns one more than current buffer size
|
|
||||||
var objectsToSpawn = m_SendSnapshot.SpawnsBufferCount + 1;
|
|
||||||
|
|
||||||
for (int i = 0; i < objectsToSpawn; i++)
|
|
||||||
{
|
|
||||||
SendSpawnToSnapshot((ulong)i);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < ticksToRun; i++)
|
|
||||||
{
|
|
||||||
AdvanceOneTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Assert(m_SpawnedObjectCount == objectsToSpawn);
|
|
||||||
Debug.Assert(m_SendSnapshot.SpawnsBufferCount > objectsToSpawn); // spawn buffer should have grown
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestSnapshotSpawnDespawns()
|
|
||||||
{
|
|
||||||
Prepare();
|
|
||||||
|
|
||||||
// test that buffers actually shrink and will grow back to needed size
|
|
||||||
m_SendSnapshot.ReduceBufferUsage();
|
|
||||||
m_RecvSnapshot.ReduceBufferUsage();
|
|
||||||
Debug.Assert(m_SendSnapshot.SpawnsBufferCount == 1);
|
|
||||||
Debug.Assert(m_SendSnapshot.DespawnsBufferCount == 1);
|
|
||||||
Debug.Assert(m_RecvSnapshot.SpawnsBufferCount == 1);
|
|
||||||
Debug.Assert(m_RecvSnapshot.DespawnsBufferCount == 1);
|
|
||||||
|
|
||||||
m_SpawnedObjectCount = 0;
|
|
||||||
m_DespawnedObjectCount = 0;
|
|
||||||
|
|
||||||
m_NextSequence = 0;
|
|
||||||
m_ExpectSpawns = true;
|
|
||||||
m_ExpectDespawns = false;
|
|
||||||
m_MinDespawns = 2; // many despawns are to be sent together
|
|
||||||
m_LoseNextMessage = false;
|
|
||||||
m_PassBackResponses = false;
|
|
||||||
|
|
||||||
var ticksToRun = 20;
|
|
||||||
|
|
||||||
// spawns one more than current buffer size
|
|
||||||
var objectsToSpawn = 10;
|
|
||||||
|
|
||||||
for (int i = 0; i < objectsToSpawn; i++)
|
|
||||||
{
|
|
||||||
SendSpawnToSnapshot((ulong)i);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < ticksToRun; i++)
|
|
||||||
{
|
|
||||||
AdvanceOneTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < objectsToSpawn; i++)
|
|
||||||
{
|
|
||||||
SendDespawnToSnapshot((ulong)i);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ExpectSpawns = true; // the un'acked spawns will still be present
|
|
||||||
m_MinSpawns = 1; // but we don't really care how they are grouped then
|
|
||||||
m_ExpectDespawns = true;
|
|
||||||
|
|
||||||
for (int i = 0; i < ticksToRun; i++)
|
|
||||||
{
|
|
||||||
AdvanceOneTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Assert(m_DespawnedObjectCount == objectsToSpawn);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestSnapshotMessageLoss()
|
|
||||||
{
|
|
||||||
var r = new Random();
|
|
||||||
Prepare();
|
|
||||||
|
|
||||||
m_SpawnedObjectCount = 0;
|
|
||||||
m_NextSequence = 0;
|
|
||||||
m_ExpectSpawns = true;
|
|
||||||
m_ExpectDespawns = false;
|
|
||||||
m_MinSpawns = 1;
|
|
||||||
m_LoseNextMessage = false;
|
|
||||||
m_PassBackResponses = false;
|
|
||||||
|
|
||||||
var ticksToRun = 10;
|
|
||||||
|
|
||||||
for (int i = 0; i < ticksToRun; i++)
|
|
||||||
{
|
|
||||||
m_LoseNextMessage = (r.Next() % 2) > 0;
|
|
||||||
|
|
||||||
SendSpawnToSnapshot((ulong)i);
|
|
||||||
AdvanceOneTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_LoseNextMessage = false;
|
|
||||||
AdvanceOneTick();
|
|
||||||
AdvanceOneTick();
|
|
||||||
|
|
||||||
Debug.Assert(m_SpawnedObjectCount == ticksToRun);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestSnapshotAcks()
|
|
||||||
{
|
|
||||||
Prepare();
|
|
||||||
|
|
||||||
m_SpawnedObjectCount = 0;
|
|
||||||
m_NextSequence = 0;
|
|
||||||
m_ExpectSpawns = true;
|
|
||||||
m_ExpectDespawns = false;
|
|
||||||
m_MinSpawns = 1;
|
|
||||||
m_LoseNextMessage = false;
|
|
||||||
m_PassBackResponses = true;
|
|
||||||
|
|
||||||
var objectsToSpawn = 10;
|
|
||||||
|
|
||||||
for (int i = 0; i < objectsToSpawn; i++)
|
|
||||||
{
|
|
||||||
SendSpawnToSnapshot((ulong)i);
|
|
||||||
}
|
|
||||||
AdvanceOneTickSendSide(); // let's tick the send multiple time, to check it still tries to send
|
|
||||||
AdvanceOneTick();
|
|
||||||
|
|
||||||
m_ExpectSpawns = false; // all spawns should have made it back and forth and be absent from next messages
|
|
||||||
AdvanceOneTick();
|
|
||||||
|
|
||||||
for (int i = 0; i < objectsToSpawn; i++)
|
|
||||||
{
|
|
||||||
SendDespawnToSnapshot((ulong)i);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ExpectDespawns = true; // we should now be seeing despawns
|
|
||||||
AdvanceOneTickSendSide(); // let's tick the send multiple time, to check it still tries to send
|
|
||||||
AdvanceOneTick();
|
|
||||||
|
|
||||||
Debug.Assert(m_SpawnedObjectCount == objectsToSpawn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 3d41788be1de34b7c8bcfce6a2877754
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
8
Tests/Editor/Transports.meta
Normal file
8
Tests/Editor/Transports.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f4ecf3bb8c5654c1aae7f73d21e8c56e
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
193
Tests/Editor/Transports/BatchedReceiveQueueTests.cs
Normal file
193
Tests/Editor/Transports/BatchedReceiveQueueTests.cs
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
using System;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Netcode.Transports.UTP;
|
||||||
|
using Unity.Networking.Transport;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.EditorTests
|
||||||
|
{
|
||||||
|
public class BatchedReceiveQueueTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void BatchedReceiveQueue_EmptyReader()
|
||||||
|
{
|
||||||
|
var data = new NativeArray<byte>(0, Allocator.Temp);
|
||||||
|
|
||||||
|
var reader = new DataStreamReader(data);
|
||||||
|
var q = new BatchedReceiveQueue(reader);
|
||||||
|
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
|
||||||
|
Assert.True(q.IsEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedReceiveQueue_SingleMessage()
|
||||||
|
{
|
||||||
|
var dataLength = sizeof(int) + 1;
|
||||||
|
|
||||||
|
var data = new NativeArray<byte>(dataLength, Allocator.Temp);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
writer.WriteInt(1);
|
||||||
|
writer.WriteByte((byte)42);
|
||||||
|
|
||||||
|
var reader = new DataStreamReader(data);
|
||||||
|
var q = new BatchedReceiveQueue(reader);
|
||||||
|
|
||||||
|
Assert.False(q.IsEmpty);
|
||||||
|
|
||||||
|
var message = q.PopMessage();
|
||||||
|
Assert.AreEqual(1, message.Count);
|
||||||
|
Assert.AreEqual((byte)42, message.Array[message.Offset]);
|
||||||
|
|
||||||
|
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
|
||||||
|
Assert.True(q.IsEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedReceiveQueue_MultipleMessages()
|
||||||
|
{
|
||||||
|
var dataLength = (sizeof(int) + 1) * 2;
|
||||||
|
|
||||||
|
var data = new NativeArray<byte>(dataLength, Allocator.Temp);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
writer.WriteInt(1);
|
||||||
|
writer.WriteByte((byte)42);
|
||||||
|
writer.WriteInt(1);
|
||||||
|
writer.WriteByte((byte)142);
|
||||||
|
|
||||||
|
var reader = new DataStreamReader(data);
|
||||||
|
var q = new BatchedReceiveQueue(reader);
|
||||||
|
|
||||||
|
Assert.False(q.IsEmpty);
|
||||||
|
|
||||||
|
var message1 = q.PopMessage();
|
||||||
|
Assert.AreEqual(1, message1.Count);
|
||||||
|
Assert.AreEqual((byte)42, message1.Array[message1.Offset]);
|
||||||
|
|
||||||
|
var message2 = q.PopMessage();
|
||||||
|
Assert.AreEqual(1, message2.Count);
|
||||||
|
Assert.AreEqual((byte)142, message2.Array[message2.Offset]);
|
||||||
|
|
||||||
|
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
|
||||||
|
Assert.True(q.IsEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedReceiveQueue_PartialMessage()
|
||||||
|
{
|
||||||
|
var dataLength = sizeof(int);
|
||||||
|
|
||||||
|
var data = new NativeArray<byte>(dataLength, Allocator.Temp);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
writer.WriteInt(42);
|
||||||
|
|
||||||
|
var reader = new DataStreamReader(data);
|
||||||
|
var q = new BatchedReceiveQueue(reader);
|
||||||
|
|
||||||
|
Assert.False(q.IsEmpty);
|
||||||
|
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedReceiveQueue_PushReader_ToFilledQueue()
|
||||||
|
{
|
||||||
|
var data1Length = sizeof(int);
|
||||||
|
var data2Length = sizeof(byte);
|
||||||
|
|
||||||
|
var data1 = new NativeArray<byte>(data1Length, Allocator.Temp);
|
||||||
|
var data2 = new NativeArray<byte>(data2Length, Allocator.Temp);
|
||||||
|
|
||||||
|
var writer1 = new DataStreamWriter(data1);
|
||||||
|
writer1.WriteInt(1);
|
||||||
|
var writer2 = new DataStreamWriter(data2);
|
||||||
|
writer2.WriteByte(42);
|
||||||
|
|
||||||
|
var reader1 = new DataStreamReader(data1);
|
||||||
|
var reader2 = new DataStreamReader(data2);
|
||||||
|
|
||||||
|
var q = new BatchedReceiveQueue(reader1);
|
||||||
|
|
||||||
|
Assert.False(q.IsEmpty);
|
||||||
|
|
||||||
|
q.PushReader(reader2);
|
||||||
|
|
||||||
|
Assert.False(q.IsEmpty);
|
||||||
|
|
||||||
|
var message = q.PopMessage();
|
||||||
|
Assert.AreEqual(1, message.Count);
|
||||||
|
Assert.AreEqual((byte)42, message.Array[message.Offset]);
|
||||||
|
|
||||||
|
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
|
||||||
|
Assert.True(q.IsEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedReceiveQueue_PushReader_ToPartiallyFilledQueue()
|
||||||
|
{
|
||||||
|
var dataLength = sizeof(int) + 1;
|
||||||
|
|
||||||
|
var data = new NativeArray<byte>(dataLength, Allocator.Temp);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
writer.WriteInt(1);
|
||||||
|
writer.WriteByte((byte)42);
|
||||||
|
|
||||||
|
var reader = new DataStreamReader(data);
|
||||||
|
var q = new BatchedReceiveQueue(reader);
|
||||||
|
|
||||||
|
reader = new DataStreamReader(data);
|
||||||
|
q.PushReader(reader);
|
||||||
|
|
||||||
|
var message = q.PopMessage();
|
||||||
|
Assert.AreEqual(1, message.Count);
|
||||||
|
Assert.AreEqual((byte)42, message.Array[message.Offset]);
|
||||||
|
|
||||||
|
reader = new DataStreamReader(data);
|
||||||
|
q.PushReader(reader);
|
||||||
|
|
||||||
|
message = q.PopMessage();
|
||||||
|
Assert.AreEqual(1, message.Count);
|
||||||
|
Assert.AreEqual((byte)42, message.Array[message.Offset]);
|
||||||
|
|
||||||
|
message = q.PopMessage();
|
||||||
|
Assert.AreEqual(1, message.Count);
|
||||||
|
Assert.AreEqual((byte)42, message.Array[message.Offset]);
|
||||||
|
|
||||||
|
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
|
||||||
|
Assert.True(q.IsEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedReceiveQueue_PushReader_ToEmptyQueue()
|
||||||
|
{
|
||||||
|
var dataLength = sizeof(int) + 1;
|
||||||
|
|
||||||
|
var data = new NativeArray<byte>(dataLength, Allocator.Temp);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
writer.WriteInt(1);
|
||||||
|
writer.WriteByte((byte)42);
|
||||||
|
|
||||||
|
var reader = new DataStreamReader(data);
|
||||||
|
var q = new BatchedReceiveQueue(reader);
|
||||||
|
|
||||||
|
Assert.False(q.IsEmpty);
|
||||||
|
|
||||||
|
q.PopMessage();
|
||||||
|
|
||||||
|
Assert.True(q.IsEmpty);
|
||||||
|
|
||||||
|
reader = new DataStreamReader(data);
|
||||||
|
q.PushReader(reader);
|
||||||
|
|
||||||
|
var message = q.PopMessage();
|
||||||
|
Assert.AreEqual(1, message.Count);
|
||||||
|
Assert.AreEqual((byte)42, message.Array[message.Offset]);
|
||||||
|
|
||||||
|
Assert.AreEqual(default(ArraySegment<byte>), q.PopMessage());
|
||||||
|
Assert.True(q.IsEmpty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Tests/Editor/Transports/BatchedReceiveQueueTests.cs.meta
Normal file
11
Tests/Editor/Transports/BatchedReceiveQueueTests.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aabb21b30a80142ea86e59d1b4d5c587
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
266
Tests/Editor/Transports/BatchedSendQueueTests.cs
Normal file
266
Tests/Editor/Transports/BatchedSendQueueTests.cs
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
using System;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Netcode.Transports.UTP;
|
||||||
|
using Unity.Networking.Transport;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.EditorTests
|
||||||
|
{
|
||||||
|
public class BatchedSendQueueTests
|
||||||
|
{
|
||||||
|
private const int k_TestQueueCapacity = 1024;
|
||||||
|
private const int k_TestMessageSize = 42;
|
||||||
|
|
||||||
|
private ArraySegment<byte> m_TestMessage;
|
||||||
|
|
||||||
|
private void AssertIsTestMessage(NativeArray<byte> data)
|
||||||
|
{
|
||||||
|
var reader = new DataStreamReader(data);
|
||||||
|
Assert.AreEqual(k_TestMessageSize, reader.ReadInt());
|
||||||
|
for (int i = 0; i < k_TestMessageSize; i++)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(m_TestMessage.Array[i], reader.ReadByte());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[OneTimeSetUp]
|
||||||
|
public void InitializeTestMessage()
|
||||||
|
{
|
||||||
|
var data = new byte[k_TestMessageSize];
|
||||||
|
for (int i = 0; i < k_TestMessageSize; i++)
|
||||||
|
{
|
||||||
|
data[i] = (byte)i;
|
||||||
|
}
|
||||||
|
m_TestMessage = new ArraySegment<byte>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_EmptyOnCreation()
|
||||||
|
{
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
|
||||||
|
Assert.AreEqual(0, q.Length);
|
||||||
|
Assert.True(q.IsEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_NotCreatedAfterDispose()
|
||||||
|
{
|
||||||
|
var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
q.Dispose();
|
||||||
|
Assert.False(q.IsCreated);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_PushMessageReturnValue()
|
||||||
|
{
|
||||||
|
// Will fit a single test message, but not two (with overhead included).
|
||||||
|
var queueCapacity = (k_TestMessageSize * 2) + BatchedSendQueue.PerMessageOverhead;
|
||||||
|
|
||||||
|
using var q = new BatchedSendQueue(queueCapacity);
|
||||||
|
|
||||||
|
Assert.True(q.PushMessage(m_TestMessage));
|
||||||
|
Assert.False(q.PushMessage(m_TestMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_LengthIncreasedAfterPush()
|
||||||
|
{
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
Assert.AreEqual(k_TestMessageSize + BatchedSendQueue.PerMessageOverhead, q.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_PushedMessageGeneratesCopy()
|
||||||
|
{
|
||||||
|
var messageLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
|
||||||
|
var queueCapacity = messageLength * 2;
|
||||||
|
|
||||||
|
using var q = new BatchedSendQueue(queueCapacity);
|
||||||
|
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
q.Consume(messageLength);
|
||||||
|
Assert.IsTrue(q.PushMessage(m_TestMessage));
|
||||||
|
Assert.AreEqual(queueCapacity, q.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_FillWriterWithMessages_ReturnValue()
|
||||||
|
{
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
var filled = q.FillWriterWithMessages(ref writer);
|
||||||
|
Assert.AreEqual(k_TestMessageSize + BatchedSendQueue.PerMessageOverhead, filled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_FillWriterWithMessages_NoopIfNoPushedMessages()
|
||||||
|
{
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
Assert.AreEqual(0, q.FillWriterWithMessages(ref writer));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_FillWriterWithMessages_NoopIfNotEnoughCapacity()
|
||||||
|
{
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
using var data = new NativeArray<byte>(2, Allocator.Temp);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
Assert.AreEqual(0, q.FillWriterWithMessages(ref writer));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_FillWriterWithMessages_SinglePushedMessage()
|
||||||
|
{
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
q.FillWriterWithMessages(ref writer);
|
||||||
|
AssertIsTestMessage(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_FillWriterWithMessages_MultiplePushedMessages()
|
||||||
|
{
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
q.FillWriterWithMessages(ref writer);
|
||||||
|
|
||||||
|
var messageLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
|
||||||
|
AssertIsTestMessage(data);
|
||||||
|
AssertIsTestMessage(data.GetSubArray(messageLength, messageLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_FillWriterWithMessages_PartialPushedMessages()
|
||||||
|
{
|
||||||
|
var messageLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
|
||||||
|
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
using var data = new NativeArray<byte>(messageLength, Allocator.Temp);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
Assert.AreEqual(messageLength, q.FillWriterWithMessages(ref writer));
|
||||||
|
AssertIsTestMessage(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_FillWriterWithBytes_NoopIfNoData()
|
||||||
|
{
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
Assert.AreEqual(0, q.FillWriterWithBytes(ref writer));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_FillWriterWithBytes_WriterCapacityMoreThanLength()
|
||||||
|
{
|
||||||
|
var dataLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
|
||||||
|
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
using var data = new NativeArray<byte>(k_TestQueueCapacity, Allocator.Temp);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
Assert.AreEqual(dataLength, q.FillWriterWithBytes(ref writer));
|
||||||
|
AssertIsTestMessage(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_FillWriterWithBytes_WriterCapacityLessThanLength()
|
||||||
|
{
|
||||||
|
var dataLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
|
||||||
|
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
using var data = new NativeArray<byte>(dataLength, Allocator.Temp);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
Assert.AreEqual(dataLength, q.FillWriterWithBytes(ref writer));
|
||||||
|
AssertIsTestMessage(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_FillWriterWithBytes_WriterCapacityEqualToLength()
|
||||||
|
{
|
||||||
|
var dataLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
|
||||||
|
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
using var data = new NativeArray<byte>(dataLength, Allocator.Temp);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
var writer = new DataStreamWriter(data);
|
||||||
|
Assert.AreEqual(dataLength, q.FillWriterWithBytes(ref writer));
|
||||||
|
AssertIsTestMessage(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_ConsumeLessThanLength()
|
||||||
|
{
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
var messageLength = k_TestMessageSize + BatchedSendQueue.PerMessageOverhead;
|
||||||
|
q.Consume(messageLength);
|
||||||
|
Assert.AreEqual(messageLength, q.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_ConsumeExactLength()
|
||||||
|
{
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
q.Consume(k_TestMessageSize + BatchedSendQueue.PerMessageOverhead);
|
||||||
|
Assert.AreEqual(0, q.Length);
|
||||||
|
Assert.True(q.IsEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BatchedSendQueue_ConsumeMoreThanLength()
|
||||||
|
{
|
||||||
|
using var q = new BatchedSendQueue(k_TestQueueCapacity);
|
||||||
|
|
||||||
|
q.PushMessage(m_TestMessage);
|
||||||
|
|
||||||
|
q.Consume(k_TestQueueCapacity);
|
||||||
|
Assert.AreEqual(0, q.Length);
|
||||||
|
Assert.True(q.IsEmpty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Tests/Editor/Transports/BatchedSendQueueTests.cs.meta
Normal file
11
Tests/Editor/Transports/BatchedSendQueueTests.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 51a68dc80bf18443180f3600eb5890d7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
84
Tests/Editor/Transports/UnityTransportTests.cs
Normal file
84
Tests/Editor/Transports/UnityTransportTests.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using Unity.Netcode.Transports.UTP;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.EditorTests
|
||||||
|
{
|
||||||
|
public class UnityTransportTests
|
||||||
|
{
|
||||||
|
// Check that starting a server doesn't immediately result in faulted tasks.
|
||||||
|
[Test]
|
||||||
|
public void BasicInitServer()
|
||||||
|
{
|
||||||
|
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
|
||||||
|
transport.Initialize();
|
||||||
|
|
||||||
|
Assert.True(transport.StartServer());
|
||||||
|
|
||||||
|
transport.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that starting a client doesn't immediately result in faulted tasks.
|
||||||
|
[Test]
|
||||||
|
public void BasicInitClient()
|
||||||
|
{
|
||||||
|
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
|
||||||
|
transport.Initialize();
|
||||||
|
|
||||||
|
Assert.True(transport.StartClient());
|
||||||
|
|
||||||
|
transport.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we can't restart a server.
|
||||||
|
[Test]
|
||||||
|
public void NoRestartServer()
|
||||||
|
{
|
||||||
|
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
|
||||||
|
transport.Initialize();
|
||||||
|
|
||||||
|
transport.StartServer();
|
||||||
|
Assert.False(transport.StartServer());
|
||||||
|
|
||||||
|
transport.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we can't restart a client.
|
||||||
|
[Test]
|
||||||
|
public void NoRestartClient()
|
||||||
|
{
|
||||||
|
UnityTransport transport = new GameObject().AddComponent<UnityTransport>();
|
||||||
|
transport.Initialize();
|
||||||
|
|
||||||
|
transport.StartClient();
|
||||||
|
Assert.False(transport.StartClient());
|
||||||
|
|
||||||
|
transport.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we can't start both a server and client on the same transport.
|
||||||
|
[Test]
|
||||||
|
public void NotBothServerAndClient()
|
||||||
|
{
|
||||||
|
UnityTransport transport;
|
||||||
|
|
||||||
|
// Start server then client.
|
||||||
|
transport = new GameObject().AddComponent<UnityTransport>();
|
||||||
|
transport.Initialize();
|
||||||
|
|
||||||
|
transport.StartServer();
|
||||||
|
Assert.False(transport.StartClient());
|
||||||
|
|
||||||
|
transport.Shutdown();
|
||||||
|
|
||||||
|
// Start client then server.
|
||||||
|
transport = new GameObject().AddComponent<UnityTransport>();
|
||||||
|
transport.Initialize();
|
||||||
|
|
||||||
|
transport.StartClient();
|
||||||
|
Assert.False(transport.StartServer());
|
||||||
|
|
||||||
|
transport.Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Tests/Editor/Transports/UnityTransportTests.cs.meta
Normal file
11
Tests/Editor/Transports/UnityTransportTests.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1b0137a26ef0140f0bf5167c09eecb96
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
"Unity.Multiplayer.MetricTypes",
|
"Unity.Multiplayer.MetricTypes",
|
||||||
"Unity.Multiplayer.NetStats",
|
"Unity.Multiplayer.NetStats",
|
||||||
"Unity.Multiplayer.Tools.MetricTypes",
|
"Unity.Multiplayer.Tools.MetricTypes",
|
||||||
"Unity.Multiplayer.Tools.NetStats"
|
"Unity.Multiplayer.Tools.NetStats",
|
||||||
|
"Unity.Networking.Transport"
|
||||||
],
|
],
|
||||||
"optionalUnityReferences": [
|
"optionalUnityReferences": [
|
||||||
"TestAssemblies"
|
"TestAssemblies"
|
||||||
|
|||||||
78
Tests/Runtime/ClientOnlyConnectionTests.cs
Normal file
78
Tests/Runtime/ClientOnlyConnectionTests.cs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
using Unity.Netcode.TestHelpers.Runtime;
|
||||||
|
#if UNITY_UNET_PRESENT
|
||||||
|
using Unity.Netcode.Transports.UNET;
|
||||||
|
#else
|
||||||
|
using Unity.Netcode.Transports.UTP;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Unity.Netcode.RuntimeTests
|
||||||
|
{
|
||||||
|
public class ClientOnlyConnectionTests
|
||||||
|
{
|
||||||
|
private NetworkManager m_ClientNetworkManager;
|
||||||
|
private GameObject m_NetworkManagerGameObject;
|
||||||
|
private WaitForSeconds m_DefaultWaitForTick = new WaitForSeconds(1.0f / 30);
|
||||||
|
private bool m_WasDisconnected;
|
||||||
|
private TimeoutHelper m_TimeoutHelper;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
m_WasDisconnected = false;
|
||||||
|
m_NetworkManagerGameObject = new GameObject();
|
||||||
|
m_ClientNetworkManager = m_NetworkManagerGameObject.AddComponent<NetworkManager>();
|
||||||
|
m_ClientNetworkManager.NetworkConfig = new NetworkConfig();
|
||||||
|
#if UNITY_UNET_PRESENT
|
||||||
|
m_TimeoutHelper = new TimeoutHelper(30);
|
||||||
|
m_ClientNetworkManager.NetworkConfig.NetworkTransport = m_NetworkManagerGameObject.AddComponent<UNetTransport>();
|
||||||
|
#else
|
||||||
|
// Default is 1000ms per connection attempt and 60 connection attempts (60s)
|
||||||
|
// Currently there is no easy way to set these values other than in-editor
|
||||||
|
m_TimeoutHelper = new TimeoutHelper(70);
|
||||||
|
m_ClientNetworkManager.NetworkConfig.NetworkTransport = m_NetworkManagerGameObject.AddComponent<UnityTransport>();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator ClientFailsToConnect()
|
||||||
|
{
|
||||||
|
// Wait for the disconnected event
|
||||||
|
m_ClientNetworkManager.OnClientDisconnectCallback += ClientNetworkManager_OnClientDisconnectCallback;
|
||||||
|
|
||||||
|
// Only start the client (so it will timeout)
|
||||||
|
m_ClientNetworkManager.StartClient();
|
||||||
|
|
||||||
|
#if !UNITY_UNET_PRESENT
|
||||||
|
// Unity Transport throws an error when it times out
|
||||||
|
LogAssert.Expect(LogType.Error, "Failed to connect to server.");
|
||||||
|
#endif
|
||||||
|
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => m_WasDisconnected, m_TimeoutHelper);
|
||||||
|
Assert.False(m_TimeoutHelper.TimedOut, "Timed out waiting for client to timeout waiting to connect!");
|
||||||
|
|
||||||
|
// Shutdown the client
|
||||||
|
m_ClientNetworkManager.Shutdown();
|
||||||
|
|
||||||
|
// Wait for a tick
|
||||||
|
yield return m_DefaultWaitForTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClientNetworkManager_OnClientDisconnectCallback(ulong clientId)
|
||||||
|
{
|
||||||
|
m_WasDisconnected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TearDown]
|
||||||
|
public void TearDown()
|
||||||
|
{
|
||||||
|
if (m_NetworkManagerGameObject != null)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(m_NetworkManagerGameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
11
Tests/Runtime/ClientOnlyConnectionTests.cs.meta
Normal file
11
Tests/Runtime/ClientOnlyConnectionTests.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 639fe2161ab25c54f94ce6eb991e668a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -103,46 +103,6 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
m_NetworkVariableUInt = new NetworkVariable<uint>(1);
|
m_NetworkVariableUInt = new NetworkVariable<uint>(1);
|
||||||
m_NetworkVariableUShort = new NetworkVariable<ushort>(1);
|
m_NetworkVariableUShort = new NetworkVariable<ushort>(1);
|
||||||
|
|
||||||
m_NetworkVariableBool = new NetworkVariable<bool>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableByte = new NetworkVariable<byte>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableColor = new NetworkVariable<Color>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableColor32 = new NetworkVariable<Color32>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableDouble = new NetworkVariable<double>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableFloat = new NetworkVariable<float>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableInt = new NetworkVariable<int>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableLong = new NetworkVariable<long>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableSByte = new NetworkVariable<sbyte>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableQuaternion = new NetworkVariable<Quaternion>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableShort = new NetworkVariable<short>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableVector4 = new NetworkVariable<Vector4>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableVector3 = new NetworkVariable<Vector3>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableVector2 = new NetworkVariable<Vector2>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableRay = new NetworkVariable<Ray>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableULong = new NetworkVariable<ulong>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableUInt = new NetworkVariable<uint>(NetworkVariableReadPermission.Everyone);
|
|
||||||
m_NetworkVariableUShort = new NetworkVariable<ushort>(NetworkVariableReadPermission.Everyone);
|
|
||||||
|
|
||||||
|
|
||||||
// NetworkVariable Value Type and NetworkVariableSettings Constructor Test Coverage
|
|
||||||
m_NetworkVariableBool = new NetworkVariable<bool>(NetworkVariableReadPermission.Everyone, true);
|
|
||||||
m_NetworkVariableByte = new NetworkVariable<byte>(NetworkVariableReadPermission.Everyone, 0);
|
|
||||||
m_NetworkVariableColor = new NetworkVariable<Color>(NetworkVariableReadPermission.Everyone, new Color(1, 1, 1, 1));
|
|
||||||
m_NetworkVariableColor32 = new NetworkVariable<Color32>(NetworkVariableReadPermission.Everyone, new Color32(1, 1, 1, 1));
|
|
||||||
m_NetworkVariableDouble = new NetworkVariable<double>(NetworkVariableReadPermission.Everyone, 1.0);
|
|
||||||
m_NetworkVariableFloat = new NetworkVariable<float>(NetworkVariableReadPermission.Everyone, 1.0f);
|
|
||||||
m_NetworkVariableInt = new NetworkVariable<int>(NetworkVariableReadPermission.Everyone, 1);
|
|
||||||
m_NetworkVariableLong = new NetworkVariable<long>(NetworkVariableReadPermission.Everyone, 1);
|
|
||||||
m_NetworkVariableSByte = new NetworkVariable<sbyte>(NetworkVariableReadPermission.Everyone, 0);
|
|
||||||
m_NetworkVariableQuaternion = new NetworkVariable<Quaternion>(NetworkVariableReadPermission.Everyone, Quaternion.identity);
|
|
||||||
m_NetworkVariableShort = new NetworkVariable<short>(NetworkVariableReadPermission.Everyone, 1);
|
|
||||||
m_NetworkVariableVector4 = new NetworkVariable<Vector4>(NetworkVariableReadPermission.Everyone, new Vector4(1, 1, 1, 1));
|
|
||||||
m_NetworkVariableVector3 = new NetworkVariable<Vector3>(NetworkVariableReadPermission.Everyone, new Vector3(1, 1, 1));
|
|
||||||
m_NetworkVariableVector2 = new NetworkVariable<Vector2>(NetworkVariableReadPermission.Everyone, new Vector2(1, 1));
|
|
||||||
m_NetworkVariableRay = new NetworkVariable<Ray>(NetworkVariableReadPermission.Everyone, new Ray());
|
|
||||||
m_NetworkVariableULong = new NetworkVariable<ulong>(NetworkVariableReadPermission.Everyone, 1);
|
|
||||||
m_NetworkVariableUInt = new NetworkVariable<uint>(NetworkVariableReadPermission.Everyone, 1);
|
|
||||||
m_NetworkVariableUShort = new NetworkVariable<ushort>(NetworkVariableReadPermission.Everyone, 1);
|
|
||||||
|
|
||||||
// Use this nifty class: NetworkVariableHelper
|
// Use this nifty class: NetworkVariableHelper
|
||||||
// Tracks if NetworkVariable changed invokes the OnValueChanged callback for the given instance type
|
// Tracks if NetworkVariable changed invokes the OnValueChanged callback for the given instance type
|
||||||
Bool_Var = new NetworkVariableHelper<bool>(m_NetworkVariableBool);
|
Bool_Var = new NetworkVariableHelper<bool>(m_NetworkVariableBool);
|
||||||
@@ -193,7 +153,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
{
|
{
|
||||||
if (EnableTesting)
|
if (EnableTesting)
|
||||||
{
|
{
|
||||||
//Added timeout functionality for near future changes to NetworkVariables and the Snapshot system
|
//Added timeout functionality for near future changes to NetworkVariables
|
||||||
if (!m_FinishedTests && m_ChangesAppliedToNetworkVariables)
|
if (!m_FinishedTests && m_ChangesAppliedToNetworkVariables)
|
||||||
{
|
{
|
||||||
//We finish testing if all NetworkVariables changed their value or we timed out waiting for
|
//We finish testing if all NetworkVariables changed their value or we timed out waiting for
|
||||||
|
|||||||
69
Tests/Runtime/Metrics/ConnectionMetricsTests.cs
Normal file
69
Tests/Runtime/Metrics/ConnectionMetricsTests.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#if MULTIPLAYER_TOOLS
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Unity.Multiplayer.Tools.MetricTypes;
|
||||||
|
using Unity.Netcode.TestHelpers.Runtime;
|
||||||
|
using Unity.Netcode.TestHelpers.Runtime.Metrics;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.RuntimeTests.Metrics
|
||||||
|
{
|
||||||
|
[TestFixture(ClientCount.OneClient, HostOrServer.Host)]
|
||||||
|
[TestFixture(ClientCount.TwoClients, HostOrServer.Host)]
|
||||||
|
[TestFixture(ClientCount.OneClient, HostOrServer.Server)]
|
||||||
|
[TestFixture(ClientCount.TwoClients, HostOrServer.Server)]
|
||||||
|
public class ConnectionMetricsTests : NetcodeIntegrationTest
|
||||||
|
{
|
||||||
|
protected override int NumberOfClients => m_ClientCount;
|
||||||
|
|
||||||
|
private int m_ClientCount;
|
||||||
|
|
||||||
|
public enum ClientCount
|
||||||
|
{
|
||||||
|
OneClient = 1,
|
||||||
|
TwoClients,
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionMetricsTests(ClientCount clientCount, HostOrServer hostOrServer)
|
||||||
|
: base(hostOrServer)
|
||||||
|
{
|
||||||
|
m_ClientCount = (int)clientCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetClientCountForFixture()
|
||||||
|
{
|
||||||
|
return m_ClientCount + ((m_UseHost) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator UpdateConnectionCountOnServer()
|
||||||
|
{
|
||||||
|
var waitForGaugeValues = new WaitForGaugeMetricValues((m_ServerNetworkManager.NetworkMetrics as NetworkMetrics).Dispatcher, NetworkMetricTypes.ConnectedClients);
|
||||||
|
|
||||||
|
yield return waitForGaugeValues.WaitForMetricsReceived();
|
||||||
|
|
||||||
|
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
|
||||||
|
Assert.AreEqual(GetClientCountForFixture(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator UpdateConnectionCountOnClient()
|
||||||
|
{
|
||||||
|
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
||||||
|
{
|
||||||
|
var waitForGaugeValues = new WaitForGaugeMetricValues((clientNetworkManager.NetworkMetrics as NetworkMetrics).Dispatcher, NetworkMetricTypes.ConnectedClients);
|
||||||
|
|
||||||
|
yield return waitForGaugeValues.WaitForMetricsReceived();
|
||||||
|
|
||||||
|
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
|
||||||
|
Assert.AreEqual(1, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
3
Tests/Runtime/Metrics/ConnectionMetricsTests.cs.meta
Normal file
3
Tests/Runtime/Metrics/ConnectionMetricsTests.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1845aef61dbb4f2b9d2be9145262ab90
|
||||||
|
timeCreated: 1647023529
|
||||||
@@ -39,7 +39,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
|
|
||||||
var networkMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
|
var networkMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
|
||||||
|
|
||||||
// We should have 1 NamedMessage and some potential SnapshotMessage
|
// We should have 1 NamedMessage
|
||||||
Assert.That(networkMessageSentMetricValues, Has.Exactly(1).Matches<NetworkMessageEvent>(x => x.Name == nameof(NamedMessage)));
|
Assert.That(networkMessageSentMetricValues, Has.Exactly(1).Matches<NetworkMessageEvent>(x => x.Name == nameof(NamedMessage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
yield return waitForMetricValues.WaitForMetricsReceived();
|
yield return waitForMetricValues.WaitForMetricsReceived();
|
||||||
|
|
||||||
var networkMessageReceivedValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
|
var networkMessageReceivedValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
|
||||||
// We should have 1 NamedMessage and some potential SnapshotMessage
|
// We should have 1 NamedMessage
|
||||||
Assert.That(networkMessageReceivedValues, Has.Exactly(1).Matches<NetworkMessageEvent>(x => x.Name == nameof(NamedMessage)));
|
Assert.That(networkMessageReceivedValues, Has.Exactly(1).Matches<NetworkMessageEvent>(x => x.Name == nameof(NamedMessage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,6 +189,76 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
Assert.AreEqual(1, objectDestroyedSentMetricValues.Select(x => x.BytesCount).Distinct().Count());
|
Assert.AreEqual(1, objectDestroyedSentMetricValues.Select(x => x.BytesCount).Distinct().Count());
|
||||||
Assert.That(objectDestroyedSentMetricValues.Select(x => x.BytesCount), Has.All.Not.EqualTo(0));
|
Assert.That(objectDestroyedSentMetricValues.Select(x => x.BytesCount), Has.All.Not.EqualTo(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator TrackNetworkObjectCountAfterSpawnOnServer()
|
||||||
|
{
|
||||||
|
SpawnNetworkObject();
|
||||||
|
|
||||||
|
var waitForGaugeValues = new WaitForGaugeMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects);
|
||||||
|
|
||||||
|
yield return s_DefaultWaitForTick;
|
||||||
|
yield return waitForGaugeValues.WaitForMetricsReceived();
|
||||||
|
|
||||||
|
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
|
||||||
|
Assert.AreEqual(3, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator TrackNetworkObjectCountAfterSpawnOnClient()
|
||||||
|
{
|
||||||
|
SpawnNetworkObject();
|
||||||
|
|
||||||
|
//By default, we have 2 network objects
|
||||||
|
//There's a slight delay between the spawn on the server and the spawn on the client
|
||||||
|
//We want to have metrics when the value is different than the 2 default one to confirm the client has the new value
|
||||||
|
var waitForGaugeValues = new WaitForGaugeMetricValues(ClientMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects, metric => (int)metric != 2);
|
||||||
|
|
||||||
|
yield return waitForGaugeValues.WaitForMetricsReceived();
|
||||||
|
|
||||||
|
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
|
||||||
|
Assert.AreEqual(3, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator TrackNetworkObjectCountAfterDespawnOnServer()
|
||||||
|
{
|
||||||
|
var objectList = Server.SpawnManager.SpawnedObjectsList;
|
||||||
|
for (int i = objectList.Count - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
objectList.ElementAt(i).Despawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
var waitForGaugeValues = new WaitForGaugeMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects);
|
||||||
|
|
||||||
|
yield return s_DefaultWaitForTick;
|
||||||
|
yield return waitForGaugeValues.WaitForMetricsReceived();
|
||||||
|
|
||||||
|
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
|
||||||
|
Assert.AreEqual(0, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator TrackNetworkObjectCountAfterDespawnOnClient()
|
||||||
|
{
|
||||||
|
var objectList = Server.SpawnManager.SpawnedObjectsList;
|
||||||
|
for (int i = objectList.Count - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
objectList.ElementAt(i).Despawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
//By default, we have 2 network objects
|
||||||
|
//There's a slight delay between the spawn on the server and the spawn on the client
|
||||||
|
//We want to have metrics when the value is different than the 2 default one to confirm the client has the new value
|
||||||
|
var waitForGaugeValues = new WaitForGaugeMetricValues(ClientMetrics.Dispatcher, NetworkMetricTypes.NetworkObjects, metric => (int)metric != 2);
|
||||||
|
|
||||||
|
yield return waitForGaugeValues.WaitForMetricsReceived();
|
||||||
|
|
||||||
|
var value = waitForGaugeValues.AssertMetricValueHaveBeenFound();
|
||||||
|
Assert.AreEqual(0, value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
89
Tests/Runtime/Metrics/PacketLossMetricsTests.cs
Normal file
89
Tests/Runtime/Metrics/PacketLossMetricsTests.cs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#if MULTIPLAYER_TOOLS
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Multiplayer.Tools.MetricTypes;
|
||||||
|
using Unity.Netcode.TestHelpers.Runtime;
|
||||||
|
using Unity.Netcode.TestHelpers.Runtime.Metrics;
|
||||||
|
using Unity.Netcode.Transports.UTP;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.RuntimeTests.Metrics
|
||||||
|
{
|
||||||
|
public class PacketLossMetricsTests : NetcodeIntegrationTest
|
||||||
|
{
|
||||||
|
protected override int NumberOfClients => 1;
|
||||||
|
private readonly int m_PacketLossRate = 25;
|
||||||
|
private int m_DropInterval = 5;
|
||||||
|
|
||||||
|
public PacketLossMetricsTests()
|
||||||
|
: base(HostOrServer.Server)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected override void OnOneTimeSetup()
|
||||||
|
{
|
||||||
|
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.UTP;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnServerAndClientsCreated()
|
||||||
|
{
|
||||||
|
var clientTransport = (UnityTransport)m_ClientNetworkManagers[0].NetworkConfig.NetworkTransport;
|
||||||
|
clientTransport.SetDebugSimulatorParameters(0, 0, m_PacketLossRate);
|
||||||
|
|
||||||
|
base.OnServerAndClientsCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator TrackPacketLossAsServer()
|
||||||
|
{
|
||||||
|
var waitForPacketLossMetric = new WaitForGaugeMetricValues((m_ServerNetworkManager.NetworkMetrics as NetworkMetrics).Dispatcher,
|
||||||
|
NetworkMetricTypes.PacketLoss,
|
||||||
|
metric => metric == 0.0d);
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; ++i)
|
||||||
|
{
|
||||||
|
using (var writer = new FastBufferWriter(sizeof(byte), Allocator.Persistent))
|
||||||
|
{
|
||||||
|
writer.WriteByteSafe(42);
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage("Test", m_ServerNetworkManager.ConnectedClientsIds, writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return waitForPacketLossMetric.WaitForMetricsReceived();
|
||||||
|
|
||||||
|
var packetLossValue = waitForPacketLossMetric.AssertMetricValueHaveBeenFound();
|
||||||
|
Assert.AreEqual(0d, packetLossValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator TrackPacketLossAsClient()
|
||||||
|
{
|
||||||
|
double packetLossRate = m_PacketLossRate/100d;
|
||||||
|
var clientNetworkManager = m_ClientNetworkManagers[0];
|
||||||
|
var waitForPacketLossMetric = new WaitForGaugeMetricValues((clientNetworkManager.NetworkMetrics as NetworkMetrics).Dispatcher,
|
||||||
|
NetworkMetricTypes.PacketLoss,
|
||||||
|
metric => Math.Abs(metric - packetLossRate) < Double.Epsilon);
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; ++i)
|
||||||
|
{
|
||||||
|
using (var writer = new FastBufferWriter(sizeof(byte), Allocator.Persistent))
|
||||||
|
{
|
||||||
|
writer.WriteByteSafe(42);
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage("Test", m_ServerNetworkManager.ConnectedClientsIds, writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return waitForPacketLossMetric.WaitForMetricsReceived();
|
||||||
|
|
||||||
|
var packetLossValue = waitForPacketLossMetric.AssertMetricValueHaveBeenFound();
|
||||||
|
Assert.AreEqual(packetLossRate, packetLossValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
3
Tests/Runtime/Metrics/PacketLossMetricsTests.cs.meta
Normal file
3
Tests/Runtime/Metrics/PacketLossMetricsTests.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 12e64da4670d49a4a89da38d18e64396
|
||||||
|
timeCreated: 1648133968
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#if MULTIPLAYER_TOOLS
|
#if MULTIPLAYER_TOOLS
|
||||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
@@ -15,11 +15,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
|
|
||||||
protected override void OnOneTimeSetup()
|
protected override void OnOneTimeSetup()
|
||||||
{
|
{
|
||||||
#if UTP_ADAPTER
|
|
||||||
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.UTP;
|
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.UTP;
|
||||||
#else
|
|
||||||
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.SIP;
|
|
||||||
#endif
|
|
||||||
base.OnOneTimeSetup();
|
base.OnOneTimeSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#if MULTIPLAYER_TOOLS
|
#if MULTIPLAYER_TOOLS
|
||||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -46,11 +46,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected override void OnOneTimeSetup()
|
protected override void OnOneTimeSetup()
|
||||||
{
|
{
|
||||||
#if UTP_ADAPTER
|
|
||||||
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.UTP;
|
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.UTP;
|
||||||
#else
|
|
||||||
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.SIP;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
@@ -92,7 +88,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
foreach (var clientGaugeMetricValue in clientGaugeMetricValues)
|
foreach (var clientGaugeMetricValue in clientGaugeMetricValues)
|
||||||
{
|
{
|
||||||
var rttValue = clientGaugeMetricValue.AssertMetricValueHaveBeenFound();
|
var rttValue = clientGaugeMetricValue.AssertMetricValueHaveBeenFound();
|
||||||
Assert.That(rttValue, Is.GreaterThanOrEqualTo(1f));
|
Assert.That(rttValue, Is.GreaterThanOrEqualTo(1e-3f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#if COM_UNITY_MODULES_ANIMATION
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@@ -235,3 +236,4 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // COM_UNITY_MODULES_ANIMATION
|
||||||
|
|||||||
133
Tests/Runtime/NetworkManagerTransportTests.cs
Normal file
133
Tests/Runtime/NetworkManagerTransportTests.cs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Unity.Netcode.TestHelpers.Runtime;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.RuntimeTests
|
||||||
|
{
|
||||||
|
[TestFixture(HostOrServer.Host)]
|
||||||
|
[TestFixture(HostOrServer.Server)]
|
||||||
|
public class NetworkManagerTransportTests : NetcodeIntegrationTest
|
||||||
|
{
|
||||||
|
protected override int NumberOfClients => 1;
|
||||||
|
|
||||||
|
private bool m_CanStartServerAndClients = false;
|
||||||
|
|
||||||
|
public NetworkManagerTransportTests(HostOrServer hostOrServer) : base(hostOrServer) { }
|
||||||
|
|
||||||
|
protected override IEnumerator OnSetup()
|
||||||
|
{
|
||||||
|
m_CanStartServerAndClients = false;
|
||||||
|
return base.OnSetup();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool CanStartServerAndClients()
|
||||||
|
{
|
||||||
|
return m_CanStartServerAndClients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validate that if the NetworkTransport fails to start the NetworkManager
|
||||||
|
/// will not continue the startup process and will shut itself down.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="testClient">if true it will test the client side</param>
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator DoesNotStartWhenTransportFails([Values] bool testClient)
|
||||||
|
{
|
||||||
|
// The error message we should expect
|
||||||
|
var messageToCheck = "";
|
||||||
|
if (!testClient)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(m_ServerNetworkManager.NetworkConfig.NetworkTransport);
|
||||||
|
m_ServerNetworkManager.NetworkConfig.NetworkTransport = m_ServerNetworkManager.gameObject.AddComponent<FailedTransport>();
|
||||||
|
m_ServerNetworkManager.NetworkConfig.NetworkTransport.Initialize(m_ServerNetworkManager);
|
||||||
|
// The error message we should expect
|
||||||
|
messageToCheck = $"Server is shutting down due to network transport start failure of {m_ServerNetworkManager.NetworkConfig.NetworkTransport.GetType().Name}!";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var client in m_ClientNetworkManagers)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(client.NetworkConfig.NetworkTransport);
|
||||||
|
client.NetworkConfig.NetworkTransport = client.gameObject.AddComponent<FailedTransport>();
|
||||||
|
client.NetworkConfig.NetworkTransport.Initialize(m_ServerNetworkManager);
|
||||||
|
}
|
||||||
|
// The error message we should expect
|
||||||
|
messageToCheck = $"Client is shutting down due to network transport start failure of {m_ClientNetworkManagers[0].NetworkConfig.NetworkTransport.GetType().Name}!";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trap for the nested NetworkManager exception
|
||||||
|
LogAssert.Expect(LogType.Error, messageToCheck);
|
||||||
|
m_CanStartServerAndClients = true;
|
||||||
|
// Due to other errors, we must not send clients if testing the server-host side
|
||||||
|
// We can test both server and client(s) when testing client-side only
|
||||||
|
if (testClient)
|
||||||
|
{
|
||||||
|
NetcodeIntegrationTestHelpers.Start(m_UseHost, m_ServerNetworkManager, m_ClientNetworkManagers);
|
||||||
|
yield return s_DefaultWaitForTick;
|
||||||
|
foreach (var client in m_ClientNetworkManagers)
|
||||||
|
{
|
||||||
|
Assert.False(client.IsListening);
|
||||||
|
Assert.False(client.IsConnectedClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NetcodeIntegrationTestHelpers.Start(m_UseHost, m_ServerNetworkManager, new NetworkManager[] { });
|
||||||
|
yield return s_DefaultWaitForTick;
|
||||||
|
Assert.False(m_ServerNetworkManager.IsListening);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Does nothing but simulate a transport that failed to start
|
||||||
|
/// </summary>
|
||||||
|
public class FailedTransport : TestingNetworkTransport
|
||||||
|
{
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ulong ServerClientId => 0;
|
||||||
|
|
||||||
|
public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte> payload, out float receiveTime)
|
||||||
|
{
|
||||||
|
clientId = 0;
|
||||||
|
payload = new ArraySegment<byte>();
|
||||||
|
receiveTime = 0;
|
||||||
|
return NetworkEvent.Nothing;
|
||||||
|
}
|
||||||
|
public override bool StartClient()
|
||||||
|
{
|
||||||
|
// Simulate failure, always return false
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public override bool StartServer()
|
||||||
|
{
|
||||||
|
// Simulate failure, always return false
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDelivery networkDelivery)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DisconnectRemoteClient(ulong clientId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize(NetworkManager networkManager = null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public override ulong GetCurrentRtt(ulong clientId)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public override void DisconnectLocalClient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Tests/Runtime/NetworkManagerTransportTests.cs.meta
Normal file
11
Tests/Runtime/NetworkManagerTransportTests.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 798d76599e527b245a14b7cc9cfd2607
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,78 +1,55 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.TestTools;
|
using UnityEngine.TestTools;
|
||||||
using Unity.Netcode.TestHelpers.Runtime;
|
using Unity.Netcode.TestHelpers.Runtime;
|
||||||
using Object = UnityEngine.Object;
|
|
||||||
|
|
||||||
namespace Unity.Netcode.RuntimeTests
|
namespace Unity.Netcode.RuntimeTests
|
||||||
{
|
{
|
||||||
public class NetworkObjectDontDestroyWithOwnerTests
|
[TestFixture(HostOrServer.Host)]
|
||||||
|
[TestFixture(HostOrServer.Server)]
|
||||||
|
public class NetworkObjectDontDestroyWithOwnerTests : NetcodeIntegrationTest
|
||||||
{
|
{
|
||||||
|
private const int k_NumberObjectsToSpawn = 32;
|
||||||
|
protected override int NumberOfClients => 1;
|
||||||
|
|
||||||
|
protected GameObject m_PrefabToSpawn;
|
||||||
|
|
||||||
|
public NetworkObjectDontDestroyWithOwnerTests(HostOrServer hostOrServer) : base(hostOrServer) { }
|
||||||
|
|
||||||
|
protected override void OnServerAndClientsCreated()
|
||||||
|
{
|
||||||
|
m_PrefabToSpawn = CreateNetworkObjectPrefab("ClientOwnedObject");
|
||||||
|
m_PrefabToSpawn.GetComponent<NetworkObject>().DontDestroyWithOwner = true;
|
||||||
|
}
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator DontDestroyWithOwnerTest()
|
public IEnumerator DontDestroyWithOwnerTest()
|
||||||
{
|
{
|
||||||
// create server and client instances
|
var client = m_ClientNetworkManagers[0];
|
||||||
NetcodeIntegrationTestHelpers.Create(1, out NetworkManager server, out NetworkManager[] clients);
|
var clientId = client.LocalClientId;
|
||||||
|
var networkObjects = SpawnObjects(m_PrefabToSpawn, m_ClientNetworkManagers[0], k_NumberObjectsToSpawn);
|
||||||
|
|
||||||
// create prefab
|
// wait for object spawn on client to reach k_NumberObjectsToSpawn + 1 (k_NumberObjectsToSpawn and 1 for the player)
|
||||||
var gameObject = new GameObject("ClientOwnedObject");
|
yield return WaitForConditionOrTimeOut(() => client.SpawnManager.GetClientOwnedObjects(clientId).Count() == k_NumberObjectsToSpawn + 1);
|
||||||
var networkObject = gameObject.AddComponent<NetworkObject>();
|
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for client to have 33 NetworkObjects spawned! Only {client.SpawnManager.GetClientOwnedObjects(clientId).Count()} were assigned!");
|
||||||
networkObject.DontDestroyWithOwner = true;
|
|
||||||
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
|
|
||||||
|
|
||||||
server.NetworkConfig.NetworkPrefabs.Add(new NetworkPrefab()
|
|
||||||
{
|
|
||||||
Prefab = gameObject
|
|
||||||
});
|
|
||||||
|
|
||||||
for (int i = 0; i < clients.Length; i++)
|
|
||||||
{
|
|
||||||
clients[i].NetworkConfig.NetworkPrefabs.Add(new NetworkPrefab()
|
|
||||||
{
|
|
||||||
Prefab = gameObject
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// start server and connect clients
|
|
||||||
NetcodeIntegrationTestHelpers.Start(false, server, clients);
|
|
||||||
|
|
||||||
// wait for connection on client side
|
|
||||||
yield return NetcodeIntegrationTestHelpers.WaitForClientsConnected(clients);
|
|
||||||
|
|
||||||
// wait for connection on server side
|
|
||||||
yield return NetcodeIntegrationTestHelpers.WaitForClientConnectedToServer(server);
|
|
||||||
|
|
||||||
// network objects
|
|
||||||
var networkObjects = new List<NetworkObject>();
|
|
||||||
|
|
||||||
// create instances
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
var no = Object.Instantiate(gameObject).GetComponent<NetworkObject>();
|
|
||||||
no.NetworkManagerOwner = server;
|
|
||||||
networkObjects.Add(no);
|
|
||||||
no.SpawnWithOwnership(clients[0].LocalClientId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for object spawn on client
|
|
||||||
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => clients[0].SpawnManager.SpawnedObjects.Count == 32);
|
|
||||||
|
|
||||||
// disconnect the client that owns all the clients
|
// disconnect the client that owns all the clients
|
||||||
NetcodeIntegrationTestHelpers.StopOneClient(clients[0]);
|
NetcodeIntegrationTestHelpers.StopOneClient(client);
|
||||||
|
|
||||||
|
var remainingClients = Mathf.Max(0, TotalClients - 1);
|
||||||
// wait for disconnect
|
// wait for disconnect
|
||||||
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => server.ConnectedClients.Count == 0);
|
yield return WaitForConditionOrTimeOut(() => m_ServerNetworkManager.ConnectedClients.Count == remainingClients);
|
||||||
|
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for client to disconnect!");
|
||||||
|
|
||||||
for (int i = 0; i < networkObjects.Count; i++)
|
for (int i = 0; i < networkObjects.Count; i++)
|
||||||
{
|
{
|
||||||
|
var networkObject = networkObjects[i].GetComponent<NetworkObject>();
|
||||||
// ensure ownership was transferred back
|
// ensure ownership was transferred back
|
||||||
Assert.That(networkObjects[i].OwnerClientId == server.ServerClientId);
|
Assert.That(networkObject.OwnerClientId == m_ServerNetworkManager.LocalClientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup
|
|
||||||
NetcodeIntegrationTestHelpers.Destroy();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
yield return s_DefaultWaitForTick;
|
yield return s_DefaultWaitForTick;
|
||||||
|
|
||||||
// The object is owned by server
|
// The object is owned by server
|
||||||
Assert.False(m_ServerNetworkManager.ConnectedClients[m_ClientNetworkManagers[0].LocalClientId].OwnedObjects.Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
|
Assert.False(m_ServerNetworkManager.SpawnManager.GetClientOwnedObjects(m_ClientNetworkManagers[0].LocalClientId).Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
|
||||||
|
|
||||||
// Change the ownership
|
// Change the ownership
|
||||||
serverObject.ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
|
serverObject.ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
|
||||||
@@ -51,8 +51,8 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
yield return s_DefaultWaitForTick;
|
yield return s_DefaultWaitForTick;
|
||||||
|
|
||||||
// Ensure it's now added to the list
|
// Ensure it's now added to the list
|
||||||
Assert.True(m_ServerNetworkManager.ConnectedClients[m_ClientNetworkManagers[0].LocalClientId].OwnedObjects.Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
|
Assert.True(m_ClientNetworkManagers[0].SpawnManager.GetClientOwnedObjects(m_ClientNetworkManagers[0].LocalClientId).Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
|
||||||
|
Assert.True(m_ServerNetworkManager.SpawnManager.GetClientOwnedObjects(m_ClientNetworkManagers[0].LocalClientId).Any(x => x.NetworkObjectId == serverObject.NetworkObjectId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user