upm_guru_dof_lib/Runtime/GameEventMgr.cs

510 lines
18 KiB
C#
Raw Normal View History

2023-08-30 12:26:51 +00:00
using System;
using System.Collections.Generic;
using UnityEngine;
namespace DofLibrary
2023-08-30 12:26:51 +00:00
{
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;
2023-09-04 06:06:38 +00:00
if (_isDebugMode)
2023-08-30 12:26:51 +00:00
{
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;
}
}
2023-09-04 06:06:38 +00:00
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>>();
2023-08-30 12:26:51 +00:00
// Using raising event list to prevent infinite loop call or changing delegate list
2023-09-04 06:06:38 +00:00
private readonly List<int> _raisingEventIds = new List<int>();
2023-08-30 12:26:51 +00:00
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++)
{
2023-09-04 06:06:38 +00:00
if (listeners[i].gameObject == null)
2023-08-30 12:26:51 +00:00
{
listeners.RemoveAt(i--);
}
}
}
}
private List<Delegate> SpawnDelegateList()
{
2023-09-04 06:06:38 +00:00
if (_listenerListQueue.Count > 0)
2023-08-30 12:26:51 +00:00
{
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];
2023-09-04 06:06:38 +00:00
if (listener != null)
2023-08-30 12:26:51 +00:00
{
try
{
listener.Method.Invoke(listener.Target, args);
result = true;
}
catch (Exception ex)
{
listeners.RemoveAt(i--);
LogException(ex, event_id.ToString(), "DoRaise");
}
}
2023-09-04 06:06:38 +00:00
}
2023-08-30 12:26:51 +00:00
}
_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();
}
}
}