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 Handler[] _handlers = new Handler[InitialSize]; private static int[] _freeSlots = new int[InitialSize]; private static int _freeCount = InitialSize; private static int[] _activeIndices = new int[InitialSize]; private static int _activeCount; private static int _version; #if Event_StrickCheck private static System.Collections.Generic.HashSet> _activeHandlers = new(); #endif static EventContainer() { for (int i = 0; i < InitialSize; i++) { _freeSlots[i] = i; } } [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.DivideByZeroChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] [StructLayout(LayoutKind.Sequential, Pack = 4)] private struct Handler { public Action Callback; public int Version; public int ActiveSlot; } [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.DivideByZeroChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EventRuntimeHandle Subscribe(Action callback) { #if Event_StrickCheck if (_activeHandlers.Contains(callback)) { Log.Warning($"重复订阅事件处理程序: {callback.Method.Name}"); return default; } else { _activeHandlers.Add(callback); } #endif int handlerIndex = GetFreeSlot(); if (_activeCount >= _activeIndices.Length) { Array.Resize(ref _activeIndices, _activeIndices.Length * 2); } int activeIndex = _activeCount++; _activeIndices[activeIndex] = handlerIndex; int version = ++_version; _handlers[handlerIndex] = new Handler { Callback = callback, Version = version, ActiveSlot = activeIndex }; return new EventRuntimeHandle(Unsubscribe, handlerIndex, version); } [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.DivideByZeroChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetFreeSlot() { if (_freeCount > 0) return _freeSlots[--_freeCount]; int oldLen = _handlers.Length; int newSize = oldLen == 0 ? 64 : oldLen * 2; Array.Resize(ref _handlers, newSize); Array.Resize(ref _freeSlots, newSize); for (int i = newSize - 1; i >= oldLen; i--) { _freeSlots[_freeCount++] = i; } return _freeSlots[--_freeCount]; } [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.DivideByZeroChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Unsubscribe(int handlerIndex, int version) { ref Handler handler = ref _handlers[handlerIndex]; if (handler.Version != version) return; int lastActiveIndex = --_activeCount; int lastHandlerIndex = _activeIndices[lastActiveIndex]; int currentActiveIndex = handler.ActiveSlot; _activeIndices[currentActiveIndex] = lastHandlerIndex; _handlers[lastHandlerIndex].ActiveSlot = currentActiveIndex; #if Event_StrickCheck _activeHandlers.Remove(handler.Callback); #endif handler.Callback = null; handler.Version = 0; if (_freeCount >= _freeSlots.Length) { Array.Resize(ref _freeSlots, _freeSlots.Length * 2); } _freeSlots[_freeCount++] = handlerIndex; } [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.DivideByZeroChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Publish(in TPayload payload) { int count = _activeCount; int[] indices = _activeIndices; Handler[] handlers = _handlers; int i = 0; for (; i <= count - 4; i += 4) { handlers[indices[i]].Callback(payload); handlers[indices[i + 1]].Callback(payload); handlers[indices[i + 2]].Callback(payload); handlers[indices[i + 3]].Callback(payload); } for (; i < count; i++) { handlers[indices[i]].Callback(payload); } } } }