com.alicizax.unity.framework/Runtime/ABase/GameObjectPool/Data/PoolConfig.cs

468 lines
14 KiB
C#

using System;
using AlicizaX.ObjectPool;
using UnityEngine;
namespace AlicizaX
{
public enum PoolResourceLoaderType
{
AssetBundle = 0,
Resources = 1
}
public enum PoolMatchMode
{
Exact = 0,
Prefix = 1
}
public enum PoolCategory
{
Default = 0,
Effect = 1,
Monster = 2,
Building = 3,
UI = 4,
Custom = 5
}
[Serializable]
public sealed class PoolEntry
{
public const string DefaultGroup = "DefaultGroup";
public const string DefaultEntryName = "PoolRule";
public string entryName = DefaultEntryName;
public string group = DefaultGroup;
public string assetPath = string.Empty;
public PoolMatchMode matchMode = PoolMatchMode.Exact;
public PoolResourceLoaderType loaderType = PoolResourceLoaderType.AssetBundle;
public PoolCategory category = PoolCategory.Default;
[Min(1)]
public int softCapacity = 8;
[Min(1)]
public int hardCapacity = 16;
public int priority;
public void Normalize()
{
entryName = string.IsNullOrWhiteSpace(entryName) ? DefaultEntryName : entryName.Trim();
group = string.IsNullOrWhiteSpace(group) ? DefaultGroup : group.Trim();
assetPath = NormalizeAssetPath(assetPath);
softCapacity = Mathf.Max(1, softCapacity);
hardCapacity = Mathf.Max(softCapacity, hardCapacity);
}
public bool Matches(string requestedAssetPath, string requestedGroup = null)
{
if (string.IsNullOrEmpty(assetPath) || string.IsNullOrEmpty(requestedAssetPath))
{
return false;
}
if (!string.IsNullOrEmpty(requestedGroup) &&
!string.Equals(group, requestedGroup, StringComparison.Ordinal))
{
return false;
}
return matchMode == PoolMatchMode.Exact
? string.Equals(requestedAssetPath, assetPath, StringComparison.Ordinal)
: requestedAssetPath.StartsWith(assetPath, StringComparison.Ordinal);
}
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 == null ? 0 : left.assetPath.Length;
int rightLength = right.assetPath == null ? 0 : right.assetPath.Length;
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('\\', '/');
}
}
internal struct PoolCompiledRule
{
public int ruleIndex;
public string entryName;
public string group;
public string assetPath;
public PoolMatchMode matchMode;
public PoolResourceLoaderType loaderType;
public PoolCategory category;
public int softCapacity;
public int hardCapacity;
public int priority;
public bool IsPrefix => matchMode == PoolMatchMode.Prefix;
public static PoolCompiledRule FromEntry(PoolEntry entry, int ruleIndex)
{
return new PoolCompiledRule
{
ruleIndex = ruleIndex,
entryName = entry.entryName,
group = entry.group,
assetPath = entry.assetPath,
matchMode = entry.matchMode,
loaderType = entry.loaderType,
category = entry.category,
softCapacity = entry.softCapacity,
hardCapacity = entry.hardCapacity,
priority = entry.priority
};
}
}
internal sealed class PoolCompiledCatalog
{
private readonly PoolCompiledRule[] _rules;
private StringOpenHashMap _groupIndexMap;
private PoolCompiledGroup[] _groups;
private StringOpenHashMap _globalExactMap;
private PoolPrefixTrie _globalPrefixTrie;
private PoolCompiledCatalog(
PoolCompiledRule[] rules,
StringOpenHashMap groupIndexMap,
PoolCompiledGroup[] groups,
StringOpenHashMap globalExactMap,
PoolPrefixTrie globalPrefixTrie)
{
_rules = rules;
_groupIndexMap = groupIndexMap;
_groups = groups;
_globalExactMap = globalExactMap;
_globalPrefixTrie = globalPrefixTrie;
}
public bool IsEmpty => _rules == null || _rules.Length == 0;
public int RuleCount => _rules == null ? 0 : _rules.Length;
public ref readonly PoolCompiledRule GetRule(int ruleIndex)
{
return ref _rules[ruleIndex];
}
public int Resolve(string assetPath, string group)
{
if (string.IsNullOrEmpty(assetPath) || _rules == null || _rules.Length == 0)
{
return -1;
}
if (!string.IsNullOrEmpty(group))
{
if (_groupIndexMap.TryGetValue(group, out int groupIndex))
{
return _groups[groupIndex].Resolve(assetPath);
}
return -1;
}
if (_globalExactMap.TryGetValue(assetPath, out int exactRuleIndex))
{
return exactRuleIndex;
}
return _globalPrefixTrie.Resolve(assetPath);
}
public static PoolCompiledCatalog Empty()
{
return new PoolCompiledCatalog(
Array.Empty<PoolCompiledRule>(),
new StringOpenHashMap(8),
Array.Empty<PoolCompiledGroup>(),
new StringOpenHashMap(8),
new PoolPrefixTrie(0));
}
public static PoolCompiledCatalog Build(PoolEntry[] entries)
{
if (entries == null || entries.Length == 0)
{
return Empty();
}
int entryCount = entries.Length;
var groupIndexMap = new StringOpenHashMap(entryCount);
var groupNames = new string[entryCount];
var groupPrefixChars = new int[entryCount];
int groupCount = 0;
int globalPrefixChars = 0;
for (int i = 0; i < entryCount; i++)
{
PoolEntry entry = entries[i];
if (entry == null || string.IsNullOrEmpty(entry.assetPath))
{
continue;
}
if (!groupIndexMap.TryGetValue(entry.group, out int groupIndex))
{
groupIndex = groupCount;
groupIndexMap.AddOrUpdate(entry.group, groupIndex);
groupNames[groupCount] = entry.group;
groupCount++;
}
if (entry.matchMode == PoolMatchMode.Prefix)
{
int pathLength = entry.assetPath.Length;
groupPrefixChars[groupIndex] += pathLength;
globalPrefixChars += pathLength;
}
}
var groups = new PoolCompiledGroup[groupCount];
for (int i = 0; i < groupCount; i++)
{
groups[i] = new PoolCompiledGroup(groupNames[i], entryCount, groupPrefixChars[i]);
}
var rules = new PoolCompiledRule[entryCount];
var globalExactMap = new StringOpenHashMap(entryCount);
var globalPrefixTrie = new PoolPrefixTrie(globalPrefixChars);
for (int i = 0; i < entryCount; i++)
{
PoolEntry entry = entries[i];
if (entry == null || string.IsNullOrEmpty(entry.assetPath))
{
continue;
}
PoolCompiledRule rule = PoolCompiledRule.FromEntry(entry, i);
rules[i] = rule;
groupIndexMap.TryGetValue(rule.group, out int groupIndex);
groups[groupIndex].Register(rule);
if (rule.matchMode == PoolMatchMode.Exact)
{
if (!globalExactMap.TryGetValue(rule.assetPath, out _))
{
globalExactMap.AddOrUpdate(rule.assetPath, i);
}
}
else
{
globalPrefixTrie.Register(rule.assetPath, i);
}
}
return new PoolCompiledCatalog(rules, groupIndexMap, groups, globalExactMap, globalPrefixTrie);
}
}
internal sealed class PoolCompiledGroup
{
private readonly string _name;
private StringOpenHashMap _exactMap;
private PoolPrefixTrie _prefixTrie;
public PoolCompiledGroup(string name, int exactCapacity, int prefixChars)
{
_name = name;
_exactMap = new StringOpenHashMap(exactCapacity);
_prefixTrie = new PoolPrefixTrie(prefixChars);
}
public void Register(in PoolCompiledRule rule)
{
if (rule.matchMode == PoolMatchMode.Exact)
{
if (!_exactMap.TryGetValue(rule.assetPath, out _))
{
_exactMap.AddOrUpdate(rule.assetPath, rule.ruleIndex);
}
return;
}
_prefixTrie.Register(rule.assetPath, rule.ruleIndex);
}
public int Resolve(string assetPath)
{
if (_exactMap.TryGetValue(assetPath, out int exactRuleIndex))
{
return exactRuleIndex;
}
return _prefixTrie.Resolve(assetPath);
}
}
internal sealed class PoolPrefixTrie
{
private struct Node
{
public char character;
public int firstChild;
public int nextSibling;
public int ruleIndex;
}
private Node[] _nodes;
private int _nodeCount;
public PoolPrefixTrie(int prefixCharCount)
{
int capacity = Mathf.Max(1, prefixCharCount + 1);
_nodes = new Node[capacity];
_nodes[0].firstChild = -1;
_nodes[0].nextSibling = -1;
_nodes[0].ruleIndex = -1;
_nodeCount = 1;
}
public void Register(string prefix, int ruleIndex)
{
if (string.IsNullOrEmpty(prefix))
{
return;
}
int nodeIndex = 0;
int length = prefix.Length;
for (int i = 0; i < length; i++)
{
nodeIndex = GetOrCreateChild(nodeIndex, prefix[i]);
}
if (_nodes[nodeIndex].ruleIndex < 0)
{
_nodes[nodeIndex].ruleIndex = ruleIndex;
}
}
public int Resolve(string value)
{
if (string.IsNullOrEmpty(value) || _nodes == null)
{
return -1;
}
int nodeIndex = 0;
int bestRuleIndex = -1;
int length = value.Length;
for (int i = 0; i < length; i++)
{
nodeIndex = FindChild(nodeIndex, value[i]);
if (nodeIndex < 0)
{
break;
}
int matchedRuleIndex = _nodes[nodeIndex].ruleIndex;
if (matchedRuleIndex >= 0)
{
bestRuleIndex = matchedRuleIndex;
}
}
return bestRuleIndex;
}
private int GetOrCreateChild(int nodeIndex, char character)
{
int childIndex = _nodes[nodeIndex].firstChild;
while (childIndex >= 0)
{
if (_nodes[childIndex].character == character)
{
return childIndex;
}
childIndex = _nodes[childIndex].nextSibling;
}
EnsureCapacity(_nodeCount + 1);
int newNodeIndex = _nodeCount++;
_nodes[newNodeIndex].character = character;
_nodes[newNodeIndex].firstChild = -1;
_nodes[newNodeIndex].nextSibling = _nodes[nodeIndex].firstChild;
_nodes[newNodeIndex].ruleIndex = -1;
_nodes[nodeIndex].firstChild = newNodeIndex;
return newNodeIndex;
}
private int FindChild(int nodeIndex, char character)
{
int childIndex = _nodes[nodeIndex].firstChild;
while (childIndex >= 0)
{
if (_nodes[childIndex].character == character)
{
return childIndex;
}
childIndex = _nodes[childIndex].nextSibling;
}
return -1;
}
private void EnsureCapacity(int required)
{
if (_nodes.Length >= required)
{
return;
}
int newCapacity = Mathf.Max(required, _nodes.Length << 1);
var newNodes = new Node[newCapacity];
Array.Copy(_nodes, 0, newNodes, 0, _nodeCount);
_nodes = newNodes;
}
}
}