private static void UpdateReferences(SessionViewModel session) { if (session == null) { throw new ArgumentNullException(nameof(session)); } var dirtyReferencers = new HashSet <AssetViewModel>(); var dirtyAssets = new HashSet <AssetViewModel>(); var dirtyReferenced = new HashSet <AssetViewModel>(); var dirtyDirectReferenced = new HashSet <AssetViewModel>(); var referencerAssets = new HashSet <AssetViewModel>(); var referencedAssets = new HashSet <AssetViewModel>(); TaskCompletionSource <int> tcs; lock (DirtyDependencies) { foreach (var asset in DirtyDependencies) { // Add dirty dependencies from the previous reference values dirtyReferencers.AddRange(asset.Dependencies.RecursiveReferencerAssets); dirtyReferenced.AddRange(asset.Dependencies.RecursiveReferencedAssets); dirtyDirectReferenced.AddRange(asset.Dependencies.ReferencedAssets); referencerAssets.Clear(); referencedAssets.Clear(); if (!asset.IsDeleted) { var dependencyManager = session.DependencyManager; var dependencies = dependencyManager.ComputeDependencies(asset.AssetItem.Id, AssetDependencySearchOptions.In | AssetDependencySearchOptions.Out, ContentLinkType.Reference); // TODO: Change ContentLinkType.Reference to handle other types if (dependencies != null) { dependencies.LinksIn.Select(x => session.GetAssetById(x.Item.Id)).NotNull().ForEach(x => referencerAssets.Add(x)); dependencies.LinksOut.Select(x => session.GetAssetById(x.Item.Id)).NotNull().ForEach(x => referencedAssets.Add(x)); } dirtyAssets.Add(asset); // Add dirty dependencies from the updated reference values dirtyReferencers.AddRange(referencerAssets); dirtyReferenced.AddRange(referencedAssets); dirtyDirectReferenced.AddRange(referencedAssets); } // Note: the collections can be empty. This is especially needed when reimporting - we want to be sure that references are cleared. asset.Dependencies.ReferencerAssets = referencerAssets.ToList(); asset.Dependencies.ReferencedAssets = referencedAssets.ToList(); } DirtyDependencies.Clear(); // Clear the task now that we processed all the dirty dependencies tcs = dependenciesUpdated; dependenciesUpdated = null; } dirtyDirectReferenced.ExceptWith(dirtyAssets); // Add the dirty assets to the list of asset that needs to update recursive referenced assets. dirtyReferencers.AddRange(dirtyAssets); // Imported/undeleted assets must update their recursive referencers dirtyReferenced.AddRange(dirtyAssets); // Update the referencers of the (previous/updated) directly referenced assets foreach (var asset in dirtyDirectReferenced) { var dependencyManager = session.DependencyManager; var dependencies = dependencyManager.ComputeDependencies(asset.AssetItem.Id, AssetDependencySearchOptions.In, ContentLinkType.Reference); // TODO: Change ContentLinkType.Reference to handle other types referencerAssets.Clear(); dependencies?.LinksIn.Select(x => session.GetAssetById(x.Item.Id)).NotNull().ForEach(x => referencerAssets.Add(x)); asset.Dependencies.ReferencerAssets = referencerAssets.ToList(); } // Update recursive lists of referenced/referenced assets for assets affected by the changes foreach (var asset in dirtyReferenced) { asset.Dependencies.OnPropertyChanging(nameof(IsIndirectlyIncluded), nameof(IsExcluded)); asset.Dependencies.UpdateRecursiveReferencerAssets(); asset.Dependencies.OnPropertyChanged(nameof(IsIndirectlyIncluded), nameof(IsExcluded)); } foreach (var asset in dirtyReferencers) { asset.Dependencies.UpdateRecursiveReferencedAssets(); } foreach (var asset in dirtyAssets) { asset.Dependencies.OnPropertyChanging(nameof(IsIndirectlyIncluded), nameof(IsExcluded)); asset.Dependencies.OnPropertyChanged(nameof(IsIndirectlyIncluded), nameof(IsExcluded)); } // Job is completed, notify anything awaiting on it tcs.SetResult(0); }
private async void OpenDefaultScene(SessionViewModel session) { var startupPackage = session.LocalPackages.OfType <ProjectViewModel>().SingleOrDefault(x => x.IsCurrentProject); if (startupPackage == null) { return; } var gameSettingsAsset = startupPackage.Assets.FirstOrDefault(x => x.Url == Assets.GameSettingsAsset.GameSettingsLocation); if (gameSettingsAsset == null) { // Scan dependencies for game settings // TODO: Scanning order? (direct dependencies first) // TODO: Switch to using startupPackage.Dependencies view model instead foreach (var dependency in startupPackage.PackageContainer.FlattenedDependencies) { if (dependency.Package == null) { continue; } var dependencyPackageViewModel = session.AllPackages.First(x => x.Package == dependency.Package); if (dependencyPackageViewModel == null) { continue; } gameSettingsAsset = dependencyPackageViewModel.Assets.FirstOrDefault(x => x.Url == Assets.GameSettingsAsset.GameSettingsLocation); if (gameSettingsAsset != null) { break; } } } if (gameSettingsAsset == null) { return; } var defaultScene = ((Assets.GameSettingsAsset)gameSettingsAsset?.Asset)?.DefaultScene; if (defaultScene == null) { return; } var defaultSceneReference = AttachedReferenceManager.GetAttachedReference(defaultScene); if (defaultSceneReference == null) { return; } var asset = session.GetAssetById(defaultSceneReference.Id); if (asset == null) { return; } Editor.Session.ActiveAssetView.SelectAssets(asset.Yield()); await assetEditorsManager.OpenAssetEditorWindow(asset); }
private async Task PullSourceFileChanges() { var sourceTracker = session.SourceTracker; var bufferBlock = new BufferBlock <IReadOnlyList <SourceFileChangedData> >(); var changedAssets = new HashSet <AssetViewModel>(); var sourceFileChanges = new List <SourceFileChangedData>(); using (sourceTracker.SourceFileChanged.LinkTo(bufferBlock)) { sourceTracker.EnableTracking = true; while (!IsDestroyed) { // Await for source file changes await bufferBlock.OutputAvailableAsync(cancel.Token); if (cancel.IsCancellationRequested) { return; } // Try as much as possible to process all changes at once IList <IReadOnlyList <SourceFileChangedData> > changes; bufferBlock.TryReceiveAll(out changes); sourceFileChanges.AddRange(changes.SelectMany(x => x)); if (sourceFileChanges.Count == 0) { continue; } for (var i = 0; i < sourceFileChanges.Count; i++) { var sourceFileChange = sourceFileChanges[i]; // Look first in the assets of the session, then in the list of deleted assets. var asset = sessionViewModel.GetAssetById(sourceFileChange.AssetId) ?? sessionViewModel.AllPackages.SelectMany(x => x.DeletedAssets).FirstOrDefault(x => x.Id == sourceFileChange.AssetId); if (asset == null) { continue; } // We care only about changes that needs to update if (sourceFileChange.NeedUpdate) { switch (sourceFileChange.Type) { case SourceFileChangeType.Asset: // The asset itself has changed and now references a new source asset.Sources.UpdateUsedHashes(sourceFileChange.Files); break; case SourceFileChangeType.SourceFile: // A source file referenced by the asset has changed asset.Sources.ComputeNeedUpdateFromSource(); break; default: throw new ArgumentOutOfRangeException(); } } // The asset has been updated, remove the change data from the list sourceFileChanges.RemoveAt(i--); if (!asset.IsDeleted) { // We will notify only for undeleted assets changedAssets.Add(asset); } } // Notify all changed assets at once. if (changedAssets.Count > 0) { sessionViewModel.NotifyAssetPropertiesChanged(changedAssets.ToList()); changedAssets.Clear(); } } } }