177 lines
5.1 KiB
C#
177 lines
5.1 KiB
C#
using System.Collections.Generic;
|
|
using Steamworks;
|
|
using UnityEngine;
|
|
|
|
namespace MegaKoop.Game.Networking
|
|
{
|
|
/// <summary>
|
|
/// Lightweight cache of the latest character transform messages so systems (AI, prediction) can query peer positions.
|
|
/// </summary>
|
|
[DefaultExecutionOrder(-900)]
|
|
public class SteamCharacterStateCache : MonoBehaviour
|
|
{
|
|
public readonly struct CharacterState
|
|
{
|
|
public readonly int NetworkId;
|
|
public readonly Vector3 Position;
|
|
public readonly Quaternion Rotation;
|
|
public readonly Vector3 Velocity;
|
|
public readonly float LastUpdateTime;
|
|
public readonly ulong SourceSteamId;
|
|
|
|
public CharacterState(int networkId, Vector3 position, Quaternion rotation, Vector3 velocity, float timestamp, ulong steamId)
|
|
{
|
|
NetworkId = networkId;
|
|
Position = position;
|
|
Rotation = rotation;
|
|
Velocity = velocity;
|
|
LastUpdateTime = timestamp;
|
|
SourceSteamId = steamId;
|
|
}
|
|
}
|
|
|
|
private static SteamCharacterStateCache instance;
|
|
|
|
private readonly Dictionary<int, CharacterState> states = new();
|
|
private SteamCoopNetworkManager networkManager;
|
|
private bool isRegistered;
|
|
|
|
public static SteamCharacterStateCache Instance
|
|
{
|
|
get
|
|
{
|
|
if (instance == null)
|
|
{
|
|
var go = new GameObject("SteamCharacterStateCache");
|
|
instance = go.AddComponent<SteamCharacterStateCache>();
|
|
DontDestroyOnLoad(go);
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
if (instance != null && instance != this)
|
|
{
|
|
Destroy(gameObject);
|
|
return;
|
|
}
|
|
|
|
instance = this;
|
|
DontDestroyOnLoad(gameObject);
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
EnsureSubscription();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
EnsureSubscription();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
Unsubscribe();
|
|
}
|
|
|
|
private void EnsureSubscription()
|
|
{
|
|
var current = SteamCoopNetworkManager.Instance;
|
|
if (current == networkManager)
|
|
{
|
|
if (networkManager != null && !isRegistered)
|
|
{
|
|
networkManager.RegisterHandler(NetworkMessageType.CharacterTransform, HandleCharacterTransformMessage);
|
|
isRegistered = true;
|
|
}
|
|
return;
|
|
}
|
|
|
|
Unsubscribe();
|
|
networkManager = current;
|
|
|
|
if (networkManager != null)
|
|
{
|
|
networkManager.RegisterHandler(NetworkMessageType.CharacterTransform, HandleCharacterTransformMessage);
|
|
isRegistered = true;
|
|
}
|
|
}
|
|
|
|
private void Unsubscribe()
|
|
{
|
|
if (networkManager != null && isRegistered)
|
|
{
|
|
networkManager.UnregisterHandler(NetworkMessageType.CharacterTransform, HandleCharacterTransformMessage);
|
|
isRegistered = false;
|
|
}
|
|
}
|
|
|
|
private void HandleCharacterTransformMessage(NetworkMessage message)
|
|
{
|
|
CharacterTransformMessage transformMessage = CharacterTransformMessage.Deserialize(message.Payload);
|
|
StoreState(transformMessage.NetworkId, transformMessage.Position, transformMessage.Rotation, transformMessage.Velocity, message.Sender);
|
|
}
|
|
|
|
private void StoreState(int networkId, Vector3 position, Quaternion rotation, Vector3 velocity, ulong steamId)
|
|
{
|
|
if (networkId == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
states[networkId] = new CharacterState(networkId, position, rotation, velocity, Time.time, steamId);
|
|
}
|
|
|
|
private static ulong GetLocalSteamId()
|
|
{
|
|
#if STEAMWORKSNET
|
|
if (MegaKoop.Steam.SteamManager.Initialized)
|
|
{
|
|
return SteamUser.GetSteamID().m_SteamID;
|
|
}
|
|
#else
|
|
if (SteamBootstrap.IsInitialized)
|
|
{
|
|
return SteamUser.GetSteamID().m_SteamID;
|
|
}
|
|
#endif
|
|
return 0UL;
|
|
}
|
|
|
|
public static bool TryGetState(int networkId, out CharacterState state)
|
|
{
|
|
if (Instance.states.TryGetValue(networkId, out state))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
state = default;
|
|
return false;
|
|
}
|
|
|
|
public static void ReportLocalState(int networkId, Vector3 position, Quaternion rotation, Vector3 velocity)
|
|
{
|
|
if (networkId == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Instance.StoreState(networkId, position, rotation, velocity, GetLocalSteamId());
|
|
}
|
|
|
|
public static void RemoveState(int networkId)
|
|
{
|
|
if (networkId == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Instance.states.Remove(networkId);
|
|
}
|
|
}
|
|
}
|