Skip to content

Commit d23f93d

Browse files
fix: NetworkAnimator does not work using the server authoritative model [MTT-3745] (#2003)
1 parent a0b6aa1 commit d23f93d

File tree

10 files changed

+701
-124
lines changed

10 files changed

+701
-124
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
2121

2222
### Fixed
2323

24+
- Fixed `NetworkAnimator` could not run in the server authoritative mode. (#2003)
2425
- Fixed issue where late joining clients would get a soft synchronization error if any in-scene placed NetworkObjects were parented under another `NetworkObject`. (#1985)
2526
- Fixed issue where `NetworkBehaviourReference` would throw a type cast exception if using `NetworkBehaviourReference.TryGet` and the component type was not found. (#1984)
2627
- Fixed `NetworkSceneManager` was not sending scene event notifications for the currently active scene and any additively loaded scenes when loading a new scene in `LoadSceneMode.Single` mode. (#1975)

com.unity.netcode.gameobjects/Components/NetworkAnimator.cs

Lines changed: 81 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,10 @@ public void NetworkUpdate(NetworkUpdateStage updateStage)
5858
m_ProcessParameterUpdates.Clear();
5959

6060
// Only owners check for Animator changes
61-
if (m_NetworkAnimator.IsOwner)
61+
if (m_NetworkAnimator.IsOwner && !m_NetworkAnimator.IsServerAuthoritative() || m_NetworkAnimator.IsServerAuthoritative() && m_NetworkAnimator.NetworkManager.IsServer)
6262
{
6363
m_NetworkAnimator.CheckForAnimatorChanges();
6464
}
65-
6665
break;
6766
}
6867
}
@@ -209,6 +208,19 @@ public Animator Animator
209208
}
210209
}
211210

211+
internal bool IsServerAuthoritative()
212+
{
213+
return OnIsServerAuthoritative();
214+
}
215+
216+
/// <summary>
217+
/// Override this method and return false to switch to owner authoritative mode
218+
/// </summary>
219+
protected virtual bool OnIsServerAuthoritative()
220+
{
221+
return true;
222+
}
223+
212224
// Animators only support up to 32 params
213225
private const int k_MaxAnimationParams = 32;
214226

@@ -276,13 +288,10 @@ public override void OnDestroy()
276288
base.OnDestroy();
277289
}
278290

279-
280291
private List<int> m_ParametersToUpdate;
281-
282292
private List<ulong> m_ClientSendList;
283293
private ClientRpcParams m_ClientRpcParams;
284294

285-
286295
public override void OnNetworkSpawn()
287296
{
288297
if (IsOwner || IsServer)
@@ -439,7 +448,7 @@ private void OnClientConnectedCallback(ulong playerId)
439448

440449
internal void CheckForAnimatorChanges()
441450
{
442-
if (!IsOwner)
451+
if (!IsOwner && !IsServerAuthoritative() || IsServerAuthoritative() && !IsServer)
443452
{
444453
return;
445454
}
@@ -747,25 +756,32 @@ private unsafe void UpdateAnimationState(AnimationMessage animationState)
747756
}
748757

749758
/// <summary>
750-
/// Sever-side animator parameter update request
759+
/// Server-side animator parameter update request
751760
/// The server sets its local parameters and then forwards the message to the remaining clients
752761
/// </summary>
753762
[ServerRpc]
754763
private unsafe void SendParametersUpdateServerRpc(ParametersUpdateMessage parametersUpdate, ServerRpcParams serverRpcParams = default)
755764
{
756-
if (serverRpcParams.Receive.SenderClientId != OwnerClientId)
765+
if (IsServerAuthoritative())
757766
{
758-
return;
767+
m_NetworkAnimatorStateChangeHandler.SendParameterUpdate(parametersUpdate);
759768
}
760-
UpdateParameters(parametersUpdate);
761-
if (NetworkManager.ConnectedClientsIds.Count - 2 > 0)
769+
else
762770
{
763-
m_ClientSendList.Clear();
764-
m_ClientSendList.AddRange(NetworkManager.ConnectedClientsIds);
765-
m_ClientSendList.Remove(serverRpcParams.Receive.SenderClientId);
766-
m_ClientSendList.Remove(NetworkManager.ServerClientId);
767-
m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList;
768-
m_NetworkAnimatorStateChangeHandler.SendParameterUpdate(parametersUpdate, m_ClientRpcParams);
771+
if (serverRpcParams.Receive.SenderClientId != OwnerClientId)
772+
{
773+
return;
774+
}
775+
UpdateParameters(parametersUpdate);
776+
if (NetworkManager.ConnectedClientsIds.Count - 2 > 0)
777+
{
778+
m_ClientSendList.Clear();
779+
m_ClientSendList.AddRange(NetworkManager.ConnectedClientsIds);
780+
m_ClientSendList.Remove(serverRpcParams.Receive.SenderClientId);
781+
m_ClientSendList.Remove(NetworkManager.ServerClientId);
782+
m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList;
783+
m_NetworkAnimatorStateChangeHandler.SendParameterUpdate(parametersUpdate, m_ClientRpcParams);
784+
}
769785
}
770786
}
771787

@@ -775,33 +791,40 @@ private unsafe void SendParametersUpdateServerRpc(ParametersUpdateMessage parame
775791
[ClientRpc]
776792
internal unsafe void SendParametersUpdateClientRpc(ParametersUpdateMessage parametersUpdate, ClientRpcParams clientRpcParams = default)
777793
{
778-
if (!IsOwner)
794+
var isServerAuthoritative = IsServerAuthoritative();
795+
if (!isServerAuthoritative && !IsOwner || isServerAuthoritative)
779796
{
780797
m_NetworkAnimatorStateChangeHandler.ProcessParameterUpdate(parametersUpdate);
781798
}
782799
}
783800

784801
/// <summary>
785-
/// Sever-side animation state update request
802+
/// Server-side animation state update request
786803
/// The server sets its local state and then forwards the message to the remaining clients
787804
/// </summary>
788805
[ServerRpc]
789806
private unsafe void SendAnimStateServerRpc(AnimationMessage animSnapshot, ServerRpcParams serverRpcParams = default)
790807
{
791-
if (serverRpcParams.Receive.SenderClientId != OwnerClientId)
808+
if (IsServerAuthoritative())
792809
{
793-
return;
810+
m_NetworkAnimatorStateChangeHandler.SendAnimationUpdate(animSnapshot);
794811
}
795-
796-
UpdateAnimationState(animSnapshot);
797-
if (NetworkManager.ConnectedClientsIds.Count - 2 > 0)
812+
else
798813
{
799-
m_ClientSendList.Clear();
800-
m_ClientSendList.AddRange(NetworkManager.ConnectedClientsIds);
801-
m_ClientSendList.Remove(serverRpcParams.Receive.SenderClientId);
802-
m_ClientSendList.Remove(NetworkManager.ServerClientId);
803-
m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList;
804-
m_NetworkAnimatorStateChangeHandler.SendAnimationUpdate(animSnapshot, m_ClientRpcParams);
814+
if (serverRpcParams.Receive.SenderClientId != OwnerClientId)
815+
{
816+
return;
817+
}
818+
UpdateAnimationState(animSnapshot);
819+
if (NetworkManager.ConnectedClientsIds.Count - 2 > 0)
820+
{
821+
m_ClientSendList.Clear();
822+
m_ClientSendList.AddRange(NetworkManager.ConnectedClientsIds);
823+
m_ClientSendList.Remove(serverRpcParams.Receive.SenderClientId);
824+
m_ClientSendList.Remove(NetworkManager.ServerClientId);
825+
m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList;
826+
m_NetworkAnimatorStateChangeHandler.SendAnimationUpdate(animSnapshot, m_ClientRpcParams);
827+
}
805828
}
806829
}
807830

@@ -811,36 +834,44 @@ private unsafe void SendAnimStateServerRpc(AnimationMessage animSnapshot, Server
811834
[ClientRpc]
812835
private unsafe void SendAnimStateClientRpc(AnimationMessage animSnapshot, ClientRpcParams clientRpcParams = default)
813836
{
814-
if (!IsOwner)
837+
var isServerAuthoritative = IsServerAuthoritative();
838+
if (!isServerAuthoritative && !IsOwner || isServerAuthoritative)
815839
{
816840
UpdateAnimationState(animSnapshot);
817841
}
818842
}
819843

820844
/// <summary>
821-
/// Sever-side trigger state update request
845+
/// Server-side trigger state update request
822846
/// The server sets its local state and then forwards the message to the remaining clients
823847
/// </summary>
824848
[ServerRpc]
825849
private void SendAnimTriggerServerRpc(AnimationTriggerMessage animationTriggerMessage, ServerRpcParams serverRpcParams = default)
826850
{
827-
if (serverRpcParams.Receive.SenderClientId != OwnerClientId)
851+
if (IsServerAuthoritative())
828852
{
829-
return;
853+
m_NetworkAnimatorStateChangeHandler.SendTriggerUpdate(animationTriggerMessage);
830854
}
831-
832-
// trigger the animation locally on the server...
833-
m_Animator.SetBool(animationTriggerMessage.Hash, animationTriggerMessage.IsTriggerSet);
834-
835-
if (NetworkManager.ConnectedClientsIds.Count - 2 > 0)
855+
else
836856
{
837-
m_ClientSendList.Clear();
838-
m_ClientSendList.AddRange(NetworkManager.ConnectedClientsIds);
839-
m_ClientSendList.Remove(serverRpcParams.Receive.SenderClientId);
840-
m_ClientSendList.Remove(NetworkManager.ServerClientId);
841-
m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList;
842-
m_NetworkAnimatorStateChangeHandler.SendTriggerUpdate(animationTriggerMessage, m_ClientRpcParams);
857+
if (serverRpcParams.Receive.SenderClientId != OwnerClientId)
858+
{
859+
return;
860+
}
861+
// trigger the animation locally on the server...
862+
m_Animator.SetBool(animationTriggerMessage.Hash, animationTriggerMessage.IsTriggerSet);
863+
864+
if (NetworkManager.ConnectedClientsIds.Count - 2 > 0)
865+
{
866+
m_ClientSendList.Clear();
867+
m_ClientSendList.AddRange(NetworkManager.ConnectedClientsIds);
868+
m_ClientSendList.Remove(serverRpcParams.Receive.SenderClientId);
869+
m_ClientSendList.Remove(NetworkManager.ServerClientId);
870+
m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList;
871+
m_NetworkAnimatorStateChangeHandler.SendTriggerUpdate(animationTriggerMessage, m_ClientRpcParams);
872+
}
843873
}
874+
844875
}
845876

846877
/// <summary>
@@ -852,7 +883,8 @@ private void SendAnimTriggerServerRpc(AnimationTriggerMessage animationTriggerMe
852883
[ClientRpc]
853884
internal void SendAnimTriggerClientRpc(AnimationTriggerMessage animationTriggerMessage, ClientRpcParams clientRpcParams = default)
854885
{
855-
if (!IsOwner)
886+
var isServerAuthoritative = IsServerAuthoritative();
887+
if (!isServerAuthoritative && !IsOwner || isServerAuthoritative)
856888
{
857889
m_Animator.SetBool(animationTriggerMessage.Hash, animationTriggerMessage.IsTriggerSet);
858890
}
@@ -869,10 +901,11 @@ public void SetTrigger(string triggerName)
869901

870902
/// <inheritdoc cref="SetTrigger(string)" />
871903
/// <param name="hash">The hash for the trigger to activate</param>
872-
/// <param name="reset">If true, resets the trigger</param>
904+
/// <param name="setTrigger">sets (true) or resets (false) the trigger. The default is to set it (true).</param>
873905
public void SetTrigger(int hash, bool setTrigger = true)
874906
{
875-
if (IsOwner)
907+
var isServerAuthoritative = IsServerAuthoritative();
908+
if (IsOwner && !isServerAuthoritative || IsServer && isServerAuthoritative)
876909
{
877910
var animTriggerMessage = new AnimationTriggerMessage() { Hash = hash, IsTriggerSet = setTrigger };
878911
if (IsServer)

testproject/Assets/Tests/Manual/NetworkAnimatorTests/AnimatedCubeController.cs

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,112 @@ public class AnimatedCubeController : NetworkBehaviour
1212
private Animator m_Animator;
1313
private bool m_Rotate;
1414
private NetworkAnimator m_NetworkAnimator;
15+
private bool m_IsServerAuthoritative = true;
1516

1617
private void Awake()
1718
{
1819
m_Animator = GetComponent<Animator>();
1920
m_NetworkAnimator = GetComponent<NetworkAnimator>();
21+
if (m_NetworkAnimator == null)
22+
{
23+
m_NetworkAnimator = GetComponent<OwnerNetworkAnimator>();
24+
if (m_NetworkAnimator != null)
25+
{
26+
m_IsServerAuthoritative = false;
27+
}
28+
else
29+
{
30+
throw new System.Exception($"{nameof(AnimatedCubeController)} requires that it is paired with either a {nameof(NetworkAnimator)} or {nameof(OwnerNetworkAnimator)}. Neither of the two components were found!");
31+
}
32+
}
2033
m_Rotate = m_Animator.GetBool("Rotate");
2134
}
2235

2336
public override void OnNetworkSpawn()
2437
{
25-
if (!IsOwner)
38+
if (HasAuthority())
2639
{
2740
enabled = false;
2841
}
2942
}
3043

44+
private bool HasAuthority()
45+
{
46+
if (IsOwnerAuthority() || IsServerAuthority())
47+
{
48+
return true;
49+
}
50+
return false;
51+
}
52+
53+
private bool IsServerAuthority()
54+
{
55+
if (IsServer && m_IsServerAuthoritative)
56+
{
57+
return true;
58+
}
59+
return false;
60+
}
61+
62+
private bool IsOwnerAuthority()
63+
{
64+
if (IsOwner && !m_IsServerAuthoritative)
65+
{
66+
return true;
67+
}
68+
return false;
69+
}
70+
71+
72+
[ServerRpc(RequireOwnership = false)]
73+
private void ToggleRotateAnimationServerRpc(bool rotate)
74+
{
75+
m_Rotate = rotate;
76+
m_Animator.SetBool("Rotate", m_Rotate);
77+
}
78+
3179
internal void ToggleRotateAnimation()
3280
{
3381
m_Rotate = !m_Rotate;
34-
m_Animator.SetBool("Rotate", m_Rotate);
82+
if (m_IsServerAuthoritative)
83+
{
84+
if (!IsServer && IsOwner)
85+
{
86+
ToggleRotateAnimationServerRpc(m_Rotate);
87+
}
88+
else if (IsServer && IsOwner)
89+
{
90+
m_Animator.SetBool("Rotate", m_Rotate);
91+
}
92+
}
93+
else if (IsOwner)
94+
{
95+
m_Animator.SetBool("Rotate", m_Rotate);
96+
}
97+
}
98+
99+
[ServerRpc(RequireOwnership = false)]
100+
private void PlayPulseAnimationServerRpc(bool rotate)
101+
{
102+
m_NetworkAnimator.SetTrigger("Pulse");
35103
}
36104

37-
internal void PlayPulseAnimation(bool useNetworkAnimator = true)
105+
internal void PlayPulseAnimation()
38106
{
39-
if (useNetworkAnimator)
107+
if (m_IsServerAuthoritative)
40108
{
41-
m_NetworkAnimator.SetTrigger("Pulse");
109+
if (!IsServer && IsOwner)
110+
{
111+
PlayPulseAnimationServerRpc(m_Rotate);
112+
}
113+
else if (IsServer && IsOwner)
114+
{
115+
m_NetworkAnimator.SetTrigger("Pulse");
116+
}
42117
}
43-
else
118+
else if (IsOwner)
44119
{
45-
m_Animator.SetBool("Pulse", true);
120+
m_NetworkAnimator.SetTrigger("Pulse");
46121
}
47122
}
48123

testproject/Assets/Tests/Manual/NetworkAnimatorTests/AnimationUserInput.cs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ private void Start()
1717
// Update is called once per frame
1818
private void LateUpdate()
1919
{
20-
if (!IsSpawned || !IsOwner)
20+
if (!IsSpawned || !IsOwner && !IsServer)
2121
{
2222
return;
2323
}
@@ -37,22 +37,6 @@ private void LateUpdate()
3737
controller.PlayPulseAnimation();
3838
}
3939
}
40-
41-
if (Input.GetKeyDown(KeyCode.Return))
42-
{
43-
foreach (var controller in m_Controllers)
44-
{
45-
controller.PlayPulseAnimation(false);
46-
}
47-
}
48-
49-
if (Input.GetKeyDown(KeyCode.T))
50-
{
51-
foreach (var controller in m_Controllers)
52-
{
53-
controller.TestAnimator();
54-
}
55-
}
5640
}
5741
}
5842
}

0 commit comments

Comments
 (0)