From 71ff0c6362bad2fc436166216c78b3bb031e1aaa Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 30 Mar 2026 10:13:25 +0200 Subject: [PATCH] Fix root linkage for prefab instances copy pasted in Editor #3945 --- Source/Engine/Level/Actor.cpp | 38 +++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 70a71eee2..fe896d1ab 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -1185,11 +1185,15 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) } else if (!parent && parentId.IsValid()) { + // Skip warning if object was mapped to empty id (intentionally ignored) Guid tmpId; - if (_prefabObjectID.IsValid()) - LOG(Warning, "Missing parent actor {0} for \'{1}\', prefab object {2}", parentId, ToString(), _prefabObjectID); - else if (!modifier->IdsMapping.TryGet(parentId, tmpId) || tmpId.IsValid()) // Skip warning if object was mapped to empty id (intentionally ignored) - LOG(Warning, "Missing parent actor {0} for \'{1}\'", parentId, ToString()); + if (!modifier->IdsMapping.TryGet(parentId, tmpId) || tmpId.IsValid()) + { + if (_prefabObjectID.IsValid()) + LOG(Warning, "Missing parent actor {0} for \'{1}\', prefab object {2}", parentId, ToString(), _prefabObjectID); + else + LOG(Warning, "Missing parent actor {0} for \'{1}\'", parentId, ToString()); + } } } } @@ -1862,6 +1866,32 @@ bool Actor::FromBytes(const Span& data, Array& output, ISerializeM sceneObjects->Resize(objectsCount); SceneObjectsFactory::Context context(modifier); + // Fix root linkage for prefab instances (eg. when user duplicates a sub-prefab actor but not a root one) + SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, document, modifier); + SceneObjectsFactory::SetupPrefabInstances(context, prefabSyncData); + for (auto& instance : context.Instances) + { + Guid prefabObjectId; + if (!JsonTools::GetGuidIfValid(prefabObjectId, document[instance.RootIndex], "PrefabObjectID")) + continue; + + // Get the original object from prefab + SceneObject* prefabObject = instance.Prefab->GetDefaultInstance(prefabObjectId); + if (prefabObject && prefabObject->GetParent()) + { + // Add empty mapping to parent object in prefab to prevent linking to it + auto prefabObjectParentId = prefabObject->GetParent()->GetPrefabObjectID(); + instance.IdsMapping[prefabObjectParentId] = Guid::Empty; + modifier->IdsMapping[prefabObjectParentId] = Guid::Empty; + Guid nestedPrefabId, nestedPrefabObjectId; + if (instance.Prefab->GetNestedObject(prefabObjectParentId, nestedPrefabId, nestedPrefabObjectId)) + { + instance.IdsMapping[nestedPrefabObjectId] = Guid::Empty; + modifier->IdsMapping[nestedPrefabObjectId] = Guid::Empty; + } + } + } + // Deserialize objects Scripting::ObjectsLookupIdMapping.Set(&modifier->IdsMapping); for (int32 i = 0; i < objectsCount; i++)