510 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			510 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C#
		
	
	
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using UnityEngine;
 | 
						|
 | 
						|
namespace Guru
 | 
						|
{
 | 
						|
 | 
						|
    public class GameEventMgr
 | 
						|
    {
 | 
						|
        private static GameEventMgr _instance;
 | 
						|
        public static GameEventMgr Instance
 | 
						|
        {
 | 
						|
            get
 | 
						|
            {
 | 
						|
                if (_instance == null)
 | 
						|
                {
 | 
						|
                    _instance = new GameEventMgr();
 | 
						|
                }
 | 
						|
                return _instance;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public static void Init(bool is_debug_mode = false)
 | 
						|
        {
 | 
						|
            if (_instance == null)
 | 
						|
            {
 | 
						|
                _instance = new GameEventMgr();
 | 
						|
            }
 | 
						|
 | 
						|
            _isDebugMode = is_debug_mode;
 | 
						|
 | 
						|
            if (_isDebugMode)
 | 
						|
            {
 | 
						|
                Debug.Log($"[GameEventMgr]Init: Running in debug mode");
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public static bool _isDebugMode = false;
 | 
						|
 | 
						|
        struct DelegateGameObjectBonding
 | 
						|
        {
 | 
						|
            public GameObject gameObject;
 | 
						|
            public Delegate listener;
 | 
						|
 | 
						|
            public DelegateGameObjectBonding(GameObject game_object, Delegate del)
 | 
						|
            {
 | 
						|
                gameObject = game_object;
 | 
						|
                listener = del;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private readonly Queue<List<Delegate>> _listenerListQueue = new Queue<List<Delegate>>();
 | 
						|
        private readonly Queue<List<DelegateGameObjectBonding>> _listenerBondingListQueue = new Queue<List<DelegateGameObjectBonding>>();
 | 
						|
        private readonly Dictionary<int, List<Delegate>> _eventTable = new Dictionary<int, List<Delegate>>();
 | 
						|
        private readonly Dictionary<int, List<DelegateGameObjectBonding>> _eventObjTable = new Dictionary<int, List<DelegateGameObjectBonding>>();
 | 
						|
        private readonly Dictionary<int, List<Delegate>> _pendingAddTable = new Dictionary<int, List<Delegate>>();
 | 
						|
        private readonly Dictionary<int, List<DelegateGameObjectBonding>> _pendingObjAddTable = new Dictionary<int, List<DelegateGameObjectBonding>>();
 | 
						|
        private readonly Dictionary<int, List<Delegate>> _pendingRemoveTable = new Dictionary<int, List<Delegate>>();
 | 
						|
        private readonly Dictionary<int, List<DelegateGameObjectBonding>> _pendingObjRemoveTable = new Dictionary<int, List<DelegateGameObjectBonding>>();
 | 
						|
 | 
						|
        // Using raising event list to prevent infinite loop call or changing delegate list
 | 
						|
        private readonly List<int> _raisingEventIds = new List<int>();
 | 
						|
 | 
						|
        private GameEventMgr() { }
 | 
						|
 | 
						|
        public void AddListener(int event_id, Action new_listener, GameObject life_cycle_obj = null)
 | 
						|
        {
 | 
						|
            DoAddListener(event_id, new_listener, life_cycle_obj);
 | 
						|
        }
 | 
						|
 | 
						|
        public void AddListener<T>(int event_id, Action<T> new_listener, GameObject life_cycle_obj = null)
 | 
						|
        {
 | 
						|
            DoAddListener(event_id, new_listener, life_cycle_obj);
 | 
						|
        }
 | 
						|
 | 
						|
        public void AddListener<T1, T2>(int event_id, Action<T1, T2> new_listener, GameObject life_cycle_obj = null)
 | 
						|
        {
 | 
						|
            DoAddListener(event_id, new_listener, life_cycle_obj);
 | 
						|
        }
 | 
						|
 | 
						|
        public void AddListener<T1, T2, T3>(int event_id, Action<T1, T2, T3> new_listener, GameObject life_cycle_obj = null)
 | 
						|
        {
 | 
						|
            DoAddListener(event_id, new_listener, life_cycle_obj);
 | 
						|
        }
 | 
						|
 | 
						|
        public void RemoveListener(int event_id, Action listener, GameObject life_cycle_obj = null)
 | 
						|
        {
 | 
						|
            DoRemoveListener(event_id, listener, life_cycle_obj);
 | 
						|
        }
 | 
						|
 | 
						|
        public void RemoveListener<T>(int event_id, Action<T> listener, GameObject life_cycle_obj = null)
 | 
						|
        {
 | 
						|
            DoRemoveListener(event_id, listener, life_cycle_obj);
 | 
						|
        }
 | 
						|
 | 
						|
        public void RemoveListener<T1, T2>(int event_id, Action<T1, T2> listener, GameObject life_cycle_obj = null)
 | 
						|
        {
 | 
						|
            DoRemoveListener(event_id, listener, life_cycle_obj);
 | 
						|
        }
 | 
						|
 | 
						|
        public void RemoveListener<T1, T2, T3>(int event_id, Action<T1, T2, T3> listener, GameObject life_cycle_obj = null)
 | 
						|
        {
 | 
						|
            DoRemoveListener(event_id, listener, life_cycle_obj);
 | 
						|
        }
 | 
						|
 | 
						|
        public void Raise(int event_id)
 | 
						|
        {
 | 
						|
            DoRaise(event_id);
 | 
						|
        }
 | 
						|
 | 
						|
        public void Raise<T>(int event_id, T arg1)
 | 
						|
        {
 | 
						|
            DoRaise(event_id, arg1);
 | 
						|
        }
 | 
						|
 | 
						|
        public void Raise<T1, T2>(int event_id, T1 arg1, T2 arg2)
 | 
						|
        {
 | 
						|
            DoRaise(event_id, arg1, arg2);
 | 
						|
        }
 | 
						|
 | 
						|
        public void Raise<T1, T2, T3>(int event_id, T1 arg1, T2 arg2, T3 arg3)
 | 
						|
        {
 | 
						|
            DoRaise(event_id, arg1, arg2, arg3);
 | 
						|
        }
 | 
						|
 | 
						|
        public void ClearNullGameObjectListeners()
 | 
						|
        {
 | 
						|
            var etor = _eventObjTable.GetEnumerator();
 | 
						|
 | 
						|
            while (etor.MoveNext())
 | 
						|
            {
 | 
						|
                var listeners = etor.Current.Value;
 | 
						|
 | 
						|
                for (int i = 0; i < listeners.Count; i++)
 | 
						|
                {
 | 
						|
                    if (listeners[i].gameObject == null)
 | 
						|
                    {
 | 
						|
                        listeners.RemoveAt(i--);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private List<Delegate> SpawnDelegateList()
 | 
						|
        {
 | 
						|
            if (_listenerListQueue.Count > 0)
 | 
						|
            {
 | 
						|
                return _listenerListQueue.Dequeue();
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                return new List<Delegate>();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private void DespawnDelegateList(List<Delegate> list)
 | 
						|
        {
 | 
						|
            if (list == null) return;
 | 
						|
 | 
						|
            list.Clear();
 | 
						|
            _listenerListQueue.Enqueue(list);
 | 
						|
        }
 | 
						|
 | 
						|
        private List<DelegateGameObjectBonding> SpawnDelegateBondingList()
 | 
						|
        {
 | 
						|
            if (_listenerBondingListQueue.Count > 0)
 | 
						|
            {
 | 
						|
                return _listenerBondingListQueue.Dequeue();
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                return new List<DelegateGameObjectBonding>();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private void DespawnDelegateBondingList(List<DelegateGameObjectBonding> list)
 | 
						|
        {
 | 
						|
            if (list == null) return;
 | 
						|
 | 
						|
            list.Clear();
 | 
						|
            _listenerBondingListQueue.Enqueue(list);
 | 
						|
        }
 | 
						|
 | 
						|
        private bool DoAddListener(int event_id, Delegate new_listener, GameObject life_cycle_obj = null)
 | 
						|
        {
 | 
						|
            if (new_listener == null)
 | 
						|
            {
 | 
						|
                Debug.LogError($"[GameEventMgr]DoAddListener: Can't add empty listener for event {event_id}!");
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
 | 
						|
            bool result = true;
 | 
						|
 | 
						|
            // If event is raising, add it into pending list
 | 
						|
            if (_raisingEventIds.Contains(event_id))
 | 
						|
            {
 | 
						|
                if (life_cycle_obj != null)
 | 
						|
                {
 | 
						|
                    if (!_pendingObjAddTable.TryGetValue(event_id, out var pending_listeners))
 | 
						|
                    {
 | 
						|
                        pending_listeners = SpawnDelegateBondingList();
 | 
						|
                        _pendingObjAddTable[event_id] = pending_listeners;
 | 
						|
                    }
 | 
						|
 | 
						|
                    pending_listeners.Add(new DelegateGameObjectBonding(life_cycle_obj, new_listener));
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    if (!_pendingAddTable.TryGetValue(event_id, out var pending_listeners))
 | 
						|
                    {
 | 
						|
                        pending_listeners = SpawnDelegateList();
 | 
						|
                        _pendingAddTable[event_id] = pending_listeners;
 | 
						|
                    }
 | 
						|
 | 
						|
                    pending_listeners.Add(new_listener);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                if (life_cycle_obj != null)
 | 
						|
                {
 | 
						|
                    if (!_eventObjTable.TryGetValue(event_id, out var listener_bondings))
 | 
						|
                    {
 | 
						|
                        listener_bondings = SpawnDelegateBondingList();
 | 
						|
                        _eventObjTable[event_id] = listener_bondings;
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (listener_bondings.Count == 0 || listener_bondings[0].listener.GetType() == new_listener.GetType())
 | 
						|
                    {
 | 
						|
                        var bonding = new DelegateGameObjectBonding(life_cycle_obj, new_listener);
 | 
						|
 | 
						|
                        if (_isDebugMode && listener_bondings.Contains(bonding))
 | 
						|
                        {
 | 
						|
                            Debug.LogError($"[GameEventMgr]DoAddListener: Already in the list, will not add it again [{new_listener.Target}.{new_listener.Method?.Name}]");
 | 
						|
                        }
 | 
						|
                        else
 | 
						|
                        {
 | 
						|
                            listener_bondings.Add(bonding);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else if (listener_bondings[0].listener.GetType() != new_listener.GetType())
 | 
						|
                    {
 | 
						|
                        Debug.LogError($"[GameEventMgr]DoAddListener: Attempting to add listener with inconsistent signature for event {event_id}. Current listeners type({listener_bondings[0].listener.GetType().Name}) != added type({new_listener.GetType().Name})");
 | 
						|
                        result = false;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    if (!_eventTable.TryGetValue(event_id, out var listeners))
 | 
						|
                    {
 | 
						|
                        listeners = SpawnDelegateList();
 | 
						|
                        _eventTable[event_id] = listeners;
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (listeners.Count == 0 || listeners[0].GetType() == new_listener.GetType())
 | 
						|
                    {
 | 
						|
                        if (_isDebugMode && listeners.Contains(new_listener))
 | 
						|
                        {
 | 
						|
                            Debug.LogError($"[GameEventMgr]DoAddListener: Already in the list, will not add it again [{new_listener.Target}.{new_listener.Method?.Name}]");
 | 
						|
                        }
 | 
						|
                        else
 | 
						|
                        {
 | 
						|
                            listeners.Add(new_listener);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else if (listeners[0].GetType() != new_listener.GetType())
 | 
						|
                    {
 | 
						|
                        Debug.LogError($"[GameEventMgr]DoAddListener: Attempting to add listener with inconsistent signature for event {event_id}. Current listeners type({listeners[0].GetType().Name}) != added type({new_listener.GetType().Name})");
 | 
						|
                        result = false;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return result;
 | 
						|
        }
 | 
						|
 | 
						|
        private bool DoRemoveListener(int event_id, Delegate listener, GameObject life_cycle_obj = null)
 | 
						|
        {
 | 
						|
            if (listener == null)
 | 
						|
            {
 | 
						|
                Debug.LogError($"[GameEventMgr]DoRemoveListener: Can't remove empty listener for event {event_id}!");
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
 | 
						|
            bool result = false;
 | 
						|
 | 
						|
            // If event is raising, add it into pending list
 | 
						|
            if (_raisingEventIds.Contains(event_id))
 | 
						|
            {
 | 
						|
                if (life_cycle_obj != null)
 | 
						|
                {
 | 
						|
                    if (!_pendingObjRemoveTable.TryGetValue(event_id, out var pending_listeners))
 | 
						|
                    {
 | 
						|
                        pending_listeners = SpawnDelegateBondingList();
 | 
						|
                        _pendingObjRemoveTable[event_id] = pending_listeners;
 | 
						|
                    }
 | 
						|
 | 
						|
                    pending_listeners.Add(new DelegateGameObjectBonding(life_cycle_obj, listener));
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    if (!_pendingRemoveTable.TryGetValue(event_id, out var pending_listeners))
 | 
						|
                    {
 | 
						|
                        pending_listeners = SpawnDelegateList();
 | 
						|
                        _pendingRemoveTable[event_id] = pending_listeners;
 | 
						|
                    }
 | 
						|
 | 
						|
                    pending_listeners.Add(listener);
 | 
						|
                }
 | 
						|
 | 
						|
                result = true;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                if (life_cycle_obj != null)
 | 
						|
                {
 | 
						|
                    if (_eventObjTable.TryGetValue(event_id, out var listeners))
 | 
						|
                    {
 | 
						|
                        if (listeners.Count > 0)
 | 
						|
                        {
 | 
						|
                            listeners.Remove(new DelegateGameObjectBonding(life_cycle_obj, listener));
 | 
						|
                            result = true;
 | 
						|
                        }
 | 
						|
 | 
						|
                        if (listeners.Count == 0)
 | 
						|
                        {
 | 
						|
                            DespawnDelegateBondingList(listeners);
 | 
						|
                            _eventObjTable.Remove(event_id);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    if (_eventTable.TryGetValue(event_id, out var listeners))
 | 
						|
                    {
 | 
						|
                        if (listeners.Count > 0)
 | 
						|
                        {
 | 
						|
                            listeners.Remove(listener);
 | 
						|
                            result = true;
 | 
						|
                        }
 | 
						|
 | 
						|
                        if (listeners.Count == 0)
 | 
						|
                        {
 | 
						|
                            DespawnDelegateList(listeners);
 | 
						|
                            _eventTable.Remove(event_id);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return result;
 | 
						|
        }
 | 
						|
 | 
						|
        private void LogException(Exception ex, string event_tag, string method_name)
 | 
						|
        {
 | 
						|
            string msg = ex.Message;
 | 
						|
            string stack_trace = ex.StackTrace;
 | 
						|
 | 
						|
            if (ex.InnerException != null)
 | 
						|
            {
 | 
						|
                msg = ex.InnerException.Message;
 | 
						|
                stack_trace = ex.InnerException.StackTrace;
 | 
						|
            }
 | 
						|
 | 
						|
            Debug.LogError($"[GameEventMgr]{method_name}: Event({event_tag}) {msg}\nStack Trace: {stack_trace}");
 | 
						|
        }
 | 
						|
 | 
						|
        private bool DoRaise(int event_id, params object[] args)
 | 
						|
        {
 | 
						|
            if (_raisingEventIds.Contains(event_id))
 | 
						|
            {
 | 
						|
                Debug.LogError($"[GameEventMgr]DoRaise: Can't raise event({event_id}) again inside the same event raising process!");
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
 | 
						|
            bool result = false;
 | 
						|
            _raisingEventIds.Add(event_id);
 | 
						|
 | 
						|
            if (_eventObjTable.TryGetValue(event_id, out var bond_listeners))
 | 
						|
            {
 | 
						|
                for (int i = 0; i < bond_listeners.Count; i++)
 | 
						|
                {
 | 
						|
                    var bond_listener = bond_listeners[i];
 | 
						|
 | 
						|
                    if (bond_listener.gameObject != null)
 | 
						|
                    {
 | 
						|
                        try
 | 
						|
                        {
 | 
						|
                            bond_listener.listener.Method.Invoke(bond_listener.listener.Target, args);
 | 
						|
                            result = true;
 | 
						|
                        }
 | 
						|
                        catch (Exception ex)
 | 
						|
                        {
 | 
						|
                            bond_listeners.RemoveAt(i--);
 | 
						|
                            LogException(ex, $"{event_id}_Obj[{bond_listener.gameObject.name}]", "DoRaise");
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        bond_listeners.RemoveAt(i--);
 | 
						|
                        Debug.Log($"[GameEventMgr]DoRaise: Remove null GameObject listener ({event_id})!");
 | 
						|
                        continue;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (_eventTable.TryGetValue(event_id, out var listeners))
 | 
						|
            {
 | 
						|
                for (int i = 0; i < listeners.Count; i++)
 | 
						|
                {
 | 
						|
                    var listener = listeners[i];
 | 
						|
 | 
						|
                    if (listener != null)
 | 
						|
                    {
 | 
						|
                        try
 | 
						|
                        {
 | 
						|
                            listener.Method.Invoke(listener.Target, args);
 | 
						|
                            result = true;
 | 
						|
                        }
 | 
						|
                        catch (Exception ex)
 | 
						|
                        {
 | 
						|
                            listeners.RemoveAt(i--);
 | 
						|
                            LogException(ex, event_id.ToString(), "DoRaise");
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            _raisingEventIds.Remove(event_id);
 | 
						|
 | 
						|
            if (_pendingObjAddTable.TryGetValue(event_id, out var add_bond_listeners))
 | 
						|
            {
 | 
						|
                if (add_bond_listeners != null && add_bond_listeners.Count > 0)
 | 
						|
                {
 | 
						|
                    bond_listeners.AddRange(add_bond_listeners);
 | 
						|
                    DespawnDelegateBondingList(add_bond_listeners);
 | 
						|
                }
 | 
						|
 | 
						|
                _pendingObjAddTable.Remove(event_id);
 | 
						|
            }
 | 
						|
 | 
						|
            if (_pendingObjRemoveTable.TryGetValue(event_id, out var remove_bond_listeners))
 | 
						|
            {
 | 
						|
                if (remove_bond_listeners != null && remove_bond_listeners.Count > 0)
 | 
						|
                {
 | 
						|
                    for (int i = 0; i < remove_bond_listeners.Count; i++)
 | 
						|
                    {
 | 
						|
                        bond_listeners.Remove(remove_bond_listeners[i]);
 | 
						|
                    }
 | 
						|
 | 
						|
                    DespawnDelegateBondingList(remove_bond_listeners);
 | 
						|
                }
 | 
						|
 | 
						|
                _pendingObjRemoveTable.Remove(event_id);
 | 
						|
            }
 | 
						|
 | 
						|
            if (bond_listeners?.Count == 0)
 | 
						|
            {
 | 
						|
                DespawnDelegateBondingList(bond_listeners);
 | 
						|
                _eventObjTable.Remove(event_id);
 | 
						|
            }
 | 
						|
 | 
						|
            if (_pendingAddTable.TryGetValue(event_id, out var add_listeners))
 | 
						|
            {
 | 
						|
                if (add_listeners != null && add_listeners.Count > 0)
 | 
						|
                {
 | 
						|
                    listeners.AddRange(add_listeners);
 | 
						|
                    DespawnDelegateList(add_listeners);
 | 
						|
                }
 | 
						|
 | 
						|
                _pendingAddTable.Remove(event_id);
 | 
						|
            }
 | 
						|
 | 
						|
            if (_pendingRemoveTable.TryGetValue(event_id, out var remove_listeners))
 | 
						|
            {
 | 
						|
                if (remove_listeners != null && remove_listeners.Count > 0)
 | 
						|
                {
 | 
						|
                    for (int i = 0; i < remove_listeners.Count; i++)
 | 
						|
                    {
 | 
						|
                        listeners.Remove(remove_listeners[i]);
 | 
						|
                    }
 | 
						|
 | 
						|
                    DespawnDelegateList(remove_listeners);
 | 
						|
                }
 | 
						|
 | 
						|
                _pendingRemoveTable.Remove(event_id);
 | 
						|
            }
 | 
						|
 | 
						|
            if (listeners?.Count == 0)
 | 
						|
            {
 | 
						|
                DespawnDelegateList(listeners);
 | 
						|
                _eventTable.Remove(event_id);
 | 
						|
            }
 | 
						|
 | 
						|
            return result;
 | 
						|
        }
 | 
						|
 | 
						|
        public void Clear()
 | 
						|
        {
 | 
						|
            _eventTable.Clear();
 | 
						|
            _eventObjTable.Clear();
 | 
						|
            _pendingAddTable.Clear();
 | 
						|
            _pendingObjAddTable.Clear();
 | 
						|
            _pendingRemoveTable.Clear();
 | 
						|
            _pendingObjRemoveTable.Clear();
 | 
						|
            _raisingEventIds.Clear();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |