com.alicizax.unity.framework/Runtime/ABase/Event/EventContainer.cs

435 lines
15 KiB
C#
Raw Normal View History

2026-04-20 14:08:04 +08:00
using System;
2025-11-14 11:38:28 +08:00
using System.Runtime.CompilerServices;
using Unity.IL2CPP.CompilerServices;
using UnityEngine;
namespace AlicizaX
{
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
public static class EventContainer<TPayload> where TPayload : struct, IEventArgs
{
private static readonly int InitialSize = EventInitialSize<TPayload>.Size;
2025-12-24 14:34:26 +08:00
private static readonly int TypeId;
2026-04-20 14:08:04 +08:00
private static Action<TPayload>[] _valueCallbacks;
private static InEventHandler<TPayload>[] _inCallbacks;
2025-12-24 14:34:26 +08:00
private static int[] _versions;
private static int[] _activeSlots;
private static int[] _freeSlots;
private static int[] _activeIndices;
2026-04-20 14:08:04 +08:00
private static int _freeCount;
2025-11-14 11:38:28 +08:00
private static int _activeCount;
2026-04-20 14:08:04 +08:00
private static int _valueSubscriberCount;
private static int _inSubscriberCount;
2025-11-14 11:38:28 +08:00
private static int _version;
2026-04-20 14:08:04 +08:00
private static int _publishDepth;
2025-11-14 11:38:28 +08:00
#if UNITY_EDITOR
2026-04-20 14:08:04 +08:00
private static System.Collections.Generic.HashSet<Action<TPayload>> _activeValueHandlers;
private static System.Collections.Generic.HashSet<InEventHandler<TPayload>> _activeInHandlers;
2025-11-14 11:38:28 +08:00
#endif
static EventContainer()
{
2025-12-24 14:34:26 +08:00
TypeId = UnsubscribeRegistry.Register(Unsubscribe);
2026-04-20 14:08:04 +08:00
_valueCallbacks = new Action<TPayload>[InitialSize];
_inCallbacks = new InEventHandler<TPayload>[InitialSize];
2025-12-24 14:34:26 +08:00
_versions = new int[InitialSize];
_activeSlots = new int[InitialSize];
_freeSlots = new int[InitialSize];
_activeIndices = new int[InitialSize];
_freeCount = InitialSize;
2025-11-14 11:38:28 +08:00
for (int i = 0; i < InitialSize; i++)
{
_freeSlots[i] = i;
}
#if UNITY_EDITOR
2026-04-20 14:08:04 +08:00
_activeValueHandlers = new System.Collections.Generic.HashSet<Action<TPayload>>();
_activeInHandlers = new System.Collections.Generic.HashSet<InEventHandler<TPayload>>();
EventDebugRegistry.RegisterContainer<TPayload>(
GetDebugSubscriberCount,
GetDebugCapacity,
GetDebugValueSubscriberCount,
GetDebugInSubscriberCount,
GetDebugSubscribers);
#endif
2025-11-14 11:38:28 +08:00
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EventRuntimeHandle Subscribe(Action<TPayload> callback)
{
2026-04-20 14:08:04 +08:00
if (callback == null) throw new ArgumentNullException(nameof(callback));
ThrowIfMutatingDuringPublish("subscribe");
2026-04-20 14:08:04 +08:00
#if UNITY_EDITOR
if (_activeValueHandlers.Contains(callback))
2025-11-14 11:38:28 +08:00
{
2026-04-17 14:21:46 +08:00
Log.Warning($"重复订阅事件处理程序: {callback.Method.Name}");
2025-11-14 11:38:28 +08:00
return default;
}
2025-12-24 14:34:26 +08:00
2026-04-20 14:08:04 +08:00
_activeValueHandlers.Add(callback);
2025-11-14 11:38:28 +08:00
#endif
2026-04-20 14:08:04 +08:00
return SubscribeCore(callback, null);
}
2025-12-24 14:34:26 +08:00
2026-04-20 14:08:04 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EventRuntimeHandle Subscribe(InEventHandler<TPayload> callback)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
ThrowIfMutatingDuringPublish("subscribe");
#if UNITY_EDITOR
if (_activeInHandlers.Contains(callback))
2025-11-14 11:38:28 +08:00
{
2026-04-20 14:08:04 +08:00
Log.Warning($"重复订阅事件处理程序: {callback.Method.Name}");
return default;
2025-11-14 11:38:28 +08:00
}
2026-04-20 14:08:04 +08:00
_activeInHandlers.Add(callback);
#endif
return SubscribeCore(null, callback);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static EventRuntimeHandle SubscribeCore(Action<TPayload> valueCallback, InEventHandler<TPayload> inCallback)
{
int handlerIndex = GetFreeSlot();
EnsureActiveIndicesCapacity();
2025-11-14 11:38:28 +08:00
int activeIndex = _activeCount++;
_activeIndices[activeIndex] = handlerIndex;
int version = ++_version;
2026-04-20 14:08:04 +08:00
_valueCallbacks[handlerIndex] = valueCallback;
_inCallbacks[handlerIndex] = inCallback;
2025-12-24 14:34:26 +08:00
_versions[handlerIndex] = version;
_activeSlots[handlerIndex] = activeIndex;
2025-11-14 11:38:28 +08:00
2026-04-20 14:08:04 +08:00
if (inCallback != null)
{
_inSubscriberCount++;
}
else
{
_valueSubscriberCount++;
}
#if UNITY_EDITOR
2026-04-20 14:08:04 +08:00
EventDebugRegistry.RecordSubscribe<TPayload>(_activeCount, _valueCallbacks.Length);
#endif
2025-12-24 14:34:26 +08:00
return new EventRuntimeHandle(TypeId, handlerIndex, version);
2025-11-14 11:38:28 +08:00
}
2026-04-20 14:08:04 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void EnsureActiveIndicesCapacity()
{
if (_activeCount < _activeIndices.Length)
{
return;
}
int newSize = _activeIndices.Length == 0 ? 64 : _activeIndices.Length << 1;
Array.Resize(ref _activeIndices, newSize);
}
2025-11-14 11:38:28 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetFreeSlot()
{
2025-12-24 14:34:26 +08:00
if (_freeCount > 0)
{
return _freeSlots[--_freeCount];
}
2026-04-20 14:08:04 +08:00
int oldLen = _valueCallbacks.Length;
2025-12-24 14:34:26 +08:00
int newSize = oldLen == 0 ? 64 : oldLen << 1;
2025-11-14 11:38:28 +08:00
2026-04-20 14:08:04 +08:00
Array.Resize(ref _valueCallbacks, newSize);
Array.Resize(ref _inCallbacks, newSize);
2025-12-24 14:34:26 +08:00
Array.Resize(ref _versions, newSize);
Array.Resize(ref _activeSlots, newSize);
2025-11-14 11:38:28 +08:00
Array.Resize(ref _freeSlots, newSize);
2025-11-14 13:34:04 +08:00
2025-11-14 11:38:28 +08:00
for (int i = newSize - 1; i >= oldLen; i--)
{
_freeSlots[_freeCount++] = i;
}
#if UNITY_EDITOR
2026-04-20 14:08:04 +08:00
EventDebugRegistry.RecordResize<TPayload>(_activeCount, _valueCallbacks.Length);
#endif
2025-11-14 11:38:28 +08:00
return _freeSlots[--_freeCount];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Unsubscribe(int handlerIndex, int version)
{
ThrowIfMutatingDuringPublish("unsubscribe");
2026-03-10 18:56:51 +08:00
2026-04-20 14:08:04 +08:00
if ((uint)handlerIndex >= (uint)_versions.Length) return;
2025-12-24 14:34:26 +08:00
if (_versions[handlerIndex] != version) return;
2025-11-14 11:38:28 +08:00
2025-12-24 14:34:26 +08:00
int currentActiveIndex = _activeSlots[handlerIndex];
2026-04-20 14:08:04 +08:00
int lastActiveIndex = --_activeCount;
2025-11-14 11:38:28 +08:00
2026-04-20 14:08:04 +08:00
if (currentActiveIndex != lastActiveIndex)
{
int lastHandlerIndex = _activeIndices[lastActiveIndex];
_activeIndices[currentActiveIndex] = lastHandlerIndex;
_activeSlots[lastHandlerIndex] = currentActiveIndex;
}
2025-11-14 11:38:28 +08:00
2026-04-17 14:21:46 +08:00
#if UNITY_EDITOR
2026-04-20 14:08:04 +08:00
RemoveActiveHandler(handlerIndex);
2025-11-14 11:38:28 +08:00
#endif
2025-11-14 13:34:04 +08:00
2026-04-20 14:08:04 +08:00
if (_inCallbacks[handlerIndex] != null)
2025-11-14 11:38:28 +08:00
{
2026-04-20 14:08:04 +08:00
_inSubscriberCount--;
}
else
{
_valueSubscriberCount--;
2025-11-14 11:38:28 +08:00
}
2026-04-20 14:08:04 +08:00
_valueCallbacks[handlerIndex] = null;
_inCallbacks[handlerIndex] = null;
_versions[handlerIndex] = 0;
_activeSlots[handlerIndex] = 0;
_activeIndices[lastActiveIndex] = 0;
2025-11-14 11:38:28 +08:00
_freeSlots[_freeCount++] = handlerIndex;
#if UNITY_EDITOR
2026-04-20 14:08:04 +08:00
EventDebugRegistry.RecordUnsubscribe<TPayload>(_activeCount, _valueCallbacks.Length);
#endif
2025-11-14 11:38:28 +08:00
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Publish(in TPayload payload)
{
_publishDepth++;
try
{
2026-04-20 14:08:04 +08:00
int count = _activeCount;
#if UNITY_EDITOR
2026-04-20 14:08:04 +08:00
EventDebugRegistry.RecordPublish<TPayload>(count, _valueCallbacks.Length);
#endif
2026-04-20 14:08:04 +08:00
if (count == 0) return;
2025-12-24 14:34:26 +08:00
2026-04-20 14:08:04 +08:00
int[] indices = _activeIndices;
Action<TPayload>[] valueCallbacks = _valueCallbacks;
InEventHandler<TPayload>[] inCallbacks = _inCallbacks;
2025-11-14 11:38:28 +08:00
2026-04-20 14:08:04 +08:00
int i = 0;
int unrolled = count & ~3;
2025-12-24 14:34:26 +08:00
2026-04-20 14:08:04 +08:00
for (; i < unrolled; i += 4)
{
InvokeHandler(valueCallbacks, inCallbacks, indices[i], in payload);
InvokeHandler(valueCallbacks, inCallbacks, indices[i + 1], in payload);
InvokeHandler(valueCallbacks, inCallbacks, indices[i + 2], in payload);
InvokeHandler(valueCallbacks, inCallbacks, indices[i + 3], in payload);
}
2025-11-14 11:38:28 +08:00
2026-04-20 14:08:04 +08:00
switch (count - i)
{
case 3:
InvokeHandler(valueCallbacks, inCallbacks, indices[i], in payload);
InvokeHandler(valueCallbacks, inCallbacks, indices[i + 1], in payload);
InvokeHandler(valueCallbacks, inCallbacks, indices[i + 2], in payload);
break;
case 2:
InvokeHandler(valueCallbacks, inCallbacks, indices[i], in payload);
InvokeHandler(valueCallbacks, inCallbacks, indices[i + 1], in payload);
break;
case 1:
InvokeHandler(valueCallbacks, inCallbacks, indices[i], in payload);
break;
}
}
finally
{
_publishDepth--;
2025-11-14 11:38:28 +08:00
}
2026-04-20 14:08:04 +08:00
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InvokeHandler(Action<TPayload>[] valueCallbacks, InEventHandler<TPayload>[] inCallbacks, int handlerIndex, in TPayload payload)
{
InEventHandler<TPayload> inCallback = inCallbacks[handlerIndex];
if (inCallback != null)
{
inCallback(in payload);
return;
}
valueCallbacks[handlerIndex](payload);
2025-11-14 11:38:28 +08:00
}
2025-12-24 14:34:26 +08:00
public static int SubscriberCount
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _activeCount;
}
public static void EnsureCapacity(int capacity)
{
2026-04-20 14:08:04 +08:00
if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity));
ThrowIfMutatingDuringPublish("ensure capacity");
2026-04-20 14:08:04 +08:00
if (_valueCallbacks.Length >= capacity) return;
2025-12-24 14:34:26 +08:00
2026-04-20 14:08:04 +08:00
int oldLen = _valueCallbacks.Length;
Array.Resize(ref _valueCallbacks, capacity);
Array.Resize(ref _inCallbacks, capacity);
2025-12-24 14:34:26 +08:00
Array.Resize(ref _versions, capacity);
Array.Resize(ref _activeSlots, capacity);
Array.Resize(ref _freeSlots, capacity);
Array.Resize(ref _activeIndices, capacity);
for (int i = capacity - 1; i >= oldLen; i--)
{
_freeSlots[_freeCount++] = i;
}
#if UNITY_EDITOR
2026-04-20 14:08:04 +08:00
EventDebugRegistry.RecordResize<TPayload>(_activeCount, _valueCallbacks.Length);
#endif
2025-12-24 14:34:26 +08:00
}
public static void Clear()
{
ThrowIfMutatingDuringPublish("clear");
2025-12-24 14:34:26 +08:00
for (int i = 0; i < _activeCount; i++)
{
int idx = _activeIndices[i];
2026-04-20 14:08:04 +08:00
#if UNITY_EDITOR
RemoveActiveHandler(idx);
#endif
_valueCallbacks[idx] = null;
_inCallbacks[idx] = null;
2025-12-24 14:34:26 +08:00
_versions[idx] = 0;
2026-04-20 14:08:04 +08:00
_activeSlots[idx] = 0;
2025-12-24 14:34:26 +08:00
_freeSlots[_freeCount++] = idx;
2026-04-20 14:08:04 +08:00
_activeIndices[i] = 0;
2025-12-24 14:34:26 +08:00
}
_activeCount = 0;
2026-04-20 14:08:04 +08:00
_valueSubscriberCount = 0;
_inSubscriberCount = 0;
2025-12-24 14:34:26 +08:00
#if UNITY_EDITOR
2026-04-20 14:08:04 +08:00
EventDebugRegistry.RecordClear<TPayload>(_activeCount, _valueCallbacks.Length);
_activeValueHandlers.Clear();
_activeInHandlers.Clear();
2025-12-24 14:34:26 +08:00
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ThrowIfMutatingDuringPublish(string operation)
{
if (_publishDepth <= 0) return;
2026-04-20 14:08:04 +08:00
#if UNITY_EDITOR
EventDebugRegistry.RecordMutationRejected<TPayload>(_activeCount, _valueCallbacks.Length);
#endif
throw new InvalidOperationException(
$"EventContainer<{typeof(TPayload).Name}> cannot {operation} while publishing. " +
2026-04-20 14:08:04 +08:00
"This bus guarantees a stable subscriber set during dispatch. Apply the mutation after publish completes.");
}
#if UNITY_EDITOR
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void RemoveActiveHandler(int handlerIndex)
{
Action<TPayload> valueCallback = _valueCallbacks[handlerIndex];
if (valueCallback != null)
{
_activeValueHandlers.Remove(valueCallback);
return;
}
InEventHandler<TPayload> inCallback = _inCallbacks[handlerIndex];
if (inCallback != null)
{
_activeInHandlers.Remove(inCallback);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetDebugSubscriberCount() => _activeCount;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2026-04-20 14:08:04 +08:00
private static int GetDebugCapacity() => _valueCallbacks.Length;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetDebugValueSubscriberCount() => _valueSubscriberCount;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetDebugInSubscriberCount() => _inSubscriberCount;
private static EventDebugSubscriberInfo[] GetDebugSubscribers()
{
int count = _activeCount;
if (count == 0)
{
return Array.Empty<EventDebugSubscriberInfo>();
}
EventDebugSubscriberInfo[] subscribers = new EventDebugSubscriberInfo[count];
for (int i = 0; i < count; i++)
{
int handlerIndex = _activeIndices[i];
2026-04-20 14:08:04 +08:00
Action<TPayload> valueCallback = _valueCallbacks[handlerIndex];
InEventHandler<TPayload> inCallback = _inCallbacks[handlerIndex];
Delegate callback = (Delegate)inCallback ?? valueCallback;
object target = callback.Target;
bool isStatic = target == null;
bool isUnityObjectDestroyed = false;
2026-04-20 14:08:04 +08:00
bool usesInParameter = inCallback != null;
bool isCompilerGeneratedTarget = !isStatic && target.GetType().IsDefined(typeof(CompilerGeneratedAttribute), false);
bool isCompilerGeneratedMethod = callback.Method.IsDefined(typeof(CompilerGeneratedAttribute), false);
UnityEngine.Object unityTarget = null;
if (!isStatic && target is UnityEngine.Object engineObject)
{
unityTarget = engineObject;
isUnityObjectDestroyed = engineObject == null;
}
subscribers[i] = new EventDebugSubscriberInfo(
handlerIndex,
_versions[handlerIndex],
callback.Method.DeclaringType?.FullName ?? "<UnknownType>",
callback.Method.Name,
target?.GetType().FullName ?? "<Static>",
unityTarget,
isStatic,
2026-04-20 14:08:04 +08:00
isUnityObjectDestroyed,
usesInParameter,
isCompilerGeneratedTarget,
isCompilerGeneratedMethod);
}
return subscribers;
}
#endif
2025-11-14 11:38:28 +08:00
}
}