First commit for private source control. Older commits available on Github.
This commit is contained in:
3
Assets/Scripts/Framework/Coordinators/Events.meta
Normal file
3
Assets/Scripts/Framework/Coordinators/Events.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6d5109c39334a33a1ec140fed9aa971
|
||||
timeCreated: 1769705112
|
||||
BIN
Assets/Scripts/Framework/Coordinators/Events/.DS_Store
vendored
Normal file
BIN
Assets/Scripts/Framework/Coordinators/Events/.DS_Store
vendored
Normal file
Binary file not shown.
185
Assets/Scripts/Framework/Coordinators/Events/EventCoordinator.cs
Normal file
185
Assets/Scripts/Framework/Coordinators/Events/EventCoordinator.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BriarQueen.Framework.Events.System;
|
||||
using UnityEngine;
|
||||
using VContainer.Unity;
|
||||
|
||||
namespace BriarQueen.Framework.Coordinators.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A type-safe, queued event bus for managing gameplay events.
|
||||
/// </summary>
|
||||
public class EventCoordinator : IDisposable, ITickable, IInitializable
|
||||
{
|
||||
private const int MAX_EVENTS_PER_FRAME = 500;
|
||||
|
||||
public static bool EnableDebugLogs = false;
|
||||
|
||||
private readonly Queue<(Type type, IEvent data)> _eventQueue = new();
|
||||
private readonly object _queueLock = new();
|
||||
private readonly Dictionary<Type, Dictionary<object, Action<IEvent>>> _typedEventListeners = new();
|
||||
|
||||
private bool _isProcessingQueue;
|
||||
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_queueLock)
|
||||
{
|
||||
_typedEventListeners.Clear();
|
||||
_eventQueue.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
ProcessQueue();
|
||||
}
|
||||
|
||||
public void Subscribe<T>(Action<T> listener) where T : IEvent
|
||||
{
|
||||
if (listener == null) return;
|
||||
|
||||
var eventType = typeof(T);
|
||||
|
||||
if (!_typedEventListeners.TryGetValue(eventType, out var listeners))
|
||||
{
|
||||
listeners = new Dictionary<object, Action<IEvent>>();
|
||||
_typedEventListeners[eventType] = listeners;
|
||||
}
|
||||
|
||||
if (listeners.ContainsKey(listener)) return;
|
||||
|
||||
Action<IEvent> wrapper = e => listener((T)e);
|
||||
listeners[listener] = wrapper;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log(
|
||||
$"[EventManager] Subscribed '{eventType.Name}' to '{listener.Method.Name}' from '{listener.Target}'");
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Unsubscribe<T>(Action<T> listener) where T : IEvent
|
||||
{
|
||||
if (listener == null) return;
|
||||
|
||||
var eventType = typeof(T);
|
||||
|
||||
if (_typedEventListeners.TryGetValue(eventType, out var listeners))
|
||||
{
|
||||
listeners.Remove(listener);
|
||||
if (listeners.Count == 0)
|
||||
_typedEventListeners.Remove(eventType);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Publish<T>(T eventData) where T : IEvent
|
||||
{
|
||||
if (eventData == null) return;
|
||||
|
||||
lock (_queueLock)
|
||||
{
|
||||
_eventQueue.Enqueue((typeof(T), eventData));
|
||||
}
|
||||
|
||||
if (EnableDebugLogs)
|
||||
Debug.Log($"[EventManager] Enqueued '{typeof(T).Name}'");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void PublishImmediate<T>(T eventData) where T : IEvent
|
||||
{
|
||||
if (eventData == null) return;
|
||||
|
||||
var type = typeof(T);
|
||||
|
||||
if (_typedEventListeners.TryGetValue(type, out var listeners) && listeners.Count > 0)
|
||||
{
|
||||
var snapshot = new Action<IEvent>[listeners.Count];
|
||||
listeners.Values.CopyTo(snapshot, 0);
|
||||
|
||||
for (var i = 0; i < snapshot.Length; i++)
|
||||
{
|
||||
var listener = snapshot[i];
|
||||
try
|
||||
{
|
||||
listener?.Invoke(eventData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[EventManager] Exception in '{type.Name}' listener: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (EnableDebugLogs)
|
||||
Debug.Log($"[EventManager] Fired '{type.Name}' (Immediate)");
|
||||
}
|
||||
|
||||
private void ProcessQueue()
|
||||
{
|
||||
if (_isProcessingQueue) return;
|
||||
|
||||
_isProcessingQueue = true;
|
||||
|
||||
try
|
||||
{
|
||||
var processed = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
(Type type, IEvent data) evt;
|
||||
|
||||
lock (_queueLock)
|
||||
{
|
||||
if (_eventQueue.Count == 0) break;
|
||||
evt = _eventQueue.Dequeue();
|
||||
}
|
||||
|
||||
processed++;
|
||||
if (processed > MAX_EVENTS_PER_FRAME)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
"[EventManager] Max event processing limit reached. Possible infinite publish loop.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (_typedEventListeners.TryGetValue(evt.type, out var listeners) && listeners.Count > 0)
|
||||
{
|
||||
var snapshot = new Action<IEvent>[listeners.Count];
|
||||
listeners.Values.CopyTo(snapshot, 0);
|
||||
|
||||
for (var i = 0; i < snapshot.Length; i++)
|
||||
{
|
||||
var listener = snapshot[i];
|
||||
try
|
||||
{
|
||||
listener?.Invoke(evt.data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"[EventManager] Exception in '{evt.type.Name}' listener during queue processing: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (EnableDebugLogs)
|
||||
Debug.Log($"[EventManager] Fired '{evt.type.Name}' (Queued)");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isProcessingQueue = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f0594880d694d8e9297c88bbffbce8f
|
||||
timeCreated: 1769705112
|
||||
Reference in New Issue
Block a user