using System; using System.Collections.Generic; using Data.ScriptableObjects; using FishNet.Connection; using FishNet.Object; using FishNet.Object.Synchronizing; using Types; using UnityEngine; using UnityEngine.Serialization; namespace Managers { public class GameManager : NetworkBehaviour { [SerializeField] private SaveManager saveManager; [SerializeField] private HotkeyManager hotkeyManager; [SerializeField] private AudioManager audioManager; [SerializeField] private UIManager uiManager; [SerializeField] private SyncVar isPaused; // person who's running the game, owner. [FormerlySerializedAs("playerManager")] [SerializeField] private PlayerManager ownerPlayerManager; [SerializeField] public ItemDatabase itemDatabase; private readonly SyncVar _syncGameSeed = new(new SyncTypeSettings { WritePermission = WritePermission.ServerOnly, ReadPermission = ReadPermission.Observers }); private readonly SyncVar _syncGameStatus = new(new SyncTypeSettings { WritePermission = WritePermission.ServerOnly, ReadPermission = ReadPermission.Observers }); private readonly SyncVar _syncGameTimeSettings = new(new SyncTypeSettings { WritePermission = WritePermission.ServerOnly, ReadPermission = ReadPermission.Observers }); private readonly SyncDictionary _syncPlayers = new(new SyncTypeSettings { WritePermission = WritePermission.ServerOnly, ReadPermission = ReadPermission.Observers }); private readonly SyncVar _syncWeather = new(new SyncTypeSettings { WritePermission = WritePermission.ServerOnly, ReadPermission = ReadPermission.Observers }); public static GameManager Instance { get; set; } private void Awake() { Debug.Log("Starting GameManager"); itemDatabase = Resources.Load("GameItems/ItemDatabase"); // Players = new Dictionary(), // TimeSettings = new GameTimeSettings(), // GameStatus = GameStatus.WaitingForPlayers, // Weather = WeatherConditions.Sunny, // Initialize _syncGameTimeSettings with a default value _syncGameTimeSettings.Value = new GameTimeSettings(); _syncPlayers.OnChange += SyncPlayersChange; _syncGameTimeSettings.OnChange += SyncGameTimeSettingsChange; _syncGameStatus.OnChange += SyncGameStatusChange; _syncWeather.OnChange += SyncWeatherChange; if (Instance != null) { Destroy(this); return; } Instance = this; } private void OnDestroy() { if (Instance == this) Instance = null; Debug.Log("GameManager destroyed"); } // set default server variable value public override void OnStartServer() { base.OnStartServer(); Debug.Log("GameManager.OnStartServer() triggered"); if (IsServerInitialized) InitializeServerData(); // create a new instance of GameTimeManager and instantiate it on the server if (GameTimeManager.Instance == null) { Debug.Log("GameTimeManager not found, creating new one"); gameObject.AddComponent(); // base.Spawn(GameTimeManager.Instance); // spawn on server } } private void InitializeServerData() { if (_syncGameTimeSettings.Value == null) _syncGameTimeSettings.SetInitialValues(new GameTimeSettings()); } private void SyncWeatherChange(WeatherConditions prev, WeatherConditions next, bool asserver) { throw new NotImplementedException(); } private void SyncGameStatusChange(GameStatus prev, GameStatus next, bool asserver) { throw new NotImplementedException(); } private void SyncGameTimeSettingsChange(GameTimeSettings prev, GameTimeSettings next, bool asserver) { Debug.Log("SyncGameTimeSettingsChange Triggered"); Debug.Log("Prev: " + prev.CurrentTime); Debug.Log("Next: " + next.CurrentTime); } private void SyncPlayersChange(SyncDictionaryOperation op, int key, GamePlayer value, bool asServer) { Debug.Log($"SyncPlayersChange Triggered - Operation: {op}, Key: {key}, AsServer: {asServer}"); switch (op) { case SyncDictionaryOperation.Add: Debug.Log($"Player added - ID: {key}, Name: {value.PlayerName}"); // Implement logic for when a new player is added OnPlayerAdded(key, value); break; case SyncDictionaryOperation.Remove: Debug.Log($"Player removed - ID: {key}"); // Implement logic for when a player is removed OnPlayerRemoved(key); break; case SyncDictionaryOperation.Clear: Debug.Log("All players cleared"); // Implement logic for when all players are cleared OnAllPlayersCleared(); break; case SyncDictionaryOperation.Set: Debug.Log($"Player updated - ID: {key}, Name: {value.PlayerName}"); // Implement logic for when a player's data is updated OnPlayerUpdated(key, value); break; } // Notify any listeners that the player list has changed OnPlayersListChanged(); } private void OnPlayerAdded(int playerId, GamePlayer player) { // Implement logic for when a new player is added // For example, update UI, spawn player object, etc. } private void OnPlayerRemoved(int playerId) { // Implement logic for when a player is removed // For example, remove player from UI, destroy player object, etc. } private void OnAllPlayersCleared() { // Implement logic for when all players are cleared // For example, reset UI, clear all player objects, etc. } private void OnPlayerUpdated(int playerId, GamePlayer player) { // Implement logic for when a player's data is updated // For example, update UI elements, update player object properties, etc. } private void OnPlayersListChanged() { // Notify any parts of your game that need to know when the player list changes // For example, update a player list UI, recalculate game state, etc. } [ServerRpc(RequireOwnership = false)] public void RegisterPlayerServerRpc(NetworkConnection conn, GamePlayer player) { try { Debug.Log( $"Attempting to register player. Connection ID: {conn?.ClientId}, Player null?: {player == null}"); if (conn == null) { Debug.LogError("RegisterPlayerServerRpc: NetworkConnection is null"); return; } if (player == null) { Debug.LogError($"RegisterPlayerServerRpc: GamePlayer is null for connection {conn.ClientId}"); return; } if (_syncPlayers.TryAdd(conn.ClientId, player)) { Debug.Log($"Player registered successfully: {conn.ClientId}"); OnPlayerRegisteredObserversRpc(conn.ClientId); _syncPlayers.Dirty(conn.ClientId); } else { Debug.LogWarning($"Player with ID {conn.ClientId} is already registered"); } } catch (Exception e) { Debug.LogError($"Error in RegisterPlayerServerRpc: {e.Message}\nStack Trace: {e.StackTrace}"); } } [ObserversRpc] private void OnPlayerRegisteredObserversRpc(int clientId) { Debug.Log($"OBS: Player with ID {clientId} has joined the game!"); // You can add any client-side logic here when a new player joins } public override void OnStartNetwork() { base.OnStartNetwork(); Debug.Log("Network started"); } // method to handle player connections public override void OnStartClient() { Debug.Log("Client connected to server"); base.OnStartClient(); if (IsServerInitialized) Instance = this; // Server logic for new player connections } // get player by client id public GamePlayer GetPlayer(int clientId) { // Debug.Log("Getting player: " + clientId); if (_syncPlayers.Count == 0) { Debug.LogWarning("Players not yet initialized"); return null; } // Debug.Log("Players: " + _syncGameState.Value.Players.Count); return _syncPlayers.GetValueOrDefault(clientId); } public Dictionary GetPlayers() { return (Dictionary)_syncPlayers.Values; } public override void OnStopClient() { base.OnStopClient(); // client specific cleanup code? if (IsServerInitialized) { // server logic for disconnection } // client logic for disconnection } [ServerRpc(RequireOwnership = false)] public void StartGameServerRpc() { UIManager.ShowMainMenuScreen(false); // AudioManager.PlayMusic("MainTheme"); } [ObserversRpc] public void StartGameObserversRpc() { UIManager.ShowMainMenuScreen(false); } [ServerRpc(RequireOwnership = false)] // client ot server comm? i.e client is telling the server to run this code public void EndGameServerRpc() { Debug.Log("ServerRPC Game Over"); UIManager.ShowGameOverScreen(true); // AudioManager.PlayMusic("GameOverTheme"); } [ObserversRpc] // server to client communication i.e server is telling the client to run this code public void EndGameObserversRpc() { Debug.Log("ObserverRPC Game Over"); UIManager.ShowGameOverScreen(true); } public void StartGame() { if (IsServerInitialized) StartGameServerRpc(); else StartGameObserversRpc(); } public GameTimeSettings GetTimeSettings() { Debug.Log("GameManager.GetTimeSettings()"); Debug.Log("GameTimeSettings: " + _syncGameTimeSettings.Value); return _syncGameTimeSettings.Value; } private static GameStateChange GetGameStateChanges(GameState oldState, GameState newState) { var changes = new GameStateChange(); // Compare players foreach (var kvp in newState.Players) if (!oldState.Players.TryGetValue(kvp.Key, out var oldPlayer) || !oldPlayer.Equals(kvp.Value)) changes.PlayerChanges[kvp.Key] = kvp.Value; // Compare TimeSettings if (!oldState.TimeSettings.Equals(newState.TimeSettings)) changes.TimeSettingsChanges = newState.TimeSettings; // Compare GameStatus if (oldState.GameStatus != newState.GameStatus) changes.StatusChange = newState.GameStatus; // Compare Weather if (oldState.Weather != newState.Weather) changes.WeatherChange = newState.Weather; // Compare GameSeed if (oldState.GameSeed != newState.GameSeed) changes.GameSeedChange = newState.GameSeed; return changes; } [Server] private void UpdatePlayer(int playerId, GamePlayer player) { Debug.Log("Updating player: " + playerId); _syncPlayers[playerId] = player; _syncPlayers.Dirty(playerId); } [Server] private void UpdateTimeSettings(GameTimeSettings timeSettings) { _syncGameTimeSettings.Value = new GameTimeSettings(timeSettings); } [Server] private void UpdateGameStatus(GameStatus status) { _syncGameStatus.Value = status; } [Server] private void UpdateWeather(WeatherConditions weather) { _syncWeather.Value = weather; } [ServerRpc(RequireOwnership = false)] public void RequestUpdatePlayerServerRpc(int playerId, GamePlayer player, NetworkConnection sender = null) { // Validate the request (e.g., ensure the sender is allowed to update this player) if (sender != null && (sender.ClientId == playerId || IsServerInitialized)) UpdatePlayer(playerId, player); } [ServerRpc(RequireOwnership = false)] public void RequestUpdateTimeSettingsServerRpc(GameTimeSettings timeSettings) { Debug.Log("RequestUpdateTimeSettingsServerRpc Triggered"); Debug.Log("CurrentTime: " + timeSettings.CurrentTime); // You might want to add validation here UpdateTimeSettings(timeSettings); } [ServerRpc(RequireOwnership = false)] public void RequestUpdateGameStatusServerRpc(GameStatus status) { // Add validation if needed UpdateGameStatus(status); } [ServerRpc(RequireOwnership = false)] public void RequestUpdateWeatherServerRpc(WeatherConditions weather) { // Add validation if needed UpdateWeather(weather); } public GamePlayer GetCurrentPlayer() { if (!IsServerInitialized && !IsClientInitialized) { Debug.LogError("GameManager not initialized"); return null; } var clientId = ClientManager.Connection.ClientId; if (_syncPlayers.TryGetValue(clientId, out var player)) { Debug.Log("Found player for clientId: " + clientId); Debug.Log("Player name: " + player.PlayerName); return player; } Debug.LogError("Player not found for clientId: " + clientId); return null; } public void PauseGame() { isPaused.Value = true; } public void ResumeGame() { isPaused.Value = false; } } }