/// <summary> /// Tries to parse an asset reference in the format "GUID:Location". /// </summary> /// <param name="referenceType"></param> /// <param name="assetReferenceText">The asset reference.</param> /// <param name="assetReference">The reference.</param> /// <returns><c>true</c> if parsing was successful, <c>false</c> otherwise.</returns> public static bool TryParse(Type referenceType, string assetReferenceText, out AssetReference assetReference) { if (referenceType == null) { throw new ArgumentNullException("referenceType"); } if (assetReferenceText == null) { throw new ArgumentNullException("assetReferenceText"); } assetReference = null; Guid guid; UFile location; Guid referenceId; if (!TryParse(assetReferenceText, out referenceId, out guid, out location)) { return(false); } assetReference = New(referenceType, guid, location); if (referenceId != Guid.Empty) { IdentifiableHelper.SetId(assetReference, referenceId); } return(true); }
public void TestResolveCollectionBroken() { var memberPath = new MemberPath(); memberPath.Push(MemberSubs); memberPath.Push(ListClassDesc, 0); var referenceElt = new MyClass(); IdentifiableHelper.SetId(referenceElt, Guid.NewGuid()); var reference = new MyClass { Subs = { referenceElt } }; var dual = new MyClass(); dual.Subs.Add(new MyClass()); dual.Subs.Add(new MyClass()); var resolvedPaths = memberPath.Resolve(reference, dual).ToList(); Assert.AreEqual(0, resolvedPaths.Count); memberPath = new MemberPath(); memberPath.Push(MemberSubs); memberPath.Push(ListClassDesc, 1); resolvedPaths = memberPath.Resolve(reference, dual).ToList(); Assert.AreEqual(0, resolvedPaths.Count); }
protected override void TransformObjectAfterRead(ref ObjectContext objectContext) { if (IsSerializingAsReference) { // Transform the deserialized reference into a fake Entity, EntityComponent, etc... // Fake objects will later be fixed later with EntityAnalysis.FixupEntityReferences() if (!objectContext.SerializerContext.IsSerializing) { var entityComponentReference = objectContext.Instance as EntityComponentReference; if (entityComponentReference != null) { var entityReference = new Entity { Id = entityComponentReference.Entity.Id }; var entityComponent = (EntityComponent)Activator.CreateInstance(entityComponentReference.ComponentType); IdentifiableHelper.SetId(entityComponent, entityComponentReference.Id); entityComponent.Entity = entityReference; objectContext.Instance = entityComponent; } else if (objectContext.Instance is EntityReference) { objectContext.Instance = new Entity { Id = ((EntityReference)objectContext.Instance).Id }; } else { base.TransformObjectAfterRead(ref objectContext); } } } }
protected override object ResolveReference(object partReference) { var entityComponentReference = partReference as EntityComponent; if (entityComponentReference != null) { var containingEntity = entityComponentReference.Entity; if (containingEntity == null) { throw new InvalidOperationException("Found a reference to a component which doesn't have any entity"); } var realEntity = (Entity)base.ResolveReference(containingEntity); if (realEntity == null) { return(null); } var componentId = IdentifiableHelper.GetId(entityComponentReference); var realComponent = realEntity.Components.FirstOrDefault(c => IdentifiableHelper.GetId(c) == componentId); return(realComponent); } return(base.ResolveReference(partReference)); }
private static void FixupEntityReferences(object rootToVisit, EntityHierarchyData entityHierarchy) { var entityAnalysisResult = Visit(rootToVisit); // Reverse the list, so that we can still properly update everything // (i.e. if we have a[0], a[1], a[1].Test, we have to do it from back to front to be valid at each step) entityAnalysisResult.EntityReferences.Reverse(); // Updates Entity/EntityComponent references foreach (var entityLink in entityAnalysisResult.EntityReferences) { object obj = null; if (entityLink.EntityComponent != null) { var containingEntity = entityLink.EntityComponent.Entity; if (containingEntity == null) { throw new InvalidOperationException("Found a reference to a component which doesn't have any entity"); } EntityDesign realEntity; if (entityHierarchy.Entities.TryGetValue(containingEntity.Id, out realEntity)) { var componentId = IdentifiableHelper.GetId(entityLink.EntityComponent); obj = realEntity.Entity.Components.FirstOrDefault(c => IdentifiableHelper.GetId(c) == componentId); if (obj == entityLink.EntityComponent) { continue; } } } else { EntityDesign realEntity; if (entityHierarchy.Entities.TryGetValue(entityLink.Entity.Id, out realEntity)) { obj = realEntity.Entity; // If we already have the proper item, let's skip if (obj == entityLink.Entity) { continue; } } } if (obj != null) { // We could find the referenced item, let's use it entityLink.Path.Apply(rootToVisit, MemberPathAction.ValueSet, obj); } else { // Item could not be found, let's null it entityLink.Path.Apply(rootToVisit, MemberPathAction.ValueClear, null); } } }
public EntityComponentReference(EntityComponent entityComponent) { this.Entity = new EntityReference() { Id = entityComponent.Entity.Id }; this.Id = IdentifiableHelper.GetId(entityComponent); this.Value = entityComponent; }
public void FillFromPart(object assetPart) { var component = (EntityComponent)assetPart; Entity = new EntityReference { Id = component.Entity.Id }; Id = IdentifiableHelper.GetId(component); }
private static ModelMaterial AttachId(ModelMaterial modelMaterial) { // Compute an id for the list item based on the name of the material var materialNameKey = modelMaterial.Name; var modelMaterialId = ObjectId.FromBytes(Encoding.UTF8.GetBytes(materialNameKey)).ToGuid(); IdentifiableHelper.SetId(modelMaterial, modelMaterialId); return(modelMaterial); }
public object GenerateProxyPart(Type partType) { var component = (EntityComponent)Activator.CreateInstance(partType); component.Entity = new Entity { Id = Entity.Id }; IdentifiableHelper.SetId(component, Id); return(component); }
public void TestHash() { var obj1 = new TestAssetClonerObject { Name = "Test1", SubObject = new TestAssetClonerObject() { Name = "Test2" }, ObjectWithAttachedReference = new TestObjectReference() }; // Create a fake reference to make sure that the attached reference will not be serialized var attachedReference = AttachedReferenceManager.GetOrCreateAttachedReference(obj1.ObjectWithAttachedReference); attachedReference.Url = "just_for_test"; attachedReference.Id = Guid.NewGuid(); // Setup some proper id on objects so serialization is stable IdentifiableHelper.SetId(obj1, new Guid("EC86143E-896F-45C5-9A4D-627317D22955")); IdentifiableHelper.SetId(obj1.SubObject, new Guid("34E160CD-1D94-468E-8BFD-F82FF96013FC")); var obj2 = (TestAssetClonerObject)AssetCloner.Clone(obj1); var hash1 = AssetHash.Compute(obj1); var hash2 = AssetHash.Compute(obj2); Assert.AreEqual(hash1, hash2); obj1.Name = "Yes"; var hash11 = AssetHash.Compute(obj1); Assert.AreNotEqual(hash11, hash2); obj1.Name = "Test1"; var hash12 = AssetHash.Compute(obj1); Assert.AreEqual(hash12, hash2); // Test the same with overrides var objDesc = TypeDescriptorFactory.Default.Find(typeof(TestAssetClonerObject)); var memberDesc = objDesc.Members.First(t => t.Name == "Name"); obj1.SetOverride(memberDesc, OverrideType.New); obj1.SubObject.SetOverride(memberDesc, OverrideType.Sealed); obj2 = (TestAssetClonerObject)AssetCloner.Clone(obj1); var hash1WithOverrides = AssetHash.Compute(obj1); var hash2WithOverrides = AssetHash.Compute(obj2); Assert.AreNotEqual(hash1, hash1WithOverrides); Assert.AreNotEqual(hash2, hash2WithOverrides); Assert.AreEqual(hash1WithOverrides, hash2WithOverrides); }
private static void PrepareMembersCallback(SharpYaml.Serialization.Descriptors.ObjectDescriptor objDesc, List <IMemberDescriptor> memberDescriptors) { var type = objDesc.Type; if (IdentifiableHelper.IsIdentifiable(type) && !typeof(IIdentifiable).IsAssignableFrom(type)) { memberDescriptors.Add(CustomDynamicMemberDescriptor); } // Call custom callbacks to prepare members PrepareMembersEvent?.Invoke(objDesc, memberDescriptors); }
public override string ConvertTo(ref ObjectContext objectContext) { var attachedReference = AttachedReferenceManager.GetAttachedReference(objectContext.Instance); if (attachedReference == null) { throw new YamlException($"Unable to extract asset reference from object [{objectContext.Instance}]"); } var referenceId = IdentifiableHelper.GetId(objectContext.Instance); return($"{referenceId}/{attachedReference.Id}:{attachedReference.Url}"); }
private static void PrepareMembersCallback(SharpYaml.Serialization.Descriptors.ObjectDescriptor objDesc, List <IMemberDescriptor> memberDescriptors) { var type = objDesc.Type; // Early exit if we don't need to add a unique identifier to a type if (!IdentifiableHelper.IsIdentifiable(type) || typeof(IIdentifiable).IsAssignableFrom(type)) { return; } // Otherwise we can add it memberDescriptors.Add(CustomDynamicMemberDescriptor); }
public override object ConvertFrom(ref ObjectContext objectContext, Scalar fromScalar) { Guid id; if (!Guid.TryParse(fromScalar.Value, out id)) { throw new YamlException(fromScalar.Start, fromScalar.End, $"Unable to parse id [{fromScalar.Value}]"); } var materialNull = new MaterialNull(); IdentifiableHelper.SetId(materialNull, id); return(materialNull); }
public void TestResolveComplex() { var memberPath = new MemberPath(); memberPath.Push(MemberSubs); memberPath.Push(ListClassDesc, 1); memberPath.Push(MemberSub); memberPath.Push(MemberValue); var id = Guid.NewGuid(); var referenceElt = new MyClass { Sub = new MyClass { Value = 1 } }; var dualElt1 = new MyClass { Sub = new MyClass { Value = 2 } }; var dualElt2 = new MyClass { Sub = new MyClass { Value = 3 } }; var brokenDual = new MyClass(); IdentifiableHelper.SetId(referenceElt, id); IdentifiableHelper.SetId(dualElt1, id); IdentifiableHelper.SetId(dualElt2, id); IdentifiableHelper.SetId(brokenDual, id); var reference = new MyClass { Subs = { new MyClass(), referenceElt } }; var dual = new MyClass { Subs = { new MyClass(), new MyClass(), dualElt1, brokenDual, new MyClass(), dualElt2 } }; var resolvedPaths = memberPath.Resolve(reference, dual).ToList(); Assert.AreEqual(2, resolvedPaths.Count); object value; Assert.IsTrue(resolvedPaths[0].TryGetValue(dual, out value)); Assert.AreEqual(dual.Subs[2].Sub.Value, value); Assert.IsTrue(resolvedPaths[1].TryGetValue(dual, out value)); Assert.AreEqual(dual.Subs[5].Sub.Value, value); }
public override object ConvertFrom(ref ObjectContext context, Scalar fromScalar) { AssetReference assetReference; Guid referenceId; if (!AssetReference.TryParse(fromScalar.Value, out assetReference, out referenceId)) { throw new YamlException(fromScalar.Start, fromScalar.End, "Unable to decode asset reference [{0}]. Expecting format GUID:LOCATION".ToFormat(fromScalar.Value)); } if (referenceId != Guid.Empty) { IdentifiableHelper.SetId(assetReference, referenceId); } return(assetReference); }
private static AssetItem ImportSkeleton(List <AssetItem> assetReferences, UFile assetSource, UFile localPath, EntityInfo entityInfo) { var asset = new SkeletonAsset { Source = assetSource }; if (entityInfo.Nodes != null) { for (int i = 0; i < entityInfo.Nodes.Count; i++) { var node = entityInfo.Nodes[i]; var nodeInfo = new NodeInformation(node.Name, node.Depth, node.Preserve); // Try to keep identifier id consistent // TODO: We might remove this as we don't expect Skeleton asset to be inherited, but they could int sameNameAndDepthCount = 0; for (int j = 0; j < i; j++) { var againstNode = entityInfo.Nodes[i]; // If we found a node with the same name and depth, we use a increment a counter if (againstNode.Name == node.Name && againstNode.Depth == node.Depth) { sameNameAndDepthCount++; } } var nodeNameKey = nodeInfo.Name + nodeInfo.Depth + ((sameNameAndDepthCount > 0) ? "_" + sameNameAndDepthCount : string.Empty); var nodeId = ObjectId.FromBytes(Encoding.UTF8.GetBytes(nodeNameKey)).ToGuid(); IdentifiableHelper.SetId(nodeInfo, nodeId); asset.Nodes.Add(nodeInfo); } } if (entityInfo.AnimationNodes != null && entityInfo.AnimationNodes.Count > 0) { asset.PreserveNodes(entityInfo.AnimationNodes); } var skeletonUrl = new UFile(localPath.GetFileName() + " Skeleton"); var assetItem = new AssetItem(skeletonUrl, asset); assetReferences.Add(assetItem); return(assetItem); }
public override object ConvertFrom(ref ObjectContext context, Scalar fromScalar) { AssetId guid; UFile location; Guid referenceId; if (!AssetReference.TryParse(fromScalar.Value, out guid, out location, out referenceId)) { throw new YamlException(fromScalar.Start, fromScalar.End, "Unable to decode asset reference [{0}]. Expecting format GUID:LOCATION".ToFormat(fromScalar.Value)); } var instance = AttachedReferenceManager.CreateProxyObject(context.Descriptor.Type, guid, location); if (referenceId != Guid.Empty) { IdentifiableHelper.SetId(instance, referenceId); } return(instance); }
/// <summary> /// Tries to parse a package reference in the format {guid:location}. /// </summary> /// <param name="packageReferenceAsText">The package reference as text.</param> /// <param name="packageReference">The package reference.</param> /// <returns><c>true</c> if the package reference is a valid reference, <c>false</c> otherwise.</returns> public static bool TryParse(string packageReferenceAsText, out PackageReference packageReference) { Guid id; UFile location; packageReference = null; Guid referenceId; if (AssetReference.TryParse(packageReferenceAsText, out referenceId, out id, out location)) { packageReference = new PackageReference(id, location); if (referenceId != Guid.Empty) { IdentifiableHelper.SetId(packageReference, referenceId); } return(true); } return(false); }
public override string ReadMemberName(ref ObjectContext objectContext, string memberName, out bool skipMember) { var newMemberName = memberName.Trim(Override.PostFixSealed, Override.PostFixNew); var objectType = objectContext.Instance.GetType(); if (newMemberName.Length != memberName.Length) { var overrideType = OverrideType.Base; if (memberName.Contains(Override.PostFixNewSealed) || memberName.EndsWith(Override.PostFixNewSealedAlt)) { overrideType = OverrideType.New | OverrideType.Sealed; } else if (memberName.EndsWith(Override.PostFixNew)) { overrideType = OverrideType.New; } else if (memberName.EndsWith(Override.PostFixSealed)) { overrideType = OverrideType.Sealed; } if (overrideType != OverrideType.Base) { if (cachedDescriptor == null || cachedDescriptor.Type != objectType) { cachedDescriptor = typeDescriptorFactory.Find(objectType); } var memberDescriptor = cachedDescriptor[newMemberName]; objectContext.Instance.SetOverride(memberDescriptor, overrideType); } } var resultMemberName = base.ReadMemberName(ref objectContext, newMemberName, out skipMember); // If ~Id was not found as a member, don't generate an error, as we may have switched an object // to NonIdentifiable but we don't want to write an upgrader for this if (!IdentifiableHelper.IsIdentifiable(objectType) && memberName == IdentifiableHelper.YamlSpecialId) { skipMember = true; } return(resultMemberName); }
/// <inheritdoc/> protected override void TransformObjectAfterRead(ref ObjectContext objectContext) { if (!AreCollectionItemsIdentifiable(ref objectContext)) { base.TransformObjectAfterRead(ref objectContext); return; } var info = (InstanceInfo)objectContext.Properties[InstanceInfoKey]; // This is to be backward compatible with previous serialization. We fetch ids from the ~Id member of each item if (info.Instance != null) { ICollection <ItemId> deletedItems; objectContext.Properties.TryGetValue(DeletedItemsKey, out deletedItems); TransformAfterDeserialization((IDictionary)objectContext.Instance, info.Descriptor, info.Instance, deletedItems); } objectContext.Instance = info.Instance; var enumerable = objectContext.Instance as IEnumerable; if (enumerable != null) { var ids = CollectionItemIdHelper.GetCollectionItemIds(objectContext.Instance); var descriptor = (DictionaryDescriptor)info.Descriptor; foreach (var item in descriptor.GetEnumerator(objectContext.Instance)) { ItemId id; if (ids.TryGet(item.Key, out id) && id != ItemId.Empty) { continue; } var guid = item.Value != null?IdentifiableHelper.GetId(item.Value) : Guid.NewGuid(); ids[item.Key] = guid != Guid.Empty ? new ItemId(guid.ToByteArray()) : ItemId.New(); } } base.TransformObjectAfterRead(ref objectContext); }
private void DiffValue(Diff3Node diff3, ref NodeDescription baseNodeDesc, ref NodeDescription asset1NodeDesc, ref NodeDescription asset2NodeDesc) { var node = diff3.Asset1Node ?? diff3.Asset2Node ?? diff3.BaseNode; var dataVisitMember = node as DataVisitMember; if (dataVisitMember != null) { var diffMember = dataVisitMember.MemberDescriptor.GetCustomAttributes <DiffMemberAttribute>(true).FirstOrDefault(); if (diffMember != null) { if (diffMember.PreferredChange.HasValue) { diff3.ChangeType = diffMember.PreferredChange.Value; } diff3.Weight = diffMember.Weight; } } var instanceType = asset1NodeDesc.Instance?.GetType() ?? asset2NodeDesc.Instance?.GetType(); object baseInstance = baseNodeDesc.Instance; object asset1Instance = asset1NodeDesc.Instance; object asset2Instance = asset2NodeDesc.Instance; // If this is an identifiable type (but we are for example not visiting its member), compare only the Ids instead if (UseOverrideMode && instanceType != null && IdentifiableHelper.IsIdentifiable(instanceType)) { baseInstance = IdentifiableHelper.GetId(baseInstance); asset1Instance = IdentifiableHelper.GetId(asset1Instance); asset2Instance = IdentifiableHelper.GetId(asset2Instance); } var baseAsset1Equals = Equals(baseInstance, asset1Instance); var baseAsset2Equals = Equals(baseInstance, asset2Instance); var asset1And2Equals = Equals(asset1Instance, asset2Instance); diff3.ChangeType = baseAsset1Equals && baseAsset2Equals ? Diff3ChangeType.None : baseAsset2Equals ? Diff3ChangeType.MergeFromAsset1 : baseAsset1Equals ? Diff3ChangeType.MergeFromAsset2 : asset1And2Equals ? Diff3ChangeType.MergeFromAsset1And2 : Diff3ChangeType.Conflict; }
public override void Serialize(ref MaterialNull obj, ArchiveMode mode, SerializationStream stream) { if (stream.Context.SerializerSelector.HasProfile("AssetClone")) { // At design time, when performing a clone, we keep the associated id of this instance. if (mode == ArchiveMode.Serialize) { var id = IdentifiableHelper.GetId(obj); stream.Write(id); } else { var id = stream.Read <Guid>(); obj = new MaterialNull(); IdentifiableHelper.SetId(obj, id); } } else { // For runtime serialization, a MaterialNull becomes null obj = null; } }
/// <inheritdoc/> protected override void TransformObjectAfterRead(ref ObjectContext objectContext) { InstanceInfo info; if (!objectContext.Properties.TryGetValue(InstanceInfoKey, out info)) { base.TransformObjectAfterRead(ref objectContext); if (AreCollectionItemsIdentifiable(ref objectContext)) { // This is to be backward compatible with previous serialization. We fetch ids from the ~Id member of each item var enumerable = objectContext.Instance as IEnumerable; if (enumerable != null) { var ids = CollectionItemIdHelper.GetCollectionItemIds(objectContext.Instance); var i = 0; foreach (var item in enumerable) { var id = item != null?IdentifiableHelper.GetId(item) : Guid.NewGuid(); ids[i] = id != Guid.Empty ? new ItemId(id.ToByteArray()) : ItemId.New(); ++i; } } } return; } var instance = info.Instance ?? objectContext.SerializerContext.ObjectFactory.Create(info.Descriptor.Type); ICollection <ItemId> deletedItems; objectContext.Properties.TryGetValue(DeletedItemsKey, out deletedItems); TransformAfterDeserialization((IDictionary)objectContext.Instance, info.Descriptor, instance, deletedItems); objectContext.Instance = instance; base.TransformObjectAfterRead(ref objectContext); }
public override object ConvertFrom(ref ObjectContext context, Scalar fromScalar) { Guid guid; UFile location; Guid referenceId; if (!AssetReference.TryParse(fromScalar.Value, out referenceId, out guid, out location)) { throw new YamlException(fromScalar.Start, fromScalar.End, "Unable to decode asset reference [{0}]. Expecting format GUID:LOCATION".ToFormat(fromScalar.Value)); } var instance = AttachedReferenceManager.CreateSerializableVersion(context.Descriptor.Type, guid, location); // If the referenceId is empty, force its creation, else attach it to the reference if (referenceId == Guid.Empty) { IdentifiableHelper.GetId(instance); } else { IdentifiableHelper.SetId(instance, referenceId); } return(instance); }
public void TestIdentifierHelper() { // Has IdentifierHelper is using ShadowObject, we will test it here ShadowObject.Enable = true; var obj = new object(); var id = IdentifiableHelper.GetId(obj); Assert.AreNotEqual(Guid.Empty, id); var id1 = IdentifiableHelper.GetId(obj); Assert.AreEqual(id, id1); // We should not get an id for a collection var idCollection = IdentifiableHelper.GetId(new List <object>()); Assert.AreEqual(Guid.Empty, idCollection); // We should not get an id for a dictionary var idDict = IdentifiableHelper.GetId(new MyDictionary()); Assert.AreEqual(Guid.Empty, idDict); }
public override void Set(object thisObject, object value) { IdentifiableHelper.SetId(thisObject, (Guid)value); }
public override object Get(object thisObject) { return(IdentifiableHelper.GetId(thisObject)); }
private void DiffCollection(Diff3Node diff3, DataVisitNode baseNode, DataVisitNode asset1Node, DataVisitNode asset2Node) { diff3.Type = Diff3NodeType.Collection; var baseItems = baseNode != null ? baseNode.Items ?? EmptyNodes : EmptyNodes; var asset1Items = asset1Node != null ? asset1Node.Items ?? EmptyNodes : EmptyNodes; var asset2Items = asset2Node != null ? asset2Node.Items ?? EmptyNodes : EmptyNodes; var itemEqualityComparer = equalityComparer; var node = diff3.Asset1Node ?? diff3.Asset2Node ?? diff3.BaseNode; IEnumerable <Diff3Change> changes; bool recurseDiff = false; // Find an item in any of the list var firstItem = baseItems.FirstOrDefault(item => item.Instance != null) ?? asset1Items.FirstOrDefault(item => item.Instance != null) ?? asset2Items.FirstOrDefault(item => item.Instance != null); // For now, in the context of UseOverrideMode and we have identifiers per item, use DiffCollectionByIds instead if (UseOverrideMode && firstItem != null) { if (IdentifiableHelper.IsIdentifiable(firstItem.Instance.GetType())) { DiffCollectionByIds(diff3, baseNode, asset1Node, asset2Node); return; } else if (firstItem.Instance is Guid) { DiffCollectionByGuids(diff3, baseNode, asset1Node, asset2Node); return; } } // If we have a DiffUseAsset1Attribute, list of Asset1Node becomes authoritative. var dataVisitMember = node as DataVisitMember; var diffMemberAttribute = dataVisitMember != null?dataVisitMember.MemberDescriptor.GetCustomAttributes <DiffMemberAttribute>(true).FirstOrDefault() : null; if (diffMemberAttribute != null && diffMemberAttribute.PreferredChange.HasValue) { diff3.Weight = diffMemberAttribute.Weight; } if (diffMemberAttribute != null && diffMemberAttribute.PreferredChange.HasValue) { var diffChange = diffMemberAttribute.PreferredChange.Value == Diff3ChangeType.MergeFromAsset2 ? new Diff3Change { ChangeType = SharpDiff.Diff3ChangeType.MergeFrom2, From2 = new Span(0, asset2Items.Count - 1) } : new Diff3Change { ChangeType = SharpDiff.Diff3ChangeType.MergeFrom1, From1 = new Span(0, asset1Items.Count - 1) }; changes = new[] { diffChange }; // TODO: Try to merge back data of matching nodes } else if (firstItem != null && typeof(IDiffKey).IsAssignableFrom(firstItem.InstanceType)) { // If item implement IDataDiffKey, we will use that as equality key changes = Diff3.Compare( baseItems.Select(x => ((IDiffKey)x.Instance).GetDiffKey()).ToList(), asset1Items.Select(x => ((IDiffKey)x.Instance).GetDiffKey()).ToList(), asset2Items.Select(x => ((IDiffKey)x.Instance).GetDiffKey()).ToList()); recurseDiff = true; } else { // Otherwise, do a full node comparison itemEqualityComparer.Reset(); changes = Diff3.Compare(baseItems, asset1Items, asset2Items, itemEqualityComparer); } foreach (var change in changes) { switch (change.ChangeType) { case SharpDiff.Diff3ChangeType.Equal: for (int i = 0; i < change.Base.Length; i++) { var diff3Node = recurseDiff ? DiffNode(baseItems[change.Base.From + i], asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) : new Diff3Node(baseItems[change.Base.From + i], asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.None }; AddItem(diff3, diff3Node, change.From1.From != 0); } break; case SharpDiff.Diff3ChangeType.MergeFrom1: for (int i = 0; i < change.From1.Length; i++) { var diff3Node = new Diff3Node(null, asset1Items[change.From1.From + i], null) { ChangeType = Diff3ChangeType.MergeFromAsset1 }; AddItem(diff3, diff3Node, change.From1.From != 0); } break; case SharpDiff.Diff3ChangeType.MergeFrom2: for (int i = 0; i < change.From2.Length; i++) { var diff3Node = new Diff3Node(null, null, asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.MergeFromAsset2 }; AddItem(diff3, diff3Node, true); } break; case SharpDiff.Diff3ChangeType.MergeFrom1And2: for (int i = 0; i < change.From2.Length; i++) { var diff3Node = recurseDiff ? DiffNode(null, asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) : new Diff3Node(null, asset1Items[change.From1.From + i], asset2Items[change.From2.From + i]) { ChangeType = Diff3ChangeType.MergeFromAsset1And2 }; AddItem(diff3, diff3Node, change.From1.From != 0); } break; case SharpDiff.Diff3ChangeType.Conflict: int baseIndex = change.Base.IsValid ? change.Base.From : -1; int from1Index = change.From1.IsValid ? change.From1.From : -1; int from2Index = change.From2.IsValid ? change.From2.From : -1; // If there are changes only from 1 or 2 or base.Length == list1.Length == list2.Length, then try to make a diff per item // else output the conflict as a full conflict bool tryResolveConflict = false; if (baseIndex >= 0) { if (from1Index >= 0 && from2Index >= 0) { if ((change.Base.Length == change.From1.Length && change.Base.Length == change.From2.Length) || (change.From1.Length == change.From2.Length)) { tryResolveConflict = true; } } else if (from1Index >= 0) { tryResolveConflict = change.Base.Length == change.From1.Length; } else if (from2Index >= 0) { tryResolveConflict = change.Base.Length == change.From2.Length; } else { tryResolveConflict = true; } } // Iterate on items while ((baseIndex >= 0 && baseItems.Count > 0) || (from1Index >= 0 && asset1Items.Count > 0) || (from2Index >= 0 && asset2Items.Count > 0)) { var baseItem = GetSafeFromList(baseItems, ref baseIndex, ref change.Base); var asset1Item = GetSafeFromList(asset1Items, ref from1Index, ref change.From1); var asset2Item = GetSafeFromList(asset2Items, ref from2Index, ref change.From2); var diff3Node = tryResolveConflict || recurseDiff? DiffNode(baseItem, asset1Item, asset2Item) : new Diff3Node(baseItem, asset1Item, asset2Item) { ChangeType = Diff3ChangeType.Conflict }; AddItem(diff3, diff3Node, true); } break; } } // Any missing item? (we can detect this only at the end) var newItemCount = diff3.Items != null ? diff3.Items.Count : 0; if (asset1Items.Count != newItemCount) { diff3.ChangeType = Diff3ChangeType.Children; } }
private void FixReferencesToEntities(EntityDesign newEntityDesign, Dictionary <GroupPartKey, Guid> mapBaseIdToNewId) { var newEntity = newEntityDesign.Entity; // We need to visit all references to entities/components in order to fix references // (e.g entities removed, entity added from base referencing an entity from base that we have to redirect to the new child entity...) // Suppose for example that: // // base newBase newAsset // EA EA EA' // EB EB EB' // EC EC EC' // ED (+link to EA via script or whather) ED' + link to EA' (we need to change from EA to EA') // // So in the example above, when merging ED into newAsset, all references to entities declared in newBase are not automatically // remapped to the new entities in newAsset. This is the purpose of this whole method var entityVisitor = new SingleLevelVisitor(typeof(Entity), true); var entityComponentVisitor = new SingleLevelVisitor(typeof(EntityComponent), true); DataVisitNodeBuilder.Run(TypeDescriptorFactory.Default, newEntity, new List <IDataCustomVisitor>() { entityVisitor, entityComponentVisitor }); // Fix Entity and EntityComponent references foreach (var idNodes in entityVisitor.References.Concat(entityComponentVisitor.References)) { var id = idNodes.Key; var nodes = idNodes.Value; // If entity id is not in the current list, it is more likely that it was a link to a base entity if (!newAsset.Hierarchy.Parts.ContainsKey(id)) { var groupKey = new GroupPartKey(newEntityDesign.BasePartInstanceId, id); // We are trying to remap the base id to the new id from known entities from newAsset Guid newId; if (mapBaseIdToNewId.TryGetValue(groupKey, out newId)) { var linkedEntity = newAsset.Hierarchy.Parts[newId].Entity; foreach (var node in nodes) { var entityComponent = node.Instance as EntityComponent; if (entityComponent != null) { var entityComponentId = IdentifiableHelper.GetId(entityComponent); // TODO: In case of a DataVisitMember node, we need to set an OverrideType to New if we are actually removing a base value var newEntityComponent = (EntityComponent)linkedEntity.Components.FirstOrDefault(t => IdentifiableHelper.GetId(t) == entityComponentId); node.SetValue(newEntityComponent); } else { // TODO: In case of a DataVisitMember node, we need to set an OverrideType to New if we are actually removing a base value // Else the node applies to an entity node.SetValue(linkedEntity); } } } else { // TODO: In case of a DataVisitMember node, we need to set an OverrideType to New if we are actually removing a base value // If we are trying to link to an entity/component that was removed, we need to remove it foreach (var node in nodes) { node.RemoveValue(); } } } } }