/// <summary> /// Reloads a content. If possible, same recursively referenced objects are reused. /// </summary> /// <param name="obj">The object to reload.</param> /// <param name="newUrl">The url of the new object to load. This allows to replace an asset by another one, or to handle renamed content.</param> /// <param name="settings">The loader settings.</param> /// <returns>True if it could be reloaded, false otherwise.</returns> /// <exception cref="System.InvalidOperationException">Content not loaded through this ContentManager.</exception> public bool Reload(object obj, string newUrl = null, ContentManagerLoaderSettings settings = null) { if (settings == null) { settings = ContentManagerLoaderSettings.Default; } lock (LoadedAssetUrls) { Reference reference; if (!LoadedAssetReferences.TryGetValue(obj, out reference)) { return(false); // The object is not loaded } var url = newUrl ?? reference.Url; using (var profile = Profiler.Begin(ContentProfilingKeys.ContentReload, url)) { DeserializeObject(reference.Url, url, obj.GetType(), obj, settings); } if (url != reference.Url) { LoadedAssetUrls.Remove(reference.Url); } return(true); } }
/// <summary> /// Loads content from the specified URL. /// </summary> /// <param name="type">The type.</param> /// <param name="url">The URL.</param> /// <param name="settings">The settings.</param> /// <returns>The loaded content.</returns> /// <remarks>If the asset is already loaded, it just increases the reference count of the asset and return the same instance.</remarks> /// <exception cref="System.ArgumentNullException">url</exception> public object Load(Type type, string url, ContentManagerLoaderSettings settings = null) { if (settings == null) { settings = ContentManagerLoaderSettings.Default; } if (url == null) { throw new ArgumentNullException(nameof(url)); } lock (LoadedAssetUrls) { using (var profile = Profiler.Begin(ContentProfilingKeys.ContentLoad, url)) { return(DeserializeObject(url, url, type, null, settings)); } } }
private object DeserializeObject(Queue <DeserializeOperation> serializeOperations, Reference parentReference, string url, Type objType, object obj, ContentManagerLoaderSettings settings) { // Try to find already loaded object Reference reference = FindDeserializedObject(url, objType); if (reference != null && reference.Deserialized) { // Add reference bool isRoot = parentReference == null; if (isRoot || parentReference.References.Add(reference)) { IncrementReference(reference, isRoot); } // Check if need to fully stream resource if (!settings.AllowContentStreaming) { var streamingManager = services.GetService <IStreamingManager>(); streamingManager?.FullyLoadResource(reference.Object); } return(reference.Object); } if (!FileProvider.FileExists(url)) { HandleAssetNotFound(url); return(null); } ContentSerializerContext contentSerializerContext; object result; // Open asset binary stream try { using (var stream = FileProvider.OpenStream(url, VirtualFileMode.Open, VirtualFileAccess.Read)) { // File does not exist // TODO/Benlitz: Add a log entry for that, it's not expected to happen if (stream == null) { return(null); } Type headerObjType = null; // Read header var streamReader = new BinarySerializationReader(stream); var chunkHeader = ChunkHeader.Read(streamReader); if (chunkHeader != null) { headerObjType = AssemblyRegistry.GetType(chunkHeader.Type); } // Find serializer var serializer = Serializer.GetSerializer(headerObjType, objType); if (serializer == null) { throw new InvalidOperationException($"Content serializer for {headerObjType}/{objType} could not be found."); } contentSerializerContext = new ContentSerializerContext(url, ArchiveMode.Deserialize, this) { LoadContentReferences = settings.LoadContentReferences, AllowContentStreaming = settings.AllowContentStreaming, }; // Read chunk references if (chunkHeader != null && chunkHeader.OffsetToReferences != -1) { // Seek to where references are stored and deserialize them streamReader.NativeStream.Seek(chunkHeader.OffsetToReferences, SeekOrigin.Begin); contentSerializerContext.SerializeReferences(streamReader); streamReader.NativeStream.Seek(chunkHeader.OffsetToObject, SeekOrigin.Begin); } if (reference == null) { // Create Reference reference = new Reference(url, parentReference == null); result = obj ?? serializer.Construct(contentSerializerContext); SetAssetObject(reference, result); } else { result = reference.Object; } reference.Deserialized = true; PrepareSerializerContext(contentSerializerContext, streamReader.Context); contentSerializerContext.SerializeContent(streamReader, serializer, result); // Add reference parentReference?.References.Add(reference); } } catch (Exception exception) { throw new ContentManagerException($"Unexpected exception while loading asset [{url}]. Reason: {exception.Message}. Check inner-exception for details.", exception); } if (settings.LoadContentReferences) { // Process content references // TODO: Should we work at ChunkReference level? foreach (var contentReference in contentSerializerContext.ContentReferences) { bool shouldBeLoaded = true; //Reference childReference; settings.ContentFilter?.Invoke(contentReference, ref shouldBeLoaded); if (shouldBeLoaded) { serializeOperations.Enqueue(new DeserializeOperation(reference, contentReference.Location, contentReference.Type, contentReference.ObjectValue)); } } } return(result); }
private object DeserializeObject(string initialUrl, string newUrl, Type type, object obj, ContentManagerLoaderSettings settings) { var serializeOperations = new Queue <DeserializeOperation>(); serializeOperations.Enqueue(new DeserializeOperation(null, newUrl, type, obj)); Reference reference = null; if (obj != null) { reference = FindDeserializedObject(initialUrl, type); if (reference.Object != obj) { throw new InvalidOperationException("Object doesn't match, can't reload"); } } // Let's put aside old references, so that we unload them only afterwise (avoid a referenced object to be unloaded for no reason) HashSet <Reference> references = null; if (reference != null) { // Let's collect dependent reference, and reset current list references = reference.References; reference.References = new HashSet <Reference>(); // Mark object as not deserialized yet reference.Deserialized = false; } bool isFirstOperation = true; object result = null; while (serializeOperations.Count > 0) { var serializeOperation = serializeOperations.Dequeue(); var deserializedObject = DeserializeObject(serializeOperations, serializeOperation.ParentReference, serializeOperation.Url, serializeOperation.ObjectType, serializeOperation.Object, settings); if (isFirstOperation) { result = deserializedObject; isFirstOperation = false; } } if (reference != null) { foreach (var dependentReference in references) { DecrementReference(dependentReference, false); } } return(result); }
/// <summary> /// Loads an asset from the specified URL asynchronously. /// </summary> /// <param name="type">The type.</param> /// <param name="url">The URL.</param> /// <param name="settings">The settings.</param> /// <remarks>If the asset is already loaded, it just increases the reference count of the asset and return the same instance.</remarks> /// <returns>The loaded content.</returns> public Task <object> LoadAsync(Type type, string url, ContentManagerLoaderSettings settings = null) { return(ScheduleAsync(() => Load(type, url, settings))); }
/// <summary> /// Loads an asset from the specified URL asynchronously. /// </summary> /// <typeparam name="T">The content type.</typeparam> /// <param name="url">The URL to load from.</param> /// <param name="settings">The settings. If null, fallback to <see cref="ContentManagerLoaderSettings.Default" />.</param> /// <remarks>If the asset is already loaded, it just increases the reference count of the asset and return the same instance.</remarks> /// <returns>The loaded content.</returns> public Task <T> LoadAsync <T>(string url, ContentManagerLoaderSettings settings = null) where T : class { return(ScheduleAsync(() => Load <T>(url, settings))); }
/// <summary> /// Reloads a content asynchronously. If possible, same recursively referenced objects are reused. /// </summary> /// <param name="obj">The object to reload.</param> /// <param name="newUrl">The url of the new object to load. This allows to replace an asset by another one, or to handle renamed content.</param> /// <param name="settings">The loader settings.</param> /// <returns>A task that completes when the content has been reloaded. The result of the task is True if it could be reloaded, false otherwise.</returns> /// <exception cref="System.InvalidOperationException">Content not loaded through this ContentManager.</exception> public Task <bool> ReloadAsync(object obj, string newUrl = null, ContentManagerLoaderSettings settings = null) { return(ScheduleAsync(() => Reload(obj, newUrl, settings))); }
/// <summary> /// Loads content from the specified URL. /// </summary> /// <typeparam name="T">The content type.</typeparam> /// <param name="url">The URL to load from.</param> /// <param name="settings">The settings. If null, fallback to <see cref="ContentManagerLoaderSettings.Default" />.</param> /// <remarks>If the asset is already loaded, it just increases the reference count of the asset and return the same instance.</remarks> /// <returns>The loaded content.</returns> public T Load <T>(string url, ContentManagerLoaderSettings settings = null) where T : class { return((T)Load(typeof(T), url, settings)); }