219 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C#
		
	
	
		
		
			
		
	
	
			219 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C#
		
	
	
| 
								 | 
							
								using System;
							 | 
						|||
| 
								 | 
							
								using System.Collections.Generic;
							 | 
						|||
| 
								 | 
							
								using System.Threading;
							 | 
						|||
| 
								 | 
							
								using UnityEngine;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								namespace Guru
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    // Loom lets you easily run code on another thread and have that other thread run code on the main game thread when it needs to.
							 | 
						|||
| 
								 | 
							
								    public class Loom : MonoBehaviour
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        public static int maxThreads = 10;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private static int numThreads;
							 | 
						|||
| 
								 | 
							
								        private static Loom _instance;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public static Loom Instance
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            get
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Initialize();
							 | 
						|||
| 
								 | 
							
								                return _instance;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        void Awake()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            _instance = this;
							 | 
						|||
| 
								 | 
							
								            initialized = true;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        static bool initialized;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public static void Initialize()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (!initialized)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (!Application.isPlaying) return;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                initialized = true;
							 | 
						|||
| 
								 | 
							
								                var g = new GameObject("Loom");
							 | 
						|||
| 
								 | 
							
								                DontDestroyOnLoad(g);
							 | 
						|||
| 
								 | 
							
								                _instance = g.AddComponent<Loom>();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public struct DelayedQueueItem
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            public float time;
							 | 
						|||
| 
								 | 
							
								            public Action action;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private List<Action> _actions = new List<Action>();
							 | 
						|||
| 
								 | 
							
								        private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
							 | 
						|||
| 
								 | 
							
								        private List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        // Runs a set of statements on the main thread
							 | 
						|||
| 
								 | 
							
								        public static void QueueOnMainThread(Action action)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            QueueOnMainThread(action, 0f);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        // Runs a set of statements on the main thread (with an optional delay).
							 | 
						|||
| 
								 | 
							
								        public static void QueueOnMainThread(Action action, float time)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (time != 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                lock (_instance._delayed)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    _instance._delayed.Add(new DelayedQueueItem { time = Time.unscaledTime + time, action = action });
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                lock (_instance._actions)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    _instance._actions.Add(action);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        //  Runs a set of statements on another thread
							 | 
						|||
| 
								 | 
							
								        public static Thread RunAsync(Action a)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            Initialize();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            while (numThreads >= maxThreads)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Thread.Sleep(1);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            Interlocked.Increment(ref numThreads);
							 | 
						|||
| 
								 | 
							
								            ThreadPool.QueueUserWorkItem(RunAction, a);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            return null;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private static void RunAction(object action)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            try
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ((Action)action)();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            catch
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            finally
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Interlocked.Decrement(ref numThreads);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public static void Reset()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (_instance == null) return;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            lock (_instance._delayed)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                _instance._delayed.Clear();
							 | 
						|||
| 
								 | 
							
								                _instance._currentDelayed.Clear();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            lock (_instance._actions)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                _instance._actions.Clear();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public bool IsThreadsMax()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            return numThreads >= maxThreads;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        void OnDisable()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (_instance == this)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                _instance = null;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        List<Action> _currentActions = new List<Action>();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        void Update()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (_actions.Count > 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                lock (_actions)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    _currentActions.Clear();
							 | 
						|||
| 
								 | 
							
								                    _currentActions.AddRange(_actions);
							 | 
						|||
| 
								 | 
							
								                    _actions.Clear();
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                for (int i = 0; i < _currentActions.Count; i++)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    try
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        _currentActions[i].Invoke();
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    catch(Exception ex)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        LogException(ex);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                if (_delayed.Count > 0)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    lock (_delayed)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        _currentDelayed.Clear();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        for (int i = 0; i < _delayed.Count; i++)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            var d = _delayed[i];
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            if (d.time <= Time.unscaledTime)
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                _currentDelayed.Add(d);
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        for (int i = 0; i < _currentDelayed.Count; i++)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            var item = _currentDelayed[i];
							 | 
						|||
| 
								 | 
							
								                            _delayed.Remove(item);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    for (int i = 0; i < _currentDelayed.Count; i++)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        try
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            _currentDelayed[i].action.Invoke();
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        catch (Exception ex)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            LogException(ex);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        void LogException(Exception ex)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            string msg = ex.Message;
							 | 
						|||
| 
								 | 
							
								            string stack_trace = ex.StackTrace;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (ex.InnerException != null)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                msg = ex.InnerException.Message;
							 | 
						|||
| 
								 | 
							
								                stack_trace = ex.InnerException.StackTrace;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            Debug.LogError($"[Loom]InvokeMainThreadAction: {msg}\nStack Trace: {stack_trace}");
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 |