using System; using System.Collections; using GameServer.Client; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; public class serverSelector : MonoBehaviour { private const string DefaultServerUrl = "http://47.112.187.172:8080"; private const float RefreshIntervalSeconds = 5f; private const float StatusCacheDurationSeconds = 60f; [Header("button")] [SerializeField] private Button serverSelectorButton; [Header("text")] [SerializeField] private Text currentServerName; [SerializeField] private Image currentServerStatus; [SerializeField] private uServerStatus currentServerStatusDisplay; [Header("Server Status Sprites")] [SerializeField] private Sprite serverOkaySprite; [SerializeField] private Sprite serverBusySprite; [SerializeField] private Sprite serverErrorSprite; [SerializeField] private Sprite serverSupportingSprite; [SerializeField] private Sprite userOfflineSprite; [Header("objs")] [SerializeField] private GameObject serverSelectorPanel; [SerializeField] private Button shutSSPanel; [Header("network")] [SerializeField] private string fallbackServerUrl = DefaultServerUrl; [SerializeField] private float requestTimeoutSeconds = 5f; private Coroutine _pollCoroutine; private bool _started; private static bool s_hasCachedStatus; private static uServerStatus.ServerStatus s_cachedStatus; private static DateTime s_lastValidationUtc; [Serializable] private class ServerStatusResponse { public bool success; public string server_state; public bool maintenance_enabled; public string maintenance_message; public float requests_per_second; public float busy_qps_threshold; } private void Awake() { if (serverSelectorButton != null) { serverSelectorButton.onClick.RemoveListener(OpenServerSelectorPanel); serverSelectorButton.onClick.AddListener(OpenServerSelectorPanel); } if (shutSSPanel != null) { shutSSPanel.onClick.RemoveListener(CloseServerSelectorPanel); shutSSPanel.onClick.AddListener(CloseServerSelectorPanel); } } private void Start() { _started = true; if (serverSelectorPanel != null) { serverSelectorPanel.SetActive(false); } if (OnlineModeSettings.IsLocalOnlyMode) { ApplyStatus(uServerStatus.ServerStatus.Offline); return; } StartOrRestartPolling(refreshImmediately: true); } private void OnEnable() { if (!_started || !Application.isPlaying) { return; } if (OnlineModeSettings.IsLocalOnlyMode) { ApplyStatus(uServerStatus.ServerStatus.Offline); return; } StartOrRestartPolling(refreshImmediately: true); } private void StartOrRestartPolling(bool refreshImmediately) { if (_pollCoroutine != null) { StopCoroutine(_pollCoroutine); _pollCoroutine = null; } _pollCoroutine = StartCoroutine(StatusPollLoop(refreshImmediately)); } private void OnDisable() { if (_pollCoroutine != null) { StopCoroutine(_pollCoroutine); _pollCoroutine = null; } } private IEnumerator StatusPollLoop(bool refreshImmediately) { if (refreshImmediately) { if (TryApplyFreshCachedStatus()) { float remainingLockDuration = GetRemainingCacheLockSeconds(); if (remainingLockDuration > 0f) { yield return new WaitForSecondsRealtime(remainingLockDuration); } } yield return RefreshServerStatus(); } while (true) { yield return new WaitForSecondsRealtime(RefreshIntervalSeconds); yield return RefreshServerStatus(); } } private IEnumerator RefreshServerStatus() { if (OnlineModeSettings.IsLocalOnlyMode) { ApplyStatus(uServerStatus.ServerStatus.Offline); yield break; } if (Application.internetReachability == NetworkReachability.NotReachable) { ApplyStatus(uServerStatus.ServerStatus.Offline); yield break; } string statusUrl = BuildServerStatusUrl(); using (UnityWebRequest request = UnityWebRequest.Get(statusUrl)) { request.timeout = Mathf.Max(1, Mathf.RoundToInt(requestTimeoutSeconds)); yield return request.SendWebRequest(); if (request.result != UnityWebRequest.Result.Success) { CacheAndApplyStatus(uServerStatus.ServerStatus.Unreachable); yield break; } string responseText = request.downloadHandler != null ? request.downloadHandler.text : string.Empty; if (string.IsNullOrWhiteSpace(responseText)) { CacheAndApplyStatus(uServerStatus.ServerStatus.Unreachable); yield break; } ServerStatusResponse response; try { response = JsonUtility.FromJson(responseText); } catch { CacheAndApplyStatus(uServerStatus.ServerStatus.Unreachable); yield break; } if (response == null || !response.success) { CacheAndApplyStatus(uServerStatus.ServerStatus.Unreachable); yield break; } CacheAndApplyStatus(ResolveStatus(response)); } } private void OpenServerSelectorPanel() { if (serverSelectorPanel != null) { serverSelectorPanel.SetActive(true); } if (!OnlineModeSettings.IsLocalOnlyMode) { StartCoroutine(RefreshServerStatusIfNeeded()); } else { ApplyStatus(uServerStatus.ServerStatus.Offline); } } private void CloseServerSelectorPanel() { if (serverSelectorPanel != null) { serverSelectorPanel.SetActive(false); } } private string ResolveServerBaseUrl() { NetworkManager manager = NetworkManager.Instance; if (manager != null && !string.IsNullOrWhiteSpace(manager.ServerUrl)) { return manager.ServerUrl.TrimEnd('/'); } return string.IsNullOrWhiteSpace(fallbackServerUrl) ? DefaultServerUrl : fallbackServerUrl.TrimEnd('/'); } private string BuildServerStatusUrl() { return $"{ResolveServerBaseUrl()}/api/server-status"; } private IEnumerator RefreshServerStatusIfNeeded() { if (TryApplyFreshCachedStatus()) { yield break; } yield return RefreshServerStatus(); } private uServerStatus.ServerStatus ResolveStatus(ServerStatusResponse response) { if (response == null) { return uServerStatus.ServerStatus.Unreachable; } if (response.maintenance_enabled) { return uServerStatus.ServerStatus.Maintenance; } string normalizedState = (response.server_state ?? string.Empty).Trim().ToUpperInvariant(); switch (normalizedState) { case "MAINTENANCE": return uServerStatus.ServerStatus.Maintenance; case "BUSY": return uServerStatus.ServerStatus.Fair; case "OK": if (response.busy_qps_threshold > 0f && response.requests_per_second >= response.busy_qps_threshold) { return uServerStatus.ServerStatus.Fair; } return uServerStatus.ServerStatus.Good; default: return uServerStatus.ServerStatus.Unknown; } } private bool TryApplyFreshCachedStatus() { if (!HasFreshCachedStatus()) { return false; } ApplyStatus(s_cachedStatus); return true; } private bool HasFreshCachedStatus() { if (!s_hasCachedStatus) { return false; } return (DateTime.UtcNow - s_lastValidationUtc).TotalSeconds < StatusCacheDurationSeconds; } private float GetRemainingCacheLockSeconds() { if (!s_hasCachedStatus) { return 0f; } double elapsedSeconds = (DateTime.UtcNow - s_lastValidationUtc).TotalSeconds; return Mathf.Max(0f, StatusCacheDurationSeconds - (float)elapsedSeconds); } private void CacheAndApplyStatus(uServerStatus.ServerStatus status) { s_cachedStatus = status; s_lastValidationUtc = DateTime.UtcNow; s_hasCachedStatus = true; ApplyStatus(status); } private Sprite ResolveSpriteForStatus(uServerStatus.ServerStatus status) { switch (status) { case uServerStatus.ServerStatus.Good: return serverOkaySprite; case uServerStatus.ServerStatus.Fair: return serverBusySprite; case uServerStatus.ServerStatus.Maintenance: return serverSupportingSprite; case uServerStatus.ServerStatus.Offline: return userOfflineSprite != null ? userOfflineSprite : serverErrorSprite; case uServerStatus.ServerStatus.Unreachable: case uServerStatus.ServerStatus.Unknown: case uServerStatus.ServerStatus.Test: default: return serverErrorSprite; } } private void ApplyStatus(uServerStatus.ServerStatus status) { Sprite sprite = ResolveSpriteForStatus(status); if (currentServerStatus == null || sprite == null) { if (currentServerStatusDisplay != null) { currentServerStatusDisplay.SetStatus(status); } return; } currentServerStatus.sprite = sprite; if (currentServerStatusDisplay != null) { currentServerStatusDisplay.SetStatus(status); } } }