Skip to content

Commit

Permalink
feature: allow NetworkBehaviour components in children. solves #2276 (#…
Browse files Browse the repository at this point in the history
…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 <[email protected]>
  • Loading branch information
2 people authored and mischa committed May 27, 2023
1 parent 26e83ce commit 686efb5
Show file tree
Hide file tree
Showing 25 changed files with 662 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ public class NetworkLerpRigidbody : NetworkBehaviour

bool ClientWithAuthority => clientAuthority && isOwned;

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();
if (target == null)
target = GetComponent<Rigidbody>();
}
Expand Down
3 changes: 2 additions & 1 deletion Assets/Mirror/Components/Experimental/NetworkRigidbody.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ public class NetworkRigidbody : NetworkBehaviour
/// </summary>
readonly ClientSyncState previousValue = new ClientSyncState();

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();
if (target == null)
target = GetComponent<Rigidbody>();
}
Expand Down
3 changes: 2 additions & 1 deletion Assets/Mirror/Components/Experimental/NetworkRigidbody2D.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ public class NetworkRigidbody2D : NetworkBehaviour
/// </summary>
readonly ClientSyncState previousValue = new ClientSyncState();

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();
if (target == null)
target = GetComponent<Rigidbody2D>();
}
Expand Down
2 changes: 1 addition & 1 deletion Assets/Mirror/Components/NetworkAnimator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ namespace Mirror
/// <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>
/// <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>
/// </remarks>
// [RequireComponent(typeof(NetworkIdentity))] disabled to allow child NetworkBehaviours
[AddComponentMenu("Network/Network Animator")]
[RequireComponent(typeof(NetworkIdentity))]
[HelpURL("https://mirror-networking.gitbook.io/docs/components/network-animator")]
public class NetworkAnimator : NetworkBehaviour
{
Expand Down
13 changes: 8 additions & 5 deletions Assets/Mirror/Components/NetworkTransformBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace Mirror
public abstract class NetworkTransformBase : NetworkBehaviour
{
// target transform to sync. can be on a child.
// TODO this field is kind of unnecessary since we now support child NetworkBehaviours
[Header("Target")]
[Tooltip("The Transform component to sync. May be on on this GameObject, or on a child.")]
public Transform target;
Expand Down Expand Up @@ -67,17 +68,17 @@ public abstract class NetworkTransformBase : NetworkBehaviour
public bool timelineOffset = false;

// Ninja's Notes on offset & mulitplier:
//
//
// In a no multiplier scenario:
// 1. Snapshots are sent every frame (frame being 1 NM send interval).
// 2. Time Interpolation is set to be 'behind' by 2 frames times.
// 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.
//
//
// In a multiplier scenario:
// 1. Snapshots are sent every 10 frames.
// 2. Time Interpolation remains 'behind by 2 frames'.
// When everything works, we are receiving NT snapshots every 10 frames, but start interpolating after 2.
// 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.
// When everything works, we are receiving NT snapshots every 10 frames, but start interpolating after 2.
// 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.
//
protected double timeStampAdjustment => NetworkServer.sendInterval * (sendIntervalMultiplier - 1);
protected double offset => timelineOffset ? NetworkServer.sendInterval * sendIntervalMultiplier : 0;
Expand All @@ -92,8 +93,10 @@ public abstract class NetworkTransformBase : NetworkBehaviour
// make sure to call this when inheriting too!
protected virtual void Awake() { }

protected virtual void OnValidate()
protected override void OnValidate()
{
base.OnValidate();

// set target to self if none yet
if (target == null) target = transform;

Expand Down
3 changes: 2 additions & 1 deletion Assets/Mirror/Components/RemoteStatistics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,9 @@ void LoadPassword()
}
}

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();
syncMode = SyncMode.Owner;
}

Expand Down
23 changes: 22 additions & 1 deletion Assets/Mirror/Core/NetworkBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public enum SyncMode { Observers, Owner }
public enum SyncDirection { ServerToClient, ClientToServer }

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

protected virtual void OnValidate()
{
// we now allow child NetworkBehaviours.
// we can not [RequireComponent(typeof(NetworkIdentity))] anymore.
// instead, we need to ensure a NetworkIdentity is somewhere in the
// parents.
// only run this in Editor. don't add more runtime overhead.

#if UNITY_EDITOR
if (GetComponent<NetworkIdentity>() == null &&
#if UNITY_2020_3_OR_NEWER
GetComponentInParent<NetworkIdentity>(true) == null)
#else
GetComponentInParent<NetworkIdentity>() == null)
#endif
{
Debug.LogError($"{GetType()} on {name} requires a NetworkIdentity. Please add a NetworkIdentity component to {name} or it's parents.");
}
#endif
}

// pass full function name to avoid ClassA.Func <-> ClassB.Func collisions
protected void SendCommandInternal(string functionFullName, int functionHashCode, NetworkWriter writer, int channelId, bool requiresAuthority = true)
{
Expand Down
3 changes: 3 additions & 0 deletions Assets/Mirror/Core/NetworkClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,9 @@ static void RegisterPrefabIdentity(NetworkIdentity prefab)
return;
}

// disallow child NetworkIdentities.
// TODO likely not necessary anymore due to the new check in
// NetworkIdentity.OnValidate.
NetworkIdentity[] identities = prefab.GetComponentsInChildren<NetworkIdentity>();
if (identities.Length > 1)
{
Expand Down
27 changes: 24 additions & 3 deletions Assets/Mirror/Core/NetworkIdentity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,12 @@ internal static void ResetServerStatics()
// BUT internal so tests can add them after creating the NetworkIdentity
internal void InitializeNetworkBehaviours()
{
// Get all NetworkBehaviours
// (never null. GetComponents returns [] if none found)
NetworkBehaviours = GetComponents<NetworkBehaviour>();
// Get all NetworkBehaviour components, including children.
// Some users need NetworkTransform on child bones, etc.
// => Deterministic: https://forum.unity.com/threads/getcomponentsinchildren.4582/#post-33983
// => Never null. GetComponents returns [] if none found.
// => Include inactive. We need all child components.
NetworkBehaviours = GetComponentsInChildren<NetworkBehaviour>(true);
ValidateComponents();

// initialize each one
Expand Down Expand Up @@ -347,11 +350,29 @@ void OnValidate()
hasSpawned = false;

#if UNITY_EDITOR
DisallowChildNetworkIdentities();
SetupIDs();
#endif
}

#if UNITY_EDITOR
// child NetworkIdentities are not supported.
// Disallow them and show an error for the user to fix.
// This needs to work for Prefabs & Scene objects, so the previous check
// in NetworkClient.RegisterPrefab is not enough.
void DisallowChildNetworkIdentities()
{
#if UNITY_2020_3_OR_NEWER
NetworkIdentity[] identities = GetComponentsInChildren<NetworkIdentity>(true);
#else
NetworkIdentity[] identities = GetComponentsInChildren<NetworkIdentity>();
#endif
if (identities.Length > 1)
{
Debug.LogError($"'{name}' has {identities.Length} NetworkIdentity components. There should only be one NetworkIdentity, and it must be on the root object.");
}
}

void AssignAssetID(string path)
{
// only set if not empty. fixes https://github.com/vis2k/Mirror/issues/2765
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ public enum GroundState : byte { Jumping, Falling, Grounded }
public Vector3Int velocity;
public Vector3 direction;

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();

if (characterController == null)
characterController = GetComponent<CharacterController>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ public enum GroundState : byte { Jumping, Falling, Grounded }
public Vector3Int velocity;
public Vector3 direction;

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();

if (characterController == null)
characterController = GetComponent<CharacterController>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ public class PhysicsCollision : NetworkBehaviour

public Rigidbody rigidbody3D;

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();

if (rigidbody3D == null)
rigidbody3D = GetComponent<Rigidbody>();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ public enum GroundState : byte { Jumping, Falling, Grounded }
public Vector3Int velocity;
public Vector3 direction;

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();

if (characterController == null)
characterController = GetComponent<CharacterController>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ public class Reward : NetworkBehaviour
public bool available = true;
public RandomColor randomColor;

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();

if (randomColor == null)
randomColor = GetComponent<RandomColor>();
}
Expand Down
4 changes: 3 additions & 1 deletion Assets/Mirror/Examples/RigidbodyPhysics/Scripts/AddForce.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ public class AddForce : NetworkBehaviour
public Rigidbody rigidbody3d;
public float force = 500f;

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();

rigidbody3d = GetComponent<Rigidbody>();
rigidbody3d.isKinematic = true;
}
Expand Down
4 changes: 3 additions & 1 deletion Assets/Mirror/Examples/Room/Scripts/PlayerController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ public enum GroundState : byte { Jumping, Falling, Grounded }
public Vector3Int velocity;
public Vector3 direction;

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();

if (characterController == null)
characterController = GetComponent<CharacterController>();

Expand Down
4 changes: 3 additions & 1 deletion Assets/Mirror/Examples/Room/Scripts/Reward.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ public class Reward : NetworkBehaviour
public bool available = true;
public RandomColor randomColor;

void OnValidate()
protected override void OnValidate()
{
base.OnValidate();

if (randomColor == null)
randomColor = GetComponent<RandomColor>();
}
Expand Down
82 changes: 45 additions & 37 deletions Assets/Mirror/Examples/Tanks/Prefabs/Tank.prefab
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ GameObject:
- component: {fileID: 4492442352427800}
- component: {fileID: 114118589361100106}
- component: {fileID: 114250499875391520}
- component: {fileID: 7144377311613369288}
- component: {fileID: 114654712548978148}
- component: {fileID: 2240606817507776182}
- component: {fileID: 6900008319038825817}
Expand Down Expand Up @@ -90,39 +89,6 @@ MonoBehaviour:
positionSensitivity: 0.01
rotationSensitivity: 0.01
scaleSensitivity: 0.01
--- !u!114 &7144377311613369288
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1916082411674582}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2f74aedd71d9a4f55b3ce499326d45fb, type: 3}
m_Name:
m_EditorClassIdentifier:
syncDirection: 1
syncMode: 0
syncInterval: 0
target: {fileID: 7831918942946891958}
clientAuthority: 0
syncPosition: 0
syncRotation: 1
syncScale: 0
interpolatePosition: 0
interpolateRotation: 1
interpolateScale: 0
sendIntervalMultiplier: 3
timelineOffset: 1
showGizmos: 0
showOverlay: 0
overlayColor: {r: 0, g: 0, b: 0, a: 0.5}
onlySyncOnChange: 1
bufferResetMultiplier: 5
positionSensitivity: 0.01
rotationSensitivity: 0.01
scaleSensitivity: 0.01
--- !u!114 &114654712548978148
MonoBehaviour:
m_ObjectHideFlags: 0
Expand All @@ -144,7 +110,8 @@ MonoBehaviour:
turret: {fileID: 7831918942946891958}
rotationSpeed: 80
shootKey: 32
projectilePrefab: {fileID: 5890560936853567077, guid: b7dd46dbf38c643f09e206f9fa4be008, type: 3}
projectilePrefab: {fileID: 5890560936853567077, guid: b7dd46dbf38c643f09e206f9fa4be008,
type: 3}
projectileMount: {fileID: 5718089106632469514}
health: 4
--- !u!95 &2240606817507776182
Expand Down Expand Up @@ -408,11 +375,52 @@ PrefabInstance:
m_SourcePrefab: {fileID: 100100000, guid: 38b49695fc0a4418bbc350f2366660c5, type: 3}
--- !u!4 &7831918942946891954 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 400010, guid: 38b49695fc0a4418bbc350f2366660c5, type: 3}
m_CorrespondingSourceObject: {fileID: 400010, guid: 38b49695fc0a4418bbc350f2366660c5,
type: 3}
m_PrefabInstance: {fileID: 7831918942947279416}
m_PrefabAsset: {fileID: 0}
--- !u!4 &7831918942946891958 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 400014, guid: 38b49695fc0a4418bbc350f2366660c5, type: 3}
m_CorrespondingSourceObject: {fileID: 400014, guid: 38b49695fc0a4418bbc350f2366660c5,
type: 3}
m_PrefabInstance: {fileID: 7831918942947279416}
m_PrefabAsset: {fileID: 0}
--- !u!1 &7831918942947312790 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 100014, guid: 38b49695fc0a4418bbc350f2366660c5,
type: 3}
m_PrefabInstance: {fileID: 7831918942947279416}
m_PrefabAsset: {fileID: 0}
--- !u!114 &9196118806080746389
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7831918942947312790}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2f74aedd71d9a4f55b3ce499326d45fb, type: 3}
m_Name:
m_EditorClassIdentifier:
syncDirection: 1
syncMode: 0
syncInterval: 0
target: {fileID: 7831918942946891958}
clientAuthority: 0
syncPosition: 0
syncRotation: 1
syncScale: 0
interpolatePosition: 0
interpolateRotation: 1
interpolateScale: 0
sendIntervalMultiplier: 3
timelineOffset: 1
showGizmos: 0
showOverlay: 0
overlayColor: {r: 0, g: 0, b: 0, a: 0.5}
onlySyncOnChange: 1
bufferResetMultiplier: 5
positionSensitivity: 0.01
rotationSensitivity: 0.01
scaleSensitivity: 0.01
Loading

0 comments on commit 686efb5

Please sign in to comment.