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).
|
||||
|
||||
## [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
|
||||
|
||||
### 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)
|
||||
- Improved performance in NetworkAnimator (#1735)
|
||||
- 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
|
||||
|
||||
|
||||
@@ -186,7 +186,14 @@ namespace Unity.Netcode
|
||||
|
||||
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
|
||||
@@ -218,6 +225,8 @@ namespace Unity.Netcode
|
||||
{
|
||||
m_LastBufferedItemReceived = new BufferedItem(newMeasurement, sentTime);
|
||||
ResetTo(newMeasurement, sentTime);
|
||||
// Next line keeps renderTime above m_StartTimeConsumed. Fixes pause/unpause issues
|
||||
m_Buffer.Add(m_LastBufferedItemReceived);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if COM_UNITY_MODULES_ANIMATION
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
@@ -5,7 +6,7 @@ using UnityEngine;
|
||||
namespace Unity.Netcode.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// A prototype component for syncing animations
|
||||
/// NetworkAnimator enables remote synchronization of <see cref="UnityEngine.Animator"/> state for on network objects.
|
||||
/// </summary>
|
||||
[AddComponentMenu("Netcode/" + nameof(NetworkAnimator))]
|
||||
[RequireComponent(typeof(Animator))]
|
||||
@@ -380,9 +381,7 @@ namespace Unity.Netcode.Components
|
||||
SetTrigger(Animator.StringToHash(triggerName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the trigger for the associated animation. See note for SetTrigger(string)
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="SetTrigger(string)" />
|
||||
/// <param name="hash">The hash for the trigger to activate</param>
|
||||
/// <param name="reset">If true, resets the trigger</param>
|
||||
public void SetTrigger(int hash, bool reset = false)
|
||||
@@ -413,7 +412,7 @@ namespace Unity.Netcode.Components
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <param name="triggerName">The string name of the trigger to reset</param>
|
||||
public void ResetTrigger(string triggerName)
|
||||
@@ -421,13 +420,12 @@ namespace Unity.Netcode.Components
|
||||
ResetTrigger(Animator.StringToHash(triggerName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the trigger for the associated animation. See note for SetTrigger(string)
|
||||
/// </summary>
|
||||
/// <param name="hash">The hash for the trigger to reset</param>
|
||||
/// <inheritdoc cref="ResetTrigger(string)" path="summary" />
|
||||
/// <param name="hash">The hash for the trigger to activate</param>
|
||||
public void ResetTrigger(int hash)
|
||||
{
|
||||
SetTrigger(hash, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // COM_UNITY_MODULES_ANIMATION
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if COM_UNITY_MODULES_PHYSICS
|
||||
using UnityEngine;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
/// </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
|
||||
public bool CanCommitToTransform;
|
||||
public bool CanCommitToTransform { get; protected set; }
|
||||
protected bool m_CachedIsServer;
|
||||
protected NetworkManager m_CachedNetworkManager;
|
||||
|
||||
@@ -691,7 +691,6 @@ namespace Unity.Netcode.Components
|
||||
{
|
||||
if (!NetworkObject.IsSpawned)
|
||||
{
|
||||
// todo MTT-849 should never happen but yet it does! maybe revisit/dig after NetVar updates and snapshot system lands?
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -785,7 +784,7 @@ namespace Unity.Netcode.Components
|
||||
{
|
||||
m_ReplicatedNetworkState.SetDirty(true);
|
||||
}
|
||||
else
|
||||
else if (m_Transform != null)
|
||||
{
|
||||
ApplyInterpolatedNetworkStateToTransform(m_ReplicatedNetworkState.Value, m_Transform);
|
||||
}
|
||||
|
||||
@@ -5,5 +5,22 @@
|
||||
"Unity.Netcode.Runtime",
|
||||
"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();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,10 +134,14 @@ namespace Unity.Netcode.Editor
|
||||
{
|
||||
m_NetworkPrefabsList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.NetworkPrefabs)), true, true, true, true);
|
||||
m_NetworkPrefabsList.elementHeightCallback = index =>
|
||||
{
|
||||
var networkOverrideInt = 0;
|
||||
if (m_NetworkPrefabsList.count > 0)
|
||||
{
|
||||
var networkPrefab = m_NetworkPrefabsList.serializedProperty.GetArrayElementAtIndex(index);
|
||||
var networkOverrideProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.Override));
|
||||
var networkOverrideInt = networkOverrideProp.enumValueIndex;
|
||||
networkOverrideInt = networkOverrideProp.enumValueIndex;
|
||||
}
|
||||
|
||||
return 8 + (networkOverrideInt == 0 ? EditorGUIUtility.singleLineHeight : (EditorGUIUtility.singleLineHeight * 2) + 5);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Unity.Netcode.Editor
|
||||
@@ -25,7 +27,6 @@ namespace Unity.Netcode.Editor
|
||||
{
|
||||
Singleton = new NetworkManagerHelper();
|
||||
NetworkManager.NetworkManagerHelper = Singleton;
|
||||
|
||||
EditorApplication.playModeStateChanged -= EditorApplication_playModeStateChanged;
|
||||
EditorApplication.hierarchyChanged -= EditorApplication_hierarchyChanged;
|
||||
|
||||
@@ -40,19 +41,105 @@ namespace Unity.Netcode.Editor
|
||||
case PlayModeStateChange.ExitingEditMode:
|
||||
{
|
||||
s_LastKnownNetworkManagerParents.Clear();
|
||||
ScenesInBuildActiveSceneCheck();
|
||||
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()
|
||||
{
|
||||
var allNetworkManagers = Resources.FindObjectsOfTypeAll<NetworkManager>();
|
||||
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>
|
||||
/// Handles notifying the user, via display dialog window, that they have nested a NetworkManager.
|
||||
|
||||
@@ -100,5 +100,32 @@ namespace Unity.Netcode.Editor
|
||||
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
|
||||
{
|
||||
[CustomEditor(typeof(NetworkTransform))]
|
||||
[CustomEditor(typeof(NetworkTransform), true)]
|
||||
public class NetworkTransformEditor : UnityEditor.Editor
|
||||
{
|
||||
private SerializedProperty m_SyncPositionXProperty;
|
||||
@@ -112,6 +112,7 @@ namespace Unity.Netcode.Editor
|
||||
EditorGUILayout.PropertyField(m_InLocalSpaceProperty);
|
||||
EditorGUILayout.PropertyField(m_InterpolateProperty);
|
||||
|
||||
#if COM_UNITY_MODULES_PHYSICS
|
||||
// if rigidbody is present but network rigidbody is not present
|
||||
var go = ((NetworkTransform)target).gameObject;
|
||||
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" +
|
||||
"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)
|
||||
{
|
||||
EditorGUILayout.HelpBox("This GameObject contains a Rigidbody2D but no NetworkRigidbody2D.\n" +
|
||||
"Add a NetworkRigidbody2D component to improve Rigidbody2D synchronization.", MessageType.Warning);
|
||||
}
|
||||
#endif // COM_UNITY_MODULES_PHYSICS2D
|
||||
|
||||
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
|
||||
guid: 69c3c1c5a885d4aed99ee2e1fa40f763
|
||||
guid: df5ed97df956b4aad91a221ba59fa304
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
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.TestHelpers.Runtime")]
|
||||
[assembly: InternalsVisibleTo("Unity.Netcode.Adapter.UTP")]
|
||||
|
||||
|
||||
@@ -138,19 +138,6 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
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 RttWindowSize = 64; // number of slots to use for RTT computations (max number of in-flight packets)
|
||||
/// <summary>
|
||||
|
||||
@@ -20,6 +20,17 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// The NetworkObject's owned by this Client
|
||||
/// </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
|
||||
// to ourself. Sadly we have to figure that out from the list of clientIds :(
|
||||
bool shouldSendToHost = false;
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
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;
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldSendToHost = IsHost;
|
||||
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, NetworkManager.ConnectedClientsIds);
|
||||
var observerEnumerator = NetworkObject.Observers.GetEnumerator();
|
||||
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
|
||||
@@ -228,6 +248,12 @@ namespace Unity.Netcode
|
||||
#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>
|
||||
/// 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
|
||||
@@ -235,42 +261,42 @@ namespace Unity.Netcode
|
||||
public NetworkManager NetworkManager => NetworkObject.NetworkManager;
|
||||
|
||||
/// <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>
|
||||
public bool IsLocalPlayer => NetworkObject.IsLocalPlayer;
|
||||
public bool IsLocalPlayer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the object is owned by the local player or if the object is the local player object
|
||||
/// </summary>
|
||||
public bool IsOwner => NetworkObject.IsOwner;
|
||||
public bool IsOwner { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets if we are executing as server
|
||||
/// </summary>
|
||||
protected bool IsServer => IsRunning && NetworkManager.IsServer;
|
||||
protected bool IsServer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets if we are executing as client
|
||||
/// </summary>
|
||||
protected bool IsClient => IsRunning && NetworkManager.IsClient;
|
||||
protected bool IsClient { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets if we are executing as Host, I.E Server and Client
|
||||
/// </summary>
|
||||
protected bool IsHost => IsRunning && NetworkManager.IsHost;
|
||||
|
||||
private bool IsRunning => NetworkManager && NetworkManager.IsListening;
|
||||
protected bool IsHost { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets Whether or not the object has a owner
|
||||
/// </summary>
|
||||
public bool IsOwnedByServer => NetworkObject.IsOwnedByServer;
|
||||
public bool IsOwnedByServer { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public bool IsSpawned => HasNetworkObject ? NetworkObject.IsSpawned : false;
|
||||
public bool IsSpawned { get; internal set; }
|
||||
|
||||
internal bool IsBehaviourEditable()
|
||||
{
|
||||
@@ -327,12 +353,12 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Gets the NetworkId of the NetworkObject that owns this NetworkBehaviour
|
||||
/// </summary>
|
||||
public ulong NetworkObjectId => NetworkObject.NetworkObjectId;
|
||||
public ulong NetworkObjectId { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets NetworkId for this NetworkBehaviour from the owner NetworkObject
|
||||
/// </summary>
|
||||
public ushort NetworkBehaviourId => NetworkObject.GetNetworkBehaviourOrderIndex(this);
|
||||
public ushort NetworkBehaviourId { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internally caches the Id of this behaviour in a NetworkObject. Makes look-up faster
|
||||
@@ -352,7 +378,47 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Gets the ClientId that owns the NetworkObject
|
||||
/// </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>
|
||||
/// 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()
|
||||
{
|
||||
IsSpawned = true;
|
||||
InitializeVariables();
|
||||
UpdateNetworkProperties();
|
||||
OnNetworkSpawn();
|
||||
}
|
||||
|
||||
internal void InternalOnNetworkDespawn() { }
|
||||
internal void InternalOnNetworkDespawn()
|
||||
{
|
||||
IsSpawned = false;
|
||||
UpdateNetworkProperties();
|
||||
OnNetworkDespawn();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets called when the local client gains ownership of this object
|
||||
/// </summary>
|
||||
public virtual void OnGainedOwnership() { }
|
||||
|
||||
internal void InternalOnGainedOwnership()
|
||||
{
|
||||
UpdateNetworkProperties();
|
||||
OnGainedOwnership();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets called when we loose ownership of this object
|
||||
/// </summary>
|
||||
public virtual void OnLostOwnership() { }
|
||||
|
||||
internal void InternalOnLostOwnership()
|
||||
{
|
||||
UpdateNetworkProperties();
|
||||
OnLostOwnership();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed
|
||||
/// </summary>
|
||||
@@ -433,12 +519,10 @@ namespace Unity.Netcode
|
||||
|
||||
m_VarInit = true;
|
||||
|
||||
FieldInfo[] sortedFields = GetFieldInfoForType(GetType());
|
||||
|
||||
var sortedFields = GetFieldInfoForType(GetType());
|
||||
for (int i = 0; i < sortedFields.Length; i++)
|
||||
{
|
||||
Type fieldType = sortedFields[i].FieldType;
|
||||
|
||||
var fieldType = sortedFields[i].FieldType;
|
||||
if (fieldType.IsSubclassOf(typeof(NetworkVariableBase)))
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -507,37 +591,29 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
PreNetworkVariableWrite();
|
||||
NetworkVariableUpdate(clientId, NetworkBehaviourId);
|
||||
NetworkVariableUpdate(targetClientId, NetworkBehaviourId);
|
||||
}
|
||||
|
||||
internal readonly List<int> NetworkVariableIndexesToReset = new List<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())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (NetworkManager.NetworkConfig.UseSnapshotDelta)
|
||||
{
|
||||
for (int k = 0; k < NetworkVariableFields.Count; k++)
|
||||
{
|
||||
NetworkManager.SnapshotSystem.Store(NetworkObjectId, behaviourIndex, k, NetworkVariableFields[k]);
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
var networkVariable = NetworkVariableFields[k];
|
||||
if (networkVariable.IsDirty() && networkVariable.CanClientRead(targetClientId))
|
||||
{
|
||||
shouldSend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,7 +624,7 @@ namespace Unity.Netcode
|
||||
NetworkObjectId = NetworkObjectId,
|
||||
NetworkBehaviourIndex = NetworkObject.GetNetworkBehaviourOrderIndex(this),
|
||||
NetworkBehaviour = this,
|
||||
ClientId = clientId,
|
||||
TargetClientId = targetClientId,
|
||||
DeliveryMappedNetworkVariableIndex = m_DeliveryMappedNetworkVariableIndices[j]
|
||||
};
|
||||
// TODO: Serialization is where the IsDirty flag gets changed.
|
||||
@@ -556,7 +632,7 @@ namespace Unity.Netcode
|
||||
// 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)
|
||||
if (IsServer && targetClientId == NetworkManager.ServerClientId)
|
||||
{
|
||||
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
||||
using (tmpWriter)
|
||||
@@ -566,8 +642,7 @@ namespace Unity.Netcode
|
||||
}
|
||||
else
|
||||
{
|
||||
NetworkManager.SendMessage(ref message, m_DeliveryTypesForNetworkVariableGroups[j], clientId);
|
||||
}
|
||||
NetworkManager.SendMessage(ref message, m_DeliveryTypesForNetworkVariableGroups[j], targetClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
@@ -604,7 +679,7 @@ namespace Unity.Netcode
|
||||
|
||||
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
||||
{
|
||||
bool canClientRead = NetworkVariableFields[j].CanClientRead(clientId);
|
||||
bool canClientRead = NetworkVariableFields[j].CanClientRead(targetClientId);
|
||||
|
||||
if (canClientRead)
|
||||
{
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Unity.Netcode
|
||||
{
|
||||
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
|
||||
using Unity.Profiling;
|
||||
using UnityEngine.SceneManagement;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace Unity.Netcode
|
||||
@@ -45,7 +46,7 @@ namespace Unity.Netcode
|
||||
private static ProfilerMarker s_TransportDisconnect = new ProfilerMarker($"{nameof(NetworkManager)}.TransportDisconnect");
|
||||
#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
|
||||
|
||||
internal static string PrefabDebugHelper(NetworkPrefab networkPrefab)
|
||||
@@ -53,7 +54,6 @@ namespace Unity.Netcode
|
||||
return $"{nameof(NetworkPrefab)} \"{networkPrefab.Prefab.gameObject.name}\"";
|
||||
}
|
||||
|
||||
internal SnapshotSystem SnapshotSystem { get; private set; }
|
||||
internal NetworkBehaviourUpdater BehaviourUpdater { get; private set; }
|
||||
|
||||
internal MessagingSystem MessagingSystem { get; private set; }
|
||||
@@ -125,13 +125,11 @@ namespace Unity.Netcode
|
||||
public bool OnVerifyCanReceive(ulong senderId, Type messageType)
|
||||
{
|
||||
if (m_NetworkManager.PendingClients.TryGetValue(senderId, out PendingClient client) &&
|
||||
(client.ConnectionState == PendingClient.State.PendingApproval ||
|
||||
(client.ConnectionState == PendingClient.State.PendingConnection &&
|
||||
messageType != typeof(ConnectionRequestMessage))))
|
||||
(client.ConnectionState == PendingClient.State.PendingApproval || (client.ConnectionState == PendingClient.State.PendingConnection && messageType != typeof(ConnectionRequestMessage))))
|
||||
{
|
||||
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;
|
||||
@@ -229,14 +227,12 @@ namespace Unity.Netcode
|
||||
|
||||
public NetworkSceneManager SceneManager { get; private set; }
|
||||
|
||||
public readonly ulong ServerClientId = 0;
|
||||
public const ulong ServerClientId = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the networkId of the server
|
||||
/// </summary>
|
||||
private ulong m_ServerTransportId => NetworkConfig.NetworkTransport?.ServerClientId ??
|
||||
throw new NullReferenceException(
|
||||
$"The transport in the active {nameof(NetworkConfig)} is null");
|
||||
private ulong m_ServerTransportId => NetworkConfig.NetworkTransport?.ServerClientId ?? throw new NullReferenceException($"The transport in the active {nameof(NetworkConfig)} is null");
|
||||
|
||||
/// <summary>
|
||||
/// 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="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>
|
||||
public delegate void ConnectionApprovedDelegate(bool createPlayerObject, uint? playerPrefabHash, bool approved,
|
||||
Vector3? position, Quaternion? rotation);
|
||||
public delegate void ConnectionApprovedDelegate(bool createPlayerObject, uint? playerPrefabHash, bool approved, Vector3? position, Quaternion? rotation);
|
||||
|
||||
/// <summary>
|
||||
/// The callback to invoke during connection approval
|
||||
/// </summary>
|
||||
public event Action<byte[], ulong, ConnectionApprovedDelegate> ConnectionApprovalCallback = null;
|
||||
|
||||
internal void InvokeConnectionApproval(byte[] payload, ulong clientId, ConnectionApprovedDelegate action) =>
|
||||
ConnectionApprovalCallback?.Invoke(payload, clientId, action);
|
||||
internal void InvokeConnectionApproval(byte[] payload, ulong clientId, ConnectionApprovedDelegate action) => ConnectionApprovalCallback?.Invoke(payload, clientId, action);
|
||||
|
||||
/// <summary>
|
||||
/// The current NetworkConfig
|
||||
@@ -565,13 +559,6 @@ namespace Unity.Netcode
|
||||
|
||||
NetworkConfig.NetworkTransport.NetworkMetrics = NetworkMetrics;
|
||||
|
||||
//This 'if' should never enter
|
||||
if (SnapshotSystem != null)
|
||||
{
|
||||
SnapshotSystem.Dispose();
|
||||
SnapshotSystem = null;
|
||||
}
|
||||
|
||||
if (server)
|
||||
{
|
||||
NetworkTimeSystem = NetworkTimeSystem.ServerTimeSystem();
|
||||
@@ -584,8 +571,6 @@ namespace Unity.Netcode
|
||||
NetworkTickSystem = new NetworkTickSystem(NetworkConfig.TickRate, 0, 0);
|
||||
NetworkTickSystem.Tick += OnNetworkManagerTick;
|
||||
|
||||
SnapshotSystem = new SnapshotSystem(this, NetworkConfig, NetworkTickSystem);
|
||||
|
||||
this.RegisterNetworkUpdate(NetworkUpdateStage.PreUpdate);
|
||||
|
||||
// This is used to remove entries not needed or invalid
|
||||
@@ -800,35 +785,19 @@ namespace Unity.Netcode
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (NetworkConfig.ConnectionApproval)
|
||||
{
|
||||
if (ConnectionApprovalCallback == null)
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning(
|
||||
"No ConnectionApproval callback defined. Connection approval will timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -836,8 +805,15 @@ namespace Unity.Netcode
|
||||
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();
|
||||
}
|
||||
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -850,26 +826,26 @@ namespace Unity.Netcode
|
||||
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;
|
||||
}
|
||||
|
||||
Initialize(false);
|
||||
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;
|
||||
IsClient = true;
|
||||
IsListening = true;
|
||||
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -882,31 +858,21 @@ namespace Unity.Netcode
|
||||
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;
|
||||
}
|
||||
|
||||
if (NetworkConfig.ConnectionApproval)
|
||||
{
|
||||
if (ConnectionApprovalCallback == null)
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning(
|
||||
"No ConnectionApproval callback defined. Connection approval will timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
LocalClientId = ServerClientId;
|
||||
NetworkMetrics.SetConnectionId(LocalClientId);
|
||||
@@ -942,7 +908,53 @@ namespace Unity.Netcode
|
||||
|
||||
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()
|
||||
@@ -1014,6 +1026,7 @@ namespace Unity.Netcode
|
||||
internal interface INetworkManagerHelper
|
||||
{
|
||||
bool NotifyUserOfNestedNetworkManager(NetworkManager networkManager, bool ignoreNetworkManagerCache = false, bool editorTest = false);
|
||||
void CheckAndNotifyUserNetworkObjectRemoved(NetworkManager networkManager, bool editorTest = false);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1133,12 +1146,6 @@ namespace Unity.Netcode
|
||||
|
||||
this.UnregisterAllNetworkUpdates();
|
||||
|
||||
if (SnapshotSystem != null)
|
||||
{
|
||||
SnapshotSystem.Dispose();
|
||||
SnapshotSystem = null;
|
||||
}
|
||||
|
||||
if (NetworkTickSystem != null)
|
||||
{
|
||||
NetworkTickSystem.Tick -= OnNetworkManagerTick;
|
||||
@@ -1274,7 +1281,11 @@ namespace Unity.Netcode
|
||||
if (!m_ShuttingDown || !m_StopProcessingMessages)
|
||||
{
|
||||
MessagingSystem.ProcessSendQueues();
|
||||
NetworkMetrics.UpdateNetworkObjectsCount(SpawnManager.SpawnedObjects.Count);
|
||||
NetworkMetrics.UpdateConnectionsCount((IsServer) ? ConnectedClients.Count : 1);
|
||||
NetworkMetrics.DispatchFrame();
|
||||
|
||||
NetworkObject.VerifyParentingStatus();
|
||||
}
|
||||
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):
|
||||
/// - collect commands/inputs and send them to the server (TBD)
|
||||
/// - call NetworkFixedUpdate on all NetworkBehaviours in prediction/client authority mode
|
||||
/// - create a snapshot from resulting state
|
||||
/// </summary>
|
||||
private void OnNetworkManagerTick()
|
||||
{
|
||||
@@ -1419,18 +1429,15 @@ namespace Unity.Netcode
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
s_TransportDisconnect.Begin();
|
||||
#endif
|
||||
clientId = TransportIdToClientId(clientId);
|
||||
|
||||
OnClientDisconnectCallback?.Invoke(clientId);
|
||||
|
||||
m_TransportIdToClientIdMap.Remove(transportId);
|
||||
m_ClientIdToTransportIdMap.Remove(clientId);
|
||||
clientId = TransportIdCleanUp(clientId, transportId);
|
||||
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||
{
|
||||
NetworkLog.LogInfo($"Disconnect Event From {clientId}");
|
||||
}
|
||||
|
||||
OnClientDisconnectCallback?.Invoke(clientId);
|
||||
|
||||
if (IsServer)
|
||||
{
|
||||
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)
|
||||
where TMessageType : INetworkMessage
|
||||
where TClientIdListType : IReadOnlyList<ulong>
|
||||
@@ -1591,17 +1623,30 @@ 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
|
||||
// Only log this in verbose/developer mode
|
||||
if (LogLevel == LogLevel.Developer)
|
||||
{
|
||||
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 (!ownedObject.DontDestroyWithOwner)
|
||||
{
|
||||
if (PrefabHandler.ContainsHandler(ConnectedClients[clientId].OwnedObjects[i]
|
||||
.GlobalObjectIdHash))
|
||||
if (PrefabHandler.ContainsHandler(clientOwnedObjects[i].GlobalObjectIdHash))
|
||||
{
|
||||
PrefabHandler.HandleNetworkPrefabDestroy(ConnectedClients[clientId].OwnedObjects[i]);
|
||||
PrefabHandler.HandleNetworkPrefabDestroy(clientOwnedObjects[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1614,9 +1659,9 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Could(should?) be replaced with more memory per client, by storing the visibility
|
||||
|
||||
foreach (var sobj in SpawnManager.SpawnedObjectsList)
|
||||
{
|
||||
sobj.Observers.Remove(clientId);
|
||||
@@ -1764,7 +1809,7 @@ namespace Unity.Netcode
|
||||
|
||||
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.IsSceneObject = false;
|
||||
|
||||
@@ -54,8 +54,6 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
internal NetworkManager NetworkManagerOwner;
|
||||
|
||||
private ulong m_NetworkObjectId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique Id of this object that is synced across the network
|
||||
/// </summary>
|
||||
@@ -64,33 +62,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// Gets the ClientId of the owner of this NetworkObject
|
||||
/// </summary>
|
||||
public ulong OwnerClientId
|
||||
{
|
||||
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;
|
||||
public ulong OwnerClientId { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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");
|
||||
}
|
||||
|
||||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
||||
{
|
||||
SnapshotSpawn(clientId);
|
||||
}
|
||||
|
||||
Observers.Add(clientId);
|
||||
|
||||
NetworkManager.SpawnManager.SendSpawnCallForObject(clientId, this);
|
||||
@@ -314,15 +281,8 @@ namespace Unity.Netcode
|
||||
throw new VisibilityChangeException("Cannot hide an object from the server");
|
||||
}
|
||||
|
||||
|
||||
Observers.Remove(clientId);
|
||||
|
||||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
||||
{
|
||||
SnapshotDespawn(clientId);
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = new DestroyObjectMessage
|
||||
{
|
||||
NetworkObjectId = NetworkObjectId
|
||||
@@ -331,7 +291,6 @@ namespace Unity.Netcode
|
||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
|
||||
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides a list of objects from a client
|
||||
@@ -345,14 +304,14 @@ namespace Unity.Netcode
|
||||
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)
|
||||
{
|
||||
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");
|
||||
}
|
||||
@@ -384,84 +343,21 @@ namespace Unity.Netcode
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned
|
||||
&& (IsSceneObject == null || (IsSceneObject != null && IsSceneObject.Value != true)))
|
||||
if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned &&
|
||||
(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.");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
private void SpawnInternal(bool destroyWithScene, ulong? ownerClientId, bool playerObject)
|
||||
private void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool playerObject)
|
||||
{
|
||||
if (!NetworkManager.IsListening)
|
||||
{
|
||||
@@ -475,12 +371,6 @@ namespace Unity.Netcode
|
||||
|
||||
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++)
|
||||
{
|
||||
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>
|
||||
public void Spawn(bool destroyWithScene = false)
|
||||
{
|
||||
SpawnInternal(destroyWithScene, null, false);
|
||||
SpawnInternal(destroyWithScene, NetworkManager.ServerClientId, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -547,17 +437,29 @@ namespace Unity.Netcode
|
||||
|
||||
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++)
|
||||
{
|
||||
ChildNetworkBehaviours[i].OnLostOwnership();
|
||||
ChildNetworkBehaviours[i].InternalOnLostOwnership();
|
||||
}
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
ChildNetworkBehaviours[i].OnGainedOwnership();
|
||||
ChildNetworkBehaviours[i].InternalOnGainedOwnership();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,13 +658,7 @@ namespace Unity.Netcode
|
||||
|
||||
if (!NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(m_LatestParent.Value))
|
||||
{
|
||||
if (OrphanChildren.Add(this))
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning($"{nameof(NetworkObject)} ({name}) cannot find its parent, added to {nameof(OrphanChildren)} set");
|
||||
}
|
||||
}
|
||||
OrphanChildren.Add(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -793,19 +689,21 @@ namespace Unity.Netcode
|
||||
|
||||
internal void InvokeBehaviourNetworkSpawn()
|
||||
{
|
||||
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId);
|
||||
|
||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||
{
|
||||
ChildNetworkBehaviours[i].InternalOnNetworkSpawn();
|
||||
ChildNetworkBehaviours[i].OnNetworkSpawn();
|
||||
}
|
||||
}
|
||||
|
||||
internal void InvokeBehaviourNetworkDespawn()
|
||||
{
|
||||
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId, true);
|
||||
|
||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
var behavior = ChildNetworkBehaviours[i];
|
||||
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)
|
||||
{
|
||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||
@@ -918,7 +837,6 @@ namespace Unity.Netcode
|
||||
public bool IsSceneObject;
|
||||
public bool HasTransform;
|
||||
public bool IsReparented;
|
||||
public bool HasNetworkVariables;
|
||||
}
|
||||
|
||||
public HeaderData Header;
|
||||
@@ -979,11 +897,8 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
if (Header.HasNetworkVariables)
|
||||
{
|
||||
OwnerObject.WriteNetworkVariableData(writer, TargetClientId);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
@@ -1033,7 +948,6 @@ namespace Unity.Netcode
|
||||
OwnerClientId = OwnerClientId,
|
||||
IsSceneObject = IsSceneObject ?? true,
|
||||
Hash = HostCheckForGlobalObjectIdHashOverride(),
|
||||
HasNetworkVariables = includeNetworkVariableData
|
||||
},
|
||||
OwnerObject = this,
|
||||
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;
|
||||
|
||||
// internal logging
|
||||
internal static void LogInfo(string message) => Debug.Log($"[Netcode] {message}");
|
||||
internal static void LogWarning(string message) => Debug.LogWarning($"[Netcode] {message}");
|
||||
internal static void LogError(string message) => Debug.LogError($"[Netcode] {message}");
|
||||
public static void LogInfo(string message) => Debug.Log($"[Netcode] {message}");
|
||||
public static void LogWarning(string message) => Debug.LogWarning($"[Netcode] {message}");
|
||||
public static void LogError(string message) => Debug.LogError($"[Netcode] {message}");
|
||||
|
||||
/// <summary>
|
||||
/// Logs an info log locally and on the server if possible.
|
||||
@@ -62,9 +62,9 @@ namespace Unity.Netcode
|
||||
LogType = logType,
|
||||
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)
|
||||
{
|
||||
|
||||
var networkManager = (NetworkManager)context.SystemOwner;
|
||||
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
|
||||
|
||||
if (networkObject.OwnerClientId == networkManager.LocalClientId)
|
||||
{
|
||||
//We are current owner.
|
||||
networkObject.InvokeBehaviourOnLostOwnership();
|
||||
}
|
||||
var originalOwner = networkObject.OwnerClientId;
|
||||
|
||||
networkObject.OwnerClientId = OwnerClientId;
|
||||
|
||||
// We are current owner.
|
||||
if (originalOwner == networkManager.LocalClientId)
|
||||
{
|
||||
networkObject.InvokeBehaviourOnLostOwnership();
|
||||
}
|
||||
|
||||
// We are new owner.
|
||||
if (OwnerClientId == networkManager.LocalClientId)
|
||||
{
|
||||
//We are new owner.
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Unity.Netcode
|
||||
public ushort NetworkBehaviourIndex;
|
||||
|
||||
public HashSet<int> DeliveryMappedNetworkVariableIndex;
|
||||
public ulong ClientId;
|
||||
public ulong TargetClientId;
|
||||
public NetworkBehaviour NetworkBehaviour;
|
||||
|
||||
private FastBufferReader m_ReceivedNetworkVariableData;
|
||||
@@ -31,9 +31,9 @@ namespace Unity.Netcode
|
||||
writer.WriteValue(NetworkObjectId);
|
||||
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.
|
||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
@@ -48,15 +48,17 @@ namespace Unity.Netcode
|
||||
continue;
|
||||
}
|
||||
|
||||
// if I'm dirty AND a client, write (server always has all permissions)
|
||||
// if I'm dirty AND the server AND the client can read me, send.
|
||||
bool shouldWrite = NetworkBehaviour.NetworkVariableFields[k].ShouldWrite(ClientId, NetworkBehaviour.NetworkManager.IsServer);
|
||||
var startingSize = writer.Length;
|
||||
var networkVariable = NetworkBehaviour.NetworkVariableFields[i];
|
||||
var shouldWrite = networkVariable.IsDirty() &&
|
||||
networkVariable.CanClientRead(TargetClientId) &&
|
||||
(NetworkBehaviour.NetworkManager.IsServer || networkVariable.CanClientWrite(NetworkBehaviour.NetworkManager.LocalClientId));
|
||||
|
||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
{
|
||||
if (!shouldWrite)
|
||||
{
|
||||
writer.WriteValueSafe((ushort)0);
|
||||
BytePacker.WriteValueBitPacked(writer, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -68,34 +70,34 @@ namespace Unity.Netcode
|
||||
{
|
||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
{
|
||||
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, short.MaxValue);
|
||||
NetworkBehaviour.NetworkVariableFields[k].WriteDelta(tmpWriter);
|
||||
var tempWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
||||
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)}");
|
||||
}
|
||||
|
||||
writer.WriteValue((ushort)tmpWriter.Length);
|
||||
tmpWriter.CopyTo(writer);
|
||||
tempWriter.CopyTo(writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
NetworkBehaviour.NetworkVariableFields[k].WriteDelta(writer);
|
||||
networkVariable.WriteDelta(writer);
|
||||
}
|
||||
|
||||
if (!NetworkBehaviour.NetworkVariableIndexesToResetSet.Contains(k))
|
||||
if (!NetworkBehaviour.NetworkVariableIndexesToResetSet.Contains(i))
|
||||
{
|
||||
NetworkBehaviour.NetworkVariableIndexesToResetSet.Add(k);
|
||||
NetworkBehaviour.NetworkVariableIndexesToReset.Add(k);
|
||||
NetworkBehaviour.NetworkVariableIndexesToResetSet.Add(i);
|
||||
NetworkBehaviour.NetworkVariableIndexesToReset.Add(i);
|
||||
}
|
||||
|
||||
NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
|
||||
ClientId,
|
||||
TargetClientId,
|
||||
NetworkBehaviour.NetworkObject,
|
||||
NetworkBehaviour.NetworkVariableFields[k].Name,
|
||||
networkVariable.Name,
|
||||
NetworkBehaviour.__getTypeName(),
|
||||
writer.Length);
|
||||
writer.Length - startingSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,9 +123,9 @@ namespace Unity.Netcode
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -132,13 +134,12 @@ namespace Unity.Netcode
|
||||
}
|
||||
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)
|
||||
{
|
||||
m_ReceivedNetworkVariableData.ReadValueSafe(out varSize);
|
||||
ByteUnpacker.ReadValueBitPacked(m_ReceivedNetworkVariableData, out varSize);
|
||||
|
||||
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
|
||||
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.LogError($"[{behaviour.NetworkVariableFields[i].GetType().Name}]");
|
||||
NetworkLog.LogWarning($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(networkBehaviour)} - VariableIndex: {i}");
|
||||
NetworkLog.LogError($"[{networkVariable.GetType().Name}]");
|
||||
}
|
||||
|
||||
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.
|
||||
//This is after all a developer fault. A critical error should be fine.
|
||||
// - 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($"[{behaviour.NetworkVariableFields[i].GetType().Name}]");
|
||||
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($"[{networkVariable.GetType().Name}]");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
int readStartPos = m_ReceivedNetworkVariableData.Position;
|
||||
|
||||
behaviour.NetworkVariableFields[i].ReadDelta(m_ReceivedNetworkVariableData, networkManager.IsServer);
|
||||
networkVariable.ReadDelta(m_ReceivedNetworkVariableData, networkManager.IsServer);
|
||||
|
||||
networkManager.NetworkMetrics.TrackNetworkVariableDeltaReceived(
|
||||
context.SenderId,
|
||||
networkObject,
|
||||
behaviour.NetworkVariableFields[i].Name,
|
||||
behaviour.__getTypeName(),
|
||||
networkVariable.Name,
|
||||
networkBehaviour.__getTypeName(),
|
||||
context.MessageSize);
|
||||
|
||||
|
||||
@@ -202,7 +205,7 @@ namespace Unity.Netcode
|
||||
{
|
||||
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);
|
||||
@@ -211,7 +214,7 @@ namespace Unity.Netcode
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -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 FRAGMENTED_MESSAGE_MAX_SIZE = int.MaxValue;
|
||||
public const int FRAGMENTED_MESSAGE_MAX_SIZE = BytePacker.BitPackedIntMax;
|
||||
|
||||
internal struct MessageWithHandler
|
||||
{
|
||||
|
||||
@@ -87,7 +87,13 @@ namespace Unity.Netcode
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace Unity.Netcode
|
||||
private readonly EventMetric<SceneEventMetric> m_SceneEventSentEvent = new EventMetric<SceneEventMetric>(NetworkMetricTypes.SceneEventSent.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)
|
||||
{
|
||||
ShouldResetOnDispatch = true,
|
||||
@@ -79,6 +79,15 @@ namespace Unity.Netcode
|
||||
{
|
||||
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
|
||||
|
||||
private ulong m_NumberOfMetricsThisFrame;
|
||||
@@ -97,9 +106,12 @@ namespace Unity.Netcode
|
||||
.WithMetricEvents(m_RpcSentEvent, m_RpcReceivedEvent)
|
||||
.WithMetricEvents(m_ServerLogSentEvent, m_ServerLogReceivedEvent)
|
||||
.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)
|
||||
.WithGauges(m_RttToServerGauge)
|
||||
.WithGauges(m_NetworkObjectsGauge)
|
||||
.WithGauges(m_ConnectionsGauge)
|
||||
.WithGauges(m_PacketLossGauge)
|
||||
#endif
|
||||
.Build();
|
||||
|
||||
@@ -428,7 +440,7 @@ namespace Unity.Netcode
|
||||
|
||||
public void TrackPacketSent(uint packetCount)
|
||||
{
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
if (!CanSendMetrics)
|
||||
{
|
||||
return;
|
||||
@@ -441,7 +453,7 @@ namespace Unity.Netcode
|
||||
|
||||
public void TrackPacketReceived(uint packetCount)
|
||||
{
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
if (!CanSendMetrics)
|
||||
{
|
||||
return;
|
||||
@@ -452,15 +464,51 @@ namespace Unity.Netcode
|
||||
#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)
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
public event OnListChangedDelegate OnListChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a NetworkList with the default value and settings
|
||||
/// </summary>
|
||||
public NetworkList() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a NetworkList with the default value and custom settings
|
||||
/// </summary>
|
||||
/// <param name="readPerm">The read permission to use for the NetworkList</param>
|
||||
/// <param name="values">The initial value to use for the NetworkList</param>
|
||||
public NetworkList(NetworkVariableReadPermission readPerm, IEnumerable<T> values) : base(readPerm)
|
||||
public NetworkList(IEnumerable<T> values = default,
|
||||
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
||||
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||
: base(readPerm, writePerm)
|
||||
{
|
||||
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 />
|
||||
public override void ResetDirty()
|
||||
{
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
@@ -22,7 +24,8 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
@@ -37,16 +40,13 @@ namespace Unity.Netcode
|
||||
|
||||
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
|
||||
// type.
|
||||
//
|
||||
// These static delegates provide the right implementation for writing and reading a particular network variable type.
|
||||
// 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()
|
||||
// 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
|
||||
// optimize bandwidth usage.
|
||||
// In the future we may be able to use this to provide packing implementations for floats and integers to optimize bandwidth usage.
|
||||
//
|
||||
// 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,
|
||||
@@ -69,38 +69,11 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
/// <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)
|
||||
public NetworkVariable(T value = default,
|
||||
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
||||
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||
: base(readPerm, writePerm)
|
||||
{
|
||||
m_InternalValue = value;
|
||||
}
|
||||
@@ -116,19 +89,36 @@ namespace Unity.Netcode
|
||||
get => m_InternalValue;
|
||||
set
|
||||
{
|
||||
// this could be improved. The Networking Manager is not always initialized here
|
||||
// Good place to decouple network manager from the network variable
|
||||
|
||||
// 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))
|
||||
// Compare bitwise
|
||||
if (ValueEquals(ref m_InternalValue, ref value))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
m_IsDirty = true;
|
||||
@@ -146,7 +136,6 @@ namespace Unity.Netcode
|
||||
WriteField(writer);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads value from the reader and applies it
|
||||
/// </summary>
|
||||
|
||||
@@ -19,9 +19,15 @@ namespace Unity.Netcode
|
||||
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;
|
||||
@@ -37,6 +43,8 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
public readonly NetworkVariableReadPermission ReadPerm;
|
||||
|
||||
public readonly NetworkVariableWritePermission WritePerm;
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether or not the variable needs to be delta synced
|
||||
/// </summary>
|
||||
@@ -62,26 +70,28 @@ namespace Unity.Netcode
|
||||
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)
|
||||
{
|
||||
switch (ReadPerm)
|
||||
{
|
||||
default:
|
||||
case NetworkVariableReadPermission.Everyone:
|
||||
return true;
|
||||
case NetworkVariableReadPermission.OwnerOnly:
|
||||
return m_NetworkBehaviour.OwnerClientId == clientId;
|
||||
case NetworkVariableReadPermission.Owner:
|
||||
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>
|
||||
@@ -107,7 +117,6 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
/// <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>
|
||||
|
||||
public abstract void ReadDelta(FastBufferReader reader, bool keepDirtyDelta);
|
||||
|
||||
public virtual void Dispose()
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Permission type
|
||||
/// </summary>
|
||||
public enum NetworkVariableReadPermission
|
||||
{
|
||||
/// <summary>
|
||||
/// Everyone
|
||||
/// </summary>
|
||||
Everyone,
|
||||
Owner,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Owner-ownly
|
||||
/// </summary>
|
||||
OwnerOnly,
|
||||
public enum NetworkVariableWritePermission
|
||||
{
|
||||
Server,
|
||||
Owner
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace Unity.Netcode
|
||||
private const NetworkDelivery k_DeliveryType = NetworkDelivery.ReliableFragmentedSequenced;
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
@@ -488,9 +488,19 @@ namespace Unity.Netcode
|
||||
var scenePath = SceneUtility.GetScenePathByBuildIndex(i);
|
||||
var hash = XXHash.Hash32(scenePath);
|
||||
var buildIndex = SceneUtility.GetBuildIndexByScenePath(scenePath);
|
||||
|
||||
// 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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -520,7 +530,8 @@ namespace Unity.Netcode
|
||||
}
|
||||
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,
|
||||
SceneName = SceneNameFromHash(sceneEventProgress.SceneHash),
|
||||
ClientId = m_NetworkManager.ServerClientId,
|
||||
ClientId = NetworkManager.ServerClientId,
|
||||
LoadSceneMode = sceneEventProgress.LoadSceneMode,
|
||||
ClientsThatCompleted = sceneEventProgress.DoneClients,
|
||||
ClientsThatTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(),
|
||||
@@ -947,10 +958,10 @@ namespace Unity.Netcode
|
||||
SceneEventType = sceneEventData.SceneEventType,
|
||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||
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 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
|
||||
// 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
|
||||
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
|
||||
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,
|
||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||
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));
|
||||
@@ -1043,7 +1054,7 @@ namespace Unity.Netcode
|
||||
// Clients send a notification back to the server they have completed the unload scene event
|
||||
if (!m_NetworkManager.IsServer)
|
||||
{
|
||||
SendSceneEventData(sceneEventId, new ulong[] { m_NetworkManager.ServerClientId });
|
||||
SendSceneEventData(sceneEventId, new ulong[] { NetworkManager.ServerClientId });
|
||||
}
|
||||
|
||||
EndSceneEvent(sceneEventId);
|
||||
@@ -1079,7 +1090,7 @@ namespace Unity.Netcode
|
||||
SceneEventType = SceneEventType.Unload,
|
||||
SceneName = keyHandleEntry.Value.name,
|
||||
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,
|
||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||
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 sceneEventProgress.Status;
|
||||
@@ -1187,7 +1198,6 @@ namespace Unity.Netcode
|
||||
// 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
|
||||
// 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)
|
||||
{
|
||||
IsSpawnedObjectsPendingInDontDestroyOnLoad = true;
|
||||
@@ -1278,7 +1288,9 @@ namespace Unity.Netcode
|
||||
{
|
||||
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++)
|
||||
{
|
||||
var clientId = m_NetworkManager.ConnectedClientsList[j].ClientId;
|
||||
if (clientId != m_NetworkManager.ServerClientId)
|
||||
if (clientId != NetworkManager.ServerClientId)
|
||||
{
|
||||
sceneEventData.TargetClientId = clientId;
|
||||
var message = new SceneEventMessage
|
||||
@@ -1309,16 +1321,16 @@ namespace Unity.Netcode
|
||||
SceneEventType = SceneEventType.LoadComplete,
|
||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
||||
ClientId = m_NetworkManager.ServerClientId,
|
||||
ClientId = NetworkManager.ServerClientId,
|
||||
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
|
||||
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
||||
{
|
||||
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(m_NetworkManager.ServerClientId);
|
||||
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(NetworkManager.ServerClientId);
|
||||
}
|
||||
EndSceneEvent(sceneEventId);
|
||||
}
|
||||
@@ -1333,7 +1345,7 @@ namespace Unity.Netcode
|
||||
sceneEventData.DeserializeScenePlacedObjects();
|
||||
|
||||
sceneEventData.SceneEventType = SceneEventType.LoadComplete;
|
||||
SendSceneEventData(sceneEventId, new ulong[] { m_NetworkManager.ServerClientId });
|
||||
SendSceneEventData(sceneEventId, new ulong[] { NetworkManager.ServerClientId });
|
||||
m_IsSceneEventActive = false;
|
||||
|
||||
// Notify local client that the scene was loaded
|
||||
@@ -1544,9 +1556,9 @@ namespace Unity.Netcode
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -1600,7 +1612,7 @@ namespace Unity.Netcode
|
||||
sceneEventData.SynchronizeSceneNetworkObjects(m_NetworkManager);
|
||||
|
||||
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
|
||||
m_NetworkManager.IsConnectedClient = true;
|
||||
@@ -1627,7 +1639,7 @@ namespace Unity.Netcode
|
||||
OnSceneEvent?.Invoke(new SceneEvent()
|
||||
{
|
||||
SceneEventType = sceneEventData.SceneEventType,
|
||||
ClientId = m_NetworkManager.ServerClientId, // Server sent this to client
|
||||
ClientId = NetworkManager.ServerClientId, // Server sent this to client
|
||||
});
|
||||
|
||||
EndSceneEvent(sceneEventId);
|
||||
@@ -1642,7 +1654,7 @@ namespace Unity.Netcode
|
||||
SceneEventType = sceneEventData.SceneEventType,
|
||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
||||
ClientId = m_NetworkManager.ServerClientId,
|
||||
ClientId = NetworkManager.ServerClientId,
|
||||
ClientsThatCompleted = sceneEventData.ClientsCompleted,
|
||||
ClientsThatTimedOut = sceneEventData.ClientsTimedOut,
|
||||
});
|
||||
@@ -1734,8 +1746,6 @@ namespace Unity.Netcode
|
||||
// NetworkObjects
|
||||
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)
|
||||
{
|
||||
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
|
||||
/// distinguish between duplicate in-scene placed NetworkObjects
|
||||
/// </summary>
|
||||
private void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true)
|
||||
internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true)
|
||||
{
|
||||
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
|
||||
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)
|
||||
if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy &&
|
||||
networkObjectInstance.gameObject.scene.handle == sceneToFilterBy.handle)
|
||||
var globalObjectIdHash = networkObjectInstance.GlobalObjectIdHash;
|
||||
var sceneHandle = networkObjectInstance.gameObject.scene.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
|
||||
{
|
||||
var exitingEntryName = ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash][networkObjectInstance.gameObject.scene.handle] != null ?
|
||||
ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash][networkObjectInstance.gameObject.scene.handle].name : "Null Entry";
|
||||
var exitingEntryName = ScenePlacedObjects[globalObjectIdHash][sceneHandle] != null ? ScenePlacedObjects[globalObjectIdHash][sceneHandle].name : "Null Entry";
|
||||
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>Message Flow:</b> Server to client<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>
|
||||
ReSynchronize,
|
||||
/// <summary>
|
||||
@@ -592,9 +591,6 @@ namespace Unity.Netcode
|
||||
networkObject.IsSpawned = false;
|
||||
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))
|
||||
{
|
||||
m_NetworkManager.SpawnManager.SpawnedObjects.Remove(networkObjectId);
|
||||
|
||||
@@ -281,6 +281,17 @@ namespace Unity.Netcode
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteValueBitPacked<T>(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value);
|
||||
#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>
|
||||
/// 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.
|
||||
@@ -307,7 +318,7 @@ namespace Unity.Netcode
|
||||
public static void WriteValueBitPacked(FastBufferWriter writer, ushort value)
|
||||
{
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
if (value >= 0b1000_0000_0000_0000)
|
||||
if (value >= BitPackedUshortMax)
|
||||
{
|
||||
throw new ArgumentException("BitPacked ushorts must be <= 15 bits");
|
||||
}
|
||||
@@ -356,7 +367,7 @@ namespace Unity.Netcode
|
||||
public static void WriteValueBitPacked(FastBufferWriter writer, uint value)
|
||||
{
|
||||
#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");
|
||||
}
|
||||
@@ -396,7 +407,7 @@ namespace Unity.Netcode
|
||||
public static void WriteValueBitPacked(FastBufferWriter writer, ulong value)
|
||||
{
|
||||
#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");
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Unity.Netcode
|
||||
#endif
|
||||
}
|
||||
|
||||
internal readonly unsafe ReaderHandle* Handle;
|
||||
internal unsafe ReaderHandle* Handle;
|
||||
|
||||
/// <summary>
|
||||
/// Get the current read position
|
||||
@@ -39,6 +39,11 @@ namespace Unity.Netcode
|
||||
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)]
|
||||
internal unsafe void CommitBitwiseReads(int amount)
|
||||
{
|
||||
@@ -196,6 +201,7 @@ namespace Unity.Netcode
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
UnsafeUtility.Free(Handle, Handle->Allocator);
|
||||
Handle = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Unity.Netcode
|
||||
#endif
|
||||
}
|
||||
|
||||
internal readonly unsafe WriterHandle* Handle;
|
||||
internal unsafe WriterHandle* Handle;
|
||||
|
||||
private static byte[] s_ByteArrayCache = new byte[65535];
|
||||
|
||||
@@ -62,6 +62,11 @@ namespace Unity.Netcode
|
||||
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)]
|
||||
internal unsafe void CommitBitwiseWrites(int amount)
|
||||
@@ -111,6 +116,7 @@ namespace Unity.Netcode
|
||||
UnsafeUtility.Free(Handle->BufferPointer, Handle->Allocator);
|
||||
}
|
||||
UnsafeUtility.Free(Handle, Handle->Allocator);
|
||||
Handle = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -21,6 +21,122 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
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
|
||||
{
|
||||
public FastBufferReader Reader;
|
||||
@@ -96,8 +212,7 @@ namespace Unity.Netcode
|
||||
/// <summary>
|
||||
/// 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,
|
||||
/// either due to it being requested in OnNetworkSpawn before the spawn call has been executed, or with
|
||||
/// snapshot spawns enabled where the spawn is sent unreliably and not until the end of the frame.
|
||||
/// either due to it being requested in OnNetworkSpawn before the spawn call has been executed
|
||||
///
|
||||
/// 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.
|
||||
@@ -194,18 +309,10 @@ namespace Unity.Netcode
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the connected client entry exists before trying to remove ownership.
|
||||
if (TryGetNetworkClient(networkObject.OwnerClientId, out NetworkClient networkClient))
|
||||
{
|
||||
for (int i = networkClient.OwnedObjects.Count - 1; i > -1; i--)
|
||||
{
|
||||
if (networkClient.OwnedObjects[i] == networkObject)
|
||||
{
|
||||
networkClient.OwnedObjects.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
// Server removes the entry and takes over ownership before notifying
|
||||
UpdateOwnershipTable(networkObject, NetworkManager.ServerClientId, true);
|
||||
|
||||
networkObject.OwnerClientIdInternal = null;
|
||||
networkObject.OwnerClientId = NetworkManager.ServerClientId;
|
||||
|
||||
var message = new ChangeOwnershipMessage
|
||||
{
|
||||
@@ -219,14 +326,6 @@ namespace Unity.Netcode
|
||||
NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject, size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning($"No connected clients prior to removing ownership for {networkObject.name}. Make sure you are not initializing or shutting down when removing ownership.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to get a network client for a clientId from the NetworkManager.
|
||||
@@ -265,25 +364,10 @@ namespace Unity.Netcode
|
||||
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;
|
||||
|
||||
if (TryGetNetworkClient(clientId, out NetworkClient newNetworkClient))
|
||||
{
|
||||
newNetworkClient.OwnedObjects.Add(networkObject);
|
||||
}
|
||||
// Server adds entries for all client ownership
|
||||
UpdateOwnershipTable(networkObject, networkObject.OwnerClientId);
|
||||
|
||||
var message = new ChangeOwnershipMessage
|
||||
{
|
||||
@@ -414,7 +498,7 @@ namespace Unity.Netcode
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -452,15 +536,12 @@ namespace Unity.Netcode
|
||||
throw new SpawnStateException("Object is already spawned");
|
||||
}
|
||||
|
||||
if (sceneObject.Header.HasNetworkVariables)
|
||||
{
|
||||
networkObject.SetNetworkVariableData(variableData);
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
@@ -474,35 +555,42 @@ namespace Unity.Netcode
|
||||
// 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
|
||||
// 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
|
||||
// after the below initialization code. However cowardice compels me to hold off on moving this until
|
||||
// that commit
|
||||
// the current design banks on getting the network behaviour set and then only reading from it after the
|
||||
// below initialization code. However cowardice compels me to hold off on moving this until that commit
|
||||
networkObject.IsSceneObject = sceneObject;
|
||||
networkObject.NetworkObjectId = networkId;
|
||||
|
||||
networkObject.DestroyWithScene = sceneObject || destroyWithScene;
|
||||
|
||||
networkObject.OwnerClientIdInternal = ownerClientId;
|
||||
networkObject.OwnerClientId = ownerClientId;
|
||||
|
||||
networkObject.IsPlayerObject = playerObject;
|
||||
|
||||
SpawnedObjects.Add(networkObject.NetworkObjectId, networkObject);
|
||||
SpawnedObjectsList.Add(networkObject);
|
||||
|
||||
if (ownerClientId != null)
|
||||
{
|
||||
if (NetworkManager.IsServer)
|
||||
{
|
||||
if (playerObject)
|
||||
{
|
||||
NetworkManager.ConnectedClients[ownerClientId.Value].PlayerObject = networkObject;
|
||||
}
|
||||
else
|
||||
// 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].OwnedObjects.Add(networkObject);
|
||||
NetworkManager.ConnectedClients[ownerClientId].PlayerObject.IsPlayerObject = false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -549,11 +637,8 @@ namespace Unity.Netcode
|
||||
|
||||
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]
|
||||
//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)
|
||||
{
|
||||
return;
|
||||
@@ -568,7 +653,6 @@ namespace Unity.Netcode
|
||||
|
||||
networkObject.MarkVariablesDirty();
|
||||
}
|
||||
}
|
||||
|
||||
internal ulong? GetSpawnParentId(NetworkObject networkObject)
|
||||
{
|
||||
@@ -605,16 +689,14 @@ namespace Unity.Netcode
|
||||
// Makes scene objects ready to be reused
|
||||
internal void ServerResetShudownStateForSceneObjects()
|
||||
{
|
||||
foreach (var sobj in SpawnedObjectsList)
|
||||
{
|
||||
if ((sobj.IsSceneObject != null && sobj.IsSceneObject == true) || sobj.DestroyWithScene)
|
||||
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsSceneObject != null && c.IsSceneObject == true);
|
||||
foreach (var sobj in networkObjects)
|
||||
{
|
||||
sobj.IsSpawned = false;
|
||||
sobj.DestroyWithScene = false;
|
||||
sobj.IsSceneObject = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets called only by NetworkSceneManager.SwitchScene
|
||||
@@ -653,14 +735,12 @@ namespace Unity.Netcode
|
||||
else if (networkObjects[i].IsSpawned)
|
||||
{
|
||||
// If it is an in-scene placed NetworkObject then just despawn
|
||||
// and let it be destroyed when the scene is unloaded. Otherwise,
|
||||
// despawn and destroy it.
|
||||
var shouldDestroy = !(networkObjects[i].IsSceneObject != null
|
||||
&& networkObjects[i].IsSceneObject.Value);
|
||||
// and let it be destroyed when the scene is unloaded. Otherwise, despawn and destroy it.
|
||||
var shouldDestroy = !(networkObjects[i].IsSceneObject != null && networkObjects[i].IsSceneObject.Value);
|
||||
|
||||
OnDespawnObject(networkObjects[i], shouldDestroy);
|
||||
}
|
||||
else
|
||||
else if (networkObjects[i].IsSceneObject != null && !networkObjects[i].IsSceneObject.Value)
|
||||
{
|
||||
UnityEngine.Object.Destroy(networkObjects[i].gameObject);
|
||||
}
|
||||
@@ -711,9 +791,10 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
|
||||
if (NetworkManager != null && NetworkManager.IsServer)
|
||||
@@ -782,12 +851,6 @@ namespace Unity.Netcode
|
||||
});
|
||||
}
|
||||
|
||||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
||||
{
|
||||
networkObject.SnapshotDespawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (networkObject != null)
|
||||
{
|
||||
// As long as we have any remaining clients, then notify of the object being destroy.
|
||||
@@ -817,7 +880,6 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
networkObject.IsSpawned = false;
|
||||
|
||||
|
||||
@@ -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
|
||||
guid: a32aeecf69a2542469927066f5b88005
|
||||
guid: e9ead10b891184bd5b8f2650fd66a5b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
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
|
||||
guid: c275febadb27c4d18b41218e3353b84b
|
||||
guid: ddf8f97f695d740f297dc42242b76b8c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
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
|
||||
guid: bd9e1475e8c8e4a6d935fe2409e3bd26
|
||||
guid: adb0270501ff1421896ce15cc75bd56a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
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.NetStatsReporting",
|
||||
"Unity.Multiplayer.Tools.NetworkSolutionInterface",
|
||||
"Unity.Collections"
|
||||
"Unity.Networking.Transport",
|
||||
"Unity.Collections",
|
||||
"Unity.Burst"
|
||||
],
|
||||
"allowUnsafeCode": true,
|
||||
"versionDefines": [
|
||||
@@ -26,8 +28,8 @@
|
||||
},
|
||||
{
|
||||
"name": "com.unity.multiplayer.tools",
|
||||
"expression": "1.0.0-pre.4",
|
||||
"define": "MULTIPLAYER_TOOLS_1_0_0_PRE_4"
|
||||
"expression": "1.0.0-pre.7",
|
||||
"define": "MULTIPLAYER_TOOLS_1_0_0_PRE_7"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
@@ -111,6 +112,33 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
|
||||
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>
|
||||
/// The very first thing invoked during the <see cref="OneTimeSetup"/> that
|
||||
/// determines how this integration test handles NetworkManager instantiation
|
||||
@@ -130,11 +158,17 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetup()
|
||||
{
|
||||
m_EnableVerboseDebug = OnSetVerboseDebug();
|
||||
|
||||
VerboseDebug($"Entering {nameof(OneTimeSetup)}");
|
||||
|
||||
m_NetworkManagerInstatiationMode = OnSetIntegrationTestMode();
|
||||
|
||||
// Enable NetcodeIntegrationTest auto-label feature
|
||||
NetcodeIntegrationTestHelpers.RegisterNetcodeIntegrationTest(true);
|
||||
OnOneTimeSetup();
|
||||
|
||||
VerboseDebug($"Exiting {nameof(OneTimeSetup)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -153,6 +187,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
[UnitySetUp]
|
||||
public IEnumerator SetUp()
|
||||
{
|
||||
VerboseDebug($"Entering {nameof(SetUp)}");
|
||||
|
||||
yield return OnSetup();
|
||||
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.AllTests && m_ServerNetworkManager == null ||
|
||||
m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.PerTest)
|
||||
@@ -161,6 +197,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
|
||||
yield return StartServerAndClients();
|
||||
}
|
||||
VerboseDebug($"Exiting {nameof(SetUp)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -173,6 +210,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
|
||||
private void CreatePlayerPrefab()
|
||||
{
|
||||
VerboseDebug($"Entering {nameof(CreatePlayerPrefab)}");
|
||||
// Create playerPrefab
|
||||
m_PlayerPrefab = new GameObject("Player");
|
||||
NetworkObject networkObject = m_PlayerPrefab.AddComponent<NetworkObject>();
|
||||
@@ -181,6 +219,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(networkObject);
|
||||
|
||||
OnCreatePlayerPrefab();
|
||||
|
||||
VerboseDebug($"Exiting {nameof(CreatePlayerPrefab)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -207,6 +247,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
/// <param name="numberOfClients"></param>
|
||||
protected void CreateServerAndClients(int numberOfClients)
|
||||
{
|
||||
VerboseDebug($"Entering {nameof(CreateServerAndClients)}");
|
||||
|
||||
CreatePlayerPrefab();
|
||||
|
||||
// Create multiple NetworkManager instances
|
||||
@@ -235,6 +277,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
// Provides opportunity to allow child derived classes to
|
||||
// modify the NetworkManager's configuration before starting.
|
||||
OnServerAndClientsCreated();
|
||||
|
||||
VerboseDebug($"Exiting {nameof(CreateServerAndClients)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -272,6 +316,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
{
|
||||
if (CanStartServerAndClients())
|
||||
{
|
||||
VerboseDebug($"Entering {nameof(StartServerAndClients)}");
|
||||
|
||||
// Start the instances and pass in our SceneManagerInitialization action that is invoked immediately after host-server
|
||||
// is started and after each client is started.
|
||||
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!");
|
||||
|
||||
if (s_GlobalTimeoutHelper.TimedOut)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (m_UseHost || m_ServerNetworkManager.IsHost)
|
||||
{
|
||||
// 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,
|
||||
// started, and connected on both sides.
|
||||
yield return OnServerAndClientsConnected();
|
||||
|
||||
VerboseDebug($"Exiting {nameof(StartServerAndClients)}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,6 +441,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
/// </summary>
|
||||
protected void ShutdownAndCleanUp()
|
||||
{
|
||||
VerboseDebug($"Entering {nameof(ShutdownAndCleanUp)}");
|
||||
// Shutdown and clean up both of our NetworkManager instances
|
||||
try
|
||||
{
|
||||
@@ -427,6 +471,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
|
||||
// reset the m_ServerWaitForTick for the next test to initialize
|
||||
s_DefaultWaitForTick = new WaitForSeconds(1.0f / k_DefaultTickRate);
|
||||
VerboseDebug($"Exiting {nameof(ShutdownAndCleanUp)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -441,12 +486,15 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
[UnityTearDown]
|
||||
public IEnumerator TearDown()
|
||||
{
|
||||
VerboseDebug($"Entering {nameof(TearDown)}");
|
||||
yield return OnTearDown();
|
||||
|
||||
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.PerTest)
|
||||
{
|
||||
ShutdownAndCleanUp();
|
||||
}
|
||||
|
||||
VerboseDebug($"Exiting {nameof(TearDown)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -462,6 +510,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
[OneTimeTearDown]
|
||||
public void OneTimeTearDown()
|
||||
{
|
||||
VerboseDebug($"Entering {nameof(OneTimeTearDown)}");
|
||||
OnOneTimeTearDown();
|
||||
|
||||
if (m_NetworkManagerInstatiationMode == NetworkManagerInstatiationMode.AllTests)
|
||||
@@ -471,6 +520,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
|
||||
// Disable NetcodeIntegrationTest auto-label feature
|
||||
NetcodeIntegrationTestHelpers.RegisterNetcodeIntegrationTest(false);
|
||||
|
||||
VerboseDebug($"Exiting {nameof(OneTimeTearDown)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Unity.Netcode.Transports.UTP;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using Object = UnityEngine.Object;
|
||||
@@ -109,9 +110,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
public enum InstanceTransport
|
||||
{
|
||||
SIP,
|
||||
#if UTP_ADAPTER
|
||||
UTP
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static IntegrationTestSceneHandler ClientSceneHandler = null;
|
||||
@@ -172,12 +171,10 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
||||
switch (instanceTransport)
|
||||
{
|
||||
case InstanceTransport.SIP:
|
||||
default:
|
||||
return go.AddComponent<SIPTransport>();
|
||||
#if UTP_ADAPTER
|
||||
default:
|
||||
case InstanceTransport.UTP:
|
||||
return go.AddComponent<UnityTransport>();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"rootNamespace": "Unity.Netcode.TestHelpers.Runtime",
|
||||
"references": [
|
||||
"Unity.Netcode.Runtime",
|
||||
"Unity.Netcode.Adapter.UTP",
|
||||
"Unity.Multiplayer.MetricTypes",
|
||||
"Unity.Multiplayer.NetStats",
|
||||
"Unity.Multiplayer.Tools.MetricTypes",
|
||||
@@ -18,15 +17,10 @@
|
||||
"expression": "",
|
||||
"define": "MULTIPLAYER_TOOLS"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.netcode.adapter.utp",
|
||||
"expression": "",
|
||||
"define": "UTP_ADAPTER"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.multiplayer.tools",
|
||||
"expression": "1.0.0-pre.4",
|
||||
"define": "MULTIPLAYER_TOOLS_1_0_0_PRE_4"
|
||||
"expression": "1.0.0-pre.7",
|
||||
"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
|
||||
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]
|
||||
public void WhenCallingReadByteWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown()
|
||||
{
|
||||
|
||||
@@ -891,6 +891,29 @@ namespace Unity.Netcode.EditorTests
|
||||
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]
|
||||
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.NetStats",
|
||||
"Unity.Multiplayer.Tools.MetricTypes",
|
||||
"Unity.Multiplayer.Tools.NetStats"
|
||||
"Unity.Multiplayer.Tools.NetStats",
|
||||
"Unity.Networking.Transport"
|
||||
],
|
||||
"optionalUnityReferences": [
|
||||
"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_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
|
||||
// Tracks if NetworkVariable changed invokes the OnValueChanged callback for the given instance type
|
||||
Bool_Var = new NetworkVariableHelper<bool>(m_NetworkVariableBool);
|
||||
@@ -193,7 +153,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
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)
|
||||
{
|
||||
//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();
|
||||
|
||||
// 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)));
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
||||
yield return waitForMetricValues.WaitForMetricsReceived();
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -189,6 +189,76 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
||||
Assert.AreEqual(1, objectDestroyedSentMetricValues.Select(x => x.BytesCount).Distinct().Count());
|
||||
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
|
||||
|
||||
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_1_0_0_PRE_4
|
||||
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using Unity.Collections;
|
||||
@@ -15,11 +15,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
||||
|
||||
protected override void OnOneTimeSetup()
|
||||
{
|
||||
#if UTP_ADAPTER
|
||||
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.UTP;
|
||||
#else
|
||||
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.SIP;
|
||||
#endif
|
||||
base.OnOneTimeSetup();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#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.Generic;
|
||||
@@ -46,11 +46,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
||||
/// </summary>
|
||||
protected override void OnOneTimeSetup()
|
||||
{
|
||||
#if UTP_ADAPTER
|
||||
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.UTP;
|
||||
#else
|
||||
m_NetworkTransport = NetcodeIntegrationTestHelpers.InstanceTransport.SIP;
|
||||
#endif
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
@@ -92,7 +88,7 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
||||
foreach (var clientGaugeMetricValue in clientGaugeMetricValues)
|
||||
{
|
||||
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.Generic;
|
||||
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.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
|
||||
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]
|
||||
public IEnumerator DontDestroyWithOwnerTest()
|
||||
{
|
||||
// create server and client instances
|
||||
NetcodeIntegrationTestHelpers.Create(1, out NetworkManager server, out NetworkManager[] clients);
|
||||
var client = m_ClientNetworkManagers[0];
|
||||
var clientId = client.LocalClientId;
|
||||
var networkObjects = SpawnObjects(m_PrefabToSpawn, m_ClientNetworkManagers[0], k_NumberObjectsToSpawn);
|
||||
|
||||
// create prefab
|
||||
var gameObject = new GameObject("ClientOwnedObject");
|
||||
var networkObject = gameObject.AddComponent<NetworkObject>();
|
||||
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);
|
||||
// wait for object spawn on client to reach k_NumberObjectsToSpawn + 1 (k_NumberObjectsToSpawn and 1 for the player)
|
||||
yield return WaitForConditionOrTimeOut(() => client.SpawnManager.GetClientOwnedObjects(clientId).Count() == k_NumberObjectsToSpawn + 1);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for client to have 33 NetworkObjects spawned! Only {client.SpawnManager.GetClientOwnedObjects(clientId).Count()} were assigned!");
|
||||
|
||||
// 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
|
||||
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++)
|
||||
{
|
||||
var networkObject = networkObjects[i].GetComponent<NetworkObject>();
|
||||
// ensure ownership was transferred back
|
||||
Assert.That(networkObjects[i].OwnerClientId == server.ServerClientId);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
NetcodeIntegrationTestHelpers.Destroy();
|
||||
Assert.That(networkObject.OwnerClientId == m_ServerNetworkManager.LocalClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// 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
|
||||
serverObject.ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
|
||||
@@ -51,8 +51,8 @@ namespace Unity.Netcode.RuntimeTests
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// 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