Add new plugins, refactor exp, introduce stat forge to replace GAS

This commit is contained in:
2026-07-15 22:48:04 +02:00
parent 3c084d9669
commit da0a0b643f
267 changed files with 33330 additions and 48 deletions

View File

@@ -0,0 +1,262 @@
// Copyright Steve Streeting 2022
// Released under the MIT license https://opensource.org/license/MIT/
#include "SUDSScriptFactory.h"
#include "PackageTools.h"
#include "SUDSEditorSettings.h"
#include "SUDSEditorVoiceOverTools.h"
#include "SUDSMessageLogger.h"
#include "SUDSScript.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "EditorFramework/AssetImportData.h"
#include "Internationalization/StringTable.h"
#include "Editor.h"
#include "ObjectTools.h"
#include "Internationalization/StringTableCore.h"
USUDSScriptFactory::USUDSScriptFactory()
{
SupportedClass = USUDSScript::StaticClass();
bCreateNew = false;
bEditorImport = true;
bText = true;
Formats.Add(TEXT("sud;SUDS Script File"));
}
UObject* USUDSScriptFactory::FactoryCreateText(UClass* InClass,
UObject* InParent,
FName InName,
EObjectFlags Flags,
UObject* Context,
const TCHAR* Type,
const TCHAR*& Buffer,
const TCHAR* BufferEnd,
FFeedbackContext* Warn)
{
Flags |= RF_Transactional;
FSUDSMessageLogger::ClearMessages();
FSUDSMessageLogger Logger;
USUDSScript* Result = nullptr;
GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetPreImport(this, InClass, InParent, InName, Type);
const FString FactoryCurrentFilename = UFactory::GetCurrentFilename();
FString CurrentSourcePath;
FString FilenameNoExtension;
FString UnusedExtension;
FPaths::Split(FactoryCurrentFilename, CurrentSourcePath, FilenameNoExtension, UnusedExtension);
const FString LongPackagePath = FPackageName::GetLongPackagePath(InParent->GetOutermost()->GetPathName());
const FString NameForErrors(InName.ToString());
// Now parse this using utility
if(Importer.ImportFromBuffer(Buffer, BufferEnd - Buffer, NameForErrors, &Logger, false))
{
// Populate with data
Result = NewObject<USUDSScript>(InParent, InName, Flags);
UStringTable* StringTable = CreateStringTable(InParent, InName, Result, Flags, &Logger);
Importer.PopulateAsset(Result, StringTable);
// Register source info
const FMD5Hash Hash = FSUDSScriptImporter::CalculateHash(Buffer, BufferEnd - Buffer);
Result->AssetImportData->Update(FactoryCurrentFilename, Hash);
// VO assets at import time?
if (ShouldGenerateVoiceAssets(LongPackagePath))
{
FSUDSEditorVoiceOverTools::GenerateAssets(Result, Flags, &Logger);
}
}
GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetPostImport(this, Result);
return Result;
}
bool USUDSScriptFactory::ShouldGenerateVoiceAssets(const FString& PackagePath) const
{
if (auto Settings = GetDefault<USUDSEditorSettings>())
{
return Settings->ShouldGenerateVoiceAssets(PackagePath);
}
return false;
}
UStringTable* USUDSScriptFactory::CreateStringTable(UObject* ScriptParent, FName InName, USUDSScript* Script, EObjectFlags Flags, FSUDSMessageLogger* Logger)
{
auto Settings = GetDefault<USUDSEditorSettings>();
const bool bCreateSeparatePackage = Settings && Settings->bCreateStringTablesAsSeparatePackages;
const FName StringTableName = FName(InName.ToString() + "Strings");
UStringTable* Table = nullptr;
auto Registry = &FModuleManager::LoadModuleChecked<FAssetRegistryModule>(AssetRegistryConstants::ModuleName).Get();
const FString PackageDir = FPackageName::GetLongPackagePath(Script->GetOuter()->GetOutermost()->GetPathName());
const FString PackageName = UPackageTools::SanitizePackageName(PackageDir / StringTableName.ToString());
if (bCreateSeparatePackage)
{
// Create string table as its own package (.uasset)
{
// Destroy any string table packed together with the script
const FString ScriptPackageName = Script->GetPackage()->GetPathName();
TArray<FAssetData> ScriptAssets;
Registry->GetAssetsByPackageName(*ScriptPackageName, ScriptAssets);
TArray<UObject*> StringTableAssets;
for (auto& Asset : ScriptAssets)
{
if (Asset.GetClass() == UStringTable::StaticClass())
{
StringTableAssets.Add(Asset.GetAsset());
}
}
if (StringTableAssets.Num() > 0)
{
ObjectTools::ForceDeleteObjects(StringTableAssets, false );
}
}
if (FPackageName::DoesPackageExist(*PackageName))
{
// Package already exists, so try and import over the top of it, if it doesn't already have a source file path
TArray<FAssetData> Assets;
if (Registry->GetAssetsByPackageName(*PackageName, Assets))
{
if (Assets.Num() > 0)
{
if (Assets[0].GetAsset()->IsA(UStringTable::StaticClass()))
{
Table = Cast<UStringTable>(Assets[0].GetAsset());
Table->GetMutableStringTable()->ClearSourceStrings();
Table->MarkPackageDirty();
}
else
{
Logger->Logf(ELogVerbosity::Error,
TEXT(
"Asset %s already exists but is not a String Table! Cannot replace, please move aside and re-import script."),
*PackageName);
return nullptr;
}
}
}
}
if (!Table)
{
// String Table didn't exist (although package might have)
// It's safe to call CreatePackage either way, it'll return the existing one if needed
UPackage* Package = CreatePackage(*PackageName);
if (!ensure(Package))
{
Logger->Logf(ELogVerbosity::Error,
TEXT("Failed to create/retrieve package for string table %s"),
*PackageName);
}
else
{
//Logger->Logf(ELogVerbosity::Display, TEXT("Creating string table %s"), *PackageName);
// This constructor registers the string table with FStringTableRegistry
Table = NewObject<UStringTable>(Package, StringTableName, Flags);
Package->FullyLoad();
Table->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(Table);
}
}
}
else
{
// Destroy separate strings package if it exists (could flip this option off after importing)
if (FPackageName::DoesPackageExist(*PackageName))
{
TArray<FAssetData> Assets;
if (Registry->GetAssetsByPackageName(*PackageName, Assets))
{
if (Assets.Num() > 0)
{
// Note: we need to force deletion because old string table will be referenced
// Unfortunately there is no ObjectTools::ForceDeleteAssets, only the inner
// ForceDeleteObjects, so we need to do it ourselves
ForceDeleteAssets(Assets);
}
}
}
// Default route, create string table inside script package
Table = NewObject<UStringTable>(ScriptParent, StringTableName, Flags);
}
return Table;
}
void USUDSScriptFactory::ForceDeleteAssets(const TArray<FAssetData>& AssetsToDelete)
{
// Copied from ObjectTools::DeleteAssets, except that we're using ForceDeleteObjects so we
// don't have to display any confusing messages about moving the string table into the shared asset
TArray<TWeakObjectPtr<UPackage>> PackageFilesToDelete;
TArray<UObject*> ObjectsToDelete;
for ( int i = 0; i < AssetsToDelete.Num(); i++ )
{
const FAssetData& AssetData = AssetsToDelete[i];
UObject *ObjectToDelete = AssetData.GetAsset({ ULevel::LoadAllExternalObjectsTag });
// Assets can be loaded even when their underlying type/class no longer exists...
if ( ObjectToDelete!=nullptr )
{
ObjectsToDelete.Add( ObjectToDelete );
}
else if ( AssetData.IsUAsset() )
{
// ... In this cases there is no underlying asset or type so remove the package itself directly after confirming it's valid to do so.
FString PackageFilename;
if( !FPackageName::DoesPackageExist( AssetData.PackageName.ToString(), &PackageFilename ) )
{
// Could not determine filename for package so we can not delete
continue;
}
UPackage* Package = FindPackage(nullptr, *AssetData.PackageName.ToString());
if ( Package )
{
PackageFilesToDelete.Add(Package);
}
}
}
int32 NumObjectsToDelete = ObjectsToDelete.Num();
if ( NumObjectsToDelete > 0 )
{
ObjectTools::ForceDeleteObjects( ObjectsToDelete, false );
}
const int32 NumPackagesToDelete = PackageFilesToDelete.Num();
if (NumPackagesToDelete > 0)
{
TArray<UPackage*> PackagePointers;
for ( const auto& PkgIt : PackageFilesToDelete )
{
UPackage* Package = PkgIt.Get();
if ( Package )
{
PackagePointers.Add(Package);
}
}
if ( PackagePointers.Num() > 0 )
{
const bool bPerformReferenceCheck = true;
ObjectTools::CleanupAfterSuccessfulDelete(PackagePointers, bPerformReferenceCheck);
}
}
}