2023-08-30 12:26:51 +00:00
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 ;
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 ( ) ;
}
}
}