Zabiju je 2. Epicky thriller od tvurce Zabiju je
This commit is contained in:
@@ -14,9 +14,14 @@ namespace Subsystems
|
||||
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 GameManager _manager;
|
||||
private bool _pendingMapBuild;
|
||||
|
||||
/// <summary>
|
||||
/// Authoritative game state; written here, read by GameManager_UI.
|
||||
/// </summary>
|
||||
public GameState State { get; } = new GameState();
|
||||
|
||||
public GameManager_Network(GameClient gameClient, GameManager manager)
|
||||
{
|
||||
_gameClient = gameClient;
|
||||
@@ -45,49 +50,40 @@ namespace Subsystems
|
||||
}
|
||||
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
|
||||
delayMs = Mathf.Min(delayMs * 2, 30000);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterEventHandlers()
|
||||
{
|
||||
_gameClient.OnConnected += OnConnected;
|
||||
_gameClient.OnConnected += OnConnected;
|
||||
_gameClient.OnDisconnected += OnDisconnected;
|
||||
_gameClient.OnError += OnError;
|
||||
_gameClient.OnMessage += OnMessage;
|
||||
_gameClient.OnGameEvent += OnGameEvent;
|
||||
_gameClient.OnError += OnError;
|
||||
_gameClient.OnMessage += OnMessage;
|
||||
_gameClient.OnGameEvent += OnGameEvent;
|
||||
}
|
||||
|
||||
private void OnConnected()
|
||||
{
|
||||
Debug.Log("Successfully connected to the server.");
|
||||
}
|
||||
private void OnConnected() => Debug.Log("Successfully connected to the server.");
|
||||
private void OnError(string e) => Debug.LogError($"Network error: {e}");
|
||||
|
||||
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)
|
||||
private 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;
|
||||
@@ -98,6 +94,7 @@ namespace Subsystems
|
||||
HandlePositionBroadcast(message as PositionBroadcast);
|
||||
break;
|
||||
case "Ack":
|
||||
case "GameEvent":
|
||||
break;
|
||||
default:
|
||||
Debug.Log("Received message of type: " + message.Type);
|
||||
@@ -107,16 +104,19 @@ namespace Subsystems
|
||||
|
||||
private void OnGameEvent(GameEvent gameEvent)
|
||||
{
|
||||
// Always sync player list from lobby state after any event
|
||||
SyncPlayersFromLobby();
|
||||
|
||||
switch (gameEvent.EventType)
|
||||
{
|
||||
case "PlayerJoined":
|
||||
case "PlayerLeft":
|
||||
case "HostChanged":
|
||||
// SDK already updates CurrentLobbyState; just refresh UI
|
||||
_manager?.uiSubsystem?.NotifyLobbyChanged();
|
||||
break;
|
||||
|
||||
case "GameStarting":
|
||||
State.Phase = GamePhase.Loading;
|
||||
HandleGameStarting();
|
||||
break;
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace Subsystems
|
||||
break;
|
||||
|
||||
case "GameStarted":
|
||||
HandleGameStarted();
|
||||
State.Phase = GamePhase.Playing;
|
||||
break;
|
||||
|
||||
case "RoleAssigned":
|
||||
@@ -142,13 +142,17 @@ namespace Subsystems
|
||||
|
||||
case "BodyReported":
|
||||
case "EmergencyMeetingCalled":
|
||||
HandleMeetingCalled(gameEvent);
|
||||
Toast("Meeting called! Head to the meeting point.");
|
||||
break;
|
||||
|
||||
case "MeetingStarted":
|
||||
HandleMeetingStarted(gameEvent);
|
||||
break;
|
||||
|
||||
case "PlayerVoted":
|
||||
HandlePlayerVoted(gameEvent);
|
||||
break;
|
||||
|
||||
case "VotingClosed":
|
||||
HandleVotingClosed(gameEvent);
|
||||
break;
|
||||
@@ -167,6 +171,7 @@ namespace Subsystems
|
||||
|
||||
case "SabotageRepaired":
|
||||
case "SabotageMeltdown":
|
||||
State.ActiveSabotage = null;
|
||||
_manager?.uiSubsystem?.HideSabotageTimer();
|
||||
_manager?.mapSubsystem?.ClearSabotageMarkers();
|
||||
break;
|
||||
@@ -188,10 +193,8 @@ namespace Subsystems
|
||||
if (message == null) return;
|
||||
if (message.Success)
|
||||
{
|
||||
Debug.Log($"Lobby created. Code: {message.JoinCode}, ID: {message.LobbyId}");
|
||||
// Navigate to the create/waiting scene
|
||||
Debug.Log($"Lobby created. Code: {message.JoinCode}");
|
||||
SceneManager.LoadScene("create", LoadSceneMode.Single);
|
||||
// Mark lobby UI dirty so LobbyDisplayUI refreshes once the scene is loaded
|
||||
_manager?.uiSubsystem?.NotifyLobbyChanged();
|
||||
}
|
||||
else
|
||||
@@ -207,7 +210,6 @@ namespace Subsystems
|
||||
{
|
||||
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
|
||||
@@ -216,12 +218,24 @@ namespace Subsystems
|
||||
}
|
||||
}
|
||||
|
||||
// ── Game flow events ──────────────────────────────────────────────────
|
||||
// ── Game flow ─────────────────────────────────────────────────────────
|
||||
|
||||
private void HandleGameStarting()
|
||||
{
|
||||
_pendingMapBuild = false;
|
||||
// SDK sets Phase = Loading; load Client.unity
|
||||
// Reset per-game state
|
||||
State.MyRole = null;
|
||||
State.IsDead = false;
|
||||
State.MyTasks = new List<GameTask>();
|
||||
State.MyCompletedTaskIds = new HashSet<string>();
|
||||
State.TotalCompleted = 0;
|
||||
State.TotalRequired = 0;
|
||||
State.ActiveMeeting = null;
|
||||
State.LastVoteResult = null;
|
||||
State.VotedPlayerIds = new HashSet<string>();
|
||||
State.ActiveSabotage = null;
|
||||
State.GameEndData = null;
|
||||
State.KillCooldownRemaining = 0;
|
||||
SceneManager.LoadScene("Client", LoadSceneMode.Single);
|
||||
}
|
||||
|
||||
@@ -231,10 +245,6 @@ namespace Subsystems
|
||||
TryBuildMapAndMarkers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from GameManager.OnSceneLoaded("Client") after scene objects are bound.
|
||||
/// Ensures map construction still happens even if MapDataReady arrived earlier.
|
||||
/// </summary>
|
||||
public void OnClientSceneReady()
|
||||
{
|
||||
TryBuildMapAndMarkers();
|
||||
@@ -243,34 +253,40 @@ namespace Subsystems
|
||||
private void TryBuildMapAndMarkers()
|
||||
{
|
||||
if (!_pendingMapBuild) return;
|
||||
if (_manager?.mapSubsystem == null) return;
|
||||
if (!_manager.mapSubsystem.IsSceneReady) return;
|
||||
if (_manager?.mapSubsystem == null || !_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
|
||||
Debug.Log("[Network] Map built.");
|
||||
}
|
||||
|
||||
private void HandleRoleAssigned(GameEvent evt)
|
||||
{
|
||||
var payload = evt.GetPayload<RoleAssignedPayload>();
|
||||
if (payload == null || payload.ClientUuid != _gameClient.ClientUuid) return;
|
||||
Debug.Log($"Role: {payload.Role}, Tasks: {payload.Tasks?.Count ?? 0}");
|
||||
_manager?.taskSubsystem?.Initialize(_gameClient.MyTasks);
|
||||
|
||||
State.MyRole = payload.Role;
|
||||
State.MyTasks = payload.Tasks ?? new List<GameTask>();
|
||||
State.MyCompletedTaskIds.Clear();
|
||||
|
||||
Debug.Log($"Role: {payload.Role}, Tasks: {State.MyTasks.Count}");
|
||||
_manager?.taskSubsystem?.Initialize(State.MyTasks);
|
||||
}
|
||||
|
||||
private void HandleTaskCompleted(GameEvent evt)
|
||||
{
|
||||
var payload = evt.GetPayload<TaskCompletedPayload>();
|
||||
if (payload == null) return;
|
||||
|
||||
// Track if it's our task
|
||||
if (payload.ClientUuid == _gameClient.ClientUuid)
|
||||
State.MyCompletedTaskIds.Add(payload.TaskId);
|
||||
|
||||
State.TotalCompleted = payload.TotalCompleted;
|
||||
State.TotalRequired = payload.TotalTasks;
|
||||
|
||||
_manager?.uiSubsystem?.UpdateTaskProgress(payload.TotalCompleted, payload.TotalTasks);
|
||||
_manager?.mapSubsystem?.RemoveTaskMarker(payload.TaskId);
|
||||
}
|
||||
@@ -279,28 +295,58 @@ namespace Subsystems
|
||||
{
|
||||
var payload = evt.GetPayload<PlayerKilledPayload>();
|
||||
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();
|
||||
_manager?.mapSubsystem?.CreateBodyMarker(payload.BodyId, payload.Location);
|
||||
|
||||
if (payload.VictimId == _gameClient.ClientUuid)
|
||||
{
|
||||
State.IsDead = true;
|
||||
_manager?.uiSubsystem?.OnLocalPlayerDied();
|
||||
}
|
||||
|
||||
// Update player state in our list
|
||||
var p = State.Players.Find(x => x.ClientUuid == payload.VictimId);
|
||||
if (p != null) p.State = PlayerState.Dead;
|
||||
}
|
||||
|
||||
private void HandleMeetingStarted(GameEvent evt)
|
||||
{
|
||||
var payload = evt.GetPayload<MeetingStartedPayload>();
|
||||
if (payload == null) return;
|
||||
_manager?.uiSubsystem?.ShowMeetingPanel(_gameClient.CurrentLobbyState?.Players, payload);
|
||||
|
||||
State.Phase = GamePhase.Meeting;
|
||||
State.ActiveMeeting = payload;
|
||||
State.VotedPlayerIds = new HashSet<string>();
|
||||
State.LastVoteResult = null;
|
||||
|
||||
SyncPlayersFromLobby();
|
||||
_manager?.uiSubsystem?.ShowMeetingPanel(State.Players, payload);
|
||||
}
|
||||
|
||||
private void HandlePlayerVoted(GameEvent evt)
|
||||
{
|
||||
var payload = evt.GetPayload<PlayerVotedPayload>();
|
||||
if (payload == null) return;
|
||||
State.VotedPlayerIds.Add(payload.VoterId);
|
||||
}
|
||||
|
||||
private void HandleVotingClosed(GameEvent evt)
|
||||
{
|
||||
var payload = evt.GetPayload<VotingClosedPayload>();
|
||||
if (payload == null) return;
|
||||
_manager?.uiSubsystem?.ShowVoteResult(payload, _gameClient.CurrentLobbyState?.Players);
|
||||
|
||||
State.Phase = GamePhase.Playing;
|
||||
State.ActiveMeeting = null;
|
||||
State.LastVoteResult = payload;
|
||||
|
||||
// Mark ejected player dead in our list
|
||||
if (!string.IsNullOrEmpty(payload.EjectedPlayerId))
|
||||
{
|
||||
var p = State.Players.Find(x => x.ClientUuid == payload.EjectedPlayerId);
|
||||
if (p != null) p.State = PlayerState.Dead;
|
||||
}
|
||||
|
||||
_manager?.uiSubsystem?.ShowVoteResult(payload, State.Players);
|
||||
_manager?.mapSubsystem?.ClearBodyMarkers();
|
||||
}
|
||||
|
||||
@@ -308,11 +354,16 @@ namespace Subsystems
|
||||
{
|
||||
var payload = evt.GetPayload<GameEndedPayload>();
|
||||
if (payload == null) return;
|
||||
|
||||
State.Phase = GamePhase.Ended;
|
||||
State.GameEndData = payload;
|
||||
|
||||
_manager?.uiSubsystem?.ShowGameEndPanel(payload, _gameClient.ClientUuid);
|
||||
}
|
||||
|
||||
private void HandleReturnedToLobby()
|
||||
{
|
||||
State.Phase = GamePhase.Lobby;
|
||||
if (_gameClient.IsOwner)
|
||||
SceneManager.LoadScene("create", LoadSceneMode.Single);
|
||||
else
|
||||
@@ -323,6 +374,9 @@ namespace Subsystems
|
||||
{
|
||||
var payload = evt.GetPayload<SabotageStartedPayload>();
|
||||
if (payload == null) return;
|
||||
|
||||
State.ActiveSabotage = payload;
|
||||
|
||||
_manager?.mapSubsystem?.CreateSabotageMarkers(payload.RepairStations);
|
||||
if (payload.Type == SabotageType.CriticalMeltdown && payload.Deadline.HasValue)
|
||||
_manager?.uiSubsystem?.ShowSabotageTimer(payload.Deadline.Value);
|
||||
@@ -336,6 +390,21 @@ namespace Subsystems
|
||||
_manager?.mapSubsystem?.UpdatePlayerAvatars(_gameClient.PlayerPositions, _gameClient.ClientUuid);
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────
|
||||
|
||||
private void SyncPlayersFromLobby()
|
||||
{
|
||||
var lobby = _gameClient.CurrentLobbyState;
|
||||
if (lobby?.Players != null)
|
||||
State.Players = lobby.Players;
|
||||
}
|
||||
|
||||
private void Toast(string message)
|
||||
{
|
||||
State.ToastMessage = message;
|
||||
State.ToastExpiry = UnityEngine.Time.time + 4f;
|
||||
}
|
||||
|
||||
// ── Send helpers ──────────────────────────────────────────────────────
|
||||
|
||||
public void CreateLobby(double lat, double lon, double radius = 500, int impostorCount = 1, int taskCount = 5)
|
||||
@@ -352,6 +421,7 @@ namespace Subsystems
|
||||
public void LeaveLobby()
|
||||
{
|
||||
_gameClient.LeaveLobby();
|
||||
State.Phase = GamePhase.Lobby;
|
||||
SceneManager.LoadScene(_manager?.firstMenuScene ?? "main menu asi idk lol", LoadSceneMode.Single);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user