using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using AsyncOperation = UnityEngine.AsyncOperation;
namespace Unity.Netcode
{
///
/// Used by to determine if a server invoked scene event has started.
/// The returned status is stored in the property.
/// Note: This was formally known as SwitchSceneProgress which contained the .
/// All s are now delivered by the event handler
/// via the parameter.
///
public enum SceneEventProgressStatus
{
///
/// No scene event progress status can be used to initialize a variable that will be checked over time.
///
None,
///
/// The scene event was successfully started.
///
Started,
///
/// Returned if you try to unload a scene that was not yet loaded.
///
SceneNotLoaded,
///
/// Returned if you try to start a new scene event before a previous one is finished.
///
SceneEventInProgress,
///
/// Returned if the scene name used with
/// or is invalid.
///
InvalidSceneName,
///
/// Server side: Returned if the delegate handler returns false
/// (i.e. scene is considered not valid/safe to load).
///
SceneFailedVerification,
///
/// This is used for internal error notifications.
/// If you receive this event then it is most likely due to a bug (please open a GitHub issue with steps to replicate).
///
InternalNetcodeError,
}
///
/// Server side only:
/// This tracks the progress of clients during a load or unload scene event
///
internal class SceneEventProgress
{
///
/// List of clientIds of those clients that is done loading the scene.
///
internal List DoneClients { get; } = new List();
///
/// The NetworkTime at the moment the scene switch was initiated by the server.
///
internal NetworkTime TimeAtInitiation { get; }
///
/// Delegate type for when the switch scene progress is completed. Either by all clients done loading the scene or by time out.
///
internal delegate bool OnCompletedDelegate(SceneEventProgress sceneEventProgress);
///
/// The callback invoked when the switch scene progress is completed. Either by all clients done loading the scene or by time out.
///
internal OnCompletedDelegate OnComplete;
///
/// Is this scene switch progresses completed, all clients are done loading the scene or a timeout has occurred.
///
internal bool IsCompleted { get; private set; }
internal bool TimedOut { get; private set; }
///
/// If all clients are done loading the scene, at the moment of completed.
///
internal bool AreAllClientsDoneLoading { get; private set; }
///
/// The hash value generated from the full scene path
///
internal uint SceneHash { get; set; }
internal Guid Guid { get; } = Guid.NewGuid();
private Coroutine m_TimeOutCoroutine;
private AsyncOperation m_SceneLoadOperation;
private NetworkManager m_NetworkManager { get; }
internal SceneEventProgressStatus Status { get; set; }
internal SceneEventType SceneEventType { get; set; }
internal LoadSceneMode LoadSceneMode;
internal SceneEventProgress(NetworkManager networkManager, SceneEventProgressStatus status = SceneEventProgressStatus.Started)
{
if (status == SceneEventProgressStatus.Started)
{
m_NetworkManager = networkManager;
m_TimeOutCoroutine = m_NetworkManager.StartCoroutine(TimeOutSceneEventProgress());
TimeAtInitiation = networkManager.LocalTime;
}
Status = status;
}
internal IEnumerator TimeOutSceneEventProgress()
{
yield return new WaitForSecondsRealtime(m_NetworkManager.NetworkConfig.LoadSceneTimeOut);
TimedOut = true;
CheckCompletion();
}
internal void AddClientAsDone(ulong clientId)
{
DoneClients.Add(clientId);
CheckCompletion();
}
internal void RemoveClientAsDone(ulong clientId)
{
DoneClients.Remove(clientId);
CheckCompletion();
}
internal void SetSceneLoadOperation(AsyncOperation sceneLoadOperation)
{
m_SceneLoadOperation = sceneLoadOperation;
m_SceneLoadOperation.completed += operation => CheckCompletion();
}
internal void CheckCompletion()
{
if ((!IsCompleted && DoneClients.Count == m_NetworkManager.ConnectedClientsList.Count && m_SceneLoadOperation.isDone) || (!IsCompleted && TimedOut))
{
IsCompleted = true;
AreAllClientsDoneLoading = true;
// If OnComplete is not registered or it is and returns true then remove this from the progress tracking
if (OnComplete == null || (OnComplete != null && OnComplete.Invoke(this)))
{
m_NetworkManager.SceneManager.SceneEventProgressTracking.Remove(Guid);
}
m_NetworkManager.StopCoroutine(m_TimeOutCoroutine);
}
}
}
}