262 lines
8.8 KiB
C#
262 lines
8.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Timers;
|
|
using UnityEngine;
|
|
|
|
public static class EventBus
|
|
{
|
|
private static readonly Dictionary<Type, Delegate> globalListeners = new();
|
|
private static readonly Dictionary<Type, Delegate> globalClassListeners = new();
|
|
|
|
private static readonly Dictionary<int, Dictionary<Type, Delegate>> channelListeners = new();
|
|
private static readonly Dictionary<int, Dictionary<Type, Delegate>> channelClassListeners = new();
|
|
|
|
private static Timer cleanupTimer;
|
|
|
|
public static bool DebugEnabled = false;
|
|
|
|
private static void DebugLog(string message)
|
|
{
|
|
if (DebugEnabled)
|
|
Console.WriteLine($"[EventBus] {message}");
|
|
}
|
|
|
|
// ===== GLOBAL STRUCT EVENTS =====
|
|
public static void Subscribe<T>(Action<T> listener) where T : struct
|
|
{
|
|
var type = typeof(T);
|
|
|
|
if (!globalListeners.ContainsKey(typeof(T)))
|
|
globalListeners[type] = null;
|
|
|
|
globalListeners[type] = (Action<T>)globalListeners[type] + listener;
|
|
}
|
|
|
|
public static void Unsubscribe<T>(Action<T> listener) where T : struct
|
|
{
|
|
var type = typeof(T);
|
|
if (globalListeners.ContainsKey(typeof(T)))
|
|
{
|
|
globalListeners[type] = (Action<T>)globalListeners[type] - listener;
|
|
if (globalListeners[type] == null)
|
|
globalListeners.Remove(type);
|
|
}
|
|
}
|
|
|
|
public static void Publish<T>(T eventData) where T : struct
|
|
{
|
|
var type = typeof(T);
|
|
if (globalListeners.TryGetValue(type, out var del) && del is Action<T> action)
|
|
{
|
|
DebugLog($"Publish Global Struct Event: {typeof(T).Name}");
|
|
action.Invoke(eventData);
|
|
}
|
|
}
|
|
|
|
// ===== GLOBAL CLASS EVENTS =====
|
|
public static void SubscribeClass<T>(Action<T> listener) where T : class
|
|
{
|
|
var type = typeof(T);
|
|
if (!globalClassListeners.ContainsKey(type))
|
|
globalClassListeners[type] = null;
|
|
|
|
globalClassListeners[type] = (Action<T>)globalClassListeners[type] + listener;
|
|
}
|
|
|
|
public static void UnsubscribeClass<T>(Action<T> listener) where T : class
|
|
{
|
|
var type = typeof(T);
|
|
if (globalClassListeners.ContainsKey(type))
|
|
{
|
|
globalClassListeners[type] = (Action<T>)globalClassListeners[type] - listener;
|
|
if (globalClassListeners[type] == null)
|
|
globalClassListeners.Remove(type);
|
|
}
|
|
}
|
|
|
|
public static void PublishClass<T>(T eventData) where T : class
|
|
{
|
|
var type = typeof(T);
|
|
if (globalClassListeners.TryGetValue(type, out var del) && del is Action<T> action)
|
|
{
|
|
DebugLog($"Publish Global Class Event: {typeof(T).Name}");
|
|
action.Invoke(eventData);
|
|
}
|
|
}
|
|
|
|
// ===== CHANNEL STRUCT EVENTS =====
|
|
public static void SubscribeChannel<T>(int channelId, Action<T> listener) where T : struct
|
|
{
|
|
var type = typeof(T);
|
|
|
|
if (!channelListeners.ContainsKey(channelId))
|
|
channelListeners[channelId] = new Dictionary<Type, Delegate>();
|
|
|
|
if (!channelListeners[channelId].ContainsKey(type))
|
|
channelListeners[channelId][type] = null;
|
|
|
|
channelListeners[channelId][type] = (Action<T>)channelListeners[channelId][type] + listener;
|
|
}
|
|
|
|
public static void UnsubscribeChannel<T>(int channelId, Action<T> listener) where T : struct
|
|
{
|
|
var type = typeof(T);
|
|
if (channelListeners.ContainsKey(channelId) && channelListeners[channelId].ContainsKey(type))
|
|
{
|
|
channelListeners[channelId][type] = (Action<T>)channelListeners[channelId][type] - listener;
|
|
if (channelListeners[channelId][type] == null)
|
|
channelListeners[channelId].Remove(type);
|
|
if (channelListeners[channelId].Count == 0)
|
|
channelListeners.Remove(channelId);
|
|
}
|
|
}
|
|
|
|
public static void PublishChannel<T>(int channelId, T eventData) where T : struct
|
|
{
|
|
var type = typeof(T);
|
|
if (channelListeners.TryGetValue(channelId, out var listeners) &&
|
|
listeners.TryGetValue(type, out var del) &&
|
|
del is Action<T> action)
|
|
{
|
|
DebugLog($"Publish Channel Struct Event: {type.Name} to channel '{channelId}'");
|
|
action.Invoke(eventData);
|
|
}
|
|
}
|
|
|
|
// ===== CHANNEL CLASS EVENTS =====
|
|
public static void SubscribeChannelClass<T>(int channelId, Action<T> listener) where T : class
|
|
{
|
|
var type = typeof(T);
|
|
if (!channelClassListeners.ContainsKey(channelId))
|
|
channelClassListeners[channelId] = new Dictionary<Type, Delegate>();
|
|
|
|
if (!channelClassListeners[channelId].ContainsKey(type))
|
|
channelClassListeners[channelId][type] = null;
|
|
|
|
channelClassListeners[channelId][type] = (Action<T>)channelClassListeners[channelId][type] + listener;
|
|
}
|
|
|
|
public static void UnsubscribeChannelClass<T>(int channelId, Action<T> listener) where T : class
|
|
{
|
|
var type = typeof(T);
|
|
if (channelClassListeners.ContainsKey(channelId) && channelClassListeners[channelId].ContainsKey(type))
|
|
{
|
|
channelClassListeners[channelId][type] = (Action<T>)channelClassListeners[channelId][type] - listener;
|
|
if (channelClassListeners[channelId][type] == null)
|
|
channelClassListeners[channelId].Remove(type);
|
|
if (channelClassListeners[channelId].Count == 0)
|
|
channelClassListeners.Remove(channelId);
|
|
}
|
|
}
|
|
|
|
public static void PublishChannelClass<T>(int channelId, T eventData) where T : class
|
|
{
|
|
var type = typeof(T);
|
|
if (channelClassListeners.TryGetValue(channelId, out var listeners) &&
|
|
listeners.TryGetValue(type, out var del) &&
|
|
del is Action<T> action)
|
|
{
|
|
DebugLog($"Publish Channel Class Event: {type.Name} to channel '{channelId}'");
|
|
action.Invoke(eventData);
|
|
}
|
|
}
|
|
|
|
// ===== ONE-TIME SUBSCRIBE =====
|
|
public static void SubscribeOnce<T>(Action<T> listener) where T : struct
|
|
{
|
|
Action<T> wrapper = null;
|
|
wrapper = (data) =>
|
|
{
|
|
listener(data);
|
|
Unsubscribe(wrapper);
|
|
};
|
|
Subscribe(wrapper);
|
|
}
|
|
|
|
public static void SubscribeOnceClass<T>(Action<T> listener) where T : class
|
|
{
|
|
Action<T> wrapper = null;
|
|
wrapper = (data) =>
|
|
{
|
|
listener(data);
|
|
UnsubscribeClass(wrapper);
|
|
};
|
|
SubscribeClass(wrapper);
|
|
}
|
|
|
|
public static void SubscribeOnceChannel<T>(int channelId, Action<T> listener) where T : struct
|
|
{
|
|
Action<T> wrapper = null;
|
|
wrapper = (data) =>
|
|
{
|
|
listener(data);
|
|
UnsubscribeChannel(channelId, wrapper);
|
|
};
|
|
SubscribeChannel(channelId, wrapper);
|
|
}
|
|
|
|
public static void SubscribeOnceChannelClass<T>(int channelId, Action<T> listener) where T : class
|
|
{
|
|
Action<T> wrapper = null;
|
|
wrapper = (data) =>
|
|
{
|
|
listener(data);
|
|
UnsubscribeChannelClass(channelId, wrapper);
|
|
};
|
|
SubscribeChannelClass(channelId, wrapper);
|
|
}
|
|
|
|
// ===== CHANNEL UTILITIES =====
|
|
public static bool ChannelExists(int channelId)
|
|
{
|
|
return channelListeners.ContainsKey(channelId) || channelClassListeners.ContainsKey(channelId);
|
|
}
|
|
|
|
public static void EnableAutoCleanup(float delaySeconds = 5f)
|
|
{
|
|
cleanupTimer = new Timer(delaySeconds * 1000);
|
|
cleanupTimer.Elapsed += (sender, e) =>
|
|
{
|
|
var emptyStructChannels = channelListeners
|
|
.Where(pair => pair.Value.Values.All(d => d == null))
|
|
.Select(pair => pair.Key)
|
|
.ToList();
|
|
|
|
foreach (var id in emptyStructChannels)
|
|
{
|
|
channelListeners.Remove(id);
|
|
DebugLog($"Removed empty struct channel: {id}");
|
|
}
|
|
|
|
var emptyClassChannels = channelClassListeners
|
|
.Where(pair => pair.Value.Values.All(d => d == null))
|
|
.Select(pair => pair.Key)
|
|
.ToList();
|
|
|
|
foreach (var id in emptyClassChannels)
|
|
{
|
|
channelClassListeners.Remove(id);
|
|
DebugLog($"Removed empty class channel: {id}");
|
|
}
|
|
};
|
|
cleanupTimer.AutoReset = true;
|
|
cleanupTimer.Start();
|
|
}
|
|
#if UNITY_EDITOR
|
|
public static void LogAllActiveListeners()
|
|
{
|
|
foreach (var kv in globalListeners)
|
|
{
|
|
var methods = kv.Value?.GetInvocationList();
|
|
if (methods != null && methods.Length > 0)
|
|
{
|
|
Debug.LogWarning($"[EventBus Leak] {kv.Key.Name} still has {methods.Length} listeners:");
|
|
foreach (var m in methods)
|
|
Debug.LogWarning($" - Target: {m.Target}, Method: {m.Method}");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|