Skip to content

Commit ea4f91f

Browse files
feat: NetworkTransform synchronization mode is configured on NetworkConfig
The project wide setting reached NetworkConfig through an IProcessSceneWithReport, which cannot reach a scene placed NetworkManager in play mode. NetworkManager.OnEnable moves itself into the DontDestroyOnLoad scene, and it does so before the callback runs, so the scene the callback is handed no longer contains the object it is looking for. It applied to zero NetworkManagers every time and nothing reported that, because the setting it read was correct and only the write was missing. A build was unaffected, since there the callback runs at build time against the authored scene. Its sibling SetInScenePlaced survives the same callback only because NetworkObject sets InScenePlaced at runtime as a fallback, which is why this is the first place the callback's limit was visible. TransformSyncMode is now public on NetworkConfig and authored per NetworkManager under "Transform Synchronization" in the inspector. It is serialized by the same mechanism as every other NetworkConfig value, so scenes, prefabs and builds all carry it with no build pipeline involvement. That also removes the prefab processor, which ran on import and therefore baked whatever the setting happened to be the last time a prefab was imported. Being public also lets a project offer the mode as a pre session choice instead of an editor round trip. NetworkManager captures the value into ActiveTransformSyncMode when it starts and everything on the send and receive paths reads that, so a write during a session cannot leave already spawned instances registered under one mode while new ones use the other, and cannot move the connection configuration hash out from under a client that is still joining. NetworkTransform's inspector no longer hides UseUnreliableDeltas under the batched mode. The mode belongs to the NetworkManager that will run the instance, which a prefab cannot know, and the runtime already ignores the value while batching. NetworkTransformSyncModeConfigurationTests covers the delivery that nothing covered before: every other fixture reaches the mode through the same assignment the integration test harness makes, so a delivery path that never ran left them all green.
1 parent c884fed commit ea4f91f

12 files changed

Lines changed: 219 additions & 145 deletions

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
1010

1111
### Added
1212

13+
- Added `NetworkConfig.TransformSyncMode`, which determines whether `NetworkTransform` instances detect and synchronize their state individually or as a single batched message per tick. It is set per `NetworkManager` under "Transform Synchronization" in the inspector or from script before a session starts, and the value the session starts with applies for the duration of that session. Every peer in a session has to use the same mode, which the connection configuration hash enforces. (#4123)
14+
1315
### Changed
1416

1517
- Changed `NetworkTransform.UseHalfFloatPrecision` to synchronize position with a resolution of approximately 1mm regardless of how far an object has travelled. Previously the resolution could degrade to approximately 3cm. This does not increase bandwidth, but projects using `NetworkTransform.UseUnreliableDeltas` will send full precision position updates more often. (#4129)

com.unity.netcode.gameobjects/Editor/Configuration/NetcodeForGameObjectsProjectSettings.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,6 @@ private void OnEnable()
3737
[SerializeField]
3838
public bool GenerateDefaultNetworkPrefabs = true;
3939

40-
/// <summary>
41-
/// The project wide <see cref="TransformSyncModes"/> that is applied to <see cref="NetworkConfig.TransformSyncMode"/>.
42-
/// </summary>
43-
/// <remarks>
44-
/// The two modes are not wire compatible with one another, so this is authored once for the project as
45-
/// opposed to per <see cref="NetworkManager"/>.
46-
/// </remarks>
47-
[SerializeField]
48-
public TransformSyncModes TransformSyncMode = TransformSyncModes.PerInstance;
49-
5040
internal void SaveSettings()
5141
{
5242
Save(true);

com.unity.netcode.gameobjects/Editor/Configuration/NetcodeSettingsProvider.cs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System.Collections.Generic;
22
using System.IO;
3-
using Unity.Netcode.Components;
43
using UnityEditor;
54
using UnityEngine;
65
using Directory = UnityEngine.Windows.Directory;
@@ -133,7 +132,6 @@ private static void OnGuiHandler(string obj)
133132
var settings = NetcodeForGameObjectsProjectSettings.instance;
134133
var generateDefaultPrefabs = settings.GenerateDefaultNetworkPrefabs;
135134
var networkPrefabsPath = settings.TempNetworkPrefabsPath;
136-
var transformSyncMode = settings.TransformSyncMode;
137135

138136
EditorGUI.BeginChangeCheck();
139137

@@ -194,26 +192,6 @@ private static void OnGuiHandler(string obj)
194192
networkPrefabsPath,
195193
GUILayout.Width(s_MaxLabelWidth + 270));
196194
GUILayout.EndVertical();
197-
198-
GUILayout.BeginVertical("Box");
199-
GUILayout.Label("NetworkTransform Synchronization", EditorStyles.boldLabel);
200-
transformSyncMode = (TransformSyncModes)EditorGUILayout.EnumPopup(
201-
new GUIContent(
202-
"Synchronization Mode",
203-
"Determines how NetworkTransform instances detect and synchronize their state. " +
204-
"Batched mode detects changes for all instances within a job and sends them as a single message per tick. " +
205-
"This is a global setting for all NetworkTransforms since the two modes are not compatible on a per instance basis."),
206-
transformSyncMode,
207-
GUILayout.Width(s_MaxLabelWidth + 120));
208-
209-
if (transformSyncMode == TransformSyncModes.Batched)
210-
{
211-
EditorGUILayout.HelpBox(
212-
$"{nameof(NetworkTransform.UseUnreliableDeltas)} does not apply in this mode and will no longer be visible when viewing " +
213-
"NetworkTransform in the inspector view. Delivery is determined per state update as opposed to per component.",
214-
MessageType.Info);
215-
}
216-
GUILayout.EndVertical();
217195
}
218196
EditorGUILayout.EndFoldoutHeaderGroup();
219197
GUILayout.EndVertical();
@@ -224,7 +202,6 @@ private static void OnGuiHandler(string obj)
224202
NetcodeForGameObjectsEditorSettings.SetNetcodeInstallMultiplayerToolTips(multiplayerToolsTipStatus ? 0 : 1);
225203
settings.GenerateDefaultNetworkPrefabs = generateDefaultPrefabs;
226204
settings.TempNetworkPrefabsPath = networkPrefabsPath;
227-
settings.TransformSyncMode = transformSyncMode;
228205
settings.SaveSettings();
229206
}
230207
}

com.unity.netcode.gameobjects/Editor/Configuration/TransformSyncModeProcessor.cs

Lines changed: 0 additions & 65 deletions
This file was deleted.

com.unity.netcode.gameobjects/Editor/Configuration/TransformSyncModeProcessor.cs.meta

Lines changed: 0 additions & 2 deletions
This file was deleted.

com.unity.netcode.gameobjects/Editor/NetworkManagerEditor.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5+
using Unity.Netcode.Components;
56
using Unity.Netcode.GameObjects.Editor.Configuration;
67
using Unity.Netcode.Logging;
78
using UnityEditor;
@@ -51,6 +52,7 @@ public class NetworkManagerEditor : NetcodeEditorBase<NetworkManager>
5152
private SerializedProperty m_SpawnTimeOutProperty;
5253
private SerializedProperty m_RpcHashSizeProperty;
5354
private SerializedProperty m_LoadSceneTimeOutProperty;
55+
private SerializedProperty m_TransformSyncModeProperty;
5456
private SerializedProperty m_PrefabsList;
5557

5658
private SerializedProperty m_NetworkProfileMetrics;
@@ -138,6 +140,7 @@ private void Initialize()
138140
m_NetworkMessageMetrics = m_NetworkConfigProperty.FindPropertyRelative("NetworkMessageMetrics");
139141
#endif
140142
m_RpcHashSizeProperty = m_NetworkConfigProperty.FindPropertyRelative("RpcHashSize");
143+
m_TransformSyncModeProperty = m_NetworkConfigProperty.FindPropertyRelative(nameof(NetworkConfig.TransformSyncMode));
141144
m_PrefabsList = m_NetworkConfigProperty
142145
.FindPropertyRelative(nameof(NetworkConfig.Prefabs))
143146
.FindPropertyRelative(nameof(NetworkPrefabs.NetworkPrefabsLists));
@@ -183,6 +186,7 @@ private void CheckNullProperties()
183186
#endif
184187

185188
m_RpcHashSizeProperty = m_NetworkConfigProperty.FindPropertyRelative("RpcHashSize");
189+
m_TransformSyncModeProperty = m_NetworkConfigProperty.FindPropertyRelative(nameof(NetworkConfig.TransformSyncMode));
186190
m_PrefabsList = m_NetworkConfigProperty
187191
.FindPropertyRelative(nameof(NetworkConfig.Prefabs))
188192
.FindPropertyRelative(nameof(NetworkPrefabs.NetworkPrefabsLists));
@@ -319,6 +323,17 @@ private void DisplayNetworkManagerProperties()
319323
EditorGUILayout.PropertyField(m_PrefabsList);
320324
}
321325

326+
EditorGUILayout.Space();
327+
EditorGUILayout.LabelField("Transform Synchronization", EditorStyles.boldLabel);
328+
EditorGUILayout.PropertyField(m_TransformSyncModeProperty, new GUIContent("Mode"));
329+
if (m_NetworkManager.NetworkConfig.TransformSyncMode == TransformSyncModes.Batched)
330+
{
331+
EditorGUILayout.HelpBox(
332+
$"{nameof(NetworkTransform.UseUnreliableDeltas)} does not apply in this mode. Delivery is determined per state " +
333+
"update as opposed to per component. Every peer in a session has to use the same mode.",
334+
MessageType.Info);
335+
}
336+
322337
EditorGUILayout.Space();
323338
EditorGUILayout.LabelField("Scene Management Settings", EditorStyles.boldLabel);
324339
EditorGUILayout.PropertyField(m_EnableSceneManagementProperty);

com.unity.netcode.gameobjects/Editor/NetworkTransformEditor.cs

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System.Runtime.CompilerServices;
22
using Unity.Netcode.Components;
3-
using Unity.Netcode.GameObjects.Editor.Configuration;
43
using UnityEditor;
54
using UnityEngine;
65
using UnityEngine.Scripting.APIUpdating;
@@ -220,41 +219,37 @@ private void DisplayNetworkTransformProperties()
220219
EditorGUILayout.LabelField("Delivery", EditorStyles.boldLabel);
221220
EditorGUILayout.PropertyField(m_TickSyncChildren);
222221

223-
// UseUnreliableDeltas only applies to per instance synchronization mode. Under the batched mode
224-
// delivery is determined per state update as opposed to per component, so the property (and
225-
// everything it constrains) is hidden. See Project Settings -> Multiplayer -> Netcode for GameObjects.
226-
var perInstanceSync = NetcodeForGameObjectsProjectSettings.instance.TransformSyncMode == TransformSyncModes.PerInstance;
227-
if (perInstanceSync)
222+
// UseUnreliableDeltas only applies to per instance synchronization mode, but the mode is authored on
223+
// the NetworkManager that will run this instance, which a prefab cannot know. So it is always drawn
224+
// and the runtime ignores it under the batched mode, where delivery is determined per state update
225+
// as opposed to per component.
226+
// If both are set from a previous configuration, then SwitchTransformSpaceWhenParented takes
227+
// precedence.
228+
if (networkTransform.UseUnreliableDeltas && networkTransform.SwitchTransformSpaceWhenParented)
228229
{
229-
// If both are set from a previous configuration, then SwitchTransformSpaceWhenParented takes
230-
// precedence.
231-
if (networkTransform.UseUnreliableDeltas && networkTransform.SwitchTransformSpaceWhenParented)
232-
{
233-
networkTransform.UseUnreliableDeltas = false;
234-
}
235-
SetGUIActive(!networkTransform.SwitchTransformSpaceWhenParented);
236-
if (networkTransform.SwitchTransformSpaceWhenParented)
237-
{
238-
EditorGUILayout.BeginHorizontal();
239-
EditorGUILayout.PropertyField(m_UseUnreliableDeltas);
240-
EditorGUILayout.LabelField($"Cannot use with {nameof(NetworkTransform.SwitchTransformSpaceWhenParented)}.");
241-
EditorGUILayout.EndHorizontal();
242-
}
243-
else
244-
{
245-
EditorGUILayout.PropertyField(m_UseUnreliableDeltas);
246-
}
247-
248-
SetGUIActive(true);
230+
networkTransform.UseUnreliableDeltas = false;
231+
}
232+
SetGUIActive(!networkTransform.SwitchTransformSpaceWhenParented);
233+
if (networkTransform.SwitchTransformSpaceWhenParented)
234+
{
235+
EditorGUILayout.BeginHorizontal();
236+
EditorGUILayout.PropertyField(m_UseUnreliableDeltas);
237+
EditorGUILayout.LabelField($"Cannot use with {nameof(NetworkTransform.SwitchTransformSpaceWhenParented)}.");
238+
EditorGUILayout.EndHorizontal();
239+
}
240+
else
241+
{
242+
EditorGUILayout.PropertyField(m_UseUnreliableDeltas);
249243
}
250244

245+
SetGUIActive(true);
246+
251247
EditorGUILayout.Space();
252248
EditorGUILayout.LabelField("Configurations", EditorStyles.boldLabel);
253249

254250
// SwitchTransformSpaceWhenParented is only constrained by UseUnreliableDeltas while the latter applies.
255-
var blockedByUnreliableDeltas = perInstanceSync && networkTransform.UseUnreliableDeltas;
256-
SetGUIActive(!blockedByUnreliableDeltas);
257-
if (blockedByUnreliableDeltas)
251+
SetGUIActive(!networkTransform.UseUnreliableDeltas);
252+
if (networkTransform.UseUnreliableDeltas)
258253
{
259254
EditorGUILayout.BeginHorizontal();
260255
EditorGUILayout.PropertyField(m_SwitchTransformSpaceWhenParented);
@@ -269,10 +264,7 @@ private void DisplayNetworkTransformProperties()
269264
if (m_SwitchTransformSpaceWhenParented.boolValue)
270265
{
271266
m_TickSyncChildren.boolValue = true;
272-
if (perInstanceSync)
273-
{
274-
networkTransform.UseUnreliableDeltas = false;
275-
}
267+
networkTransform.UseUnreliableDeltas = false;
276268
}
277269
else
278270
{

com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1971,7 +1971,7 @@ protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
19711971
// This is the best place to define the handle since it is the first thing that reaches
19721972
// every receiver and is only ever invoked once for the entire duration of the objects spawn
19731973
// life cycle.
1974-
if (NetworkManager.NetworkConfig.TransformSyncMode == TransformSyncModes.Batched)
1974+
if (NetworkManager.NetworkConfig.ActiveTransformSyncMode == TransformSyncModes.Batched)
19751975
{
19761976
if (serializer.IsWriter && TransformHandle == TransformHandleAllocator.InvalidHandle)
19771977
{
@@ -2403,7 +2403,7 @@ private TransformDeltaConfig GetTransformDeltaConfig()
24032403
// unreliable deltas to compensate for. Forcing this off also retires the axial frame
24042404
// synchronization: that exists solely to re-send a full set of axes once a second in case an
24052405
// unreliable delta was lost, which cannot happen here.
2406-
UseUnreliableDeltas = UseUnreliableDeltas && m_CachedNetworkManager.NetworkConfig.TransformSyncMode != TransformSyncModes.Batched,
2406+
UseUnreliableDeltas = UseUnreliableDeltas && m_CachedNetworkManager.NetworkConfig.ActiveTransformSyncMode != TransformSyncModes.Batched,
24072407
SwitchTransformSpaceWhenParented = SwitchTransformSpaceWhenParented,
24082408
#if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D
24092409
UseRigidbodyForMotion = m_UseRigidbodyForMotion,
@@ -3883,7 +3883,7 @@ private void ReleaseTransformHandle()
38833883
/// </summary>
38843884
private void RegisterForBatchedInterpolation()
38853885
{
3886-
if (m_CachedNetworkManager == null || m_CachedNetworkManager.NetworkConfig.TransformSyncMode != TransformSyncModes.Batched)
3886+
if (m_CachedNetworkManager == null || m_CachedNetworkManager.NetworkConfig.ActiveTransformSyncMode != TransformSyncModes.Batched)
38873887
{
38883888
return;
38893889
}
@@ -3916,7 +3916,7 @@ private void DeregisterFromBatchedInterpolation()
39163916

39173917
private void RegisterForBatchedStateTracking()
39183918
{
3919-
if (m_CachedNetworkManager == null || m_CachedNetworkManager.NetworkConfig.TransformSyncMode != TransformSyncModes.Batched)
3919+
if (m_CachedNetworkManager == null || m_CachedNetworkManager.NetworkConfig.ActiveTransformSyncMode != TransformSyncModes.Batched)
39203920
{
39213921
return;
39223922
}
@@ -5163,7 +5163,7 @@ internal void TickUpdate()
51635163
}
51645164

51655165
//
5166-
if (m_NetworkManager.NetworkConfig.TransformSyncMode == TransformSyncModes.Batched)
5166+
if (m_NetworkManager.NetworkConfig.ActiveTransformSyncMode == TransformSyncModes.Batched)
51675167
{
51685168
// Batched: every registered instance is checked in parallel and anything that comes back
51695169
// dirty will add its state update to the outbound batch.

0 commit comments

Comments
 (0)