using System; using System.Collections.Generic; using UnityEngine; namespace AlicizaX { public enum PoolResourceLoaderType { AssetBundle = 0, Resources = 1 } public enum PoolMatchMode { Exact = 0, Prefix = 1 } public enum PoolOverflowPolicy { FailFast = 0, InstantiateOneShot = 1, AutoExpand = 2, RecycleOldestInactive = 3, DropNewestRequest = 4 } public enum PoolTrimPolicy { None = 0, IdleOnly = 1, IdleAndPriority = 2, AggressiveOnLowMemory = 3 } public enum PoolActivationMode { SetActive = 0, SleepWake = 1, Custom = 2 } public enum PoolResetMode { TransformOnly = 0, PoolableCallbacks = 1, FullReset = 2, Custom = 3 } [Serializable] public sealed class PoolEntry { public const string DefaultGroup = "Default"; public const string DefaultEntryName = "DefaultPool"; public string entryName = DefaultEntryName; public string group = DefaultGroup; public string assetPath; public PoolMatchMode matchMode = PoolMatchMode.Exact; public PoolResourceLoaderType loaderType = PoolResourceLoaderType.AssetBundle; public PoolOverflowPolicy overflowPolicy = PoolOverflowPolicy.FailFast; public PoolTrimPolicy trimPolicy = PoolTrimPolicy.IdleOnly; public PoolActivationMode activationMode = PoolActivationMode.SetActive; public PoolResetMode resetMode = PoolResetMode.PoolableCallbacks; [Min(0)] public int minRetained = 0; [Min(1)] public int softCapacity = 8; [Min(1)] public int hardCapacity = 8; [Min(0f)] public float idleTrimDelay = 30f; [Min(0f)] public float prefabUnloadDelay = 60f; [Min(0f)] public float autoRecycleDelay = 0f; [Min(1)] public int trimBatchPerTick = 2; [Min(1)] public int warmupBatchPerFrame = 2; [Min(0f)] public float warmupFrameBudgetMs = 1f; public bool allowRuntimeExpand; public bool keepPrefabResident; public bool aggressiveTrimOnLowMemory; public int priority; public void Normalize() { entryName = string.IsNullOrWhiteSpace(entryName) ? DefaultEntryName : entryName.Trim(); group = string.IsNullOrWhiteSpace(group) ? DefaultGroup : group.Trim(); assetPath = NormalizeAssetPath(assetPath); minRetained = Mathf.Max(0, minRetained); softCapacity = Mathf.Max(1, softCapacity); hardCapacity = Mathf.Max(softCapacity, hardCapacity); minRetained = Mathf.Min(minRetained, hardCapacity); idleTrimDelay = Mathf.Max(0f, idleTrimDelay); prefabUnloadDelay = Mathf.Max(0f, prefabUnloadDelay); autoRecycleDelay = Mathf.Max(0f, autoRecycleDelay); trimBatchPerTick = Mathf.Max(1, trimBatchPerTick); warmupBatchPerFrame = Mathf.Max(1, warmupBatchPerFrame); warmupFrameBudgetMs = Mathf.Max(0f, warmupFrameBudgetMs); } public bool Matches(string requestedAssetPath, string requestedGroup = null) { if (string.IsNullOrWhiteSpace(assetPath) || string.IsNullOrWhiteSpace(requestedAssetPath)) { return false; } if (!string.IsNullOrWhiteSpace(requestedGroup) && !string.Equals(group, requestedGroup, StringComparison.Ordinal)) { return false; } return matchMode switch { PoolMatchMode.Exact => string.Equals(requestedAssetPath, assetPath, StringComparison.Ordinal), PoolMatchMode.Prefix => requestedAssetPath.StartsWith(assetPath, StringComparison.Ordinal), _ => false }; } public string BuildResolvedPoolKey(string resolvedAssetPath) { string concretePath = NormalizeAssetPath(resolvedAssetPath); return $"{group}|{(int)matchMode}|{assetPath}|{(int)loaderType}|{concretePath}|{entryName}"; } public static int CompareByPriority(PoolEntry left, PoolEntry right) { if (ReferenceEquals(left, right)) { return 0; } if (left == null) { return 1; } if (right == null) { return -1; } int priorityCompare = right.priority.CompareTo(left.priority); if (priorityCompare != 0) { return priorityCompare; } int modeCompare = left.matchMode.CompareTo(right.matchMode); if (modeCompare != 0) { return modeCompare; } int leftLength = left.assetPath?.Length ?? 0; int rightLength = right.assetPath?.Length ?? 0; int pathLengthCompare = rightLength.CompareTo(leftLength); if (pathLengthCompare != 0) { return pathLengthCompare; } return string.Compare(left.group, right.group, StringComparison.Ordinal); } public static string NormalizeAssetPath(string value) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } return value.Trim().Replace('\\', '/'); } } [Serializable] public sealed class ResolvedPoolConfig { public string entryName; public string group; public string assetPath; public PoolMatchMode matchMode; public PoolResourceLoaderType loaderType; public PoolOverflowPolicy overflowPolicy; public PoolTrimPolicy trimPolicy; public PoolActivationMode activationMode; public PoolResetMode resetMode; public int minRetained; public int softCapacity; public int hardCapacity; public float idleTrimDelay; public float prefabUnloadDelay; public float autoRecycleDelay; public int trimBatchPerTick; public int warmupBatchPerFrame; public float warmupFrameBudgetMs; public bool allowRuntimeExpand; public bool keepPrefabResident; public bool aggressiveTrimOnLowMemory; public int priority; public string BuildResolvedPoolKey(string resolvedAssetPath) { string concretePath = PoolEntry.NormalizeAssetPath(resolvedAssetPath); return $"{group}|{(int)matchMode}|{assetPath}|{(int)loaderType}|{concretePath}|{entryName}"; } public static ResolvedPoolConfig From(PoolEntry entry) { if (entry == null) { throw new ArgumentNullException(nameof(entry)); } return new ResolvedPoolConfig { entryName = entry.entryName, group = entry.group, assetPath = entry.assetPath, matchMode = entry.matchMode, loaderType = entry.loaderType, overflowPolicy = entry.overflowPolicy, trimPolicy = entry.trimPolicy, activationMode = entry.activationMode, resetMode = entry.resetMode, minRetained = entry.minRetained, softCapacity = entry.softCapacity, hardCapacity = entry.hardCapacity, idleTrimDelay = entry.idleTrimDelay, prefabUnloadDelay = entry.prefabUnloadDelay, autoRecycleDelay = entry.autoRecycleDelay, trimBatchPerTick = entry.trimBatchPerTick, warmupBatchPerFrame = entry.warmupBatchPerFrame, warmupFrameBudgetMs = entry.warmupFrameBudgetMs, allowRuntimeExpand = entry.allowRuntimeExpand, keepPrefabResident = entry.keepPrefabResident, aggressiveTrimOnLowMemory = entry.aggressiveTrimOnLowMemory, priority = entry.priority }; } } [Serializable] public sealed class PoolConfigCatalog { public readonly List entries; public PoolConfigCatalog(List entries) { this.entries = entries ?? throw new ArgumentNullException(nameof(entries)); } } }