mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-08-01 00:08:05 -04:00
* New map selector with support for workshops + mounts * New mounts menu on main menu and pause menu to enable/disable mounts * Mount scenes multiplayer support - incl. auto-mounting the game if we can, disconnect if we can't (not installed) * MapInstance supports non-package scenes from LaunchArgs * ResourceLoader: don't use cached result if it's no longer IsValid, like after it's destroyed from a resourcelibrary clear * MapInstance: failing to load from LaunchArgs fallsback to the preconfigured map * Skip SoundFile.Create when IsHeadless * Quake mount: hide non-map bsps
157 lines
3.5 KiB
C#
157 lines
3.5 KiB
C#
namespace Sandbox.Mounting;
|
|
|
|
public static class MountUtility
|
|
{
|
|
static readonly Dictionary<string, Texture> _cache = new();
|
|
static readonly List<RenderJob> _jobs = new();
|
|
static readonly HashSet<RenderJob> _activeJobs = new();
|
|
|
|
/// <summary>
|
|
/// Is this a mount:// resource path? Resources behind such a path are provided by a
|
|
/// mounted game and are cached/reused rather than loaded from disk.
|
|
/// </summary>
|
|
public static bool IsMountPath( string path )
|
|
{
|
|
return !string.IsNullOrWhiteSpace( path ) && path.StartsWith( "mount://", StringComparison.Ordinal );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempt to parse an ident from a mount path
|
|
/// </summary>
|
|
public static bool TryParse( string path, out string ident )
|
|
{
|
|
ident = default;
|
|
|
|
if ( !IsMountPath( path ) )
|
|
return false;
|
|
|
|
var sourceName = path[8..];
|
|
var i = sourceName.IndexOf( '/' );
|
|
|
|
if ( i < 0 )
|
|
return false;
|
|
|
|
ident = sourceName[..i];
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find a ResourceLoader by its mount path.
|
|
/// </summary>
|
|
public static ResourceLoader FindLoader( string loaderPath )
|
|
{
|
|
if ( !IsMountPath( loaderPath ) ) return null;
|
|
|
|
var partIndex = loaderPath.IndexOf( '/', 8 );
|
|
if ( partIndex <= 8 ) return null;
|
|
var mountName = loaderPath[8..partIndex];
|
|
|
|
var mount = Directory.Get( mountName );
|
|
if ( mount is null ) return null;
|
|
|
|
return mount.GetByPath( loaderPath );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a preview texture for the given resource loader.
|
|
/// </summary>
|
|
public static Texture GetPreviewTexture( string loaderPath )
|
|
{
|
|
var loader = FindLoader( loaderPath );
|
|
if ( loader is null ) return null;
|
|
return GetPreviewTexture( loader );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a preview texture for the given resource loader.
|
|
/// </summary>
|
|
public static Texture GetPreviewTexture( ResourceLoader loader )
|
|
{
|
|
Assert.NotNull( loader, "loader was null" );
|
|
|
|
var path = loader.Path;
|
|
|
|
// In-memory cache
|
|
if ( _cache.TryGetValue( path, out var cached ) )
|
|
return cached;
|
|
|
|
// Disk cache
|
|
var cacheKey = $"preview/icons/{path.Md5()}.png";
|
|
if ( FileSystem.Cache.TryGet( cacheKey, out var data ) )
|
|
{
|
|
using var bitmap = Bitmap.CreateFromBytes( data );
|
|
var tex = bitmap.ToTexture( true );
|
|
_cache[path] = tex;
|
|
return tex;
|
|
}
|
|
|
|
// Queue render job
|
|
var job = new RenderJob( path, cacheKey );
|
|
_jobs.Add( job );
|
|
_cache[path] = job.Texture;
|
|
|
|
return job.Texture;
|
|
}
|
|
|
|
internal static void TickPreviewRenders()
|
|
{
|
|
_activeJobs.RemoveWhere( x => x.IsFinished );
|
|
|
|
while ( _jobs.Count > 0 && _activeJobs.Count < 2 )
|
|
{
|
|
var job = _jobs.OrderBy( x => x.Texture.LastUsed ).First();
|
|
_jobs.Remove( job );
|
|
|
|
_activeJobs.Add( job );
|
|
job.Start();
|
|
}
|
|
}
|
|
|
|
internal static void FlushCache()
|
|
{
|
|
_jobs.Clear();
|
|
_activeJobs.Clear();
|
|
_cache.Clear();
|
|
}
|
|
}
|
|
|
|
class RenderJob
|
|
{
|
|
public string Path { get; }
|
|
public Texture Texture { get; }
|
|
|
|
Task _task;
|
|
readonly string _cacheKey;
|
|
|
|
public bool IsFinished => _task is null || _task.IsCompleted;
|
|
|
|
public RenderJob( string path, string cacheKey )
|
|
{
|
|
_cacheKey = cacheKey;
|
|
Path = path;
|
|
|
|
using var bitmap = new Bitmap( 256, 256 );
|
|
bitmap.Clear( Color.Transparent );
|
|
Texture = bitmap.ToTexture( true );
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
_task = Run();
|
|
}
|
|
|
|
public async Task Run()
|
|
{
|
|
var modelResource = await Model.LoadAsync( Path );
|
|
if ( !modelResource.IsValid() ) return;
|
|
|
|
using var bitmap = new Bitmap( 512, 512 );
|
|
SceneUtility.RenderModelBitmap( modelResource, bitmap );
|
|
|
|
using var resized = bitmap.Resize( 256, 256 );
|
|
Texture.Update( resized );
|
|
|
|
FileSystem.Cache.Set( _cacheKey, resized.ToPng() );
|
|
}
|
|
}
|