diff --git a/.yamato/_triggers.yml b/.yamato/_triggers.yml index 72145210c2..8b8d74f86b 100644 --- a/.yamato/_triggers.yml +++ b/.yamato/_triggers.yml @@ -87,8 +87,10 @@ pr_code_changes_checks: # Coverage on other standalone machines is present in Nightly job so it's enough to not run all of them for PRs # desktop_standalone_test and cmb_service_standalone_test are both reusing desktop_standalone_build dependency so we run those in the same configuration on PRs to reduce waiting time. # Note that our daily tests will anyway run both test configurations in "minimal supported" and "trunk" configurations - - .yamato/desktop-standalone-tests.yml#desktop_standalone_test_testproject_ubuntu_il2cpp_trunk - - .yamato/cmb-service-standalone-tests.yml#cmb_service_standalone_test_testproject_ubuntu_il2cpp_trunk + + # TODO: Move these tests back to trunk once CMB Service has addressed https://jira.unity3d.com/browse/MTTB-1680 + - .yamato/desktop-standalone-tests.yml#desktop_standalone_test_testproject_ubuntu_il2cpp_6000.4 + - .yamato/cmb-service-standalone-tests.yml#cmb_service_standalone_test_testproject_ubuntu_il2cpp_6000.4 triggers: expression: |- (pull_request.comment eq "ngo" OR diff --git a/.yamato/project.metafile b/.yamato/project.metafile index bc3de36f73..13f7801cf7 100644 --- a/.yamato/project.metafile +++ b/.yamato/project.metafile @@ -162,6 +162,7 @@ validation_editors: - 6000.0 - 6000.2 - 6000.3 + - 6000.4 - trunk minimal: - 6000.0 diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index ab7434e0fa..beeb949d80 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -11,6 +11,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Added - Added NetworkRigidbody documentation section. (#3664) +- Added new fields to the `SceneMap` struct when using Unity 6.3 or higher. These fields allow referencing scene handles via the new `SceneHandle` struct. (#3734) ### Changed @@ -24,6 +25,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Deprecated +- On Unity 6.5 some `SceneMap` fields that use an `int` to represent a `SceneHandle` are deprecated. (#3734) ### Removed diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneHandle.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneHandle.cs index c0cb3fc042..e544eb1f24 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneHandle.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneHandle.cs @@ -36,7 +36,7 @@ public void NetworkSerialize(BufferSerializer serializer) where T : IReade var reader = serializer.GetFastBufferReader(); // DANGO-TODO Rust needs to be updated to either handle this ulong or to remove the scene store. #if SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG - reader.ReadValue(out ulong rawData); + reader.ReadValueSafe(out ulong rawData); m_Handle = SceneHandle.FromRawData(rawData); #elif SCENE_MANAGEMENT_SCENE_HANDLE_NO_INT_CONVERSION reader.ReadValueSafe(out int rawData); @@ -92,8 +92,14 @@ internal NetworkSceneHandle(int handle, bool asMock) /// /// Implicit conversion from to . /// - /// + /// The SceneHandle to covert public static implicit operator NetworkSceneHandle(SceneHandle handle) => new(handle); + + /// + /// Implicit conversion from to . + /// + /// The NetworkSceneHandle to convert + public static implicit operator SceneHandle(NetworkSceneHandle handle) => handle.m_Handle; #else /// /// Implicit conversion from to . diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 8f852a0fd6..6ab3e930f2 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -3115,19 +3115,101 @@ public struct SceneMap : INetworkSerializable /// The name of the scene /// public string SceneName; + +#if SCENE_MANAGEMENT_SCENE_HANDLE_NO_INT_CONVERSION + /// + /// The scene's server handle (a.k.a network scene handle) + /// + /// + /// This is deprecated in favor of ServerSceneHandle + /// + [Obsolete("Int representation of a SceneHandle is deprecated, please use SceneHandle instead.")] +#else /// /// The scene's server handle (a.k.a network scene handle) /// +#endif public int ServerHandle; + +#if SCENE_MANAGEMENT_SCENE_HANDLE_NO_INT_CONVERSION /// /// The mapped handled. This could be the ServerHandle or LocalHandle depending upon context (client or server). /// + /// + /// This is deprecated in favor of MappedLocalSceneHandle + /// + [Obsolete("Int representation of a SceneHandle is deprecated, please use SceneHandle instead.")] +#else + /// + /// The mapped handled. This could be the ServerHandle or LocalHandle depending upon context (client or server). + /// +#endif public int MappedLocalHandle; + +#if SCENE_MANAGEMENT_SCENE_HANDLE_NO_INT_CONVERSION + /// + /// The local handle of the scene. + /// + /// + /// This is deprecated in favor of LocalSceneHandle + /// + [Obsolete("Int representation of a SceneHandle is deprecated, please use SceneHandle instead.")] +#else /// /// The local handle of the scene. /// +#endif public int LocalHandle; +#if SCENE_MANAGEMENT_SCENE_HANDLE_AVAILABLE + /// + /// The scene's server handle (a.k.a network scene handle) + /// + public SceneHandle ServerSceneHandle; + /// + /// The mapped handled. This could be the ServerSceneHandle or LocalSceneHandle depending upon context (client or server). + /// + public SceneHandle MappedLocalSceneHandle; + /// + /// The local handle of the scene. + /// + public SceneHandle LocalSceneHandle; +#endif + + private NetworkSceneHandle m_ServerHandle; + private NetworkSceneHandle m_MappedLocalHandle; + private NetworkSceneHandle m_LocalHandle; + + internal SceneMap(MapTypes mapType, Scene scene, bool isScenePresent, NetworkSceneHandle serverHandle, NetworkSceneHandle mappedLocalHandle) + { + MapType = mapType; + Scene = scene; + ScenePresent = isScenePresent; + SceneName = isScenePresent ? scene.name : "Not Present"; + + m_ServerHandle = serverHandle; + m_MappedLocalHandle = mappedLocalHandle; + m_LocalHandle = new NetworkSceneHandle(scene.handle); + +#if SCENE_MANAGEMENT_SCENE_HANDLE_AVAILABLE + ServerSceneHandle = serverHandle; + MappedLocalSceneHandle = mappedLocalHandle; + LocalSceneHandle = scene.handle; +#endif + +#pragma warning disable CS0618 // Type or member is obsolete +#if SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG + ServerHandle = (int)m_ServerHandle.GetRawData(); + MappedLocalHandle = (int)m_MappedLocalHandle.GetRawData(); + LocalHandle = (int)m_LocalHandle.GetRawData(); +#else + ServerHandle = m_ServerHandle.GetRawData(); + MappedLocalHandle = m_MappedLocalHandle.GetRawData(); + LocalHandle = m_LocalHandle.GetRawData(); +#endif +#pragma warning restore CS0618 // Type or member is obsolete + } + /// public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { @@ -3140,11 +3222,45 @@ public void NetworkSerialize(BufferSerializer serializer) where T : IReade if (ScenePresent) { serializer.SerializeValue(ref SceneName); +#pragma warning disable CS0618 // Type or member is obsolete serializer.SerializeValue(ref LocalHandle); - } serializer.SerializeValue(ref ServerHandle); serializer.SerializeValue(ref MappedLocalHandle); +#pragma warning restore CS0618 // Type or member is obsolete + + +#if SCENE_MANAGEMENT_SCENE_HANDLE_AVAILABLE + // Ensure the SceneHandles are valid to be serialized + if (serializer.IsWriter) + { + if (m_LocalHandle.IsEmpty() && LocalSceneHandle != SceneHandle.None) + { + m_LocalHandle = LocalSceneHandle; + } + if (m_ServerHandle.IsEmpty() && ServerSceneHandle != SceneHandle.None) + { + m_ServerHandle = ServerSceneHandle; + } + if (m_MappedLocalHandle.IsEmpty() && MappedLocalSceneHandle != SceneHandle.None) + { + m_MappedLocalHandle = MappedLocalSceneHandle; + } + } + + // Serialize the INetworkSerializable representations + serializer.SerializeValue(ref m_LocalHandle); + serializer.SerializeValue(ref m_ServerHandle); + serializer.SerializeValue(ref m_MappedLocalHandle); + + // If we're reading, convert back into the raw SceneHandle + if (serializer.IsReader) + { + ServerSceneHandle = m_ServerHandle; + ServerSceneHandle = m_LocalHandle; + ServerSceneHandle = m_MappedLocalHandle; + } +#endif } } @@ -3156,43 +3272,14 @@ public void NetworkSerialize(BufferSerializer serializer) where T : IReade public List GetSceneMapping(MapTypes mapType) { var mapping = new List(); - if (mapType == MapTypes.ServerToClient) - { - foreach (var entry in ServerSceneHandleToClientSceneHandle) - { - var scene = ScenesLoaded[entry.Value]; - var sceneIsPresent = scene.IsValid() && scene.isLoaded; - var sceneMap = new SceneMap() - { - MapType = mapType, - ServerHandle = entry.Key.GetRawData(), - MappedLocalHandle = entry.Value.GetRawData(), - LocalHandle = new NetworkSceneHandle(scene.handle).GetRawData(), - Scene = scene, - ScenePresent = sceneIsPresent, - SceneName = sceneIsPresent ? scene.name : "NotPresent", - }; - mapping.Add(sceneMap); - } - } - else + var map = mapType == MapTypes.ServerToClient ? ServerSceneHandleToClientSceneHandle : ClientSceneHandleToServerSceneHandle; + + foreach (var entry in map) { - foreach (var entry in ClientSceneHandleToServerSceneHandle) - { - var scene = ScenesLoaded[entry.Key]; - var sceneIsPresent = scene.IsValid() && scene.isLoaded; - var sceneMap = new SceneMap() - { - MapType = mapType, - ServerHandle = entry.Key.GetRawData(), - MappedLocalHandle = entry.Value.GetRawData(), - LocalHandle = new NetworkSceneHandle(scene.handle).GetRawData(), - Scene = scene, - ScenePresent = sceneIsPresent, - SceneName = sceneIsPresent ? scene.name : "NotPresent", - }; - mapping.Add(sceneMap); - } + var scene = ScenesLoaded[entry.Key]; + var sceneIsPresent = scene.IsValid() && scene.isLoaded; + var sceneMap = new SceneMap(mapType, scene, sceneIsPresent, entry.Key, entry.Value); + mapping.Add(sceneMap); } return mapping; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/NetworkSceneHandleTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/NetworkSceneHandleTests.cs new file mode 100644 index 0000000000..8be50e5530 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/NetworkSceneHandleTests.cs @@ -0,0 +1,57 @@ +using NUnit.Framework; +using Unity.Collections; + +namespace Unity.Netcode.EditorTests +{ + internal class NetworkSceneHandleTests + { + [Test] + public void NetworkSceneHandleSerializationTest() + { + var handle = new NetworkSceneHandle(1234, true); + + using var writer = new FastBufferWriter(sizeof(ulong), Allocator.Temp); + Assert.That(writer.Position, Is.EqualTo(0), "Writer position should be zero"); + + writer.WriteNetworkSerializable(handle); +#if SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG + Assert.That(writer.Position, Is.EqualTo(sizeof(ulong)), $"Writer position should not be beyond size! Expected: {sizeof(ulong)} Actual: {writer.Position}"); +#else + Assert.That(writer.Position, Is.EqualTo(sizeof(int)), $"Writer position should not be beyond size! Expected: {sizeof(int)} Actual: {writer.Position}"); +#endif + + var reader = new FastBufferReader(writer, Allocator.Temp); + Assert.That(reader.Position, Is.EqualTo(0), "Reader position should be zero"); + reader.ReadNetworkSerializable(out NetworkSceneHandle deserializedHandle); +#if SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG + Assert.That(reader.Position, Is.EqualTo(sizeof(ulong)), $"Reader position should not be beyond size! Expected: {sizeof(ulong)} Actual: {reader.Position}"); +#else + Assert.That(reader.Position, Is.EqualTo(sizeof(int)), $"Reader position should not be beyond size! Expected: {sizeof(int)} Actual: {reader.Position}"); +#endif + + Assert.AreEqual(handle, deserializedHandle); + + // Now serialize a list of SceneHandles + var handles = new NetworkSceneHandle[] { handle, new NetworkSceneHandle(4567, true), new NetworkSceneHandle(7890, true) }; + + using var listWriter = new FastBufferWriter(1024, Allocator.Temp); + + Assert.That(listWriter.Position, Is.EqualTo(0), "Writer position should be zero"); + + listWriter.WriteNetworkSerializable(handles); +#if SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG + var expectedSize = sizeof(int) + (sizeof(ulong) * handles.Length); +#else + var expectedSize = sizeof(int) + (sizeof(int) * handles.Length); +#endif + Assert.That(listWriter.Position, Is.EqualTo(expectedSize), $"Writer position should not be beyond size! Expected: {expectedSize} Actual: {listWriter.Position}"); + + var listReader = new FastBufferReader(listWriter, Allocator.Temp); + Assert.That(listReader.Position, Is.EqualTo(0), "Reader position should be zero"); + listReader.ReadNetworkSerializable(out NetworkSceneHandle[] deserializedHandleList); + Assert.That(listReader.Position, Is.EqualTo(expectedSize), $"Reader position should not be beyond expected size! Expected: {expectedSize} Actual: {listReader.Position}"); + + Assert.AreEqual(handles, deserializedHandleList); + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/NetworkSceneHandleTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/NetworkSceneHandleTests.cs.meta new file mode 100644 index 0000000000..321da91062 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/NetworkSceneHandleTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3c489c7005c84ba4a42d90c28a3d0536 +timeCreated: 1761163221 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef b/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef index f45952ca0e..c213a6de64 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef +++ b/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef @@ -43,6 +43,16 @@ "name": "Unity", "expression": "6000.1.0a1", "define": "HOSTNAME_RESOLUTION_AVAILABLE" + }, + { + "name": "Unity", + "expression": "6000.4.0a3", + "define": "SCENE_MANAGEMENT_SCENE_HANDLE_NO_INT_CONVERSION" + }, + { + "name": "Unity", + "expression": "6000.5.0a1", + "define": "SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG" } ], "noEngineReferences": false