@@ -13,20 +13,31 @@ public protocol ResourceImporter: Sendable {
1313 init ( )
1414
1515 #if GATEENGINE_PLATFORM_HAS_SynchronousFileSystem
16+ /// Preloads the ResourceImporters data in preparation for decoding.
1617 mutating func synchronousPrepareToImportResourceFrom( path: String ) throws ( GateEngineError)
1718 #endif
1819 #if GATEENGINE_PLATFORM_HAS_AsynchronousFileSystem
20+ /// Preloads the ResourceImporters data in preparation for decoding.
1921 mutating func prepareToImportResourceFrom( path: String ) async throws ( GateEngineError)
2022 #endif
2123
24+ /// A list of file extensions that this resource importer is able to process.
2225 static func supportedFileExtensions( ) -> [ String ]
23- static func canProcessFile( _ path: String ) -> Bool
26+
27+ /**
28+ Checks if a file can be processed by this resource importer.
29+ - parameter path: The resource path of a file to check
30+ - returns: `true` if this resource importer can load a resource from the file at `path`
31+ - warning: This function is an advanced option rarely needed. Please implement `supportedFileExtensions()` instead.
32+ - seeAlso: `supportedFileExtensions()`
33+ */
34+ static func canProcessFile( at path: String ) -> Bool
2435
2536 /**
2637 Importers can report if they are capable of returning multiple resource instances from the same file.
2738
2839 Properly returning `true` or `false` will effect performance. If the importer can decode multiple resources,
29- it will be kept in memory for a period of time allowing it to deocde more resources from the already accessed file data.
40+ it will be kept in memory for a period of time allowing it to deocde more resources from the already loaded file data.
3041 - returns: `true` if this importer is able to return more then one resources from a single file, otherwise `false`.
3142 */
3243 mutating func currentFileContainsMutipleResources( ) -> Bool
@@ -46,9 +57,9 @@ public extension ResourceImporter {
4657 return [ ]
4758 }
4859
49- static func canProcessFile( _ path: String ) -> Bool {
60+ static func canProcessFile( at path: String ) -> Bool {
5061 let supportedExtensions = self . supportedFileExtensions ( )
51- precondition ( supportedExtensions. isEmpty == false , " Imporers must implement `supportedFileExtensions()` or `canProcessFile(_ :)`. " )
62+ precondition ( supportedExtensions. isEmpty == false , " Imporers must implement `supportedFileExtensions()` or `canProcessFile(at :)`. " )
5263 let fileExtension = URL ( fileURLWithPath: path) . pathExtension
5364 guard fileExtension. isEmpty == false else { return false }
5465 for supportedFileExtension in supportedExtensions {
@@ -107,38 +118,68 @@ extension ResourceManager {
107118 TiledTMJImporter . self,
108119 ]
109120
110- private var activeImporters : [ ActiveImporterKey : ActiveImporter ] = [ : ]
111- private struct ActiveImporterKey : Hashable , Sendable {
112- let path : String
113- }
114- private struct ActiveImporter : Sendable {
115- let importer : any ResourceImporter
116- var lastAccessed : Date = . now
117- }
118-
119- internal mutating func getImporter< I: ResourceImporter > ( path: String , type: I . Type ) async throws ( GateEngineError) -> I {
120- let key = ActiveImporterKey ( path: path)
121- if let existing = activeImporters [ key] {
122- // Make sure the importer can be the type requested
123- if let importer = existing. importer as? I {
124- activeImporters [ key] ? . lastAccessed = . now
125- return importer
121+ /**
122+ Isolated cache of resource importers. Importers are mutable, but no changes are saved outside of the scode they are made.
123+ */
124+ actor ActiveImporters {
125+ struct Key : Hashable , Sendable {
126+ let path : String
127+ }
128+ struct Importer : Sendable {
129+ let importer : any ResourceImporter
130+ var lastAccessed : Date = . now
131+ }
132+ var activeImporters : [ Key : Importer ] = [ : ]
133+
134+ subscript ( _ key: Key ) -> Importer ? {
135+ get {
136+ return self . activeImporters [ key]
137+ }
138+ set {
139+ self . activeImporters [ key] = newValue
140+ }
141+ }
142+
143+ func setLastAccessed( for key: Key ) {
144+ self . activeImporters [ key] ? . lastAccessed = . now
145+ }
146+
147+ func getImporter< I: ResourceImporter > ( for path: String , type: I . Type ) async throws ( GateEngineError) -> I {
148+ let key = Key ( path: path)
149+ if let existing = activeImporters [ key] {
150+ // Make sure the importer can be the type requested
151+ if let importer = existing. importer as? I {
152+ activeImporters [ key] ? . lastAccessed = . now
153+ return importer
154+ }
155+ }
156+ var importer = type. init ( )
157+ try await importer. prepareToImportResourceFrom ( path: path)
158+ if importer. currentFileContainsMutipleResources ( ) {
159+ let active = ActiveImporters . Importer ( importer: importer, lastAccessed: . now)
160+ activeImporters [ key] = active
126161 }
162+ return importer
127163 }
128- var importer = type. init ( )
129- try await importer. prepareToImportResourceFrom ( path: path)
130- if importer. currentFileContainsMutipleResources ( ) {
131- let active = ActiveImporter ( importer: importer, lastAccessed: . now)
132- activeImporters [ key] = active
164+
165+ func clean( ) {
166+ for key in activeImporters. keys {
167+ if activeImporters [ key] !. lastAccessed. timeIntervalSinceNow < - 5 {
168+ activeImporters. removeValue ( forKey: key)
169+ }
170+ }
133171 }
134- return importer
135172 }
173+ var activeImporters : ActiveImporters = . init( )
136174
137- internal mutating func clean( ) {
138- for key in activeImporters. keys {
139- if activeImporters [ key] !. lastAccessed. timeIntervalSinceNow < - 60 {
140- activeImporters. removeValue ( forKey: key)
141- }
175+ internal func getImporter< I: ResourceImporter > ( path: String , type: I . Type ) async throws ( GateEngineError) -> I {
176+ return try await activeImporters. getImporter ( for: path, type: type)
177+ }
178+
179+ internal func clean( ) {
180+ let activeImporters = activeImporters
181+ Task {
182+ await activeImporters. clean ( )
142183 }
143184 }
144185 }
0 commit comments