using GeoSus.Client; using System.Collections; using System.Threading.Tasks; using UnityEngine; using System.Collections.Generic; using Subsystems; using System.Linq; using UnityEngine.SceneManagement; namespace Subsystems { public class GameManager_Network { private const string _serverAddress = "geosus.honzuvkod.dev"; private const int _serverPort = 7777; private GameClient _gameClient; private GameManager _manager; // may be null for test clients private bool _pendingMapBuild; public GameManager_Network(GameClient gameClient, GameManager manager) { _gameClient = gameClient; _manager = manager; RegisterEventHandlers(); } public async void OpenConnection() { int retries = 0; int delayMs = 5000; while (true) { Task state = _gameClient.ConnectAsync(_serverAddress, _serverPort); await state; if (state.Result) { Debug.Log("Connected to server."); break; } retries++; if (retries >= 10) { Debug.LogError("Failed to connect after 10 attempts. Giving up."); break; } Debug.Log($"Failed to connect (attempt {retries}). Retrying in {delayMs / 1000}s..."); await Task.Delay(delayMs); delayMs = Mathf.Min(delayMs * 2, 30000); // exponential backoff, cap 30s } } public void RegisterEventHandlers() { _gameClient.OnConnected += OnConnected; _gameClient.OnDisconnected += OnDisconnected; _gameClient.OnError += OnError; _gameClient.OnMessage += OnMessage; _gameClient.OnGameEvent += OnGameEvent; } private void OnConnected() { Debug.Log("Successfully connected to the server."); } private void OnDisconnected(string reason) { Debug.Log($"Disconnected: {reason}"); // Auto-reconnect unless the app is quitting if (reason != "Disposed" && _manager != null) _manager.StartCoroutine(ReconnectAfterDelay(3f)); } private System.Collections.IEnumerator ReconnectAfterDelay(float seconds) { yield return new UnityEngine.WaitForSeconds(seconds); Debug.Log("Attempting to reconnect..."); OpenConnection(); } private void OnError(string error) { Debug.LogError($"Network error: {error}"); } private void OnMessage(Message message) { switch (message.Type) { case "GameEvent": // handled via OnGameEvent break; case "CreateLobbyResponse": HandleCreateLobbyResponse(message as CreateLobbyResponse); break; case "JoinLobbyResponse": HandleJoinLobbyResponse(message as JoinLobbyResponse); break; case "PositionBroadcast": HandlePositionBroadcast(message as PositionBroadcast); break; case "Ack": break; default: Debug.Log("Received message of type: " + message.Type); break; } } private void OnGameEvent(GameEvent gameEvent) { switch (gameEvent.EventType) { case "PlayerJoined": case "PlayerLeft": case "HostChanged": // SDK already updates CurrentLobbyState; just refresh UI _manager?.uiSubsystem?.NotifyLobbyChanged(); break; case "GameStarting": HandleGameStarting(); break; case "MapDataReady": HandleMapDataReady(); break; case "GameStarted": HandleGameStarted(); break; case "RoleAssigned": HandleRoleAssigned(gameEvent); break; case "TaskCompleted": HandleTaskCompleted(gameEvent); break; case "PlayerKilled": HandlePlayerKilled(gameEvent); break; case "BodyReported": case "EmergencyMeetingCalled": HandleMeetingCalled(gameEvent); break; case "MeetingStarted": HandleMeetingStarted(gameEvent); break; case "VotingClosed": HandleVotingClosed(gameEvent); break; case "GameEnded": HandleGameEnded(gameEvent); break; case "ReturnedToLobby": HandleReturnedToLobby(); break; case "SabotageStarted": HandleSabotageStarted(gameEvent); break; case "SabotageRepaired": case "SabotageMeltdown": _manager?.uiSubsystem?.HideSabotageTimer(); _manager?.mapSubsystem?.ClearSabotageMarkers(); break; case "MapDataError": Debug.LogError("Server could not generate map data."); break; default: Debug.Log("GameEvent: " + gameEvent.EventType); break; } } // ── Lobby responses ─────────────────────────────────────────────────── private void HandleCreateLobbyResponse(CreateLobbyResponse message) { if (message == null) return; if (message.Success) { Debug.Log($"Lobby created. Code: {message.JoinCode}, ID: {message.LobbyId}"); // Navigate to the create/waiting scene SceneManager.LoadScene("create", LoadSceneMode.Single); // Mark lobby UI dirty so LobbyDisplayUI refreshes once the scene is loaded _manager?.uiSubsystem?.NotifyLobbyChanged(); } else { Debug.LogError("Failed to create lobby: " + message.Error); } } private void HandleJoinLobbyResponse(JoinLobbyResponse message) { if (message == null) return; if (message.Success) { Debug.Log($"Joined lobby: {message.LobbyId}"); SceneManager.LoadScene("join loading", LoadSceneMode.Single); // Mark lobby UI dirty so LobbyDisplayUI refreshes once the scene is loaded _manager?.uiSubsystem?.NotifyLobbyChanged(); } else { Debug.LogError("Failed to join lobby: " + message.Error); } } // ── Game flow events ────────────────────────────────────────────────── private void HandleGameStarting() { _pendingMapBuild = false; // SDK sets Phase = Loading; load Client.unity SceneManager.LoadScene("Client", LoadSceneMode.Single); } private void HandleMapDataReady() { _pendingMapBuild = true; TryBuildMapAndMarkers(); } /// /// Called from GameManager.OnSceneLoaded("Client") after scene objects are bound. /// Ensures map construction still happens even if MapDataReady arrived earlier. /// public void OnClientSceneReady() { TryBuildMapAndMarkers(); } private void TryBuildMapAndMarkers() { if (!_pendingMapBuild) return; if (_manager?.mapSubsystem == null) return; if (!_manager.mapSubsystem.IsSceneReady) return; if (_gameClient?.CurrentLobbyState?.MapData == null) return; _manager.mapSubsystem.BuildMap(); _manager.mapSubsystem.CreateTaskMarkers(_gameClient.MyTasks); _pendingMapBuild = false; Debug.Log("[Network] Map built and task markers refreshed."); } private void HandleGameStarted() { Debug.Log("Game started"); // Phase is now Playing; GPS loop will start sending positions } private void HandleRoleAssigned(GameEvent evt) { var payload = evt.GetPayload(); if (payload == null || payload.ClientUuid != _gameClient.ClientUuid) return; Debug.Log($"Role: {payload.Role}, Tasks: {payload.Tasks?.Count ?? 0}"); _manager?.taskSubsystem?.Initialize(_gameClient.MyTasks); } private void HandleTaskCompleted(GameEvent evt) { var payload = evt.GetPayload(); if (payload == null) return; _manager?.uiSubsystem?.UpdateTaskProgress(payload.TotalCompleted, payload.TotalTasks); _manager?.mapSubsystem?.RemoveTaskMarker(payload.TaskId); } private void HandlePlayerKilled(GameEvent evt) { var payload = evt.GetPayload(); if (payload == null) return; _manager?.mapSubsystem?.CreateBodyMarker(payload.BodyId, payload.Location); if (payload.VictimId == _gameClient.ClientUuid) _manager?.uiSubsystem?.OnLocalPlayerDied(); } private void HandleMeetingCalled(GameEvent evt) { _manager?.uiSubsystem?.ShowMeetingAlert(); } private void HandleMeetingStarted(GameEvent evt) { var payload = evt.GetPayload(); if (payload == null) return; _manager?.uiSubsystem?.ShowMeetingPanel(_gameClient.CurrentLobbyState?.Players, payload); } private void HandleVotingClosed(GameEvent evt) { var payload = evt.GetPayload(); if (payload == null) return; _manager?.uiSubsystem?.ShowVoteResult(payload, _gameClient.CurrentLobbyState?.Players); _manager?.mapSubsystem?.ClearBodyMarkers(); } private void HandleGameEnded(GameEvent evt) { var payload = evt.GetPayload(); if (payload == null) return; _manager?.uiSubsystem?.ShowGameEndPanel(payload, _gameClient.ClientUuid); } private void HandleReturnedToLobby() { if (_gameClient.IsOwner) SceneManager.LoadScene("create", LoadSceneMode.Single); else SceneManager.LoadScene("join loading", LoadSceneMode.Single); } private void HandleSabotageStarted(GameEvent evt) { var payload = evt.GetPayload(); if (payload == null) return; _manager?.mapSubsystem?.CreateSabotageMarkers(payload.RepairStations); if (payload.Type == SabotageType.CriticalMeltdown && payload.Deadline.HasValue) _manager?.uiSubsystem?.ShowSabotageTimer(payload.Deadline.Value); if (payload.Type == SabotageType.CommsBlackout) _manager?.uiSubsystem?.SetCommsBlackout(true); } private void HandlePositionBroadcast(PositionBroadcast broadcast) { if (broadcast == null) return; _manager?.mapSubsystem?.UpdatePlayerAvatars(_gameClient.PlayerPositions, _gameClient.ClientUuid); } // ── Send helpers ────────────────────────────────────────────────────── public void CreateLobby(double lat, double lon, double radius = 500, int impostorCount = 1, int taskCount = 5) { _gameClient.CreateLobby(new Position(lat, lon), impostorCount, taskCount, null, radius); } public void JoinLobby(string joinCode) { try { _gameClient.JoinLobby(joinCode); } catch (System.Exception ex) { Debug.LogError("JoinLobby error: " + ex.Message); } } public void LeaveLobby() { _gameClient.LeaveLobby(); SceneManager.LoadScene(_manager?.firstMenuScene ?? "main menu asi idk lol", LoadSceneMode.Single); } public void StartGame() { _gameClient.StartGame(); } } }