Files
megakoop/Game/Scripts/Networking/SteamGameControllerNetworkBridge.cs
2025-10-27 12:37:18 +01:00

366 lines
12 KiB
C#

using Game.Scripts.Runtime.Data;
using Game.Scripts.Runtime.Game;
using Game.Scripts.Runtime.Spawning;
using Steamworks;
using UnityEngine;
namespace MegaKoop.Game.Networking
{
[DefaultExecutionOrder(-140)]
[DisallowMultipleComponent]
public class SteamGameControllerNetworkBridge : MonoBehaviour
{
[Header("References")]
[SerializeField] private GameController gameController;
[SerializeField] private EnemySpawner enemySpawner;
[Header("Heartbeat")]
[SerializeField, Min(0.1f)] private float heartbeatInterval = 0.5f;
private SteamCoopNetworkManager networkManager;
private bool handlersRegistered;
private bool controllerSubscribed;
private bool cachedAuthority;
private float heartbeatTimer;
private int lastWaveBroadcast = -1;
private void Awake()
{
gameController ??= GetComponent<GameController>();
enemySpawner ??= GetComponent<EnemySpawner>();
cachedAuthority = DetermineAuthority();
ApplyAuthorityOverride(cachedAuthority);
RegisterBossDefinitions();
}
private void OnEnable()
{
RefreshNetworkManager();
RegisterHandlers();
SubscribeControllerEvents();
cachedAuthority = DetermineAuthority();
ApplyAuthorityOverride(cachedAuthority);
heartbeatTimer = heartbeatInterval;
RegisterBossDefinitions();
}
private void OnDisable()
{
UnsubscribeControllerEvents();
UnregisterHandlers();
}
private void Update()
{
RefreshNetworkManager();
bool authority = DetermineAuthority();
if (authority != cachedAuthority)
{
cachedAuthority = authority;
ApplyAuthorityOverride(authority);
}
if (authority)
{
RunHeartbeat(Time.deltaTime);
}
}
private void RunHeartbeat(float deltaTime)
{
heartbeatTimer -= deltaTime;
if (heartbeatTimer > 0f)
{
return;
}
heartbeatTimer = Mathf.Max(0.1f, heartbeatInterval);
SendState(GameStateEvent.Heartbeat, lastWaveBroadcast);
}
private void SubscribeControllerEvents()
{
if (gameController == null || controllerSubscribed)
{
return;
}
gameController.OnGameStarted += HandleGameStarted;
gameController.OnGamePaused += HandleGamePaused;
gameController.OnGameResumed += HandleGameResumed;
gameController.OnGameStopped += HandleGameStopped;
gameController.OnWaveStarted += HandleWaveStarted;
gameController.OnBossSpawned += HandleBossSpawned;
controllerSubscribed = true;
}
private void UnsubscribeControllerEvents()
{
if (gameController == null || !controllerSubscribed)
{
return;
}
gameController.OnGameStarted -= HandleGameStarted;
gameController.OnGamePaused -= HandleGamePaused;
gameController.OnGameResumed -= HandleGameResumed;
gameController.OnGameStopped -= HandleGameStopped;
gameController.OnWaveStarted -= HandleWaveStarted;
gameController.OnBossSpawned -= HandleBossSpawned;
controllerSubscribed = false;
}
private void HandleGameStarted()
{
if (!DetermineAuthority())
{
return;
}
SendState(GameStateEvent.Started, -1);
lastWaveBroadcast = gameController != null ? gameController.CurrentWaveIndex : -1;
}
private void HandleGamePaused()
{
if (!DetermineAuthority())
{
return;
}
SendState(GameStateEvent.Paused, gameController != null ? gameController.CurrentWaveIndex : -1);
}
private void HandleGameResumed()
{
if (!DetermineAuthority())
{
return;
}
SendState(GameStateEvent.Resumed, gameController != null ? gameController.CurrentWaveIndex : -1);
}
private void HandleGameStopped()
{
if (!DetermineAuthority())
{
return;
}
SendState(GameStateEvent.Stopped, gameController != null ? gameController.CurrentWaveIndex : -1);
lastWaveBroadcast = -1;
}
private void HandleWaveStarted(int waveIndex)
{
if (!DetermineAuthority())
{
return;
}
int previousWave = lastWaveBroadcast >= 0 ? lastWaveBroadcast : waveIndex - 1;
lastWaveBroadcast = waveIndex;
SendState(GameStateEvent.WaveAdvanced, previousWave);
}
private void HandleBossSpawned(EnemyDefinition boss, int count)
{
if (!DetermineAuthority())
{
return;
}
EnemyDefinitionRegistry.Register(boss);
string definitionId = EnemyDefinitionRegistry.ResolveId(boss);
SendState(GameStateEvent.BossSpawned, lastWaveBroadcast, definitionId, count);
}
private void SendState(GameStateEvent evt, int previousWave, string definitionId = "", int count = 0)
{
if (networkManager == null || gameController == null)
{
return;
}
float elapsed = gameController.Elapsed;
int currentWave = gameController.CurrentWaveIndex;
bool isPaused = gameController.IsPaused;
bool isRunning = gameController.IsGameActive;
var message = new GameStateMessage(evt, elapsed, currentWave, previousWave, isPaused, isRunning, definitionId, count);
byte[] payload = GameStateMessage.Serialize(message);
var sendType = evt == GameStateEvent.Heartbeat ? EP2PSend.k_EP2PSendUnreliableNoDelay : EP2PSend.k_EP2PSendReliable;
networkManager.SendToAll(NetworkMessageType.GameState, payload, sendType);
}
private void RegisterHandlers()
{
if (handlersRegistered)
{
return;
}
RefreshNetworkManager();
if (networkManager == null)
{
return;
}
networkManager.RegisterHandler(NetworkMessageType.GameState, HandleGameStateMessage);
handlersRegistered = true;
}
private void UnregisterHandlers()
{
if (!handlersRegistered || networkManager == null)
{
return;
}
networkManager.UnregisterHandler(NetworkMessageType.GameState, HandleGameStateMessage);
handlersRegistered = false;
}
private void HandleGameStateMessage(NetworkMessage message)
{
if (DetermineAuthority())
{
return;
}
GameStateMessage state = GameStateMessage.Deserialize(message.Payload);
ApplyRemoteState(state);
}
private void ApplyRemoteState(GameStateMessage state)
{
if (gameController == null)
{
return;
}
switch (state.Event)
{
case GameStateEvent.Started:
if (!gameController.IsGameActive)
{
gameController.StartGame();
}
gameController.SetElapsedFromRemote(state.Elapsed);
gameController.SetRunningFlagsFromRemote(state.IsRunning, state.IsPaused);
gameController.ApplyRemoteWaveStarted(state.CurrentWave);
lastWaveBroadcast = state.CurrentWave;
break;
case GameStateEvent.Paused:
if (!gameController.IsPaused && gameController.IsGameActive)
{
gameController.PauseGame();
}
gameController.SetElapsedFromRemote(state.Elapsed);
gameController.SetRunningFlagsFromRemote(state.IsRunning, state.IsPaused);
break;
case GameStateEvent.Resumed:
if (gameController.IsPaused)
{
gameController.ResumeGame();
}
gameController.SetElapsedFromRemote(state.Elapsed);
gameController.SetRunningFlagsFromRemote(state.IsRunning, state.IsPaused);
break;
case GameStateEvent.Stopped:
if (gameController.IsGameActive)
{
gameController.StopGame();
}
lastWaveBroadcast = -1;
gameController.SetElapsedFromRemote(state.Elapsed);
gameController.SetRunningFlagsFromRemote(state.IsRunning, state.IsPaused);
break;
case GameStateEvent.WaveAdvanced:
gameController.SetElapsedFromRemote(state.Elapsed);
gameController.ApplyRemoteWaveAdvance(state.PreviousWave, state.CurrentWave);
lastWaveBroadcast = state.CurrentWave;
break;
case GameStateEvent.BossSpawned:
gameController.SetElapsedFromRemote(state.Elapsed);
if (EnemyDefinitionRegistry.TryGet(state.DefinitionId, out var bossDefinition))
{
gameController.ApplyRemoteBossSpawned(bossDefinition, state.Count);
}
break;
case GameStateEvent.Heartbeat:
gameController.SetElapsedFromRemote(state.Elapsed);
gameController.SetRunningFlagsFromRemote(state.IsRunning, state.IsPaused);
if (state.CurrentWave >= 0 && state.CurrentWave != gameController.CurrentWaveIndex)
{
gameController.ApplyRemoteWaveAdvance(gameController.CurrentWaveIndex, state.CurrentWave);
lastWaveBroadcast = state.CurrentWave;
}
break;
}
}
private void RefreshNetworkManager()
{
if (networkManager == null)
{
networkManager = SteamCoopNetworkManager.Instance;
}
}
private bool DetermineAuthority()
{
RefreshNetworkManager();
if (networkManager == null)
{
return true;
}
if (!networkManager.IsConnected)
{
return true;
}
return networkManager.IsHost;
}
private void ApplyAuthorityOverride(bool authority)
{
gameController?.SetAuthorityOverride(authority);
enemySpawner?.SetAuthorityOverride(authority);
}
private void RegisterBossDefinitions()
{
if (gameController?.BossSchedule?.Events == null)
{
return;
}
foreach (var bossEvent in gameController.BossSchedule.Events)
{
if (bossEvent?.Boss == null)
{
continue;
}
EnemyDefinitionRegistry.Register(bossEvent.Boss);
}
}
}
}