218 lines
6.9 KiB
C#
218 lines
6.9 KiB
C#
using Steamworks;
|
|
using UnityEngine;
|
|
|
|
namespace MegaKoop.Game.Networking
|
|
{
|
|
/// <summary>
|
|
/// Host-authoritative transform replication that mirrors state to all peers via Steam P2P messages.
|
|
/// </summary>
|
|
[DisallowMultipleComponent]
|
|
public class SteamNetworkTransform : MonoBehaviour
|
|
{
|
|
[Header("References")]
|
|
[SerializeField] private Transform targetTransform;
|
|
[SerializeField] private NetworkIdentity identity;
|
|
[SerializeField] private Rigidbody trackedRigidbody;
|
|
[SerializeField] private UnityEngine.AI.NavMeshAgent trackedNavMeshAgent;
|
|
|
|
[Header("Authority")]
|
|
[SerializeField] private bool authorityIsHost = true;
|
|
[SerializeField] private bool simulateWhenDisconnected = true;
|
|
|
|
[Header("Synchronization")]
|
|
[SerializeField] private float broadcastInterval = 0.05f;
|
|
[SerializeField] private float remoteLerpSpeed = 12f;
|
|
[SerializeField] private float teleportThreshold = 4f;
|
|
|
|
private SteamCoopNetworkManager networkManager;
|
|
private float broadcastTimer;
|
|
private bool isRegistered;
|
|
private Vector3 remoteTargetPosition;
|
|
private Quaternion remoteTargetRotation;
|
|
private Vector3 remoteTargetVelocity;
|
|
private bool haveRemoteState;
|
|
|
|
private void Awake()
|
|
{
|
|
if (targetTransform == null)
|
|
{
|
|
targetTransform = transform;
|
|
}
|
|
|
|
if (identity == null)
|
|
{
|
|
identity = GetComponent<NetworkIdentity>();
|
|
}
|
|
|
|
if (trackedRigidbody == null)
|
|
{
|
|
trackedRigidbody = GetComponent<Rigidbody>();
|
|
}
|
|
|
|
if (trackedNavMeshAgent == null)
|
|
{
|
|
trackedNavMeshAgent = GetComponent<UnityEngine.AI.NavMeshAgent>();
|
|
}
|
|
|
|
remoteTargetPosition = targetTransform.position;
|
|
remoteTargetRotation = targetTransform.rotation;
|
|
remoteTargetVelocity = Vector3.zero;
|
|
broadcastTimer = Random.Range(0f, Mathf.Max(0.01f, broadcastInterval));
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
EnsureNetworkManager();
|
|
RegisterHandlers();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
UnregisterHandlers();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
RefreshNetworkManager();
|
|
|
|
if (HasAuthority())
|
|
{
|
|
broadcastTimer -= Time.deltaTime;
|
|
if (broadcastTimer <= 0f)
|
|
{
|
|
BroadcastState();
|
|
broadcastTimer = Mathf.Max(0.01f, broadcastInterval);
|
|
}
|
|
}
|
|
else if (haveRemoteState)
|
|
{
|
|
float lerpFactor = Mathf.Clamp01(remoteLerpSpeed * Time.deltaTime);
|
|
Vector3 currentPosition = targetTransform.position;
|
|
float sqrDistance = (remoteTargetPosition - currentPosition).sqrMagnitude;
|
|
if (sqrDistance >= teleportThreshold * teleportThreshold)
|
|
{
|
|
targetTransform.SetPositionAndRotation(remoteTargetPosition, remoteTargetRotation);
|
|
}
|
|
else
|
|
{
|
|
targetTransform.position = Vector3.Lerp(currentPosition, remoteTargetPosition, lerpFactor);
|
|
targetTransform.rotation = Quaternion.Slerp(targetTransform.rotation, remoteTargetRotation, lerpFactor);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RefreshNetworkManager()
|
|
{
|
|
if (networkManager == null)
|
|
{
|
|
networkManager = SteamCoopNetworkManager.Instance;
|
|
if (networkManager != null && isActiveAndEnabled)
|
|
{
|
|
RegisterHandlers();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EnsureNetworkManager()
|
|
{
|
|
RefreshNetworkManager();
|
|
}
|
|
|
|
private bool HasAuthority()
|
|
{
|
|
if (!authorityIsHost)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (networkManager == null)
|
|
{
|
|
return simulateWhenDisconnected;
|
|
}
|
|
|
|
if (!networkManager.IsConnected)
|
|
{
|
|
return simulateWhenDisconnected;
|
|
}
|
|
|
|
return networkManager.IsHost;
|
|
}
|
|
|
|
private void BroadcastState()
|
|
{
|
|
if (identity == null || targetTransform == null || networkManager == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Vector3 velocity = Vector3.zero;
|
|
if (trackedNavMeshAgent != null && trackedNavMeshAgent.enabled)
|
|
{
|
|
velocity = trackedNavMeshAgent.velocity;
|
|
}
|
|
else if (trackedRigidbody != null)
|
|
{
|
|
velocity = trackedRigidbody.velocity;
|
|
}
|
|
|
|
SteamCharacterStateCache.ReportLocalState(identity.NetworkId, targetTransform.position, targetTransform.rotation, velocity);
|
|
var message = new CharacterTransformMessage(identity.NetworkId, targetTransform.position, targetTransform.rotation, velocity);
|
|
byte[] payload = CharacterTransformMessage.Serialize(message);
|
|
networkManager.SendToAll(NetworkMessageType.CharacterTransform, payload, EP2PSend.k_EP2PSendUnreliableNoDelay);
|
|
}
|
|
|
|
private void RegisterHandlers()
|
|
{
|
|
if (isRegistered || networkManager == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
networkManager.RegisterHandler(NetworkMessageType.CharacterTransform, HandleTransformMessage);
|
|
isRegistered = true;
|
|
}
|
|
|
|
private void UnregisterHandlers()
|
|
{
|
|
if (!isRegistered || networkManager == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
networkManager.UnregisterHandler(NetworkMessageType.CharacterTransform, HandleTransformMessage);
|
|
isRegistered = false;
|
|
}
|
|
|
|
private void HandleTransformMessage(NetworkMessage message)
|
|
{
|
|
if (identity == null || targetTransform == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (HasAuthority())
|
|
{
|
|
return;
|
|
}
|
|
|
|
CharacterTransformMessage transformMessage = CharacterTransformMessage.Deserialize(message.Payload);
|
|
if (transformMessage.NetworkId != identity.NetworkId)
|
|
{
|
|
return;
|
|
}
|
|
|
|
remoteTargetPosition = transformMessage.Position;
|
|
remoteTargetRotation = transformMessage.Rotation;
|
|
remoteTargetVelocity = transformMessage.Velocity;
|
|
haveRemoteState = true;
|
|
}
|
|
|
|
private void OnValidate()
|
|
{
|
|
broadcastInterval = Mathf.Max(0.01f, broadcastInterval);
|
|
remoteLerpSpeed = Mathf.Max(0f, remoteLerpSpeed);
|
|
teleportThreshold = Mathf.Max(0.01f, teleportThreshold);
|
|
}
|
|
}
|
|
}
|