Exemplo n.º 1
0
        /// <inheritdoc />
        public override bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath)
        {
            var asset     = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)targetRootObject;
            var hierarchy = data as AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart>;

            if (hierarchy == null)
            {
                return(false);
            }

            // Create a temporary asset to host the hierarchy to paste, so we have a property graph to manipulate it.
            var tempAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(asset.GetType());

            tempAsset.Hierarchy = hierarchy;
            // Use temporary containers so that any created nodes are discarded after the processing.
            var tempNodeContainer = new AssetNodeContainer {
                NodeBuilder = { NodeFactory = new AssetNodeFactory() }
            };
            var definition = AssetQuantumRegistry.GetDefinition(asset.GetType());
            var rootNode   = tempNodeContainer.GetOrCreateNode(tempAsset);

            // If different asset or if at least one part already exists, create a custom clone.
            if (asset.Id != sourceId || hierarchy.Parts.Values.Any(part => asset.ContainsPart(part.Part.Id)))
            {
                // Clone again to create new ids for any IIdentifiable, but keep references to external object intact.
                var cloneExternalReferences = ExternalReferenceCollector.GetExternalReferences(definition, rootNode);
                hierarchy = AssetCloner.Clone(hierarchy, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, cloneExternalReferences, out var idRemapping);
                // Remap indices of parts in Hierarchy.Part
                AssetCloningHelper.RemapIdentifiablePaths(overrides, idRemapping, basePath);
                // Make new base instances ids in case the part are inherited.
                AssetPartsAnalysis.GenerateNewBaseInstanceIds(hierarchy);
                // Update the temporary asset with this cloned hierarchy.
                rootNode[nameof(AssetCompositeHierarchy <TAssetPartDesign, TAssetPart> .Hierarchy)].Update(hierarchy);
            }

            // Collect all referenceable objects from the target asset (where we're pasting)
            var targetPropertyGraph  = graphContainer.TryGetGraph(asset.Id);
            var referenceableObjects = IdentifiableObjectCollector.Collect(targetPropertyGraph.Definition, targetPropertyGraph.RootNode);

            // Replace references in the hierarchy being pasted by the real objects from the target asset.
            var externalReferences = new HashSet <Guid>(ExternalReferenceCollector.GetExternalReferences(definition, rootNode).Select(x => x.Id));
            var visitor            = new ObjectReferencePathGenerator(definition)
            {
                ShouldOutputReference = x => externalReferences.Contains(x)
            };

            visitor.Visit(rootNode);

            FixupObjectReferences.FixupReferences(tempAsset, visitor.Result, referenceableObjects, true);

            data = hierarchy;

            return(true);
        }
Exemplo n.º 2
0
        /// <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);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Deletes the given parts and all its children, recursively, and clear all object references to it.
        /// </summary>
        /// <param name="partDesigns">The parts to delete.</param>
        /// <param name="deletedPartsMapping">A mapping of the base information (base part id, instance id) of the deleted parts that have a base.</param>
        public void DeleteParts([NotNull] IEnumerable <TAssetPartDesign> partDesigns, [NotNull] out HashSet <Tuple <Guid, Guid> > deletedPartsMapping)
        {
            if (partDesigns == null)
            {
                throw new ArgumentNullException(nameof(partDesigns));
            }
            var partsToDelete     = new Stack <TAssetPartDesign>(partDesigns);
            var referencesToClear = new HashSet <Guid>();

            deletedPartsMapping = new HashSet <Tuple <Guid, Guid> >();
            while (partsToDelete.Count > 0)
            {
                // We need to remove children first to keep consistency in our data
                var partToDelete = partsToDelete.Peek();
                var children     = Asset.EnumerateChildPartDesigns(partToDelete, Asset.Hierarchy, false).ToList();
                if (children.Count > 0)
                {
                    // Enqueue children if there is any, and re-process the stack
                    children.ForEach(x => partsToDelete.Push(x));
                    continue;
                }
                // No children to process, we can safely remove the current part from the stack
                partToDelete = partsToDelete.Pop();
                // First remove all references to the part we are deleting
                // Note: we must do this first so instances of this base will be able to properly make the connection with the base part being cleared
                var containedIdentifiables = IdentifiableObjectCollector.Collect(Definition, Container.NodeContainer.GetNode(partToDelete.Part));
                containedIdentifiables.Keys.ForEach(x => referencesToClear.Add(x));
                referencesToClear.Add(partToDelete.Part.Id);
                // Then actually remove the part from the hierarchy
                RemovePartFromAsset(partToDelete);
                // Keep track of deleted part instances
                if (partToDelete.Base != null)
                {
                    deletedPartsMapping.Add(Tuple.Create(partToDelete.Base.BasePartId, partToDelete.Base.InstanceId));
                }
            }
            TrackDeletedInstanceParts(deletedPartsMapping);
            ClearReferencesToObjects(referencesToClear);
        }
Exemplo n.º 5
0
        /// <inheritdoc />
        public override bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath)
        {
            if (targetRootObject == null)
            {
                throw new ArgumentNullException(nameof(targetRootObject));
            }
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            var asset = (Asset)targetRootObject;
            var targetPropertyGraph = graphContainer.TryGetGraph(asset.Id);

            // We use a container object in case the data itself is an object reference
            var container = isRootDataObjectReference ? new FixupContainer {
                Data = data
            } : data;
            var rootNode           = targetPropertyGraph.Container.NodeContainer.GetOrCreateNode(container);
            var externalReferences = ExternalReferenceCollector.GetExternalReferences(targetPropertyGraph.Definition, rootNode);

            try
            {
                // Clone to create new ids for any IIdentifiable, except passed external references that will be maintained
                Dictionary <Guid, Guid> idRemapping;
                data = AssetCloner.Clone <object>(data, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, externalReferences, out idRemapping);
            }
            // TODO: have a proper exception type for serialization failure
            catch (Exception)
            {
                // Note: this can fail if the type doesn't have a binary serializer.
                return(false);
            }

            var  targetTypeDescriptor = TypeDescriptorFactory.Default.Find(targetMemberType);
            bool result;

            switch (targetTypeDescriptor.Category)
            {
            case DescriptorCategory.Collection:
                result = ConvertForCollection((CollectionDescriptor)targetTypeDescriptor, ref data);
                break;

            case DescriptorCategory.Dictionary:
                result = ConvertForDictionary((DictionaryDescriptor)targetTypeDescriptor, ref data);
                break;

            case DescriptorCategory.Primitive:
            case DescriptorCategory.Object:
            case DescriptorCategory.NotSupportedObject:
            case DescriptorCategory.Nullable:
                result = ConvertForProperty(targetTypeDescriptor.Type, ref data);
                break;

            case DescriptorCategory.Array:
            case DescriptorCategory.Custom:
                throw new NotSupportedException();

            default:
                throw new ArgumentOutOfRangeException();
            }

            if (!result)
            {
                return(false);
            }

            // Collect all referenceable objects from the target asset (where we're pasting)
            var referenceableObjects = IdentifiableObjectCollector.Collect(targetPropertyGraph.Definition, targetPropertyGraph.RootNode);

            // We use a container object in case the data itself is an object reference
            container = isRootDataObjectReference ? new FixupContainer {
                Data = data
            } : data;
            rootNode = targetPropertyGraph.Container.NodeContainer.GetOrCreateNode(container);

            // Generate YAML paths for the external reference so we can go through the normal deserialization fixup method.
            var externalReferenceIds = new HashSet <Guid>(externalReferences.Select(x => x.Id));
            var visitor = new ObjectReferencePathGenerator(targetPropertyGraph.Definition)
            {
                ShouldOutputReference = x => externalReferenceIds.Contains(x)
            };

            visitor.Visit(rootNode);

            // Fixup external references
            FixupObjectReferences.FixupReferences(container, visitor.Result, referenceableObjects, true);

            data = (container as FixupContainer)?.Data ?? data;
            return(true);
        }