using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Unity.IL2CPP.CompilerServices; using UnityEngine; namespace AlicizaX { [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.DivideByZeroChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] public static class EventContainer where TPayload : struct, IEventArgs { private static readonly int InitialSize = EventInitialSize.Size; private static readonly int TypeId; private static Action[] _callbacks; private static int[] _versions; private static int[] _activeSlots; private static int[] _freeSlots; private static int _freeCount; private static int[] _activeIndices; private static int _activeCount; private static int _version; #if UNITY_EDITOR private static int _publishDepth; private static System.Collections.Generic.HashSet> _activeHandlers; #endif static EventContainer() { TypeId = UnsubscribeRegistry.Register(Unsubscribe); _callbacks = new Action[InitialSize]; _versions = new int[InitialSize]; _activeSlots = new int[InitialSize]; _freeSlots = new int[InitialSize]; _activeIndices = new int[InitialSize]; _freeCount = InitialSize; for (int i = 0; i < InitialSize; i++) { _freeSlots[i] = i; } #if UNITY_EDITOR _activeHandlers = new System.Collections.Generic.HashSet>(); EventDebugRegistry.RegisterContainer(GetDebugSubscriberCount, GetDebugCapacity, GetDebugSubscribers); #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EventRuntimeHandle Subscribe(Action callback) { #if UNITY_EDITOR ThrowIfMutatingDuringPublish("subscribe"); if (_activeHandlers.Contains(callback)) { Log.Warning($"重复订阅事件处理程序: {callback.Method.Name}"); return default; } _activeHandlers.Add(callback); #endif int handlerIndex = GetFreeSlot(); if (_activeCount >= _activeIndices.Length) { Array.Resize(ref _activeIndices, _activeIndices.Length << 1); } int activeIndex = _activeCount++; _activeIndices[activeIndex] = handlerIndex; int version = ++_version; _callbacks[handlerIndex] = callback; _versions[handlerIndex] = version; _activeSlots[handlerIndex] = activeIndex; #if UNITY_EDITOR EventDebugRegistry.RecordSubscribe(_activeCount, _callbacks.Length); #endif return new EventRuntimeHandle(TypeId, handlerIndex, version); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetFreeSlot() { if (_freeCount > 0) { return _freeSlots[--_freeCount]; } int oldLen = _callbacks.Length; int newSize = oldLen == 0 ? 64 : oldLen << 1; Array.Resize(ref _callbacks, newSize); Array.Resize(ref _versions, newSize); Array.Resize(ref _activeSlots, newSize); Array.Resize(ref _freeSlots, newSize); for (int i = newSize - 1; i >= oldLen; i--) { _freeSlots[_freeCount++] = i; } #if UNITY_EDITOR EventDebugRegistry.RecordResize(_activeCount, _callbacks.Length); #endif return _freeSlots[--_freeCount]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Unsubscribe(int handlerIndex, int version) { #if UNITY_EDITOR ThrowIfMutatingDuringPublish("unsubscribe"); #endif if (_versions[handlerIndex] != version) return; int lastActiveIndex = --_activeCount; int lastHandlerIndex = _activeIndices[lastActiveIndex]; int currentActiveIndex = _activeSlots[handlerIndex]; _activeIndices[currentActiveIndex] = lastHandlerIndex; _activeSlots[lastHandlerIndex] = currentActiveIndex; #if UNITY_EDITOR _activeHandlers.Remove(_callbacks[handlerIndex]); #endif _callbacks[handlerIndex] = null; _versions[handlerIndex] = 0; if (_freeCount >= _freeSlots.Length) { Array.Resize(ref _freeSlots, _freeSlots.Length << 1); } _freeSlots[_freeCount++] = handlerIndex; #if UNITY_EDITOR EventDebugRegistry.RecordUnsubscribe(_activeCount, _callbacks.Length); #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Publish(in TPayload payload) { #if UNITY_EDITOR _publishDepth++; try { #endif int count = _activeCount; #if UNITY_EDITOR EventDebugRegistry.RecordPublish(count, _callbacks.Length); #endif if (count == 0) return; int[] indices = _activeIndices; Action[] callbacks = _callbacks; int i = 0; int unrolled = count & ~3; // count - (count % 4) for (; i < unrolled; i += 4) { int idx0 = indices[i]; int idx1 = indices[i + 1]; int idx2 = indices[i + 2]; int idx3 = indices[i + 3]; callbacks[idx0](payload); callbacks[idx1](payload); callbacks[idx2](payload); callbacks[idx3](payload); } switch (count - i) { case 3: callbacks[indices[i]](payload); callbacks[indices[i + 1]](payload); callbacks[indices[i + 2]](payload); break; case 2: callbacks[indices[i]](payload); callbacks[indices[i + 1]](payload); break; case 1: callbacks[indices[i]](payload); break; } #if UNITY_EDITOR } finally { _publishDepth--; } #endif } public static int SubscriberCount { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _activeCount; } public static void EnsureCapacity(int capacity) { #if UNITY_EDITOR ThrowIfMutatingDuringPublish("ensure capacity"); #endif if (_callbacks.Length >= capacity) return; int oldLen = _callbacks.Length; Array.Resize(ref _callbacks, capacity); 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 EventDebugRegistry.RecordResize(_activeCount, _callbacks.Length); #endif } public static void Clear() { #if UNITY_EDITOR ThrowIfMutatingDuringPublish("clear"); #endif for (int i = 0; i < _activeCount; i++) { int idx = _activeIndices[i]; _callbacks[idx] = null; _versions[idx] = 0; _freeSlots[_freeCount++] = idx; } _activeCount = 0; #if UNITY_EDITOR EventDebugRegistry.RecordClear(_activeCount, _callbacks.Length); _activeHandlers.Clear(); #endif } #if UNITY_EDITOR [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ThrowIfMutatingDuringPublish(string operation) { if (_publishDepth <= 0) return; throw new InvalidOperationException( $"EventContainer<{typeof(TPayload).Name}> cannot {operation} while publishing. " + "Supporting dispatch-time mutations would require slower publish semantics."); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetDebugSubscriberCount() => _activeCount; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetDebugCapacity() => _callbacks.Length; private static EventDebugSubscriberInfo[] GetDebugSubscribers() { int count = _activeCount; if (count == 0) { return Array.Empty(); } EventDebugSubscriberInfo[] subscribers = new EventDebugSubscriberInfo[count]; for (int i = 0; i < count; i++) { int handlerIndex = _activeIndices[i]; Action callback = _callbacks[handlerIndex]; object target = callback.Target; bool isStatic = target == null; bool isUnityObjectDestroyed = 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 ?? "", callback.Method.Name, target?.GetType().FullName ?? "", unityTarget, isStatic, isUnityObjectDestroyed); } return subscribers; } #endif } }