// Tencent is pleased to support the open source community by making UnLua available. // // Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. // // Licensed under the MIT License (the "License"); // you may not use this file except in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and limitations under the License. #include "Compat/UObjectHash.h" #include "UnLuaEditorStyle.h" #include "UnLuaEditorCommands.h" #include "Misc/CoreDelegates.h" #include "Editor.h" #include "BlueprintEditorModule.h" #include "UnLuaIntelliSenseGenerator.h" #include "UnLua.h" #include "ISettingsModule.h" #include "ISettingsSection.h" #include "UnLuaEditorSettings.h" #include "UnLuaEditorFunctionLibrary.h" #include "UnLuaFunctionLibrary.h" #include "Kismet2/DebuggerCommands.h" #include "Interfaces/IMainFrameModule.h" #include "Interfaces/IPluginManager.h" #include "Settings/ProjectPackagingSettings.h" #include "Toolbars/AnimationBlueprintToolbar.h" #include "Toolbars/BlueprintToolbar.h" #include "Toolbars/MainMenuToolbar.h" #if ENGINE_MAJOR_VERSION > 4 #include "UObject/ObjectSaveContext.h" #endif #define LOCTEXT_NAMESPACE "FUnLuaEditorModule" class FUnLuaEditorModule : public IModuleInterface { public: FUnLuaEditorModule() { } virtual void StartupModule() override { Style = FUnLuaEditorStyle::GetInstance(); FUnLuaEditorCommands::Register(); FCoreDelegates::OnPostEngineInit.AddRaw(this, &FUnLuaEditorModule::OnPostEngineInit); MainMenuToolbar = MakeShareable(new FMainMenuToolbar); BlueprintToolbar = MakeShareable(new FBlueprintToolbar); AnimationBlueprintToolbar = MakeShareable(new FAnimationBlueprintToolbar); UUnLuaEditorFunctionLibrary::WatchScriptDirectory(); #if ENGINE_MAJOR_VERSION > 4 UPackage::PreSavePackageWithContextEvent.AddRaw(this, &FUnLuaEditorModule::OnPackageSavingWithContext); UPackage::PackageSavedWithContextEvent.AddRaw(this, &FUnLuaEditorModule::OnPackageSavedWithContext); #else UPackage::PreSavePackageEvent.AddRaw(this, &FUnLuaEditorModule::OnPackageSaving); UPackage::PackageSavedEvent.AddRaw(this, &FUnLuaEditorModule::OnPackageSaved); #endif SetupPackagingSettings(); } virtual void ShutdownModule() override { FUnLuaEditorCommands::Unregister(); FCoreDelegates::OnPostEngineInit.RemoveAll(this); UnregisterSettings(); #if ENGINE_MAJOR_VERSION > 4 UPackage::PreSavePackageWithContextEvent.RemoveAll(this); UPackage::PackageSavedWithContextEvent.RemoveAll(this); #else UPackage::PreSavePackageEvent.RemoveAll(this); UPackage::PackageSavedEvent.RemoveAll(this); #endif } private: void OnPostEngineInit() { RegisterSettings(); if (!GEditor) { // Loading MainFrame module with '-game' is not supported return; } MainMenuToolbar->Initialize(); BlueprintToolbar->Initialize(); AnimationBlueprintToolbar->Initialize(); FUnLuaIntelliSenseGenerator::Get()->Initialize(); IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked(TEXT("MainFrame")); MainFrameModule.OnMainFrameCreationFinished().AddRaw(this, &FUnLuaEditorModule::OnMainFrameCreationFinished); } void OnMainFrameCreationFinished(TSharedPtr InRootWindow, bool bIsNewProjectWindow) { // register default key input to 'HotReload' Lua FPlayWorldCommands::GlobalPlayWorldActions->MapAction( FUnLuaEditorCommands::Get().HotReload, FExecuteAction::CreateStatic(UUnLuaFunctionLibrary::HotReload), FCanExecuteAction()); } void RegisterSettings() const { ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"); if (SettingsModule) { const TSharedPtr Section = SettingsModule->RegisterSettings("Project", "Plugins", "UnLua Editor", LOCTEXT("UnLuaEditorSettingsName", "UnLua Editor"), LOCTEXT("UnLuaEditorSettingsDescription", "UnLua Editor Settings"), GetMutableDefault()); Section->OnModified().BindRaw(this, &FUnLuaEditorModule::OnSettingsModified); } } void UnregisterSettings() const { ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"); if (SettingsModule) SettingsModule->UnregisterSettings("Project", "Plugins", "UnLua Editor"); } bool OnSettingsModified() const { const auto BuildFile = IPluginManager::Get().FindPlugin(TEXT("UnLua"))->GetBaseDir() / "Source/UnLua/UnLua.Build.cs"; auto& FileManager = IFileManager::Get(); if (FileManager.FileExists(*BuildFile)) FileManager.SetTimeStamp(*BuildFile, FDateTime::UtcNow()); return true; } #if ENGINE_MAJOR_VERSION > 4 void OnPackageSavingWithContext(UPackage* Package, FObjectPreSaveContext Context) { OnPackageSaving(Package); } void OnPackageSavedWithContext(const FString& PackageFileName, UPackage* Package, FObjectPostSaveContext Context) { OnPackageSaved(PackageFileName, Package); } #endif void OnPackageSaving(UPackage* Package) { if (!GEditor || !GEditor->PlayWorld) return; ForEachObjectWithPackage(Package, [this, Package](UObject* Object) { const auto Class = Cast(Object); if (!Class || Class->GetName().StartsWith(TEXT("SKEL_")) || Class->GetName().StartsWith(TEXT("REINST_"))) return true; SuspendedPackages.Add(Package, Class); return false; }, false); for (const auto Pair : SuspendedPackages) ULuaFunction::SuspendOverrides(Pair.Value); } void OnPackageSaved(const FString& PackageFileName, UObject* Outer) { if (!GEditor || !GEditor->PlayWorld) return; UPackage* Package = (UPackage*)Outer; if (SuspendedPackages.Contains(Package)) { ULuaFunction::ResumeOverrides(SuspendedPackages[Package]); SuspendedPackages.Remove(Package); } } void SetupPackagingSettings() { auto ScriptPaths = TArray{TEXT("Script"), TEXT("../Plugins/UnLua/Content/Script")}; const auto Plugins = IPluginManager::Get().GetEnabledPlugins(); for (const auto Plugin : Plugins) { if (!Plugin->CanContainContent()) continue; const auto ContentDir = Plugin->GetContentDir(); if (!ContentDir.Contains("UnLuaExtensions")) continue; auto ScriptPath = ContentDir / "Script"; if (!FPaths::DirectoryExists(ScriptPath)) continue; if (FPaths::MakePathRelativeTo(ScriptPath, *FPaths::ProjectContentDir())) ScriptPaths.Add(ScriptPath); } const auto PackagingSettings = GetMutableDefault(); bool bModified = false; auto Exists = [&](const auto Path) { for (const auto& DirPath : PackagingSettings->DirectoriesToAlwaysStageAsUFS) { if (DirPath.Path == Path) return true; } return false; }; for (auto& ScriptPath : ScriptPaths) { if (Exists(ScriptPath)) continue; FDirectoryPath DirectoryPath; DirectoryPath.Path = ScriptPath; PackagingSettings->DirectoriesToAlwaysStageAsUFS.Add(DirectoryPath); bModified = true; } if (bModified) { #if ENGINE_MAJOR_VERSION >= 5 PackagingSettings->TryUpdateDefaultConfigFile(); #else PackagingSettings->UpdateDefaultConfigFile(); #endif } } TSharedPtr BlueprintToolbar; TSharedPtr AnimationBlueprintToolbar; TSharedPtr MainMenuToolbar; TSharedPtr IntelliSenseGenerator; TSharedPtr Style; TMap SuspendedPackages; }; #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FUnLuaEditorModule, UnLuaEditor)