using System.IO; namespace Editor; /// /// A filesystem that can be accessed by the game. /// public static class FileSystem { /// /// Paths from tool addons which are mounted. /// public static BaseFileSystem Mounted => Sandbox.FileSystem.Mounted; /// /// Root of the game's folder. /// public static BaseFileSystem Root => EngineFileSystem.Root; /// /// The engine /game/.source2/ folder for temporary files and caches. /// public static BaseFileSystem Temporary { get; internal set; } /// /// The engine /game/config/ folder /// public static BaseFileSystem Config => EngineFileSystem.Config; /// /// The engine /game/.source2/http/ folder. /// public static BaseFileSystem WebCache { get; internal set; } /// /// The current project's .sbox/ folder for temporary files and caches. /// public static BaseFileSystem ProjectTemporary { get; internal set; } /// /// The current project's .sbox/cloud/ folder. We download files from sbox.game right into this filesystem. /// public static BaseFileSystem Cloud { get; internal set; } /// /// The current project's .sbox/transient/ folder. This is where assets are created at runtime. These are assets /// that are created by another asset,that don't need to be stored in source control or anything - because they /// can get re-created at will. /// public static BaseFileSystem Transient { get; internal set; } /// /// Content from active addons (and content paths) /// public static BaseFileSystem Content { get; internal set; } /// /// The current project's ProjectSettings folder /// public static BaseFileSystem ProjectSettings { get; internal set; } /// /// The current project's Libraries folder /// public static BaseFileSystem Libraries { get; internal set; } /// /// The current project's Localization folder /// public static BaseFileSystem Localization { get; internal set; } /// /// Should be called on startup and whenever the mounted local addons have changed /// internal static void RebuildContentPath() { Content?.Dispose(); Content = null; Content = new AggregateFileSystem(); Content.CreateAndMount( EngineFileSystem.Root, "/core/" ); Content.CreateAndMount( EngineFileSystem.Root, "/addons/base/assets/" ); Content.CreateAndMount( EngineFileSystem.Root, "/addons/citizen/assets/" ); Content.Mount( Cloud ); foreach ( var addon in Project.All.Where( x => x.Active ) ) { var contentPath = addon.GetAssetsPath(); if ( string.IsNullOrWhiteSpace( contentPath ) ) continue; if ( !System.IO.Directory.Exists( contentPath ) ) continue; Content.CreateAndMount( contentPath ); } var watch = Content.Watch(); watch.OnChangedFile += OnContentFileChanged; // Mount assets from tool addons for Qt, example being hammer outliner foreach ( var addon in Project.All.Where( x => x.Active && x.Config.Type == "tool" ) ) { var contentPath = addon.GetAssetsPath(); if ( string.IsNullOrWhiteSpace( contentPath ) ) continue; if ( !System.IO.Directory.Exists( contentPath ) ) continue; QDir.addSearchPath( "toolimages", contentPath ); } } /// /// Called when a file has changed on the path. /// private static void OnContentFileChanged( string filename ) { ThreadSafe.AssertIsMainThread(); // Check to see if this asset was deleted from explorer - and mark it as deleted in the asset system. if ( AssetSystem.FindByPath( filename ) is Asset asset ) { var fileExists = System.IO.File.Exists( asset.AbsolutePath ); if ( !asset.IsDeleted && !fileExists ) { asset.IsDeleted = true; } // Source file for a gameresource was edited, let's compile it (for detecting changes from version control) if ( asset.TryLoadResource( out var gameResource ) ) { asset.Compile( false ); } } EditorEvent.Run( "content.changed", filename ); } /// /// Stop the game from triggering a hotload for this file - because presumably you have /// already reloaded it. /// public static void SuppressNextHotload() { FileWatch.SuppressWatchers = RealTime.Now + 0.5f; } /// /// Initialize the editor filesytems from this project, which is assumably the main game project. /// internal static void InitializeFromProject( Project project ) { var root = project.GetRootPath(); var projectsFolder = Path.Combine( root, "ProjectSettings" ); Directory.CreateDirectory( projectsFolder ); ProjectSettings = new LocalFileSystem( projectsFolder ); var librariesFolder = Path.Combine( root, "Libraries" ); Directory.CreateDirectory( librariesFolder ); Libraries = new LocalFileSystem( librariesFolder ); var localizationFolder = Path.Combine( root, "Localization" ); Directory.CreateDirectory( localizationFolder ); Localization = new LocalFileSystem( localizationFolder ); var sboxFolder = Path.Combine( root, ".sbox" ); Directory.CreateDirectory( sboxFolder ); ProjectTemporary = new LocalFileSystem( sboxFolder ); // Set folder as hidden. Will hide it from explorer (by default) and from the asset browser var di = new DirectoryInfo( sboxFolder ); di.Attributes |= FileAttributes.Hidden; var cloudFolder = Path.Combine( sboxFolder, "cloud" ); Directory.CreateDirectory( cloudFolder ); Cloud = new LocalFileSystem( cloudFolder ); Mounted.Mount( Cloud ); var transientFolder = Path.Combine( sboxFolder, "transient" ); Directory.CreateDirectory( transientFolder ); Transient = new LocalFileSystem( transientFolder ); Mounted.Mount( Transient ); } }