using System; using System.Collections.Generic; using System.Linq; namespace ShrinkModFramework { public interface IReadOnlyShrinkModRegistry { string Name { get; } IReadOnlyCollection> Entries { get; } IReadOnlyCollection> BaseEntries { get; } bool Contains(string key); bool TryGet(string key, out T value); bool TryGetEntry(string key, out ShrinkRegistryEntry entry); IReadOnlyList> GetOverrideCandidates(string key); } internal interface IShrinkOwnedRegistry { void RemoveOwnedEntries(string ownerModId); } internal sealed class ShrinkModRegistry : IReadOnlyShrinkModRegistry, IShrinkOwnedRegistry { private readonly Dictionary> _baseEntries = new(StringComparer.Ordinal); private readonly Dictionary>> _overrides = new(StringComparer.Ordinal); public string Name { get; } internal ShrinkModRegistry(string name) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("注册表名称不能为空。", nameof(name)); Name = name.Trim(); } /// 当前生效项;存在覆盖时返回最高优先级的覆盖项。 public IReadOnlyCollection> Entries => _baseEntries.Keys .OrderBy(key => key, StringComparer.Ordinal) .Select(ResolveEntry) .ToArray(); /// 不应用覆盖的基础注册项快照。 public IReadOnlyCollection> BaseEntries => _baseEntries.Values .OrderBy(entry => entry.Key, StringComparer.Ordinal) .ToArray(); private void RegisterBase(string ownerModId, string key, T value) { ownerModId = NormalizeOwner(ownerModId); key = NormalizeKey(key); if (_baseEntries.ContainsKey(key)) throw new InvalidOperationException($"注册表 '{Name}' 中已存在键 '{key}'。"); _baseEntries.Add(key, new ShrinkRegistryEntry(ownerModId, key, value)); } /// /// 使用 owner 作为 namespace 注册内容,并返回最终键。localKey 只能是局部名称,不能包含冒号。 /// public string RegisterNamespaced(string ownerModId, string localKey, T value) { ownerModId = NormalizeOwner(ownerModId); if (ownerModId.IndexOf(':') >= 0) throw new ArgumentException("归属模组 ID 不能包含 ':'。", nameof(ownerModId)); if (string.IsNullOrWhiteSpace(localKey)) throw new ArgumentException("局部注册键不能为空。", nameof(localKey)); localKey = localKey.Trim(); if (localKey.IndexOf(':') >= 0) throw new ArgumentException("局部注册键不能包含 ':'。", nameof(localKey)); var key = ownerModId + ":" + localKey; RegisterBase(ownerModId, key, value); return key; } /// /// 为已有基础项注册显式覆盖。优先级越高越先命中;同一目标的相同优先级视为硬冲突。 /// public void RegisterOverride(string ownerModId, string targetKey, int priority, T value) { ownerModId = NormalizeOwner(ownerModId); targetKey = NormalizeKey(targetKey); if (!_baseEntries.ContainsKey(targetKey)) throw new InvalidOperationException( $"注册表 '{Name}' 的覆盖目标 '{targetKey}' 不存在。"); if (!_overrides.TryGetValue(targetKey, out var candidates)) { candidates = new List>(); _overrides.Add(targetKey, candidates); } if (candidates.Any(candidate => candidate.Priority == priority)) throw new InvalidOperationException( $"注册表 '{Name}' 的键 '{targetKey}' 在优先级 {priority} 存在覆盖冲突。"); candidates.Add(ShrinkRegistryEntry.CreateOverride(ownerModId, targetKey, priority, value)); } public IReadOnlyList> GetOverrideCandidates(string key) { if (string.IsNullOrWhiteSpace(key) || !_overrides.TryGetValue(key.Trim(), out var candidates)) return Array.Empty>(); return candidates .OrderByDescending(candidate => candidate.Priority) .ThenBy(candidate => candidate.OwnerModId, StringComparer.Ordinal) .ToArray(); } public bool Contains(string key) => !string.IsNullOrWhiteSpace(key) && _baseEntries.ContainsKey(key.Trim()); public bool TryGet(string key, out T value) { if (TryGetEntry(key, out var entry)) { value = entry.Value; return true; } value = default; return false; } public bool TryGetEntry(string key, out ShrinkRegistryEntry entry) { if (!string.IsNullOrWhiteSpace(key) && _baseEntries.ContainsKey(key.Trim())) { entry = ResolveEntry(key.Trim()); return true; } entry = default; return false; } void IShrinkOwnedRegistry.RemoveOwnedEntries(string ownerModId) { if (string.IsNullOrWhiteSpace(ownerModId)) return; ownerModId = ownerModId.Trim(); var keys = _baseEntries .Where(pair => string.Equals(pair.Value.OwnerModId, ownerModId, StringComparison.Ordinal)) .Select(pair => pair.Key) .ToArray(); foreach (var key in keys) _baseEntries.Remove(key); var emptyOverrideTargets = new List(); foreach (var pair in _overrides) { pair.Value.RemoveAll(candidate => string.Equals(candidate.OwnerModId, ownerModId, StringComparison.Ordinal)); if (pair.Value.Count == 0) emptyOverrideTargets.Add(pair.Key); } foreach (var targetKey in emptyOverrideTargets) _overrides.Remove(targetKey); } private ShrinkRegistryEntry ResolveEntry(string key) { if (_overrides.TryGetValue(key, out var candidates) && candidates.Count > 0) { return candidates .OrderByDescending(candidate => candidate.Priority) .ThenBy(candidate => candidate.OwnerModId, StringComparer.Ordinal) .First(); } return _baseEntries[key]; } private static string NormalizeOwner(string ownerModId) { if (string.IsNullOrWhiteSpace(ownerModId)) throw new ArgumentException("归属模组 ID 不能为空。", nameof(ownerModId)); return ownerModId.Trim(); } private static string NormalizeKey(string key) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("注册键不能为空。", nameof(key)); return key.Trim(); } } public readonly struct ShrinkRegistryEntry { public string OwnerModId { get; } public string Key { get; } public T Value { get; } public bool IsOverride { get; } public int Priority { get; } public ShrinkRegistryEntry(string ownerModId, string key, T value) : this(ownerModId, key, value, false, 0) { } private ShrinkRegistryEntry(string ownerModId, string key, T value, bool isOverride, int priority) { OwnerModId = ownerModId; Key = key; Value = value; IsOverride = isOverride; Priority = priority; } internal static ShrinkRegistryEntry CreateOverride( string ownerModId, string key, int priority, T value) => new(ownerModId, key, value, true, priority); } }