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();
|
|
}
|
|
}
|
|
}
|