diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs new file mode 100644 index 0000000000..596831bf8b --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs @@ -0,0 +1,103 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Unity.Netcode +{ + /// + /// A helper struct for serializing s over the network. Can be used in RPCs and . + /// Note: network ids get recycled by the NetworkManager after a while. So a reference pointing to + /// + public struct NetworkBehaviourReference : INetworkSerializable, IEquatable + { + private NetworkObjectReference m_NetworkObjectReference; + private ushort m_NetworkBehaviourId; + + /// + /// Creates a new instance of the struct. + /// + /// The to reference. + /// + public NetworkBehaviourReference(NetworkBehaviour networkBehaviour) + { + if (networkBehaviour == null) + { + throw new ArgumentNullException(nameof(networkBehaviour)); + } + if (networkBehaviour.NetworkObject == null) + { + throw new ArgumentException($"Cannot create {nameof(NetworkBehaviourReference)} from {nameof(NetworkBehaviour)} without a {nameof(NetworkObject)}."); + } + + m_NetworkObjectReference = networkBehaviour.NetworkObject; + m_NetworkBehaviourId = networkBehaviour.NetworkBehaviourId; + } + + /// + /// Tries to get the referenced by this reference. + /// + /// The which was found. Null if the corresponding was not found. + /// The networkmanager. Uses to resolve if null. + /// True if the was found; False if the was not found. This can happen if the corresponding has not been spawned yet. you can try getting the reference at a later point in time. + public bool TryGet(out NetworkBehaviour networkBehaviour, NetworkManager networkManager = null) + { + networkBehaviour = GetInternal(this, null); + return networkBehaviour != null; + } + + /// + /// Tries to get the referenced by this reference. + /// + /// The which was found. Null if the corresponding was not found. + /// The networkmanager. Uses to resolve if null. + /// The type of the networkBehaviour for convenience. + /// True if the was found; False if the was not found. This can happen if the corresponding has not been spawned yet. you can try getting the reference at a later point in time. + public bool TryGet(out T networkBehaviour, NetworkManager networkManager = null) where T : NetworkBehaviour + { + networkBehaviour = (T)GetInternal(this, null); + return networkBehaviour != null; + } + + /// + public void NetworkSerialize(NetworkSerializer serializer) + { + m_NetworkObjectReference.NetworkSerialize(serializer); + serializer.Serialize(ref m_NetworkBehaviourId); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static NetworkBehaviour GetInternal(NetworkBehaviourReference networkBehaviourRef, NetworkManager networkManager = null) + { + if (networkBehaviourRef.m_NetworkObjectReference.TryGet(out NetworkObject networkObject, networkManager)) + { + return networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourRef.m_NetworkBehaviourId); + } + + return null; + } + + /// + public bool Equals(NetworkBehaviourReference other) + { + return m_NetworkObjectReference.Equals(other.m_NetworkObjectReference) && m_NetworkBehaviourId == other.m_NetworkBehaviourId; + } + + /// + public override bool Equals(object obj) + { + return obj is NetworkBehaviourReference other && Equals(other); + } + + /// + public override int GetHashCode() + { + unchecked + { + return (m_NetworkObjectReference.GetHashCode() * 397) ^ m_NetworkBehaviourId.GetHashCode(); + } + } + + public static implicit operator NetworkBehaviour(NetworkBehaviourReference networkBehaviourRef) => GetInternal(networkBehaviourRef); + + public static implicit operator NetworkBehaviourReference(NetworkBehaviour networkBehaviour) => new NetworkBehaviourReference(networkBehaviour); + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs.meta new file mode 100644 index 0000000000..a65b9efd02 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkBehaviourReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0a9cea52f48ea70499020aebe4073ba8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs new file mode 100644 index 0000000000..783749ec32 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs @@ -0,0 +1,131 @@ +using System; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace Unity.Netcode +{ + /// + /// A helper struct for serializing s over the network. Can be used in RPCs and . + /// + public struct NetworkObjectReference : INetworkSerializable, IEquatable + { + private ulong m_NetworkObjectId; + + /// + /// The of the referenced . + /// + public ulong NetworkObjectId + { + get => m_NetworkObjectId; + internal set => m_NetworkObjectId = value; + } + + /// + /// Creates a new instance of the struct. + /// + /// The to reference. + /// + /// + public NetworkObjectReference(NetworkObject networkObject) + { + if (networkObject == null) + { + throw new ArgumentNullException(nameof(networkObject)); + } + + if (networkObject.IsSpawned == false) + { + throw new ArgumentException($"{nameof(NetworkObjectReference)} can only be created from spawned {nameof(NetworkObject)}s."); + } + + m_NetworkObjectId = networkObject.NetworkObjectId; + } + + /// + /// Creates a new instance of the struct. + /// + /// The GameObject from which the component will be referenced. + /// + /// + public NetworkObjectReference(GameObject gameObject) + { + if (gameObject == null) + { + throw new ArgumentNullException(nameof(gameObject)); + } + + var networkObject = gameObject.GetComponent(); + + if (networkObject == null) + { + throw new ArgumentException($"Cannot create {nameof(NetworkObjectReference)} from {nameof(GameObject)} without a {nameof(NetworkObject)} component."); + } + + if (networkObject.IsSpawned == false) + { + throw new ArgumentException($"{nameof(NetworkObjectReference)} can only be created from spawned {nameof(NetworkObject)}s."); + } + + m_NetworkObjectId = networkObject.NetworkObjectId; + } + + /// + /// Tries to get the referenced by this reference. + /// + /// The which was found. Null if no object was found. + /// The networkmanager. Uses to resolve if null. + /// True if the was found; False if the was not found. This can happen if the has not been spawned yet. you can try getting the reference at a later point in time. + public bool TryGet(out NetworkObject networkObject, NetworkManager networkManager = null) + { + networkObject = Resolve(this, networkManager); + return networkObject != null; + } + + /// + public void NetworkSerialize(NetworkSerializer serializer) + { + serializer.Serialize(ref m_NetworkObjectId); + } + + /// + /// Resolves the corresponding for this reference. + /// + /// The reference. + /// The networkmanager. Uses to resolve if null. + /// The resolves . Returns null if the networkobject was not found + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static NetworkObject Resolve(NetworkObjectReference networkObjectRef, NetworkManager networkManager = null) + { + networkManager = networkManager != null ? networkManager : NetworkManager.Singleton; + networkManager.SpawnManager.SpawnedObjects.TryGetValue(networkObjectRef.m_NetworkObjectId, out NetworkObject networkObject); + + return networkObject; + } + + /// + public bool Equals(NetworkObjectReference other) + { + return m_NetworkObjectId == other.m_NetworkObjectId; + } + + /// + public override bool Equals(object obj) + { + return obj is NetworkObjectReference other && Equals(other); + } + + /// + public override int GetHashCode() + { + return m_NetworkObjectId.GetHashCode(); + } + + public static implicit operator NetworkObject(NetworkObjectReference networkObjectRef) => Resolve(networkObjectRef); + + public static implicit operator NetworkObjectReference(NetworkObject networkObject) => new NetworkObjectReference(networkObject); + + public static implicit operator GameObject(NetworkObjectReference networkObjectRef) => Resolve(networkObjectRef).gameObject; + + public static implicit operator NetworkObjectReference(GameObject gameObject) => new NetworkObjectReference(gameObject); + } +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs.meta new file mode 100644 index 0000000000..fecff26098 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/NetworkObjectReference.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 463f3b530aad5d849964ee157646818e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Serialization.meta b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization.meta new file mode 100644 index 0000000000..1f1dc0f8b1 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 90d51da7691e302498265bba08c43636 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkBehaviourReferenceTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkBehaviourReferenceTests.cs new file mode 100644 index 0000000000..bcc7119a00 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkBehaviourReferenceTests.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Unity.Netcode.RuntimeTests.Serialization +{ + /// + /// Unit tests to test: + /// - Serializing NetworkObject to NetworkObjectReference + /// - Deserializing NetworkObjectReference to NetworkObject + /// - Implicit operators of NetworkObjectReference + /// + public class NetworkBehaviourReferenceTests : IDisposable + { + private class TestNetworkBehaviour : NetworkBehaviour + { + public NetworkVariable TestVariable = new NetworkVariable(); + + public TestNetworkBehaviour RpcReceivedBehaviour; + + [ServerRpc] + public void SendReferenceServerRpc(NetworkBehaviourReference value) + { + RpcReceivedBehaviour = (TestNetworkBehaviour)value; + } + } + + [UnityTest] + public IEnumerator TestRpc() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + var testNetworkBehaviour = networkObjectContext.Object.gameObject.AddComponent(); + networkObjectContext.Object.Spawn(); + + using var otherObjectContext = UnityObjectContext.CreateNetworkObject(); + otherObjectContext.Object.Spawn(); + + testNetworkBehaviour.SendReferenceServerRpc(new NetworkBehaviourReference(testNetworkBehaviour)); + + // wait for rpc completion + float t = 0; + while (testNetworkBehaviour.RpcReceivedBehaviour == null) + { + t += Time.deltaTime; + if (t > 5f) + { + new AssertionException("RPC with NetworkBehaviour reference hasn't been received"); + } + + yield return null; + } + + // validate + Assert.AreEqual(testNetworkBehaviour, testNetworkBehaviour.RpcReceivedBehaviour); + } + + [UnityTest] + public IEnumerator TestRpcImplicitNetworkBehaviour() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + var testNetworkBehaviour = networkObjectContext.Object.gameObject.AddComponent(); + networkObjectContext.Object.Spawn(); + + using var otherObjectContext = UnityObjectContext.CreateNetworkObject(); + otherObjectContext.Object.Spawn(); + + testNetworkBehaviour.SendReferenceServerRpc(testNetworkBehaviour); + + // wait for rpc completion + float t = 0; + while (testNetworkBehaviour.RpcReceivedBehaviour == null) + { + t += Time.deltaTime; + if (t > 5f) + { + new AssertionException("RPC with NetworkBehaviour reference hasn't been received"); + } + + yield return null; + } + + // validate + Assert.AreEqual(testNetworkBehaviour, testNetworkBehaviour.RpcReceivedBehaviour); + } + + [Test] + public void TestNetworkVariable() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + var testNetworkBehaviour = networkObjectContext.Object.gameObject.AddComponent(); + networkObjectContext.Object.Spawn(); + + using var otherObjectContext = UnityObjectContext.CreateNetworkObject(); + otherObjectContext.Object.Spawn(); + + // check default value is null + Assert.IsNull((NetworkBehaviour)testNetworkBehaviour.TestVariable.Value); + + testNetworkBehaviour.TestVariable.Value = testNetworkBehaviour; + + Assert.AreEqual((NetworkBehaviour)testNetworkBehaviour.TestVariable.Value, testNetworkBehaviour); + } + + [Test] + public void FailSerializeNonSpawnedNetworkObject() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + var component = networkObjectContext.Object.gameObject.AddComponent(); + + Assert.Throws(() => + { + NetworkBehaviourReference outReference = component; + }); + } + + [Test] + public void FailSerializeGameObjectWithoutNetworkObject() + { + using var gameObjectContext = UnityObjectContext.CreateGameObject(); + var component = gameObjectContext.Object.gameObject.AddComponent(); + + Assert.Throws(() => + { + NetworkBehaviourReference outReference = component; + }); + } + + [Test] + public void FailSerializeNullBehaviour() + { + Assert.Throws(() => + { + NetworkBehaviourReference outReference = null; + }); + } + + public void Dispose() + { + //Stop, shutdown, and destroy + NetworkManagerHelper.ShutdownNetworkManager(); + } + + public NetworkBehaviourReferenceTests() + { + //Create, instantiate, and host + NetworkManagerHelper.StartNetworkManager(out _); + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkBehaviourReferenceTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkBehaviourReferenceTests.cs.meta new file mode 100644 index 0000000000..6ae11f3c32 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkBehaviourReferenceTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0fca807d195f9fc49a400cfce86b085d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs new file mode 100644 index 0000000000..4d2c3b3cf4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs @@ -0,0 +1,335 @@ +using System; +using System.Collections; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using Object = UnityEngine.Object; + +namespace Unity.Netcode.RuntimeTests.Serialization +{ + /// + /// Unit tests to test: + /// - Serializing NetworkObject to NetworkObjectReference + /// - Deserializing NetworkObjectReference to NetworkObject + /// - Implicit operators of NetworkObjectReference + /// + public class NetworkObjectReferenceTests : IDisposable + { + private class TestNetworkBehaviour : NetworkBehaviour + { + public NetworkVariable TestVariable = new NetworkVariable(); + + public NetworkObject RpcReceivedNetworkObject; + + public GameObject RpcReceivedGameObject; + + [ServerRpc] + public void SendReferenceServerRpc(NetworkObjectReference value) + { + RpcReceivedGameObject = value; + RpcReceivedNetworkObject = value; + } + } + + [Test] + public void TestSerializeNetworkObject() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + networkObjectContext.Object.Spawn(); + using var outStream = PooledNetworkBuffer.Get(); + using var outWriter = PooledNetworkWriter.Get(outStream); + using var inStream = PooledNetworkBuffer.Get(); + using var inReader = PooledNetworkReader.Get(inStream); + + // serialize + var outSerializer = new NetworkSerializer(outWriter); + NetworkObjectReference outReference = networkObjectContext.Object; + outReference.NetworkSerialize(outSerializer); + + // deserialize + NetworkObjectReference inReference = default; + inStream.Write(outStream.ToArray()); + inStream.Position = 0; + var inSerializer = new NetworkSerializer(inReader); + inReference.NetworkSerialize(inSerializer); + + // validate + Assert.NotNull((NetworkObject)inReference); + Assert.AreEqual(inReference.NetworkObjectId, networkObjectContext.Object.NetworkObjectId); + Assert.AreEqual(outReference, inReference); + Assert.AreEqual(networkObjectContext.Object, (NetworkObject)inReference); + } + + [Test] + public void TestSerializeGameObject() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + networkObjectContext.Object.Spawn(); + using var outStream = PooledNetworkBuffer.Get(); + using var outWriter = PooledNetworkWriter.Get(outStream); + using var inStream = PooledNetworkBuffer.Get(); + using var inReader = PooledNetworkReader.Get(inStream); + + // serialize + var outSerializer = new NetworkSerializer(outWriter); + NetworkObjectReference outReference = networkObjectContext.Object.gameObject; + outReference.NetworkSerialize(outSerializer); + + // deserialize + NetworkObjectReference inReference = default; + inStream.Write(outStream.ToArray()); + inStream.Position = 0; + var inSerializer = new NetworkSerializer(inReader); + inReference.NetworkSerialize(inSerializer); + GameObject gameObject = inReference; + + // validate + Assert.AreEqual(outReference, inReference); + Assert.AreEqual(networkObjectContext.Object.gameObject, gameObject); + } + + [Test] + public void TestTryGet() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + networkObjectContext.Object.Spawn(); + + NetworkObjectReference networkObjectReference = networkObjectContext.Object; + + Assert.True(networkObjectReference.TryGet(out NetworkObject networkObject)); + Assert.NotNull(networkObject); + networkObjectReference.TryGet(out NetworkObject result); + Assert.AreEqual(networkObject, result); + } + + [UnityTest] + public IEnumerator TestRpc() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + var testNetworkBehaviour = networkObjectContext.Object.gameObject.AddComponent(); + networkObjectContext.Object.Spawn(); + + using var otherObjectContext = UnityObjectContext.CreateNetworkObject(); + otherObjectContext.Object.Spawn(); + + testNetworkBehaviour.SendReferenceServerRpc(new NetworkObjectReference(otherObjectContext.Object)); + + // wait for rpc completion + float t = 0; + while (testNetworkBehaviour.RpcReceivedGameObject == null) + { + t += Time.deltaTime; + if (t > 5f) + { + new AssertionException("RPC with NetworkBehaviour reference hasn't been received"); + } + + yield return null; + } + + // validate + Assert.AreEqual(otherObjectContext.Object, testNetworkBehaviour.RpcReceivedNetworkObject); + Assert.AreEqual(otherObjectContext.Object.gameObject, testNetworkBehaviour.RpcReceivedGameObject); + } + + [UnityTest] + public IEnumerator TestRpcImplicitNetworkObject() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + var testNetworkBehaviour = networkObjectContext.Object.gameObject.AddComponent(); + networkObjectContext.Object.Spawn(); + + using var otherObjectContext = UnityObjectContext.CreateNetworkObject(); + otherObjectContext.Object.Spawn(); + + testNetworkBehaviour.SendReferenceServerRpc(otherObjectContext.Object); + + // wait for rpc completion + float t = 0; + while (testNetworkBehaviour.RpcReceivedGameObject == null) + { + t += Time.deltaTime; + if (t > 5f) + { + new AssertionException("RPC with NetworkBehaviour reference hasn't been received"); + } + + yield return null; + } + + // validate + Assert.AreEqual(otherObjectContext.Object, testNetworkBehaviour.RpcReceivedNetworkObject); + Assert.AreEqual(otherObjectContext.Object.gameObject, testNetworkBehaviour.RpcReceivedGameObject); + } + + [UnityTest] + public IEnumerator TestRpcImplicitGameObject() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + var testNetworkBehaviour = networkObjectContext.Object.gameObject.AddComponent(); + networkObjectContext.Object.Spawn(); + + using var otherObjectContext = UnityObjectContext.CreateNetworkObject(); + otherObjectContext.Object.Spawn(); + + testNetworkBehaviour.SendReferenceServerRpc(otherObjectContext.Object.gameObject); + + // wait for rpc completion + float t = 0; + while (testNetworkBehaviour.RpcReceivedGameObject == null) + { + t += Time.deltaTime; + if (t > 5f) + { + new AssertionException("RPC with NetworkBehaviour reference hasn't been received"); + } + + yield return null; + } + + // validate + Assert.AreEqual(otherObjectContext.Object, testNetworkBehaviour.RpcReceivedNetworkObject); + Assert.AreEqual(otherObjectContext.Object.gameObject, testNetworkBehaviour.RpcReceivedGameObject); + } + + [Test] + public void TestNetworkVariable() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + var testNetworkBehaviour = networkObjectContext.Object.gameObject.AddComponent(); + networkObjectContext.Object.Spawn(); + + using var otherObjectContext = UnityObjectContext.CreateNetworkObject(); + otherObjectContext.Object.Spawn(); + + // check default value is null + Assert.IsNull((NetworkObject)testNetworkBehaviour.TestVariable.Value); + + testNetworkBehaviour.TestVariable.Value = networkObjectContext.Object; + + Assert.AreEqual((GameObject)testNetworkBehaviour.TestVariable.Value, networkObjectContext.Object.gameObject); + Assert.AreEqual((NetworkObject)testNetworkBehaviour.TestVariable.Value, networkObjectContext.Object); + } + + [Test] + public void TestDespawn() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + networkObjectContext.Object.Spawn(); + var originalId = networkObjectContext.Object.NetworkObjectId; + + NetworkObjectReference networkObjectReference = networkObjectContext.Object; + Assert.AreEqual(networkObjectContext.Object, (NetworkObject)networkObjectReference); + + networkObjectContext.Object.Despawn(); + Assert.IsFalse(networkObjectReference.TryGet(out NetworkObject _)); + + networkObjectContext.Object.Spawn(); + + // After spawning again the reference will still no longer work as it still points to the old object + Assert.AreNotEqual(originalId, networkObjectContext.Object.NetworkObjectId); + Assert.IsFalse(networkObjectReference.TryGet(out NetworkObject _)); + + // creating a new reference will make it work again + networkObjectReference = networkObjectContext.Object; + Assert.AreEqual(networkObjectContext.Object, (NetworkObject)networkObjectReference); + } + + [Test] + public void FailSerializeNonSpawnedNetworkObject() + { + using var networkObjectContext = UnityObjectContext.CreateNetworkObject(); + + Assert.Throws(() => + { + NetworkObjectReference outReference = networkObjectContext.Object; + }); + } + + [Test] + public void FailSerializeGameObjectWithoutNetworkObject() + { + using var gameObjectContext = UnityObjectContext.CreateGameObject(); + + Assert.Throws(() => + { + NetworkObjectReference outReference = gameObjectContext.Object; + }); + } + + [Test] + public void FailSerializeNullNetworkObject() + { + Assert.Throws(() => + { + NetworkObjectReference outReference = (NetworkObject)null; + }); + } + + [Test] + public void FailSerializeNullGameObject() + { + Assert.Throws(() => + { + NetworkObjectReference outReference = (GameObject)null; + }); + } + + public void Dispose() + { + //Stop, shutdown, and destroy + NetworkManagerHelper.ShutdownNetworkManager(); + } + + public NetworkObjectReferenceTests() + { + //Create, instantiate, and host + NetworkManagerHelper.StartNetworkManager(out _); + } + } + + /// + /// Helper method for tests to create and destroy Unity Objects. + /// + /// The type of Object this context incorporates. + public class UnityObjectContext : UnityObjectContext where T : Object + { + private T m_Object; + + internal UnityObjectContext(T unityObject, Object root) + : base(root) + { + m_Object = unityObject; + } + + public T Object => m_Object; + } + + public class UnityObjectContext : IDisposable + { + private Object m_Root; + + protected UnityObjectContext(Object root) + { + m_Root = root; + } + + public static UnityObjectContext CreateGameObject(string name = "") + { + var gameObject = new GameObject(name); + return new UnityObjectContext(gameObject, gameObject); + } + + public static UnityObjectContext CreateNetworkObject(string name = "") + { + var gameObject = new GameObject(name); + var networkObject = gameObject.AddComponent(); + return new UnityObjectContext(networkObject, gameObject); + } + + public void Dispose() + { + Object.DestroyImmediate(m_Root); + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs.meta new file mode 100644 index 0000000000..bcaad14452 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 182c2000b73248b4bbdd79f70ec90cd2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Scripts/PingPongMover.cs b/testproject/Assets/Scripts/PingPongMover.cs new file mode 100644 index 0000000000..4e93ffdb6b --- /dev/null +++ b/testproject/Assets/Scripts/PingPongMover.cs @@ -0,0 +1,23 @@ +using UnityEngine; + +public class PingPongMover : MonoBehaviour +{ + public Vector3 Direction; + public float Time; + + private Vector3 m_StartPosition; + + // Start is called before the first frame update + private void Start() + { + m_StartPosition = transform.position; + } + + // Update is called once per frame + private void Update() + { + var t = Mathf.PingPong(UnityEngine.Time.time, Time); + var offset = Vector3.Lerp(Vector3.zero, Direction, t); + transform.position = m_StartPosition + offset; + } +} diff --git a/testproject/Assets/Scripts/PingPongMover.cs.meta b/testproject/Assets/Scripts/PingPongMover.cs.meta new file mode 100644 index 0000000000..2606d32f7f --- /dev/null +++ b/testproject/Assets/Scripts/PingPongMover.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1ce1983eca18fbf449477e5e138f44a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: