培训考核三期,新版培训,网页版培训登录器
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

360 lines
12 KiB

using System;
using System.Threading;
using BestHTTP.PlatformSupport.Threading;
using UnityEngine;
#if NETFX_CORE
using System.Threading.Tasks;
#endif
namespace BestHTTP
{
/// <summary>
/// Threading mode the plugin will use to call HTTPManager.OnUpdate().
/// </summary>
public enum ThreadingMode : int
{
/// <summary>
/// HTTPManager.OnUpdate() is called from the HTTPUpdateDelegator's Update functions (Unity's main thread).
/// </summary>
UnityUpdate,
/// <summary>
/// The plugin starts a dedicated thread to call HTTPManager.OnUpdate() periodically.
/// </summary>
Threaded,
/// <summary>
/// HTTPManager.OnUpdate() will not be called automatically.
/// </summary>
None
}
/// <summary>
/// Will route some U3D calls to the HTTPManager.
/// </summary>
[ExecuteInEditMode]
[BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
public sealed class HTTPUpdateDelegator : MonoBehaviour
{
#region Public Properties
/// <summary>
/// The singleton instance of the HTTPUpdateDelegator
/// </summary>
public static HTTPUpdateDelegator Instance { get; private set; }
/// <summary>
/// True, if the Instance property should hold a valid value.
/// </summary>
public static bool IsCreated { get; private set; }
/// <summary>
/// Set it true before any CheckInstance() call, or before any request sent to dispatch callbacks on another thread.
/// </summary>
public static bool IsThreaded { get; set; }
/// <summary>
/// It's true if the dispatch thread running.
/// </summary>
public static bool IsThreadRunning { get; private set; }
public ThreadingMode CurrentThreadingMode { get { return _currentThreadingMode; } set { SetThreadingMode(value); } }
private ThreadingMode _currentThreadingMode = ThreadingMode.UnityUpdate;
/// <summary>
/// How much time the plugin should wait between two update call. Its default value 100 ms.
/// </summary>
public static int ThreadFrequencyInMS { get; set; }
/// <summary>
/// Called in the OnApplicationQuit function. If this function returns False, the plugin will not start to
/// shut down itself.
/// </summary>
public static System.Func<bool> OnBeforeApplicationQuit;
/// <summary>
/// Called when the Unity application's foreground state changed.
/// </summary>
public static System.Action<bool> OnApplicationForegroundStateChanged;
#endregion
private static bool isSetupCalled;
private int isHTTPManagerOnUpdateRunning;
private AutoResetEvent pingEvent = new AutoResetEvent(false);
private int updateThreadCount = 0;
#if UNITY_EDITOR
/// <summary>
/// Called after scene loaded to support Configurable Enter Play Mode (https://docs.unity3d.com/2019.3/Documentation/Manual/ConfigurableEnterPlayMode.html)
/// </summary>
#if UNITY_2019_3_OR_NEWER
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
#endif
static void ResetSetup()
{
isSetupCalled = false;
HTTPManager.Logger.Information("HTTPUpdateDelegator", "Reset called!");
}
#endif
static HTTPUpdateDelegator()
{
ThreadFrequencyInMS = 100;
}
/// <summary>
/// Will create the HTTPUpdateDelegator instance and set it up.
/// </summary>
public static void CheckInstance()
{
try
{
if (!IsCreated)
{
GameObject go = GameObject.Find("HTTP Update Delegator");
if (go != null)
Instance = go.GetComponent<HTTPUpdateDelegator>();
if (Instance == null)
{
go = new GameObject("HTTP Update Delegator");
go.hideFlags = HideFlags.HideAndDontSave;
Instance = go.AddComponent<HTTPUpdateDelegator>();
}
IsCreated = true;
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying)
{
UnityEditor.EditorApplication.update -= Instance.Update;
UnityEditor.EditorApplication.update += Instance.Update;
}
#if UNITY_2017_2_OR_NEWER
UnityEditor.EditorApplication.playModeStateChanged -= Instance.OnPlayModeStateChanged;
UnityEditor.EditorApplication.playModeStateChanged += Instance.OnPlayModeStateChanged;
#else
UnityEditor.EditorApplication.playmodeStateChanged -= Instance.OnPlayModeStateChanged;
UnityEditor.EditorApplication.playmodeStateChanged += Instance.OnPlayModeStateChanged;
#endif
#endif
// https://docs.unity3d.com/ScriptReference/Application-wantsToQuit.html
Application.wantsToQuit -= UnityApplication_WantsToQuit;
Application.wantsToQuit += UnityApplication_WantsToQuit;
HTTPManager.Logger.Information("HTTPUpdateDelegator", "Instance Created!");
}
}
catch
{
HTTPManager.Logger.Error("HTTPUpdateDelegator", "Please call the BestHTTP.HTTPManager.Setup() from one of Unity's event(eg. awake, start) before you send any request!");
}
}
private void Setup()
{
if (isSetupCalled)
return;
isSetupCalled = true;
HTTPManager.Logger.Information("HTTPUpdateDelegator", string.Format("Setup called Threading Mode: {0}, IsThreaded: {1}", _currentThreadingMode, IsThreaded));
HTTPManager.Setup();
#if UNITY_WEBGL && !UNITY_EDITOR
// Threads are not implemented in WEBGL builds, disable it for now.
IsThreaded = false;
#endif
SetThreadingMode(IsThreaded ? ThreadingMode.Threaded : ThreadingMode.UnityUpdate);
// Unity doesn't tolerate well if the DontDestroyOnLoad called when purely in editor mode. So, we will set the flag
// only when we are playing, or not in the editor.
if (!Application.isEditor || Application.isPlaying)
GameObject.DontDestroyOnLoad(this.gameObject);
HTTPManager.Logger.Information("HTTPUpdateDelegator", "Setup done!");
}
/// <summary>
/// Set directly the threading mode to use.
/// </summary>
public void SetThreadingMode(ThreadingMode mode)
{
if (_currentThreadingMode == mode)
return;
HTTPManager.Logger.Information("HTTPUpdateDelegator", "SetThreadingMode: " + mode);
_currentThreadingMode = mode;
#if !UNITY_WEBGL || UNITY_EDITOR
switch (_currentThreadingMode)
{
case ThreadingMode.UnityUpdate:
case ThreadingMode.None:
IsThreadRunning = false;
PingUpdateThread();
break;
case ThreadingMode.Threaded:
ThreadedRunner.RunLongLiving(ThreadFunc);
break;
}
#endif
}
/// <summary>
/// Swaps threading mode between Unity's Update function or a distinct thread.
/// </summary>
public void SwapThreadingMode() => SetThreadingMode(_currentThreadingMode == ThreadingMode.Threaded ? ThreadingMode.UnityUpdate : ThreadingMode.Threaded);
/// <summary>
/// Pings the update thread to call HTTPManager.OnUpdate immediately.
/// </summary>
/// <remarks>Works only when the current threading mode is Threaded!</remarks>
public void PingUpdateThread() => pingEvent.Set();
void ThreadFunc()
{
HTTPManager.Logger.Information("HTTPUpdateDelegator", "Update Thread Started");
ThreadedRunner.SetThreadName("BestHTTP.Update Thread");
try
{
if (Interlocked.Increment(ref updateThreadCount) > 1)
{
HTTPManager.Logger.Information("HTTPUpdateDelegator", "An update thread already started.");
return;
}
// Threading mode might be already changed, so set IsThreadRunning to IsThreaded's value.
IsThreadRunning = CurrentThreadingMode == ThreadingMode.Threaded;
while (IsThreadRunning)
{
CallOnUpdate();
pingEvent.WaitOne(ThreadFrequencyInMS);
}
}
finally
{
Interlocked.Decrement(ref updateThreadCount);
HTTPManager.Logger.Information("HTTPUpdateDelegator", "Update Thread Ended");
}
}
void Update()
{
if (!isSetupCalled)
Setup();
if (CurrentThreadingMode == ThreadingMode.UnityUpdate)
CallOnUpdate();
}
private void CallOnUpdate()
{
// Prevent overlapping call of OnUpdate from unity's main thread and a separate thread
if (Interlocked.CompareExchange(ref isHTTPManagerOnUpdateRunning, 1, 0) == 0)
{
try
{
HTTPManager.OnUpdate();
}
finally
{
Interlocked.Exchange(ref isHTTPManagerOnUpdateRunning, 0);
}
}
}
#if UNITY_EDITOR
#if UNITY_2017_2_OR_NEWER
void OnPlayModeStateChanged(UnityEditor.PlayModeStateChange playMode)
{
if (playMode == UnityEditor.PlayModeStateChange.EnteredPlayMode)
{
UnityEditor.EditorApplication.update -= Update;
}
else if (playMode == UnityEditor.PlayModeStateChange.EnteredEditMode)
{
UnityEditor.EditorApplication.update -= Update;
UnityEditor.EditorApplication.update += Update;
HTTPUpdateDelegator.ResetSetup();
HTTPManager.ResetSetup();
}
}
#else
void OnPlayModeStateChanged()
{
if (UnityEditor.EditorApplication.isPlaying)
UnityEditor.EditorApplication.update -= Update;
else if (!UnityEditor.EditorApplication.isPlaying)
UnityEditor.EditorApplication.update += Update;
}
#endif
#endif
void OnDisable()
{
HTTPManager.Logger.Information("HTTPUpdateDelegator", "OnDisable Called!");
#if UNITY_EDITOR
if (UnityEditor.EditorApplication.isPlaying)
#endif
UnityApplication_WantsToQuit();
}
void OnApplicationPause(bool isPaused)
{
HTTPManager.Logger.Information("HTTPUpdateDelegator", "OnApplicationPause isPaused: " + isPaused);
if (HTTPUpdateDelegator.OnApplicationForegroundStateChanged != null)
HTTPUpdateDelegator.OnApplicationForegroundStateChanged(isPaused);
}
private static bool UnityApplication_WantsToQuit()
{
HTTPManager.Logger.Information("HTTPUpdateDelegator", "UnityApplication_WantsToQuit Called!");
if (OnBeforeApplicationQuit != null)
{
try
{
if (!OnBeforeApplicationQuit())
{
HTTPManager.Logger.Information("HTTPUpdateDelegator", "OnBeforeApplicationQuit call returned false, postponing plugin and application shutdown.");
return false;
}
}
catch (System.Exception ex)
{
HTTPManager.Logger.Exception("HTTPUpdateDelegator", string.Empty, ex);
}
}
IsThreadRunning = false;
Instance.PingUpdateThread();
if (!IsCreated)
return true;
IsCreated = false;
HTTPManager.OnQuit();
return true;
}
}
}