Terraria v1.4.4.9
Terraria source code documentation
Loading...
Searching...
No Matches
AssetRepository.cs
Go to the documentation of this file.
1using System;
4using System.IO;
5using System.Linq;
10
11namespace ReLogic.Content;
12
29{
30 internal struct ContinuationScheduler
31 {
32 public readonly IAsset asset;
33
34 public readonly AssetRepository repository;
35
37 {
38 this.asset = asset;
39 this.repository = repository;
40 }
41
42 public void OnCompleted(Action continuation)
43 {
44 if (asset == null)
45 {
46 throw new Exception("Main thread transition requested without an asset");
47 }
48 continuation = continuation.OnlyRunnableOnce();
50 asset.Continuation = continuation;
51 }
52 }
53
54 private static Thread _mainThread;
55
57
59
60 protected readonly AssetReaderCollection _readers;
61
62 private readonly object _requestLock = new object();
63
65
66 private bool _isDisposed;
67
68 private int _Remaining;
69
70 public static bool IsMainThread => Thread.CurrentThread == _mainThread;
71
72 protected IContentSource[] _sources { get; private set; }
73
74 public bool IsDisposed => _isDisposed;
75
77
78 public int TotalAssets { get; private set; }
79
80 public int LoadedAssets { get; private set; }
81
82 public bool IsAsyncLoadingEnabled => true;
83
85
86 public static void SetMainThread()
87 {
88 if (_mainThread != null)
89 {
90 throw new InvalidOperationException("Main thread already set");
91 }
93 }
94
95 public static void ThrowIfNotMainThread()
96 {
97 if (!IsMainThread)
98 {
99 throw new Exception("Must be on main thread");
100 }
101 }
102
103 private void Invoke(Action action)
104 {
105 if (_readers == null)
106 {
108 return;
109 }
110 ManualResetEvent evt = new ManualResetEvent(initialState: false);
112 {
113 action();
114 evt.Set();
115 });
116 evt.WaitOne();
117 }
118
120 {
122 {
123 return _assets.Values.ToArray();
124 }
125 }
126
132
134 {
138 {
140 _sources = sources.ToArray();
142 if (mode == AssetRequestMode.ImmediateLoad && _Remaining > 0)
143 {
144 throw new Exception("Some assets loaded asynchronously, despite AssetRequestMode.ImmediateLoad on main thread");
145 }
146 }
147 }
148
149 internal Asset<T> Request<T>(string assetName) where T : class
150 {
151 return Request<T>(assetName, AssetRequestMode.ImmediateLoad);
152 }
153
154 public virtual Asset<T> Request<T>(string assetName, AssetRequestMode mode = AssetRequestMode.AsyncLoad) where T : class
155 {
156 if (_readers == null)
157 {
158 mode = AssetRequestMode.DoNotLoad;
159 }
162 Asset<T> asset = null;
164 {
166 {
167 asset = value as Asset<T>;
168 }
169 if (asset == null)
170 {
171 asset = new Asset<T>(assetName);
172 _assets[assetName] = asset;
173 }
174 if (asset.State == AssetState.NotLoaded)
175 {
177 LoadAsset(asset, mode);
178 }
179 }
180 if (mode == AssetRequestMode.ImmediateLoad)
181 {
182 asset.Wait();
183 }
184 return asset;
185 }
186
187 public void TransferAllAssets()
188 {
189 if (!IsMainThread)
190 {
192 return;
193 }
194 while (_Remaining > 0)
195 {
197 }
198 }
199
201 {
205 {
206 Action action;
208 {
209 action();
210 }
211 }
212 }
213
215 {
216 foreach (IAsset item in _assets.Values.Where((IAsset asset) => asset.IsLoaded))
217 {
219 if (contentSource == null)
220 {
222 }
223 else if (item.Source != contentSource)
224 {
225 ForceReloadAsset(item, mode);
226 }
227 }
228 }
229
230 private void LoadAsset<T>(Asset<T> asset, AssetRequestMode mode) where T : class
231 {
232 if (mode != 0)
233 {
235 asset.Wait = delegate
236 {
237 SafelyWaitForLoad(asset, loadTask, tracked: true);
238 };
239 }
240 }
241
243 {
244 _ = 3;
245 try
246 {
248 {
249 throw new Exception("Asset load started without holding _requestLock");
250 }
251 TotalAssets++;
252 asset.SetToLoadingState();
254 new List<string>();
257 foreach (IContentSource source in sources)
258 {
259 if (source.Rejections.IsRejected(asset.Name))
260 {
261 continue;
262 }
263 string extension = source.GetExtension(asset.Name);
264 if (extension == null)
265 {
266 continue;
267 }
268 if (!_readers.TryGetReader(extension, out var reader))
269 {
271 continue;
272 }
273 if (mode == AssetRequestMode.AsyncLoad)
274 {
275 await Task.Yield();
276 }
278 {
279 await Task.Yield();
280 }
281 T resultAsset;
282 using (Stream stream = source.OpenStream(asset.Name + extension))
283 {
284 _ = 2;
285 try
286 {
287 resultAsset = await reader.FromStream<T>(stream, mainThreadCtx);
288 }
289 catch (Exception e2)
290 {
291 source.Rejections.Reject(asset.Name, new ContentRejectionAssetReaderException(e2));
292 continue;
293 }
294 }
295 if (source.ContentValidator != null && !source.ContentValidator.AssetIsValid(resultAsset, asset.Name, out var rejectionReason))
296 {
297 source.Rejections.Reject(asset.Name, rejectionReason);
298 continue;
299 }
302 {
303 throw new Exception("Asset transfer started without holding _requestLock");
304 }
305 asset.SubmitLoadedContent(resultAsset, source);
306 LoadedAssets++;
307 return;
308 }
309 throw AssetLoadException.FromMissingAsset(asset.Name);
310 }
311 catch (Exception e)
312 {
313 AssetLoadFailHandler?.Invoke(asset.Name, e);
314 if (mode == AssetRequestMode.ImmediateLoad)
315 {
316 throw;
317 }
318 }
319 finally
320 {
322 }
323 }
324
325 private void SafelyWaitForLoad<T>(Asset<T> asset, Task loadTask, bool tracked) where T : class
326 {
327 if (asset.State == AssetState.Loaded)
328 {
329 return;
330 }
331 if (!loadTask.IsCompleted && IsMainThread)
332 {
333 while (asset.Continuation == null)
334 {
335 Thread.Yield();
336 }
337 if (tracked)
338 {
340 {
341 asset.Continuation();
342 }
343 }
344 else
345 {
346 asset.Continuation();
347 }
348 if (!loadTask.IsCompleted)
349 {
350 throw new Exception("Load task not completed after running continuations on main thread?");
351 }
352 }
353 loadTask.GetAwaiter().GetResult();
354 if (asset.State == AssetState.Loaded)
355 {
356 return;
357 }
358 throw new Exception("How did you get here?");
359 }
360
362 {
363 string ext = Path.GetExtension(name);
364 if (!_readers.TryGetReader(ext, out var reader))
365 {
367 }
368 int length = ext.Length;
369 Asset<T> asset = new Asset<T>(name.Substring(0, name.Length - length));
370 Task loadTask = LoadUntracked(stream, reader, asset, mode);
371 asset.Wait = delegate
372 {
373 SafelyWaitForLoad(asset, loadTask, tracked: false);
374 };
375 if (mode == AssetRequestMode.ImmediateLoad)
376 {
377 asset.Wait();
378 }
379 return asset;
380 }
381
383 {
384 if (mode == AssetRequestMode.AsyncLoad)
385 {
386 await Task.Yield();
387 }
389 asset.SubmitLoadedContent(await reader.FromStream<T>(stream, mainThreadCtx), null);
390 }
391
392 private void ForceReloadAsset(IAsset asset, AssetRequestMode mode)
393 {
394 _typeSpecificReloadActions[asset.GetType()](asset, mode);
395 }
396
401
402 private void ForceReloadAsset<T>(IAsset asset, AssetRequestMode mode) where T : class
403 {
404 Asset<T> asset2 = (Asset<T>)asset;
405 if (asset.IsLoaded)
406 {
407 LoadedAssets--;
408 }
409 if (asset.State != 0)
410 {
411 TotalAssets--;
412 }
413 asset2.Unload();
414 LoadAsset(asset2, mode);
415 }
416
418 {
419 return _sources.FirstOrDefault((IContentSource source) => source.HasAsset(assetName)) ?? throw AssetLoadException.FromMissingAsset(assetName);
420 }
421
422 private void ThrowIfDisposed()
423 {
424 if (_isDisposed)
425 {
426 throw new ObjectDisposedException("AssetRepository is disposed.");
427 }
428 }
429
430 protected virtual void Dispose(bool disposing)
431 {
432 if (_isDisposed)
433 {
434 return;
435 }
436 if (disposing)
437 {
438 foreach (KeyValuePair<string, IAsset> asset in _assets)
439 {
440 asset.Value.Dispose();
441 }
442 }
443 _isDisposed = true;
444 }
445
446 public void Dispose()
447 {
448 if (!IsMainThread)
449 {
451 }
452 else
453 {
454 Dispose(disposing: true);
455 }
456 }
457}
static AssetLoadException FromMissingReader(string extension)
static AssetLoadException FromMissingAsset(string assetName, Exception innerException=null)
static string CleanPath(string path)
bool TryGetReader(string extension, out IAssetReader reader)
async Task LoadAssetWithPotentialAsync< T >(Asset< T > asset, AssetRequestMode mode)
readonly AssetReaderCollection _readers
async Task LoadUntracked< T >(Stream stream, IAssetReader reader, Asset< T > asset, AssetRequestMode mode)
Asset< T > Request< T >(string assetName)
void ReloadAssetsIfSourceChanged(AssetRequestMode mode)
virtual void SetSources(IEnumerable< IContentSource > sources, AssetRequestMode mode=AssetRequestMode.ImmediateLoad)
readonly ConcurrentQueue< Action > _assetTransferQueue
readonly Dictionary< Type, Action< IAsset, AssetRequestMode > > _typeSpecificReloadActions
AssetRepository(AssetReaderCollection readers, IEnumerable< IContentSource > sources=null)
void ForceReloadAsset< T >(IAsset asset, AssetRequestMode mode)
void SafelyWaitForLoad< T >(Asset< T > asset, Task loadTask, bool tracked)
readonly Dictionary< string, IAsset > _assets
IContentSource FindSourceForAsset(string assetName)
virtual void Dispose(bool disposing)
void LoadAsset< T >(Asset< T > asset, AssetRequestMode mode)
Asset< T > CreateUntracked< T >(Stream stream, string name, AssetRequestMode mode=AssetRequestMode.ImmediateLoad)
FailedToLoadAssetCustomAction AssetLoadFailHandler
void ForceReloadAsset(IAsset asset, AssetRequestMode mode)
Async loading has been fully integrated into AssetRepository Assets which are asynchronously loaded w...
bool TryDequeue([MaybeNullWhen(false)] out T result)
bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
static ? string GetExtension(string? path)
Definition Path.cs:168
static int Decrement(ref int location)
static int Increment(ref int location)
static bool IsEntered(object obj)
Definition Monitor.cs:71
static YieldAwaitable Yield()
Definition Task.cs:2220
static bool Yield()
Definition Thread.cs:412
static Thread CurrentThread
Definition Thread.cs:312
delegate void FailedToLoadAssetCustomAction(string assetName, Exception e)
ContinuationScheduler(IAsset asset, AssetRepository repository)