Files

365 lines
10 KiB
C#

using System;
using System.Collections;
using GameServer.Client;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
public class serverSelector : MonoBehaviour
{
private const string DefaultServerUrl = "https://game.bansonic.top";
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<ServerStatusResponse>(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);
}
}
}