327 lines
10 KiB
C#
327 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Timers;
|
|
|
|
public static class EventBus
|
|
{
|
|
// Cho phép null ở value để có thể "clear" slot
|
|
private static readonly Dictionary<Type, Delegate?> globalListeners =
|
|
new Dictionary<Type, Delegate?>();
|
|
|
|
private static readonly Dictionary<Type, Delegate?> globalClassListeners =
|
|
new Dictionary<Type, Delegate?>();
|
|
|
|
private static readonly Dictionary<int, Dictionary<Type, Delegate?>> channelListeners =
|
|
new Dictionary<int, Dictionary<Type, Delegate?>>();
|
|
|
|
private static readonly Dictionary<int, Dictionary<Type, Delegate?>> channelClassListeners =
|
|
new Dictionary<int, Dictionary<Type, Delegate?>>();
|
|
|
|
// Cho phép null khi chưa bật auto-cleanup
|
|
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(type))
|
|
globalListeners[type] = null;
|
|
|
|
var current = globalListeners[type] as Action<T>;
|
|
current += listener;
|
|
globalListeners[type] = current;
|
|
}
|
|
|
|
public static void Unsubscribe<T>(Action<T> listener) where T : struct
|
|
{
|
|
var type = typeof(T);
|
|
if (!globalListeners.ContainsKey(type))
|
|
return;
|
|
|
|
var current = globalListeners[type] as Action<T>;
|
|
current -= listener;
|
|
if (current == null)
|
|
globalListeners.Remove(type);
|
|
else
|
|
globalListeners[type] = current;
|
|
}
|
|
|
|
public static void Publish<T>(T eventData) where T : struct
|
|
{
|
|
var type = typeof(T);
|
|
if (globalListeners.TryGetValue(type, out var del))
|
|
{
|
|
var action = del as Action<T>;
|
|
if (action != null)
|
|
{
|
|
DebugLog($"Publish Global Struct Event: {type.Name}");
|
|
action(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;
|
|
|
|
var current = globalClassListeners[type] as Action<T>;
|
|
current += listener;
|
|
globalClassListeners[type] = current;
|
|
}
|
|
|
|
public static void UnsubscribeClass<T>(Action<T> listener) where T : class
|
|
{
|
|
var type = typeof(T);
|
|
if (!globalClassListeners.ContainsKey(type))
|
|
return;
|
|
|
|
var current = globalClassListeners[type] as Action<T>;
|
|
current -= listener;
|
|
if (current == null)
|
|
globalClassListeners.Remove(type);
|
|
else
|
|
globalClassListeners[type] = current;
|
|
}
|
|
|
|
public static void PublishClass<T>(T eventData) where T : class
|
|
{
|
|
var type = typeof(T);
|
|
if (globalClassListeners.TryGetValue(type, out var del))
|
|
{
|
|
var action = del as Action<T>;
|
|
if (action != null)
|
|
{
|
|
DebugLog($"Publish Global Class Event: {type.Name}");
|
|
action(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;
|
|
|
|
var current = channelListeners[channelId][type] as Action<T>;
|
|
current += listener;
|
|
channelListeners[channelId][type] = current;
|
|
}
|
|
|
|
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))
|
|
return;
|
|
|
|
var current = channelListeners[channelId][type] as Action<T>;
|
|
current -= listener;
|
|
|
|
if (current == null)
|
|
{
|
|
channelListeners[channelId].Remove(type);
|
|
if (channelListeners[channelId].Count == 0)
|
|
channelListeners.Remove(channelId);
|
|
}
|
|
else
|
|
{
|
|
channelListeners[channelId][type] = current;
|
|
}
|
|
}
|
|
|
|
public static void PublishChannel<T>(int channelId, T eventData) where T : struct
|
|
{
|
|
var type = typeof(T);
|
|
if (channelListeners.TryGetValue(channelId, out var dict))
|
|
{
|
|
Delegate? del;
|
|
if (dict.TryGetValue(type, out del))
|
|
{
|
|
var action = del as Action<T>;
|
|
if (action != null)
|
|
{
|
|
DebugLog($"Publish Channel Struct Event: {type.Name} to channel '{channelId}'");
|
|
action(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;
|
|
|
|
var current = channelClassListeners[channelId][type] as Action<T>;
|
|
current += listener;
|
|
channelClassListeners[channelId][type] = current;
|
|
}
|
|
|
|
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))
|
|
return;
|
|
|
|
var current = channelClassListeners[channelId][type] as Action<T>;
|
|
current -= listener;
|
|
|
|
if (current == null)
|
|
{
|
|
channelClassListeners[channelId].Remove(type);
|
|
if (channelClassListeners[channelId].Count == 0)
|
|
channelClassListeners.Remove(channelId);
|
|
}
|
|
else
|
|
{
|
|
channelClassListeners[channelId][type] = current;
|
|
}
|
|
}
|
|
|
|
public static void PublishChannelClass<T>(int channelId, T eventData) where T : class
|
|
{
|
|
var type = typeof(T);
|
|
if (channelClassListeners.TryGetValue(channelId, out var dict))
|
|
{
|
|
Delegate? del;
|
|
if (dict.TryGetValue(type, out del))
|
|
{
|
|
var action = del as Action<T>;
|
|
if (action != null)
|
|
{
|
|
DebugLog($"Publish Channel Class Event: {type.Name} to channel '{channelId}'");
|
|
action(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();
|
|
}
|
|
public static void UnsubscribeAllChannels()
|
|
{
|
|
channelListeners.Clear();
|
|
channelClassListeners.Clear();
|
|
DebugLog("Unsubscribed all events from all channels");
|
|
}
|
|
public static void UnsubscribeAllInChannel(int channelId)
|
|
{
|
|
if (channelListeners.ContainsKey(channelId))
|
|
{
|
|
channelListeners[channelId].Clear();
|
|
channelListeners.Remove(channelId);
|
|
DebugLog($"Unsubscribed all struct events in channel {channelId}");
|
|
}
|
|
|
|
if (channelClassListeners.ContainsKey(channelId))
|
|
{
|
|
channelClassListeners[channelId].Clear();
|
|
channelClassListeners.Remove(channelId);
|
|
DebugLog($"Unsubscribed all class events in channel {channelId}");
|
|
}
|
|
}
|
|
|
|
}
|