mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-04-19 05:48:07 -04:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
242 lines
5.8 KiB
C#
242 lines
5.8 KiB
C#
using Editor.AssetPickers;
|
|
|
|
namespace Editor;
|
|
|
|
public abstract class AssetPicker : Dialog
|
|
{
|
|
public struct PickerOptions
|
|
{
|
|
/// <summary>
|
|
/// Allows searching and selecting cloud assets
|
|
/// </summary>
|
|
public bool EnableCloud { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Cloud assets will be shown in a separate tab
|
|
/// </summary>
|
|
public bool SeparateCloudTab { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Allows selecting multiple assets
|
|
/// </summary>
|
|
public bool EnableMultiselect { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// Pass-thru of additioanl types requested from native that aren't the primary resource type
|
|
/// </summary>
|
|
public List<AssetType> AdditionalTypes { get; internal set; }
|
|
|
|
public PickerOptions() { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asset was highlighted, but not picked.
|
|
/// </summary>
|
|
public Action<Asset[]> OnAssetHighlighted { get; set; }
|
|
|
|
/// <summary>
|
|
/// An asset was picked. The asset picker will be closed after this.
|
|
/// </summary>
|
|
public Action<Asset[]> OnAssetPicked { get; set; }
|
|
|
|
/// <summary>
|
|
/// A package was picked. The asset picker will be closed after this.
|
|
/// </summary>
|
|
public Action<Package> OnPackagePicked { get; set; }
|
|
|
|
public AssetType AssetType { get; init; }
|
|
|
|
public PickerOptions Options { get; init; }
|
|
|
|
public string Title { set => Window.Title = value; }
|
|
|
|
public AssetPicker( Widget parent, AssetType assetType, PickerOptions options ) : base( parent, true )
|
|
{
|
|
AssetType = assetType;
|
|
Options = options;
|
|
|
|
Window.SetWindowIcon( "find_in_page" );
|
|
|
|
Window.WindowFlags = WindowFlags.Dialog | WindowFlags.Customized | WindowFlags.WindowTitle | WindowFlags.CloseButton | WindowFlags.WindowSystemMenuHint;
|
|
|
|
Window.SetModal( true );
|
|
}
|
|
|
|
public override void Show()
|
|
{
|
|
Window.Show();
|
|
}
|
|
|
|
public override void OnDestroyed()
|
|
{
|
|
base.OnDestroyed();
|
|
EditorUtility.StopAssetSound();
|
|
}
|
|
|
|
public void SetSelection( string resource )
|
|
{
|
|
Asset asset = AssetSystem.FindByPath( resource );
|
|
if ( asset is null && Package.TryGetCached( resource, out Package package ) )
|
|
{
|
|
SetSelection( package );
|
|
return;
|
|
}
|
|
|
|
SetSelection( asset );
|
|
}
|
|
|
|
protected virtual bool ShouldFilterAsset( Asset asset )
|
|
{
|
|
//
|
|
// Filter out unwanted crap
|
|
//
|
|
|
|
if ( asset.Name.Contains( ".generated", StringComparison.OrdinalIgnoreCase ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( asset.AbsolutePath.Contains( ".sbox" ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
protected void Submit( Asset asset ) => Submit( [asset] );
|
|
|
|
protected virtual void Submit( Asset[] assets )
|
|
{
|
|
OnAssetPicked?.Invoke( assets );
|
|
Window.Close();
|
|
}
|
|
|
|
protected virtual async void Submit( Package package )
|
|
{
|
|
package = await Package.FetchAsync( package.FullIdent, false );
|
|
|
|
OnPackagePicked?.Invoke( package );
|
|
|
|
if ( AssetSystem.CanCloudInstall( package ) )
|
|
{
|
|
Asset a = null;
|
|
if ( !AssetSystem.IsCloudInstalled( package ) )
|
|
{
|
|
Hide();
|
|
a = await AssetSystem.InstallAsync( package.FullIdent );
|
|
if ( a is null )
|
|
{
|
|
Log.Error( $"Failed to install package: {package.FullIdent}" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string assetPath = package.GetMeta<string>( "PrimaryAsset" );
|
|
a = AssetSystem.FindByPath( assetPath );
|
|
|
|
if ( a is null )
|
|
{
|
|
Log.Error( $"Failed to find installed package: {package.FullIdent}" );
|
|
}
|
|
}
|
|
|
|
Submit( a );
|
|
}
|
|
else
|
|
{
|
|
Hide();
|
|
}
|
|
}
|
|
|
|
|
|
public virtual void SetSelection( Asset asset ) { }
|
|
public virtual void SetSelection( Package package ) { }
|
|
|
|
public virtual void SetSearchText( string value ) { }
|
|
|
|
// --
|
|
|
|
public static AssetPicker Create( Widget parent, AssetType assetType, PickerOptions? options = null )
|
|
{
|
|
options ??= new();
|
|
Type resourceType = assetType?.ResourceType;
|
|
if ( resourceType is null && assetType is not null )
|
|
{
|
|
switch ( assetType.FileExtension )
|
|
{
|
|
case "vmap": resourceType = typeof( Map ); break;
|
|
case "vmat": resourceType = typeof( Material ); break;
|
|
case "vmdl": resourceType = typeof( Model ); break;
|
|
case "vsnd": resourceType = typeof( SoundFile ); break;
|
|
case "sound": resourceType = typeof( SoundEvent ); break;
|
|
}
|
|
}
|
|
if ( resourceType is not null )
|
|
{
|
|
var matchingTypes = EditorTypeLibrary
|
|
.GetTypesWithAttribute<AssetPickerAttribute>()
|
|
.Where( x => typeof( AssetPicker ).IsAssignableFrom( x.Type.TargetType ) )
|
|
.SelectMany( x => x.Attribute.ResourceTypes
|
|
.Where( assetType => resourceType.IsAssignableTo( assetType ) )
|
|
.Select( assetType => new { ParentType = x.Type, AssetType = assetType } ) )
|
|
.ToList();
|
|
|
|
var customType = matchingTypes
|
|
.OrderBy( x => GetSpecificityScore( x.AssetType, resourceType ) )
|
|
.Select( x => x.ParentType )
|
|
.FirstOrDefault();
|
|
|
|
if ( customType != null )
|
|
{
|
|
try
|
|
{
|
|
var picker = customType.Create<AssetPicker>( [parent, assetType, options] );
|
|
if ( picker.IsValid() )
|
|
return picker;
|
|
}
|
|
catch ( Exception ex )
|
|
{
|
|
Log.Error( ex, $"Failed to create AssetPicker for: {assetType} ({customType.Name})" );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No resource type, so all types are allowed
|
|
//
|
|
return new GenericPicker( parent, null, options.Value );
|
|
}
|
|
|
|
var types = new List<AssetType>() { assetType };
|
|
|
|
if ( assetType.IsGameResource )
|
|
{
|
|
// also all types that inherit from the resource type
|
|
types.AddRange( EditorTypeLibrary.GetTypes( assetType.ResourceType ).Select( t => AssetType.FromType( t.TargetType ) ) );
|
|
}
|
|
|
|
if ( options?.AdditionalTypes != null )
|
|
{
|
|
types.AddRange( options?.AdditionalTypes );
|
|
}
|
|
|
|
return new GenericPicker( parent, types.Distinct().ToList(), options.Value );
|
|
}
|
|
|
|
// helper to weight the implementations by inheritance depth
|
|
private static int GetSpecificityScore( Type candidate, Type target )
|
|
{
|
|
int score = 0;
|
|
while ( target != null && target != typeof( object ) )
|
|
{
|
|
if ( target == candidate )
|
|
return score;
|
|
target = target.BaseType;
|
|
score++;
|
|
}
|
|
return int.MaxValue;
|
|
}
|
|
}
|