using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using Phantom.Common.Data.Java; using Phantom.Utils.Cryptography; namespace Phantom.Agent.Minecraft.Java; public sealed class JavaRuntimeRepository { private readonly Dictionary<string, Guid> guidsByPath = new (); private readonly Dictionary<Guid, JavaRuntimeExecutable> runtimesByGuid = new (); private readonly ReaderWriterLockSlim rwLock = new (LockRecursionPolicy.NoRecursion); public ImmutableArray<TaggedJavaRuntime> All { get { rwLock.EnterReadLock(); try { return runtimesByGuid.Select(static kvp => new TaggedJavaRuntime(kvp.Key, kvp.Value.Runtime)).OrderBy(static taggedRuntime => taggedRuntime.Runtime).ToImmutableArray(); } finally { rwLock.ExitReadLock(); } } } public void Include(JavaRuntimeExecutable runtime) { rwLock.EnterWriteLock(); try { if (!guidsByPath.TryGetValue(runtime.ExecutablePath, out var guid)) { guidsByPath[runtime.ExecutablePath] = guid = GenerateStableGuid(runtime.ExecutablePath); } runtimesByGuid[guid] = runtime; } finally { rwLock.ExitWriteLock(); } } public bool TryGetByGuid(Guid guid, [MaybeNullWhen(false)] out JavaRuntimeExecutable runtime) { rwLock.EnterReadLock(); try { return runtimesByGuid.TryGetValue(guid, out runtime); } finally { rwLock.ExitReadLock(); } } private static Guid GenerateStableGuid(string executablePath) { Random rand = new Random(StableHashCode.ForString(executablePath)); Span<byte> bytes = stackalloc byte[16]; rand.NextBytes(bytes); return new Guid(bytes); } }