From 40e08a8d859e02f0f2815c2edf17f25cd77201b1 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 7 Oct 2026 11:29:03 -0500 Subject: [PATCH 01/10] chore: drop N4E experimental bridge define and OUT_OF_BAND_RPC N4E is removing NETCODE_GAMEOBJECT_BRIDGE_EXPERIMENTAL and making its GameObject layer public, so UNIFIED_NETCODE is now the only define the unified path needs. testproject takes N4E from unity/unity trunk until that change ships in a package, which makes manifest-unified.json redundant. --- .yamato/unified-tests.yml | 9 +-- .../Components/Helpers/NetworkObjectBridge.cs | 2 - .../Components/Helpers/UnifiedBootstrap.cs | 3 - .../Runtime/Core/NetworkManager.cs | 4 +- .../Unified/UnifiedNetcodeTransport.cs | 2 +- testproject/Packages/manifest-unified.json | 62 ------------------- testproject/Packages/manifest.json | 1 + 7 files changed, 7 insertions(+), 76 deletions(-) delete mode 100644 testproject/Packages/manifest-unified.json diff --git a/.yamato/unified-tests.yml b/.yamato/unified-tests.yml index 73e987477d..fb3a51ccbc 100644 --- a/.yamato/unified-tests.yml +++ b/.yamato/unified-tests.yml @@ -10,7 +10,7 @@ # 2. For all unified editors (currently a single pinned alpha, see project.metafile) # QUALITY CONSIDERATIONS-------------------------------------------------------------------- - # manifest-unified.json takes N4E from unity/unity trunk, so this job can break on N4E changes made outside NGO. + # testproject takes N4E from unity/unity trunk, so this job can break on N4E changes made outside NGO. #------------------------------------------------------------------------------------ @@ -34,11 +34,8 @@ unified_test_{{ project.name }}_{{ platform.name }}_{{ editor }}: UNIFIED_TESTS: "true" commands: - # Swap in the manifest that takes N4E from unity/unity trunk Workspace/com.unity.netcode. - - cp {{ project.path }}/Packages/manifest-unified.json {{ project.path }}/Packages/manifest.json - # Turn the unified API on. UNIFIED_NETCODE is opt-in, and N4E keeps its GameObject bridge internal - # until NETCODE_GAMEOBJECT_BRIDGE_EXPERIMENTAL is defined, so both are needed here and nowhere else. - - sed -i "s/UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT/UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT;NETCODE_GAMEOBJECT_BRIDGE_EXPERIMENTAL;UNIFIED_NETCODE;OUT_OF_BAND_RPC/" {{ project.path }}/ProjectSettings/ProjectSettings.asset + # Turn the unified API on. UNIFIED_NETCODE is opt-in and needed here and nowhere else. + - sed -i "s/UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT/UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT;UNIFIED_NETCODE/" {{ project.path }}/ProjectSettings/ProjectSettings.asset - unity-downloader-cli --fast --wait -u {{ editor }} -c Editor - UnifiedTestRunner --testproject={{ project.path }} --suite=playmode --artifacts-path=test-results --editor-location=.Editor --rerun-strategy=Test --retry={{ num_test_retries }} --clean-library-on-rerun --timeout={{ test_timeout }} artifacts: diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs index 07818501c5..0dbb68be92 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs @@ -17,8 +17,6 @@ namespace Unity.Netcode [DefaultExecutionOrder(GhostObject.ExecutionOrder + 1)] //BREAK --- Fix this on UNIFIED side 1st - // Internal: GhostBehaviour is only public when NETCODE_GAMEOBJECT_BRIDGE_EXPERIMENTAL is defined, and a public - // type cannot derive from an internal one. internal partial class NetworkObjectBridge : GhostBehaviour { // DefaultExecutionOrder diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs index 3a41f7b3a9..11c3c88884 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs @@ -1,6 +1,3 @@ -#if UNIFIED_NETCODE && !NETCODE_GAMEOBJECT_BRIDGE_EXPERIMENTAL -#error UNIFIED_NETCODE also requires NETCODE_GAMEOBJECT_BRIDGE_EXPERIMENTAL: Netcode for Entities keeps its GameObject bridge (GhostObject, GhostBehaviour, GhostField) internal until that define is set. -#endif #if UNIFIED_NETCODE using System; using Unity.Entities; diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 41f69dc2bf..6414612001 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -25,7 +25,7 @@ // - TestProject has been updated to include N4E. // - TestProject and Runtime tests have been updated to use UnifiedHost. // Remove the conditional compilation and just use the namespace. -#if UNIFIED_NETCODE && OUT_OF_BAND_RPC +#if UNIFIED_NETCODE using Unity.Netcode.Unified; #endif using UnityEngine; @@ -1256,7 +1256,7 @@ internal void Initialize(bool server) // UnityTransport dependencies are then initialized RealTimeProvider = ComponentFactory.Create(this); -#if UNIFIED_NETCODE && OUT_OF_BAND_RPC +#if UNIFIED_NETCODE // TODO-FixMe: // We assign transport at this point to preceed the NetworkConnectionManager // being initialized. However, HasGhostPrefabs might not be set at this point diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs index de23142229..611e2d597e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs @@ -1,4 +1,4 @@ -#if UNIFIED_NETCODE && OUT_OF_BAND_RPC +#if UNIFIED_NETCODE using System; using System.Collections.Generic; using Unity.Burst; diff --git a/testproject/Packages/manifest-unified.json b/testproject/Packages/manifest-unified.json deleted file mode 100644 index 48f23b48d5..0000000000 --- a/testproject/Packages/manifest-unified.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "disableProjectUpdate": false, - "dependencies": { - "com.unity.addressables": "4.0.1", - "com.unity.ai.navigation": "2.0.14", - "com.unity.collab-proxy": "2.13.6", - "com.unity.ide.rider": "3.0.40", - "com.unity.ide.visualstudio": "2.0.26", - "com.unity.mathematics": "1.4.0", - "com.unity.multiplayer.tools": "2.2.11", - "com.unity.netcode": "https://github.cds.internal.unity3d.com/unity/unity.git?path=Workspace/com.unity.netcode#trunk", - "com.unity.netcode.gameobjects": "file:../../com.unity.netcode.gameobjects", - "com.unity.package-validation-suite": "0.49.0-preview", - "com.unity.services.authentication": "3.7.4", - "com.unity.services.multiplayer": "2.3.3", - "com.unity.test-framework": "1.9.0", - "com.unity.test-framework.performance": "6.7.0", - "com.unity.timeline": "6.7.0", - "com.unity.ugui": "2.7.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", - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.cloth": "1.0.0", - "com.unity.modules.director": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.localizationruntime": "1.0.0", - "com.unity.modules.particlesystem": "1.0.0", - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.physics2d": "1.0.0", - "com.unity.modules.physicscore2d": "1.0.0", - "com.unity.modules.screencapture": "1.0.0", - "com.unity.modules.smartstrings": "1.0.0", - "com.unity.modules.terrain": "1.0.0", - "com.unity.modules.terrainphysics": "1.0.0", - "com.unity.modules.tetgen": "1.0.0", - "com.unity.modules.tilemap": "1.0.0", - "com.unity.modules.timelinefoundation": "1.0.0", - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.uielements": "1.0.0", - "com.unity.modules.umbra": "1.0.0", - "com.unity.modules.unityanalytics": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.unitywebrequestassetbundle": "1.0.0", - "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.wind": "1.0.0", - "com.unity.modules.xr": "1.0.0" - }, - "testables": [ - "com.unity.netcode.gameobjects" - ] -} diff --git a/testproject/Packages/manifest.json b/testproject/Packages/manifest.json index 864b9d586a..48f23b48d5 100644 --- a/testproject/Packages/manifest.json +++ b/testproject/Packages/manifest.json @@ -8,6 +8,7 @@ "com.unity.ide.visualstudio": "2.0.26", "com.unity.mathematics": "1.4.0", "com.unity.multiplayer.tools": "2.2.11", + "com.unity.netcode": "https://github.cds.internal.unity3d.com/unity/unity.git?path=Workspace/com.unity.netcode#trunk", "com.unity.netcode.gameobjects": "file:../../com.unity.netcode.gameobjects", "com.unity.package-validation-suite": "0.49.0-preview", "com.unity.services.authentication": "3.7.4", From e107a83237c1a40695af7361ab13f215cbf9b48d Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 7 Oct 2026 13:12:08 -0500 Subject: [PATCH 02/10] Enable the unified path from N4E 7.2.0 and only for hybrid prefab sessions UNIFIED_NETCODE is now an asmdef versionDefine on com.unity.netcode 7.2.0, the release that makes the GameObject layer public, so no project define is needed. UNIFIED_NETCODE_7_0_0 is gone since every supported N4E uses the new namespace. UnifiedBootstrap is marked DisableBootstrapOverrides and created by NetworkManager only when the session has hybrid prefabs, so projects without them keep N4E's own startup bootstrap. A hybrid prefab added after a session started without one is rejected. testproject takes N4E from the remove-experimental-define branch until that PR merges. --- .yamato/unified-tests.yml | 2 -- .../Editor/NetworkObjectEditor.cs | 5 ----- .../Editor/Unity.Netcode.Editor.asmdef | 4 ++-- .../Components/Helpers/NetworkObjectBridge.cs | 7 +------ .../Components/Helpers/UnifiedBootstrap.cs | 8 ++++--- .../Helpers/UnifiedUpdateConnections.cs | 3 --- .../Runtime/Configuration/NetworkPrefabs.cs | 16 +++++++------- .../Runtime/Core/NetworkManager.cs | 14 +++---------- .../Runtime/Core/NetworkObject.cs | 3 --- .../Runtime/Spawning/NetworkPrefabHandler.cs | 3 --- .../Unified/UnifiedNetcodeTransport.cs | 4 ---- .../Runtime/Unity.Netcode.Runtime.asmdef | 4 ++-- .../UnifiedHybridPrefabValidationTests.cs | 21 +++++++++++++++++++ .../TestHelpers/NetcodeIntegrationTest.cs | 11 +++------- .../Unity.Netcode.Runtime.Tests.asmdef | 4 ++-- .../Runtime/TestProject.Runtime.Tests.asmdef | 5 +++++ testproject/Packages/manifest.json | 2 +- 17 files changed, 54 insertions(+), 62 deletions(-) diff --git a/.yamato/unified-tests.yml b/.yamato/unified-tests.yml index fb3a51ccbc..a55b26449d 100644 --- a/.yamato/unified-tests.yml +++ b/.yamato/unified-tests.yml @@ -34,8 +34,6 @@ unified_test_{{ project.name }}_{{ platform.name }}_{{ editor }}: UNIFIED_TESTS: "true" commands: - # Turn the unified API on. UNIFIED_NETCODE is opt-in and needed here and nowhere else. - - sed -i "s/UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT/UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT;UNIFIED_NETCODE/" {{ project.path }}/ProjectSettings/ProjectSettings.asset - unity-downloader-cli --fast --wait -u {{ editor }} -c Editor - UnifiedTestRunner --testproject={{ project.path }} --suite=playmode --artifacts-path=test-results --editor-location=.Editor --rerun-strategy=Test --retry={{ num_test_retries }} --clean-library-on-rerun --timeout={{ test_timeout }} artifacts: diff --git a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs index f6c57c8b61..312e875850 100644 --- a/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs +++ b/com.unity.netcode.gameobjects/Editor/NetworkObjectEditor.cs @@ -1,11 +1,6 @@ using System.Collections.Generic; #if UNIFIED_NETCODE -#if UNIFIED_NETCODE_7_0_0 using Unity.Netcode.Editor; -#else -using Unity.NetCode; -using Unity.NetCode.Editor; -#endif #endif using UnityEditor; using UnityEngine; diff --git a/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef b/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef index 381763e3c6..b402c5badc 100644 --- a/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef +++ b/com.unity.netcode.gameobjects/Editor/Unity.Netcode.Editor.asmdef @@ -23,8 +23,8 @@ "versionDefines": [ { "name": "com.unity.netcode", - "expression": "7.0.0", - "define": "UNIFIED_NETCODE_7_0_0" + "expression": "7.2.0", + "define": "UNIFIED_NETCODE" }, { "name": "com.unity.multiplayer.tools", diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs index 0dbb68be92..f03f4c6263 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs @@ -1,8 +1,5 @@ #if UNIFIED_NETCODE using Unity.Mathematics; -#if !UNIFIED_NETCODE_7_0_0 -using Unity.NetCode; -#endif using Unity.Transforms; using UnityEngine; @@ -88,11 +85,10 @@ internal void ApplyScale(Vector3 scale) } } -#if UNIFIED_NETCODE_7_0_0 /// /// Stands in for N4E's GhostObject.ApplyPostTransformMatrixScale, which 7.0.0 removed along with the /// non-uniform scale rework that gave the GameObject-to-entity transform sync ownership of the - /// . 6.7.0 still has the method, so this is only compiled against 7.0.0. + /// . /// Remove it once N4E exposes a supported way to push scale to a ghost. /// internal static class GhostObjectScaleExtensions @@ -126,6 +122,5 @@ internal static void ApplyPostTransformMatrixScale(this GhostObject ghost, Vecto entityManager.SetComponentData(entity, localTransform); } } -#endif } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs index 11c3c88884..bebceb49d1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs @@ -1,9 +1,6 @@ #if UNIFIED_NETCODE using System; using Unity.Entities; -#if !UNIFIED_NETCODE_7_0_0 -using Unity.NetCode; -#endif using UnityEngine; namespace Unity.Netcode @@ -13,6 +10,11 @@ namespace Unity.Netcode /// Handles the bootstrap process for both client and server in unified mode. This is used to create the world and set it on the /// NetworkManager during initialization. /// + /// + /// Only creates it, and only for a session with hybrid prefabs. Without the attribute, + /// Entities would pick it as the startup bootstrap for every project, replacing Netcode for Entities' own. + /// + [DisableBootstrapOverrides] internal class UnifiedBootstrap : ClientServerBootstrap { public static UnifiedBootstrap Instance { get; private set; } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs index db3988b0fd..513f84cfc7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs @@ -2,9 +2,6 @@ using System.Collections.Generic; using Unity.Collections; using Unity.Entities; -#if !UNIFIED_NETCODE_7_0_0 -using Unity.NetCode; -#endif using UnityEngine; namespace Unity.Netcode.Components diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs index bfe91891b3..9ae47ce8d7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkPrefabs.cs @@ -173,7 +173,7 @@ internal void Shutdown() list.OnRemove -= RemoveTriggeredByNetworkPrefabList; } #if UNIFIED_NETCODE - m_RejectGhostPrefabs = false; + m_GhostPrefabRejection = null; #endif } @@ -367,18 +367,20 @@ public bool Contains(NetworkPrefab prefab) #if UNIFIED_NETCODE internal const string DistributedAuthorityHybridPrefabError = "Distributed authority does not support hybrid prefabs."; + internal const string HybridPrefabAfterStartError = "Hybrid prefabs must be registered before the NetworkManager starts."; internal bool HasGhostPrefabs { get; private set; } - // Cleared in Shutdown. - private bool m_RejectGhostPrefabs; + // The error a hybrid prefab added during the session is rejected with, or null. Cleared in Shutdown. + private string m_GhostPrefabRejection; /// - /// A distributed authority session rejects hybrid prefabs added while it runs. + /// Hybrid mode is decided when the session starts. A distributed authority session, or one that started without + /// hybrid prefabs, rejects hybrid prefabs added while it runs. /// internal void OnSessionStarting(bool distributedAuthority) { - m_RejectGhostPrefabs = distributedAuthority; + m_GhostPrefabRejection = distributedAuthority ? DistributedAuthorityHybridPrefabError : HasGhostPrefabs ? null : HybridPrefabAfterStartError; } /// @@ -426,9 +428,9 @@ private bool AddPrefabRegistration(NetworkPrefab networkPrefab) if (networkPrefab.HasGhost) { // Registering a hybrid prefab mid-session would switch NetworkManager into hybrid mode and stop its send queue. - if (m_RejectGhostPrefabs) + if (m_GhostPrefabRejection != null) { - Debug.LogError($"{DistributedAuthorityHybridPrefabError} {networkPrefab.GetDebugName()} was not added."); + Debug.LogError($"{m_GhostPrefabRejection} {networkPrefab.GetDebugName()} was not added."); return false; } HasGhostPrefabs = true; diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 6414612001..3bb99bb602 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -6,15 +6,8 @@ using Unity.Entities; // N4E's own Netcode class cannot be aliased as "Netcode": inside namespace Unity.Netcode that name // resolves to the enclosing Unity.Netcode namespace before any file-scope alias is considered, so it gets -// its own name here. 6.7.0 additionally keeps the config and world types under the older Unity.NetCode -// casing, aliased to the 7.0.0 spellings so the use sites below read the same either way. -#if UNIFIED_NETCODE_7_0_0 +// its own name here. using EntitiesNetcode = Unity.Netcode.Netcode; -#else -using EntitiesNetcode = Unity.NetCode.Netcode; -using NetcodeConfig = Unity.NetCode.NetCodeConfig; -using NetcodeWorld = Unity.NetCode.NetcodeWorld; -#endif #endif using Unity.Netcode.Components; using Unity.Netcode.GameObjects.Timing; @@ -1399,10 +1392,9 @@ internal void InitializeNetcodeWorld() EntitiesNetcode.Reset(); } - /// !! Initialize worlds here !! - /// Worlds are created here: + TypeManager.Initialize(); UnifiedBootstrap.CurrentNetworkManagerForInitialization = this; - DefaultWorldInitialization.Initialize("Default World", false); + new UnifiedBootstrap().Initialize("Default World"); } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 6a51c7fecd..d3951a2732 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -7,9 +7,6 @@ using Unity.Netcode.Components; using Unity.Netcode.Logging; using Unity.Netcode.Runtime; -#if UNIFIED_NETCODE && !UNIFIED_NETCODE_7_0_0 -using Unity.NetCode; -#endif #if UNITY_EDITOR using UnityEditor; diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs index 6d4e1987d2..22a03169b8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -#if UNIFIED_NETCODE && !UNIFIED_NETCODE_7_0_0 -using Unity.NetCode; -#endif using UnityEngine; namespace Unity.Netcode diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs index 611e2d597e..29faa62c21 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs @@ -6,10 +6,6 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Entities; -#if !UNIFIED_NETCODE_7_0_0 -using Unity.NetCode; -using NetcodeConnectionEvent = Unity.NetCode.NetCodeConnectionEvent; -#endif using Unity.Netcode.GameObjects.Timing; using Unity.Netcode.Transports.UTP; using UnityEngine; diff --git a/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef b/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef index 847c5494ed..e31ea05704 100644 --- a/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef +++ b/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef @@ -28,8 +28,8 @@ "versionDefines": [ { "name": "com.unity.netcode", - "expression": "7.0.0", - "define": "UNIFIED_NETCODE_7_0_0" + "expression": "7.2.0", + "define": "UNIFIED_NETCODE" }, { "name": "com.unity.multiplayer.tools", diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/UnifiedHybridPrefabValidationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/UnifiedHybridPrefabValidationTests.cs index 1769656bf8..bd1d2ba296 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/UnifiedHybridPrefabValidationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/UnifiedHybridPrefabValidationTests.cs @@ -12,6 +12,7 @@ namespace Unity.Netcode.RuntimeTests /// /// Validates the hybrid prefab registrations that are rejected:
/// - Any hybrid prefab in a distributed authority session. The start fails, and one added during the session is not registered.
+ /// - A hybrid prefab added during a session that started without hybrid prefabs. It is not registered.
/// - A NetworkPrefab override with a hybrid source or target prefab, in any topology. The entry is ignored.
///
/// @@ -129,6 +130,26 @@ public void FailedStartDoesNotRejectHybridPrefabs() Assert.IsTrue(networkManager.NetworkConfig.Prefabs.Contains(m_HybridPrefab), "The hybrid prefab was rejected after a failed distributed authority start!"); } + [UnityTest] + public IEnumerator HybridPrefabAddedAfterStartIsRejected() + { + var networkManager = CreateNetworkManager(NetworkTopologyTypes.ClientServer); + var prefabs = networkManager.NetworkConfig.Prefabs; + Assert.IsTrue(networkManager.StartHost(), "Failed to start the session!"); + + LogAssert.Expect(LogType.Error, new Regex($"{Regex.Escape(NetworkPrefabs.HybridPrefabAfterStartError)}.*{m_HybridPrefab.name}")); + networkManager.AddNetworkPrefab(m_HybridPrefab); + Assert.IsFalse(prefabs.Contains(m_HybridPrefab), "The hybrid prefab was registered during a session that started without hybrid prefabs!"); + Assert.IsFalse(prefabs.HasGhostPrefabs, $"{nameof(NetworkPrefabs.HasGhostPrefabs)} was set during the session!"); + + networkManager.Shutdown(); + yield return WaitForConditionOrTimeOut(() => !networkManager.IsListening); + AssertOnTimeout("The session did not shut down!"); + + networkManager.AddNetworkPrefab(m_HybridPrefab); + Assert.IsTrue(prefabs.Contains(m_HybridPrefab), "The hybrid prefab was rejected after the session ended!"); + } + [Test] public void PrefabListAddDuringSessionRegistersOnce() { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index aab259cc27..f3f75a2688 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -7,12 +7,7 @@ using System.Text; using NUnit.Framework; #if UNIFIED_NETCODE -#if UNIFIED_NETCODE_7_0_0 using EntitiesNetcode = Unity.Netcode.Netcode; -#else -using Unity.NetCode; -using EntitiesNetcode = Unity.NetCode.Netcode; -#endif #endif using Unity.Netcode.GameObjects.Timing; using Unity.Netcode.RuntimeTests; @@ -708,7 +703,7 @@ public void OneTimeSetup() InternalOnOneTimeSetup(); } -#if UNIFIED_NETCODE_7_0_0 && UNITY_EDITOR +#if UNITY_EDITOR private bool m_PreviousWarnBatchedTicks; #endif @@ -727,7 +722,7 @@ private void InternalOnOneTimeSetup() // Enable NetcodeIntegrationTest auto-label feature NetcodeIntegrationTestHelpers.RegisterNetcodeIntegrationTest(true); -#if UNIFIED_NETCODE_7_0_0 && UNITY_EDITOR +#if UNITY_EDITOR // Netcode for Entities emits a performance-dependent "Server Tick Batching" warning on loaded CI agents that would fail strict log assertions. m_PreviousWarnBatchedTicks = MultiplayerPlayModePreferences.WarnBatchedTicks; MultiplayerPlayModePreferences.WarnBatchedTicks = false; @@ -1924,7 +1919,7 @@ public void OneTimeTearDown() // Disable NetcodeIntegrationTest auto-label feature NetcodeIntegrationTestHelpers.RegisterNetcodeIntegrationTest(false); -#if UNIFIED_NETCODE_7_0_0 && UNITY_EDITOR +#if UNITY_EDITOR MultiplayerPlayModePreferences.WarnBatchedTicks = m_PreviousWarnBatchedTicks; #endif 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 0bb1905817..cb3dec690f 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 @@ -32,8 +32,8 @@ "versionDefines": [ { "name": "com.unity.netcode", - "expression": "7.0.0", - "define": "UNIFIED_NETCODE_7_0_0" + "expression": "7.2.0", + "define": "UNIFIED_NETCODE" }, { "name": "com.unity.multiplayer.tools", diff --git a/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef b/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef index 38af444a61..15cd7f5f6b 100644 --- a/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef +++ b/testproject/Assets/Tests/Runtime/TestProject.Runtime.Tests.asmdef @@ -33,6 +33,11 @@ "name": "com.unity.addressables", "expression": "", "define": "TESTPROJECT_USE_ADDRESSABLES" + }, + { + "name": "com.unity.netcode", + "expression": "7.2.0", + "define": "UNIFIED_NETCODE" } ], "noEngineReferences": false diff --git a/testproject/Packages/manifest.json b/testproject/Packages/manifest.json index 48f23b48d5..6b7ddbf826 100644 --- a/testproject/Packages/manifest.json +++ b/testproject/Packages/manifest.json @@ -8,7 +8,7 @@ "com.unity.ide.visualstudio": "2.0.26", "com.unity.mathematics": "1.4.0", "com.unity.multiplayer.tools": "2.2.11", - "com.unity.netcode": "https://github.cds.internal.unity3d.com/unity/unity.git?path=Workspace/com.unity.netcode#trunk", + "com.unity.netcode": "https://github.cds.internal.unity3d.com/unity/unity.git?path=Workspace/com.unity.netcode#netcode/remove-exp-define-ghost-object", "com.unity.netcode.gameobjects": "file:../../com.unity.netcode.gameobjects", "com.unity.package-validation-suite": "0.49.0-preview", "com.unity.services.authentication": "3.7.4", From f18de5df9cfb8d68419c2cf79261da9db206e5c3 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 7 Oct 2026 16:09:57 -0500 Subject: [PATCH 03/10] Register the player prefab before the session decides hybrid mode A hybrid PlayerPrefab outside the prefab lists was registered after the transport choice and the session-start rejection, so it was rejected. It now registers with the prefab lists before start, and distributed authority rejects it like any other hybrid prefab. --- .../Runtime/Configuration/NetworkConfig.cs | 6 +++-- .../Runtime/Core/NetworkManager.cs | 2 +- .../UnifiedHybridPrefabValidationTests.cs | 27 +++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs index 13a5756f69..90780e40f7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs @@ -390,11 +390,13 @@ internal void InitializePrefabs() #if UNIFIED_NETCODE /// - /// Registers any prefab list assigned after Awake, and returns false for a distributed authority session with a hybrid prefab registered. + /// Registers any prefab list assigned after Awake and the , and returns false for a distributed authority session with a hybrid prefab registered. /// - internal bool InitializePrefabsForStart() + internal bool InitializePrefabsForStart(NetworkPrefabHandler prefabHandler) { InitializePrefabs(); + // A player prefab outside the prefab lists still decides hybrid mode, so it is registered before the session starts. + prefabHandler.RegisterPlayerPrefab(); return NetworkTopology != NetworkTopologyTypes.DistributedAuthority || Prefabs.ValidateForDistributedAuthority(); } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 3bb99bb602..8a197ed546 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -1361,7 +1361,7 @@ private bool CanStart(StartType type) } #if UNIFIED_NETCODE - if (!NetworkConfig.InitializePrefabsForStart()) + if (!NetworkConfig.InitializePrefabsForStart(PrefabHandler)) { return false; } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/UnifiedHybridPrefabValidationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/UnifiedHybridPrefabValidationTests.cs index bd1d2ba296..a117274b10 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/UnifiedHybridPrefabValidationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/UnifiedHybridPrefabValidationTests.cs @@ -4,6 +4,7 @@ using System.Text.RegularExpressions; using NUnit.Framework; using Unity.Netcode.TestHelpers.Runtime; +using Unity.Netcode.Unified; using UnityEngine; using UnityEngine.TestTools; @@ -13,6 +14,7 @@ namespace Unity.Netcode.RuntimeTests /// Validates the hybrid prefab registrations that are rejected:
/// - Any hybrid prefab in a distributed authority session. The start fails, and one added during the session is not registered.
/// - A hybrid prefab added during a session that started without hybrid prefabs. It is not registered.
+ /// - A hybrid player prefab counts toward hybrid mode before the session starts, so distributed authority rejects it.
/// - A NetworkPrefab override with a hybrid source or target prefab, in any topology. The entry is ignored.
///
/// @@ -150,6 +152,31 @@ public IEnumerator HybridPrefabAddedAfterStartIsRejected() Assert.IsTrue(prefabs.Contains(m_HybridPrefab), "The hybrid prefab was rejected after the session ended!"); } + /// + /// A hybrid that is in no prefab list still decides hybrid mode before the session starts. + /// + [Test] + public void HybridPlayerPrefabCountsBeforeStart() + { + var networkManager = CreateNetworkManager(NetworkTopologyTypes.DistributedAuthority); + networkManager.NetworkConfig.PlayerPrefab = m_HybridPrefab; + LogAssert.Expect(LogType.Error, new Regex($"{Regex.Escape(NetworkPrefabs.DistributedAuthorityHybridPrefabError)}.*{m_HybridPrefab.name}")); + Assert.IsFalse(networkManager.StartHost(), "Started a distributed authority session with a hybrid player prefab!"); + + networkManager.NetworkConfig.NetworkTopology = NetworkTopologyTypes.ClientServer; + // Only the registration is under test. The fixture cannot map a player ghost spawned by a NetworkManager it did not start. + networkManager.NetworkConfig.ConnectionApproval = true; + networkManager.ConnectionApprovalCallback = (request, response) => + { + response.Approved = true; + response.CreatePlayerObject = false; + }; + Assert.IsTrue(networkManager.StartHost(), "Failed to start a session with a hybrid player prefab!"); + Assert.IsTrue(networkManager.NetworkConfig.Prefabs.Contains(m_HybridPrefab), "The hybrid player prefab was not registered!"); + Assert.IsTrue(networkManager.NetworkConfig.Prefabs.HasGhostPrefabs, $"The hybrid player prefab did not set {nameof(NetworkPrefabs.HasGhostPrefabs)}!"); + Assert.IsInstanceOf(networkManager.NetworkConfig.NetworkTransport, "The session did not switch to the unified transport!"); + } + [Test] public void PrefabListAddDuringSessionRegistersOnce() { From d82f061267e4c3598b730f3b82376dfc08b99d39 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 7 Oct 2026 17:03:28 -0500 Subject: [PATCH 04/10] style: format unified netcode code now that UNIFIED_NETCODE compiles in testproject UNIFIED_NETCODE is now defined automatically from N4E 7.2.0, so the Standards check formats code behind it for the first time. --- .../Components/NetworkRigidBodyBase.cs | 5 +-- .../Runtime/Spawning/GhostSpawnManager.cs | 12 +++---- .../Runtime/Spawning/NetworkSpawnManager.cs | 34 +++++++++---------- .../Unified/UnifiedNetcodeTransport.cs | 4 +-- 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs index 4e8b643db3..b9b2468a8b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs @@ -289,10 +289,7 @@ private bool InitializeHybrid() } // Clears any registration left behind by a prior Initialize call. - if (NetworkTransform != null) - { - NetworkTransform.UnregisterRigidbody(); - } + NetworkTransform?.UnregisterRigidbody(); m_OriginalKinematicState = IsKinematic(); return true; diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/GhostSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/GhostSpawnManager.cs index ed6b512494..268bff35e5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/GhostSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/GhostSpawnManager.cs @@ -182,13 +182,13 @@ internal void ProcessAllGhostsPendingSynchronization() m_GhostSynchronizationPendingRemoval.Add(networkObjectId); } else - if ((ghost.Value.RegistrationTime + spawnTimeout) < Time.realtimeSinceStartup) - { - m_Log.Info(new Context(LogLevel.Developer, $"Registering {nameof(NetworkObject.SerializedObject)} for pending synchronization").AddInfo(nameof(NetworkObject.NetworkObjectId), networkObjectId)); + if ((ghost.Value.RegistrationTime + spawnTimeout) < Time.realtimeSinceStartup) + { + m_Log.Info(new Context(LogLevel.Developer, $"Registering {nameof(NetworkObject.SerializedObject)} for pending synchronization").AddInfo(nameof(NetworkObject.NetworkObjectId), networkObjectId)); - // Timed out entries are removed too - m_GhostSynchronizationPendingRemoval.Add(ghost.Key); - } + // Timed out entries are removed too + m_GhostSynchronizationPendingRemoval.Add(ghost.Key); + } } foreach (var networkObjectId in m_GhostSynchronizationPendingRemoval) diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 28803104f4..8751174403 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -981,27 +981,27 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SerializedObject s } else #endif - // If scene management is disabled or the NetworkObject was dynamically spawned - if (!NetworkManager.NetworkConfig.EnableSceneManagement || !serializedObject.IsSceneObject) - { - 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) + // If scene management is disabled or the NetworkObject was dynamically spawned + if (!NetworkManager.NetworkConfig.EnableSceneManagement || !serializedObject.IsSceneObject) { - NetworkLog.LogErrorServer(new Context(LogLevel.Error, $"{nameof(NetworkPrefab)} hash was not found! In-Scene placed {nameof(NetworkObject)} soft synchronization failure!").AddInfo(nameof(NetworkObject.GlobalObjectIdHash), globalObjectIdHash)); - return null; + networkObject = GetNetworkObjectToSpawn(serializedObject.Hash, serializedObject.OwnerClientId, position, rotation, serializedObject.IsSceneObject, instantiationData); } - - // 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) + else // Get the in-scene placed NetworkObject { - networkObject.gameObject.SetActive(true); + networkObject = NetworkManager.SceneManager.GetSceneRelativeInSceneNetworkObject(globalObjectIdHash, serializedObject.NetworkSceneHandle); + if (networkObject == null) + { + NetworkLog.LogErrorServer(new Context(LogLevel.Error, $"{nameof(NetworkPrefab)} hash was not found! In-Scene placed {nameof(NetworkObject)} soft synchronization failure!").AddInfo(nameof(NetworkObject.GlobalObjectIdHash), globalObjectIdHash)); + 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); + } } - } if (networkObject == null) { return null; diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs index 29faa62c21..769599b91c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs @@ -164,7 +164,7 @@ protected override void OnUpdate() NetworkManager.MessageManager.ProcessSendQueues(); using var commandBuffer = new EntityCommandBuffer(Allocator.Temp); - foreach(var (networkId, _, entity) in SystemAPI.Query, RefRO>().WithEntityAccess()) + foreach (var (networkId, _, entity) in SystemAPI.Query, RefRO>().WithEntityAccess()) { var connectionId = networkId.ValueRO.Value; DynamicBuffer rpcs = EntityManager.GetBuffer(entity); @@ -175,7 +175,7 @@ protected override void OnUpdate() { Transport.DispatchMessage(connectionId, buffer); } - catch(Exception e) + catch (Exception e) { Debug.LogException(e); } From 8ce01e51596ba76b70f5d64d02566f05d82a369f Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 7 Oct 2026 17:17:19 -0500 Subject: [PATCH 05/10] style: format the hybrid code merged from develop-3.x.x Keeps an implicit bool check where null propagation would call into a destroyed NetworkBehaviour. --- .../Runtime/NetworkVariable/NetworkVariableBase.cs | 3 ++- .../Runtime/Transports/Unified/UnifiedNetcodeTransport.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs index 3e128fa508..c02614279d 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs @@ -282,7 +282,8 @@ public virtual void SetDirty(bool isDirty) if (m_IsDirty) { #if UNIFIED_NETCODE - if (m_NetworkBehaviour != null) + // The implicit bool skips a destroyed behaviour, whose name the warning reads; ?. would not. + if (m_NetworkBehaviour) { m_NetworkBehaviour.WarnIfNetworkVariableWrittenInPredictionLoop(Name); } diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs index 5a64354bcf..c95a72053b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs @@ -488,7 +488,7 @@ public override void Initialize(NetworkManager networkManager = null) public override void Shutdown() { - var netcodeWorld = m_NetworkManager != null ? m_NetworkManager.NetcodeWorld : null; + var netcodeWorld = m_NetworkManager?.NetcodeWorld; if (netcodeWorld != null) { netcodeWorld.OnConnectionEvent -= OnClientConnectionEvent; From 2eaca3d204c90f711bc01200f6281c8ae02a498e Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Thu, 8 Oct 2026 20:06:39 -0500 Subject: [PATCH 06/10] chore: stop tracking the NetcodeConfig assets in testproject and minimalproject NGO now applies the NetcodeConfig values it needs at session start, so nothing in either asset is required. Netcode for Entities creates the asset and its project settings entry on first open. The settings file is removed too, since keeping it tracked would make every fresh checkout rewrite it. --- minimalproject/Assets/NetcodeConfig.asset | 81 ------------------ .../Assets/NetcodeConfig.asset.meta | 8 -- .../NetCodeClientAndServerSettings.asset | 35 -------- testproject/Assets/NetCodeConfig.asset | 82 ------------------- testproject/Assets/NetCodeConfig.asset.meta | 8 -- .../NetCodeClientAndServerSettings.asset | 35 -------- 6 files changed, 249 deletions(-) delete mode 100644 minimalproject/Assets/NetcodeConfig.asset delete mode 100644 minimalproject/Assets/NetcodeConfig.asset.meta delete mode 100644 minimalproject/ProjectSettings/NetCodeClientAndServerSettings.asset delete mode 100644 testproject/Assets/NetCodeConfig.asset delete mode 100644 testproject/Assets/NetCodeConfig.asset.meta delete mode 100644 testproject/ProjectSettings/NetCodeClientAndServerSettings.asset diff --git a/minimalproject/Assets/NetcodeConfig.asset b/minimalproject/Assets/NetcodeConfig.asset deleted file mode 100644 index 630b6989db..0000000000 --- a/minimalproject/Assets/NetcodeConfig.asset +++ /dev/null @@ -1,81 +0,0 @@ -%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 - 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 - AlwaysRollbackAllPredictedGhosts: 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 - TracingConfig: - TracingMemoryLimitMb: 2048 - FuzzyFactor: 0.0001 - IgnoreDTDiffs: 0 - IgnorePartialTicks: 0 diff --git a/minimalproject/Assets/NetcodeConfig.asset.meta b/minimalproject/Assets/NetcodeConfig.asset.meta deleted file mode 100644 index e53a35b288..0000000000 --- a/minimalproject/Assets/NetcodeConfig.asset.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 81fc37e454ef2374283353c132cbdc51 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 11400000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/minimalproject/ProjectSettings/NetCodeClientAndServerSettings.asset b/minimalproject/ProjectSettings/NetCodeClientAndServerSettings.asset deleted file mode 100644 index dacb5865dd..0000000000 --- a/minimalproject/ProjectSettings/NetCodeClientAndServerSettings.asset +++ /dev/null @@ -1,35 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &1 -MonoBehaviour: - m_ObjectHideFlags: 53 - 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: 4111970df3904877aade1a474116e5c2, type: 3} - m_Name: - m_EditorClassIdentifier: Unity.NetCode.Authoring.Hybrid::Unity.NetCode.Hybrid.NetCodeClientAndServerSettings - FilterSettings: - ExcludedBakingSystemAssemblies: [] - AdditionalScriptingDefines: [] - GlobalNetcodeConfig: {fileID: 11400000, guid: 81fc37e454ef2374283353c132cbdc51, type: 2} - CurrentImportanceSuggestions: - - MinValue: 1 - MaxValue: 4 - Name: Low Importance - Tooltip: For cosmetic (i.e. visual-only) ghosts like glass bottles, signs, beach-balls, and cones etc. Typically Static. - - MinValue: 5 - MaxValue: 40 - Name: Medium Importance - Tooltip: For common gameplay-affecting ghosts like trees, doors, explosive barrels, dropped loot etc. Typically Static. - - MinValue: 50 - MaxValue: 250 - Name: High Importance - Tooltip: For per-player and objective-critical ghosts like Player Character Controllers and CTF flags etc. Typically for Dynamic i.e. Predicted ghosts. UsePreSerialization is likely a good fit. - - MinValue: 1000 - MaxValue: 0 - Name: Critical Importance - Tooltip: For gameplay critical singletons like the one keeping the current score, or the one denoting whether or not the current round has started etc. Choose UsePreSerialization, and use sparingly. diff --git a/testproject/Assets/NetCodeConfig.asset b/testproject/Assets/NetCodeConfig.asset deleted file mode 100644 index 4f87186053..0000000000 --- a/testproject/Assets/NetCodeConfig.asset +++ /dev/null @@ -1,82 +0,0 @@ -%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: 30 - PredictedFixedStepSimulationTickRatio: 1 - NetworkTickRate: 30 - MaxSimulationStepsPerFrame: 1 - MaxSimulationStepBatchSize: 4 - TargetFrameRateMode: 0 - m_SendSnapshotsForCatchUpTicks: 0 - SnapshotAckMaskCapacity: 4096 - m_ClampPartialTicksThreshold: 5 - HandshakeApprovalTimeoutMS: 5000 - ClientTickRate: - InterpolationTimeNetTicks: 0 - InterpolationTimeMS: 50 - MaxExtrapolationTimeSimTicks: 20 - ForcedInputLatencyTicks: 0 - MaxPredictAheadTimeMS: 500 - NumAdditionalClientPredictedGhostLifetimeTicks: 0 - DefaultClassificationAllowableTickPeriod: 5 - TargetCommandSlack: 2 - NumAdditionalCommandsToSend: 2 - MaxPredictionStepBatchSizeRepeatedTick: 0 - MaxPredictionStepBatchSizeFirstTimeTick: 0 - AlwaysRollbackAllPredictedGhosts: 0 - PredictionLoopUpdateMode: 0 - InterpolationDelayJitterScale: 1.25 - InterpolationDelayMaxDeltaTicksFraction: 0.15 - InterpolationDelayCorrectionFraction: 0.1 - InterpolationTimeScaleMin: 0.9 - InterpolationTimeScaleMax: 1.33 - CommandAgeCorrectionFraction: 0.1 - PredictionTimeScaleMin: 0.9 - PredictionTimeScaleMax: 1.1 - GhostSendSystemData: - DefaultSnapshotPacketSize: 15000 - PercentReservedForDespawnMessages: 0.2 - 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: 128 - ClientReceiveQueueCapacity: 128 - ServerSendQueueCapacity: 512 - ServerReceiveQueueCapacity: 512 - MaxMessageSize: 1400 - TracingConfig: - TracingMemoryLimitMb: 2048 - FuzzyFactor: 0.0001 - IgnoreDTDiffs: 0 - IgnorePartialTicks: 0 diff --git a/testproject/Assets/NetCodeConfig.asset.meta b/testproject/Assets/NetCodeConfig.asset.meta deleted file mode 100644 index 222ccfadf4..0000000000 --- a/testproject/Assets/NetCodeConfig.asset.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: c547acbddd81d32a0ba5e62ddfc4f4e3 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 11400000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/testproject/ProjectSettings/NetCodeClientAndServerSettings.asset b/testproject/ProjectSettings/NetCodeClientAndServerSettings.asset deleted file mode 100644 index 9994ae3ddb..0000000000 --- a/testproject/ProjectSettings/NetCodeClientAndServerSettings.asset +++ /dev/null @@ -1,35 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &1 -MonoBehaviour: - m_ObjectHideFlags: 53 - 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: 4111970df3904877aade1a474116e5c2, type: 3} - m_Name: - m_EditorClassIdentifier: Unity.NetCode.Authoring.Hybrid::Unity.NetCode.Hybrid.NetCodeClientAndServerSettings - FilterSettings: - ExcludedBakingSystemAssemblies: [] - AdditionalScriptingDefines: [] - GlobalNetcodeConfig: {fileID: 11400000, guid: c547acbddd81d32a0ba5e62ddfc4f4e3, type: 2} - CurrentImportanceSuggestions: - - MinValue: 1 - MaxValue: 4 - Name: Low Importance - Tooltip: For cosmetic (i.e. visual-only) ghosts like glass bottles, signs, beach-balls, and cones etc. Typically Static. - - MinValue: 5 - MaxValue: 40 - Name: Medium Importance - Tooltip: For common gameplay-affecting ghosts like trees, doors, explosive barrels, dropped loot etc. Typically Static. - - MinValue: 50 - MaxValue: 250 - Name: High Importance - Tooltip: For per-player and objective-critical ghosts like Player Character Controllers and CTF flags etc. Typically for Dynamic i.e. Predicted ghosts. UsePreSerialization is likely a good fit. - - MinValue: 1000 - MaxValue: 0 - Name: Critical Importance - Tooltip: For gameplay critical singletons like the one keeping the current score, or the one denoting whether or not the current round has started etc. Choose UsePreSerialization, and use sparingly. From 04b99d72ead58da29bbf6e1680ec366193784d7b Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Thu, 8 Oct 2026 20:35:41 -0500 Subject: [PATCH 07/10] chore: ignore the NetcodeConfig assets Netcode for Entities creates on first open The assets are no longer tracked, so the files N4E writes when a project opens would otherwise show up as untracked changes and could be committed back. --- minimalproject/.gitignore | 5 +++++ testproject/.gitignore | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/minimalproject/.gitignore b/minimalproject/.gitignore index fbe0ca9b9e..832ae5d1de 100644 --- a/minimalproject/.gitignore +++ b/minimalproject/.gitignore @@ -73,3 +73,8 @@ crashlytics-build.properties /[Aa]ssets/[Ss]treamingAssets/BuildInfo.json.meta InitTestScene* + +# Created by Netcode for Entities on first open. NGO applies the values it needs at runtime. +/[Aa]ssets/[Nn]et[Cc]ode[Cc]onfig*.asset +/[Aa]ssets/[Nn]et[Cc]ode[Cc]onfig*.asset.meta +/[Pp]roject[Ss]ettings/[Nn]et[Cc]ode[Cc]lient[Aa]nd[Ss]erver[Ss]ettings.asset diff --git a/testproject/.gitignore b/testproject/.gitignore index adf747b1fc..338ae823d9 100644 --- a/testproject/.gitignore +++ b/testproject/.gitignore @@ -82,3 +82,8 @@ InitTestScene* boot.config SceneTemplateSettings.json *BurstAotSettings*.json + +# Created by Netcode for Entities on first open. NGO applies the values it needs at runtime. +/[Aa]ssets/[Nn]et[Cc]ode[Cc]onfig*.asset +/[Aa]ssets/[Nn]et[Cc]ode[Cc]onfig*.asset.meta +/[Pp]roject[Ss]ettings/[Nn]et[Cc]ode[Cc]lient[Aa]nd[Ss]erver[Ss]ettings.asset From 74508be7044deb9de8ff0cf7b642ef18c0d6b7c0 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Thu, 8 Oct 2026 22:06:56 -0500 Subject: [PATCH 08/10] fix: keep Unity's destroyed-object check where the formatter used null propagation Implicit bool checks instead of ?. on NetworkTransform and NetworkManager, which the Standards formatter does not rewrite. --- .../Runtime/Components/NetworkRigidBodyBase.cs | 5 ++++- .../Runtime/Transports/Unified/UnifiedNetcodeTransport.cs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs index b9b2468a8b..d55b2f30e8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkRigidBodyBase.cs @@ -289,7 +289,10 @@ private bool InitializeHybrid() } // Clears any registration left behind by a prior Initialize call. - NetworkTransform?.UnregisterRigidbody(); + if (NetworkTransform) + { + NetworkTransform.UnregisterRigidbody(); + } m_OriginalKinematicState = IsKinematic(); return true; diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs index c95a72053b..a7ddf73f32 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs @@ -488,7 +488,7 @@ public override void Initialize(NetworkManager networkManager = null) public override void Shutdown() { - var netcodeWorld = m_NetworkManager?.NetcodeWorld; + var netcodeWorld = m_NetworkManager ? m_NetworkManager.NetcodeWorld : null; if (netcodeWorld != null) { netcodeWorld.OnConnectionEvent -= OnClientConnectionEvent; From 0267481ee16b7e751173ab14a6f0502ee8b9964e Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Fri, 9 Oct 2026 10:09:35 -0500 Subject: [PATCH 09/10] fix: keep UnifiedBootstrap as the Entities startup bootstrap [DisableBootstrapOverrides] let Netcode for Entities' own bootstrap run at startup. Without a NetcodeConfig that disables automatic bootstrapping, it creates a host world listening on port 7979, so a hybrid host could not bind the port and the unified tests failed. As the startup bootstrap, UnifiedBootstrap only creates a local world, which is how #4144 resolved this. --- .../Runtime/Components/Helpers/UnifiedBootstrap.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs index dd980606eb..6629cdc43b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs @@ -12,10 +12,10 @@ namespace Unity.Netcode /// NetworkManager during initialization. ///
/// - /// Only creates it, and only for a session with hybrid prefabs. Without the attribute, - /// Entities would pick it as the startup bootstrap for every project, replacing Netcode for Entities' own. + /// Entities picks this as the startup bootstrap, which only creates a local world. Do not add + /// [DisableBootstrapOverrides]: Netcode for Entities' own bootstrap would then run and, unless automatic + /// bootstrapping is disabled, create a host world that holds the port a hybrid session listens on. /// - [DisableBootstrapOverrides] internal class UnifiedBootstrap : ClientServerBootstrap { public static UnifiedBootstrap Instance { get; private set; } From aaef0750edea00544c9081713d89be4889fc269b Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Fri, 9 Oct 2026 19:59:49 -0500 Subject: [PATCH 10/10] chore: point the Netcode for Entities git dependency at trunk The GameObject layer change has merged to trunk, so testproject follows trunk instead of the pull request branch, and minimalproject gets the same entry so both compile against N4E 7.2.0. These switch to the published 7.2.0 package once it is available. --- minimalproject/Packages/manifest.json | 1 + testproject/Packages/manifest.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/minimalproject/Packages/manifest.json b/minimalproject/Packages/manifest.json index c6a0266583..43e93f5ede 100644 --- a/minimalproject/Packages/manifest.json +++ b/minimalproject/Packages/manifest.json @@ -2,6 +2,7 @@ "disableProjectUpdate": false, "dependencies": { "com.unity.ide.rider": "3.0.38", + "com.unity.netcode": "https://github.cds.internal.unity3d.com/unity/unity.git?path=Workspace/com.unity.netcode#trunk", "com.unity.netcode.gameobjects": "file:../../com.unity.netcode.gameobjects", "com.unity.test-framework": "1.9.0", "com.unity.modules.adaptiveperformance": "1.0.0", diff --git a/testproject/Packages/manifest.json b/testproject/Packages/manifest.json index 6b7ddbf826..48f23b48d5 100644 --- a/testproject/Packages/manifest.json +++ b/testproject/Packages/manifest.json @@ -8,7 +8,7 @@ "com.unity.ide.visualstudio": "2.0.26", "com.unity.mathematics": "1.4.0", "com.unity.multiplayer.tools": "2.2.11", - "com.unity.netcode": "https://github.cds.internal.unity3d.com/unity/unity.git?path=Workspace/com.unity.netcode#netcode/remove-exp-define-ghost-object", + "com.unity.netcode": "https://github.cds.internal.unity3d.com/unity/unity.git?path=Workspace/com.unity.netcode#trunk", "com.unity.netcode.gameobjects": "file:../../com.unity.netcode.gameobjects", "com.unity.package-validation-suite": "0.49.0-preview", "com.unity.services.authentication": "3.7.4",