void FBlueprintCompileReinstancer::ReconstructOwnerInstances(TSubclassOf<UActorComponent> ComponentClass) { if (ComponentClass == nullptr) { return; } TArray<UObject*> ComponentInstances; GetObjectsOfClass(ComponentClass, ComponentInstances, /*bIncludeDerivedClasses =*/false); TSet<AActor*> OwnerInstances; for (UObject* ComponentObj : ComponentInstances) { UActorComponent* Component = CastChecked<UActorComponent>(ComponentObj); if (AActor* OwningActor = Component->GetOwner()) { // we don't just rerun construction here, because we could end up // doing it twice for the same actor (if it had multiple components // of this kind), so we put that off as a secondary pass OwnerInstances.Add(OwningActor); } } for (AActor* ComponentOwner : OwnerInstances) { ComponentOwner->RerunConstructionScripts(); } }
void FSkookumScriptEditor::on_class_updated(UClass * ue_class_p) { // 1) Refresh actions (in Blueprint editor drop down menu) FBlueprintActionDatabase::Get().RefreshClassActions(ue_class_p); // Storage for gathered objects TArray<UObject*> obj_array; // 2) Refresh node display of all SkookumScript function call nodes obj_array.Reset(); GetObjectsOfClass(UK2Node_CallFunction::StaticClass(), obj_array, true, RF_ClassDefaultObject); for (auto obj_p : obj_array) { UK2Node_CallFunction * function_node_p = Cast<UK2Node_CallFunction>(obj_p); UFunction * target_function_p = function_node_p->GetTargetFunction(); // Also refresh all nodes with no target function as it is probably a Sk function that was deleted //if (!target_function_p || get_runtime()->is_skookum_blueprint_function(target_function_p)) if (target_function_p && get_runtime()->is_skookum_blueprint_function(target_function_p)) { const UEdGraphSchema * schema_p = function_node_p->GetGraph()->GetSchema(); schema_p->ReconstructNode(*function_node_p, true); } } // 3) Refresh node display of all SkookumScript event nodes obj_array.Reset(); GetObjectsOfClass(UK2Node_Event::StaticClass(), obj_array, true, RF_ClassDefaultObject); for (auto obj_p : obj_array) { UK2Node_Event * event_node_p = Cast<UK2Node_Event>(obj_p); UFunction * event_function_p = event_node_p->FindEventSignatureFunction(); if (event_function_p && get_runtime()->is_skookum_blueprint_event(event_function_p)) { const UEdGraphSchema * schema_p = event_node_p->GetGraph()->GetSchema(); schema_p->ReconstructNode(*event_node_p, true); } } // 4) Try recompiling any Blueprints that previously had errors recompile_blueprints_with_errors(); }
//--------------------------------------------------------------------------------------- // Get array of actors of the given class or a superclass static UClass * get_actor_super_class_array(SkClass * sk_class_p, TArray<UObject*> * object_array_p, SkClass ** sk_superclass_pp) { UClass * ue_superclass_p; SkClass * sk_superclass_p = SkUEClassBindingHelper::find_most_derived_super_class_known_to_ue(sk_class_p, &ue_superclass_p); if (ue_superclass_p) { object_array_p->Reserve(1024); GetObjectsOfClass(ue_superclass_p, *object_array_p, true, RF_ClassDefaultObject); } *sk_superclass_pp = sk_superclass_p; return ue_superclass_p; }
//--------------------------------------------------------------------------------------- // Find blueprints that have compile errors and try recompiling them // (as errors might be due to SkookumScript not having been initialized at previous compile time void FSkookumScriptEditor::recompile_blueprints_with_errors() { TArray<UObject*> blueprint_array; GetObjectsOfClass(UBlueprint::StaticClass(), blueprint_array, false, RF_ClassDefaultObject); for (UObject * obj_p : blueprint_array) { UBlueprint * blueprint_p = static_cast<UBlueprint *>(obj_p); if (blueprint_p->Status == BS_Error) { FKismetEditorUtilities::CompileBlueprint(blueprint_p); } } }
void FBlueprintCompileReinstancer::ReinstanceFast() { UE_LOG(LogBlueprint, Log, TEXT("BlueprintCompileReinstancer: Doing a fast path refresh on class '%s'."), *GetPathNameSafe(ClassToReinstance)); TArray<UObject*> ObjectsToReplace; GetObjectsOfClass(DuplicatedClass, ObjectsToReplace, /*bIncludeDerivedClasses=*/ false); const bool bIsActor = ClassToReinstance->IsChildOf<AActor>(); const bool bIsAnimInstance = ClassToReinstance->IsChildOf<UAnimInstance>(); const bool bIsComponent = ClassToReinstance->IsChildOf<UActorComponent>(); for (auto Obj : ObjectsToReplace) { UE_LOG(LogBlueprint, Log, TEXT(" Fast path is refreshing (not replacing) %s"), *Obj->GetFullName()); if ((!Obj->IsTemplate() || bIsComponent) && !Obj->IsPendingKill()) { const bool bIsSelected = bIsActor ? Obj->IsSelected() : false; Obj->SetClass(ClassToReinstance); if (bIsActor) { auto Actor = CastChecked<AActor>(Obj); Actor->ReregisterAllComponents(); Actor->RerunConstructionScripts(); if (bIsSelected) { GEditor->SelectActor(Actor, /*bInSelected =*/true, /*bNotify =*/true, false, true); } } if (bIsAnimInstance) { // Initialising the anim instance isn't enough to correctly set up the skeletal mesh again in a // paused world, need to initialise the skeletal mesh component that contains the anim instance. if (USkeletalMeshComponent* SkelComponent = Cast<USkeletalMeshComponent>(Obj->GetOuter())) { SkelComponent->InitAnim(true); } } } } TArray<UObject*> SourceObjects; TMap<UObject*, UObject*> OldToNewInstanceMap; TMap<FStringAssetReference, UObject*> ReinstancedObjectsWeakReferenceMap; FReplaceReferenceHelper::IncludeCDO(DuplicatedClass, ClassToReinstance, OldToNewInstanceMap, SourceObjects, OriginalCDO); FReplaceReferenceHelper::FindAndReplaceReferences(SourceObjects, &ObjectsThatShouldUseOldStuff, ObjectsToReplace, OldToNewInstanceMap, ReinstancedObjectsWeakReferenceMap); }
//--------------------------------------------------------------------------------------- // Generate SkookumScript class script files for all known blueprint assets void FSkookumScriptEditor::generate_all_class_script_files() { if (!m_overlay_path.IsEmpty()) { TArray<UObject*> blueprint_array; GetObjectsOfClass(UBlueprint::StaticClass(), blueprint_array, false, RF_ClassDefaultObject); for (UObject * obj_p : blueprint_array) { generate_class_script_files(static_cast<UBlueprint *>(obj_p)->GeneratedClass, true); } generate_used_class_script_files(); } }
// TODO: shell we cache the dependency? static TArray<UDataTable*> GetTablesDependentOnStruct(UUserDefinedStruct* Struct) { TArray<UDataTable*> Result; if (Struct) { TArray<UObject*> DataTables; GetObjectsOfClass(UDataTable::StaticClass(), DataTables); for (auto DataTableObj : DataTables) { auto DataTable = Cast<UDataTable>(DataTableObj); if (DataTable && (Struct == DataTable->RowStruct)) { Result.Add(DataTable); } } } return Result; }
//Performs a quick Map lookup if we're already tracking this key, full TObjectIteration otherwise void FLiveEditorManager::FPieObjectCache::FindPiePartners( const class UObject* EditorObject, TArray< TWeakObjectPtr<UObject> >& OutValues ) { if ( EditorObject == NULL || !bCacheActive ) return; if ( ObjectLookupCache.Contains(EditorObject) ) { ObjectLookupCache.MultiFind( EditorObject, OutValues ); } else { UWorld *OldWorld = NULL; if ( GEditor->PlayWorld != NULL ) { OldWorld = GWorld; GWorld = GEditor->PlayWorld; } TArray<UObject*> ObjectsToChange; const bool bIncludeDerivedClasses = true; GetObjectsOfClass(EditorObject->GetClass(), ObjectsToChange, bIncludeDerivedClasses); for ( auto ObjIt = ObjectsToChange.CreateIterator(); ObjIt; ++ObjIt ) { UObject *Object = *ObjIt; //UWorld *World = GEngine->GetWorldFromContextObject( Object, false ); //if ( World == NULL || World->WorldType != EWorldType::PIE ) // continue; if ( !nPieObjectCache::IsPiePartner(*EditorObject, *Object) ) continue; ObjectLookupCache.AddUnique( EditorObject, Object ); TrackedObjects.Add( Object ); OutValues.Add( Object ); } if ( GEditor->PlayWorld != NULL ) { GWorld = OldWorld; } } }
void FBlueprintCompileReinstancer::ReplaceInstancesOfClass(UClass* OldClass, UClass* NewClass, UObject* OriginalCDO, TSet<UObject*>* ObjectsThatShouldUseOldStuff) { USelection* SelectedActors; bool bSelectionChanged = false; TArray<UObject*> ObjectsToReplace; const bool bLogConversions = false; // for debugging // Map of old objects to new objects TMap<UObject*, UObject*> OldToNewInstanceMap; TMap<UClass*, UClass*> OldToNewClassMap; OldToNewClassMap.Add(OldClass, NewClass); TMap<FStringAssetReference, UObject*> ReinstancedObjectsWeakReferenceMap; // actors being replace TArray<FActorReplacementHelper> ReplacementActors; // A list of objects (e.g. Blueprints) that potentially have editors open that we need to refresh TArray<UObject*> PotentialEditorsForRefreshing; // A list of component owners that need their construction scripts re-ran (because a component of theirs has been reinstanced) TSet<AActor*> OwnersToReconstruct; // Set global flag to let system know we are reconstructing blueprint instances TGuardValue<bool> GuardTemplateNameFlag(GIsReconstructingBlueprintInstances, true); struct FObjectRemappingHelper { void OnObjectsReplaced(const TMap<UObject*, UObject*>& InReplacedObjects) { ReplacedObjects.Append(InReplacedObjects); } TMap<UObject*, UObject*> ReplacedObjects; } ObjectRemappingHelper; FDelegateHandle OnObjectsReplacedHandle = GEditor->OnObjectsReplaced().AddRaw(&ObjectRemappingHelper,&FObjectRemappingHelper::OnObjectsReplaced); { BP_SCOPED_COMPILER_EVENT_STAT(EKismetReinstancerStats_ReplaceInstancesOfClass); const bool bIncludeDerivedClasses = false; GetObjectsOfClass(OldClass, ObjectsToReplace, bIncludeDerivedClasses); SelectedActors = GEditor->GetSelectedActors(); SelectedActors->BeginBatchSelectOperation(); SelectedActors->Modify(); // Then fix 'real' (non archetype) instances of the class for (UObject* OldObject : ObjectsToReplace) { // Skip non-archetype instances, EXCEPT for component templates const bool bIsComponent = NewClass->IsChildOf(UActorComponent::StaticClass()); if ((!bIsComponent && OldObject->IsTemplate()) || OldObject->IsPendingKill()) { continue; } UBlueprint* CorrespondingBlueprint = Cast<UBlueprint>(OldObject->GetClass()->ClassGeneratedBy); UObject* OldBlueprintDebugObject = nullptr; // If this object is being debugged, cache it off so we can preserve the 'object being debugged' association if ((CorrespondingBlueprint != nullptr) && (CorrespondingBlueprint->GetObjectBeingDebugged() == OldObject)) { OldBlueprintDebugObject = OldObject; } AActor* OldActor = Cast<AActor>(OldObject); UObject* NewUObject = nullptr; // if the object to replace is an actor... if (OldActor != nullptr) { FVector Location = FVector::ZeroVector; FRotator Rotation = FRotator::ZeroRotator; if (USceneComponent* OldRootComponent = OldActor->GetRootComponent()) { Location = OldActor->GetActorLocation(); Rotation = OldActor->GetActorRotation(); } // If this actor was spawned from an Archetype, we spawn the new actor from the new version of that archetype UObject* OldArchetype = OldActor->GetArchetype(); UWorld* World = OldActor->GetWorld(); AActor* NewArchetype = Cast<AActor>(OldToNewInstanceMap.FindRef(OldArchetype)); // Check that either this was an instance of the class directly, or we found a new archetype for it check(OldArchetype == OldClass->GetDefaultObject() || NewArchetype); // Spawn the new actor instance, in the same level as the original, but deferring running the construction script until we have transferred modified properties ULevel* ActorLevel = OldActor->GetLevel(); UClass** MappedClass = OldToNewClassMap.Find(OldActor->GetClass()); UClass* SpawnClass = MappedClass ? *MappedClass : NewClass; FActorSpawnParameters SpawnInfo; SpawnInfo.OverrideLevel = ActorLevel; SpawnInfo.Template = NewArchetype; SpawnInfo.bNoCollisionFail = true; SpawnInfo.bDeferConstruction = true; // Temporarily remove the deprecated flag so we can respawn the Blueprint in the level const bool bIsClassDeprecated = SpawnClass->HasAnyClassFlags(CLASS_Deprecated); SpawnClass->ClassFlags &= ~CLASS_Deprecated; AActor* NewActor = World->SpawnActor(SpawnClass, &Location, &Rotation, SpawnInfo); // Reassign the deprecated flag if it was previously assigned if (bIsClassDeprecated) { SpawnClass->ClassFlags |= CLASS_Deprecated; } check(NewActor != nullptr); NewUObject = NewActor; // store the new actor for the second pass (NOTE: this detaches // OldActor from all child/parent attachments) // // running the NewActor's construction-script is saved for that // second pass (because the construction-script may reference // another instance that hasn't been replaced yet). ReplacementActors.Add(FActorReplacementHelper(NewActor, OldActor)); ReinstancedObjectsWeakReferenceMap.Add(OldObject, NewUObject); OldActor->DestroyConstructedComponents(); // don't want to serialize components from the old actor // Unregister native components so we don't copy any sub-components they generate for themselves (like UCameraComponent does) OldActor->UnregisterAllComponents(); // Unregister any native components, might have cached state based on properties we are going to overwrite NewActor->UnregisterAllComponents(); UEditorEngine::CopyPropertiesForUnrelatedObjects(OldActor, NewActor); // reset properties/streams NewActor->ResetPropertiesForConstruction(); // register native components NewActor->RegisterAllComponents(); // // clean up the old actor (unselect it, remove it from the world, etc.)... if (OldActor->IsSelected()) { GEditor->SelectActor(OldActor, /*bInSelected =*/false, /*bNotify =*/false); bSelectionChanged = true; } if (GEditor->Layers.IsValid()) // ensure(NULL != GEditor->Layers) ?? While cooking the Layers is NULL. { GEditor->Layers->DisassociateActorFromLayers(OldActor); } World->EditorDestroyActor(OldActor, /*bShouldModifyLevel =*/true); OldToNewInstanceMap.Add(OldActor, NewActor); } else { FName OldName(OldObject->GetFName()); OldObject->Rename(NULL, OldObject->GetOuter(), REN_DoNotDirty | REN_DontCreateRedirectors); NewUObject = NewObject<UObject>(OldObject->GetOuter(), NewClass, OldName); check(NewUObject != nullptr); UEditorEngine::CopyPropertiesForUnrelatedObjects(OldObject, NewUObject); if (UAnimInstance* AnimTree = Cast<UAnimInstance>(NewUObject)) { // Initialising the anim instance isn't enough to correctly set up the skeletal mesh again in a // paused world, need to initialise the skeletal mesh component that contains the anim instance. if (USkeletalMeshComponent* SkelComponent = Cast<USkeletalMeshComponent>(AnimTree->GetOuter())) { SkelComponent->InitAnim(true); } } OldObject->RemoveFromRoot(); OldObject->MarkPendingKill(); OldToNewInstanceMap.Add(OldObject, NewUObject); if (bIsComponent) { UActorComponent* Component = Cast<UActorComponent>(NewUObject); AActor* OwningActor = Component->GetOwner(); if (OwningActor) { OwningActor->ResetOwnedComponents(); // Check to see if they have an editor that potentially needs to be refreshed if (OwningActor->GetClass()->ClassGeneratedBy) { PotentialEditorsForRefreshing.AddUnique(OwningActor->GetClass()->ClassGeneratedBy); } // we need to keep track of actor instances that need // their construction scripts re-ran (since we've just // replaced a component they own) OwnersToReconstruct.Add(OwningActor); } } } // If this original object came from a blueprint and it was in the selected debug set, change the debugging to the new object. if ((CorrespondingBlueprint) && (OldBlueprintDebugObject) && (NewUObject)) { CorrespondingBlueprint->SetObjectBeingDebugged(NewUObject); } if (bLogConversions) { UE_LOG(LogBlueprint, Log, TEXT("Converted instance '%s' to '%s'"), *OldObject->GetPathName(), *NewUObject->GetPathName()); } } } GEditor->OnObjectsReplaced().Remove(OnObjectsReplacedHandle); // Now replace any pointers to the old archetypes/instances with pointers to the new one TArray<UObject*> SourceObjects; TArray<UObject*> DstObjects; OldToNewInstanceMap.GenerateKeyArray(SourceObjects); OldToNewInstanceMap.GenerateValueArray(DstObjects); // Also look for references in new spawned objects. SourceObjects.Append(DstObjects); FReplaceReferenceHelper::IncludeCDO(OldClass, NewClass, OldToNewInstanceMap, SourceObjects, OriginalCDO); FReplaceReferenceHelper::FindAndReplaceReferences(SourceObjects, ObjectsThatShouldUseOldStuff, ObjectsToReplace, OldToNewInstanceMap, ReinstancedObjectsWeakReferenceMap); { BP_SCOPED_COMPILER_EVENT_STAT(EKismetReinstancerStats_ReplacementConstruction); // the process of setting up new replacement actors is split into two // steps (this here, is the second)... // // the "finalization" here runs the replacement actor's construction- // script and is left until late to account for a scenario where the // construction-script attempts to modify another instance of the // same class... if this were to happen above, in the ObjectsToReplace // loop, then accessing that other instance would cause an assert in // UProperty::ContainerPtrToValuePtrInternal() (which appropriatly // complains that the other instance's type doesn't match because it // hasn't been replaced yet... that's why we wait until after // FArchiveReplaceObjectRef to run construction-scripts). for (FActorReplacementHelper& ReplacementActor : ReplacementActors) { ReplacementActor.Finalize(ObjectRemappingHelper.ReplacedObjects); } } SelectedActors->EndBatchSelectOperation(); if (bSelectionChanged) { GEditor->NoteSelectionChange(); } if (GEditor) { // Refresh any editors for objects that we've updated components for for (auto BlueprintAsset : PotentialEditorsForRefreshing) { FBlueprintEditor* BlueprintEditor = static_cast<FBlueprintEditor*>(FAssetEditorManager::Get().FindEditorForAsset(BlueprintAsset, /*bFocusIfOpen =*/false)); if (BlueprintEditor) { BlueprintEditor->RefreshEditors(); } } } // in the case where we're replacing component instances, we need to make // sure to re-run their owner's construction scripts for (AActor* ActorInstance : OwnersToReconstruct) { ActorInstance->RerunConstructionScripts(); } }
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); } } }
//--------------------------------------------------------------------------------------- // void FSkookumScriptRuntime::tick_remote() { if (!IsRunningCommandlet()) { // Request recompilation of binaries if script files changed if (m_freshen_binaries_requested) { m_remote_client.cmd_compiled_state(true); m_freshen_binaries_requested = false; } // Remote communication to and from SkookumScript IDE. // Needs to be called whether in editor or game and whether paused or not // $Revisit - CReis This is probably a hack. The remote client update should probably // live somewhere other than a tick method such as its own thread. m_remote_client.process_incoming(); // Re-load compiled binaries? // If the game is currently running, delay until it's not if (m_remote_client.is_load_compiled_binaries_requested() && SkookumScript::get_initialization_level() < SkookumScript::InitializationLevel_gameplay) { // Makes sure the SkookumScript runtime object is initialized at this point ensure_runtime_initialized(); // Load the Skookum class hierarchy scripts in compiled binary form bool is_first_time = !is_skookum_initialized(); bool success_b = m_runtime.load_and_bind_compiled_scripts(); SK_ASSERTX(success_b, AErrMsg("Unable to load SkookumScript compiled binaries!", AErrLevel_notify)); m_remote_client.clear_load_compiled_binaries_requested(); // After reloading, re-resolve the raw data of all dynamic classes #if WITH_EDITORONLY_DATA TArray<UObject*> blueprint_array; GetObjectsOfClass(UBlueprint::StaticClass(), blueprint_array, true, RF_ClassDefaultObject); for (UObject * obj_p : blueprint_array) { UBlueprint * blueprint_p = static_cast<UBlueprint *>(obj_p); if (blueprint_p->GeneratedClass) { SkClass * sk_class_p = SkUEClassBindingHelper::get_sk_class_from_ue_class(blueprint_p->GeneratedClass); if (sk_class_p) { SkUEClassBindingHelper::resolve_raw_data(sk_class_p, blueprint_p->GeneratedClass); } } } #endif if (is_first_time && is_skookum_initialized()) { #if WITH_EDITOR // Recompile Blueprints in error state as such error state might have been due to SkookumScript not being initialized at the time of compile if (m_runtime.get_editor_interface()) { m_runtime.get_editor_interface()->recompile_blueprints_with_errors(); } #endif } } } }