using System.Linq;
using System.Collections.Generic;
using MLAPI.Messaging;
using MLAPI.Serialization;
using UnityEngine;
namespace MLAPI.Prototyping
{
///
/// A prototype component for syncing animations
///
[AddComponentMenu("MLAPI/NetworkAnimator")]
public class NetworkAnimator : NetworkBehaviour
{
private struct AnimParams : INetworkSerializable
{
public Dictionary Parameters;
public void NetworkSerialize(NetworkSerializer serializer)
{
int paramCount = serializer.IsReading ? 0 : Parameters.Count;
serializer.Serialize(ref paramCount);
var paramArray = serializer.IsReading ? new KeyValuePair[paramCount] : Parameters.ToArray();
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++)
{
int paramId = serializer.IsReading ? 0 : paramArray[paramIndex].Key;
serializer.Serialize(ref paramId);
byte paramType = serializer.IsReading ? (byte)0 : (byte)paramArray[paramIndex].Value.Type;
serializer.Serialize(ref paramType);
object paramBoxed = null;
switch (paramType)
{
case (byte)AnimatorControllerParameterType.Float:
float paramFloat = serializer.IsReading ? 0 : (float)paramArray[paramIndex].Value.Boxed;
serializer.Serialize(ref paramFloat);
paramBoxed = paramFloat;
break;
case (byte)AnimatorControllerParameterType.Int:
int paramInt = serializer.IsReading ? 0 : (int)paramArray[paramIndex].Value.Boxed;
serializer.Serialize(ref paramInt);
paramBoxed = paramInt;
break;
case (byte)AnimatorControllerParameterType.Bool:
bool paramBool = serializer.IsReading ? false : (bool)paramArray[paramIndex].Value.Boxed;
serializer.Serialize(ref paramBool);
paramBoxed = paramBool;
break;
}
if (serializer.IsReading)
{
paramArray[paramIndex] = new KeyValuePair(paramId, ((AnimatorControllerParameterType)paramType, paramBoxed));
}
}
if (serializer.IsReading)
{
Parameters = paramArray.ToDictionary(pair => pair.Key, pair => pair.Value);
}
}
}
public float SendRate = 0.1f;
[SerializeField]
private Animator m_Animator;
public Animator Animator => m_Animator;
[HideInInspector]
[SerializeField]
private uint m_TrackedParamFlags = 0;
public void SetParamTracking(int paramIndex, bool isTracking)
{
if (paramIndex >= 32) return;
if (isTracking)
{
m_TrackedParamFlags |= (uint)(1 << paramIndex);
}
else
{
m_TrackedParamFlags &= (uint)~(1 << paramIndex);
}
}
public bool GetParamTracking(int paramIndex)
{
if (paramIndex >= 32) return false;
return (m_TrackedParamFlags & (uint)(1 << paramIndex)) != 0;
}
public void ResetTrackedParams()
{
m_TrackedParamFlags = 0;
}
private void FixedUpdate()
{
if (!IsOwner) return;
if (CheckSendRate())
{
SendTrackedParams();
}
if (CheckStateChange(out int animStateHash, out float animStateTime))
{
SendAllParamsAndState(animStateHash, animStateTime);
}
}
private float m_NextSendTime = 0.0f;
private bool CheckSendRate()
{
var networkTime = NetworkManager.Singleton.NetworkTime;
if (SendRate != 0 && m_NextSendTime < networkTime)
{
m_NextSendTime = networkTime + SendRate;
return true;
}
return false;
}
private int m_LastAnimStateHash = 0;
private bool CheckStateChange(out int outAnimStateHash, out float outAnimStateTime)
{
var animStateInfo = Animator.GetCurrentAnimatorStateInfo(0);
var animStateHash = animStateInfo.fullPathHash;
var animStateTime = animStateInfo.normalizedTime;
if (animStateHash != m_LastAnimStateHash)
{
m_LastAnimStateHash = animStateHash;
outAnimStateHash = animStateHash;
outAnimStateTime = animStateTime;
return true;
}
outAnimStateHash = 0;
outAnimStateTime = 0;
return false;
}
private AnimParams GetAnimParams(bool trackedOnly = false)
{
var animParams = new AnimParams();
animParams.Parameters = new Dictionary(32);
for (int paramIndex = 0; paramIndex < 32 && paramIndex < Animator.parameters.Length; paramIndex++)
{
if (trackedOnly && !GetParamTracking(paramIndex)) continue;
var animParam = Animator.parameters[paramIndex];
var animParamHash = animParam.nameHash;
var animParamType = animParam.type;
object animParamBoxed = null;
switch (animParamType)
{
case AnimatorControllerParameterType.Float:
animParamBoxed = Animator.GetFloat(animParamHash);
break;
case AnimatorControllerParameterType.Int:
animParamBoxed = Animator.GetInteger(animParamHash);
break;
case AnimatorControllerParameterType.Bool:
animParamBoxed = Animator.GetBool(animParamHash);
break;
}
animParams.Parameters.Add(animParamHash, (animParamType, animParamBoxed));
}
return animParams;
}
private void SetAnimParams(AnimParams animParams)
{
foreach (var animParam in animParams.Parameters)
{
switch (animParam.Value.Type)
{
case AnimatorControllerParameterType.Float:
Animator.SetFloat(animParam.Key, (float)animParam.Value.Boxed);
break;
case AnimatorControllerParameterType.Int:
Animator.SetInteger(animParam.Key, (int)animParam.Value.Boxed);
break;
case AnimatorControllerParameterType.Bool:
Animator.SetBool(animParam.Key, (bool)animParam.Value.Boxed);
break;
}
}
}
private void SendTrackedParams()
{
var animParams = GetAnimParams( /* trackedOnly = */ true);
if (IsServer)
{
var clientRpcParams = new ClientRpcParams
{
Send = new ClientRpcSendParams
{
TargetClientIds = NetworkManager.Singleton.ConnectedClientsList
.Where(c => c.ClientId != NetworkManager.Singleton.ServerClientId)
.Select(c => c.ClientId)
.ToArray()
}
};
UpdateTrackedParamsClientRpc(animParams, clientRpcParams);
}
else
{
UpdateTrackedParamsServerRpc(animParams);
}
}
private void SendAllParamsAndState(int animStateHash, float animStateTime)
{
var animParams = GetAnimParams();
if (IsServer)
{
var clientRpcParams = new ClientRpcParams
{
Send = new ClientRpcSendParams
{
TargetClientIds = NetworkManager.Singleton.ConnectedClientsList
.Where(c => c.ClientId != NetworkManager.Singleton.ServerClientId)
.Select(c => c.ClientId)
.ToArray()
}
};
UpdateAnimStateClientRpc(animStateHash, animStateTime, clientRpcParams);
UpdateTrackedParamsClientRpc(animParams, clientRpcParams);
}
else
{
UpdateAnimStateServerRpc(animStateHash, animStateTime);
UpdateTrackedParamsServerRpc(animParams);
}
}
[ServerRpc]
private void UpdateTrackedParamsServerRpc(AnimParams animParams, ServerRpcParams serverRpcParams = default)
{
if (IsOwner) return;
SetAnimParams(animParams);
var clientRpcParams = new ClientRpcParams
{
Send = new ClientRpcSendParams
{
TargetClientIds = NetworkManager.Singleton.ConnectedClientsList
.Where(c => c.ClientId != serverRpcParams.Receive.SenderClientId)
.Select(c => c.ClientId)
.ToArray()
}
};
UpdateTrackedParamsClientRpc(animParams, clientRpcParams);
}
[ClientRpc]
private void UpdateTrackedParamsClientRpc(AnimParams animParams, ClientRpcParams clientRpcParams = default)
{
if (IsOwner) return;
SetAnimParams(animParams);
}
[ServerRpc]
private void UpdateAnimStateServerRpc(int animStateHash, float animStateTime, ServerRpcParams serverRpcParams = default)
{
if (IsOwner) return;
Animator.Play(animStateHash, 0, animStateTime);
var clientRpcParams = new ClientRpcParams
{
Send = new ClientRpcSendParams
{
TargetClientIds = NetworkManager.Singleton.ConnectedClientsList
.Where(c => c.ClientId != serverRpcParams.Receive.SenderClientId)
.Select(c => c.ClientId)
.ToArray()
}
};
UpdateAnimStateClientRpc(animStateHash, animStateTime, clientRpcParams);
}
[ClientRpc]
private void UpdateAnimStateClientRpc(int animStateHash, float animStateTime, ClientRpcParams clientRpcParams = default)
{
if (IsOwner) return;
Animator.Play(animStateHash, 0, animStateTime);
}
}
}