void FHotReloadClassReinstancer::SetupNewClassReinstancing(UClass* InNewClass, UClass* InOldClass) { // Set base class members to valid values ClassToReinstance = InNewClass; DuplicatedClass = InOldClass; OriginalCDO = InOldClass->GetDefaultObject(); bHasReinstanced = false; bSkipGarbageCollection = false; bNeedsReinstancing = true; NewClass = InNewClass; // Collect the original CDO property values SerializeCDOProperties(InOldClass->GetDefaultObject(), OriginalCDOProperties); // Collect the property values of the new CDO SerializeCDOProperties(InNewClass->GetDefaultObject(), ReconstructedCDOProperties); SaveClassFieldMapping(InOldClass); ObjectsThatShouldUseOldStuff.Add(InOldClass); //CDO of REINST_ class can be used as archetype TArray<UClass*> ChildrenOfClass; GetDerivedClasses(InOldClass, ChildrenOfClass); for (auto ClassIt = ChildrenOfClass.CreateConstIterator(); ClassIt; ++ClassIt) { UClass* ChildClass = *ClassIt; UBlueprint* ChildBP = Cast<UBlueprint>(ChildClass->ClassGeneratedBy); if (ChildBP && !ChildBP->HasAnyFlags(RF_BeingRegenerated)) { // If this is a direct child, change the parent and relink so the property chain is valid for reinstancing if (!ChildBP->HasAnyFlags(RF_NeedLoad)) { if (ChildClass->GetSuperClass() == InOldClass) { ReparentChild(ChildBP); } Children.AddUnique(ChildBP); if (ChildBP->ParentClass == InOldClass) { ChildBP->ParentClass = NewClass; } } else { // If this is a child that caused the load of their parent, relink to the REINST class so that we can still serialize in the CDO, but do not add to later processing ReparentChild(ChildClass); } } } // Finally, remove the old class from Root so that it can get GC'd and mark it as CLASS_NewerVersionExists InOldClass->RemoveFromRoot(); InOldClass->ClassFlags |= CLASS_NewerVersionExists; }
void FHotReloadClassReinstancer::RecreateCDOAndSetupOldClassReinstancing(UClass* InOldClass) { // Set base class members to valid values ClassToReinstance = InOldClass; DuplicatedClass = InOldClass; OriginalCDO = InOldClass->GetDefaultObject(); bHasReinstanced = false; bSkipGarbageCollection = false; bNeedsReinstancing = false; NewClass = InOldClass; // The class doesn't change in this case // Collect the original property values SerializeCDOProperties(InOldClass->GetDefaultObject(), OriginalCDOProperties); // Remember all the basic info about the object before we rename it EObjectFlags CDOFlags = OriginalCDO->GetFlags(); UObject* CDOOuter = OriginalCDO->GetOuter(); FName CDOName = OriginalCDO->GetFName(); // Rename original CDO, so we can store this one as OverridenArchetypeForCDO // and create new one with the same name and outer. OriginalCDO->Rename( *MakeUniqueObjectName( GetTransientPackage(), OriginalCDO->GetClass(), *FString::Printf(TEXT("BPGC_ARCH_FOR_CDO_%s"), *InOldClass->GetName()) ).ToString(), GetTransientPackage(), REN_DoNotDirty | REN_DontCreateRedirectors | REN_NonTransactional | REN_SkipGeneratedClasses | REN_ForceNoResetLoaders); // Re-create the CDO, re-running its constructor ReconstructClassDefaultObject(InOldClass, CDOOuter, CDOName, CDOFlags); ReconstructedCDOsMap.Add(OriginalCDO, InOldClass->GetDefaultObject()); // Collect the property values after re-constructing the CDO SerializeCDOProperties(InOldClass->GetDefaultObject(), ReconstructedCDOProperties); // We only want to re-instance the old class if its CDO's values have changed or any of its DSOs' property values have changed if (DefaultPropertiesHaveChanged()) { bNeedsReinstancing = true; SaveClassFieldMapping(InOldClass); TArray<UClass*> ChildrenOfClass; GetDerivedClasses(InOldClass, ChildrenOfClass); for (auto ClassIt = ChildrenOfClass.CreateConstIterator(); ClassIt; ++ClassIt) { UClass* ChildClass = *ClassIt; UBlueprint* ChildBP = Cast<UBlueprint>(ChildClass->ClassGeneratedBy); if (ChildBP && !ChildBP->HasAnyFlags(RF_BeingRegenerated)) { if (!ChildBP->HasAnyFlags(RF_NeedLoad)) { Children.AddUnique(ChildBP); auto BPGC = Cast<UBlueprintGeneratedClass>(ChildBP->GeneratedClass); auto CurrentCDO = BPGC ? BPGC->GetDefaultObject(false) : nullptr; if (CurrentCDO && (OriginalCDO == CurrentCDO->GetArchetype())) { BPGC->OverridenArchetypeForCDO = OriginalCDO; } } } } } }
FBlueprintCompileReinstancer::FBlueprintCompileReinstancer(UClass* InClassToReinstance, bool bIsBytecodeOnly, bool bSkipGC) : ClassToReinstance(InClassToReinstance) , DuplicatedClass(NULL) , OriginalCDO(NULL) , bHasReinstanced(false) , bSkipGarbageCollection(bSkipGC) , ClassToReinstanceDefaultValuesCRC(0) { if( InClassToReinstance != NULL ) { bIsReinstancingSkeleton = FKismetEditorUtilities::IsClassABlueprintSkeleton(ClassToReinstance); SaveClassFieldMapping(InClassToReinstance); // Remember the initial CDO for the class being resinstanced OriginalCDO = ClassToReinstance->GetDefaultObject(); // Duplicate the class we're reinstancing into the transient package. We'll re-class all objects we find to point to this new class GIsDuplicatingClassForReinstancing = true; ClassToReinstance->ClassFlags |= CLASS_NewerVersionExists; const FName RenistanceName = MakeUniqueObjectName(GetTransientPackage(), ClassToReinstance->GetClass(), *FString::Printf(TEXT("REINST_%s"), *ClassToReinstance->GetName())); DuplicatedClass = (UClass*)StaticDuplicateObject(ClassToReinstance, GetTransientPackage(), *RenistanceName.ToString(), ~RF_Transactional); ClassToReinstance->ClassFlags &= ~CLASS_NewerVersionExists; GIsDuplicatingClassForReinstancing = false; auto BPGDuplicatedClass = Cast<UBlueprintGeneratedClass>(DuplicatedClass); auto DuplicatedClassUberGraphFunction = BPGDuplicatedClass ? BPGDuplicatedClass->UberGraphFunction : nullptr; if (DuplicatedClassUberGraphFunction) { DuplicatedClassUberGraphFunction->Bind(); DuplicatedClassUberGraphFunction->StaticLink(true); } // Bind and link the duplicate class, so that it has the proper duplicate property offsets DuplicatedClass->Bind(); DuplicatedClass->StaticLink(true); // Copy over the ComponentNametoDefaultObjectMap, which tells CopyPropertiesForUnrelatedObjects which components are instanced and which aren't GIsDuplicatingClassForReinstancing = true; UObject* OldCDO = ClassToReinstance->GetDefaultObject(); DuplicatedClass->ClassDefaultObject = (UObject*)StaticDuplicateObject(OldCDO, GetTransientPackage(), *DuplicatedClass->GetDefaultObjectName().ToString()); GIsDuplicatingClassForReinstancing = false; DuplicatedClass->ClassDefaultObject->SetFlags(RF_ClassDefaultObject); DuplicatedClass->ClassDefaultObject->SetClass(DuplicatedClass); OldCDO->SetClass(DuplicatedClass); ObjectsThatShouldUseOldStuff.Add(DuplicatedClass); //CDO of REINST_ class can be used as archetype if( !bIsBytecodeOnly ) { TArray<UObject*> ObjectsToChange; const bool bIncludeDerivedClasses = false; GetObjectsOfClass(ClassToReinstance, ObjectsToChange, bIncludeDerivedClasses); for (auto ObjIt = ObjectsToChange.CreateConstIterator(); ObjIt; ++ObjIt) { (*ObjIt)->SetClass(DuplicatedClass); } TArray<UClass*> ChildrenOfClass; GetDerivedClasses(ClassToReinstance, ChildrenOfClass); for ( auto ClassIt = ChildrenOfClass.CreateConstIterator(); ClassIt; ++ClassIt ) { UClass* ChildClass = *ClassIt; UBlueprint* ChildBP = Cast<UBlueprint>(ChildClass->ClassGeneratedBy); if (ChildBP) { const bool bClassIsDirectlyGeneratedByTheBlueprint = (ChildBP->GeneratedClass == ChildClass) || (ChildBP->SkeletonGeneratedClass == ChildClass); if (ChildBP->HasAnyFlags(RF_BeingRegenerated) || !bClassIsDirectlyGeneratedByTheBlueprint) { if (ChildClass->GetSuperClass() == ClassToReinstance) { ReparentChild(ChildClass); } //TODO: some stronger condition would be nice if (!bClassIsDirectlyGeneratedByTheBlueprint) { ObjectsThatShouldUseOldStuff.Add(ChildClass); } } // If this is a direct child, change the parent and relink so the property chain is valid for reinstancing else if( !ChildBP->HasAnyFlags(RF_NeedLoad) ) { if( ChildClass->GetSuperClass() == ClassToReinstance ) { ReparentChild(ChildBP); } Children.AddUnique(ChildBP); } else { // If this is a child that caused the load of their parent, relink to the REINST class so that we can still serialize in the CDO, but do not add to later processing ReparentChild(ChildClass); } } } } // Pull the blueprint that generated this reinstance target, and gather the blueprints that are dependent on it UBlueprint* GeneratingBP = Cast<UBlueprint>(ClassToReinstance->ClassGeneratedBy); check(GeneratingBP || GIsAutomationTesting); if(GeneratingBP) { ClassToReinstanceDefaultValuesCRC = GeneratingBP->CrcPreviousCompiledCDO; FBlueprintEditorUtils::GetDependentBlueprints(GeneratingBP, Dependencies); } } }