UI/ReplayPreviewCache.cs
using System;
using System.Collections.Generic;
using Sandbox;
namespace BlockParty;
/// <summary>Content-shared static replay overlays. Live panels pin entries; up to 128 unused
/// textures are retained for scrolling back, then disposed in least-recently-used order.</summary>
internal static class ReplayPreviewCache
{
private const int IdleLimit = 128;
private static readonly Dictionary<string, Entry> Entries = new( StringComparer.Ordinal );
private static readonly LinkedList<Entry> Idle = new();
internal sealed class Entry
{
internal string Key;
internal Texture Texture;
internal int Users;
internal LinkedListNode<Entry> IdleNode;
}
internal static Entry Acquire( LevelDef level )
{
// Use the complete canonical wire representation, NOT ContentHash (which strips cosmetics)
// or the level id (which can name many editor revisions). Full strings avoid hash collisions.
string key = level is null ? "null" : EditorLevel.FromLevelDef( level ).ToJsonString();
if ( !Entries.TryGetValue( key, out var entry ) )
{
using var bitmap = WorkshopThumbnail.Render( level, 240, previewOverlay: true );
entry = new Entry { Key = key, Texture = bitmap.ToTexture() };
Entries.Add( key, entry );
}
if ( entry.IdleNode is not null )
{
Idle.Remove( entry.IdleNode );
entry.IdleNode = null;
}
entry.Users++;
return entry;
}
internal static void Release( Entry entry )
{
if ( entry is null || --entry.Users != 0 ) return;
entry.IdleNode = Idle.AddLast( entry );
while ( Idle.Count > IdleLimit )
{
var oldest = Idle.First.Value;
Idle.RemoveFirst();
oldest.IdleNode = null;
Entries.Remove( oldest.Key );
oldest.Texture?.Dispose();
}
}
}