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 ();
 | |
|         private readonly Queue<List<DelegateGameObjectBonding>> _listenerBondingListQueue = new ();
 | |
|         private readonly Dictionary<int, List<Delegate>> _eventTable = new ();
 | |
|         private readonly Dictionary<int, List<DelegateGameObjectBonding>> _eventObjTable = new ();
 | |
|         private readonly Dictionary<int, List<Delegate>> _pendingAddTable = new ();
 | |
|         private readonly Dictionary<int, List<DelegateGameObjectBonding>> _pendingObjAddTable = new ();
 | |
|         private readonly Dictionary<int, List<Delegate>> _pendingRemoveTable = new ();
 | |
|         private readonly Dictionary<int, List<DelegateGameObjectBonding>> _pendingObjRemoveTable = new ();
 | |
| 
 | |
|         // Using raising event list to prevent infinite loop call or changing delegate list
 | |
|         private readonly List<int> _raisingEventIds = new ();
 | |
| 
 | |
|         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();
 | |
|         }
 | |
|     }
 | |
| }
 |