366 lines
12 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|