292 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			292 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								// 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 "Misc/EngineVersionComparison.h"
							 | 
						||
| 
								 | 
							
								#include "UnLuaIntelliSenseGenerator.h"
							 | 
						||
| 
								 | 
							
								#if UE_VERSION_NEWER_THAN(5, 1, 0)
							 | 
						||
| 
								 | 
							
								#include "AssetRegistry/AssetRegistryModule.h"
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								#include "AssetRegistryModule.h"
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								#include "CoreUObject.h"
							 | 
						||
| 
								 | 
							
								#include "UnLua.h"
							 | 
						||
| 
								 | 
							
								#include "UnLuaEditorSettings.h"
							 | 
						||
| 
								 | 
							
								#include "UnLuaIntelliSense.h"
							 | 
						||
| 
								 | 
							
								#include "WidgetBlueprint.h"
							 | 
						||
| 
								 | 
							
								#include "Blueprint/WidgetTree.h"
							 | 
						||
| 
								 | 
							
								#include "Engine/Blueprint.h"
							 | 
						||
| 
								 | 
							
								#include "Interfaces/IPluginManager.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define LOCTEXT_NAMESPACE "UnLuaIntelliSenseGenerator"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TSharedPtr<FUnLuaIntelliSenseGenerator> FUnLuaIntelliSenseGenerator::Singleton;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TSharedRef<FUnLuaIntelliSenseGenerator> FUnLuaIntelliSenseGenerator::Get()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (!Singleton.IsValid())
							 | 
						||
| 
								 | 
							
								        Singleton = MakeShareable(new FUnLuaIntelliSenseGenerator);
							 | 
						||
| 
								 | 
							
								    return Singleton.ToSharedRef();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::Initialize()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (bInitialized)
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    OutputDir = IPluginManager::Get().FindPlugin("UnLua")->GetBaseDir() + "/Intermediate/IntelliSense";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
							 | 
						||
| 
								 | 
							
								    AssetRegistryModule.Get().OnAssetAdded().AddRaw(this, &FUnLuaIntelliSenseGenerator::OnAssetAdded);
							 | 
						||
| 
								 | 
							
								    AssetRegistryModule.Get().OnAssetRemoved().AddRaw(this, &FUnLuaIntelliSenseGenerator::OnAssetRemoved);
							 | 
						||
| 
								 | 
							
								    AssetRegistryModule.Get().OnAssetRenamed().AddRaw(this, &FUnLuaIntelliSenseGenerator::OnAssetRenamed);
							 | 
						||
| 
								 | 
							
								    AssetRegistryModule.Get().OnAssetUpdated().AddRaw(this, &FUnLuaIntelliSenseGenerator::OnAssetUpdated);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    bInitialized = true;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::UpdateAll()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    FARFilter Filter;
							 | 
						||
| 
								 | 
							
								#if UE_VERSION_OLDER_THAN(5, 1, 0)
							 | 
						||
| 
								 | 
							
								    Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName());
							 | 
						||
| 
								 | 
							
								    Filter.ClassNames.Add(UWidgetBlueprint::StaticClass()->GetFName());
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								    Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName());
							 | 
						||
| 
								 | 
							
								    Filter.ClassPaths.Add(UWidgetBlueprint::StaticClass()->GetClassPathName());
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    TArray<FAssetData> BlueprintAssets;
							 | 
						||
| 
								 | 
							
								    TArray<const UField*> NativeTypes;
							 | 
						||
| 
								 | 
							
								    AssetRegistryModule.Get().GetAssets(Filter, BlueprintAssets);
							 | 
						||
| 
								 | 
							
								    CollectTypes(NativeTypes);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    auto TotalCount = BlueprintAssets.Num() + NativeTypes.Num();
							 | 
						||
| 
								 | 
							
								    if (TotalCount == 0)
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    TotalCount++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    FScopedSlowTask SlowTask(TotalCount, LOCTEXT("GeneratingBlueprintsIntelliSense", "Generating Blueprints InstelliSense"));
							 | 
						||
| 
								 | 
							
								    SlowTask.MakeDialog();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (int32 i = 0; i < BlueprintAssets.Num(); i++)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (SlowTask.ShouldCancel())
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								        OnAssetUpdated(BlueprintAssets[i]);
							 | 
						||
| 
								 | 
							
								        SlowTask.EnterProgressFrame();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (const auto Type : NativeTypes)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (SlowTask.ShouldCancel())
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Export(Type);
							 | 
						||
| 
								 | 
							
								        SlowTask.EnterProgressFrame();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (SlowTask.ShouldCancel())
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    ExportUE(NativeTypes);
							 | 
						||
| 
								 | 
							
								    ExportUnLua();
							 | 
						||
| 
								 | 
							
								    SlowTask.EnterProgressFrame();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool FUnLuaIntelliSenseGenerator::IsBlueprint(const FAssetData& AssetData)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								#if UE_VERSION_OLDER_THAN(5, 1, 0)
							 | 
						||
| 
								 | 
							
								    const FName AssetClass = AssetData.AssetClass;
							 | 
						||
| 
								 | 
							
								    return AssetClass == UBlueprint::StaticClass()->GetFName() || AssetClass == UWidgetBlueprint::StaticClass()->GetFName();
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								    const auto AssetClassPath = AssetData.AssetClassPath.ToString();
							 | 
						||
| 
								 | 
							
								    return AssetClassPath == UBlueprint::StaticClass()->GetName() || AssetClassPath == UWidgetBlueprint::StaticClass()->GetName();
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool FUnLuaIntelliSenseGenerator::ShouldExport(const FAssetData& AssetData, bool bLoad)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    const auto& Settings = *GetDefault<UUnLuaEditorSettings>();
							 | 
						||
| 
								 | 
							
								    if (!Settings.bGenerateIntelliSense)
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!IsBlueprint(AssetData))
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const auto Asset = AssetData.FastGetAsset(bLoad);
							 | 
						||
| 
								 | 
							
								    if (!Asset)
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const auto Blueprint = Cast<UBlueprint>(Asset);
							 | 
						||
| 
								 | 
							
								    if (!Blueprint)
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (Blueprint->SkeletonGeneratedClass || Blueprint->GeneratedClass)
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::Export(const UBlueprint* Blueprint)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    Export(Blueprint->GeneratedClass);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::Export(const UField* Field)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 26)
							 | 
						||
| 
								 | 
							
								    const UPackage* Package = Field->GetPackage();
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								    const UPackage* Package = (UPackage*)Field->GetTypedOuter(UPackage::StaticClass());
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								    auto ModuleName = Package->GetName();
							 | 
						||
| 
								 | 
							
								    if (!Field->IsNative())
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        int32 LastSlashIndex;
							 | 
						||
| 
								 | 
							
								        if (ModuleName.FindLastChar('/', LastSlashIndex))
							 | 
						||
| 
								 | 
							
								            ModuleName.LeftInline(LastSlashIndex);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    FString FileName = UnLua::IntelliSense::GetTypeName(Field);
							 | 
						||
| 
								 | 
							
								    if (FileName.EndsWith("_C"))
							 | 
						||
| 
								 | 
							
								        FileName.LeftChopInline(2);
							 | 
						||
| 
								 | 
							
								    const FString Content = UnLua::IntelliSense::Get(Field);
							 | 
						||
| 
								 | 
							
								    SaveFile(ModuleName, FileName, Content);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::ExportUE(const TArray<const UField*> Types)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    const FString Content = UnLua::IntelliSense::GetUE(Types);
							 | 
						||
| 
								 | 
							
								    SaveFile("", "UE", Content);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::ExportUnLua()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    const auto ContentDir = IPluginManager::Get().FindPlugin(TEXT("UnLua"))->GetContentDir();
							 | 
						||
| 
								 | 
							
								    const auto SrcDir = ContentDir / "IntelliSense";
							 | 
						||
| 
								 | 
							
								    const auto DstDir = OutputDir;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
							 | 
						||
| 
								 | 
							
								    if (!PlatformFile.DirectoryExists(*SrcDir))
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    PlatformFile.CopyDirectoryTree(*DstDir, *SrcDir, true);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::CollectTypes(TArray<const UField*>& Types)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    for (TObjectIterator<UClass> It; It; ++It)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        const UClass* Class = *It;
							 | 
						||
| 
								 | 
							
								        const FString ClassName = Class->GetName();
							 | 
						||
| 
								 | 
							
								        // ReSharper disable StringLiteralTypo
							 | 
						||
| 
								 | 
							
								        if (ClassName.StartsWith("SKEL_")
							 | 
						||
| 
								 | 
							
								            || ClassName.StartsWith("PLACEHOLDER-CLASS")
							 | 
						||
| 
								 | 
							
								            || ClassName.StartsWith("REINST_")
							 | 
						||
| 
								 | 
							
								            || ClassName.StartsWith("TRASHCLASS_")
							 | 
						||
| 
								 | 
							
								            || ClassName.StartsWith("HOTRELOADED_")
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            // skip nonsense types
							 | 
						||
| 
								 | 
							
								            continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // ReSharper restore StringLiteralTypo
							 | 
						||
| 
								 | 
							
								        Types.Add(Class);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (TObjectIterator<UScriptStruct> It; It; ++It)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        const UScriptStruct* ScriptStruct = *It;
							 | 
						||
| 
								 | 
							
								        Types.Add(ScriptStruct);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (TObjectIterator<UEnum> It; It; ++It)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        const UEnum* Enum = *It;
							 | 
						||
| 
								 | 
							
								        Types.Add(Enum);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::SaveFile(const FString& ModuleName, const FString& FileName, const FString& GeneratedFileContent)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    IFileManager& FileManager = IFileManager::Get();
							 | 
						||
| 
								 | 
							
								    const FString Directory = OutputDir / ModuleName;
							 | 
						||
| 
								 | 
							
								    if (!FileManager.DirectoryExists(*Directory))
							 | 
						||
| 
								 | 
							
								        FileManager.MakeDirectory(*Directory);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const FString FilePath = FString::Printf(TEXT("%s/%s.lua"), *Directory, *FileName);
							 | 
						||
| 
								 | 
							
								    FString FileContent;
							 | 
						||
| 
								 | 
							
								    FFileHelper::LoadFileToString(FileContent, *FilePath);
							 | 
						||
| 
								 | 
							
								    if (FileContent != GeneratedFileContent)
							 | 
						||
| 
								 | 
							
								        FFileHelper::SaveStringToFile(GeneratedFileContent, *FilePath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::DeleteFile(const FString& ModuleName, const FString& FileName)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    IFileManager& FileManager = IFileManager::Get();
							 | 
						||
| 
								 | 
							
								    const FString Directory = OutputDir / ModuleName;
							 | 
						||
| 
								 | 
							
								    if (!FileManager.DirectoryExists(*Directory))
							 | 
						||
| 
								 | 
							
								        FileManager.MakeDirectory(*Directory);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const FString FilePath = FString::Printf(TEXT("%s/%s.lua"), *Directory, *FileName);
							 | 
						||
| 
								 | 
							
								    if (FileManager.FileExists(*FilePath))
							 | 
						||
| 
								 | 
							
								        FileManager.Delete(*FilePath);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::OnAssetAdded(const FAssetData& AssetData)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (!ShouldExport(AssetData))
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    OnAssetUpdated(AssetData);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    TArray<const UField*> Types;
							 | 
						||
| 
								 | 
							
								    CollectTypes(Types);
							 | 
						||
| 
								 | 
							
								    ExportUE(Types);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::OnAssetRemoved(const FAssetData& AssetData)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (!ShouldExport(AssetData))
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    DeleteFile(FString("/Game"), AssetData.AssetName.ToString());
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::OnAssetRenamed(const FAssetData& AssetData, const FString& OldPath)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (!ShouldExport(AssetData))
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    //remove old Blueprint name
							 | 
						||
| 
								 | 
							
								    const FString OldPackageName = FPackageName::GetShortName(OldPath);
							 | 
						||
| 
								 | 
							
								    DeleteFile("/Game", OldPackageName);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    //update new name 
							 | 
						||
| 
								 | 
							
								    OnAssetUpdated(AssetData);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void FUnLuaIntelliSenseGenerator::OnAssetUpdated(const FAssetData& AssetData)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (!ShouldExport(AssetData, true))
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    UBlueprint* Blueprint = LoadObject<UBlueprint>(nullptr, *AssetData.ObjectPath.ToString());
							 | 
						||
| 
								 | 
							
								    if (!Blueprint)
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Export(Blueprint);
							 | 
						||
| 
								 | 
							
								}
							 |