rusteal 0.8.0

Rust bindings for Unreal Engine 5.8.2
#include "BlueprintActionDatabase.h"
#include "Engine/Blueprint.h"
#include "K2Node.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "Kismet2/ReloadUtilities.h"
#include "MessageLogInitializationOptions.h"
#include "MessageLogModule.h"
#include "Misc/Paths.h"
#include "SourceCodeNavigation.h"
#include "Modules/ModuleManager.h"
#include "UObject/UObjectIterator.h"

DEFINE_LOG_CATEGORY_STATIC(LogRustealReload, Log, All);
#include "RustealAutoReload.h"
#include "RustealLibraries.h"
#include "URustealReifiedClass.h"

static bool UsesAny(UBlueprint *Blueprint, const TArray<UClass *> &Classes) {
  TArray<UK2Node *> Nodes;
  FBlueprintEditorUtils::GetAllNodesOfClass(Blueprint, Nodes);
  TArray<UStruct *> Dependencies;

  for (UK2Node *Node : Nodes) {
    Dependencies.Reset();

    if (!Node->HasExternalDependencies(&Dependencies)) {
      continue;
    }

    for (UStruct *Dependency : Dependencies) {
      const UClass *Class = Cast<UClass>(Dependency);

      for (UClass *Replaced : Classes) {
        if (Class && Class->IsChildOf(Replaced)) {
          return true;
        }
      }
    }
  }

  return false;
}

static void
CompileBlueprintsUsing(const TArray<TPair<UClass *, UClass *>> &Replaced) {
  TArray<UClass *> Classes;

  for (const TPair<UClass *, UClass *> &Pair : Replaced) {
    Classes.Add(Pair.Key);
    Classes.Add(Pair.Value);
  }

  TArray<UBlueprint *> Users;

  for (TObjectIterator<UBlueprint> It; It; ++It) {
    UBlueprint *Blueprint = *It;
    UClass *Generated = Blueprint->GeneratedClass;

    if (!Generated ||
        Blueprint->HasAnyFlags(RF_NeedLoad | RF_ClassDefaultObject) ||
        Blueprint->GetOutermost() == GetTransientPackage() ||
        Generated->HasAnyClassFlags(CLASS_NewerVersionExists)) {
      continue;
    }

    const bool bChild = Classes.ContainsByPredicate(
        [Generated](UClass *Class) { return Generated->IsChildOf(Class); });

    if (!bChild && UsesAny(Blueprint, Classes)) {
      Users.Add(Blueprint);
    }
  }

  for (UBlueprint *Blueprint : Users) {
    UE_LOG(LogRustealReload, Display,
           TEXT("Recompiling %s, which uses a replaced class"),
           *Blueprint->GetPathName());

    FKismetEditorUtilities::CompileBlueprint(
        Blueprint, EBlueprintCompileOptions::SkipGarbageCollection);
  }
}

static void
ReinstanceClasses(const TArray<TPair<UClass *, UClass *>> &Classes) {
  FReload Reload(EActiveReloadType::Reinstancing, TEXT("RUSTEAL"), *GLog);

  for (const TPair<UClass *, UClass *> &Pair : Classes) {
    Reload.NotifyChange(Pair.Value, Pair.Key);
  }

  Reload.Reinstance();

  CompileBlueprintsUsing(Classes);

  Reload.Finalize(false);
}

static void RefreshInterfaces(const TArray<UClass *> &Interfaces) {
  TArray<UBlueprint *> Users;

  for (TObjectIterator<UBlueprint> It; It; ++It) {
    UBlueprint *Blueprint = *It;
    UClass *Generated = Blueprint->GeneratedClass;

    if (!Generated ||
        Blueprint->HasAnyFlags(RF_NeedLoad | RF_ClassDefaultObject) ||
        Blueprint->GetOutermost() == GetTransientPackage() ||
        Generated->HasAnyClassFlags(CLASS_NewerVersionExists) ||
        (Cast<URustealReifiedClass>(Generated) &&
         Generated->ClassGeneratedBy == Blueprint)) {
      continue;
    }

    const bool bImplements =
        Interfaces.ContainsByPredicate([Generated](UClass *Interface) {
          return Generated->ImplementsInterface(Interface);
        });

    if (bImplements || UsesAny(Blueprint, Interfaces)) {
      Users.Add(Blueprint);
    }
  }

  for (UBlueprint *Blueprint : Users) {
    UE_LOG(LogRustealReload, Display,
           TEXT("Recompiling %s, which uses a changed interface"),
           *Blueprint->GetPathName());

    FBlueprintEditorUtils::ConformImplementedInterfaces(Blueprint);
    FBlueprintEditorUtils::RefreshAllNodes(Blueprint);

    FKismetEditorUtilities::CompileBlueprint(
        Blueprint, EBlueprintCompileOptions::SkipGarbageCollection);
  }

  if (FBlueprintActionDatabase *Actions = FBlueprintActionDatabase::TryGet()) {
    for (UClass *Interface : Interfaces) {
      Actions->RefreshClassActions(Interface);
    }
  }
}

#define LOCTEXT_NAMESPACE "RustealEditor"

static void OpenRustSource(const FString &File, int32 Line, int32 Column) {
  if (!FPaths::FileExists(File)) {
    UE_LOG(LogRustealReload, Warning, TEXT("No file %s to open"), *File);
    return;
  }

  if (!FSourceCodeNavigation::OpenSourceFile(File, Line, Column)) {
    FPlatformProcess::LaunchFileInDefaultExternalApplication(*File);
  }
}

static void RegisterMessageLog() {
  FMessageLogModule &MessageLog =
      FModuleManager::LoadModuleChecked<FMessageLogModule>("MessageLog");

  FMessageLogInitializationOptions Options;
  Options.bShowFilters = true;
  Options.bShowPages = false;
  Options.bAllowClear = true;

  MessageLog.RegisterLogListing("Rusteal", LOCTEXT("RustealLog", "Rusteal"),
                                Options);
}

class FRustealEditorModule : public IModuleInterface {
public:
  virtual void StartupModule() override {
    RegisterMessageLog();
    RustealSetSourceOpener(&OpenRustSource);
    RustealSetClassReinstancer(&ReinstanceClasses);
    RustealSetInterfaceRefresher(&RefreshInterfaces);
    AutoReload.Start();
  }

  virtual void ShutdownModule() override {
    AutoReload.Stop();
    RustealSetInterfaceRefresher(nullptr);
    RustealSetClassReinstancer(nullptr);
    RustealSetSourceOpener(nullptr);

    if (FMessageLogModule *MessageLog =
            FModuleManager::GetModulePtr<FMessageLogModule>("MessageLog")) {
      MessageLog->UnregisterLogListing("Rusteal");
    }
  }

private:
  FRustealAutoReload AutoReload;
};

#undef LOCTEXT_NAMESPACE

IMPLEMENT_MODULE(FRustealEditorModule, RustealEditor)