namespace Editor; [SkipHotload] internal class NativeAsset : Asset { internal IAsset native; internal NativeAsset( IAsset native ) { this.native = native; AssetId = native.GetAssetIndexInt(); } internal override void UpdateInternals( bool compileImmediately = true ) { if ( AssetType.AssetTypeCache.TryGetValue( native.GetAssetTypeId(), out var type ) ) AssetType = type; Assert.NotNull( AssetType ); // agh? Maybe we mock up an unknown type type? AssetId = native.GetAssetIndexInt(); Name = native.GetFriendlyName_Transient().NormalizeFilename( false ); RelativePath = native.GetRelativePath_Transient( AssetLocation_t.Invalid ).NormalizeFilename( false ); Path = System.IO.Path.ChangeExtension( RelativePath, AssetType.FileExtension ).NormalizeFilename( false ); AbsolutePath = native.GetAbsolutePath_Transient( AssetLocation_t.Invalid ).NormalizeFilename( false ); // invalid means get any AbsoluteSourcePath = native.GetAbsolutePath_Transient( AssetLocation_t.Content ).NormalizeFilename( false ); // invalid means get any AbsoluteCompiledPath = native.GetAbsolutePath_Transient( AssetLocation_t.Game ).NormalizeFilename( false ); // invalid means get any IsDeleted = string.IsNullOrEmpty( AbsolutePath ); if ( AssetSystem.CloudDirectory is not null ) { Package = AssetSystem.CloudDirectory.FindPackage( AbsolutePath, RelativePath ); } // If we need a dependency update at this point, then this has taken a weird path through AssetSystem.AssetChanged // And will be resolved on the very next tick // The weird path it's taking was calling these below methods which resolving unresolved references before those assets had a chance to register... // I don't have a better solution that doesn't involve ripping it all up if ( native.NeedAnyDependencyUpdate_Virtual() ) return; IsTrivialChild = native.IsTrivialChildAsset(); // Reload all tags. LoadUserTags(); UpdateAutoTags(); if ( compileImmediately && !IsCompiled && AssetType.IsGameResource ) { Compile( false ); } } /// /// Can this asset be recompiled? /// public override bool CanRecompile => native.CanRecompile(); internal override void OnRemoved() { base.OnRemoved(); native = default; } /// /// Returns the compiled file path, if the asset is compiled. /// /// Whether the path should be absolute or relative.F /// The compiled file path, or null if the asset was not compiled. public override string GetCompiledFile( bool absolute = false ) { if ( absolute ) return native.GetAbsolutePath_Transient( AssetLocation_t.Game ).NormalizeFilename( false ); return native.GetRelativePath_Transient( AssetLocation_t.Game ).NormalizeFilename( false ); } /// /// Returns the source file path, if the sources are present. /// /// Whether the path should be absolute or relative. /// The source file path, or null if the source files are not present. public override string GetSourceFile( bool absolute = false ) { if ( absolute ) return native.GetAbsolutePath_Transient( AssetLocation_t.Content ).NormalizeFilename( false ); return native.GetRelativePath_Transient( AssetLocation_t.Content ).NormalizeFilename( false ); } internal override int FindIntEditInfo( string name ) { return native.FindIntEditInfo( name ); } public override string FindStringEditInfo( string name ) { return native.FindStringEditInfo( name ); } /// /// Try to open this asset in a supported editor. /// You can specify nativeEditor to open in a specific editor. /// /// A native editor specified in enginetools.txt (e.g modeldoc_editor, hammer, pet..) public override void OpenInEditor( string nativeEditor = null ) { if ( AssetType == AssetType.Shader ) { EditorEvent.Run( "open.shader", AbsolutePath ); return; } if ( nativeEditor != null ) { native.OpenInSecondaryTool( nativeEditor ); return; } if ( IAssetEditor.OpenInEditor( this, out _ ) ) { return; } native.OpenInTool(); } /// /// Returns assets that this asset references/uses. /// /// Whether to recurse. For example, will also include textures referenced by the materials used by this model asset, as opposed to returning just the materials. public override List GetReferences( bool deep ) { var list = NativeEngine.CUtlVectorAsset.Create( 4, 4 ); native.GetAssetsIReference( list, deep ); return GetAssetList( list, true ); } /// /// Returns assets that depend/use this asset. /// /// Whether to recurse. For example, will also include maps that are using models which use this material asset, as opposed to returning just the models. public override List GetDependants( bool deep ) { var list = NativeEngine.CUtlVectorAsset.Create( 4, 4 ); native.GetAssetsReferencingMe( list, deep ); return GetAssetList( list, true ); } List GetAssetList( NativeEngine.CUtlVectorAsset v, bool free ) { var l = new List(); for ( int i = 0; i < v.Count(); i++ ) { l.Add( AssetSystem.Get( v.Element( i ) ) ); } if ( free ) v.DeleteThis(); return l; } /// /// Gets additional related files. This includes like .rect files for materials, all .fbx and .lxo files for models, etc. /// public override List GetAdditionalRelatedFiles() { var l = new List(); for ( int i = 0; i < native.AdditionalRelatedFileCount(); i++ ) { l.Add( native.GetAdditionalRelatedFile_Transient( i ) ); } return l; } /// /// Gets input dependencies for an asset. This'll be tga's for a texture and stuff like that. /// public override List GetInputDependencies() { var l = new List(); for ( int i = 0; i < native.AdditionalInputDependencyCount(); i++ ) { l.Add( native.GetAdditionalInputDependency_Transient( i ) ); } return l; } /// /// Unrecognized reference paths listed by the data that could not be resolved into Asset*s /// public override List GetUnrecognizedReferencePaths() { var list = NativeEngine.CUtlVectorString.Create( 4, 4 ); native.GetUnrecognizedRelatedPaths( AssetRelatedPathType_t.Reference, list ); var l = new List(); for ( int i = 0; i < list.Count(); i++ ) { l.Add( list.Element( i ) ); } list.DeleteThis(); return l; } /// /// Forcibly recompile the asset. /// /// TODO public override bool Compile( bool full ) { ThreadSafe.AssertIsMainThread(); if ( AssetType == AssetType.Shader ) { EditorEvent.Run( "compile.shader", Path ); return true; } return IAssetSystem.RecompileAsset( native, full ); } /// /// Try to create a preview model if we're fbx, obj, etc /// public override Model GetPreviewModel() { return Model.FromNative( IAssetPreviewSystem.GetModelForAsset( native ), true ); } /// /// Tell asset system that this asset was opened. Sticks it on the recent opened list. /// public override void RecordOpened() { IAssetSystem.RecordAssetOpen( native ); } /// /// Whether the asset is compiled. /// public override bool IsCompiled => native.IsCompiled(); /// /// Whether the asset is compiled and all dependencies are up to date. (Slower than IsCompiled) /// public override bool IsCompiledAndUpToDate => native.IsCompiledAndUpToDate(); /// /// Whether the asset failed to compile. /// public override bool IsCompileFailed => native.IsCompileFailed(); /// /// Returns a task that will resolve when the asset is compiled. If the asset is already compiled, do nothing. Does not support maps. /// /// true if the compile was needed, and was successful. public override async ValueTask CompileIfNeededAsync( float timeout = 30.0f ) { if ( IsCompiled ) return false; // Map assets, shader assets, animation subgraphs do not compile via asset system if ( !CanRecompile ) return false; Log.Warning( $"Not Compiled: {this} ({native.GetCompileStateReason_Transient()})" ); // This is completely synchronous right now Compile( true ); RealTimeSince t = 0; while ( !IsCompiled ) { // If we don't run the frame, IsCompiled / HasFileInLocation will never have info and we'll be stuck IAssetSystem.RunFrame(); await Task.Delay( 2 ); if ( IsCompileFailed ) { Log.Warning( $"Error compiling {this}" ); return false; } if ( t.Relative > timeout ) { Log.Warning( $"CompileIfNeededAsync took over {timeout} seconds {this} ({native.GetCompileStateReason_Transient()})" ); return false; } } return true; } /// /// True if we have a source file, and aren't just a _c file /// public override bool HasSourceFile => native.HasSourceFile(); /// /// True if we have a compiled file, and aren't just a source file /// public override bool HasCompiledFile => native.HasCompiledFile(); /// /// Set data for this asset which will be compiled in memory. This is used to preview /// asset changes (like materials) before committing to disk. /// public override bool SetInMemoryReplacement( string sourceData ) { return native.SetInMemoryReplacement( sourceData ); } /// /// Reverse the changes of SetInMemoryReplacement /// public override void ClearInMemoryReplacement() { native.DiscardInMemoryReplacement(); } internal override void Uncache() { native.UncacheAsset(); } internal override async Task CacheAsync() { native.CacheAsset( true ); if ( AssetType.IsGameResource || native.IsCached() ) return true; await Task.Delay( 16 ); return false; } }