Files
2025-10-09 08:52:31 +07:00

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}");
}
}
}