diff --git a/.github/workflows/conventional-pr.yml b/.github/workflows/conventional-pr.yml index 30274254e2..e5176d4912 100644 --- a/.github/workflows/conventional-pr.yml +++ b/.github/workflows/conventional-pr.yml @@ -6,6 +6,7 @@ on: branches: - develop - develop-2.0.0 + - develop-3.x.x # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" diff --git a/.yamato/_triggers.yml b/.yamato/_triggers.yml index d51d6b3940..87af9083fb 100644 --- a/.yamato/_triggers.yml +++ b/.yamato/_triggers.yml @@ -58,6 +58,7 @@ pr_minimal_required_checks: (pull_request.comment eq "ngo" OR (pull_request.target eq "develop" OR pull_request.target eq "develop-2.0.0" OR + pull_request.target eq "develop-3.x.x" OR pull_request.target match "release/*")) AND NOT pull_request.draft cancel_old_ci: true @@ -75,13 +76,13 @@ pr_code_changes_checks: # Run API validation to early-detect all new APIs that would force us to release new minor version of the package. Note that for this to work the package version in package.json must correspond to "actual package state" which means that it should be higher than last released version - .yamato/vetting-test.yml#vetting_test - # Run package EditMode and Playmode package tests on trunk and an older supported editor (6000.0) + # Run package EditMode and Playmode package tests on trunk and an older supported editor (6000.3) - .yamato/package-tests.yml#package_test_-_ngo_trunk_mac - - .yamato/package-tests.yml#package_test_-_ngo_6000.0_win + - .yamato/package-tests.yml#package_test_-_ngo_6000.3_win - # Run testproject EditMode and Playmode project tests on trunk and an older supported editor (6000.0) + # Run testproject EditMode and Playmode project tests on trunk and an older supported editor (6000.3) - .yamato/project-tests.yml#test_testproject_win_trunk - - .yamato/project-tests.yml#test_testproject_mac_6000.0 + - .yamato/project-tests.yml#test_testproject_mac_6000.3 # Run standalone test. We run it only on Ubuntu since it's the fastest machine, and it was noted that for example distribution on macOS is taking 40m since we switched to Apple Silicon # Coverage on other standalone machines is present in Nightly job so it's enough to not run all of them for PRs @@ -94,6 +95,7 @@ pr_code_changes_checks: (pull_request.comment eq "ngo" OR (pull_request.target eq "develop" OR pull_request.target eq "develop-2.0.0" OR + pull_request.target eq "develop-3.x.x" OR pull_request.target match "release/*")) AND NOT pull_request.draft AND pull_request.changes.any match [ diff --git a/.yamato/project.metafile b/.yamato/project.metafile index 1c3037a322..6d0ce1da0f 100644 --- a/.yamato/project.metafile +++ b/.yamato/project.metafile @@ -176,13 +176,12 @@ validation_editors: default: - 6000.3 all: - - 6000.0 - 6000.3 - 6000.4 - 6000.5 - trunk minimal: - - 6000.0 + - 6000.3 # Scripting backends used by Standalone RunTimeTests--------------------------------------------------- diff --git a/com.unity.netcode.gameobjects/Editor/AssemblyInfo.cs b/com.unity.netcode.gameobjects/Editor/AssemblyInfo.cs index 0f4e7e2b1d..6dd01c138e 100644 --- a/com.unity.netcode.gameobjects/Editor/AssemblyInfo.cs +++ b/com.unity.netcode.gameobjects/Editor/AssemblyInfo.cs @@ -2,6 +2,9 @@ #if UNITY_INCLUDE_TESTS #if UNITY_EDITOR +[assembly: InternalsVisibleTo("Unity.Netcode.GameObjects.Editor.Tests")] +[assembly: InternalsVisibleTo("Unity.Netcode.GameObjects.EditorTests")] +[assembly: InternalsVisibleTo("Unity.Netcode.GameObjects.Editor")] [assembly: InternalsVisibleTo("Unity.Netcode.Editor.Tests")] [assembly: InternalsVisibleTo("TestProject.Runtime.Tests")] #endif // UNITY_EDITOR diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs index 8d29e112f0..2b3b7d99f1 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs @@ -12,7 +12,7 @@ using UnityEngine; using Object = System.Object; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal static class CodeGenHelpers { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs index d74b737bde..03bea544bb 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkMessageILPP.cs @@ -11,7 +11,7 @@ using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor; using MethodAttributes = Mono.Cecil.MethodAttributes; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal sealed class INetworkMessageILPP : ILPPInterface { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs index abfc9a26ae..c4123d5b88 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs @@ -8,7 +8,7 @@ using Unity.CompilationPipeline.Common.ILPostProcessing; using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal sealed class INetworkSerializableILPP : ILPPInterface { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 04a72d7c9e..b1852baf44 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -16,7 +16,7 @@ using MethodAttributes = Mono.Cecil.MethodAttributes; using ParameterAttributes = Mono.Cecil.ParameterAttributes; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal sealed class NetworkBehaviourILPP : ILPPInterface { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorAssemblyResolver.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorAssemblyResolver.cs index 4adbbfd8ed..369fb04097 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorAssemblyResolver.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorAssemblyResolver.cs @@ -6,7 +6,7 @@ using Mono.Cecil; using Unity.CompilationPipeline.Common.ILPostProcessing; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal class PostProcessorAssemblyResolver : IAssemblyResolver { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporter.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporter.cs index f96ba3396c..7c904750f5 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporter.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporter.cs @@ -2,7 +2,7 @@ using System.Reflection; using Mono.Cecil; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal class PostProcessorReflectionImporter : DefaultReflectionImporter { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporterProvider.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporterProvider.cs index 81f80f04ed..1f7e41c3c8 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporterProvider.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/PostProcessorReflectionImporterProvider.cs @@ -1,6 +1,6 @@ using Mono.Cecil; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal class PostProcessorReflectionImporterProvider : IReflectionImporterProvider { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs index 57295be52e..941e492c13 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs @@ -7,7 +7,7 @@ using Unity.CompilationPipeline.Common.ILPostProcessing; using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor; -namespace Unity.Netcode.Editor.CodeGen +namespace Unity.Netcode.GameObjects.Editor.CodeGen { internal sealed class RuntimeAccessModifiersILPP : ILPPInterface { diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef b/com.unity.netcode.gameobjects/Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef index 1accd5e5d7..aa2d61f4d2 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef @@ -1,6 +1,6 @@ { - "name": "Unity.Netcode.Editor.CodeGen", - "rootNamespace": "Unity.Netcode.Editor.CodeGen", + "name": "Unity.Netcode.GameObjects.Editor.CodeGen", + "rootNamespace": "Unity.Netcode.GameObjects.Editor.CodeGen", "references": [ "Unity.Netcode.Runtime", "Unity.Collections" diff --git a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsProjectSettings.cs b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsProjectSettings.cs index 48ddd77fde..05f83876bd 100644 --- a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsProjectSettings.cs +++ b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsProjectSettings.cs @@ -1,7 +1,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor.Configuration +namespace Unity.Netcode.GameObjects.Editor.Configuration { /// /// A of type . diff --git a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsSettings.cs b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsSettings.cs index 70dfc02021..6a43d62e7e 100644 --- a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsSettings.cs +++ b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsSettings.cs @@ -1,6 +1,6 @@ using UnityEditor; -namespace Unity.Netcode.Editor.Configuration +namespace Unity.Netcode.GameObjects.Editor.Configuration { internal class NetcodeForGameObjectsEditorSettings { diff --git a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeSettingsProvider.cs b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeSettingsProvider.cs index 22ed67e48c..a8b521117c 100644 --- a/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeSettingsProvider.cs +++ b/com.unity.netcode.gameobjects/Editor/Configuration/NetcodeSettingsProvider.cs @@ -5,7 +5,7 @@ using Directory = UnityEngine.Windows.Directory; using File = UnityEngine.Windows.File; -namespace Unity.Netcode.Editor.Configuration +namespace Unity.Netcode.GameObjects.Editor.Configuration { internal static class NetcodeSettingsProvider { diff --git a/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabProcessor.cs b/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabProcessor.cs index f74530e60f..cab11b3be1 100644 --- a/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabProcessor.cs +++ b/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabProcessor.cs @@ -2,7 +2,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor.Configuration +namespace Unity.Netcode.GameObjects.Editor.Configuration { /// /// Updates the default instance when prefabs are updated (created, moved, deleted) in the project. diff --git a/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabsEditor.cs b/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabsEditor.cs index 68113ca32f..6b1128c829 100644 --- a/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabsEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/Configuration/NetworkPrefabsEditor.cs @@ -2,7 +2,7 @@ using UnityEditorInternal; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// The custom editor for the . diff --git a/com.unity.netcode.gameobjects/Editor/HiddenScriptEditor.cs b/com.unity.netcode.gameobjects/Editor/HiddenScriptEditor.cs index 2311da1477..98193cc631 100644 --- a/com.unity.netcode.gameobjects/Editor/HiddenScriptEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/HiddenScriptEditor.cs @@ -5,7 +5,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// Internal use. Hides the script field for the given component. diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources.meta b/com.unity.netcode.gameobjects/Editor/Icons.meta similarity index 77% rename from testproject/Legacy/MultiprocessRuntime/readme-ressources.meta rename to com.unity.netcode.gameobjects/Editor/Icons.meta index 00a2ce978f..47f083a506 100644 --- a/testproject/Legacy/MultiprocessRuntime/readme-ressources.meta +++ b/com.unity.netcode.gameobjects/Editor/Icons.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5d13190bb106b4188934d5576df7e777 +guid: 09ac49ca8d1df6347814a8a4760eb02c folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png b/com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png new file mode 100644 index 0000000000..c94cfb2e9b Binary files /dev/null and b/com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png differ diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg.meta b/com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png.meta similarity index 51% rename from testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg.meta rename to com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png.meta index e675f9e356..2a11d94057 100644 --- a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg.meta +++ b/com.unity.netcode.gameobjects/Editor/Icons/NOBridgeIcon.png.meta @@ -1,9 +1,9 @@ fileFormatVersion: 2 -guid: 4d67ff4fda6ec4805b2fd16af441aa47 +guid: 04d779b185ebc8d488b5d25eeb21c611 TextureImporter: internalIDToNameTable: [] externalObjects: {} - serializedVersion: 11 + serializedVersion: 13 mipmaps: mipMapMode: 0 enableMipMap: 1 @@ -20,10 +20,12 @@ TextureImporter: externalNormalMap: 0 heightScale: 0.25 normalMapFilter: 0 + flipGreenChannel: 0 isReadable: 0 streamingMipmaps: 0 streamingMipmapsPriority: 0 vTOnly: 0 + ignoreMipmapLimit: 0 grayScaleToAlpha: 0 generateCubemap: 6 cubemapConvolution: 0 @@ -50,7 +52,7 @@ TextureImporter: spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteGenerateFallbackPhysicsShape: 1 alphaUsage: 1 - alphaIsTransparency: 0 + alphaIsTransparency: 1 spriteTessellationDetail: -1 textureType: 0 textureShape: 1 @@ -62,10 +64,12 @@ TextureImporter: textureFormatSet: 0 ignorePngGamma: 0 applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 platformSettings: - - serializedVersion: 3 + - serializedVersion: 4 buildTarget: DefaultTexturePlatform - maxTextureSize: 2048 + maxTextureSize: 64 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 @@ -73,12 +77,66 @@ TextureImporter: crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: iOS + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: WindowsStoreApps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: [] outline: [] + customData: physicsShape: [] bones: [] spriteID: @@ -88,9 +146,11 @@ TextureImporter: edges: [] weights: [] secondaryTextures: [] - spritePackingTag: + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 userData: assetBundleName: assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Editor/NetworkBehaviourEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkBehaviourEditor.cs index 1312ff69e6..bd0aff1bea 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkBehaviourEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkBehaviourEditor.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Reflection; -using Unity.Netcode.Editor.Configuration; +using Unity.Netcode.GameObjects.Editor.Configuration; using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// The for @@ -321,20 +321,6 @@ private void OnEnable() CheckForNetworkObject((target as NetworkBehaviour).gameObject); } - /// - /// Recursively finds the root parent of a - /// - /// The current we are inspecting for a parent - /// the root parent for the first passed into the method - public static Transform GetRootParentTransform(Transform transform) - { - if (transform.parent == null || transform.parent == transform) - { - return transform; - } - return GetRootParentTransform(transform.parent); - } - /// /// Used to determine if a GameObject has one or more NetworkBehaviours but /// does not already have a NetworkObject component. If not it will notify @@ -358,7 +344,7 @@ public static void CheckForNetworkObject(GameObject gameObject, bool networkObje } // Now get the root parent transform to the current GameObject (or itself) - var rootTransform = GetRootParentTransform(gameObject.transform); + var rootTransform = gameObject.transform.root; if (!rootTransform.TryGetComponent(out var networkManager)) { networkManager = rootTransform.GetComponentInChildren(); diff --git a/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs index a4e339f558..c337dbadd9 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs @@ -2,7 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Unity.Netcode.Editor.Configuration; +using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor.Configuration; using Unity.Netcode.Logging; using UnityEditor; using UnityEngine; @@ -10,7 +11,7 @@ using UnityEngine.Assemblies; #endif -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// This handles the translation between the and diff --git a/com.unity.netcode.gameobjects/Editor/NetworkManagerHelper.cs b/com.unity.netcode.gameobjects/Editor/NetworkManagerHelper.cs index 5c7e554baf..4bfe9985d9 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkManagerHelper.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkManagerHelper.cs @@ -1,12 +1,13 @@ using System.Collections.Generic; using System.Linq; -using Unity.Netcode.Editor.Configuration; +using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor.Configuration; using Unity.Netcode.Logging; using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { #if UNITY_EDITOR /// diff --git a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs index 04e0d1f698..924e519f27 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs @@ -5,7 +5,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// The for @@ -32,6 +32,9 @@ private void Initialize() m_Initialized = true; m_NetworkObject = (NetworkObject)target; +#if UNIFIED_NETCODE + m_NetworkObject.UnifiedValidation(); +#endif } /// diff --git a/com.unity.netcode.gameobjects/Editor/NetworkTransformEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkTransformEditor.cs index 31f3314bea..f82989df5e 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkTransformEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkTransformEditor.cs @@ -1,8 +1,9 @@ using Unity.Netcode.Components; +using Unity.Netcode.Editor; using UnityEditor; using UnityEngine; -namespace Unity.Netcode.Editor +namespace Unity.Netcode.GameObjects.Editor { /// /// The for diff --git a/com.unity.netcode.gameobjects/Editor/PackageChecker/UTPAdapterChecker.cs b/com.unity.netcode.gameobjects/Editor/PackageChecker/UTPAdapterChecker.cs index 0d109a0115..8f6ffd67a6 100644 --- a/com.unity.netcode.gameobjects/Editor/PackageChecker/UTPAdapterChecker.cs +++ b/com.unity.netcode.gameobjects/Editor/PackageChecker/UTPAdapterChecker.cs @@ -5,7 +5,7 @@ using UnityEditor.PackageManager; using UnityEditor.PackageManager.Requests; -namespace Unity.Netcode.Editor.PackageChecker +namespace Unity.Netcode.GameObjects.Editor.PackageChecker { [InitializeOnLoad] internal class UTPAdapterChecker diff --git a/com.unity.netcode.gameobjects/Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef b/com.unity.netcode.gameobjects/Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef index 9599c27b6c..aeb8755a14 100644 --- a/com.unity.netcode.gameobjects/Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef +++ b/com.unity.netcode.gameobjects/Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef @@ -1,7 +1,6 @@ { - "name": "Unity.Netcode.PackageChecker.Editor", - "rootNamespace": "Unity.Netcode.Editor.PackageChecker", - "references": [], + "name": "Unity.Netcode.GameObjects.Editor.PackageChecker", + "rootNamespace": "Unity.Netcode.GameObjects.Editor.PackageChecker", "includePlatforms": [ "Editor" ], diff --git a/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef b/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef index 926f85b604..e4d210bd4d 100644 --- a/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef +++ b/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef @@ -1,13 +1,14 @@ { - "name": "Unity.Netcode.Editor", - "rootNamespace": "Unity.Netcode.Editor", + "name": "Unity.Netcode.GameObjects.Editor", + "rootNamespace": "Unity.Netcode.GameObjects.Editor", "references": [ "Unity.Netcode.Runtime", "Unity.Netcode.Components", "Unity.Services.Relay", "Unity.Networking.Transport", "Unity.Services.Core", - "Unity.Services.Authentication" + "Unity.Services.Authentication", + "Unity.NetCode" ], "includePlatforms": [ "Editor" @@ -43,6 +44,16 @@ "name": "com.unity.services.multiplayer", "expression": "0.2.0", "define": "MULTIPLAYER_SERVICES_SDK_INSTALLED" + }, + { + "name": "com.unity.netcode", + "expression": "1.10.1", + "define": "UNIFIED_NETCODE" + }, + { + "name": "com.unity.multiplayer.playmode", + "expression": "0.1.0", + "define": "UNITY_MULTIPLAYER_PLAYMODE" } ], "noEngineReferences": false diff --git a/com.unity.netcode.gameobjects/Runtime/AssemblyInfo.cs b/com.unity.netcode.gameobjects/Runtime/AssemblyInfo.cs index 59d36853a3..0468e67d0e 100644 --- a/com.unity.netcode.gameobjects/Runtime/AssemblyInfo.cs +++ b/com.unity.netcode.gameobjects/Runtime/AssemblyInfo.cs @@ -1,8 +1,8 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Unity.Netcode.Components")] #if UNITY_EDITOR -[assembly: InternalsVisibleTo("Unity.Netcode.Editor")] -[assembly: InternalsVisibleTo("Unity.Netcode.Editor.CodeGen")] +[assembly: InternalsVisibleTo("Unity.Netcode.GameObjects.Editor")] +[assembly: InternalsVisibleTo("Unity.Netcode.GameObjects.Editor.CodeGen")] #endif // UNITY_EDITOR #if COM_UNITY_NETCODE_ADAPTER_UTP @@ -14,6 +14,8 @@ [assembly: InternalsVisibleTo("Unity.Netcode.TestHelpers.Runtime")] [assembly: InternalsVisibleTo("TestProject.Runtime.Tests")] #if UNITY_EDITOR +[assembly: InternalsVisibleTo("Unity.Netcode.GameObjects.Editor.Tests")] +[assembly: InternalsVisibleTo("TestProject.EditorTests")] [assembly: InternalsVisibleTo("Unity.Netcode.Editor.Tests")] [assembly: InternalsVisibleTo("TestProject.Editor.Tests")] #endif // UNITY_EDITOR @@ -21,7 +23,7 @@ #if MULTIPLAYER_TOOLS [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.GameObjects.Tests")] [assembly: InternalsVisibleTo("TestProject.ToolsIntegration.RuntimeTests")] -[assembly: InternalsVisibleTo("TestProject.Netcode.GameObjejct.Runtime.Tests")] +[assembly: InternalsVisibleTo("TestProject.Netcode.GameObject.Runtime.Tests")] #endif // MULTIPLAYER_TOOLS #endif // UNITY_INCLUDE_TESTS // Should always be visible when multiplayer tools package is installed. diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs new file mode 100644 index 0000000000..75ad66a278 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs @@ -0,0 +1,55 @@ +#if UNIFIED_NETCODE +using Unity.NetCode; + +namespace Unity.Netcode +{ +#if UNIFIED_NETCODE + /// + /// TODO-UNIFIED: Needs further peer review and exploring alternate ways of handling this. + /// + /// + /// If used, we most likely would make this internal + /// + public partial class NetworkObjectBridge : GhostBehaviour + { + +#if UNITY_EDITOR && !UNITY_INCLUDE_TESTS + [UnityEngine.HideInInspector] + [UnityEngine.SerializeField] + private bool m_Sorted = false; + private void OnValidate() + { + // Sort only once when we have first been added. + if (!m_Sorted) + { + while (UnityEditorInternal.ComponentUtility.MoveComponentUp(this)) + { + // Keep moving until it can't go higher + } + var ghostAdapter = gameObject.GetComponent(); + // Now move the GhostAdapter to the top so it is above NetworkObjectBridge + while (ghostAdapter != null && UnityEditorInternal.ComponentUtility.MoveComponentUp(ghostAdapter)) + { + // Keep moving until it can't go higher + } + + m_Sorted = true; + } + } +#endif + + + /// + /// This is used to link data to + /// N4E-spawned hybrid prefab instances. + /// + internal GhostField NetworkObjectId = new GhostField(); + public void SetNetworkObjectId(ulong networkObjectId) + { + NetworkObjectId.PresetValue(networkObjectId); + NetworkObjectId.Value = networkObjectId; + } + } +#endif +} +#endif diff --git a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs.meta similarity index 61% rename from testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs.meta index 3f89ca9cb5..0e4dd44b97 100644 --- a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs.meta @@ -1,11 +1,11 @@ fileFormatVersion: 2 -guid: 80b877babc0ce4b0d8cf31e73216d49a +guid: 510c5bb08d2f5724e85aa4fb66a8a4ff MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 - icon: {instanceID: 0} + icon: {fileID: 2800000, guid: 04d779b185ebc8d488b5d25eeb21c611, type: 3} userData: assetBundleName: assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs new file mode 100644 index 0000000000..4c9f9ad0a9 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs @@ -0,0 +1,94 @@ +#if UNIFIED_NETCODE +using System; +using Unity.Entities; +using Unity.NetCode; +using UnityEngine; + +namespace Unity.Netcode +{ + /// + /// TODO-UNIFIED: Would need to be reviewed for alternate ways of handling this. + /// Creates the hosted world and provides a means to configuring + /// the 2nd port for unified netcode connection. + /// + internal class UnifiedBootstrap : ClientServerBootstrap + { + public static UnifiedBootstrap Instance { get; private set; } + public static Action OnInitialized; + public static ushort Port = 7979; + public static NetworkManager CurrentNetworkManagerForInitialization; + + public static World LastCreatedWorld { get; private set; } + + private static int s_WorldCounter = 0; + + public override bool Initialize(string defaultWorldName) + { + var networkManager = CurrentNetworkManagerForInitialization; + if (networkManager == NetworkManager.Singleton) + { + Instance = this; + } + + AutoConnectPort = Port; + if (base.Initialize(defaultWorldName)) + { + Debug.LogError($"[{nameof(UnifiedBootstrap)}] Auto-bootstrap is enabled!!! This will break the POC!"); + return true; + } + + if (networkManager != null) + { + Debug.Log($"Starting a world for {(networkManager.IsServer ? "Host" : "Client")}"); + s_WorldCounter++; + LastCreatedWorld = networkManager.IsServer ? CreateSingleWorldHost($"HostSingleWorld-{s_WorldCounter}") + : CreateClientWorld($"ClientWorld-{s_WorldCounter}"); + + if (LastCreatedWorld == null) + { + s_WorldCounter--; + Debug.LogError($"[{nameof(UnifiedBootstrap)}] World is null!"); + return false; + } + + if (!LastCreatedWorld.IsCreated) + { + s_WorldCounter--; + Debug.LogError($"[{nameof(UnifiedBootstrap)}] World was not created!"); + return false; + } + + //if (networkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogInfo($"[{nameof(UnifiedBootstrap)}] Created world: {LastCreatedWorld.Name} / {LastCreatedWorld.SequenceNumber}"); + } + + networkManager.NetcodeWorld = (NetcodeWorld)LastCreatedWorld; + if (networkManager.NetworkConfig.Prefabs.HasPendingGhostPrefabs) + { + if (networkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogInfo($"[{nameof(UnifiedBootstrap)}] Registering hybrid prefabs..."); + } + + networkManager.NetworkConfig.Prefabs.RegisterGhostPrefabs(networkManager); + } + } + else + { + LastCreatedWorld = CreateLocalWorld("LocalWorld"); + } + + OnInitialized?.Invoke(); + + return true; + } + + ~UnifiedBootstrap() + { + LastCreatedWorld = null; + Instance = null; + } + } +} +#endif diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs.meta new file mode 100644 index 0000000000..a9a3d5ee88 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0671ce785ad022344a6721d1be9559ad \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs new file mode 100644 index 0000000000..b9e8bf55a6 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs @@ -0,0 +1,128 @@ +#if UNIFIED_NETCODE +using System.Collections.Generic; +using Unity.Collections; +using Unity.Entities; +using Unity.NetCode; +using UnityEngine; + +namespace Unity.Netcode.Components +{ + + public struct NetcodeConnection + { + internal World World; + internal Entity Entity; + public int NetworkId; + + public bool IsServer => World.IsServer(); + public void GoInGame() + { + World.EntityManager.AddComponentData(Entity, default(NetworkStreamInGame)); + } + public void SendMessage(T message) where T : unmanaged, IRpcCommand + { + var req = World.EntityManager.CreateEntity(); + World.EntityManager.AddComponentData(req, new SendRpcCommandRequest { TargetConnection = Entity }); + World.EntityManager.AddComponentData(req, message); + } + } + + internal partial class UnifiedUpdateConnections : SystemBase + { + private List m_TempConnections = new List(); + + private Dictionary m_NewConnections = new Dictionary(); + + protected override void OnUpdate() + { + var isServer = World.IsServer(); + var commandBuffer = new EntityCommandBuffer(Allocator.Temp); +// var networkManager = NetworkManager.Singleton; + foreach (var networkManager in GameObject.FindObjectsByType()) + { + foreach (var (networkId, connectionState, entity) in SystemAPI.Query() + .WithNone().WithEntityAccess()) + { + commandBuffer.RemoveComponent(entity); + m_TempConnections.Add(new NetcodeConnection + { World = World, Entity = entity, NetworkId = networkId.Value }); + } + + foreach (var con in m_TempConnections) + { + NetworkManager.OnNetCodeDisconnect?.Invoke(con); + } + + m_TempConnections.Clear(); + + // TODO: We should figure out how to associate the N4E NetworkId with the NGO ClientId + foreach (var (networkId, entity) in SystemAPI.Query().WithAll() + .WithNone().WithEntityAccess()) + { + if (!m_NewConnections.ContainsKey(networkId.Value)) + { + var newConnection = new NetcodeConnection + { World = World, Entity = entity, NetworkId = networkId.Value }; + m_NewConnections.Add(networkId.Value, newConnection); + } + } + + // If we have any pending connections + if (m_NewConnections.Count > 0) + { + foreach (var entry in m_NewConnections) + { + // Server: always connect + // Client: wait until we have synchronized before announcing we are ready to receive snapshots + if (networkManager.IsServer || (!networkManager.IsServer && networkManager.IsConnectedClient)) + { + // Set the connection in-game + commandBuffer.AddComponent(entry.Value.Entity); + commandBuffer.AddComponent(entry.Value.Entity, default(ConnectionState)); + NetworkManager.OnNetCodeConnect?.Invoke(entry.Value); + m_TempConnections.Add(entry.Value); + } + } + + // Remove any connections that have "gone in-game". + foreach (var connection in m_TempConnections) + { + m_NewConnections.Remove(connection.NetworkId); + } + } + + m_TempConnections.Clear(); + + // If the local NetworkManager is shutting down or no longer connected, then + // make sure we have disconnected all known connections. + if (networkManager.ShutdownInProgress || !networkManager.IsListening) + { + foreach (var (networkId, entity) in SystemAPI.Query().WithEntityAccess()) + { + commandBuffer.RemoveComponent(entity); + NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection + { World = World, Entity = entity, NetworkId = networkId.Value }); + } + } + } + + commandBuffer.Playback(EntityManager); + } + + /// + /// Always disconnect all known connections when being destroyed. + /// + protected override void OnDestroy() + { + var commandBuffer = new EntityCommandBuffer(Allocator.Temp); + foreach (var (networkId, entity) in SystemAPI.Query().WithEntityAccess()) + { + commandBuffer.RemoveComponent(entity); + NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection { World = World, Entity = entity, NetworkId = networkId.Value }); + } + commandBuffer.Playback(EntityManager); + base.OnDestroy(); + } + } +} +#endif diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs.meta b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs.meta new file mode 100644 index 0000000000..01feb655c4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d5f2f5fd179c39f43b68ec502cdec9c4 diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs index 0d0de9afa8..842c289a1a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs @@ -194,6 +194,11 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network #endif #if COM_UNITY_MODULES_PHYSICS && COM_UNITY_MODULES_PHYSICS2D +#if UNIFIED_NETCODE + // Used to keep track of the original kinematic state upon awake. + // (see OnDestroy below) + private bool m_OriginalKinematicState; +#endif /// /// Initializes the networked Rigidbody based on the /// passed in as a parameter. @@ -247,9 +252,31 @@ protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform network if (AutoUpdateKinematicState) { +#if UNIFIED_NETCODE + // Keep track of the original kinematic state. (see OnDestroy) + m_OriginalKinematicState = IsKinematic(); +#endif SetIsKinematic(true); } } + +#if UNIFIED_NETCODE + public override void OnDestroy() + { + base.OnDestroy(); + // If the user has left this component on their prefab and this is a hybrid prefab, + // then we want to set the rigid body back to its original kinematic settings since + // we are automatically destroying these components at runtime when it is a hybrid + // prefab that is spawned. + if (NetworkObject && NetworkObject.HasGhost) + { + if (m_InternalRigidbody || m_InternalRigidbody2D) + { + SetIsKinematic(m_OriginalKinematicState); + } + } + } +#endif #endif internal Vector3 GetAdjustedPositionThreshold() { diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 3ca9093b34..5a9e7c08bc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -1793,6 +1793,17 @@ internal void RegisterRigidbody(NetworkRigidbodyBase networkRigidbody) m_UseRigidbodyForMotion = m_NetworkRigidbodyInternal.UseRigidBodyForMotion; } } + +#if UNIFIED_NETCODE + internal void UnregisterRigidbody() + { + if (m_NetworkRigidbodyInternal) + { + m_NetworkRigidbodyInternal = null; + m_UseRigidbodyForMotion = false; + } + } +#endif #endif #if DEBUG_NETWORKTRANSFORM || UNITY_INCLUDE_TESTS @@ -3617,6 +3628,8 @@ protected virtual void Awake() } CachedTransform = transform; + + } private NetworkObject m_CachedNetworkObject; @@ -3634,6 +3647,12 @@ internal override void InternalOnNetworkPreSpawn(ref NetworkManager networkManag /// public override void OnNetworkSpawn() { +#if UNIFIED_NETCODE + if (NetworkObject.HasGhost) + { + return; + } +#endif m_ParentedChildren.Clear(); Initialize(); @@ -3726,8 +3745,15 @@ private void ResetInterpolatedStateToCurrentAuthoritativeState() /// The internal initialization method to allow for internal API adjustments /// /// - private void InternalInitialization(bool isOwnershipChange = false) + internal virtual void InternalInitialization(bool isOwnershipChange = false) { + +#if UNIFIED_NETCODE + if (NetworkObject.HasGhost) + { + return; + } +#endif if (!IsSpawned) { return; diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs index c6f30d2835..25d75adf26 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefab.cs @@ -57,6 +57,14 @@ public class NetworkPrefab /// public GameObject OverridingTargetPrefab; +#if UNIFIED_NETCODE + /// + /// Used to determine if this prefab needs to be registered + /// via the unified API. + /// + internal bool HasGhost { get; private set; } +#endif + /// /// Compares this NetworkPrefab with another to determine equality /// @@ -166,6 +174,11 @@ public bool Validate(int index = -1) return false; } +#if UNIFIED_NETCODE + // Mark this network prefab as having to be registered via the unified API + HasGhost = networkObject.HasGhost; +#endif + return true; } @@ -183,7 +196,6 @@ public bool Validate(int index = -1) return false; } - break; } case NetworkPrefabOverride.Prefab: diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs index 45d93ad9d7..a0321a7ac4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs @@ -47,6 +47,11 @@ public class NetworkPrefabs [NonSerialized] private List m_Prefabs = new List(); +#if UNIFIED_NETCODE + [NonSerialized] + internal Dictionary PrefabTable = new Dictionary(); +#endif + [NonSerialized] private List m_RuntimeAddedPrefabs = new List(); @@ -57,12 +62,21 @@ private void AddTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab) // Don't add this to m_RuntimeAddedPrefabs // This prefab is now in the PrefabList, so if we shutdown and initialize again, we'll pick it up from there. m_Prefabs.Add(networkPrefab); +#if UNIFIED_NETCODE + if (!PrefabTable.ContainsKey(networkPrefab.SourcePrefabGlobalObjectIdHash)) + { + PrefabTable.Add(networkPrefab.SourcePrefabGlobalObjectIdHash, networkPrefab); + } +#endif } } private void RemoveTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab) { m_Prefabs.Remove(networkPrefab); +#if UNIFIED_NETCODE + PrefabTable.Remove(networkPrefab.SourcePrefabGlobalObjectIdHash); +#endif } /// @@ -95,6 +109,9 @@ public void Initialize(bool warnInvalid = true) { m_Prefabs.Clear(); NetworkPrefabsLists.RemoveAll(x => x == null); +#if UNIFIED_NETCODE + PrefabTable.Clear(); +#endif foreach (var list in NetworkPrefabsLists) { list.OnAdd += AddTriggeredByNetworkPrefabList; @@ -127,10 +144,22 @@ public void Initialize(bool warnInvalid = true) if (AddPrefabRegistration(networkPrefab)) { m_Prefabs.Add(networkPrefab); +#if UNIFIED_NETCODE + if (!PrefabTable.ContainsKey(networkPrefab.SourcePrefabGlobalObjectIdHash)) + { + PrefabTable.Add(networkPrefab.SourcePrefabGlobalObjectIdHash, networkPrefab); + } +#endif } else { removeList?.Add(networkPrefab); +#if UNIFIED_NETCODE + if (PrefabTable.ContainsKey(networkPrefab.SourcePrefabGlobalObjectIdHash)) + { + PrefabTable.Remove(networkPrefab.SourcePrefabGlobalObjectIdHash); + } +#endif } } @@ -139,10 +168,22 @@ public void Initialize(bool warnInvalid = true) if (AddPrefabRegistration(networkPrefab)) { m_Prefabs.Add(networkPrefab); +#if UNIFIED_NETCODE + if (!PrefabTable.ContainsKey(networkPrefab.SourcePrefabGlobalObjectIdHash)) + { + PrefabTable.Add(networkPrefab.SourcePrefabGlobalObjectIdHash, networkPrefab); + } +#endif } else { removeList?.Add(networkPrefab); +#if UNIFIED_NETCODE + if (PrefabTable.ContainsKey(networkPrefab.SourcePrefabGlobalObjectIdHash)) + { + PrefabTable.Remove(networkPrefab.SourcePrefabGlobalObjectIdHash); + } +#endif } } @@ -175,6 +216,12 @@ public bool Add(NetworkPrefab networkPrefab) { m_Prefabs.Add(networkPrefab); m_RuntimeAddedPrefabs.Add(networkPrefab); +#if UNIFIED_NETCODE + if (!PrefabTable.ContainsKey(networkPrefab.SourcePrefabGlobalObjectIdHash)) + { + PrefabTable.Add(networkPrefab.SourcePrefabGlobalObjectIdHash, networkPrefab); + } +#endif return true; } @@ -202,6 +249,12 @@ public void Remove(NetworkPrefab prefab) m_RuntimeAddedPrefabs.Remove(prefab); OverrideToNetworkPrefab.Remove(prefab.TargetPrefabGlobalObjectIdHash); NetworkPrefabOverrideLinks.Remove(prefab.SourcePrefabGlobalObjectIdHash); +#if UNIFIED_NETCODE + if (PrefabTable.ContainsKey(prefab.SourcePrefabGlobalObjectIdHash)) + { + PrefabTable.Remove(prefab.SourcePrefabGlobalObjectIdHash); + } +#endif } /// @@ -277,6 +330,53 @@ public bool Contains(NetworkPrefab prefab) return false; } +#if UNIFIED_NETCODE + /// + /// TODO: Either keep or remove prior to freeze. + /// Leaving this here in case we have to control when things get registered. + /// + internal bool HasPendingGhostPrefabs { get; private set; } + internal bool HasGhostPrefabs { get; private set; } + private List m_PendingGhostRegistration = new List(); + + /// + /// UNIFIED-POC
+ /// Hybrid NetworkObject-Ghost Prefab Registration
+ ///
+ /// + /// When is true, s + /// will mark themselves as having a ghost during . + /// After validation, if a network prefab's value is + /// set, then it is added to . + /// Within during the , + /// if is true then will be invoked. + /// This will repeat until the hosted single world instance is created. + /// + /// + internal void RegisterGhostPrefabs(NetworkManager networkManager) + { + if (!HasPendingGhostPrefabs) + { + Debug.LogWarning($"Should not be invoking!"); + return; + } + var isHost = networkManager.IsHost; + for (int i = m_PendingGhostRegistration.Count - 1; i >= 0; i--) + { + var networkPrefab = m_PendingGhostRegistration[i]; + + // Returns false if the single world is not available yet + if (NetCode.Netcode.RegisterPrefabSingleWorld(networkPrefab.Prefab, isHost, networkManager.NetcodeWorld)) + { + Debug.Log($"[{nameof(NetworkPrefabs)}][{nameof(RegisterGhostPrefabs)}] Registered hybrid spawned object: {networkPrefab.Prefab.name}"); + m_PendingGhostRegistration.RemoveAt(i); + } + } + HasPendingGhostPrefabs = m_PendingGhostRegistration.Count > 0; + } +#endif + + /// /// Configures for the given /// @@ -295,6 +395,15 @@ private bool AddPrefabRegistration(NetworkPrefab networkPrefab) uint source = networkPrefab.SourcePrefabGlobalObjectIdHash; uint target = networkPrefab.TargetPrefabGlobalObjectIdHash; +#if UNIFIED_NETCODE + if (networkPrefab.HasGhost) + { + //HasPendingGhostPrefabs = true; + HasGhostPrefabs = true; + //m_PendingGhostRegistration.Add(networkPrefab); + } +#endif + // Make sure the prefab isn't already registered. if (NetworkPrefabOverrideLinks.ContainsKey(source)) { diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs index bcdb8ba05a..aaeb67f4db 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs @@ -946,6 +946,37 @@ internal void ProcessClientsToDisconnect() m_ClientsToDisconnect.Clear(); } + /// + /// The checks to find the right GlobalObjectIdHash value + /// are complex enough to deserve a method that includes + /// an easy to follow logical flow. + /// This also makes it a quick check to determine if there + /// even is a player prefab to spawn (it is valid to not + /// have any player spawned upon connection). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private (bool IsValid, uint GlobalObjectIdHash) GetPlayerPrefabHash(uint? playerPrefabHash) + { + if (playerPrefabHash != null && playerPrefabHash.HasValue) + { + return (true, playerPrefabHash.Value); + } + else + if (NetworkManager.NetworkConfig.PlayerPrefab != null) + { + var networkObject = NetworkManager.NetworkConfig.PlayerPrefab.GetComponent(); + if (networkObject != null) + { + return (true, networkObject.GlobalObjectIdHash); + } + else + { + NetworkManager.Log.Error(new Logging.Context(LogLevel.Error, $"Player prefab {NetworkManager.NetworkConfig.PlayerPrefab.name} has no {nameof(NetworkObject)}!")); + } + } + return (false, 0); + } + /// /// Server Side: Handles the approval of a client /// @@ -972,10 +1003,10 @@ internal void HandleConnectionApproval(ulong ownerClientId, bool createPlayerObj } // Server-side spawning (only if there is a prefab hash or player prefab provided) - var idHashToSpawn = playerPrefabHash ?? NetworkManager.NetworkConfig.PlayerPrefab?.GetComponent()?.GlobalObjectIdHash; - if (!NetworkManager.DistributedAuthorityMode && createPlayerObject && idHashToSpawn.HasValue) + var idHashToSpawn = GetPlayerPrefabHash(playerPrefabHash); + if (!NetworkManager.DistributedAuthorityMode && createPlayerObject && idHashToSpawn.IsValid) { - var playerObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(idHashToSpawn.Value, ownerClientId, playerPosition, playerRotation); + var playerObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(idHashToSpawn.GlobalObjectIdHash, ownerClientId, playerPosition, playerRotation); if (playerObject == null) { diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 59f9e637b6..696bef2234 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -1,8 +1,10 @@ #pragma warning disable IDE0005 using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using Unity.Collections; +#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE) +using System.Runtime.CompilerServices; +#endif using UnityEngine; #pragma warning restore IDE0005 diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index b9a2c15789..265702e87d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -1,10 +1,17 @@ using System; using System.Collections.Generic; -using Unity.Collections; using System.Linq; +using Unity.Collections; +#if UNIFIED_NETCODE +using Unity.Entities; +using Unity.NetCode; +#endif using Unity.Netcode.Components; using Unity.Netcode.Logging; using Unity.Netcode.Runtime; +#if UNIFIED_NETCODE && OUT_OF_BAND_RPC +using Unity.Netcode.Unified; +#endif using UnityEngine; #if UNITY_EDITOR using UnityEditor; @@ -12,6 +19,8 @@ #endif using UnityEngine.SceneManagement; + + namespace Unity.Netcode { /// @@ -1009,11 +1018,13 @@ internal bool NetworkManagerCheckForParent(bool ignoreNetworkManagerCache = fals { #if UNITY_EDITOR var isParented = NetworkManagerHelper.NotifyUserOfNestedNetworkManager(this, ignoreNetworkManagerCache); + + #else - var isParented = transform.root != transform; + var isParented = transform.parent != null; if (isParented) { - throw new Exception(GenerateNestedNetworkManagerMessage(transform)); + Log.Error(new Context(LogLevel.Error, GenerateNestedNetworkManagerMessage(transform))); } #endif return isParented; @@ -1029,7 +1040,17 @@ internal static string GenerateNestedNetworkManagerMessage(Transform transform) /// private void OnTransformParentChanged() { +#if UNITY_EDITOR + // During editor playmode, we log the message as a dialog box + // and that script sets the parent back to root/null. NetworkManagerCheckForParent(); +#else + if (NetworkManagerCheckForParent()) + { + // During runtime, we log the message and set our parent back to root/null. + transform.parent = null; + } +#endif } /// @@ -1216,6 +1237,23 @@ internal void Initialize(bool server) // UnityTransport dependencies are then initialized RealTimeProvider = ComponentFactory.Create(this); + +#if UNIFIED_NETCODE && OUT_OF_BAND_RPC + // TODO-FixMe: + // We assign transport at this point to preceed the NetworkConnectionManager + // being initialized. However, HasGhostPrefabs might not be set at this point + // if the prefabs list was set after instantiating the NetworkManager. + // Integration tests do this, but user code could do this too. + // To-Investigate: + // Determine if this really impacts anything having prefabs initialze/register + // at this point versus last. + NetworkConfig.InitializePrefabs(); + if (NetworkConfig.Prefabs.HasGhostPrefabs) + { + NetworkConfig.NetworkTransport = gameObject.AddComponent(); + } +#endif + MetricsManager.Initialize(this); { @@ -1262,8 +1300,9 @@ internal void Initialize(bool server) BehaviourUpdater = new NetworkBehaviourUpdater(); BehaviourUpdater.Initialize(this); - +#if !UNIFIED_NETCODE NetworkConfig.InitializePrefabs(); +#endif PrefabHandler.RegisterPlayerPrefab(); #if UNITY_EDITOR BeginNetworkSession(); @@ -1310,6 +1349,82 @@ private bool CanStart(StartType type) return true; } +#if UNIFIED_NETCODE + /// + /// The world instance assigned to this NetworkManager instance. + /// + public NetcodeWorld NetcodeWorld { get; internal set; } + private System.Collections.IEnumerator WaitForHybridPrefabRegistration(StartType startType) + { + if (this == Singleton) + { + if (NetCode.Netcode.IsActive) + { + NetworkLog.LogInfo($"[{nameof(WaitForHybridPrefabRegistration)}] Netcode is not active but has an instance at this point."); + } + /// !! Important !! + /// Clear out any pre-existing configuration in the event this applicatioin instance has already been connected to a session. + NetCode.Netcode.Reset(); + } + + /// !! Initialize worlds here !! + /// Worlds are created here: + UnifiedBootstrap.CurrentNetworkManagerForInitialization = this; + DefaultWorldInitialization.Initialize("Default World", false); + + // This should not be needed at this point, but this is here in the event something changes. + if (NetworkConfig.Prefabs.HasPendingGhostPrefabs) + { + NetworkLog.LogWarning($"[{nameof(WaitForHybridPrefabRegistration)}] !!!!! (Ghosts are still pending registration) !!!!!"); + var waitTime = new WaitForSeconds(0.016f); + while (NetworkConfig.Prefabs.HasPendingGhostPrefabs) + { + NetworkConfig.Prefabs.RegisterGhostPrefabs(this); + yield return waitTime; + } + } + + if (LogLevel <= LogLevel.Developer) + { + NetworkLog.LogInfo($"[{nameof(WaitForHybridPrefabRegistration)}] All hybrid prefabs have been registered!"); + NetworkLog.LogInfo($"[{nameof(WaitForHybridPrefabRegistration)}] Finalizing NetworkManager start..."); + } + switch (startType) + { + case StartType.Server: + { + InternalStartServer(); + break; + } + case StartType.Host: + { + InternalStartHost(); + break; + } + case StartType.Client: + { + InternalStartClient(); + break; + } + } + } + + private bool UnifiedIsConfiguredCorrectly() + { + if (NetCodeConfig.Global == null) + { + Debug.LogError($"[{nameof(NetworkManager)}][Unified] You must create a {nameof(NetCodeConfig)} and set it to a single world in order to run in hybrid mode!"); + return false; + } + if (NetCodeConfig.Global.HostWorldModeSelection != NetCodeConfig.HostWorldMode.SingleWorld) + { + Debug.LogError($"[{nameof(NetworkManager)}][Unified] You must configure {nameof(NetCodeConfig)} to only use a single world in order to run in hybrid mode!"); + return false; + } + return true; + } +#endif + /// /// Starts a server /// @@ -1341,6 +1456,34 @@ public bool StartServer() return false; } +#if UNIFIED_NETCODE + // TODO-UNIFIED: Review and align on this being a way to handle knowing if the world should be created. + if (NetworkConfig.Prefabs.HasGhostPrefabs) + { + if (!UnifiedIsConfiguredCorrectly()) + { + m_ShuttingDown = true; + ShutdownInternal(); + return false; + } + if (LogLevel <= LogLevel.Developer) + { + Debug.Log("Creating world: Default world"); + } + StartCoroutine(WaitForHybridPrefabRegistration(StartType.Server)); + return true; + } + else + { + return InternalStartServer(); + } +#else + return InternalStartServer(); +#endif + } + + internal bool InternalStartServer() + { try { IsListening = NetworkConfig.NetworkTransport.StartServer(); @@ -1366,7 +1509,6 @@ public bool StartServer() ShutdownInternal(); IsListening = false; } - return IsListening; } @@ -1399,6 +1541,36 @@ public bool StartClient() return false; } +#if UNIFIED_NETCODE + // TODO-UNIFIED: Review and align on this being a way to handle knowing if the world should be created. + if (NetworkConfig.Prefabs.HasGhostPrefabs) + { + if (!UnifiedIsConfiguredCorrectly()) + { + m_ShuttingDown = true; + ShutdownInternal(); + return false; + } + if (LogLevel <= LogLevel.Developer) + { + Debug.Log("Creating world: Default world"); + } + StartCoroutine(WaitForHybridPrefabRegistration(StartType.Client)); + // TODO-UNIFIED: Need a way to signal everything completed. + return true; + } + else + { + return InternalStartClient(); + } +#else + return InternalStartClient(); +#endif + + } + + internal bool InternalStartClient() + { try { IsListening = NetworkConfig.NetworkTransport.StartClient(); @@ -1423,6 +1595,7 @@ public bool StartClient() return IsListening; } + /// /// Starts a Host /// @@ -1453,6 +1626,36 @@ public bool StartHost() return false; } +#if UNIFIED_NETCODE + // TODO-UNIFIED: Review and align on this being a way to handle knowing if the world should be created. + if (NetworkConfig.Prefabs.HasGhostPrefabs) + { + if (!UnifiedIsConfiguredCorrectly()) + { + m_ShuttingDown = true; + ShutdownInternal(); + return false; + } + if (LogLevel <= LogLevel.Developer) + { + Debug.Log("Creating world: Default world"); + } + StartCoroutine(WaitForHybridPrefabRegistration(StartType.Host)); + // TODO-UNIFIED: Need a way to signal everything completed. + return true; + } + else + { + return InternalStartHost(); + } +#else + return InternalStartHost(); +#endif + + } + + internal bool InternalStartHost() + { try { IsListening = NetworkConfig.NetworkTransport.StartServer(); @@ -1661,6 +1864,23 @@ internal void ShutdownInternal() IsListening = false; m_ShuttingDown = false; + +#if UNIFIED_NETCODE + // TODO-UNIFIED: Review and align on this being a way to handle knowing if the world should be created. + if (NetworkConfig != null && NetworkConfig.Prefabs != null && NetworkConfig.Prefabs.HasGhostPrefabs) + { + try + { + // Dispose of all worlds + World.DisposeAllWorlds(); + } + catch (Exception ex) + { + Debug.LogException(ex); + } + } +#endif + // Generate a local notification that the host client is disconnected if (IsHost) { @@ -1689,7 +1909,6 @@ internal void ShutdownInternal() NetworkTimeSystem?.Shutdown(); NetworkTickSystem = null; - if (localClient.IsClient) { // If we were a client, we want to know if we were a host @@ -1965,5 +2184,12 @@ internal static void OnOneTimeTearDown() } #endif +#if UNIFIED_NETCODE + // TODO-UNIFIED: We might not need all of this (i.e. UnifiedUpdateConnections might be handled differently in unified) + public delegate void OnConnectDelegate(NetcodeConnection connection); + public delegate void OnDisconnectDelegate(NetcodeConnection connection); + public static OnConnectDelegate OnNetCodeConnect; + public static OnDisconnectDelegate OnNetCodeDisconnect; +#endif } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index d2f5aa5869..4005627d6a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -5,13 +5,13 @@ using System.Text; using Unity.Netcode.Components; using Unity.Netcode.Runtime; +#if UNIFIED_NETCODE +using Unity.NetCode; +#endif + #if UNITY_EDITOR using UnityEditor; -#if UNITY_2021_2_OR_NEWER using UnityEditor.SceneManagement; -#else -using UnityEditor.Experimental.SceneManagement; -#endif #endif using UnityEngine; using UnityEngine.SceneManagement; @@ -287,6 +287,10 @@ internal void OnValidate() // Always check for in-scene placed to assure any previous version scene assets with in-scene place NetworkObjects gets updated. CheckForInScenePlaced(); +#if UNIFIED_NETCODE + UnifiedValidation(); +#endif + // If the GlobalObjectIdHash value changed, then mark the asset dirty. if (GlobalObjectIdHash != oldValue) { @@ -348,6 +352,39 @@ private void CheckForInScenePlaced() } #endif // UNITY_EDITOR +#if UNIFIED_NETCODE + [HideInInspector] + [SerializeField] + internal GhostAdapter GhostAdapter; + + [HideInInspector] + [SerializeField] + internal bool HasGhost; + + [HideInInspector] + [SerializeField] + internal bool HadBridge; +#if UNITY_EDITOR + internal void UnifiedValidation() + { + NetworkObjectBridge = GetComponent(); + GhostAdapter = GetComponent(); + HasGhost = GhostAdapter != null; + if (HasGhost && NetworkObjectBridge == null) + { + NetworkObjectBridge = gameObject.AddComponent(); + HadBridge = true; + // Transform synchronization is handled by unified netcode + SynchronizeTransform = false; + } + else if (HadBridge && !HasGhost && !NetworkObjectBridge) + { + HadBridge = false; + SynchronizeTransform = true; + } + } +#endif +#endif /// /// Gets the NetworkManager that owns this NetworkObject instance /// @@ -1737,10 +1774,19 @@ private void OnDestroy() return; } + var spawnManager = NetworkManager.SpawnManager; + // Always attempt to remove from scene changed updates - networkManager.SpawnManager?.RemoveNetworkObjectFromSceneChangedUpdates(this); + spawnManager?.RemoveNetworkObjectFromSceneChangedUpdates(this); +#if UNIFIED_NETCODE + spawnManager?.GhostsPendingSpawn.Remove(NetworkObjectId); + spawnManager?.GhostsPendingSynchronization.Remove(NetworkObjectId); + // N4E controls this on the client, allow this if there is a ghost + if (IsSpawned && !HasGhost && !networkManager.ShutdownInProgress) +#else if (IsSpawned && !networkManager.ShutdownInProgress) +#endif { // An authorized destroy is when done by the authority instance or done due to a scene event and the NetworkObject // was marked as destroy pending scene event (which means the destroy with scene property was set). @@ -1768,11 +1814,11 @@ private void OnDestroy() } } - if (networkManager.SpawnManager != null && networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject)) + if (spawnManager != null && spawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject)) { if (this == networkObject) { - networkManager.SpawnManager.OnDespawnObject(networkObject, false); + spawnManager.OnDespawnObject(networkObject, false); } } } @@ -2634,6 +2680,7 @@ internal static void CheckOrphanChildren() internal void InvokeBehaviourNetworkPreSpawn() { + var networkManager = NetworkManager; InitializeChildNetworkBehaviours(); foreach (var childBehaviour in ChildNetworkBehaviours.Values) @@ -2665,7 +2712,7 @@ internal void InvokeBehaviourNetworkSpawn() childBehaviour.InternalOnNetworkSpawn(); } - // After initialization, we can then invoke OnNetworkSpawn on each child NetworkBehaviour. + // After internally spawning, we can then invoke OnNetworkSpawn on each child NetworkBehaviour. foreach (var childBehaviour in ChildNetworkBehaviours.Values) { if (!childBehaviour.gameObject.activeInHierarchy) @@ -2770,9 +2817,7 @@ internal bool InitializeChildNetworkBehaviours() networkTransform.IsNested = networkTransform.gameObject != gameObject; NetworkTransforms.Add(networkTransform); } - #if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D - var rigidbodyBase = behaviour as NetworkRigidbodyBase; if (rigidbodyBase != null) { @@ -2780,7 +2825,37 @@ internal bool InitializeChildNetworkBehaviours() } #endif } +#if UNIFIED_NETCODE + // For now, cycle through all known NetworkTransform and NetworkRigidbodyBase derived components + // and destroy them all if this is a hybrid prefab instance. + // This allows a user to not have to make direct adjustments until trying out their NGO prefab + // as a hybrid spawned prefab (optional to completely remove, will eventually become obsolete and + // automatically removed later). + if (HasGhost) + { + if (NetworkRigidbodies != null) + { + for (int i = NetworkRigidbodies.Count - 1; i >= 0; i--) + { + ChildNetworkBehaviours.Remove(NetworkRigidbodies[i].NetworkBehaviourId); + Destroy(NetworkRigidbodies[i]); + } + NetworkRigidbodies.Clear(); + } + // TODO: We might want to make this whole thing a noop as opposed to completely + // removing it. + if (NetworkTransforms != null) + { + for (int i = NetworkTransforms.Count - 1; i >= 0; i--) + { + ChildNetworkBehaviours.Remove(NetworkTransforms[i].NetworkBehaviourId); + Destroy(NetworkTransforms[i]); + } + NetworkTransforms.Clear(); + } + } +#endif return true; } @@ -2914,18 +2989,21 @@ internal struct SerializedObject public ulong OwnerClientId; public ushort OwnershipFlags; - private const ushort k_IsPlayerObject = 0x001; - private const ushort k_HasParent = 0x002; - private const ushort k_IsSceneObject = 0x004; - private const ushort k_HasTransform = 0x008; - private const ushort k_IsLatestParentSet = 0x010; - private const ushort k_WorldPositionStays = 0x020; - private const ushort k_DestroyWithScene = 0x040; - private const ushort k_DontDestroyWithOwner = 0x080; - private const ushort k_HasOwnershipFlags = 0x100; - private const ushort k_SyncObservers = 0x200; - private const ushort k_SpawnWithObservers = 0x400; - private const ushort k_HasInstantiationData = 0x800; + private const ushort k_IsPlayerObject = 0x0001; + private const ushort k_HasParent = 0x0002; + private const ushort k_IsSceneObject = 0x0004; + private const ushort k_HasTransform = 0x0008; + private const ushort k_IsLatestParentSet = 0x0010; + private const ushort k_WorldPositionStays = 0x0020; + private const ushort k_DestroyWithScene = 0x0040; + private const ushort k_DontDestroyWithOwner = 0x0080; + private const ushort k_HasOwnershipFlags = 0x0100; + private const ushort k_SyncObservers = 0x0200; + private const ushort k_SpawnWithObservers = 0x0400; + private const ushort k_HasInstantiationData = 0x0800; +#if UNIFIED_NETCODE + private const ushort k_HasGhost = 0x1000; +#endif public bool IsPlayerObject; public bool HasParent; @@ -2946,6 +3024,9 @@ internal struct SerializedObject public bool SyncObservers; public bool SpawnWithObservers; public bool HasInstantiationData; +#if UNIFIED_NETCODE + public bool HasGhost; +#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ushort GetBitsetRepresentation() @@ -2995,10 +3076,19 @@ internal ushort GetBitsetRepresentation() { bitset |= k_SpawnWithObservers; } + if (HasInstantiationData) { bitset |= k_HasInstantiationData; } + +#if UNIFIED_NETCODE + if (HasGhost) + { + bitset |= k_HasGhost; + } + +#endif return bitset; } @@ -3017,6 +3107,9 @@ internal void SetStateFromBitset(ushort bitset) SyncObservers = (bitset & k_SyncObservers) != 0; SpawnWithObservers = (bitset & k_SpawnWithObservers) != 0; HasInstantiationData = (bitset & k_HasInstantiationData) != 0; +#if UNIFIED_NETCODE + HasGhost = (bitset & k_HasGhost) != 0; +#endif } // When handling the initial synchronization of NetworkObjects, @@ -3282,7 +3375,10 @@ internal SerializedObject Serialize(ulong targetClientId = NetworkManager.Server Hash = CheckForGlobalObjectIdHashOverride(), OwnerObject = this, TargetClientId = targetClientId, - HasInstantiationData = InstantiationData != null && InstantiationData.Length > 0 + HasInstantiationData = InstantiationData != null && InstantiationData.Length > 0, +#if UNIFIED_NETCODE + HasGhost = HasGhost, +#endif }; // Handle Parenting @@ -3552,7 +3648,91 @@ private void Awake() { SetCachedParent(transform.parent); SceneOrigin = gameObject.scene; + + } + +#if UNIFIED_NETCODE + +#if DEBUG_ENABLE_DISABLE + private void OnEnable() + { + Debug.Log("Enabled!"); + } + + private void OnDisable() + { + Debug.Log("Disabled!"); + if (IsSpawned || HasGhost) + { + if (HasGhost && GhostAdapter.IsPrefab()) + { + return; + } + gameObject.SetActive(true); + } + + try + { + throw new Exception("Disabled trap!"); + } + catch (Exception ex) + { + Debug.LogWarning($"[{name}][{ex.Message}] Callstack:\n{ex.StackTrace}"); + } + } +#endif + + private void Start() + { + InitGhost(); + } + [SerializeField] + [HideInInspector] + internal NetworkObjectBridge NetworkObjectBridge; + + private void InitGhost() + { + // All instances with Ghosts are automatically registered + if (HasGhost && NetworkObjectBridge && !GhostAdapter.IsPrefab()) + { + if (NetworkManager.LogLevel == LogLevel.Developer) + { + Debug.Log($"[{nameof(NetworkObject)}] GhostBridge {name} detected and instantiated."); + } + if (GhostAdapter.WasInitialized && NetworkObjectBridge.NetworkObjectId.Value != 0) + { + RegisterGhostBridge(); + } + } + } + + internal void RegisterGhostBridge() + { + if (NetworkManager.LogLevel == LogLevel.Developer) + { + Debug.Log($"[{nameof(NetworkObject)}][{nameof(NetworkObjectId)}] NetworkObjectBridge notified instance exists with assigned ID of: {NetworkObjectBridge.NetworkObjectId.Value}"); + if (!NetworkManager.IsListening) + { + Debug.LogWarning($"[{nameof(NetworkObject)}] Did not register because there is no session in progress!"); + return; + } + } + + // Set when running through integration tests in order to initially bypass the + // normal registration. This is because at this point in the instantiation process, + // NetworkObject's NetworkManager is pointing to the singleton which means all instances + // (even if intended to be for a specific client) will end up registering with whichever + // NetworkManager instance is being pointed to by the singleton. + if (NetworkSpawnManager.RegisterPendingGhost != null) + { + NetworkSpawnManager.RegisterPendingGhost(this, NetworkObjectBridge.NetworkObjectId.Value); + } + else if (!NetworkManager.IsServer) + { + NetworkManager.SpawnManager.RegisterGhostPendingSpawn(this, NetworkObjectBridge.NetworkObjectId.Value); + } } +#endif /// /// Update diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/IDeferredNetworkMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/IDeferredNetworkMessageManager.cs index 910021f606..2e6dd7cfbd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/IDeferredNetworkMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/IDeferredNetworkMessageManager.cs @@ -8,6 +8,9 @@ internal enum TriggerType OnSpawn, OnAddPrefab, OnNextFrame, +#if UNIFIED_NETCODE + OnGhostSpawned, +#endif } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs index cf518a6216..f00c7cb0f3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs @@ -120,6 +120,25 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int ByteUnpacker.ReadValuePacked(reader, out NetworkObjectId); } +#if UNIFIED_NETCODE + // Leaving for debugging purposes + //if (networkManager.LogLevel == LogLevel.Developer) + //{ + // UnityEngine.Debug.Log($"Received {nameof(CreateObjectMessage)} for NetworkObjectId-{ObjectInfo.NetworkObjectId}."); + //} + + // For now, we will defer the create object message until the associated Ghost is spawned + if (ObjectInfo.HasGhost && !networkManager.SpawnManager.GhostsPendingSpawn.ContainsKey(ObjectInfo.NetworkObjectId)) + { + if (networkManager.LogLevel == LogLevel.Developer) + { + UnityEngine.Debug.Log($"[{nameof(NetworkObject)}-{ObjectInfo.NetworkObjectId}] Deferring {nameof(CreateObjectMessage)} to wait for Ghost."); + } + networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnGhostSpawned, ObjectInfo.NetworkObjectId, reader, ref context, k_Name); + return false; + } +#endif + if (!networkManager.NetworkConfig.ForceSamePrefabs && !networkManager.SpawnManager.HasPrefab(ObjectInfo)) { networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab, ObjectInfo.Hash, reader, ref context, k_Name); diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs index 25c53a2786..c0452146b4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/SceneEventMessage.cs @@ -1,4 +1,3 @@ - namespace Unity.Netcode { // Todo: Would be lovely to get this one nicely formatted with all the data it sends in the struct diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs index c66aa62273..a2a8a11488 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs @@ -228,6 +228,8 @@ internal void HandleIncomingData(ulong clientId, ArraySegment data, float { unsafe { + + //Debug.Log($"Receiving {data.Count} bytes: {ByteArrayToString(data.Array, data.Offset, data.Count)}"); fixed (byte* dataPtr = data.Array) { var batchReader = new FastBufferReader(dataPtr + data.Offset, Allocator.None, data.Count); @@ -870,6 +872,7 @@ internal unsafe void ProcessSendQueues() try { + //Debug.Log($"Sending {queueItem.Writer.Length} bytes: {ByteArrayToString(queueItem.Writer.ToArray(), 0, queueItem.Writer.Length)}"); m_Sender.Send(clientId, queueItem.NetworkDelivery, queueItem.Writer); for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx) diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index 2fbdf1fbe9..641fed9b8e 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -2756,8 +2756,14 @@ internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearSceneP 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 UNIFIED_NETCODE + if (!networkObjectInstance.HasGhost && networkObjectInstance.IsSceneObject != false && (networkObjectInstance.NetworkManager == NetworkManager || + networkObjectInstance.NetworkManagerOwner == null) && sceneHandle == sceneToFilterBy.handle) +#else if (networkObjectInstance.IsSceneObject != false && (networkObjectInstance.NetworkManager == NetworkManager || networkObjectInstance.NetworkManagerOwner == null) && sceneHandle == sceneToFilterBy.handle) + +#endif { if (!ScenePlacedObjects.ContainsKey(globalObjectIdHash)) { diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 0fff313574..e5ae1aafbb 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -5,6 +5,7 @@ using Unity.Collections; using UnityEngine.SceneManagement; + namespace Unity.Netcode { /// @@ -1106,6 +1107,10 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) { builder.AppendLine($"[Read][Synchronize Objects][WPos: {InternalBuffer.Position}][NO-Count: {newObjectsCount}] Begin:"); } +#if UNIFIED_NETCODE + // TODO-UNIFIED: This is a temporary POC fix to handle hybrid spawning where the Ghost instance might not yet exist. + var spawnManager = m_NetworkManager.SpawnManager; +#endif for (int i = 0; i < newObjectsCount; i++) { @@ -1113,6 +1118,42 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) var serializedObject = new NetworkObject.SerializedObject(); serializedObject.Deserialize(InternalBuffer); +#if UNIFIED_NETCODE + // TODO-UNIFIED: This is a temporary POC fix to handle synchronizing hybrid spawned objects where the Ghost instance might not yet exist. + if (serializedObject.HasGhost) + { + if (!networkManager.SpawnManager.GhostsPendingSpawn.ContainsKey(serializedObject.NetworkObjectId)) + { + if (networkManager.LogLevel == LogLevel.Developer) + { + UnityEngine.Debug.Log($"[{nameof(SceneEventData)}][{nameof(SynchronizeSceneNetworkObjects)}] Deferring creation of NetworkObjectId-{serializedObject.NetworkObjectId} to wait for Ghost."); + } + + var newEntry = new PendingGhostSpawnEntry() + { + RegistrationTime = UnityEngine.Time.realtimeSinceStartup, + SerializedObject = serializedObject, + Buffer = new FastBufferReader(InternalBuffer, Allocator.Persistent, serializedObject.SynchronizationDataSize, InternalBuffer.Position) + }; + + spawnManager.RegisterGhostPendingSynchronization(newEntry); + InternalBuffer.Seek(InternalBuffer.Position + serializedObject.SynchronizationDataSize); + continue; + } + else if (networkManager.SpawnManager.GhostsPendingSpawn[serializedObject.NetworkObjectId] == null) + { + if (networkManager.LogLevel == LogLevel.Developer) + { + UnityEngine.Debug.Log($"[{nameof(SceneEventData)}][{nameof(SynchronizeSceneNetworkObjects)}] Dropping creation of NetworkObjectId-{serializedObject.NetworkObjectId} as it has an entry but no longer exists!"); + } + // If it no longer exists, then just remove the entry and skip it. + InternalBuffer.Seek(InternalBuffer.Position + serializedObject.SynchronizationDataSize); + networkManager.SpawnManager.GhostsPendingSpawn.Remove(serializedObject.NetworkObjectId); + continue; + } + } +#endif + // If the sceneObject is in-scene placed, then set the scene being synchronized if (serializedObject.IsSceneObject) { @@ -1120,9 +1161,9 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) } var spawnedNetworkObject = NetworkObject.Deserialize(serializedObject, InternalBuffer, networkManager); - var noStop = InternalBuffer.Position; if (EnableSerializationLogs) { + var noStop = InternalBuffer.Position; builder.AppendLine($"[Head: {noStart}][Tail: {noStop}][Size: {noStop - noStart}][{spawnedNetworkObject.name}][NID-{spawnedNetworkObject.NetworkObjectId}][Children: {spawnedNetworkObject.ChildNetworkBehaviours.Count}]"); LogArray(InternalBuffer.ToArray(), noStart, noStop, builder); } @@ -1399,4 +1440,14 @@ internal SceneEventData(NetworkManager networkManager) SceneEventId = XXHash.Hash32(Guid.NewGuid().ToString()); } } + +#if UNIFIED_NETCODE + internal struct PendingGhostSpawnEntry + { + public float RegistrationTime; + public FastBufferReader Buffer; + public NetworkObject.SerializedObject SerializedObject; + + } +#endif } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 52e891f8e1..6bae4d5c2f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -33,6 +33,167 @@ public class NetworkSpawnManager /// public readonly HashSet SpawnedObjectsList = new HashSet(); +#if UNIFIED_NETCODE + + internal readonly Dictionary GhostsPendingSpawn = new Dictionary(); + + // TODO: We might want to make this a mock interfacebut temporary solution to validate + // the need to assure we are registering with the right NetworkManager instance when testing (everything + // will use the singleton during Awake and Start when we need to register). + internal delegate void RegisterPendingGhostDelegateHandler(NetworkObject networkObject, ulong networkObjectId); + + internal static RegisterPendingGhostDelegateHandler RegisterPendingGhost; + + internal void RegisterGhostPendingSpawn(NetworkObject networkObject, ulong networkObjectId) + { + if (NetworkManager.LogLevel == LogLevel.Developer) + { + Debug.Log($"[{nameof(RegisterGhostPendingSpawn)}] Registering {networkObject.name} with a {nameof(NetworkObject.NetworkObjectId)} of {networkObjectId}."); + } + if (GhostsPendingSpawn.TryAdd(networkObjectId, networkObject)) + { + // TODO-REVIEW-BELOW: *** This is very likely no longer an issue with the new connection sequence *** + // TODO-UNIFIED: We need a better way to preserve any hybrid instances pending NGO spawn. + // Edge-Case scenario: During initial client synchronization (i.e. !NetworkManager.IsConnectedClient). + // + // Description: A client can receive snapshots before finishing the NGO synchronization process. + // This is when an edge case scenario can happen where the initial NGO synchronization information + // can include new scenes to load. If one of those scenes is configured to load in SingleMode, then + // any instantiated ghosts pending synchronization would be instantiated in whatever the currently + // active scene was when the client was processing the synchronization data. If the ghosts pending + // synchrpnization are in the currently active scene when the new scene is loaded in SingleMode, then + // they would be destroyed. + // + // Current Fix: + // If the client is not yet synchronized, then any ghost pending spawn get migrated into the DDOL. + // + // Further review: + // We need to make sure that we are migrating NetworkObjects into their assigned scene (if scene + // management is enabled). Currently, we assume all instances were in the DDOL and just migrate + // them into the currently active scene upon spawn. + if (!NetworkManager.IsConnectedClient && !GhostsPendingSynchronization.ContainsKey(networkObjectId)) + { + Object.DontDestroyOnLoad(networkObject.gameObject); + } + else // There is matching spawn data for this pending Ghost, process the pending spawn for this hybrid instance. + { + NetworkManager.DeferredMessageManager.ProcessTriggers(IDeferredNetworkMessageManager.TriggerType.OnGhostSpawned, networkObjectId); + if (GhostsPendingSynchronization.ContainsKey(networkObjectId)) + { + ProcessGhostPendingSynchronization(networkObjectId); + } + } + } + else + { + Debug.LogError($"[{networkObject.name}-{networkObjectId}] Has already been registered as a pending ghost!"); + } + } + + internal NetworkObject GetGhostNetworkObjectForSpawn(ulong networkObjectId) + { + if (!GhostsPendingSpawn.ContainsKey(networkObjectId)) + { + Debug.LogError($"[{nameof(GetGhostNetworkObjectForSpawn)}] Attempting to spawn NetworkObject-{networkObjectId} with no instance to spawn!"); + return null; + } + var networkObject = GhostsPendingSpawn[networkObjectId]; + + GhostsPendingSpawn.Remove(networkObjectId); + if (networkObject != null) + { + // TODO-UNIFIED: We need a better way to preserve any hybrid instances pending NGO spawn. + // NOTE: We might be able to use the NetworkSceneHandle to get the associated local scene handle to which we can use to get the targeted scene. + UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(networkObject.gameObject, UnityEngine.SceneManagement.SceneManager.GetActiveScene()); + } + return networkObject; + } + + internal bool GhostsArePendingSynchronization; + internal readonly Dictionary GhostsPendingSynchronization = new Dictionary(); + internal void RegisterGhostPendingSynchronization(PendingGhostSpawnEntry pendingGhostSpawnEntry) + { + var networkObjectId = pendingGhostSpawnEntry.SerializedObject.NetworkObjectId; + if (NetworkManager.LogLevel == LogLevel.Developer) + { + Debug.Log($"[{nameof(RegisterGhostPendingSpawn)}] Registering {nameof(NetworkObject)}-{networkObjectId} for pending synchronization."); + } + GhostsPendingSynchronization.TryAdd(networkObjectId, pendingGhostSpawnEntry); + GhostsArePendingSynchronization = true; + } + + internal void ProcessGhostPendingSynchronization(ulong networkObjectId, bool removeUponSpawn = true) + { + var ghostPendingSynch = GhostsPendingSynchronization[networkObjectId]; + var serializedObject = ghostPendingSynch.SerializedObject; + var reader = ghostPendingSynch.Buffer; + if (removeUponSpawn) + { + GhostsPendingSynchronization.Remove(networkObjectId); + } + + if (serializedObject.IsSceneObject) + { + NetworkManager.SceneManager.SetTheSceneBeingSynchronized(serializedObject.NetworkSceneHandle); + } + var networkObject = NetworkObject.Deserialize(serializedObject, reader, NetworkManager); + // TODO-UNIFIED: How do we handle the "all in-scene placed objects are spawned notification"? + //if (serializedObject.IsSceneObject) + //{ + // networkObject.InternalInSceneNetworkObjectsSpawned(); + //} + if (removeUponSpawn) + { + GhostsPendingSynchronization.Remove(networkObjectId); + GhostsArePendingSynchronization = GhostsPendingSynchronization.Count > 0; + ghostPendingSynch.Buffer.Dispose(); + } + } + + + private HashSet m_GhostSynchronizationPendingRemoval = new HashSet(); + + internal void ProcessAllGhostsPendingSynchronization() + { + var spawnTimeout = NetworkManager.NetworkConfig.SpawnTimeout; + var logLevel = NetworkManager.LogLevel; + if (!GhostsArePendingSynchronization) + { + return; + } + foreach (var ghost in GhostsPendingSynchronization) + { + var networkObjectId = ghost.Value.SerializedObject.NetworkObjectId; + if (GhostsPendingSpawn.ContainsKey(networkObjectId)) + { + // Process it, but don't remove it as we handle that a little later + ProcessGhostPendingSynchronization(ghost.Value.SerializedObject.NetworkObjectId, false); + m_GhostSynchronizationPendingRemoval.Add(networkObjectId); + } + else + if ((ghost.Value.RegistrationTime + spawnTimeout) < Time.realtimeSinceStartup) + { + if (logLevel == LogLevel.Developer) + { + Debug.LogWarning($"[{nameof(NetworkSpawnManager)}][{nameof(ProcessAllGhostsPendingSynchronization)}] NetworkObject-{networkObjectId} pending Ghost spawn timed out wiating for the Ghost instance to spawn!"); + } + // Timed out entries are removed too + m_GhostSynchronizationPendingRemoval.Add(ghost.Key); + } + } + + foreach (var networkObjectId in m_GhostSynchronizationPendingRemoval) + { + var entry = GhostsPendingSynchronization[networkObjectId]; + GhostsPendingSynchronization.Remove(networkObjectId); + entry.Buffer.Dispose(); + } + m_GhostSynchronizationPendingRemoval.Clear(); + GhostsArePendingSynchronization = GhostsPendingSynchronization.Count > 0; + } + +#endif + /// /// Use to get all NetworkObjects owned by a client /// Ownership to Objects Table Format: @@ -966,32 +1127,49 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SerializedObject s var parentNetworkId = serializedObject.HasParent ? serializedObject.ParentObjectId : default; var worldPositionStays = (!serializedObject.HasParent) || serializedObject.WorldPositionStays; - // If scene management is disabled or the NetworkObject was dynamically spawned - if (!NetworkManager.NetworkConfig.EnableSceneManagement || !serializedObject.IsSceneObject) +#if UNIFIED_NETCODE + if (serializedObject.HasGhost) { - networkObject = GetNetworkObjectToSpawn(serializedObject.Hash, serializedObject.OwnerClientId, position, rotation, serializedObject.IsSceneObject, instantiationData); + // TODO-UNIFIED: Get this working somehow (or if not possible prevent this from happening prior to getting to this point) + if (serializedObject.HasInstantiationData) + { + Debug.LogError($"[{nameof(NetworkObject)}] Pre-spawn instantiation data does not work in this version!"); + } + networkObject = GetGhostNetworkObjectForSpawn(serializedObject.NetworkObjectId); + if (networkObject == null) + { + throw new Exception($"Failed to get spawned Ghost object!"); + } } - else // Get the in-scene placed NetworkObject + else +#endif { - networkObject = NetworkManager.SceneManager.GetSceneRelativeInSceneNetworkObject(globalObjectIdHash, serializedObject.NetworkSceneHandle); - if (networkObject == null) + // If scene management is disabled or the NetworkObject was dynamically spawned + if (!NetworkManager.NetworkConfig.EnableSceneManagement || !serializedObject.IsSceneObject) { - if (NetworkLog.CurrentLogLevel <= LogLevel.Error) + networkObject = GetNetworkObjectToSpawn(serializedObject.Hash, serializedObject.OwnerClientId, position, rotation, serializedObject.IsSceneObject, instantiationData); + } + else // Get the in-scene placed NetworkObject + { + networkObject = NetworkManager.SceneManager.GetSceneRelativeInSceneNetworkObject(globalObjectIdHash, serializedObject.NetworkSceneHandle); + if (networkObject == null) { - NetworkLog.LogError($"{nameof(NetworkPrefab)} hash was not found! In-Scene placed {nameof(NetworkObject)} soft synchronization failure for Hash: {globalObjectIdHash}!"); - } + if (NetworkLog.CurrentLogLevel <= LogLevel.Error) + { + NetworkLog.LogError($"{nameof(NetworkPrefab)} hash was not found! In-Scene placed {nameof(NetworkObject)} soft synchronization failure for Hash: {globalObjectIdHash}!"); + } - return null; - } + return null; + } - // Since this NetworkObject is an in-scene placed NetworkObject, if it is disabled then enable it so - // NetworkBehaviours will have their OnNetworkSpawn method invoked - if (!networkObject.gameObject.activeInHierarchy) - { - networkObject.gameObject.SetActive(true); + // Since this NetworkObject is an in-scene placed NetworkObject, if it is disabled then enable it so + // NetworkBehaviours will have their OnNetworkSpawn method invoked + if (!networkObject.gameObject.activeInHierarchy) + { + networkObject.gameObject.SetActive(true); + } } } - if (networkObject == null) { return null; @@ -1189,6 +1367,13 @@ internal bool AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n return false; } +#if UNIFIED_NETCODE + if (networkObject.HasGhost) + { + networkObject.NetworkObjectBridge.SetNetworkObjectId(networkObject.NetworkObjectId); + } +#endif + // When done spawning invoke post spawn networkObject.InvokeBehaviourNetworkPostSpawn(); @@ -1914,7 +2099,14 @@ internal void OnDespawnObject([NotNull] NetworkObject networkObject, bool destro { RemovePlayerObject(networkObject, destroyGameObject); } - +#if UNIFIED_NETCODE + // Let unified netcode handle destroying + if (destroyGameObject && networkObject.HasGhost && !NetworkManager.IsServer) + { + // exit early + return; + } +#endif var gobj = networkObject.gameObject; if (destroyGameObject && gobj != null) { @@ -2057,6 +2249,10 @@ internal NetworkSpawnManager(NetworkManager networkManager) internal void Shutdown() { +#if UNIFIED_NETCODE + GhostsPendingSpawn.Clear(); + GhostsPendingSynchronization.Clear(); +#endif NetworkObjectsToSynchronizeSceneChanges?.Clear(); CleanUpDisposedObjects?.Clear(); } diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified.meta b/com.unity.netcode.gameobjects/Runtime/Transports/Unified.meta new file mode 100644 index 0000000000..b4340f3a18 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5ce5c8d0dc8948aab8482e617eccabcc +timeCreated: 1772129529 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs new file mode 100644 index 0000000000..c7c813b872 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs @@ -0,0 +1,474 @@ +#if UNIFIED_NETCODE && OUT_OF_BAND_RPC +using System; +using System.Collections.Generic; +using Unity.Burst; +using Unity.Burst.Intrinsics; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Entities; +using Unity.NetCode; +using Unity.Netcode.Transports.UTP; +using UnityEngine; + +namespace Unity.Netcode.Unified +{ + [BurstCompile] + internal unsafe struct FixedBytes1280 + { + public fixed byte Buffer[1280]; + public int Length; + + // Returns a direct pointer to the data in the buffer. + // Implemented as a static with an in-parameter to avoid the buffer being copied while keeping its memory allocation fixed/non-heap + // Note that the buffer MUST outlive the returned pointer, as it is an alias. + public static byte* GetUnsafePtr(in FixedBytes1280 data) + { + fixed (byte* buffer = data.Buffer) + { + return buffer; + } + } + + // Returns a native array that is an alias of the existing data without copying it + // Implemented as a static with an in-parameter to avoid the buffer being copied while keeping its memory allocation fixed/non-heap + // Note that the buffer MUST outlive the returned array, as it is an alias. + public static NativeArray ToNativeArray(in FixedBytes1280 data) + { + var array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(GetUnsafePtr(data), data.Length, Allocator.None); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + var safety = CollectionHelper.CreateSafetyHandle(Allocator.None); + NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, safety); +#endif + return array; + } + } + + [BurstCompile] + internal struct TransportRpc : IOutOfBandRpcCommand, IRpcCommandSerializer + { + public FixedBytes1280 Buffer; + public ulong Order; + + public unsafe void Serialize(ref DataStreamWriter writer, in RpcSerializerState state, in TransportRpc data) + { + writer.WriteULong(data.Order); + writer.WriteInt(data.Buffer.Length); + var span = new Span(FixedBytes1280.GetUnsafePtr(data.Buffer), data.Buffer.Length); + writer.WriteBytes(span); + } + + public unsafe void Deserialize(ref DataStreamReader reader, in RpcDeserializerState state, ref TransportRpc data) + { + data.Order = reader.ReadULong(); + var length = reader.ReadInt(); + data.Buffer = new FixedBytes1280 + { + Length = length + }; + + var span = new Span(FixedBytes1280.GetUnsafePtr(data.Buffer), length); + reader.ReadBytes(span); + } + + [BurstCompile(DisableDirectCall = true)] + private static void InvokeExecute(ref RpcExecutor.Parameters parameters) + { + RpcExecutor.ExecuteCreateRequestComponent(ref parameters); + } + + private static readonly PortableFunctionPointer k_InvokeExecuteFunctionPointer = new PortableFunctionPointer(InvokeExecute); + + public PortableFunctionPointer CompileExecute() + { + return k_InvokeExecuteFunctionPointer; + } + } + + [UpdateInGroup(typeof(RpcCommandRequestSystemGroup))] + [CreateAfter(typeof(RpcSystem))] + [BurstCompile] + internal partial struct TransportRpcCommandRequestSystem : ISystem + { + private RpcCommandRequest m_Request; + + [BurstCompile] + internal struct SendRpc : IJobChunk + { + public RpcCommandRequest.SendRpcData Data; + + public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) + { + Data.Execute(chunk, unfilteredChunkIndex); + } + } + + public void OnCreate(ref SystemState state) + { + m_Request.OnCreate(ref state); + } + + [BurstCompile] + public void OnUpdate(ref SystemState state) + { + var sendJob = new SendRpc { Data = m_Request.InitJobData(ref state) }; + state.Dependency = sendJob.Schedule(m_Request.Query, state.Dependency); + } + } + + internal partial class UnifiedNetcodeUpdateSystem : SystemBase + { + public UnifiedNetcodeTransport Transport; + + public List DisconnectQueue = new List(); + + public void Disconnect(Connection connection) + { + DisconnectQueue.Add(connection); + } + + protected override void OnUpdate() + { + using var commandBuffer = new EntityCommandBuffer(Allocator.Temp); + foreach (var (request, rpc, entity) in SystemAPI.Query, RefRO>().WithEntityAccess()) + { + var connectionId = SystemAPI.GetComponent(request.ValueRO.SourceConnection).Value; + + var buffer = rpc.ValueRO.Buffer; + try + { + Transport.DispatchMessage(connectionId, buffer, rpc.ValueRO.Order); + } + finally + { + commandBuffer.DestroyEntity(entity); + } + } + + foreach (var connection in DisconnectQueue) + { + commandBuffer.AddComponent(connection.ConnectionEntity); + } + DisconnectQueue.Clear(); + + commandBuffer.Playback(EntityManager); + + } + } + + internal class UnifiedNetcodeTransport : NetworkTransport + { + private const int k_MaxPacketSize = 1280; + + private int m_ServerClientId = -1; + public override ulong ServerClientId => (ulong)m_ServerClientId; + + private NetworkManager m_NetworkManager; + + private IRealTimeProvider m_RealTimeProvider; + + private class ConnectionInfo + { + public BatchedSendQueue SendQueue; + public BatchedReceiveQueue ReceiveQueue; + public Connection Connection; + public ulong LastSent; + public ulong LastReceived; + public Dictionary DeferredMessages; + } + + private Dictionary m_Connections; + + internal void DispatchMessage(int connectionId, in FixedBytes1280 buffer, ulong order) + { + var connectionInfo = m_Connections[connectionId]; + + if (order <= connectionInfo.LastReceived) + { + Debug.LogWarning("Received duplicate message, ignoring."); + return; + } + + if (order != connectionInfo.LastReceived + 1) + { + if (connectionInfo.DeferredMessages == null) + { + connectionInfo.DeferredMessages = new Dictionary(); + } + + connectionInfo.DeferredMessages[order] = buffer; + return; + } + + using var arr = FixedBytes1280.ToNativeArray(buffer); + var reader = new DataStreamReader(arr); + if (connectionInfo.ReceiveQueue == null) + { + connectionInfo.ReceiveQueue = new BatchedReceiveQueue(reader); + } + else + { + connectionInfo.ReceiveQueue.PushReader(reader); + } + + connectionInfo.LastReceived = order; + if (connectionInfo.DeferredMessages != null) + { + var next = order + 1; + while (connectionInfo.DeferredMessages.Remove(next, out var nextBuffer)) + { + reader = new DataStreamReader(FixedBytes1280.ToNativeArray(nextBuffer)); + connectionInfo.ReceiveQueue.PushReader(reader); + connectionInfo.LastReceived = next; + ++next; + } + } + + var message = connectionInfo.ReceiveQueue.PopMessage(); + while (message.Count != 0) + { + InvokeOnTransportEvent(NetworkEvent.Data, (ulong)connectionId, message, + m_RealTimeProvider.RealTimeSinceStartup); + message = connectionInfo.ReceiveQueue.PopMessage(); + } + } + + public override unsafe void Send(ulong clientId, ArraySegment payload, NetworkDelivery networkDelivery) + { + if (!m_Connections.TryGetValue((int)clientId, out ConnectionInfo connectionInfo)) + { + return; + } + + connectionInfo.SendQueue.PushMessage(payload); + + while (!connectionInfo.SendQueue.IsEmpty) + { + var rpc = new TransportRpc + { + Buffer = new FixedBytes1280(), + }; + + var writer = new DataStreamWriter(FixedBytes1280.GetUnsafePtr(rpc.Buffer), k_MaxPacketSize); + + var amount = connectionInfo.SendQueue.FillWriterWithBytes(ref writer, k_MaxPacketSize); + rpc.Buffer.Length = amount; + rpc.Order = ++connectionInfo.LastSent; + + var req = m_NetworkManager.NetcodeWorld.EntityManager.CreateEntity(ComponentType.ReadWrite(), ComponentType.ReadWrite()); + m_NetworkManager.NetcodeWorld.EntityManager.SetComponentData(req, new SendRpcCommandRequest{TargetConnection = connectionInfo.Connection.ConnectionEntity}); + m_NetworkManager.NetcodeWorld.EntityManager.SetComponentData(req, rpc); + + connectionInfo.SendQueue.Consume(amount); + } + } + + public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment payload, out float receiveTime) + { + clientId = 0; + payload = default; + receiveTime = 0; + return NetworkEvent.Nothing; + } + + private void OnClientConnectedToServer(Connection connection, NetCodeConnectionEvent connectionEvent) + { + m_Connections[connection.NetworkId.Value] = new ConnectionInfo + { + ReceiveQueue = null, + SendQueue = new BatchedSendQueue(BatchedSendQueue.MaximumMaximumCapacity), + Connection = connection + }; + m_ServerClientId = connection.NetworkId.Value; + InvokeOnTransportEvent(NetworkEvent.Connect, (ulong)connection.NetworkId.Value, default, m_RealTimeProvider.RealTimeSinceStartup); + } + + private void OnServerNewClientConnection(Connection connection, NetCodeConnectionEvent connectionEvent) + { + m_Connections[connection.NetworkId.Value] = new ConnectionInfo + { + ReceiveQueue = null, + SendQueue = new BatchedSendQueue(BatchedSendQueue.MaximumMaximumCapacity), + Connection = connection + }; ; + InvokeOnTransportEvent(NetworkEvent.Connect, (ulong)connection.NetworkId.Value, default, m_RealTimeProvider.RealTimeSinceStartup); + } + + private const string k_InvalidRpcMessage = "An invalid RPC was received"; + private const string k_HandshakeTimeoutMessage = "The connection was closed because the handshake timed out."; + private const string k_ApprovalFailureMessage = "The connection was closed because the connection was not approved by the server."; + private const string k_ApprovalTimeoutMessage = "The connection was closed because the connection approval process timed out."; + + private string GetDisconnectMessageFromNetworkStreamDisconnectReason(NetworkStreamDisconnectReason reason) + { + switch (reason) + { + case NetworkStreamDisconnectReason.ConnectionClose: + return UnityTransportNotificationHandler.DisconnectedMessage; + case NetworkStreamDisconnectReason.Timeout: + return UnityTransportNotificationHandler.TimeoutMessage; + case NetworkStreamDisconnectReason.MaxConnectionAttempts: + return UnityTransportNotificationHandler.MaxConnectionAttemptsMessage; + case NetworkStreamDisconnectReason.ClosedByRemote: + return UnityTransportNotificationHandler.ClosedRemoteConnectionMessage; + case NetworkStreamDisconnectReason.BadProtocolVersion: + return UnityTransportNotificationHandler.ProtocolErrorMessage; + case NetworkStreamDisconnectReason.InvalidRpc: + return k_InvalidRpcMessage; + case NetworkStreamDisconnectReason.AuthenticationFailure: + return UnityTransportNotificationHandler.AuthenticationFailureMessage; + case NetworkStreamDisconnectReason.ProtocolError: + return UnityTransportNotificationHandler.ProtocolErrorMessage; + case NetworkStreamDisconnectReason.HandshakeTimeout: + return k_HandshakeTimeoutMessage; + case NetworkStreamDisconnectReason.ApprovalFailure: + return k_ApprovalFailureMessage; + case NetworkStreamDisconnectReason.ApprovalTimeout: + return k_ApprovalTimeoutMessage; + } + return "Unknown reason"; + } + + private DisconnectEvents GetDisconnectEventFromNetworkStreamDisconnectReason(NetworkStreamDisconnectReason reason) + { + switch (reason) + { + case NetworkStreamDisconnectReason.ConnectionClose: + return DisconnectEvents.Disconnected; + case NetworkStreamDisconnectReason.Timeout: + return DisconnectEvents.ProtocolTimeout; + case NetworkStreamDisconnectReason.MaxConnectionAttempts: + return DisconnectEvents.MaxConnectionAttempts; + case NetworkStreamDisconnectReason.ClosedByRemote: + return DisconnectEvents.ClosedByRemote; + case NetworkStreamDisconnectReason.BadProtocolVersion: + return DisconnectEvents.ProtocolError; + case NetworkStreamDisconnectReason.InvalidRpc: + return DisconnectEvents.ProtocolError; + case NetworkStreamDisconnectReason.AuthenticationFailure: + return DisconnectEvents.AuthenticationFailure; + case NetworkStreamDisconnectReason.ProtocolError: + return DisconnectEvents.ProtocolError; + case NetworkStreamDisconnectReason.HandshakeTimeout: + return DisconnectEvents.ProtocolError; + case NetworkStreamDisconnectReason.ApprovalFailure: + return DisconnectEvents.AuthenticationFailure; + case NetworkStreamDisconnectReason.ApprovalTimeout: + return DisconnectEvents.ProtocolTimeout; + } + return DisconnectEvents.Disconnected; + } + + private void OnClientDisconnectFromServer(Connection connection, NetCodeConnectionEvent connectionEvent) + { + SetDisconnectEvent( + GetDisconnectEventFromNetworkStreamDisconnectReason(connectionEvent.DisconnectReason), + GetDisconnectMessageFromNetworkStreamDisconnectReason(connectionEvent.DisconnectReason) + ); + InvokeOnTransportEvent(NetworkEvent.Disconnect, (ulong)connection.NetworkId.Value, default, m_RealTimeProvider.RealTimeSinceStartup); + } + + private void OnServerClientDisconnected(Connection connection, NetCodeConnectionEvent connectionEvent) + { + InvokeOnTransportEvent(NetworkEvent.Disconnect, (ulong)connection.NetworkId.Value, default, m_RealTimeProvider.RealTimeSinceStartup); + } + + private void OnClientConnectionEvent(Connection connection, NetCodeConnectionEvent connectionEvent) + { + switch (connectionEvent.State) + { + case ConnectionState.State.Connected: + OnClientConnectedToServer(connection, connectionEvent); + break; + case ConnectionState.State.Disconnected: + OnClientDisconnectFromServer(connection, connectionEvent); + break; + } + } + + private void OnServerConnectionEvent(Connection connection, NetCodeConnectionEvent connectionEvent) + { + switch (connectionEvent.State) + { + case ConnectionState.State.Connected: + OnServerNewClientConnection(connection, connectionEvent); + break; + case ConnectionState.State.Disconnected: + OnServerClientDisconnected(connection, connectionEvent); + break; + } + } + + public override bool StartClient() + { + m_NetworkManager.NetcodeWorld.OnConnectionEvent += OnClientConnectionEvent; + var updateSystem = m_NetworkManager.NetcodeWorld.GetExistingSystemManaged(); + updateSystem.Transport = this; + return true; + } + + public override bool StartServer() + { + foreach (var connection in m_NetworkManager.NetcodeWorld.AllConnections) + { + OnServerNewClientConnection(connection, default); + } + + m_NetworkManager.NetcodeWorld.OnConnectionEvent += OnServerConnectionEvent; + var updateSystem = m_NetworkManager.NetcodeWorld.GetExistingSystemManaged(); + updateSystem.Transport = this; + return true; + } + + public override void DisconnectRemoteClient(ulong clientId) + { + m_NetworkManager.NetcodeWorld.DisconnectAClient(m_Connections[(int)clientId].Connection); + m_Connections.Remove((int)clientId); + } + + public override void DisconnectLocalClient() + { + // Remove the connection 1st (the world might not be available) + m_Connections.Remove((int)ServerClientId); + + // TODO-FIX-REVIEW-ME: + // This was causing errors to occur upon shutdown during an integration test. + // The cases being trapped for below yield no errors, but there might be some + // form of other underlying issue here: + + if (m_NetworkManager.NetcodeWorld == null || !m_NetworkManager.NetcodeWorld.IsCreated) + { + return; + } + + if (m_NetworkManager.IsServer || m_NetworkManager.NetcodeWorld.IsHost()) + { + if (m_NetworkManager.LogLevel <= LogLevel.Developer) + { + Debug.LogWarning("Host is attempting to shutdown the local client which is not required with a single world host."); + } + return; + } + m_NetworkManager.NetcodeWorld.RequestDisconnectFromServer(); + + } + + public override ulong GetCurrentRtt(ulong clientId) + { + var (transportId, _) = m_NetworkManager.ConnectionManager.ClientIdToTransportId(clientId); + return (ulong)m_Connections[(int)transportId].Connection.RTT; + } + + public override void Initialize(NetworkManager networkManager = null) + { + m_Connections = new Dictionary(); + m_RealTimeProvider = networkManager.RealTimeProvider; + m_NetworkManager = networkManager; + } + + public override void Shutdown() + { + + } + } +} +#endif diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs.meta b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs.meta new file mode 100644 index 0000000000..ace36beccc --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9980b4e2027240ceb731e395dc270359 +timeCreated: 1772129541 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef b/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef index 3d0e9a58bf..98a2cd6a29 100644 --- a/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef +++ b/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef @@ -13,7 +13,9 @@ "Unity.Networking.Transport", "Unity.Collections", "Unity.Burst", - "Unity.Mathematics" + "Unity.Mathematics", + "Unity.NetCode", + "Unity.Entities" ], "includePlatforms": [], "excludePlatforms": [], @@ -88,6 +90,16 @@ "expression": "6000.5.0a1", "define": "SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG" }, + { + "name": "com.unity.netcode", + "expression": "1.10.1", + "define": "UNIFIED_NETCODE" + }, + { + "name": "com.unity.multiplayer.playmode", + "expression": "0.1.0", + "define": "UNITY_MULTIPLAYER_PLAYMODE" + }, { "name": "Unity", "expression": "[6000.4.0b5,6000.5.0a1)", diff --git a/com.unity.netcode.gameobjects/Tests/Editor/ArithmeticTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/ArithmeticTests.cs index 2791e4ac84..f3f60669ed 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/ArithmeticTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/ArithmeticTests.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class ArithmeticTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs index 7ee23412bf..2715f33d93 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs @@ -5,7 +5,7 @@ using UnityEditor.Build.Reporting; using UnityEngine; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BuildTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/DisconnectMessageTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/DisconnectMessageTests.cs index e30ceccb1f..d7724be9c4 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/DisconnectMessageTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/DisconnectMessageTests.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using Unity.Collections; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class DisconnectMessageTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/InterpolatorTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/InterpolatorTests.cs index 666183abe6..520633a961 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/InterpolatorTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/InterpolatorTests.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class InterpolatorTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DisconnectOnSendTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DisconnectOnSendTests.cs index a33e7ac6f4..37a711a29c 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DisconnectOnSendTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/DisconnectOnSendTests.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class DisconnectOnSendTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageCorruptionTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageCorruptionTests.cs index a584ea61e6..aee1f2096d 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageCorruptionTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageCorruptionTests.cs @@ -8,7 +8,7 @@ using UnityEngine; using UnityEngine.TestTools; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class MessageCorruptionTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs index d77bf4ea72..5a879f1c20 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageReceivingTests.cs @@ -5,7 +5,7 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class MessageReceivingTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs index 66898947e6..025b3d54e4 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class MessageRegistrationTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs index ae2a1e955e..85049eec56 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageSendingTests.cs @@ -8,7 +8,7 @@ using UnityEngine.TestTools; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class MessageSendingTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageVersioningTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageVersioningTests.cs index 222c08b24b..623fff9227 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageVersioningTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageVersioningTests.cs @@ -3,7 +3,7 @@ using NUnit.Framework; using NUnit.Framework.Internal; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class MessageVersioningTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs index 3f5a35b3a3..a22d4c7375 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Messaging/NopMessageSender.cs @@ -1,4 +1,4 @@ -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NopMessageSender : INetworkMessageSender { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerConfigurationTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerConfigurationTests.cs index ea4a4b155b..52cdcdbf96 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerConfigurationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerConfigurationTests.cs @@ -1,14 +1,14 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using NUnit.Framework; -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using Unity.Netcode.Transports.UTP; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.TestTools; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NetworkManagerConfigurationTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkObjectTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkObjectTests.cs index e4f127564a..fd3bae3cb8 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkObjectTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkObjectTests.cs @@ -1,10 +1,10 @@ using System.Text.RegularExpressions; using NUnit.Framework; -using Unity.Netcode.Editor; +using Unity.Netcode.GameObjects.Editor; using UnityEngine; using UnityEngine.TestTools; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NetworkObjectTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkPrefabProcessorTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkPrefabProcessorTests.cs index f50e502633..0cd1ae134f 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkPrefabProcessorTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkPrefabProcessorTests.cs @@ -1,9 +1,9 @@ using NUnit.Framework; -using Unity.Netcode.Editor.Configuration; +using Unity.Netcode.GameObjects.Editor.Configuration; using UnityEditor; using UnityEngine; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NetworkPrefabProcessorTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/NetworkVar/NetworkVarTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/NetworkVar/NetworkVarTests.cs index 77ad69dd62..a411b994b0 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/NetworkVar/NetworkVarTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/NetworkVar/NetworkVarTests.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using UnityEngine; -namespace Unity.Netcode.EditorTests.NetworkVar +namespace Unity.Netcode.GameObjects.EditorTests.NetworkVar { internal class NetworkVarTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs index b6234001c8..f89c6ab3d3 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -4,7 +4,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal abstract class BaseFastBufferReaderWriterTest { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 9200d6b987..10a32a25b5 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BitCounterTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index de83183880..5dbf8f7f39 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -2,7 +2,7 @@ using NUnit.Framework; using Unity.Collections; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BitReaderTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index b4a84ff710..dfc3f5caf4 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -2,7 +2,7 @@ using NUnit.Framework; using Unity.Collections; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BitWriterTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index b6684ff0e0..7ee8cf5e8f 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -3,7 +3,7 @@ using Unity.Collections; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BufferSerializerTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 1bd197997f..30881efff0 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -6,7 +6,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BytePackerTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index 85aabbb52e..3c955f39aa 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -5,7 +5,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class FastBufferReaderTests : BaseFastBufferReaderWriterTest { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 32e580c66f..f52307d462 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -6,7 +6,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class FastBufferWriterTests : BaseFastBufferReaderWriterTest { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/UserBitReaderAndBitWriterTests_NCCBUG175.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/UserBitReaderAndBitWriterTests_NCCBUG175.cs index f279f4de05..5565a166f6 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/UserBitReaderAndBitWriterTests_NCCBUG175.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/UserBitReaderAndBitWriterTests_NCCBUG175.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using Unity.Collections; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class UserBitReaderAndBitWriterTests_NCCBUG175 { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Timing/ClientNetworkTimeSystemTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Timing/ClientNetworkTimeSystemTests.cs index fc7cb01d69..7b435523ea 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Timing/ClientNetworkTimeSystemTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Timing/ClientNetworkTimeSystemTests.cs @@ -2,7 +2,7 @@ using NUnit.Framework; using UnityEngine; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { /// /// Tests for running a as a client. diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs index 2a137bde2b..93403ef555 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Timing/NetworkTimeTests.cs @@ -5,7 +5,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class NetworkTimeTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Timing/ServerNetworkTimeSystemTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Timing/ServerNetworkTimeSystemTests.cs index 8d5edba0a4..8905363573 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Timing/ServerNetworkTimeSystemTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Timing/ServerNetworkTimeSystemTests.cs @@ -1,7 +1,7 @@ using NUnit.Framework; using UnityEngine; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class ServerNetworkTimeSystemTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Timing/TimingTestHelper.cs b/com.unity.netcode.gameobjects/Tests/Editor/Timing/TimingTestHelper.cs index e1dee9b3a7..4ed20b4a33 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Timing/TimingTestHelper.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Timing/TimingTestHelper.cs @@ -2,7 +2,7 @@ using UnityEngine; using Random = System.Random; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { /// /// Helper functions for timing related tests. Allows to get a set of time steps and simulate time advancing without the need of a full playmode test. diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedReceiveQueueTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedReceiveQueueTests.cs index 97671f906f..26a9ca94cb 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedReceiveQueueTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedReceiveQueueTests.cs @@ -3,7 +3,7 @@ using Unity.Collections; using Unity.Netcode.Transports.UTP; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BatchedReceiveQueueTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedSendQueueTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedSendQueueTests.cs index f27bef95d3..81d65b92f9 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedSendQueueTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/BatchedSendQueueTests.cs @@ -3,7 +3,7 @@ using Unity.Collections; using Unity.Netcode.Transports.UTP; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class BatchedSendQueueTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs index 34f785074a..22e075eec1 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs @@ -4,7 +4,7 @@ using UnityEngine; using UnityEngine.TestTools; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class UnityTransportTests { 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 06002bf7d2..20be2d58f0 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 @@ -1,6 +1,6 @@ { - "name": "Unity.Netcode.Editor.Tests", - "rootNamespace": "Unity.Netcode.EditorTests", + "name": "Unity.Netcode.GameObjects.Editor.Tests", + "rootNamespace": "Unity.Netcode.GameObjects.EditorTests", "references": [ "Unity.Collections", "Unity.Netcode.Runtime", @@ -13,7 +13,8 @@ "Unity.Mathematics", "UnityEngine.TestRunner", "UnityEditor.TestRunner", - "Unity.Netcode.Runtime.Tests" + "Unity.Netcode.Runtime.Tests", + "Unity.Netcode.GameObjects.Editor" ], "includePlatforms": [ "Editor" @@ -45,9 +46,9 @@ "define": "SCENE_MANAGEMENT_SCENE_HANDLE_NO_INT_CONVERSION" }, { - "name": "Unity", - "expression": "6000.5.0a1", - "define": "SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG" + "name": "Unity", + "expression": "6000.5.0a1", + "define": "SCENE_MANAGEMENT_SCENE_HANDLE_MUST_USE_ULONG" } ], "noEngineReferences": false diff --git a/com.unity.netcode.gameobjects/Tests/Editor/XXHashTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/XXHashTests.cs index 30fbb52aff..076f2e2325 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/XXHashTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/XXHashTests.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace Unity.Netcode.EditorTests +namespace Unity.Netcode.GameObjects.EditorTests { internal class XXHashTests { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs new file mode 100644 index 0000000000..cfc14cf437 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs @@ -0,0 +1,134 @@ +#if UNIFIED_NETCODE +using System.Collections; +using NUnit.Framework; +using Unity.Netcode.Components; +using Unity.Netcode.TestHelpers.Runtime; +using Unity.NetCode; +using UnityEngine; +using UnityEngine.TestTools; + + +namespace Unity.Netcode.RuntimeTests +{ + /// + /// Test class that deliberately removes some functionality from NetworkTransform that is conditionally disabled + /// by the presence of ghost objects in the base class. This is to help be certain that the network transform + /// is not doing the work, but that the work is being done by N4E's snapshots. + /// + internal class DoNothingNetworkTransform : NetworkTransform + { + public override void OnNetworkSpawn() + { + // Deliberately left empty + } + + internal override void InternalInitialization(bool isOwnershipChange = false) + { + // Deliberately left empty + } + } + + internal class UnifiedNetworkTransformTest : IntegrationTestWithApproximation + { + protected override int NumberOfClients => 2; + + private GameObject m_Prefab; + private NetworkObject m_Instance; + + protected override IEnumerator OnSetup() + { + // Creates the hybrid prefab + m_Prefab = CreateHybridPrefab("HybridPrefab", true); + m_Prefab.AddComponent(); + NetworkSpawnManager.RegisterPendingGhost = RegisterPendingGhost; + return base.OnSetup(); + } + + protected override IEnumerator OnTearDown() + { + NetworkSpawnManager.RegisterPendingGhost = null; + m_EnableVerboseDebug = false; + return base.OnTearDown(); + } + + private void RegisterPendingGhost(NetworkObject networkObject, ulong networkObjectId) + { + var ghost = networkObject.GetComponent(); + Assert.IsNotNull(ghost, $"[RegisterPendingGhost][NetworkObject-{networkObjectId}] Has no {nameof(GhostAdapter)}!"); + foreach (var networkManager in m_NetworkManagers) + { + // If the world matches, then register the instance with this NetworkManager's spawn manager. + if (networkManager.NetcodeWorld == ghost.World) + { + networkManager.SpawnManager.RegisterGhostPendingSpawn(networkObject, networkObjectId); + return; + } + } + Debug.LogError($"Did not find a world for NetworkObject-{networkObjectId}!!"); + } + + protected override void OnServerAndClientsCreated() + { + + // Add the hybrid prefab to the prefabs list for + // all NetworkManager instances. + // TODO: Emma and I discussed actually not making it + // a requirement to have NetworkManager instances. + // We can get that PR landed and merged back into the + // unified branch so this is no longer needed. + // (We can modify CreateHybridPrefab to use whatever list + // is used to handle this when using the normal prefab creation + // methods). + var networkPrefab = new NetworkPrefab() + { + Prefab = m_Prefab, + }; + foreach (var networkManager in m_NetworkManagers) + { + networkManager.LogLevel = LogLevel.Developer; + networkManager.NetworkConfig.Prefabs.Add(networkPrefab); + // Set the deferred message timeout to be 5 seconds for this test. + // (To see if the messages for the instances ever get processed.) + // Enable this to debug deferred + //networkManager.NetworkConfig.SpawnTimeout = 5; + } + } + + [UnityTest] + public IEnumerator BasicMovementTest() + { + m_EnableVerboseDebug = true; + var authority = GetAuthorityNetworkManager(); + m_Instance = SpawnObject(m_Prefab, m_ServerNetworkManager).GetComponent(); + + // Wait 5 seconds so we will dump any deferred messages if it failed on clients + // when checking to see if it spawned or not on the clients next. + // Enable this to debug deferred + //yield return new WaitForSeconds(5); + + yield return WaitForSpawnedOnAllOrTimeOut(m_Instance); + AssertOnTimeout($"Failed to spawn {m_Instance.name} on all clients!"); + + VerboseDebug("All clients spawned instance!"); + + var originalPos = authority.LocalClient.PlayerObject.transform.position; + var newPos = originalPos + new Vector3(1, 1, 1); + + m_Instance.transform.position = newPos; + + foreach (var client in m_ClientNetworkManagers) + { + Assert.IsTrue(Approximately(originalPos, s_GlobalNetworkObjects[client.LocalClientId][m_Instance.NetworkObjectId].transform.position)); + } + + yield return new WaitForSeconds(1); + + foreach (var client in m_ClientNetworkManagers) + { + Assert.IsTrue(Approximately(newPos, s_GlobalNetworkObjects[client.LocalClientId][m_Instance.NetworkObjectId].transform.position)); + } + VerboseDebug("Test Passed!"); + } + } +} +#endif diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs.meta new file mode 100644 index 0000000000..10d990cfa3 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0f26fa7bd5474b3f9947e0813374b50f +timeCreated: 1775078549 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs index ecaf3ec792..7f4dba5f16 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs @@ -1,6 +1,6 @@ using System; using System.Collections; -using System.Linq; +using System.Collections.Generic; using NUnit.Framework; using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; @@ -41,9 +41,10 @@ public void RegisterCustomLoopInTheMiddle() PlayerLoop.SetPlayerLoop(curPlayerLoop); NetworkUpdateLoop.UnregisterLoopSystems(); - + var subSystemArray = PlayerLoop.GetCurrentPlayerLoop().subSystemList[0].subSystemList; + var lastType = subSystemArray[subSystemArray.Length - 1].type; // our custom `PlayerLoopSystem` with the type of `NetworkUpdateLoopTests` should still exist - Assert.AreEqual(typeof(NetworkUpdateLoopTests), PlayerLoop.GetCurrentPlayerLoop().subSystemList[0].subSystemList.Last().type); + Assert.AreEqual(typeof(NetworkUpdateLoopTests), lastType); } // replace the current PlayerLoop with the cached PlayerLoop after the test PlayerLoop.SetPlayerLoop(cachedPlayerLoop); @@ -94,7 +95,14 @@ public void UpdateStageSystems() for (int i = 0; i < currentPlayerLoop.subSystemList.Length; i++) { var playerLoopSystem = currentPlayerLoop.subSystemList[i]; - var subsystems = playerLoopSystem.subSystemList.ToList(); + // New behaviour (6000.6.x) + // Some PlayerLoopSystems can evidently now have no sub-system lists. + if (playerLoopSystem.subSystemList == null) + { + // Ignore any PlayerLoopSystem with no sub-system lists. + continue; + } + var subsystems = new List(playerLoopSystem.subSystemList); if (playerLoopSystem.type == typeof(Initialization)) { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index fe22bdeb52..d6ff6392c0 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -6,6 +6,9 @@ using System.Runtime.CompilerServices; using System.Text; using NUnit.Framework; +#if UNIFIED_NETCODE +using Unity.NetCode; +#endif using Unity.Netcode.RuntimeTests; using Unity.Netcode.Transports.UTP; using UnityEngine; @@ -1757,6 +1760,20 @@ protected void DestroySceneNetworkObjects() if (CanDestroyNetworkObject(networkObject)) { +#if UNIFIED_NETCODE + // Handle removing the prefab reference and destroying it + // and then destroying the ghostAdapter prior to destroying + // a hybrid prefab. + var ghostAdapter = networkObject.GetComponent(); + if (ghostAdapter != null && ghostAdapter.prefabReference != null) + { + var prefabReference = ghostAdapter.prefabReference; + prefabReference.Prefab = null; + ghostAdapter.prefabReference = null; + Object.Destroy(prefabReference); + Object.Destroy(ghostAdapter); + } +#endif // Destroy the GameObject that holds the NetworkObject component Object.DestroyImmediate(networkObject.gameObject); } @@ -2204,6 +2221,73 @@ protected GameObject CreateNetworkObjectPrefab(string baseName) return prefabObject; } +#if UNIFIED_NETCODE + protected GameObject CreateHybridPrefab(string baseName, bool moveToDDOL = true) + { + // Prevent from trying to register/spawn when creating this hybrid prefab + var gameObject = new GameObject + { + name = baseName + }; + + // Order of operations in how these execute is actually important. + // GhostObject should execute 1st. + // NetworkObjectBridge 2nd. + // NetworkObject 3rd. + // NetworkBehaviours will execute in the order they are arranged unless otherwise specified. + + // When adding a Hybrid/Ghost prefab: + // - We disabled the GameObject prior to adding the GhostPrefabReference (so IsPrefab() == true). + // - Add the GhostAdapter and GhostPrefabReference + // - Then set it back to active. + gameObject.SetActive(false); + var adapter = gameObject.AddComponent(); + // Mark the reference as post processing to avoid registering this instance automatically. + GhostPrefabReference.s_IsPostProcessing = true; + adapter.prefabReference = ScriptableObject.CreateInstance(); + adapter.prefabReference.name = "GhostPrefabReference"; + adapter.prefabReference.Prefab = gameObject; + adapter.prefabReference.Ghost = adapter; + GhostPrefabReference.s_IsPostProcessing = false; + + // TODO: This might be part of the CreateHybridPrefab parameters + // For now, just use normal interpolation until we get integration + // tests running. + // Once we have validated prediction works and have a working manual + // test, we can circle back to this (possibly make that a sub-task + // with the dependency to prediction manual test). + adapter.SupportedGhostModes = GhostModeMask.Interpolated; + + // Once done with setting up the GhostAdapter, we can set it back to active in the hierarchy + gameObject.SetActive(true); + + // GhostBehaviours that are part of a prefab will not invoke Ghost.InternalAcquireEntityReference + // Add the bridge + var bridge = gameObject.AddComponent(); + + // Now add NGO components + var no = gameObject.AddComponent(); + + // NetworkObject Ghost specific settings + no.HasGhost = true; + no.GhostAdapter = adapter; + no.HadBridge = true; + no.NetworkObjectBridge = bridge; + + // Disable transform synchronization for NetworkObject serialization + // since that is handled by the GhostAdapter. + no.SynchronizeTransform = false; + + // Turn it into a test prefab + NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(no); + if (moveToDDOL) + { + Object.DontDestroyOnLoad(gameObject); + } + return gameObject; + } +#endif + /// /// Overloaded method /// @@ -2306,6 +2390,14 @@ protected void SpawnObjectInstance(NetworkObject networkObjectToSpawn, NetworkMa private GameObject SpawnObject(NetworkObject prefabNetworkObject, NetworkManager owner, bool destroyWithScene = false, bool isPlayerObject = false) { Assert.IsTrue(prefabNetworkObject.GlobalObjectIdHash > 0, $"{nameof(GameObject)} {prefabNetworkObject.name} has a {nameof(NetworkObject.GlobalObjectIdHash)} value of 0! Make sure to make it a valid prefab before trying to spawn!"); +#if UNIFIED_NETCODE + // TODO-FixMe: NetCode.Netcode.Instance is a singleton and might cause issues + // assigning this. + if (prefabNetworkObject.HasGhost) + { + NetCode.Netcode.Instance.m_ActiveWorld = owner.NetcodeWorld; + } +#endif var newInstance = Object.Instantiate(prefabNetworkObject.gameObject); var networkObjectToSpawn = newInstance.GetComponent(); SpawnObjectInstance(networkObjectToSpawn, owner, destroyWithScene, isPlayerObject); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef b/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef index cde139b07f..86b6bae95f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef @@ -13,7 +13,9 @@ "Unity.Netcode.TestHelpers.Runtime", "Unity.Mathematics", "UnityEngine.TestRunner", - "UnityEditor.TestRunner" + "UnityEditor.TestRunner", + "Unity.NetCode", + "Unity.Entities" ], "includePlatforms": [], "excludePlatforms": [], @@ -47,6 +49,11 @@ "name": "Unity", "expression": "6000.1.0a1", "define": "HOSTNAME_RESOLUTION_AVAILABLE" + }, + { + "name": "com.unity.netcode", + "expression": "1.10.1", + "define": "UNIFIED_NETCODE" } ], "noEngineReferences": false diff --git a/com.unity.netcode.gameobjects/package.json b/com.unity.netcode.gameobjects/package.json index d4a1bf74c6..a37383e00d 100644 --- a/com.unity.netcode.gameobjects/package.json +++ b/com.unity.netcode.gameobjects/package.json @@ -2,7 +2,7 @@ "name": "com.unity.netcode.gameobjects", "displayName": "Netcode for GameObjects", "description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.", - "version": "2.11.2", + "version": "3.0.0", "unity": "6000.0", "dependencies": { "com.unity.nuget.mono-cecil": "1.11.4", diff --git a/pvpExceptions.json b/pvpExceptions.json index 83e996338b..334f50cde3 100644 --- a/pvpExceptions.json +++ b/pvpExceptions.json @@ -5,9 +5,18 @@ "CHANGELOG.md: line 9: Unreleased section is not allowed for public release" ] }, + "PVP-130-2": { + "errors": [ + "Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef: assembly name does not match filename: Unity.Netcode.GameObjects.Editor.CodeGen", + "Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef: assembly name does not match filename: Unity.Netcode.GameObjects.Editor.PackageChecker", + "Editor/Unity.Netcode.Editor.asmdef: assembly name does not match filename: Unity.Netcode.GameObjects.Editor", + "Tests/Editor/Unity.Netcode.Editor.Tests.asmdef: assembly name does not match filename: Unity.Netcode.GameObjects.Editor.Tests" + ] + }, "PVP-132-2": { "errors": [ - "Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef: name of editor assembly should end with '.Editor'" + "Editor/CodeGen/Unity.Netcode.Editor.CodeGen.asmdef: name of editor assembly should end with '.Editor'", + "Editor/PackageChecker/Unity.Netcode.PackageChecker.Editor.asmdef: name of editor assembly should end with '.Editor'" ] } } diff --git a/testproject/.gitignore b/testproject/.gitignore index 2800399634..cf2c720f71 100644 --- a/testproject/.gitignore +++ b/testproject/.gitignore @@ -77,7 +77,8 @@ crashlytics-build.properties *.pem.meta InitTestScene* - +# Do not include the packages-lock file +packages-lock.json boot.config SceneTemplateSettings.json *BurstAotSettings*.json diff --git a/testproject/Assets/NetCodeConfig.asset b/testproject/Assets/NetCodeConfig.asset new file mode 100644 index 0000000000..b0d1add6cc --- /dev/null +++ b/testproject/Assets/NetCodeConfig.asset @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: abd30ee0214cf6a45b2d76765a4615b1, type: 3} + m_Name: NetCodeConfig + m_EditorClassIdentifier: Unity.NetCode::Unity.NetCode.NetCodeConfig + IsGlobalConfig: 1 + EnableClientServerBootstrap: 0 + HostWorldModeSelection: 1 + ClientServerTickRate: + SimulationTickRate: 60 + PredictedFixedStepSimulationTickRatio: 1 + NetworkTickRate: 0 + MaxSimulationStepsPerFrame: 1 + MaxSimulationStepBatchSize: 4 + TargetFrameRateMode: 0 + m_SendSnapshotsForCatchUpTicks: 0 + SnapshotAckMaskCapacity: 4096 + m_ClampPartialTicksThreshold: 5 + HandshakeApprovalTimeoutMS: 5000 + ClientTickRate: + InterpolationTimeNetTicks: 2 + InterpolationTimeMS: 0 + MaxExtrapolationTimeSimTicks: 20 + ForcedInputLatencyTicks: 0 + MaxPredictAheadTimeMS: 500 + NumAdditionalClientPredictedGhostLifetimeTicks: 0 + DefaultClassificationAllowableTickPeriod: 5 + TargetCommandSlack: 2 + NumAdditionalCommandsToSend: 2 + MaxPredictionStepBatchSizeRepeatedTick: 0 + MaxPredictionStepBatchSizeFirstTimeTick: 0 + PredictionLoopUpdateMode: 0 + InterpolationDelayJitterScale: 1.25 + InterpolationDelayMaxDeltaTicksFraction: 0.1 + InterpolationDelayCorrectionFraction: 0.1 + InterpolationTimeScaleMin: 0.85 + InterpolationTimeScaleMax: 1.1 + CommandAgeCorrectionFraction: 0.1 + PredictionTimeScaleMin: 0.9 + PredictionTimeScaleMax: 1.1 + GhostSendSystemData: + DefaultSnapshotPacketSize: 0 + PercentReservedForDespawnMessages: 0.33 + MinSendImportance: 0 + MinDistanceScaledSendImportance: 0 + MaxIterateChunks: 0 + MaxSendChunks: 0 + MaxSendEntities: 0 + m_ForceSingleBaseline: 0 + m_ForcePreSerialize: 0 + m_KeepSnapshotHistoryOnStructuralChange: 1 + m_EnablePerComponentProfiling: 0 + CleanupConnectionStatePerTick: 1 + m_FirstSendImportanceMultiplier: 1 + m_IrrelevantImportanceDownScale: 1 + m_TempStreamSize: 8192 + m_UseCustomSerializer: 0 + ConnectTimeoutMS: 1000 + MaxConnectAttempts: 60 + DisconnectTimeoutMS: 30000 + HeartbeatTimeoutMS: 500 + ReconnectionTimeoutMS: 2000 + ClientSendQueueCapacity: 64 + ClientReceiveQueueCapacity: 64 + ServerSendQueueCapacity: 512 + ServerReceiveQueueCapacity: 512 + MaxMessageSize: 1400 diff --git a/testproject/Legacy/MultiprocessRuntime/readme.md.meta b/testproject/Assets/NetCodeConfig.asset.meta similarity index 52% rename from testproject/Legacy/MultiprocessRuntime/readme.md.meta rename to testproject/Assets/NetCodeConfig.asset.meta index 5ef9bc8c31..222ccfadf4 100644 --- a/testproject/Legacy/MultiprocessRuntime/readme.md.meta +++ b/testproject/Assets/NetCodeConfig.asset.meta @@ -1,7 +1,8 @@ fileFormatVersion: 2 -guid: 39b2fcb99dff6414e8f41b93f4c92b88 -TextScriptImporter: +guid: c547acbddd81d32a0ba5e62ddfc4f4e3 +NativeFormatImporter: externalObjects: {} + mainObjectFileID: 11400000 userData: assetBundleName: assetBundleVariant: diff --git a/testproject/Assets/Scripts/testproject.asmdef b/testproject/Assets/Scripts/testproject.asmdef index 942bddd1f0..34b7976d61 100644 --- a/testproject/Assets/Scripts/testproject.asmdef +++ b/testproject/Assets/Scripts/testproject.asmdef @@ -3,7 +3,7 @@ "rootNamespace": "", "references": [ "Unity.Netcode.Runtime", - "Unity.Netcode.Editor", + "Unity.Netcode.GameObjects.Editor", "Unity.Netcode.Components", "Unity.Netcode.Adapter.UTP", "Unity.Services.Authentication", diff --git a/testproject/Assets/Tests/Runtime/AnalyticsTests.cs b/testproject/Assets/Tests/Runtime/AnalyticsTests.cs index a841333900..51a35e5016 100644 --- a/testproject/Assets/Tests/Runtime/AnalyticsTests.cs +++ b/testproject/Assets/Tests/Runtime/AnalyticsTests.cs @@ -22,7 +22,7 @@ internal class AnalyticsTests : NetcodeIntegrationTest protected override IEnumerator OnSetup() { NetcodeAnalytics.EnableIntegrationTestAnalytics = true; - m_NetcodeAnalytics = Unity.Netcode.Editor.NetworkManagerHelper.Singleton.NetcodeAnalytics; + m_NetcodeAnalytics = Unity.Netcode.GameObjects.Editor.NetworkManagerHelper.Singleton.NetcodeAnalytics; yield return base.OnSetup(); } diff --git a/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef b/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef index d94d369852..1bd9925469 100644 --- a/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef +++ b/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef @@ -15,7 +15,8 @@ "Unity.Mathematics", "UnityEngine.TestRunner", "UnityEditor.TestRunner", - "Unity.Netcode.Runtime.Tests" + "Unity.Netcode.Runtime.Tests", + "Unity.Netcode.GameObjects.Editor" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs b/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs deleted file mode 100644 index 8e853a0b19..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs +++ /dev/null @@ -1,225 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.SceneManagement; -using UnityEngine.TestTools; -using Object = UnityEngine.Object; -using Unity.Netcode.Transports.UTP; - - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class MultiprocessTestsAttribute : CategoryAttribute - { - public const string MultiprocessCategoryName = "Multiprocess"; - public MultiprocessTestsAttribute() : base(MultiprocessCategoryName) { } - } - - [MultiprocessTests] - public abstract class BaseMultiprocessTests - { - protected string[] platformList { get; set; } - - protected int GetWorkerCount() - { - platformList = MultiprocessOrchestration.GetRemotePlatformList(); - return platformList == null ? WorkerCount : platformList.Length; - } - protected bool m_LaunchRemotely; - private bool m_HasSceneLoaded = false; - // TODO: Remove UTR check once we have Multiprocess tests fully working - protected bool IgnoreMultiprocessTests => MultiprocessOrchestration.ShouldIgnoreUTRTests(); - - protected virtual bool IsPerformanceTest => false; - - /// - /// Implement this to specify the amount of workers to spawn from your main test runner - /// Note: If using remote workers, the woorker count will come from the environment variable - /// - protected abstract int WorkerCount { get; } - - private const string k_FirstPartOfTestRunnerSceneName = "InitTestScene"; - - // Since we want to additively load our BuildMultiprocessTestPlayer.MainSceneName - // We want to keep a reference to the - private Scene m_OriginalActiveScene; - - [OneTimeSetUp] - public virtual void SetupTestSuite() - { - MultiprocessLogger.Log("Running SetupTestSuite - OneTimeSetup"); - MultiprocessOrchestration.IsPerformanceTest = IsPerformanceTest; - if (IgnoreMultiprocessTests) - { - Assert.Ignore("Ignoring tests under UTR. For testing, include the \"-bypassIgnoreUTR\" command line parameter."); - } - - if (IsPerformanceTest) - { - Assert.Ignore("Performance tests should be run from remote test execution on device (this can be ran using the \"run selected tests (your platform)\" button"); - } - MultiprocessLogger.Log($"Currently active scene {SceneManager.GetActiveScene().name}"); - var currentlyActiveScene = SceneManager.GetActiveScene(); - - // Just adding a sanity check here to help with debugging in the event that SetupTestSuite is - // being invoked and the TestRunner scene has not been set to the active scene yet. - // This could mean that TeardownSuite wasn't called or SceneManager is not finished unloading - // or could not unload the BuildMultiprocessTestPlayer.MainSceneName. - if (!currentlyActiveScene.name.StartsWith(k_FirstPartOfTestRunnerSceneName)) - { - MultiprocessLogger.LogError( - $"Expected the currently active scene to begin with ({k_FirstPartOfTestRunnerSceneName}) but" + - $" currently active scene is {currentlyActiveScene.name}"); - } - m_OriginalActiveScene = currentlyActiveScene; - - SceneManager.sceneLoaded += OnSceneLoaded; - SceneManager.LoadScene(BuildMultiprocessTestPlayer.MainSceneName, LoadSceneMode.Additive); - } - - private void OnSceneLoaded(Scene scene, LoadSceneMode mode) - { - SceneManager.sceneLoaded -= OnSceneLoaded; - if (scene.name == BuildMultiprocessTestPlayer.MainSceneName) - { - SceneManager.SetActiveScene(scene); - } - - var transport = NetworkManager.Singleton.NetworkConfig.NetworkTransport; - switch (transport) - { - case UnityTransport unityTransport: - unityTransport.ConnectionData.ServerListenAddress = "0.0.0.0"; - MultiprocessLogger.Log($"Setting unityTransport.ConnectionData.Port {unityTransport.ConnectionData.ServerListenAddress}"); - break; - default: - MultiprocessLogger.LogError($"The transport {transport} has no case"); - break; - } - - MultiprocessLogger.Log("Starting Host"); - NetworkManager.Singleton.StartHost(); - - // Use scene verification to make sure we don't try to get clients to synchronize the TestRunner scene - NetworkManager.Singleton.SceneManager.VerifySceneBeforeLoading = VerifySceneIsValidForClientsToLoad; - - m_HasSceneLoaded = true; - } - - /// - /// We want to exclude the TestRunner scene on the host-server side so it won't try to tell clients to - /// synchronize to this scene when they connect (host-server side only for multiprocess) - /// - /// true - scene is fine to synchronize/inform clients to load and false - scene should not be loaded by clients - private bool VerifySceneIsValidForClientsToLoad(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode) - { - if (sceneName.StartsWith(k_FirstPartOfTestRunnerSceneName)) - { - return false; - } - return true; - } - - [UnitySetUp] - public virtual IEnumerator Setup() - { - yield return new WaitUntil(() => NetworkManager.Singleton != null); - yield return new WaitUntil(() => NetworkManager.Singleton.IsServer); - yield return new WaitUntil(() => NetworkManager.Singleton.IsListening); - yield return new WaitUntil(() => m_HasSceneLoaded == true); - var startTime = Time.time; - m_LaunchRemotely = MultiprocessOrchestration.IsRemoteOperationEnabled(); - - MultiprocessLogger.Log($"Active Worker Count is {MultiprocessOrchestration.ActiveWorkerCount()}" + - $" and connected client count is {NetworkManager.Singleton.ConnectedClients.Count} " + - $" and WorkerCount is {GetWorkerCount()} " + - $" and LaunchRemotely is {m_LaunchRemotely}"); - if (MultiprocessOrchestration.ActiveWorkerCount() + 1 < NetworkManager.Singleton.ConnectedClients.Count) - { - MultiprocessLogger.Log("Is this a bad state?"); - } - - // Moved this out of OnSceneLoaded as OnSceneLoaded is a callback from the SceneManager and just wanted to avoid creating - // processes from within the same callstack/context as the SceneManager. This will instantiate up to the WorkerCount and - // then any subsequent calls to Setup if there are already workers it will skip this step - if (!m_LaunchRemotely) - { - if (NetworkManager.Singleton.ConnectedClients.Count - 1 < WorkerCount) - { - var numProcessesToCreate = WorkerCount - (NetworkManager.Singleton.ConnectedClients.Count - 1); - for (int i = 1; i <= numProcessesToCreate; i++) - { - MultiprocessLogger.Log($"Spawning testplayer {i} since connected client count is {NetworkManager.Singleton.ConnectedClients.Count} is less than {WorkerCount} and Number of spawned external players is {MultiprocessOrchestration.ActiveWorkerCount()} "); - string logPath = MultiprocessOrchestration.StartWorkerNode(); // will automatically start built player as clients - MultiprocessLogger.Log($"logPath to new process is {logPath}"); - MultiprocessLogger.Log($"Active Worker Count {MultiprocessOrchestration.ActiveWorkerCount()} and connected client count is {NetworkManager.Singleton.ConnectedClients.Count}"); - } - } - } - else - { - var launchProcessList = new List(); - if (NetworkManager.Singleton.ConnectedClients.Count - 1 < GetWorkerCount()) - { - var machines = MultiprocessOrchestration.GetRemoteMachineList(); - foreach (var machine in machines) - { - MultiprocessLogger.Log($"Would launch on {machine.Name} too get worker count to {GetWorkerCount()} from {NetworkManager.Singleton.ConnectedClients.Count - 1}"); - launchProcessList.Add(MultiprocessOrchestration.StartWorkersOnRemoteNodes(machine)); - } - } - } - - var timeOutTime = Time.realtimeSinceStartup + TestCoordinator.MaxWaitTimeoutSec; - while (NetworkManager.Singleton.ConnectedClients.Count <= WorkerCount) - { - yield return new WaitForSeconds(0.2f); - - if (Time.realtimeSinceStartup > timeOutTime) - { - throw new Exception($" {DateTime.Now:T} Waiting too long to see clients to connect, got {NetworkManager.Singleton.ConnectedClients.Count - 1} clients, and ActiveWorkerCount: {MultiprocessOrchestration.ActiveWorkerCount()} but was expecting {WorkerCount}, failing"); - } - } - TestCoordinator.Instance.KeepAliveClientRpc(); - MultiprocessLogger.Log($"Active Worker Count {MultiprocessOrchestration.ActiveWorkerCount()} and connected client count is {NetworkManager.Singleton.ConnectedClients.Count}"); - } - - - [TearDown] - public virtual void Teardown() - { - MultiprocessLogger.Log("Running teardown"); - if (!IgnoreMultiprocessTests) - { - TestCoordinator.Instance.TestRunTeardown(); - } - } - - [OneTimeTearDown] - public virtual void TeardownSuite() - { - MultiprocessLogger.Log($"TeardownSuite"); - if (!IgnoreMultiprocessTests) - { - MultiprocessLogger.Log($"TeardownSuite - ShutdownAllProcesses"); - MultiprocessOrchestration.ShutdownAllProcesses(); - MultiprocessLogger.Log($"TeardownSuite - NetworkManager.Singleton.Shutdown"); - NetworkManager.Singleton.Shutdown(); - Object.Destroy(NetworkManager.Singleton.gameObject); // making sure we clear everything before reloading our scene - MultiprocessLogger.Log($"Currently active scene {SceneManager.GetActiveScene().name}"); - MultiprocessLogger.Log($"m_OriginalActiveScene.IsValid {m_OriginalActiveScene.IsValid()}"); - if (m_OriginalActiveScene.IsValid()) - { - SceneManager.SetActiveScene(m_OriginalActiveScene); - } - MultiprocessLogger.Log($"TeardownSuite - Unload {BuildMultiprocessTestPlayer.MainSceneName}"); - SceneManager.UnloadSceneAsync(BuildMultiprocessTestPlayer.MainSceneName); - MultiprocessLogger.Log($"TeardownSuite - Unload {BuildMultiprocessTestPlayer.MainSceneName}"); - } - } - } -} - diff --git a/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs.meta b/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs.meta deleted file mode 100644 index 6b52bd9e90..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/BaseMultiprocessTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f77e60aa394b9419784b6c46618fb553 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs b/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs deleted file mode 100644 index f09dc0f412..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs +++ /dev/null @@ -1,293 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using NUnit.Framework; -using NUnit.Framework.Interfaces; -using Unity.Netcode; -using Unity.Netcode.MultiprocessRuntimeTests; -using UnityEngine; -using UnityEngine.TestTools; -using Debug = UnityEngine.Debug; - -/// -/// Allows for context based delegate execution. -/// Can specify where you want that lambda executed (client side? server side?) and it'll automatically wait for the end -/// of a clientRPC server side and vice versa. -/// todo this could be used as an in-game tool too? for protocols that require a lot of back and forth? -/// -public class ExecuteStepInContext : CustomYieldInstruction -{ - public enum StepExecutionContext - { - Server, - Clients - } - - [AttributeUsage(AttributeTargets.Method)] - public class MultiprocessContextBasedTestAttribute : NUnitAttribute, IOuterUnityTestAction - { - public IEnumerator BeforeTest(ITest test) - { - yield return new WaitUntil(() => TestCoordinator.Instance != null && HasRegistered); - } - - public IEnumerator AfterTest(ITest test) - { - yield break; - } - } - - private StepExecutionContext m_ActionContext; - private Action m_StepToExecute; - private string m_CurrentActionId; - - // as a remote worker, I store all available actions so I can execute them when triggered from RPCs - public static Dictionary AllActions = new Dictionary(); - private static Dictionary s_MethodIdCounter = new Dictionary(); - - private NetworkManager m_NetworkManager; - private bool m_IsRegistering; - private List> m_ClientIsFinishedChecks = new List>(); - private Func m_AdditionalIsFinishedWaiter; - - private bool m_WaitMultipleUpdates; - private bool m_IgnoreTimeoutException; - - private float m_StartTime; - private bool isTimingOut => Time.time - m_StartTime > TestCoordinator.MaxWaitTimeoutSec; - private bool shouldExecuteLocally => (m_ActionContext == StepExecutionContext.Server && m_NetworkManager.IsServer) || (m_ActionContext == StepExecutionContext.Clients && !m_NetworkManager.IsServer); - - public static bool IsRegistering; - public static bool HasRegistered; - private static List s_AllClientTestInstances = new List(); // to keep an instance for each tests, so captured context in each step is kept - - /// - /// This MUST be called at the beginning of each test in order to use context based steps. - /// Assumes this is called from same callsite as ExecuteStepInContext (and assumes this is called from IEnumerator, the method full name is unique - /// even with the same method name and different parameters). - /// This relies on the name to be unique for each generated IEnumerator state machines - /// - public static void InitializeContextSteps() - { - var callerMethod = new StackFrame(1).GetMethod(); - var methodIdentifier = GetMethodIdentifier(callerMethod); // since this is called from IEnumerator, this should be a generated class, making it unique - s_MethodIdCounter[methodIdentifier] = 0; - } - - private static string GetMethodIdentifier(MethodBase callerMethod) - { - return callerMethod.DeclaringType.FullName; - } - - internal static void InitializeAllSteps() - { - MultiprocessLogger.Log("InitializeAllSteps - Start"); - // registering magically all context based steps - IsRegistering = true; - var registeredMethods = typeof(TestCoordinator).Assembly.GetTypes().SelectMany(t => t.GetMethods()) - .Where(m => m.GetCustomAttributes(typeof(MultiprocessContextBasedTestAttribute), true).Length > 0) - .ToArray(); - var typesWithContextMethods = new HashSet(); - foreach (var method in registeredMethods) - { - typesWithContextMethods.Add(method.ReflectedType); - } - - if (registeredMethods.Length == 0) - { - throw new Exception($"Couldn't find any registered methods for multiprocess testing. Is {nameof(TestCoordinator)} in same assembly as test methods?"); - } - - object[] GetParameterValuesToPassFunc(ParameterInfo[] parameterInfo) - { - object[] parametersToReturn = new object[parameterInfo.Length]; - for (int i = 0; i < parameterInfo.Length; i++) - { - var paramType = parameterInfo[i].GetType(); - object defaultObj = null; - if (paramType.IsValueType) - { - defaultObj = Activator.CreateInstance(paramType); - } - - parametersToReturn[i] = defaultObj; - } - - return parametersToReturn; - } - - foreach (var contextType in typesWithContextMethods) - { - var allConstructors = contextType.GetConstructors(); - if (allConstructors.Length > 1) - { - throw new NotImplementedException("Case not implemented where test has more than one constructor"); - } - - var instance = Activator.CreateInstance(contextType, allConstructors.Length > 0 ? GetParameterValuesToPassFunc(allConstructors[0].GetParameters()) : null); - s_AllClientTestInstances.Add(instance); // keeping that instance so tests can use captured local attributes - - var typeMethodsWithContextSteps = new List(); - foreach (var method in contextType.GetMethods()) - { - if (method.GetCustomAttributes(typeof(MultiprocessContextBasedTestAttribute), true).Length > 0) - { - typeMethodsWithContextSteps.Add(method); - } - } - - foreach (var method in typeMethodsWithContextSteps) - { - var parametersToPass = GetParameterValuesToPassFunc(method.GetParameters()); - var enumerator = (IEnumerator)method.Invoke(instance, parametersToPass.ToArray()); - while (enumerator.MoveNext()) { } - } - } - - IsRegistering = false; - HasRegistered = true; - MultiprocessLogger.Log("InitializeAllSteps - Done"); - } - - /// - /// Executes an action with the specified context. This allows writing tests all in the same sequential flow, - /// making it more readable. This allows not having to jump between static client methods and test method - /// - /// context to use. for example, should execute client side? server side? - /// action to execute - /// waits for timeout and just finishes step execution silently - /// parameters to pass to action - /// - /// waits multiple frames before allowing the execution to continue. This means ClientFinishedServerRpc must be called manually - /// - public ExecuteStepInContext(StepExecutionContext actionContext, Action stepToExecute, bool ignoreTimeoutException = false, byte[] paramToPass = default, NetworkManager networkManager = null, bool waitMultipleUpdates = false, Func additionalIsFinishedWaiter = null) - { - m_StartTime = Time.time; - m_IsRegistering = IsRegistering; - m_ActionContext = actionContext; - m_StepToExecute = stepToExecute; - m_WaitMultipleUpdates = waitMultipleUpdates; - m_IgnoreTimeoutException = ignoreTimeoutException; - - if (additionalIsFinishedWaiter != null) - { - m_AdditionalIsFinishedWaiter = additionalIsFinishedWaiter; - } - - if (networkManager == null) - { - networkManager = NetworkManager.Singleton; - } - - m_NetworkManager = networkManager; // todo test using this for multiinstance tests too? - - var callerMethod = new StackFrame(1).GetMethod(); // one skip frame for current method - - var methodId = GetMethodIdentifier(callerMethod); // assumes called from IEnumerator MoveNext, which should be the case since we're a CustomYieldInstruction. This will return a generated class name which should be unique - if (!s_MethodIdCounter.ContainsKey(methodId)) - { - s_MethodIdCounter[methodId] = 0; - } - - string currentActionId = $"{methodId}-{s_MethodIdCounter[methodId]++}"; - m_CurrentActionId = currentActionId; - - if (m_IsRegistering) - { - Assert.That(AllActions, Does.Not.Contain(currentActionId)); // sanity check - AllActions[currentActionId] = this; - MultiprocessLogger.Log($"InitializeAllSteps - Registering {currentActionId}"); - } - else - { - MultiprocessLogger.Log($"InitializeAllSteps - Not Registering {currentActionId}"); - if (shouldExecuteLocally) - { - m_StepToExecute.Invoke(paramToPass); - } - else - { - if (networkManager.IsServer) - { - TestCoordinator.Instance.TriggerActionIdClientRpc(currentActionId, paramToPass, m_IgnoreTimeoutException, - clientRpcParams: new ClientRpcParams - { - Send = new ClientRpcSendParams { TargetClientIds = TestCoordinator.AllClientIdsExceptMine.ToArray() } - }); - foreach (var clientId in TestCoordinator.AllClientIdsExceptMine) - { - m_ClientIsFinishedChecks.Add(TestCoordinator.ConsumeClientIsFinished(clientId)); - } - } - else - { - throw new NotImplementedException(); - } - } - } - } - - public void Invoke(byte[] args) - { - m_StepToExecute.Invoke(args); - if (!m_WaitMultipleUpdates) - { - if (!m_NetworkManager.IsServer) - { - TestCoordinator.Instance.ClientFinishedServerRpc(); - } - else - { - throw new NotImplementedException("todo implement"); - } - } - } - - public override bool keepWaiting - { - get - { - if (isTimingOut) - { - if (m_IgnoreTimeoutException) - { - Debug.LogWarning($"Timeout ignored for action ID {m_CurrentActionId}"); - return false; - } - - throw new Exception($"timeout for Context Step with action ID {m_CurrentActionId}"); - } - - if (m_AdditionalIsFinishedWaiter != null) - { - var isFinished = m_AdditionalIsFinishedWaiter.Invoke(); - if (!isFinished) - { - return true; - } - } - - if (m_IsRegistering || shouldExecuteLocally || m_ClientIsFinishedChecks == null) - { - return false; - } - - for (int i = m_ClientIsFinishedChecks.Count - 1; i >= 0; i--) - { - if (m_ClientIsFinishedChecks[i].Invoke()) - { - m_ClientIsFinishedChecks.RemoveAt(i); - } - else - { - return true; - } - } - - return false; - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs.meta b/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs.meta deleted file mode 100644 index 2b5f013d83..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContext.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1ff1efc1d00c64914905497db918aadc -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs b/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs deleted file mode 100644 index ebacd0ecda..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/ExecuteStepInContextTests.cs +++ /dev/null @@ -1,261 +0,0 @@ -using System; -using System.Collections; -using System.Text.RegularExpressions; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; -using static ExecuteStepInContext; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - /// - /// Smoke tests for ExecuteStepInContext, to make sure it's working properly before being used in other tests - /// - [TestFixture(1)] - [TestFixture(2)] - public class ExecuteStepInContextTests : BaseMultiprocessTests - { - private int m_WorkerCountToTest; - - public ExecuteStepInContextTests(int workerCountToTest) - { - m_WorkerCountToTest = workerCountToTest; - } - - protected override int WorkerCount => m_WorkerCountToTest; - protected override bool IsPerformanceTest => false; - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator TestWithSameName([Values(1)] int a) - { - // ExecuteStepInContext bases itself on method name to identify steps. We need to make sure that methods with - // same names, but different signatures behave correctly - InitializeContextSteps(); - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Assert.That(a, Is.EqualTo(1)); - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - Assert.That(BitConverter.ToInt32(bytes, 0), Is.EqualTo(1)); - }, paramToPass: BitConverter.GetBytes(a)); - } - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator TestWithSameName([Values(2)] int a, [Values(3)] int b) - { - InitializeContextSteps(); - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Assert.That(b, Is.EqualTo(3)); - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - Assert.That(BitConverter.ToInt32(bytes, 0), Is.EqualTo(3)); - }, paramToPass: BitConverter.GetBytes(b)); - } - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator TestWithParameters([Values(1, 2, 3)] int a) - { - InitializeContextSteps(); - - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Assert.Less(a, 4); - Assert.Greater(a, 0); - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - var clientA = BitConverter.ToInt32(bytes, 0); - Assert.True(!NetworkManager.Singleton.IsServer); - Assert.Less(clientA, 4); - Assert.Greater(clientA, 0); - }, paramToPass: BitConverter.GetBytes(a)); - } - - [UnityTest, MultiprocessContextBasedTest] - [TestCase(1, 2, ExpectedResult = null)] - [TestCase(2, 3, ExpectedResult = null)] - [TestCase(3, 4, ExpectedResult = null)] - public IEnumerator TestWithParameters(int a, int b) - { - InitializeContextSteps(); - - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Assert.Less(a, 4); - Assert.Greater(a, 0); - Assert.Less(b, 5); - Assert.Greater(b, 1); - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - var clientB = BitConverter.ToInt32(bytes, 0); - Assert.True(!NetworkManager.Singleton.IsServer); - Assert.Less(clientB, 5); - Assert.Greater(clientB, 1); - }, paramToPass: BitConverter.GetBytes(b)); - } - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator TestExceptionClientSide() - { - InitializeContextSteps(); - - const string exceptionMessageToTest = "This is an exception for TestCoordinator that's expected"; - yield return new ExecuteStepInContext(StepExecutionContext.Clients, _ => - { - throw new Exception(exceptionMessageToTest); - }, ignoreTimeoutException: true); - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - for (int i = 0; i < m_WorkerCountToTest; i++) - { - LogAssert.Expect(LogType.Error, new Regex($".*{exceptionMessageToTest}.*")); - } - }); - - const string exceptionUpdateMessageToTest = "This is an exception for update loop client side that's expected"; - yield return new ExecuteStepInContext(StepExecutionContext.Clients, _ => - { - void UpdateFunc(float _) - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= UpdateFunc; - throw new Exception(exceptionUpdateMessageToTest); - } - - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += UpdateFunc; - }, ignoreTimeoutException: true); - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - for (int i = 0; i < m_WorkerCountToTest; i++) - { - LogAssert.Expect(LogType.Error, new Regex($".*{exceptionUpdateMessageToTest}.*")); - } - }); - } - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator ContextTestWithAdditionalWait() - { - InitializeContextSteps(); - - const int maxValue = 10; - yield return new ExecuteStepInContext(StepExecutionContext.Clients, _ => - { - int count = 0; - - void UpdateFunc(float _) - { - TestCoordinator.Instance.WriteTestResultsServerRpc(count++); - if (count > maxValue) - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= UpdateFunc; - } - } - - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += UpdateFunc; - }, additionalIsFinishedWaiter: () => - { - int nbFinished = 0; - for (int i = 0; i < m_WorkerCountToTest; i++) - { - if (TestCoordinator.PeekLatestResult(TestCoordinator.AllClientIdsExceptMine[i]) == maxValue) - { - nbFinished++; - } - } - - return nbFinished == m_WorkerCountToTest; - }); - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - Assert.That(TestCoordinator.AllClientIdsExceptMine.Count, Is.EqualTo(m_WorkerCountToTest)); - foreach (var clientId in TestCoordinator.AllClientIdsExceptMine) - { - var current = 0; - foreach (var res in TestCoordinator.ConsumeCurrentResult(clientId)) - { - Assert.That(res, Is.EqualTo(current++)); - } - - Assert.That(current - 1, Is.EqualTo(maxValue)); - } - }); - } - - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator TestExecuteInContext() - { - InitializeContextSteps(); - - int stepCountExecuted = 0; - yield return new ExecuteStepInContext(StepExecutionContext.Server, args => - { - stepCountExecuted++; - int count = BitConverter.ToInt32(args, 0); - Assert.That(count, Is.EqualTo(1)); - }, paramToPass: BitConverter.GetBytes(1)); - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, args => - { - int count = BitConverter.ToInt32(args, 0); - Assert.That(count, Is.EqualTo(2)); - TestCoordinator.Instance.WriteTestResultsServerRpc(12345); -#if UNITY_EDITOR - Assert.Fail("Should not be here!! This should only execute on client!!"); -#endif - }, paramToPass: BitConverter.GetBytes(2)); - - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - stepCountExecuted++; - int resultCountFromWorkers = 0; - foreach (var res in TestCoordinator.ConsumeCurrentResult()) - { - resultCountFromWorkers++; - Assert.AreEqual(12345, res.result); - } - - Assert.That(resultCountFromWorkers, Is.EqualTo(WorkerCount)); - }); - - const int timeToWait = 4; - yield return new ExecuteStepInContext(StepExecutionContext.Clients, _ => - { - void UpdateFunc(float _) - { - if (Time.time > timeToWait) - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= UpdateFunc; - TestCoordinator.Instance.WriteTestResultsServerRpc(Time.time); - - TestCoordinator.Instance.ClientFinishedServerRpc(); // since finishOnInvoke is false, we need to do this manually - } - } - - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += UpdateFunc; - }, waitMultipleUpdates: true); // waits multiple frames before allowing the next action to continue. - - yield return new ExecuteStepInContext(StepExecutionContext.Server, args => - { - stepCountExecuted++; - int count = 0; - foreach (var res in TestCoordinator.ConsumeCurrentResult()) - { - count++; - Assert.GreaterOrEqual(res.result, timeToWait); - } - - Assert.Greater(count, 0); - }); - - if (!IsRegistering) - { - Assert.AreEqual(3, stepCountExecuted); - } - } - } -} - diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers.meta b/testproject/Legacy/MultiprocessRuntime/Helpers.meta deleted file mode 100644 index 2b9c70c07b..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: ae0eb1e25098241b182babd91479ea26 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs deleted file mode 100644 index b992dfb53b..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System; -using System.IO; -#if UNITY_EDITOR -using UnityEditor; -using UnityEditor.Build.Reporting; -#endif -using UnityEngine; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - /// - /// This is needed as Unity throws "An abnormal situation has occurred: the PlayerLoop internal function has been called recursively. Please contact Customer Support with a sample project so that we can reproduce the problem and troubleshoot it." - /// when trying to build from Setup() steps in tests. - /// - public static class BuildMultiprocessTestPlayer - { - public const string MultiprocessBaseMenuName = "Netcode/Multiprocess Test"; - public const string BuildAndExecuteMenuName = MultiprocessBaseMenuName + "/Build Test Player #t"; - public const string MainSceneName = "MultiprocessTestScene"; - - private static string BuildPathDirectory => Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", "MultiprocessTests"); - public static string BuildPath => Path.Combine(BuildPathDirectory, "MultiprocessTestPlayer"); - public const string BuildInfoFileName = "BuildInfo.json"; - -#if UNITY_EDITOR - /// - /// Build the standalone player on the current platform - /// This method is both a menu item as well as a public method that can be called from CI - /// in order to build the standalone player - /// - [MenuItem(BuildAndExecuteMenuName)] - public static void BuildRelease() - { - var report = BuildPlayerUtility(); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } - - [MenuItem(MultiprocessBaseMenuName + "/Build Test Player (Debug)")] - public static void BuildDebug() - { - var report = BuildPlayerUtility(BuildTarget.NoTarget, null, true); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } - - [MenuItem(MultiprocessBaseMenuName + "/Delete Test Build")] - public static void DeleteBuild() - { - if (Directory.Exists(BuildPathDirectory)) - { - Directory.Delete(BuildPathDirectory, recursive: true); - } - else - { - Debug.Log($"[{nameof(BuildMultiprocessTestPlayer)}] build directory does not exist ({BuildPathDirectory}) not deleting anything"); - } - } - - private static BuildReport BuildPlayerUtility(BuildTarget buildTarget = BuildTarget.NoTarget, string buildPathExtension = null, bool buildDebug = false) - { - SaveBuildInfo(new BuildInfo() { BuildPath = BuildPath }); - - // deleting so we don't end up testing on outdated builds if there's a build failure - DeleteBuild(); - - if (buildTarget == BuildTarget.NoTarget) - { - if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor) - { - buildPathExtension += ".exe"; - buildTarget = BuildTarget.StandaloneWindows64; - } - else if (Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.OSXEditor) - { - buildPathExtension += ".app"; - buildTarget = BuildTarget.StandaloneOSX; - } - else if (Application.platform == RuntimePlatform.LinuxEditor || Application.platform == RuntimePlatform.LinuxPlayer) - { - buildPathExtension += ""; - buildTarget = BuildTarget.StandaloneLinux64; - } - } - - var buildPathToUse = BuildPath; - buildPathToUse += buildPathExtension; - - var buildPlayerOptions = new BuildPlayerOptions - { - scenes = new[] { "Assets/Scenes/MultiprocessTestScene.unity" }, - locationPathName = buildPathToUse, - target = buildTarget - }; - var buildOptions = BuildOptions.None; - if (buildDebug || buildTarget == BuildTarget.Android) - { - buildOptions |= BuildOptions.Development; - buildOptions |= BuildOptions.AllowDebugging; - } - - buildOptions |= BuildOptions.StrictMode; - buildOptions |= BuildOptions.IncludeTestAssemblies; - buildPlayerOptions.options = buildOptions; - - BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions); - BuildSummary summary = report.summary; - - if (summary.result == BuildResult.Succeeded) - { - Debug.Log($"Build succeeded: {summary.totalSize} bytes at {summary.outputPath}"); - } - - return report; - } - - [MenuItem(MultiprocessBaseMenuName + "/Windows Standalone Player")] - public static void BuildWindowsStandalonePlayer() - { - var report = BuildPlayerUtility(BuildTarget.StandaloneWindows64, ".exe"); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } - - [MenuItem(MultiprocessBaseMenuName + "/Build OSX")] - public static void BuildOSX() - { - var report = BuildPlayerUtility(BuildTarget.StandaloneOSX, ".app"); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } - - [MenuItem(MultiprocessBaseMenuName + "/Build Linux")] - public static void BuildLinux() - { - var report = BuildPlayerUtility(BuildTarget.StandaloneLinux64, ""); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } - - [MenuItem(MultiprocessBaseMenuName + "/Build Android")] - public static void BuildAndroid() - { - var report = BuildPlayerUtility(BuildTarget.Android, ".apk"); - if (report.summary.result != BuildResult.Succeeded) - { - throw new Exception($"Build failed! {report.summary.totalErrors} errors"); - } - } -#endif - - [Serializable] - public struct BuildInfo - { - public string BuildPath; - public bool IsDebug; - } - - public static bool DoesBuildInfoExist() - { - var buildfileInfo = new FileInfo(Path.Combine(Application.streamingAssetsPath, BuildInfoFileName)); - return buildfileInfo.Exists; - } - - public static BuildInfo ReadBuildInfo() - { - var jsonString = File.ReadAllText(Path.Combine(Application.streamingAssetsPath, BuildInfoFileName)); - return JsonUtility.FromJson(jsonString); - } - - public static void SaveBuildInfo(BuildInfo toSave) - { - var buildInfoJson = JsonUtility.ToJson(toSave); - File.WriteAllText(Path.Combine(Application.streamingAssetsPath, BuildInfoFileName), buildInfoJson); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs.meta deleted file mode 100644 index 87acf05da8..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b389565fd8544431db4c24940cb569c6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs deleted file mode 100644 index df0e14f61c..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using UnityEngine; - -/// -/// Component who's purpose is to expose callbacks to code tests -/// -public class CallbackComponent : MonoBehaviour -{ - public Action OnUpdate; - - // Update is called once per frame - private void Update() - { - try - { - OnUpdate?.Invoke(Time.deltaTime); - } - catch (Exception e) - { - TestCoordinator.Instance.WriteErrorServerRpc(e.ToString()); - throw; - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs.meta deleted file mode 100644 index 7347e89e7b..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/CallbackComponent.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 55d1c75ce242745ac98f7e7aca6d2d19 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs deleted file mode 100644 index efca1c8970..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using UnityEngine; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class ConfigurationTools - { - public static async Task GetRemoteConfig() - { - var theList = new JobQueueItemArray(); - using var client = new HttpClient(); - var cancelAfterDelay = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - var response = await client.GetAsync("https://multiprocess-log-event-manager.cds.internal.unity3d.com/api/JobWithFile", - HttpCompletionOption.ResponseHeadersRead, cancelAfterDelay.Token).ConfigureAwait(false); - var content = await response.Content.ReadAsStringAsync(); - JsonUtility.FromJsonOverwrite(content, theList); - return theList; - } - - public static async void CompleteJobQueueItem(JobQueueItem item) - { - await PostJobQueueItem(item, "/complete"); - } - - public static async void ClaimJobQueueItem(JobQueueItem item) - { - await PostJobQueueItem(item, "/claim"); - } - - public static async Task PostJobQueueItem(JobQueueItem item, string path = "") - { - using var client = new HttpClient(); - using var request = new HttpRequestMessage(HttpMethod.Post, "https://multiprocess-log-event-manager.cds.internal.unity3d.com/api/JobWithFile" + path); - var json = JsonUtility.ToJson(item); - using var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - request.Content = stringContent; - MultiprocessLogger.Log($"Posting remoteConfig to server {json}"); - var cancelAfterDelay = new CancellationTokenSource(TimeSpan.FromSeconds(20)); - var response = await client - .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancelAfterDelay.Token).ConfigureAwait(false); - MultiprocessLogger.Log($"remoteConfig posted, checking response {response.StatusCode}"); - } - } - - [Serializable] - public class JobQueueItemArray - { - public List JobQueueItems; - } - - [Serializable] - public class JobQueueItem - { - public int Id; - public long JobId; - public string GitHash; - public string HostIp; - public int PlatformId; - public int JobStateId; - public string CreatedBy; - public string UpdatedBy; - public string TransportName; - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs.meta deleted file mode 100644 index daf965ddca..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationTools.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ad11397b80c04404cb24e82cc7872334 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs deleted file mode 100644 index bb9d097f62..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs +++ /dev/null @@ -1,9 +0,0 @@ - -public enum ConfigurationType -{ - Unknown, - Remote, - CommandLine, - ResourceFile, - Host -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs.meta deleted file mode 100644 index 9e89a3cb23..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/ConfigurationType.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5b189bac115ca4587ba64943bfcc67f6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs deleted file mode 100644 index 41558e04dc..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using UnityEngine; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class CustomPrefabSpawnerForPerformanceTests : INetworkPrefabInstanceHandler, IDisposable where T : NetworkBehaviour - { - private GameObjectPool m_ObjectPool; - private Action m_SetupSpawnedObject; - private Action m_OnRelease; - - public CustomPrefabSpawnerForPerformanceTests(T prefabToSpawn, int maxObjectsToSpawn, Action setupSpawnedObject, Action onRelease) - { - m_ObjectPool = new GameObjectPool(); - m_ObjectPool.Initialize(maxObjectsToSpawn, prefabToSpawn); - m_SetupSpawnedObject = setupSpawnedObject; - m_OnRelease = onRelease; - } - - public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation) - { - var netBehaviour = m_ObjectPool.Get(); - var networkObject = netBehaviour.NetworkObject; - Transform netTransform = networkObject.transform; - netTransform.position = position; - netTransform.rotation = rotation; - m_SetupSpawnedObject(netBehaviour); - return networkObject; - } - - public void Destroy(NetworkObject networkObject) - { - var behaviour = networkObject.gameObject.GetComponent(); // todo expensive, only used in teardown for now, should optimize eventually - m_OnRelease(behaviour); - Transform netTransform = networkObject.transform; - netTransform.position = Vector3.zero; - netTransform.rotation = Quaternion.identity; - m_ObjectPool.Release(behaviour); - } - - public void Dispose() - { - m_ObjectPool.Dispose(); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs.meta deleted file mode 100644 index e36dcb4466..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/CustomPrefabSpawnerForPerformanceTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: dbe82ff88ee654428b03632ec6ffff07 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs deleted file mode 100644 index 41224a47c5..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Collections.Generic; -using Object = UnityEngine.Object; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - /// - /// Have to implement our own pool here for compatibility with Unity 2020LTS - /// This shouldn't be needed if we were supporting only 2021 (and its new Pool) - /// - public class GameObjectPool : IDisposable where T : NetworkBehaviour - { - private List m_AllGameObject; - private Stack m_FreeIndexes; - private Dictionary m_ReverseLookup = new Dictionary(); - - public void Initialize(int originalCount, T prefabToSpawn) - { - m_AllGameObject = new List(originalCount); - m_FreeIndexes = new Stack(originalCount); - for (int i = 0; i < originalCount; i++) - { - var go = Object.Instantiate(prefabToSpawn); - go.gameObject.SetActive(false); - m_AllGameObject.Add(go); - m_FreeIndexes.Push(i); - m_ReverseLookup[go] = i; - } - } - - public void Dispose() - { - foreach (var gameObject in m_AllGameObject) - { - Object.Destroy(gameObject); - } - m_AllGameObject = null; - m_FreeIndexes = null; - m_ReverseLookup = null; - } - - public T Get() - { - if (m_FreeIndexes.Count == 0) - { - throw new Exception("Pool full!"); - } - var o = m_AllGameObject[m_FreeIndexes.Pop()]; - o.gameObject.SetActive(true); - return o; - } - - public void Release(T toRelease) - { - int index = m_ReverseLookup[toRelease]; - m_FreeIndexes.Push(index); - toRelease.gameObject.SetActive(false); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs.meta deleted file mode 100644 index 5a5f42a8e8..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/GameObjectPool.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: dc81fb2a3c7347a3a0b5e98d2ba49ed7 -timeCreated: 1622655847 \ No newline at end of file diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs deleted file mode 100644 index 37ea12c6ee..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using NUnit.Framework; -using UnityEngine; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class MultiprocessLogger - { - private static Logger s_Logger; - - static MultiprocessLogger() => s_Logger = new Logger(logHandler: new MultiprocessLogHandler()); - - public static void Log(string msg) - { - s_Logger.Log(msg); - } - - public static void LogError(string msg) - { - s_Logger.LogError("", msg); - } - - public static void LogWarning(string msg) - { - s_Logger.LogWarning("", msg); - } - } - - public class MultiprocessLogHandler : ILogHandler - { - public static long JobId; - static MultiprocessLogHandler() - { - if (JobId == 0) - { - string sJobId = Environment.GetEnvironmentVariable("YAMATO_JOB_ID"); - if (!long.TryParse(sJobId, out JobId)) - { - JobId = -2; - } - } - } - public void LogException(Exception exception, UnityEngine.Object context) - { - Debug.unityLogger.LogException(exception, context); - } - - public void LogFormat(LogType logType, UnityEngine.Object context, string format, params object[] args) - { - string testName = null; - try - { - testName = TestContext.CurrentContext.Test.Name; - } - catch (Exception) - { - // ignored - } - - if (string.IsNullOrEmpty(testName)) - { - testName = "unknown"; - } - - Debug.LogFormat(logType, LogOption.NoStacktrace, context, $"MPLOG({DateTime.Now:T}) : {testName} : {format}", args); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs.meta deleted file mode 100644 index 2c4934f392..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessLogger.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8c1a196a93520415cbf79751b2bb8eee -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs b/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs deleted file mode 100644 index 6776bc4151..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs +++ /dev/null @@ -1,278 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using Unity.Netcode.MultiprocessRuntimeTests; -using UnityEngine; -using Debug = UnityEngine.Debug; - -public class MultiprocessOrchestration -{ - public static bool IsPerformanceTest; - public const string IsWorkerArg = "-isWorker"; - private static DirectoryInfo s_MultiprocessDirInfo; - public static DirectoryInfo MultiprocessDirInfo - { - private set => s_MultiprocessDirInfo = value; - get => s_MultiprocessDirInfo ?? initMultiprocessDirinfo(); - } - private static List s_Processes = new List(); - private static int s_TotalProcessCounter = 0; - public static string PathToDll { get; private set; } - public static List ProcessList = new List(); - private static FileInfo s_Localip_fileinfo; - - private static DirectoryInfo initMultiprocessDirinfo() - { - string userprofile = ""; - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - userprofile = Environment.GetEnvironmentVariable("USERPROFILE"); - } - else - { - userprofile = Environment.GetEnvironmentVariable("HOME"); - } - s_MultiprocessDirInfo = new DirectoryInfo(Path.Combine(userprofile, ".multiprocess")); - if (!MultiprocessDirInfo.Exists) - { - MultiprocessDirInfo.Create(); - } - s_Localip_fileinfo = new FileInfo(Path.Combine(s_MultiprocessDirInfo.FullName, "localip")); - - return s_MultiprocessDirInfo; - } - - static MultiprocessOrchestration() - { - initMultiprocessDirinfo(); - MultiprocessLogger.Log($" userprofile: {s_MultiprocessDirInfo.FullName} localipfile: {s_Localip_fileinfo}"); - var rootdir_FileInfo = new FileInfo(Path.Combine(MultiprocessDirInfo.FullName, "rootdir")); - MultiprocessLogger.Log($"Checking for the existence of {rootdir_FileInfo.FullName}"); - if (rootdir_FileInfo.Exists) - { - var rootDirText = (File.ReadAllText(rootdir_FileInfo.FullName)).Trim(); - PathToDll = Path.Combine(rootDirText, "multiplayer-multiprocess-test-tools/BokkenForNetcode/ProvisionBokkenMachines/bin/Debug/netcoreapp3.1/osx-x64", "ProvisionBokkenMachines.dll"); - } - else - { - MultiprocessLogger.Log("PathToDll cannot be set as rootDir doesn't exist"); - PathToDll = "unknown"; - } - } - - /// - /// This is to detect if we should ignore Multiprocess tests - /// For testing, include the -bypassIgnoreUTR command line parameter when running UTR. - /// - public static bool ShouldIgnoreUTRTests() - { - return Environment.GetCommandLineArgs().Contains("-automated") && !Environment.GetCommandLineArgs().Contains("-bypassIgnoreUTR"); - } - - public static int ActiveWorkerCount() - { - int activeWorkerCount = 0; - if (s_Processes == null) - { - return activeWorkerCount; - } - - if (s_Processes.Count > 0) - { - foreach (var p in s_Processes) - { - if ((p != null) && (!p.HasExited)) - { - activeWorkerCount++; - } - } - } - return activeWorkerCount; - } - - public static string StartWorkerNode() - { - if (s_Processes == null) - { - s_Processes = new List(); - } - - var workerProcess = new Process(); - s_TotalProcessCounter++; - if (s_Processes.Count > 0) - { - string message = ""; - foreach (var p in s_Processes) - { - message += $" {p.Id} {p.HasExited} {p.StartTime} "; - } - MultiprocessLogger.Log($"Current process count {s_Processes.Count} with data {message}"); - } - - //TODO this should be replaced eventually by proper orchestration for all supported platforms - // Starting new local processes is a solution to help run perf tests locally. CI should have multi machine orchestration to - // run performance tests with more realistic conditions. - string buildInstructions = $"You probably didn't generate your build. Please make sure you build a player using the '{BuildMultiprocessTestPlayer.BuildAndExecuteMenuName}' menu"; - string extraArgs = ""; - try - { - var buildPath = BuildMultiprocessTestPlayer.ReadBuildInfo().BuildPath; - switch (Application.platform) - { - case RuntimePlatform.OSXPlayer: - case RuntimePlatform.OSXEditor: - workerProcess.StartInfo.FileName = $"{buildPath}.app/Contents/MacOS/testproject"; - // extraArgs += "-popupwindow -screen-width 100 -screen-height 100"; - extraArgs += "-batchmode -nographics"; - break; - case RuntimePlatform.WindowsPlayer: - case RuntimePlatform.WindowsEditor: - workerProcess.StartInfo.FileName = $"{buildPath}.exe"; - //extraArgs += "-popupwindow -screen-width 100 -screen-height 100"; - extraArgs += "-popupwindow"; - break; - case RuntimePlatform.LinuxPlayer: - case RuntimePlatform.LinuxEditor: - workerProcess.StartInfo.FileName = $"{buildPath}"; - // extraArgs += "-popupwindow -screen-width 100 -screen-height 100"; - extraArgs += "-batchmode -nographics"; - break; - default: - throw new NotImplementedException($"{nameof(StartWorkerNode)}: Current platform is not supported"); - } - } - catch (FileNotFoundException) - { - Debug.LogError($"Could not find build info file. {buildInstructions}"); - throw; - } - - string logPath = Path.Combine(MultiprocessDirInfo.FullName, $"logfile-mp{s_TotalProcessCounter}.log"); - - workerProcess.StartInfo.UseShellExecute = false; - workerProcess.StartInfo.RedirectStandardError = true; - workerProcess.StartInfo.RedirectStandardOutput = true; - workerProcess.StartInfo.Arguments = $"{IsWorkerArg} {extraArgs} -logFile {logPath}"; - - try - { - MultiprocessLogger.Log($"Attempting to start new process, current process count: {s_Processes.Count} with arguments {workerProcess.StartInfo.Arguments}"); - var newProcessStarted = workerProcess.Start(); - if (!newProcessStarted) - { - throw new Exception("Failed to start worker process!"); - } - s_Processes.Add(workerProcess); - } - catch (Win32Exception e) - { - MultiprocessLogger.LogError($"Error starting player, {buildInstructions}, {e}"); - throw; - } - return logPath; - } - - public static void ShutdownAllProcesses() - { - MultiprocessLogger.Log("Shutting down all processes.."); - foreach (var process in s_Processes) - { - MultiprocessLogger.Log($"Shutting down process {process.Id} with state {process.HasExited}"); - try - { - if (!process.HasExited) - { - // Close process by sending a close message to its main window. - process.CloseMainWindow(); - - // Free resources associated with process. - process.Close(); - } - } - catch (Exception ex) - { - Debug.LogException(ex); - } - } - - s_Processes.Clear(); - } - - public static bool IsRemoteOperationEnabled() - { - string encodedPlatformList = Environment.GetEnvironmentVariable("MP_PLATFORM_LIST"); - if (encodedPlatformList != null && encodedPlatformList.Split(',').Length > 1) - { - return true; - } - return false; - } - - public static string[] GetRemotePlatformList() - { - // "default-win:test-win,default-mac:test-mac" - if (!IsRemoteOperationEnabled()) - { - return null; - } - string encodedPlatformList = Environment.GetEnvironmentVariable("MP_PLATFORM_LIST"); - string[] separated = encodedPlatformList.Split(','); - return separated; - } - - public static List GetRemoteMachineList() - { - var machineJson = new List(); - foreach (var f in MultiprocessDirInfo.GetFiles("*.json")) - { - if (f.Name.Equals("remoteConfig.json")) - { - continue; - } - else - { - machineJson.Add(f); - } - } - return machineJson; - } - - public static Process StartWorkersOnRemoteNodes(FileInfo machine) - { - string command = $" --command launch " + - $"--input-path {machine.FullName} "; - - var workerProcess = new Process(); - - workerProcess.StartInfo.FileName = Path.Combine("dotnet"); - workerProcess.StartInfo.UseShellExecute = false; - workerProcess.StartInfo.RedirectStandardError = true; - workerProcess.StartInfo.RedirectStandardOutput = true; - workerProcess.StartInfo.Arguments = $"{PathToDll} {command} "; - try - { - var newProcessStarted = workerProcess.Start(); - - if (!newProcessStarted) - { - throw new Exception("Failed to start worker process!"); - } - } - catch (Win32Exception e) - { - MultiprocessLogger.LogError($"Error starting bokken process, {e.Message} {e.Data} {e.ErrorCode}"); - throw; - } - - - ProcessList.Add(workerProcess); - - MultiprocessLogger.Log($"Execute Command: {PathToDll} {command} End"); - return workerProcess; - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs.meta b/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs.meta deleted file mode 100644 index 4797a6c5a7..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2b59a46cbb2c54f4d977a05103227453 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs b/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs deleted file mode 100644 index 218c80d61a..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs +++ /dev/null @@ -1,263 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using NUnit.Framework; -using Unity.PerformanceTesting; -using UnityEngine; -using UnityEngine.Profiling; -using UnityEngine.SceneManagement; -using UnityEngine.TestTools; -using static ExecuteStepInContext; -using Object = UnityEngine.Object; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class NetworkVariablePerformanceTests : BaseMultiprocessTests - { - protected override int WorkerCount { get; } = 1; - private const int k_MaxObjectsToSpawn = 10000; - private List m_ServerSpawnedObjects = new List(); - private static GameObjectPool s_ServerObjectPool; - private CustomPrefabSpawnerForPerformanceTests m_ClientPrefabHandler; - private OneNetVar m_PrefabToSpawn; - protected override bool IsPerformanceTest => true; - - private class OneNetVar : NetworkBehaviour - { - public static int InstanceCount; - public NetworkVariable OneInt = new NetworkVariable(); - - public void Initialize() - { - InstanceCount++; - if (IsServer) - { - OneInt.Value = 1; - } - } - - public static void Stop() - { - InstanceCount--; - } - } - - [OneTimeSetUp] - public override void SetupTestSuite() - { - base.SetupTestSuite(); - if (!IgnoreMultiprocessTests) - { - SceneManager.sceneLoaded += OnSceneLoadedInitSetupSuite; - } - } - - private void OnSceneLoadedInitSetupSuite(Scene scene, LoadSceneMode loadSceneMode) - { - SceneManager.sceneLoaded -= OnSceneLoadedInitSetupSuite; - InitializePrefab(); - s_ServerObjectPool = new GameObjectPool(); - s_ServerObjectPool.Initialize(k_MaxObjectsToSpawn, m_PrefabToSpawn); - } - - private void InitializePrefab() - { - if (m_PrefabToSpawn == null) - { - var prefabCopy = Object.Instantiate(PrefabReference.Instance.ReferencedPrefab); - m_PrefabToSpawn = prefabCopy.AddComponent(); - } - } - - [UnityTest, Performance, MultiprocessContextBasedTest] - public IEnumerator TestSpawningManyObjects([Values(1, 2, 1000, 2000, 10000)] int nbObjects) - { - InitializeContextSteps(); - - if (!IsRegistering && TestCoordinator.Instance.NetworkManager.IsServer && BuildMultiprocessTestPlayer.ReadBuildInfo().IsDebug) - { - // build test player in debug mode to enable this - var timeToWait = 20; - Debug.Log($"Debug mode tests enabled, waiting {timeToWait} seconds to give some time to attach debugger"); - yield return new WaitForSeconds(timeToWait); - } - - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - Assert.LessOrEqual(nbObjects, k_MaxObjectsToSpawn); // sanity check - }); - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, stepToExecute: nbObjectsBytes => - { - // setup clients - InitializePrefab(); - var targetCount = BitConverter.ToInt32(nbObjectsBytes, 0); - - m_ClientPrefabHandler = new CustomPrefabSpawnerForPerformanceTests(m_PrefabToSpawn, k_MaxObjectsToSpawn, SetupSpawnedObject, StopSpawnedObject); - var hasAddedHandler = NetworkManager.Singleton.PrefabHandler.AddHandler(m_PrefabToSpawn.NetworkObject, m_ClientPrefabHandler); - Assert.That(hasAddedHandler); - - // add client side reporter for later spawn steps - void UpdateFunc(float deltaTime) - { - var count = OneNetVar.InstanceCount; - if (count > 0) - { - TestCoordinator.Instance.WriteTestResultsServerRpc(count); - - if (count >= targetCount) - { - // we got what we want, don't update results any longer - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= UpdateFunc; - } - } - } - - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += UpdateFunc; - }, paramToPass: BitConverter.GetBytes(nbObjects)); - - yield return new ExecuteStepInContext(StepExecutionContext.Server, _ => - { - // start test - using (Measure.Scope($"Time Taken For Spawning {nbObjects} objects server side and getting report")) - { - // spawn prefabs for test - var totalAllocSampleGroup = new SampleGroup("GC Alloc", SampleUnit.Kilobyte); - var beforeAllocatedMemory = Profiler.GetTotalAllocatedMemoryLong(); - Measure.Custom(totalAllocSampleGroup, beforeAllocatedMemory / 1024f); - for (int i = 0; i < nbObjects; i++) - { - var spawnedObject = s_ServerObjectPool.Get(); - SetupSpawnedObject(spawnedObject); - spawnedObject.NetworkObject.Spawn(destroyWithScene: true); - m_ServerSpawnedObjects.Add(spawnedObject); - } - - var afterAllocatedMemory = Profiler.GetTotalAllocatedMemoryLong(); - Measure.Custom(totalAllocSampleGroup, afterAllocatedMemory / 1024f); - var diffAllocSampleGroup = new SampleGroup("GC Alloc diff for Spawn Server side", SampleUnit.Byte); - Measure.Custom(diffAllocSampleGroup, afterAllocatedMemory - beforeAllocatedMemory); - } - }, additionalIsFinishedWaiter: () => - { - // wait for spawn results coming from clients - int finishedCount = 0; - if (TestCoordinator.AllClientIdsWithResults.Count != WorkerCount) - { - return false; - } - - foreach (var clientIdWithResult in TestCoordinator.AllClientIdsWithResults) - { - var latestResult = TestCoordinator.PeekLatestResult(clientIdWithResult); - if (latestResult == nbObjects) - { - finishedCount++; - } - } - - return finishedCount == WorkerCount; - }); - - var serverLastResult = 0f; - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - // add measurements - // todo add more client-side metrics like memory usage, time taken to execute, etc - var allocated = new SampleGroup("NbSpawnedPerFrame client side", SampleUnit.Undefined); - - foreach (var clientId in TestCoordinator.AllClientIdsWithResults) - { - var lastResult = TestCoordinator.PeekLatestResult(clientId); - Assert.That(lastResult, Is.EqualTo(nbObjects)); - } - - Assert.That(TestCoordinator.AllClientIdsWithResults.Count, Is.EqualTo(WorkerCount)); - foreach (var (clientId, result) in TestCoordinator.ConsumeCurrentResult()) - { - Measure.Custom(allocated, result); - serverLastResult = result; - } - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, nbObjectsBytes => - { - var nbObjectsParam = BitConverter.ToInt32(nbObjectsBytes, 0); -#if UNITY_2023_1_OR_NEWER - Assert.That(Object.FindObjectsByType(FindObjectsSortMode.None).Length, Is.EqualTo(nbObjectsParam + 1), "Wrong number of spawned objects client side"); // +1 for the prefab to spawn -#else - Assert.That(Object.FindObjectsOfType(typeof(OneNetVar)).Length, Is.EqualTo(nbObjectsParam + 1), "Wrong number of spawned objects client side"); // +1 for the prefab to spawn -#endif - - - }, paramToPass: BitConverter.GetBytes(nbObjects)); - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Debug.Log($"finished with test for {nbObjects} expected objects and got {serverLastResult} objects"); - }); - } - - [UnityTearDown, MultiprocessContextBasedTest] - public IEnumerator UnityTeardown() - { - if (!IgnoreMultiprocessTests) - { - InitializeContextSteps(); - - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - foreach (var spawnedObject in m_ServerSpawnedObjects) - { - spawnedObject.NetworkObject.Despawn(false); - s_ServerObjectPool.Release(spawnedObject); - StopSpawnedObject(spawnedObject); - } - - m_ServerSpawnedObjects.Clear(); - }); - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate = null; // todo move access to callbackcomponent to singleton - - void UpdateWaitForAllOneNetVarToDespawnFunc(float deltaTime) - { - if (OneNetVar.InstanceCount == 0) - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= UpdateWaitForAllOneNetVarToDespawnFunc; - TestCoordinator.Instance.ClientFinishedServerRpc(); - } - } - - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += UpdateWaitForAllOneNetVarToDespawnFunc; - }, waitMultipleUpdates: true, ignoreTimeoutException: true); // ignoring timeout since you don't want to hide any issues in the main tests - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, _ => - { - m_ClientPrefabHandler.Dispose(); - NetworkManager.Singleton.PrefabHandler.RemoveHandler(m_PrefabToSpawn.NetworkObject); - }); - } - yield return null; - } - - [OneTimeTearDown] - public override void TeardownSuite() - { - base.TeardownSuite(); - if (!IsPerformanceTest && !IgnoreMultiprocessTests) - { - s_ServerObjectPool.Dispose(); - } - } - - private static void SetupSpawnedObject(OneNetVar spawnedObject) - { - spawnedObject.Initialize(); - } - - private static void StopSpawnedObject(OneNetVar destroyedObject) - { - OneNetVar.Stop(); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs.meta b/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs.meta deleted file mode 100644 index 492f738794..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/NetworkVariablePerformanceTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 91f4160a1de3f40c1bcdb9c18daf9bf2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs b/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs deleted file mode 100644 index 0e62bf0edb..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs +++ /dev/null @@ -1,506 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using Unity.Netcode; -using NUnit.Framework; -using UnityEngine; -using Unity.Netcode.MultiprocessRuntimeTests; -using Unity.Netcode.Transports.UTP; - -/// -/// TestCoordinator -/// Used for coordinating multiprocess end to end tests. Used to call RPCs on other nodes and gather results -/// This is needed to coordinate server and client execution steps. The current remote player test runner hardcodes test -/// to run in a bootstrap scene before launching the player and doesn't call each tests individually. There's not opportunity -/// to coordinate test execution between client and server with that model. -/// The only per tests communication already existing is to get the results per test as they are running -/// With this test coordinator, it's not possible to start a main test node with the test runner and have that server start other worker nodes -/// on which to execute client tests. We use netcode as both a test framework and as the target of our performance tests. -/// -[RequireComponent(typeof(NetworkObject))] -public class TestCoordinator : NetworkBehaviour -{ - public const int PerTestTimeoutSec = 5 * 60; // seconds - - public const float MaxWaitTimeoutSec = 60; - private const char k_MethodFullNameSplitChar = '@'; - - private bool m_ShouldShutdown; - private float m_TimeSinceLastConnected; - private float m_TimeSinceLastKeepAlive; - - public static TestCoordinator Instance; - - private Dictionary> m_TestResultsLocal = new Dictionary>(); // this isn't super efficient, but since it's used for signaling around the tests, shouldn't be too bad - private Dictionary m_ClientIsFinished = new Dictionary(); - - public static List AllClientIdsWithResults => Instance.m_TestResultsLocal.Keys.ToList(); - public static List AllClientIdsExceptMine => NetworkManager.Singleton.ConnectedClients.Keys.ToList().FindAll(client => client != NetworkManager.Singleton.LocalClientId); - - // Multimachine support - private static int s_ProcessId; - public static string Rawgithash; - - private ConfigurationType m_ConfigurationType; - public ConfigurationType ConfigurationType - { - get { return m_ConfigurationType; } - private set - { - if (m_ConfigurationType != value) - { - m_ConfigurationType = value; - } - } - } - private string m_ConnectAddress = "127.0.0.1"; - public static string Port = "7777"; - private bool m_IsClient; - - private void SetConfigurationTypeAndConnect(ConfigurationType type) - { - ConfigurationType = type; - SetAddressAndPort(); - bool startClientResult = NetworkManager.Singleton.StartClient(); - MultiprocessLogger.Log($"Starting client"); - } - - public void Awake() - { - enabled = false; - NetworkManager.OnClientConnectedCallback += OnClientConnectedCallback; - - MultiprocessLogger.Log("Awake - Initialize All Steps"); - ExecuteStepInContext.InitializeAllSteps(); - - s_ProcessId = Process.GetCurrentProcess().Id; - ReadGitHashFile(); - - // Configuration via command line (supported for many but not all platforms) - bool isClient = Environment.GetCommandLineArgs().Any(value => value == MultiprocessOrchestration.IsWorkerArg); - if (isClient) - { - MultiprocessLogger.Log("Setting up via command line - client"); - m_IsClient = isClient; - var cli = new CommandLineProcessor(Environment.GetCommandLineArgs()); - if (Environment.GetCommandLineArgs().Any(value => value == "-ip")) - { - m_ConnectAddress = cli.TransportAddress; - } - if (Environment.GetCommandLineArgs().Any(value => value == "-p")) - { - Port = cli.TransportPort; - } - SetConfigurationTypeAndConnect(ConfigurationType.CommandLine); - } - - if (ConfigurationType == ConfigurationType.Unknown) - { - bool isHost = Environment.GetCommandLineArgs().Any(value => value == "host"); - if (isHost) - { - MultiprocessLogger.Log("Setting up via command line - host"); - var cli = new CommandLineProcessor(Environment.GetCommandLineArgs()); - ConfigurationType = ConfigurationType.CommandLine; - } - } - - - // Configuration via configuration file - all platform support but set at build time - if (ConfigurationType == ConfigurationType.Unknown) - { - //TODO: For next PR - } - - // configuration via WebApi - works on all platforms and is set at run time - if (ConfigurationType == ConfigurationType.Unknown) - { - MultiprocessLogger.Log($"Awake {s_ProcessId} - Calling ConfigureViewWebApi"); - ConfigureViaWebApi(); - MultiprocessLogger.Log($"Awake {s_ProcessId} - Calling ConfigureViewWebApi completed"); - } - - - // if we've tried all the configuration types and none of them are correct then we should log it and just go with the default values - if (ConfigurationType == ConfigurationType.Unknown) - { - MultiprocessLogger.Log("Unable to determine configuration for NetworkManager via commandline, webapi or config file"); - } - - if (Instance != null) - { - MultiprocessLogger.LogError("Multiple test coordinator, destroying this instance"); - Destroy(gameObject); - return; - } - - Instance = this; - } - - private async void ConfigureViaWebApi() - { - MultiprocessLogger.Log($"ConfigureViaWebApi - start"); - var jobQueue = await ConfigurationTools.GetRemoteConfig(); - foreach (var job in jobQueue.JobQueueItems) - { - if (Rawgithash.Equals(job.GitHash)) - { - ConfigurationTools.ClaimJobQueueItem(job); - m_ConnectAddress = job.HostIp; - m_IsClient = true; - MultiprocessLogHandler.JobId = job.JobId; - SetConfigurationTypeAndConnect(ConfigurationType.Remote); - break; - } - else - { - MultiprocessLogger.Log($"No match between {Rawgithash} and {job.GitHash}"); - } - } - MultiprocessLogger.Log($"ConfigureViaWebApi - end {ConfigurationType}"); - } - - private void ReadGitHashFile() - { - Rawgithash = "uninitialized"; - try - { - var githash_resource = Resources.Load("Text/githash"); - if (githash_resource != null) - { - Rawgithash = githash_resource.ToString(); - if (!string.IsNullOrEmpty(Rawgithash)) - { - Rawgithash = Rawgithash.Trim(); - MultiprocessLogger.Log($"Rawgithash is {Rawgithash}"); - } - } - } - catch (Exception e) - { - MultiprocessLogger.Log($"Exception getting githash resource file: {e.Message}"); - } - } - - private void SetAddressAndPort() - { - MultiprocessLogger.Log($"SetAddressAndPort - {Port} {m_ConnectAddress} {m_IsClient} "); - var ushortport = ushort.Parse(Port); - var transport = NetworkManager.Singleton.NetworkConfig.NetworkTransport; - MultiprocessLogger.Log($"transport is {transport}"); - switch (transport) - { - case UnityTransport unityTransport: - MultiprocessLogger.Log($"Setting unityTransport.ConnectionData.Port {ushortport}, isClient: {m_IsClient}, Address {m_ConnectAddress}"); - unityTransport.ConnectionData.Port = ushortport; - unityTransport.ConnectionData.Address = m_ConnectAddress; - break; - default: - MultiprocessLogger.LogError($"The transport {transport} has no case"); - break; - } - } - - public void Start() - { - MultiprocessLogger.Log($"TestCoordinator - Start"); - } - - public void Update() - { - if (Time.time - m_TimeSinceLastKeepAlive > PerTestTimeoutSec) - { - QuitApplication(); - Assert.Fail("Stayed idle too long"); - } - - if ((IsServer && NetworkManager.Singleton.IsListening) || (IsClient && NetworkManager.Singleton.IsConnectedClient)) - { - m_TimeSinceLastConnected = Time.time; - } - else if (Time.time - m_TimeSinceLastConnected > MaxWaitTimeoutSec || m_ShouldShutdown) - { - // Make sure we don't have zombie processes - MultiprocessLogger.Log($"quitting application, shouldShutdown set to {m_ShouldShutdown}, is listening {NetworkManager.Singleton.IsListening}, is connected client {NetworkManager.Singleton.IsConnectedClient}"); - if (!m_ShouldShutdown) - { - QuitApplication(); - Assert.Fail($"something wrong happened, was not connected for {Time.time - m_TimeSinceLastConnected} seconds"); - } - } - } - - private static void QuitApplication() - { -#if UNITY_EDITOR - UnityEditor.EditorApplication.isPlaying = false; -#else - Application.Quit(); -#endif - } - - public void TestRunTeardown() - { - m_TestResultsLocal.Clear(); - } - - public void OnEnable() - { - MultiprocessLogger.Log("OnEnable - Setting OnClientDisconnectCallback"); - NetworkManager.OnClientDisconnectCallback += OnClientDisconnectCallback; - } - - public void OnDisable() - { - if (IsSpawned && NetworkObject != null && NetworkObject.NetworkManager != null) - { - MultiprocessLogger.Log("OnDisable - Removing OnClientDisconnectCallback"); - NetworkManager.OnClientDisconnectCallback -= OnClientDisconnectCallback; - } - - base.OnDestroy(); - } - - // Once we are connected, we can run the update method - public void OnClientConnectedCallback(ulong clientId) - { - if (enabled == false) - { - MultiprocessLogger.Log($"OnClientConnectedCallback enabling behavior clientId: {clientId} {NetworkManager.Singleton.IsHost}/{NetworkManager.Singleton.IsClient} IsRegistering:{ExecuteStepInContext.IsRegistering}"); - enabled = true; - } - } - - private static void OnClientDisconnectCallback(ulong clientId) - { - if (clientId == NetworkManager.ServerClientId || clientId == NetworkManager.Singleton.LocalClientId) - { - // if disconnect callback is for me or for server, quit, we're done here - MultiprocessLogger.Log($"received disconnect from {clientId}, quitting"); - QuitApplication(); - } - } - - private static string GetMethodInfo(Action method) - { - return $"{method.Method.DeclaringType.FullName}{k_MethodFullNameSplitChar}{method.Method.Name}"; - } - - private static string GetMethodInfo(Action method) - { - return $"{method.Method.DeclaringType.FullName}{k_MethodFullNameSplitChar}{method.Method.Name}"; - } - - public static IEnumerable<(ulong clientId, float result)> ConsumeCurrentResult() - { - foreach (var kv in Instance.m_TestResultsLocal) - { - while (kv.Value.Count > 0) - { - var toReturn = (kv.Key, kv.Value[0]); - kv.Value.RemoveAt(0); - yield return toReturn; - } - } - } - - public static IEnumerable ConsumeCurrentResult(ulong clientId) - { - var allResults = Instance.m_TestResultsLocal[clientId]; - while (allResults.Count > 0) - { - var toReturn = allResults[0]; - allResults.RemoveAt(0); - yield return toReturn; - } - } - - public static float PeekLatestResult(ulong clientId) - { - if (Instance.m_TestResultsLocal.ContainsKey(clientId) && Instance.m_TestResultsLocal[clientId].Count > 0) - { - return Instance.m_TestResultsLocal[clientId].Last(); - } - - return float.NaN; - } - - /// - /// Returns appropriate lambda according to parameters - /// Includes time check to make sure this times out - /// - /// - /// - /// - public static Func ResultIsSet(bool useTimeoutException = true) - { - var startWaitTime = Time.time; - return () => - { - if (Time.time - startWaitTime > MaxWaitTimeoutSec) - { - if (useTimeoutException) - { - throw new Exception($"timeout while waiting for results, didn't get results for {Time.time - startWaitTime} seconds"); - } - - return true; - } - - foreach (var clientIdAndTestResultList in Instance.m_TestResultsLocal) - { - if (clientIdAndTestResultList.Value.Count > 0) - { - return true; - } - } - - return false; - }; - } - - public static Func ConsumeClientIsFinished(ulong clientId, bool useTimeoutException = true) - { - var startWaitTime = Time.time; - return () => - { - if (Time.time - startWaitTime > MaxWaitTimeoutSec) - { - if (useTimeoutException) - { - throw new Exception($"timeout while waiting for client finished, didn't get results for {Time.time - startWaitTime} seconds"); - } - else - { - return true; - } - } - - if (Instance.m_ClientIsFinished.ContainsKey(clientId) && Instance.m_ClientIsFinished[clientId]) - { - Instance.m_ClientIsFinished[clientId] = false; // consume - return true; - } - - return false; - }; - } - - [Rpc(SendTo.Server)] - public void ClientFinishedServerRpc(ServerRpcParams p = default) - { - // signal from clients to the server to say the client is done with it's task - m_ClientIsFinished[p.Receive.SenderClientId] = true; - } - - public void InvokeFromMethodActionRpc(Action methodInfo, params byte[] args) - { - var methodInfoString = GetMethodInfo(methodInfo); - InvokeFromMethodNameClientRpc(methodInfoString, args, new ClientRpcParams { Send = new ClientRpcSendParams { TargetClientIds = AllClientIdsExceptMine.ToArray() } }); - } - - public void InvokeFromMethodActionRpc(Action methodInfo) - { - var methodInfoString = GetMethodInfo(methodInfo); - InvokeFromMethodNameClientRpc(methodInfoString, null, new ClientRpcParams { Send = new ClientRpcSendParams { TargetClientIds = AllClientIdsExceptMine.ToArray() } }); - } - - [ClientRpc] - public void TriggerActionIdClientRpc(string actionId, byte[] args, bool ignoreException, ClientRpcParams clientRpcParams = default) - { - MultiprocessLogger.Log($"received RPC from server, client side triggering action ID {actionId}"); - WriteLogServerRpc($"received RPC from server, client side triggering action ID {actionId} {ExecuteStepInContext.AllActions.Count}"); - try - { - ExecuteStepInContext.AllActions[actionId].Invoke(args); - } - catch (Exception e) - { - WriteErrorServerRpc(e.ToString()); - - if (!ignoreException) - { - throw; - } - else - { - Instance.ClientFinishedServerRpc(); - } - } - } - - [ClientRpc] - public void InvokeFromMethodNameClientRpc(string methodInfoString, byte[] args, ClientRpcParams clientRpcParams = default) - { - try - { - var split = methodInfoString.Split(k_MethodFullNameSplitChar); - var (classToExecute, staticMethodToExecute) = (split[0], split[1]); - - var foundType = Type.GetType(classToExecute) ?? throw new Exception($"couldn't find {classToExecute}"); - var foundMethod = foundType.GetMethod(staticMethodToExecute, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) ?? throw new MissingMethodException($"couldn't find method {staticMethodToExecute}"); - foundMethod.Invoke(null, args != null ? new object[] { args } : null); - } - catch (Exception e) - { - WriteErrorServerRpc(e.ToString()); - throw; - } - } - - [ClientRpc] - public void CloseRemoteClientRpc() - { - try - { - NetworkManager.Singleton.Shutdown(); - m_ShouldShutdown = true; // wait until isConnectedClient is false to run Application Quit in next update - MultiprocessLogger.Log("Quitting player cleanly"); - Application.Quit(); - } - catch (Exception e) - { - WriteErrorServerRpc(e.ToString()); - throw; - } - } - - [ClientRpc] - public void KeepAliveClientRpc() - { - m_TimeSinceLastKeepAlive = Time.time; - } - - [Rpc(SendTo.Server)] - public void WriteTestResultsServerRpc(float result, ServerRpcParams receiveParams = default) - { - var senderId = receiveParams.Receive.SenderClientId; - MultiprocessLogger.Log($"Server received result [{result}] from sender [{senderId}]"); - if (!m_TestResultsLocal.ContainsKey(senderId)) - { - m_TestResultsLocal[senderId] = new List(); - } - - m_TestResultsLocal[senderId].Add(result); - } - - /// - /// Use this to communicate client-side errors for server-side logging using the MultiprocessLogger. - /// - /// - /// Use to log server-side without MultiprocessLogger formatting. - /// - [Rpc(SendTo.Server)] - public void WriteErrorServerRpc(string errorMessage, ServerRpcParams receiveParams = default) - { - MultiprocessLogger.LogError($"[Netcode-Server Sender={receiveParams.Receive.SenderClientId}] {errorMessage}"); - } - - [Rpc(SendTo.Server)] - public void WriteLogServerRpc(string logMessage, ServerRpcParams receiveParams = default) - { - MultiprocessLogger.Log($"[Netcode-Server Sender={receiveParams.Receive.SenderClientId}] {logMessage}"); - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs.meta b/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs.meta deleted file mode 100644 index f8c2a3c472..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/TestCoordinator.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ef1240e0784f84eadb77fe822e2e03c7 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs b/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs deleted file mode 100644 index 3152627cbd..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Collections; -using System.Linq; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - [TestFixture(1)] - [TestFixture(2)] - public class TestCoordinatorTests : BaseMultiprocessTests - { - private int m_WorkerCount; - protected override int WorkerCount => m_WorkerCount; - - protected override bool IsPerformanceTest => false; - - public TestCoordinatorTests(int workerCount) - { - m_WorkerCount = workerCount; - } - - private static float s_ValueToValidateAgainst; - private static void ValidateSimpleCoordinatorTestValue(float resultReceived) - { - Assert.AreEqual(s_ValueToValidateAgainst, resultReceived); - } - - private static void ExecuteSimpleCoordinatorTest() - { - s_ValueToValidateAgainst = float.PositiveInfinity; - TestCoordinator.Instance.WriteTestResultsServerRpc(s_ValueToValidateAgainst); - } - - private static void ExecuteWithArgs(byte[] args) - { - s_ValueToValidateAgainst = args[0]; - TestCoordinator.Instance.WriteTestResultsServerRpc(s_ValueToValidateAgainst); - } - - [UnityTest] - public IEnumerator CheckTestCoordinator() - { - // Sanity check for TestCoordinator - // Call the method - TestCoordinator.Instance.InvokeFromMethodActionRpc(ExecuteSimpleCoordinatorTest); - - var nbResults = 0; - for (int i = 0; i < WorkerCount; i++) // wait and test for the two clients - { - yield return new WaitUntil(TestCoordinator.ResultIsSet()); - - var (clientId, result) = TestCoordinator.ConsumeCurrentResult().Take(1).Single(); - Assert.Greater(result, 0f); - nbResults++; - } - Assert.That(nbResults, Is.EqualTo(WorkerCount)); - } - - [UnityTest] - public IEnumerator CheckTestCoordinatorWithArgs() - { - TestCoordinator.Instance.InvokeFromMethodActionRpc(ExecuteWithArgs, 99); - var nbResults = 0; - - for (int i = 0; i < WorkerCount; i++) // wait and test for the two clients - { - yield return new WaitUntil(TestCoordinator.ResultIsSet()); - - var (clientId, result) = TestCoordinator.ConsumeCurrentResult().Take(1).Single(); - Assert.That(result, Is.EqualTo(99)); - nbResults++; - } - Assert.That(nbResults, Is.EqualTo(WorkerCount)); - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs.meta b/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs.meta deleted file mode 100644 index 104be914d0..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/TestCoordinatorTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f5e62651568514685a0b50d623fa8a96 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs b/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs deleted file mode 100644 index 232c8ca05f..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs +++ /dev/null @@ -1,88 +0,0 @@ -using UnityEngine; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class ThreeDText : MonoBehaviour - { - public bool IsTestCoordinatorActiveAndEnabled = false; - public string CommandLineArguments = ""; - private long m_UpdateCounter; - private bool m_HasFired; - private string m_TransportString; - - public void Awake() - { - if (MultiprocessOrchestration.IsPerformanceTest) - { - gameObject.SetActive(false); - } - } - - // Start is called before the first frame update - public void Start() - { - Debug.Log("ThreeDText - Start"); - m_HasFired = false; - m_UpdateCounter = 0; - m_TransportString = "null"; - var jsonTextFile = Resources.Load("Text/multiprocess_tests"); - Debug.Log(jsonTextFile); - - var t = GetComponent(); - t.text = "On Start"; - CommandLineArguments = System.Environment.CommandLine; - - string[] args = System.Environment.GetCommandLineArgs(); - foreach (var arg in args) - { - if (arg.Length > 15) - { - CommandLineArguments += " " + arg.Substring(0, 14); - } - else - { - CommandLineArguments += "\n" + arg; - } - } - } - - // Update is called once per frame - public void Update() - { - m_UpdateCounter++; - var testCoordinator = TestCoordinator.Instance; - if (testCoordinator == null) - { - return; - } - - var transport = NetworkManager.Singleton?.NetworkConfig.NetworkTransport; - var transportString = ""; - if (transport == null) - { - transportString = "null"; - } - else - { - transportString = transport.ToString(); - } - - var t = GetComponent(); - - if (IsTestCoordinatorActiveAndEnabled != testCoordinator.isActiveAndEnabled || - !m_HasFired || - m_UpdateCounter % 25 == 0 || - !m_TransportString.Equals(transportString)) - { - m_HasFired = true; - m_TransportString = transportString; - IsTestCoordinatorActiveAndEnabled = testCoordinator.isActiveAndEnabled; - t.text = $"On Update -\ntestCoordinator.isActiveAndEnabled:{testCoordinator.isActiveAndEnabled} {testCoordinator.ConfigurationType}\n" + - $"Transport: {transportString}\n" + - $"{CommandLineArguments}\n" + - $"IsHost: {NetworkManager.Singleton.IsHost} IsClient: {NetworkManager.Singleton.IsClient} {NetworkManager.Singleton.IsConnectedClient}\n" + - $"{m_UpdateCounter}\n"; - } - } - } -} diff --git a/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs.meta b/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs.meta deleted file mode 100644 index 9d9011054b..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/ThreeDText.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 04cf3cc1396054b009a1ed283aa50021 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg b/testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg deleted file mode 100644 index 8663fcf757..0000000000 Binary files a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Building-Player.jpg and /dev/null differ diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg b/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg deleted file mode 100644 index 72f13b3f05..0000000000 Binary files a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg and /dev/null differ diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg.meta b/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg.meta deleted file mode 100644 index 6d73b54a8f..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/readme-ressources/Multiprocess.jpg.meta +++ /dev/null @@ -1,96 +0,0 @@ -fileFormatVersion: 2 -guid: d2e6ce5793e6a4e74843dca06fd36778 -TextureImporter: - internalIDToNameTable: [] - externalObjects: {} - serializedVersion: 11 - mipmaps: - mipMapMode: 0 - enableMipMap: 1 - sRGBTexture: 1 - linearTexture: 0 - fadeOut: 0 - borderMipMap: 0 - mipMapsPreserveCoverage: 0 - alphaTestReferenceValue: 0.5 - mipMapFadeDistanceStart: 1 - mipMapFadeDistanceEnd: 3 - bumpmap: - convertToNormalMap: 0 - externalNormalMap: 0 - heightScale: 0.25 - normalMapFilter: 0 - isReadable: 0 - streamingMipmaps: 0 - streamingMipmapsPriority: 0 - vTOnly: 0 - grayScaleToAlpha: 0 - generateCubemap: 6 - cubemapConvolution: 0 - seamlessCubemap: 0 - textureFormat: 1 - maxTextureSize: 2048 - textureSettings: - serializedVersion: 2 - filterMode: 1 - aniso: 1 - mipBias: 0 - wrapU: 0 - wrapV: 0 - wrapW: 0 - nPOTScale: 1 - lightmap: 0 - compressionQuality: 50 - spriteMode: 0 - spriteExtrude: 1 - spriteMeshType: 1 - alignment: 0 - spritePivot: {x: 0.5, y: 0.5} - spritePixelsToUnits: 100 - spriteBorder: {x: 0, y: 0, z: 0, w: 0} - spriteGenerateFallbackPhysicsShape: 1 - alphaUsage: 1 - alphaIsTransparency: 0 - spriteTessellationDetail: -1 - textureType: 0 - textureShape: 1 - singleChannelComponent: 0 - flipbookRows: 1 - flipbookColumns: 1 - maxTextureSizeSet: 0 - compressionQualitySet: 0 - textureFormatSet: 0 - ignorePngGamma: 0 - applyGammaDecoding: 0 - platformSettings: - - serializedVersion: 3 - buildTarget: DefaultTexturePlatform - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - spriteSheet: - serializedVersion: 2 - sprites: [] - outline: [] - physicsShape: [] - bones: [] - spriteID: - internalID: 0 - vertices: [] - indices: - edges: [] - weights: [] - secondaryTextures: [] - spritePackingTag: - pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg b/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg deleted file mode 100644 index f346dd85b7..0000000000 Binary files a/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg and /dev/null differ diff --git a/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg.meta b/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg.meta deleted file mode 100644 index 84c6f4f98b..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/readme-ressources/OrchestrationOverview.jpg.meta +++ /dev/null @@ -1,96 +0,0 @@ -fileFormatVersion: 2 -guid: 7b6f909ad23994f9c9cb51710d1e1e07 -TextureImporter: - internalIDToNameTable: [] - externalObjects: {} - serializedVersion: 11 - mipmaps: - mipMapMode: 0 - enableMipMap: 1 - sRGBTexture: 1 - linearTexture: 0 - fadeOut: 0 - borderMipMap: 0 - mipMapsPreserveCoverage: 0 - alphaTestReferenceValue: 0.5 - mipMapFadeDistanceStart: 1 - mipMapFadeDistanceEnd: 3 - bumpmap: - convertToNormalMap: 0 - externalNormalMap: 0 - heightScale: 0.25 - normalMapFilter: 0 - isReadable: 0 - streamingMipmaps: 0 - streamingMipmapsPriority: 0 - vTOnly: 0 - grayScaleToAlpha: 0 - generateCubemap: 6 - cubemapConvolution: 0 - seamlessCubemap: 0 - textureFormat: 1 - maxTextureSize: 2048 - textureSettings: - serializedVersion: 2 - filterMode: 1 - aniso: 1 - mipBias: 0 - wrapU: 0 - wrapV: 0 - wrapW: 0 - nPOTScale: 1 - lightmap: 0 - compressionQuality: 50 - spriteMode: 0 - spriteExtrude: 1 - spriteMeshType: 1 - alignment: 0 - spritePivot: {x: 0.5, y: 0.5} - spritePixelsToUnits: 100 - spriteBorder: {x: 0, y: 0, z: 0, w: 0} - spriteGenerateFallbackPhysicsShape: 1 - alphaUsage: 1 - alphaIsTransparency: 0 - spriteTessellationDetail: -1 - textureType: 0 - textureShape: 1 - singleChannelComponent: 0 - flipbookRows: 1 - flipbookColumns: 1 - maxTextureSizeSet: 0 - compressionQualitySet: 0 - textureFormatSet: 0 - ignorePngGamma: 0 - applyGammaDecoding: 0 - platformSettings: - - serializedVersion: 3 - buildTarget: DefaultTexturePlatform - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - spriteSheet: - serializedVersion: 2 - sprites: [] - outline: [] - physicsShape: [] - bones: [] - spriteID: - internalID: 0 - vertices: [] - indices: - edges: [] - weights: [] - secondaryTextures: [] - spritePackingTag: - pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Legacy/MultiprocessRuntime/readme.md b/testproject/Legacy/MultiprocessRuntime/readme.md deleted file mode 100644 index 6efdcba540..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/readme.md +++ /dev/null @@ -1,279 +0,0 @@ -# Multiprocess testing - -## Why -Multiprocess testing can be used for different use cases like -- integration tests (Netcode + actual transport or multi-scene testing for example) -- performance testing. -- Anything requiring a more realistic environment for testing that involves having a full client and server, communicating on a real network interface using real transports in separate Unity processes. - -The tests you write and test locally will be deployed dynamically to bokken instances. The tests shouldn't have to worry about what hardware it runs on, this should be abstracted away by "workers" and "coordinator". - -## How to write a multiprocess test -There's a few steps to write a multiprocess test - -1. Your test class needs to inherit from `BaseMultiprocessTests` -2. Each test method needs the `MultiprocessContextBasedTest` attribute -3. Each test method needs to run `InitializeContextSteps();` -4. Each context based step can use -```cs -yield return new ExecuteStepInContext(StepExecutionContext.Clients, stepToExecute: nbObjectsBytes => { - // Something here -}); -``` -A test method would look like -```cs - [UnityTest, MultiprocessContextBasedTest] - public IEnumerator MyTest() - { - InitializeContextSteps(); // the only call that should be made outside of context based tests - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Debug.Log("server stuff"); - }); - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - Debug.Log("client stuff"); - Assert.That(1, Is.EqualTo(1)); - throw new Exception("asdf"); // this client side exception will be communicated to the coordinator, making the test fail - }); - } -``` -Your test code shouldn't execute outside of these steps (as that test method can be executed multiple times, once for step registration and once for the actual test run for example) - -Another way to write a multiprocess test without context based steps is to use TestCoordinator directly. -```cs - private static void ExecuteSimpleCoordinatorTest() - { - TestCoordinator.Instance.WriteTestResultsServerRpc(float.PositiveInfinity); - } - - [UnityTest] - public IEnumerator CheckTestCoordinator() - { - // Call the client side method - TestCoordinator.Instance.InvokeFromMethodActionRpc(ExecuteSimpleCoordinatorTest); - - var resultCount = 0; - for (int i = 0; i < WorkerCount; i++) // wait and test for the two clients - { - yield return new WaitUntil(TestCoordinator.ResultIsSet()); - - var (clientId, result) = TestCoordinator.ConsumeCurrentResult().Take(1).Single(); - Assert.Greater(result, 0f); - resultCount++; - } - - Assert.That(resultCount, Is.EqualTo(WorkerCount)); - } -``` - -Here's a complete set of examples using the API - -```cs -using System; -using System.Collections; -using System.Collections.Generic; -using NUnit.Framework; -using Unity.PerformanceTesting; -using UnityEngine; -using UnityEngine.Profiling; -using UnityEngine.TestTools; -using static ExecuteStepInContext; - -namespace Unity.Netcode.MultiprocessRuntimeTests -{ - public class DemoProcessTest : BaseMultiprocessTests - { - protected override int WorkerCount { get; } = 2; // spawns 2 clients connecting to the test runner - protected override bool m_IsPerformanceTest { get; } = false; // specifies whether this should execute from editor or not - - [UnityTest, MultiprocessContextBasedTest] // attribute necessary for context based step execution - public IEnumerator MyTest() - { - InitializeContextSteps(); // necessary to initialize context based steps - - // These steps execute sequentially. - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - Debug.Log("server stuff"); - }); - // for example, the test runner will yield on the same step until clients all report they are done with this step. Once all clients report they are done, the test can continue to the same step. - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - Debug.Log("client stuff"); - Assert.That(1, Is.EqualTo(1)); - throw new Exception("asdf"); // this client side exception will be communicated to the coordinator, making the test fail - }); - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - // To write results to the test runner, call this method: - TestCoordinator.Instance.WriteTestResultsServerRpc(123); - TestCoordinator.Instance.WriteTestResultsServerRpc(123); - TestCoordinator.Instance.WriteTestResultsServerRpc(123); // could be replaced by json string instead for ease of use? - }); - yield return new ExecuteStepInContext(StepExecutionContext.Server, bytes => - { - // consumes first result sent above from any client - TestCoordinator.ConsumeCurrentResult(); - // consumes all results from all clients - foreach (var (clientID, result) in TestCoordinator.ConsumeCurrentResult()) - { - Assert.That(result, Is.EqualTo(123)); - } - // consumes results for individual clients - foreach (var clientID in TestCoordinator.AllClientIdsExceptMine) - { - TestCoordinator.ConsumeCurrentResult(clientID); - } - }); - - int someValue = 456; // one caveat to executeStepInContext is contrary to instinct, this is not shared between server and client execution. - // to send that value to clients, "paramToPass" needs to be used - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - var valueComingFromServer = BitConverter.ToInt32(bytes, 0); - }, paramToPass: BitConverter.GetBytes(456)); // could be replaced by JSON string instead for ease of use? - // useful for taking in [Values] method parameters as these are only known by the server - - // when you have client steps that take more than one frame, you can subscribe to the OnUpdate callback on CallbackComponent - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - void Update(float _) - { - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate -= Update; - TestCoordinator.Instance.ClientFinishedServerRpc(); // since finishOnInvoke is false, we need to do this manually - } - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += Update; - }, waitMultipleUpdates: true); // this keeps waiting "are you done? are you done? are you done?" and relies on the clients calling the "ClientFinishedServerRpc" - - yield return new ExecuteStepInContext(StepExecutionContext.Clients, bytes => - { - int cpt = 0; - void Update(float _) - { - TestCoordinator.Instance.WriteTestResultsServerRpc(Time.time); - } - NetworkManager.Singleton.gameObject.GetComponent().OnUpdate += Update; - }, additionalIsFinishedWaiter: () => // this keeps waiting "are you done? are you done? are you done?" until this lambda returns true - { - foreach (var (clientId, latest) in TestCoordinator.ConsumeCurrentResult()) - { - return latest >= 10; - } - return false; - }); - } - - [UnityTest, Performance] // already existing performance framework https://docs.unity3d.com/Packages/com.unity.test-framework.performance@2.8/manual/index.html - public IEnumerator PerfTest() - { - var totalAllocSampleGroup = new SampleGroup("GC Alloc", SampleUnit.Kilobyte); - var allocStat = Profiler.GetTotalAllocatedMemoryLong(); - Measure.Custom(totalAllocSampleGroup, allocStat / 1024); // this will record in Unity's shared Performance DB. - // Dashboards will be able to display these stats overtime - yield return null; - } - } -} - - -``` - - -## How to run a test -**Local**: Test players need to be built first to test locally. - -**Automated**: Integration with CI should do this automatically. - -![](readme-ressources/Building-Player.jpg) - -Then run the tests from Unity's test runner. - -Note that performance tests should be run from external processes (not from editor). This way the server code will run in a build, just as much as client code, for more realistic test results. - -![](readme-ressources/Multiprocess.jpg) - -## How it's done -### Multiple processes orchestration - -Test code and host code execute in the same process: When writing play mode tests, Unity will start a unity environment for that test, including game loop, scene, object hierarchy, all that fun stuff. This means the test itself has access to everything other unity scripts would have access to. At test startup, the test will ask unity to switch scene to MultiprocessTestScene containing a GameObject already placed in that scene called TestCoordinator . The test will callStartHost that will listen for connections, still all in the same process. -Once that's done, that same test will then spawn new client processes. These clients will also start with that MultiprocessTestScene loaded at startup, also containing a TestCoordinator . These client side TestCoordinators will detect they are clients (using command line arg) and instead of calling StartHost will call StartClient . This is where new code to specify the IP to connect to would stand (the lines I sent you). -A good way I could have clarified that code is to separate TestCoordinator into TestCoordinatorClient and TestCoordinatorServer thinking of it. Right now TestCoordinator code does both. -Once the connection is established (tests yield wait for connection in Setup code), then the tests can start sending RPCs to each other. -The test (that's server side) will call multiple TriggerActionIdClientRpc . This will trigger these actions on all clients. The clients execute their test code, then answer back with ClientFinishedServerRpc. -Once all the tests are done exchanging commands, a final RPC CloseRemoteClientRpc is called to tell the clients they are done. -If that RPC fails to send for some reason, clients also have a keep alive that tells them to self destroy when it expires. -If you look at the “how it's done” section in the multiprocess readme.md testproject/Assets/Tests/Runtime/MultiprocessRuntime/readme.md there's a few drawings to explain that flow. - -So: -Editor -- Run tests -- Run host code -- Launches builds -Separate build -- Runs client RPCs that execute test code, not the tests themselves - - -Now just to bake your noodles a bit more, Unity will take an additional step before launching these steps. The above is true if you launch these tests from the editor. If you launch these tests in a separate player, Unity in the background will create a separate process for that player. That player will then connect using a plain tcp connection to the editor to report back on its test results. This is important to know if you want to have the test/host part launch on different platforms like mobile. - -So the above would become -Editor -- Launches test player on platform -Test player -- Run tests -- Run host code -- Launches builds -Separate build -- Runs client RPCs … - -With the bokken integration, we'll need to be careful about ressource contention at Unity, these tests could be heavy on ressources. -Tests when launched locally will simply create new OS processes for each worker players. - - - -![](readme-ressources/OrchestrationOverview.jpg) -*Note that this diagram is still WIP for the CI part* -### Bokken orchestration -Bokken Orchestration can be performed with the support of the following tool: -[Multiplayer Multiprocess Test Tools](https://github.cds.internal.unity3d.com/unity/multiplayer-multiprocess-test-tools) -[Documentation](https://backstage.corp.unity3d.com/catalog/default/component/multiplayer-multiprocess-test-tools/docs/) - -### CI -todo -#### Performance report dashboards -todo -### Client-server test coordination -A Test Coordinator is in charge of managing communication between the nodes, executing remote test code. The test coordinator is also in charge of process cleanup, if for example the server crashes, so we don't have zombie clients laying around. -The test coordinator in client mode will automatically try to connect to a server on Start(). -### Context based step execution -Test methods are executed twice. Once in "registration" mode, to have all the steps register themselves using a unique ID. This ID is deterministic between client and server processes, so that when a server calls a step during actual test execution, the clients have the same ID associated with the same lambda. -During test execution, the main node's step will call an RPC on clients to trigger their pre-registered lambda. The main node's step will then yield until it receives a "client done" RPC from all clients. The main node's test will then be able to continue execution to the next step. - -## The MultiprocessTestPlayer -The MultiprocessTestPlayer, which is built by the sub-menu items under "Netcode" -> "Multiprocess Test", supports many workflows and configurations. - -### Command Line -Currently the MultiprocessTestPlayer can be configured via the command line on all platforms that support parsing of command line arguments in the C# layer. -For example, this means that command line configuration is not available on Android. - -#### Setting the transport address -Default Values on Client: 127.0.0.1, 3076 - -In order to set the transport address for either the server/host or the client, the options of "-ip" and "-p" can be used. For example: - - -ip 127.0.0.1 -p 3076 - -These options can be passed when starting the client, for example, in order to let it know where the host is to connect to. - -#### Setting the transport -Default Values: UNET - -The default transport is UNET but this can be switched to UTP by using - - -transport utp - - -# Future considerations -- Integrate with local MultiInstance tests? -- Have ExecuteStepInContext a game facing feature for sequencing client-server actions? diff --git a/testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef b/testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef deleted file mode 100644 index 13baf50a69..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "TestProject.MultiprocessTests", - "rootNamespace": "", - "references": [ - "Unity.Netcode.Runtime", - "TestProject", - "Unity.Netcode.Editor", - "Unity.Netcode.Components", - "ScriptsForAutomatedTesting", - "Unity.PerformanceTesting", - "UnityEngine.TestRunner", - "UnityEditor.TestRunner" - ], - "includePlatforms": [ - "Editor", - "macOSStandalone", - "LinuxStandalone64", - "WindowsStandalone32", - "WindowsStandalone64" - ], - "excludePlatforms": [], - "allowUnsafeCode": true, - "overrideReferences": false, - "precompiledReferences": [ - "nunit.framework.dll" - ], - "autoReferenced": true, - "defineConstraints": [ - "UNITY_INCLUDE_TESTS" - ], - "noEngineReferences": false, - "versionDefines": [ - ] -} diff --git a/testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef.meta b/testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef.meta deleted file mode 100644 index a06b37d08b..0000000000 --- a/testproject/Legacy/MultiprocessRuntime/testproject.multiprocesstests.asmdef.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 66273ab9e01074f7da305fe84e13da47 -AssemblyDefinitionImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/Packages/manifest.json b/testproject/Packages/manifest.json index f7832aaad0..21fe1a8f2e 100644 --- a/testproject/Packages/manifest.json +++ b/testproject/Packages/manifest.json @@ -1,23 +1,23 @@ { "disableProjectUpdate": false, "dependencies": { - "com.unity.addressables": "2.7.4", - "com.unity.ai.navigation": "2.0.9", - "com.unity.collab-proxy": "2.10.1", - "com.unity.ide.rider": "3.0.38", - "com.unity.ide.visualstudio": "2.0.25", + "com.unity.addressables": "2.9.1", + "com.unity.ai.navigation": "2.0.12", + "com.unity.collab-proxy": "2.12.4", + "com.unity.ide.rider": "3.0.39", + "com.unity.ide.visualstudio": "2.0.26", "com.unity.mathematics": "1.3.3", - "com.unity.multiplayer.tools": "2.2.6", + "com.unity.multiplayer.tools": "2.2.8", "com.unity.netcode.gameobjects": "file:../../com.unity.netcode.gameobjects", "com.unity.package-validation-suite": "0.49.0-preview", - "com.unity.services.authentication": "3.5.2", - "com.unity.services.multiplayer": "1.2.0", + "com.unity.services.authentication": "3.6.1", + "com.unity.services.multiplayer": "2.1.3", "com.unity.test-framework": "1.6.0", - "com.unity.test-framework.performance": "3.2.0", - "com.unity.timeline": "1.8.9", - "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.11", + "com.unity.test-framework.performance": "3.4.0", + "com.unity.timeline": "1.8.12", "com.unity.ugui": "2.0.0", "com.unity.modules.accessibility": "1.0.0", + "com.unity.modules.adaptiveperformance": "1.0.0", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", @@ -44,6 +44,7 @@ "com.unity.modules.unitywebrequestaudio": "1.0.0", "com.unity.modules.unitywebrequesttexture": "1.0.0", "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vectorgraphics": "1.0.0", "com.unity.modules.vehicles": "1.0.0", "com.unity.modules.video": "1.0.0", "com.unity.modules.vr": "1.0.0", diff --git a/testproject/Packages/packages-lock.json b/testproject/Packages/packages-lock.json deleted file mode 100644 index 675de94bf2..0000000000 --- a/testproject/Packages/packages-lock.json +++ /dev/null @@ -1,570 +0,0 @@ -{ - "dependencies": { - "com.unity.addressables": { - "version": "2.7.4", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.profiling.core": "1.0.2", - "com.unity.test-framework": "1.4.5", - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.scriptablebuildpipeline": "2.4.3", - "com.unity.modules.unitywebrequestassetbundle": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ai.navigation": { - "version": "2.0.9", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.ai": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.burst": { - "version": "1.8.25", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.mathematics": "1.2.1", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.collab-proxy": { - "version": "2.10.1", - "depth": 0, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.collections": { - "version": "2.6.2", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.23", - "com.unity.mathematics": "1.3.2", - "com.unity.test-framework": "1.4.6", - "com.unity.nuget.mono-cecil": "1.11.5", - "com.unity.test-framework.performance": "3.0.3" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ext.nunit": { - "version": "2.0.5", - "depth": 1, - "source": "builtin", - "dependencies": {} - }, - "com.unity.ide.rider": { - "version": "3.0.38", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ext.nunit": "1.0.6" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ide.visualstudio": { - "version": "2.0.25", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.1.31" - }, - "url": "https://packages.unity.com" - }, - "com.unity.mathematics": { - "version": "1.3.3", - "depth": 0, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.multiplayer.tools": { - "version": "2.2.6", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.18", - "com.unity.collections": "2.5.1", - "com.unity.mathematics": "1.3.2", - "com.unity.profiling.core": "1.0.2", - "com.unity.nuget.mono-cecil": "1.11.4", - "com.unity.modules.uielements": "1.0.0", - "com.unity.nuget.newtonsoft-json": "3.2.1" - }, - "url": "https://packages.unity.com" - }, - "com.unity.netcode.gameobjects": { - "version": "file:../../com.unity.netcode.gameobjects", - "depth": 0, - "source": "local", - "dependencies": { - "com.unity.nuget.mono-cecil": "1.11.4", - "com.unity.transport": "2.6.0" - } - }, - "com.unity.nuget.mono-cecil": { - "version": "1.11.5", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.nuget.newtonsoft-json": { - "version": "3.2.1", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.package-validation-suite": { - "version": "0.49.0-preview", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.nuget.mono-cecil": "0.1.6-preview.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.profiling.core": { - "version": "1.0.2", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.scriptablebuildpipeline": { - "version": "2.4.3", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.4.5", - "com.unity.modules.assetbundle": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.authentication": { - "version": "3.5.2", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.ugui": "1.0.0", - "com.unity.services.core": "1.15.1", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.core": { - "version": "1.15.1", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.modules.androidjni": "1.0.0", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.deployment": { - "version": "1.6.2", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.services.core": "1.15.1", - "com.unity.services.deployment.api": "1.1.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.deployment.api": { - "version": "1.1.2", - "depth": 2, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.services.multiplayer": { - "version": "1.2.0", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.transport": "2.5.0", - "com.unity.collections": "2.2.1", - "com.unity.services.qos": "1.3.0", - "com.unity.services.core": "1.15.1", - "com.unity.services.wire": "1.4.0", - "com.unity.services.deployment": "1.6.2", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.services.authentication": "3.5.1" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.qos": { - "version": "1.3.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.collections": "1.2.4", - "com.unity.services.core": "1.12.4", - "com.unity.nuget.newtonsoft-json": "3.0.2", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.services.authentication": "2.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.services.wire": { - "version": "1.4.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.services.core": "1.12.5", - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.services.authentication": "2.7.4" - }, - "url": "https://packages.unity.com" - }, - "com.unity.sysroot": { - "version": "2.0.10", - "depth": 1, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, - "com.unity.sysroot.linux-x86_64": { - "version": "2.0.9", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10" - }, - "url": "https://packages.unity.com" - }, - "com.unity.test-framework": { - "version": "1.6.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.ext.nunit": "2.0.3", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.test-framework.performance": { - "version": "3.2.0", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.test-framework": "1.1.33", - "com.unity.modules.jsonserialize": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.timeline": { - "version": "1.8.9", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.director": "1.0.0", - "com.unity.modules.animation": "1.0.0", - "com.unity.modules.particlesystem": "1.0.0" - }, - "url": "https://packages.unity.com" - }, - "com.unity.toolchain.win-x86_64-linux-x86_64": { - "version": "2.0.11", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.sysroot": "2.0.10", - "com.unity.sysroot.linux-x86_64": "2.0.9" - }, - "url": "https://packages.unity.com" - }, - "com.unity.transport": { - "version": "2.6.0", - "depth": 1, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.8.24", - "com.unity.collections": "2.2.1", - "com.unity.mathematics": "1.3.2" - }, - "url": "https://packages.unity.com" - }, - "com.unity.ugui": { - "version": "2.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0" - } - }, - "com.unity.modules.accessibility": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.ai": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.androidjni": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.animation": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.assetbundle": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.audio": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.cloth": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.director": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.animation": "1.0.0" - } - }, - "com.unity.modules.hierarchycore": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.imageconversion": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.imgui": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.jsonserialize": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.particlesystem": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.physics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.physics2d": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.screencapture": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.subsystems": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", - "dependencies": { - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.modules.terrain": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.terrainphysics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.terrain": "1.0.0" - } - }, - "com.unity.modules.tilemap": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics2d": "1.0.0" - } - }, - "com.unity.modules.ui": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.uielements": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.hierarchycore": "1.0.0", - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.umbra": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.unityanalytics": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0" - } - }, - "com.unity.modules.unitywebrequest": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.unitywebrequestassetbundle": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - } - }, - "com.unity.modules.unitywebrequestaudio": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.audio": "1.0.0" - } - }, - "com.unity.modules.unitywebrequesttexture": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.unitywebrequestwww": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.unitywebrequestassetbundle": "1.0.0", - "com.unity.modules.unitywebrequestaudio": "1.0.0", - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0" - } - }, - "com.unity.modules.vehicles": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0" - } - }, - "com.unity.modules.video": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0" - } - }, - "com.unity.modules.vr": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.xr": "1.0.0" - } - }, - "com.unity.modules.wind": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": {} - }, - "com.unity.modules.xr": { - "version": "1.0.0", - "depth": 0, - "source": "builtin", - "dependencies": { - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.subsystems": "1.0.0" - } - } - } -} diff --git a/testproject/ProjectSettings/PackageManagerSettings.asset b/testproject/ProjectSettings/PackageManagerSettings.asset index b01b2f8da9..6e57db2799 100644 --- a/testproject/ProjectSettings/PackageManagerSettings.asset +++ b/testproject/ProjectSettings/PackageManagerSettings.asset @@ -12,32 +12,33 @@ MonoBehaviour: m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: - m_EnablePreviewPackages: 1 - m_EnablePackageDependencies: 1 + m_EnablePreReleasePackages: 0 m_AdvancedSettingsExpanded: 1 m_ScopedRegistriesSettingsExpanded: 1 + m_SeeAllPackageVersions: 0 + m_DismissPreviewPackagesInUse: 0 oneTimeWarningShown: 1 - m_Registries: - - m_Id: main + oneTimePackageErrorsPopUpShown: 0 + m_MainRegistry: + m_Id: main m_Name: m_Url: https://packages.unity.com m_Scopes: [] m_IsDefault: 1 + m_IsUnityRegistry: 1 m_Capabilities: 7 + m_ConfigSource: 0 + m_Compliance: + m_Status: 0 + m_Violations: [] + m_ScopedRegistries: [] m_UserSelectedRegistryName: m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: - m_ErrorMessage: - m_Original: - m_Id: - m_Name: - m_Url: - m_Scopes: [] - m_IsDefault: 0 - m_Capabilities: 0 m_Modified: 0 - m_Name: - m_Url: - m_Scopes: - - - m_SelectedScopeIndex: 0 + m_ErrorMessage: + m_UserModificationsEntityId: + m_rawData: 568105584918791443 + m_OriginalEntityId: + m_rawData: 568105584918791444 + m_LoadAssets: 0 diff --git a/testproject/ProjectSettings/ProjectSettings.asset b/testproject/ProjectSettings/ProjectSettings.asset index 3bb16ba357..06561ea008 100644 --- a/testproject/ProjectSettings/ProjectSettings.asset +++ b/testproject/ProjectSettings/ProjectSettings.asset @@ -3,7 +3,7 @@ --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 - serializedVersion: 28 + serializedVersion: 30 productGUID: bba99b16607b94720b7d04f7f1a82989 AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 @@ -41,7 +41,6 @@ PlayerSettings: height: 1 m_SplashScreenLogos: [] m_VirtualRealitySplashScreen: {fileID: 0} - m_HolographicTrackingLossScreen: {fileID: 0} defaultScreenWidth: 1280 defaultScreenHeight: 720 defaultScreenWidthWeb: 960 @@ -66,10 +65,15 @@ PlayerSettings: useOSAutorotation: 1 use32BitDisplayBuffer: 1 preserveFramebufferAlpha: 0 + adjustIOSFPSUsingThermalState: 1 + thermalStateSeriousIOSFPS: 30 + thermalStateCriticalIOSFPS: 15 disableDepthAndStencilBuffers: 0 androidStartInFullscreen: 1 androidRenderOutsideSafeArea: 1 androidUseSwappy: 1 + androidRequestedVisibleInsets: 0 + androidSystemBarsBehavior: 2 androidDisplayOptions: 1 androidBlitType: 0 androidResizeableActivity: 0 @@ -84,6 +88,7 @@ PlayerSettings: defaultIsNativeResolution: 1 macRetinaSupport: 1 runInBackground: 1 + callOnDisableOnAssetBundleUnload: 1 muteOtherAudioSources: 0 Prepare IOS For Recording: 0 Force IOS Speakers When Recording: 0 @@ -114,6 +119,7 @@ PlayerSettings: xboxEnableGuest: 0 xboxEnablePIXSampling: 0 metalFramebufferOnly: 0 + metalUseMetalDisplayLink: 0 xboxOneResolution: 0 xboxOneSResolution: 0 xboxOneXResolution: 3 @@ -147,12 +153,10 @@ PlayerSettings: preloadedAssets: [] metroInputSource: 0 wsaTransparentSwapchain: 0 - m_HolographicPauseOnTrackingLoss: 1 xboxOneDisableKinectGpuReservation: 1 xboxOneEnable7thCore: 1 vrSettings: enable360StereoCapture: 0 - isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 enableOpenGLProfilerGPURecorders: 1 allowHDRDisplaySupport: 0 @@ -174,9 +178,10 @@ PlayerSettings: tvOS: 0 overrideDefaultApplicationIdentifier: 0 AndroidBundleVersionCode: 1 - AndroidMinSdkVersion: 23 + AndroidMinSdkVersion: 26 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 + AndroidPreferredDataLocation: 1 aotOptions: nimt-trampolines=1024 stripEngineCode: 1 iPhoneStrippingLevel: 0 @@ -191,13 +196,14 @@ PlayerSettings: VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 iOSSimulatorArchitecture: 0 - iOSTargetOSVersionString: 13.0 + iOSTargetOSVersionString: 15.0 tvOSSdkVersion: 0 tvOSSimulatorArchitecture: 0 tvOSRequireExtendedGameController: 0 - tvOSTargetOSVersionString: 13.0 + tvOSTargetOSVersionString: 15.0 VisionOSSdkVersion: 0 VisionOSTargetOSVersionString: 1.0 + xcodeProjectType: 0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 @@ -451,12 +457,6 @@ PlayerSettings: - m_BuildTarget: WindowsStandaloneSupport m_APIs: 0200000012000000 m_Automatic: 0 - m_BuildTargetVRSettings: - - m_BuildTarget: Standalone - m_Enabled: 0 - m_Devices: - - Oculus - - OpenVR m_DefaultShaderChunkSizeInMB: 16 m_DefaultShaderChunkCount: 0 openGLRequireES31: 0 @@ -484,7 +484,7 @@ PlayerSettings: locationUsageDescription: microphoneUsageDescription: bluetoothUsageDescription: - macOSTargetOSVersion: 11.0 + macOSTargetOSVersion: 12.0 switchNMETAOverride: switchNetLibKey: switchSocketMemoryPoolSize: 6144 @@ -630,6 +630,8 @@ PlayerSettings: switchMicroSleepForYieldTime: 25 switchRamDiskSpaceSize: 12 switchUpgradedPlayerSettingsToNMETA: 0 + switchCaStoreSource: 0 + switchCaStoreFilePath: ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: @@ -739,7 +741,7 @@ PlayerSettings: webWasm2023: 0 webEnableSubmoduleStrippingCompatibility: 0 scriptingDefineSymbols: - Standalone: UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT + Standalone: UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT;NETCODE_GAMEOBJECT_BRIDGE_EXPERIMENTAL;NETCODE_EXPERIMENTAL_SINGLE_WORLD_HOST;OUT_OF_BAND_RPC additionalCompilerArguments: Standalone: - -warnaserror @@ -747,6 +749,7 @@ PlayerSettings: scriptingBackend: {} il2cppCompilerConfiguration: {} il2cppCodeGeneration: {} + il2cppLTOMode: {} il2cppStacktraceInformation: {} managedStrippingLevel: EmbeddedLinux: 1 @@ -794,8 +797,7 @@ PlayerSettings: metroDefaultTileSize: 1 metroTileForegroundText: 2 metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} - metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, - a: 1} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} metroSplashScreenUseBackgroundColor: 0 syncCapabilities: 0 platformCapabilities: {} @@ -831,7 +833,6 @@ PlayerSettings: XboxOneXTitleMemory: 8 XboxOneOverrideIdentityName: XboxOneOverrideIdentityPublisher: - vrEditorSettings: {} cloudServicesEnabled: Analytics: 0 Build: 0 @@ -862,6 +863,7 @@ PlayerSettings: captureStartupLogs: {} activeInputHandler: 0 windowsGamepadBackendHint: 0 + enableDirectStorage: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 qualitySettingsNames: [] @@ -877,3 +879,4 @@ PlayerSettings: androidVulkanAllowFilterList: [] androidVulkanDeviceFilterListAsset: {fileID: 0} d3d12DeviceFilterListAsset: {fileID: 0} + allowedHttpConnections: 3 diff --git a/testproject/ProjectSettings/ProjectVersion.txt b/testproject/ProjectSettings/ProjectVersion.txt index c31f18a127..e0b58cc2e0 100644 --- a/testproject/ProjectSettings/ProjectVersion.txt +++ b/testproject/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 6000.2.12f1 -m_EditorVersionWithRevision: 6000.2.12f1 (e89d5df0e333) +m_EditorVersion: 6000.3.15f1 +m_EditorVersionWithRevision: 6000.3.15f1 (c1aa84e375f6)