/// <inheritdoc/> /// <remarks> /// Derived class should override this method, implement specific cleanup and then call the base implementation. /// </remarks> public virtual void Destroy() { EnsureNotDestroyed(); if (IsDestroying) { return; } Loader.Dispose(); EditorDebugTools.UnregisterDebugPage(debugPage); UnregisterFromDragDropEvents(); GameForm?.Host?.Dispose(); IsDestroying = true; // Clean after everything PostTask(async x => { if (serviceRegistry != null) { await serviceRegistry.DisposeAsync(); } Game.Exit(); GameForm?.Dispose(); }, int.MaxValue) // Ensure the properties are correctly set, even in case of a failure (default continuation) .ContinueWith(t => { Asset.Dispatcher.Invoke(() => { IsDestroyed = true; IsDestroying = false; }); }); GameSideNodeContainer.Clear(); }
/// <inheritdoc /> public override Task RemovePart([NotNull] EntityHierarchyElementViewModel parent, Entity assetSidePart) { EnsureAssetAccess(); return(InvokeAsync(() => { Logger.Debug($"Removing entity {assetSidePart.Id} from game-side scene"); var partId = new AbsoluteId(AssetId.Empty, assetSidePart.Id); var part = (Entity)FindPart(partId); if (part == null) { throw new InvalidOperationException($"The given {nameof(assetSidePart.Id)} does not correspond to any existing part."); } if (parent is PrefabRootViewModel) { Game.UnloadEntity(part); } else { var parentEntity = (Entity)FindPart(parent.Id); if (parentEntity == null) { throw new InvalidOperationException($"The given {nameof(parent.Id)} does not correspond to any existing part."); } var i = parentEntity.Transform.Children.IndexOf(part.Transform); GameSideNodeContainer.GetNode(parentEntity.Transform.Children).Remove(part.Transform, new NodeIndex(i)); } })); }
/// <inheritdoc /> public override Task AddPart([NotNull] EntityHierarchyElementViewModel parent, Entity assetSidePart) { EnsureAssetAccess(); var gameSidePart = ClonePartForGameSide(parent.Asset.Asset, assetSidePart); return(InvokeAsync(() => { Logger.Debug($"Adding entity {assetSidePart.Id} to game-side scene"); if (parent is PrefabRootViewModel) { Game.LoadEntity(gameSidePart); } else { var parentEntity = (Entity)FindPart(parent.Id); if (parentEntity == null) { throw new InvalidOperationException($"The given {nameof(parent.Id)} does not correspond to any existing part."); } GameSideNodeContainer.GetNode(parentEntity.Transform.Children).Add(gameSidePart.Transform); } })); }
/// <inheritdoc/> protected override Dictionary <Guid, IIdentifiable> CollectIdentifiableObjects() { var allElements = RootElements.Values.BreadthFirst(x => x.VisualChildren); var definition = AssetQuantumRegistry.GetDefinition(Asset.Asset.GetType()); var identifiableObjects = new Dictionary <Guid, IIdentifiable>(); foreach (var entityNode in allElements.Select(x => GameSideNodeContainer.GetOrCreateNode(x))) { foreach (var identifiable in IdentifiableObjectCollector.Collect(definition, entityNode)) { identifiableObjects.Add(identifiable.Key, identifiable.Value); } } return(identifiableObjects); }
/// <inheritdoc/> protected override Dictionary <Guid, IIdentifiable> CollectIdentifiableObjects() { var allEntities = Game.ContentScene.Yield().BreadthFirst(x => x.Children).SelectMany(x => x.Entities).BreadthFirst(x => x.Transform.Children.Select(y => y.Entity)); var definition = AssetQuantumRegistry.GetDefinition(Asset.Asset.GetType()); var identifiableObjects = new Dictionary <Guid, IIdentifiable>(); foreach (var entityNode in allEntities.Select(x => GameSideNodeContainer.GetOrCreateNode(x))) { foreach (var identifiable in IdentifiableObjectCollector.Collect(definition, entityNode)) { identifiableObjects.Add(identifiable.Key, identifiable.Value); } } return(identifiableObjects); }
/// <inheritdoc /> public override Task RemovePart([NotNull] UIHierarchyItemViewModel parent, UIElement assetSidePart) { EnsureAssetAccess(); return(InvokeAsync(() => { Logger.Debug($"Removing element {assetSidePart.Id} from game-side scene"); var partId = new AbsoluteId(AssetId.Empty, assetSidePart.Id); var part = (UIElement)FindPart(partId); if (part == null) { throw new InvalidOperationException($"The given {nameof(assetSidePart.Id)} does not correspond to any existing part."); } var parentId = (parent as UIElementViewModel)?.Id; if (parentId == null) { RootElements.Remove(assetSidePart.Id); } else { var parentElement = (UIElement)FindPart(parentId.Value); if (parentElement == null) { throw new InvalidOperationException($"The given {nameof(parentId)} does not correspond to any existing part."); } var panel = parentElement as Panel; var contentControl = parentElement as ContentControl; if (panel != null) { var i = panel.Children.IndexOf(part); GameSideNodeContainer.GetNode(panel.Children).Remove(part, new NodeIndex(i)); } else if (contentControl != null) { if (contentControl.Content != null) { throw new InvalidOperationException($"The control corresponding to the given {nameof(parentId)} is a ContentControl that already has a Content."); } GameSideNodeContainer.GetNode(contentControl)[nameof(contentControl.Content)].Update(null); } } })); }
/// <inheritdoc /> public override Task AddPart([NotNull] UIHierarchyItemViewModel parent, UIElement assetSidePart) { EnsureAssetAccess(); var gameSidePart = ClonePartForGameSide(parent.Asset.Asset, assetSidePart); return(InvokeAsync(() => { Logger.Debug($"Adding element {assetSidePart.Id} to game-side scene"); var parentId = (parent as UIElementViewModel)?.Id; if (parentId == null) { RootElements[assetSidePart.Id] = gameSidePart; } else { var parentElement = (UIElement)FindPart(parentId.Value); if (parentElement == null) { throw new InvalidOperationException($"The given {nameof(parentId)} does not correspond to any existing part."); } var panel = parentElement as Panel; var contentControl = parentElement as ContentControl; if (panel != null) { GameSideNodeContainer.GetNode(panel.Children).Add(gameSidePart); } else if (contentControl != null) { if (contentControl.Content != null) { throw new InvalidOperationException($"The control corresponding to the given {nameof(parentId)} is a ContentControl that already has a Content."); } GameSideNodeContainer.GetNode(contentControl)[nameof(contentControl.Content)].Update(gameSidePart); } } })); }
protected IEnumerable <TAssetPart> ClonePartsForGameSide([NotNull] AssetCompositeHierarchy <TAssetPartDesign, TAssetPart> asset, [NotNull] IEnumerable <TAssetPart> parts) { var flags = SubHierarchyCloneFlags.RemoveOverrides; var sourceContainer = Asset.PropertyGraph.Container.NodeContainer; var targetContainer = GameSideNodeContainer; var clone = AssetCompositeHierarchyPropertyGraph <TAssetPartDesign, TAssetPart> .CloneSubHierarchies(sourceContainer, targetContainer, asset, parts.Select(p => p.Id), flags, out _); // Collect external references after cloning, we need to fix them up! var rootNode = GameSideNodeContainer.GetOrCreateNode(clone); var definition = AssetQuantumRegistry.GetDefinition(asset.GetType()); var unresolvedReferences = ExternalReferenceCollector.GetExternalReferenceAccessors(definition, rootNode); // Retrieve all available game-side identifiable objects, so we can try to resolve external references with items from this collection. var identifiableObjects = CollectIdentifiableObjects(); foreach (var reference in unresolvedReferences) { if (identifiableObjects.TryGetValue(reference.Key.Id, out var realObject)) { // Target object found, let's update the reference with the real game-side object. foreach (var accessor in reference.Value) { accessor.UpdateValue(realObject); } } else { // Target object not found, let's clear the reference since the currently set object could be asset-side, or a temporary proxy, etc. foreach (var accessor in reference.Value) { accessor.UpdateValue(null); } } } return(clone.RootParts); }