using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Unity.IL2CPP.CompilerServices; using UnityEngine; namespace AlicizaX { public delegate void TimerHandler(params object[] args); public delegate void TimerHandlerNoArgs(); [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] [StructLayout(LayoutKind.Sequential, Pack = 4)] internal struct TimerInfo : IMemory { public int TimerId; public float TriggerTime; public float Interval; public TimerHandler Handler; public TimerHandlerNoArgs HandlerNoArgs; public Delegate HandlerGeneric; public bool IsLoop; public bool IsRunning; public bool IsUnscaled; public bool IsActive; public byte HandlerType; public object[] Args; public object GenericArg; public int Level; public int SlotIndex; public int NextTimerIndex; public int PrevTimerIndex; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { Handler = null; HandlerNoArgs = null; HandlerGeneric = null; Args = null; GenericArg = null; IsActive = false; Level = -1; SlotIndex = -1; NextTimerIndex = -1; PrevTimerIndex = -1; } } [UnityEngine.Scripting.Preserve] [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] internal sealed class TimerModule : ITimerModule { private int _curTimerId; private HierarchicalTimeWheel _scaledTimeWheel; private HierarchicalTimeWheel _unscaledTimeWheel; private TimerInfo[] _timerPool; private int[] _timerIdToPoolIndex; private int _timerPoolCapacity; private int[] _freeIndices; private int _freeCount; private int[] _pendingRemoveTimers; private int _pendingRemoveCount; private class HierarchicalTimeWheel { private const int SLOT_COUNT_LEVEL0 = 256; private const int SLOT_COUNT_LEVEL1 = 64; private const int SLOT_COUNT_LEVEL2 = 64; private readonly float _slotInterval; private readonly TimeWheelLevel[] _levels; private float _currentTime; private class TimeWheelLevel { public readonly int[] SlotHeads; public readonly int SlotCount; public int CurrentSlot; public TimeWheelLevel(int slotCount) { SlotCount = slotCount; SlotHeads = new int[slotCount]; for (int i = 0; i < slotCount; i++) SlotHeads[i] = -1; } } public HierarchicalTimeWheel(float slotInterval) { _slotInterval = slotInterval; _levels = new TimeWheelLevel[3]; _levels[0] = new TimeWheelLevel(SLOT_COUNT_LEVEL0); _levels[1] = new TimeWheelLevel(SLOT_COUNT_LEVEL1); _levels[2] = new TimeWheelLevel(SLOT_COUNT_LEVEL2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddTimer(int poolIndex, TimerInfo[] pool, float currentTime) { ref TimerInfo timer = ref pool[poolIndex]; if (!timer.IsActive) return; float delta = timer.TriggerTime - currentTime; if (delta < 0) delta = 0; int totalSlots = Mathf.FloorToInt(delta / _slotInterval); int level = 0; int slotIndex = 0; if (totalSlots < SLOT_COUNT_LEVEL0) { level = 0; slotIndex = (_levels[0].CurrentSlot + totalSlots) % SLOT_COUNT_LEVEL0; } else if (totalSlots < SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1) { level = 1; int level1Slots = totalSlots / SLOT_COUNT_LEVEL0; slotIndex = (_levels[1].CurrentSlot + level1Slots) % SLOT_COUNT_LEVEL1; } else { level = 2; int level2Slots = totalSlots / (SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1); slotIndex = (_levels[2].CurrentSlot + level2Slots) % SLOT_COUNT_LEVEL2; } timer.Level = level; timer.SlotIndex = slotIndex; timer.NextTimerIndex = _levels[level].SlotHeads[slotIndex]; timer.PrevTimerIndex = -1; if (_levels[level].SlotHeads[slotIndex] != -1) { pool[_levels[level].SlotHeads[slotIndex]].PrevTimerIndex = poolIndex; } _levels[level].SlotHeads[slotIndex] = poolIndex; timer.IsRunning = true; } public void Advance(float currentTime, TimerInfo[] pool, Action processTimer) { float timeDelta = currentTime - _currentTime; if (timeDelta <= 0) return; int steps = Mathf.FloorToInt(timeDelta / _slotInterval); for (int step = 0; step < steps; step++) { _currentTime += _slotInterval; ProcessLevel(0, pool, processTimer); if ((_levels[0].CurrentSlot % SLOT_COUNT_LEVEL0) == 0) { ProcessLevel(1, pool, processTimer); } if ((_levels[0].CurrentSlot % (SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1)) == 0) { ProcessLevel(2, pool, processTimer); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ProcessLevel(int level, TimerInfo[] pool, Action processTimer) { TimeWheelLevel wheelLevel = _levels[level]; wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) % wheelLevel.SlotCount; int currentHead = wheelLevel.SlotHeads[wheelLevel.CurrentSlot]; wheelLevel.SlotHeads[wheelLevel.CurrentSlot] = -1; int currentIndex = currentHead; while (currentIndex != -1) { ref TimerInfo timer = ref pool[currentIndex]; int nextIndex = timer.NextTimerIndex; timer.NextTimerIndex = -1; timer.PrevTimerIndex = -1; timer.Level = -1; if (timer.IsActive && timer.IsRunning) { if (level == 0) { processTimer(currentIndex); } else { AddTimer(currentIndex, pool, _currentTime); } } currentIndex = nextIndex; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveTimer(int poolIndex, TimerInfo[] pool) { if (poolIndex < 0 || poolIndex >= pool.Length) return; ref TimerInfo timer = ref pool[poolIndex]; if (timer.Level < 0 || timer.Level >= _levels.Length) return; int level = timer.Level; int slotIndex = timer.SlotIndex; if (slotIndex < 0 || slotIndex >= _levels[level].SlotCount) return; if (timer.PrevTimerIndex != -1) { if (timer.PrevTimerIndex < pool.Length) { pool[timer.PrevTimerIndex].NextTimerIndex = timer.NextTimerIndex; } } else { _levels[level].SlotHeads[slotIndex] = timer.NextTimerIndex; } if (timer.NextTimerIndex != -1) { if (timer.NextTimerIndex < pool.Length) { pool[timer.NextTimerIndex].PrevTimerIndex = timer.PrevTimerIndex; } } timer.Level = -1; timer.SlotIndex = -1; timer.NextTimerIndex = -1; timer.PrevTimerIndex = -1; } } public TimerModule() { _timerPoolCapacity = 64; _timerPool = new TimerInfo[_timerPoolCapacity]; _timerIdToPoolIndex = new int[1024]; _freeIndices = new int[_timerPoolCapacity]; _freeCount = _timerPoolCapacity; _pendingRemoveTimers = new int[64]; _pendingRemoveCount = 0; for (int i = 0; i < _timerPoolCapacity; i++) { _freeIndices[i] = i; _timerPool[i].NextTimerIndex = -1; _timerPool[i].PrevTimerIndex = -1; _timerPool[i].Level = -1; _timerPool[i].SlotIndex = -1; } _scaledTimeWheel = new HierarchicalTimeWheel(0.001f); _unscaledTimeWheel = new HierarchicalTimeWheel(0.001f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int AddTimer(TimerHandlerNoArgs callback, float time, bool isLoop = false, bool isUnscaled = false) { int poolIndex = AcquireTimerFromPool(); ref TimerInfo timer = ref _timerPool[poolIndex]; timer.TimerId = ++_curTimerId; timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; timer.Interval = isLoop ? time : 0f; timer.HandlerNoArgs = callback; timer.HandlerType = 1; timer.IsLoop = isLoop; timer.IsRunning = true; timer.IsUnscaled = isUnscaled; timer.IsActive = true; timer.Args = null; timer.GenericArg = null; RegisterTimer(timer.TimerId, poolIndex); HierarchicalTimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; targetWheel.AddTimer(poolIndex, _timerPool, isUnscaled ? Time.unscaledTime : Time.time); return timer.TimerId; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int AddTimer(Action callback, T arg, float time, bool isLoop = false, bool isUnscaled = false) { int poolIndex = AcquireTimerFromPool(); ref TimerInfo timer = ref _timerPool[poolIndex]; timer.TimerId = ++_curTimerId; timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; timer.Interval = isLoop ? time : 0f; timer.HandlerGeneric = callback; timer.GenericArg = arg; timer.HandlerType = 2; timer.IsLoop = isLoop; timer.IsRunning = true; timer.IsUnscaled = isUnscaled; timer.IsActive = true; timer.Args = null; RegisterTimer(timer.TimerId, poolIndex); HierarchicalTimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; targetWheel.AddTimer(poolIndex, _timerPool, isUnscaled ? Time.unscaledTime : Time.time); return timer.TimerId; } public int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args) { int poolIndex = AcquireTimerFromPool(); ref TimerInfo timer = ref _timerPool[poolIndex]; timer.TimerId = ++_curTimerId; timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; timer.Interval = isLoop ? time : 0f; timer.Handler = callback; timer.HandlerType = 0; timer.IsLoop = isLoop; timer.IsRunning = true; timer.IsUnscaled = isUnscaled; timer.IsActive = true; timer.Args = args; timer.GenericArg = null; RegisterTimer(timer.TimerId, poolIndex); HierarchicalTimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; targetWheel.AddTimer(poolIndex, _timerPool, isUnscaled ? Time.unscaledTime : Time.time); return timer.TimerId; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int AcquireTimerFromPool() { if (_freeCount > 0) { return _freeIndices[--_freeCount]; } int oldCapacity = _timerPoolCapacity; _timerPoolCapacity *= 2; Array.Resize(ref _timerPool, _timerPoolCapacity); Array.Resize(ref _freeIndices, _timerPoolCapacity); for (int i = _timerPoolCapacity - 1; i >= oldCapacity; i--) { _freeIndices[_freeCount++] = i; _timerPool[i].NextTimerIndex = -1; _timerPool[i].PrevTimerIndex = -1; _timerPool[i].Level = -1; _timerPool[i].SlotIndex = -1; } return _freeIndices[--_freeCount]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void RegisterTimer(int timerId, int poolIndex) { if (timerId >= _timerIdToPoolIndex.Length) { Array.Resize(ref _timerIdToPoolIndex, _timerIdToPoolIndex.Length * 2); } _timerIdToPoolIndex[timerId] = poolIndex; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetPoolIndex(int timerId) { return timerId > 0 && timerId < _timerIdToPoolIndex.Length ? _timerIdToPoolIndex[timerId] : -1; } public void Stop(int timerId) { int poolIndex = GetPoolIndex(timerId); if (poolIndex >= 0 && poolIndex < _timerPool.Length && _timerPool[poolIndex].IsActive) _timerPool[poolIndex].IsRunning = false; } public void Resume(int timerId) { int poolIndex = GetPoolIndex(timerId); if (poolIndex >= 0 && poolIndex < _timerPool.Length && _timerPool[poolIndex].IsActive) _timerPool[poolIndex].IsRunning = true; } public bool IsRunning(int timerId) { int poolIndex = GetPoolIndex(timerId); return poolIndex >= 0 && poolIndex < _timerPool.Length && _timerPool[poolIndex].IsActive && _timerPool[poolIndex].IsRunning; } public float GetLeftTime(int timerId) { int poolIndex = GetPoolIndex(timerId); if (poolIndex < 0 || poolIndex >= _timerPool.Length || !_timerPool[poolIndex].IsActive) return 0; ref TimerInfo timer = ref _timerPool[poolIndex]; return Mathf.Max(timer.TriggerTime - (timer.IsUnscaled ? Time.unscaledTime : Time.time), 0); } public void Restart(int timerId) { int poolIndex = GetPoolIndex(timerId); if (poolIndex < 0 || poolIndex >= _timerPool.Length || !_timerPool[poolIndex].IsActive) return; ref TimerInfo timer = ref _timerPool[poolIndex]; HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; targetWheel.RemoveTimer(poolIndex, _timerPool); timer.TriggerTime = (timer.IsUnscaled ? Time.unscaledTime : Time.time) + timer.Interval; targetWheel.AddTimer(poolIndex, _timerPool, timer.IsUnscaled ? Time.unscaledTime : Time.time); } public void RemoveTimer(int timerId) { int poolIndex = GetPoolIndex(timerId); if (poolIndex < 0 || poolIndex >= _timerPool.Length || !_timerPool[poolIndex].IsActive) return; ref TimerInfo timer = ref _timerPool[poolIndex]; timer.IsActive = false; if (timer.Level >= 0) { HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; targetWheel.RemoveTimer(poolIndex, _timerPool); } ReleaseTimerToPool(poolIndex); } public void RemoveAllTimer() { for (int i = 0; i < _timerPoolCapacity; i++) { if (_timerPool[i].IsActive) { _timerPool[i].Clear(); _freeIndices[_freeCount++] = i; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReleaseTimerToPool(int poolIndex) { if (poolIndex >= 0 && poolIndex < _timerPool.Length) { _timerPool[poolIndex].Clear(); if (_freeCount >= _freeIndices.Length) { Array.Resize(ref _freeIndices, _freeIndices.Length * 2); } _freeIndices[_freeCount++] = poolIndex; } } void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds) { _scaledTimeWheel.Advance(Time.time, _timerPool, ProcessTimer); _unscaledTimeWheel.Advance(Time.unscaledTime, _timerPool, ProcessTimer); ProcessPendingRemovals(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ProcessTimer(int poolIndex) { if (poolIndex < 0 || poolIndex >= _timerPool.Length) return; ref TimerInfo timer = ref _timerPool[poolIndex]; if (!timer.IsActive || !timer.IsRunning) return; try { switch (timer.HandlerType) { case 0: // TimerHandler timer.Handler?.Invoke(timer.Args); break; case 1: // NoArgs timer.HandlerNoArgs?.Invoke(); break; case 2: // Generic ((dynamic)timer.HandlerGeneric)?.Invoke((dynamic)timer.GenericArg); break; } } catch (Exception e) { Log.Error($"Timer callback error: {e}"); } if (!timer.IsActive) return; if (timer.IsLoop) { timer.TriggerTime += timer.Interval; HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; targetWheel.AddTimer(poolIndex, _timerPool, timer.IsUnscaled ? Time.unscaledTime : Time.time); } else { AddPendingRemoval(timer.TimerId); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AddPendingRemoval(int timerId) { if (_pendingRemoveCount >= _pendingRemoveTimers.Length) { Array.Resize(ref _pendingRemoveTimers, _pendingRemoveTimers.Length * 2); } _pendingRemoveTimers[_pendingRemoveCount++] = timerId; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ProcessPendingRemovals() { for (int i = 0; i < _pendingRemoveCount; i++) { RemoveTimer(_pendingRemoveTimers[i]); } _pendingRemoveCount = 0; } void IModule.Dispose() => RemoveAllTimer(); public int Priority => 0; } }