Skip to content

Commit 686efb5

Browse files
miwarnecMrGadget1024
authored andcommitted
feature: allow NetworkBehaviour components in children. solves #2276 (#3486)
* feature: allow child NetworkBehaviour components in children * Tanks demo: move Turret NetworkTransform to child * NT TODO * RequireInParents NI * NetworkAnimatore allow in children * call base onvalidate * Add OnValidate to Script Template * fix for pre-2020.3 Unity * MirrorTest: CreateNetworked with NetworkBehaviour in children * Tests: child networkbehaviours --------- Co-authored-by: MrGadget1024 <9826063+MrGadget1024@users.noreply.github.com>
1 parent 26e83ce commit 686efb5

25 files changed

+662
-60
lines changed

Assets/Mirror/Components/Experimental/NetworkLerpRigidbody.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ public class NetworkLerpRigidbody : NetworkBehaviour
3333

3434
bool ClientWithAuthority => clientAuthority && isOwned;
3535

36-
void OnValidate()
36+
protected override void OnValidate()
3737
{
38+
base.OnValidate();
3839
if (target == null)
3940
target = GetComponent<Rigidbody>();
4041
}

Assets/Mirror/Components/Experimental/NetworkRigidbody.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ public class NetworkRigidbody : NetworkBehaviour
3737
/// </summary>
3838
readonly ClientSyncState previousValue = new ClientSyncState();
3939

40-
void OnValidate()
40+
protected override void OnValidate()
4141
{
42+
base.OnValidate();
4243
if (target == null)
4344
target = GetComponent<Rigidbody>();
4445
}

Assets/Mirror/Components/Experimental/NetworkRigidbody2D.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ public class NetworkRigidbody2D : NetworkBehaviour
3737
/// </summary>
3838
readonly ClientSyncState previousValue = new ClientSyncState();
3939

40-
void OnValidate()
40+
protected override void OnValidate()
4141
{
42+
base.OnValidate();
4243
if (target == null)
4344
target = GetComponent<Rigidbody2D>();
4445
}

Assets/Mirror/Components/NetworkAnimator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ namespace Mirror
1313
/// <para>If the object has authority on the server, then it should be animated on the server and state information will be sent to all clients. This is common for objects not related to a specific client, such as an enemy unit.</para>
1414
/// <para>The NetworkAnimator synchronizes all animation parameters of the selected Animator. It does not automatically synchronize triggers. The function SetTrigger can by used by an object with authority to fire an animation trigger on other clients.</para>
1515
/// </remarks>
16+
// [RequireComponent(typeof(NetworkIdentity))] disabled to allow child NetworkBehaviours
1617
[AddComponentMenu("Network/Network Animator")]
17-
[RequireComponent(typeof(NetworkIdentity))]
1818
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-animator")]
1919
public class NetworkAnimator : NetworkBehaviour
2020
{

Assets/Mirror/Components/NetworkTransformBase.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace Mirror
2525
public abstract class NetworkTransformBase : NetworkBehaviour
2626
{
2727
// target transform to sync. can be on a child.
28+
// TODO this field is kind of unnecessary since we now support child NetworkBehaviours
2829
[Header("Target")]
2930
[Tooltip("The Transform component to sync. May be on on this GameObject, or on a child.")]
3031
public Transform target;
@@ -67,17 +68,17 @@ public abstract class NetworkTransformBase : NetworkBehaviour
6768
public bool timelineOffset = false;
6869

6970
// Ninja's Notes on offset & mulitplier:
70-
//
71+
//
7172
// In a no multiplier scenario:
7273
// 1. Snapshots are sent every frame (frame being 1 NM send interval).
7374
// 2. Time Interpolation is set to be 'behind' by 2 frames times.
7475
// In theory where everything works, we probably have around 2 snapshots before we need to interpolate snapshots. From NT perspective, we should always have around 2 snapshots ready, so no stutter.
75-
//
76+
//
7677
// In a multiplier scenario:
7778
// 1. Snapshots are sent every 10 frames.
7879
// 2. Time Interpolation remains 'behind by 2 frames'.
79-
// When everything works, we are receiving NT snapshots every 10 frames, but start interpolating after 2.
80-
// Even if I assume we had 2 snapshots to begin with to start interpolating (which we don't), by the time we reach 13th frame, we are out of snapshots, and have to wait 7 frames for next snapshot to come. This is the reason why we absolutely need the timestamp adjustment. We are starting way too early to interpolate.
80+
// When everything works, we are receiving NT snapshots every 10 frames, but start interpolating after 2.
81+
// Even if I assume we had 2 snapshots to begin with to start interpolating (which we don't), by the time we reach 13th frame, we are out of snapshots, and have to wait 7 frames for next snapshot to come. This is the reason why we absolutely need the timestamp adjustment. We are starting way too early to interpolate.
8182
//
8283
protected double timeStampAdjustment => NetworkServer.sendInterval * (sendIntervalMultiplier - 1);
8384
protected double offset => timelineOffset ? NetworkServer.sendInterval * sendIntervalMultiplier : 0;
@@ -92,8 +93,10 @@ public abstract class NetworkTransformBase : NetworkBehaviour
9293
// make sure to call this when inheriting too!
9394
protected virtual void Awake() { }
9495

95-
protected virtual void OnValidate()
96+
protected override void OnValidate()
9697
{
98+
base.OnValidate();
99+
97100
// set target to self if none yet
98101
if (target == null) target = transform;
99102

Assets/Mirror/Components/RemoteStatistics.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,9 @@ void LoadPassword()
133133
}
134134
}
135135

136-
void OnValidate()
136+
protected override void OnValidate()
137137
{
138+
base.OnValidate();
138139
syncMode = SyncMode.Owner;
139140
}
140141

Assets/Mirror/Core/NetworkBehaviour.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ public enum SyncMode { Observers, Owner }
1919
public enum SyncDirection { ServerToClient, ClientToServer }
2020

2121
/// <summary>Base class for networked components.</summary>
22+
// [RequireComponent(typeof(NetworkIdentity))] disabled to allow child NetworkBehaviours
2223
[AddComponentMenu("")]
23-
[RequireComponent(typeof(NetworkIdentity))]
2424
[HelpURL("https://mirror-networking.gitbook.io/docs/guides/networkbehaviour")]
2525
public abstract class NetworkBehaviour : MonoBehaviour
2626
{
@@ -293,6 +293,27 @@ protected void InitSyncObject(SyncObject syncObject)
293293
};
294294
}
295295

296+
protected virtual void OnValidate()
297+
{
298+
// we now allow child NetworkBehaviours.
299+
// we can not [RequireComponent(typeof(NetworkIdentity))] anymore.
300+
// instead, we need to ensure a NetworkIdentity is somewhere in the
301+
// parents.
302+
// only run this in Editor. don't add more runtime overhead.
303+
304+
#if UNITY_EDITOR
305+
if (GetComponent<NetworkIdentity>() == null &&
306+
#if UNITY_2020_3_OR_NEWER
307+
GetComponentInParent<NetworkIdentity>(true) == null)
308+
#else
309+
GetComponentInParent<NetworkIdentity>() == null)
310+
#endif
311+
{
312+
Debug.LogError($"{GetType()} on {name} requires a NetworkIdentity. Please add a NetworkIdentity component to {name} or it's parents.");
313+
}
314+
#endif
315+
}
316+
296317
// pass full function name to avoid ClassA.Func <-> ClassB.Func collisions
297318
protected void SendCommandInternal(string functionFullName, int functionHashCode, NetworkWriter writer, int channelId, bool requiresAuthority = true)
298319
{

Assets/Mirror/Core/NetworkClient.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,9 @@ static void RegisterPrefabIdentity(NetworkIdentity prefab)
534534
return;
535535
}
536536

537+
// disallow child NetworkIdentities.
538+
// TODO likely not necessary anymore due to the new check in
539+
// NetworkIdentity.OnValidate.
537540
NetworkIdentity[] identities = prefab.GetComponentsInChildren<NetworkIdentity>();
538541
if (identities.Length > 1)
539542
{

Assets/Mirror/Core/NetworkIdentity.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,12 @@ internal static void ResetServerStatics()
292292
// BUT internal so tests can add them after creating the NetworkIdentity
293293
internal void InitializeNetworkBehaviours()
294294
{
295-
// Get all NetworkBehaviours
296-
// (never null. GetComponents returns [] if none found)
297-
NetworkBehaviours = GetComponents<NetworkBehaviour>();
295+
// Get all NetworkBehaviour components, including children.
296+
// Some users need NetworkTransform on child bones, etc.
297+
// => Deterministic: https://forum.unity.com/threads/getcomponentsinchildren.4582/#post-33983
298+
// => Never null. GetComponents returns [] if none found.
299+
// => Include inactive. We need all child components.
300+
NetworkBehaviours = GetComponentsInChildren<NetworkBehaviour>(true);
298301
ValidateComponents();
299302

300303
// initialize each one
@@ -347,11 +350,29 @@ void OnValidate()
347350
hasSpawned = false;
348351

349352
#if UNITY_EDITOR
353+
DisallowChildNetworkIdentities();
350354
SetupIDs();
351355
#endif
352356
}
353357

354358
#if UNITY_EDITOR
359+
// child NetworkIdentities are not supported.
360+
// Disallow them and show an error for the user to fix.
361+
// This needs to work for Prefabs & Scene objects, so the previous check
362+
// in NetworkClient.RegisterPrefab is not enough.
363+
void DisallowChildNetworkIdentities()
364+
{
365+
#if UNITY_2020_3_OR_NEWER
366+
NetworkIdentity[] identities = GetComponentsInChildren<NetworkIdentity>(true);
367+
#else
368+
NetworkIdentity[] identities = GetComponentsInChildren<NetworkIdentity>();
369+
#endif
370+
if (identities.Length > 1)
371+
{
372+
Debug.LogError($"'{name}' has {identities.Length} NetworkIdentity components. There should only be one NetworkIdentity, and it must be on the root object.");
373+
}
374+
}
375+
355376
void AssignAssetID(string path)
356377
{
357378
// only set if not empty. fixes https://github.com/vis2k/Mirror/issues/2765

Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerController.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ public enum GroundState : byte { Jumping, Falling, Grounded }
5454
public Vector3Int velocity;
5555
public Vector3 direction;
5656

57-
void OnValidate()
57+
protected override void OnValidate()
5858
{
59+
base.OnValidate();
60+
5961
if (characterController == null)
6062
characterController = GetComponent<CharacterController>();
6163

0 commit comments

Comments
 (0)