From 51191b0dce4b96bb9d1f5525bfd258403a58141e Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Mon, 16 Aug 2021 16:24:01 +0100 Subject: [PATCH 1/5] feat: networktransform pos/rot/sca thresholds on state sync --- .../Prototyping/NetworkTransform.cs | 155 ++++++---- .../NetworkTransformStateTests.cs | 284 +++++++++++++++--- 2 files changed, 341 insertions(+), 98 deletions(-) diff --git a/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs b/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs index 3a559e62ef..ffa57eb672 100644 --- a/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs @@ -166,6 +166,8 @@ public void NetworkSerialize(NetworkSerializer serializer) public bool SyncRotAngleX = true, SyncRotAngleY = true, SyncRotAngleZ = true; public bool SyncScaleX = true, SyncScaleY = true, SyncScaleZ = true; + public float PositionThreshold, RotAngleThreshold, ScaleThreshold; + /// /// The base amount of sends per seconds to use when range is disabled /// @@ -184,63 +186,102 @@ public void NetworkSerialize(NetworkSerializer serializer) Authority == NetworkAuthority.Server && IsServer || Authority == NetworkAuthority.Shared; - internal bool IsNetworkStateDirty(NetworkState networkState) + internal bool UpdateNetworkState(NetworkState networkState) { - if (networkState == null) - { - return false; - } - var position = InLocalSpace ? m_Transform.localPosition : m_Transform.position; var rotAngles = InLocalSpace ? m_Transform.localEulerAngles : m_Transform.eulerAngles; var scale = InLocalSpace ? m_Transform.localScale : m_Transform.lossyScale; - return - // InLocalSpace Check - (InLocalSpace != networkState.InLocalSpace) || - // Position Check - (SyncPositionX && !Mathf.Approximately(position.x, networkState.PositionX)) || - (SyncPositionY && !Mathf.Approximately(position.y, networkState.PositionY)) || - (SyncPositionZ && !Mathf.Approximately(position.z, networkState.PositionZ)) || - // RotAngles Check - (SyncRotAngleX && !Mathf.Approximately(rotAngles.x, networkState.RotAngleX)) || - (SyncRotAngleY && !Mathf.Approximately(rotAngles.y, networkState.RotAngleY)) || - (SyncRotAngleZ && !Mathf.Approximately(rotAngles.z, networkState.RotAngleZ)) || - // Scale Check - (SyncScaleX && !Mathf.Approximately(scale.x, networkState.ScaleX)) || - (SyncScaleY && !Mathf.Approximately(scale.y, networkState.ScaleY)) || - (SyncScaleZ && !Mathf.Approximately(scale.z, networkState.ScaleZ)); - } + bool isDirty = false; - internal void UpdateNetworkState() - { - var position = InLocalSpace ? m_Transform.localPosition : m_Transform.position; - var rotAngles = InLocalSpace ? m_Transform.localEulerAngles : m_Transform.eulerAngles; - var scale = InLocalSpace ? m_Transform.localScale : m_Transform.lossyScale; + if (InLocalSpace != networkState.InLocalSpace) + { + networkState.InLocalSpace = InLocalSpace; + isDirty |= true; + } - // InLocalSpace Bit - ReplNetworkState.Value.InLocalSpace = InLocalSpace; - // Position Bits - (ReplNetworkState.Value.HasPositionX, ReplNetworkState.Value.HasPositionY, ReplNetworkState.Value.HasPositionZ) = - (SyncPositionX, SyncPositionY, SyncPositionZ); - // RotAngle Bits - (ReplNetworkState.Value.HasRotAngleX, ReplNetworkState.Value.HasRotAngleY, ReplNetworkState.Value.HasRotAngleZ) = - (SyncRotAngleX, SyncRotAngleY, SyncRotAngleZ); - // Scale Bits - (ReplNetworkState.Value.HasScaleX, ReplNetworkState.Value.HasScaleY, ReplNetworkState.Value.HasScaleZ) = - (SyncScaleX, SyncScaleY, SyncScaleZ); - - // Position Values - (ReplNetworkState.Value.PositionX, ReplNetworkState.Value.PositionY, ReplNetworkState.Value.PositionZ) = - (position.x, position.y, position.z); - // RotAngle Values - (ReplNetworkState.Value.RotAngleX, ReplNetworkState.Value.RotAngleY, ReplNetworkState.Value.RotAngleZ) = - (rotAngles.x, rotAngles.y, rotAngles.z); - // Scale Values - (ReplNetworkState.Value.ScaleX, ReplNetworkState.Value.ScaleY, ReplNetworkState.Value.ScaleZ) = - (scale.x, scale.y, scale.z); - - ReplNetworkState.SetDirty(true); + if (SyncPositionX && + Mathf.Abs(networkState.PositionX - position.x) >= PositionThreshold && + !Mathf.Approximately(networkState.PositionX, position.x)) + { + networkState.PositionX = position.x; + networkState.HasPositionX = true; + isDirty |= true; + } + + if (SyncPositionY && + Mathf.Abs(networkState.PositionY - position.y) >= PositionThreshold && + !Mathf.Approximately(networkState.PositionY, position.y)) + { + networkState.PositionY = position.y; + networkState.HasPositionY = true; + isDirty |= true; + } + + if (SyncPositionZ && + Mathf.Abs(networkState.PositionZ - position.z) >= PositionThreshold && + !Mathf.Approximately(networkState.PositionZ, position.z)) + { + networkState.PositionZ = position.z; + networkState.HasPositionZ = true; + isDirty |= true; + } + + if (SyncRotAngleX && + Mathf.Abs(networkState.RotAngleX - rotAngles.x) >= RotAngleThreshold && + !Mathf.Approximately(networkState.RotAngleX, rotAngles.x)) + { + networkState.RotAngleX = rotAngles.x; + networkState.HasRotAngleX = true; + isDirty |= true; + } + + if (SyncRotAngleY && + Mathf.Abs(networkState.RotAngleY - rotAngles.y) >= RotAngleThreshold && + !Mathf.Approximately(networkState.RotAngleY, rotAngles.y)) + { + networkState.RotAngleY = rotAngles.y; + networkState.HasRotAngleY = true; + isDirty |= true; + } + + if (SyncRotAngleZ && + Mathf.Abs(networkState.RotAngleZ - rotAngles.z) >= RotAngleThreshold && + !Mathf.Approximately(networkState.RotAngleZ, rotAngles.z)) + { + networkState.RotAngleZ = rotAngles.z; + networkState.HasRotAngleZ = true; + isDirty |= true; + } + + if (SyncScaleX && + Mathf.Abs(networkState.ScaleX - scale.x) >= ScaleThreshold && + !Mathf.Approximately(networkState.ScaleX, scale.x)) + { + networkState.ScaleX = scale.x; + networkState.HasScaleX = true; + isDirty |= true; + } + + if (SyncScaleY && + Mathf.Abs(networkState.ScaleY - scale.y) >= ScaleThreshold && + !Mathf.Approximately(networkState.ScaleY, scale.y)) + { + networkState.ScaleY = scale.y; + networkState.HasScaleY = true; + isDirty |= true; + } + + if (SyncScaleZ && + Mathf.Abs(networkState.ScaleZ - scale.z) >= ScaleThreshold && + !Mathf.Approximately(networkState.ScaleZ, scale.z)) + { + networkState.ScaleZ = scale.z; + networkState.HasScaleZ = true; + isDirty |= true; + } + + return isDirty; } // TODO: temporary! the function body below probably needs to be rewritten later with interpolation in mind @@ -398,18 +439,10 @@ private void FixedUpdate() return; } - if (CanUpdateTransform && IsNetworkStateDirty(ReplNetworkState.Value)) + if (CanUpdateTransform) { - UpdateNetworkState(); - } - else - { - if (IsNetworkStateDirty(PrevNetworkState)) - { - Debug.LogWarning("A local change without authority detected, revert back to latest network state!"); - } - - ApplyNetworkState(ReplNetworkState.Value); + bool isDirty = UpdateNetworkState(ReplNetworkState.Value); + ReplNetworkState.SetDirty(isDirty); } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs index 8a570c66b5..c7e0f295dc 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs @@ -13,7 +13,7 @@ public void TestSyncAxes( [Values] bool syncRotX, [Values] bool syncRotY, [Values] bool syncRotZ, [Values] bool syncScaX, [Values] bool syncScaY, [Values] bool syncScaZ) { - var gameObject = new GameObject($"Test-{nameof(NetworkTransformStateTests)}"); + var gameObject = new GameObject($"Test-{nameof(NetworkTransformStateTests)}.{nameof(TestSyncAxes)}"); var networkObject = gameObject.AddComponent(); var networkTransform = gameObject.AddComponent(); networkTransform.enabled = false; // do not tick `FixedUpdate()` or `Update()` @@ -59,121 +59,331 @@ public void TestSyncAxes( InLocalSpace = inLocalSpace }; - // Step 1: change properties, expect state to be dirty (tests comparison) + // Step 1: change properties, expect state to be dirty { networkTransform.InLocalSpace = !inLocalSpace; networkTransform.transform.position = new Vector3(3, 4, 5); networkTransform.transform.eulerAngles = new Vector3(30, 45, 90); networkTransform.transform.localScale = new Vector3(1.1f, 0.5f, 2.5f); - Assert.IsTrue(networkTransform.IsNetworkStateDirty(networkTransform.ReplNetworkState.Value)); + bool isDirty = networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value); + networkTransform.ReplNetworkState.SetDirty(isDirty); + Assert.IsTrue(isDirty); } - // Step 2: update state, expect netvar to be dirty (tests serialization) - { - networkTransform.UpdateNetworkState(); - - Assert.IsTrue(networkTransform.ReplNetworkState.IsDirty()); - } - - // Step 3: apply current state locally, expect state to be not dirty/different (tests deserialization) + // Step 2: apply current state locally, expect state to be not dirty/different { networkTransform.ApplyNetworkState(networkTransform.ReplNetworkState.Value); - Assert.IsFalse(networkTransform.IsNetworkStateDirty(networkTransform.ReplNetworkState.Value)); + bool isDirty = networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value); + Assert.IsFalse(isDirty); } - // Step 4: disable a particular sync flag, expect state to be not dirty (tests individual sync flags) + // Step 3: disable a particular sync flag, expect state to be not dirty { + var position = networkTransform.transform.position; + var rotAngles = networkTransform.transform.eulerAngles; + var scale = networkTransform.transform.localScale; + // SyncPositionX { networkTransform.SyncPositionX = false; - var position = networkTransform.transform.position; position.x++; networkTransform.transform.position = position; - Assert.IsFalse(networkTransform.IsNetworkStateDirty(networkTransform.ReplNetworkState.Value)); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); } // SyncPositionY { networkTransform.SyncPositionY = false; - var position = networkTransform.transform.position; position.y++; networkTransform.transform.position = position; - Assert.IsFalse(networkTransform.IsNetworkStateDirty(networkTransform.ReplNetworkState.Value)); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); } // SyncPositionZ { networkTransform.SyncPositionZ = false; - var position = networkTransform.transform.position; position.z++; networkTransform.transform.position = position; - Assert.IsFalse(networkTransform.IsNetworkStateDirty(networkTransform.ReplNetworkState.Value)); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); } + // SyncRotAngleX { networkTransform.SyncRotAngleX = false; - var rotationAngles = networkTransform.transform.eulerAngles; - rotationAngles.x++; - networkTransform.transform.eulerAngles = rotationAngles; + rotAngles.x++; + networkTransform.transform.eulerAngles = rotAngles; - Assert.IsFalse(networkTransform.IsNetworkStateDirty(networkTransform.ReplNetworkState.Value)); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); } // SyncRotAngleY { networkTransform.SyncRotAngleY = false; - var rotationAngles = networkTransform.transform.eulerAngles; - rotationAngles.y++; - networkTransform.transform.eulerAngles = rotationAngles; + rotAngles.y++; + networkTransform.transform.eulerAngles = rotAngles; - Assert.IsFalse(networkTransform.IsNetworkStateDirty(networkTransform.ReplNetworkState.Value)); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); } // SyncRotAngleZ { networkTransform.SyncRotAngleZ = false; - var rotationAngles = networkTransform.transform.eulerAngles; - rotationAngles.z++; - networkTransform.transform.eulerAngles = rotationAngles; + rotAngles.z++; + networkTransform.transform.eulerAngles = rotAngles; - Assert.IsFalse(networkTransform.IsNetworkStateDirty(networkTransform.ReplNetworkState.Value)); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); } + // SyncScaleX { networkTransform.SyncScaleX = false; - var scale = networkTransform.transform.localScale; scale.x++; networkTransform.transform.localScale = scale; - Assert.IsFalse(networkTransform.IsNetworkStateDirty(networkTransform.ReplNetworkState.Value)); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); } // SyncScaleY { networkTransform.SyncScaleY = false; - var scale = networkTransform.transform.localScale; scale.y++; networkTransform.transform.localScale = scale; - Assert.IsFalse(networkTransform.IsNetworkStateDirty(networkTransform.ReplNetworkState.Value)); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); } // SyncScaleZ { networkTransform.SyncScaleZ = false; - var scale = networkTransform.transform.localScale; scale.z++; networkTransform.transform.localScale = scale; - Assert.IsFalse(networkTransform.IsNetworkStateDirty(networkTransform.ReplNetworkState.Value)); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + } + } + + Object.DestroyImmediate(networkTransform); + } + + [Test] + public void TestThresholds( + [Values] bool inLocalSpace, + [Values(0, 1.0f)] float positionThreshold, + [Values(0, 1.0f)] float rotAngleThreshold, + [Values(0, 0.5f)] float scaleThreshold) + { + var gameObject = new GameObject($"Test-{nameof(NetworkTransformStateTests)}.{nameof(TestSyncAxes)}"); + var networkObject = gameObject.AddComponent(); + var networkTransform = gameObject.AddComponent(); + networkTransform.enabled = false; // do not tick `FixedUpdate()` or `Update()` + + var initialPosition = Vector3.zero; + var initialRotAngles = Vector3.zero; + var initialScale = Vector3.one; + + networkTransform.transform.position = initialPosition; + networkTransform.transform.eulerAngles = initialRotAngles; + networkTransform.transform.localScale = initialScale; + networkTransform.SyncPositionX = true; + networkTransform.SyncPositionY = true; + networkTransform.SyncPositionZ = true; + networkTransform.SyncRotAngleX = true; + networkTransform.SyncRotAngleY = true; + networkTransform.SyncRotAngleZ = true; + networkTransform.SyncScaleX = true; + networkTransform.SyncScaleY = true; + networkTransform.SyncScaleZ = true; + networkTransform.InLocalSpace = inLocalSpace; + networkTransform.PositionThreshold = positionThreshold; + networkTransform.RotAngleThreshold = rotAngleThreshold; + networkTransform.ScaleThreshold = scaleThreshold; + + networkTransform.ReplNetworkState.Value = new NetworkTransform.NetworkState + { + PositionX = initialPosition.x, + PositionY = initialPosition.y, + PositionZ = initialPosition.z, + RotAngleX = initialRotAngles.x, + RotAngleY = initialRotAngles.y, + RotAngleZ = initialRotAngles.z, + ScaleX = initialScale.x, + ScaleY = initialScale.y, + ScaleZ = initialScale.z, + InLocalSpace = inLocalSpace + }; + + // Step 1: change properties, expect state to be dirty + { + networkTransform.InLocalSpace = !inLocalSpace; + networkTransform.transform.position = new Vector3(3, 4, 5); + networkTransform.transform.eulerAngles = new Vector3(30, 45, 90); + networkTransform.transform.localScale = new Vector3(1.1f, 0.5f, 2.5f); + + bool isDirty = networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value); + networkTransform.ReplNetworkState.SetDirty(isDirty); + Assert.IsTrue(isDirty); + } + + // Step 2: apply current state locally, expect state to be not dirty/different + { + networkTransform.ApplyNetworkState(networkTransform.ReplNetworkState.Value); + + bool isDirty = networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value); + Assert.IsFalse(isDirty); + } + + // Step 3: make changes below and above thresholds + { + // Position + if (!Mathf.Approximately(positionThreshold, 0.0f)) + { + var position = networkTransform.transform.position; + + // PositionX + { + position.x += positionThreshold / 2; + networkTransform.transform.position = position; + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + position.x += positionThreshold * 2; + networkTransform.transform.position = position; + Assert.IsTrue(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + networkTransform.ApplyNetworkState(networkTransform.ReplNetworkState.Value); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + } + + // PositionY + { + position.y += positionThreshold / 2; + networkTransform.transform.position = position; + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + position.y += positionThreshold * 2; + networkTransform.transform.position = position; + Assert.IsTrue(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + networkTransform.ApplyNetworkState(networkTransform.ReplNetworkState.Value); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + } + + // PositionZ + { + position.z += positionThreshold / 2; + networkTransform.transform.position = position; + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + position.z += positionThreshold * 2; + networkTransform.transform.position = position; + Assert.IsTrue(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + networkTransform.ApplyNetworkState(networkTransform.ReplNetworkState.Value); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + } + } + + // RotAngles + if (!Mathf.Approximately(rotAngleThreshold, 0.0f)) + { + var rotAngles = networkTransform.transform.eulerAngles; + + // RotAngleX + { + rotAngles.x += rotAngleThreshold / 2; + networkTransform.transform.eulerAngles = rotAngles; + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + rotAngles.x += rotAngleThreshold * 2; + networkTransform.transform.eulerAngles = rotAngles; + Assert.IsTrue(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + networkTransform.ApplyNetworkState(networkTransform.ReplNetworkState.Value); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + } + + // RotAngleY + { + rotAngles.y += rotAngleThreshold / 2; + networkTransform.transform.eulerAngles = rotAngles; + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + rotAngles.y += rotAngleThreshold * 2; + networkTransform.transform.eulerAngles = rotAngles; + Assert.IsTrue(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + networkTransform.ApplyNetworkState(networkTransform.ReplNetworkState.Value); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + } + + // RotAngleZ + { + rotAngles.z += rotAngleThreshold / 2; + networkTransform.transform.eulerAngles = rotAngles; + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + rotAngles.z += rotAngleThreshold * 2; + networkTransform.transform.eulerAngles = rotAngles; + Assert.IsTrue(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + networkTransform.ApplyNetworkState(networkTransform.ReplNetworkState.Value); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + } + } + + // Scale + if (!Mathf.Approximately(scaleThreshold, 0.0f) && inLocalSpace) + { + var scale = networkTransform.transform.localScale; + + // ScaleX + { + scale.x += scaleThreshold / 2; + networkTransform.transform.localScale = scale; + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + scale.x += scaleThreshold * 2; + networkTransform.transform.localScale = scale; + Assert.IsTrue(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + networkTransform.ApplyNetworkState(networkTransform.ReplNetworkState.Value); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + } + + // ScaleY + { + scale.y += scaleThreshold / 2; + networkTransform.transform.localScale = scale; + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + scale.y += scaleThreshold * 2; + networkTransform.transform.localScale = scale; + Assert.IsTrue(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + networkTransform.ApplyNetworkState(networkTransform.ReplNetworkState.Value); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + } + + // ScaleZ + { + scale.z += scaleThreshold / 2; + networkTransform.transform.localScale = scale; + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + scale.z += scaleThreshold * 2; + networkTransform.transform.localScale = scale; + Assert.IsTrue(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + + networkTransform.ApplyNetworkState(networkTransform.ReplNetworkState.Value); + Assert.IsFalse(networkTransform.UpdateNetworkState(networkTransform.ReplNetworkState.Value)); + } } } From eda3904646264f784f31e8ab5738c146071982d2 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Mon, 16 Aug 2021 16:26:38 +0100 Subject: [PATCH 2/5] minor typo fix --- .../Runtime/NetworkTransform/NetworkTransformStateTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs index c7e0f295dc..2e315d446b 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs @@ -180,7 +180,7 @@ public void TestThresholds( [Values(0, 1.0f)] float rotAngleThreshold, [Values(0, 0.5f)] float scaleThreshold) { - var gameObject = new GameObject($"Test-{nameof(NetworkTransformStateTests)}.{nameof(TestSyncAxes)}"); + var gameObject = new GameObject($"Test-{nameof(NetworkTransformStateTests)}.{nameof(TestThresholds)}"); var networkObject = gameObject.AddComponent(); var networkTransform = gameObject.AddComponent(); networkTransform.enabled = false; // do not tick `FixedUpdate()` or `Update()` From ed57c50a1fa77aa31e781aee61ef8e7c269df85a Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Mon, 16 Aug 2021 16:54:34 +0100 Subject: [PATCH 3/5] add a few comments --- com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs | 2 ++ .../Runtime/NetworkTransform/NetworkTransformStateTests.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs b/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs index ffa57eb672..77c1a06d07 100644 --- a/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs @@ -186,6 +186,8 @@ public void NetworkSerialize(NetworkSerializer serializer) Authority == NetworkAuthority.Server && IsServer || Authority == NetworkAuthority.Shared; + // updates `NetworkState` properties if they need to and returns a `bool` indicating whether or not there was any changes made + // returned boolean would be useful to change encapsulating `NetworkVariable`'s dirty state, e.g. ReplNetworkState.SetDirty(isDirty); internal bool UpdateNetworkState(NetworkState networkState) { var position = InLocalSpace ? m_Transform.localPosition : m_Transform.position; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs index 2e315d446b..ecb8db4cda 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs @@ -241,6 +241,8 @@ public void TestThresholds( } // Step 3: make changes below and above thresholds + // changes below the threshold should not make `NetworkState` dirty + // changes above the threshold should make `NetworkState` dirty { // Position if (!Mathf.Approximately(positionThreshold, 0.0f)) From 43fa000fe708b070cbe96b589cb1b26ea2fc8630 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Mon, 16 Aug 2021 18:46:15 +0100 Subject: [PATCH 4/5] disable some networktransform tests until post-perms changes --- .../Prototyping/NetworkTransform.cs | 12 ++++++++++-- .../NetworkTransform/NetworkTransformTests.cs | 16 ++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs b/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs index 77c1a06d07..10fc79803d 100644 --- a/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs @@ -190,6 +190,11 @@ public void NetworkSerialize(NetworkSerializer serializer) // returned boolean would be useful to change encapsulating `NetworkVariable`'s dirty state, e.g. ReplNetworkState.SetDirty(isDirty); internal bool UpdateNetworkState(NetworkState networkState) { + if (networkState == null) + { + return false; + } + var position = InLocalSpace ? m_Transform.localPosition : m_Transform.position; var rotAngles = InLocalSpace ? m_Transform.localEulerAngles : m_Transform.eulerAngles; var scale = InLocalSpace ? m_Transform.localScale : m_Transform.lossyScale; @@ -443,8 +448,11 @@ private void FixedUpdate() if (CanUpdateTransform) { - bool isDirty = UpdateNetworkState(ReplNetworkState.Value); - ReplNetworkState.SetDirty(isDirty); + ReplNetworkState.SetDirty(UpdateNetworkState(ReplNetworkState.Value)); + } + else if (UpdateNetworkState(PrevNetworkState)) + { + ApplyNetworkState(ReplNetworkState.Value); } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs index 60960d57a1..000f61323f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs @@ -1,11 +1,7 @@ -using System; using System.Collections; -using System.Text.RegularExpressions; using Unity.Netcode.Prototyping; using NUnit.Framework; -using UnityEngine; using UnityEngine.TestTools; -using static Unity.Netcode.Prototyping.NetworkTransform; namespace Unity.Netcode.RuntimeTests { @@ -45,7 +41,8 @@ public override IEnumerator Setup() m_ClientSideClientPlayer = clientClientPlayerResult.Result; } - [UnityTest] + // TODO: rewrite after perms & authority changes + /* [UnityTest] [TestCase(true, NetworkAuthority.Client, ExpectedResult = null)] [TestCase(true, NetworkAuthority.Server, ExpectedResult = null)] [TestCase(false, NetworkAuthority.Client, ExpectedResult = null)] @@ -95,7 +92,7 @@ static bool HasAuthorityFunc(NetworkTransform transform) yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.rotation.eulerAngles.x > approximation, waitResult, maxFrames: 120)); if (!waitResult.Result) { - throw new Exception("timeout while waiting for position change"); + throw new Exception("timeout while waiting for rotation change"); } // approximation needed here since eulerAngles isn't super precise. Assert.LessOrEqual(Math.Abs(45 - otherSideNetworkTransform.transform.rotation.eulerAngles.x), approximation, $"wrong rotation on ghost on x, got {otherSideNetworkTransform.transform.rotation.eulerAngles.x}"); @@ -110,7 +107,7 @@ static bool HasAuthorityFunc(NetworkTransform transform) yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => otherSideNetworkTransform.transform.lossyScale.x > 1f + approximation, waitResult, maxFrames: 120)); if (!waitResult.Result) { - throw new Exception("timeout while waiting for position change"); + throw new Exception("timeout while waiting for scale change"); } UnityEngine.Assertions.Assert.AreApproximatelyEqual(2f, otherSideNetworkTransform.transform.lossyScale.x, "wrong scale on ghost"); UnityEngine.Assertions.Assert.AreApproximatelyEqual(3f, otherSideNetworkTransform.transform.lossyScale.y, "wrong scale on ghost"); @@ -137,15 +134,14 @@ public IEnumerator TestCantChangeTransformFromOtherSideAuthority(NetworkAuthorit yield return new WaitForFixedUpdate(); - LogAssert.Expect(LogType.Warning, new Regex(".*[Aa]uthority.*")); Assert.AreEqual(Vector3.zero, otherSideNetworkTransform.transform.position, "got authority error, but other side still moved!"); - } + } */ [UnityTearDown] public override IEnumerator Teardown() { yield return base.Teardown(); - UnityEngine.Object.Destroy(m_PlayerPrefab); + UnityEngine.Object.DestroyImmediate(m_PlayerPrefab); } } } From 0134c5cececb7463cf22dada9215d5a5f77d00d6 Mon Sep 17 00:00:00 2001 From: "M. Fatih MAR" Date: Tue, 17 Aug 2021 19:10:48 +0100 Subject: [PATCH 5/5] chore: remove authority & netvar perms from NetworkTransform --- .../Prototyping/NetworkTransform.cs | 66 +------------------ 1 file changed, 1 insertion(+), 65 deletions(-) diff --git a/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs b/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs index 10fc79803d..992ae1f04d 100644 --- a/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs @@ -9,18 +9,6 @@ namespace Unity.Netcode.Prototyping [AddComponentMenu("Netcode/" + nameof(NetworkTransform))] public class NetworkTransform : NetworkBehaviour { - /// - /// Server authority allows only the server to update this transform - /// Client authority allows only the owner client to update this transform - /// Shared authority allows everyone to update this transform - /// - public enum NetworkAuthority - { - Server = 0, - Client, - Shared - } - internal class NetworkState : INetworkSerializable { internal const int InLocalSpaceBit = 0; @@ -140,13 +128,6 @@ public void NetworkSerialize(NetworkSerializer serializer) } } - /// - /// TODO this will need refactoring - /// Specifies who can update this transform - /// - [Tooltip("Defines who can update this transform")] - public NetworkAuthority Authority = NetworkAuthority.Server; - /// /// The network channel to use send updates /// @@ -178,14 +159,6 @@ public void NetworkSerialize(NetworkSerializer serializer) internal readonly NetworkVariable ReplNetworkState = new NetworkVariable(new NetworkState()); internal NetworkState PrevNetworkState; - /// - /// Does this instance (client or server) has authority to update transform? - /// - public bool CanUpdateTransform => - Authority == NetworkAuthority.Client && IsClient && IsOwner || - Authority == NetworkAuthority.Server && IsServer || - Authority == NetworkAuthority.Shared; - // updates `NetworkState` properties if they need to and returns a `bool` indicating whether or not there was any changes made // returned boolean would be useful to change encapsulating `NetworkVariable`'s dirty state, e.g. ReplNetworkState.SetDirty(isDirty); internal bool UpdateNetworkState(NetworkState networkState) @@ -390,39 +363,13 @@ private void OnNetworkStateChanged(NetworkState oldState, NetworkState newState) return; } - if (Authority == NetworkAuthority.Client && IsClient && IsOwner) - { - // todo MTT-768 this shouldn't happen anymore with new tick system (tick written will be higher than tick read, so netvar wouldn't change in that case) - return; - } - ApplyNetworkState(newState); } - private void UpdateNetVarPerms() - { - switch (Authority) - { - default: - throw new NotImplementedException($"Authority: {Authority} is not handled"); - case NetworkAuthority.Server: - ReplNetworkState.Settings.WritePermission = NetworkVariablePermission.ServerOnly; - break; - case NetworkAuthority.Client: - ReplNetworkState.Settings.WritePermission = NetworkVariablePermission.OwnerOnly; - break; - case NetworkAuthority.Shared: - ReplNetworkState.Settings.WritePermission = NetworkVariablePermission.Everyone; - break; - } - } - private void Awake() { m_Transform = transform; - UpdateNetVarPerms(); - ReplNetworkState.Settings.SendNetworkChannel = Channel; ReplNetworkState.Settings.SendTickrate = FixedSendsPerSecond; @@ -446,7 +393,7 @@ private void FixedUpdate() return; } - if (CanUpdateTransform) + if (IsServer) { ReplNetworkState.SetDirty(UpdateNetworkState(ReplNetworkState.Value)); } @@ -456,17 +403,6 @@ private void FixedUpdate() } } - /// - /// Updates the NetworkTransform's authority model at runtime - /// - internal void SetAuthority(NetworkAuthority authority) - { - Authority = authority; - UpdateNetVarPerms(); - // todo this should be synced with the other side. - // let's wait for a more final solution before adding more code here - } - /// /// Teleports the transform to the given values without interpolating ///